Commit 9a24abe5 authored by Christian Heimes's avatar Christian Heimes

Moving branch to the right location

parents

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

Place files containing code for External Method objects in this
directory.
Welcome to The Zope Source Release
Zope is an open-source web application server.
This document provides some general information about the Zope
source release and provides links to other documents.
Installation information can be found in "doc/INSTALL.txt". Other
documentation is also in the "doc" directory and in the Zope.org
documentation section at http://www.zope.org/Documentation/.
General Zope information is available at http://www.zope.org/
Report problems with this release on the Zope mailing list
(zope@zope.org) To subscribe to the list send mail to
zope-request@zope.org with "subscribe" in the subject line.
Introduction
The source release is intended for tinkerers, those who want to use
Zope components separately, people who want to use their own Python,
and people who work on platforms that are not supported by
a binary distribution.
License
The Zope License is included in "doc/LICENSE.txt" Send your feedback
about the license to zope-license@zope.org.
Installing Zope
Follow the instructions in "doc/INSTALL.txt" to install Zope.
#!/bin/sh
# Zope configure script
# $Id$
#####################################################################
# BEGIN EDITABLE PARAMETERS #
#####################################################################
# Place the Zope major version number below.
ZOPE_VERS=2.8
# Place the optimal target version number for Zope (as returned by sys.version)
# below
TARGET="2.3.5"
# Order a list of "acceptable" python version numbers (as returned by
# sys.version) below in "best" to "worst" order, not including the
# target version. Up to six acceptable python versions are allowed.
# Do not include the target version number in this list!
ACCEPTABLE=""
# provide the executable names for all the acceptable versions
# (and the target version) below
EXENAMES="python python2 python2.3"
#####################################################################
# END EDITABLE PARAMETERS #
#####################################################################
# where are we?
HERE=`dirname $0`
# should we be quiet?
QUIET=""
usage()
{
echo
echo "configure [--help] [--quiet] [--with-python=path] [--prefix=path] "
echo " [--build-base=path] [--ignore-largefile] [--ignore-zlib]"
echo " [--optimize]"
echo
echo " Creates a Makefile suitable for building and installing Zope"
echo
echo " Options: "
echo " --help shows usage and quits"
echo " --quiet suppress nonessential output"
echo " --with-python specify a path to a Python interpreter to use"
echo " --prefix specify an installation path for binary data"
echo " --build-base specify a temporary path for build files"
echo " --ignore-largefile ignore large file support warnings"
echo " --ignore-expat ignore warnings about expat/pyexpat"
echo " --ignore-zlib ignore warnings about zlib"
echo " --optimize optimize compiled Python bytecode"
echo " --no-compile Dont compile Python bytecode"
echo
echo " Given no options, configure will search your PATH for a suitable"
echo " Python interpreter and will use '/opt/Zope-$ZOPE_VERS' as a prefix."
echo
}
# bootstrap ourselves by finding a Python interpreter if necessary
get_python() {
OLDIFS="$IFS"
IFS=":"
FOUND=""
VERSION=""
FOUNDLIST=""
out "Testing for an acceptable Python interpreter..."
out ""
for DIR in $PATH; do
IFS="$OLDIFS"
for EXECUTABLE in $EXENAMES; do
FULL="$DIR/$EXECUTABLE"
if [ -x "$FULL" -a ! -d "$FULL" ]; then
CMD="import string,sys;a=string.split(sys.version)[0]"
# Strip trailing + from version number
CMD="$CMD;a=(a[-1]=='+')and(a[:-1])or(a);print a"
VERSION=`"$FULL" -c "$CMD"`
out " Python version $VERSION found at $FULL"
if [ "$VERSION" = "$TARGET" ]; then
FOUND="$FULL"
FOUNDVERSION=$VERSION
break 2
else
i=1;
for ACC in $ACCEPTABLE; do
i=`expr $i + 1`
for SLOT in $FOUNDLIST; do
if [ $SLOT -eq $i ]; then
# slot "i" already populated. This means we've
# already found this particular version of
# python. Continue the for ACC in
# $ACCEPTABLE loop and don't overwrite the
# one we already found (interpreters first
# on the path win).
continue 2
fi
done
if [ "$VERSION" = "$ACC" ]; then
FOUNDLIST="$FOUNDLIST $i"
eval "FOUND$i=$FULL"
eval "FOUNDVERSION$i=$VERSION"
fi
done
fi
fi
done
done
if [ "$VERSION" = "$TARGET" ]; then
out ""
out " The optimum Python version ($TARGET) was found at $FOUND."
elif [ -z "$FOUND1" ] && [ -z "$FOUND2" ] && [ -z "$FOUND3" ] &&
[ -z "$FOUND4" ] && [ -z "$FOUND5" ] && [ -z "$FOUND6" ] ; then
out ""
out " No suitable Python version found. You should install"
out " Python version $TARGET before continuing."
if [ "$ACCEPTABLE" ]; then
out " Versions $ACCEPTABLE also work, but not as optimally."
fi
exit 1
else
if [ -n "$FOUND1" ]; then
FOUND=$FOUND1
FOUNDVERSION=$FOUNDVERSION1
elif [ -n "$FOUND2" ]; then
FOUND=$FOUND2
FOUNDVERSION=$FOUNDVERSION2
elif [ -n "$FOUND3" ]; then
FOUND=$FOUND3
FOUNDVERSION=$FOUNDVERSION3
elif [ -n "$FOUND4" ]; then
FOUND=$FOUND4
FOUNDVERSION=$FOUNDVERSION4
elif [ -n "$FOUND5" ]; then
FOUND=$FOUND5
FOUNDVERSION=$FOUNDVERSION5
elif [ -n "$FOUND6" ]; then
FOUND=$FOUND6
FOUNDVERSION=$FOUNDVERSION6
fi
out ""
out " !! WARNING !! "
out " An acceptable, but non-optimal Python version ($FOUNDVERSION) "
out " was found at '$FOUND'."
out " But consider installing version '$TARGET' before running "
out " 'make'. If this isn't the Python version or interpreter "
out " instance you wish to use, you may specify a Python interpreter"
out " manually by rerunning the ./configure script with the "
out " '--with-python' option."
fi
out ""
}
out() {
if [ -z "$QUIET" ]; then
echo $1
fi
}
NEWOPTS=""
for OPT in $@; do
case "$OPT" in
--h* | -h*)
usage
exit 0
;;
--with-python=*)
# pop this argument from the arglist, it is not valid to
# pass this along to the Python configurator.
shift;
FOUND=`echo $OPT | sed -e 's/--with-python\=//'`
# use eval to do tilde expansion below
eval "FOUND='$FOUND'"
out ""
out "Using Python interpreter at $FOUND"
;;
--with-python)
# in case someone passed in a --with-python without a value,
# we raise an error instead of passing it along to configure.py
# (which would raise an inscrutable error were it to receive this
# option).
out "--with-python argument requires an option"
exit 1
;;
--quiet* | -q*)
QUIET="true"
NEWOPTS="$NEWOPTS $OPT"
;;
*)
NEWOPTS="$NEWOPTS $OPT"
;;
esac
done
out ""
out "Configuring Zope installation"
out ""
if [ -z "$FOUND" ]; then
get_python
fi
# run the Python configurator
"$FOUND" "$HERE/inst/configure.py" $NEWOPTS
This diff is collapsed.
Credits
The Zope software receives contributions from far and wide. Here's
the Zope Hall of Fame:
o Stephen Purcell allows us to distribute his PyUnit unit testing
framework with Zope.
o Jeff Bauer is Zope Dude Number One. Jeff took over PCGI and
kept pushing it forward through the years.
o Sam Rushing worked with us at Digital Creations to make Medusa
the publishing platform for ZServer and the concurrency of Zope2.
o A subset of windows guru Mark Hammond's win32 extensions are
bundled with win32 binary distributions of Zope.
o Martijn Pieters and Brian Hooper contributed the #in reverse
attribute.
o Phillip Eby contributed the DTML 'let' tag and many
other useful ideas, including the inspiration for the DTML
'call', 'with' and 'return'
tags.
o The DateTime module was based on work from Ted Horst.
o Jordan Baker contributed the 'try' tag, something we've wanted
for a long, long time.
o Martijn Pieters chipped in with a safe range function.
o Michael Hauser came up with the name "Zope".
o Eric Kidd from Userland contributed to ZPublisher's support for
XML-RPC.
o Andrew M. Kuchling wrote the initial version of mod_pcgi, making
him extremely cool in our book.
o Oleg Broytmann has taken up the standard of mod_pcgi and moving
it to be a really amazing thing, and ready for prime time.
o Jephte CLAIN made some patches to European ZopeTime.
o Thanks to Gregor Hoffleit for his work in getting Zope into the
Debian distribution.
o All the other Zopistas far and wide that stuck with us during
the Bobo/Principia days and politely push us to make the best damn
app server on this or any other planet.
o Of course the list of credits would be quite incomplete without
mentioning Guido van Rossum, benevolent dictator of Python and
long-time friend of Digital Creations. Zope Power is Python
Power.
o Special thanks to Richard Stallman and the Free Software
Foundation for their assistance and feedback on the
GPL-compatible 2.0 version of the Zope Public License.
Running Zope in Debug Mode
If you wish to run Zope in debug mode, set the 'debug-mode'
configuration file parameter to 'on' (this is the default). This
will have the following effects::
o On UNIX, Zope will not detach from the controlling terminal.
o The Z_DEBUG_MODE environment variable gets set, which causes
behavioral changes to Zope appropriate for software development.
See the configuration file description of 'debug-mode' for more
information.
Using 'zopectl debug'
In Zopes 2.7 and better, a utility known as 'zopectl' is installed
into generated instance homes. You can use it to inspect a Zope
instance's running state via an interactive Python interpreter by
passing zopectl the 'debug' parameter on the command line. The
'top-level' Zope object (the root folder) will be bound to the name
'app' within the interpreter. You can then use normal Python method
calls against app and use the Python interpreter normally to inspect
results::
[chrism@james Trunk]$ bin/zopectl debug
Starting debugger (the name "app" is bound to the top-level Zope object)
>>> app.objectIds()
['acl_users', 'Control_Panel', 'temp_folder', 'browser_id_manager', 'session _data_manager', 'error_log', 'index_html', 'standard_error_message', 'standa rd_html_footer', 'standard_html_header', 'standard_template.pt']
>>>
Using the Medusa Monitor
NOTE: as of Zope 2.7, the Medusa monitor client is known to have
operational problems due to lack of maintenance. It may work, it
may not. If it doesn't work as advertised, please try 'zopectl
debug' instead.
If you're running Zope with a medusa monitor port, you can connect
and interact via a python prompt with a running Zope session, while
others interact with it over the web.
Some constraints:
o You must connect to a server running on the local host.
o You must authenticate as superuser.
o At the time of writing the superuser password (in the access
file) must be plain text, not encrypted.
To use the monitor, start it from the shell prompt like so (as if
from the Zope root):
python lib/python/ZServer/medusa/monitor_client.py localhost 8099
You'll get back a python prompt. To connect with the running Zope
session:
>>> import Zope
>>> app = Zope.app()
>>> dir(app.Control_Panel.Products)
['ExternalMethod', 'MIMETools', 'MailHost', 'OFSP', 'ZCatalog',
'ZGadflyDA', 'ZSQLMethods', '_objects']
>>>
To see ODB changes caused by access concurrent with your debug
session (eg, those web connections that can still be happening, or
other debug sessions!), you may have to do a bit of magic:
>>> app._p_jar.sync()
To commit changes you make, before exiting, you may have to do
another bit of magic:
>>> get_transaction().commit()
This diff is collapsed.
Zope Help System
The Zope Help System provides context-sensitive on-line help for
Zope users. The system is flexible and can provide help for
Python and ZClass-based Zope Products.
In the future the Help System will be expanded to provide additional
help including API documentation.
Using the Help System
Every standard Zope management screen should include a help button
which provides access to help for that screen.
Additionally all the installed help topics can be browsed and
searched.
Architecture
The Help System is based around Zope Products. When a product is
installed, its help objects are installed along with it. All help
content is associated with a product.
Help content is provided by 'Help Topic' objects. These objects live
inside Product folders within a special container object called a
'Product Help' object. When you browse a Product folder in the
Control Panel you will see these 'Product Help' objects and their
'Help Topics'.
In general you get access to the Help System through the help system
object which has methods for drawing help buttons. This object lives
in the Zope application object and has an id of 'HelpSys'.
Writing Help for ZClasses
Suppose you've created an addable type of object with ZClasses.
You'd like the management screens of your objects to have help
buttons just like the standard Zope management screens.
First create some Help Topics though the web which document your
management screens. Do this by going to your ZClass's Product and
creating new Help Topics inside the Product Help object.
Next go to your ZClass and click on the 'Views' management tab. On
this screen you define your object's management views. Each view has
a name, a method, and optionally a help topic. If you select a help
topic for a view, a help button will be drawn on that management
view and it will be linked to the help topic you select.
Writing Help for Python Products
To support help your Python product needs to register help topics
during product registration, and it needs to indicate which help
topics should be associated with which management screens.
Registering Help Topics
To register help topics use the 'registerHelp' method on the
ProductContext object. For example::
def initialize(context):
...
context.registerHelp()
This method will create help topics for all files found in the
'help' subdirectory of the product. Supported file types include:
.html, .htm, .txt, .stx, .dtml, .gif, .jpg, .png. Appropriate
classes of help topics are used depending on the suffix of the
help files.
If you want more control over how your help topics are created you
can use the 'registerHelpTopic' method that takes an id and a help
topic object as arguments. For example::
from mySpecialHelpTopics import MyTopic
def initialize(context):
...
context.registerHelpTopic('myTopic', MyTopic())
Associating Help Topics with Management Screens
The chief way to bind a help topic to a management screen is to
include information about the help topic in the class's
'manage_options' structure. For example::
manage_options=(
{'label':'Edit',
'action':'editMethod',
'help':('productId','topicId')},
)
In this example, 'productId' refers to the name of the Zope
Product in which the class is defined, and 'topicId' refers to the
id of the Help Topic associated with this management view.
When Zope draws the management view it will automatically include
a help button pointing to the right help topic if you provide this
information in the 'manage_options' structure.
Note: sometimes Zope gets confused and defaults to highlighting
the first management tab in place of the correct one. To fix this,
set the 'management_view' variable to the name of the correct
view. If the wrong view is hilighted, then the wrong help button
will be drawn.
To draw a help button on a management screen that is not a view,
use the 'HelpButton' method of the 'HelpSys' object like so::
<dtml-var "HelpSys.HelpButton('productId', 'topicId')">
This will draw a help button linked to the specified help topic.
If you prefer to draw your own help button you can use the helpURL
method instead like so::
<dtml-var "HelpSys.helpURL(
topic='productId',
product='topicId')">
This will give you a URL to the help topic. You can choose to draw
whatever sort of button or link you wish.
\ No newline at end of file
This diff is collapsed.
Building and installing Zope from source
----------------------------------------
Welcome to Zope! This document describes building and installing
Zope on UNIX and Linux.
System requirements when building from source
bash or another Bourne shell variant
Python 2.3.5 or later installed somewhere in the system PATH
(Note: Python 2.4 is officially *not* supported because there has not
been a security audit for Python 2.4 and Zope 2 so far. If you use
Python 2.4 with Zope then you use it at your own risk from the security
perspective).
GNU make
A C compiler (gcc recommended)
Recommendations
- You are recommended to build and install Zope as a non-root user.
Building Zope
To build Zope, run the conventional UNIX build sequence from within
the Zope source tree::
./configure --prefix=/where/to/install/zope
make
If you do not specify a '--prefix' option, during a later step, Zope
will be installed into a default location.
If the configure script cannot find a suitable Python interpreter
for use with Zope, it will complain with an informative error
message. If this happens, you may use the '--with-python'
command-line option to 'configure' to specify which Python
interpreter to use. Run './configure --help' to see other
command-line options available via the configure script.
Optional: Building and Installing Zope 'In-Place'
Older versions of Zope were typically run directly from the
'source' directory itself. This is useful for development
purposes. You can regain that behavior by performing the
following sequence of commands within a Zope source directory:
./configure
make instance
This command also creates an administrative user with the
specified username and password.
WARNING: "make instance" doesn't work on FreeBSD 5.0 and
presumably other platforms. You should either use GNU make
(gmake instance), or use "make install" instead.
Making an "In-Place" instance builds the binary files and creates
the files necessary for a Zope instance to be run directly
within the build directory (e.g. 'in-place'). You may start
Zope by running::
./bin/runzope
Optional: Building Zope Using The "Clean-Source-Tree" Method
You can run the 'configure' command from outside of the Zope
source tree. If you do so, the makefile will be written to your
current directory. The files generated by the build process (via
'make') will be written to the directory from which you run
'configure'. You can then use 'make install' to install these
files to their canonical locations. This feature is to support
source locations which are not writable.
Installing Zope
To install Zope to the place you've specified via the '--prefix'
option (or to the default location if you didn't specify a prefix),
type::
make install
Creating a Zope Instance Home
Once you've performed the install step, to begin actually using
Zope, you will need to create an "instance home", which is a
directory that contains configuration and data for a Zope server
process. The instance home is created using the 'mkzopeinstance.py'
script::
/where/to/install/zope/bin/mkzopeinstance.py
You will be asked to provide a user name and password for an
administrator's account during 'mkzopeinstance'. Command-line
options to 'mkzopeinstance' are available, and can be investigated
by running 'mkzopeinstance.py --help'.
Starting Zope
Once an instance home has been created, the Zope server can now be
started using this command:
/location/of/zope/instance/bin/runzope
If you get errors indicating that addresses are in use, then you
will have to supply arguments to runzope to change the ports used
for HTTP or FTP. The default HTTP and FTP ports used by Zope are
8080 and 8021 respectively. You can change the ports used by
specifying the "port-base" parameter to runzope. For example, to
run Zope on ports 9080 and 9021::
./bin/runzope -X port-base=1000
There is also an interactive Zope command shell named 'zopectl' that
may be invoked by running 'bin/zopectl'. By default, 'zopectl
start' will start a background process that manages Zope and
'zopectl stop' will stop the background process.
Logging In To Zope
Once you've started Zope, you can then connect to the Zope webserver
by directing your browser to::
http://yourhost:8080/manage
.. where 'yourhost' is the DNS name or IP address of the machine
running Zope. If you changed the HTTP port as described, use a port
number of 8080 + the port-base value.
You will be prompted for a user name and password. Use the user name
and password you provided in response to the prompts issued during
the "make instance" process.
Now you're off and running! You should be looking at the Zope
management screen which is divided into two frames. On the left you
can navigate between Zope objects and on the right you can edit them
by selecting different management functions with the tabs at the top
of the frame.
If you haven't used Zope before, you should head to the Zope web
site and read some documentation. The Zope Book is a good place to
start. You can access the Zope Book at:
http://www.zope.org/Documentation/Books/ZopeBook
Integrating Zope With An Existing Webserver
Zope doesn't require any existing webserver to run, but you can
integrate it with other webservers as necessary. See the
WEBSERVERS.txt file for more information about configuring Zope with
an existing web server. There is also information about integrating
Zope with existing webservers on the Zope.org website.
Troubleshooting
- This version of Zope requires Python 2.3.4 or better.
- The Python you run Zope with *must* have threads compiled in,
which is the case for a vanilla build. Warning: Zope will not run
with a Python version that uses libpth. You *must* use
libpthread.
- To build Python extensions you need to have Python configuration
information available. If your Python comes from an RPM you may
need the python-devel (or python-dev) package installed too. If
you built Python from source all the configuration information
should already be available.
- See CHANGES.txt for important notes on this version of Zope.
Zope Public License (ZPL) Version 2.0
-----------------------------------------------
This software is Copyright (c) Zope Corporation (tm) and
Contributors. All rights reserved.
This license has been certified as open source. It has also
been designated as GPL compatible by the Free Software
Foundation (FSF).
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the
following conditions are met:
1. Redistributions in source code must retain the above
copyright notice, this list of conditions, and the following
disclaimer.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions, and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
3. The name Zope Corporation (tm) must not be used to
endorse or promote products derived from this software
without prior written permission from Zope Corporation.
4. The right to distribute this software or to use it for
any purpose does not give you the right to use Servicemarks
(sm) or Trademarks (tm) of Zope Corporation. Use of them is
covered in a separate agreement (see
http://www.zope.com/Marks).
5. If any files are modified, you must cause the modified
files to carry prominent notices stating that you changed
the files and the date of any change.
Disclaimer
THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION ``AS IS''
AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
NO EVENT SHALL ZOPE CORPORATION OR ITS CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
This software consists of contributions made by Zope
Corporation and many individuals on behalf of Zope
Corporation. Specific attributions are listed in the
accompanying credits file.
from Thiebaut Champenier
"python wo_pcgi.py" doesn't build the extensions correctly due to
linking problems. To fix it, I just SymLinked ld_so_aix, makexp_aix
and python.exp (found in <python-build>/Modules/) into each of the
dirs in <Zope-build>/python/lib that contain .c files... and it
worked just great. But I'm sure it's not the best solution
from Wolfgang Roesner (reporting segfaults and a fix)
The default stack size for a pthread on AIX (at least in my
configuration) is 96kB. On Solaris it's 1M. As soon as I explicitly
set the initial stack size to 1M the problem went away and it
appears as if Zope is up and running.
Unfortunately I really had to change "thread_pthread.h" in Python
which obviously is not a nice thing to do as I now have a need for
my own private version of Python. But at least I have a running
version of Zope on AIX.
Steps to building python for use with Zope on FreeBSD to avoid
segmentation faults (by Andrew Sawyers for Pythons up to 2.3).
The default thread stack size for FreeBSD is too small; when building
Python for use with Zope it is often necessary to increase the thread
stack size to avoid segmentation faults. There are two ways which you
can increase the default python thread stack size.
Way #1: In your Python source directory:
* >cd Python
* >edit thread_pthreads.h
at the top of the header file, add #define THREAD_STACK_SIZE 1000000
* >save
* >cd ..; ./configure
Way #2: In your Python source directory:
* >./configure
* >make OPT="-DNDEBUG -g -O3 -Wall -Wstrict-prototypes -DTHREAD_STACK_SIZE=1000000"
Passing in the define or adding the define to your source solves all
experiences I have experienced with Zope segfaulting while running on
my FreeBSD platforms.
This directory is to collect notes that people send us on using Zope
on Platforms that we (Digital Creations) don't have and can't support
directly.
Integration of reStructuredText (reST) in Zope:
Zope 2.7 or higher integrates reST as part of the Python
docutils package. The syntax of reST is defined under
http://docutils.sf.net/spec/rst/introduction.html
Usage inside DTML:
<dtml-var rest-document fmt="restructured-text">
Usage inside ZPT:
<span tal:content="structure python: modules['Products.PythonScripts.standard'].restructured_test(rest_txt)" />
Usage inside PythonScripts:
from Products.PythonScripts.standard import restructured_text
rendered_html = restructured_test(rest_txt)
return rendered_html
Usage inside Zope products:
from reStructuredText import HTML
rendered_html = HTML(rest_txt)
...
Character set issues:
reST processes the reST document internally using unicode. A reST
document is converted using Pythons default encoding to unicode and
converted back from unicode to the default encoding on the output side.
This means you must ensure that Python default encoding is properly.
You can customize the default encoding by creating a file sitecustomize.py
somewhere in yout PYTHONPATH:
import sys
sys.setdefaultencoding("iso-8859-1")
This version of Zope also includes the ZReST product written by Richard Jones.
ZRest is a standalone Zope product to handle reStructuredText documents.
Setting the initial user name and password
Because Zope is managed through the web, user names and passwords must be
used to assure that only authorized people can make changes to a Zope
installation.
Some user name and password is needed to "bootstrap" the creation of
normal managers of your Zope site. This is accomplished through the
use of the file 'inituser'. The first time Zope starts, it will detect
that no users have been defined in the root user folder. It will search
for the 'inituser' file and, if it exists, will add the user defined
in the file to the root user folder.
Normally, 'inituser' is created by the Zope install scripts. Either
the installer prompts for the password or a randomly generated
password is created and displayed at the end of the build script.
You can use the 'zpasswd.py' script to create 'inituser' yourself.
Execute 'zpasswd.py' like this::
python zpasswd.py inituser
The script will prompt you for the name, password, and allowed
domains. The default is to encode the password with SHA, so please
remember this password as there is no way to recover it (although
'zpasswd.py' lets you reset it.)
In some situations you may need to bypass normal security controls
because you have lost your password or because the security settings
have been mixed up. Zope provides a facility called an "emergency
user" so that you can reset passwords and correct security
settings.
The emergency user password must be defined outside the application
user interface. It is defined in the 'access' file located
in the Zope directory. It should be readable only by the user
as which your web server runs.
To create the emergency user, use 'zpasswd.py' to create the
'access' file like this::
python zpasswd.py access
In order to provide a somewhat higher level of security, various
encoding schemes are supported which provide access to either SHA-1
encryption or the standard UNIX crypt facility if it has been compiled
into Python. Unless you have some special requirements (see below),
you should use the SHA-1 facility, which is the default.
Format of 'inituser' and 'access'
A password file should consist of a single line of the form:
name:password
Note that you may also add an optional third component to the line
in the access file to restrict access by domain.
For example, the line:
mario:nintendoRules:*.mydomain.com
in your 'access' file will only allow permit emergency user access
from *.mydomain.com machines. Attempts to access the system from
other domains will fail, even if the correct emergency user name
and password are used.
Please note that if you use the ZServer monitor capability, you will
need to run with a clear text password.
Setting permissions on the var directory.
You need to set permissions on the Zope var directory.
Zope needs to read and write data from its var directory. Before
running Zope you should ensure that you give adequate permissions
to the Zope var directory for the userid Zope will run under.
Depending on how you choose to run Zope you will need to give
different permissions to the var directory. If you use Zope with an
existing web server, it will probably run Zope as 'nobody'. In this
case 'nobody' needs read and write permissions to the var directory.
If you change the way you run Zope you may need to modify the permissions
of the var directory and the files in it to allow Zope to read and write
under its changed userid.
Zope effective user support
Zope can bind its network service to low ports such as 21 (FTP) and
80 (HTTP). In order to bind to low ports, Zope must be started as
the root user. However, Zope will only run as root long enough to
bind to these low ports. It will then attempt to setuid to a less
privileged user.
You must specify the user to which Zope will attempt to setuid by
changing the 'effective-user' parameter in the zope.conf
configuration file to an existing username or UID. All runtime
files will be written as this user. If you do not specify an
'effective-user' in the configuration file, and you attempt to start
Zope, it will refuse to start.
Zope additionally emits a warning if you specify 'nobody' as the
'effective-user'. The rationale for this warning stems from the
fact that, historically, many other UNIX services dropped privileges
to the 'nobody' account after starting as root. Any security
defects in these services could cause someone to gain access as the
'nobody' account on your system. If someone was to gain control of
your 'nobody' account they could compromise your Zope files.
The most important thing to remember about effective user support is
that you don't have to start Zope as root unless you want to listen
for requests on low ports (ports beneath 1024). In fact, if you
don't have this need, you are much better off just starting Zope
under a dedicated user account.
Signals (POSIX only)
Signals are a POSIX inter-process communications mechanism.
If you are using Windows then this documentation does not apply.
Zope responds to signals which are sent to the process id
specified in the file '$INSTANCE_HOME/var/Z2.pid'::
SIGHUP - close open database connections, then restart the server
process. A idiom for restarting a Zope server is:
kill -HUP `cat $INSTANCE_HOME/var/z2.pid`
SIGTERM - close open database connections then shut down. A common
idiom for shutting down Zope is:
kill -TERM `cat $INSTANCE_HOME/var/Z2.pid`
SIGINT - same as SIGTERM
SIGUSR2 - close and re-open all Zope log files (z2.log, event log,
detailed log.) A common idiom after rotating Zope log files
is:
kill -USR2 `cat $INSTANCE_HOME/var/z2.pid`
The following is a TODO list dealing with installation and startup
changes proposed for the Zope trunk. It is organized into these
categories:
"BEFORE 2.7 FINAL" (things that need to be done before we're
finished, but not currently on the critical path)
"MAYBE NEVER BUT NICE TO HAVE" (items that are not on any critical
path)
"COMMUNITY CONCERNS" (uncategorized concerns dealing with features
missing from HEAD and prior 2.7 releases)
----------------------------------
BEFORE RELEASE OF ZOPE 2.7 FINAL
----------------------------------
Provide more intuitive command line option overrides
Currently, you can override config file options by using the
-X command line switch to runzope.py, followed by key/value
pairs separated by an equal sign.
This isn't explained anywhere and I'm not sure what limitations
it has (can you change any value in the config file this way?).
Zope-specific command-line options that don't require the -X
(e.g. --event-log-file=something) should be provided to the
user via changes to Zope/Startup/options/ZopeOptions.
Make 'runzope -h' work and give better error messages when a configuration
fails to make the grade.
Currently runzope -h just emits a traceback, and the message printed by
run.py says to run 'run.py -h' which can't work because things that
it needs to import aren't on the PYTHONPATH.
Config file needs better inline docs
The Zope ZConfig config file has some inline docs. They need to be
completed. Additional docs may come as a result of writing a schema-to-HTML
translator.
Make as many things defaultable as feasible
Maybe we can allow a config file in which everything is commented
out. We'll see.
Write some more unit tests
Write unit tests for Zope.Startup packages.
What to do about envvars?
Envvars are still used "under the hood" in ZConfig handlers as the
result of particular configuration declarations in order to make
Zope do the right thing (e.g. INSTANCE_HOME, SOFTWARE_HOME,
DTML_REQUEST_AUTOQUOTE, SESSION_TIMEOUT_MINUTES and other envvars
are set in ZConfig handlers for their respective keys). But envvars
should not be used to try to configure Zope, as the handlers
overwrite existing envvars with prejudice. We need to come down on
one side or the other about envvars.. either they should be
respected at startup as they always have been or they should be
explicitly not respected. Currently they are not respected.
We need to communicate this decision to developers and update
doc/ENVIRONMENT.txt as necessary.
win32-eventlog needs testing
The "win32-eventlog" log handler (which is creatable via the config
file) needs to be tested.
Note: As of Jul 6, 2003, it has been confirmed to not work. The
service name that it is logging under ("Zope") is not registered
with the NT event service properly, causing a warning to be
written to the log every time a log call is made.
Server construction errors need to be better
When a server is constructed, it attempts to bind to a port. If the
port cannot be bound, an error is raised. The error currently doesn't
include the port number, and should.
I propose that we add two more options to the config file:
Create import-directory and extensions-directory directives
These would both be multikeys which specify some number of
directories that contained importable zexp files and external
methods, respectively. This would allow us to not require any fixed
instance home directory. Instead, each path required by each
subsystem is specifiable by itself in the config file.
I'm sure that utilizing these options in the config file will break
things that rely on having a monolithic INSTANCE_HOME such as
products that attempt to do something like "import_dir =
os.path.join(INSTANCE_HOME, 'import').
So I propose that the stock Zope instance home install continue to
follow the old pattern (where everything is installed into a single
instance home directory), but we provide the advanced config file
options for roll-your-own packagers and advanced users.
I would like to do the same thing for the software home, but I
haven't thought much about it yet.
Review the Zope Book 2.6 Edition chapters and come up with revisions
or at least create a Zope 2.7 Install HowTo
The 2.6 edition Zope Book at
http://www.zope.org/Documentation/Books/ZopeBook/2_6Edition has
three chapters which detail installation (Installing and Starting
Zope), maintenance (Maintaining Zope) and ZEO (Scalability and ZEO).
These chapters should be reviewed for inaccuracies with respect to
the forthcoming trunk changes and changes should be made "offline"
to allow a Zope Book 2.7 edition.
At least create a HowTo which summarizes the differences between
installing 2.6 and installing 2.7.
------------------------------
MAYBE NEVER BUT NICE TO HAVE
------------------------------
ZConfig defaults
We deferred several issues that we recognized as areas for
improvement in ZConfig that might make it possible to avoid writing
nasty procedural code for default handling unnecessary
(e.g. Zope.Startup.handlers.root_handler). See
http://my.zope.com/CPM/CPM/issues/4 and
http://my.zope.com/CPM/CPM/issues/3. Not necessary for merge, but
useful to think about for future.
ZConfig should keep enough state to be able to reconstitute the
textual representation of the configuration.
It would be nice if ZConfig kept enough state to be able to
reconstitute the configuration in textual representation
(to aid GUI builders and to make it possible to have
a meaningful 'zopectl showconfig' or somesuch).
----------------------------------
COMMUNITY CONCERNS (uncategorized)
----------------------------------
Status: Request for comment sent to the community:
http://mail.zope.org/pipermail/zope-dev/2003-March/018999.html
Lots of discussion!
- ZConfig is too complex when compared with envvar parsing.
- Somewhat unrelated, but the features of "debug mode" should be
disentangled from one another and specifiable individually.
- Need to support arbitrary storage types.
- Provide --swhome and ---with-python switch to mkzopeinstance, which will
allow folks to create an instance that points to particular (known)
Python versions and software homes.
XXX This doesn't make sense. mkzopeinstance is part of the
software home; if you want to use a different one, use the
mkzopeinstance from the software home you want to use. The same
goes for the Python to use; that's "built-in" to a software home.
Using a different Python doesn't make sense given that the software
home includes compiled modules.
AAA This is to service to-be-chrooted installs.
- Explain how to set up and use a ZEO server using mkzeoinst
included in 2.7's ZEO.
(Use the installed bin/mkzeoinstance script; use --help for more
information.)
Richard Jones has done work to allow you to create a ZEO instance
in a way similar to creating a Zope instance (yay!) after
creating a software home.
- Give ZConfig replacement access to the environment or shell
somehow. For instance, some folks use the same 'start' script in
all of their instances right now (under 2.6). The script does
things based on the value of an envvar that can be used to
distinguish the config values of the instance from other instances.
We could allow for the same sort of behavior E.g.:
%define HOSTNAME `hostname` (assuming `hostname` resolves to a hostname)
lockfile-name /var/lock/$HOSTNAME-lockfile
- Give installaler an option to put libs in a user-specifiable
directory at software home installation time.
- Give installer an option to put docs in a user-specifiable directory
at software home installation time.
- Make it possible to install Zope-related Python libraries to
The site-packages of the Python used to invoke setup.py.
- Offer to install software home 'bin' scripts into a directory
separate from the software home 'bin' directory.
- Allow for the installation of platform-dependent files (basically
Python extensions) to be installed to a place separate than that of
platform independent files (as requested by Luca DeVitis).
- Upon failure of Windows service startup, it's possible for the
reason for the failure to not be logged anywhere. This is because
we carefully wait til late in the startup process to write logfiles
so UNIX has a chance to setuid. This is unnecessary for Windows.
This diff is collapsed.
This diff is collapsed.
ZEO Documentation
=================
This directory contains ZEO documentation.
howto.txt
The first place to look.
It provides a high-level overview of ZEO features and details on
how to install and configure clients and servers. Includes a
configuration reference.
cache.txt
Explains how the client cache works.
trace.txt
Describe cache trace tool used to determine ideal cache size.
ZopeREADME.txt
A somewhat dated description of how to integrate ZEO with Zope.
It provides a few hints that are not yet in howto.txt.
Zope Enterprise Objects (ZEO)
Installation
ZEO 2.0 requires Zope 2.4 or higher and Python 2.1 or higher.
If you use Python 2.1, we recommend the latest minor release
(2.1.3 as of this writing) because it includes a few bug fixes
that affect ZEO.
Put the package (the ZEO directory, without any wrapping directory
included in a distribution) in your Zope lib/python.
The setup.py script in the top-level ZEO directory can also be
used. Run "python setup.py install --home=ZOPE" where ZOPE is the
top-level Zope directory.
You can test ZEO before installing it with the test script::
python test.py -v
Run the script with the -h option for a full list of options. The
ZEO 2.0b2 release contains 122 unit tests on Unix.
Starting (and configuring) the ZEO Server
To start the storage server, go to your Zope install directory and
run::
python lib/python/ZEO/start.py -p port_number
This run the storage sever under zdaemon. zdaemon automatically
restarts programs that exit unexpectedly.
The server and the client don't have to be on the same machine.
If they are on the same machine, then you can use a Unix domain
socket::
python lib/python/ZEO/start.py -U filename
The start script provides a number of options not documented here.
See doc/start.txt for more information.
Running Zope as a ZEO client
To get Zope to use the server, create a custom_zodb module,
custom_zodb.py, in your Zope install directory, so that Zope uses a
ClientStorage::
from ZEO.ClientStorage import ClientStorage
Storage = ClientStorage(('', port_number))
You can specify a host name (rather than '') if you want. The port
number is, of course, the port number used to start the storage
server.
You can also give the name of a Unix domain socket file::
from ZEO.ClientStorage import ClientStorage
Storage = ClientStorage(filename)
There are a number of configuration options available for the
ClientStorage. See doc/ClientStorage.txt for details.
If you want a persistent client cache which retains cache contents
across ClientStorage restarts, you need to define the environment
variable, ZEO_CLIENT, or set the client keyword argument to the
constructor to a unique name for the client. This is needed so
that unique cache name files can be computed. Otherwise, the
client cache is stored in temporary files which are removed when
the ClientStorage shuts down. For example, to start two Zope
processes with unique caches, use something like::
python z2.py -P8700 ZEO_CLIENT=8700
python z2.py -P8800 ZEO_CLIENT=8800
Zope product installation
Normally, Zope updates the Zope database during startup to reflect
product changes or new products found. It makes no sense for
multiple ZEO clients to do the same installation. Further, if
different clients have different software installed, the correct
state of the database is ambiguous.
Zope will not modify the Zope database during product installation
if the environment variable ZEO_CLIENT is set.
Normally, Zope ZEO clients should be run with ZEO_CLIENT set so
that product initialization is not performed.
If you do install new Zope products, then you need to take a
special step to cause the new products to be properly registered
in the database. The easiest way to do this is to start Zope
once with the environment variable FORCE_PRODUCT_LOAD set.
The interaction between ZEO and Zope product installation is
unfortunate.
ZEO Client Cache
The Client cache provides a disk based cache for each ZEO client.
The client cache allows reads to be done from local disk rather than
by remote access to the storage server.
The cache may be persistent or transient. If the cache is
persistent, then the cache files are retained for use after process
restarts. A non-persistent cache uses temporary files that are
removed when the client storage is closed.
The client cache is managed as two files. The cache manager
endeavors to maintain the two files at sizes less than or equal to
one half the cache size. One of the cache files is designated the
"current" cache file. The other cache file is designated the "old"
cache file, if it exists. All writes are done to the current cache
files. When transactions are committed on the client, transactions
are not split between cache files. Large transactions may cause
cache files to be larger than one half the target cache size.
The life of the cache is as follows:
- When the cache is created, the first of the two cache files is
created and designated the "current" cache file.
- Cache records are written to the cache file, either as
transactions commit locally, or as data are loaded from the
server.
- When the cache file size exceeds one half the cache size, the
second cache file is created and designated the "current" cache
file. The first cache file becomes the "old" cache file.
- Cache records are written to the new current cache file, either as
transactions commit locally, or as data are loaded from the
server.
- When a cache hit is found in the old cache file, it is copied to
the current cache file.
- When the current cache file size exceeds one half the cache size, the
first cache file is recreated and designated the "current" cache
file. The second cache file becomes the "old" cache file.
and so on.
Persistent cache files are created in the directory named in the
'var' argument to the ClientStorage (see ClientStorage.txt) or in
the 'var' subdirectory of the directory given by the INSTANCE_HOME
builtin (created by Zope), or in the current working directory.
Persistent cache files have names of the form::
cstorage-client-n.zec
where:
storage -- the storage name
client -- the client name, as given by the 'ZEO_CLIENT' environment
variable or the 'client' argument provided when creating a client
storage.
n -- '0' for the first cache file and '1' for the second.
For example, the second cache file for storage 'spam' and client 8881
would be named 'cspam-8881-1.zec'.
This diff is collapsed.
ZEO Client Cache Tracing
========================
An important question for ZEO users is: how large should the ZEO
client cache be? ZEO 2 (as of ZEO 2.0b2) has a new feature that lets
you collect a trace of cache activity and tools to analyze this trace,
enabling you to make an informed decision about the cache size.
Don't confuse the ZEO client cache with the Zope object cache. The
ZEO client cache is only used when an object is not in the Zope object
cache; the ZEO client cache avoids roundtrips to the ZEO server.
Enabling Cache Tracing
----------------------
To enable cache tracing, set the environment variable ZEO_CACHE_TRACE
to the name of a file to which the ZEO client process can write. ZEO
will append a hyphen and the storage name to the filename, to
distinguish different storages. If the file doesn't exist, the ZEO
will try to create it. If there are problems with the file, a log
message is written to the standard Zope log file. To start or stop
tracing, the ZEO client process (typically a Zope application server)
must be restarted.
The trace file can grow pretty quickly; on a moderately loaded server,
we observed it growing by 5 MB per hour. The file consists of binary
records, each 24 bytes long; a detailed description of the record
lay-out is given in stats.py. No sensitive data is logged.
Analyzing a Cache Trace
-----------------------
The stats.py command-line tool is the first-line tool to analyze a
cache trace. Its default output consists of two parts: a one-line
summary of essential statistics for each segment of 15 minutes,
interspersed with lines indicating client restarts and "cache flip
events" (more about those later), followed by a more detailed summary
of overall statistics.
The most important statistic is probably the "hit rate", a percentage
indicating how many requests to load an object could be satisfied from
the cache. Hit rates around 70% are good. 90% is probably close to
the theoretical maximum. If you see a hit rate under 60% you can
probably improve the cache performance (and hence your Zope
application server's performance) by increasing the ZEO cache size.
This is normally configured using the cache_size keyword argument to
the ClientStorage() constructor in your custom_zodb.py file. The
default cache size is 20 MB.
The stats.py tool shows its command line syntax when invoked without
arguments. The tracefile argument can be a gzipped file if it has a
.gz extension. It will read from stdin (assuming uncompressed data)
if the tracefile argument is '-'.
Simulating Different Cache Sizes
--------------------------------
Based on a cache trace file, you can make a prediction of how well the
cache might do with a different cache size. The simul.py tool runs an
accurate simulation of the ZEO client cache implementation based upon
the events read from a trace file. A new simulation is started each
time the trace file records a client restart event; if a trace file
contains more than one restart event, a separate line is printed for
each simulation, and line with overall statistics is added at the end.
Example, assuming the trace file is in /tmp/cachetrace.log::
$ python simul.py -s 100 /tmp/cachetrace.log
START TIME DURATION LOADS HITS INVALS WRITES FLIPS HITRATE
Sep 4 11:59 38:01 59833 40473 257 20 2 67.6%
$
This shows that with a 100 MB cache size, the cache hit rate is
67.6%. So let's try this again with a 200 MB cache size::
$ python simul.py -s 200 /tmp/cachetrace.log
START TIME DURATION LOADS HITS INVALS WRITES FLIPS HITRATE
Sep 4 11:59 38:01 59833 40921 258 20 1 68.4%
$
This showed hardly any improvement. So let's try a 300 MB cache
size::
$ python2.0 simul.py -s 300 /tmp/cachetrace.log
ZEOCacheSimulation, cache size 300,000,000 bytes
START TIME DURATION LOADS HITS INVALS WRITES FLIPS HITRATE
Sep 4 11:59 38:01 59833 40921 258 20 0 68.4%
$
This shows that for this particular trace file, the maximum attainable
hit rate is 68.4%. This is probably caused by the fact that nearly a
third of the objects mentioned in the trace were loaded only once --
the cache only helps if an object is loaded more than once.
The simul.py tool also supports simulating different cache
strategies. Since none of these are implemented, these are not
further documented here.
Cache Flips
-----------
The cache uses two files, which are managed as follows:
- Data are written to file 0 until file 0 exceeds limit/2 in size.
- Data are written to file 1 until file 1 exceeds limit/2 in size.
- File 0 is truncated to size 0 (or deleted and recreated).
- Data are written to file 0 until file 0 exceeds limit/2 in size.
- File 1 is truncated to size 0 (or deleted and recreated).
- Data are written to file 1 until file 1 exceeds limit/2 in size.
and so on.
A switch from file 0 to file 1 is called a "cache flip". At all cache
flips except the first, half of the cache contents is wiped out. This
affects cache performance. How badly this impact is can be seen from
the per-15-minutes summaries printed by stats.py. The -i option lets
you choose a smaller summary interval which shows the impact more
acutely.
The simul.py tool shows the number of cache flips in the FLIPS column.
If you see more than one flip per hour the cache may be too small.
The Zope Object Database, ZODB, version 3.0
ZODB is the next generation of our object database architecture.
It provides a number of advantages over or existing databases:
- Support for concurrency,
- Well-defined storage management interface that will allow many
different storage management strategies to be used, from file
storage, to RDBMS storage, to memory storage,
- More robust file storage format,
- Much better version support and integrations of
versions with the transaction system. For example,
it is possible to commit from one version to another,
to undo version commit and abort, and to use "temporary
versions" to reduce memory use when manipulating large
quantities of data.
- Support for multiple databases in the same object system.
- Support for multi-process storage managers, although the
standard distribution will not include any multiple process
storage managers.
Note: Using ZODB 3 from Python
In ZODB 2, you could access the top-level application object
with::
import Main
app=Main.app
You could then navigate from the top-level object by getting
attributes, calling methods, etc..
In ZODB 3, you get the top-level object like this::
import Zope
app=Zope.app()
... do stuff with objects
# and when you're done:
app._p_jar.close()
You have to import the Zope application module, which uses
ZODB 3 rather than ZODB 2. In ZODB 3, you have to get a
connection to a database before you access an object. The
'app()' function combines opening a connection and getting the
top level object. When your done using the top-level object
(or any objects accessible from it) you can close the database
connection by calling the 'close' method on the '_p_jar'
attribute, which is the database connection. You don't need
to close the connection if you are going to exit Python
without doing any more work.
Don't forget to::
get_transaction().commit()
If you want any changes to made to be saved.
Note: Converting ZODB 2 (aka BoboPOS) data files to ZODB 3.
The bbb.py script in utilities can be used to convert data
files from ZODB 2 to ZODB 3 format::
utilities/bbb.py -f output_file input_file
Here's a example::
utilities/bbb.py -f var/Data.fs var/Data.bbb
You can also convert export files from ZODB 2 by inclding the
-x option::
utilities/bbb.py -x -f output.zexp input.bbe
ZODB 3 and Zope Database Adapters
Most database adapters are *currently* likely to be problematic unless
the underlying extensions and libraries:
- allow multiple simultaneous database connections,
- are thread safe,
- give up the Python global interpreter lock when
making database calls.
This is only a problem when running Zope 2 with multiple
threads.
ZODB 3 Futures
These are features that are lkely to wait for releases after 2.0.
- Multiple database support
- OPTIMIZATION: FileStorage will get a more efficient data
structure for maintaining index information and key methods
in the ZODB framework will move to C.
This directory is used by the running Zope process to import objects
into the ZODB. Please place any files that you wish to be able to
import into this directy. For more information, please see The Zope
Book.
# Zope2 build and install Makefile.
# We do as much as possible in Python in order to avoid needing to
# learn autoconf or some other awful thing. ;-)
NAME=Zope
MAJOR_VERSION=<<ZOPE_MAJOR_VERSION>>
MINOR_VERSION=<<ZOPE_MINOR_VERSION>>
RELEASE_TAG=<<VERSION_RELEASE_TAG>>
PACKAGE_NAME=${NAME}-${MAJOR_VERSION}.${MINOR_VERSION}-${RELEASE_TAG}
PYTHON="<<PYTHON>>"
TMPDIR=/tmp
PREFIX=<<PREFIX>>
BASE_DIR=<<BASE_DIR>>
BUILD_BASE=<<BUILD_BASE>>
DISTUTILS_OPTS=<<DISTUTILS_OPTS>>
INSTALL_FLAGS=<<INSTALL_FLAGS>>
TESTOPTS=-v
BUILD_FLAGS=--build-base="${BUILD_BASE}" \
--build-lib="${BUILD_BASE}/build-lib" \
--build-scripts="${BUILD_BASE}/build-scripts"\
--build-temp="${BUILD_BASE}/build-temp"
RM=rm -f
RMRF=rm -rf
FIND=find
XARGS=xargs
CD=cd
LN=ln -sfn
CP=cp
TAR=tar
MKDIR=mkdir -p
.PHONY : clean install instance untestinst testinst build unbuild
.PHONY : default
# default: The default step (invoked when make is called without a target)
default: build
@echo
@echo Zope built. Next, do \'make install\' \(or \'make instance\'
@echo to run a Zope instance directly from the build directory\).
@echo
# build: Do whatever 'setup.py build' implies
build:
${PYTHON} "${BASE_DIR}/setup.py" \
${DISTUTILS_OPTS} build ${BUILD_FLAGS}
# unbuild: Remove the build directory (undo the make build step)
unbuild:
${RMRF} ${BUILD_BASE}
${BASE_DIR}/lib/python/version.txt:
printf "Zope ${MAJOR_VERSION}.${MINOR_VERSION}-${RELEASE_TAG}" >\
"${BASE_DIR}/lib/python/version.txt"
# install: Install a software home.
install: build ${BASE_DIR}/lib/python/version.txt
${PYTHON} "${BASE_DIR}/setup.py" ${DISTUTILS_OPTS} install \
--home="${PREFIX}" ${BUILD_FLAGS} ${INSTALL_FLAGS}
[ -f ${PREFIX}/bin/python ] || ${LN} ${PYTHON} ${PREFIX}/bin/python
@echo
@echo Zope binaries installed successfully.
@echo Now run \'${PREFIX}/bin/mkzopeinstance.py\'
# inplace: Install a software home into to the source directory.
#
# Note: We used to run 'build_ext -i' for 'inplace', but that was
# suboptimal because it had a tendency to try to rebuild all of the
# (possibly already-built) extensions that might be built during a
# previous 'make' step. built_ext doesn't understand '--build-base'
# and friends so we can't stop it from doing this easily. So instead,
# we rely on the stock install step and name the prefix as the current
# directory. This is a little less efficient than just building the
# extensions because it also compiles bytecode, but it's more intuitive and
# less expensive in the common case than letting distutils
# potentially rebuild the binaries when we've done that already.
inplace: PREFIX=${BASE_DIR}
inplace: install
# instance: Do an inplace build and create an instance home in the resulting
# software home.
instance: inplace
${PYTHON} "${BASE_DIR}/bin/mkzopeinstance.py" ${MKZ_FLAGS} \
--dir="${BASE_DIR}"
# uninstance: Remove the instance files made by make instance (w/ prejudice)
uninstance:
${RMRF} "${BASE_DIR}/bin"
${RMRF} "${BASE_DIR}/etc"
${RMRF} "${BASE_DIR}/log"
${RMRF} "${BASE_DIR}/var"
${RMRF} "${BASE_DIR}/Products"
# testinst: Perform an inplace build and create an instance home in the
# resulting software home without asking questions. Useful when
# performing automated testing.
testinst: MKZ_FLAGS=--user=admin:admin
testinst: instance
# test: Do an inplace build and run the Zope test suite.
test: inplace
${PYTHON} "${BASE_DIR}/test.py" ${TESTOPTS}
# clean: Delete the build files and any binaries/bytecode files in
# the source directory for good measure.
clean: unbuild
${FIND} "${BASE_DIR}" \
-name '*.py[co]' -o -name '*.so' -o -name '*.o' | ${XARGS} ${RM}
# sdist: Create a source distribution file (implies clobber).
#
sdist: clobber sdist_tgz
# sdist_tgz: Create a tgz archive file as a source distribution.
#
sdist_tgz: ${BASE_DIR}/lib/python/version.txt
${MKDIR} ${TMPDIR}
${CD} ${TMPDIR} && ${LN} ${BASE_DIR} ${PACKAGE_NAME} && \
${TAR} czfh ${BASE_DIR}/${PACKAGE_NAME}.tgz ${PACKAGE_NAME} \
--exclude=${PACKAGE_NAME}.tgz\
--exclude=CVS \
--exclude=.cvsignore \
--exclude=makefile \
--exclude=build-base \
--exclude=*~ \
--exclude=.#*
${RMRF} ${TMPDIR}/${PACKAGE_NAME}
# clobber: Make the source tree 'pristine' again.
clobber: clean uninstance
# distclean: Make the source tree *really* 'pristine' again.
distclean: clobber
${RM} makefile Makefile
${RMRF} build-base
# Zope2 build and install Makefile for win32 (nmake-style).
# We do as much as possible in Python in order to avoid needing to
# learn autoconf or some other awful thing. ;-)
NAME=Zope
MAJOR_VERSION=<<ZOPE_MAJOR_VERSION>>
MINOR_VERSION=<<ZOPE_MINOR_VERSION>>
RELEASE_TAG=<<VERSION_RELEASE_TAG>>
PACKAGE_NAME=$(NAME)-$(MAJOR_VERSION).$(MINOR_VERSION)-$(RELEASE_TAG)
PYTHON="<<PYTHON>>"
PREFIX=<<PREFIX>>
BASE_DIR=<<BASE_DIR>>
BUILD_BASE=<<BUILD_BASE>>
DISTUTILS_OPTS=<<DISTUTILS_OPTS>>
INSTALL_FLAGS=<<INSTALL_FLAGS>>
TESTOPTS=-v
BUILD_FLAGS=--build-base="$(BUILD_BASE)" \
--build-lib="$(BUILD_BASE)\build-lib" \
--build-scripts="$(BUILD_BASE)\build-scripts" \
--build-temp="$(BUILD_BASE)\build-temp"
RM=del /f /q
!IF ("$(OS)" == "Windows_NT")
RMRF=rmdir /s /q
!ELSE
RMRF=deltree /y
!ENDIF
CD=cd
XCOPY=xcopy /i /s /e /y
COPY=copy
.PHONY: clean install build unbuild
.PHONY: default
default: build
# default: The default step (invoked when make is called without a target)
@ echo.
@ echo Zope built. Next, do 'nmake install'.
@ echo
# build: Do whatever 'setup.py build' implies
build:
$(PYTHON) "$(BASE_DIR)\setup.py" \
$(DISTUTILS_OPTS) build $(BUILD_FLAGS)
# unbuild: Remove the build directory (undo the make build step)
unbuild:
$(RMRF) $(BUILD_BASE)
# install: Install a software home.
install: build
$(PYTHON) "$(BASE_DIR)\setup.py" $(DISTUTILS_OPTS) install \
--prefix="$(PREFIX)" $(BUILD_FLAGS) $(INSTALL_FLAGS)
@ echo.
@ echo Zope binaries installed successfully.
@ echo Now run '$(PYTHON) $(PREFIX)\bin\mkzopeinstance.py'
# clean: Delete the build files and any binaries/bytecode files in
# the source directory for good measure.
clean: unbuild
$(CD) "$(BASE_DIR)
$(RM) /s *.pyc *.pyo *.dll *.o *.obj *.pyd
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import compileall, os, sys
class Shutup:
def write(*args): pass # :)
class NoteErr:
wrote = 0
def write(self, *args):
self.wrote = 1
apply(stderr.write, args)
def compile_non_test(dir):
"""Byte-compile all modules except those in test directories."""
success = compileall.compile_dir(dir, maxlevels=0)
try:
names = os.listdir(dir)
except os.error:
print "Can't list", dir
names = []
names.sort()
for name in names:
fullname = os.path.join(dir, name)
if (name != os.curdir and name != os.pardir and
os.path.isdir(fullname) and not os.path.islink(fullname) and
name != 'test' and name != 'tests' and name != 'skins'):
success = success and compile_non_test(fullname)
return success
print
print '-'*78
print 'Compiling python modules'
stdout = sys.stdout
stderr = sys.stderr
try:
try:
success = 0
sys.stdout = Shutup()
sys.stderr = NoteErr()
success = compile_non_test(os.getcwd())
finally:
success = success and not sys.stderr.wrote
sys.stdout = stdout
sys.stderr = stderr
except:
success = 0
import traceback
traceback.print_exc()
if not success:
print
print '!' * 78
print 'There were errors during Python module compilation.'
print '!' * 78
print
sys.exit(1)
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""
Create a Makefile for building and installing Zope.
"""
import getopt
import os
import sys
import versions
QUIET=0
if sys.platform == 'win32':
PREFIX = 'c:\\Zope-' + versions.ZOPE_MAJOR_VERSION
IN_MAKEFILE = 'Makefile.win.in'
MAKE_COMMAND='the Visual C++ batch file "VCVARS32.bat" and then "nmake build"'
else:
PREFIX = '/opt/Zope-' + versions.ZOPE_MAJOR_VERSION
IN_MAKEFILE = 'Makefile.in'
MAKE_COMMAND='make'
def main():
# below assumes this script is in the BASE_DIR/inst directory
global PREFIX
BASE_DIR=os.path.abspath(os.path.dirname(os.path.dirname(sys.argv[0])))
BUILD_BASE=os.path.join(os.getcwd(), 'build-base', 'python-%s.%s' % sys.version_info[:2])
PYTHON=sys.executable
MAKEFILE=open(os.path.join(BASE_DIR, 'inst', IN_MAKEFILE)).read()
REQUIRE_LF_ENABLED = 1
REQUIRE_ZLIB = 1
REQUIRE_EXPAT = 1
INSTALL_FLAGS = ''
DISTUTILS_OPTS = ''
try:
longopts = ['help', 'ignore-largefile', 'ignore-zlib',
'ignore-expat', 'prefix=',
'build-base=', 'optimize', 'no-compile', 'quiet']
opts, args = getopt.getopt(sys.argv[1:], 'h', longopts)
except getopt.GetoptError, v:
print v
usage()
sys.exit(1)
for o, a in opts:
if o in ('-h', '--help'):
usage()
sys.exit()
if o == '--prefix':
PREFIX=os.path.abspath(os.path.expanduser(a))
if o == '--ignore-largefile':
REQUIRE_LF_ENABLED=0
if o == '--ignore-zlib':
REQUIRE_ZLIB=0
if o == '--ignore-expat':
REQUIRE_EXPAT=0
if o == '--optimize':
INSTALL_FLAGS = '--optimize=1 --no-compile'
if o == '--no-compile':
INSTALL_FLAGS = '--no-compile'
if o == '--build-base':
BUILD_BASE = a
if o == '--quiet':
DISTUTILS_OPTS = '-q'
global QUIET
QUIET = 1
if REQUIRE_LF_ENABLED:
test_largefile()
if REQUIRE_ZLIB:
test_zlib()
if REQUIRE_EXPAT:
test_expat()
out(' - Zope top-level binary directory will be %s.' % PREFIX)
if INSTALL_FLAGS:
out(' - Distutils install flags will be "%s"' % INSTALL_FLAGS)
idata = {
'<<PYTHON>>':PYTHON,
'<<PREFIX>>':PREFIX,
'<<BASE_DIR>>':BASE_DIR,
'<<BUILD_BASE>>':BUILD_BASE,
'<<INSTALL_FLAGS>>':INSTALL_FLAGS,
'<<ZOPE_MAJOR_VERSION>>':versions.ZOPE_MAJOR_VERSION,
'<<ZOPE_MINOR_VERSION>>':versions.ZOPE_MINOR_VERSION,
'<<VERSION_RELEASE_TAG>>':versions.VERSION_RELEASE_TAG,
'<<DISTUTILS_OPTS>>':DISTUTILS_OPTS,
}
for k,v in idata.items():
MAKEFILE = MAKEFILE.replace(k, v)
f = open(os.path.join(os.getcwd(), 'makefile'), 'w')
f.write(MAKEFILE)
out(' - Makefile written.')
out('')
out(' Next, run %s.' % MAKE_COMMAND)
out('')
def usage():
usage = ("""
%(program)s configures and writes a Makefile for Zope.
Defaults for options are specified in brackets.
Configuration:
-h, --help display this help and exit
Options:
--quiet suppress nonessential output
--ignore-zlib allow configuration to proceeed if
Python zlib module is not found.
--ignore-largefile allow configuration to proceed without
Python large file support.
--ignore-expat allow configuration to proceed if the expat
XML parsing module is not found.
--optimize compile Python files as .pyo files
instead of as .pyc files
--no-compile don't compile Python files
Directories:
--build-base=DIR use DIR to store temporary build files
--prefix=DIR install Zope files in DIR [%(PREFIX)s]
By default, 'make install' will install Zope software files in
'%(PREFIX)s' You can specify an alternate location for these
files by using '--prefix', for example: '--prefix=$HOME/zope'.
""" % ({'program':sys.argv[0], 'PREFIX':PREFIX})
)
print usage
def test_expat():
try:
import xml.parsers.expat
except ImportError:
print (
"""
The Python interpreter you are using does not appear to have the 'pyexpat'
library module installed. For many Zope features to work properly, including
Zope Page Templates, you must install a Python interpreter which includes the
pyexpat module, or install the pyexpat library into your Python interpreter
manually. The file which represents the library is named 'pyexpat.so' (UNIX)
or 'pyexpat.dll' (Windows) and is typically located in the 'lib-dynload'
directory of your Python's library directory. Some Python packagers ship the
pyexpat module as a separate installable binary. If you are using a
system-provided Python installation, you may want to look for a 'python-xml'
or 'python-pyexpat' package (or something like it) and install it to make the
pyexpat module available to Zope. If you've compiled your Python interpreter
from source, you may need to recompile and reinstall it after installing James
Clark's expat libraries and development packages (look for libexpat.so and
expat.h). Typically, these come as part of your operating system's libexpat
and libexpat-dev packages, respectively.
Run the configure script with the --ignore-expat option to prevent this
warning with the understanding that some Zope features may not work properly
until you've installed the pyexpat module.
"""
)
sys.exit(1)
def test_zlib():
try:
import zlib
except ImportError:
print (
"""
The Python interpreter you are using does not appear to have the 'zlib'
library module installed. For Zope to be able to run, you must install a
Python interpreter which includes the zlib module, or install the zlib library
into your Python interpreter manually. The file which represents the library
is named 'zlib.so' (UNIX) or 'zlib.dll' (Windows) and is typically located in
the 'lib-dynload' directory of your Python's library directory. Some
Python packagers ship the zlib module as a separate installable binary. If you
are using a system-provided Python installation, you may want to look for
a 'python-zlib' package (or something like it) and install it to make the
Python zlib module available to Zope.
Run the configure script with the --ignore-zlib option to prevent this
warning with the understanding that Zope will not start properly until
you've installed the zlib module.
"""
)
sys.exit(1)
except:
print 'An error occurred while trying to import zlib!'
import traceback; traceback.print_exc()
sys.exit(1)
def test_largefile():
OK=0
f = open(sys.argv[0], 'r')
try:
# 2**31 == 2147483648
f.seek(2147483649L)
f.close()
OK=1
except (IOError, OverflowError):
f.close()
if OK:
return
print (
"""
This Python interpreter does not have 'large file support' enabled. Large
file support is required to allow the default Zope ZODB database to grow
larger than 2GB on most platforms. Either install a Python interpreter with
large file support (see
http://www.python.org/doc/current/lib/posix-large-files.html) or run this
program again with the --ignore-largefile option to prevent this warning,
with the understanding that your Zope may fail if the ZODB database
size ever exceeds 2GB.
"""
)
sys.exit(1)
def out(s):
if not QUIET:
print s
if __name__ == '__main__':
main()
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""
Reads a file named by 'src', performs textual replacements on the
file based on sed-style markup, and writes the result to the file named
by 'dst' unless 'dst' already exists.
"""
import getopt, os, sys
from os.path import abspath, split, dirname
import shutil
import versions
default_map = {
'PYTHON' : sys.executable,
'BASE_DIR' : abspath(split(dirname(sys.argv[0]))[0]),
'ZOPE_MAJOR_VERSION' : versions.ZOPE_MAJOR_VERSION,
'ZOPE_MINOR_VERSION' : versions.ZOPE_MINOR_VERSION,
'ZOPE_BRANCH_NAME' : versions.ZOPE_BRANCH_NAME,
'VERSION_RELEASE_TAG' : versions.VERSION_RELEASE_TAG,
}
def main(source, dest, map, force):
if not force and os.path.exists(dest):
print '%s exists, so I left it alone' % dest
else:
txt = open(source, 'rb').read()
for k, v in map.items():
txt = txt.replace('<<%s>>' % k, v)
outfile = open(dest, 'wb')
outfile.write(txt)
outfile.close()
shutil.copystat(source, dest)
print "Wrote %s from %s" % (dest, source)
def usage():
print "%s [opts] src dst" % sys.argv[0]
print
print "Reads a file named by 'src', performs textual replacements on "
print "the file based on sed-style markup embedded in the infile, and "
print "and writes the result to the file named by 'dst' unless 'dst'."
print "already exists. The generated file will have the same permissions"
print "and other mode bit settings as the source file."
print
print "Options:"
print
print " --force Force replacement of dst even if it already exists."
for name, value in default_map.items():
print (" --%s=value controls text replacement, default '%s'"
% (name, value))
if __name__ == '__main__':
if len(sys.argv) < 3:
usage()
sys.exit(127)
map = default_map.copy()
force = 0
try:
longopts = ['help', 'force']
for name in default_map.keys():
longopts.append('%s=' % name)
opts, args = getopt.getopt(sys.argv[1:], 'h', longopts)
except getopt.GetoptError, v:
print v
usage()
sys.exit(1)
try:
source, dest = args
except:
usage()
sys.exit(1)
for o, a in opts:
if o in ('-h', '--help'):
usage()
sys.exit()
if o == '--force':
force = 1
if o in map.keys():
map[o] = a
main(source, dest, map, force)
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""
Generic file and directory installer.
Typically called when installing Zope via the Makefile written by
'configure.py' in the 'make install' step.
"""
import getopt
import os
import re
import shutil
import stat
import sys
# RE that, if a pathname's base name matches, causes it to be ignored
# when copying that file or directory.
default_omitpattern = r'(\..*|CVS|.*~)$'
def main(src, dst, dirmode=0755, fmode=0644,
omitpattern=default_omitpattern, retain_xbit=1):
"""
Copy a file or directory named by src to a file or directory named by
dst, normalizing mode bit settings as necessary. Recursively copies
directory trees using shutil.copy2().
Errors are reported to standard output.
- 'dirmode' is the directory creation mode. All directories
are created with this mode.
- 'fmode' is the default file creation mode. This mode
is modified by the status of the source file. If the source
file is executable, mod the fmode to be +wgo executable.
- omitpattern is a Python-style regex pattern. If a file
or directory name matches this pattern, it will never be copied.
- if the dst directory already exists, don't raise an error.
"""
try:
if os.path.isdir(src):
copydir(src, dst, dirmode, fmode, omitpattern,
retain_xbit)
else:
names = omit([src], omitpattern)
names and copyfile(names[0], dst, fmode, retain_xbit)
except (IOError, os.error), why:
print "Can't copy %s to %s: %s" % (`src`, `dst`, str(why))
def copydir(src, dst, dirmode, fmode, omitpattern, retain_xbit):
names = omit(os.listdir(src), omitpattern)
try:
# always create directories with dirmode
os.makedirs(dst, dirmode)
except os.error, why:
if why[0] == 17:
# directory already exists
pass
else:
raise
for name in omit(names, omitpattern):
srcname = os.path.join(src, name)
dstname = os.path.join(dst, name)
if os.path.isdir(srcname):
copydir(srcname, dstname, dirmode,fmode,omitpattern,
retain_xbit)
else:
copyfile(srcname, dstname, fmode, retain_xbit)
def copylink(src, dst):
linkto = os.readlink(src)
os.symlink(linkto, dst)
def copyfile(src, dst, mode, retain_xbit):
shutil.copy2(src, dst)
# change dest file mode to fmode but
# make +wgo executable if source file is executable
dstmode = mode
st = os.stat(src)
srcmode = st[stat.ST_MODE]
if retain_xbit and (srcmode & stat.S_IEXEC):
dstmode = (mode | 0111)
if os.path.isdir(dst):
# if dst is a directory, copy the file in to it
os.chmod(os.path.join(dst, os.path.split(src)[-1]), dstmode)
else:
os.chmod(dst, dstmode)
omitcache = {}
def omit(names, omitpattern):
return [ n for n in names
if not re.match(omitpattern, os.path.basename(n)) ]
def usage():
print "%s [opts] source dest" % sys.argv[0]
print
print "Copies a file or directory specified by 'source' to 'dest'"
print "normalizing mode bit settings as necessary."
print
print "If src is a file and dst is a directory, the file will be"
print "copied into the dst directory. However, if src is a directory"
print "and dst is a directory, the contents of src will be copied into"
print "dst."
print
print "opts: --dirmode=mode --fmode=mode --omitpattern=patt"
print
print " --dontcopyxbit when copying a file marked as executable,"
print " don't make the copy executable."
print " --dirmode mode bit settings of dest dirs (e.g. '755')"
print " --fmode mode bit settings of dest files (e.g. '644')"
print " (modified wgo+x when dontcopyxbit is not"
print " specified)"
print " --omitpattern a Python-style regex pattern. File and"
print " directory names which match this pattern will "
print " not be copied. The default omitpattern is"
print " '%s'" % default_omitpattern
if __name__ == '__main__':
if len(sys.argv) < 3:
print "too few arguments"
usage()
sys.exit(2)
dirmode = 0755
fmode = 0644
omitpattern = default_omitpattern
retain_xbit = 1
longopts = ['dirmode=', 'fmode=', 'omitpattern=', 'help',
'copyxmode' ]
try:
opts, args = getopt.getopt(sys.argv[1:], 'h', longopts)
except getopt.GetoptError, v:
print v
usage()
sys.exit(2)
try:
source, dest = args
except:
print "wrong number of arguments"
usage()
sys.exit(2)
for o, a in opts:
if o in ('-h', '--help'):
usage()
sys.exit()
if o == '--dirmode':
if not a.startswith('0'):
a = '0%s' % a
dirmode = eval(a)
if o == '--fmode':
if not a.startswith('0'):
a = '0%s' % a
fmode = eval(a)
if o == '--omitpattern':
omitpattern = a
if o == '--dontcopyxbit':
retain_xbit = 0
main(source, dest, dirmode, fmode, omitpattern, retain_xbit)
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Build a PCGI resource file.
You must be in the directory containing this script.
"""
import os
from do import *
def main(cwd=os.getcwd(), name='Zope', user='', group=''):
python=sys.executable
print '-'*78
print 'Writing the pcgi resource file (ie cgi script), %s' % name
cwd=os.environ.get('ZDIR',cwd)
open(name,'w').write('''#!%(cwd)s/pcgi/pcgi-wrapper
PCGI_NAME=Zope
PCGI_MODULE_PATH=%(cwd)s/lib/python/Zope
PCGI_PUBLISHER=%(cwd)s/pcgi/pcgi_publisher.py
PCGI_EXE=%(python)s
PCGI_SOCKET_FILE=%(cwd)s/var/pcgi.soc
PCGI_PID_FILE=%(cwd)s/var/pcgi.pid
PCGI_ERROR_LOG=%(cwd)s/var/pcgi.log
PCGI_DISPLAY_ERRORS=1
BOBO_REALM=%(name)s
BOBO_DEBUG_MODE=1
INSTANCE_HOME=%(cwd)s
''' % vars())
ch(name, user, group, 0755)
ZOPE_MAJOR_VERSION = '2.8'
ZOPE_MINOR_VERSION = '0'
ZOPE_BRANCH_NAME = '$Name$'[6:] or 'no-branch'
# always start prerelease branches with '0' to avoid upgrade
# issues in RPMs
VERSION_RELEASE_TAG = 'a1'
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import os, sys
DEBUG = 0
if os.name in ('posix', 'nt', 'dos'):
EXCLUDED_NAMES=['..', '.']
else:
EXCLUDED_NAMES=[]
# extend EXCLUDED_NAMES here manually with filenames ala "asyncore.pyc" for
# files that are only distributed in compiled format (.pyc, .pyo)
# if necessary (not currently necessary in 2.3.1 AFAIK) - chrism
def walkandscrub(path):
path = os.path.expandvars(os.path.expanduser(path))
print
print '-'*78
sys.stdout.write(
"Deleting '.pyc' and '.pyo' files recursively under %s...\n" % path
)
os.path.walk(path, scrub, [])
sys.stdout.write('Done.\n')
def scrub(list, dirname, filelist):
for name in filelist:
if name in EXCLUDED_NAMES:
continue
prefix, ext = os.path.splitext(name)
if ext == '.pyo' or ext == '.pyc':
full = os.path.join(dirname, name)
os.unlink(full)
if DEBUG: print full
if __name__ == '__main__':
DEBUG = 1
walkandscrub(os.getcwd())
This diff is collapsed.
/*****************************************************************************
Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
FOR A PARTICULAR PURPOSE
****************************************************************************/
#include "Python.h"
#if defined(__unix__) || defined(unix) || defined(__NetBSD__) || defined(__MACH__) /* Mac OS X */
#include <grp.h>
#include <sys/types.h>
#include <unistd.h>
static PyObject *
initgroups_initgroups(PyObject *self, PyObject *args)
{
char *username;
unsigned int igid;
gid_t gid;
if (!PyArg_ParseTuple(args, "sI:initgroups", &username, &igid))
return NULL;
gid = igid;
if (initgroups(username, gid) == -1)
return PyErr_SetFromErrno(PyExc_OSError);
Py_INCREF(Py_None);
return Py_None;
}
static PyMethodDef InitgroupsMethods[] = {
{"initgroups", initgroups_initgroups, METH_VARARGS},
{NULL, NULL}
};
#else
/* This module is empty on non-UNIX systems. */
static PyMethodDef InitgroupsMethods[] = {
{NULL, NULL}
};
#endif /* defined(__unix__) || defined(unix) */
void
initinitgroups(void)
{
Py_InitModule("initgroups", InitgroupsMethods);
}
Security Architecture
---------------------
Users
-----
Objects representing users may be created in Principia
User Folder objects. User objects maintain the information
used to authenticate users, and allow roles to be associated
with a user.
Permissions
-----------
A "permission" is the smallest unit of access to an object,
roughly equivalent to the atomic permissions seen in NT:
R (Read), W(Write), X(Execute), etc. In Principia, a permission
usually describes a fine-grained logical operation on an object,
such as "View Management Screens", "Add Properties", etc.
Different types of objects will define different permissions
as appropriate for the object.
Types of access
---------------
A "type of access" is a named grouping of 0 or more of the
permissions defined by an object. All objects have one predefined
type of access called Full Access (all permissions defined by that
object). A user who has the special role "Manager" always has Full
Access to all objects at or below the level in the object hierarchy
at which the user is defined.
New types of access may be defined as combinations of the
various permissions defined by a given object. These new
types of access may be defined by the programmer, or by
users at runtime.
Roles
-----
A role is a name that ties users (authentication of identity)
to permissions (authorization for that identity) in the system.
Roles may be defined in any Folder (or Folderish) object in the
system. Sub folders can make use of roles defined higher in the
hierarchy. These roles can be assigned to users. All users,
including non-authenticated users have the built-in role of
"Anonymous".
Principia objects allow the association of defined roles
with a single "type of access" each, in the context of that
object. A single role is associated with one and only one
type of access in the context of a given object.
Examples
--------
User Object1
o has the role "RoleA" o has given "RoleA" Full Access
Result: the user has Full Access to Object1.
User Object2
o has the role "RoleA" o has given "RoleB" Full Access
o has given the role "RoleA" View Access,
a custom type of access that allows only
viewing of the object.
Result: the user has only View Access.
Notes
-----
All objects define a permission called "Default permission". If this
permission is given to a role, users with that role will be able to
access subobjects which do not explicitly restrict access.
Technical
---------
Objects define their permissions as logical operations.
Programmers have to determine the appropriate operations
for their object type, and provide a mapping of permission
name to attribute names. It is important to note that permissions
cannot overlap - none of the attributes named in a permission
can occur in any of the other permissions. The following are
proposed permissions for some current principia objects:
Folder
o View management screens
o Change permissions
o Undo changes
o Add objects
o Delete objects
o Add properties
o Change properties
o Delete properties
o Default permission
Confera Topic
o View management screens
o Change permissions
o Undo changes
o Add objects
o Delete objects
o Add properties
o Change properties
o Delete properties
o Default permission
o Change Configuration
o Add Messages
o Change Messages
o Delete Messages
Tabula Collection
o View management screens
o Change permissions
o Undo changes
o Add objects
o Delete objects
o Add properties
o Change properties
o Delete properties
o Default permission
o Change schema
o Upload data
o Add computed fields
o Change computed fields
o Delete computed fields
Document/Image/File
o View management screens
o Change permissions
o Change/upload data
o View
Session
o View management screens
o Change permissions
o Change session config
o Join/leave session
o Save/discard session
Mail Host
o View management screens
o Change permissions
o Change configuration
To support the architecture, developers must derive an
object from the AccessControl.RoleManager mixin class,
and define in their class an __ac_permissions__ attribute.
This should be a tuple of tuples, where each tuple represents
a permission and contains a string permission name as its first
element and a list of attribute names as its second element.
Example:
__ac_permissions__=(
('View management screens',
['manage','manage_menu','manage_main','manage_copyright',
'manage_tabs','manage_propertiesForm','manage_UndoForm']),
('Undo changes', ['manage_undo_transactions']),
('Change permissions', ['manage_access']),
('Add objects', ['manage_addObject']),
('Delete objects', ['manage_delObjects']),
('Add properties', ['manage_addProperty']),
('Change properties', ['manage_editProperties']),
('Delete properties', ['manage_delProperties']),
('Default permission', ['']),
)
The developer may also predefine useful types of access, by
specifying an __ac_types__ attribute. This should be a tuple of
tuples, where each tuple represents a type of access and contains
a string name as its first element and a list of permission names
as its second element.
By default, only "Full Access" is defined (by the RoleManager mixin).
If you wish to override __ac_types__ to provide convenient types of
access, you must always be sure to define "Full Access" as containing
the names of all possible permissions for your object.
Example:
__ac_types__=(
('Full Access', map(lambda x: x[0], __ac_permissions__)),
('Change', ['Add Objects', 'Add Properties', 'Change Properties']),
)
Developers may also provide pre-defined role names that are
not deletable via the interface by specifying an __ac_roles__
attribute. This is probably not something we'll ever use under
the new architecture, but it's there if you need it.
Example:
__ac_roles__=('Manager', 'Anonymous')
This diff is collapsed.
This diff is collapsed.
class IStandardUserFolder:
def getUser(self, name):
"""
Returns the user object specified by name. If there is no
user named 'name' in the user folder, return None.
"""
def getUsers(self):
"""
Returns a sequence of all user objects which reside in the user
folder.
"""
def getUserNames(self):
"""
Returns a sequence of names of the users which reside in the user
folder.
"""
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
*shared*
cAccessControl cAccessControl.c -I../../Components/ExtensionClass/src
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
# test module, partially private
def priv():
pass
def pub():
pass
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment