[Checkins] SVN: zdgbook/trunk/ Zope Developers' Guide STX source, extracted from www.zope.org

Tres Seaver tseaver at palladion.com
Mon Feb 16 23:44:21 EST 2009


Log message for revision 96617:
  Zope Developers' Guide STX source, extracted from www.zope.org

Changed:
  A   zdgbook/trunk/
  A   zdgbook/trunk/Acquisition.stx
  A   zdgbook/trunk/AppendixA.stx
  A   zdgbook/trunk/AppendixB.stx
  A   zdgbook/trunk/ComponentsAndInterfaces.stx
  A   zdgbook/trunk/Credits.stx
  A   zdgbook/trunk/Errata.stx
  A   zdgbook/trunk/Figures/
  A   zdgbook/trunk/Figures/2-1.png
  A   zdgbook/trunk/Figures/2-2.png
  A   zdgbook/trunk/Gotchas.stx
  A   zdgbook/trunk/Introduction.stx
  A   zdgbook/trunk/Makefile
  A   zdgbook/trunk/ObjectPublishing.stx
  A   zdgbook/trunk/Outline.stx
  A   zdgbook/trunk/Products.stx
  A   zdgbook/trunk/Security.stx
  A   zdgbook/trunk/TestingAndDebugging.stx
  A   zdgbook/trunk/ZODBPersistentComponents.stx
  A   zdgbook/trunk/bootstrap.py
  A   zdgbook/trunk/build/
  A   zdgbook/trunk/buildout.cfg
  A   zdgbook/trunk/examples/
  A   zdgbook/trunk/examples/Poll-1.0.tgz
  A   zdgbook/trunk/examples/Poll.py
  A   zdgbook/trunk/examples/PollImplementation.py
  A   zdgbook/trunk/examples/PollProduct.py
  A   zdgbook/trunk/source/
  A   zdgbook/trunk/source/.static/
  A   zdgbook/trunk/source/.templates/
  A   zdgbook/trunk/source/conf.py
  A   zdgbook/trunk/source/index.rst

-=-
Added: zdgbook/trunk/Acquisition.stx
===================================================================
--- zdgbook/trunk/Acquisition.stx	                        (rev 0)
+++ zdgbook/trunk/Acquisition.stx	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,527 @@
+Chapter 5: Acquisition
+
+  Acquisition is a mechanism that allows objects to obtain attributes
+  from their environment.  It is similar to inheritance, except that,
+  rather than searching an inheritance hierarchy to obtain attributes,
+  a containment hierarchy is traversed.
+
+    % mcdonc - June 6, 2002 12:00 pm:
+     This chapter needs an explanation of the inheritedAttribute method.
+
+  Introductory Example
+
+    Zope implements acquisition with "Extension Class" mix-in
+    classes. To use acquisition your classes must inherit from an
+    acquisition base class. For example::
+
+      import ExtensionClass, Acquisition
+
+      class C(ExtensionClass.Base):
+        color='red'
+
+      class A(Acquisition.Implicit):
+
+        def report(self):
+          print self.color
+
+      a=A()
+      c=C()
+      c.a=A()
+
+      c.a.report() # prints 'red'
+
+      d=C()
+      d.color='green'
+      d.a=a
+
+      d.a.report() # prints 'green'
+
+      a.report() # raises an attribute error
+
+    The class 'A' inherits acquisition behavior from
+    'Acquisition.Implicit'.  The object, 'a', "has" the color of
+    objects 'c' and 'd' when it is accessed through them, but it has
+    no color by itself.  The object 'a' obtains attributes from its
+    environment, where its environment is defined by the access path
+    used to reach 'a'.
+
+  Acquisition Wrappers
+
+    When an object that supports acquisition is accessed through an
+    extension class instance, a special object, called an acquisition
+    wrapper, is returned.  In the example above, the expression 'c.a'
+    returns an acquisition wrapper that contains references to both
+    'c' and 'a'.  It is this wrapper that performs attribute lookup in
+    'c' when an attribute cannot be found in 'a'.
+
+    Acquisition wrappers provide access to the wrapped objects through
+    the attributes 'aq_parent', 'aq_self', 'aq_base'.  In the example
+    above, the expressions::
+
+       'c.a.aq_parent is c'
+
+    and::
+
+       'c.a.aq_self is a'
+
+      % Anonymous User - Sep. 29, 2005 1:59 am:
+       >>> c.a.aq_self is a
+       False
+       ..this is because "c.a" is not the same object "a" which was created earlierly. "c.a=A()" creates new object
+       of class A wrapped inside object c. Check out the locations of objects "c.a" and "a".
+       >>> c.a
+       <__main__.A object at 0xb7e00f0c>
+       >>> a
+       <__main__.A object at 0xb7e0402c>
+       (location mostly be different on your machine).
+
+      % Anonymous User - Dec. 19, 2003 8:10 am:
+       OK, this doesn't work because of the line (above):
+       c.a = A()
+       which creates a new object of class A.
+       I suppose it should work if the above line is replaced with:
+       c.a = a
+
+      % Anonymous User - May 25, 2003 12:13 pm:
+       Hmm. Mine isn't doing this. Here's some pasted output:
+       [snip]
+       AttributeError: color
+       >>> c.a.aq_parent is c
+       1
+       >>> c.a.aq_self is a
+       0
+
+    both evaluate to true, but the expression::
+
+       'c.a is a'
+
+    evaluates to false, because the expression 'c.a' evaluates to an
+    acquisition wrapper around 'c' and 'a', not 'a' itself.
+
+    The attribute 'aq_base' is similar to 'aq_self'.  Wrappers may be
+    nested and 'aq_self' may be a wrapped object.  The 'aq_base'
+    attribute is the underlying object with all wrappers removed.
+
+    You can manually wrap an instance of an object that inherits from
+    an acquisition base class by using its '__of__' method. For
+    example::
+
+      class A(Acquisition.Implicit):
+          pass
+
+      a=A()
+      a.color='red'
+      b=A()
+      a.b=b
+
+      print b.__of__(a).color # prints red
+
+      % Anonymous User - Apr. 5, 2003 11:19 pm:
+       Why the a.b=b here? Surely that statement should be omitted because it only confuses the issue.
+       b.__of__(a).color will still return 'red' without it. The entire point of __of__() is to provide a context
+       for an object, b in this case, and saying a.b=b also provides this context.
+
+    The expression 'b.__of__(a)' wraps 'b' in an acquisition wrapper
+    explicitly, and returns the acquisition wrapper.  The 'color'
+    attrribute of 'a' is found via acquisition when this expression
+    is executed.
+
+      % Anonymous User - Nov. 8, 2002 11:54 pm:
+       i really like to see the code of __of__. Its not python, is it?
+       what exactly is an acquisition wrapper?
+
+  Explicit and Implicit Acquisition
+
+    Two styles of acquisition are supported: implicit and explicit
+    acquisition.
+
+    Implicit acquisition
+
+      Implicit acquisition is so named because it searches for
+      attributes from the environment automatically whenever an
+      attribute cannot be obtained directly from an object or through
+      inheritance.
+
+      An attribute can be implicitly acquired if its name does not
+      begin with an underscore.
+
+      To support implicit acquisition, your class should inherit from
+      the mix-in class 'Acquisition.Implicit'.
+
+    Explicit Acquisition
+
+      When explicit acquisition is used, attributes are not
+      automatically obtained from the environment.  Instead, the
+      method 'aq_acquire' must be used. For example::
+
+        print c.a.aq_acquire('color')
+
+      To support explicit acquisition, your class should inherit from
+      the mix-in class 'Acquisition.Explicit'.
+
+        % Anonymous User - Feb. 28, 2002 4:55 pm - Meaning that, in the above example, class A, of which a is an instance, should inherit from Acquisition.Implicit.
+
+    Controlling Acquisition
+
+      A class (or instance) can provide attribute by attribute control
+      over acquisition.  Your should subclass from
+      'Acquisition.Explicit', and set all attributes that should be
+      acquired to the special value 'Acquisition.Acquired'.  Setting
+      an attribute to this value also allows inherited attributes to
+      be overridden with acquired ones.  For example::
+
+        class C(Acquisition.Explicit):
+           id=1
+           secret=2
+           color=Acquisition.Acquired
+           __roles__=Acquisition.Acquired
+
+      The *only* attributes that are automatically acquired from
+      containing objects are 'color', and '__roles__'.  Note that the
+      '__roles__' attribute is acquired even though its name begins
+      with an underscore.  In fact, the special 'Acquisition.Acquired'
+      value can be used in 'Acquisition.Implicit' objects to
+      implicitly acquire selected objects that smell like private
+      objects.
+
+      Sometimes, you want to dynamically make an implicitly acquiring
+      object acquire explicitly. You can do this by getting the object's
+      'aq_explicit' attribute. This attribute provides the object with
+      an explicit wrapper that places the original implicit wrapper.
+
+  Filtered Acquisition
+
+    The acquisition method, 'aq_acquire', accepts two optional
+    arguments. The first of the additional arguments is a
+    "filtering" function that is used when considering whether to
+    acquire an object.  The second of the additional arguments is an
+    object that is passed as extra data when calling the filtering
+    function and which defaults to 'None'.  The filter function is
+    called with five arguments:
+
+      - The object that the 'aq_acquire' method was called on,
+
+      - The object where an object was found,
+
+      - The name of the object, as passed to 'aq_acquire',
+
+      - The object found, and
+
+      - The extra data passed to 'aq_acquire'.
+
+    If the filter returns a true object that the object found is
+    returned, otherwise, the acquisition search continues.
+
+    For example, in::
+
+      from Acquisition import Explicit
+
+      class HandyForTesting:
+          def __init__(self, name):
+              self.name=name
+          def __str__(self):
+              return "%s(%s)" % (self.name, self.__class__.__name__)
+          __repr__=__str__
+
+      class E(Explicit, HandyForTesting): pass
+
+      class Nice(HandyForTesting):
+          isNice=1
+          def __str__(self):
+              return HandyForTesting.__str__(self)+' and I am nice!'
+          __repr__=__str__
+
+      a=E('a')
+      a.b=E('b')
+      a.b.c=E('c')
+      a.p=Nice('spam')
+      a.b.p=E('p')
+
+      def find_nice(self, ancestor, name, object, extra):
+          return hasattr(object,'isNice') and object.isNice
+
+      print a.b.c.aq_acquire('p', find_nice)
+
+    The filtered acquisition in the last line skips over the first
+    attribute it finds with the name 'p', because the attribute
+    doesn't satisfy the condition given in the filter. The output of
+    the last line is::
+
+      spam(Nice) and I am nice!
+
+    Filtered acquisition is rarely used in Zope.
+
+  Acquiring from Context
+
+    Normally acquisition allows objects to acquire data from their
+    containers. However an object can acquire from objects that aren't
+    its containers.
+
+    Most of the example's we've seen so far show establishing of an
+    acquisition *context* using 'getattr' symanitics. For example,
+    'a.b' is a reference to 'b' in the context of 'a'.
+
+      % Anonymous User - Oct. 9, 2002 11:40 am:
+       Typos. 
+       s/example's/examples/
+       s/symanitics/semantics/
+
+    You can also manuallyset acquisition context using the '__of__'
+    method. For example::
+
+      from Acquisition import Implicit
+      class C(Implicit): pass
+      a=C()
+      b=C()
+      a.color="red"
+      print b.__of__(a).color # prints red
+
+      % Anonymous User - Sep. 2, 2002 12:38 pm:
+       s/manuallyset/manually set/
+
+    In this case, 'a' does not contain 'b', but it is put in 'b''s
+    context using the '__of__' method.
+
+    Here's another subtler example that shows how you can construct an
+    acquisition context that includes non-container objects::
+
+      from Acquisition import Implicit
+
+      class C(Implicit):
+          def __init__(self, name):
+              self.name=name
+
+      a=C("a")
+      a.b=C("b")
+      a.b.color="red"
+      a.x=C("x")
+
+      print a.b.x.color # prints red
+
+    Even though 'b' does not contain 'x', 'x' can acquire the 'color'
+    attribute from 'b'. This works because in this case, 'x' is
+    accessed in the context of 'b' even though it is not contained by
+    'b'. 
+
+    Here acquisition context is defined by the objects used to access
+    another object.
+
+  Containment Before Context
+
+    If in the example above suppose both 'a' and 'b' have an
+    'color' attribute::
+
+      a=C("a")
+      a.color="green"
+      a.b=C("b")
+      a.b.color="red"
+      a.x=C("x")
+
+      print a.b.x.color # prints green
+
+      % Anonymous User - Nov. 21, 2003 5:04 am:
+       what i print is :
+       Traceback (most recent call last):
+         File "<stdin>", line 1, in ?
+       AttributeError: x
+
+      % Anonymous User - Nov. 21, 2003 5:37 am:
+       oh, forget the last comment. sorry, i am wrong.
+
+    Why does 'a.b.x.color' acquire 'color' from 'a' and not from 'b'?
+    The answer is that an object acquires from its containers before
+    non-containers in its context.
+
+    To see why consider this example in terms of expressions using the
+    '__of__' method::
+
+      a.x -> x.__of__(a)
+
+      a.b -> b.__of__(a)
+
+      a.b.x -> x.__of__(a).__of__(b.__of__(a))
+
+    Keep in mind that attribute lookup in a wrapper is done by trying
+    to look up the attribute in the wrapped object first and then in
+    the parent object.  So in the expressions above proceeds from left
+    to right.
+
+      % Anonymous User - Feb. 28, 2002 6:07 pm - It looks like it should be stated a.b.x -> x.__of__(b.__of__(a)), but maybe I'm not really understanding this.
+
+      % Anonymous User - July 15, 2004 12:14 pm:
+       I think the reason that it's this way is because when you look for x in b.__of__(a), you try from left to
+       right. However, b doesn't contain an x, so you end up with the x.__of__(a) which is contained in the
+       b.__of__(a).
+
+    The upshot of these rules is that attributes are looked up by
+    containment before context. 
+
+    This rule holds true also for more complex examples. For example,
+    'a.b.c.d.e.f.g.attribute' would search for 'attribute' in 'g' and
+    all its containers first. (Containers are searched in order from
+    the innermost parent to the outermost container.) If the attribute
+    is not found in g or any of its containers, then the search moves
+    to 'f' and all its containers, and so on.
+
+  Additional Attributes and Methods
+
+    You can use the special method 'aq_inner' to access an object
+    wrapped only by containment. So in the example above::
+
+      a.b.x.aq_inner
+
+    is equivalent to::
+
+       a.x
+
+    You can find out the acquisition context of an object using the
+    'aq_chain' method like so::
+
+      a.b.x.aq_chain # returns [x, b, a]
+
+    You can find out if an object is in the acquisition context of
+    another object using the 'aq_inContextOf' method. For example::
+
+      a.b.x.aq_inContextOf(a.b) # returns 1
+
+      % Anonymous User - Apr. 20, 2005 10:00 am:
+       returns 0, because:
+       "aq_inContextOf = <CMethod object> 
+       Test whether the object is currently in the context of the argument"
+       and not test in the acquisition context.
+
+      % Anonymous User - Apr. 20, 2005 10:02 am:
+       Ok, 
+       a.b.x.aq_inContextOf(a.b) # returns 0
+       in Zope 2.7.5 :)
+
+    You can also pass an additional argument to 'aq_inContextOf' to
+    indicate whether to only check containment rather than the full
+    acquisition context. For example::
+
+      a.b.x.aq_inContextOf(a.b, 1) # returns 0
+
+    Note: as of this writing the 'aq_inContextOf' examples don't
+    work. According to Jim, this is because 'aq_inContextOf' works by
+    comparing object pointer addresses, which (because they are
+    actually different wrapper objects) doesn't give you the expected
+    results. He acknowledges that this behavior is controversial, and
+    says that there is a collector entry to change it so that you
+    would get the answer you expect in the above. (We just need to get
+    to it).
+
+      % Anonymous User - Feb. 10, 2003 2:58 pm:
+       A search on collector.zope.org for aq_inContextOf returns nothing.
+       Has this issue been resolved? Was there ever a collector issue for it?
+       In other words, does aq_inContextOf work or not?
+
+  Acquisition Module Functions
+
+    In addition to using acquisition attributes and methods directly
+    on objects you can use similar functions defined in the
+    'Acquisition' module. These functions have the advantage that you
+    don't need to check to make sure that the object has the method or
+    attribute before calling it.
+
+      'aq_acquire(object, name [, filter, extra, explicit, default, containment])' -- Acquires an object with the given name.
+
+        This function can be used to explictly acquire when using
+        explicit acquisition and to acquire names that wouldn't
+        normally be acquired.
+
+        The function accepts a number of optional
+        arguments:
+
+          'filter' -- A callable filter object that is used to decide if
+            an object should be acquired.
+
+            The filter is called with 
+            five arguments:
+
+              - The object that the aq_acquire method was called on,
+
+              - The object where an object was found,
+
+              - The name of the object, as passed to aq_acquire,
+
+              - The object found, and
+
+              - The extra argument passed to aq_acquire.
+
+            If the filter returns a true object that the
+            object found is returned, otherwise, the acquisition
+            search continues.
+
+          'extra' -- extra data to be passed as the last argument to the
+            filter.
+
+          'explicit' -- A flag (boolean value) indicating whether
+            explicit acquisition should be used. The default value
+            is true.  If the flag is true, then acquisition will
+            proceed regardless of whether wrappers encountered
+            in the search of the acquisition hierarchy are explicit or
+            implicit wrappers. If the flag is false, then parents of
+            explicit wrappers are not searched.
+
+            This argument is useful if you want to apply a filter without
+            overriding explicit wrappers.
+
+          'default' -- A default value to return if no value can be acquired.
+
+          'containment' -- A flag indicating whether the search
+            should be limited to the containment hierarchy. 
+
+        In addition, arguments can be provided as keywords.
+
+      'aq_base(object)' -- Return the object with all wrapping removed.
+
+      'aq_chain(object [, containment])' -- Return a list containing the object
+        and it's acquisition parents. The optional argument, 'containment', 
+        controls whether the containment or access hierarchy is used.
+
+      'aq_get(object, name [, default, containment])' --
+        Acquire an attribute, name. A default value can be provided, as
+        can a flag that limits search to the containment hierarchy.
+
+      'aq_inner(object)' -- Return the object with all but the innermost 
+        layer of wrapping removed.
+
+      'aq_parent(object)' -- Return the acquisition parent of the object
+        or 'None' if the object is unwrapped.
+
+      'aq_self(object)' -- Return the object with one layer of wrapping 
+        removed, unless the object is unwrapped, in which case the 
+        object is returned.
+
+    In most cases it is more convenient to use these module functions
+    instead of the acquisition attributes and methods directly.
+
+  Acquisition and Methods
+
+    Python methods of objects that support acquisition can use
+    acquired attributes.  When a Python method is called on an object
+    that is wrapped by an acquisition wrapper, the wrapper is passed
+    to the method as the first argument.  This rule also applies to
+    user-defined method types and to C methods defined in pure mix-in
+    classes.
+
+    Unfortunately, C methods defined in extension base classes that
+    define their own data structures, cannot use aquired attributes at
+    this time.  This is because wrapper objects do not conform to the
+    data structures expected by these methods. In practice, you will
+    seldom find this a problem.
+
+  Conclusion
+
+    Acquisition provides a powerful way to dynamically share
+    information between objects. Zope using acquisition for a number
+    of its key features including security, object publishing, and
+    DTML variable lookup. Acquisition also provides an elegant
+    solution to the problem of circular references for many classes of
+    problems. While acquisition is powerful, you should take care when
+    using acquisition in your applications. The details can get
+    complex, especially with the differences between acquiring from
+    context and acquiring from containment.
+
+      % acornet - Feb. 17, 2003 10:22 am:
+       s/using/uses/

Added: zdgbook/trunk/AppendixA.stx
===================================================================
--- zdgbook/trunk/AppendixA.stx	                        (rev 0)
+++ zdgbook/trunk/AppendixA.stx	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,172 @@
+Appendix A: Zope Core Permissions
+
+  This is a list of standard permissions included with Zope. It is a
+  good idea to use these permissions when applicable with your Zope
+  products, rather than creating new ones.
+
+    % mcdonc - June 11, 2002 6:46 pm:
+     The Copy or Move permission was added as of 2.6. If you revoke the Copy and Move permission from a role
+     respective to an object, even if a user has "View management screen" permissions, he or she will not be able
+     to copy or move the object.
+
+    % Anonymous User - Dec. 20, 2002 1:15 pm:
+     I don't mean to bitch, but the following list is absolutely unnecessary. Every permission seems to be defined
+     by quoting the permission and adding 'object' or the like. To me, this list contains no information.
+     Couldn't you put longer descriptions and examples for each right?
+
+    % Anonymous User - Dec. 20, 2002 1:20 pm:
+     I'd rather just remove the chapter if it becomes more than this. Right now I can't maintain it. If someone
+     else wanted to do this and keep it up to date, I'd be fine with it. -- chrism
+
+    % proton - May 9, 2003 7:14 am:
+     Sorry, Anon, "the following list" is absolutely NECESSARY. Yes, the descriptions are a little skimpy, but the
+     whole reason I found this page was that I needed to see the ENTIRE list of permissions without being filtered
+     by type. (i.e. if I go to the Zope management interface, I see only the permissions that apply to the type of
+     object that I've selected.)
+     Perhaps the page could be automatically generated from the Python source? I'd like to see a table-oriented
+     display: name of permission, object types it applies to, 1-sentence description, and "warnings / gotchas /
+     special notes."
+
+    % Anonymous User - June 21, 2004 12:22 pm:
+     This page needs expansion. I'm not a believer in "self-documentation", so administrators need to be able to
+     know for sure what security settings they're putting on a page. Yes, we could check the source. . .or we
+     could use a product that tells us what we're dealing with. I like the security model, the permission layering
+     is great, but we need a bit more info.
+
+    % Anonymous User - June 21, 2004 12:27 pm:
+     This page needs expansion. I'm not a believer in "self-documentation", so administrators need to be able to
+     know for sure what security settings they're putting on a page. Yes, we could check the source. . .or we
+     could use a product that tells us what we're dealing with. I like the security model, the permission layering
+     is great, but we need a bit more info.
+
+    % Anonymous User - Jan. 9, 2005 11:57 pm:
+     Pathetic .. breaks every known rule of good documentation I can think of.
+
+    % Anonymous User - Mar. 23, 2005 9:04 am:
+     This page absolutely needs expansion. I think having a list of which permission is used where, _and_
+     information for developers and admins on what the goal of each permissions, and some use cases would be
+     helpful.
+     I found this page because I wanted to know what "access content information" needs, because I have a zope
+     where there is no permission given to Anonymous, and wanted to give view permission to a wiki inside a wiki
+     inside zope. I figured out that "access content information" should be given to the zope root to Anonymous to
+     achieve this. From this page I figure it could be something like "access metadata" to some extent, but I
+     still cannot decide whether my private data is safe if I do this.
+     If there would be documentation on where this permission is used, It would be easier to figure out whether I
+     have to fear. I guess pythondoc could be easily hacked to extract this info.
+     If there would be some more words on goals of permissions, then developers could have more hints on which
+     permission should be used for what. This could be further clarified with usage examples.
+     The above two information would make easier for system administrators to set exactly the needed permissions
+     without a lot of experimenting.
+
+  Core Permissions
+
+    Access contents information -- get "directory listing" info
+
+    Add Accelerated HTTP Cache Managers -- add HTTP Cache Manager objects
+
+    Add Database Methods -- add ZSQL Method objects
+
+    Add Documents, Images, and Files -- add DTML Method/Document objects,
+     Image objects, and File objects
+
+    Add External Methods  -- add External Method objects
+
+    Add Folders -- add Folder objects
+
+    Add MailHost objects  -- add MailHost objects
+
+    Add Python Scripts  -- Add Python Script objects
+
+    Add RAM Cache Managers  -- Add RAM Cache manager objects
+
+    Add Site Roots -- add Site Root objects
+
+    Add User Folders  -- add User Folder objects
+
+    Add Versions  -- add Version objects
+
+    Add Virtual Host Monsters  -- add Virtual Host Monster objects
+
+    Add Vocabularies  -- add Vocabulary objects (ZCatalog-related)
+
+    Add ZCatalogs  -- add ZCatalog objects
+
+    Add Zope Tutorials  -- add Zope Tutorial objects
+
+    Change DTML Documents -- modify DTML Documents
+
+    Change DTML Methods  -- modify DTML Methods
+
+    Change Database Connections  -- change database connection objects
+
+    Change Database Methods  -- change ZSQL method objects
+
+    Change External Methods -- change External Method objects
+
+    Change Images and Files  -- change Image and File objects
+
+    Change Python Scripts  -- change Python Script objects
+
+    Change Versions  -- change Version objects
+
+    Change bindings  -- change bindings (for Python Scripts)
+
+    Change cache managers  -- change cache manager objects
+
+    Change cache settings  -- change cache settings (cache mgr parameters)
+
+    Change configuration  -- generic
+
+    Change permissions  -- change permissions
+
+    Change proxy roles  -- change proxy roles
+
+    Create class instances  -- used for ZClass permission mappings
+
+    Delete objects  -- delete objects
+
+    Edit Factories  -- edit Factory objects (ZClass)
+
+    FTP access  -- allow FTP access to this object
+
+    Import/Export objects  -- export and import objects
+
+    Join/leave Versions  -- join and leave Zope versions
+
+    Manage Access Rules -- manage access rule objects
+
+    Manage Vocabulary  -- manage Vocabulary objects
+
+    Manage Z Classes  -- Manage ZClass objects (in the control panel)
+
+    Manage ZCatalog Entries  -- catalog and uncatalog objects
+
+    Manage properties -- manage properties of an object
+
+    Manage users  -- manage Zope users
+
+    Open/Close Database Connections  -- open and close database connections    
+
+    Query Vocabulary -- query Vocabulary objects (ZCatalog-related)
+
+    Save/discard Version changes -- save or discard Zope version changes
+
+    Search ZCatalog -- search a ZCatalog instance
+
+    Take ownership  -- take ownership of an object
+
+    Test Database Connections  -- test database connection objects
+
+    Undo changes  -- undo changes to the ZODB (e.g. use the Undo tab)
+
+    Use Database Methods  -- use ZSQL methods
+
+    Use Factories  -- use Factory objects (ZClass-related)
+
+    Use mailhost services -- use MailHost object services
+
+    View -- view or execute an object
+
+    View History -- view ZODB history of an object
+
+    View management screens -- view management screens related to an object

Added: zdgbook/trunk/AppendixB.stx
===================================================================
--- zdgbook/trunk/AppendixB.stx	                        (rev 0)
+++ zdgbook/trunk/AppendixB.stx	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,84 @@
+Appendix B: Zope Directories
+
+  This is a list of some important directories in the Zope source
+  code.
+
+    'Extensions' -- Code for External Methods go in this
+    directory.
+
+    'ZServer' -- Python code for ZServer and Medusa.
+
+    'ZServer/medusa' -- Sam Rushing's Medusa package upon which
+    ZServer is built.
+
+    'doc' -- Miscellaneous documentation. 
+
+    'import' -- Place Zope export files here in order to import them
+    into Zope.
+
+    'inst' -- Installation scripts.
+
+    'pcgi' --  C and Python code for PCGI.
+
+    'utilities' -- Miscellaneous utilities.
+
+    'var' -- Contains the ZODB data file (Data.fs) and various other
+    files (logs, pids, etc.) This directory should be owned and
+    writable by the userid that Zope is run as.
+
+    'lib/Components' -- Python extension modules written in C
+    including BTree, ExtensionClass, cPickle, zlib, etc.
+
+    'lib/python' -- Most of the Zope Python code is in here.
+
+    'lib/python/AccessControl' -- Security classes.
+
+    'lib/python/App' -- Zope application classes. Stuff like product
+    registration, and the control panel.
+
+    'lib/python/BTrees' -- Btrees package.
+
+    'lib/python/DateTime' -- DateTime package.
+
+    'lib/python/DocumentTemplate' -- DTML templating package. DTML
+    Document and DTML Method use this.
+
+    'lib/python/HelpSys' -- Online help system.
+
+    'lib/python/Interface' -- Scarecrow interfaces package.
+
+    'lib/python/OFS' -- Object File System code. Includes basic Zope
+    classes (Folder, DTML Document) and interfaces (ObjectManager,
+    SimpleItem).
+
+    'lib/python/Products' -- Zope products are installed here.
+
+    'lib/python/Products/OFSP' -- The OFS product. Contains
+    initialization code for basic Zope objects like Folder and DTML
+    Document.
+
+    'lib/python/RestrictedPython' -- Python security used by DTML and
+    Python Scripts.
+
+    'lib/python/SearchIndex' -- Indexes used by ZCatalog.
+
+    'lib/python/Shared' -- Shared code for use by multiple Products.
+
+    'lib/python/StructuredText' -- Structured Text package.
+
+    'lib/python/TreeDisplay' -- Tree tag package.
+
+    'lib/python/ZClasses' -- ZClasses package.
+
+    'lib/python/ZLogger' -- Logging package.
+
+    'lib/python/ZODB' -- ZODB package.
+
+    'lib/python/ZPublisher' -- The Zope ORB.
+
+    'lib/python/Zope' -- The Zope package published by ZPublisher.
+
+    'lib/python/webDAV' -- WebDAV support classes and interfaces.
+
+    % Anonymous User - Mar. 12, 2003 2:21 am:
+     What's different between <zope>/lib/python/Products and <zope>/Products?

Added: zdgbook/trunk/ComponentsAndInterfaces.stx
===================================================================
--- zdgbook/trunk/ComponentsAndInterfaces.stx	                        (rev 0)
+++ zdgbook/trunk/ComponentsAndInterfaces.stx	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,631 @@
+Chapter 1: Components and Interfaces
+
+  Zope is becoming a component system. Zope components will be Python
+  objects with interfaces that describe them. Right now only some of
+  the Zope code base uses interfaces. In coming releases more and more
+  of Zope will include interfaces. As a Zope developer you can use
+  interfaces right now to build your Zope components.
+
+    % Anonymous User - Jan. 24, 2002 6:48 am - I would move this chapter to the end, because you don't need it to  get the first "hello world" python product.
+
+    % Anonymous User - Jan. 29, 2003 8:59 am:
+     (After rading this chapter)Is it ZopeDG or PythonDG???
+
+    % Anonymous User - Nov. 5, 2003 2:59 pm:
+     This user comment thing is good. But what would be nice is a user "moderation" which can optionally provide
+     some users with ability to filter some comments off, so we can get on with the document.
+     Still, this whole thing is cool. Cant believe I have heard of Zope all these days and never gave it a shot
+     thinking
+     its nothing but another hype :(
+     Sorry !
+
+    % Anonymous User - Nov. 23, 2003 10:20 am:
+     Maybe you can get a first tutorial about how to write a very minimal product at:
+     http://www.zope.org/Members/maxm/HowTo/minimal_01/
+     Don't know if it helps. I'm just going through it.
+
+    % Anonymous User - June 8, 2004 11:29 am:
+     just another comment!
+     These comments are usefull?
+     Karluz
+
+  Zope Components
+
+    Components are objects that are associated with interfaces.  An
+    interface is a Python object that describes how you work with
+    other Python objects.  In this chapter, you'll see some simple
+    examples of creating components, and a description of interfaces
+    and how they work.
+
+      % Anonymous User - Feb. 27, 2003 6:20 am:
+       werwer
+
+      % Anonymous User - Apr. 26, 2004 7:27 am:
+       Привет!
+
+      % Anonymous User - Aug. 25, 2004 12:01 pm:
+       Norok tavarisch !
+
+      % Anonymous User - Sep. 12, 2005 4:54 pm:
+       111
+
+    Here is a very simple component that says hello.  Like all components,
+    this one generally consists of two pieces, an interface, and an
+    implementation::
+
+      from Interface import Base
+
+      class Hello(Base):
+          """ The Hello interface provides greetings. """
+
+          def hello(self, name):
+              """ Say hello to the name """
+
+      class HelloComponent:
+
+          __implements__ = Hello
+
+          def hello(self, name):
+              return "hello %s!" % name
+
+      % Chris Gray - Aug. 23, 2002 12:44 pm:
+       This information and these sorts of examples as presented here by themselves are all but useless to someone
+       very new to Zope development. Where do these classes go? How do I install them in Zope? How do you
+       instantiate the classes? How do you access the instantiations? How can I experiment with this code? You seem
+       to be presenting all the high-level information without any practical information on how you actually put it
+       to use. The equivalent would be a beginner's book on Python that assumed you would read the source code for
+       the interpreter to discover how to invoke it. It seems to me there is a large gap between what you would
+       learn from the Zope Book and what you need to make use of the information presented here.
+
+      % Anonymous User - Oct. 3, 2002 5:15 am:
+       I must say I have to agree with Chris.
+       A few days ago I picked up a PHP book, and in 20 minutes I was up and running with my first PHP code
+       snippets.
+       I'm keen to choose Zope because I've been using Python for while but all this documentation is so nebulous!
+       I've been reading for days and am still totally vague as to how to get even something elementary happening.
+       I know there are fabulous people behind Zope and Python. I wish they could hear comments such as those of
+       Chris'.
+
+      % Anonymous User - Oct. 4, 2002 12:29 pm:
+       I'm sure you were up and running quickly with PHP. But it's likely that you could have been up and running in
+       Zope as well in the same amount of time by reading the Zope Book. My advice is to not read this guide until
+       you've thoroughly read the Zope Book and done a little Zope development.
+       Zope is loosely the equivalent of PHP, Apache, and Oracle wrapped up into a single piece of software, so it
+       makes sense that it would take more time to grok than straight PHP. That said, there is a gap between the
+       Zope Book and this guide. It's likely that that gap will need to be filled by 3rd-party published books.
+
+      % Anonymous User - Nov. 11, 2002 3:39 am:
+       New developers can be attracted to hacking code, by allowing them to see the key points of technology are.
+       With this example as many other ones, -> zope book, web,
+       there is alot of information missing, as one can learn from inferred data from other places. This just sucks.
+       I love python, because it is consice, to the point,
+       with this stuff, its just superfrustrating. very many people i know gave up, because they wanted all the
+       details, and all they were given is a gobs of vague ideas, and some code that did not relate, as in between
+       examples, so it was hard to make out what the hell is going on.
+
+      % Anonymous User - Dec. 3, 2002 1:06 am:
+       The COM OFF button rules.
+
+      % exdesign - Jan. 9, 2003 10:31 pm:
+       Just opened __init__.py in /lib/python/Interface for Zope 2.6.0. It says:
+       <begin quote from /lib/python/Interface/__init__.py>
+       This package implements the Python "scarecrow" proposal.
+       The package exports a single name, 'Interface' directly. Interface
+       is used to create an interface with a class statement, as in:
+         from Interface import Interface
+         class IMyInterface(Interface):
+           '''Interface documentation
+           '''
+           def meth(arg1, arg2):
+               '''Documentation for meth
+               '''
+           # Note that there is no self argument
+       To find out what you can do with interfaces, see the interface
+       interface, IInterface in the IInterface module.
+       <snip>
+       Revision information:
+       $Id: __init__.py,v 1.9 2002/08/14 21:35:32 mj Exp $
+       """
+       from _Interface import Interface
+       from Attribute import Attribute
+       Base = Interface # XXX We need to stamp out Base usage
+       <end>
+       From this I infer that this page is not only nebulous, but very much out of date; both the self arguments and
+       the use of the Base term are deprecated.
+
+      % Anonymous User - May 4, 2003 5:59 pm:
+       Zope documentation started out opaque and it's only gotten foggier. Perhaps comments could be indented, small
+       or on an extra column
+
+      % Anonymous User - Aug. 13, 2003 4:31 am:
+       Is this standard Python or a Zope extension to Python? Can't find anything related to Interface on
+       www.python.org.
+
+      % Anonymous User - Aug. 15, 2003 8:04 pm:
+       I concur. I love Python for its economy and eloquence and would therefore love to use Zope, but the
+       documentation needs to get better. Just spent 4 hours poring over it and still do not understand how I would
+       start to structure my data for the company website I would like to use as a testbed. The Zope Book spends to
+       much time on basics and building blocks, without at single complete example. Where is the well documented and
+       commented 'pet store' style app? All that comes with Zope is a one page shopping cart.
+
+    Let's take a look at this step by step.  Here, you see two Python class
+    statements.  The first statement creates the *interface*, and the
+    second statement creates the *implementation*.
+
+      % Anonymous User - Dec. 5, 2001 6:46 am - In scripts you only use instances of the implementation class, in this case HelloComponent. Perhaps it would be good for educational purposes to add an __init__ as well to HelloComponent. Or refer to chapter 3 for the use.
+
+      % Anonymous User - Dec. 12, 2001 2:57 pm - Not comment really, but question.   Where I find the Interface module. I can't with myself. Not at my filesystem nor at my ZOPE. I'd  like to do practice also with this tutorial.
+
+      % Anonymous User - Dec. 13, 2001 10:18 am - {ZOPEHOME}/lib/python/Interface
+
+      % jshell - Jan. 24, 2002 12:56 pm - This should all be updated to reflect where interfaces are going for Zope 3.  First, the interface should be named "IHello", and there should be no 'self' in the signature for the 'hello()' method.
+
+      % Anonymous User - July 11, 2002 12:44 pm:
+       Where can I find more info regarding Zope 3? I realize that it is a work-in-progress.
+
+      % Anonymous User - Aug. 8, 2002 11:58 pm:
+       Get rid of the 'self' param/sig?.., yeh! This is one thing I've found distasteful in my wonderings/wanderings
+       thru zope. Now lets consider the double underscores. Javascript seems more elegant/simple and widespread, imo
+       - it WILL be on the client...why learn both? I'd use zope 'for real' if that were the case.
+
+      % Anonymous User - Sep. 13, 2002 7:12 pm:
+       The reason for 'self' is so that one can distinguish between class and instance
+       methods in Python.  Unlike Java, Python does not have a 'static' keyword to
+       describe class methods.
+
+    The first class statement creates the 'Hello' interface.  This
+    interface describes one method, called 'hello'.  Notice that there is
+    no implementation for this method, interfaces do not define behavior,
+    they just describe a specification.
+
+    The second 'class' statement creates the 'HelloComponent' class.
+    This class is the actual component that *does* what 'Hello'
+    *describes*.  This is usualy referred to as the *implementation*
+    of 'Hello'.  In order for you to know what interfaces
+    'HelloComponent' implements, it must somehow associate itself with
+    an interface. The '__implements__' class attribute does just
+    that. It says, "I implement these interfaces".  In this case,
+    'HelloComponent' asserts that it implements one interface,
+    'Hello'.
+
+      % Anonymous User - Mar. 27, 2002 11:48 am - usualy -> usually
+
+    The interface describes how you would work with the object, but it
+    doesn't dictate how that description is implemented. For example,
+    here's a more complex implementation of the 'Hello' interface::
+
+      import xmlrpclib
+      class XMLRPCHello:
+
+          __implements__ = Hello
+
+          def hello(self, name):
+              """
+              Delegates the hello call to a remote object using XML-RPC.
+              """
+              s = xmlrpclib.Server('http://www.zope.org/')
+              return s.hello(name)
+
+    This component contacts a remote server and gets its hello
+    greeting from a remote component.
+
+    And that's all there is to components, really.  The rest of this
+    chapter describes interfaces and how you can work with them from
+    the perspective of components.  In Chapter 3, we'll put all this
+    together into a Zope product.
+
+  Python Interfaces
+
+    Interface describe the behavior of an object by containing useful
+    information about the object.  This information includes:
+
+      o Prose documentation about the object.  In Python terms, this is
+        called the "doc string" of the interface.  In this element, you
+        describe how the object works in prose language and any other
+        useful information about the object.
+
+      o Descriptions of attributes.  Attribute descriptions include the
+        name of the attribute and prose documentation describing the
+        attributes usage.
+
+      o Descriptions of methods.  Method descriptions can include:
+
+        o Prose "doc string" documentation about the method and its usage.
+
+        o A sequence of parameter objects that describes the parameters
+          expected by the method.
+
+      o Optional tagged data.  Interface objects (and their
+        attributes, methods, and method parameters) can have optional,
+        application specific tagged data associated with them.
+        Examples uses for this are security assertions, pre/post
+        conditions, unit tests, and other possible information you may
+        want to associate with an Interface or its attributes.
+
+      % tibi - Feb. 22, 2002 5:51 am - Maybe put a link to the corresponding PEP on Python.org and say this interface thing could be integrated into Python 2.3.
+
+      % Anonymous User - Apr. 12, 2005 5:34 pm:
+       For those new to interfaces: If an object is a 3D cube then its interfaces would be the 6 external 2D faces
+       sorrounding it. Interfaces are the "view" of the object, what the other objects see about it. The inner cube
+       can change but if the 6 sorrounding faces keep untouched the rest of the cubes (objects) will not need to
+       change anything. In practice is common practice to desing the views (what we want the others to expect from
+       our object) and then to implement the object itself restricted to its interfaces. Well designed interfaces
+       try to be orthogonal (a face will provide the access to the object state, another face will provide the
+       security of the object, another one the persistence of the object, another its graphical representation in
+       the screen, ...) and the different objects will try to implement the defined set of interfaces as requested.
+
+      % Anonymous User - Apr. 12, 2005 5:55 pm:
+       For inte. newbies: If an object is a 3D cube then its interfaces would be the 6 external 2D faces. Ifaces.
+       are the cube "view", what the other objects can see/touch. If the cube's inners change keeping constant the
+       sorrounding Ifaces the other cubes/objects will need no change. In practice Ifaces are designed first in the
+       time line (what we want our object to look lie), then commes the object 3D implementation. Well designed
+       interfaces try to be orthogonal (one face shows the cube state, another treats the security, another shows
+       its visual representation in the screen, ...). Different cubes/objects will try to implement the defined set
+       of interfaces as requested.
+
+    Not all of this information is mandatory.  For example, you may only
+    want the methods of your interface to have prose documentation and not
+    describe the arguments of the method in exact detail.  Interface
+    objects are flexible and let you give or take any of these
+    components.
+
+      % Anonymous User - Oct. 16, 2001 10:29 am - The current Interface implementation as it ships with Zope 2.4 + does not support non-function attributes in interfaces.
+
+  Why Use Interfaces?
+
+    Interfaces solve a number of problems that arise while developing
+    large systems with lots of developers.
+
+      o Developers waste a lot of time looking at the source code of your
+        system to figure out how objects work.  This is even worse if
+        someone else has already wasted their time doing the same thing.
+
+      o Developers who are new to your system may misunderstand how your
+        object works, causing, and possibly propagating, usage errors.
+
+      o Because an object's interface is inferred from the source,
+        developers may end up using methods and attributes that are meant
+        for "internal use only".
+
+      o Code inspection can be hard, and very discouraging to novice
+        programmers trying to understand code written by gurus.
+
+    Interfaces try to solve these problems by providing a way for you to
+    describe how to use an object, and a mechanism for discovering that
+    description.
+
+      % Anonymous User - Jan. 25, 2005 3:04 pm:
+       Forget novices, reading code that implements interfaces turns beginers' brains into mush.
+
+  Creating Interfaces                                       
+
+    The first step to creating a component, as you've been shown, is
+    to create an interface.
+
+      % Anonymous User - Jan. 11, 2002 3:40 pm - I'm confused as to why CORBA IDL was not mentioned/used here.  I am sure it was considered, but it would be helpful to mention why the technology was considered unsuitable in this case.  Thanks.
+
+    Interface objects can be conveniently constructed using the Python
+    'class' statement.  Keep in mind that this syntax can be a little
+    misleading, because interfaces are *not* classes.  It is important to
+    understand that using Python's class syntax is just a convenience, and
+    that the resulting object is an *interface*, not a class.
+
+      % Anonymous User - Aug. 7, 2002 10:45 pm:
+       You might want to point out that interfaces are also known as Abstract Base Classes. This fits in better with
+       the python view of things, and you do mention "concrete classes" down the road.
+       You also might want to consider raising NotImplementedError exceptions in the Interface.
+
+      % Anonymous User - Apr. 12, 2005 5:49 pm:
+       I think the "Abstract Base Classes" is missliding since it hides the purpose of interfaces. "Base Classes"
+       has more to do with non interface aware language (Python/C++). I think "Abtract Base Classes" is a the way in
+       which they implement Interfaces and not the opposite. Of course Python has classes and not Interfaces, but
+       that's a lack in the language that pythoniers are trying to solve in an elegant way. (Dont' get me wrogn,
+       Python continues to be my favourite language with or without interfaces)
+
+    To create an interface object using Python's class syntax, create a
+    Python class that subclasses from 'Interface.Base'::
+
+      from Interface import Base
+
+      class Hello(Base):
+
+          def hello(self, name):
+              """ Say hello to the world """
+
+      % Anonymous User - Oct. 25, 2002 6:00 pm:
+       'from Interface import Base' SHOULD BE 'from Interface import Interface'. From the __init__.py file of the
+       Interface package: Base = Interface # XXX We need to stamp out Base usage.
+
+      % Anonymous User - Jan. 5, 2003 8:59 am:
+       Could you mention that Interface.Base is a Zope module. Im a java/c++ developer trying to adopt zope as a
+       platform. I would be useful to mention that Interface.Base is zope specific and not something from the python
+       relm.
+
+      % Anonymous User - Jan. 5, 2003 9:01 am:
+       it's me again. the Word interface is used to describe constructs in some languages (like Java and virutal
+       classes in C++) What could help here is a pointer back to the API guide for Interface.
+
+    This interface does not implement behavior for its methods, it
+    just describes an interface that a typical "Hello" object would
+    realize.  By subclassing the 'Interface.Base' interface, the
+    resulting object 'Hello' is an interface object. The Python
+    interpreter confirms this::
+
+      >>> Hello
+      <Interface Hello at 812cbd4>
+
+    Now, you can associate the 'Hello' Interface with your new,
+    concrete class in which you define your user behavior. For
+    example::
+
+      class HelloComponent:
+
+          __implements__ = Hello
+
+          def hello(self, name):
+              return "Hello %s!" % name
+
+    This new class, 'HelloComponent' is a concrete class that
+    implements the 'Hello' interface.  A class can realize more than one
+    interface.  For example, say you had an interface called 'Item' that
+    described how an object worked as an item in a "Container" object.  If
+    you wanted to assert that 'HelloComponent' instances realized the
+    'Item' interface as well as 'Hello', you can provide a sequence of
+    Interface objects to the 'HelloComponent' class::
+
+      class HelloComponent:
+
+          __implements__ = Hello, Item
+
+    This '__implements__' attribute is called an *interface assertion*.  An
+    interface assertion can be either an interface, or a sequence of
+    interface assertions. Here's a more complex example::
+
+      class Sandwich:
+
+          __implements__ = (Food, (Nourishing, Delicious), (GetsStaleQuickly, 
+                           (EdibleWithHands, GoodForLunch)))
+
+      % Anonymous User - Jan. 5, 2005 9:44 am:
+       I understood the first example given where the class was implementing the methods of two abstract classes. 
+       e.g. 
+       class Sandwich:
+           __implements__= Food, GetsStaleQuickly
+       however i do not understand what is meant by the different groupings with parenthesis. Are these used to
+       imply a hierarchy in the relationships between the different classes??
+       So can you explain whats going on in the line:
+       __implements__ = (Food, (Nourishing, Delicious), (GetsStaleQuickly, 
+                            (EdibleWithHands, GoodForLunch)))
+       Thanks
+       Tom.
+
+      % Anonymous User - Jan. 5, 2005 10:01 am:
+       I have been looking more at this line of code.
+       Am I right in assuming that it is stating for example that it is implementing methods described in Food, also
+       implementing methods described in subclasses of Food :- Nourishing and Delicious etc...
+       Thank 
+       Tom.
+
+    Interface assertions allow complex nesting of interfaces. This is
+    mostly useful when you wish to assert that your class implements
+    some specific interfaces, along with whatever interfaces your base
+    class implements::
+
+      class Sandwich(Food):
+
+          __implements__ = (EdibleWithHands, GoodForLunch, Food.__implements__)
+
+     Take care before you assert that your class implements the
+     interfaces of your base classes.
+
+       % Anonymous User - Feb. 4, 2002 12:51 pm - What about attributes of a class? Is it possible to describe attributes or  properties of a class instead of methods only?
+
+       % Anonymous User - May 21, 2002 12:11 pm:
+        I dont know! Languages like C++ allow you to define attributes and methods as public, however even then it is
+        usually considered to be good practice to write accessor functions and methods rather than to make the
+        attributes public.
+
+       % Anonymous User - May 29, 2002 11:29 am:
+        it seems 'interface' is the *public* part of the class *definition*, right ?
+
+  The Interface Model
+
+    Interfaces can extend other interfaces.  For example, let's extend the
+    'Hello' interface by adding an additional method::
+
+      class SmartHello(Hello):
+          """  A Hello object that remembers who it's greeted """
+
+          def lastGreeted(self):
+              """ Returns the name of the last person greeted. """
+
+      % Anonymous User - Apr. 12, 2005 6:10 pm:
+       I don't think interface inheritance is a good practice. Interfaces try to declare realm behaviour so it's
+       confusing (maybe even wrong) to say iface1 declares this behaviour for object implementing it while iface2
+       will declares the same behaviour ¿and a bit more?. Inherintance is risky for classes and for interfaces can
+       be deadly.
+
+    'SmartHello' extends the 'Hello' interface.  It does this by using the
+    same syntax a class would use to subclass another class.
+
+      % eradan - Mar. 4, 2002 7:12 am - You declared: "from Interface import Hello"; did you?
+
+      % Anonymous User - Apr. 12, 2005 6:16 pm:
+       better than
+        iface1: Hello
+        iface1 <- iface2:lastGreeted
+        object implemens iface2
+       will be:
+        iface1: Hello
+        iface2: logging
+        object implement iface1, iface2
+        (logging = lastGreeted, lastGoodbye,...)
+        ifaces must be orthogonals. Don't inherit them, danger, danger!!!
+
+    Now, you can ask the 'SmartHello' for a list of the interfaces it
+    extends with 'getBases'::
+
+      >>> SmartHello.getBases()
+      [<interface Hello at 80c72c8>]
+
+      % Anonymous User - May 29, 2002 11:32 am:
+       so, 'getBases' is a method of class *base* from 'interface' package, isn't it ?
+
+    An interface can extend any number of other interfaces, and
+    'getBases' will return that list of interfaces for you.  If you
+    want to know if 'SmartHello' extends any other interface,
+    you could call 'getBases' and search through the list, but a
+    convenience method called 'extends' is provided that returns true
+    or false for this purpose::
+
+      >>> SmartHello.extends(Hello)
+      1
+      >>> SmartHello.extends(Sandwich)
+      0
+      >>>
+
+    Here you can see 'extends' can be used to determine if one interface
+    extends another.
+
+    You may notice a similarity between interfaces extending from
+    other interfaces and classes sub-classing from other classes.
+    This *is* a similar concept, but the two should not be considered
+    equal.  There is no assumption that classes and interfaces exist
+    in a one to one relationship; one class may implement several
+    interfaces, and a class may not implement its base classes's
+    interfaces.
+
+      % Anonymous User - Jan. 11, 2002 9:08 am - Then why use 'class' instead of 'interface' (historical/political reasons?), especially since GvR is w Zope.com?
+
+    The distinction between a class and an interface should always be
+    kept clear.  The purpose of a class is to share the implementation
+    of how an object works.  The purpose of an interface is to
+    document how to work *with* an object, not how the object is
+    implemented.  It is possible to have several different classes
+    with very different implementations realize the same interface.
+    Because of this, interfaces and classes should never be confused.
+
+      % Anonymous User - May 29, 2002 11:35 am:
+       ok, so an interface is actually a class in the python meanings, but not in the Zope meaning, since it has not
+       implicit implementation.. right ?
+
+      % Anonymous User - May 29, 2002 11:37 am:
+       well, you mean one can implement an interface without extending the actual classes tha implements this
+       interface's parents & grand-parents.. is it ?
+
+  Querying an Interface
+
+    % Anonymous User - Nov. 3, 2001 7:58 am - Note that there should be a description of where the Interface package actually lives so people can try it interactively.
+
+    Interfaces can be queried for information.
+    The simplest case is to ask an interface the names of all
+    the various interface items it describes.  From the Python interpreter,
+    for example, you can walk right up to an interface and ask it for its
+    *names*::
+
+      >>> User.names()
+      ['getUserName', 'getFavoriteColor', 'getPassword']
+
+      % Anonymous User - Nov. 18, 2002 11:39 pm:
+       Where did User come from? This might be better using Hello, or SmartHello.
+
+      % Anonymous User - May 8, 2004 6:59 am:
+       I think the following line is missing:
+       from AccessControl import User
+       But I can't access User.names() anyway...
+
+      % Anonymous User - Jan. 25, 2005 3:39 pm:
+       I thought interfaces were to be prefixed with a capitol I?
+       should it not be IUser.names()?
+
+    Interfaces can also give you more interesting information about their
+    items.  Interface objects can return a list of '(name, description)'
+    tuples about their items by calling the *namesAndDescriptions* method.
+
+      % Anonymous User - Dec. 20, 2001 2:39 pm - "[...] ask an interface the names of all the various interface items it describes."    So WTF is an "item" as used in the last sentence?  An object?  A class?  A component?  A Method or Attribute?  Please use consistent terms.
+
+      % Anonymous User - Jan. 7, 2002 11:53 am - You might want to stick to the earlier introduced examples of interfaces: Hello and SmartHello. To me it was not clear that User is a interface, too.  In general, your readers might benefit even more if this chapter would be based on a single session at the python console - like Guido van Rossum did it in his Python Tutorial at www.python.org.
+
+    For example::
+
+      >>> User.namesAndDescriptions()
+      [('getUserName', <Interface.Method.Method instance at 80f38f0>),
+      ('getFavoriteColor', <Interface.Method.Method instance at 80b24f0>),
+      ('getPassword', <Interface.Method.Method instance at 80fded8>)]
+
+    As you can see, the "description" of the Interface's three items
+    in these cases are all 'Method' objects.  Description objects can
+    be either 'Attribute' or 'Method' objects.  Attributes, methods,
+    and interface objects implement the following interface::
+
+      'getName()' -- Returns the name of the object.
+
+      'getDoc()' -- Returns the documentation for the object.
+
+      % Anonymous User - May 29, 2002 11:42 am:
+       which items have implicitly these 2 methods in Hello interface ? ?
+
+    Method objects provide a way to describe rich meta-data about Python
+    methods. Method objects have the following methods:
+
+      'getSignatureInfo()' -- Returns a dictionary describing the
+      method parameters.
+
+      'getSignatureString()' -- Returns a human-readable string
+      representation of the method's signature.
+
+    For example::
+
+      >>> m=User.namesAndDescriptions()[0][1]
+      >>> m
+      <Interface.Method.Method instance at 80f38f0>
+      >>> m.getSignatureString()
+      '(fullName=1)'
+      >>> m.getSignatureInfo()   
+      {'varargs': None, 'kwargs': None, 'optional': {'fullName': 1}, 
+      'required': (), 'positional': ('fullName',)}  
+
+    You can use 'getSignatureInfo' to find out the names and types of
+    the method parameters.
+
+      % Anonymous User - May 29, 2002 11:44 am:
+       i lost my way on how to fill then access this meta-data..
+
+  Checking Implementation
+
+    You can ask an interface if a certain class or instance that you hand
+    it implements that interface.  For example, say you want to know if
+    instances of the 'HelloComponent' class implement 'Hello'::
+
+      Hello.implementedByInstancesOf(HelloComponent)
+
+      % icemac - Apr. 9, 2003 9:12 am:
+       now, as of Zope 2.6.1 it is:
+       isImplementedByInstancesOf
+
+    This is a true expression.  If you had an instance of
+    'HelloComponent', you can also ask the interface if that instance
+    implements the interface::
+
+      Hello.implementedBy(my_hello_instance)
+
+      % icemac - Apr. 9, 2003 9:13 am:
+       see above:
+       isImplementedBy
+
+    This would also return true if *my_hello_instance* was an instance of
+    *HelloComponent*, or any other class that implemented the *Hello*
+    Interface.
+
+  Conclusion
+
+    Interfaces provide a simple way to describe your Python
+    objects. By using interfaces you document your objects'
+    capabilities. As Zope becomes more component oriented, your
+    objects will fit right in.  While components and interfaces are
+    forward looking technologies, they are useful today for
+    documentation and verification.
+
+      % Anonymous User - Aug. 9, 2002 12:23 am:
+       A succinct real world example illustrating the difference between a class and interface might help. eg,
+       interface 'a' is implemented by class 'b' and 'this' is why is helps me be a better zoper.

Added: zdgbook/trunk/Credits.stx
===================================================================
--- zdgbook/trunk/Credits.stx	                        (rev 0)
+++ zdgbook/trunk/Credits.stx	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,11 @@
+Credits
+
+ - Amos Latteier
+
+ - Michel Pelletier
+
+ - Shane Hathaway
+
+ - Chris McDonough
+
+ - Beehive

Added: zdgbook/trunk/Errata.stx
===================================================================
--- zdgbook/trunk/Errata.stx	                        (rev 0)
+++ zdgbook/trunk/Errata.stx	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,104 @@
+Changes to Zope Developers Guide, Chapter 2, Object Publishing to support
+Toby's Unicode changes.
+
+* Add the following before the section 'HTTP Responses' under 'Stringifying 
+the published object'
+
+Character Encodings for Responses
+
+ If the published method returns an object of type 'string', a plain
+ 8-bit character string, the publisher will use it directly as the body of the
+ response. 
+
+ Things are different if the published method returns a unicode string,
+ because the publisher has to apply some character encoding. The published
+ method can choose which character encoding it uses by setting a
+ 'Content-Type' response header which includes a 'charset' property
+ (setting response headers is explained later in this chapter). A
+ common choice of character encoding is UTF-8. To cause the publisher
+ to send unicode results as UTF-8 you need to set a
+ 'Content-Type' header with the value 'text/html; charset=UTF-8'
+
+ If the 'Content-Type' header does not include a charser property (or if this
+ header has not been set by the published method) then the publisher will
+ choose a default character encoding. Today this default is ISO-8859-1
+ (also known as Latin-1) for compatability with old versions of Zope which
+ did not include Unicode support. At some time in the future this default
+ is likely to change to UTF-8.
+
+* Inside the section 'Argument Conversion' is a list of type conversion 
+marshalling tags. Insert the following definition of 'ustring' under 'string'
+
+ ustring
+  Converts a variable to a Python unicode string.
+
+* and insert this definition at the bottom of the list
+
+ ulines, utokens, utext
+  like lines, tokens, text, but using unicode strings instead of
+  plain strings.
+
+* Insert this section before 'Method Arguments'
+
+Character Encodings for Arguments
+
+ The publisher needs to know what character encoding was used by the browser
+ to encode form fields into the request. That depends on whether the form
+ was submitted using GET or POST (which the publisher can work out for itself)
+ and on the character encoding used by the page which contained the form
+ (for which the publisher needs your help).
+
+ In some cases you need to add a specification of the character encoding
+ to each fields type converter. The full details of how this works are
+ explained below, however most users do not need to deal with the full
+ details:
+
+ 1 If your pages all use the UTF-8 character encoding (or at least all the
+   pages that contain forms) the browsers will always use UTF-8 for
+   arguments. You need to add ':utf8' into all argument type converts. For
+   example:
+
+   <input type="text" name="name:utf8:ustring">
+   <input type="checkbox" name="numbers:list:int:utf8" value="1">
+   <input type="checkbox" name="numbers:list:int:utf8" value="1">
+
+     % Anonymous User - Apr. 6, 2004 5:56 pm:
+      121
+
+ 2 If your pages all use a character encoding which has ASCII as a subset
+   (such as Latin-1, UTF-8, etc) then you do not need to specify any
+   chatacter encoding for boolean, int, long, float, and date types.
+   You can also omit the character encoding type converter from string,
+   tokens, lines, and text types if you only need to handle ASCII characters
+   in that form field.
+
+  Character Encodings for Arguments; The Full Story
+
+   If you are not in one of those two easy categories, you first need
+   to determine which character encoding will be used by the browser to
+   encode the arguments in submitted forms.
+
+   1. Forms submitted using GET, or using POST with 
+      "application/x-www-form-urlencoded" (the default) 
+
+      1. Page uses an encoding of unicode:
+         Forms are submitted using UTF8, as required by RFC 2718 2.2.5
+
+      2. Page uses another regional 8 bit encoding:
+         Forms are often submitted using the same encoding as the
+         page. If you choose to use such an encoding then you should
+         also verify how browsers behave.
+
+   2. Forms submitted using "multipart/form-data": 
+
+      According to HTML 4.01 (section 17.13.4) browsers should state which
+      character encoding they are using for each field in a Content-Type
+      header, however this is poorly supported. The current crop of
+      browsers appear to use the same encoding as the page containing
+      the form. 
+
+   Every field needs that character encoding name appended to is converter.
+   The tag parser insists that tags must only use alphanumberic characters
+   or an underscore, so you might need to use a short form of the
+   encoding name from the Python 'encodings' library package (such
+   as utf8 rather than UTF-8).

Added: zdgbook/trunk/Figures/2-1.png
===================================================================
(Binary files differ)


Property changes on: zdgbook/trunk/Figures/2-1.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: zdgbook/trunk/Figures/2-2.png
===================================================================
(Binary files differ)


Property changes on: zdgbook/trunk/Figures/2-2.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: zdgbook/trunk/Gotchas.stx
===================================================================
--- zdgbook/trunk/Gotchas.stx	                        (rev 0)
+++ zdgbook/trunk/Gotchas.stx	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,169 @@
+Here is the place to add comments about "gotchas" related to Zope development.  We will try to work
+these in to the ZDG narrative from time to time.
+
+  % mcdonc - Jan. 17, 2002 6:23 pm - Methods declared in Product classes must
+    have docstrings to callable by visiting them "through the web" (by calling
+    them up by URL via a browser).
+
+  % Anonymous User - Feb. 27, 2002 10:52 pm - Volatile attributes are not shared
+    between threads, so using them for  page counters or holding state between 
+    requests will not work (although it will sometimes appear to until your server
+    is under load)
+
+  % Anonymous User - May 22, 2002 7:18 am:
+   DTMLMethod with a name ending '_alpha' was not rendered at all
+
+  % Anonymous User - July 17, 2002 1:40 pm:
+   From Zope Maillist:
+   On Tue, 16 Jul 2002, Ross Boylan wrote:
+   > The Zope Developer's Guide and the API docs (Zope 2.5) present
+   > different stories about how to add things to object managers.  I don't
+   > really follow what the API stuff is doing.  This is a request for
+   > clarification.
+   >
+   > Devguide says do
+   > def addFunction(dispatcher, id):
+   >    "Create object and add to self"
+   >    p = SomeProduct(id)
+   >    dispatcher.Destination()._setObject(id, p)
+   >
+   > This makes sense, though, as commented at
+   > http://www.zope.org//Members/michel/Projects/Interfaces/ObjectManager,
+   > it relies on a private, undocumented method.  addFunction is in outer
+   > scope of a module, and is hooked up wtih various calls in ___init__.py.
+   Call this the "add function".  Defined by the product, it is passed
+   the dispatcher for the ObjectManager to which the object is being added.
+   As you say, it apparently uses a private function, however despite
+   it's (historical artifact of a) name, it is the proper way to do
+   this operation.  And it is documented, in the Dev Guide <wry grin>.
+   > On the other hand, the ObjectManager API says to do
+   > self.manage_addProduct['OFSP'].manage_addFolder(id, title).
+   This is the action that non-Product code uses to add an object from
+   the Product to an arbitrary object manager.  'self' being the object
+   manager in question (eg: when called from an External Method, self
+   will be the folder the External Method was called on).  This sequence
+   *calls* the "add function" defined above.  In this case, it is
+   calling the add function defined by the Folder object in the OFSP
+   Product.
+   > First, what scope should that be done in?  Second, this seems to
+   > create one product (OFSP) and then add a folder to it, so that it
+   > creates two, nested products.
+   Actually, it doesn't.  This syntax has bothered me since I first
+   encoutered it, and I'm sure I'm not alone.  But we are stuck with
+   it (for now).  "manage_addProduct[<someproductname>]" is, essentially,
+   a lookup in a registry keyed by product name.  So that part looks
+   up the Product from the registry, and then you have access to the
+   module level functions in that Product, such as the "add function"
+   defined above.
+   > The API for Folder mentions manage_addFolder as a constructor, but
+   > again the scope is unclear to me.  Presumably it needs to know what to
+   > add it to, but that is not passed in.
+   You are calling the method on 'self' in the API example, or on some
+   other reference to an ObjectManager in some other context.
+   (Literally 'context' if you were doing this from a pythonscript.)
+   'manage_addProduct' gets "acquired" (from where I'm not quite sure
+   <wry grin>), and some magic that I haven't had a need to look in
+   to arranges things so that your add function gets passed a dispatcher
+   that has that ObjectManager as its Destination() as its first
+   argument, followed by whatever arguments (id, title) in the API
+   example) you pass it explicitly.  This is an example of Zope2
+   "magic" that we are trying hard to eliminate from Zope3 <grin>.
+   > Finally, the use of id in two places in the first code snippet (one
+   > apparently associated with the container, the other with the object)
+   > seems like an invitation to trouble.  Why is it done that way?
+   Tell us about it.  This is an old Zope2 (or was it bobo?) design
+   decision that has been revisited in Zope3.  In Zope3, only containers
+   know about ids in the general case, not the objects referenced by
+   them.
+   > One reason I want to know this is that I'm considering having a
+   > container that would automatically label (or set id) of its contents
+   > (e.g., 'a' is first, 'b' is second....).
+   >
+   > Thanks for any light you can shed.
+   Hopefully I haven't told you anything untrue up above, but if I have
+   someone will doubtless correct me <grin>.
+   --RDM
+
+  % eckamm - Nov. 11, 2002 10:05 pm:
+   This one has bit me on more than one occassion:
+       manage_add_XYZ_form = DTMLFile('dtml/manage_add_XYZ_form')
+   instead of:
+       manage_add_XYZ_form = DTMLFile('dtml/manage_add_XYZ_form', globals())
+   The error message is tough to interpret; it looks like an attempt
+   is made to find manage_add_XYZ_form.dtml in {base}/lib/python/dtml/.
+   This may send you on a wild goose chase if you're like me.
+
+  % slinkp - Mar. 21, 2003 12:53 pm:
+   i just posted this to the zope list...
+   I find "Monkey patches" a useful way to expand or modify functionality provided by
+   zope or 3rd-party products, but I just discovered something very important:
+   it's apparently NOT safe to "refresh" a MonkeyPatch-style product.
+   I have a monkeypatch that works fine until I refresh it, then I get mysterious
+   errors like "Object of type 'None' is not callable" with no indication in the
+   traceback of what object is None or even what's trying to call it!
+   I've spent hours in a debugging session trying to find out what's None and
+   came up with nothing.
+   Then i discovered that if i restart zope, everything's fine. :-P
+
+  % Anonymous User - Mar. 29, 2003 12:43 am:
+   slinkp: Yes, that's right, you should never try to refresh monkey patch
+   products. However, I haven't found the right way to spread this warning
+   so that the people who need to know it will hear it. How do you suggest
+   this be documented?
+   Shane Hathaway
+
+  % Anonymous User - Aug. 5, 2003 4:51 pm:
+   Seems obvious to some, but wansn't obvious to me:
+   ZCatalog method searchResults' sort_limit argument only works when sort_on
+   argument is present as well. A "nice to have" would be to limit unsorted
+   results coming back from the catalog as opposed to doing it after the fact.
+
+  % slinkp - Dec. 8, 2003 3:32 pm:
+   Don't confuse paths and URLs. Specifically, avoid using absolute_url(1) or path variables from REQUEST when
+   constructing paths that will be fed to restrictedTraverse() or similar. Everything works fine until virtual
+   hosting comes into play, at which point some part of your path may be meaningful to the client but not
+   correspond to any object in Zope. This can lead to much hair-tearing when your working app suddenly breaks at
+   deployment time. Solution: if you need to fiddle with paths, use '/'.join(foo.getPhysicalPath()) instead.
+   This is unaffected by virtual hosting.
+   The behavior of absolute_url(1) *may* change in zope 2.7 or later, we're still arguing about it :-)
+
+  % slinkp - Jan. 24, 2004 6:26 pm:
+   Set self._p_changed=1  *before* mutating something, not after.
+   From Steve Alexander on the zodb-dev mailing list:
+   > The reason to set _p_changed before mutating the object is because if 
+   > there is an exception raised after a mutation but before setting 
+   > _p_changed, your persistnent object might stay around in the cache. That 
+   > is, it would not be reloaded with fresh state.
+   > 
+   > Consider this example:
+   > 
+   >      def storeMany(self, keys, values):
+   >          for key, value in zip(keys, values):
+   >              self.bar[key] = value
+   >          self._p_changed = 1  # This is too late!
+   > 
+   > What happens if I call the method as below?
+   > 
+   >   obj.storeMany(['fish', {}, 'fluid'], [6, 28, 496])
+   > 
+   > The method will raise a TypeError because dicts are unhashable. However, 
+   > the entry 'fish': 6 will have been added to the dict. The ZODB will not 
+   > realize that the object has effectively changed, so the object can stick 
+   > around in the cache.
+   Personally, I now prefer to avoid directly touching self._p_changed when possible: 
+   1) as the zdg says, it isn't necessary for immutables.
+   2) for mutables, many cases can be covered by BTrees, PersistentLists, and 
+   PersistentMappings which are very kindly provided by ZODB, so we should
+   use them :-)
+
+  % slinkp - Apr. 18, 2005 9:59 am:
+   ComputedAttributes are not documented by the ZDG, and they should be.
+
+  % slinkp - Apr. 18, 2005 10:32 am:
+   I just added some ComputedAttribute doc at http://zopewiki.org/ComputedAttribute,
+   feel free to copy this for next version of ZDG.
+
+  % Anonymous User - Aug. 19, 2005 6:24 pm:
+   Make sure that your classes inherit from Acquisition.Implicit or Acquisition.Explicit. In mid-development I
+   thought that was probably unnecessary and deleted that base class, which later caused objects I added to not
+   acquire the security information needed, leaving me unable to manage those objects.

Added: zdgbook/trunk/Introduction.stx
===================================================================
--- zdgbook/trunk/Introduction.stx	                        (rev 0)
+++ zdgbook/trunk/Introduction.stx	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,244 @@
+Introduction
+
+  Zope is an open source web application framework.  It has three
+  distinct audiences:
+
+    % chrism - Dec. 10, 2001 11:02 am - Note that folks are encouraged to make
+      comments using the comment system.  Thanks!
+
+    *Site Managers* -- individuals who use of Zope's "out of the box" features
+     to build websites.  This audience is interested in making use
+     of Zope's existing array of features to create content
+     management solutions.  They will likely make heavy use of
+     "through the web" scripting using DTML, Page Templates, and
+     Python Scripts as well as (of course) HTML and XML.  They are
+     generally less concerned about code reuse than the speed with
+     which they can create a custom application or website.
+
+    *Developers* -- individuals who wish to extend Zope to create
+     highly customized solutions.  This audience is likely interested
+     in creating highly reusable custom code that makes Zope do something
+     new and interesting.
+
+    *Administrators* -- individuals responsible for keeping a Zope
+     site running and performing installations and upgrades.
+
+    % Anonymous User - Sep. 10, 2002 8:09 pm:
+     I really like the idea of a downloadable PDF. However the header on every 
+     page is "Introduction" (same in the Zope Book). This doesn't give you much
+     clue which chapter you are on and can be confusing.
+     I know that HTMLDOC supports putting the chapter name in the header. Can 
+     you do this instead?
+
+    % Anonymous User - Jan. 10, 2003 4:05 am:
+     This system is amazing. I like Python a lot!
+
+    % Anonymous User - Feb. 24, 2003 7:02 pm:
+     About the "Comment" images.
+     They have a thin black border around them (which is probably not what you 
+     want) but not in Mozilla or the other popular browser.
+     If you change 'border="off"' to 'border="0"' in the HTML then the border 
+     disappears.
+     The border, though deprecated, is supposed to be an integer.
+     I know that this is a very picky point but it is not a really good 
+     introduction to Zope.
+
+    % Anonymous User - Apr. 9, 2003 9:51 am:
+     Is it possible to mention somewhere in the menu of this page that this 
+     beautifull functionnality that allow the user to include comments directly
+     on this page to maintain and enrich the contents is achieved with a Zope
+     product called "BackTalk" (with a link to the corresponding page). Perhaps
+     it is written somewhere but I've not been able to find it.
+
+    % Anonymous User - Apr. 9, 2003 2:19 pm:
+     See http://backtalk.sourceforge.net for the latest version...
+
+    % Anonymous User - May 4, 2003 5:37 pm:
+     I like commentable sites and even tried to design one once. But the comments 
+     here are too distracting, too large and generally annoying.
+     They should either be restricted to areas that are fuzzy and uncertain or 
+     they should be really small and unnoticable.
+
+    % Anonymous User - Sep. 23, 2003 8:04 am:
+     To add a comment please go to the top of any page and ascertain by clicking 
+     on the COM button whether is is ON or OFF. Make sure it is OFF (Figure that 
+     one out!).
+     Go to the first available comment and then scroll to the far right. You will 
+     find a small button in the lower right corner which, when clicked, adds a 
+     "Comment" window at the bottom of the page.
+     Add your comment.
+
+    % Anonymous User - Nov. 8, 2003 2:39 pm:
+     Lots of the comments are impossible to read on a small (laptop) screen, they 
+     extend beyond the browser border and there are no scroll bar below.
+     (of course you can copy/paste it, but that's not really a solution ;))
+     This i is actually a general problem with the zope site. The text width is
+     fixed, it doesnt wrap around.
+
+    % Anonymous User - Mar. 24, 2004 10:03 am:
+     ... and folks should be encouraged to read them and take note and clear them down because otherwise they clog
+     the place up. There are more comments here than original text.
+
+  This guide is intended to document Zope for the second audience,
+  developers, as defined above.  If you fit more into the "user"
+  audience defined above, you'll probably want to start by reading
+  "The Zope Book":http://www.zope.org/Members/michel/ZB/ .  If
+  you fit more into the "administrator" audience defined above,
+  you'll likely be interested in "The Zope Administrator's
+  Guide":http://www.zope.org/DocProjects/AdminGuide, although it
+  is currently unfinished.
+
+    % Anonymous User - Oct. 23, 2003 10:19 pm:
+     Then why don't you come up with the examples and contribute them?
+
+    % Anonymous User - Oct. 26, 2003 2:54 am:
+     Is there a cross reference system for all this literature somewhere? A system
+     in which I look up say "tal:content" and find links to appropriate places in
+     The Zope Book, How-tos, ZDG, etc.?
+     Tom Snell
+
+    % Anonymous User - Nov. 29, 2003 6:40 pm:
+     Great. So in order to create a web page with this, we now need YET another programming language on a system,
+     and need to learn YET ANOTHER scripting language.
+     Why isn't Python used in more places than - say - Perl, PHP, Java, C, or C++? In short, Python Blows Monkey
+     Chunks. I don't want to use a lisp-like language with some Perl and C functionality thrown every now and then
+     in what they perceive as "objects".
+     Python, Perl, and other scripting languages should be thrown back into the dark abysmal holes from which
+     their prototyped shells came from. Who in their right mind is going to use a Python-based web -APPLICATION-
+     server on their box? And pay for it?
+     The only reason people are using this is because no one is paying for it. And if they DID have to pay for it,
+     they wouldn't buy it. Web application developers are mainly looking for ways to get to market quickly, and
+     looking for a larger ROI. Now, we're forced to learn another language, another convoluted, mish-mashed way of
+     putting something together, and hoping and praying to God that our code works, and works every time. EVERY
+     time.
+     Products like these are only free if your time has no value. This goes for ANY Open Source software in
+     general. There's no throat to choke when something breaks. You hope and pray that someone will fix the
+     problem - or at least post a work-around - when you encounter one. I wish all of you luck in your pursuits
+     with Zope.
+
+    % Anonymous User - Dec. 18, 2003 1:05 pm:
+     I'm not 100% sharing your point of view, but you've got a point : using Zope means learning 2 news languages
+     for Zope-newbie : python and dtml.
+     Why not using something more common - more user friendly? Python looks like COBOL.
+
+    % Anonymous User - Dec. 18, 2003 8:52 pm:
+     Sigh.  Python looks as much like COBOL as you look like you know what you're talking about.
+
+    % Anonymous User - Mar. 5, 2004 1:40 pm:
+     Useless comments just clog up space. I like reading these comments, but only if they are useful. Criticizing
+     Zope, Python, and Open-Source in general is not useful. If you don't like it, don't read it.
+
+    % Anonymous User - Mar. 5, 2004 1:59 pm:
+     Folks, the only proper response to a troll is *THWACK*!
+
+    % Anonymous User - June 29, 2005 11:59 pm:
+     "Comment" feature is too much flexibillty... to allow user to comment and clog up the webspace.. and distract
+     users from the actual text.. Even this comment .. oops
+
+  Throughout this guide, it is assumed that you know how to program
+  in the "Python":http://www.python.org/ programming language.  Most
+  of the examples in this guide will be in Python.  There are a number
+  of great resources and books for learning Python; the best online
+  resource is the "Python.org web site":http://www.python.org/ and
+  many books can be found on the shelves of your local bookstore.
+
+    % Anonymous User - Dec. 16, 2003 12:37 pm:
+     Test Comment
+
+    % Anonymous User - Apr. 12, 2004 1:19 am:
+     This comment thing is rather distracting and leaves things open for abuse.
+
+    % Anonymous User - May 26, 2004 7:33 am:
+     I agree completely. The comments don't belong here. At least they should be appended to the very bottom of
+     the page like they do in MySQL user guide.
+
+    % Anonymous User - July 17, 2004 11:44 am:
+     There's a button at the top and bottom of each page to turn comments on and off.
+     Those of you who don't like the comments might consider clicking on it.
+
+    % Anonymous User - July 17, 2004 11:49 am:
+     There's a button at the top and bottom of each page to turn comments on and off.
+     Those of you who don't like the comments might consider clicking on it.
+
+    % Anonymous User - July 17, 2004 11:53 am:
+     There's a button at the top and bottom of each page to turn comments on and off.
+     Those of you who don't like the comments might consider clicking on it.
+
+    % Anonymous User - Aug. 19, 2004 12:20 am:
+     Python is gaining popularity.
+
+    % Anonymous User - Nov. 18, 2004 5:19 am:
+     testing
+
+    % iacaqa - Nov. 18, 2004 2:19 pm:
+     <a href='http://www.hongjuda.com' target='_blank' title='北京网站建设'>北京网站建设</a>
+     <a href="http://www.hongjuda.com" title="北京网站建设">北京网站建设</a>
+     <a href="http://www.hongjuda.com" target="_blank">北京网站建设</a>
+     <a href="http://www.hongjuda.com">北京网站建设</a>
+
+    % Anonymous User - Aug. 21, 2005 2:51 am:
+     http://www.paper-translation.com
+
+    % Anonymous User - Aug. 21, 2005 2:56 am:
+     http://www.paper-translation.com
+
+    % Anonymous User - Aug. 21, 2005 2:59 am:
+     http://www.paper-translation.com
+
+  This book describes Zope's services to the developer from a
+  hands on, example-oriented standpoint.  This book is not a complete
+  reference to the Zope API, but rather a practical guide to applying
+  Zope's services to develop and deploy your own web applications.
+  This book covers the following topics:
+
+    *Components and Interfaces* -- Zope is moving toward a
+    component-centric development model.  This chapter describes the
+    new component model in Zope and how Zope components are described
+    through interfaces.
+
+    *Object Publishing* -- Developing applications for Zope involves
+    more than just creating a component, that component must be
+    *publishable* on the web.  This chapter describes publication, and
+    how your components need to be designed to be published.
+
+    *Zope Products* -- New Zope components are distributed and installed
+    in packages called "Products".  This chapter explains Products in
+    detail.
+
+    *Persistent Components* -- Zope provides a built-in, transparent
+    Python object database called ZODB.  This chapter describes how to
+    create persistent components, and how they work in conjunction
+    with the ZODB.
+
+    *Acquisition* -- Zope relies heavily on a dynamic technique called
+    acquisition. This chapter explores acquisition thoroughly.
+
+    *Security* -- When your component is used by many different people
+    through the web, security becomes a big concern.  This chapter
+    describes Zope's security API and how you can use it to make
+    security assertions about your object.
+
+    *Debugging and Testing* -- Zope has built in debugging and testing
+    support.  This chapter describes these facilities and how you can
+    debug and test your components.
+
+    % Anonymous User - Apr. 28, 2002 11:22 pm:
+     Are ZClasses to be deprecated as a development tool?
+     It seems to be no longer included as a development option.
+
+    % mcdonc - May 30, 2002 11:06 am:
+     ZClasses are documented in the Zope Book.
+
+    % Anonymous User - Dec. 18, 2003 1:06 pm:
+     Do we have to BUY a book in order to have help on a FREE application?
+
+    % Anonymous User - Dec. 18, 2003 8:51 pm:
+     #1, even if you did that shouldn't surprise or offend you, but #2, no it's available online at http://www.zope.org/Documentation/Books/ZopeBook
+     (or if you just look in the left hand list of links, it's the one under Documentation that says "The Zope
+     Book"). Gotta love indignance.
+
+    % Anonymous User - Dec. 15, 2004 12:40 pm:
+     asdf
+
+    % Anonymous User - Sep. 22, 2005 3:57 am:
+     uyiuuub jnb kmdfs dfs dfkmgg gsdfpoew efr  ef ss  s a adasdasd

Added: zdgbook/trunk/Makefile
===================================================================
--- zdgbook/trunk/Makefile	                        (rev 0)
+++ zdgbook/trunk/Makefile	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,75 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+
+.PHONY: help clean html web pickle htmlhelp latex changes linkcheck
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html      to make standalone HTML files"
+	@echo "  pickle    to make pickle files"
+	@echo "  json      to make JSON files"
+	@echo "  htmlhelp  to make HTML files and a HTML help project"
+	@echo "  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  changes   to make an overview over all changed/added/deprecated items"
+	@echo "  linkcheck to check all external links for integrity"
+
+clean:
+	-rm -rf build/*
+
+html:
+	mkdir -p build/html build/doctrees
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) build/html
+	@echo
+	@echo "Build finished. The HTML pages are in build/html."
+
+pickle:
+	mkdir -p build/pickle build/doctrees
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) build/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+web: pickle
+
+json:
+	mkdir -p build/json build/doctrees
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) build/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	mkdir -p build/htmlhelp build/doctrees
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) build/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in build/htmlhelp."
+
+latex:
+	mkdir -p build/latex build/doctrees
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) build/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in build/latex."
+	@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+	      "run these through (pdf)latex."
+
+changes:
+	mkdir -p build/changes build/doctrees
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) build/changes
+	@echo
+	@echo "The overview file is in build/changes."
+
+linkcheck:
+	mkdir -p build/linkcheck build/doctrees
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) build/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in build/linkcheck/output.txt."

Added: zdgbook/trunk/ObjectPublishing.stx
===================================================================
--- zdgbook/trunk/ObjectPublishing.stx	                        (rev 0)
+++ zdgbook/trunk/ObjectPublishing.stx	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,1623 @@
+Chapter 2: Object Publishing
+
+  Introduction
+
+    Zope puts your objects on the web. This is called *object
+    publishing*. One of Zope's unique characteristics is the way it
+    allows you to walk up to your objects and call methods on them
+    with simple URLs.  In addition to HTTP, Zope makes your objects
+    available to other network protocols including FTP, WebDAV and
+    XML-RPC.
+
+      % Anonymous User - Feb. 1, 2005 9:05 am:
+       This article should be very informative. But it is really confusing with a lot of useful, useless,
+       contradicting comments. Please some one, who is capable enough or the author should make an effort of
+       rewriting this.
+
+      % Anonymous User - Mar. 3, 2005 7:40 am:
+       has anyone else noticed how many useless comments get saved here for ever and ever?
+
+      % Anonymous User - Mar. 4, 2005 11:49 pm:
+       I want to use Zope as a XML-RPC server on Windows 2003 with minimal configuration. Thogh installation is
+       easy, I am totally lost and do not know how to put my Python class/method exposed to client via Zope
+       (External methods?). Google keywords "Zope XML RPC" does not get enough clues. Many articles just tell you
+       Zope supports it and then continue the coding stuff. I think it's better create a Quick Start region for Zope
+       document.
+
+      % Anonymous User - Mar. 4, 2005 11:57 pm:
+       More on the organization of the document and comments. Though comments are useful addition information if
+       someone is dedicate to curate them (like I have seen in MySQL document), in most case, they just interrupt
+       the understanding of the documents. I suggest create a single forum like many J2EE open source project
+       (JBoss, Spring, Hibernate). I am a novice in Zope and really need some useful help online.
+
+    In this chapter you'll find out exactly how Zope publishes
+    objects. You'll learn all you need to know in order to design your
+    objects for web publishing.
+
+ 
+  HTTP Publishing
+
+    When you contact Zope with a web browser, your browser sends an
+    HTTP request to Zope's web server. After the request is completely
+    received, it is processed by 'ZPublisher', which is Zope's object
+    publisher. 'ZPublisher' is a kind of light-weight ORB (Object
+    Request Broker). It takes the request and locates an object to
+    handle the request. The publisher uses the request URL as a map to
+    locate the published object. Finding an object to handle the
+    request is called *traversal*, since the publisher moves from
+    object to object as it looks for the right one. Once the published
+    object is found, the publisher calls a method on the published
+    object, passing it parameters as necessary.  The publisher uses
+    information in the request to determine which method to call, and
+    what parameters to pass. The process of extracting parameters from
+    the request is called *argument marshalling*. The published object
+    then returns a response, which is passed back to Zope's web
+    server. The web server, then passes the response back to your web
+    browser.
+
+      % Anonymous User - Dec. 21, 2001 8:39 am - I don't understand.Sorry.
+
+      % Anonymous User - Jan. 11, 2002 9:53 am - Maybe clarify, that "Web Server" is just the
+        front end, passing requests to and receiving responses from  ZPublisher, ZPublisher mapping requests to  
+        responses?
+
+      % tibi - Feb. 22, 2002 6:00 am - Prehaps give an example at this point: say the URL http://zope/animals/monkey/feed?fruit=banana&num=17 is translated in Python to the call animals.monkey.feed(fruit = banana, num = 17)
+
+      % Jace - July 8, 2002 5:41 am:
+       In the above example, the actual call will be animals.monkey.feed(fruit = "banana", num = "17"). Note that
+       they are passed as strings. If the second parameter was meant to be passed as an integer, the URL should have
+       been "http://zope/animals/monkey/feed?fruit=banana&num:int=17.
+
+      % Anonymous User - Aug. 9, 2002 12:29 am:
+       Last 2 comments are illustrative and helpful and might be expanded upon.
+
+      % Anonymous User - Sep. 4, 2002 11:24 pm:
+       num:int is not a standard convention
+
+      % Anonymous User - Oct. 4, 2002 10:55 pm:
+       num:int nevertheless is part of Zope and above examples are on the right track:
+       exemplify URL traversal as July 8 above;
+       point out "feed" need not be contained in ~/monkey (acquisition) 
+       and pattern Form/Action/Response is eased by num:int (conversion) 
+       (with [apologies for lack of]![refs to] detail) 
+       then reader may see the promised land. How ease it is to publish objects...:)blf
+
+      % Anonymous User - Dec. 31, 2002 2:54 pm:
+       What is the significance of the phrase "After the request is completely received". What is the difference
+       between receiving the request and "completely" receiving the the request?
+
+      % Anonymous User - Dec. 2, 2003 5:44 am:
+       Some web servers start to process the HTTP request when it partially arrives at the server. Imagine for
+       example a huge file upload. When the header is already at the server some action may be taken to check if the
+       client is allowed to upload any file at all. This may help to prevent DOS attacks that upload huge files
+       which are discarded but only after they were processed and put a huge load on the server.
+
+      % Anonymous User - Jan. 14, 2005 7:56 am:
+       It's not a *published object*, rather it's an Zope object *to be published*. 
+       The incorrect use of *published* is a huge source of confusion. The publisher *publishes* the attributes of
+       Zope object, an object yet to be published.
+
+    The publishing process is summarized in [2-1]
+
+    "Object publishing":img:2-1:Figures/2-1.png
+
+      % limaye - Sep. 25, 2002 5:21 pm:
+       The Zope side [the server side of the above figure] can be thought of as a collection of containers [zope
+       server container, zope publisher container, etc.] - each responsible for managing a set of issues [e.g., the
+       zope server container manages "sessions" with its clients (http, ftp, webdav, xml-rpc) and could also decide
+       on the issues like load balancing, security etc.] The zope publisher container can provide services of
+       mapping the URL path to the zope object and of dispatching the request; prior to dispatching the request the
+       publisher would also validate the access control constraints; help provide transaction control; load the
+       object in memory; provide life cycle support to these zope objects; provide the object[method invocation]
+       with a "context" so on and so forth. As it may be obvious there is an entity relationship between each of
+       these containers and their childern - whose focus is on managing the call control issues - this hierarchy of
+       containers is used to provide a framework in which the zope objects can do the job on hand without worrying
+       too much about the middleware issues.
+       With the above paragraph setting the technical framework now [hopefully] you can see the value of interfaces.
+       They help publish a contract [e.g. a "set" of interfaces need to be implemented by any zope object (or by the
+       zope framework components themselves) so as to be integrated within the zope framework] which is implemented
+       by the "publishers" (don't confuse this with Zope Publisher) and consumed by the consumers. The other good
+       feature about the interfaces is that the consumer (which can be more than 1) need not worry about who
+       implements the interface and can safely assume that the invocations will be properly processed - in case of
+       an error the publisher of the interface will throw the appropriate exception to report it.
+       MSL
+
+    Typically the published object is a persistent object that the
+    published module loads from the ZODB.  See Chapter 4 for more
+    information on the ZODB.
+
+      % Anonymous User - Jan. 11, 2002 10:09 am - What is the difference between "published module" and "published object"?  how can a module get "published"?
+
+      % pratyush - May 28, 2004 3:25 am:
+       what is exactly a published module u didnt not mention it any where it would be better to give a n example
+       for that one
+
+      % sriram - May 28, 2004 3:30 am:
+       i m not getting exactly the thing please say it clearly
+
+    This chapter will cover all the steps of object publishing in
+    detail. To summarize, object publishing consists of the main
+    steps:
+
+      1. The client sends a request to the publisher
+
+      2. The publisher locates the published object using the request
+         URL as a map.
+
+      3. The publisher calls the published object with arguments from
+         the request.
+
+      4. The publisher interprets and returns the results to the
+         client.
+
+    The chapter will also cover all the technical details, special
+    cases and extra-steps that this list glosses over.
+
+      % Anonymous User - Dec. 31, 2002 2:56 pm:
+       Previously, it was written that the objects response was passed back to the web browser by the Zope web
+       server. Then it was written that the object's respoce was "interpreted" by ZPublisher. Which is true? What
+       does "interepreted" mean?
+
+      % terry - Apr. 29, 2003 10:27 pm:
+       This section omits a useful piece of information -- where does Zope get the
+       first object?  This information is in Ch 7, but deeply buried, so I actually
+       got the answer from the mailing list.  I think you need a reference here too:
+       import Zope
+       root_object = Zope.app()
+       Doing a dir(root_object) will show you your root folder in your Zope instance!
+       Which is really cool for development testing and introspecting your existing
+       objects.
+
+  URL Traversal
+
+    Traversal is the process the publisher uses to locate the published
+    object. Typically the publisher locates the published object by
+    walking along the URL. Take for example a collection of objects::
+
+      class Classification:
+          ...
+
+      class Animal:
+          ...
+
+          def screech(self, ...):
+              ...
+
+      vertebrates=Classification(...)
+      vertebrates.mammals=Classification(...)
+      vertebrates.reptiles=Classification(...)
+      vertebrates.mammals.monkey=Animal(...)
+      vertebrates.mammals.dog=Animal(...)
+      vertebrates.reptiles.lizard=Animal(...)
+
+      % Anonymous User - June 1, 2003 2:13 am:
+       class Classification:
+           ...
+       class Animal:
+           ...
+           def screech(self, ...):
+               ...
+       vertebrates=Classification(...)
+       vertebrates.mammals=Classification(...)
+       vertebrates.reptiles=Classification(...)
+       vertebrates.mammals.monkey=Animal(...)
+       vertebrates.mammals.dog=Animal(...)
+       vertebrates.reptiles.lizard=Animal(...)
+
+    This collection of objects forms an object hierarchy. Using Zope
+    you can publish objects with URLs. For example, the URL
+    'http://zope/vertebrates/mammals/monkey/screech', will traverse
+    the object hierarchy, find the 'monkey' object and call its
+    'screech' method.
+
+    "Traversal path through an object hierarchy":img:2-2:Figures/2-2.png
+
+    The publisher starts from the root object and takes each step in
+    the URL as a key to locate the next object. It moves to the next
+    object and continues to move from object to object using the URL
+    as a guide.
+
+    Typically the next object is a sub-object of the current object
+    that is named by the path segment. So in the example above, when
+    the publisher gets to the 'vertebrates' object, the next path
+    segment is "mammals", and this tells the publisher to look for a
+    sub-object of the current object with that name. Traversal stops
+    when Zope comes to the end of the URL. If the final object is
+    found, then it is published, otherwise an error is returned.
+
+      % Anonymous User - Jan. 11, 2002 10:15 am - url not found: mammals
+
+      % Anonymous User - Nov. 19, 2002 1:40 am:
+       Sometimes the object published is not the one at the end of the URL.
+       Sometimes an object before the end is published and extra segments on
+       the URL are fed to it as parameters.
+
+      % Jace - Feb. 28, 2005 6:38 am:
+       Not necessarily. The rest of the path will be fed as parameters only if you have a traversal hook (see
+       below). Else the publisher will attempt to load those objects via acquisition, and cry 404 if NotFound.
+
+    Now let's take a more rigorous look at traversal.
+
+    Traversal Interfaces
+
+      Zope defines interfaces for publishable objects, and publishable
+      modules.
+
+        % Anonymous User - Jan. 11, 2002 10:20 am - typo? no "," before "and"
+
+        % Anonymous User - Aug. 26, 2002 3:43 am:
+         There is no typo at all, no comma is needed here.
+
+        % Anonymous User - Oct. 18, 2005 6:42 pm:
+         There is a typo now.  Please remove the comma before "and".
+         It might be equally clear to say "Zope defines interfaces for publishable objects and modules."
+
+      When you are developing for Zope you almost always use the
+      'Zope' package as your published module. However, if you are
+      using 'ZPublisher' outside of Zope you'll be interested in the
+      published module interface.
+
+        % Anonymous User - Dec. 5, 2001 8:32 am - please clarify 'your published module'
+
+        % Anonymous User - Jan. 8, 2002 3:53 pm - I think a "published module" is the code that is running and listening on a certain port number to handle requests (over http, ftp, etc.).  So my interpretation is that the statement "use the Zope package as your published module" means "install your component into Zope let Zope publish it".
+
+        % Anonymous User - Jan. 11, 2002 10:24 am - new term "Zope package". never had that one
+
+        % Anonymous User - Sep. 30, 2002 7:10 am:
+         Using the Zope package as your published module:
+         Does this intend, that your objects are contained in the ZODB (eq ZOPE package)?
+
+      Publishable Object Requirements
+
+        Zope has few restrictions on publishable objects. The basic
+        rule is that the object must have a doc string. This
+        requirement goes for method objects too.
+
+        Another requirement is that a publishable object must not have
+        a name that begin with an underscore. These two restrictions
+        are designed to keep private objects from being published.
+
+          % Anonymous User - Jan. 11, 2002 10:25 am - typo? begin --> begins
+
+          % Anonymous User - Jan. 11, 2002 10:39 am - following paragraph: why can't python modules not be published? Why the is a "Published Module" in picture 2-1? And what is the diff. between a module and a module object? fuzzy terms blur semantic categories.
+
+          % Anonymous User - Aug. 16, 2003 12:27 pm:
+           What does 'fuzzy terms blur semantic categories' mean? Please explain or delete.
+
+          % Anonymous User - May 28, 2004 3:57 am:
+           this content is nice but u didn't mention about the "doc string"(it is the restriction )u mean .doc oor DTKL
+           method of thing. It would be better to give an example to the restriction so that the devoloper can practice.
+           thank you
+
+        Finally, published objects cannot be Python module objects.
+
+      Traversal Methods
+
+        During traversal, 'ZPublisher' cuts the URL into path elements
+        delimited by slashes, and uses each path element to traverse
+        from the current object to the next object. 'ZPublisher'
+        locates the next object in one of three ways:
+
+          1. Using '__bobo_traverse__'
+
+          2. Using 'getattr'
+
+          3. Using dictionary access.
+
+        First the publisher attempts to call the traversal hook
+        method, '__bobo_traverse__'. If the current object has this
+        method it is called with the request and the current path
+        element. The method should return the next object or 'None' to
+        indicate that a next object can't be found. You can also
+        return a tuple of objects from '__bobo_traverse__' indicating
+        a sequence of sub-objects. This allows you to add additional
+        parent objects into the request. This is almost never
+        necessary.
+
+          % Anonymous User - Jan. 11, 2002 10:43 am - typo? method, --> method  maurer mentions also "__before_publishing_traverse__".
+
+          % Anonymous User - June 4, 2002 11:10 am:
+           It might be nice to have a footnote briefly describing where "bobo" came from.  Storks don't count.
+
+          % Anonymous User - July 12, 2002 2:50 pm:
+           It might be nicer to get rid of the "bobo" name altogether, why can't this be called __traverse__ ? Probably
+           too much legacy code out there.
+
+          % Anonymous User - Aug. 16, 2003 12:29 pm:
+           Storks do count. Or at least research indicates that they possess the ability - although only up to '2'.
+
+        Here's an example of how to use '__bobo_traverse__'::
+
+          def __bobo_traverse__(self, request, key):
+              # if there is a special cookie set, return special
+              # subobjects, otherwise return normal subobjects
+
+              if request.cookies.has_key('special'):
+                  # return a subobject from the special dict
+                  return self.special_subobjects.get(key, None)
+
+              # otherwise return a subobject from the normal dict
+              return self.normal_subobjects.get(key, None)
+
+          % warpeace - Feb. 17, 2004 11:14 am:
+           The above example is incomplete. This would work fine when called from a normal web request i.e. from
+           BaseRequest.Traverse. However, when an object containing this __bobo_traverse__ is traversed through
+           ObjectItem.unrestrictedTraverse or ObjectItem.restrictedTraverse, then the request object passed to
+           __bobo_traverse__ contains a single key i.e 'TraversalRequestNameStack'. In this case the attibute access
+           'request.cookies' would raise an AttributeError.
+           As an example of this consider that you call your object from the ZMI i.e. say
+           http://localhost:8080/my_bobo_object/manage_workspace.
+           Now, in manage_workspace, my_bobo_object is again traversed using 'unrestrictedTraverse' which would cause a
+           very unwieldy 'TypeError'of 'You are not authorized to view this object' and this can be very difficult to
+           track down.
+                One very rudimentary solution to above __bobo_traverse__ would be:
+           def __bobo_traverse__(self, request, key):
+                if hasattr(request, cookies):
+                     blah...
+                else:
+                     # probably from unrestrictedTraverse
+                     return getattr(self, key)
+           Of course, it would be helpful if some one could point out a more generic and an elegant solution.
+
+          % warpeace - Feb. 17, 2004 11:14 am:
+           The above example is incomplete. This would work fine when called from a normal web request i.e. from
+           BaseRequest.Traverse. However, when an object containing this __bobo_traverse__ is traversed through
+           ObjectItem.unrestrictedTraverse or ObjectItem.restrictedTraverse, then the request object passed to
+           __bobo_traverse__ contains a single key i.e 'TraversalRequestNameStack'. In this case the attibute access
+           'request.cookies' would raise an AttributeError.
+           As an example of this consider that you call your object from the ZMI i.e. say
+           http://localhost:8080/my_bobo_object/manage_workspace.
+           Now, in manage_workspace, my_bobo_object is again traversed using 'unrestrictedTraverse' which would cause a
+           very unwieldy 'TypeError'of 'You are not authorized to view this object' and this can be very difficult to
+           track down.
+                One very rudimentary solution to above __bobo_traverse__ would be:
+           def __bobo_traverse__(self, request, key):
+                if hasattr(request, cookies):
+                     blah...
+                else:
+                     # probably from unrestrictedTraverse
+                     return getattr(self, key)
+           Of course, it would be helpful if some one could point out a more generic and an elegant solution.
+
+        This example shows how you can examine the request during
+        the traversal process. 
+
+        If the current object does not define a '__bobo_traverse__'
+        method, then the next object is searched for using 'getattr'.
+        This locates sub-objects in the normal Python sense.
+
+        If the next object can't be found with 'getattr', 'ZPublisher'
+        calls on the current object as though it were a
+        dictionary. Note: the path element will be a string, not an
+        integer, so you cannot traverse sequences using index numbers
+        in the URL.
+
+        For example, suppose 'a' is the current object, and 'next' is
+        the name of the path element. Here are the three things that
+        'ZPublisher' will try in order to find the next object:
+
+          1. 'a.__bobo_traverse__("next")'
+
+          2. 'a.next'
+
+          3. 'a["next"]'
+
+          % Anonymous User - Jan. 18, 2002 6:13 pm - In the definition above there are three arguments. Where is the "request"   argument here?
+
+        If the next object isn't found by any of these means
+        'ZPublisher' returns a HTTP 404 (Not Found) exception.
+
+      Publishing Methods        
+
+        Once the published object is located with traversal, Zope
+        *publishes* it in one of three possible ways.
+
+          Calling the published object -- If the published object is a
+          function or method or other callable object, the publisher
+          calls it. Later in the chapter you'll find out how the
+          publisher figures out what arguments to pass when calling.
+
+          Calling the default method -- If the published object is not
+          callable, the publisher uses the default method. For HTTP
+          'GET' and 'POST' requests the default method is
+          'index_html'. For other HTTP requests such as 'PUT' the
+          publisher looks for a method named by the HTTP method. So for
+          an HTTP 'HEAD' request, the publisher would call the 'HEAD'
+          method on the published object.
+
+          Stringifying the published object -- If the published object
+          isn't callable, and doesn't have a default method, the
+          publisher publishes it using the Python 'str' function to turn
+          it into a string.
+
+          % shh - Jan. 13, 2003 10:24 am:
+           The order given in this paragraph is INCORRECT. If you look at the traverse() method you will find that
+           ZPublisher tries the default method *first*, and only if this fails goes on with __call__() and __str__().
+
+          % Anonymous User - Jan. 14, 2005 7:59 am:
+           In the text: "Once the published object is ... "
+           should read: "Once the publishable object is ..."
+
+        After the response method has been determined and called, the
+        publisher must interpret the results.
+
+          % Anonymous User - Jan. 11, 2002 10:52 am - What if the called method returns another callable object? is it like as long as callable objects are there, they are called, the resulting object (eg image) rendered [term used by maurer] or is Image a class w a method __call__.
+
+        HTTP Responses
+
+          Normally the published method returns a string which is
+          considered the body of the HTTP response. The response
+          headers can be controlled by calling methods on the response
+          object, which is described later in the chapter. Optionally,
+          the published method can return a tuple with the title, and
+          body of the response. In this case, the publisher returns an
+          generated HTML page, with the first item of the tuple used
+          for the HTML 'title' of the page, and the second item as the
+          contents of the HTML 'body' tag. For example a response of::
+
+            ('response', 'the response')
+
+            % Anonymous User - May 18, 2002 8:10 pm:
+             Typo - "returns an generated" should be "a generated."
+
+          is turned into this HTML page::
+
+            <html>
+            <head><title>response</title></head>
+            <body>the response</body>
+            </html>
+
+        Controlling Base HREF
+
+          When you publish an object that returns HTML relative links
+          should allow you to navigate between methods. Consider this
+          example::
+
+            class Example:
+                "example"
+
+                def one(self):
+                    "method one"
+                    return """<html>
+                              <head>
+                              <title>one</title>
+                              </head>
+                              <body>
+                              <a href="two">two</a> 
+                              </body>
+                              </html>"""
+
+                def two(self):
+                    "method two"
+                    return """<html>
+                              <head>
+                              <title>two</title>
+                              </head>
+                              <body>
+                              <a href="one">one</a> 
+                              </body>
+                              </html>"""
+
+          % Anonymous User - Jan. 21, 2002 12:34 pm - need a comma there:  "...an object that returns HTML, relative links..."
+
+          The relative links in methods 'one' and 'two' allow you to
+          navigate between the methods.
+
+            % Anonymous User - Jan. 11, 2002 10:56 am - typo (syntax) "When you... ." stumbled over this sentence.
+
+          However, the default method, 'index_html' presents a
+          problem. Since you can access the 'index_html' method
+          without specifying the method name in the URL, relative
+          links returned by the 'index_html' method won't work
+          right. For example::
+
+            class Example:
+                "example"
+
+                 def index_html(self):
+                    return """<html>
+                              <head>
+                              <title>one</title>
+                              </head>
+                              <body>
+                              <a href="one">one</a><br>
+                              <a href="two">two</a> 
+                              </body>
+                              </html>"""
+                 ...
+
+          If you publish an instance of the 'Example' class with the
+          URL 'http://zope/example', then the relative link to method
+          'one' will be 'http://zope/one', instead of the correct
+          link, 'http://zope/example/one'.
+
+            % Anonymous User - Feb. 28, 2002 2:30 pm - I believe it is still unclear just why the address of the the method "one" is not http://zope/example/one.
+
+            % Anonymous User - July 17, 2002 5:47 pm:
+             Relative links replace the last component of the URL. From http://zope/example, a relative link to href="one"
+             takes you to http://zope/one. From http://zope/example/, a relative link to href="one" would take you to
+             http://zope/example/one. Seemed clear enough to me.
+
+            % limaye - Sep. 25, 2002 7:35 pm:
+             For the link http://zope/example/introduction - where introduction is a zope object [contained in folder
+             example] could have reference to relative links "one", "two" so on. Now the relative link at the level of
+             introduction [i.e. in folder example - read on] would be interpreted by zope as http://zope/example/one,
+             http://zope/example/two etc. But now consider the case that you give http://zope/example [in this case assume
+             that there is no node following it and assume that there is a default method called index_html]; Further
+             assume that index_html duplicates the content of introduction above - then in this scenario "one", "two"
+             would be interpreted relative node "example" and subsequently it would be replaced by zope as http://zope/one
+             and http://zope/two - which is not what you wanted. If you consider the / as signifying the folder object
+             then http://zope/example can be interpreted as example contained in folder zope. While http://zope/example/
+             would be interpreted as objects contained in folder example and hence the relative link "one" would be
+             interpreted correctly relative to http://zope/example/ to mean relative to folder example; while in the case
+             of http://zope/example a relative link "one" would be interpreted relative to folder zope. I hope this
+             explanation helps.
+             MSL
+
+            % consultit - Mar. 3, 2005 5:18 am:
+             The correct sentence should be something similar to:
+             "If you publish an instance of the Example class, using one of the following URLs http://zope/example and
+             http://zope/example/index_html, then, in the second case, the relative link to method one will be
+             http://zope/example/index_html/one, instead of the correct link, http://zope/example/one."
+             But this problem should arise only if the 'one' method cannot be aquired from the example 'object' (?)
+
+          Zope solves this problem for you by inserting a 'base' tag
+          inside the 'head' tag in the HTML output of 'index_html'
+          method when it is accessed as the default method. You will
+          probably never notice this, but if you see a mysterious
+          'base' tag in your HTML output, know you know where it came
+          from. You can avoid this behavior by manually setting your
+          own base with a 'base' tag in your 'index_html' method
+          output.
+
+            % berniey - Oct. 24, 2001 6:27 am - A little typo.  "know you know where it came from" should be "now you know where it came from"
+
+            % Anonymous User - Dec. 5, 2002 2:49 pm:
+             Can we have an example or a link to the syntax to dynamically generate a corrected base for a virtual hosting
+             context.
+
+        Response Headers
+
+          The publisher and the web server take care of setting response
+          headers such as 'Content-Length' and 'Content-Type'. Later in
+          the chapter you'll find out how to control these headers.
+          Later you'll also find out how exceptions are used to set the
+          HTTP response code.
+
+      Pre-Traversal Hook
+
+        The pre-traversal hook allows your objects to take special
+        action before they are traversed. This is useful for doing
+        things like changing the request. Applications of this include
+        special authentication controls, and virtual hosting support.
+
+        If your object has a method named
+        '__before_publishing_traverse__', the publisher will call it
+        with the current object and the request, before traversing
+        your object. Most often your method will change the
+        request. The publisher ignores anything you return from the
+        pre-traversal hook method.
+
+        The 'ZPublisher.BeforeTraverse' module contains some functions
+        that help you register pre-traversal callbacks. This allows
+        you to perform fairly complex callbacks to multiple objects
+        when a given object is about to be traversed.
+
+          % Anonymous User - Jan. 11, 2002 11:49 am - Why register needed. isn't it sufficient for the object to have a __*__-method?
+
+          % Anonymous User - Jan. 17, 2002 8:20 am - chrism - BeforeTraverse sticks in a method "__before_publishing_traverse__", usually on "containerish" (folderish) objects.  __before_publishing_traverse__ is a method that calls the callbacks that you register.  For example, this is how "AccessRules" and "SiteRoots" work - they register arbitrary callbacks with __before_publishing_traverse__.  __before_publishing_traverse__ is called before __bobo_traverse__ of the same object in order to give virtual hosting a chance to do its job. It calls all of the callbacks that are registered with it.  Note that because of the time at which it's called in the traversal process, callbacks that get registered with it have no access to authentication info.
+
+          % Anonymous User - Jan. 18, 2002 6:22 pm - Thanks for the explanation! More information and an example here is really   needed.
+
+    Traversal and Acquisition
+
+      Acquisition affects traversal in several ways. See Chapter 5,
+      "Acquisition" for more information on acquisition. The most
+      obvious way in which acquisition affects traversal is in
+      locating the next object in a path. As we discussed earlier, the
+      next object during traversal is often found using
+      'getattr'. Since acquisition affects 'getattr', it will affect
+      traversal. The upshot is that when you are traversing objects
+      that support implicit acquisition, you can use traversal to walk
+      over acquired objects. Consider the object hierarchy rooted in
+      'fruit'::
+
+        from Acquisition import Implicit
+
+        class Node(Implicit):
+            ...
+
+        fruit=Node()
+        fruit.apple=Node()
+        fruit.orange=Node()
+        fruit.apple.strawberry=Node()
+        fruit.orange.banana=Node()
+
+      When publishing these objects, acquisition can come into
+      play. For example, consider the URL */fruit/apple/orange*. The
+      publisher would traverse from 'fruit', to 'apple', and then
+      using acquisition, it would traverse to 'orange'.
+
+      Mixing acquisition and traversal can get complex. Consider the
+      URL */fruit/apple/orange/strawberry/banana*. This URL is
+      functional but confusing. Here's an even more perverse but legal
+      URL */fruit/apple/orange/orange/apple/apple/banana*.
+
+        % Anonymous User - Sep. 30, 2002 8:05 am:
+         Would be <b>/fruit/apple/banana/orange/strawberry illegal</b>?
+
+        % Anonymous User - Oct. 8, 2002 9:35 pm:
+         I think so. Someone more experienced, please confirm. (I have not yet read the chapter on acquisition).
+
+        % Anonymous User - Nov. 17, 2003 5:05 am:
+         yes it is illegal. You have to think of it in "context". An object has to be found in the context of the
+         previous objects.
+         banana is in the context of orange, but in the path orange is nowhere to be found BEFORE banana. So it won't
+         be found.
+         strawberry will be found because apple is in the path before strawberry.
+         Should you express it as */fruit/apple/orange/banana/strawberry* it should work cause each object can be
+         found in the context of some other object that is in the path BEFORE the object you want to find...
+         You really should read the USER Manual (zope book), especially the chapter about acquisition. it really
+         qlarifies the concept
+
+      In general you should limit yourself to constructing URLs which
+      use acquisition to acquire along containment, rather than
+      context lines. It's reasonable to publish an object or method
+      that you acquire from your container, but it's probably a bad
+      idea to publish an object or method that your acquire from
+      outside your container. For example::
+
+        from Acquisition import Implicit
+
+        class Basket(Implicit):
+            ...
+            def numberOfItems(self):
+                "Returns the number of contained items"
+                ...
+
+        class Vegetable(Implicit):
+            ...
+            def texture(self):
+                "Returns the texture of the vegetable."
+
+        class Fruit(Implicit):
+            ...
+            def color(self):
+                "Returns the color of the fruit."
+
+         basket=Basket()
+         basket.apple=Fruit()
+         basket.carrot=Vegetable()
+
+      The URL */basket/apple/numberOfItems* uses acquisition along
+      containment lines to publish the 'numberOfItems' method
+      (assuming that 'apple' doesn't have a 'numberOfItems'
+      attribute). However, the URL */basket/carrot/apple/texture*
+      uses acquisition to locate the 'texture' method from the 'apple'
+      object's context, rather than from its container. While this
+      distinction may be obscure, the guiding idea is to keep URLs as
+      simple as possible. By keeping acquisition simple and along
+      containment lines your application increases in clarity, and
+      decreases in fragility.
+
+        % Anonymous User - Jan. 11, 2002 11:58 am - typo /your/you/. What then is the positive side of aquisition, if its freedom is dangerous and a more restrictive name resolution would suffice?
+
+        % Anonymous User - Dec. 5, 2001 9:23 am - Thus, the first URL returns the number of apples in the basket (i.e. 1)? And the second URL returns the texture of the carrot?
+
+        % rogererens - Dec. 7, 2001 6:14 am - Read chapter 5, Acquisition first.  I think it's instructional to state that the first URL is equivalent to /basket/carrot/apple/numberOfItems. The number of contained items in the basket is 2 (a carrot and an apple).  Furthermore, the second URL uses acquisition by context because the method texture cannot be acquired by containment (i.e. not in apple nor in its container, basket). Please correct me if I'm wrong.
+
+        % Anonymous User - Aug. 16, 2003 12:56 pm:
+         I don't begin to comprehend all this guff - and why's it here anyway? Doesn't seem to add anything to the
+         matter of publishing - I suggest it's best left to the real chapter on Acquisition...
+
+      A second usage of acquisition in traversal concerns the
+      request. The publisher tries to make the request available to
+      the published object via acquisition. It does this by wrapping
+      the first object in an acquisition wrapper that allows it to
+      acquire the request with the name 'REQUEST'. This means that you
+      can normally acquire the request in the published object like
+      so::
+
+        request=self.REQUEST # for implicit acquirers
+
+      or like so::
+
+        request=self.aq_acquire('REQUEST') # for explicit acquirers
+
+      Of course, this will not work if your objects do not support
+      acquisition, or if any traversed objects have an attribute named
+      'REQUEST'.
+
+      Finally, acquisition has a totally different role in object
+      publishing related to security which we'll examine next.
+
+    Traversal and Security
+
+      As the publisher moves from object to object during traversal it
+      makes security checks. The current user must be authorized to
+      access each object along the traversal path. The publisher
+      controls access in a number of ways. For more information about
+      Zope security, see Chapter 6, "Security".
+
+      Basic Publisher Security
+
+        The publisher imposes a few basic restrictions on traversable
+        objects. These restrictions are the same of those for
+        publishable objects. As previously stated, publishable objects
+        must have doc strings and must not have names beginning with
+        underscore.
+
+        The following details are not important if you are using the
+        Zope framework. However, if your are publishing your own
+        modules, the rest of this section will be helpful.
+
+        The publisher checks authorization by examining the
+        '__roles__' attribute of each object as it performs
+        traversal. If present, the '__roles__' attribute should be
+        'None' or a list of role names. If it is None, the object is
+        considered public. Otherwise the access to the object requires
+        validation.
+
+        Some objects such as functions and methods do not support
+        creating attributes (at least they didn't before Python
+        2). Consequently, if the object has no '__roles__' attribute,
+        the publisher will look for an attribute on the object's
+        parent with the name of the object followed by
+        '__roles__'. For example, a function named 'getInfo' would
+        store its roles in its parent's 'getInfo__roles__' attribute.
+
+        If an object has a '__roles__' attribute that is not empty and
+        not 'None', the publisher tries to find a user database to
+        authenticate the user. It searches for user databases by
+        looking for an '__allow_groups__' attribute, first in the
+        published object, then in the previously traversed object, and
+        so on until a user database is found.
+
+        When a user database is found, the publisher attempts to
+        validate the user against the user database. If validation
+        fails, then the publisher will continue searching for user
+        databases until the user can be validated or until no more
+        user databases can be found.
+
+        The user database may be an object that provides a validate
+        method::
+
+          validate(request, http_authorization, roles)
+
+        where 'request' is a mapping object that contains request
+        information, 'http_authorization' is the value of the HTTP
+        'Authorization' header or 'None' if no authorization header
+        was provided, and 'roles' is a list of user role names.
+
+        The validate method returns a user object if succeeds, and
+        'None' if it cannot validate the user. See Chapter 6 for more
+        information on user objects. Normally, if the validate method
+        returns 'None', the publisher will try to use other user
+        databases, however, a user database can prevent this by
+        raising an exception.
+
+          % Anonymous User - May 18, 2002 9:59 pm:
+           dshafer - if *it* succeeds (word missing)
+
+        If validation fails, Zope will return an HTTP header that
+        causes your browser to display a user name and password
+        dialog. You can control the realm name used for basic
+        authentication by providing a module variable named
+        '__bobo_realm__'. Most web browsers display the realm name in
+        the user name and password dialog box.
+
+        If validation succeeds the publisher assigns the user object
+        to the request variable, 'AUTHENTICATED_USER'. The publisher
+        places no restriction on user objects.
+
+          % Anonymous User - Aug. 11, 2002 5:18 pm:
+           Worth noting here that direct access to AUTHENTICATED_USER is deprecated for security reasons?
+
+      Zope Security
+
+        When using Zope rather than publishing your own modules, the
+        publisher uses acquisition to locate user folders and perform
+        security checks. The upshot of this is that your published
+        objects must inherit from 'Acquisition.Implicit' or
+        'Acquisition.Explicit'. See Chapter 5, "Acquisition", for more
+        information about these classes. Also when traversing each
+        object must be returned in an acquisition context. This is
+        done automatically when traversing via 'getattr', but you must
+        wrap traversed objects manually when using '__getitem__' and
+        '__bobo_traverse__'. For example::
+
+          class Example(Acquisition.Explicit):
+              ...
+
+              def __bobo_traverse__(self, name, request):
+                  ...
+                  next_object=self._get_next_object(name)
+                  return  next_object.__of__(self)      
+
+          % Anonymous User - Nov. 6, 2002 10:57 am:
+           It may be worth making a reference to this from the reference to __bobo_traverse__ earlier in the chapter.
+
+        % Anonymous User - Jan. 18, 2002 7:26 pm - There's a missing "for" here: "See Chapter 5, Acquisition more information...".
+
+        Additionally you will need to make security declarations on
+        your traversed object using 'ClassSecurityInfo' as described
+        in Chapter 6, "Security".
+
+          % Anonymous User - Jan. 11, 2002 12:10 pm - invalid href Acqisition
+
+        Finally, traversal security can be circumvented with the
+        '__allow_access_to_unprotected_subobjects__' attribute as
+        described in Chapter 6, "Security".
+
+          % Anonymous User - Sep. 27, 2002 6:06 am:
+           It isn't described anywhere in chapter 6 (or anywhere else in this book for that matter).
+
+    Environment Variables
+
+      You can control some facets of the publisher's operation by
+      setting environment variables.
+
+        'Z_DEBUG_MODE' -- Sets debug mode. In debug mode tracebacks are
+        not hidden in error pages. Also debug mode causes 'DTMLFile'
+        objects, External Methods and help topics to reload their
+        contents from disk when changed. You can also set debug mode
+        with the '-D' switch when starting Zope.
+
+        'Z_REALM' -- Sets the basic authorization realm. This controls
+        the realm name as it appears in the web browser's username and
+        password dialog. You can also set the realm with the
+        '__bobo_realm__' module variable, as mentioned previously.
+
+        'PROFILE_PUBLISHER' -- Turns on profiling and sets the name of
+        the profile file. See the Python documentation for more
+        information about the Python profiler.
+
+        % Anonymous User - Aug. 16, 2003 1:00 pm:
+         Does this still stand true in the light of the changes with i.e. Zope 2.7 ?
+
+      Many more options can be set using switches on the startup
+      script. See the *Zope Administrator's Guide* for more
+      information.
+
+    Testing
+
+      ZPublisher comes with built-in support for testing and working
+      with the Python debugger.  This topic is covered in more detail
+      in Chapter 7, "Testing and Debugging".
+
+    Publishable Module
+
+      If you are using the Zope framework, this section will be
+      irrelevant to you. However, if you are publishing your own
+      modules with 'ZPublisher' read on.
+
+      The publisher begins the traversal process by locating an object
+      in the module's global namespace that corresponds to the first
+      element of the path. Alternately the first object can be located
+      by one of two hooks.
+
+        % Anonymous User - Feb. 28, 2002 3:31 pm - isn't the first name on the path the name of the module itself?
+
+      If the module defines a 'web_objects' or 'bobo_application'
+      object, the first object is searched for in those objects. The
+      search happens according to the normal rules of traversal, using
+      '__bobo_traverse__', 'getattr', and '__getitem__'.
+
+      The module can receive callbacks before and after traversal. If
+      the module defines a '__bobo_before__' object, it will be called
+      with no arguments before traversal. Its return value is
+      ignored. Likewise, if the module defines a '__bobo_after__'
+      object, it will be called after traversal with no
+      arguments. These callbacks can be used for things like acquiring
+      and releasing locks.
+
+  Calling the Published Object
+
+    Now that we've covered how the publisher located the published
+    object and what it does with the results of calling it, let's take
+    a closer look at how the published object is called.
+
+      % Anonymous User - Nov. 26, 2001 2:02 pm - published object can be accessed by  REQUEST['PUBLISHED'], this is especially useful if you are publishing a method  inside of a container above the PUBLISHED object.
+
+    The publisher marshals arguments from the request and
+    automatically makes them available to the published object. This
+    allows you to accept parameters from web forms without having to
+    parse the forms. Your objects usually don't have to do anything
+    special to be called from the web. Consider this function::
+
+      def greet(name):
+          "greet someone"
+          return "Hello, %s" % name
+
+    You can provide the 'name' argument to this function by calling it
+    with a URL like *greet?name=World*. You can also call it with a
+    HTTP 'POST' request which includes 'name' as a form variable.
+
+    In the next sections we'll take a closer look at how the publisher
+    marshals arguments.
+
+    Marshalling Arguments from the Request
+
+      The publisher marshals form data from GET and POST
+      requests. Simple form fields are made available as Python
+      strings. Multiple fields such as form check boxes and multiple
+      selection lists become sequences of strings. File upload fields
+      are represented with 'FileUpload' objects. File upload objects
+      behave like normal Python file objects and additionally have a
+      'filename' attribute which is the name of the file and a
+      'headers' attribute which is a dictionary of file upload
+      headers.
+
+      The publisher also marshals arguments from CGI environment
+      variables and cookies. When locating arguments, the publisher
+      first looks in CGI environment variables, then other request
+      variables, then form data, and finally cookies. Once a variable
+      is found, no further searching is done. So for example, if your
+      published object expects to be called with a form variable named
+      'SERVER_URL', it will fail, since this argument will be
+      marshaled from the CGI environment first, before the form data.
+
+        % Anonymous User - Nov. 15, 2002 1:35 pm:
+         Could readers find the use of the term 'fail' somewhat confusing here? I'm guessing that the 'published
+         onject' will still get called OK, but that the value of the 'SERVER_URL' argument would be other than that
+         expected?
+
+      The publisher provides a number of additional special variables
+      such as 'URL0' which are derived from the request. These are
+      covered in the 'HTTPRequest' API documentation.
+
+    Argument Conversion
+
+      The publisher supports argument conversion. For example consider
+      this function::
+
+        def onethird(number):
+            "returns the number divided by three"
+            return number / 3.0
+
+        % Anonymous User - Apr. 25, 2005 2:11 pm:
+         def onethird():
+             "returns the number divided by three"
+             return int(number) / 3.0
+         onethird?number=66
+         Is this legal?
+
+      This function cannot be called from the web because by default
+      the publisher marshals arguments into strings, not numbers. This
+      is why the publisher provides a number of converters. To signal
+      an argument conversion you name your form variables with a colon
+      followed by a type conversion code. For example, to call the
+      above function with 66 as the argument you can use this URL
+      *onethird?number:int=66* The publisher supports many
+      converters:
+
+        boolean -- Converts a variable to true or false. Variables
+        that are 0, None, an empty string, or an empty sequence are
+        false, all others are true.
+
+        int -- Converts a variable to a Python integer.
+
+        long -- Converts a variable to a Python long integer.
+
+        float -- Converts a variable to a Python floating point
+        number.
+
+        string -- Converts a variable to a Python string.
+
+        required -- Raises an exception if the variable is not present
+        or is an empty string.
+
+        ignore_empty -- Excludes a variable from the request if the
+        variable is an empty string.
+
+        date -- Converts a string to a *DateTime* object. The formats
+        accepted are fairly flexible, for example '10/16/2000',
+        '12:01:13 pm'.
+
+        list -- Converts a variable to a Python list of values, even
+        if there is only one value.
+
+        tuple -- Converts a variable to a Python tuple of values, even
+        if there is only one value.
+
+        lines -- Converts a string to a Python list of values by
+        splitting the string on line breaks.
+
+        tokens -- Converts a string to a Python list of values by
+        splitting the string on spaces.
+
+        text -- Converts a variable to a string with normalized line
+        breaks.  Different browsers on various platforms encode line
+        endings differently, so this converter makes sure the line
+        endings are consistent, regardless of how they were encoded by
+        the browser.
+
+        % Anonymous User - Nov. 15, 2002 1:37 pm:
+         Asserting that 'This function cannot be called from the web' seems inaccurate or incomplete - especially
+         considering the subsequent explanation.
+
+        % Anonymous User - Nov. 20, 2003 1:17 am:
+         This list is missing ustring.
+
+      If the publisher cannot coerce a request variable into the type
+      required by the type converter it will raise an error. This is
+      useful for simple applications, but restricts your ability to
+      tailor error messages. If you wish to provide your own error
+      messages, you should convert arguments manually in your
+      published objects rather than relying on the publisher for
+      coercion. Another possibility is to use JavaScript to validate
+      input on the client-side before it is submitted to the server.
+
+      You can combine type converters to a limited extent. For example
+      you could create a list of integers like so::
+
+        <input type="checkbox" name="numbers:list:int" value="1">
+        <input type="checkbox" name="numbers:list:int" value="2">
+        <input type="checkbox" name="numbers:list:int" value="3">
+
+      In addition to these type converters, the publisher also supports
+      method and record arguments.
+
+        % Anonymous User - July 17, 2002 5:56 pm:
+         This is covered pretty thoroughly in the Zope Book's Chapter 10 -- perhaps a reference would be more
+         appropriate than duplicating the content?
+
+        % Anonymous User - Sep. 16, 2002 2:09 pm:
+         I feel that the explaination here is better than the explanation in the Zope Book.
+
+        % Anonymous User - Oct. 8, 2002 11:17 am:
+         v2 of the Zope book in chapter 10 refers to actions rather than methods. Also, it should be described
+         somewhere what should go in the <form> element's "action" attribute: is it just an empty string as per the
+         Zope Book example?
+
+        % Anonymous User - Aug. 9, 2004 6:03 pm:
+         What an ugly hack. The type of coercion to apply should be specified in the method that is published, not in
+         the parameter names inside the HTTP request, where it is controlled by the user making the request. Of course
+         you don't have to use this scheme and can instead use explicit coercion code, but why allow coercion specs in
+         parameter names at all then?
+
+
+      Method Arguments
+
+        Sometimes you may wish to control which object is published
+        based on form data. For example, you might want to have a form
+        with a select list that calls different methods depending on
+        the item chosen. Similarly, you might want to have multiple
+        submit buttons which invoke a different method for each
+        button.
+
+        The publisher provides a way to select methods using form
+        variables through use of the *method* argument type. The
+        method type allows the request 'PATH_INFO' to be augmented
+        using information from a form item name or value.
+
+        If the name of a form field is ':method', then the value of
+        the field is added to 'PATH_INFO'. For example, if the
+        original 'PATH_INFO' is 'foo/bar' and the value of a ':method'
+        field is 'x/y', then 'PATH_INFO' is transformed to
+        'foo/bar/x/y'. This is useful when presenting a select
+        list. Method names can be placed in the select option values.
+
+        If the name of a form field ends in ':method' then the part of
+        the name before ':method' is added to 'PATH_INFO'. For
+        example, if the original 'PATH_INFO' is 'foo/bar' and there is
+        a 'x/y:method' field, then 'PATH_INFO' is transformed to
+        'foo/bar/x/y'. In this case, the form value is ignored. This
+        is useful for mapping submit buttons to methods, since submit
+        button values are displayed and should, therefore, not contain
+        method names.
+
+        Only one method field should be provided. If more than one
+        method field is included in the request, the behavior is
+        undefined.
+
+      Record Arguments 
+
+        Sometimes you may wish to consolidate form data into a
+        structure rather than pass arguments individually. Record
+        arguments allow you to do this.
+
+        The 'record' type converter allows you to combine multiple
+        form variables into a single input variable. For example::
+
+          <input name="date.year:record:int">
+          <input name="date.month:record:int">
+          <input name="date.day:record:int">
+
+        This form will result in a single variable, 'date', with
+        attributes 'year', 'month', and 'day'.
+
+          % Anonymous User - Apr. 25, 2005 3:17 pm:
+           how much white space can I add
+           I rove white space
+           It's white.
+
+          % Anonymous User - Apr. 25, 2005 3:19 pm:
+           not much, I'll say that.
+
+        You can skip empty record elements with the 'ignore_empty'
+        converter. For example::
+
+          <input type="text" name="person.email:record:ignore_empty">
+
+        When the email form field is left blank the publisher skips
+        over the variable rather than returning a null string as its
+        value. When the record 'person' is returned it will not have
+        an 'email' attribute if the user did not enter one.
+
+        You can also provide default values for record elements with
+        the 'default' converter. For example::
+
+          <input type="hidden"
+                 name="pizza.toppings:record:list:default" 
+                 value="All">
+          <select multiple name="pizza.toppings:record:list:ignore_empty">
+          <option>Cheese</option>
+          <option>Onions</option>
+          <option>Anchovies</option>
+          <option>Olives</option>
+          <option>Garlic<option>
+          </select>
+
+        The 'default' type allows a specified value to be inserted
+        when the form field is left blank. In the above example, if
+        the user does not select values from the list of toppings, the
+        default value will be used. The record 'pizza' will have the
+        attribute 'toppings' and its value will be the list containing
+        the word "All" (if the field is empty) or a list containing
+        the selected toppings.
+
+        You can even marshal large amounts of form data into multiple
+        records with the 'records' type converter. Here's an example::
+
+          <h2>Member One</h2>
+          Name:
+          <input type="text" name="members.name:records"><BR>
+          Email:
+          <input type="text" name="members.email:records"><BR>
+          Age:
+          <input type="text" name="members.age:int:records"><BR>
+
+          <H2>Member Two</H2>
+          Name:
+          <input type="text" name="members.name:records"><BR>
+          Email:
+          <input type="text" name="members.email:records"><BR>
+          Age:
+          <input type="text" name="members.age:int:records"><BR>
+
+        This form data will be marshaled into a list of records named
+        'members'. Each record will have a 'name', 'email', and 'age'
+        attribute.
+
+        Record marshalling provides you with the ability to create
+        complex forms. However, it is a good idea to keep your web
+        interfaces as simple as possible.
+
+  Exceptions
+
+    Unhandled exceptions are caught by the object publisher and are
+    translated automatically to nicely formatted HTTP output.
+
+    When an exception is raised, the exception type is mapped to an
+    HTTP code by matching the value of the exception type with a list
+    of standard HTTP status names. Any exception types that do not
+    match standard HTTP status names are mapped to "Internal Error"
+    (500). The standard HTTP status names are: "OK", "Created",
+    "Accepted", "No Content", "Multiple Choices", "Redirect", "Moved
+    Permanently", "Moved Temporarily", "Not Modified", "Bad Request",
+    "Unauthorized", "Forbidden", "Not Found", "Internal Error", "Not
+    Implemented", "Bad Gateway", and "Service Unavailable". Variations
+    on these names with different cases and without spaces are also
+    valid.
+
+      % Anonymous User - Dec. 5, 2001 11:16 am - Typo: Bad Gateway needs to be quoted
+
+      % Anonymous User - Feb. 15, 2002 2:17 pm - Actually, if you view the structured text source, this isn't a typo. It *is* quoted, with a comma, and the word and between Gateway and Service.     I think this is likely a structured text problem, not the author's fault.
+
+    An attempt is made to use the exception value as the body of the
+    returned response. The object publisher will examine the exception
+    value. If the value is a string that contains some white space,
+    then it will be used as the body of the return error message. If
+    it appears to be HTML, the error content type will be set to
+    'text/html', otherwise, it will be set to 'text/plain'. If the
+    exception value is not a string containing white space, then the
+    object publisher will generate its own error message.
+
+    There are two exceptions to the
+    above rule:
+
+      1. If the exception type is: "Redirect", "Multiple Choices"
+         "Moved Permanently", "Moved Temporarily", or "Not
+         Modified", and the exception value is an absolute URI, then
+         no body will be provided and a 'Location' header will be
+         included in the output with the given URI.
+
+      2. If the exception type is "No Content", then no body will be
+         returned.
+
+      % Anonymous User - Dec. 5, 2001 11:18 am - Typos: No Content and Move Temporarily need quotes
+
+      % Anonymous User - May 18, 2002 10:07 pm:
+       dshafer - Comma needed between "Mutiple Choices" and "Moved Permanently".
+
+    When a body is returned, traceback information will be included in
+    a comment in the output. As mentioned earlier, the environment
+    variable 'Z_DEBUG_MODE' can be used to control how tracebacks are
+    included. If this variable is set then tracebacks are included in
+    'PRE' tags, rather than in comments. This is very handy during
+    debugging.
+
+    Exceptions and Transactions
+
+      When Zope receives a request it begins a transaction. Then it
+      begins the process of traversal. Zope automatically commits the
+      transaction after the published object is found and called. So
+      normally each web request constitutes one transaction which Zope
+      takes care of for you. See Chapter 4. for more information on
+      transactions.
+
+      If an unhandled exception is raised during the publishing
+      process, Zope aborts the transaction. As detailed in Chapter
+      4. Zope handles 'ConflictErrors' by re-trying the request up to
+      three times.  This is done with the 'zpublisher_exception_hook'.
+
+      In addition, the error hook is used to return an error message
+      to the user. In Zope the error hook creates error messages by
+      calling the 'raise_standardErrorMessage' method. This method is
+      implemented by 'SimpleItem.Item'. It acquires the
+      'standard_error_message' DTML object, and calls it with
+      information about the exception.
+
+      You will almost never need to override the
+      'raise_standardErrorMessage' method in your own classes, since
+      it is only needed to handle errors that are raised by other
+      components. For most errors, you can simply catch the exceptions
+      normally in your code and log error messages as needed. If you
+      need to, you should be able to customize application error
+      reporting by overriding the 'standard_error_message' DTML object
+      in your application.
+
+  Manual Access to Request and Response
+
+    You do not need to access the request and response directly most
+    of the time. In fact, it is a major design goal of the publisher
+    that most of the time your objects need not even be aware that
+    they are being published on the web. However, you have the ability
+    to exert more precise control over reading the request and
+    returning the response.
+
+    Normally published objects access the request and response by
+    listing them in the signature of the published method. If this is
+    not possible you can usually use acquisition to get a reference to
+    the request. Once you have the request, you can always get the
+    response from the request like so::
+
+      response=REQUEST.RESPONSE
+
+      % Anonymous User - Aug. 16, 2003 1:12 pm:
+       an example of 'use acquisition to get the request' would be very useful here for us novices.
+
+    The APIs of the request and response are covered in the API
+    documentation. Here we'll look at a few common uses of the request
+    and response.
+
+    One reason to access the request is to get more precise
+    information about form data. As we mentioned earlier, argument
+    marshalling comes from a number of places including cookies, form
+    data, and the CGI environment. For example, you can use the
+    request to differentiate between form and cookie data::
+
+      cookies=REQUEST.cookies # a dictionary of cookie data
+      form=REQUEST.form # a dictionary of form data
+
+    One common use of the response object is to set response headers.
+    Normally the publisher in concert with the web server will take
+    care of response headers for you. However, sometimes you may wish
+    manually control headers::
+
+      RESPONSE.setHeader('Pragma', 'No-Cache')
+
+    Another reason to access the response is to stream response
+    data. You can do this with the 'write' method::
+
+      while 1:
+          data=getMoreData() #this call may block for a while
+          if not data:
+              break
+          RESPONSE.write(data)
+
+    Here's a final example that shows how to detect if your method is
+    being called from the web. Consider this function::
+
+      def feedParrot(parrot_id, REQUEST=None):
+          ...
+
+          if REQUEST is not None:
+              return "<html><p>Parrot %s fed</p></html>" % parrot_id
+
+    The 'feedParrot' function can be called from Python, and also from
+    the web. By including 'REQUEST=None' in the signature you can
+    differentiate between being called from Python and being called
+    form the web. When the function is called from Python nothing is
+    returned, but when it is called from the web the function returns
+    an HTML confirmation message.
+
+      % Anonymous User - Nov. 15, 2002 1:47 pm:
+       typo: 'called form the web' should read 'called from the web'
+
+  Other Network Protocols
+
+    FTP
+
+      Zope comes with an FTP server which allows users to treat the
+      Zope object hierarchy like a file server. As covered in Chapter
+      3, Zope comes with base classes ('SimpleItem' and
+      'ObjectManager') which provide simple FTP support for all Zope
+      objects. The FTP API is covered in the API reference.
+
+      To support FTP in your objects you'll need to find a way to
+      represent your object's state as a file. This is not possible or
+      reasonable for all types of objects. You should also consider
+      what users will do with your objects once they access them via
+      FTP.  You should find out which tools users are likely to edit
+      your object files.  For example, XML may provide a good way to
+      represent your object's state, but it may not be easily editable
+      by your users.  Here's an example class that represents itself
+      as a file using RFC 822 format::
+
+        from rfc822 import Message
+        from cStringIO import StringIO
+
+        class Person(...):
+
+            def __init__(self, name, email, age):
+                self.name=name
+                self.email=email
+                self.age=age
+
+            def writeState(self):
+                "Returns object state as a string"
+                return "Name: %s\nEmail: %s\nAge: %s" % (self.name,
+                                                         self.email, 
+                                                         self.age)
+            def readState(self, data):
+                "Sets object state given a string"
+                m=Message(StringIO(data))
+                self.name=m['name']
+                self.email=m['email']
+                self.age=int(m['age'])
+
+      The 'writeState' and 'readState' methods serialize and
+      unserialize the 'name', 'age', and 'email' attributes to and
+      from a string. There are more efficient ways besides RFC 822 to
+      store instance attributes in a file, however RFC 822 is a simple
+      format for users to edit with text editors.
+
+      To support FTP all you need to do at this point is implement the
+      'manage_FTPget' and 'PUT' methods. For example::
+
+        def manage_FTPget(self):
+            "Returns state for FTP"
+            return self.writeState()
+
+        def PUT(self, REQUEST):
+            "Sets state from FTP"
+             self.readState(REQUEST['BODY'])
+
+      You may also choose to implement a 'get_size' method which
+      returns the size of the string returned by 'manage_FTPget'. This
+      is only necessary if calling 'manage_FTPget' is expensive, and
+      there is a more efficient way to get the size of the file. In
+      the case of this example, there is no reason to implement a
+      'get_size' method.
+
+      One side effect of implementing 'PUT' is that your object now
+      supports HTTP PUT publishing. See the next section on WebDAV for
+      more information on HTTP PUT.
+
+      That's all there is to making your object work with FTP. As
+      you'll see next WebDAV support is similar.
+
+    WebDAV
+
+      WebDAV is a protocol for collaboratively edit and manage files
+      on remote servers. It provides much the same functionality as
+      FTP, but it works over HTTP.
+
+        % Anonymous User - Mar. 5, 2004 6:27 pm:
+         change "for collaboratively edit and manage files" to read "to collaboratively edit and manage files (roy)"
+
+      It is not difficult to implement WebDAV support for your
+      objects. Like FTP, the most difficult part is to figure out how
+      to represent your objects as files.
+
+      Your class must inherit from 'webdav.Resource' to get basic DAV
+      support. However, since 'SimpleItem' inherits from 'Resource',
+      your class probably already inherits from 'Resource'. For
+      container classes you must inherit from
+      'webdav.Collection'. However, since 'ObjectManager' inherits
+      from 'Collection' you are already set so long as you inherit
+      from 'ObjectManager'.
+
+      In addition to inheriting from basic DAV classes, your classes
+      must implement 'PUT' and 'manage_FTPget'. These two methods are
+      also required for FTP support. So by implementing WebDAV
+      support, you also implement FTP support.
+
+      The permissions that you assign to these two methods will
+      control the ability to read and write to your class through
+      WebDAV, but the ability to see your objects is controlled
+      through the "WebDAV access" permission.
+
+      Supporting Write Locking
+
+        Write locking is a feature of WebDAV that allows users to put
+        a lock on objects they are working on. Support write locking
+        is easy. To implement write locking you must assert that your
+        class implements the 'WriteLockInterface'. For example::
+
+          from webdav.WriteLockInterface import WriteLockInterface
+
+          class MyContentClass(OFS.SimpleItem.Item, Persistent):
+              __implements__ = (WriteLockInterface,)
+
+          % Anonymous User - Nov. 15, 2002 2:21 pm:
+           typo: 'Support write locking' should read e.g. 'Supporting write locking'
+
+        It's sufficient to inherit from 'SimpleItem.Item', since it
+        inherits from 'webdav.Resource', which provides write locking
+        along with other DAV support.
+
+        In addition, your 'PUT' method should begin with calls to
+        'dav__init' and 'dav_simpleifhandler'. For example::
+
+          def PUT(self, REQUEST, RESPONSE):
+              """
+              Implement WebDAV/HTTP PUT/FTP put method for this object.
+              """
+              self.dav__init(REQUEST, RESPONSE)
+              self.dav__simpleifhandler(REQUEST, RESPONSE)
+              ...
+
+         Finally your class's edit methods should check to determine
+         whether your object is locked using the 'ws_isLocked'
+         method. If someone attempts to change your object when it is
+         locked you should raise the 'ResourceLockedError'. For
+         example::
+
+           from webdav import ResourceLockedError
+
+           class MyContentClass(...):
+               ...
+
+               def edit(self, ...):
+                   if self.ws_isLocked():
+                       raise ResourceLockedError
+                   ...
+
+         WebDAV support is not difficult to implement, and as more
+         WebDAV editors become available, it will become more
+         valuable. If you choose to add FTP support to your class you
+         should probably go ahead and support WebDAV too since it is
+         so easy once you've added FTP support.
+
+    XML-RPC
+
+      "XML-RPC":http://www.xmlrpc.com is a light-weight Remote
+      Procedure Call protocol that uses XML for encoding and HTTP for
+      transport. Fredrick Lund maintains a Python "XML-RPC
+      module":http://www.pythonware.com/products/xmlrpc.
+
+      All objects in Zope support XML-RPC publishing. Generally you
+      will select a published object as the end-point and select one
+      of its methods as the method. For example you can call the
+      'getId' method on a Zope folder at 'http://example.com/myfolder'
+      like so::
+
+        import xmlrpclib
+        folder=xmlrpclib.Server('http://example.com/myfolder')
+        ids=folder.getId()
+
+      You can also do traversal via a dotted method name. For
+      example::
+
+        import xmlrpclib
+
+        # traversal via dotted method name
+        app=xmlrpclib.Server('http://example.com/app')
+        id1=app.folderA.folderB.getId()
+
+        # walking directly up to the published object
+        folderB=xmlrpclib.Server('http://example.com/app/folderA/folderB')
+        id2=folderB.getId()
+
+        print id1==id2
+
+      This example shows different routes to the same object
+      publishing call.
+
+      XML-RPC supports marshalling of basic Python types for both
+      publishing requests and responses. The upshot of this
+      arrangement is that when you are designing methods for use via
+      XML-RPC you should limit your arguments and return values to
+      simple values such as Python strings, lists, numbers and
+      dictionaries. You should not accept or return Zope objects from
+      methods that will be called via XML-RPC.
+
+        % Anonymous User - Aug. 18, 2002 9:08 pm:
+         Hm, I keep getting the same kind of error when trying these example calls to 'getId()' etc.:
+         <Fault -1: 'Unexpected Zope exception: cannot marshal <extension class Acquisition.ImplicitAcquirerWrapper at
+         401a8840> objects'>
+         So this is the kind of Zope object that won't be returned? I'd love to see some *working* examples of XML-RPC
+         in Zope (please).
+
+        % godefroy - May 27, 2004 6:29 pm:
+         The simpliest possible usage of XMLRPC in Zope:
+         As you probably know, "files" are folders' methods in Zope. So, you can put a Page Template in root
+         directory, and name it abc. Now abs is a method of / folder. Later you can put something more dynamic instead
+         of Page Template. For now, just delete contents of abc Page Template and replace them with single line of
+         text: "hello world".
+         This is a working example of XMLRPC Server for Zope. Cool, isn't it?
+         To test it:
+         1) run python
+         2) issue commands: 
+         >>> import xmlrpclib
+         >>> s = xmlrpclib.Server('http://localhost:8080/')
+         >>> s.abc()
+         3) you should get methods result:
+         'hello, world\n'
+         (Assumed that Zope is on localhost:8080, and you have abc method (file, page template, whatever) in root
+         directory.)
+
+      XML-RPC does not support keyword arguments. This is a problem if
+      your method expect keyword arguments.  This problem is
+      noticeable when calling DTMLMethods and DTMLDocuments with XML-RPC. 
+      Normally a DTML object should be called with the request as the 
+      first argument, and additional variables as keyword arguments. 
+      You can get around this problem by passing a dictionary as the 
+      first argument. This will allow your DTML methods and documents 
+      to reference your variables with the 'var' tag. 
+      However, you cannot do the following::
+
+        <dtml-var expr="REQUEST['argument']">
+
+      Although the following will work::
+
+        <dtml-var expr="_['argument']">
+
+      This is because in this case arguments *are* in the DTML
+      namespace, but they are not coming from the web request.
+
+      In general it is not a good idea to call DTML from XML-RPC since
+      DTML usually expects to be called from normal HTTP requests.
+
+      One thing to be aware of is that Zope returns 'false' for
+      published objects which return None since XML-RPC has no concept
+      of null.
+
+      Another issue you may run into is that 'xmlrpclib' does not yet
+      support HTTP basic authentication. This makes it difficult to
+      call protected web resources. One solution is to patch
+      'xmlrpclib'. Another solution is to accept authentication
+      credentials in the signature of your published method.
+
+        % Anonymous User - Dec. 5, 2003 6:12 pm:
+         This is false. At least with the most recent version of Zope/CMF/Plone (Plone 1.5), basic authentication is
+         supported and working. (I just tested it.)
+
+        % panxing_personal - Feb. 15, 2005 1:44 am:
+         could zope use corba to communate with other services? Thanks a lot!
+
+  Summary
+
+    Object publishing is a simple and powerful way to bring objects to
+    the web. Two of Zope's most appealing qualities is how it maps
+    objects to URLs, and you don't need to concern yourself with web
+    plumbing. If you wish, there are quite a few details that you can
+    use to customize how your objects are located and published.
+
+      % Anonymous User - Nov. 4, 2002 1:11 pm:
+       Note additional documentation for Zope 2.6+ wrt Unicode and object publishing is at
+       http://www.zope.org/Members/htrd/howto/unicode-zdg-changes and
+       http://www.zope.org/Members/htrd/howto/unicode.

Added: zdgbook/trunk/Outline.stx
===================================================================
--- zdgbook/trunk/Outline.stx	                        (rev 0)
+++ zdgbook/trunk/Outline.stx	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,162 @@
+Outline
+
+  Introduction
+
+    Covers audience, topic, and scope.  Gives brief description of the
+    developers guide and what goals the guide tries to acomplish.  Gives
+    simple chapter by chapter overview of entire guide.
+
+      % mcdonc - Oct. 12, 2001 5:15 pm - I wonder if this chapter should be taken out.  It's sort of useless at this point.
+
+  1. Interfaces
+
+    Author: MichelP
+
+    Zope is moving toward a more "self-documenting" model, where Zope
+    component describe themselves with interfaces.  Many of the prose
+    descriptions and examples in this guide will be working with these
+    kinds of components.  This chapter gives a brief overview of Zope
+    interfaces, how they describe Zope components, and how developers can
+    create their own interfaces.
+
+      % Anonymous User - Nov. 9, 2002 7:38 am:
+       if selfdocumenting, take care to give the resp. source *.py-files and explain some code.
+
+    This section is meant to enable the reader to discover the Zope "API"
+    for themselves.  One of the goals of this guide is *not* to be an
+    exhaustive descrption of the Zope API, that can be found in the online
+    help system and from Zope objects through their interfaces.
+
+    The majority of the content of this chapter will come from the
+    "Interface
+    documentation":http://www.zope.org/Wikis/Interfaces/InterfaceUserDocumentation
+
+      1. What are interfaces, why are they useful?
+
+      2. Reading interfaces
+
+      3. Using and testing interfaces
+
+      4. Defining interfaces
+
+  2. Publishing
+
+    Author: AmosL
+
+    One key facility that Zope provides for a component developer is access
+    to a component through various network protocols, like HTTP.  While a
+    component can be designed to work exclusivly with other components
+    through Python only interfaces, most components are designed to be used
+    and managed through a network interface, most commonly HTTP.
+
+    Zope provides network access to components by "publishing" them through
+    various network interfaces like HTTP, FTP, WebDAV and XML-RPC.  This
+    chapter describes how a component developer can publish their
+    components "through the web" and other network protocols.
+
+      1. Object publishing overview
+
+      2. Traversal
+
+      3. Network Protocols
+
+      4. Publishable Interfaces
+
+      5. Object marshalling
+
+      6. Creating user interfaces
+
+        * with DTMLFile
+
+        * with presentation templates
+
+  3. Products
+
+    Author: AmosL/ShaneH
+
+    Zope defines a system that allows component developers to distribute
+    their components to other Zope users.  Components can be placed into a
+    package called a "Product".  Products can be created either through the
+    web, or in Python.  Through the web products are covered in *The Zope
+    Book*, and this chapter describes the more advanced Python product
+    interfaces that developers can use to distribute their Python-based
+    components.
+
+    The majority of the content of this chapter will come from
+    Amos/Shane's "Product
+    Tutorial":http://www.zope.org/Members/hathawsh/PythonProductTutorial
+
+      1. Introduction
+
+      2. Development Process
+
+      3. Product Architecture
+
+      4. Building Product Classes
+
+      5. Building Management Interfaces
+
+      6. Packaging Products
+
+      7. Evolving Products
+
+  4. Persistence
+
+    Author: MichelP  
+
+    Most Zope components live in the Zope Object DataBase (ZODB).
+    Components that are stored in ZODB are called *persistent*.  Creating
+    persistent components is, for the most part, a trivial exercise, but
+    ZODB does impose a few rules that persistent components must obey in
+    oder to work properly.  This chapter describes the persistent model and
+    the interfaces that persistent objects can use to live inside the
+    ZODB.
+
+      1. Persistence Architecture
+
+      2. Using Persistent components
+
+      3. Creating Persistent Objects
+
+      4. Transactions
+
+  5. Security
+
+    Author: ChrisM
+
+    Zope has a very fine-grained, uniquely powerful security model.  This
+    model allows Zope developers to create components that work safely in
+    an environment used by many different users, all with varying levels of
+    security privledge.
+
+    This section describes Zope's security model and how component
+    developers can work with it and manipulate it to define security
+    policies specific to their needs or the needs of their components.
+
+    The majority of the content of this chapter will come from Chris'
+    first cut at "Security
+    documentation":http://www.zope.org/Members/mcdonc/PDG/6-1-Security.stx
+
+      1. Security architecture
+
+      2. Using protected components
+
+      3. Implementing Security in your Component
+
+      4. Security Policies
+
+  6. Debugging and Testing
+
+    Author: Beehive
+
+    Covers debugging Zope and unit testing.     
+
+      * pdb debugging
+
+      * Control Panel debug view
+
+      * -D z2.py switch
+
+      * unit testing
+
+        * zope fixtures for unit testing

Added: zdgbook/trunk/Products.stx
===================================================================
--- zdgbook/trunk/Products.stx	                        (rev 0)
+++ zdgbook/trunk/Products.stx	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,1846 @@
+Chapter 3: Zope Products
+
+  Introduction
+
+    Zope *products* extend Zope with new functionality. Products most
+    often provide new addable objects, but they can also extend Zope
+    with new DTML tags, new ZClass base classes, and other services.
+
+      % Anonymous User - Nov. 8, 2002 8:49 pm:
+       Zope has an online help system that you can use. Its main features are context-sensitive help and API help.
+       You might also inspect the python source files.
+       Additionally there are mailing lists and mailing list archives which can be searched (at NIP or activestate).
+
+      % Anonymous User - Nov. 10, 2002 9:19 am:
+       API Reference http://www.zope.org/Documentation/Books/ZopeBook/current/AppendixB.stx
+
+      % Anonymous User - May 7, 2004 11:55 am:
+       The width of this page in my browser is huge. This is not the case for other books (Zope Book) on this site. I'm
+       using Mac/Safari
+
+      % Anonymous User - July 15, 2004 2:05 am:
+       I know this is offtopic, but the width was just bugging the hell out of me too. On Mac Safari, Mac Mozilla,
+       and GNU/Linux (Debian) Firefox. I'm about to download the PDF. Maybe there will be fewer offtopic posts on
+       that ;)
+
+    There are two ways to create products in Zope, through the web, and
+    with files in the filesystem. In this chapter we are going to look
+    at building products on the file system. For information on through
+    the web products, and ZClasses see the *Zope Book*, Chapter 12. 
+
+      % tibi - Feb. 22, 2002 6:25 am - Right now it's chapter 14, better use chapter name "Extending Zope"
+
+      % jancolpaert - May 5, 2002 4:51 am:
+       I think that at this stage we need to make clear whether it is possible to 
+       have a mix between both models (on the file system and on the web). Since both 
+       models have their advantages. If such a mix is not possible, we should state 
+       this clearly here since a lot of users might waste their time finding a 
+       solution for this.
+
+      % Anonymous User - Nov. 23, 2003 4:13 pm:
+       xcxcxcxcx
+
+    In comparison to through the web products, filesystem products
+    require more overhead to build, but offer more power and
+    flexibility, and they can be developed with familiar tools such as
+    Emacs and CVS.
+
+      % to_be - Oct. 31, 2002 9:47 am:
+       Newbies should be warned that web products might not run in the next Zope version. I just tried to import our
+       product (developed using v2.5.1) in Zope 2.6.0, and it didn't work. I tried to re-implement it, and it didn't
+       work because of a bug with nested ZClasses.
+
+    Soon we will make the examples referenced in this chapter
+    available for download as an example product.  Until that time,
+    you will see references to files in this chapter that are not
+    available yet.  This will be made available soon.
+
+      % Anonymous User - Jan. 15, 2003 5:04 pm:
+       Are these files already available? That would be real great to get started. Please point out if these
+       examples already come with zope (would be real nice) or if downloadable seperable please point out the URL.
+       djrap
+
+  Development Process
+
+    This chapter begins with a discussion of how you will develop products.
+    We'll focus on common engineering tasks that you'll encounter as you
+    develop products.
+
+    Consider Alternatives
+
+      Before you jump into the development of a product you should
+      consider the alternatives. Would your problem be better solved
+      with ZClasses, External Methods, or Python Scripts? Products excel
+      at extending Zope with new addable classes of objects. If this
+      does not figure centrally in your solution, you should look
+      elsewhere. Products, like External Methods allow you to write
+      unrestricted Python code on the filesystem.
+
+        % Anonymous User - Nov. 17, 2002 6:01 am:
+         Considering the dearth of 'background' material on Zope approaches in general, and relating to Products in
+         particular, I would find it hugely useful for someone knowlegeable to expand this section to discuss these
+         alternatives at much greater length/depth. Please. Pretty please.
+
+        % Anonymous User - Dec. 4, 2002 5:46 pm:
+         Here is what I did.
+         I started with ZClasses.  That was really easy to do, and got me a long way.
+         Then I added some Python extensions, really simple function calls using the 
+         full security allowed to file system based software.
+         Now I am starting to use simple python classes that implement just the application logic, and include
+         persistance. They are then inherited by Zclasses
+         that bring to bear the full zope functionality in a black box format.
+         Then I will move to full python based products.
+         Regards
+         Christopher Lozinski
+         http://Python.jobmart.com
+
+    Starting with Interfaces
+
+      The first step in creating a product is to create one or more
+      interfaces which describe the product. See Chapter 1 for more
+      information on interfaces and how to create them.
+
+      Creating interfaces before you build an implementation is a good
+      idea since it helps you see your design and assess how well it
+      fulfills your requirements.
+
+      Consider this interface for a multiple choice poll component (see
+      "Poll.py":examples/Poll.py)::
+
+        from Interface import Base
+
+        class Poll(Base):
+            "A multiple choice poll"
+
+            def castVote(self, index):
+                "Votes for a choice"
+
+            def getTotalVotes(self):
+                "Returns total number of votes cast"
+
+            def getVotesFor(self, index):
+                "Returns number of votes cast for a given response"
+
+            def getResponses(self):
+                "Returns the sequence of responses"
+
+            def getQuestion(self):
+                "Returns the question
+
+        % dshafer - June 2, 2002 4:21 pm:
+         The last comment in the interface is missing a closing quotation mark.
+
+        % Anonymous User - Feb. 11, 2003 7:10 pm:
+         Is it possible to use a variable to hold the value of the question ? I mean is the use of a method to access
+         so simple information mandatory ?
+
+        % Anonymous User - Apr. 15, 2004 9:43 am:
+         Interfaces are supposed to only have methods, and no variables.
+
+      How you name your interfaces is entirely up to you. Here we've
+      decided not to use an "I" or any other special indicator in the
+      name of the interface. 
+
+    Implementing Interfaces
+
+      After you have defined an interface for your product, the next
+      step is to create a prototype in Python that implements your
+      interface.
+
+        % Anonymous User - Jan. 7, 2002 2:34 pm - typo: "Here is a prototype of a PollImplemtation class that implements the interface you just examined" ... PollImplementation would be correct
+
+      Here is a prototype of a 'PollImplemtation' class that
+      implements the interface you just examined (see
+      "PollImplementation.py":examples/PollImplementation.py)::
+
+        from Poll import Poll
+
+        class PollImplementation:
+            """
+            A multiple choice poll, implements the Poll interface.
+
+            The poll has a question and a sequence of responses. Votes
+            are stored in a dictionary which maps response indexes to a
+            number of votes.
+            """
+
+            __implements__=Poll
+
+            def __init__(self, question, responses):
+                self._question = question
+                self._responses = responses
+                self._votes = {}
+                for i in range(len(responses)):
+                    self._votes[i] = 0
+
+            def castVote(self, index):
+                "Votes for a choice"
+                self._votes[index] = self._votes[index] + 1
+
+            def getTotalVotes(self):
+                "Returns total number of votes cast"
+                total = 0
+                for v in self._votes.values():
+                    total = total + v
+                return total
+
+            def getVotesFor(self, index):
+                "Returns number of votes cast for a given response"
+                return self._votes[index]
+
+            def getResponses(self):
+                "Returns the sequence of responses"
+                return tuple(self._responses)
+
+            def getQuestion(self):
+                "Returns the question"
+                return self._question
+
+        % Anonymous User - June 4, 2002 7:42 am:
+         Typo:  Here is a prototype of a _PollImplementation_ ...
+
+      You can use this class interactively and test it. Here's an
+      example of interactive testing::
+
+        >>> from PollImplementation import PollImplementation
+        >>> p=PollImplementation("What's your favorite color?", ["Red", "Green", "Blue", "I forget"])
+        >>> p.getQuestion()
+        "What's your favorite color?"
+        >>> p.getResponses()
+        ('Red', 'Green', 'Blue', 'I forget')
+        >>> p.getVotesFor(0)
+        0
+        >>> p.castVote(0)
+        >>> p.getVotesFor(0)
+        1
+        >>> p.castVote(2)
+        >>> p.getTotalVotes()
+        2
+        >>> p.castVote(4)
+        Traceback (innermost last):
+        File "<stdin>", line 1, in ?
+        File "PollImplementation.py", line 23, in castVote
+        self._votes[index] = self._votes[index] + 1
+        KeyError: 4          
+
+      Interactive testing is one of Python's great features. It lets you
+      experiment with your code in a simple but powerful way.
+
+        % Anonymous User - Dec. 11, 2001 9:07 pm - I'm having problems getting the interactive code to work with python 2.1.1 .  do you need more includes?  The interpreter says:  "No module named Interface"  was the Interface module included in a later version, or is this just a zope thing?
+
+        % Anonymous User - Dec. 12, 2001 3:51 pm - I'm just supporting previous blame. Same problem here()Python 2.1.1 (Win) Zope 2.5)
+
+        % Anonymous User - Jan. 7, 2002 3:20 pm - Yes, this is a Zope thing. It resides in {ZOPEHOME}/lib/python. Let's assume your ZOPEHOME is /home/Zope. Then you might want to start your interactive session like this:<br><br>  import sys<br>  sys.path.append('/home/Zope/lib/python')<br><br>  et voila! :-)
+
+        % Anonymous User - Sep. 2, 2002 9:29 pm:
+         Don't you need some kind of __init__.py in there somewhere, too?
+
+        % Anonymous User - Sep. 11, 2002 3:46 am:
+         no
+
+      At this point you can do a fair amount of work, testing and
+      refining your interfaces and classes which implement them. See
+      Chapter 7 for more information on testing.
+
+      So far you have learned how to create Python classes that are
+      documented with interfaces, and verified with testing. Next you'll
+      examine the Zope product architecture. Then you'll learn how to
+      fit your well crafted Python classes into the product framework.
+
+  Building Product Classes
+
+    To turn a component into a product you must fulfill many
+    contracts. For the most part these contracts are not yet defined
+    in terms of interfaces. Instead you must subclass from base
+    classes that implement the contracts. This makes building products
+    confusing, and this is an area that we are actively working on
+    improving.
+
+    Base Classes
+
+      Consider an example product class definition::
+
+        from Acquisition import Implicit
+        from Globals import Persistent
+        from AccessControl.Role import RoleManager
+        from OFS.SimpleItem import Item
+
+        class PollProduct(Implicit, Persistent, RoleManager, Item):
+            """
+            Poll product class
+            """
+            ...
+
+        % rboylan - July 24, 2002 1:42 am:
+         Somewhere you should clue people in that they probably want to use SimpleItem. It might also be worth noting
+         that SimpleItem is in fact less simple than Item!
+
+      The order of the base classes depends on which classes you want
+      to take precedence over others.  Most Zope classes do not define
+      similar names, so you usually don't need to worry about what
+      order these classes are used in your product.  Let's take a look
+      at each of these base classes:
+
+        % Anonymous User - Jan. 9, 2002 7:19 am - Arrgh, that's not fair to your Newbie readers: it seems that (in an interactive session on the console) the import of Persisten fails if 'import ZODB' isn't issued before ...
+
+        % Anonymous User - Jan. 9, 2002 7:23 am - Hint for those who have Python 2 installed as part of the Zope installation (but the system installation is still 1.x): in the folder bin/ of the Zope installation is a python 2 executable which can be used for the interactive sessions as well.
+
+        % chrism - Jan. 15, 2002 10:54 pm - Yup, indeed "import ZODB" should be the first thing you do in an interactive session.  You should also consider starting Zope from within the Zope/lib/python directory to make life easy.  For the inevitable question "why?", the answer is "so it will work" ;-)
+
+        % wleftwich - May 30, 2002 7:55 am:
+         Easy way to use Python interpreter while writing Products
+         Somewhere in your Python path, make a file called e.g. zopeinit.py, containing:
+         import sys
+         sys.path.append('/pathToZope/lib/python')
+         import ZODB
+         Then you can start the Python interpreter in the directory
+         where you're writing the product, and say
+         >> from zopinit import *
+
+        % Anonymous User - Jan. 24, 2003 8:43 am:
+         previous comment, last line should be: >> from zopeinit import *
+
+        % Anonymous User - Mar. 6, 2004 7:56 am:
+         I have a debian install of zope which uses /var/... as the root for the zope
+         instance files, such as Data.fs. In my case, the extra that I had to do to start zope manually was:
+           os.environ['INSTANCE_HOME'] = "/var/lib/zope/instance/default"
+         before the 'import Zope' line. (roy)
+
+      'Acquisition.Implicit'
+
+        This is the normal acquisition base class. See the *API
+        Reference* for the full details on this class. Many Zope
+        services such as object publishing and security use acquisition,
+        so inheriting from this class is required for
+        products. Actually, you can choose to inherit from
+        'Acquisition.Explicit' if you prefer, however, it will prevent
+        folks from dynamically binding Python Scripts and DTML Methods
+        to instances of your class. In general you should subclass from
+        'Acquisition.Implicit' unless you have a good reason not to.
+
+          % Anonymous User - Jan. 16, 2003 4:57 am:
+           Where can I find the "API Reference" for these classes ???
+
+        XXX is this true?  I thought that any ExtensionClass.Base can
+        be acquired.  The Implicit and Explicit just control how the
+        class can acquire, not how it *is* acquired.
+
+      'Globals.Persistent'
+
+        This base class makes instances of your product
+        persistent. For more information on persistence and this class
+        see Chapter 4.
+
+        In order to make your poll class persistent you'll need to make
+        one change. Since '_votes' is a dictionary this means that it's
+        a mutable non-persistent sub-object. You'll need to let the
+        persistence machinery know when you change it::
+
+          def castVote(self, index):
+              "Votes for a choice"
+              self._votes[index] = self._votes[index] + 1
+              self._p_changed = 1
+
+        The last line of this method sets the '_p_changed' attribute
+        to 1.  This tells the persistence machinery that this object
+        has changed and should be marked as 'dirty', meaning that its
+        new state should be written to the database at the conclusion
+        of the current transaction.  A more detailed explanation is
+        given in the Persistence chapter of this guide. 
+
+          % Anonymous User - Aug. 24, 2004 7:10 pm:
+           Why is that line (self._p_changed = 1) omited in the packed example (downloadable under "Packaging Products"
+           -> Poll-1.0.tgz) and why is this package working anyway?
+
+          % Anonymous User - Aug. 24, 2004 7:15 pm:
+           Why is that line (self._p_changed = 1) omited in the packed example (downloadable under "Packaging Products"
+           -> Poll-1.0.tgz) and why is this package working anyway?
+
+      'OFS.SimpleItem.Item'
+
+        This base class provides your product with the basics needed to
+        work with the Zope management interface.  By inheriting from
+        'Item' your product class gains a whole host of features: the
+        ability to be cut and pasted, capability with management views,
+        WebDAV support, basic FTP support, undo support, ownership
+        support, and traversal controls.  It also gives you some
+        standard methods for management views and error display
+        including 'manage_main()'.  You also get the 'getId()',
+        'title_or_id()', 'title_and_id()' methods and the 'this()' DTML
+        utility method. Finally this class gives your product basic
+        *dtml-tree* tag support.
+       'Item' is really an
+        everything-but-the-kitchen-sink kind of base class. 
+
+          % Anonymous User - Dec. 10, 2002 9:32 am:
+           Shouldn't this be : 
+           from OFS.SimpleItem import SimpleItem;  ?
+
+          % Anonymous User - Dec. 10, 2002 9:49 am:
+           Delete please :)
+
+        'Item' requires that your class and instances have some
+        management interface related attributes.
+
+          'meta_type' -- This attribute should be a short string which
+          is the name of your product class as it appears in the product
+          add list. For example the poll product class could have a
+          'meta_type' of 'Poll'. 
+
+          'id' or '__name__' -- All 'Item' instances must have an 'id'
+          string attribute which uniquely identifies the instance within
+          it's container. As an alternative you may use '__name__'
+          instead of 'id'. 
+
+          'title' -- All 'Item' instances must have a 'title' string
+          attribute. A title may be an empty string if your instance
+          does not have a title.
+
+          % Anonymous User - Nov. 23, 2003 9:23 pm:
+           If you use __name__ instead of id, you need to mix in the Item_w__name__ class from OFS.SimpleItem
+
+        In order to make your poll class work correctly as an 'Item'
+        you'll need to make a few changes. You must add a 'meta_type'
+        class attribute, and you may wish to add an 'id' parameter to
+        the constructor::
+
+          class PollProduct(..., Item):
+
+              meta_type='Poll'
+              ...
+
+              def __init__(self, id, question, responses):
+                  self.id=id
+                  self._question = question
+                  self._responses = responses
+                  self._votes = {}
+                  for i in range(len(responses)):
+                      self._votes[i] = 0
+
+          % Anonymous User - Sep. 30, 2002 9:55 am:
+           I guess class PollProduct should have a title atrribute!?!
+
+          % Anonymous User - Oct. 9, 2002 4:52 am:
+           As I understand it: either a title='' attribute, so the class will never have a title, or add a title
+           parameter to the __init__ and assign it to self.title.
+
+         Finally, you should probably place 'Item' last in your list of
+         base classes. The reason for this is that 'Item' provides
+         defaults that other classes such as 'ObjectManager' and
+         'PropertyManager' override. By placing other base classes
+         before 'Item' you allow them to override methods in 'Item'.
+
+      'AccessControl.Role.RoleManager'
+
+        This class provides your product with the ability to have its
+        security policies controlled through the web. See Chapter
+        6 for more information on security policies and this
+        class.
+
+      'OFS.ObjectManager'
+
+        This base class gives your product the ability to contain other
+        'Item' instances. In other words, it makes your product class
+        like a Zope folder. This base class is optional. See the *API
+        Reference* for more details. This base class gives you
+        facilities for adding Zope objects, importing and exporting Zope
+        objects, WebDAV, and FTP. It also gives you the 'objectIds',
+        'objectValues', and 'objectItems' methods.
+
+          % Anonymous User - Oct. 1, 2003 5:58 am:
+           What that mean??
+
+        'ObjectManager' makes few requirements on classes that subclass
+        it. You can choose to override some of its methods but there is
+        little that you must do.
+
+        If you wish to control which types of objects can be contained
+        by instances of your product you can set the 'meta_types' class
+        attribute. This attribute should be a tuple of meta_types. This
+        keeps other types of objects from being created in or pasted
+        into instances of your product. The 'meta_types' attribute is
+        mostly useful when you are creating specialized container
+        products.
+
+          % Anonymous User - Mar. 27, 2002 8:04 pm - And what is 'a tuple of meta_types' ? A tuple of strings doesn't work...
+
+          % Anonymous User - June 19, 2002 10:09 am:
+           The following seems to work:
+           meta_types = ( { 'name' : 'Image', 'action' : 'manage_addImageForm', 'permission' : 'Add Image' } , )
+           Then you need to define the action 'manage_addImageForm' in your class:
+           from OFS.Image import Image
+           manage_addImageForm = Image.manage_addImageForm
+
+          % Anonymous User - Sep. 16, 2002 8:56 pm:
+           This method appears to add an object type, not restrict the types that can
+           be contained.  Does anyone have a method that actually works properly in Zope 
+           2.5.1?
+
+          % Anonymous User - Nov. 8, 2002 7:49 pm:
+           When making a special container product with a specialized set of addable
+           components (e.g. a report document with included db-generated images)
+           which should themselves be like products,
+           it maybe desired that these component products should be registered in the
+           container class only and not in zope, the main effect of this is that 
+           these component products wont appear in ordinary zope folder add lists
+           and are only addable in the master container and nowhere else.
+           It would be nice to have this specialized product registry explained also. blf
+
+          % Anonymous User - Feb. 23, 2003 3:05 am:
+           Here's a solution seems to work:
+           all_meta_types = ( { 'name' : 'Image', 'action' : 'manage_addImageForm', 'permission' : 'Add Image' } , )
+           Now the add-list will only show the products specified.
+
+      'OFS.PropertyManager'
+
+        This base class provides your product with the ability to have
+        user-managed instance attributes. See the *API Reference*
+        for more details. This base class is optional.
+
+          % Anonymous User - Jan. 15, 2002 7:58 am - where is this API reference? None seen so far.  Or do you mean Zope Book Appendix B?
+
+          % chrism - Jan. 15, 2002 10:54 pm - API reference is part of Zope help system.
+
+        Your class may specify that it has one or more predefined
+        properties, by specifying a '_properties' class attribute. For
+        example::
+
+          _properties=({'id':'title', 'type': 'string', 'mode': 'w'},
+                       {'id':'color', 'type': 'string', 'mode': 'w'},
+                       )
+
+          % rboylan - July 24, 2002 1:39 am:
+           Is this OK if you also inherit from Item, which has title as one of its magic variables? It seems to work for
+           me, and I suspect the example is meant to indicate it is OK. It might be worth noting explicitly.
+
+        The '_properties' structure is a sequence of dictionaries, where
+        each dictionary represents a predefined property. Note that if a
+        predefined property is defined in the '_properties' structure,
+        you must provide an attribute with that name in your class or
+        instance that contains the default value of the predefined
+        property.
+
+        Each entry in the '_properties' structure must have at least an
+        'id' and a 'type' key. The 'id' key contains the name of the
+        property, and the 'type' key contains a string representing the
+        object's type.  The 'type' string must be one of the values:
+        'float', 'int', 'long', 'string', 'lines', 'text', 'date',
+        'tokens', 'selection', or 'multiple section'. For more
+        information on Zope properties see the *Zope Book*.
+
+        For 'selection' and 'multiple selection' properties, you must
+        include an addition item in the property dictionary,
+        'select_variable' which provides the name of a property or
+        method which returns a list of strings from which the
+        selection(s) can be chosen. For example::
+
+          _properties=({'id' : 'favorite_color', 
+                        'type' : 'selection',
+                        'select_variable' : 'getColors'
+                       },
+                      )
+
+          % Anonymous User - Aug. 23, 2002 11:00 am:
+           Could we have an example of the property or method 'getColors' please? I cannot figure out how to create the
+           options list and nominate the default selected item.
+
+          % Anonymous User - Oct. 29, 2002 5:21 am:
+           getColors = ['red','blue','green']  
+           or a bit fancy  
+           def getColors(self):  
+             """return colors"""  
+             return somelistwithcolors
+
+        Each entry in the '_properties' structure may optionally provide
+        a 'mode' key, which specifies the mutability of the
+        property. The 'mode' string, if present, must be 'w', 'd', or 'wd'.
+
+          % groovehunter - Jan. 24, 2003 9:37 am:
+           În the ZWikiPage.py of the ZWiki product there are properties with mode 'r'. So i miss 'r' in the list above.
+           Or do i confuse something?
+
+          % SmileyChris - Feb. 1, 2005 4:00 pm:
+           I have noticed a lot of products use it but as far as I know, it's exactly the same as leaving it blank ('').
+
+        A 'w' present in the mode string indicates that the value of the
+        property may be changed by the user. A 'd' indicates that the user
+        can delete the property. An empty mode string indicates that the
+        property and its value may be shown in property listings, but that
+        it is read-only and may not be deleted.
+
+          % Anonymous User - Nov. 3, 2002 6:12 pm:
+           Example:
+           _properties=({'id' : 'favorite_color', 
+                         'type' : 'selection',
+                         'select_variable' : 'getColors'
+                        },
+                       )
+
+          % Anonymous User - Nov. 3, 2002 6:18 pm:
+           # Example of mode:
+           _properties=(
+                         {'id':'title', 'type':'string', 'mode':'w'},
+                         {'id' : 'favorite_color', 
+                          'type' : 'selection', 
+                          'select_variable' : 'getColors'
+                          'mode':'wd'},
+                       )
+           # In this case, we don't want the user to delete the property
+           # of 'title'. They can change it, but not remove it altogether.
+
+        Entries in the '_properties' structure which do not have a
+        'mode' item are assumed to have the mode 'wd' (writable and
+        deleteable).
+
+    Security Declarations
+
+      In addition to inheriting from a number of standard base
+      classes, you must declare security information in order to turn
+      your component into a product. See Chapter 6 for more
+      information on security and instructions for declaring security
+      on your components.
+
+        % Anonymous User - May 22, 2004 10:14 pm:
+         s/See Chapter 6/See Chapter 7/
+
+      Here's an example of how to declare security on the poll class::
+
+        from AccessControl import ClassSecurityInfo
+
+        class PollProduct(...):
+            ...
+
+            security=ClassSecurityInfo()
+
+            security.declareProtected('Use Poll', 'castVote')
+            def castVote(self, index):
+                ...
+
+            security.declareProtected('View Poll results', 'getTotalVotes') 
+            def getTotalVotes(self):
+                ...
+
+            security.declareProtected('View Poll results', 'getVotesFor') 
+            def getVotesFor(self, index):
+                ...
+
+            security.declarePublic('getResponses') 
+            def getResponses(self):
+                ...
+
+            security.declarePublic('getQuestion') 
+            def getQuestion(self):
+                ...
+
+        % Anonymous User - Oct. 19, 2005 4:56 am:
+         Where do I find AccessControl module?
+         -Shyam
+
+      For security declarations to be set up Zope requires that you
+      initialize your product class. Here's how to initialize your poll
+      class::
+
+        from Globals import InitializeClass
+
+        class PollProduct(...):
+           ...
+
+        InitializeClass(PollProduct)
+
+    Summary
+
+      Congratulations, you've created a product class. Here it is in all
+      its glory (see "PollProduct.py":examples/PollProduct.py)::
+
+        from Poll import Poll
+        from AccessControl import ClassSecurityInfo
+        from Globals import InitializeClass
+        from Acquisition import Implicit
+        from Globals import Persistent
+        from AccessControl.Role import RoleManager
+        from OFS.SimpleItem import Item
+
+        class PollProduct(Implicit, Persistent, RoleManager, Item):
+            """
+            Poll product class, implements Poll interface.
+
+            The poll has a question and a sequence of responses. Votes
+            are stored in a dictionary which maps response indexes to a
+            number of votes.
+            """
+
+            __implements__=Poll
+
+            meta_type='Poll'
+
+            security=ClassSecurityInfo()
+
+            def __init__(self, id, question, responses):
+                self.id=id
+                self._question = question
+                self._responses = responses
+                self._votes = {}
+                for i in range(len(responses)):
+                    self._votes[i] = 0
+
+            security.declareProtected('Use Poll', 'castVote')
+            def castVote(self, index):
+                "Votes for a choice"
+                self._votes[index] = self._votes[index] + 1
+                self._p_changed = 1
+
+            security.declareProtected('View Poll results', 'getTotalVotes') 
+            def getTotalVotes(self):
+                "Returns total number of votes cast"
+                total = 0
+                for v in self._votes.values():
+                    total = total + v
+                return total
+
+            security.declareProtected('View Poll results', 'getVotesFor') 
+            def getVotesFor(self, index):
+                "Returns number of votes cast for a given response"
+                return self._votes[index]
+
+            security.declarePublic('getResponses') 
+            def getResponses(self):
+                "Returns the sequence of responses"
+                return tuple(self._responses)
+
+            security.declarePublic('getQuestion') 
+            def getQuestion(self):
+                "Returns the question"
+                return self._question
+
+        InitializeClass(Poll)
+
+        % Anonymous User - Nov. 7, 2002 3:39 am:
+         Last line must be InitializeClass(PollProduct).
+
+      Now it's time to test your product class in Zope. To do this you
+      must register your product class with Zope.
+
+        % 1jerry - Nov. 15, 2001 8:41 pm - castVote above didn't work for me without "index = int(index)" after the line "Votes for a choice".  The web passes strings.  a[string] attempts a dictionary lookup.
+
+        % 1jerry - Nov. 19, 2001 10:15 pm - It seems *most* of the examples in this document have typos.  Make sure and read the comments, and/or search for poll-1.0 to find the pointer to download the correct examples.
+
+        % Anonymous User - Jan. 3, 2002 7:43 am - InitialiseClass(PollProduct) seems to have been replaced by InitialiseClass(Poll) in the full listing.
+
+        % Anonymous User - Jan. 3, 2002 7:44 am - That should be "Initialize", by the way.
+
+        % Anonymous User - Jan. 9, 2002 7:59 am - In the full listing, the line 'from Globals import InitializeClass' is missing
+
+  Registering Products
+
+    Products are Python packages that live in 'lib/python/Products'.
+    Products are loaded into Zope when Zope starts up. This process is
+    called *product initialization*. During product initialization, each
+    product is given a chance to register its capabilities with Zope.
+
+    Product Initialization
+
+      When Zope starts up it imports each product and calls the
+      product's 'initialize' function passing it a registrar object. The
+      'initialize' function uses the registrar to tell Zope about its
+      capabilities. Here is an example '__init__.py' file::
+
+        from PollProduct import PollProduct, addForm, addFunction
+
+        def initialize(registrar):
+            registrar.registerClass(
+                PollProduct, 
+                constructors = (addForm, addFunction),
+                )
+
+        % Anonymous User - Nov. 30, 2002 1:09 pm:
+         The advice was to put addForm and addFuction to the Poll module, but here we're trying to import them from
+         the PollProduct module. Can this work?
+
+      This function makes one call to the registrar object which
+      registers a class as an addable object.  The registrar figures
+      out the name to put in the product add list by looking at the
+      'meta_type' of the class. Zope also deduces a permission based
+      on the class's meta-type, in this case *Add Polls* (Zope
+      automatically pluralizes "Poll" by adding an "s").  The
+      'constructors' argument is a tuple of objects consisting of two
+      functions: an add form which is called when a user selects the
+      object from the product add list, and the add method which is
+      the method called by the add form. Note that these functions are
+      protected by the constructor permission.
+
+        % Anonymous User - Oct. 9, 2002 5:12 am:
+         Zope automatically pluralizes by adding an "s"?! Wow, the road to i18n-ization is still long...
+
+        % Anonymous User - Mar. 6, 2004 8:12 am:
+         '-ization' is redundant. i18n stands for 'internationalization' :)
+
+      Note that you cannot restrict which types of containers can
+      contain instances of your classes. In other words, when you
+      register a class, it will appear in the product add list in
+      folders if the user has the constructor permission.
+
+        % Anonymous User - Feb. 10, 2002 9:12 pm - Worse...  I've *LOOKED* in the API reference that can be found from the Zope Help system, and I don't see a mention of ProductRegistrar, nor does "search" find a reference to "registrar".  This is in 2.5.0 Zope.
+
+        % Anonymous User - Apr. 4, 2002 11:47 am:
+         To our opinion, registrar is a 'dummy' name for a parameter. It could be context or SantaClaus. The registrar
+         parameter contains the context of the object, isn't it.
+
+        % Anonymous User - June 2, 2002 2:00 am:
+         A little-known fact.  So little-known, in fact, that I've been unaware of it for three years:
+         Adding a "methods" attribute to your Product's __init__.py as a dictionary in the form:
+         methods = {'function': function, 'function2': function2}
+         Causes the functions to be available as attributes of the root Zope object! This means that they will be
+         available anywhere via acquisition, sort of as mini-singletons.
+         These functions must accept a single argument, which will be the object from which they are acquired.
+         I wish I knew about this a long time ago.
+
+        % Anonymous User - Aug. 9, 2002 8:01 am:
+         Another thing, important for readers and maybe a nice thing to mention here would be the visibility=None
+         thing, to prevent the product from showing up in the add list. I think it's not described anywhere.
+
+        % to_be - Nov. 26, 2002 7:01 am:
+         >>  a nice thing to mention here would be the visibility=None thing
+         I agree. It seems to be quite tricky. A good explanation of _implements_ and Interfaces would be nice. By the
+         way, does anyone know where I can find documentation regarding this topic?
+
+      See the *API Reference* for more information on the
+      'ProductRegistrar' interface.
+
+        % Anonymous User - Jan. 15, 2002 9:31 am - Again, where is this API Reference. Would be more welcomed than this book!
+
+        % Anonymous User - June 13, 2002 10:22 am:
+         That should be ProductContext instead of ProductRegistrar. I couldn't find the class in the reference
+         documentation either (at least not in Apendix B of the Zope Book) but the source file
+         lib/python/App/ProductContext.py is pretty well documented.
+         M. S.
+
+    Factories and Constructors
+
+      Factories allow you to create Zope objects that can be added to
+      folders and other object managers. Factories are discussed in
+      Chapter 12 of the *Zope Book*. The basic work a factory does is
+      to put a name into the product add list and associate a
+      permission and an action with that name. If you have the
+      required permission then the name will appear in the product add
+      list, and when you select the name from the product add list,
+      the action method will be called.
+
+      Products use Zope factory capabilities to allow instances of
+      product classes to be created with the product add list.  In the
+      above example of product initialization you saw how a factory is
+      created by the product registrar. Now let's see how to create the
+      add form and the add list.
+
+      The add form is a function that returns an HTML form that allows a
+      users to create an instance of your product class. Typically this
+      form collects that id and title of the instance along with other
+      relevant data. Here's a very simple add form function for the poll
+      class::
+
+        def addForm():
+            """
+            Returns an HTML form.
+            """
+            return """<html>
+            <head><title>Add Poll</title></head>
+            <body>   
+            <form action="addFunction">
+            id <input type="type" name="id"><br>
+            question <input type="type" name="question"><br>
+            responses (one per line)
+            <textarea name="responses:lines"></textarea>
+            </form>
+            </body>
+            </html>"""
+
+        % Anonymous User - May 11, 2002 2:15 am:
+         I think a <input type=submit> needs to be added to the above 
+         form.
+
+      Notice how the action of the form is 'addFunction'. Also notice
+      how the lines of the response are marshalled into a
+      sequence. See Chapter 2 for more information about argument
+      marshalling and object publishing.
+
+        % strobl - Oct. 18, 2001 6:00 pm - In addForm, the value of "type" is wrong. Replace type="type" by type="text". In context:    id <input type="type" name="id"><br>  question <input type="type" name="question"><br>    should be replaced by    id <input type="text" name="id"><br>  question <input type="text" name="question"><br>
+
+        % Anonymous User - Nov. 6, 2001 10:41 am - Using Zope 2.4 this function does not function properly.  I had to make following changes:  replace "def addForm():" with "def addForm(self):"  and adding  <input type="submit" value="Add">  before </form>
+
+        % Anonymous User - Dec. 20, 2001 10:09 am - "def AddForm(self)" might be missleading. The argument passed to AddFrom()  is a dispatcher-object. As AddForm is not a class-member, "self" is not provided.
+
+        % Anonymous User - Jan. 9, 2002 9:05 am - Note that addForm and addFunction are not methods of the PollProduct class.
+
+        % Anonymous User - June 13, 2002 11:48 am:
+         Under Zope 2.5.1,  an arguement IS passed to the addForm method.
+         From printing it out, i beleive it is a Dispatcher object.
+         (It's type is __FactoryDispatcher__)
+
+        % Anonymous User - Sep. 20, 2005 4:39 pm:
+         It is not clear where the addForm and addFuntion methods should go. Unlike Poll and PollProduct, a link is
+         not provided for __init__.py, or any other files that might contain these methods.
+
+      It's also important to include a HTML 'head' tag in the add
+      form. This is necessary so that Zope can set the base URL to make
+      sure that the relative link to the 'addFunction' works correctly.
+
+      The add function will be passed a 'FactoryDispatcher' as its
+      first argument which proxies the location (usually a Folder)
+      where your product was added. The add function may also be
+      passed any form variables which are present in your add form
+      according to normal object publishing rules.
+
+        % Anonymous User - Jan. 3, 2002 11:18 am - (below example) it might be a good idea to return some kind of confirmation message, in a similar fashion to that used in the previous constructor function.
+
+      Here's an add function for your poll class::
+
+        def addFunction(dispatcher, id, question, responses):
+            """
+            Create a new poll and add it to myself
+            """
+            p=PollProduct(id, question, responses)
+            dispatcher.Destination()._setObject(id, p)
+
+      The dispatcher has three methods:
+
+        'Destination' -- The 'ObjectManager' where your product was
+        added.
+
+        'DestinationURL' -- The URL of the 'ObjectManager' where your
+        product was added.
+
+        'manage_main' -- Redirects to a management view of the
+        'ObjectManager' where your product was added.
+
+      Notice how it calls the '_setObject()' method of the destination
+      'ObjectManager' class to add the poll to the folder. See the
+      *API Reference* for more information on the 'ObjectManager'
+      interface.
+
+        % Anonymous User - Jan. 15, 2002 9:43 am - Where is the API Reference. "_setObject()" is not explained in    http://www.zope.org/Members/michel/ZB/AppendixB.dtml#class ObjectManager
+
+        % zigg - Jan. 26, 2002 10:57 pm - All this blather about the ZB aside, _setObject also does not appear in the online API ref in my copy of Zope 2.5.0.
+
+        % Anonymous User - May 21, 2002 7:03 am:
+         _setObject() must be deprecated, because it's private -- it's not a piece of API.
+
+        % Anonymous User - May 21, 2002 9:34 am:
+         Sory, previous comment (my) isn't correct. _setObject() is OK and shouldn't be deprecated. The rest of the
+         comment is correct -- _setObject() is out of API, because it's private
+
+      The add function should also check the validity of its
+      input. For example the add function should complain if the
+      question or response arguments are not of the correct type.
+
+        % Zen - Mar. 23, 2002 2:03 am - The add function should also redirect the user back to the management screen. To do this, the add function needs 'REQUEST=None' as a keyword argument, and the following code:  if REQUEST is not None: return self.manage_main(self,REQUEST,update_menu=0) # update_menu should be 1 if we are an object manager.
+
+        % Anonymous User - June 13, 2002 3:49 pm:
+         Ok the add function should redirect but self here has no meaning, you must
+         add something like:
+         if REQUEST is not None:
+                     dispatcher.manage_main(dispatcher,REQUEST,update_menu=0)
+
+      Finally you should recognize that the constructor functions are
+      *not* methods on your product class. In fact they are called
+      before any instances of your product class are created. The
+      constructor functions are published on the web so they need to
+      have doc strings, and are protected by a permission defined in
+      during product initialization.
+
+        % Anonymous User - Feb. 15, 2002 3:35 pm - At this point I would like to see another COMPLETE source code summary that lists each file source code... to clarify where all these methods are declared... it's not exactly clear to someone who's never done this before.
+
+        % Anonymous User - Feb. 19, 2002 11:13 pm - Is it possible to define destructor for the product class? If the product instance does something outside zope it will probably have to clean up before being destroyed.
+
+        % Anonymous User - May 30, 2003 4:52 pm:
+         I've been searching everywhere to answer this questions!  I need destructors
+         and copy constructors as I've built a Product whose instances have a 1 to 1 
+         corelation with database tables.  Does anyone have any ideas?  I've been digging
+         through zope's python and have yet to turn anything up!
+
+    Testing
+
+      Now you're ready to register your product with Zope. You need to
+      add the add form and add method to the poll module. Then you
+      should create a 'Poll' directory in your 'lib/python/Products'
+      directory and add the 'Poll.py', 'PollProduct.py', and
+      '__init__.py' files. Then restart Zope.
+
+      Now login to Zope as a manager and visit the web management
+      interface. You should see a 'Poll' product listed inside the
+      *Products* folder in the *Control_Panel*. If Zope had trouble
+      initializing your product you will see a traceback here. Fix your
+      problems, if any and restart Zope. If you are tired of all this
+      restarting, take a look at the *Refresh* facility covered in
+      Chapter 7.
+
+      Now go to the root folder. Select *Poll* from the product add
+      list. Notice how you are taken to the add form. Provide an id, a
+      question, and a list of responses and click *Add*. Notice how you
+      get a black screen. This is because your add method does not
+      return anything. Notice also that your poll has a broken icon, and
+      only has the management views. Don't worry about these
+      problems now, you'll find out how to fix these problems in the next
+      section.
+
+        % Anonymous User - Nov. 6, 2001 10:45 am - I don't end with a black screen (Zope 2.4): the addForm remains visible.
+
+        % 1jerry - Nov. 19, 2001 10:04 pm - Since this doc leaves you hanging, to redirect the page add ,REQUEST=None as an additional parameter on the addFunction.  Then add this line to the bottom of addFunction:  if REQUEST is not None:  dispatcher.manage_main(dispatcher, REQUEST)
+
+      Now you should build some DTML Methods and Python Scripts to test
+      your poll instance. Here's a Python Script to figure out voting
+      percentages::
+
+        ## Script (Python) "getPercentFor"
+        ##parameters=index
+        ##
+        """
+        Returns the percentage of the vote given a response index. Note,
+        this script should be bound a poll by acquisition context.
+        """
+        poll=context
+        return float(poll.getVotesFor(index)) / poll.getTotalVotes()
+
+        % Anonymous User - Nov. 16, 2002 3:01 am:
+         Just exactly where should one add this Script?
+
+      % Anonymous User - Nov. 6, 2001 10:47 am - Entering above Script within Zope does not function.  Entering it as an external Python-file is not explained.  I am stuck at this point of the manual.
+
+      % 1jerry - Nov. 15, 2001 8:21 pm - This does not work because the first time the total votes will be 0.  Python does not like / 0.  Also, it shows a ratio, not percent.  Replace last line with these lines (<br> = <newline>):  if poll.getTotalVotes(): <br>    return "%d" % (float(poll.getVotesFor(index)) / poll.getTotalVotes()* 100) <br>  else: return 0
+
+      Here's a DTML Method that displays poll results and allows you to
+      vote::
+
+        <dtml-var standard_html_header>
+
+        <h2>
+          <dtml-var getQuestion>
+        </h2>
+
+        <form> <!-- calls this dtml method -->
+
+        <dtml-in getResponses>
+          <p>
+            <input type="radio" name="index" value="&dtml-sequence-index;">
+            <dtml-var sequence-item>
+          </p>
+        </dtml-in>
+
+        <input type="submit" value=" Vote ">
+
+        </form>
+
+        <!-- process form -->
+
+        <dtml-if index>
+          <dtml-call expr="castVote(index)">
+        </dtml-if>
+
+        <!-- display results -->
+
+        <h2>Results</h2>
+
+        <p><dtml-var getTotalVotes> votes cast</p>
+
+        <dtml-in getResponses>
+          <p>
+            <dtml-var sequence-item> - 
+            <dtml-var expr="getPercentFor(_.get('sequence-index'))">%
+          </p>
+        </dtml-in>
+
+        <dtml-var standard_html_footer>
+
+        % Anonymous User - Dec. 21, 2001 10:39 am - err function call
+
+      To use this DTML Method, call it on your poll instance. Notice how
+      this DTML makes calls to both your poll instance and the
+      'getPercentFor' Python script.
+
+        % Anonymous User - Nov. 6, 2001 10:50 am - This does not function anymore.  Zope blocks on the function "get" in the (last-4)-th line
+
+        % 1jerry - Nov. 15, 2001 8:26 pm - The above DTML Method also does not work.  Replace _.get('sequence-index') with _.getitem('sequence-index').
+
+        % 1jerry - Nov. 15, 2001 8:33 pm - Also, for beginners, "call it on your poll instance" means append the poll instance name (p1, if you named it that) and the name of the DTML Method (usepoll, or whatever you named it) to your URL.  I.E. if you placed your poll in your root of your site, use something like http://www.yoursitename.com/p1/usepoll as the address in your browser.
+
+        % groovehunter - Jan. 24, 2003 11:26 am:
+         I got the tarball and added the (corrected) script and dtml-method manually. 
+         It does not work: KeyError, Value is the index of the vote (0-4 in my case)::
+          File /usr/local/Zope-2.5.0-src/lib/python/DocumentTemplate/DT_Util.py, line 159, in eval
+             (Object: castVote(index))
+             (Info: index)
+           File <string>, line 2, in f
+           File /usr/local/Zope-2.5.0-src/lib/python/Products/Poll/PollProduct.py, line 88, in castVote
+             (Object: Poll)
+         KeyError: (see above)
+
+        % groovehunter - Jan. 24, 2003 3:51 pm:
+         It's me again. I did some debugging (good intro in the debugging chapter) and found out that 'index' is a
+         string in this function, so it can't be used as an index. - I added the line
+         index = int(index)
+         before 
+         self._votes[index] = self._votes[index] + 1
+         Now it works for me.
+
+      At this point there's quite a bit of testing and refinement that
+      you can do. Your main annoyance will be having to restart Zope
+      each time you make a change to your product class (but see
+      Chapter 7 for information on how to avoid all this
+      restarting). If you vastly change your class you may break
+      existing poll instances, and will need to delete them and create
+      new ones. See Chapter 7 for more information on debugging
+      techniques which will come in handy.
+
+        % Anonymous User - Nov. 8, 2002 8:15 pm:
+         ... if you vastly change: more specific: if you change the class signature
+         by adding/renaming/deleting class members (attribute or method) 
+         or any of the class methods parameter lists
+         product instances of previous trials will break.
+
+  Building Management Interfaces
+
+    Now that you have a working product let's see how to beef up its
+    user interface and create online management facilities.
+
+    Defining Management Views
+
+      All Zope products can be managed through the web. Products have a
+      collection of management tabs or *views* which allow managers to
+      control different aspects of the product.
+
+      A product's management views are defined in the 'manage_options'
+      class attribute. Here's an example::
+
+        manage_options=(
+            {'label' : 'Edit', 'action' : 'editMethod'},
+            {'label' : 'View', 'action' : 'viewMethod'},
+            )
+
+      The 'manage_options' structure is a tuple that contains
+      dictionaries. Each dictionary defines a management view. The view
+      dictionary can have a number of items.
+
+        'label' -- This is the name of the management view
+
+        'action' -- This is the URL that is called when the view is
+        chosen. Normally this is the name of a method that displays a
+        management view.
+
+        'target' -- An optional target frame to display the action. This
+        item is rarely needed.
+
+        'help' -- Optional help information associated with the
+        view. You'll find out more about this option later.
+
+        % Anonymous User - Nov. 8, 2002 8:17 pm:
+         /The view dictionary can have/Each dictionary can have/ blf
+
+      Management views are displayed in the order they are
+      defined. However, only those management views for which the
+      current user has permissions are displayed. This means that
+      different users may see different management views when managing
+      your product.
+
+      Normally you will define a couple custom views and reusing some
+      existing views that are defined in your base classes. Here's an
+      example::
+
+        class PollProduct(..., Item):
+            ...
+
+            manage_options=(
+                {'label' : 'Edit', 'action' : 'editMethod'},
+                {'label' : 'Options', 'action' : 'optionsMethod'},
+                ) + RoleManager.manage_options + Item.manage_options
+
+      This example would include the standard management view defined
+      by 'RoleManager' which is *Security* and those defined by 'Item'
+      which are *Undo* and *Ownership*. You should include these
+      standard management views unless you have good reason not to. If
+      your class has a default view method ('index_html') you should
+      also include a *View* view whose action is an empty string. See
+      Chapter 2 for more information on 'index_html'.
+
+      Note: you should not make the *View* view the first view on your
+      class. The reason is that the first management view is displayed when
+      you click on an object in the Zope management interface. If the
+      *View* view is displayed first, users will be unable to navigate
+      to the other management views since the view tabs will not be
+      visible.
+
+    Creating Management Views
+
+      The normal way to create management view methods is to use
+      DTML. You can use the 'DTMLFile' class to create a DTML Method
+      from a file. For example::
+
+        from Globals import DTMLFile
+
+        class PollProduct(...):
+          ...
+
+          editForm=DTMLFile('dtml/edit', globals())
+          ...
+
+      This creates a DTML Method on your class which is defined in the
+      'dtml/edit.dtml' file. Notice that you do not have to include the
+      '.dtml' file extension. Also, don't worry about the forward slash
+      as a path separator; this convention will work fine on Windows. By
+      convention DTML files are placed in a 'dtml' subdirectory of your
+      product. The 'globals()' argument to the 'DTMLFile' constructor
+      allows it to locate your product directory. If you are running
+      Zope in debug mode then changes to DTML files are reflected right
+      away. In other words you can change the DTML of your product's
+      views without restarting Zope to see the changes.
+
+        % Anonymous User - Dec. 9, 2003 5:50 pm:
+         <quote>don't worry about the forward slash as a path separator; this convention will work fine on
+         Windows</quote> but not in my case (Plone 1.0.5 on Win2000). I would therefore recommend not to use a
+         dtml-folder. (That works.)
+
+      DTML class methods are callable directly from the web, just like
+      other methods. So now users can see your edit form by calling the
+      'editForm' method on instances of your poll class. Typically DTML
+      methods will make calls back to your instance to gather
+      information to display. Alternatively you may decide to wrap your
+      DTML methods with normal methods. This allows you to calculate
+      information needed by your DTML before you call it. This
+      arrangement also ensures that users always access your DTML
+      through your wrapper. Here's an example::
+
+        from Globals import DTMLFile
+
+        class PollProduct(...):
+          ...
+
+          _editForm=DTMLFile('dtml/edit', globals())
+
+          def editForm(self, ...):
+              ...
+
+              return self._editForm(REQUEST, ...)
+
+        % Anonymous User - Nov. 8, 2002 8:24 pm:
+         Please beef up example with parameters to the dtml like
+             return self._editForm(REQUEST, a, b, c)
+
+      % Anonymous User - Jan. 9, 2002 2:07 pm - The following code doesn't work for two reasons: 1) you have declare editForm with the REQUEST parameter: def editForm(self,REQUEST,...) 2) editForm is a published Method so it needs a doc string, see Chapter 2
+
+      When creating management views you should include the DTML
+      variables 'manage_page_header' and 'manage_tabs' at the top, and
+      'manage_page_footer' at the bottom.  These variables are acquired
+      by your product and draw a standard management view header, tabs
+      widgets, and footer. The management header also includes CSS
+      information which you can take advantage of if you wish to add CSS
+      style information to your management views. The management CSS
+      information is defined in the
+      'lib/python/App/dtml/manage_page_style.css.dtml' file. Here are
+      the CSS classes defined in this file and conventions for their
+      use.
+
+        'form-help' -- Explanatory text related to forms. In the future,
+        users may have the option to hide this text.
+
+        'std-text' -- Declarative text unrelated to forms. You should
+        rarely use this class.
+
+        'form-title' -- Form titles.
+
+        'form-label' -- Form labels for required form elements. 
+
+        'form-optional' -- Form labels for optional form elements.
+
+        'form-element' -- Form elements. Note, because of a Netscape
+        bug, you should not use this class on 'textarea' elements.
+
+        'form-text' -- Declarative text in forms.
+
+        'form-mono' -- Fixed width text in forms. You should rarely use
+        this class.
+
+      Here's an example management view for your poll class. It allows
+      you to edit the poll question and responses (see
+      'editPollForm.dtml')::
+
+        <dtml-var manage_page_header>
+        <dtml-var manage_tabs>
+
+        <p class="form-help">
+        This form allows you to change the poll's question and
+        responses. <b>Changing a poll's question and responses
+        will reset the poll's vote tally.</b>.
+        </p>
+
+        <form action="editPoll">
+        <table>
+
+          <tr valign="top">
+            <th class="form-label">Question</th>
+            <td><input type="text" name="question" class="form-element"
+            value="&dtml-getQuestion;"></td>
+          </tr>
+
+          <tr valign="top">
+            <th class="form-label">Responses</th>
+            <td><textarea name="responses:lines" cols="50" rows="10">
+            <dtml-in getResponses>
+            <dtml-var sequence-item html_quote>
+            </dtml-in>
+            </textarea>
+            </td>
+          </tr>
+
+          <tr>
+            <td></td>
+            <td><input type="submit" value="Change" class="form-element"></td>
+          </tr>
+
+        </table>
+        </form>
+
+        <dtml-var manage_page_header>
+
+      This DTML method displays an edit form that allows you to change
+      the questions and responses of your poll. Notice how poll
+      properties are HTML quoted either by using 'html_quote' in the
+      'dtml-var' tag, or by using the 'dtml-var' entity syntax.
+
+        % Anonymous User - Dec. 26, 2001 6:06 pm - the last tag in the script above should be "<dtml-var manage_page_footer>".
+
+      Assuming this DTML is stored in a file 'editPollForm.dtml' in your
+      product's 'dtml' directory, here's how to define this method on
+      your class::
+
+        class PollProduct(...):
+            ...
+
+            security.declareProtected('View management screens', 'editPollForm')
+            editPollForm=DTML('dtml/editPollForm', globals())
+
+      Notice how the edit form is protected by the 'View management
+      screens' permission. This ensures that only managers will be able
+      to call this method.
+
+        % berniey - Nov. 6, 2001 9:19 pm - Should '''editPollForm=DTML('dtml/editPollForm', globals())''' become  '''editPollForm=DTMLFile('dtml/editPollForm', globals())''' ?
+
+        % Anonymous User - Jan. 13, 2002 6:28 pm - yes
+
+      Notice also that the action of this form is 'editPoll'. Since
+      the poll as it stands doesn't include any edit methods you must
+      define one to accept the changes. Here's an 'editPoll' method::
+
+        class PollProduct(...):
+            ...
+
+            def __init__(self, id, question, responses):
+                self.id=id
+                self.editPoll(question, response)
+
+            ...
+
+            security.declareProtected('Change Poll', 'editPoll')
+            def editPoll(self, question, responses):
+                """
+                Changes the question and responses.
+                """
+                self._question = question
+                self._responses = responses
+                self._votes = {}
+                for i in range(len(responses)):
+                    self._votes[i] = 0
+
+      Notice how the '__init__' method has been refactored to use the
+      new 'editPoll' method. Also notice how the 'editPoll' method
+      is protected by a new permissions, 'Change Poll'.
+
+        % Anonymous User - Jan. 9, 2002 12:01 pm - in the above __init__ method, the second argument to self.editPoll should be 'responses' (plural)
+
+      There still is a problem with the 'editPoll' method. When you call
+      it from the 'editPollForm' through the web nothing is
+      returned. This is a bad management interface. You want this method
+      to return an HTML response when called from the web, but you do
+      not want it to do this when it is called from '__init__'. Here's
+      the solution::
+
+        class Poll(...):
+            ...
+
+            def editPoll(self, question, responses, REQUEST=None):
+                """
+                Changes the question and responses.
+                """
+                self._question = question
+                self._responses = responses
+                self._votes = {}
+                for i in range(len(responses)):
+                    self._votes[i] = 0
+                if REQUEST is not None:
+                    return self.editPollForm(REQUEST,
+                        manage_tabs_message='Poll question and responses changed.')
+
+        % rboylan - July 24, 2002 1:49 am:
+         In general, if you want to end up on a certain tab, how do you do it? Are there any conventions or standards?
+         Working it out now seems to require cruising around the base classes to try to find the right tab, and then
+         tracing around some more to find the actual filename that gives the contents of the tab.
+
+        % Anonymous User - Nov. 8, 2002 8:34 pm:
+         wouldnt a redirect do better since it refreshes the client browsers url?
+         (but then of course the tabs_message would not appear...)
+
+      If this method is called from the web, then Zope will
+      automatically supply the 'REQUEST' parameter. (See chapter 2 for
+      more information on object publishing). By testing the 'REQUEST'
+      you can find out if your method was called from the web or not. If
+      you were called from the web you return the edit form again.
+
+      A management interface convention that you should use is the
+      'manage_tab_message' DTML variable. If you set this variable when
+      calling a management view, it displays a status message at the top
+      of the page. You should use this to provide feedback to users
+      indicating that their actions have been taken when it is not
+      obvious. For example if you don't return a status message from
+      your 'editPoll' method, users may be confused and may not realize
+      that their changes have been made.
+
+      Sometimes when displaying management views, the wrong tab will be
+      highlighted. This is because 'manage_tabs' can't figure out from
+      the URL which view should be highlighted. The solution is to set
+      the 'management_view' variable to the label of the view that
+      should be highlighted. Here's an example, using the 'editPoll'
+      method::
+
+        def editPoll(self, question, responses, REQUEST=None):
+            """
+            Changes the question and responses.
+            """
+            self._question = question
+            self._responses = responses
+            self._votes = {}
+            for i in range(len(responses)):
+                self._votes[i] = 0
+            if REQUEST is not None:
+                return self.editPollForm(REQUEST,
+                    management_view='Edit',
+                    manage_tabs_message='Poll question and responses changed.')
+
+      Now let's take a look a how to define an icon for your product.
+
+    Icons
+
+      Zope products are identified in the management interface with
+      icons. An icon should be a 16 by 16 pixel GIF image with a
+      transparent background. Normally icons files are located in a
+      'www' subdirectory of your product package. To associate an icon
+      with a product class, use the 'icon' parameter to the
+      'registerClass' method in your product's constructor. For
+      example::
+
+        def initialize(registrar):
+            registrar.registerClass(
+                PollProduct, 
+                constructors = (addForm, addFunction),
+                icon = 'www/poll.gif'
+                )
+
+        % Anonymous User - Nov. 19, 2002 11:01 am:
+         Would a PNG be acceptable/ preferable?
+
+        % Anonymous User - June 7, 2005 7:09 am:
+         unfortunately, not all browsers support PNGs. Sad but true.
+
+       Notice how in this example, the icon is identified as being
+       within the product's 'www' subdirectory.
+
+       See the *API Reference* for more information on the 'registerClass'
+       method of the 'ProductRegistrar' interface.
+
+    Online Help
+
+      Zope has an online help system that you can use to provide help
+      for your products. Its main features are context-sensitive help
+      and API help. You should provide both for your product.
+
+        % Anonymous User - Nov. 8, 2002 8:45 pm:
+         obviously there were 3 forward refs to the ominous API Reference (or is it API help? glossary drift?).
+         Better give this info at the start of this chapter. 
+         Additionally, give the python files of zope where the resp. functionality is defined (after all, this is Open
+         Source). Many insights will be gained inspecting the source. Part of the zope experience is to learn where to
+         look for help. part of the mailing list traffic would become obsolete if people knew howto search the
+         archives.
+
+        % Anonymous User - Nov. 16, 2002 3:10 am:
+         I echo the plea for some concise information/pointers on how and where to search for stuff. I'm more than
+         happy to go look for stuff (that's part of a programmers job, after all?) but it has taken me an age to get a
+         basic grip on all the various (scattered) sources for said stuff.
+
+      Context Sensitive Help
+
+        To create context sensitive help, create one help file per
+        management view in your product's 'help' directory. You have a
+        choice of formats including: HTML, DTML, structured text, GIF,
+        JPG, and PNG. 
+
+        Register your help files at product initialization with the
+        'registerHelp()' method on the registrar object::
+
+          def initialize(registrar):
+              ...
+              registrar.registerHelp()
+
+        This method will take care of locating your help files and
+        creating help topics for each help file. It can recognize these
+        file extensions: '.html', '.htm', '.dtml', '.txt', '.stx',
+        '.gif', '.jpg', '.png'.
+
+        If you want more control over how your help topics are created
+        you can use the 'registerHelpTopic()' method which takes an id
+        and a help topic object as arguments. For example::
+
+          from mySpecialHelpTopics import MyTopic
+
+          def initialize(context):
+              ...
+              context.registerHelpTopic('myTopic', MyTopic())
+
+        % Anonymous User - Jan. 15, 2002 7:40 pm - Please include something about how you create a HelpTopic and maybe (if   possible) how you group topics (HelpSys.tpValues mentions something about   topics having the same title would be grouped though I cant get it to work).
+
+        Your help topic should adhere to the 'HelpTopic'
+        interface. See the *API Reference* for more details.
+
+        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')},
+              )
+
+        The 'help' value should be a tuple with the name of your
+        product's Python package, and the file name (or other id) of
+        your help topic. Given this information, Zope will automatically
+        draw a *Help* button on your management screen and link it to
+        your help topic.
+
+        To draw a help button on a management screen that is not a view
+        (such as an add form), 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.
+
+    Other User Interfaces
+
+      In addition to providing a through the web management interface
+      your products may also support many other user interfaces. You
+      product might have no web management interfaces, and might be
+      controlled completely through some other network protocol. Zope
+      provides interfaces and support for FTP, WebDAV and XML-RPC. If
+      this isn't enough you can add other protocols.
+
+      FTP and WebDAV Interfaces
+
+        Both FTP and WebDAV treat Zope objects like files and
+        directories. See Chapter 2 for more information on FTP and
+        WebDAV.
+
+        By simply sub-classing from 'SimpleItem.Item' and
+        'ObjectManager' if necessary, you gain basic FTP and WebDAV
+        support. Without any work your objects will appear in FTP
+        directory listings and if your class is an 'ObjectManager' its
+        contents will be accessible via FTP and WebDAV.  See Chapter 2
+        for more information on implementing FTP and WebDAV support.
+
+      XML-RPC and Network Services
+
+        XML-RPC is covered in Chapter 2. All your product's methods can
+        be accessible via XML-RPC. However, if your are implementing
+        network services, you should explicitly plan one or more methods
+        for use with XML-RPC.
+
+        Since XML-RPC allows marshalling of simple strings, lists, and
+        dictionaries, your XML-RPC methods should only accept and return
+        these types. These methods should never accept or return Zope
+        objects. XML-RPC also does not support 'None' so you should use
+        zero or something else in place of 'None'.
+
+        Another issue to consider when using XML-RPC is security. Many
+        XML-RPC clients still don't support HTTP basic
+        authorization. Depending on which XML-RPC clients you
+        anticipate, you may wish to make your XML-RPC methods public and
+        accept authentication credentials as arguments to your methods.
+
+          % Anonymous User - Nov. 16, 2002 3:14 am:
+           An example here (or reference to one appearing elsewhere) showing how to accept authentication credentials as
+           arguments to your methods would be very valuable.
+
+      Content Management Framework Interface
+
+        The "Content Management Framework":http://cmf.zope.org is an
+        evolving content management extension for Zope. It provides a
+        number of interfaces and conventions for content objects. If you
+        wish to support the CMF you should consult the CMF user
+        interface guidelines and interface documentation.
+
+          % Anonymous User - Mar. 28, 2002 7:17 pm:
+           Please add pointers to the appropriate documentation. I have surfed cmf.zope.org, and looked over its
+           documentation section, and have not found anything identifiable as "user interface guidelines" or "interface
+           documentation". I found the occasional guide to creating new CMF skins, some marketing garbage, the ZWACK
+           Chapter, and that's about it. I did not find anything which explains how to write a product that is
+           CMF-aware, or why I would want so to do. Thanks in advance.
+
+        Supporting the CMF interfaces is not a large burden if you
+        already support the Zope management interface. You should
+        consider supporting the CMF if your product class handles user
+        manageable content such as documents, images, business forms,
+        etc.
+
+  Packaging Products
+
+    Zope products are normally packaged as tarballs. You should create
+    your product tarball in such a way as to allow it to be unpacked in
+    the Products directory. For example, 'cd' to the Products directory and then
+    issue a 'tar' comand like so::
+
+      $ tar cvfz MyProduct-1.0.1.tgz MyProduct
+
+    This will create a gzipped tar archive containing your product. You
+    should include your product name and version number in file name of
+    the archive.
+
+    See the "Poll-1.0.tgz":examples/Poll-1.0.tgz file for an example
+    of a fully packaged Python product.
+
+      % Anonymous User - Nov. 16, 2002 3:24 am:
+       Confusing for some - albeit momentarily - under windows / IE6 when trying to download this file IE6 screws up
+       the name (thinks it should be a simple .gz file rather than .tgz) and this in turn - if uncorrected -
+       confuses WinZip into opening it incorrectly, look like it was packed wrong.
+       Simple work-around: rename the file in the save-as dialog back to the correct .tgz extension.
+
+    Product Information Files
+
+      Along with your Python and DTML files you should include some
+      information about your product in its root directory.
+
+        'README.txt' -- Provides basic information about your product.
+        Zope will parse this file as "structured
+        text":http://www.zope.org/Members/millejoh/structuredText and
+        make it available on the *README* view of your product in the
+        control panel.
+
+        'VERSION.txt' -- Contains the name and version of your product
+        on a single line. For example, 'Mutiple Choice Poll 1.1.0'.
+        Zope will display this information as the 'version' property of
+        your product in the control panel.
+
+        'LICENSE.txt' -- Contains your product license, or a link to it.
+
+        % Anonymous User - Feb. 18, 2003 11:44 am:
+         On 2.5.1 VERSION.txt needs to be version.txt
+
+      You may also wish to provide additional information. Here are some
+      suggested optional files to include with your product.
+
+        'INSTALL.txt' -- Provides special instructions for installing
+        the product and components on which it depends. This file is
+        only optional if your product does not require more than an
+        ungzip/untar into a Zope installation to work.
+
+        'TODO.txt' -- This file should make clear where this product
+        release needs work, and what the product author intends to do
+        about it.
+
+        'CHANGES.txt' and 'HISTORY.txt' -- 'CHANGES.txt' should
+        enumerate changes made in particular product versions from the
+        last release of the product. Optionally, a 'HISTORY.txt' file
+        can be used for older changes, while 'CHANGES.txt' lists only
+        recent changes.
+
+        'DEPENDENCIES.txt' -- Lists dependencies including required os
+        platform, required Python version, required Zope version,
+        required Python packages, and required Zope products.
+
+        % Anonymous User - Jan. 15, 2002 11:09 am - typo: /Mutiple/Multiple/
+
+    Product Directory Layout
+
+      By convention your product will contain a number of
+      sub-directories. Some of these directories have already been
+      discussed in this chapter. Here is a summary of them.
+
+        'dtml' -- Contains your DTML files.
+
+        'www' -- Contains your icon files.
+
+        'help' -- Contains your help files.
+
+        'tests' -- Contains your unit tests.
+
+        % Anonymous User - Nov. 16, 2002 3:29 am:
+         Double-negative in explanation of INSTALL.txt (some way above this comment) leads to lack of clarity.
+
+      It is not necessary to include these directories if your don't
+      have anything to go in them.
+
+        % Anonymous User - Nov. 8, 2002 8:57 pm:
+         /your/you/
+
+        % jmeile - Nov. 28, 2002 2:43 pm:
+         Shouldn't be the "www" folder called "icons" or "images"?
+
+  Product Frameworks
+
+    Creating Zope products is a complex business. There are a number of
+    frameworks available to help ease the burden of creating products.
+    Different frameworks focus on different aspects of product
+    construction.
+
+    ZClass Base Classes
+
+      As an alternative to creating full blown products you may choose
+      to create Python base classes which can be used by ZClasses. This
+      allows you to focus on application logic and use ZClasses to take
+      care of management interface issues.
+
+      The chief drawback to this approach is that your code will be split
+      between a ZClass and a Python base class. This makes it harder to
+      edit and to visualize.
+
+      See the *Zope Book* for more information on ZClasses.
+
+    TransWarp and ZPatterns
+
+      TransWarp and ZPatterns are two related product framework
+      packages by Phillip Eby and Ty Sarna.  You can find out more
+      information on TransWarp from the "TransWarp Home
+      Page":http://www.zope.org/Members/pje/Wikis/TransWarp/HomePage.
+      More information on ZPatterns can be found at the "ZPatterns
+      Home Page":http://www.zope.org/Members/pje/Wikis/ZPatterns/HomePage
+
+  Evolving Products
+
+    As you develop your product classes you will generally make a series
+    of product releases. While you don't know in advance how your
+    product will change, when it does change there are measures that
+    you can take to minimize problems.
+
+    Evolving Classes
+
+      Issues can occur when you change your product class because
+      instances of these classes are generally persistent. This means
+      that instances created with an old class will start using a new
+      class. If your class changes drastically this can break existing
+      instances.
+
+      The simplest way to handle this situation is to provide class
+      attributes as defaults for newly added attributes. For example if
+      the latest version of your class expects an 'improved_spam'
+      instance attribute while earlier versions only sported 'spam'
+      attributes, you may wish to define an 'improved_spam' class
+      attribute in your new class so your old objects won't break when
+      they run with your new class. You might set 'improved_spam' to None
+      in your class, and in methods where you use this attribute you may
+      have to take into account that it may be None. For example::
+
+        class Sandwich(...):
+
+            improved_spam=None
+            ...
+
+            def assembleSandwichMeats(self):
+                ...
+                # test for old sandwich instances
+                if self.improved_spam is None:
+                    self.updateToNewSpam()
+                ...
+
+        % Anonymous User - Mar. 13, 2005 7:39 am:
+         i've searched now for hours, but could not find any example to apply *PERSISTENT* product upgrades in
+         __setstate__. every time i restart my zope, upgrades are performed again (and Data.fs does not change).
+
+      Another solution is to use the standard Python pickling hook
+      '__setstate__', however, this is in general more error prone and
+      complex.
+
+        % Anonymous User - Nov. 8, 2002 9:26 pm:
+         I'd like to get examples here.
+         Instead of pickling, how about including methods for export to/import from disk.
+         these may be editable strings (__repr__) for export and python eval for import. 
+         caveat: eval doesnt like newlines.
+         The next more complicated solution is parse/unparse methods for im/export.
+         This of course must be included in the very first distributed product and then
+         migrating to a newer version still requires manual work for each instance
+         (export, delete old instance, create new instance, import).
+         Or is there a way to modify product instances in the ZODB? example, plz.
+
+      A third option is to create a method to update old instances. Then
+      you can manually call this method on instances to update to
+      them. Note, this won't work unless the instances function well
+      enough to be accessible via the Zope management screens.
+
+      While you are developing a product you won't have to worry too much
+      about these details, since you can always delete old instances that
+      break with new class definitions. However, once you release your
+      product and other people start using it, then you need to start
+      planning for the eventuality of upgrading.
+
+      Another nasty problem that can occur is breakage caused by
+      renaming your product classes. You should avoid this since it
+      breaks all existing instances. If you really must change your
+      class name, provide aliases to it using the old name.  You may
+      however, change your class's base classes without causing these
+      kinds of problems.
+
+    Evolving Interfaces
+
+      The basic rule of evolving interfaces is *don't do it*. While you are
+      working privately you can change your interfaces all you wish. But
+      as soon as you make your interfaces public you should freeze
+      them. The reason is that it is not fair to users of your
+      interfaces to changes them after the fact. An interface is
+      contract. It specifies how to use a component and it specifies how
+      to implement types of components. Both users and developers will
+      have problems if your change the interfaces they are using or
+      implementing.
+
+        % Anonymous User - Jan. 15, 2002 11:35 am - typo: /changes/change/
+
+        % Anonymous User - Nov. 16, 2002 3:35 am:
+         I don't think 'fairness' cuts any ice in a technical discussion. Rather, explain how changing an interface
+         will likely 'break' any existing code dependent on the contract(s) implicit in that interface.
+
+      The general solution is to create simple interfaces in the first
+      place, and create new ones when you need to change an existing
+      interface. If your new interfaces are compatible with your
+      existing interfaces you can indicate this by making your new
+      interfaces extend your old ones. If your new interface replaces
+      an old one but does not extend it you should give it a new name
+      such as, 'WidgetWithBellsOn'. Your components should continue to
+      support the old interface in addition to the new one for a few
+      releases.
+
+  Conclusion
+
+    Migrating your components into fully fledged Zope products is a
+    process with a number of steps. There are many details to keep track
+    of. However, if you follow the recipe laid out in this chapter you
+    should have no problems.
+
+    As Zope grows and evolves we want to simplify the Zope development
+    model. We hope to remove much of the management interface details
+    from product development. We also want to move to a fuller component
+    framework that makes better use of interfaces.
+
+    Nevertheless, Zope products are a powerful framework for building
+    web applications. By creating products you can take advantage of
+    Zope's features including security, scalability, through the web
+    management, and collaboration.

Added: zdgbook/trunk/Security.stx
===================================================================
--- zdgbook/trunk/Security.stx	                        (rev 0)
+++ zdgbook/trunk/Security.stx	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,1693 @@
+Chapter 6: Security
+
+  Introduction
+
+    A typical web application needs to be securely managed.  Different
+    types of users need different kinds of access to the components
+    that make up an application. To this end, Zope includes a
+    comprehensive set of security features.  This chapter's goal is to
+    shed light on Zope security in the context of Zope Product
+    development.  For a more fundamental overview of Zope security,
+    you may wish to refer to the *Zope Book*, Chapter 6, Users and
+    Security.
+    Before diving into this chapter, you should have a basic
+    understanding of how to build Zope Products as well as an
+    understanding of how the Zope object publisher works. These topics
+    are covered in Chapter 2 and Chapter 3, respectively.
+
+      % Anonymous User - Sep. 12, 2002 11:30 am:
+       Our web-page is designed in a way to allow specific user groups access to certain parts of it. In order to
+       check this function we created in a lower hierarchy of the parent folder a new folder (test folder). Access
+       to the new folder has been restricted by cancelling all acquire permission settings.
+       The manager however obtains all rights to access and administrate the web-site. Another self-defined user
+       group shall only be able to view the defined pages.
+       When the web-site is reloaded in a new Internet Explorer Window the access is denied in general.
+       No matter whether this is attempted by the manager itself or some restricted user, who has been given
+       permission to enter specific parts of our web-pages. Unfortunately this is not the expected result. Has
+       anybody got a clue what is wrong ? What could be done to access a limited part of our system ?
+       contact: gis at nordschwarzwald-region.de
+
+      % Anonymous User - May 7, 2004 7:38 am:
+       This will make all lines very long, thus rendering the page almost useless. This will make all lines very
+       long, thus rendering the page almost useless. This will make all lines very long, thus rendering the page
+       almost useless. This will make all lines very long, thus rendering the page almost useless. This will make
+       all lines very long, thus rendering the page almost useless. This will make all lines very long, thus
+       rendering the page almost useless. This will make all lines very long, thus rendering the page almost
+       useless. This will make all lines very long, thus rendering the page almost useless. This will make all lines
+       very long, thus rendering the page almost useless. This will make all lines very long, thus rendering the
+       page almost useless. This will make all lines very long, thus rendering the page almost useless. This will
+       make all lines very long, thus rendering the page almost useless. This will make all lines very long, thus
+       rendering the page almost useless. This will make all lines very long, thus rendering the page almost
+       useless. This will make all lines very long, thus rendering the page almost useless. This will make all lines
+       very long, thus rendering the page almost useless. This will make all lines very long, thus rendering the
+       page almost useless. This will make all lines very long, thus rendering the page almost useless.
+
+      % Anonymous User - May 7, 2004 7:44 am:
+       While trying to read though the documentation at
+       http://zope.org/Documentation/Books/ZDG/current/Security.stx, I
+       noticed some very strange problems regarding the HTML.
+       The main problem is that the text in the <pre> tags are not 
+       automatically wrapped at 76 or 80 characters/columns. Therefore the 
+       containing element gets rendered as wide as the lines (user comments!) 
+       are long. 
+       I would recommend to wrap all user contributed text at 80 columns so 
+       that users don't have to scroll horizontal all the time.
+       Other problems regard XHTML-syntax, but I'll leave that to the 
+       page(-template) designers.
+       Regards,
+       Ronald van Engelen <zope-doc at lacocina.nl>
+
+  Security Architecture
+
+    The Zope security architecture is built around a *security
+    policy*, which you can think of as the "access control philosophy"
+    of Zope. This policy arbitrates the decisions Zope makes about
+    whether to allow or deny access to any particular object defined
+    within the system.
+
+    How The Security Policy Relates to Zope's Publishing Machinery
+
+      When access to Zope is performed via HTTP, WebDAV, or FTP,
+      Zope's publishing machinery consults the security policy in
+      order to determine whether to allow or deny access to a visitor
+      for a particular object.  For example, when a user visits the
+      root 'index_html' object of your site via HTTP, the security
+      policy is consulted by 'ZPublisher' to determine whether the user
+      has permission to view the 'index_html' object itself.  For more
+      information on this topic, see Chapter 3, 
+      "Object Publishing".
+
+      The Zope security policy is consulted when an object is accessed
+      the publishing machinery, for example when a web request
+      is submitted.
+
+        % Anonymous User - Oct. 1, 2002 9:49 am:
+         when an object is accessed the publishing machinery/when an object is accessed BY the publishing machinery
+
+    How The Security Policy Relates to Restricted Code
+
+      *Restricted code* is generally any sort of logic that may be
+      edited remotely (through the Web, FTP, via WebDAV or by other
+      means). DTML Methods, SQLMethods, Python Scripts and Perl
+      Scripts are examples of restricted code.
+
+        % Anonymous User - Aug. 4, 2004 6:00 pm:
+         this applies to some but not to others
+
+      When restricted code runs, any access to objects integrated with
+      Zope security is arbitrated by the security policy. For example
+      if you write a bit of restricted code with a line that attempts
+      to manipulate an object you don't have sufficient permission to
+      use, the security policy will deny you access to the object.
+      This generally is accomplished by raising an 'Unauthorized'
+      exception, which is a Python string exception caught by a User
+      Folder which signifies that Zope should attempt to get user
+      credentials before obeying the request.  The particular code
+      used to attempt to obtain the credentials is determined by the
+      User Folder "closest" (folder-wise) to the object being
+      accessed.
+
+      The Zope security policy is consulted when an object is accessed
+      by restricted code.
+
+    'Unauthorized' Exceptions and Through-The-Web Code
+
+      The security policy infrastructure will raise an 'Unauthorized'
+      exception automatically when access to an object is denied.
+      When an 'Unauthorized' exception is raised within Zope, it is
+      handled in a sane way by Zope, generally by having the User
+      Folder prompt the user for login information.  Using this
+      functionality, it's possible to protect Zope objects through
+      access control, only prompting the user for authentication when
+      it is necessary to perform an action which requires privilege.
+
+      An example of this behavior can be witnessed within the Zope
+      Management interface itself.  The management interface prompts
+      you to log in when visiting, for example, the '/manage' method
+      of any Zope object.  This is due to the fact that an anonymous
+      user does not generally possess the proper credentials to use the
+      management interface.  If you're using Zope in the default
+      configuration with the default User Folder, it prompts you to
+      provide login information via an HTTP basic authentication
+      dialog.
+
+    How The Security Policy Relates To Unrestricted Code
+
+      There are also types of *unrestricted code* in Zope, where the
+      logic is not constrained by the security policy. Examples of
+      unrestricted code are the methods of Python classes that
+      implement the objects in Python file-based add-on components.
+      Another example of unrestricted code can be found in External
+      Method objects, which are defined in files on the filesystem.
+      These sorts of code are allowed to run "unrestricted" because
+      access to the file system is required to define such logic.
+      Zope assumes that code defined on the filesystem is "trusted",
+      while code defined "through the web" is not.  All
+      filesystem-based code in Zope is unrestricted code.
+
+      We'll see later that while the security policy does not
+      constrain what your unrestricted code does, it can and should be
+      used to control the ability to *call* your unrestricted code
+      from within a restricted-code environment.
+
+      The Zope security policy is *not* consulted when unrestricted
+      code is run.
+
+    Details Of The Default Zope Security Policy
+
+      In short, the default Zope security policy
+      ensures the following:
+
+       - access to an object which does not have any associated security
+         information is always denied.
+
+       - if an object is associated with a permission, access is
+         granted or denied based on the user's roles.  If a user has a
+         role which has been granted the permission in question, access
+         is granted.  If the user does not possess a role that has been
+         granted the permission in question, access is denied.
+
+       - if the object has a security assertion declaring it *public* ,
+         then access will be granted.
+
+       - if the object has a security assertion declaring it *private*,
+         then access will be denied.
+
+       - accesses to objects that have names beginning with the 
+         underscore character '_' are always denied.
+
+      As we delve further into Zope security within this chapter,
+      we'll see exactly what it means to associate security
+      information with an object.
+
+        % zigg - Feb. 12, 2002 11:40 am - What about object__roles__?
+
+        % mcdonc - Mar. 2, 2002 4:45 pm - That's how roles are implemented at the bottom of the Zope security implementation, but "low level" stuff like this is unfortunately not covered in this guide.  Indeed, it's probably not comprehensively covered anywhere.
+
+        % Anonymous User - Apr. 17, 2002 5:54 am:
+         I think you should add here that access to methods without a docstring
+         is denied, too
+
+        % Anonymous User - Apr. 17, 2002 7:39 am:
+         If the user has the role 'Manager' he is allowed to access
+
+        % Anonymous User - Feb. 24, 2003 6:55 pm:
+         No, 'Manager' is not any kind of superuser.
+         Managers can remove permissions for Managers and then they will be denied!
+         Only the "emergency" users can override this.
+
+  Overview Of Using Zope Security Within Your Product
+
+    Of course, now that we know what the Zope security policy is, we
+    need to know how our Product can make use of it.  Zope developers
+    leverage the Zope security policy primarily by making security
+    declarations related to methods and objects within their Products.
+    Using security assertions, developers may deny or allow all types
+    of access to a particular object or method unilaterally, or they
+    may protect access to Zope objects more granularly by using
+    permissions to grant or deny access based on the roles of the
+    requesting user to the same objects or methods.
+
+      % Anonymous User - Nov. 19, 2001 10:22 am - what about getSecurityManager().getUser()?  I've heard a nasty unexpected API difference that  getSecurityManager().getUser().hasRole() is not preferred.  getSecurityManager().getUser().has_role() is preferred.  whats the beef?    will we get a getSecurityManager().getUser().isAuthenticated() ? I dont see this in the Basic User class.  or isAnonymous() ?  I dont see why this ease on the API couldnt be implemented as well as *desired*  we need to stop hardcoding 'Anonymous User' and 'Authenticated User' everywhere.  Please address this in this security section.    my realworld problem: I'm implemented under a 3rd party Product API in ZOPE, I need to short circuit a method if the user is not authenticated.  whats the blessed way of doing this?    george runyan
+
+      % mcdonc - Mar. 2, 2002 4:50 pm - There is no isAuthenticated method in the user API.  In Zopes after 2.4, just look for 'Authenticated' in the user's roles and switch based on that.  Finding out if a user is authenticated in Zopes prior to 2.4 is an exercise in making sure the user is not the 'Anonymous User' by checking the username. Use of hasRole is deprecated and has_role should be used.
+
+    For a more fundamental overview of Zope users, roles, and
+    permissions, see the section titled "Authorization and Managing
+    Security" in the Security
+    Chapter of
+    the *Zope Book*.
+
+    Security Declarations In Zope Products
+
+      Zope security declarations allow developers to make security
+      assertions about a Product-defined object and its methods.
+      Security declarations come in three basic forms.  These are:
+
+        public -- allow anybody to access the protected object
+        or method
+
+        private -- deny anyone access to the protected object or
+        method
+
+        protected -- protect access to the object or method via a
+        permission
+
+      We'll see how to actually "spell" these security assertions a
+      little later in this chapter.  In the meantime, just know that
+      security declarations are fundamental to Zope Product security,
+      and they can be used to protect access to an object by
+      associating it with a permission.  We will refer to security
+      declarations as "declarations" and "assertions" interchangeably
+      within this chapter.
+
+        % dudek - Apr. 28, 2002 12:10 pm:
+         Rather than "spell" which is colorful but potentiall clightly vague, maybe saying
+         "We'll see how to specify and manipulate these security asertions..."
+
+    Permissions In Zope Products
+
+      A permission is the smallest unit of access to an object in
+      Zope, roughly equivalent to the atomic permissions on files seen
+      in Windows NT or UNIX: R (Read), W(Write), X(Execute),
+      etc. However, unlike these types of mnemonic permissions shared
+      by all sorts of different file types in an operating system
+      product, in Zope, a permission usually describes a fine-grained
+      logical operation which takes place upon an object, such as
+      "View Management Screens" or "Add Properties".
+
+      Zope administrators associate these permissions with *roles*,
+      which they grant to Zope users.  Thus, declaring a protection
+      assertion on a method of "View management screens" ensures that
+      only users who possess roles which have been granted the "View
+      management screens" permission are able to perform the action
+      that the method defines.
+
+      It is important to note that Zope's security architecture
+      dictates that roles and users remain the domain of
+      administrators, while permissions remain the domain of
+      developers.  Developers of Products should not attempt to define
+      roles or users, although they may (and usually must) define
+      permissions.  Most importantly, a Zope administrator who makes
+      use of your product should have the "last word" as regards which
+      roles are granted which permissions, allowing her to protect her
+      site in a manner that fits her business goals.
+
+      Permission names are strings, and these strings are currently
+      arbitrary.  There is no permission hierarchy, or list of
+      "approved permissions".  Developers are encouraged to reuse Zope
+      core permissions (e.g. "View", "Access contents information")
+      when appropriate, or they may create their own as the need
+      arises.  It is generally wise to reuse existing Zope permission
+      names unless you specifically need to define your own.  For a
+      list of existing Zope core permissions, see Appendix A, "Zope Core
+      Permissions".
+
+      Permissions are often tied to method declarations in Zope.  Any
+      number of method declarations may share the same permission.
+      It's useful to declare the same permission on a set of methods
+      which can logically be grouped together.  For example, two
+      methods which return management forms for the object can be
+      provided with the same permission, "View management screens".
+      Likewise, two entirely different objects can share a permission
+      name to denote that the operation that's being protected is
+      fundamentally similar.  For instance, most Product-defined
+      objects reuse the Zope "View" permission, because most Zope
+      objects need to be viewed in a web browser.  If you create an
+      addable Zope class named 'MyObject', it doesn't make much sense
+      to create a permission "View MyObject", because the generic
+      "View" permission may be reused for this action.
+
+      There is an exception to the "developers should not try to
+      define roles" rule inasmuch as Zope allows developers to assign
+      "default roles" to a permission.  This is primarily for the
+      convenience of the Zope administrator, as default roles for a
+      permission cause the Zope security machinery to provide a
+      permission to a role *by default* when instances of a Product
+      class are encountered during security operations.  For example,
+      if your Product defines a permission "Add Poll Objects", this
+      permission may be associated with a set of default roles,
+      perhaps "Manager".  Default roles in Products should not be used
+      against roles other than "Manager", "Anonymous", "Owner", and
+      "Authenticated" (the four default Zope roles), as other roles
+      are not guaranteed to exist in every Zope installation.
+
+      Using security assertions in Zope is roughly analogous to
+      assigning permission bit settings and ownership information to
+      files in a UNIX or Windows filesystem.  Protecting objects via
+      permissions allows developers and administrators to secure Zope
+      objects independently of statements made in application code.
+
+  Implementing Security In Python Products
+
+    Security Assertions
+
+      You may make several kinds of security assertions at the Python
+      level.  You do this to declare accessibility of methods and
+      subobjects of your classes. Three of the most common assertions
+      that you'll want to make on your objects are:
+
+       - this object is **public** (always accessible)
+
+       - this object is **private** (not accessible by restricted 
+         code or by URL traversal)
+
+       - this object is **protected** by a specific permission
+
+      There are a few other kinds of security assertions that are 
+      much less frequently used but may be needed in some cases:
+
+       - asserting that access to subobjects that do not have explicit
+         security information should be allowed rather than denied.
+
+       - asserting what sort of protection should be used when 
+         determining access to an *object itself* rather than 
+         a particular method of the object
+
+      It is important to understand that security assertions made in
+      your Product code *do not* limit the ability of the code that
+      the assertion protects.  Assertions only protect *access to this
+      code*.  The code which constitutes the body of a protected,
+      private, or public method of a class defined in a Zope
+      disk-based Product runs completely unrestricted, and is not
+      subject to security constraints of any kind within Zope.  An
+      exception to this rule occurs when disk-based-Product code calls
+      a "through the web" method such as a Python Script or a DTML
+      Method.  In this case, the security constraints imposed by these
+      objects respective to the current request are obeyed.
+
+    When Should I Use Security Assertions?
+
+      If you are building an object that will be used from DTML or
+      other restricted code, or that will be accessible directly
+      through the web (or other remote protocols such as FTP or
+      WebDAV) then you need to define security information for your
+      object.
+
+    Making Security Assertions
+
+      As a Python developer, you make security assertions in your
+      Python classes using 'SecurityInfo' objects. A 'SecurityInfo'
+      object provides the interface for making security assertions
+      about an object in Zope.
+
+      The convention of placing security declarations inside Python
+      code may at first seem a little strange if you're used to "plain
+      old Python" which has no notion at all of security declarations.
+      But because Zope provides the ability to make these security
+      assertions at such a low level, the feature is ubiquitous
+      throughout Zope, making it easy to make these declarations once
+      in your code, usable site-wide without much effort.
+
+  Class Security Assertions
+
+    The most common kind of 'SecurityInfo' you will use as a component
+    developer is the 'ClassSecurityInfo' object.  You use
+    'ClassSecurityInfo' objects to make security assertions about
+    methods on your classes.
+
+    Classes that need security assertions are any classes that define
+    methods that can be called "through the web".  This means any
+    methods that can be called directly with URL traversal, from DTML
+    Methods, or from Python-based Script objects.
+
+    Declaring Class Security
+
+      When writing the classes in your product, you create a
+      'ClassSecurityInfo' instance *within each class that needs to
+      play with the security model*. You then use the
+      'ClassSecurityInfo' object to make assertions about your class,
+      its subobjects and its methods.
+
+      The 'ClassSecurityInfo' class is defined in the 'AccessControl'
+      package of the Zope framework. To declare class security
+      information create a 'ClassSecurityInfo' class attribute named
+      'security'.  The name 'security' is used for consistency and for
+      the benefit of new component authors, who often learn from
+      looking at other people's code. You do not have to use the
+      name 'security' for the security infrastructure to recognize
+      your assertion information, but it is recommended as a
+      convention.  For example::
+
+        from AccessControl import ClassSecurityInfo
+
+        class Mailbox(ObjectManager):
+          """A mailbox object that contains mail message objects."""
+
+          # Create a SecurityInfo for this class. We will use this 
+          # in the rest of our class definition to make security 
+          # assertions.
+          security = ClassSecurityInfo()
+
+          # Here is an example of a security assertion. We are 
+          # declaring that access to messageCount is public.
+          security.declarePublic('messageCount')
+
+          def messageCount(self):
+            """Return a count of messages."""
+            return len(self._messages)
+
+        % Anonymous User - Jan. 23, 2003 9:54 am:
+         How is security.declarePublic('messageCount') different from 
+         messageCount__roles__=None ? Or is the latter way obsolete?
+
+        % Anonymous User - May 13, 2003 4:25 pm:
+         From looking at the source (and doing some debugging), I've concluded that ClassSecurityInfo is just another
+         indirection the Zope folks have decided to place on us. If you look in the method apply() in
+         AccessControl/SecurityInfo.py around line 220, you'll notice that it pretty much automates the creation of
+         FooBar__roles__ attributes and the __ac_permissions__ .
+         I don't see any Unit Tests for any of that code though, so I wouldn't bother with ClassSecurityInfo unless
+         you're really into Zope voodoo and want your applications security to be automagically munged and then
+         injected back into your object.
+
+      Note that in the example above we called the 'declarePublic'
+      method of the 'ClassSecurityInfo' instance to declare that
+      access to the 'messageCount' method be public. To make security
+      assertions for your object, you just call the appropriate
+      methods of the 'ClassSecurityInfo' object, passing the
+      appropriate information for the assertion you are making.
+
+      'ClassSecurityInfo' approach has a number of benefits. A major
+      benefit is that it is very explicit, it allows your security
+      assertions to appear in your code near the objects they protect,
+      which makes it easier to assess the state of protection of your
+      code at a glance. The 'ClassSecurityInfo' interface also allows
+      you as a component developer to ignore the implementation
+      details in the security infrastructure and protects you from
+      future changes in those implementation details.
+
+      Let's expand on the example above and see how to make the most
+      common security assertions using the 'SecurityInfo' interface.
+
+      To assert that a method is *public* (anyone may call it) you may
+      call the 'declarePublic' method of the 'SecurityInfo' object,
+      passing the name of the method or subobject that you are making
+      the assertion on::
+
+        security.declarePublic(methodName)
+
+      To assert that a method is *private* you call the
+      'declarePrivate' method of the 'SecurityInfo' object, passing
+      the name of the method or subobject that you are making the
+      assertion on::
+
+        security.declarePrivate(methodName)
+
+      To assert that a method or subobject is *protected* by a
+      particular permission, you call the 'declareProtected' method of
+      the 'SecurityInfo' object, passing a permission name and the
+      name of a method to be protected by that permission::
+
+        security.declareProtected(permissionName, methodName)
+
+      If you have lots of methods you want to protect under the same
+      permission, you can pass as many methodNames ase you want::
+
+        security.declareProtected(permissionName, methodName1,
+        methodName2, methodName3, ...)
+
+      Passing multiple names like this works for all of the 'declare'
+      security methods ('declarePublic', 'declarePrivate', and
+      'declareProtected').
+
+    Deciding To Use 'declareProtected' vs. 'declarePublic' or 'declarePrivate'
+
+      If the method you're making the security declaration against is
+      innocuous, and you're confident that its execution will not
+      disclose private information nor make inappropriate changes to
+      system state, you should declare the method public.
+
+      If a method should never be run under any circumstances via
+      traversal or via through-the-web code, the method should be
+      declared private.  This is the default if a method has no
+      security assertion, so you needn't explicitly protect
+      unprotected methods unless you've used 'setDefaultAccess' to set
+      the object's default access policy to 'allow' (detailed in
+      *Other Assertions*, below).
+
+      If the method should only be executable by a certain class of
+      users, you should declare the method protected.
+
+    A Class Security Example
+
+      Let's look at an expanded version of our 'Mailbox' example that 
+      makes use of each of these types of security assertions::
+
+        from AccessControl import ClassSecurityInfo
+        import Globals
+
+        class Mailbox(ObjectManager):
+          """A mailbox object."""
+
+        # Create a SecurityInfo for this class
+          security = ClassSecurityInfo()
+
+          security.declareProtected('View management screens', 'manage')
+          manage=HTMLFile('mailbox_manage', globals())
+
+          security.declarePublic('messageCount')
+          def messageCount(self):
+            """Return a count of messages."""
+            return len(self._messages)
+
+          # protect 'listMessages' with the 'View Mailbox' permission
+          security.declareProtected('View Mailbox', 'listMessages')
+
+          def listMessages(self):
+            """Return a sequence of message objects."""
+            return self._messages[:]
+
+          security.declarePrivate('getMessages')
+          def getMessages(self):
+            self._messages=GoGetEm()
+            return self._messages
+
+        # call this to initialize framework classes, which
+        # does the right thing with the security assertions.
+        Globals.InitializeClass(Mailbox)
+
+      Note the last line in the example.  In order for security
+      assertions to be correctly applied to your class, you must call
+      the global class initializer ('Globals.InitializeClass') for all
+      classes that have security information. This is very important -
+      the global initializer does the "dirty work" required to ensure
+      that your object is protected correctly based on the security
+      assertions that you have made. If you don't run it on the
+      classes that you've protected with security assertions, the
+      security assertions will not be effective.
+
+    Deciding Permission Names For Protected Methods
+
+      When possible, you should make use of an existing Zope
+      permission within a 'declareProtected' assertion.  A list of the
+      permissions which are available in a default Zope installation
+      is available within Appendix A.  When it's not possible to reuse
+      an existing permission, you should choose a permission name
+      which is a verb or a verb phrase.
+
+    Object Assertions
+
+      Often you will also want to make a security assertion on the
+      *object itself*. This is important for cases where your objects
+      may be accessed in a restricted environment such as
+      DTML. Consider the example DTML code::
+
+        <dtml-var expr="some_method(someObject)">
+
+      Here we are trying to call 'some_method', passing the object
+      'someObject'. When this is evaluated in the restricted DTML
+      environment, the security policy will attempt to validate access
+      to both 'some_method' and 'someObject'. We've seen how to make
+      assertions on methods - but in the case of 'someObject' we are
+      not trying to access any particular method, but rather the
+      *object itself* (to pass it to 'some_method'). Because the
+      security machinery will try to validate access to 'someObject',
+      we need a way to let the security machinery know how to handle
+      access to the object itself in addition to protecting its
+      methods.
+
+      To make security assertions that apply to the *object itself* 
+      you call methods on the 'SecurityInfo' object that are analogous 
+      to the three that we have already seen::
+
+        security.declareObjectPublic()
+
+        security.declareObjectPrivate()
+
+        security.declareObjectProtected(permissionName)
+
+      The meaning of these methods is the same as for the method 
+      variety, except that the assertion is made on the object itself. 
+
+    An Object Assertion Example
+
+      Here is the updated 'Mailbox' example, with the addition of a 
+      security assertion that protects access to the object itself 
+      with the 'View Mailbox' permission::
+
+        from AccessControl import ClassSecurityInfo
+        import Globals
+
+        class Mailbox(ObjectManager):
+          """A mailbox object."""
+
+          # Create a SecurityInfo for this class
+          security = ClassSecurityInfo()
+
+          # Set security for the object itself
+          security.declareObjectProtected('View Mailbox')
+
+          security.declareProtected('View management screens', 'manage')
+          manage=HTMLFile('mailbox_manage', globals())
+
+          security.declarePublic('messageCount')
+          def messageCount(self):
+            """Return a count of messages."""
+            return len(self._messages)
+
+          # protect 'listMessages' with the 'View Mailbox' permission
+          security.declareProtected('View Mailbox', 'listMessages')
+
+          def listMessages(self):
+            """Return a sequence of message objects."""
+            return self._messages[:]
+
+          security.declarePrivate('getMessages')
+          def getMessages(self):
+            self._messages=GoGetEm()
+            return self._messages
+
+        # call this to initialize framework classes, which
+        # does the right thing with the security assertions.
+        Globals.InitializeClass(Mailbox)
+
+    Other Assertions
+
+      The SecurityInfo interface also supports the less common
+      security assertions noted earlier in this document.
+
+      To assert that access to subobjects that do not have explicit
+      security information should be *allowed* rather than *denied* by
+      the security policy, use::
+
+        security.setDefaultAccess("allow")
+
+      This assertion should be used with caution. It will effectively
+      change the access policy to "allow-by-default" for all
+      attributes in your object instance (not just class attributes)
+      that are not protected by explicit assertions.  By default, the
+      Zope security policy flatly denies access to attributes and
+      methods which are not mentioned within a security assertion.
+      Setting the default access of an object to "allow" effectively
+      reverses this policy, allowing access to all attributes and
+      methods which are not explicitly protected by a security
+      assertion.
+
+      'setDefaultAccess' applies to attributes that are simple Python
+      types as well as methods without explicit protection. This is
+      important because some mutable Python types (lists, dicts) can
+      then be modified by restricted code. Setting default access to
+      "allow" also affects attributes that may be defined by the base
+      classes of your class, which can lead to security holes if you
+      are not sure that the attributes of your base classes are safe
+      to access.
+
+      Setting the default access to "allow" should only be done if you
+      are sure that all of the attributes of your object are safe to
+      access, since the current architecture does not support using
+      explicit security assertions on non-method attributes.
+
+    What Happens When You Make A Mistake Making 'SecurityInfo' Declarations?
+
+      It's possible that you will make a mistake when making
+      'SecurityInfo' declarations.  For example, it is not legal to
+      declare two conflicting permissions on a method::
+
+        class Foo(SimpleItem):
+            security = ClassSecurityInfo()
+
+            meta_type='Foo'
+
+            security.declareProtected('View foos', 'index_html')
+            def index_html(self):
+                """ make index_html web-publishable """
+                return "<html><body>hi!</body></html>"
+
+            security.declareProtected('View', 'index_html')
+            # whoops, declared a conflicting permission on index_html!
+
+      When you make a mistake like this, the security machinery will
+      accept the *first* declaration made in the code and will write
+      an error to the Zope debug log upon encountering the second and
+      following conflicting declarations during class initialization.
+      It's similarly illegal to declare a method both private and
+      public, or to declare a method both private and protected, or to
+      declare a method both public and protected. A similar error will
+      be raised in all of these cases.
+
+      Note that Zope *will not* warn you if you misspell the name of
+      a method in a declareProtected, declarePublic, or declarePrivate
+      assertion.  For instance, you try to protect the 'index_html'
+      method with the 'View' permission and make a mistake,
+      spelling the name 'index_html' as 'inde_html', like so::
+
+            security.declareProtected('View', 'inde_html')
+            # whoops, declared a permission assertion for 'inde_html'
+            # when I really wanted it to be 'index_html'!
+            def index_html(self):
+                """ make index_html web-publishable """
+                return "<html><body>hi!</body></html>"
+
+      You'll need to track down these kinds of problems yourself.
+
+    Setting Default Roles For Permissions
+
+      When defining operations that are protected by permissions, one
+      thing you commonly want to do is to arrange for certain roles to
+      be associated with a particular permission *by default* for
+      instances of your object.
+
+      For example, say you are creating a *News Item* object. You want
+      'Anonymous' users to have the ability to view news items by
+      default; you don't want the site manager to have to explicitly
+      change the security settings for each *News Item* just to give
+      the 'Anonymous" role 'View' permission.
+
+      What you want as a programmer is a way to specify that certain
+      roles should have certain permissions by default on instances of
+      your object, so that your objects have sensible and useful
+      security settings at the time they are created. Site managers
+      can always *change* those settings if they need to, but you can
+      make life easier for the site manager by setting up defaults
+      that cover the common case by default.
+
+      As we saw earlier, the 'SecurityInfo' interface provided a way
+      to associate methods with permissions. It also provides a way to
+      associate a permission with a set of default roles that should
+      have that permission on instances of your object.
+
+      To associate a permission with one or more roles, use the 
+      following::
+
+        security.setPermissionDefault(permissionName, rolesList)
+
+      The *permissionName* argument should be the name of a permission
+      that you have used in your object and *rolesList* should be a
+      sequence (tuple or list) of role names that should be associated
+      with *permissionName* by default on instances of your object.
+
+      Note that it is not always necessary to use this method. All
+      permissions for which you did not set defaults using
+      'setPermissionDefault' are assumed to have a single default role
+      of 'Manager'.  Notable exceptions to this rule include 'View'
+      and 'Access contents information', which always have the default
+      roles 'Manager' and 'Anonymous'.
+
+      The 'setPermissionDefault' method of the 'SecurityInfo' 
+      object should be called only once for any given permission 
+      name. 
+
+        % Anonymous User - Oct. 21, 2003 6:20 am:
+         SecurityInfo object should be called only once for any given permission name: 
+                 is that once per object ie many times if there are several classes is a product, or once per product?
+
+        % Anonymous User - Jan. 29, 2004 2:19 am:
+         This paragraph seems to conflict with the earlier example granting 'View' to 'Anonymous', as Zope will have
+         already granted default roles to builtin permissions.
+
+    An Example of Associating Default Roles With Permissions
+
+      Here is our  'Mailbox' example, updated to associate the 
+      'View Mailbox' permission with the roles 'Manager' and 
+      'Mailbox Owner' by default::
+
+        from AccessControl import ClassSecurityInfo
+        import Globals
+
+        class Mailbox(ObjectManager):
+          """A mailbox object."""
+
+          # Create a SecurityInfo for this class
+          security = ClassSecurityInfo()
+
+          # Set security for the object itself
+          security.declareObjectProtected('View Mailbox')
+
+          security.declareProtected('View management screens', 'manage')
+          manage=DTMLFile('mailbox_manage', globals())
+
+          security.declarePublic('messageCount')
+          def messageCount(self):
+            """Return a count of messages."""
+            return len(self._messages)
+
+          security.declareProtected('View Mailbox', 'listMessages')
+          def listMessages(self):
+            """Return a sequence of message objects."""
+            return self._messages[:]
+
+          security.setPermissionDefault('View Mailbox', ('Manager', 'Mailbox Owner'))
+
+        # call this to initialize framework classes, which
+        # does the right thing with the security assertions.
+        Globals.InitializeClass(Mailbox)
+
+    What Happens When You Make A Mistake Declaring Default Roles?
+
+      It's possible that you will make a mistake when making default
+      roles declarations.  For example, it is not legal to declare two
+      conflicting default roles for a permission::
+
+        class Foo(SimpleItem):
+            security = ClassSecurityInfo()
+
+            meta_type='Foo'
+
+            security.declareProtected('View foos', 'index_html')
+            def index_html(self):
+                """ """
+                return "<html><body>hi!</body></html>"
+
+            security.setPermissionDefault('View foos', ('Manager',))
+
+            security.setPermissionDefault('View foos', ('Anonymous',))
+            # whoops, conflicting permission defaults!
+
+      When you make a mistake like this, the security machinery will
+      accept the *first* declaration made in the code and will write
+      an error to the Zope debug log about the second and following
+      conflicting declarations upon class initialization.
+
+    What Can (And Cannot) Be Protected By Class Security Info?
+
+      It is important to note what can and cannot be protected using
+      the 'ClassSecurityInfo' interface. First, the security policy
+      relies on *Acquisition* to aggregate access control information,
+      so any class that needs to work in the security policy must have
+      either 'Acquisition.Implicit' or 'Acquisition.Explicit' in its
+      base class hierarchy.
+
+        % Anonymous User - Nov. 9, 2002 12:37 pm:
+         Here a document or reference to listing base class hierarchies 
+         of zopes built-in classes might be wellcome.
+
+      The current security policy supports protection of methods and
+      protection of subobjects that are instances. It does *not*
+      currently support protection of simple attributes of basic
+      Python types (strings, ints, lists, dictionaries). For
+      instance::
+
+        from AccessControl import ClassSecurityInfo
+        import Globals
+
+        # We subclass ObjectManager, which has Acquisition in its
+        # base class hierarchy, so we can use SecurityInfo.
+
+        class MyClass(ObjectManager):
+          """example class"""
+
+          # Create a SecurityInfo for this class
+          security = ClassSecurityInfo()
+
+          # Set security for the object itself
+          security.declareObjectProtected('View')
+
+          # This is ok, because subObject is an instance
+          security.declareProtected('View management screens', 'subObject')
+          subObject=MySubObject()
+
+          # This is ok, because sayHello is a method
+          security.declarePublic('sayHello')
+          def sayHello(self):
+            """Return a greeting."""
+            return "hello!"
+
+          # This will not work, because foobar is not a method
+          # or an instance - it is a standard Python type
+          security.declarePublic('foobar')
+          foobar='some string'
+
+      Keep this in mind when designing your classes. If you need
+      simple attributes of your objects to be accessible (say via
+      DTML), then you need to use the 'setDefaultAccess' method of
+      'SecurityInfo' in your class to allow this (see the note above
+      about the security implications of this). In general, it is
+      always best to expose the functionality of your objects through
+      methods rather than exposing attributes directly.
+
+      Note also that the actual 'ClassSecurityInfo' instance you use to
+      make security assertions is implemented such that it is *never*
+      accessible from restricted code or through the Web (no action on
+      the part of the programmer is required to protect it).
+
+    Inheritance And Class Security Declarations
+
+      Python inheritance can prove confusing in the face of security
+      declarations.
+
+        % rboylan - July 20, 2002 2:14 am:
+         This comment extends beyond security to all the meta information (e.g., meta_type) that Zope embeds in the
+         class declarations. It seems that, in general, that information is not inherited as one would expect.
+         If this is so, it would probably be worth discussing it explicitly somewhere to alert people to this. Even
+         better would be to get inheritance to work "normally" with this information as well. This is one of the
+         elements of cognitive friction that slows down acquisition of Zope (pun intended).
+
+      If a base class which has already been run through
+      "InitializeClass" is inherited by a superclass, nothing special
+      needs to be done to protect the base class' methods within the
+      superclass unless you wish to modify the declarations made in
+      the base class.  The security declarations "filter down" into
+      the superclass.
+
+        % rboylan - July 20, 2002 2:10 am:
+         I really hope all occurrences of "superclass" in the preceding paragraph should be "subclass", or else I'm
+         very confused.
+
+        % jhohm - Sep. 5, 2002 7:40 pm:
+         That's the trouble with "superclass" and "subclass". The author intended that "superclass" indicate the
+         derived class that inherits from the "subclass", so the "superclass" has features that are a superset of the
+         "subclass". Others tend to look at things the opposite way. I propose using "base class" and "derived class"
+         consistently, as they are commonly understood.
+
+        % Anonymous User - Oct. 3, 2002 7:12 am:
+         That's just plain wrong. "superclass" and "subclass" are well-defined object-oriented programming terms,
+         which the author is using wrongly here. You can't "look at it the opposite way".
+
+      On the other hand, if a base class hasn't been run through the
+      global class initializer ('InitializeClass'), you need to proxy
+      its security declarations in the superclass if you wish to
+      access any of its methods within through-the-web code or via URL
+      traversal.
+
+      In other words, security declarations that you make using
+      'ClassSecurityInfo' objects effect instances of the class upon
+      which you make the declaration. You only need to make security
+      declarations for the methods and subobjects that your class
+      actually *defines*. If your class inherits from other classes,
+      the methods of the base classes are protected by the security
+      declarations made in the base classes themselves. The only time
+      you would need to make a security declaration about an object
+      defined by a base class is if you needed to *redefine* the
+      security information in a base class for instances of your own
+      class. An example below redefines a security assertion in a
+      subclass::
+
+        from AccessControl import ClassSecurityInfo
+        import Globals
+
+        class MailboxBase(ObjectManager):
+          """A mailbox base class."""
+
+          # Create a SecurityInfo for this class
+          security = ClassSecurityInfo()
+
+          security.declareProtected('View Mailbox', 'listMessages')
+          def listMessages(self):
+            """Return a sequence of message objects."""
+            return self._messages[:]
+
+          security.setPermissionDefault('View Mailbox', ('Manager', 'Mailbox Owner'))
+
+        Globals.InitializeClass(MailboxBase)
+
+        class MyMailbox(MailboxBase):
+          """A mailbox subclass, where we want the security for 
+            listMessages to be public instead of protected (as 
+            defined in the base class)."""
+
+          # Create a SecurityInfo for this class
+          security = ClassSecurityInfo()
+
+          security.declarePublic('listMessages')
+
+        Globals.InitializeClass(MyMailbox)
+
+        % Anonymous User - June 25, 2002 6:13 am:
+         "If your class inherits from other classes, the methods of the base classes are protected by the security
+         declarations made in the base classes themselves. "
+         Maybe this is true for non-static methods, however I have contradicting experience with static methods:
+         class base:
+           index_html = Globals.DTMLFile('dtml/index_html',globals())
+           security.declarePrivate('index_html')
+         class derived(base):
+           pass # index_html *is* accessible here ...
+         Regards
+         dvadasz _at_ amadeus _dot_ net
+
+        % rboylan - July 20, 2002 2:07 am:
+         There is a lot of ambiguity in this whole section.  I have summarized 
+         Chris McDonough's responses in [].
+         1. If a subclass redefines a base class method, does the subclass need
+         to do a security declaration on it?  The document says "You only need
+         to make security declarations for methods .... your class actually
+         defines.  If your class inherits from other classes, the methods of
+         the base classes are protected by the security declarations made in
+         the base classes."  The first sentence seems to indicate a security
+         declaration is necessary (since you define the method); the second
+         sentence suggests its not.  It depends partly on the meaning of
+         "define" and also "method" (that is, is redefinition considered
+         definition?  does method refer to a name or to a specific classes
+         implementation of that name?). [Yes, you do need to redeclare security.]
+         2. Does a subclass need to have
+            security = ClassSecurityInfo()
+         in it if the base class does?  Judging from the example, yes. [yes]
+         3. Under what circumstances is the declaration in 2 necessary?  For
+         example, only if new method names are introduced and protected?  Or
+         any reference to security in the subclass?  It seems the latter, from
+         the example. [any time a class references the security object it should define
+         a new one with security = ClassSecurityInfo().]
+         4. Suppose we wanted to change the security of a base class method
+         without otherwise redefining it.  What's necessary then?
+         [You need to have a security = ... statement and you need to do
+         an InitializeClass.]
+         5. Under what conditions is InitializeClass necessary for the subclass
+         when the base class has been through InitializeClass?  (The guide only
+         addresses the case when the base class has not been so processed.  It
+         also says the declarations "filter down", but the implication of this
+         for new method is unclear.)
+         [Anytime you have a security = you should do an InitializeClass.
+         You can never cause trouble by doing an InitializeClass]
+         This section has a lot of explicit discussion of odd cases (no
+         security in superclass, redefining permissions on existing methods
+         without changing them) and not enough about the normal cases (my
+         subclass extends some base class methods and defines some new ones).
+         [A later response indicated it might be possible to get away without some of these activities, but it is
+         definitely safe to do them.]
+         RossBoylan at stanfordalumni.org
+
+        % Anonymous User - July 20, 2002 2:44 pm:
+         Let's do some investigation.  This is going to be sublimely painful, I'm sure. ;-)
+         Let's define a simple Product named SecurityTest.
+         Its __init__.py is this:
+           import SecurityTest
+           from Products.Transience import Transience
+           form = SecurityTest.constructSecurityTestForm
+           constructor = SecurityTest.constructSecurityTest
+           def initialize(context):
+               context.registerClass(
+                   SecurityTest.SecurityTest,
+                   constructors=(form, constructor),
+                   )
+         We define a module named SecurityTest in the Product, which is initially just the following:
+           from Products.Transience.Transience import TransientObjectContainer
+           from Globals import HTMLFile, InitializeClass
+           from AccessControl import ClassSecurityInfo
+           class SecurityTest(TransientObjectContainer):
+               meta_type = 'Security Test'
+           constructSecurityTestForm = HTMLFile(
+               'dtml/addSecurityTest', globals())
+           def constructSecurityTest(self, id, title='', timeout_mins=20,
+               addNotification=None, delNotification=None, limit=0, REQUEST=None):
+               """ """
+               ob = SecurityTest(id, title, timeout_mins,
+                       addNotification, delNotification, limit=limit)
+               self._setObject(id, ob)
+               if REQUEST is not None:
+                   return self.manage_main(self, REQUEST, update_menu=1)
+         The HTMLFile instance is just a copy of the constructTransientObjectContainer form in the Transience product
+         modified with the right target to create a SecurityTest instance. Note that we don't run the SecurityTest
+         class through InitializeClass, nor do we allow it to make any of its own security declarations.
+         Then we fire up Zope, log in as the admin user and call the URL
+         http://localhost:8080/security_test/foo/getSubobjectLimit .
+         Note that we didn't declare any security assertions on the SecurityTest class and we didn't run the
+         SecurityTest class through InitializeClass, and we can still view the getSubobjectLimit method, which is
+         defined in the base class and protected by the 'View management screens' permission in the base class. So we
+         know we don't have to run InitializeClass on a subclass of a security-aware class which doesn't override any
+         of its superclass' methods.
+         Let's go override the getObjectLimit method in the SecurityTest subclass.
+           class SecurityTest(TransientObjectContainer):
+               meta_type = 'Security Test'
+               def getSubobjectLimit(self):
+                   """ """
+                   return 100
+         When calling the same URL, we find that we can still call the getSubobjectLimit method while logged in as the
+         admin user, even though we didn't give it any security declarations of its own (its security declaration was
+         inherited from the base class). So we know we don't need to declare security assertions on methods of classes
+         which inherit from base classes which have security assertions unless we want to change those assertions.
+         So now let's go modify the base class, declaring the getSubobjectLimit method private by changing the class:
+             #security.declareProtected(MGMT_SCREEN_PERM, 'getSubobjectLimit')
+             security.declarePrivate('getSubobjectLimit')
+             def getSubobjectLimit(self):
+                 """ """
+                 return self._limit
+         In either the case where we override the security declaration in the SecurityTest subclass or we remove the
+         method from the subclass, we are not able to access the method any longer via its URL. So we are really,
+         definitely, positively sure that we inherit security declarations from our base class.
+         Then let's make our own security declaration on the getSubobjectLimit method in the subclass, but we won't
+         run the class through InitializeClass:
+           class SecurityTest(TransientObjectContainer):
+               meta_type = 'Security Test'
+               security = ClassSecurityInfo()
+               security.declarePublic('getSubobjectLimit')
+               def getSubobjectLimit(self):
+                   """ """
+                   return 100
+         In the base class, the getSubobjectLimit method is still declared private. But we find that even though we
+         didn't run the SecurityTest class through InitializeClass, the getSubobjectLimit method is now accessible! This
+         leads us to believe that we needn't run InitializeClass on our subclass if one of its base classes has
+         already been run through InitializeClass. How this security declaration gets applied to our class, I don't
+         know. ;-) That's the job of InitializeClass, but it's apparently unnecessary in subclasses of classes that
+         are already run through InitializeClass.
+         Let's run the SecurityTest class through InitializeClass now:
+           class SecurityTest(TransientObjectContainer):
+               meta_type = 'Security Test'
+               security = ClassSecurityInfo()
+               security.declarePublic('getSubobectLimit')
+               def getSubobjectLimit(self):
+                   """ """
+                   return 100
+           InitializeClass(SecurityTest)
+         This works, of course, and the method is still public.
+         Now, lets go pare out our method declaration and our security assertions from our subclass, but we'll still
+         run the class through InitializeClass:
+           class SecurityTest(TransientObjectContainer):
+               meta_type = 'Security Test'
+           #    security = ClassSecurityInfo()
+           #    security.declarePublic('getSubobjectLimit')
+           #    def getSubobjectLimit(self):
+           #        """ """
+           #       return 100
+           InitializeClass(SecurityTest)
+         We also go back and change our TransientObjectContainer base class' security declarations back to standard:
+             security.declareProtected(MGMT_SCREEN_PERM, 'getSubobjectLimit')
+             #security.declarePrivate('getSubobjectLimit')
+             def getSubobjectLimit(self):
+                 """ """
+                 return self._limit
+         We find that we can still access getSubobjectLimit, which is what I would expect.
+         So, the (somewhat suprising) morals of the story are:
+           - you needn't use InitializeClass on classes which inherit
+             from a base class which has security assertions
+             and has itself been run through InitializeClass if
+             a) you don't add any methods to the subclass and b)
+             you're willing to accept the base class' security
+             assertions.  Not suprising.
+           - You needn't declare security assertions on overriding methods
+             of subclasses of security-aware base classes unless you want
+             to change those assertions.  Not suprising.
+           - It's always safe to run a class through InitializeClass even
+             if it does not have security declarations of its own.  Not
+             suprising.
+           - If you declare differing security assertions in your subclass,
+             you do not need to run the subclass through InitializeClass
+             for those security assertions to have an effect.  Why this
+             is the case is still somewhat a mystery.  Surprising.
+         I'm sort of stumped as to how the subclass' assertions are applied in the absence of InitializeClass! This is
+         not what I expected, I would have thought that differing assertions would only be applied if InitializeClass
+         was called on the subclass. There's some magic going on here that I don't understand.
+         But in a nutshell, if you don't want to think about any of this (and god knows I don't):
+          - declare security assertions for each method that you define,
+            even if there is an existing security declaration for
+            the method in a superclass.  It's always clear what the
+            intention of your code is then, and you won't piss off
+            any of your coworkers. ;-)
+          - always run your classes through InitializeClass
+         - C
+
+        % Anonymous User - July 20, 2002 8:43 pm:
+         Florent Guillaume clears things up:
+         > The magic is that Persistent has a __class_init__ that calls
+         > InitializeClass for you. (This attribute is actually set by
+         > App.PersistentExtra, called from Globals.)
+         This is why it's not necessary to run an inherited subclass through InitializeClass (if it inherits from
+         Persistent).
+
+        % mcdonc - Mar. 8, 2004 4:18 am:
+         From Collector issue 270:
+           I dont seem to be able to inherit security declarations for names beginning with "manage", see
+           the test product attached.  default__class_init (in App/class_init.py), ca. line 119 seems to hint
+           that names beginning with "manage" are treated differentlyIf this is intended behaviour I think it
+           should be documented in the DevGuide, under "Inheritance And Class Security Declarations" (is it
+           documented somewhere?)
+         It is now. ;-) If there is not a security assertion associated directly in the class in which they are
+         defined, methods that begin with "manage_" will automatically be protected with assertions that allow only
+         users with the 'Manager' role to call them.
+
+    Class Security Assertions In Non-Product Code (External Methods/Python Scripts)
+
+      Objects that are returned from Python Scripts or External
+      Methods need to have assertions declared for themselves before
+      they can be used in restricted code.  For example, assume you
+      have an External Method that returns instances of a custom
+      'Book' class. If you want to call this External Method from
+      DTML, and you'd like your DTML to be able to use the returned
+      'Book' instances, you will need to ensure that your class
+      supports Acquisition, and you'll need to make security
+      assertions on the 'Book' class and initialize it with the global
+      class initializer (just as you would with a class defined in a
+      Product). For example::
+
+        # an external method that returns Book instances
+
+        from AccessControl import ClassSecurityInfo
+        from Acquistion import Implicit
+        import Globals
+
+        class Book(Implicit):
+
+          def __init__(self, title):
+            self._title=title
+
+          # Create a SecurityInfo for this class
+          security = ClassSecurityInfo()
+          security.declareObjectPublic()
+
+          security.declarePublic('getTitle')
+          def getTitle(self):
+            return self._title
+
+        Globals.InitializeClass(Book)
+
+        # The actual external method
+        def GetBooks(self):
+          books=[]
+          books.append(Book('King Lear').__of__(self))
+          books.append(Book('Romeo and Juliet').__of__(self))
+          books.append(Book('The Tempest').__of__(self))
+          return books
+
+        % Anonymous User - Dec. 3, 2003 11:29 pm:
+         typo error in the above source code, line 3, missing 'i' for Acquisition:
+                                                                            ^
+           from Acquistion import Implicit
+                     ^
+
+      Note that we *wrap* the book instances by way of their __of__
+      methods to obtain a security context before returning them.
+
+        % slinkp - Sep. 19, 2005 1:17 pm:
+         This is important. If you do not wrap all your instances, all your security declarations are useless and
+         you'll get Unauthorized errors. I seem to
+         forget to do this periodically.
+         The alternative is to use allow_class() together with 
+         ModuleSecurityInfo().declarePublic() as described in a comment below,
+         under "Utility Functions For Allowing Import of Modules By Through The Web Code".
+         This is quick and expedient, but maybe gives you less control.
+
+      Note that this particular example is slightly dangerous.  You
+      need to be careful that classes defined in external methods not
+      be made persistent, as this can cause Zope object database
+      inconsistencies.  In terms of this example, this would mean that
+      you would need to be careful to not attach the Book object
+      returned from the 'GetBook' method to a persistent object within
+      the ZODB. See Chapter 4, "ZODB Persistent Components" for more
+      information.  Thus it's generally a good idea to define the Book
+      class in a Product if you want books to be persistent.  It's
+      also less confusing to have all of your security declarations in
+      Products.
+
+      However, one benefit of the 'SecurityInfo' approach is that it
+      is relatively easy to subclass and add security info to classes
+      that you did not write. For example, in an External Method, you
+      may want to return instances of 'Book' although 'Book' is
+      defined in another module out of your direct control. You can
+      still use 'SecurityInfo' to define security information for the
+      class by using::
+
+        # an external method that returns Book instances
+
+        from AccessControl import ClassSecurityInfo
+        from Acquisition import Implicit
+        import bookstuff
+        import Globals
+
+        class Book(Implicit, bookstuff.Book):
+          security = ClassSecurityInfo()
+          security.declareObjectPublic()
+          security.declarePublic('getTitle')
+
+        Globals.InitializeClass(Book)
+
+        # The actual external method
+        def GetBooks(self):
+          books=[]
+          books.append(Book('King Lear'))
+          books.append(Book('Romeo and Juliet'))
+          books.append(Book('The Tempest'))
+          return books
+
+  Module Security Assertions
+
+    Another kind of 'SecurityInfo' object you will use as a
+    component developer is the 'ModuleSecurityInfo' object.
+
+    'ModuleSecurityInfo' objects do for objects defined in modules
+    what 'ClassSecurityInfo' objects do for methods defined in
+    classes.  They allow module-level objects (generally functions) to
+    be protected by security assertions.  This is most useful when
+    attempting to allow through-the-web code to 'import' objects
+    defined in a Python module.
+
+    One major difference between 'ModuleSecurityInfo' objects and
+    ClassSecurityInfo objects is that 'ModuleSecurityInfo' objects
+    cannot be declared 'protected' by a permission.  Instead,
+    ModuleSecurityInfo objects may only declare that an object is
+    'public' or 'private'.  This is due to the fact that modules are
+    essentially "placeless", global things, while permission
+    protection depends heavily on "place" within Zope.
+
+    Declaring Module Security
+
+      In order to use a filesystem Python module from restricted code
+      such as Python Scripts, the module must have Zope security
+      declarations associated with functions within it.  There are a
+      number of ways to make these declarations:
+
+        - By embedding the security declarations in the target module.
+          A module that is written specifically for Zope may do so,
+          whereas a module not specifically written for Zope may
+          not be able to do so.
+
+        - By creating a wrapper module and embedding security
+          declarations within it.  In many cases it is difficult,
+          impossible, or simply undesirable to edit the target module.
+          If the number of objects in the module that you want to
+          protect or make public is small, you may wish to simply
+          create a wrapper module.  The wrapper module imports objects
+          from the wrapped module and provides security declarations
+          for them.
+
+        - By placing security declarations in a filesystem Product.
+          Filesystem Python code, such as the '__init__.py' of a
+          Product, can make security declarations on behalf of an
+          external module.  This is also known as an "external"
+          module security info declaration.
+
+      The 'ModuleSecurityInfo' class is defined in the 'AccessControl'
+      package of the Zope framework. 
+
+    Using ModuleSecurityInfo Objects
+
+      Instances of 'ModuleSecurityInfo' are used in two different
+      situations.  In embedded declarations, inside the module they
+      affect.  And in external declarations, made on behalf of a
+      module which may never be imported.
+
+    Embedded ModuleSecurityInfo Declarations
+
+      An embedded ModuleSecurityInfo declaration causes an object in its
+      module to be importable by through-the-web code.
+
+        % mcdonc - June 3, 2002 10:12 am:
+         Note that the code that calls ModuleSecurityInfo must be *run* at some point before these security
+         declarations will take effect. The easiest way to make sure that this happens is to put the declarations in
+         your Product's __init__.py.
+
+      Here's an example of an embedded declaration::
+
+        from AccessControl import ModuleSecurityInfo
+        modulesecurity = ModuleSecurityInfo()
+        modulesecurity.declarePublic('foo')
+
+        def foo():
+            return "hello"
+            # foo
+
+        modulesecurity.apply(globals())
+
+      When making embedded ModuleSecurityInfo declarations, you should
+      instantiate a ModuleSecurityInfo object and assign it to a name.
+      It's wise to use the recommended name 'modulesecurity' for
+      consistency's sake.  You may then use the modulesecurity
+      object's 'declarePublic' method to declare functions inside of
+      the current module as public.  Finally, appending the last line
+      ("modulesecurity.apply(globals())") is an important step.  It's
+      necessary in order to poke the security machinery into action.
+      The above example declares the 'foo' function public.
+
+      The name 'modulesecurity' is used for consistency and for the
+      benefit of new component authors, who often learn from looking
+      at other people's code.  You do not have to use the name
+      'modulesecurity' for the security infrastructure to recognize
+      your assertion information, but it is recommended as a
+      convention.
+
+    External ModuleSecurityInfo Declarations
+
+      By creating a ModuleSecurityInfo instance with a module name
+      argument, you can make declarations on behalf of a module
+      without having to edit or import the module.
+
+      Here's an example of an external declaration::
+
+         from AccessControl import ModuleSecurityInfo
+         # protect the 'foo' function within (yet-to-be-imported) 'foomodule'
+         ModuleSecurityInfo('foomodule').declarePublic('foo')
+
+      This declaration will cause the following code to work within
+      PythonScripts::
+
+         from foomodule import foo
+
+      When making external ModuleSecurityInfo declarations, you
+      needn't use the "modulesecurity.apply(globals())" idiom
+      demonstrated in the embedded declaration section above.  As a
+      result, you needn't assign the ModuleSecurityInfo object to the
+      name 'modulesecurity'.
+
+    Providing Access To A Module Contained In A Package
+
+      Note that if you want to provide access to a module inside of a
+      package which lives in your PYTHONPATH, you'll need to provide
+      security declarations for *all of the the packages and
+      sub-packages along the path used to access the module.*
+
+      For example, assume you have a function foo, which lives inside
+      a module named 'module', which lives inside a package named
+      'package2', which lives inside a package named 'package1' You
+      might declare the 'foo' function public via this chain of
+      declarations::
+
+        ModuleSecurityInfo('package1').declarePublic('package2')
+        ModuleSecurityInfo('package1.package2').declarePublic('module')
+        ModuleSecurityInfo('package1.package2.module').declarePublic('foo')
+
+      Note that in the code above we took 
+      the following steps:
+
+       - make a ModuleSecurityInfo object for 'package1'
+
+       - call the declarePublic method of the 'package1'
+         ModuleSecurityInfo object, specifying 'package2' as what
+         we're declaring public.  This allows through the web code to
+         "see" package2 inside package1.
+
+       - make a ModuleSecurityInfo object for 'package1.package2'.
+
+       - call the declarePublic method of the 'package1.package2'
+         ModuleSecurityInfo object, specifying 'module' as what we're
+         declaring public.  This allows through the web code to "see"
+         'package1.package2.module'.
+
+       - declare 'foo' public inside the ModuleSecurityInfo for
+         'package1.package2.module'. 
+
+       Through-the-web code may now perform an import ala: 'import
+       package1.package2.module.foo'
+
+       Beware that Zope is buggy from 2.3 to 2.5.0b3.  If you make
+       module security declarations in more than one Product, only one
+       of the Products' security assertions will actually take effect.
+       This is repaired in Zope 2.5.0 and beyond.
+
+       % anonymous user - Note also that from 2.5b4, you can
+        use
+        ModuleSecurityInfo('Products.Catalog').declarePublic('CatalogError')
+        You do not need multiple declarations to traverse packages
+        anymore.
+
+       Many people who use Zope will be concerned with using
+       ModuleSecurityInfo to make declarations on modules which live
+       within Zope's Products directory.  This is just an example of
+       declaring module security on a module within a package.  Here
+       is an example of using ModuleSecurityInfo to make security
+       declarations on behalf of the 'CatalogError' class in the
+       'ZCatalog.py' module.  This could be placed, for instance,
+       within the any Product's '__init__.py' module::
+
+        from AccessControl import ModuleSecurityInfo
+        ModuleSecurityInfo('Products').declarePublic('Catalog')
+        ModuleSecurityInfo('Products.Catalog').declarePublic('CatalogError')
+
+         % Anonymous User - Apr. 5, 2002 1:56 pm:
+          shouldn't it be ZCatalog, not Catalog?
+
+    Declaring Module Security On Modules Implemented In C
+
+      Certain modules, such as the standard Python 'sha' module,
+      provide extension types instead of classes, as the 'sha' module
+      is implemented in C. Security declarations typically cannot be
+      added to extension types, so the only way to use this sort of
+      module is to write a Python wrapper class, or use External
+      Methods.
+
+    Default Module Security Info Declarations
+
+      Through-the-web Python Scripts are by default able to import a
+      small number of Python modules for which there are security
+      declarations. These include 'string', 'math', and 'random'. The
+      only way to make other Python modules available for import is to
+      add security declarations to them in the filesystem.
+
+    Utility Functions For Allowing Import of Modules By Through The Web Code
+
+      Instead of manually providing security declarations for each
+      function in a module, the utility function "allow_class" and
+      "allow_module" have been created to help you declare the entire
+      contents of a class or module as public.
+
+      You can handle a module, such as base64, that contains only safe
+      functions by writing 'allow_module("module_name")'.  For
+      instance::
+
+        from Products.PythonScripts.Utility import allow_module
+        allow_module("base64")
+
+      This statement declares all functions in the 'base64' module
+      ( 'encode', 'decode', 'encodestring', and 'decodestring' ) as
+      public, and from a script you will now be able to perform an
+      import statement such as "from base64 import encodestring".
+
+        % slinkp - Sep. 19, 2005 1:06 pm:
+         allow_class(foo.Foo) allows you to import Foo in untrusted code, but
+         if the class doesn't have security declarations, you'll get Unauthorized errors
+         when you try to call methods on instances of Foo. You can avoid that by doing:
+         ModuleSecurityInfo('foo').declarePublic('Foo')
+         I find this very useful for simple record-type classes and things from third-party code that aren't worth the
+         effort to wrap in a full security-declared Zope product,
+         especially if it isn't convenient to ensure that my instances are acquisition-wrapped.
+
+      To allow access to only some names in a module, you can eschew
+      the allow_class and allow_module functions for the lessons you
+      learned in the previous section and do the protection
+      "manually"::
+
+        from AccessControl import ModuleSecurityInfo
+        ModuleSecurityInfo('module_name').declarePublic('name1','name2', ...)
+
+  Making Permission Assertions On A Constructor
+
+    When you develop a Python disk-based product, you will generally
+    be required to make "constructor" methods for the objects which
+    you wish to make accessible via the Zope management interface by
+    users of your Product.  These constructors are usually defined
+    within the modules which contain classes which are intended to be
+    turned into Zope instances.  For more information on how
+    constructors are used in Zope with security, see Chapter 3 "Zope
+    Products".
+
+    The Zope Product machinery "bootstraps" Product-based classes with
+    proper constructors into the namespace of the Zope management
+    interface "Add" list at Zope startup time.  This is done as a
+    consequence of registering a class by way of the Product's
+    '__init__.py' 'intialize' function.  If you want to make, for
+    example, the imaginary 'FooClass' in your Product available from
+    the "Add" list, you may construct an '__init__.py' file that looks
+    much like this::
+
+      from FooProduct import FooClass
+
+      def initialize(context):
+          """ Initialize classes in the FooProduct module """
+          context.registerClass(
+              FooProduct.FooClass, # the class object
+              permission='Add FooClasses',
+              constructors=(FooProduct.manage_addFooClassForm,
+                            FooProduct.manage_addFooClass),
+              icon='foo.gif'
+              )
+
+      % Anonymous User - Nov. 9, 2002 2:12 pm:
+       /imaginary FooClass in your Product/imaginary FooClass Product/
+       you mean the ZMI [Add list], not the products. Assume FooClass has no Add list. blf
+
+    The line of primary concern to us above is the one which says
+    "permission='Add FooClasses'".  This is a permission declaration
+    which, thanks to Zope product initialization, restricts the adding
+    of FooClasses to those users who have the 'Add FooClasses'
+    permission by way of a role association determined by the system
+    administrator.
+
+    If you do not include a 'permission' argument to 'registerClass',
+    then Zope will create a default permission named 'Add
+    [meta-type]s'.  So, for example, if your object had a meta_type of
+    'Animal', then Zope would create a default permission, 'Add
+    Animals'.  For the most part, it is much better to be explicit
+    then to rely on Zope to take care of security details for you, so
+    be sure to specify a permission for your object.
+
+  Designing For Security
+
+    "Security is hard." -- Jim Fulton.
+
+    When you're under a deadline, and you "just want it to work",
+    dealing with security can be difficult.  As a component developer,
+    following these basic guidelines will go a long way toward
+    avoiding problems with security integration. They also make a good
+    debugging checklist!
+
+      - Ensure that any class that needs to work with security has 
+        'Acquisition.Implicit' or 'Acquisition.Explicit' somewhere 
+        in its base class hierarchy.
+
+      - Design the interface to your objects around methods; don't
+        expect clients to access instance attributes directly.
+
+      - Ensure that all methods meant for use by restricted code 
+        have been protected with appropriate security assertions.
+
+      - Ensure that you called the global class initializer on 
+        all classes that need to work with security.
+
+  Compatibility
+
+    The implementation of the security assertions and 'SecurityInfo'
+    interfaces described in this document are available in Zope 2.3
+    and higher.
+
+    Older Zope Products do not use the 'SecurityInfo' interfaces for
+    security assertions, because these interfaces didn't exist at the
+    time.  These Zope products will continue to work without modification
+    until further notice.
+
+  Using The RoleManager Base Class With Your Zope Product
+
+    After your Product is deployed, system managers and other users of
+    your Product often must deal with security settings on instances
+    they make from your classes.
+
+    Product classes which inherit Zope's standard RoleManager base
+    class allow instances of the class to present a security
+    interface.  This security interface allows managers and developers
+    of a site to control an instance's security settings via the Zope
+    management interface.
+
+    The user interface is exposed via the *Security* management view.
+    From this view, a system administrator may secure instances of
+    your Product's class by associating roles with permissions and by
+    asserting that your object instance contains "local roles".  It
+    also allows them to create "user-defined roles" within the Zope
+    management framework in order to associate these roles with the
+    permissions of your product and with users.  This user interface
+    and its usage patterns are explained in more detail within the
+    Zope Book's security
+    chapter.
+
+      % Anonymous User - Nov. 9, 2002 2:20 pm:
+       More current: http://www.zope.org/Documentation/Books/ZopeBook/current/Security.stx
+       http://www.zope.org/Documentation/Books/ZopeBook/2_6Edition/Security.stx
+
+    If your Product's class does not inherit from 'RoleManager', its
+    methods will still retain the security assertions associated with
+    them, but you will be unable to allow users to associate roles
+    with the permissions you've defined respective to instances of
+    your class.  Your objects will also not allow local role
+    definitions.  Note that objects which inherit from the
+    'SimpleItem.SimpleItem' mixin class already inherit from
+    'RoleManager'.
+
+  Conclusion
+
+    Zope security is based upon roles and permissions. Users have
+    roles. Security policies map permissions to roles. Classes
+    protect methods with permissions. As a developer you main job is
+    to protect your classes by associating methods with
+    permissions. Of course there are many other details such as
+    protecting modules and functions, creating security user
+    interfaces, and initializing security settings.
+
+      % Anonymous User - Jan. 4, 2002 4:14 pm - This from Dieter Maurer --    The basic security mechanism uses the attribute "m__roles__" in order  to protect "m". If this attribute it "None", then "m" is public.  Otherwise, it is expected to be a sequence of roles that are allowed  to use "m".    But, "ExtensionsClass" brings with it computed attributes. This allows  "m__roles__" to be not a sequence but a method returning a sequence.  When you protect "m" with a permission "p", then  "m__roles__" is set to "PermissionRole(p)". This instance dynamically  evaluates into a sequence of roles by crawling up the "aq_container"  (which is correctly "aq_parent" after "aq_inner") chain and translating  "p" into roles by interpreting the "permission-to-role" mapping  it finds on its way to the application object.    Therefore, "declarePublic" works for non-wrapped instances while  "declareProtected" requires the wrapping.
+
+      % Anonymous User - May 3, 2002 4:47 am:
+       From Julian Munoz -- I don't find any reference to security and object properties.
+
+      % d.maurer - Aug. 22, 2002 4:04 am:
+       I would like to learn details about __guarded_getattr__,
+       __guarded_setattr__ and friends.
+       What is their semantics?
+       Are they expected to do their own security checks (I
+       expect a "yes" to this question)?
+       Is it usually safe to implement "__guarded_getattr__"
+       by:
+         result= getattr(self,key)
+         if not getSecurityManager().validate(accessed=self,
+                                              name=key,
+                                              value= result)
+           raise 'Unauthorized', attr
+         return result
+
+      % d.maurer - Aug. 22, 2002 4:29 am:
+       I would like to learn about the details of
+       "validate", at least for the ZopeSecurityPolicy.
+       What precisely are "accessed" and "container".
+       "ZopeSecurityPolicy.validate" contains several
+       mysterious instances of
+               if accessedbase is containerbase:
+                  raise Unauthorized
+       What is the purpose for this. I cannot understand
+       the accompanying comment.
+       Why does it not "return 0" but raises "Unauthorizred"?
+       What about a succint semantics for
+       "ZopeSecurityPolicy.validate". It should describe
+       when the function returns "1", "0" and raises an
+       exception.

Added: zdgbook/trunk/TestingAndDebugging.stx
===================================================================
--- zdgbook/trunk/TestingAndDebugging.stx	                        (rev 0)
+++ zdgbook/trunk/TestingAndDebugging.stx	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,946 @@
+Chapter 7: Testing and Debugging
+
+  As you develop Zope applications you will run into problems. This
+  chapter covers debugging and testing techniques that can help
+  you. The Zope debugger allow you to peek inside a running process
+  and find exactly what is going wrong. Unit testing allows you to
+  automate the testing process to ensure that your code still works
+  correctly as you change it. Finally, Zope provides logging
+  facilities which allow you to emit warnings and error messages.
+
+    % Anonymous User - Jan. 24, 2002 7:01 am - (This comment does not really fit here but I think it is important)  Use the source!    If you want to know how a function in Zope doesn't do what it should  try to find it: "cd $ZOPE_HOME/lib/python; grep -irs "def foo" *    Windows Users: grep is included in cygwin (http://www.cygwin.com/)
+
+    % Anonymous User - Jan. 8, 2003 11:50 pm:
+     rwar
+
+  Debugging
+
+    Zope provides debugging information through a number of
+    sources. It also allows you a couple avenues for getting
+    information about Zope as it runs.
+
+    The Control Panel
+
+      The control panel provides a number of views that can help you
+      debug Zope, especially in the area of performance.  The
+      *Debugging Information* link on the control panel provides two
+      views, *Debugging Info* and *Profiling*.
+
+        % Anonymous User - May 8, 2005 11:50 pm:
+         Whee!
+
+      Debugging info provides information on the number of object
+      references and the status of open requests.  The object
+      references list displays the name of the object and the number
+      of references to that object in Zope.  Understanding how
+      reference counts help debugging is a lengthy subject, but in
+      general you can spot memory leaks in your application if the
+      number of references to certain objects increases without bound.
+      The busier your site is, or the more content it holds, the more
+      reference counts you will tend to have.
+
+      Profiling uses the standard Python profiler.  This is turned on
+      by setting the 'PROFILE_PUBLISHER' environment variable before
+      executing Zope.
+
+        % Anonymous User - Mar. 11, 2003 5:31 am:
+         It doesn't work for me :(
+         In ZOPE_HOME/start i set 
+         export PROFILE_PUBLISHER="/usr/local/zope/profile.out"
+         exec /usr/local/bin/python $cwd/z2.py -D -u flo "$@"
+         but i don't get any change in the profiling tab
+
+        % Anonymous User - Mar. 17, 2003 4:22 pm:
+         It's ok now. - I'm not sure what my mistake was.
+
+      When the profiler is running, the performance of your Zope
+      system will suffer a lot.  Profiling should only be used for
+      short periods of time, or on a separate ZEO client so that your
+      normal users to not experience this significant penalty.
+
+        % Anonymous User - Jan. 28, 2002 4:01 pm - typo /users to not/users do not/
+
+      Profiling provides you with information about which methods in
+      your Zope system are taking the most time to execute.  It builds
+      a *profile*, which lists the busiest methods on your system,
+      sorted by increasing resource usage.  For details on the meaning
+      of the profiler's output, read the "standard Python
+      documentation":http://www.python.org/doc/current/lib/profile.html.
+
+        % Anonymous User - Nov. 3, 2004 7:22 am:
+         testing
+
+    Product Refresh Settings
+
+      As of Zope 2.4 there is a *Refresh* view on all Control Panel
+      Products. Refresh allows you to reload your product's modules as
+      you change them, rather than having to restart Zope to see your
+      changes. The *Refresh* view provides the same debugging
+      functionality previously provided by Shane Hathaway's Refresh
+      Product.
+
+      To turn on product refresh capabilities place a 'refresh.txt'
+      file in your product's directory. Then visit the *Refresh* view
+      of your product in the management interface. Here you can
+      manually reload your product's modules with the *Refresh this
+      product* button. This allows you to immediately see the effect
+      of your changes, without restarting Zope. You can also turn on
+      automatic refreshing which causes Zope to frequently check for
+      changes to your modules and refresh your product when it detects
+      that your files have changed. Since automatic refresh causes
+      Zope to run more slowly, it is a good idea to only turn it on
+      for a few products at a time.
+
+        % Anonymous User - Nov. 16, 2002 4:04 am:
+         Does refresh.txt require any content, or is just the presence of the file enough to work the 'magic'?
+
+        % Anonymous User - Nov. 16, 2002 4:04 am:
+         And just how do you turn on (and off) automatic refreshing?
+
+        % Anonymous User - Nov. 28, 2002 11:39 am:
+         I just found the answer to your question here:
+         http://www.zope.org/Members/dylanr/prod_tips
+         It must be an empty file. Anyway, I agree that they should be more specific. About your second question, I
+         also don't have any idea about how does it work.
+
+        % Anonymous User - Nov. 30, 2002 1:36 pm:
+         Doesn't work for me (Zope 2.5.1) using the Poll Product from the earlier in the book. Neither explicit
+         refreshes of the product nor the refresh.txt mechanism appear to reload changed code. I need to restart zope
+         before changes take effect.
+
+        % Anonymous User - Jan. 28, 2003 2:59 am:
+         I also have to restart Zope before changes take effect. (2.6.0)
+
+        % to_be - May 8, 2003 7:00 am:
+         An empty refresh.txt is sufficient, but if there is text in it, this will be displayed as 'Important
+         information about refreshing this product' (Zope 2.6.1)
+
+    Debug Mode
+
+      Setting the 'Z_DEBUG_MODE=1' environment puts Zope into debug
+      mode.  This mode reduces the performance of Zope a little bit.
+      Debug model has a number of wide ranging effects:
+
+        o Tracebacks are shown on the browser when errors are raised.
+
+        o External Methods and DTMLFile objects are checked to see if
+          they have been modified every time they are called.  If
+          modified, they are reloaded.
+
+        o Zope will not fork into the background in debug mode,
+          instead, it will remain attached to the terminal that
+          started it and the main logging information will be
+          redirected to that terminal.
+
+      Normally, debug mode is set using the '-D' switch when starting
+      Zope, though you can set the environment variable directly if
+      you wish.
+
+        % Anonymous User - Nov. 17, 2001 11:07 pm - In the source directory of your zope installation your start file should look something like this<br>  #! /bin/sh<br>  reldir=`dirname $0`<br>  PYTHONHOME=`cd $reldir; pwd`<br>  export PYTHONHOME<br>  exec /usr/bin/python \<br>       $PYTHONHOME/z2.py \<br>       -D "$@"<br>  <br>You will also notice that when running from the command line that the shell isn't returned to a command mode, this is normal.<br><br>
+
+      By using debug mode and product refresh together you will have
+      little reason to restart Zope while developing.
+
+    The Python Debugger
+
+      Zope is integrated with the Python debugger (pdb).  The Python
+      debugger is pretty simple as command line debuggers go, and
+      anyone familiar with other popular command line debuggers (like
+      gdb) will feel right at home in pdb.
+
+      For an introduction to pdb see the standard "pdb
+      documentation":http://www.python.org/doc/current/lib/module-pdb.html.
+
+      There are a number of ways to debug a
+      Zope process:
+
+        o You can shut down the Zope server and simulate a request on the
+          command line.
+
+        o You can run a special ZEO client that debugs a running server.
+
+        o You can run Zope in debug model and enter the debugger
+          through Zope's terminal session.
+
+      The first method is an easy way to debug Zope if you are not
+      running ZEO.  First, you must first shut down the Zope process.
+      It is not possible to debug Zope in this way and run it at the
+      same time.  Starting up the debugger this way will by default
+      start Zope in single threaded mode.
+
+        % Anonymous User - Jan. 25, 2002 9:25 am - Typo: debug modeL
+
+        % Anonymous User - July 6, 2002 7:08 am:
+         Typo: first you must first
+
+      For most Zope developer's purposes, the debugger is needed to
+      debug some sort of application level programming error.  A
+      common scenario is when developing a new product for Zope.
+      Products extend Zope's functionality but they also present the
+      same kind of debugging problems that are commonly found in any
+      programming environment.  It is useful to have an existing
+      debugging infrastructure to help you jump immediately to your
+      new object and debug it and play with it directly in pdb.  The
+      Zope debugger lets you do this.
+
+      In reality, the "Zope" part of the Zope debugger is actually
+      just a handy way to start up Zope with some pre-configured break
+      points and to tell the Python debugger where in Zope you want to
+      start debugging.
+
+      Simulating HTTP Requests
+
+        Now for an example. Remember, for this example to work, you
+        *must* shut down Zope. Go to your Zope's 'lib/python' directory
+        and fire up Python and import 'Zope' and 'ZPublisher'::
+
+          $ cd lib/python
+          $ python
+          Python 1.5.2 (#0, Apr 13 1999, 10:51:12) [MSC 32 bit (Intel)] on win32
+          Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
+          >>> import Zope, ZPublisher
+          >>>
+
+        Here you have run the Python interpreter (which is where using the
+        debugger takes place) and imported two modules, 'Zope' and
+        'ZPublisher'.  If Python complains about an 'ImportError' and not
+        being able to find either module, then you are probably in the wrong
+        directory, or you have not compiled Zope properly. If you get
+        this message::
+
+          ZODB.POSException.StorageSystemError: Could not lock the
+          database file. There must be another process that has opened
+          the file.
+
+        This tells you that Zope is currently running. Shutdown Zope and
+        try again.
+
+        The 'Zope' module is the main Zope application module. When you
+        import 'Zope' it sets up Zope. 'ZPublisher' is the Zope ORB. See
+        Chapter 2 for more information about 'ZPublisher'.
+
+        You can use the 'ZPublisher.Zope' function to simulate an HTTP
+        request. Pass the function a URL relative the your root Zope
+        object. Here is an example of how to simulate an HTTP request
+        from the debugger::
+
+          >>> ZPublisher.Zope('')
+          Status: 200 OK
+          X-Powered-By: Zope (www.zope.org), Python (www.python.org)
+          Content-Length: 1238
+          Content-Type: text/html
+
+          <HTML><HEAD><TITLE>Zope</TITLE>
+
+            ... blah blah...
+
+          </BODY></HTML>
+          >>> 
+
+          % Anonymous User - Nov. 22, 2002 8:42 am:
+           Can I use ZPublisher.Zope() as a link between an HTTP server and the ZPublisher machinery? I need to bypass
+           the ZServer and make Apache pass a web request directly to ZPublisher. Any howtos/docs available? Thanks
+
+        If you look closely, you will see that the content returned is
+        *exactly* what is returned when you call your root level object
+        through HTTP, including all the HTTP headers.
+
+        Keep in mind that calling Zope this way does NOT involve
+        a web server.  No ports are opened, the 'ZServer' code is not even
+        imported.  In fact, this is just an interpreter front end to
+        the same application code the ZServer *does* call.
+
+      Interactive Debugging
+
+        Debugging involves publishing a request up to a point where you think
+        it's failing, and then inspecting the state of your variables and
+        objects.  The easy part is the actual inspection, the hard part is
+        getting your program to stop at the right point.  
+
+        So, for the sake our example, let's say that you have a 'News'
+        object which is defined in a Zope Product called 'ZopeNews', and
+        is located in the 'lib/python/Products/ZopeNews' directory.  The
+        class that defines the 'News' instance is also called 'News',
+        and is defined in the 'News.py' module in your product.
+
+          % Anonymous User - Mar. 9, 2003 2:00 pm:
+           What exactly is a News object and how was it defined? Is it a class that was registered with the registrar
+           object? <a
+           href="http://www.zope.org/Documentation/Books/ZDG/current/Products.stx">http://www.zope.org/Documentation/Boo
+           ks/ZDG/current/Products.stx</a>
+           Object is to ambiguous of a term.
+
+        Therefore, from Zope's perspective the fully qualified name of
+        your class is 'Products.ZopeNews.News.News'.  All Zope objects
+        have this kind of fully qualified name.  For example, the
+        'ZCatalog' class can be found in
+        'Products.ZCatalog.ZCatalog.ZCatalog' (The redundancy is because
+        the product, module, and class are all named 'ZCatalog').
+
+        Now let's create an example method to debug.  You want your news
+        object to have a 'postnews' method, that posts news::
+
+          class News(...):
+
+              ...
+
+              def postnews(self, news, author="Anonymous"):
+                  self.news = news
+
+              def quote(self):
+                  return '%s said, "%s"' % (self.author, self.news)
+
+        You may notice that there's something wrong with the 'postnews'
+        method.  The method assigns 'news' to an instance variable, but
+        it does nothing with 'author'.  If the 'quote' method is called,
+        it will raise an 'AttributeError' when it tries to look up the
+        name 'self.author'.  Although this is a pretty obvious goof,
+        we'll use it to illustrate using the debugger to fix it.
+
+        Running the debugger is done in a very similar way to how you
+        called Zope through the python interpreter before, except that
+        you introduce one new argument to the call to 'Zope'::
+
+          >>> ZPublisher.Zope('/News/postnews?new=blah', d=1)
+          * Type "s<cr>c<cr>" to jump to beginning of real publishing process.
+          * Then type c<cr> to jump to the beginning of the URL traversal
+            algorithm.
+          * Then type c<cr> to jump to published object call.
+          > <string>(0)?()
+          pdb> 
+
+        Here, you call Zope from the interpreter, just like before,
+        but there are two differences.  First, you call the 'postnews'
+        method with an argument using the URL,
+        '/News/postnews?new=blah'.  Second, you provided a new
+        argument to the Zope call, 'd=1'.  The 'd' argument, when
+        true, causes Zope to fire up in the Python debugger, pdb.
+        Notice how the Python prompt changed from '>>>' to 'pdb>'.
+        This indicates that you are in the debugger.
+
+        When you first fire up the debugger, Zope gives you a helpful
+        message that tells you how to get to your object.  To
+        understand this message, it's useful to know how you have set
+        Zope up to be debugged.  When Zope fires up in debugger mode,
+        there are three breakpoints set for you automatically (if you
+        don't know what a breakpoint is, you need to read the python
+        "debugger
+        documentation":http://www.python.org/doc/current/lib/module-pdb.html.).
+
+        The first breakpoint stops the program at the point that
+        ZPublisher (the Zope ORB) tries to publish the application
+        module (in this case, the application module is 'Zope').  The
+        second breakpoint stops the program right before ZPublisher
+        tries to traverse down the provided URL path (in this case,
+        '/News/postnews').  The third breakpoint will stop the program
+        right before ZPublisher calls the object it finds that matches
+        the URL path (in this case, the 'News' object).
+
+        So, the little blurb that comes up and tells you some keys to
+        press is telling you these things in a terse way.  Hitting 's'
+        will *step* you into the debugger, and hitting 'c' will
+        *continue* the execution of the program until it hits a
+        breakpoint.
+
+        Note however that none of these breakpoints will stop the program at
+        'postnews'.  To stop the debugger right there, you need to tell the
+        debugger to set a new breakpoint.  Why a new breakpoint?  Because
+        Zope will stop you before it traverse your objects path, it will
+        stop you before it calls the object, but if you want to stop it
+        *exactly* at some point in your code, then you have to be explicit.
+        Sometimes the first three breakpoints are convienent, but often you
+        need to set your own special break point to get you exactly where
+        you want to go.
+
+        Setting a breakpoint is easy (and see the next section for an
+        even easier method).  For example::
+
+          pdb> import Products
+          pdb> b Products.ZopeNews.News.News.postnews
+          Breakpoint 5 at C:\Program Files\WebSite\lib\python\Products\ZopeNews\News.py:42
+          pdb> 
+
+        First, you import 'Products'.  Since your module is a Zope
+        product, it can be found in the 'Products' package.  Next, you
+        set a new breakpoint with the *break* debugger command (pdb
+        allows you to use single letter commands, but you could have
+        also used the entire word 'break').  The breakpoint you set is
+        'Products.ZopeNews.News.News.postnews'.  After setting this
+        breakpoint, the debugger will respond that it found the method
+        in question in a certain file, on a certain line (in this
+        case, the fictitious line 42) and return you to the debugger.
+
+        Now, you want to get to your 'postnews' method so you can
+        start debugging it.  But along the way, you must first
+        *continue* through the various breakpoints that Zope has set
+        for you.  Although this may seem like a bit of a burden, it's
+        actually quite good to get a feel for how Zope works
+        internally by getting down the rhythm that Zope uses to
+        publish your object.  In these next examples, my 
+        comments will begin with '#".  Obviously, you won't see these
+        comments when you are debugging.  So let's debug::
+
+          pdb> s
+          # 's'tep into the actual debugging
+
+          > <string>(1)?()
+          # this is pdb's response to being stepped into, ignore it
+
+          pdb> c
+          # now, let's 'c'ontinue onto the next breakpoint
+
+          > C:\Program Files\WebSite\lib\python\ZPublisher\Publish.py(112)publish()
+          -> def publish(request, module_name, after_list, debug=0,
+
+          # pdb has stopped at the first breakpoint, which is the point where
+          # ZPubisher tries to publish the application module.
+
+          pdb> c
+          # continuing onto the next breakpoint you get...
+
+          > C:\Program Files\WebSite\lib\python\ZPublisher\Publish.py(101)call_object()
+          -> def call_object(object, args, request):
+
+        Here, 'ZPublisher' (which is now publishing the application)
+        has found your object and is about to call it.  Calling your
+        object consists of applying the arguments supplied by
+        'ZPublisher' to the object.  Here, you can see how
+        'ZPublisher' is passing three arguments into this process.
+        The first argument is 'object' and is the actual object you
+        want to call.  This can be verified by *printing* the object::
+
+          pdb> p object
+          <News instance at 00AFE410>
+
+        Now you can inspect your object (with the *print* command) and
+        even play with it a bit.  The next argument is 'args'.  This
+        is a tuple of arguments that 'ZPublisher' will apply to your
+        object call.  The final argument is 'request'.  This is the
+        request object and will eventually be transformed in to the
+        DTML usable object 'REQUEST'. Now continue, your breakpoint is
+        next::
+
+          pdb> c    
+          > C:\Program Files\WebSite\lib\python\Products\ZopeNews\News.py(42)postnews()
+          -> def postnews(self, N)
+
+        Now you are here, at your method.  To be sure, tell the
+        debugger to show you where you are in the code with the 'l'
+        command. Now you can examine variable and perform all the
+        debugging tasks that the Python debugger provides.  From here,
+        with a little knowledge of the Python debugger, you should be
+        able to do any kind of debugging task that is needed.
+
+      Interactive Debugging Triggered From the Web
+
+        If you are running in debug mode you can set break points in
+        your code and then jump straight to the debugger when Zope comes
+        across your break points. Here's how to set a breakpoint::
+
+          import pdb
+          pdb.set_trace()
+
+        Now start Zope in debug mode and point your web browser at a URL
+        that causes Zope to execute the method that includes a
+        breakpoint.  When this code is executed, the Python debugger
+        will come up in the terminal where you started Zope. Also note
+        that from your web browser it looks like Zope is frozen. Really
+        it's just waiting for you do your debugging.
+
+          % strobl - Oct. 19, 2001 2:59 am - Frankly, I don't unterstand the explaination given in the last two paragraphs at all. Zope has to be started, somehow, in order to serve requests from the web - but how? Using z2.py? Please elaborate.
+
+          % chrism - Jan. 15, 2002 11:04 pm - Start Zope normally (using "start" or "start.bat") passing in the -D switch to put it in "debug" mode, which really means "dont detach from the controlling terminal".  When Zope encounters a pdb.set_trace statement, the debugger will just "show up" on the terminal from which Zope was started.
+
+          % mreinsch - Feb. 13, 2005 5:52 am:
+           Use 'runzope'
+           You should start runzope from an emacs shell. The debugger in the emacs python-mode always jumps to the
+           current position in the source code.
+           This is also useful for non emacs users!
+           Start emacs.
+           Open a shell by 'alt-x' 'shell'.
+           Type 'PATHTORUNZOPE/runzope'
+           Sometimes emacs doesn't open the source code while debugging. Usually it helps to open the python source in
+           an extra buffer before starting the debug session.
+
+        From the terminal you are inside the debugger as it is executing
+        your request.  Be aware that you are just debugging one thread
+        in Zope, and other requests may be being served by other
+        threads.  If you go to the *Debugging Info* screen while in the
+        debugger, you can see your debugging request and how long it has
+        been open.
+
+        It is often more convenient to use this method to enter the
+        debugger than it is to call 'ZPublisher.Zope' as detailed in the
+        last section.
+
+      Post-Mortem Debugging
+
+        Often, you need to use the debugger to chase down obscure
+        problems in your code, but sometimes, the problem is obvious,
+        because an exception gets raised.  For example, consider the
+        following method on your 'News' class::
+
+          def quote(self):
+              return '%s said, "%s"' % (self.Author, self.news)
+
+        Here, you can see that the method tries to substitute
+        'self.Author' in a string, but earlier we saw that this should
+        really be 'self.author'.  If you tried to run this method from
+        the command line, an exception would be raised::
+
+          >>> ZPublisher.Zope('/News/quote')
+          Traceback (most recent call last):
+            File "<stdin>", line 1, in ?
+            File "./News.py", line 4, in test
+              test2()
+            File "./News.py", line 3, in test2
+              return '%s said, "%s"' % (self.Author, self.news)
+          NameError: Author
+          >>>
+
+        Using Zope's normal debugging methods, you would typically
+        need to start from the "beginning" and step your way down
+        through the debugger to find this error (in this case, the
+        error is pretty obvious, but more often than not errors can be
+        pretty obscure!).
+
+        Post-mortem debugging allows you to jump *directly* to the
+        spot in your code that raised the exception, so you do not
+        need to go through the possibly tedious task of stepping your
+        way through a sea of Python code.  In the case of our example,
+        you can just pass 'ZPublisher.Zope' call a 'pm' argument that
+        is set to 1::
+
+          >>> ZPublisher.Zope('/News/quote', pm=1)
+          Traceback (most recent call last):
+            File "<stdin>", line 1, in ?
+            File "./News.py", line 4, in test
+              test2()
+            File "./News.py", line 3, in test2
+              return '%s said, "%s"' % (self.Author, self.news)
+          NameError: Author
+          (pdb)
+
+        Here, you can see that instead of taking you back to a python
+        prompt, the post mortem debugging flag has caused you to go
+        right into the debugging, *exactly* at the point in your code
+        where the exception is raised.  This can be verified with the
+        debugger's (l)ist command. Post mortem debugging offers you a
+        handy way to jump right to the section of your code that is
+        failing in some obvious way by raising an exception.
+
+      Debugging With ZEO
+
+        ZEO presents some interesting debugging abilities.  ZEO lets
+        you debug one ZEO client when other clients continue to server
+        requests for your site.  In the above examples, you have to
+        shut down Zope to run in the debugger, but with ZEO, you can
+        debug a production site while other clients continue to serve
+        requests. Using ZEO is beyond the scope of this
+        chapter. However, once you have ZEO running, you can debug a
+        client process exactly as you debug a single-process Zope.
+
+          % Anonymous User - Jan. 29, 2004 6:25 am:
+           I there a way to log which objects where
+           written to the ZODB on commit?
+           I want to know the objects (oid would be enough) and the 
+           size of the pickle.
+
+  Unit Testing
+
+    Unit testing allows you to automatically test your classes to make
+    sure they are working correctly. By using unit tests you can make
+    sure as you develop and change your classes that you are not
+    breaking them. Zope comes with Pyunit. You can find out more
+    information on Pyunit at "the Pyunit home
+    page":http://pyunit.sourceforge.net/. Pyunit is also part of the
+    Python "standard
+    library":http://www.python.org/doc/lib/module-unittest.html as of
+    Python 2.1.
+
+      % to_be - May 8, 2003 7:21 am:
+       Some newbie questions:
+       1. Are the tests run automatically when a product is recognised/refreshed?
+       2. If not, is there a possibility to enforce this?
+       3. How to run the tests manually (import problems; PYTHONPATH)?
+
+    What Are Unit Tests
+
+      A "unit" may be defined as a piece of code with a single intended
+      purpose.  A "unit test" is defined as a piece of code which exists
+      to codify the intended behavior of a unit and to compare its
+      intended behavior against its actual behavior.
+
+      Unit tests are a way for developers and quality assurance engineers
+      to quickly ascertain whether independent units of code are working as
+      expected.  Unit tests are generally written at the same time as the
+      code they are intended to test.  A unit testing framework allows a
+      collection of unit tests to be run without human intervention,
+      producing a minimum of output if all the tests in the collection are
+      successful.
+
+      It's a good idea to have a sense of the limits of unit testing.
+      From the "Extreme Programming Enthusiast
+      website":http://c2.com/cgi/wiki?UnitTestsDefined
+      here is a list of things that unit tests are *not*:
+
+        - Manually operated.
+
+        - Automated screen-driver tests that simulate user input (these
+          are "functional tests").
+
+        - Interactive.  They run "no questions asked."
+
+        - Coupled.  They run without dependencies except those native to
+          the thing being tested.
+
+        - Complicated.  Unit test code is typically straightforward
+          procedural code that simulates an event.
+
+    Writing Unit Tests
+
+      Here are the times when
+      you should write unit tests:
+
+        * When you write new code
+
+        * When you change and enhance existing code
+
+        * When you fix bugs
+
+      It's much better to write tests when you're working on code than
+      to wait until you're all done and then write tests.
+
+      You should write tests that exercise discrete "units" of
+      functionality. In other words, write simple, specific tests that test
+      one capability. A good place to start is with interfaces and
+      classes. Classes and especially interfaces already define units of
+      work which you may wish to test.
+
+      Since you can't possibly write tests for every single capability and
+      special case, you should focus on testing the riskiest parts of your
+      code. The riskiest parts are those that would be the most disastrous
+      if they failed. You may also want to test particularly tricky or
+      frequently changed things.
+
+      Here's an example test script that tests the 'News' class defined
+      earlier in this chapter::
+
+        import unittest
+        import News
+
+        class NewsTest(unittest.TestCase):
+
+            def testPost(self):
+                n=News()
+                s='example news'
+                n.postnews(s)
+                assert n.news==s
+
+            def testQuote(self):
+                n=News()
+                s='example news'
+                n.postnews(s)
+                assert n.quote()=='Anonymous said: "%s"' % s
+                a='Author'
+                n.postnews(s, a)
+                assert n.quote()=='%s said: "%s"' % (a, s)
+
+        def test_suite():
+           return unittest.makeSuite(NewsTest, 'news test')
+
+        def main():
+           unittest.TextTestRunner().run(test_suite())
+
+        if __name__=="__main__":
+            main()
+
+      You should save tests inside a 'tests' sub-directory in your
+      product's directory. Test scripts file names should start with
+      test, for example 'testNews.py'. You may accumulate many test
+      scripts in your product's 'tests' directory.  You can run test
+      your product by running the test scripts.
+
+      We cannot cover all there is to say about unit testing here. Take a
+      look at the Pyunit
+      "documentation":http://pyunit.sourceforge.net/pyunit.html for more
+      background on unit testing.
+
+    Zope Test Fixtures
+
+      One issue that you'll run into when unit testing is that you may
+      need to set up a Zope environment in order to test your
+      products. You can solve this problem in two ways. First, you can
+      structure your product so that much of it can be tested without
+      Zope (as you did in the last section). Second, you can create a
+      test fixture that sets up a Zope environment for testing.
+
+      To create a test fixture for Zope you'll
+      need to:
+
+        1. Add Zope's 'lib/python' directory to the Python path.
+
+        2. Import 'Zope' and any other needed Zope modules and packages.
+
+        3. Get a Zope application object.
+
+        4. Do your test using the application object.
+
+        5. Clean up the test by aborting or committing the transaction
+           and closing the Zope database connection.
+
+      Here's an example Zope test fixture that demonstrates how to do
+      each of these steps::
+
+        import os, os.path, sys, string
+        try:
+            import unittest
+        except ImportError:
+            fix_path()
+            import unittest
+
+        class MyTest(unittest.TestCase):
+
+            def setUp(self):
+                # Get the Zope application object and store it in an
+                # instance variable for use by test methods
+                import Zope
+                self.app=Zope.app()
+
+            def tearDown(self):
+                # Abort the transaction and shut down the Zope database
+                # connection.
+                get_transaction().abort()
+                self.app._p_jar.close()
+
+            # At this point your test methods can perform tests using
+            # self.app which refers to the Zope application object.
+
+            ...
+
+        def fix_path():
+            # Add Zope's lib/python directory to the Python path
+            file=os.path.join(os.getcwd(), sys.argv[0])
+            dir=os.path.join('lib', 'python')
+            i=string.find(file, dir)
+            sys.path.insert(0, file[:i+len(dir)])
+
+        def test_suite():
+           return unittest.makeSuite(MyTest, 'my test')
+
+        def main():
+           unittest.TextTestRunner().run(test_suite())
+
+        if __name__=="__main__":
+            fix_path()
+            main()
+
+      This example shows a fairly complete Zope test fixture. If your
+      Zope tests only needs to import Zope modules and packages you can
+      skip getting a Zope application object and closing the database
+      transaction.
+
+      Some times you may run into trouble if your test assuming that
+      there is a current Zope request. There are two ways to deal with
+      this. One is to use the 'makerequest' utility module to create a
+      fake request. For example::
+
+        class MyTest(unittest.TestCase):
+            ...
+
+            def setup(self):
+                import Zope
+                from Testing import makerequest
+                self.app=makerequest.makerequest(Zope.app())
+
+        % Anonymous User - Nov. 9, 2002 3:03 pm:
+         /if your test assuming/if your test assumes/
+
+        % to_be - May 8, 2003 7:13 am:
+         this isn't very understandable for someone who tries to add tests to his product the first time. How is this
+         'app' used then?
+         Is it possible to just create a "REQUEST" object which can then be passed as an argument?
+
+      This will create a Zope application object that is wrapped in a
+      request. This will enable code that expects to acquire a 'REQUEST'
+      attribute work correctly.
+
+      Another solution to testing methods that expect a request is to
+      use the 'ZPublisher.Zope' function described earlier. Using this
+      approach you can simulate HTTP requests in your unit tests. For
+      example::
+
+        import ZPublisher
+
+        class MyTest(unittest.TestCase):
+            ...
+
+            def testWebRequest(self):
+                ZPublisher.Zope('/a/url/representing/a/method?with=a&couple=arguments',
+                                u='username:password', 
+                                s=1, 
+                                e={'some':'environment', 'variable':'settings'})
+
+      If the 's' argument is passed to 'ZPublisher.Zope' then no output
+      will be sent to 'sys.stdout'. If you want to capture the output of
+      the publishing request and compare it to an expected value you'll
+      need to do something like this::
+
+        f=StringIO()
+        temp=sys.stdout
+        sys.stdout=f
+        ZPublisher.Zope('/myobject/mymethod')
+        sys.stdout=temp
+        assert f.getvalue() == expected_output
+
+      Here's a final note on unit testing with a Zope test fixture: you
+      may find Zope helpful. ZEO allows you to test an application while
+      it continues to serve other users. It also speeds Zope start up
+      time which can be a big relief if you start and stop Zope
+      frequently while testing.
+
+      Despite all the attention we've paid to Zope testing fixtures, you
+      should probably concentrate on unit tests that don't require a
+      Zope test fixture. If you can't test much without Zope there is a
+      good chance that your product would benefit from some refactoring
+      to make it simpler and less dependent on the Zope framework.
+
+  Logging
+
+    Zope provides a framework for logging information to Zope's
+    application log. You can configure Zope to write the application log
+    to a file, syslog, or other back-end.
+
+      % Anonymous User - Nov. 16, 2002 4:19 am:
+       Where does logging output go by default? Is that platform-dependent? What's the default logging level?
+
+    The logging API defined in the 'zLOG' module.  This module provides
+    the 'LOG' function which takes the following required arguments:
+
+      subsystem -- The subsystem generating the message (e.g. "ZODB")
+
+      severity -- The "severity" of the event.  This may be an integer
+                  or a floating point number.  Logging back ends may
+                  consider the int() of this value to be significant.
+                  For example, a back-end may consider any severity whose
+                  integer value is WARNING to be a warning.
+
+      summary -- A short summary of the event
+
+    These arguments to the 'LOG' function 
+    are optional:
+
+      detail -- A detailed description
+
+      error -- A three-element tuple consisting of an error type, value, and
+               traceback.  If provided, then a summary of the error is
+               added to the detail.
+
+      reraise -- If provided with a true value, then the error given by
+                 error is reraised.
+
+    You can use the 'LOG' function to send warning and errors to the
+    Zope application log.
+
+    Here's an example of how to use the 'LOG' function to write
+    debugging messages::
+
+      from zLOG import LOG, DEBUG
+      LOG('my app', DEBUG, 'a debugging message')
+
+      % charlesz - Dec. 11, 2002 7:53 am:
+       I get 'import of "LOG" from "zLOG" is unauthorized'. No doubt I'll work out why - but someone else might
+       not.....
+
+    You can use 'LOG' in much the same way as you would use print
+    statements to log debugging information while Zope is running. You
+    should remember that Zope can be configured to ignore log messages
+    below certain levels of severity. If you are not seeing your logging
+    messages, make sure that Zope is configured to write them to the
+    application log.
+
+    In general the debugger is a much more powerful way to locate
+    problems than using the logger. However, for simple debugging tasks
+    and for issuing warnings the logger works just fine.
+
+  Other Testing and Debugging Facilities
+
+    There is a few other testing and debugging techniques and tools
+    not commonly used to test Zope. In this section we'll mention
+    several of them.
+
+      % Anonymous User - Oct. 16, 2001 6:49 pm - The Medusa monitor client is also a good debugging tool. It permits querying the ZODB from an interactive commandline interface, and would be familiar to many Python users. The only downside is that because persisted objects are based on ExtensionClass, it is not possible to query an object for its functions and attributes in the style of "dir(thisobject)".    -- Chui Tey
+
+      % Anonymous User - Nov. 1, 2001 8:39 am - Actually dir(object) works fine.  It just breaks the *first* time you try to use it on a persistent object, where it returns an empty list.  The second and subsequent times, it will show you the methods and attributes.  Of course it won't show you acquired methods and attributes, but that's what aq_parent and such are for. -- chrism
+
+      % Anonymous User - Mar. 6, 2003 5:06 pm:
+       Perhaps it's something I'm doing, but the downside of Medusa monitor debugging on my setup is that all errors
+       fail silently, rather than raising a traceback. So if you try to look at attribute "a" of object "o", and it
+       doesn't exist, you get no response, rather than a TB/exception. Is this just me?
+
+    Debug Logging
+
+      Zope provides an analysis tool for debugging log output.  This
+      output allows may give you hints as to where your application
+      may be performing poorly, or not responding at all.  For
+      example, since writing Zope products lets your write
+      unrestricted Python code, it's very possibly to get yourself in
+      a situation where you "hang" a Zope request, possibly by getting
+      into a infinite loop.
+
+      To try and detect at which point your application hangs, use the
+      *requestprofiler.py* script in the *utilities* directory of your
+      Zope installation.  To use this script, you must run Zope with
+      the '-M' command line option.  This will turn on "detailed debug
+      logging" that is necessary for the *requestprofiler.py* script
+      to run.  The *requestprofiler.py* script has quite a few options
+      which you can learn about with the '--help' switch.
+
+        % chrism - Dec. 10, 2001 11:10 am - Note that the requestprofiler.py script is really just an analysis script for the data collected by in the the detailed debug log.  The description of the relationship above is a little muddled.
+
+      In general debug log analysis should be a last resort. Use it
+      when Zope is hanging and normal debugging and profiling is not
+      helping you solve your problem.
+
+    HTTP Benchmarking
+
+      HTTP load testing is notoriously inaccurate. However, it is
+      useful to have a sense of how many requests your server can
+      support. Zope does not come with any HTTP load testing tools,
+      but there are many available. Apache's 'ab' program is a widely
+      used free tool that can load your server with HTTP requests.
+
+        % chrism - Dec. 10, 2001 11:12 am - HTTP load testing itself is not notoriously inaccurate.  It's probably better to say that it's difficult to do "right".
+
+  Summary
+
+    Zope provides a number of different debugging and testing
+    facilities. The debugger allows you to interactively test your
+    applications. Unit tests allow help you make sure that your
+    application is develops correctly. The logger allows you to do
+    simple debugging and issue warnings.
+
+      % Anonymous User - May 28, 2002 1:57 am:
+       Typo: "Unit tests allow help you ..." should be "Unit tests help you to ..."
+
+      % Anonymous User - Dec. 28, 2003 4:57 am:
+       This doc shows how to check security issues.
+       http://www.zopelabs.com/cookbook/1046449715
+
+    To help maintain your sanity you should keeping your Zope products
+    as simple as possible, use interfaces to describe functionality,
+    and test your components outside as well as inside Zope.
+
+      % Anonymous User - Apr. 8, 2002 9:41 pm:
+       I would nice to have some detail about how to debug security problems. Trying to work out why something is
+       not authorized seems to be the hardest thing I do.
+
+      % Anonymous User - July 2, 2002 11:00 am:
+       I agree with this comment. I am struggling with several permissions problems
+       right now and cannot figure out where to even start looking for clues as 
+       to what might be happening. The callstack traces just don't yield enough
+       information.
+
+      % Anonymous User - Aug. 22, 2005 8:02 am:
+       Every time I read this stupid documentation I end up more annoyed and frustrated, and with my questions still
+       unanswered.

Added: zdgbook/trunk/ZODBPersistentComponents.stx
===================================================================
--- zdgbook/trunk/ZODBPersistentComponents.stx	                        (rev 0)
+++ zdgbook/trunk/ZODBPersistentComponents.stx	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,842 @@
+Chapter 4: ZODB Persistent Components
+
+  Most Zope components live in the Zope Object DataBase (ZODB).
+  Components that are stored in ZODB are said to be *persistent*.
+  Creating persistent components is, for the most part, a trivial
+  exercise, but ZODB does impose a few rules that persistent
+  components must obey in order to work properly.  This chapter
+  describes the persistence model and the interfaces that persistent
+  objects can use to live inside the ZODB.
+
+    % Anonymous User - July 9, 2002 8:38 am:
+     In a SAMS book, "ZOPE Web Application Construction Kit" by Martina Brockmann etc, page 41, it says "... ZODB
+     is not suitable for storing large amounts of data."
+     Is it true? If it is, we are in trouble because Zope stores everything in it. This means that Zope is not
+     suitable for large site. Can somebody please clarify this? Thanks.
+
+    % Anonymous User - July 9, 2002 8:58 am:
+     Sure. This statement is actually completely false. ZODB is very capable of storing large amounts of data. But
+     like any sort of database you need to be careful to put the right kinds of data structures in the right
+     places for maximum efficiency. Also, you may want to consider a different storage than FileStorage if you
+     have lots of data because FileStorage keeps all data in a single file, which can be problematic on systems
+     which do not support large files. Additionally, ZODB recovery tools are currently not as robust as, say,
+     Oracle recovery tools, so if your database does get corrupted due to hardware failure it may not be a
+     recoverable error and you may need to restore from backup.
+     That said, there is nothing stopping you from using Oracle or the filesystem to store all of your data just
+     like other application servers. Zope lets you do this. You don't need to use ZODB. The equation you use above
+     is inaccurate.
+
+    % Anonymous User - July 9, 2002 9:17 am:
+     Thanks for your quick reply.
+     How can I find more info about:
+     (1) any guideline regarding "to put the right kinds of data structures in the right places for maximum
+     efficiency;"
+     (2) how Zope works with other application servers if I store static html pages and graphics files on those
+     servers.
+     Thanks again.
+
+    % Anonymous User - July 9, 2002 9:57 am:
+     Ask a detailed question including what kinds of data you want to store and which other servers you want to
+     integrate with on the Zope maillist (zope at zope.org). See the Resources link of Zope.org for more maillist
+     info.
+
+    % Anonymous User - Jan. 16, 2003 1:40 am:
+     More importantly, how do you configure zope to use an Oracle database instead of ZODB? I can find information
+     on DCOracleStorage stuffs, but nothing about configuring Zope to startup and point to an Oracle database.
+
+    % axxackall - Apr. 5, 2003 11:09 am:
+     I know several projects where developers had to re-write half of the system just because it was based on
+     object persistence and POET (or other) ODBMS. It is a fact that enterprise users do not like ODBMS with
+     consequently closed architecture. They want RDBMS with ability to integrate the web portal with other
+     corporate infrmation sources. How to do that with ZODB? No way. I've tried to find any information about any
+     attempts to get rid from ZODB on the backend of Zope and found that virtually nobody is working currently on
+     it. If anyone is interesting in it, particularly in substituting ZODB by PostgreSQL, please send me email:
+     axxackall at yahoo.com as such project is too much for one busy developer.
+
+    % Anonymous User - Dec. 22, 2003 4:17 pm:
+     "get rid from ZODB" is not going to happen. Zope is a ZODB application.
+     What you should probably look at is APE, which (properly configured) will transparently
+     store zodb data in the RDBMS of your choice. It is not yet stable but some people are already using it.
+
+    % Anonymous User - June 27, 2005 7:53 pm:
+     test
+
+  Persistent Objects
+
+    Persistent objects are Python objects that live for a long
+    time. Most objects are created when a program is run and die when
+    the program finishes. Persistent objects are not destroyed when
+    the program ends, they are saved in a database.
+
+      % Anonymous User - June 27, 2005 7:52 pm:
+       test
+
+    A great benefit of persistent objects is their transparency.  As a
+    developer, you do not need to think about loading and unloading
+    the state of the object from memory.  Zope's persistent machinery
+    handles all of that for you.  
+
+      % Anonymous User - Feb. 21, 2002 7:47 pm - A note on __module_aliases__. It almost never works. Everytime I've wanted to do this, it has no effect. References to classes are to pervasive given the standard Zopish ways of doing things.
+
+      % mcdonc - Mar. 2, 2002 4:58 pm - Sorry to hear you're having difficulties with it, but it does work.  I've used it many times.
+
+    This is also a great benefit for application designers; you do not
+    need to create your own kind of "data format" that gets saved to a
+    file and reloaded again when your program stops and starts.
+    Zope's persistence machinery works with *any* kind of Python
+    objects (within the bounds of a few simple rules) and as your
+    types of objects grow, your database simply grows transparently
+    with it.
+
+      % Anonymous User - Dec. 9, 2001 3:47 pm - The topic came up on the maillist of how a persistent object "finds" its class.  A persistent object has a reference to its class' fully-qualified name including at least one module name.  For example, an instance of the DTMLMethod class finds its class at 'Products.OFSP.DTMLMethod.DTMLMethod', where 'Products' and 'OFSP' are Python packages, the first 'DTMLMethod' refers to a module file, and the last 'DTMLMethod' refers to the classname.    If you change the location of a class within the filesystem (for example, if you moved the above Python class definition to 'Foo.Bar.DTMLMethod', instances of the original class that were stored in the ZODB will not be loadable.    There is a facility named __module_aliases__ in Zope for aliasing a module to more than one name, although there is no such facility for use of ZODB outside Zope.  *Need more docs on __module_aliases__* - chrism.
+
+      % Anonymous User - Dec. 9, 2001 3:53 pm - Example of __module_aliases__ follows.  It works by putting a tuple of tuples in your Product's __init__.py module in a __module_aliases__ attribute at module scope.  For example, PythonScripts have the following __module_aliases__ attribute.      from Shared.DC import Scripts  __module_aliases__ = (        ('Products.PythonScripts.Script', Scripts.Script),        ('Products.PythonScripts.Bindings', Scripts.Bindings),        ('Products.PythonScripts.BindingsUI', Scripts.BindingsUI),)    .. this maps the module that *used* to be at Products.PythonScripts.Script  to the module that is *now* at Scripts.Script, etc.  This only works with  modules and not with classes or other types.
+
+      % Anonymous User - Jan. 17, 2002 10:12 am - change/your types of objects grow/your number of objects grow/
+
+  Persistence Example
+
+    Here is a simple example of using ZODB outside of Zope.  If all
+    you plan on doing is using persistent objects with Zope, you can
+    skip this section if you wish.
+
+      % Anonymous User - Oct. 21, 2002 10:16 am:
+       well, it is really funny that the only example about persistence is not valid inside Zope. After reading this
+       chapter, I still cannot use persistence. I think this chapter should explain how to use persistance inside
+       Zope
+
+      % Anonymous User - Nov. 8, 2002 10:27 pm:
+       Maybe the ZDG should be split into a) Product developement inside ZODB and
+       b) Advanced Developers Guide?
+
+    The first thing you need to do to start working with ZODB is to
+    create a "root object".  This process involves first opening a
+    "storage" , which is the actual backend storage location for your
+    data.
+
+    ZODB supports many pluggable storage back-ends, but for the
+    purposes of this article we're going to show you how to use the
+    'FileStorage' back-end storage, which stores your object data in a
+    file.  Other storages include storing objects in relational
+    databases, Berkeley databases, and a client to server storage that
+    stores objects on a remote storage server.
+
+      % Anonymous User - Nov. 8, 2002 10:29 pm:
+       Berkely db: URL?
+
+      % Anonymous User - Nov. 16, 2002 3:40 am:
+       A little more information or pointers to info on the different pluggable storage back-ends would go down a
+       treat
+
+    To set up a ZODB, you must first install it.  ZODB comes with
+    Zope, so the easiest way to install ZODB is to install Zope and
+    use the ZODB that comes with your Zope installation.  For those of
+    you who don't want all of Zope, but just ZODB, see the
+    instructions for downloading ZODB from the "ZODB web
+    page":http://www.zope.org/Wikis/ZODB/FrontPage.
+
+    After installing ZODB, you can start to experiment with it right
+    from the Python command line interpreter.  If you've installed
+    Zope, before running this set of commands, shut down your Zope server,
+    and "cd" to the "lib/python" directory of your Zope instance.
+    If you're using a "standalone" version of ZODB, you likely don't need
+    to do this, and you'll be able to use ZODB by importing it from a
+    standard Python package directory.  In either case, try the
+    following set of commands::
+
+      chrism at saints:/opt/zope/lib/python$ python
+      Python 2.1.1 (#1, Aug  8 2001, 21:17:50) 
+      [GCC 2.95.2 20000220 (Debian GNU/Linux)] on linux2
+      Type "copyright", "credits" or "license" for more information. 
+      >>> from ZODB import FileStorage, DB
+      >>> storage = FileStorage.FileStorage('mydatabase.fs')
+      >>> db = DB( storage )
+      >>> connection = db.open()
+      >>> root = connection.root()
+
+    Here, you create storage and use the 'mydatabse.fs' file to store
+    the object information.  Then, you create a database that uses
+    that storage.
+
+      % Anonymous User - July 18, 2002 1:36 pm:
+       s/mydatabse\.fs/mydatabase\.fs/
+
+    Next, the database needs to be "opened" by calling the 'open()'
+    method.  This will return a connection object to the database.
+    The connection object then gives you access to the 'root' of the
+    database with the 'root()' method.
+
+    The 'root' object is the dictionary that holds all of your
+    persistent objects.  For example, you can store a simple list of
+    strings in the root object::
+
+      root['employees'] = ['Bob', 'Mary', 'Jo']
+
+    Now, you have changed the persistent database by adding a new
+    object, but this change is so far only temporary.  In order to
+    make the change permanent, you must commit the current
+    transaction::
+
+      get_transaction().commit()
+
+    Transactions are ways to make a lot of changes in one atomic
+    operation.  In a later article, we'll show you how this is a very
+    powerful feature.  For now, you can think of committing
+    transactions as "checkpoints" where you save the changes you've
+    made to your objects so far.  Later on, we'll show you how to
+    abort those changes, and how to undo them after they are
+    committed.
+
+    If you had used a relational database, you would have had to issue
+    a SQL query to save even a simple python list like the above
+    example.  You would have also needed some code to convert a SQL
+    query back into the list when you wanted to use it again.  You
+    don't have to do any of this work when using ZODB.  Using ZODB is
+    almost completely transparent, in fact, ZODB based programs often
+    look suspiciously simple!
+
+    Working with simple python types is useful, but the real power of
+    ZODB comes out when you store your own kinds of objects in the
+    database.  For example, consider a class that represents a
+    employee::
+
+      from Persistence import Persistent
+
+      class Employee(Persistent):
+
+          def setName(self, name):
+              self.name = name
+
+      % Anonymous User - Oct. 16, 2002 7:43 am:
+       I get the message
+       Error Value: exceptions.ImportError on import of "Persistent" from "Persistence" is unauthorized in '', at
+       line 15, column 11
+       I am the zope administrator and I am running it in a Win98, therefore shouldn't be problems with the
+       authorization.
+       I am running it in a Python script that I call from a dtml page. Is there any problem with this? somebody can
+       tell me why I get this problem?
+
+    Calling 'setName' will set a name for the employee.  Now, you can
+    put Employee objects in your database::
+
+      for name in ['Bob', 'Mary', 'Joe']:
+          employee = Employee()
+          employee.setName(name)
+          root['employees'].append(employee)
+
+      get_transaction().commit()
+
+    Don't forget to call 'commit()', so that the changes you have made
+    so far are committed to the database, and a new transaction is
+    begun.
+
+      % Anonymous User - Apr. 10, 2003 5:03 pm:
+       This example seems to break the fourth rule as mentioned below. You are adding to the root's employees entry,
+       a list, which is a mutable thing.
+       At least, this is true when running the Python interpreter with a standalone ZODB.
+
+      % Anonymous User - Nov. 22, 2003 11:44 pm:
+       No, it is saying that you must notify the database to persist each change after it has been made, because the
+       default Python list type does not know about persistence. If you use the types provided with ZODB, then
+       changes will be persisted automatically. The type is smart enough to update the database when the in-memory
+       copy changes.
+
+      % Anonymous User - Mar. 15, 2004 9:33 am:
+       well said
+
+      % Anonymous User - Oct. 12, 2004 5:31 pm:
+       well...
+
+  Persistent Rules
+
+    There are a few rules that must be followed when your objects are
+    persistent.
+
+      o Your objects, and their attributes, must be "pickleable".
+
+      o Your object cannot have any attributes that begin with '_p_'.
+
+      o Attributes of your object that begin with '_v_' are "volatile"
+        and are not saved to the database (see next section).
+
+      o You must explicitly signal any changes made to mutable
+        attributes (such as instances, lists, and dictionaries) or use
+        persistent versions of mutable objects, like
+        'ZODB.PersistentMapping' (see below for more information on
+        'PersistentMapping'.)
+
+    In this section, we'll look at each of these special rules one by
+    one.  
+
+    The first rules says that your objects must be pickleable.  This
+    means that they can be serialized into a data format with the
+    "pickle" module.  Most python data types (numbers, lists,
+    dictionaries) can be pickled.  Code objects (method, functions,
+    classes) and file objects (files, sockets) *cannot* be pickled.
+    Instances can be persistent objects if:
+
+      o They subclass 'Persistence.Persistent'
+
+      o All of their attributes are pickleable
+
+      % Anonymous User - Nov. 8, 2002 10:57 pm:
+       The result of a ZSQL Method can not be pickled (thanks to a class r():pass)
+
+      % Anonymous User - Apr. 10, 2003 5:03 pm:
+       s/rules/rule/
+
+    The second rule is that none of your objects attributes can begin
+    with '_p_'.  For example, '_p_b_and_j' would be an illegal object
+    attribute.  This is because the persistence machinery reserves all
+    of these names for its own purposes.
+
+    The third rule is that all object attributes that begin with
+    '_v_' are "volatile" and are not saved to the database.  This
+    means that as long as the persistent object is in Zope memory
+    cache, volatile attributes can be used.  When the object is
+    deactivated (removed from memory) volatile attributes are thrown
+    away.
+
+    Volatile attributes are useful for data that is good to cache for
+    a while but can often be thrown away and easily recreated.  File
+    connections, cached calculations, rendered templates, all of these
+    kinds of things are useful applications of volatile
+    attributes. You must exercise care when using volatile attributes.
+    Since you have little control over when your objects are moved in
+    and out of memory, you never know when your volatile attributes
+    may disappear.
+
+      % poster - May 13, 2002 9:43 am:
+       I assume that you can count on a volatile attribute remaining within the life of a method call that creates
+       it. What about within a transaction? In general, while I understand there will be a point at which you can no
+       longer rely on the existence of a volatile attribute, when *can* you rely on it?
+
+      % reiman - Aug. 16, 2002 12:13 pm:
+       I also just learned that _v_ attributes are thread-specific. This too should be mentioned (and explained)
+       here.
+
+      % beyond - Oct. 5, 2002 10:44 am:
+       Within one transaction you can rely on _v_ attributes (afaik). 
+       Each thread gets its own transaction. So another thread -> another _v_ attribute 
+       -> you can't rely on it in different transactions. Tansactions are kept in a pool
+       and objects are cached so sometimes when accessing an object with a new 
+       request (which gets the old transaction out of the pool) the object is still cached
+       and it won't get loaded out of ZODB and then __setstate__ won't get called and 
+       finally the _v_ attribute remains. But this is of course not relyable. I'm just 
+       telling you this special case because recently I got some strange errors which were
+       caused by this behavior.
+
+    The fourth rule is that you must signal changes to mutable types.
+    This is because persistent objects can't detect when mutable types
+    change, and therefore, doesn't know whether or not to save the
+    persistent object or not.
+
+    For example, say you had a list of names as an attribute of your object
+    called 'departments' that you changed in a method called
+    'addDepartment'::
+
+      class DepartmentManager(Persistent):
+
+          def __init__(self):
+              self.departments = []
+
+          def addDepartment(self, department):
+              self.departments.append(department)
+
+    When you call the 'addDepartment' method you change a mutable
+    type, 'departments' but your persistent object will not save that
+    change.
+
+    There are two solutions to this problem.  First, you can assign a
+    special flag, '_p_changed'::
+
+        def addDepartment(self, department):
+            self.department.append(department)
+            self._p_changed = 1
+
+      % Anonymous User - Mar. 24, 2003 4:23 pm:
+       It says there are "two solutions", and then, "First...". What is the second solution?
+
+      % Anonymous User - Nov. 8, 2002 11:05 pm:
+       well, these are simple types. i had a (recursive) attribute 
+       self.tree = [..., {'sub': <a subtree> ...} ...] 
+       and sometimes (unreproducable) it didnt update in lieu of self._p_changed=1.
+       Explain.
+
+    Remember, '_p_' attributes do something special to the persistence
+    machinery and are reserved names. Assigning 1 to '_p_changed'
+    tells the persistence machinery that you changed the object, and
+    that it should be saved.
+
+    Another technique is to use the mutable attribute as though it
+    were immutable. In other words, after you make changes to a
+    mutable object, reassign it::
+
+        def addDepartment(self, department):
+            departments = self.departments
+            departments.append(department)
+            self.department = departments
+
+      % Anonymous User - Aug. 3, 2002 3:59 pm:
+       Reassigning self.departments stores the entire list again. ZODB can know nothing about your intent. If you
+       have an often-changing list like this, it'd likely be better to store it as a BTree (see
+       lib/python/BTrees/Interfaces.py in the source).
+
+      % Anonymous User - Aug. 3, 2002 3:45 pm:
+       which technique is more efficient? When you tag a mutable object (i.e. list) with _p_changed = 1; what does
+       ZODB do to change the list? Does it commit the entire list all over again or does it just commit the new list
+       element? Using a mutable as though it were immutable screams inefficiency to me... what if the list is HUGE
+       (i.e. 1024 instances of a persistant class)? For the assignment: departments = self.departments, does ZODB
+       pull the entire list out of storage or does it just do a shallow copy? I assume shallow... please advise.
+
+      % bernddorn - Dec. 1, 2001 7:46 am - there are some mistakes in the example code:  "self.department = departments"  should be "self.departments = departments" further above, the same mistake appears.
+
+    Here, the 'self.departments' attribute was re-assigned at the end
+    of the function to the "working copy" object 'departments'.  This
+    technique is cleaner because it doesn't have any explicit
+    '_p_changed' settings in it, but this implicit triggering of the
+    persistence machinery should always be understood, otherwise use
+    the explicit syntax.
+
+    A final option is to use persistence-aware mutable attributes such
+    as 'PersistentMapping', and 'IOBTree'. 'PersistentMapping' is a
+    mapping class that notifies ZODB when you change the mapping. You
+    can use instances of 'PersistentMapping' in place of standard
+    Python dictionaries and not worry about signaling change by
+    reassigning the attribute or using '_p_changed'. Zope's Btree
+    classes are also persistent-aware mutable containers. This
+    solution can be cleaner than using mutable objects immutably, or
+    signaling change manually assuming that there is a
+    persistence-aware class available that meets your needs.
+
+  Transactions and Persistent Objects
+
+    When changes are saved to ZODB, they are saved in a *transaction*.
+    This means that either all changes are saved, or none are saved.
+    The reason for this is data consistency.  Imagine the following
+    scenario:
+
+      1. A user makes a credit card purchase at the sandwich.com website.
+
+      2. The bank debits their account.
+
+      3. An electronic payment is made to sandwich.com.
+
+    Now imagine that an error happens during the last step of this
+    process, sending the payment to sandwich.com.  Without
+    transactions, this means that the account was debited, but the
+    payment never went to sandwich.com!  Obviously this is a bad
+    situation.  A better solution is to make all changes in a
+    transaction:    
+
+      1. A user makes a credit card purchase at the sandwich.com website.
+
+      2. The transaction begins
+
+      3. The bank debits their account.
+
+      4. An electronic payment is made to sandwich.com.
+
+      5. The transaction commits
+
+    Now, if an error is raised anywhere between steps 2 and 5, *all*
+    changes made are thrown away, so if the payment fails to go to
+    sandwich.com, the account won't be debited, and if debiting the
+    account raises an error, the payment won't be made to
+    sandwich.com, so your data is always consistent.
+
+    When using your persistent objects with Zope, Zope will automatically
+    *begin* a transaction when a web request is made, and *commit* the
+    transaction when the request is finished.  If an error occurs at any
+    time during that request, then the transaction is *aborted*, meaning
+    all the changes made are thrown away.
+
+    If you want to *intentionally* abort a transaction in the middle
+    of a request, then just raise an error at any time.  For example,
+    this snippet of Python will raise an error and cause the transaction
+    to abort::
+
+      raise SandwichError('Not enough peanut butter.')
+
+    A more likely scenario is that your code will raise an exception
+    when a problem arises. The great thing about transactions is that
+    you don't have to include cleanup code to catch exceptions and
+    undo everything you've done up to that point. Since the
+    transaction is aborted the changes made in the transaction will
+    not be saved.
+
+    Because Zope does transaction management for you, most of the time you
+    do not need to explicitly begin, commit or abort your own
+    transactions.  For more information on doing transaction management
+    manually, see the links at the end of this chapter that lead to more
+    detailed tutorials of doing your own ZODB programming.
+
+      % Anonymous User - Jan. 4, 2002 9:14 am - The text should probably mention that you have to let the exception propagate "right out of Zope" for the "rollback" to occur in Zope (of course). Otherwise, it seems to be the case that if the exception is to be handled within a Zope Product (so that a user of the application doesn't see the standard error page), then an explicit transaction abort should be performed in the exception handler in question.
+
+      % peterb - Aug. 16, 2002 3:48 am:
+       It should be even more specific and mention that if the exception is caught, whether in a product, in DTML or
+       in a script there is no automatic rollback.
+       You need to call get_transaction.abort() in your exception handler, unless you rethrow the exception and you
+       know it won't get caught.
+       This is actually guesswork due to lacking docs, I just happen to have the problem right now.
+
+  Subtransactions
+
+    Zope waits until the transaction is committed to save all the
+    changes to your objects.  This means that the changes are saved in
+    memory.  If you try to change more objects than you have memory in
+    your computer, your computer will begin to swap and thrash, and
+    maybe even run you out of memory completely.  This is bad. The
+    easiest solution to this problem is to not change huge quantities
+    of data in one transaction.
+
+    If you need to spread a transaction out of lots of data, however,
+    you can use subtransactions.  Subtransactions allow you to manage
+    Zope's memory usage yourself, so as to avoid swapping during large
+    transactions. 
+
+      % Anonymous User - Nov. 10, 2001 7:37 am - That first sentence doesn't make sense. How about "However, if you need commit a transaction containing a lot of data you can use subtransactions." -- ChrisW
+
+    Subtransactions allow you to make huge transactions. Rather than
+    being limited by available memory, you are limited by available
+    disk space.  Each subtransaction commit writes the current changes
+    out to disk and frees memory to make room for more changes.
+
+    To commit a subtransaction, you first need to get a hold of a
+    transaction object.  Zope adds a function to get the transaction
+    objects in your global namespace, 'get_transaction', and then call
+    'commit(1)' on the transaction::
+
+      get_transaction().commit(1)
+
+    You must balance speed, memory, and temporary storage concerns
+    when deciding how frequently to commit subtransactions. The more
+    subtransactions, the less memory used, the slower the operation,
+    and the more temporary space used. Here's and example of how you
+    might use subtransactions in your Zope code::
+
+      tasks_per_subtransaction = 10
+      i = 0
+      for task in tasks:
+          process(task)
+          i = i + 1
+          if i % tasks_per_subtransaction == 0:
+              get_transaction().commit(1)
+
+      % Anonymous User - Jan. 17, 2002 11:22 am - no "," before "and"
+
+    This example shows how to commit a subtransaction at regular
+    intervals while processing a number of tasks.
+
+  Threads and Conflict Errors
+
+    Zope is a multi-threaded server.  This means that many different
+    clients may be executing your Python code in different threads.
+    For most cases, this is not an issue and you don't need to worry
+    about it, but there are a few cases you should look out for.
+
+    The first case involves threads making lots of changes to objects
+    and writing to the database.  The way ZODB and threading works is
+    that each thread that uses the database gets its own *connection*
+    to the database.  Each connection gets its own *copy* of your
+    object.  All of the threads can read and change any of the
+    objects.  ZODB keeps all of these objects synchronized between the
+    threads. The upshot is that you don't have to do any locking or
+    thread synchronization yourself. Your code can act as through it
+    is single threaded.
+
+      % Anonymous User - Aug. 7, 2002 8:44 am:
+       "through".replace("r", "")
+
+    However, synchronization problems can occur when objects are
+    changed by two different threads at the same time.
+
+    Imagine that thread 1 gets its own copy of object A, as does thread
+    2.  If thread 1 changes its copy of A, then thread 2 will not see
+    those changes until thread 1 commits them.  In cases where lots of
+    objects are changing, this can cause thread 1 and 2 to try and
+    commit changes to object 1 at the same time.
+
+    When this happens, ZODB lets one transaction do the commit (it
+    "wins") and raises a 'ConflictError' in the other thread (which
+    "looses"). The looser can elect to try again, but this may raise
+    yet another 'ConflictError' if many threads are trying to change
+    object A. Zope does all of its own transaction management and
+    will retry a losing transaction three times before giving up
+    and raising the 'ConflictError' all the way up to the user.
+
+      % mcdonc - Oct. 16, 2001 9:37 am - This is "loses" and "loser".  No pun intended.
+
+  Resolving Conflicts
+
+    If a conflict happens, you have two choices. The first choice is
+    that you live with the error and you try again. Statistically,
+    conflicts are going to happen, but only in situations where objects
+    are "hot-spots".  Most problems like this can be "designed away";
+    if you can redesign your application so that the changes get
+    spread around to many different objects then you can usually get
+    rid of the hot spot.
+
+      % Anonymous User - Sep. 12, 2002 7:09 pm:
+       This is totally retarded.  Why can't we get a mutex instead?
+
+      % Anonymous User - Sep. 12, 2002 7:18 pm:
+       Code talks.  Write code or shut the hell up.
+
+      % Anonymous User - Sep. 13, 2002 11:20 am:
+       Although the comment above was not very nice, this might be a good place
+       to explain why a mutex is not appropriate.
+       A mutex would have to span multiple ZEO servers and would
+       serialize all Zope application threads.  Conflicts are one of the prices
+       of scalability, but in practice conflicts are rare enough that
+       few applications need to deal with them directly.
+
+    Your second choice is to try and *resolve* the conflict. In many
+    situations, this can be done. For example, consider the following
+    persistent object::
+
+      class Counter(Persistent):
+
+          self.count = 0
+
+          def hit(self):
+              self.count = self.count + 1
+
+    This is a simple counter.  If you hit this counter with a lot of
+    requests though, it will cause conflict errors as different threads
+    try to change the count attribute simultaneously.
+
+    But resolving the conflict between conflicting threads in this
+    case is easy.  Both threads want to increment the self.count
+    attribute by a value, so the resolution is to increment the
+    attribute by the sum of the two values and make both commits
+    happy; no 'ConflictError' is raised.
+
+      % Anonymous User - Nov. 16, 2002 3:52 am:
+       I'm with the 'code talks' aphorism - so how about a recoded example just here?
+
+    To resolve a conflict, a class should define an
+    '_p_resolveConflict' method. This method takes three arguments.
+
+       'oldState' -- The state of the object that the changes made by
+       the current transaction were based on. The method is permitted
+       to modify this value.
+
+       'savedState' -- The state of the object that is currently
+       stored in the database. This state was written after 'oldState'
+       and reflects changes made by a transaction that committed
+       before the current transaction. The method is permitted to
+       modify this value.
+
+       'newState' -- The state after changes made by the current
+       transaction.  The method is *not* permitted to modify this
+       value. This method should compute a new state by merging
+       changes reflected in 'savedState' and 'newState', relative to
+       'oldState'.
+
+      % Anonymous User - Nov. 16, 2002 3:55 am:
+       Does it return anything useful - I'm assuming it does (the new state, perhaps) as newstate (below) is
+       described as not valid for changing. But what's the actuality?
+
+      % Jace - Nov. 15, 2002 3:20 pm:
+       s/define an/define a/
+
+    The method should return the state of the object after resolving
+    the differences.  
+
+    Here is an example of a '_p_resolveConflict' in the 'Counter'
+    class::
+
+      class Counter(Persistent):
+
+          self.count = 0
+
+          def hit(self):
+              self.count = self.count + 1
+
+          def _p_resolveConflict(self, oldState, savedState, newState):
+
+              # Figure out how each state is different:
+              savedDiff= savedState['count'] - oldState['count']
+              newDiff= newState['count']- oldState['count']
+
+              # Apply both sets of changes to old state:
+              oldState['count'] = oldState['count'] + savedDiff + newDiff
+
+              return oldState
+
+    In the above example, '_p_resolveConflict' resolves the difference
+    between the two conflicting transactions.
+
+      % mcdonc - Oct. 16, 2001 9:40 am - Note that a flag on your object can be set in the form of a method named "_p_independent", which if it returns true, will prevent "read conflicts" (not talked about in this chapter, but possible) from affecting your object.  Read conflicts occur in the connection's setstate method.
+
+      % Anonymous User - Nov. 1, 2001 7:40 am - Someone asked about read conflicts and I needed to admit that I had no clue under what circumstance they were actually raised, but Toby Dickenson came to the rescue.  The rest of the comment comes from his description of read conflicts.    Zope loads objects from their persistent store into memory when they  are first accessed. So if you have a transaction that touches several  objects, you will be loading those objects gradually throughout the  transaction.     (unless they are already in memory anyway; lets assume they are not)     Consider one of the last objects to be loaded..... the question is:  what happens if that object has been modified in a different thread  since the start of this current transaction?    Ideally, Zope would load the original state of the object. That is,  the state that was current at the time when this transaction started.  However, this capability has not yet been developed. When it does read  con
 flicts will not happen.    Another option would be to load the new state of that object. This is  what the first revision of ZODB did, and it is bad because it leads to  inconsistencies. Our transaction sees some, but not all of the changes  made in a different transaction. There are some objects for which this  isnt a problem; I think the LowConflictConnection class that Chris  referred to can allow you to exploit this.
+
+      % wilm - Nov. 15, 2001 6:59 pm - What happens if *more than two* threads try to write concurrently? Will _p_resolveConflict be called multiple times?
+
+      % Anonymous User - Nov. 27, 2001 8:44 pm - (chrism, too lazy to log in) -- yes... the conflict resolution machinery will be called as many times as necessary.
+
+      % Anonymous User - Dec. 29, 2001 6:39 pm - Is it neccessary to calculate the difference like this? Couldn't you just as easily used::    return oldState['count'] = newState['count'] + 1    to get the same result?
+
+      % lettuce - Dec. 29, 2001 6:45 pm - Err, my bad. I meant oldState['count'] = savedState['count'] + 1; return oldState
+
+      % chrism - Jan. 15, 2002 8:19 pm - Nope, what you're trying to do is to count *both* "saved" and "new" states changes against "old".  If you just count the "saved" state against oldstate, you will have  lost the increment caused by "new".  It bugs me that this example refers to "saved" and "new" states because there really is no semantic difference to them.  It should really refer to just "state1" and "state2", that might make this conceptually easier.
+
+  Threadsafety of Non-Persistent Objects
+
+    ZODB takes care of threadsafety for persistent objects. However,
+    you must handle threadsafey yourself for non-persistent objects
+    which are shared between threads.
+
+      % Anonymous User - Feb. 18, 2005 5:47 pm:
+       s/safey/safety/
+
+    Mutable Default Arguments
+
+      One tricky type of non-persistent, shared objects are mutable
+      default arguments to functions, and methods.  Default arguments
+      are useful because they are cached for speed, and do not need to
+      be recreated every time the method is called.  But if these cached
+      default arguments are mutable, one thread may change (mutate) the
+      object when another thread is using it, and that can be bad.  So,
+      code like::
+
+        def foo(bar=[]):
+            bar.append('something')
+
+        % Anonymous User - Sep. 6, 2002 9:49 am:
+         need more explanation, please: what if foo is a method of class FooClass(Persistence.Persistent)? Does the
+         persistence machinery guarantee threadsafety for argument 'bar' or we are in trouble all the same?
+         Put another way, arguments of methods in persistent classes are persistent or not?
+
+        % beyond - Oct. 5, 2002 10:55 am:
+         (slightly off-topic)
+         def foo(bar=[]) looks dangerous to me.
+         When you use the default argument bar it will always be the same mutable 
+         sequence. So calling 
+         def myFoo(bar=[]):
+            bar.append('bla')
+            print bar
+         two times will result in ['bla'] and ['bla','bla']
+
+      Could get in trouble if two threads execute this code because lists are
+      mutable.  There are two solutions to this problem:
+
+        o Don't use mutable default arguments. (Good)
+
+        o If you use them, you cannot change them.  If you want to change
+          them, you will need to implement your own locking. (Bad)
+
+      We recommend the first solution because mutable default arguments
+      are confusing, generally a bad idea in the first place.
+
+    Shared Module Data
+
+      Objects stored in modules but not in the ZODB are not persistent
+      and not-thread safe. In general it's not a good idea to store
+      data (as opposed to functions, and class definitions) in modules
+      when using ZODB.
+
+        % reiman - Aug. 16, 2002 12:15 pm:
+         We should mention that module data is the easiest way to achive server-lifetime data store. This is where you
+         would normally store external references (file handles or database connections or session data) that you
+         cannot easily reconstruct.
+
+      If you decide to use module data which can change you'll need to
+      protect it with a lock to ensure that only one thread at a time
+      can make changes.
+
+        % zigg - Jan. 16, 2002 9:31 am - See also http://www.zopelabs.com/cookbook/1006189713, which demos how to use ThreadLock.  Apparently this is the "official" way?
+
+        % mcdonc - Jan. 17, 2002 8:27 am - chrism - ThreadLock provides recursive locks so that the thread that holds the mutex can re-lock the lock (maybe by recursing or looping unintentionally or even intentionally) without fear of deadlock.  threading.Lock does not allow this to happen.
+
+      For example::
+
+        from threading import Lock
+        queue=[]
+        l=Lock()
+
+        def put(obj):
+            l.acquire()
+            try:
+                queue.append(obj)
+            finally:
+                l.release()
+
+        def get():
+            l.acquire()
+            try:
+                return queue.pop()
+            finally:
+                l.release()
+
+      Note, in most cases where you are tempted to use shared module
+      data, you can likely achieve the same result with a single
+      persistent object. For example, the above queue could be
+      replaced with a single instance of this class::
+
+        class Queue(Persistent):
+
+            def __init__(self):
+                self.list=[]
+
+            def put(self, obj):
+                self.list=self.list + [obj]
+
+            def get(self):
+                obj=self.list[-1]
+                self.list=self.list[0:-1]
+                return obj
+
+       Notice how this class uses the mutable object 'self.list'
+       immutably. If this class used 'self.list.pop' and
+       'self.list.append', then the persistence machinary would not
+       notice that 'self.list' had changed.
+
+    Shared External Resources
+
+      A final category of data for which you'll need to handle
+      thread-safety is external resources such as files in the
+      filesystem, and other processes. In practice, these concerns
+      rarely come up.
+
+  Other ZODB Resources
+
+    This chapter has only covered the most important features of ZODB
+    from a Zope developer's perspective. Check out some of these
+    sources for more in depth information:
+
+      * Andrew Kuchling's "ZODB pages":http://www.kuchling.com/zodb/
+        include lots of information included a programmer's guide and
+        links to ZODB mailing lists.
+
+      * "ZODB Wiki":http://www.zope.org/Wikis/ZODB/FrontPage has
+        information about current ZODB projects.
+
+      * "ZODB UML
+        Model":http://www.zope.org/Documentation/Developer/Models/ZODB
+        has the nitty gritty details on ZODB.
+
+      * Paper "Introduction to the Zope Object
+        Database":http://www.python.org/workshops/2000-01/proceedings/papers/fulton/zodb3.html
+        by Jim Fulton, presented at the 8th Python Conference.
+
+  Summary
+
+    The ZODB is a complex and powerful system. However using
+    persistent objects is almost completely painless. Seldom do you
+    need to concern yourself with thread safety, transactions,
+    conflicts, memory management, and database replication. ZODB takes
+    care of these things for you. By following a few simple rules you
+    can create persistent objects that just work.
+
+      % Anonymous User - Aug. 21, 2002 12:42 pm:
+       Andrew Kuchling's ZODB pages link is broken!!
+
+      % Anonymous User - Aug. 21, 2002 12:53 pm:
+       use http://www.amk.ca/zodb/ instead
+
+      % Anonymous User - Oct. 17, 2003 6:47 am:
+       The link above is broken as well!
+       You can find Kuchling's guide at
+       http://www.zope.org/Wikis/ZODB/guide/zodb.html

Added: zdgbook/trunk/bootstrap.py
===================================================================
--- zdgbook/trunk/bootstrap.py	                        (rev 0)
+++ zdgbook/trunk/bootstrap.py	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,77 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id: bootstrap.py 90478 2008-08-27 22:44:46Z georgyberdyshev $
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+is_jython = sys.platform.startswith('java')
+
+try:
+    import pkg_resources
+except ImportError:
+    ez = {}
+    exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                         ).read() in ez
+    ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+    import pkg_resources
+
+if sys.platform == 'win32':
+    def quote(c):
+        if ' ' in c:
+            return '"%s"' % c # work around spawn lamosity on windows
+        else:
+            return c
+else:
+    def quote (c):
+        return c
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+ws  = pkg_resources.working_set
+
+if is_jython:
+    import subprocess
+    
+    assert subprocess.Popen([sys.executable] + ['-c', quote(cmd), '-mqNxd', 
+           quote(tmpeggs), 'zc.buildout'], 
+           env=dict(os.environ,
+               PYTHONPATH=
+               ws.find(pkg_resources.Requirement.parse('setuptools')).location
+               ),
+           ).wait() == 0
+
+else:
+    assert os.spawnle(
+        os.P_WAIT, sys.executable, quote (sys.executable),
+        '-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout',
+        dict(os.environ,
+            PYTHONPATH=
+            ws.find(pkg_resources.Requirement.parse('setuptools')).location
+            ),
+        ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)

Added: zdgbook/trunk/buildout.cfg
===================================================================
--- zdgbook/trunk/buildout.cfg	                        (rev 0)
+++ zdgbook/trunk/buildout.cfg	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,28 @@
+[buildout]
+develop =
+    src/stxutils
+parts =
+    stxpy
+
+eggs-directory = ${buildout:directory}/eggs
+versions = versions
+unzip = true
+eggs =
+    zope.structuredtext
+
+[versions]
+zc.buildout =
+zc.recipe.egg =
+zope.structuredtext = 
+
+[stxpy]
+recipe = zc.recipe.egg
+eggs =
+    stxutils
+    Sphinx
+interpreter = stxpy
+scripts =
+    stx2html
+    stx2rst
+    sphinx-build
+    sphinx-quickstart

Added: zdgbook/trunk/examples/Poll-1.0.tgz
===================================================================
(Binary files differ)


Property changes on: zdgbook/trunk/examples/Poll-1.0.tgz
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: zdgbook/trunk/examples/Poll.py
===================================================================
--- zdgbook/trunk/examples/Poll.py	                        (rev 0)
+++ zdgbook/trunk/examples/Poll.py	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,20 @@
+from Interface import Base
+
+class Poll(Base):
+    "A multiple choice poll"
+
+    def castVote(self, index):
+        "Votes for a choice"
+
+    def getTotalVotes(self):
+        "Returns total number of votes cast"
+
+    def getVotesFor(self, index):
+        "Returns number of votes cast for a given response"
+
+    def getResponses(self):
+        "Returns the sequence of responses"
+
+    def getQuestion(self):
+        "Returns the question"
+

Added: zdgbook/trunk/examples/PollImplementation.py
===================================================================
--- zdgbook/trunk/examples/PollImplementation.py	                        (rev 0)
+++ zdgbook/trunk/examples/PollImplementation.py	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,42 @@
+from Poll import Poll
+
+class PollImplementation:
+    """
+    A multiple choice poll, implements the Poll interface.
+
+    The poll has a question and a sequence of responses. Votes
+    are stored in a dictionary which maps response indexes to a
+    number of votes.
+    """
+
+    __implements__=Poll
+
+    def __init__(self, question, responses):
+        self._question = question
+        self._responses = responses
+        self._votes = {}
+        for i in range(len(responses)):
+            self._votes[i] = 0
+
+    def castVote(self, index):
+        "Votes for a choice"
+        self._votes[index] = self._votes[index] + 1
+
+    def getTotalVotes(self):
+        "Returns total number of votes cast"
+        total = 0
+        for v in self._votes.values():
+            total = total + v
+        return total
+
+    def getVotesFor(self, index):
+        "Returns number of votes cast for a given response"
+        return self._votes[index]
+
+    def getResponses(self):
+        "Returns the sequence of responses"
+        return tuple(self._responses)
+
+    def getQuestion(self):
+        "Returns the question"
+        return self._question

Added: zdgbook/trunk/examples/PollProduct.py
===================================================================
--- zdgbook/trunk/examples/PollProduct.py	                        (rev 0)
+++ zdgbook/trunk/examples/PollProduct.py	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,62 @@
+from Poll import Poll
+from AccessControl import ClassSecurityInfo
+from Globals import InitializeClass
+from Acquisition import Implicit
+from Globals import Persistent
+from AccessControl.Role import RoleManager
+from OFS.SimpleItem import Item
+
+class PollProduct(Implicit, Persistent, RoleManager, Item):
+    """
+    Poll product class, implements Poll interface.
+
+    The poll has a question and a sequence of responses. Votes
+    are stored in a dictionary which maps response indexes to a
+    number of votes.
+    """
+
+    __implements__=Poll
+
+    meta_type='Poll'
+
+    security=ClassSecurityInfo()
+
+    def __init__(self, id, question, responses):
+        self.id=id
+        self._question = question
+        self._responses = responses
+        self._votes = {}
+        for i in range(len(responses)):
+            self._votes[i] = 0
+
+    security.declareProtected('Use Poll', 'castVote')
+    def castVote(self, index):
+        "Votes for a choice"
+        self._votes[index] = self._votes[index] + 1
+        self._votes = self._votes
+
+    security.declareProtected('View Poll Results', 'getTotalVotes') 
+    def getTotalVotes(self):
+        "Returns total number of votes cast"
+        total = 0
+        for v in self._votes.values():
+            total = total + v
+        return total
+
+    security.declareProtected('View Poll Results', 'getVotesFor') 
+    def getVotesFor(self, index):
+        "Returns number of votes cast for a given response"
+        return self._votes[index]
+
+    security.declarePublic('getResponses') 
+    def getResponses(self):
+        "Returns the sequence of responses"
+        return tuple(self._responses)
+
+    security.declarePublic('getQuestion') 
+    def getQuestion(self):
+        "Returns the question"
+        return self._question
+
+
+InitializeClass(PollProduct)

Added: zdgbook/trunk/source/conf.py
===================================================================
--- zdgbook/trunk/source/conf.py	                        (rev 0)
+++ zdgbook/trunk/source/conf.py	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,190 @@
+# -*- coding: utf-8 -*-
+#
+# Zope Developers' Guide documentation build configuration file, created by
+# sphinx-quickstart on Mon Feb 16 22:23:29 2009.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# The contents of this file are pickled, so don't put values in the namespace
+# that aren't pickleable (module imports are okay, they're removed automatically).
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If your extensions are in another directory, add it here. If the directory
+# is relative to the documentation root, use os.path.abspath to make it
+# absolute, like shown here.
+#sys.path.append(os.path.abspath('.'))
+
+# General configuration
+# ---------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = []
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['.templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'Zope Developers' Guide'
+copyright = u'2009, Zope Community'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.1'
+# The full version, including alpha/beta/rc tags.
+release = '0.1'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directory, that shouldn't be searched
+# for source files.
+exclude_trees = []
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+
+# Options for HTML output
+# -----------------------
+
+# The style sheet to use for HTML and HTML Help pages. A file of that name
+# must exist either in Sphinx' static/ path, or in one of the custom paths
+# given in html_static_path.
+html_style = 'default.css'
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['.static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_use_modindex = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, the reST sources are included in the HTML build as _sources/<name>.
+#html_copy_source = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'ZopeDevelopersGuidedoc'
+
+
+# Options for LaTeX output
+# ------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, document class [howto/manual]).
+latex_documents = [
+  ('index', 'ZopeDevelopersGuide.tex', ur'Zope Developers' Guide Documentation',
+   ur'Zope Community', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True

Added: zdgbook/trunk/source/index.rst
===================================================================
--- zdgbook/trunk/source/index.rst	                        (rev 0)
+++ zdgbook/trunk/source/index.rst	2009-02-17 04:44:21 UTC (rev 96617)
@@ -0,0 +1,19 @@
+.. Zope Developers' Guide documentation master file, created by sphinx-quickstart on Mon Feb 16 22:23:29 2009.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+Welcome to Zope Developers' Guide's documentation!
+==================================================
+
+Contents:
+
+.. toctree::
+   :maxdepth: 2
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+



More information about the Checkins mailing list