[Checkins] SVN: zope2book/trunk/ Two more chapters restified

Hanno Schlichting plone at hannosch.info
Mon Feb 16 15:22:59 EST 2009


Log message for revision 96604:
  Two more chapters restified
  

Changed:
  D   zope2book/trunk/BasicScripting.stx
  D   zope2book/trunk/Sessions.stx
  A   zope2book/trunk/source/BasicScripting.rst
  A   zope2book/trunk/source/Sessions.rst

-=-
Deleted: zope2book/trunk/BasicScripting.stx
===================================================================
--- zope2book/trunk/BasicScripting.stx	2009-02-16 19:56:41 UTC (rev 96603)
+++ zope2book/trunk/BasicScripting.stx	2009-02-16 20:22:58 UTC (rev 96604)
@@ -1,707 +0,0 @@
-Basic Zope Scripting
-
-  So far, you've learned about some basic Zope objects and how to manage them
-  through the *Zope Management Interface*.  This chapter shows you how to
-  manage Zope objects programmatically.
-
-  Calling Methods From the Web
-
-    Since Zope is a web application server, the easiest way to communicate with
-    Zope is through a web browser.  Any URL your browser requests from the
-    server is mapped to both an object and a method.  The method is executed 
-    *on* the object, and a response is sent to your browser.
-
-    As you might already know, visiting the URL 'http://localhost:8080/'
-    returns the *Zope Quick Start* page.  In this case, we only specify an
-    object -- the *root* object -- but no method.  This just works because there
-    is a default method defined for *Folders*: *index_html*.  Visiting the URL
-    'http://localhost:8080/index_html' returns (almost) exactly the same page.
-    'http://localhost:8080/manage_main' also specifies the root object, but in
-    this case the *manage_main* method is called, and the workspace frame 
-    displays the root content of your Zope site, without the navigator frame.
-
-    The same method can be called *on* other objects: when you visit the URL
-    'http://localhost:8080/Control_Panel/manage_main', the *manage_main* method
-    is called *on* the *Control Panel* object.
-
-    Sometimes a query string is added to the URL, e.g.,
-    'http://localhost:8080/manage_main?skey=meta_type'.  The query string is
-    used for passing arguments to the method.  In this case, the argument 'skey'
-    specifies the sort key with the value *meta_type*.  Based on this argument, 
-    the *manage_main* method returns a modified version of the basic page: the 
-    sub-objects are sorted by *Type*, not by *Name* as they are without that 
-    query string.
-
-    While the *manage_main* method is defined in the class of the object,
-    *index_html* is (by default) a DTML Method object in the root folder that
-    can be modified through the web.  *index_html* itself is a presentation
-    object, but when called *on* a folder, it behaves as a method that returns 
-    the default view of the folder.
-
-    Method Objects and Context Objects
-
-      When you call a method, you usually want to single out some object that
-      is central to the method's task, either because that object provides
-      information that the method needs, or because the method will modify
-      that object.  In "object-oriented":ObjectOrientation.stx terms, we want
-      to call the method *on* this particular object.  But in conventional
-      object-oriented programming, each object can perform the methods that
-      are defined in (or inherited by) its class.  How is it that one Zope
-      object can be used as a method for (potentially) many other objects,
-      without its being defined by the classes that define these objects?
-
-      Recall that in the chapter entitled "Acquisition":Acquisition.stx, we
-      learned that Zope can find objects in different places by *acquiring*
-      them from parent containers.  Acquisition allows us to treat an object
-      as a method that can be called *in the context of* any suitable object,
-      just by constructing an appropriate URL.  The object *on* which we call
-      a method gives it a context in which to execute.  Or, to put it another
-      way: the context is the environment in which the method executes, from
-      which the method may get information that it needs in order to do its job.
-
-      Another way to understand the context of a method is to think of the
-      method as a function in a procedural programming language, and its
-      context as an implicit argument to that function.
-
-      While the Zope way to call methods *in the context of* objects **works**
-      differently than the normal object-oriented way to call class-defined
-      methods *on* objects, they are **used** the same way, and it is simpler
-      to say that you are calling the method *on* the object.
-
-      There are two general ways to call methods *on* objects: by visiting an
-      URL, and by calling the method from another method.
-
-    URL Traversal and Acquisition
-
-      The concept of calling methods *in the context of* objects is a powerful
-      feature that enables you to apply logic to objects, like documents or
-      folders, without having to embed any actual code within the object.
-
-      For example, suppose you have a collection of objects and methods, as
-      shown in the figure below.
-
-      "A collection of objects and methods":img:9-1:Figures/zoo.png
-
-      To call the *feed* method on the *hippo* object, you would visit the URL
-      'Zoo/LargeAnimals/hippo/feed'.  To call the *feed* method on the
-      *kangarooMouse* object you would visit the URL
-      'Zoo/SmallAnimals/kangarooMouse/feed'.  These URLs place the *feed*
-      method in the context of the *hippo* and *kangarooMouse* objects,
-      respectively.
-
-      Zope breaks apart the URL and compares it to the object hierarchy,
-      working backwards until it finds a match for each part.  This process is
-      called *URL traversal*.  For example, when you give Zope the URL
-      'Zoo/LargeAnimals/hippo/feed', it starts at the root folder and looks
-      for an object named *Zoo*.  It then moves to the *Zoo* folder and looks
-      for an object named *LargeAnimals*.  It moves to the *LargeAnimals*
-      folder and looks for an object named *hippo*.  It moves to the *hippo*
-      object and looks for an object named *feed*.  The *feed* method cannot
-      be found in the *hippo* object and is located in the *Zoo* folder by
-      using acquisition.  Zope always starts looking for an object in the last
-      object it traversed, in this case: *hippo*.  Since *hippo* does not
-      contain anything, Zope backs up to *hippo*'s immediate container,
-      *LargeAnimals*.  The *feed* method is not there, so Zope backs up to
-      *LargeAnimals*' container, *Zoo*, where *feed* is finally found.
-
-      Now Zope has reached the end of the URL and has matched objects to every
-      name in the URL.  Zope recognizes that the last object found, *feed*, is
-      callable, and calls it *in the context of* the second-to-last object
-      found: the *hippo* object.  This is how the *feed* method is called
-      *on* the *hippo* object.
-
-      Likewise, you can call the *wash* method on the *hippo* by visiting the
-      URL 'Zoo/LargeAnimals/hippo/wash'.  In this case, Zope acquires the
-      *wash* method from the *LargeAnimals* folder.
-
-      Note that *Script (Python)* and *Page Template* objects are always
-      method objects.  You can't call another method *in the context* of one
-      of them.  Given *wash* is such a method object, visiting the URL
-      'Zoo/LargeAnimals/hippo/wash/feed' would also call the *wash* method on
-      the *hippo* object.  Instead of traversing to *feed*, everything after
-      the method 'wash' is cut off of the URL and stored in the variable
-      'traverse_subpath'.
-
-    The Special Folder Object *index_html*
-
-      As already mentioned at the beginning of this chapter, Zope uses the
-      default method if no other method is specified.  The default method for
-      Folders is *index_html*, which does not necessarily need to be a method
-      itself. If it isn't a callable, the default method of the object
-      *index_html* is called on *index_html*.  This is analogous to how an
-      *index.html* file provides a default view for a directory in Apache and
-      other web servers.  Instead of explicitly including the name
-      *index_html* in your URL to show default content for a Folder, you can
-      omit it and still gain the same effect.
-
-      For example, if you create an *index_html* object in your *Zoo* Folder,
-      and view the folder by clicking the View tab or by visiting the URL
-      'http://localhost:8080/Zoo/', Zope will call the *index_html* object in
-      the *Zoo* folder and display its results.  You could instead use the more
-      explicit URL 'http://localhost:8080/Zoo/index_html', and it will display
-      the same content.
-
-      A Folder can also *acquire* an *index_html* object from its parent
-      Folders.  You can use this behavior to create a default view for a set
-      of Folders.  To do so, create an *index_html* object in a Folder that
-      contains another set of Folders.  This default view will be used for all
-      the Folders in the set.  This behavior is already evident in Zope: if
-      you create a set of empty Folders in the Zope root Folder, you may
-      notice that when you view any of the Folders via a URL, the content of
-      the "root" Folder's *index_html* method is displayed.  The *index_html*
-      in the root Folder is acquired.  Furthermore, if you create more empty
-      Folders inside the Folders you've just created in the root Folder, a
-      visit to these Folders' URLs will also display the root Folder's
-      *index_html*.  This is acquisition at work.
-
-      If you want a different default view of a given Folder, just create a
-      custom *index_html* object in that particular Folder.  This allows you
-      to override the default view of a particular Folder on a case-by-case
-      basis, while allowing other Folders defined at the same level to acquire
-      a common default view.
-
-      The *index_html* object may be a *Page Template*, a *Script (Python)*
-      object, a *DTML Method* or any other Zope object that is URL-accessible
-      and that returns browser-renderable content.  The content is typically
-      HTML, but Zope doesn't care.  You can return XML, or text, or anything
-      you like.
-
-  Using Python-based Scripts
-
-    Now let us take a look at a basic method object: *Script (Python)*.
-
-    The Python Language
-
-      "Python":http://www.python.org/ is a high-level, object oriented
-      scripting language.  Most of Zope is written in Python.  Many folks like
-      Python because of its clarity, simplicity, and ability to scale to large
-      projects.
-
-      There are many resources available for learning Python.  The python.org
-      website has lots of Python documentation including a
-      "tutorial":http://www.python.org/doc/current/tut/tut.html by Python's
-      creator, Guido van Rossum.
-
-      For people who have already some programming experience, "Dive Into
-      Python":http://diveintopython.org/ is a great online resource to learn
-      python.
-
-      Python comes with a rich set of modules and packages.  You can find out
-      more about the "Python standard
-      library":http://www.python.org/doc/current/lib/lib.html at the
-      python.org website.
-
-      Another highly respected source for reference material is *Python
-      Essential Reference* by David Beazley, published by New Riders.
-
-    Creating Python-based Scripts
-
-      To create a Python-based Script, select *Script (Python)* from the
-      Add drop-down list.  Name the script *hello*, and click the *Add and
-      Edit* button.  You should now see the *Edit* view of your script.
-
-      This screen allows you to control the parameters and body of your
-      script.  You can enter your script's parameters in the *parameter list*
-      field.  Type the body of your script in the text area at the bottom of
-      the screen.
-
-      Enter 'name="World"' into the *parameter list* field, and in the body 
-      of the script, type::
-
-        return "Hello %s." % name
-
-      Our script is now equivalent to the following function definition in 
-      standard Python syntax::
-
-        def hello(name="World"):
-            return "Hello %s." % name
-
-      The script should return a result similar to the following image:
-
-      "Script editing view":img:9-2:Figures/8-5.png
-
-      You can now test the script by going to the *Test* tab, as shown in the
-      following figure.
-
-      "Testing a Script":img:9-3:Figures/8-6.png
-
-      Leave the *name* field blank, and click the *Run Script* button.  Zope
-      should return "Hello World."   Now go back and try entering your name in
-      the *Value* field, and clicking the *Run Script* button.  Zope should now
-      say "hello" to you.
-
-      Since scripts are called on Zope objects, you can get access to Zope
-      objects via the *context* variable.  For example, this script returns
-      the number of objects contained by a given Zope object::
-
-        ## Script (Python) "numberOfObjects"
-        ##
-        return len( context.objectIds() )
-
-      Note that the lines at the top starting with a double hash ('##') are
-      generated by Zope when you view the script outside the *Edit* tab of the
-      ZMI, e.g., by clicking the *view or download* link at the bottom of the
-      *Edit* tab.  We'll use this format for our examples.
-
-      The script calls 'context.objectIds()', a method in the Zope API, to get
-      a list of the contained objects.  *objectIds* is a method of *Folders*,
-      so the context object should be a Folder-like object.  The script then
-      calls 'len()' to find the number of items in that list.  When you call
-      this script on a given Zope object, the *context* variable is bound to
-      the context object.  So, if you called this script by visiting the URL
-      'FolderA/FolderB/numberOfObjects', the 'context' parameter would refer
-      to the 'FolderB' object.
-
-      When writing your logic in Python, you'll typically want to query Zope
-      objects, call other scripts, and return reports.  Suppose
-      you want to implement a simple workflow system, in which various Zope
-      objects are tagged with properties that indicate their status.  You
-      might want to produce reports that summarize which objects are in which
-      state.  You can use Python to query objects and test their properties.
-      For example, here is a script named 'objectsForStatus' with one
-      parameter, 'status'::
-
-        ## Script (Python) "objectsForStatus"
-        ##parameters=status
-        ##
-        """ Returns all sub-objects that have a given status property.
-        """
-        results=[]
-        for object in context.objectValues():
-            if object.getProperty('status') == status:
-                results.append(object)
-        return results
-
-      This script loops through an object's sub-objects, and returns all the
-      sub-objects that have a 'status' property with a given value.
-
-    Accessing the HTTP Request
-
-      What if we need to get user input, e.g., values from a form?  We can find
-      the REQUEST object, which represents a Zope web request, in the context.
-      For example, if we visited our *feed* script via the URL
-      'Zoo/LargeAnimals/hippo/feed?food_type=spam', we could access the
-      'food_type' variable as 'context.REQUEST.food_type'.  This same technique
-      works with variables passed from forms.
-
-      Another way to get the REQUEST is to pass it as a parameter to the
-      script.  If REQUEST is one of the script's parameters, Zope will
-      automatically pass the HTTP request and assign it to this parameter. We
-      could then access the 'food_type' variable as 'REQUEST.food_type'.
-
-    String Processing in Python
-
-      One common use for scripts is to do string processing. Python has a
-      number of standard modules for string processing.  Due to security 
-      restrictions, you cannot do regular expression processing within 
-      Python-based Scripts.  If you really need regular expressions, you can 
-      easily use them in External Methods, described in a subsequent chapter. 
-      However, in a Script (Python) object, you do have access to string 
-      methods.
-
-      Suppose you want to change all the occurrences of a given word in a text
-      file. Here is a script, *replaceWord*, that accepts two arguments:
-      *word* and *replacement*.  This will change all the occurrences of a
-      given word in a File::
-
-        ## Script (Python) "replaceWord"
-        ##parameters=word, replacement
-        ##
-        """ Replaces all the occurrences of a word with a replacement word in
-        the source text of a text file. Call this script on a text file to use
-        it.
-
-        Note: you will need permission to edit the file in order to call this
-        script on the *File* object.  This script assumes that the context is
-        a *File* object, which provides 'data', 'title', 'content_type' and
-        the manage_edit() method.
-        """
-        text = context.data
-        text = text.replace(word, replacement)
-        context.manage_edit(context.title, context.content_type,
-                            filedata=text)
-
-      You can call this script from the web on a text *File* in order to change 
-      the text. For example, the URL
-      'Swamp/replaceWord?word=Alligator&replacement=Crocodile' would call the
-      *replaceWord* script on the text *File* named 'Swamp', and would replace
-      all occurrences of the word 'Alligator' with 'Crocodile'.
-
-      See the Python documentation for more information about manipulating
-      strings from Python.
-
-      One thing that you might be tempted to do with scripts is to use Python
-      to search for objects that contain a given word within their text or as a
-      property.  You can do this, but Zope has a much better facility for this
-      kind of work: the *Catalog*.  See the chapter entitled "Searching and
-      Categorizing Content":SearchingZCatalog.stx for more information on
-      searching with Catalogs.
-
-    Doing Math
-
-      Another common use of scripts is to perform mathematical calculations
-      that would be unwieldy with ZPT or DTML. The *math* and *random*
-      modules give you access from Python to many math functions.  These
-      modules are standard Python services as described on the Python.org web
-      site:
-
-        "math":http://www.python.org/doc/current/lib/module-math.html --
-        Mathematical functions such as *sin* and *cos*.
-
-        "random":http://www.python.org/doc/current/lib/module-random.html
-        -- Pseudo random number generation functions.
-
-      One interesting function of the *random* module is the *choice* function,
-      which returns a random selection from a sequence of objects. Here is an
-      example of how to use this function in a script called *randomImage*::
-
-        ## Script (Python) "randomImage"
-        ##
-        """ When called on a Folder that contains Image objects this script
-        returns a random image.
-        """
-        from random import choice
-        return choice( context.objectValues(['Image']) )
-
-      Suppose you had a Folder named 'Images' that contained a number of
-      images.  You could display a random image from the Folder in ZPT like
-      so::
-
-         <span tal:replace="structure context/Images/randomImage" />
-
-      This ZPT calls the 'randomImage' script on the 'Images' Folder. The
-      result is an HTML *IMG* tag that references a random image in the
-      'Images' Folder.
-
-    Print Statement Support
-
-      Python-based Scripts have a special facility to help you print
-      information.  Normally, printed data is sent to standard output and is
-      displayed on the console.  This is not practical for a server
-      application like Zope, since the service does not always have access to
-      the server's console.  Scripts allow you to use print anyway, and to
-      retrieve what you printed with the special variable *printed*.  For
-      example::
-
-        ## Script (Python) "printExample"
-        ##
-        for word in ('Zope', 'on', 'a', 'rope'):
-            print word
-        return printed
-
-      This script will return::
-
-        Zope
-        on
-        a
-        rope
-
-      The reason that there is a line break in between each word is that
-      Python adds a new line after every string that is printed.
-
-      You might want to use the 'print' statement to perform simple debugging in
-      your scripts.  For more complex output control, you probably should
-      manage things yourself by accumulating data, modifying it, and returning
-      it manually, rather than relying on the 'print' statement.  And for 
-      controlling presentation, you should return the script output to a Page 
-      Template or DTML page, which then displays the return value appropriately.
-
-    Built-in Functions
-
-      Python-based Scripts give you a slightly different menu of built-ins
-      than you'd find in normal Python.  Most of the changes are designed to
-      keep you from performing unsafe actions.  For example, the *open*
-      function is not available, which keeps you from being able to access the
-      file system.  To partially make up for some missing built-ins, a few extra
-      functions are available.
-
-      The following restricted built-ins work the same as standard Python
-      built-ins: *None*, *abs*, *apply*, *callable*, *chr*, *cmp*, *complex*,
-      *delattr*, *divmod*, *filter*, *float*, *getattr*, *hash*, *hex*, *int*,
-      *isinstance*, *issubclass*, *list*, *len*, *long*, *map*, *max*, *min*,
-      *oct*, *ord*, *repr*, *round*, *setattr*, *str*, and *tuple*.  For more
-      information on what these built-ins do, see the online "Python
-      Documentation":http://www.python.org/doc/.
-
-      The *range* and *pow* functions are available and work the same way they
-      do in standard Python; however, they are limited to keep them from
-      generating very large numbers and sequences.  This limitation helps
-      protect against denial-of-service attacks.
-
-      In addition, these DTML utility functions are available: *DateTime* and
-      *test*. See Appendix A, "DTML Reference":AppendixA.stx for more
-      information on these functions.
-
-      Finally, to make up for the lack of a *type* function, there is a
-      *same_type* function that compares the type of two or more objects,
-      returning *true* if they are of the same type.  So, instead of saying::
-
-        if type(foo) == type([]):
-            return "foo is a list"
-
-      ... to check if 'foo' is a list, you would instead use the *same_type*
-      function::
-
-        if same_type(foo, []):
-            return "foo is a list"
-
-  Calling ZPT from Scripts
-
-    Often, you would want to call a *Page Template* from a Script.  For
-    instance, a common pattern is to call a Script from an HTML form.  The
-    Script would process user input, and return an output page with feedback
-    messages - telling the user her request executed correctly, or signalling
-    an error as appropriate.
-
-    Scripts are good at logic and general computational tasks but ill-suited
-    for generating HTML.  Therefore, it makes sense to delegate the user
-    feedback output to a *Page Template* and call it from the Script.  Assume
-    we have this Page Template with the *id* 'hello_world_pt'::
-
-      <p>Hello <span tal:replace="options/name | default">World</span>!</p>
-
-    You will learn more about *Page Templates* in the next chapter.  For now,
-    just understand that this *Page Template* generates an HTML
-    page based on the value *name*.  Calling this template from a Script and
-    returning the result could be done with the following line::
-
-      return context.hello_world_pt(name="John Doe")
-
-    The *name* parameter to the Page Template ends up in the 'options/name'
-    path expression.  So the returned HTML will be::
-
-      <p>Hello John Doe!</p>
-
-    Note that 'context.hello_world_pt' works because there is no dot in
-    the id of the template.  In Python, dots are used to separate ids.  This is
-    the reason why Zope often uses ids like 'index_html' instead of the
-    more common 'index.html', and why this example uses 'hello_world_pt' instead 
-    of 'hello_world.pt'.  However, if desired, you can use dots within object 
-    ids. Using *getattr* to access the dotted id, the modified line would look 
-    like this::
-
-      return getattr(context, 'hello_world.pt')(name="John Doe")
-
-  Calling DTML from Scripts
-
-    For reasons similar to those outlined in the earlier section "Calling ZPT from Scripts", one might want to call *DTML Methods* or *DTML Documents* 
-    from Scripts.
-
-    Assume we have got a *DTML Method* 'a_dtml_method'. We would call it from
-    Script with::
-
-      # grab the method and the REQUEST from the context
-      dtml_method = context.a_dtml_method
-      REQUEST = context.REQUEST
-
-      # call the dtml method, for parameters see below
-      s = dtml_method(client=context, REQUEST=REQUEST, foo='bar')
-
-      # s now holds the rendered html
-      return s
-
-    Note that DTML Methods and Documents take optional *client* and *REQUEST*
-    parameters.  If a client is passed to a *DTML Method*, the method tries to
-    resolve names by looking them up as attributes of the client object.  By
-    passing our context as a client, the method will look up names in that
-    context.  Also, we can pass it a REQUEST object and additional keyword
-    arguments.  The *DTML Method* will first try to look up variables in the
-    keyword arguments, then within the namespace, and finally in the REQUEST 
-    object. See the chapter "Advanced DTML", subchapter "DTML Namespaces", for 
-    details on namespaces, and Appendix B, "API Reference":AppendixB.stx for 
-    further information on DTML Methods and Documents.
-
-  Returning Values from Scripts
-
-    Scripts have their own variable scope. In this respect, scripts in Zope
-    behave just like functions, procedures, or methods in most programming
-    languages.  If you name a script *updateInfo*, for example, and
-    *updateInfo* assigns a value to a variable *status*, then *status* is
-    local to your script: it gets cleared once the script returns.  To get
-    at the value of a script variable, we must pass it back to the caller with
-    a *return* statement.
-
-    Scripts can only return a single object. If you need to return more than
-    one value, put them in a dictionary and pass that back.
-
-    Suppose you have a Python script *compute_diets*, out of which you want to
-    get values::
-
-      ## Script (Python) "compute_diets"
-      d = {'fat': 10,
-           'protein': 20,
-           'carbohydrate': 40,
-          }
-      return d
-
-    The values would, of course, be calculated in a real application; in this
-    simple example, we've simply hard-coded some numbers.
-
-    You could call this script from ZPT like this::
-
-      <p tal:repeat="diet context/compute_diets">
-         This animal needs
-         <span tal:replace="diet/fat" />kg fat,
-         <span tal:replace="diet/protein" />kg protein, and
-         <span tal:replace="diet/carbohydrate" />kg carbohydrates.</p>
-
-    More on ZPT in the next chapter.
-
-  The Zope API
-
-    One of the main reasons to script in Zope is to get convenient access to the
-    Zope Application Programmer Interface (API).  The Zope API describes
-    built-in actions that can be called on Zope objects.  You can examine
-    the Zope API in the help system, as shown in the figure below.
-
-    "Zope API Documentation":img:9-4:Figures/8-4.png
-
-    Suppose you would like a script that takes a file you upload from
-    a form, and creates a Zope File object in a Folder.  To do this, you'd need 
-    to know a number of Zope API actions.  It's easy enough to read files in     
-    Python, but once you have the file, you must know which actions to call
-    in order to create a new File object in a Folder.
-
-    There are many other things that you might like to script using the Zope
-    API: any management task that you can perform through the web can be
-    scripted using the Zope API, including creating, modifying, and
-    deleting Zope objects.  You can even perform maintenance tasks, like
-    restarting Zope and packing the Zope database.
-
-    The Zope API is documented in Appendix B, "API Reference":AppendixB.stx, as
-    well as in the Zope online help.  The API documentation shows you which
-    classes inherit from which other classes.  For example, *Folder* inherits
-    from *ObjectManager*, which means that Folder objects have all the methods
-    listed in the *ObjectManager* section of the API reference.
-
-    To get you started and whet your appetite, we will go through some
-    example Python scripts that demonstrate how you can use the Zope API:
-
-    Get all objects in a Folder
-
-      The 'objectValues()' method returns a list of objects contained in a
-      Folder.  If the context happens not to be a Folder, nothing is
-      returned::
-
-        objs = context.objectValues()
-
-    Get the id of an object
-
-      The id is the "handle" to access an object, and is set at object
-      creation::
-
-        id = context.getId()
-
-      Note that there is no *setId()* method: you have to either use the ZMI
-      to rename them, set their 'id' attribute via security-unrestricted code,
-      or use the 'manage_renameObject' or 'manage_renameObjects' API methods
-      exposed upon the container of the object you want to rename.
-
-    Get the Zope root Folder
-
-      The root Folder is the top level element in the Zope object database::
-
-         root = context.getPhysicalRoot()
-
-    Get the physical path to an object
-
-      The 'getPhysicalPath()' method returns a list containing the ids of the
-      object's containment hierarchy::
-
-        path_list = context.getPhysicalPath()
-        path_string = "/".join(path_list)
-
-    Get an object by path
-
-      'restrictedTraverse()' is the complement to 'getPhysicalPath()'.  The path
-      can be absolute -- starting at the Zope root -- or relative to the
-      context::
-
-         path = "/Zoo/LargeAnimals/hippo"
-         hippo_obj = context.restrictedTraverse(path)
-
-    Change the content of an DTML Method or Document
-
-      You can actually change the content (and title) of a DTML Method or
-      Document, exactly as if you edited it in the Zope Management Interface,
-      by using its 'manage_edit()' method::
-
-        # context has to be a DTML method or document!
-        context.manage_edit('new content', 'new title')
-
-    Get a property
-
-      'getProperty()' returns a property of an object. Many objects support
-      properties (those that are derived from the PropertyManager class), the
-      most notable exception being DTML Methods, which do not::
-
-        pattern = context.getProperty('pattern')
-        return pattern
-
-    Change properties of an object
-
-      The object has to support properties, and the property must exist::
-
-        values = {'pattern' : 'spotted'}
-        context.manage_changeProperties(values)
-
-    Traverse to an object and add a new property
-
-      We get an object by its absolute path, add a property 'weight', and
-      set it to some value.  Again, the object must support properties in order 
-      for this to work::
-
-        path = "/Zoo/LargeAnimals/hippo"
-        hippo_obj = context.restrictedTraverse(path)
-        hippo_obj.manage_addProperty('weight', 500, 'int')
-
-    Add a new object to the context
-
-      Scripts can add objects to Folders, just like you do in the Zope
-      management interface.  The context has to be a Folder-ish object (i.e., a
-      Folder or another object derived from ObjectManager).  The general
-      pattern is::
-
-        context.manage_addProduct['PackageName'].manage_addProductName(id)
-
-      'manage_addProduct' is a mapping by which we can look up a *dispatcher*: 
-      an object that gives us the necessary *factory* for creating a new
-      object in the context.  For most of the built-in Zope classes, the
-      PackageName is 'OFSP', and the factory method is named after the product
-      class itself.  Once you have the factory method, you must pass to it
-      any arguments that are needed in order to add an object of this type. Some
-      examples will make this clear.
-
-      Let's add a DTML Method to a Folder::
-
-        add_method = context.manage_addProduct['OFSP'].manage_addDTMLMethod
-        add_method('object_id', file="Initial contents of the DTML Method")
-
-      For any other product class that comes with Zope, we need only
-      change the factory method and its arguments.
-
-      Folders --  *manage_addFolder*
-
-      UserFolders --  *manage_addUserFolder*
-
-      Images --  *manage_addImage*
-
-      Files --  *manage_addFile*
-
-      DTML Methods --  *manage_addDTMLMethod*
-
-      DTML Documents --  *manage_addDTMLDocument*
-
-      Version --  *manage_addVersion*
-
-      To get a dispatcher for add-on Products that you download and install,
-      replace 'OFSP' with the directory that contains the product code.  For
-      example, if you have installed the famous Boring product, you could add
-      one like so::
-
-        add_method = context.manage_addProduct['Boring'].manage_addBoring
-        add_method(id='test_boring')
-
-      If the product author has been conscientious, the process for adding new
-      instances of their product will be documented; but it will always look
-      something like the above examples.

Deleted: zope2book/trunk/Sessions.stx
===================================================================
--- zope2book/trunk/Sessions.stx	2009-02-16 19:56:41 UTC (rev 96603)
+++ zope2book/trunk/Sessions.stx	2009-02-16 20:22:58 UTC (rev 96604)
@@ -1,1511 +0,0 @@
-Session Management
-
-  This chapter describes Zope's built-in Session Management, available
-  with Zope versions 2.5.0 and higher.  This version of the chapter is
-  based on Zope 2.7.4+.
-
-  Terminology
-
-    Here's a mini-glossary of of key terms used within this document:
-
-      -  **Web session** -- a series of HTTP requests from the same browser 
-         to the same server during that browser's execution life-span.
-    
-      - **Browser Id** -- the string or integer used to represent a single
-        anonymous visitor to the part of the Zope site managed by a
-        single browser id manager.  E.g. "12083789728".
-
-      - **Browser Id Name** -- the name which is looked for in places
-        enumerated by the currently configured browser id namespaces.
-        E.g. "_ZopeId".
-
-      - **Browser Id Namespaces** -- the browser id name will be found in
-        one of three possible places ("namespaces"): in form elements
-        and/or query strings (aka "form"), in a cookie, or in the URL.
-
-      - **Session Data Object** -- an transient data object that is found by
-        asking a session data container for the item with a key that is
-        the current browser id value. 
-
-      - **Session Id** -- the identifier for a session data object.  This is
-        different than the browser id.  Instead of representing a single
-        *visitor*, it represents a single *visit*.
-  
-  Session Managers
-
-    Web browsers communicate with Web Servers using HTTP. HTTP does
-    not provide tools that can track users and data in the context of
-    a web session.  Zope's session management works-around the
-    problem: it provides methods able to track site visitor activity.
-    Applications like "shopping carts" use session management for this
-    reason.
-
-    Zope's session management makes use of name-spaces like cookies,
-    HTTP form elements, and/or parts of URLs "in the background" to
-    keep track of user sessions.  Which of these name-spaces are used
-    is configurable using the browser_id manager (described later).
-    
-    Session data is valid for the duration of a **configurable
-    inactivity** timeout value or browser shut-down, which ever comes
-    first.  Zope's session management keeps track of anonymous users
-    as well as those who have Zope login accounts.
-
-    Important! Data maintained by Zope's session management is no more
-    secure than HTTP itself. A session is secure if and only if:
-
-      - the connection between a browser and Zope uses strong
-        encryption (SSL normally).
-      
-      - precautions specific to the security exposure are taken.
-      
-    It's clear that you should not store sensitive information like
-    credit card numbers in a session container unless you understand
-    the vulnerabilities.  See the section entitled *Security
-    Considerations* near the end of this document.
-
-    It is advisable to use sessions only on pages where they are
-    necessary because of a performance impact on your application.
-    The severity varies depending on usage and configuration.  A good
-    "rule of thumb" is to account for a 5% - 10% speed-of-execution
-    penalty.
-
-    Some hints:
-    
-    - Do not use SESSION to store REQUEST variables. They are already
-      available in the REQUEST.
-
-    - Do not store any data in SESSION that you can get from the Zope
-      API.  Its faster (and more secure) to get user Id from Zope's
-      Security Manager then it is from the SESSION object.
-
-  Session Manager Components
-
-      - **Browser Id Manager**
-
-        This component determines a remote client's "browser id",
-        which uniquely identifies a particular browser.  The browser
-        id is encoded in a form/querystring variable, a cookie
-        variable, or as part of the URL.  The browser id manager
-        examines cookies, form and querystring elements, and URLs to
-        determine the client's browser id. It can also modify cookies
-        and URLs automatically in order to differentiate users between
-        requests.
-
-        There may be more than one browser id manager in a Zope
-        installation, but commonly there will only be one.
-        Application developers will generally not talk directly to a
-        browser id manager.  Instead, they will use the Transient Data
-        Object (REQUEST.SESSION) which delegates some calls to a
-        browser_id manager.
-        
-        Browser id managers have "fixed" Zope ids so they can be found
-        via acquisition by session data managers.  Browser id managers
-        also have interfaces for encoding a URL with browser id
-        information and performing other utility functions.
-
-        The default sessioning configuration provides a Browser Id
-        Manager as the '/browser_id_manager object'.
-
-      - **Session Data Manager**
-
-        This component is responsible for handing out session data to
-        callers.  When session data is required, the session data
-        manager:
-
-        1. talks to a browser id manager to determine the current
-           browser id
-
-        2. creates a new session data object or hands back an existing
-           session data object based on the browser id.
-
-
-        Developers generally do not directly use methods of session data 
-        managers to obtain session data objects.
-        Instead, they rely on the built-in REQUEST.SESSION
-        object, which represents *the current session data object
-        related to the user's browser id*.  
-
-        The session data object has an identifier distinct from the
-        browser id.  This identifier represents a single user session
-        with the server (unlike the browser id, which represents a
-        single browser).  Many session data managers can use one
-        browser id manager.  Many session data managers can be
-        instantiated on a single Zope installation.  Different session
-        data managers can implement different policies related to
-        session data object storage (e.g. to which session data
-        container the session data objects are stored).
-
-        The default sessioning configuration provides a Session Data
-        Manager named '/session_data_manager'.
-
-      - **Transient Object Container**
-
-        Also known as Session Data Containers, these components
-        actually hold information related to sessions.
-
-        Currently, a Transient Object Container is used to hold a
-        special "transient data object" instance for each ongoing
-        session.  Developers will generally not interact with
-        transient data containers.  Transient data containers are
-
-        responsible for expiring the session data objects which live
-        within them.
-
-        The default sessioning configuration provides a Transient
-        Object Container named '/temp_folder/session_data'.  The
-        session data objects in the default 'session_data' Transient
-        Object container are lost each time Zope is restarted.
-
-      - **Transient Data Object**
-      
-        Also known as the Session Data Object.  These are the objects
-        which are stored in session data containers and managed by
-        transient data managers.
-
-        Developers interact with a transient data object after
-        obtaining one via REQUEST.SESSION or from a session data
-        manager directly.  A single transient data object actually
-        stores the useful information related to a single user's
-        session.
-
-        Transient data objects can be expired automatically by
-        transient data containers as a result of inactivity, or they
-        can be manually invalidated in the course of a script.
-        
-  Using Session Data
-
-      You will typically access session data through the 'SESSION'
-      attribute of the REQUEST object. Session data objects are like
-      Python dictionaries: they can hold almost any kind of object as
-      a key or a value.  It's likely you will almost always use
-      "normal" Python objects such as lists, dictionaries, strings,
-      and numbers.
-
-      Here's an example of how to work with a session using a Python
-      Script.::
-
-        ## Script (Python) "sessionTest"
-        secs_per_day=24*60*60
-        session=context.REQUEST.SESSION
-        if session.has_key('last view'):
-            # The script has been viewed before, since the 'last view'
-            then=session['last view']
-            now=context.ZopeTime()
-            session['last view']=now # reset last view to now
-            return 'Seconds since last view %.2f' % ((now - then) * secs_per_day)
-            
-        # The script hasn't been viewed before, since there's no 'last view' 
-        session['last view']=context.ZopeTime()
-        return 'This is your first view'
-
-      This example shows how to access SESSION data.  But it is not a
-      "best practice" example.  If performance is an issue, you should
-      not attempt to keep last-accessed time in this manner in a
-      production application because it might slow your application
-      down dramatically and cause problems under high load.
-
-      Create a script with this body named *sessionTest* in your root
-      folder and then click its *Test* tab.  While viewing the output,
-      reload the frame a few times. Note that the script keeps track
-      of when you last viewed it and calculates how long it has been
-      since you last viewed it.  Notice that if you quit your browser
-      and come back to the script it forgets you were ever there.
-      However, if you simply visit some other pages and then return
-      within 20 minutes or so, it still remembers the last time you
-      viewed it.
-
-      See the *Concepts and Caveats* section at the end of this
-      document for things to watch out for while accessing Zope's
-      Session Manager "naively".
-      
-      You can use sessions in Page Templates and DTML Documents,
-      too. For example, here's a template snippet that displays the
-      users favorite color (as stored in a session)::
-
-        <p tal:content="request/SESSION/favorite_color">Blue</p>
-
-      Here's how to do the same thing in DTML::
-
-        <dtml-with SESSION mapping>
-          <p><dtml-var favorite_color></p>
-        </dtml-with>
-
-      Sessions have additional configuration parameters
-      and usage patterns detailed below.
-
-      For an additional example of using sessions, see the "shopping
-      cart" example that comes with Zope 2.5 and above (in the
-      Examples folder, which can be installed via the Zope QuickStart
-      page).
-
-  Default Configuration
-
-    Zope is preconfigured with a default sessioning setup as of Zope
-    versions 2.5 and higher.
-
-    The Zope "default" browser id manager lives in the root folder and
-    is named 'browser_id_manager'.
-
-    The Zope "default" session data manager lives in the root folder
-    and is named 'session_data_manager'.
-
-    A "default" transient data container (session data container) is
-    created as '/temp_folder/session_data' when Zope starts up.  The
-    'temp_folder' object is a "mounted, nonundoing" database that
-    keeps information in RAM, so "out of the box", Zope stores session
-    information in RAM.  The temp folder is a "nonundoing" storage
-    (meaning you cannot undo transactions which take place within it)
-    because accesses to transient data containers are very
-    write-intensive, and undoability adds unnecessary overhead.
-
-    A transient data container stores transient data objects.  The
-    default implementation the transient data object shipped with Zope
-    is engineered to reduce the potential inherent in the ZODB for
-    "conflict errors" related to the ZODB's "optimistic concurrency"
-    strategy.
-
-    You needn't change any of these default options to use sessioning
-    under Zope unless you want to customize your setup.  However, if
-    you have custom needs, can create your own session data managers,
-    browser id managers, temporary folders, and transient object
-    containers by choosing these items from Zope's "add" list in the
-    place of your choosing.
-
-  Advanced Development Using Sessioning
-
-    Overview
-
-      When you work with the REQUEST.SESSION object, you are working
-      with a "session data object" that is related to the current site
-      user.
-
-      Session data objects have methods of their own, including
-      methods with allow developers to get and set data.  Session data
-      objects are also "wrapped" in the acquisition context of their
-      session data manager, so you may additionally call any method on
-      a session data object that you can call on a session data
-      manager.  For information about the API of a session data
-      manager and a session data object, see the Zope Help system item
-      in "Zope Help" -> "API Reference" -> "Session API".
-
-    Obtaining A Session Data Object
-
-      The session data object associated with the browser id in the
-      current request may be obtained via REQUEST.SESSION.  If a
-      session data object does not exist in the session data
-      container, one will be created automatically when you reference
-      REQUEST.SESSION::
-
-        <dtml-let data="REQUEST.SESSION">
-          The 'data' name now refers to a new or existing session data object.
-        </dtml-let>
-
-      You may also use the 'getSessionData()' method of a session data
-      manager to do the same thing::
-
-        <dtml-let data="session_data_manager.getSessionData()">
-          The 'data' name now refers to a new or existing session data object.
-        </dtml-let>
-
-      A reference to REQUEST.SESSION or 'getSessionData()' implicitly
-      creates a new browser id if one doesn't exist in the current
-      request.  These mechanisms also create a new session data object
-      in the session data container if one does not exist related to
-      the browser id in the current request.  To inhibit this
-      behavior, use the create=0 flag to the 'getSessionData()'
-      method.  In DTML::
-
-        <dtml-let data="session_data_manager.getSessionData(create=0)"> 
-           The'data' name now refers to an existing session data object
-           or None if there was no existing browser id or session data
-           object.  
-        </dtml-let>
-
-      In 
-      ZPT::
-
-        <span tal:define="data python: context.session_data_manager.getSessionData(create=0)">        
-
-      Note: create=0 means return a reference to the session or None.
-      create=1 means return a reference if one exists or create a new
-      Session object and the reference.
-         
-    Modifying A Session Data Object
-
-      Once you've used REQUEST.SESSION or
-      'session_data_manager.getSessionData()' to obtain a session data
-      object, you can set key/value pairs of that session data object.
-
-      In
-      DTML::
-
-        <dtml-let data="REQUEST.SESSION">
-          <dtml-call "data.set('foo', 'bar')">
-          <dtml-comment>Set 'foo' key to 'bar' value.</dtml-comment>
-          <dtml-var "data.get('foo')">
-          <dtml-comment>Will print 'bar'</dtml-comment>
-          <dtml-if "data.has_key('foo')">
-            This will be printed.
-          <dtml-else>
-            This will not be printed.
-          </dtml-if>
-        </dtml-let>
-
-      In
-      ZPT::
-      
-         <span tal:define="data python: request.SESSION">
-            <tal:block define="temp python: data.set('foo','bar')">
-            <p tal:content="python: data.get('foo')">bar will print here"</p>
-            </tal:block>
-         </span>
-     
-      An essentially arbitrary set of key/value pairs can be placed
-      into a session data object.  Keys and values can be any kinds of
-      Python objects (note: see *Concepts and Caveats* section below
-      for exceptions to this rule).  The session data container which
-      houses the session data object determines its expiration policy.
-      Session data objects will be available across client requests
-      for as long as they are not expired.
-
-
-    Clearing A Session Data Object
-
-      You can clear all keys and values from a SESSION object by simply
-      calling its clear() method.  
-
-      In
-      ZPT::
-
-         <span tal:define="dummy python:request.SESSION.clear()"></span>
-
-      In
-      DTML::
-      
-         <dtml-call "REQUEST.SESSION.clear()">
-
-    Manually Invalidating A Session Data Object
-
-      Developers can manually invalidate a session data object.  When
-      a session data object is invalidated, it will be flushed from
-      the system.
-           
-      There is a caveat.  If you invalidate the session object in a script
-      then you **must** obtain a fresh copy of the session object by calling
-      getSessionData and not by reference (REQUEST.SESSION).
-
-      Here is an example using DTML::
-
-         <!-- set a SESSION key and value -->
-         <dtml-let data="REQUEST.SESSION">
-         <dtml-call "data.set('foo','bar')      
-
-         <!-- Now invalidate the SESSION -->
-         <dtml-call "data.invalidate()">
-
-         <!-- But REQUEST.SESSION gives us stale data which is bad.
-              The next statement will still show 'foo' and 'bar'
-         <dtml-var "REQUEST.SESSION>
-         
-         <!-- Heres the work-around: -->
-         data = session_data_manager.getSessionData()
-         
-         <!-- Now we get a fresh copy and life is good as 'foo' and 'bar' have gone away as expected -->
-         <dtml-var data>
-
-      Manual invalidation of session data is useful when you need a
-      "fresh" copy of a session data object.
-
-      If an "onDelete" event is defined for a session data object, the
-      onDelete method will be called before the data object is
-      invalidated.  See a following section for information about
-      session data object "onDelete" and "onAdd" events.
-
-    Manually Invalidating A Browser Id Cookie
-
-      Invalidating a session data object does not invalidate the
-      browser id cookie stored on the user's browser.  Developers may
-      manually invalidate the cookie associated with the browser id.
-      To do so, they can use the 'flushBrowserIdCookie()' method of a
-      browser id manager.  For example::
-
-        <dtml-call "REQUEST.SESSION.getBrowserIdManager().flushBrowserIdCookie()">
-
-      If the 'cookies' namespace isn't a valid browser id key
-      namespace when this call is performed, an exception will be
-      raised.
-
-      
-    Using Session Data with TAL
-
-      Here's an example of using the session data object with
-      TAL::
-
-         <span tal:define="a python:request.SESSION;
-                           dummy python:a.set('zopetime',context.ZopeTime())">
-           <p tal:content="python: a.get('zopetime')"></p>
-         </span>
-         
-
-    Using the 'mapping' Keyword With A Session Data Object in a 'dtml-with'
-
-      DTML has the facility to treat a session data object as a
-      mapping, making it easier to spell some of the more common
-      methods of access to session data objects.  The 'mapping'
-      keyword to dtml-with means "treat name lookups that follow
-      this section as queries to my contents by name."  For
-      example::
-
-        <dtml-let a="REQUEST.SESSION">
-            <dtml-call "a.set('zopetime', ZopeTime())">
-            <dtml-comment>
-              'zopetime' will be set to a datetime object for the current
-              session... the "set" it calls is the set method of the
-              session data object.
-            </dtml-comment>
-        </dtml-let>
-
-        <dtml-with "REQUEST.SESSION" mapping>
-            <dtml-var zopetime>
-            <dtml-comment>
-              'dtml-var zopetime' will print the DateTime object just set
-              because we've used the mapping keyword to map name lookups
-              into the current session data object.
-            </dtml-comment>
-        </dtml-with>
-
-    Using Session Data From Python
-
-      Here's an example of using a session data manager and session
-      data object from a set of Python external methods::
-
-        import time
-        def setCurrentTime(self):
-            a = self.REQUEST.SESSION
-            a.set('thetime', time.time())
-
-        def getLastTime(self):
-            a = self.REQUEST.SESSION
-            return a.get('thetime')
-
-      Calling the setCurrentTime will set the value of the
-      current session's "thetime" key to an integer representation of
-      the current time.  Calling the getLastTime external method will
-      return the integer representation of the last known value of
-      "thetime".
-
-    Interacting with Browser Id Data
-
-      You can obtain the browser id value associated with the current
-      request::
-
-        <dtml-var "REQUEST.SESSION.getBrowserIdManager().getBrowserId()">
-
-      Another way of doing this, which returns the same value
-      is::
-
-        <dtml-var "REQUEST.SESSION.getContainerKey()">
-
-      If no browser id exists for the current request, a new
-      browser id is created implicitly and returned.
-
-      If you wish to obtain the current browser id value without
-      implicitly creating a new browser id for the current request,
-      you can ask the browser_id_manager object explicitly for this
-      value with the 'create=0' parameter::
-
-        <dtml-var "browser_id_manager.getBrowserId(create=0)">
-
-      This snippet will print a representation of the None value if
-      there isn't a browser id associated with the current request, or
-      it will print the browser id value if there is one associated
-      with the current request.  Using 'create=0' is useful if you do
-      not wish to cause the sessioning machinery to attach a new
-      browser id to the current request, perhaps if you do not wish a
-      browser id cookie to be set.
-
-      The browser id is either a string or an integer and has no
-      business meaning.  In your code, you should not rely on the
-      browser id value composition, length, or type as a result, as it
-      is subject to change.
-
-    Determining Which Namespace Holds The Browser Id
-
-      For some applications, it is advantageous to know from which
-      namespace ( "cookies", "form", or "url") the browser id has been
-      gathered.
-      
-      It should be noted that you can configure
-      the browser_id_manager (its in Zope root by default) so that it searches
-      whatever combination of namespaces you select.
-
-      There are three methods of
-      browser id managers which allow you to accomplish this,
-      'isBrowserIdFromCookie()', 'isBrowserIdFromForm()', and
-      'isBrowserIdFromUrl()'::
-
-        <dtml-if "REQUEST.SESSION.getBrowserIdManager().isBrowserIdFromCookie()">
-          The browser id came from a cookie.
-        </dtml-if>
-
-        <dtml-if "REQUEST.SESSION.getBrowserIdManager().isBrowserIdFromForm()">
-          The browser id came from a form.
-        </dtml-if>
-
-        <dtml-if "REQUEST.SESSION.getBrowserIdManager().isBrowserIdFromUrl()">
-          The browser id came from the URL.
-        </dtml-if>
-
-      The 'isBrowserIdFromCookie()' method will return true if the
-      browser id in the current request comes from the
-      'REQUEST.cookies' namespace.  This is true if the browser id was
-      sent to the Zope server as a cookie.
-
-      The 'isBrowserIdFromForm()' method will return true if the
-      browser id in the current request comes from the 'REQUEST.form'
-      namespace.  This is true if the browser id was sent to the Zope
-      server encoded in a query string or as part of a form element.
-
-      The 'isBrowserIdFromUrl()' method will return true if the
-      browser id in the current request comes from the leading
-      elements of the URL.
-
-      If a browser id doesn't actually exist in the current
-      request when one of these methods is called, an error will be
-      raised.
-
-      During typical operations, you shouldn't need to use these
-      methods, as you shouldn't care from which namespace the browser
-      id was obtained.  However, for highly customized applications,
-      this set of methods may be useful.
-
-    Obtaining the Browser Id Name/Value Pair and Embedding It Into A Form
-
-      You can obtain the browser id name from a browser id manager
-      instance.  We've already determined how to obtain the browser id
-      itself.  It is useful to also obtain the browser id name if you
-      wish to embed a browser id name/value pair as a hidden form
-      field for use in POST requests. Here is a DTML example::
-
-        <html>
-        <body>
-        <form action="thenextmethod">
-        <input type=submit name="submit" value=" GO ">
-        <input type=hidden name="<dtml-var "REQUEST.SESSION.getBrowserIdManager().getBrowserIdName()">"
-         value="<dtml-var "REQUEST.SESSION.getBrowserIdManager().getBrowserId()">">
-        </form>
-        </body>
-        </html>
-
-      Here's a TAL example that does the
-      same::
-      
-         <span tal:define="idManager python:request.SESSION.getBrowserIdManager()">
-         <form action="thenextmethod">
-            <input type=submit name="submit" value=" GO ">
-            <input type="hidden" name="name" value="value"
-                 tal:attributes="name python: idManager.getBrowserIdName();
-                        value python: idManager.getBrowserId()">
-
-         </form>
-         </span>
-              
-      A convenience function exists for performing this action as a
-      method of a browser id manager named "getHiddenFormField"::
-
-        <html>
-        <body>
-        <form action="thenextmethod">
-        <input type="submit" name="submit" value=" GO ">
-        <dtml-var "REQUEST.SESSION.getBrowserIdManager().getHiddenFormField()">
-        </form>
-        </body>
-        </html>
-
-      When the above snippets are rendered, the
-      resulting HTML will look something like this::
-
-        <html>
-        <body>
-        <form action="thenextmethod">
-        <input type="submit" name="submit" value=" GO ">
-        <input type="hidden" name="_ZopeId" value="9as09a7fs70y1j2hd7at8g">
-        </form>
-        </body>
-        </html>
-
-      Note that to maintain state across requests when using a form
-      submission, even if you've got 'Automatically Encode
-      Zope-Generated URLs With a Browser Id' checked off in your
-      browser id manager, you'll either need to encode the form
-      "action" URL with a browser id (see "Embedding A Browser Id Into
-      An HTML Link" below) or embed a hidden form field.
-
-    Using formvar-based sessioning.
-    
-      To use formvar-based sessioning, you need to encode a link to its 
-      URL with the browser id by using the browser id manager's 'encodeUrl()'
-      method.
-
-    Determining Whether A Browser Id is "New"
-
-      A browser id is "new" if it has been set in the current request
-      but has not yet been acknowledged by the client.  "Not
-      acknowledged by the client" means it has not been sent back by
-      the client in a request.  This is the case when a new browser id
-      is created by the sessioning machinery due to a reference to
-      REQUEST.SESSION or similar as opposed to being received by the
-      sessioning machinery in a browser id name namespace.  You can
-      use the 'isBrowserIdNew()' method of a browser id manager to
-      determine whether the session is new::
-
-        <dtml-if "REQUEST.SESSION.getBrowserIdManager().isBrowserIdNew()">
-          Browser id is new.
-        <dtml-else>
-          Browser id is not new.
-        </dtml-if>
-
-      This method may be useful in cases where applications wish to
-      prevent or detect the regeneration of new browser ids when the
-      same client visits repeatedly without sending back a browser id
-      in the request (such as may be the case when a visitor has
-      cookies "turned off" in their browser and the browser id manager
-      only uses cookies).
-
-      If there is no browser id associated with the current request,
-      this method will raise an error.
-
-      You shouldn't need to use this method during typical operations,
-      but it may be useful in advanced applications.
-
-    Determining Whether A Session Data Object Exists For The Browser Id Associated With This Request
-
-      If you wish to determine whether a session data object with a
-      key that is the current request's browser id exists in the
-      session data manager's associated session data container, you
-      can use the 'hasSessionData()' method of the session data
-      manager.  This method returns true if there is session data
-      associated with the current browser id::
-
-        <dtml-if "session_data_manager.hasSessionData()">
-           The sessiondatamanager object has session data for the browser id
-           associated with this request.
-        <dtml-else>
-           The sessiondatamanager object does not have session data for
-           the browser id associated with this request.
-        </dtml-if>
-
-      The 'hasSessionData()' method is useful in highly customized
-      applications, but is probably less useful otherwise.  It is
-      recommended that you use REQUEST.SESSION instead, allowing the
-      session data manager to determine whether or not to create a new
-      data object for the current request.
-
-    Embedding A Browser Id Into An HTML Link
-
-      You can embed the browser id name/value pair into an HTML link
-      for use during HTTP GET requests.  When a user clicks on a link
-      with a URL encoded with the browser id, the browser id will be
-      passed back to the server in the REQUEST.form namespace.  If you
-      wish to use formvar-based session tracking, you will need to
-      encode all of your "public" HTML links this way.  You can use
-      the 'encodeUrl()' method of browser id managers in order to
-      perform this encoding::
-
-        <html>
-        <body>
-        <a href="<dtml-var "REQUEST.SESSION.getBrowserIdManager().encodeUrl('/amethod')">">Here</a>
-         is a link.
-        </body>
-        </html>
-
-      The above dtml snippet will encode the URL "/amethod" (the
-      target of the word "Here") with the browser id name/value pair
-      appended as a query string.  The rendered output of this DTML
-      snippet would look something like this::
-
-        <html>
-        <body>
-        <a href="/amethod?_ZopeId=7HJhy78978979JHK">Here</a>
-         is a link.
-        </body>
-        </html>
-
-      You may successfully pass URLs which already contain query
-      strings to the 'encodeUrl()' method.  The encodeUrl method will
-      preserve the existing query string and append its own name/value
-      pair.
-
-      You may choose to encode the browser id into the URL using an
-      "inline" style if you're checking for browser ids in the URL
-      (e.g. if you've checked 'URLs' in the "Look for Browser Id in"
-      form element of your browser id manager)::
-
-        <html>
-        <body>
-        <a href="<dtml-var "REQUEST.SESSION.getBrowserIdManager().encodeUrl('/amethod', style='inline')">">Here</a>
-         is a link.
-        </body>
-        </html>
-
-      The above dtml snippet will encode the URL "/amethod" (the
-      target of the word "Here") with the browser id name/value pair
-      embedded as the first two elements of the URL itself.  The
-      rendered output of this DTML snippet would look something like
-      this::
-
-        <html>
-        <body>
-        <a href="/_ZopeId/7HJhy78978979JHK/amethod">Here</a>
-         is a link.
-        </body>
-        </html>
-
-
-    Using Session onAdd and onDelete Events
-
-      The configuration of a Transient Object Container (aka a session
-      data container) allows a method to be called when a session data
-      object is created (onAdd) or when it is invalidated or timed out
-      (onDelete).  
-      
-      The events are independent of each other.  You might want an
-      onAdd method but not an onDelete method.  You may define one, both or
-      none of the TOC event methods.
-      
-      Here are examples of the kinds of things Session onAdd and
-      onDelete methods are used to do:
-      
-         - The onAdd method can be used to populate a session data 
-         object with "default" values before it's used by application code.  
-
-         - The onDelete method can write the contents of a 
-         session data object out to a permanent data store before it is 
-         timed out or invalidated.  
-
-      You can manually configure the onAdd and onDelete methods.
-      Click the *management tab* of '\temp_folder\session_data.  Enter
-      "a physical path" to either a an external method or python
-      script.  NOTE: This configuration is only good until the next
-      Zope shutdown because '\temp_folder\session_data' is in a RAM
-      database, Configure the onAdd and onDelete methods for this data
-      container via the 'zope.conf' configuration file for your Zope
-      instance. This is covered in some detail in *Setting Initial
-      Transient Object Container Parameters* later in this document.
-      
-      Note: the onAdd and onDelete events do not raise exceptions if
-      logic in the method code fails.  Instead, an error is logged in
-      the Zope event log.  In recent versions of Zope, the event.log
-      defaults to Zope-Instance/log/event.log.  This is configurable
-      in 'zope.conf'.
-
-    Writing onAdd and onDelete Methods
-
-      Session data objects optionally call a Zope method when they are
-      created and when they are timed out or invalidated.
-
-      Specially-written Script (Python) scripts or External Methods
-      can be written to serve the purpose of being called on session
-      data object creation and invalidation.
-
-      The Script (Python) or External Method should define two
-      arguments, "sdo" and "toc".  "sdo" represents the session data
-      object being created or terminated, and "toc" represents the
-      transient object container in which this object is stored.
-
-      For example, to create a method to handle a session data object
-      onAdd event which prepopulates the session data object with a
-      DateTime object, you might write a Script (Python) named 'onAdd'
-      which had function parameters "sdo" and "toc" and a body of::
-
-          sdo['date'] = context.ZopeTime()
-
-      If you set the path to this method as the onAdd event, before
-      any application handles the new session data object, it will be
-      prepopulated with a key 'date' that has the value of a DateTime
-      object set to the current time.
-
-      To create a method to handle a session onDelete event which writes
-      a log message, you might write an External Method with the
-      following body::
-
-         from zLOG import LOG, WARNING
-         def onDelete(sdo, toc):
-             logged_out = sdo.get('logged_out', None)
-             if logged_out is None:
-                 LOG('session end', WARNING,
-                     'session ended without user logging out!')
-
-      If you set the path to this method as the onDelete event, a message
-      will be logged if the 'logged_out' key is not found in the
-      session data object.
-
-      Note that for onDelete events, there is no guarantee that the
-      onDelete event will be called in the context of the user who
-      originated the session! Due to the
-      "expire-after-so-many-minutes-of-inactivity" behavior of session
-      data containers, a session data object onDelete event initiated
-      by one user may be called while a completely different user is
-      visiting the application.  Your onDelete event method should
-      not naively make any assumptions about user state.  For
-      example, the result of the Zope call
-      "getSecurityManager().getUser()" in an onDelete session event
-      method will almost surely *not* be the user who originated the
-      session.
-
-      The session data object onAdd method will always be called in
-      the context of the user who starts the session.
-
-      For both onAdd and onDelete events, it is almost always desirable
-      to set proxy roles on event methods to replace the roles granted
-      to the executing user when the method is called because the
-      executing user will likely not be the user for whom the session
-      data object was generated.  For more information about proxy
-      roles, see the chapter entitled "Users and Security":Security.stx.
-
-      For additional information about using session onDelete events
-      in combination with data object timeouts, see the section
-      entitled "Session Data Object Expiration Considerations" in the
-      Concepts and Caveats section below.
-
-  Configuration and Operation
-
-    Setting the default Transient Object Container Parameters
-          
-      Click on '/temp_folder/session_data' and you'll see options to
-      control inactivity time-outs and the maximum allowable number of
-      Session objects.  You can even include paths to python scripts
-      that handle a Session's after-add and before-delete events.
-
-      Because '/temp_folder/session_data' is stored in a RAM database,
-      it disappears and is recreated after each restart of your Zope
-      server.  This means that any changes to parameters will be lost
-      the next time you restart your Zope server.
-
-      If you need to permanently alter the default Transient Object
-      Container's configuration you must edit Zope's startup
-      configuration file 'zope.conf'.
-
-      Note that additional Transient Object Containers can be instantiated 
-      in permanent storage.  They are rarely needed.  If you do need this
-      its covered in detail later in this document.
-      
-      Here is the relevant portion of zope.conf::
-
-         # Directive: maximum-number-of-session-objects
-         # Description: An integer value representing the maximum number 
-         #              of subobjects" 
-         # allowable in the '/temp_folder/session_data' transient object container.
-         #         
-         # Default: 1000
-         # Example: maximum-number-of-session-objects 10000
-
-         # Directive: session-add-notify-script-path
-         #
-         # Description:
-         #     An optional fill Zope path name of a callable object to be set as the
-         #     "script to call on object addition" of the session_data transient
-         #     object container created in the /temp_folder folder at startup.
-         #
-         # Default: unset
-         # Example: session-add-notify-script-path /scripts/add_notifier
-
-         # Directive: session-delete-notify-script-path
-         #
-
-         # Description:
-         #     An optional fill Zope path name of a callable object to be set as the
-         #     "script to call on object deletion" of the session_data transient
-         #     object container created in the /temp_folder folder at startup.
-         #
-         # Default: unset
-         # Example: session-delete-notify-script-path /scripts/del_notifier
-
-         # Directive: session-timeout-minutes
-         #
-         # Description:
-         #     An integer value representing the number of minutes to be used as the
-         #     "data object timeout" of the '/temp_folder/session_data' transient
-         #     object container.
-         #
-         # Default: 20
-         # Example: session-timeout-minutes 30
-
-         # Directive: session-resolution-seconds
-         #
-         # Description:
-         #    An integer value representing the number of seconds to be used as the
-         #    "timeout resolution" of the '/temp_folder/session_data' transient
-         #    object container.
-         #
-         # Default: 20
-         # Example: session-resolution-seconds 60
-      
-      Important: The directives described above replace the use of
-      environmental variables that accomplish the same thing.  These
-      are deprecated::
-         
-         - ZSESSION_ADD_NOTIFY
-       
-         - ZSESSION_DEL_NOTIFY
-       
-         - ZSESSION_TIMEOUT_MINS
-       
-         - ZSESSION_OBJECT_LIMIT
-
-    Instantiating Multiple Browser Id Managers (Optional)
-
-      Transient data objects depend on a session data manager, which in turn 
-      depends on a browser id manager.  A browser id manager doles out and
-      otherwise manages browser ids.  All session data managers need
-      to talk to a browser id manager to get browser id information.
-
-      You needn't create a browser id manager to use sessioning.  One
-      is already created as a result of the initial Zope installation.
-      If you've got special needs, you may want to instantiate more
-      than one browser id manager.  Having multiple browser id
-      managers may be useful in cases where you have a "secure"
-      section of a site and an "insecure" section of a site, each
-      using a different browser id manager with respectively
-      restrictive security settings.
-
-      In the container of your choosing, select "Browser Id Manager"
-      from the add dropdown list in the Zope management interface.
-      When you add a new browser id manager, the form options
-      available are:
-
-        Id -- you cannot choose an 'id' for your browser id manager.
-        It must always be "browser_id_manager".  Additionally, you
-        cannot rename a browser id manager.  This is required in the
-        current implementation so that session data managers can find
-        session id managers via Zope acquisition.  
-
-
-        Title -- the browser id manager title.
-
-        Browser Id Name -- the name used to look up the value of the
-        browser id.  This will be the name looked up in the 'cookies'
-        or 'form' REQUEST namespaces when the browser id manager
-        attempts to find a cookie, form variable, or URL with a
-        browser id in it.
-
-        Look for Browser Id Name In -- choose the request elements to
-        look in when searching for the browser id name.  You may
-        choose "cookies", "Forms and Query Strings", and "URLs".
-
-        Automatically Encode Zope-Generated URLs With A Browser Id --
-        if this option is checked, all URLs generated by Zope (such as
-        URLs obtained via the 'absolute_url' method of all Zope
-        objects) will have a browser id name/value pair embedded
-        within them.  This typically only make sense if you've also
-        got the 'URLs' setting of "Look for Browser Id in" checked
-        off.
-
-        Cookie Path -- this is the 'path' element which should be sent
-        in the browser id cookie.  For more information, see the
-        Netscape Cookie specification at
-        http://home.netscape.com/newsref/std/cookie_spec.html.
-
-        Cookie Domain -- this is the "domain" element which should be
-        sent in the browser id cookie. For more information, see the
-        Netscape Cookie specification at
-        http://home.netscape.com/newsref/std/cookie_spec.html.
-        Leaving this form element blank results in no domain element
-        in the cookie.  If you change the cookie domain here, the
-        value you enter must have at least two dots (as per the cookie
-        spec).
-
-        Cookie Lifetime In Days -- browser id cookies sent to browsers
-        will last this many days on a remote system before expiring if
-        this value is set.  If this value is 0, cookies will persist
-        on client browsers for only as long as the browser is open.
-
-        Only Send Cookie Over HTTPS -- if this flag is set, only send
-        cookies to remote browsers if they're communicating with us
-        over https.  The browser id cookie sent under this
-        circumstance will also have the 'secure' flag set in it, which
-        the remote browser should interpret as a request to refrain
-        from sending the cookie back to the server over an insecure
-        (non-https) connection.  NOTE: In the case you wish to share
-        browser id cookies between https and non-https connections
-        from the same browser, do not set this flag.
-
-      After reviewing and changing these options, click the "Add"
-      button to instantiate a browser id manager.
-
-      You can change any of a browser id manager's initial settings by
-      visiting it in the management interface.
-
-    Instantiating A Session Data Manager (Optional)
-
-      After instantiating at least one browser id manager, it's
-      possible to instantiate a session data manager.  You don't need
-      to do this in order to begin using Zope's sessioning machinery,
-      as a default session data manager is created as
-      '/session_data_manager'.  
-
-      You can place a session data manager in any Zope container,as
-      long as a browser id manager object named 'browser_id_manager'
-      can be acquired from that container.  The session data manager
-      will use the first acquired browser id manager.
-
-      Choose "Session Data Manager" within the container you wish to
-      house the session data manager from the "Add" dropdown box in
-      the Zope management interface.
-
-      The session data manager add form displays these
-      options:
-
-        Id -- choose an id for the session data manager
-
-        Title -- choose a title for the session data manager
-
-        Transient Object Container Path -- enter the Zope path to a
-        Transient Object Container in this text box in order to use it
-        to store your session data objects.  
-        Note:  session manager's should not share transient object 
-        paths. This is an example path:
-        
-        Zope transient object container is '/MyTransientSessionFolder'
-
-      After reviewing and changing these options, click the "Add"
-      button to instantiate a session data manager.
-
-      You can manage a session data manager by visiting it in the
-      management interface.  You may change all options available
-      during the add process by doing this.
-
-    Instantiating a Transient Object Container
-
-      The default transient object container at
-      '/temp_folder/session_data' stores its objects in RAM, so these
-      objects and their data disappear when you restart Zope.
-      
-      If you want your session data to persist across server reboots, 
-      or if you have a very large collection of session data
-      objects, or if you'd like to share sessions between ZEO clients,
-      you will want to instantiate a transient data container in a
-      more permanent storage.  
-
-      A heavily-utilized transient object container *should be
-      instantiated inside a database which is nonundoing*!  Although
-      you may instantiate a transient data container in any storage,
-      if you make heavy use of an external session data container in
-      an undoing database (such as the default Zope database which is
-      backed by "FileStorage", an undoing and versioning storage),
-      your database will grow in size very quickly due to the
-      high-write nature of session tracking, forcing you to pack very
-      often.  You can "mount" additional storages within the
-      'zope.conf' file of your Zope instance.  The default
-      'temp_folder' is mounted inside a 'TemporaryStorage', which is
-      nonundoing and RAM-based.  There are other nonundoing storages,
-      such as BerkeleyStorage, although none quite as well-supported
-      as TemporaryStorage.
-
-      Here are descriptions of the add form of a Transient Object
-      Container, which may be added by selecting "Transient Object
-      Container" for the Zope Add list.:
-
-      Special note: When you add a transient object container to a
-      non-RAM-based storage, unlike the the default transient objects
-      contained in temp_folder, these instances of TOC maintain their
-      parameter settings between Zope Restarts.  Importantly, they *do
-      not* read zope.conf.
-
-        Id -- the id of the transient object container
-
-        Title (optional) -- the title of the transient object container
-
-        Data object timeout in minutes -- enter the number of minutes
-        of inactivity which causes a contained transient object be be
-        timed out.  "0" means no expiration.
-
-        Maximum number of subobjects -- enter the maximum number of
-        transient objects that can be added to this transient object
-        container.  This value helps prevent "denial of service"
-        attacks to your Zope site by effectively limiting the number
-        of concurrent sessions.
-
-        Script to call upon object add (optional) -- when a session
-        starts, you may call an external method or Script (Python).
-        This is the Zope path to the external method or Script (Python)
-        object to be called.  If you leave this option blank, no onAdd
-        function will be called.  An example of a method path is
-        '/afolder/amethod'.
-
-        Script to call upon object delete (optional) -- when a session
-        ends, you may call an external method or Script (Python).
-        This is the Zope path to the external method or Script
-        (Python) object to be called.  If you leave this option blank,
-        no onDelete function will be called.  An example of a method
-        path is '/afolder/amethod'.
-
-      Multiple session data managers can make use of a single
-      transient object container to the extent that they may share the
-      session data objects placed in the container between them.  This
-      is not a recommended practice, however, as it has not been
-      tested at all.
-
-      The 'data object timeout in minutes' value is the number of
-      minutes that session data objects are to be kept since their
-      last-accessed time before they are flushed from the data
-      container.  For instance, if a session data object is accessed
-      at 1:00 pm, and if the timeout is set to 20 minutes, if the
-      session data object is not accessed again by 1:19:59, it will be
-      flushed from the data container at 1:20:00 or a time shortly
-      thereafter.  "Accessed", in this terminology, means "pulled out
-      of the container" by a call to the session data manager's
-      getSessionData() method or an equivalent (e.g. a reference to
-      REQUEST.SESSION).  See "Session Data Object Expiration
-      Considerations" in the *Concepts and Caveats* section below for
-      details on session data expiration.
-
-      Randall Kern has additionally written a "ZEO + sessioning
-      How-To":http://www.zope.org/Members/randy/ZEO-Sessions that may
-      help, although it describes an older generation of Zope
-      sessioning machinery, so you may need to extrapolate a bit.  For
-      Zope 2.7+, you should be able to put a session data container
-      within any mounted storage (including a ZEO ClientStorage).
-
-    Configuring Sessioning Permissions
-
-      You need only configure sessioning permissions if your
-      requirements deviate substantially from the norm.  In this case,
-      here is a description of the permissions related to sessioning:
-
-        Permissions related to browser id managers:
-
-          Add Browser Id Manager -- allows a role to add browser id
-          managers.  By default, enabled for 'Manager'.
-
-          Change Browser Id Manager -- allows a role to change an
-          instance of a browser id manager.  By default, enabled for
-          'Manager'.
-
-          Access contents information -- allows a role to obtain data
-          about browser ids.  By default, enabled for 'Manager' and
-          'Anonymous'.
-
-        Permissions related to session data managers:
-
-          Add Session Data Manager -- allows a role to add session
-          data managers.  By default, enabled for 'Manager'.
-
-          Change Session Data Manager -- allows a role to call
-          management-related methods of a session data manager.  By
-          default, enabled for 'Manager'.
-
-          Access session data -- allows a role to obtain access to the
-          session data object related to the current browser id.
-          By default, enabled for 'Manager' and 'Anonymous'.  You may
-          wish to deny this permission to roles who have DTML or
-          Web-based Python scripting capabilities who should not be
-          able to access session data.
-
-          Access arbitrary user session data -- allows a role to
-          obtain and otherwise manipulate any session data object for
-          which the browser id is known.  By default, enabled for
-          'Manager'.  (For more information, see the
-          'getSessionDataByKey' method described in the sessioning API
-          in the Zope Help System.)
-
-          Access contents information -- allows a role to obtain data
-          about session data.  By default, enabled for 'Manager' and
-          'Anonymous'.
-
-        Permissions related to transient object containers:
-
-          Add Transient Object Container -- allows a role to add
-          transient objects containers.  By default, enabled for
-          'Manager'.
-
-          Change Transient Object Container -- allows a role to make
-          changes to a transient object container.
-
-          Access Transient Objects -- allows a role to obtain and otherwise
-          manipulate the transient object related to the current
-          browser id.
-
-  Concepts and Caveats
-
-    Security Considerations
-
-      Sessions are insecure by their very nature.  If an attacker gets
-      a hold of someone's browser id, and if they can construct a
-      cookie or use form elements or URL elements to pose as that user
-      from their own browser, they will have access to all information
-      in that user's session.  Sessions are not a replacement for
-      authentication for this reason.
-
-      Ideally, you'd like to make certain that nobody but the user its
-      intended for gets a hold of his browser id.  To take steps
-      in this direction, and if you're truly concerned about security,
-      you will ensure that you use cookies to maintain browser id
-      information, and you will secure the link between your users and
-      your site using SSL.  In this configuration, it is more
-      difficult to "steal" browser id information as the browser id
-      will not be evident in the URL and it will be very difficult for
-      attackers to "tap" the encrypted link between the browser and
-      the Zope site.
-
-      There are significant additional risks to user privacy in
-      employing sessions in your application, especially if you use
-      URL-based or formvar-based browser ids.  Commonly, a browser id
-      is embedded into a form/querystring or a URL in order to service
-      users who don't have cookies turned on.
-
-      For example, this kind of bug was present until recently in a
-      lot of webmail applications: if you sent a mail to someone that
-      included a link to a site whose logs you could read, and the
-      user clicked on the link in his webmail page, the full URL of
-      the page, including the authentication (stored as session
-      information in the URL) would be sent as a HTTP REFERER to your
-      site.
-
-      Nowadays all serious webmail applications either choose to store
-      at least some of the authentication information outside of the
-      URL (in a cookie for instance), or process all the
-      user-originated URLs included in the mail to make them go
-      through a redirection that sanitizes the HTTP REFERER.
-
-      The moral of the story is: if you're going to use sessions to
-      store sensitive information, and you link to external sites
-      within your own site, you're best off using *only* cookie-based
-      browser ids.
-
-    Browser Id (Non-)Expiration
-
-      A browser id will last as long as the browser id cookie persists on 
-      the client, or for as long as someone uses a bookmarked URL with a 
-      browser id encoded into it.  
-
-      The same id will be obtained by a browser \
-      id manager on every visit by that client to a site - 
-      potentially indefinitely depending on which conveyance mechanisms you 
-      use and your configuration for cookie persistence.
-
-      The transient object container implements a policy for data
-      object expiration.  If asked for a session data object related
-      to a particular browser id which has been expired by a session
-      data container, a session data manager will a return a new
-      session data object.
-
-    Session Data Object Expiration Considerations
-
-      Session data objects expire after the period between their last
-      access and "now" exceeds the timeout value provided to the
-      session data container which hold them.  No special action need
-      be taken to expire session data objects.
-
-      However, because Zope has no scheduling facility, the sessioning
-      machinery depends on the continual exercising of itself to
-      expire session data objects.  If the sessioning machinery is not
-      exercised continually, it's possible that session data
-      objects will stick around longer than the time specified by
-      their data container timeout value.  For example:
-
-        - User A exercises application machinery that generates a
-        session data object.  It is inserted into a session data
-        container which advertises a 20-minute timeout.
-
-        - User A "leaves" the site.
-
-        - 40 minutes go by with no visitors to the site.
-
-        - User B visits 60 minutes after User A first generated his
-        session data object, and exercises app code which hands out
-        session data objects.  *User A's session is expired at this
-        point, 40 minutes "late".*
-
-      As shown, the time between a session's onAdd and onDelete is not
-      by any means *guaranteed* to be anywhere close to the amount of
-      time represented by the timeout value of its session data
-      container.  The timeout value of the data container should only
-      be considered a "target" value.
-
-      Additionally, even when continually exercised, the sessioning
-      machinery has a built in error potential of roughly 20% with
-      respect to expiration of session data objects to reduce resource
-      requirements.  This means, for example, if a transient object
-      container timeout is set to 20 minutes, data objects added to it
-      may expire anywhere between 16 and 24 minutes after they are
-      last accessed. 
-
-    Sessioning and Transactions
-
-      Sessions interact with Zope's transaction system.  If a
-      transaction is aborted, the changes made to session data objects
-      during the transaction will be rolled back.
-
-    Mutable Data Stored Within Session Data Objects
-
-      If you mutate an object stored as a value within a session data
-      object, you'll need to notify the sessioning machinery that the
-      object has changed by calling 'set' or '__setitem__' on the
-      session data object with the new object value.  For example::
-
-         session = self.REQUEST.SESSION
-         foo = {}
-         foo['before'] = 1
-         session.set('foo', foo)
-
-         # mutate the dictionary
-
-         foo['after'] = 1
-
-         # performing session.get('foo') 10 minutes from now will likely
-         # return a dict with only 'before' within!
-
-      You'll need to treat mutable objects immutably, instead.  Here's
-      an example that makes the intent of the last example work by
-      doing so::
-
-         session = self.REQUEST.SESSION
-         foo = {}
-         foo['before'] = 1
-         session.set('foo', foo)
-
-         # mutate the dictionary
-         foo['after'] = 1
-
-         # tickle the persistence machinery
-         session.set('foo', foo)
-
-      An easy-to-remember rule for manipulating data objects in
-      session storage: always explicitly place an object back into
-      session storage whenever you change it.  For further reference,
-      see the "Persistent Components" chapter of the Zope Developer's
-      Guide at http://www.zope.org/Documentation/ZDG.
-
-    session.invalidate() and stale references to the session object  
-    
-      This Python Script illustrates an issue with using the
-      invalidate method of a session object::
-      
-         request = container.REQUEST
-         session = request.SESSION
-         session.set('foo','bar')
-         session.invalidate() 
-         # ............................................
-         # we expect that invalidate() flushes the session 
-         # ............................................
-         print 'after invalidate()',session.get('foo') # 'bar' still prints!
-
-         # ............................................
-         # Even this isn't enough
-         # ............................................
-         session = request.SESSION
-         print 'after invalidate()', session.get('foo') # 'bar' still prints!
-      
-         # ............................................
-         # Here's the work-around
-         # ............................................
-         session = context.session_data_manager.getSessionData()
-         print 'after getSessionData', session.get('foo') # 'bar' is GONE which is good
-         return printed
-
-      In short, after using the 'invalidate' method of a session
-      object, the next reference to the session object you obtain
-      should be through "getSessionData" rather than
-      'REQUEST.SESSION'.
- 
-    Session Data Object Keys
-
-      A session data object has essentially the same restrictions as a
-      Python dictionary.  Keys within a session data object must be
-      hashable (strings, tuples, and other immutable basic Python
-      types; or instances which have a __hash__ method).  This is a
-      requirement of all Python objects that are to be used as keys to
-      a dictionary.  For more information, see the associated Python
-      documentation at
-      http://www.python.org/doc/current/ref/types.html (Mappings ->
-      Dictionaries).
-
-    In-Memory Session Data Container RAM Utilization
-
-      Each session data object which is added to an "internal"
-      (RAM-based) session data container will consume at least 2K of
-      RAM.
-
-    Mounted Transient Object Container Caveats
-    
-      Mounted TOC's do not acquire parameter's from zope.conf (which is the
-      case for the default transient object container).  Therefore you set
-      parameters directly on the object in ZMI.
-
-      Persistent objects which have references to other persistent
-      objects in the same database cannot be committed into a mounted
-      database because the ZODB does not currently handle
-      cross-database references.
-
-      Transient object containers which are sometimes stored in a
-      "mounted" database (as is currently the case for the default
-      '/temp_folder/session_data' TOC.  If you use a transient object
-      container that is accessed via a "mounted" database, you cannot
-      store persistent object instances which have already been stored
-      in the "main" database as keys or values in a session data
-      object.  If you try to do so, it is likely that an
-      'InvalidObjectReference' exception will be raised by the ZODB
-      when the transaction involving the object attempts to commit.
-      As a result, the transaction will fail and the session data
-      object (and other objects touched in the same transaction) will
-      fail to be committed to storage.
-
-      If your "main" ZODB database is backed by a nonundoing storage,
-      you can avoid this condition by storing session data objects in
-      an transient object container instantiated within the "main"
-      ZODB database.  If this is not an option, you should ensure that
-      objects you store as values or keys in a session data object
-      held in a mounted session data container are instantiated "from
-      scratch" (via their constructors), as opposed to being "pulled
-      out" of the main ZODB.
-
-    Conflict Errors
-
-      This session tracking software stores all session state in
-      Zope's ZODB.  The ZODB uses an optimistic concurrency strategy
-      to maintain transactional integrity for simultaneous writes.
-      This means that if two objects in the ZODB are changed at the
-      same time by two different connections (site visitors) that a
-      "ConflictError" will be raised.  Zope retries requests that
-      raise a ConflictError at most 3 times.  If your site is
-      extremely busy, you may notice ConflictErrors in the Zope debug
-      log (or they may be printed to the console from which you run
-      Zope).  An example of one of these errors is as follows::
-
-       2001-01-16T04:26:58 INFO(0) Z2 CONFLICT Competing writes at, /getData
-       Traceback (innermost last):
-       File /zope/lib/python/ZPublisher/Publish.py, line 175, in publish
-       File /zope/lib/python/Zope/__init__.py, line 235, in commit
-       File /zope/lib/python/ZODB/Transaction.py, line 251, in commit
-       File /zope/lib/python/ZODB/Connection.py, line 268, in commit
-       ConflictError: '\000\000\000\000\000\000\002/'
-
-      Errors like this in your debug log (or console if you've not
-      redirected debug logging to a file) are normal to an extent.  If
-      your site is undergoing heavy load, you can expect to see a
-      ConflictError perhaps every 20 to 30 seconds.  The requests
-      which experience conflict errors will be retried automatically
-      by Zope, and the end user should *never* see one.  Generally,
-      session data objects attempt to provide application-level
-      conflict resolution to reduce the limitations imposed by
-      conflict errors NOTE: to take advantage of this feature, you
-      must store your transient object container in a storage such as
-      FileStorage or TemporaryStorage which supports application-level
-      conflict resolution.
-
-    Zope Versions and Sessioning
-
-      In the default Zope sessioning configuration , session data
-      objects are not versioned.  This means that when you change a
-      session data object while using a Zope Version, the changes will
-      be "seen" outside of the version.
-
-  Further Documentation
-
-    All of the methods implemented by Session Data Managers, Browser
-    Id Managers, Transient Data Containers and Transient Data objects
-    are fully documented in the Zope help system under Zope Help ->
-    API Reference -> Session API and Zope Help -> API Reference ->
-    Transient Object.
-

Added: zope2book/trunk/source/BasicScripting.rst
===================================================================
--- zope2book/trunk/source/BasicScripting.rst	                        (rev 0)
+++ zope2book/trunk/source/BasicScripting.rst	2009-02-16 20:22:58 UTC (rev 96604)
@@ -0,0 +1,725 @@
+Basic Zope Scripting
+####################
+
+So far, you've learned about some basic Zope objects and how to manage them
+through the *Zope Management Interface*. This chapter shows you how to manage
+Zope objects programmatically.
+
+
+Calling Methods From the Web
+============================
+
+Since Zope is a web application server, the easiest way to communicate with
+Zope is through a web browser. Any URL your browser requests from the server is
+mapped to both an object and a method. The method is executed *on* the object,
+and a response is sent to your browser.
+
+As you might already know, visiting the URL :: 
+
+  http://localhost:8080/
+
+returns the *Zope Quick Start* page. In this case, we only specify an object --
+the *root* object -- but no method. This just works because there is a default
+method defined for *Folders*: *index_html*. Visiting the URL::
+
+  http://localhost:8080/index_html
+
+returns (almost) exactly the same page.
+
+You can also specify the root object as:: 
+
+  http://localhost:8080/manage_main
+
+but in this case the *manage_main* method is called, and the workspace frame
+displays the root content of your Zope site, without the navigator frame.
+
+The same method can be called *on* other objects: when you visit the URL::
+
+  http://localhost:8080/Control_Panel/manage_main
+
+the *manage_main* method is called *on* the *Control Panel* object.
+
+Sometimes a query string is added to the URL, e.g.:: 
+
+  http://localhost:8080/manage_main?skey=meta_type
+
+The query string is used for passing arguments to the method. In this case, the
+argument::
+
+  skey
+
+specifies the sort key with the value *meta_type*. Based on this argument, the
+*manage_main* method returns a modified version of the basic page: the
+sub-objects are sorted by *Type*, not by *Name* as they are without that query
+string.
+
+While the *manage_main* method is defined in the class of the object,
+*index_html* is (by default) a DTML Method object in the root folder that can
+be modified through the web. *index_html* itself is a presentation object, but
+when called *on* a folder, it behaves as a method that returns the default view
+of the folder.
+
+Method Objects and Context Objects
+++++++++++++++++++++++++++++++++++
+
+When you call a method, you usually want to single out some object that is
+central to the method's task, either because that object provides information
+that the method needs, or because the method will modify that object. In
+`object-oriented <ObjectOrientation.stx>`_ terms, we want to call the method
+*on* this particular object. But in conventional object-oriented programming,
+each object can perform the methods that are defined in (or inherited by) its
+class. How is it that one Zope object can be used as a method for (potentially)
+many other objects, without its being defined by the classes that define these
+objects?
+
+Recall that in the chapter entitled `Acquisition <Acquisition.stx>`_, we
+learned that Zope can find objects in different places by *acquiring* them from
+parent containers. Acquisition allows us to treat an object as a method that
+can be called *in the context of* any suitable object, just by constructing an
+appropriate URL. The object *on* which we call a method gives it a context in
+which to execute. Or, to put it another way: the context is the environment in
+which the method executes, from which the method may get information that it
+needs in order to do its job.
+
+Another way to understand the context of a method is to think of the method as
+a function in a procedural programming language, and its context as an implicit
+argument to that function.
+
+While the Zope way to call methods *in the context of* objects **works**
+differently than the normal object-oriented way to call class-defined methods
+*on* objects, they are **used** the same way, and it is simpler to say that you
+are calling the method *on* the object.
+
+There are two general ways to call methods *on* objects: by visiting an URL,
+and by calling the method from another method.
+
+URL Traversal and Acquisition
++++++++++++++++++++++++++++++
+
+The concept of calling methods *in the context of* objects is a powerful
+feature that enables you to apply logic to objects, like documents or folders,
+without having to embed any actual code within the object.
+
+For example, suppose you have a collection of objects and methods, as shown in
+the figure below.
+
+`A collection of objects and methods <img:9-1:Figures/zoo.png>`_
+
+To call the *feed* method on the *hippo* object, you would visit the URL:: 
+
+  Zoo/LargeAnimals/hippo/feed
+
+To call the *feed* method on the *kangarooMouse* object you would visit the
+URL:: 
+
+  Zoo/SmallAnimals/kangarooMouse/feed
+
+These URLs place the *feed* method in the context of the *hippo* and
+*kangarooMouse* objects, respectively.
+
+Zope breaks apart the URL and compares it to the object hierarchy,
+working backwards until it finds a match for each part.  This process is
+called *URL traversal*.  For example, when you give Zope the URL:: 
+
+  Zoo/LargeAnimals/hippo/feed
+
+it starts at the root folder and looks for an object named *Zoo*. It then moves
+to the *Zoo* folder and looks for an object named *LargeAnimals*. It moves to
+the *LargeAnimals* folder and looks for an object named *hippo*. It moves to
+the *hippo* object and looks for an object named *feed*. The *feed* method
+cannot be found in the *hippo* object and is located in the *Zoo* folder by
+using acquisition. Zope always starts looking for an object in the last object
+it traversed, in this case: *hippo*. Since *hippo* does not contain anything,
+Zope backs up to *hippo's* immediate container *LargeAnimals*. The *feed*
+method is not there, so Zope backs up to *LargeAnimals* container, *Zoo*, where
+*feed* is finally found.
+
+Now Zope has reached the end of the URL and has matched objects to every name
+in the URL. Zope recognizes that the last object found, *feed*, is callable,
+and calls it *in the context of* the second-to-last object found: the *hippo*
+object. This is how the *feed* method is called *on* the *hippo* object.
+
+Likewise, you can call the *wash* method on the *hippo* by visiting the URL::
+
+  Zoo/LargeAnimals/hippo/wash
+
+In this case, Zope acquires the *wash* method from the *LargeAnimals* folder.
+
+Note that *Script (Python)* and *Page Template* objects are always method
+objects. You can't call another method *in the context* of one of them. Given
+*wash* is such a method object, visiting the URL ::
+
+  Zoo/LargeAnimals/hippo/wash/feed
+
+would also call the *wash* method on the *hippo* object. Instead of traversing
+to *feed*, everything after the method ::
+
+  wash
+
+is cut off of the URL and stored in the variable::
+
+  traverse_subpath
+
+
+The Special Folder Object *index_html*
++++++++++++++++++++++++++++++++++++++++
+
+As already mentioned at the beginning of this chapter, Zope uses the default
+method if no other method is specified. The default method for Folders is
+*index_html*, which does not necessarily need to be a method itself. If it
+isn't a callable, the default method of the object *index_html* is called on
+*index_html*. This is analogous to how an *index.html* file provides a default
+view for a directory in Apache and other web servers. Instead of explicitly
+including the name *index_html* in your URL to show default content for a
+Folder, you can omit it and still gain the same effect.
+
+For example, if you create an *index_html* object in your *Zoo* Folder, and
+view the folder by clicking the View tab or by visiting the URL::
+
+  http://localhost:8080/Zoo/
+
+Zope will call the *index_html* object in the *Zoo* folder and display its
+results. You could instead use the more explicit URL::
+
+  http://localhost:8080/Zoo/index_html
+
+and it will display the same content.
+
+A Folder can also *acquire* an *index_html* object from its parent Folders. You
+can use this behavior to create a default view for a set of Folders. To do so,
+create an *index_html* object in a Folder that contains another set of Folders.
+This default view will be used for all the Folders in the set. This behavior is
+already evident in Zope: if you create a set of empty Folders in the Zope root
+Folder, you may notice that when you view any of the Folders via a URL, the
+content of the "root" Folder's *index_html* method is displayed. The
+*index_html* in the root Folder is acquired. Furthermore, if you create more
+empty Folders inside the Folders you've just created in the root Folder, a
+visit to these Folders' URLs will also display the root Folder's *index_html*.
+This is acquisition at work.
+
+If you want a different default view of a given Folder, just create a custom
+*index_html* object in that particular Folder. This allows you to override the
+default view of a particular Folder on a case-by-case basis, while allowing
+other Folders defined at the same level to acquire a common default view.
+
+The *index_html* object may be a *Page Template*, a *Script (Python)* object, a
+*DTML Method* or any other Zope object that is URL-accessible and that returns
+browser-renderable content. The content is typically HTML, but Zope doesn't
+care. You can return XML, or text, or anything you like.
+
+Using Python-based Scripts
+==========================
+
+Now let us take a look at a basic method object: *Script (Python)*.
+
+The Python Language
++++++++++++++++++++
+
+`Python <http://www.python.org/>`_ is a high-level, object oriented scripting
+language. Most of Zope is written in Python. Many folks like Python because of
+its clarity, simplicity, and ability to scale to large projects.
+
+There are many resources available for learning Python. The python.org website
+has lots of Python documentation including a `tutorial
+<http://www.python.org/doc/current/tut/tut.html>`_ by Python's creator, Guido
+van Rossum.
+
+For people who have already some programming experience, `Dive Into Python
+<http://diveintopython.org/>`_ is a great online resource to learn python.
+
+Python comes with a rich set of modules and packages. You can find out more
+about the `Python standard library
+<http://www.python.org/doc/current/lib/lib.html>`_ at the python.org website.
+
+Creating Python-based Scripts
++++++++++++++++++++++++++++++
+
+To create a Python-based Script, select *Script (Python)* from the Add
+drop-down list. Name the script *hello*, and click the *Add and Edit* button.
+You should now see the *Edit* view of your script.
+
+This screen allows you to control the parameters and body of your script. You
+can enter your script's parameters in the *parameter list* field. Type the body
+of your script in the text area at the bottom of the screen.
+
+Enter:: 
+
+  name="World"
+
+into the *parameter list* field, and in the body of the script, type::
+
+  return "Hello %s." % name
+
+Our script is now equivalent to the following function definition in standard
+Python syntax::
+
+  def hello(name="World"):
+      return "Hello %s." % name
+
+The script should return a result similar to the following image:
+
+`Script editing view <img:9-2:Figures/8-5.png>`_
+
+You can now test the script by going to the *Test* tab, as shown in the
+following figure.
+
+`Testing a Script <img:9-3:Figures/8-6.png>`_
+
+Leave the *name* field blank, and click the *Run Script* button. Zope should
+return "Hello World." Now go back and try entering your name in the *Value*
+field, and clicking the *Run Script* button. Zope should now say "hello" to
+you.
+
+Since scripts are called on Zope objects, you can get access to Zope objects
+via the *context* variable. For example, this script returns the number of
+objects contained by a given Zope object::
+
+  ## Script (Python) "numberOfObjects"
+  ##
+  return len( context.objectIds() )
+
+Note that the lines at the top starting with a double hash (##) are generated
+by Zope when you view the script outside the *Edit* tab of the ZMI, e.g., by
+clicking the *view or download* link at the bottom of the *Edit* tab. We'll use
+this format for our examples.
+
+The script calls::
+
+  context.objectIds()
+
+a method in the Zope API, to get a list of the contained objects. *objectIds*
+is a method of *Folders*, so the context object should be a Folder-like object.
+The script then calls::
+
+  len()
+
+to find the number of items in that list. When you call this script on a given
+Zope object, the *context* variable is bound to the context object. So, if you
+called this script by visiting the URL::
+
+  FolderA/FolderB/numberOfObjects
+
+the *context* parameter would refer to the `FolderB` object.
+
+When writing your logic in Python, you'll typically want to query Zope objects,
+call other scripts, and return reports. Suppose you want to implement a simple
+workflow system, in which various Zope objects are tagged with properties that
+indicate their status. You might want to produce reports that summarize which
+objects are in which state. You can use Python to query objects and test their
+properties. For example, here is a script named::
+
+  objectsForStatus
+
+with one parameter, 'status'::
+
+  ## Script (Python) "objectsForStatus"
+  ##parameters=status
+  ##
+  """ Returns all sub-objects that have a given status property.
+  """
+  results=[]
+  for object in context.objectValues():
+      if object.getProperty('status') == status:
+          results.append(object)
+  return results
+
+This script loops through an object's sub-objects, and returns all the
+sub-objects that have a::
+
+  status
+
+property with a given value.
+
+Accessing the HTTP Request
+++++++++++++++++++++++++++
+
+What if we need to get user input, e.g., values from a form? We can find the
+REQUEST object, which represents a Zope web request, in the context. For
+example, if we visited our *feed* script via the URL::
+
+  Zoo/LargeAnimals/hippo/feed?food_type=spam
+
+we could access the:: 
+
+  food_type
+
+variable as::
+
+  context.REQUEST.food_type
+
+This same technique works with variables passed from forms.
+
+Another way to get the REQUEST is to pass it as a parameter to the script. If
+REQUEST is one of the script's parameters, Zope will automatically pass the
+HTTP request and assign it to this parameter. We could then access the::
+
+  food_type
+
+variable as::
+
+  REQUEST.food_type
+
+String Processing in Python
++++++++++++++++++++++++++++
+
+One common use for scripts is to do string processing. Python has a number of
+standard modules for string processing. Due to security restrictions, you
+cannot do regular expression processing within Python-based Scripts. If you
+really need regular expressions, you can easily use them in External Methods,
+described in a subsequent chapter. However, in a Script (Python) object, you do
+have access to string methods.
+
+Suppose you want to change all the occurrences of a given word in a text file.
+Here is a script, *replaceWord*, that accepts two arguments: *word* and
+*replacement*. This will change all the occurrences of a given word in a
+File::
+
+  ## Script (Python) "replaceWord"
+  ##parameters=word, replacement
+  ##
+  """ Replaces all the occurrences of a word with a replacement word in
+  the source text of a text file. Call this script on a text file to use
+  it.
+
+  Note: you will need permission to edit the file in order to call this
+  script on the *File* object.  This script assumes that the context is
+  a *File* object, which provides 'data', 'title', 'content_type' and
+  the manage_edit() method.
+  """
+  text = context.data
+  text = text.replace(word, replacement)
+  context.manage_edit(context.title, context.content_type, filedata=text)
+
+You can call this script from the web on a text *File* in order to change the
+text. For example, the URL::
+
+  Swamp/replaceWord?word=Alligator&replacement=Crocodile
+
+would call the *replaceWord* script on the text *File* named::
+
+  Swamp
+
+and would replace all occurrences of the word::
+
+  Alligator
+
+with::
+
+  Crocodile
+
+See the Python documentation for more information about manipulating strings
+from Python.
+
+One thing that you might be tempted to do with scripts is to use Python to
+search for objects that contain a given word within their text or as a
+property. You can do this, but Zope has a much better facility for this kind of
+work: the *Catalog*. See the chapter entitled `Searching and Categorizing
+Content <SearchingZCatalog.stx>`_ for more information on searching with
+Catalogs.
+
+Print Statement Support
++++++++++++++++++++++++
+
+Python-based Scripts have a special facility to help you print information.
+Normally, printed data is sent to standard output and is displayed on the
+console. This is not practical for a server application like Zope, since the
+service does not always have access to the server's console. Scripts allow you
+to use print anyway, and to retrieve what you printed with the special variable
+*printed*. For example::
+
+  ## Script (Python) "printExample"
+  ##
+  for word in ('Zope', 'on', 'a', 'rope'):
+      print word
+  return printed
+
+This script will return::
+
+  Zope
+  on
+  a
+  rope
+
+The reason that there is a line break in between each word is that Python adds
+a new line after every string that is printed.
+
+You might want to use the::
+
+  print
+
+statement to perform simple debugging in your scripts. For more complex output
+control, you probably should manage things yourself by accumulating data,
+modifying it, and returning it manually, rather than relying on the::
+
+  print
+
+statement. And for controlling presentation, you should return the script
+output to a Page Template or DTML page, which then displays the return value
+appropriately.
+
+Built-in Functions
+++++++++++++++++++
+
+Python-based Scripts give you a slightly different menu of built-ins than you'd
+find in normal Python. Most of the changes are designed to keep you from
+performing unsafe actions. For example, the *open* function is not available,
+which keeps you from being able to access the file system. To partially make up
+for some missing built-ins, a few extra functions are available.
+
+The following restricted built-ins work the same as standard Python built-ins:
+*None*, *abs*, *apply*, *callable*, *chr*, *cmp*, *complex*, *delattr*,
+*divmod*, *filter*, *float*, *getattr*, *hash*, *hex*, *int*, *isinstance*,
+*issubclass*, *list*, *len*, *long*, *map*, *max*, *min*, *oct*, *ord*, *repr*,
+*round*, *setattr*, *str*, and *tuple*. For more information on what these
+built-ins do, see the online `Python Documentation
+<http://www.python.org/doc/>`_.
+
+The *range* and *pow* functions are available and work the same way they do in
+standard Python; however, they are limited to keep them from generating very
+large numbers and sequences. This limitation helps protect against
+denial-of-service attacks.
+
+In addition, these DTML utility functions are available: *DateTime* and *test*.
+See Appendix A, `DTML Reference <AppendixA.stx>`_ for more information on these
+functions.
+
+Finally, to make up for the lack of a *type* function, there is a *same_type*
+function that compares the type of two or more objects, returning *true* if
+they are of the same type. So, instead of saying::
+
+  if type(foo) == type([]):
+      return "foo is a list"
+
+... to check if::
+
+  foo
+
+is a list, you would instead use the *same_type* function::
+
+  if same_type(foo, []):
+      return "foo is a list"
+
+Calling ZPT from Scripts
+========================
+
+Often, you would want to call a *Page Template* from a Script. For instance, a
+common pattern is to call a Script from an HTML form. The Script would process
+user input, and return an output page with feedback messages - telling the user
+her request executed correctly, or signalling an error as appropriate.
+
+Scripts are good at logic and general computational tasks but ill-suited for
+generating HTML. Therefore, it makes sense to delegate the user feedback output
+to a *Page Template* and call it from the Script. Assume we have this Page
+Template with the *id* 'hello_world_pt'::
+
+  <p>Hello <span tal:replace="options/name | default">World</span>!</p>
+
+You will learn more about *Page Templates* in the next chapter. For now, just
+understand that this *Page Template* generates an HTML page based on the value
+*name*. Calling this template from a Script and returning the result could be
+done with the following line::
+
+  return context.hello_world_pt(name="John Doe")
+
+The *name* parameter to the Page Template ends up in the::
+
+  options/name
+
+path expression. So the returned HTML will be::
+
+  <p>Hello John Doe!</p>
+
+Note that::
+
+  context.hello_world_pt
+
+works because there is no dot in the id of the template. In Python, dots are
+used to separate ids. This is the reason why Zope often uses ids like::
+
+  index_html
+
+instead of the more common::
+
+  index.html
+
+and why this example uses::
+
+  hello_world_pt
+
+instead of::
+
+  hello_world.pt
+
+However, if desired, you can use dots within object ids. Using *getattr* to
+access the dotted id, the modified line would look like this::
+
+  return getattr(context, 'hello_world.pt')(name="John Doe")
+
+Returning Values from Scripts
+=============================
+
+Scripts have their own variable scope. In this respect, scripts in Zope behave
+just like functions, procedures, or methods in most programming languages. If
+you name a script *updateInfo*, for example, and *updateInfo* assigns a value
+to a variable *status*, then *status* is local to your script: it gets cleared
+once the script returns. To get at the value of a script variable, we must pass
+it back to the caller with a *return* statement.
+
+Scripts can only return a single object. If you need to return more than one
+value, put them in a dictionary and pass that back.
+
+Suppose you have a Python script *compute_diets*, out of which you want to get
+values::
+
+  ## Script (Python) "compute_diets"
+  d = {'fat': 10,
+       'protein': 20,
+       'carbohydrate': 40,
+  }
+  return d
+
+The values would, of course, be calculated in a real application; in this
+simple example, we've simply hard-coded some numbers.
+
+You could call this script from ZPT like this::
+
+  <p tal:repeat="diet context/compute_diets">
+      This animal needs
+      <span tal:replace="diet/fat" />kg fat,
+      <span tal:replace="diet/protein" />kg protein, and
+      <span tal:replace="diet/carbohydrate" />kg carbohydrates.
+  </p>
+
+More on ZPT in the next chapter.
+
+The Zope API
+============
+
+One of the main reasons to script in Zope is to get convenient access to the
+Zope Application Programmer Interface (API). The Zope API describes built-in
+actions that can be called on Zope objects. You can examine the Zope API in the
+help system, as shown in the figure below.
+
+`Zope API Documentation <img:9-4:Figures/8-4.png>`_
+
+Suppose you would like a script that takes a file you upload from a form, and
+creates a Zope File object in a Folder. To do this, you'd need to know a number
+of Zope API actions. It's easy enough to read files in Python, but once you
+have the file, you must know which actions to call in order to create a new
+File object in a Folder.
+
+There are many other things that you might like to script using the Zope API:
+any management task that you can perform through the web can be scripted using
+the Zope API, including creating, modifying, and deleting Zope objects. You can
+even perform maintenance tasks, like restarting Zope and packing the Zope
+database.
+
+The Zope API is documented in Appendix B, `API Reference <AppendixB.stx>`_, as
+well as in the Zope online help. The API documentation shows you which classes
+inherit from which other classes. For example, *Folder* inherits from
+*ObjectManager*, which means that Folder objects have all the methods listed in
+the *ObjectManager* section of the API reference.
+
+To get you started and whet your appetite, we will go through some example
+Python scripts that demonstrate how you can use the Zope API:
+
+Get all objects in a Folder
++++++++++++++++++++++++++++
+
+The::
+
+  objectValues()
+
+method returns a list of objects contained in a Folder. If the context happens
+not to be a Folder, nothing is returned::
+
+  objs = context.objectValues()
+
+Get the id of an object
++++++++++++++++++++++++
+
+The id is the "handle" to access an object, and is set at object creation::
+
+  id = context.getId()
+
+Note that there is no *setId()* method: you have to either use the ZMI to
+rename them, set their::
+
+  id
+
+attribute via security-unrestricted code, or use the::
+
+  manage_renameObject
+
+or::
+
+  manage_renameObjects
+
+API methods exposed upon the container of the object you want to rename.
+
+Get the Zope root Folder
+++++++++++++++++++++++++
+
+The root Folder is the top level element in the Zope object database::
+
+  root = context.getPhysicalRoot()
+
+Get the physical path to an object
+++++++++++++++++++++++++++++++++++
+
+The::
+
+  getPhysicalPath()
+
+method returns a list containing the ids of the object's containment
+hierarchy::
+
+  path_list = context.getPhysicalPath()
+  path_string = "/".join(path_list)
+
+Get an object by path
++++++++++++++++++++++
+
+restrictedTraverse() is the complement to::
+
+  getPhysicalPath()
+
+The path can be absolute -- starting at the Zope root -- or relative to the
+context::
+
+  path = "/Zoo/LargeAnimals/hippo"
+  hippo_obj = context.restrictedTraverse(path)
+
+Get a property
+++++++++++++++
+
+getProperty()
+
+returns a property of an object. Many objects support properties (those that
+are derived from the PropertyManager class), the most notable exception being
+DTML Methods, which do not::
+
+  pattern = context.getProperty('pattern')
+  return pattern
+
+Change properties of an object
+++++++++++++++++++++++++++++++
+
+The object has to support properties, and the property must exist::
+
+  values = {'pattern' : 'spotted'}
+  context.manage_changeProperties(values)
+
+Traverse to an object and add a new property
+++++++++++++++++++++++++++++++++++++++++++++
+
+We get an object by its absolute path, add a property::
+
+  weight
+
+and set it to some value. Again, the object must support properties in order
+for this to work::
+
+  path = "/Zoo/LargeAnimals/hippo"
+  hippo_obj = context.restrictedTraverse(path)
+  hippo_obj.manage_addProperty('weight', 500, 'int')


Property changes on: zope2book/trunk/source/BasicScripting.rst
___________________________________________________________________
Added: svn:eol-style
   + native

Added: zope2book/trunk/source/Sessions.rst
===================================================================
--- zope2book/trunk/source/Sessions.rst	                        (rev 0)
+++ zope2book/trunk/source/Sessions.rst	2009-02-16 20:22:58 UTC (rev 96604)
@@ -0,0 +1,1435 @@
+Session Management
+##################
+
+This chapter describes Zope's built-in Session Management.
+
+Terminology
+===========
+
+Here's a mini-glossary of of key terms used within this document:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Web session
+  a series of HTTP requests from the same browser to the same server during
+  that browser's execution life-span.
+
+Browser Id
+  the string or integer used to represent a single anonymous visitor to the
+  part of the Zope site managed by a single browser id manager. E.g.
+  "12083789728".
+
+Browser Id Name
+  the name which is looked for in places enumerated by the currently configured
+  browser id namespaces. E.g. "_ZopeId".
+
+Browser Id Namespaces
+  the browser id name will be found in one of three possible places
+  ("namespaces"): in form elements and/or query strings (aka "form"), in a
+  cookie, or in the URL.
+
+Session Data Object
+  an transient data object that is found by asking a session data container for
+  the item with a key that is the current browser id value.
+
+Session Id
+  the identifier for a session data object. This is different than the browser
+  id. Instead of representing a single - *visitor*- , it represents a single -
+  *visit*- .
+
+Session Managers
+================
+
+Web browsers communicate with Web Servers using HTTP. HTTP does not provide
+tools that can track users and data in the context of a web session. Zope's
+session management works-around the problem: it provides methods able to track
+site visitor activity. Applications like "shopping carts" use session
+management for this reason.
+
+Zope's session management makes use of name-spaces like cookies, HTTP form
+elements, and/or parts of URLs "in the background" to keep track of user
+sessions. Which of these name-spaces are used is configurable using the
+browser_id manager (described later).
+
+Session data is valid for the duration of a **configurable inactivity** timeout
+value or browser shut-down, which ever comes first. Zope's session management
+keeps track of anonymous users as well as those who have Zope login accounts.
+
+Important! Data maintained by Zope's session management is no more secure than
+HTTP itself. A session is secure if and only if:
+
+- the connection between a browser and Zope uses strong encryption (SSL
+  normally).
+
+- precautions specific to the security exposure are taken.
+
+It's clear that you should not store sensitive information like credit card
+numbers in a session container unless you understand the vulnerabilities. See
+the section entitled *Security Considerations* near the end of this document.
+
+It is advisable to use sessions only on pages where they are necessary because
+of a performance impact on your application. The severity varies depending on
+usage and configuration. A good "rule of thumb" is to account for a 5% - 10%
+speed-of-execution penalty.
+
+Some hints:
+
+- Do not use SESSION to store REQUEST variables. They are already available in
+  the REQUEST.
+
+- Do not store any data in SESSION that you can get from the Zope API. Its
+  faster (and more secure) to get user Id from Zope's Security Manager then it
+  is from the SESSION object.
+
+Session Manager Components
+==========================
+
+- **Browser Id Manager**
+  This component determines a remote client's "browser id", which uniquely
+  identifies a particular browser. The browser id is encoded in a
+  form/querystring variable, a cookie variable, or as part of the URL. The
+  browser id manager examines cookies, form and querystring elements, and URLs
+  to determine the client's browser id. It can also modify cookies and URLs
+  automatically in order to differentiate users between requests.
+
+- There may be more than one browser id manager in a Zope installation, but
+  commonly there will only be one. Application developers will generally not
+  talk directly to a browser id manager. Instead, they will use the Transient
+  Data Object (REQUEST.SESSION) which delegates some calls to a browser_id
+  manager.
+
+- Browser id managers have "fixed" Zope ids so they can be found via
+  acquisition by session data managers. Browser id managers also have
+  interfaces for encoding a URL with browser id information and performing
+  other utility functions.
+
+- The default sessioning configuration provides a Browser Id Manager as the::
+
+    /browser_id_manager object
+
+- **Session Data Manager**
+  This component is responsible for handing out session data to callers. When
+  session data is required, the session data manager:
+
+  * talks to a browser id manager to determine the current browser id-
+
+  * creates a new session data object or hands back an existing session data
+    object based on the browser id.
+
+- Developers generally do not directly use methods of session data managers to
+  obtain session data objects. Instead, they rely on the built-in
+  REQUEST.SESSION object, which represents *the current session data object
+  related to the user's browser id*.
+
+- The session data object has an identifier distinct from the browser id. This
+  identifier represents a single user session with the server (unlike the
+  browser id, which represents a single browser). Many session data managers
+  can use one browser id manager. Many session data managers can be
+  instantiated on a single Zope installation. Different session data managers
+  can implement different policies related to session data object storage (e.g.
+  to which session data container the session data objects are stored).
+
+- The default sessioning configuration provides a Session Data Manager named::
+
+    /session_data_manager
+
+- **Transient Object Container**
+  Also known as Session Data Containers, these components actually hold
+  information related to sessions.
+
+- Currently, a Transient Object Container is used to hold a special "transient
+  data object" instance for each ongoing session. Developers will generally not
+  interact with transient data containers. Transient data containers are
+
+- responsible for expiring the session data objects which live within them.
+
+- The default sessioning configuration provides a Transient Object Container
+  named::
+
+    /temp_folder/session_data
+
+  The session data objects in the default::
+
+    session_data
+
+  Transient Object container are lost each time Zope is restarted.
+
+- **Transient Data Object**
+  Also known as the Session Data Object. These are the objects which are stored
+  in session data containers and managed by transient data managers.
+
+- Developers interact with a transient data object after obtaining one via
+  REQUEST.SESSION or from a session data manager directly. A single transient
+  data object actually stores the useful information related to a single user's
+  session.
+
+- Transient data objects can be expired automatically by transient data
+  containers as a result of inactivity, or they can be manually invalidated in
+  the course of a script.
+
+Using Session Data
+==================
+
+You will typically access session data through the::
+
+  SESSION
+
+attribute of the REQUEST object. Session data objects are like Python
+dictionaries: they can hold almost any kind of object as a key or a value. It's
+likely you will almost always use "normal" Python objects such as lists,
+dictionaries, strings, and numbers.
+
+Here's an example of how to work with a session using a Python Script::
+
+  ## Script (Python) "sessionTest"
+  secs_per_day=24*60*60
+  session=context.REQUEST.SESSION
+  if session.has_key('last view'):
+      # The script has been viewed before, since the 'last view'
+      then=session['last view']
+      now=context.ZopeTime()
+      session['last view']=now # reset last view to now
+      return 'Seconds since last view %.2f' % ((now - then) * secs_per_day)
+
+  # The script hasn't been viewed before, since there's no 'last view' 
+  session['last view']=context.ZopeTime()
+  return 'This is your first view'
+
+This example shows how to access SESSION data. But it is not a "best practice"
+example. If performance is an issue, you should not attempt to keep
+last-accessed time in this manner in a production application because it might
+slow your application down dramatically and cause problems under high load.
+
+Create a script with this body named *sessionTest* in your root folder and
+then click its *Test* tab. While viewing the output, reload the frame a few
+times. Note that the script keeps track of when you last viewed it and
+calculates how long it has been since you last viewed it. Notice that if you
+quit your browser and come back to the script it forgets you were ever there.
+However, if you simply visit some other pages and then return within 20 minutes
+or so, it still remembers the last time you viewed it.
+
+See the *Concepts and Caveats* section at the end of this document for things
+to watch out for while accessing Zope's Session Manager "naively".
+
+You can use sessions in Page Templates and DTML Documents, too. For example,
+here's a template snippet that displays the users favorite color (as stored in
+a session)::
+
+  <p tal:content="request/SESSION/favorite_color">Blue</p>
+
+Sessions have additional configuration parameters and usage patterns detailed
+below.
+
+Default Configuration
+=====================
+
+Zope is preconfigured with a default sessioning setup.
+
+The Zope "default" browser id manager lives in the root folder and is named::
+
+  browser_id_manager
+
+The Zope "default" session data manager lives in the root folder and is named::
+
+  session_data_manager
+
+A "default" transient data container (session data container) is created as::
+
+  /temp_folder/session_data
+
+when Zope starts up. The::
+
+  temp_folder
+
+object is a "mounted, nonundoing" database that keeps information in RAM, so
+"out of the box", Zope stores session information in RAM. The temp folder is a
+"nonundoing" storage (meaning you cannot undo transactions which take place
+within it) because accesses to transient data containers are very
+write-intensive, and undoability adds unnecessary overhead.
+
+A transient data container stores transient data objects. The default
+implementation the transient data object shipped with Zope is engineered to
+reduce the potential inherent in the ZODB for "conflict errors" related to the
+ZODB's "optimistic concurrency" strategy.
+
+You needn't change any of these default options to use sessioning under Zope
+unless you want to customize your setup. However, if you have custom needs, can
+create your own session data managers, browser id managers, temporary folders,
+and transient object containers by choosing these items from Zope's "add" list
+in the place of your choosing.
+
+Advanced Development Using Sessioning
+=====================================
+
+Overview
+++++++++
+
+When you work with the REQUEST.SESSION object, you are working with a "session
+data object" that is related to the current site user.
+
+Session data objects have methods of their own, including methods with allow
+developers to get and set data. Session data objects are also "wrapped" in the
+acquisition context of their session data manager, so you may additionally call
+any method on a session data object that you can call on a session data
+manager. For information about the API of a session data manager and a session
+data object, see the Zope Help system item in "Zope Help" -> "API Reference" ->
+"Session API".
+
+Obtaining A Session Data Object
++++++++++++++++++++++++++++++++
+
+The session data object associated with the browser id in the current request
+may be obtained via REQUEST.SESSION. If a session data object does not exist in
+the session data container, one will be created automatically when you
+reference REQUEST.SESSION::
+
+  <dtml-let data="REQUEST.SESSION">
+      The 'data' name now refers to a new or existing session data object.
+  </dtml-let>
+
+You may also use the::
+
+  getSessionData()
+
+method of a session data manager to do the same thing::
+
+  <dtml-let data="session_data_manager.getSessionData()">
+      The 'data' name now refers to a new or existing session data object.
+  </dtml-let>
+
+A reference to REQUEST.SESSION or::
+
+  getSessionData()
+
+implicitly creates a new browser id if one doesn't exist in the current
+request. These mechanisms also create a new session data object in the session
+data container if one does not exist related to the browser id in the current
+request. To inhibit this behavior, use the create=0 flag to the::
+
+  getSessionData()
+
+method. In ZPT::
+
+<span tal:define="data python:context.session_data_manager.getSessionData(create=0)">
+
+Note: create=0 means return a reference to the session or None. create=1 means
+return a reference if one exists or create a new Session object and the
+reference.
+
+Modifying A Session Data Object
++++++++++++++++++++++++++++++++
+
+Once you've used REQUEST.SESSION or::
+
+  session_data_manager.getSessionData()
+
+to obtain a session data object, you can set key/value pairs of that session
+data object.
+
+In ZPT::
+
+  <span tal:define="data python: request.SESSION">
+      <tal:block define="temp python: data.set('foo','bar')">
+          <p tal:content="python: data.get('foo')">bar will print here"</p>
+      </tal:block>
+  </span>
+
+An essentially arbitrary set of key/value pairs can be placed into a session
+data object. Keys and values can be any kinds of Python objects (note: see
+*Concepts and Caveats* section below for exceptions to this rule). The session
+data container which houses the session data object determines its expiration
+policy. Session data objects will be available across client requests for as
+long as they are not expired.
+
+Clearing A Session Data Object
+++++++++++++++++++++++++++++++
+
+You can clear all keys and values from a SESSION object by simply calling its
+clear() method.
+
+In ZPT::
+
+  <span tal:define="dummy python:request.SESSION.clear()"></span>
+
+Manually Invalidating A Session Data Object
++++++++++++++++++++++++++++++++++++++++++++
+
+Developers can manually invalidate a session data object. When a session data
+object is invalidated, it will be flushed from the system.
+
+There is a caveat. If you invalidate the session object in a script then you
+**must** obtain a fresh copy of the session object by calling getSessionData
+and not by reference (REQUEST.SESSION).
+
+Here is an example using DTML:::
+
+  <!-- set a SESSION key and value -->
+  <dtml-let data="REQUEST.SESSION">
+  <dtml-call "data.set('foo','bar')      
+
+  <!-- Now invalidate the SESSION -->
+  <dtml-call "data.invalidate()">
+
+  <!-- But REQUEST.SESSION gives us stale data which is bad.
+  The next statement will still show 'foo' and 'bar'
+  <dtml-var "REQUEST.SESSION>
+
+  <!-- Heres the work-around: -->
+  data = session_data_manager.getSessionData()
+
+  <!-- Now we get a fresh copy and life is good as 'foo' and 'bar' have gone away as expected -->
+  <dtml-var data>
+
+Manual invalidation of session data is useful when you need a "fresh" copy of a
+session data object.
+
+If an "onDelete" event is defined for a session data object, the onDelete
+method will be called before the data object is invalidated. See a following
+section for information about session data object "onDelete" and "onAdd"
+events.
+
+Manually Invalidating A Browser Id Cookie
++++++++++++++++++++++++++++++++++++++++++
+
+Invalidating a session data object does not invalidate the browser id cookie
+stored on the user's browser. Developers may manually invalidate the cookie
+associated with the browser id. To do so, they can use the::
+
+  flushBrowserIdCookie()
+
+method of a browser id manager. For example::
+
+  <dtml-call "REQUEST.SESSION.getBrowserIdManager().flushBrowserIdCookie()">
+
+If the::
+
+  cookies
+
+namespace isn't a valid browser id key namespace when this call is performed,
+an exception will be raised.
+
+Using Session Data with TAL
++++++++++++++++++++++++++++
+
+Here's an example of using the session data object with TAL::
+
+  <span tal:define="a python:request.SESSION;
+                    dummy python:a.set('zopetime',context.ZopeTime())">
+      <p tal:content="python: a.get('zopetime')"></p>
+  </span>
+
+Using Session Data From Python
+++++++++++++++++++++++++++++++
+
+Here's an example of using a session data manager and session data object from
+a set of Python external methods::
+
+  import time
+  def setCurrentTime(self):
+      a = self.REQUEST.SESSION
+      a.set('thetime', time.time())
+
+  def getLastTime(self):
+      a = self.REQUEST.SESSION
+      return a.get('thetime')
+
+Calling the setCurrentTime will set the value of the current session's
+"thetime" key to an integer representation of the current time. Calling the
+getLastTime external method will return the integer representation of the last
+known value of "thetime".
+
+Interacting with Browser Id Data
+++++++++++++++++++++++++++++++++
+
+You can obtain the browser id value associated with the current request::
+
+  <dtml-var "REQUEST.SESSION.getBrowserIdManager().getBrowserId()">
+
+Another way of doing this, which returns the same value is::
+
+  <dtml-var "REQUEST.SESSION.getContainerKey()">
+
+If no browser id exists for the current request, a new browser id is created
+implicitly and returned.
+
+If you wish to obtain the current browser id value without implicitly creating
+a new browser id for the current request, you can ask the browser_id_manager
+object explicitly for this value with the `create=0` parameter::
+
+  <dtml-var "browser_id_manager.getBrowserId(create=0)">
+
+This snippet will print a representation of the None value if there isn't a
+browser id associated with the current request, or it will print the browser id
+value if there is one associated with the current request. Using `create=0` is
+useful if you do not wish to cause the sessioning machinery to attach a new
+browser id to the current request, perhaps if you do not wish a browser id
+cookie to be set.
+
+The browser id is either a string or an integer and has no business meaning. In
+your code, you should not rely on the browser id value composition, length, or
+type as a result, as it is subject to change.
+
+Determining Which Namespace Holds The Browser Id
+++++++++++++++++++++++++++++++++++++++++++++++++
+
+For some applications, it is advantageous to know from which namespace (
+"cookies", "form", or "url") the browser id has been gathered.
+
+It should be noted that you can configure the browser_id_manager (its in Zope
+root by default) so that it searches whatever combination of namespaces you
+select.
+
+There are three methods of browser id managers which allow you to accomplish
+this::
+
+  <dtml-if "REQUEST.SESSION.getBrowserIdManager().isBrowserIdFromCookie()">
+      The browser id came from a cookie.
+  </dtml-if>
+
+  <dtml-if "REQUEST.SESSION.getBrowserIdManager().isBrowserIdFromForm()">
+      The browser id came from a form.
+  </dtml-if>
+
+  <dtml-if "REQUEST.SESSION.getBrowserIdManager().isBrowserIdFromUrl()">
+      The browser id came from the URL.
+  </dtml-if>
+
+The::
+
+  isBrowserIdFromCookie()
+
+method will return true if the browser id in the current request comes from
+the::
+
+  REQUEST.cookies
+
+namespace. This is true if the browser id was sent to the Zope server as a
+cookie.
+
+The::
+
+  isBrowserIdFromForm()
+
+method will return true if the browser id in the current request comes from
+the::
+
+  REQUEST.form
+
+namespace. This is true if the browser id was sent to the Zope server encoded
+in a query string or as part of a form element.
+
+The::
+
+  isBrowserIdFromUrl()
+
+method will return true if the browser id in the current request comes from the
+leading elements of the URL.
+
+If a browser id doesn't actually exist in the current request when one of these
+methods is called, an error will be raised.
+
+During typical operations, you shouldn't need to use these methods, as you
+shouldn't care from which namespace the browser id was obtained. However, for
+highly customized applications, this set of methods may be useful.
+
+Obtaining the Browser Id Name/Value Pair and Embedding It Into A Form
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+You can obtain the browser id name from a browser id manager instance. We've
+already determined how to obtain the browser id itself. It is useful to also
+obtain the browser id name if you wish to embed a browser id name/value pair as
+a hidden form field for use in POST requests. Here's a TAL example::
+
+    <span tal:define="idManager python:request.SESSION.getBrowserIdManager()">
+        <form action="thenextmethod">
+            <input type=submit name="submit" value=" GO ">
+            <input type="hidden" name="name" value="value"
+                   tal:attributes="name python: idManager.getBrowserIdName();
+                                   value python: idManager.getBrowserId()">
+        </form>
+    </span>
+
+A convenience function exists for performing this action as a method of a
+browser id manager named "getHiddenFormField"::
+
+  <html>
+  <body>
+    <form action="thenextmethod">
+      <input type="submit" name="submit" value=" GO ">
+      <dtml-var "REQUEST.SESSION.getBrowserIdManager().getHiddenFormField()">
+    </form>
+  </body>
+  </html>
+
+When the above snippets are rendered, the resulting HTML will look something
+like this::
+
+  <html>
+  <body>
+    <form action="thenextmethod">
+      <input type="submit" name="submit" value=" GO ">
+      <input type="hidden" name="_ZopeId" value="9as09a7fs70y1j2hd7at8g">
+    </form>
+  </body>
+  </html>
+
+Note that to maintain state across requests when using a form submission, even
+if you've got
+
+- Automatically Encode
+- Zope-Generated URLs With a Browser Id
+
+checked off in your browser id manager, you'll either need to encode the form
+"action" URL with a browser id (see "Embedding A Browser Id Into An HTML Link"
+below) or embed a hidden form field.
+
+Using formvar-based sessioning.
++++++++++++++++++++++++++++++++
+
+To use formvar-based sessioning, you need to encode a link to its URL with the
+browser id by using the browser id manager's::
+
+  encodeUrl()
+
+method.
+
+Determining Whether A Browser Id is "New"
++++++++++++++++++++++++++++++++++++++++++
+
+A browser id is "new" if it has been set in the current request but has not yet
+been acknowledged by the client. "Not acknowledged by the client" means it has
+not been sent back by the client in a request. This is the case when a new
+browser id is created by the sessioning machinery due to a reference to
+REQUEST.SESSION or similar as opposed to being received by the sessioning
+machinery in a browser id name namespace. You can use the::
+
+  isBrowserIdNew()
+
+method of a browser id manager to determine whether the session is new::
+
+  <dtml-if "REQUEST.SESSION.getBrowserIdManager().isBrowserIdNew()">
+      Browser id is new.
+  <dtml-else>
+      Browser id is not new.
+  </dtml-if>
+
+This method may be useful in cases where applications wish to prevent or detect
+the regeneration of new browser ids when the same client visits repeatedly
+without sending back a browser id in the request (such as may be the case when
+a visitor has cookies "turned off" in their browser and the browser id manager
+only uses cookies).
+
+If there is no browser id associated with the current request, this method will
+raise an error.
+
+You shouldn't need to use this method during typical operations, but it may be
+useful in advanced applications.
+
+
+Determining Whether A Session Data Object Exists For The Browser Id Associated With This Request
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+If you wish to determine whether a session data object with a key that is the
+current request's browser id exists in the session data manager's associated
+session data container, you can use the::
+
+  hasSessionData()
+
+method of the session data manager. This method returns true if there is
+session data associated with the current browser id::
+
+  <dtml-if "session_data_manager.hasSessionData()">
+    The sessiondatamanager object has session data for the browser id
+    associated with this request.
+  <dtml-else>
+    The sessiondatamanager object does not have session data for
+    the browser id associated with this request.
+  </dtml-if>
+
+The::
+
+  hasSessionData()
+
+method is useful in highly customized applications, but is probably less useful
+otherwise. It is recommended that you use REQUEST.SESSION instead, allowing the
+session data manager to determine whether or not to create a new data object
+for the current request.
+
+Embedding A Browser Id Into An HTML Link
+++++++++++++++++++++++++++++++++++++++++
+
+You can embed the browser id name/value pair into an HTML link for use during
+HTTP GET requests. When a user clicks on a link with a URL encoded with the
+browser id, the browser id will be passed back to the server in the
+REQUEST.form namespace. If you wish to use formvar-based session tracking, you
+will need to encode all of your "public" HTML links this way. You can use the::
+
+  encodeUrl()
+
+method of browser id managers in order to perform this encoding::
+
+  <html>
+  <body>
+    <a href="<dtml-var "REQUEST.SESSION.getBrowserIdManager().encodeUrl('/amethod')">">
+      Here
+    </a>
+    is a link.
+  </body>
+  </html>
+
+The above dtml snippet will encode the URL "/amethod" (the target of the word
+"Here") with the browser id name/value pair appended as a query string. The
+rendered output of this DTML snippet would look something like this::
+
+  <html>
+  <body>
+    <a href="/amethod?_ZopeId=7HJhy78978979JHK">Here</a>
+    is a link.
+  </body>
+  </html>
+
+You may successfully pass URLs which already contain query strings to the::
+
+  encodeUrl()
+
+method. The encodeUrl method will preserve the existing query string and append
+its own name/value pair.
+
+You may choose to encode the browser id into the URL using an "inline" style if
+you're checking for browser ids in the URL (e.g. if you've checked::
+
+  URLs
+
+in the "Look for Browser Id in" form element of your browser id manager)::
+
+  <html>
+  <body>
+    <a href="<dtml-var "REQUEST.SESSION.getBrowserIdManager().encodeUrl('/amethod', style='inline')">">Here</a>
+    is a link.
+  </body>
+  </html>
+
+The above dtml snippet will encode the URL "/amethod" (the target of the word
+"Here") with the browser id name/value pair embedded as the first two elements
+of the URL itself. The rendered output of this DTML snippet would look
+something like this::
+
+  <html>
+  <body>
+    <a href="/_ZopeId/7HJhy78978979JHK/amethod">Here</a>
+    is a link.
+  </body>
+  </html>
+
+Using Session onAdd and onDelete Events
++++++++++++++++++++++++++++++++++++++++
+
+The configuration of a Transient Object Container (aka a session data
+container) allows a method to be called when a session data object is created
+(onAdd) or when it is invalidated or timed out (onDelete).
+
+The events are independent of each other. You might want an onAdd method but
+not an onDelete method. You may define one, both or none of the TOC event
+methods.
+
+Here are examples of the kinds of things Session onAdd and onDelete methods are
+used to do:
+
+- The onAdd method can be used to populate a session data object with "default"
+  values before it's used by application code.
+
+- The onDelete method can write the contents of a session data object out to a
+  permanent data store before it is timed out or invalidated.
+
+You can manually configure the onAdd and onDelete methods. Click the
+*management tab* of '\temp_folder\session_data. Enter "a physical path" to
+either a an external method or python script. NOTE: This configuration is only
+good until the next Zope shutdown because::
+
+  \temp_folder\session_data
+
+is in a RAM database, Configure the onAdd and onDelete methods for this data
+container via the::
+
+  zope.conf
+
+configuration file for your Zope instance. This is covered in some detail in
+*Setting Initial Transient Object Container Parameters* later in this document.
+
+Note: the onAdd and onDelete events do not raise exceptions if logic in the
+method code fails. Instead, an error is logged in the Zope event log. In recent
+versions of Zope, the event.log defaults to Zope-Instance/log/event.log. This
+is configurable in::
+
+  zope.conf
+
+Writing onAdd and onDelete Methods
+++++++++++++++++++++++++++++++++++
+
+Session data objects optionally call a Zope method when they are created and
+when they are timed out or invalidated.
+
+Specially-written Script (Python) scripts can be written to serve the purpose
+of being called on session data object creation and invalidation.
+
+The Script (Python) should define two arguments, "sdo" and "toc". "sdo"
+represents the session data object being created or terminated, and "toc"
+represents the transient object container in which this object is stored.
+
+For example, to create a method to handle a session data object onAdd event
+which prepopulates the session data object with a DateTime object, you might
+write a Script (Python) named::
+
+  onAdd
+
+which had function parameters "sdo" and "toc" and a body of::
+
+  sdo['date'] = context.ZopeTime()
+
+If you set the path to this method as the onAdd event, before any application
+handles the new session data object, it will be prepopulated with a key::
+
+  date
+
+that has the value of a DateTime object set to the current time.
+
+To create a method to handle a session onDelete event which writes a log
+message, you might write an External Method with the following body::
+
+  from zLOG import LOG, WARNING
+  def onDelete(sdo, toc):
+      logged_out = sdo.get('logged_out', None)
+      if logged_out is None:
+          LOG('session end', WARNING,
+              'session ended without user logging out!')
+
+If you set the path to this method as the onDelete event, a message will be
+logged if the::
+
+  logged_out
+
+key is not found in the session data object.
+
+Note that for onDelete events, there is no guarantee that the onDelete event
+will be called in the context of the user who originated the session! Due to
+the "expire-after-so-many-minutes-of-inactivity" behavior of session data
+containers, a session data object onDelete event initiated by one user may be
+called while a completely different user is visiting the application. Your
+onDelete event method should not naively make any assumptions about user state.
+For example, the result of the Zope call "getSecurityManager().getUser()" in an
+onDelete session event method will almost surely *not* be the user who
+originated the session.
+
+The session data object onAdd method will always be called in the context of
+the user who starts the session.
+
+For both onAdd and onDelete events, it is almost always desirable to set proxy
+roles on event methods to replace the roles granted to the executing user when
+the method is called because the executing user will likely not be the user for
+whom the session data object was generated. For more information about proxy
+roles, see the chapter entitled `Users and Security <Security.stx>`_.
+
+For additional information about using session onDelete events in combination
+with data object timeouts, see the section entitled "Session Data Object
+Expiration Considerations" in the Concepts and Caveats section below.
+
+
+Configuration and Operation
+===========================
+
+Setting the default Transient Object Container Parameters
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Click on::
+
+  /temp_folder/session_data
+
+and you'll see options to control inactivity time-outs and the maximum
+allowable number of Session objects. You can even include paths to python
+scripts that handle a Session's after-add and before-delete events.
+
+Because::
+
+  /temp_folder/session_data
+
+is stored in a RAM database, it disappears and is recreated after each restart
+of your Zope server. This means that any changes to parameters will be lost the
+next time you restart your Zope server.
+
+If you need to permanently alter the default Transient Object Container's
+configuration you must edit Zope's startup configuration file::
+
+  zope.conf
+
+Note that additional Transient Object Containers can be instantiated in
+permanent storage. They are rarely needed. If you do need this its covered in
+detail later in this document.
+
+Here is the relevant portion of zope.conf::
+
+  # Directive: maximum-number-of-session-objects
+  # Description: An integer value representing the maximum number 
+  #              of subobjects" 
+  # allowable in the '/temp_folder/session_data' transient object container.
+  #         
+  # Default: 1000
+  # Example: maximum-number-of-session-objects 10000
+
+  # Directive: session-add-notify-script-path
+  #
+  # Description:
+  #     An optional fill Zope path name of a callable object to be set as the
+  #     "script to call on object addition" of the session_data transient
+  #     object container created in the /temp_folder folder at startup.
+  #
+  # Default: unset
+  # Example: session-add-notify-script-path /scripts/add_notifier
+
+  # Directive: session-delete-notify-script-path
+  #
+
+  # Description:
+  #     An optional fill Zope path name of a callable object to be set as the
+  #     "script to call on object deletion" of the session_data transient
+  #     object container created in the /temp_folder folder at startup.
+  #
+  # Default: unset
+  # Example: session-delete-notify-script-path /scripts/del_notifier
+
+  # Directive: session-timeout-minutes
+  #
+  # Description:
+  #     An integer value representing the number of minutes to be used as the
+  #     "data object timeout" of the '/temp_folder/session_data' transient
+  #     object container.
+  #
+  # Default: 20
+  # Example: session-timeout-minutes 30
+
+  # Directive: session-resolution-seconds
+  #
+  # Description:
+  #    An integer value representing the number of seconds to be used as the
+  #    "timeout resolution" of the '/temp_folder/session_data' transient
+  #    object container.
+  #
+  # Default: 20
+  # Example: session-resolution-seconds 60
+
+Instantiating Multiple Browser Id Managers (Optional)
++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Transient data objects depend on a session data manager, which in turn depends
+on a browser id manager. A browser id manager doles out and otherwise manages
+browser ids. All session data managers need to talk to a browser id manager to
+get browser id information.
+
+You needn't create a browser id manager to use sessioning. One is already
+created as a result of the initial Zope installation. If you've got special
+needs, you may want to instantiate more than one browser id manager. Having
+multiple browser id managers may be useful in cases where you have a "secure"
+section of a site and an "insecure" section of a site, each using a different
+browser id manager with respectively restrictive security settings.
+
+In the container of your choosing, select "Browser Id Manager" from the add
+dropdown list in the Zope management interface. When you add a new browser id
+manager, the form options available are:
+
+Id
+  You cannot choose an id for your browser id manager. It must always be
+  "browser_id_manager". Additionally, you cannot rename a browser id manager.
+  This is required in the current implementation so that session data managers
+  can find session id managers via Zope acquisition.
+
+Title
+  the browser id manager title.
+
+Browser Id Name
+  the name used to look up the value of the browser id. This will be the name
+  looked up in the `cookies` or `form` REQUEST namespaces when the browser id
+  manager attempts to find a cookie, form variable, or URL with a browser id in
+  it.
+
+Look for Browser Id Name In
+  choose the request elements to look in when searching for the browser id
+  name. You may choose "cookies", "Forms and Query Strings", and "URLs".
+
+Automatically Encode Zope-Generated URLs With A Browser Id
+
+  if this option is checked, all URLs generated by Zope (such as URLs obtained
+  via the `absolute_url` method of all Zope objects) will have a browser id
+  name/value pair embedded within them. This typically only make sense if
+  you've also got the `URLs` setting of "Look for Browser Id in" checked off.
+
+Cookie Path
+  this is the `path` element which should be sent in the browser id cookie. For
+  more information, see the Netscape Cookie specification at
+  http://home.netscape.com/newsref/std/cookie_spec.html.
+
+Cookie Domain
+  this is the "domain" element which should be sent in the browser id cookie.
+  For more information, see the Netscape Cookie specification at
+  http://home.netscape.com/newsref/std/cookie_spec.html. Leaving this form
+  element blank results in no domain element in the cookie. If you change the
+  cookie domain here, the value you enter must have at least two dots (as per
+  the cookie spec).
+
+Cookie Lifetime In Days
+  browser id cookies sent to browsers will last this many days on a remote
+  system before expiring if this value is set. If this value is 0, cookies will
+  persist on client browsers for only as long as the browser is open.
+
+Only Send Cookie Over HTTPS
+
+  if this flag is set, only send cookies to remote browsers if they're
+  communicating with us over https. The browser id cookie sent under this
+  circumstance will also have the `secure` flag set in it, which the remote
+  browser should interpret as a request to refrain from sending the cookie back
+  to the server over an insecure (non-https) connection. NOTE: In the case you
+  wish to share browser id cookies between https and non-https connections from
+  the same browser, do not set this flag.
+
+After reviewing and changing these options, click the "Add" button to
+instantiate a browser id manager.
+
+You can change any of a browser id manager's initial settings by visiting it in
+the management interface.
+
+Instantiating A Session Data Manager (Optional)
++++++++++++++++++++++++++++++++++++++++++++++++
+
+After instantiating at least one browser id manager, it's possible to
+instantiate a session data manager. You don't need to do this in order to begin
+using Zope's sessioning machinery, as a default session data manager is created
+as::
+
+  /session_data_manager
+
+You can place a session data manager in any Zope container,as long as a browser
+id manager object named::
+
+  browser_id_manager
+
+can be acquired from that container. The session data manager will use the
+first acquired browser id manager.
+
+Choose "Session Data Manager" within the container you wish to house the
+session data manager from the "Add" dropdown box in the Zope management
+interface.
+
+The session data manager add form displays these options:
+
+Id
+  choose an id for the session data manager
+
+Title
+  choose a title for the session data manager
+
+Transient Object Container Path
+  enter the Zope path to a Transient Object Container in this text box in order
+  to use it to store your session data objects. Note: session manager's should
+  not share transient object paths. This is an example path:
+
+  Zope transient object container is::
+
+    /MyTransientSessionFolder
+
+After reviewing and changing these options, click the "Add" button to
+instantiate a session data manager.
+
+You can manage a session data manager by visiting it in the management
+interface. You may change all options available during the add process by doing
+this.
+
+Instantiating a Transient Object Container
+++++++++++++++++++++++++++++++++++++++++++
+
+The default transient object container at::
+
+  /temp_folder/session_data
+
+stores its objects in RAM, so these objects and their data disappear when you
+restart Zope.
+
+If you want your session data to persist across server reboots, or if you have
+a very large collection of session data objects, or if you'd like to share
+sessions between ZEO clients, you will want to instantiate a transient data
+container in a more permanent storage.
+
+A heavily-utilized transient object container *should be instantiated inside a
+database which is nonundoing*! Although you may instantiate a transient data
+container in any storage, if you make heavy use of an external session data
+container in an undoing database (such as the default Zope database which is
+backed by "FileStorage", an undoing and versioning storage), your database will
+grow in size very quickly due to the high-write nature of session tracking,
+forcing you to pack very often. You can "mount" additional storages within the
+`zope.conf` file of your Zope instance. The default `temp_folder` is mounted
+inside a `TemporaryStorage` , which is nonundoing and RAM-based. There are
+other nonundoing storages, such as BerkeleyStorage, although none quite as
+well-supported as TemporaryStorage.
+
+Here are descriptions of the add form of a Transient Object Container, which
+may be added by selecting "Transient Object Container" for the Zope Add list.:
+
+  Special note: When you add a transient object container to a non-RAM-based
+  storage, unlike the the default transient objects contained in temp_folder,
+  these instances of TOC maintain their parameter settings between Zope
+  Restarts. Importantly, they *do not* read zope.conf.
+
+Id
+  the id of the transient object container
+
+Title (optional)
+  the title of the transient object container
+
+Data object timeout in minutes
+  enter the number of minutes of inactivity which causes a contained transient
+  object be be timed out. "0" means no expiration.
+
+Maximum number of subobjects
+  enter the maximum number of transient objects that can be added to this
+  transient object container. This value helps prevent "denial of service"
+  attacks to your Zope site by effectively limiting the number of concurrent
+  sessions.
+
+Script to call upon object add (optional)
+  when a session starts, you may call an external method or Script (Python).
+  This is the Zope path to the external method or Script (Python) object to be
+  called. If you leave this option blank, no onAdd function will be called. An
+  example of a method path is `/afolder/amethod`.
+
+Script to call upon object delete (optional)
+  when a session ends, you may call an external method or Script (Python). This
+  is the Zope path to the external method or Script (Python) object to be
+  called. If you leave this option blank, no onDelete function will be called.
+  An example of a method path is `/afolder/amethod`.
+
+
+Multiple session data managers can make use of a single transient object
+container to the extent that they may share the session data objects placed in
+the container between them. This is not a recommended practice, however, as it
+has not been tested at all.
+
+The `data object timeout in minutes` value is the number of minutes that
+session data objects are to be kept since their last-accessed time before they
+are flushed from the data container. For instance, if a session data object is
+accessed at 1:00 pm, and if the timeout is set to 20 minutes, if the session
+data object is not accessed again by 1:19:59, it will be flushed from the data
+container at 1:20:00 or a time shortly thereafter. "Accessed", in this
+terminology, means "pulled out of the container" by a call to the session data
+manager's getSessionData() method or an equivalent (e.g. a reference to
+REQUEST.SESSION). See "Session Data Object Expiration Considerations" in the
+*Concepts and Caveats* section below for details on session data expiration.
+
+Configuring Sessioning Permissions
+++++++++++++++++++++++++++++++++++
+
+You need only configure sessioning permissions if your requirements deviate
+substantially from the norm. In this case, here is a description of the
+permissions related to sessioning.
+
+Permissions related to browser id managers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Add Browser Id Manager
+  allows a role to add browser id managers. By default, enabled for `Manager`.
+
+Change Browser Id Manager
+  allows a role to change an instance of a browser id manager. By default,
+  enabled for `Manager`.
+
+Access contents information
+  allows a role to obtain data about browser ids. By default, enabled for
+  `Manager` and `Anonymous`.
+
+
+Permissions related to session data managers:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Add Session Data Manager
+  allows a role to add session data managers. By default, enabled for
+  `Manager`.
+
+Change Session Data Manager
+  allows a role to call management-related methods of a session data manager.
+  By default, enabled for `Manager`.
+
+Access session data
+  allows a role to obtain access to the session data object related to the
+  current browser id. By default, enabled for `Manager` and `Anonymous`. You
+  may wish to deny this permission to roles who have DTML or Web-based Python
+  scripting capabilities who should not be able to access session data.
+
+Access arbitrary user session data
+  allows a role to obtain and otherwise manipulate any session data object for
+  which the browser id is known. By default, enabled for `Manager`.
+
+Access contents information
+  allows a role to obtain data about session data. By default, enabled for
+  `Manager` and `Anonymous`.
+
+Permissions related to transient object containers:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Add Transient Object Container
+  allows a role to add transient objects containers. By default, enabled for
+  `Manager`.
+
+Change Transient Object Container
+  allows a role to make changes to a transient object container.
+
+Access Transient Objects
+  allows a role to obtain and otherwise manipulate the transient object related
+  to the current browser id.
+
+Concepts and Caveats
+====================
+
+Security Considerations
++++++++++++++++++++++++
+
+Sessions are insecure by their very nature. If an attacker gets a hold of
+someone's browser id, and if they can construct a cookie or use form elements
+or URL elements to pose as that user from their own browser, they will have
+access to all information in that user's session. Sessions are not a
+replacement for authentication for this reason.
+
+Ideally, you'd like to make certain that nobody but the user its intended for
+gets a hold of his browser id. To take steps in this direction, and if you're
+truly concerned about security, you will ensure that you use cookies to
+maintain browser id information, and you will secure the link between your
+users and your site using SSL. In this configuration, it is more difficult to
+"steal" browser id information as the browser id will not be evident in the URL
+and it will be very difficult for attackers to "tap" the encrypted link between
+the browser and the Zope site.
+
+There are significant additional risks to user privacy in employing sessions in
+your application, especially if you use URL-based or formvar-based browser ids.
+Commonly, a browser id is embedded into a form/querystring or a URL in order to
+service users who don't have cookies turned on.
+
+For example, this kind of bug was present until recently in a lot of webmail
+applications: if you sent a mail to someone that included a link to a site
+whose logs you could read, and the user clicked on the link in his webmail
+page, the full URL of the page, including the authentication (stored as session
+information in the URL) would be sent as a HTTP REFERER to your site.
+
+Nowadays all serious webmail applications either choose to store at least some
+of the authentication information outside of the URL (in a cookie for
+instance), or process all the user-originated URLs included in the mail to make
+them go through a redirection that sanitizes the HTTP REFERER.
+
+The moral of the story is: if you're going to use sessions to store sensitive
+information, and you link to external sites within your own site, you're best
+off using *only* cookie-based browser ids.
+
+Browser Id (Non-)Expiration
++++++++++++++++++++++++++++
+
+A browser id will last as long as the browser id cookie persists on the client,
+or for as long as someone uses a bookmarked URL with a browser id encoded into
+it.
+
+The same id will be obtained by a browser id manager on every visit by that
+client to a site - potentially indefinitely depending on which conveyance
+mechanisms you use and your configuration for cookie persistence.
+
+The transient object container implements a policy for data object expiration.
+If asked for a session data object related to a particular browser id which has
+been expired by a session data container, a session data manager will a return
+a new session data object.
+
+Session Data Object Expiration Considerations
++++++++++++++++++++++++++++++++++++++++++++++
+
+Session data objects expire after the period between their last access and
+"now" exceeds the timeout value provided to the session data container which
+hold them. No special action need be taken to expire session data objects.
+
+However, because Zope has no scheduling facility, the sessioning machinery
+depends on the continual exercising of itself to expire session data objects.
+If the sessioning machinery is not exercised continually, it's possible that
+session data objects will stick around longer than the time specified by their
+data container timeout value. For example:
+
+- User A exercises application machinery that generates a session data object.
+  It is inserted into a session data container which advertises a 20-minute
+  timeout.
+
+- User A "leaves" the site.
+
+- 40 minutes go by with no visitors to the site.
+
+- User B visits 60 minutes after User A first generated his session data
+  object, and exercises app code which hands out session data objects. - *User
+  A's session is expired at this point, 40 minutes "late".*
+
+As shown, the time between a session's onAdd and onDelete is not by any means
+*guaranteed* to be anywhere close to the amount of time represented by the
+timeout value of its session data container. The timeout value of the data
+container should only be considered a "target" value.
+
+Additionally, even when continually exercised, the sessioning machinery has a
+built in error potential of roughly 20% with respect to expiration of session
+data objects to reduce resource requirements. This means, for example, if a
+transient object container timeout is set to 20 minutes, data objects added to
+it may expire anywhere between 16 and 24 minutes after they are last accessed.
+
+Sessioning and Transactions
++++++++++++++++++++++++++++
+
+Sessions interact with Zope's transaction system. If a transaction is aborted,
+the changes made to session data objects during the transaction will be rolled
+back.
+
+Mutable Data Stored Within Session Data Objects
++++++++++++++++++++++++++++++++++++++++++++++++
+
+If you mutate an object stored as a value within a session data object, you'll
+need to notify the sessioning machinery that the object has changed by calling
+`set` or `__setitem__` on the session data object with the new object value.
+For example::
+
+  session = self.REQUEST.SESSION
+  foo = {}
+  foo['before'] = 1
+  session.set('foo', foo)
+
+  # mutate the dictionary
+
+  foo['after'] = 1
+
+  # performing session.get('foo') 10 minutes from now will likely
+  # return a dict with only 'before' within!
+
+You'll need to treat mutable objects immutably, instead. Here's an example that
+makes the intent of the last example work by doing so::
+
+  session = self.REQUEST.SESSION
+  foo = {}
+  foo['before'] = 1
+  session.set('foo', foo)
+
+  # mutate the dictionary
+  foo['after'] = 1
+
+  # tickle the persistence machinery
+  session.set('foo', foo)
+
+An easy-to-remember rule for manipulating data objects in session storage:
+always explicitly place an object back into session storage whenever you change
+it. For further reference, see the "Persistent Components" chapter of the Zope
+Developer's Guide at http://www.zope.org/Documentation/ZDG.
+
+session.invalidate() and stale references to the session object
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+This Python Script illustrates an issue with using the invalidate method of a
+session object::
+
+  request = container.REQUEST
+  session = request.SESSION
+  session.set('foo','bar')
+  session.invalidate() 
+  # ............................................
+  # we expect that invalidate() flushes the session 
+  # ............................................
+  print 'after invalidate()',session.get('foo') # 'bar' still prints!
+
+  # ............................................
+  # Even this isn't enough
+  # ............................................
+  session = request.SESSION
+  print 'after invalidate()', session.get('foo') # 'bar' still prints!
+
+  # ............................................
+  # Here's the work-around
+  # ............................................
+  session = context.session_data_manager.getSessionData()
+  print 'after getSessionData', session.get('foo') # 'bar' is GONE which is good
+  return printed
+
+In short, after using the `invalidate` method of a session object, the next
+reference to the session object you obtain should be through "getSessionData"
+rather than `REQUEST.SESSION`.
+
+Session Data Object Keys
+++++++++++++++++++++++++
+
+A session data object has essentially the same restrictions as a Python
+dictionary. Keys within a session data object must be hashable (strings,
+tuples, and other immutable basic Python types; or instances which have a
+__hash__ method). This is a requirement of all Python objects that are to be
+used as keys to a dictionary. For more information, see the associated Python
+documentation at http://www.python.org/doc/current/ref/types.html (Mappings ->
+Dictionaries).
+
+In-Memory Session Data Container RAM Utilization
+++++++++++++++++++++++++++++++++++++++++++++++++
+
+Each session data object which is added to an "internal" (RAM-based) session
+data container will consume at least 2K of RAM.
+
+Mounted Transient Object Container Caveats
+++++++++++++++++++++++++++++++++++++++++++
+
+Mounted TOC's do not acquire parameter's from zope.conf (which is the case for
+the default transient object container). Therefore you set parameters directly
+on the object in ZMI.
+
+Persistent objects which have references to other persistent objects in the
+same database cannot be committed into a mounted database because the ZODB does
+not currently handle cross-database references.
+
+Transient object containers which are sometimes stored in a "mounted" database
+(as is currently the case for the default ::
+
+  /temp_folder/session_data
+
+TOC. If you use a transient object container that is accessed via a "mounted"
+database, you cannot store persistent object instances which have already been
+stored in the "main" database as keys or values in a session data object. If
+you try to do so, it is likely that an ::
+
+  InvalidObjectReference
+
+exception will be raised by the ZODB when the transaction involving the object
+attempts to commit. As a result, the transaction will fail and the session data
+object (and other objects touched in the same transaction) will fail to be
+committed to storage.
+
+If your "main" ZODB database is backed by a nonundoing storage, you can avoid
+this condition by storing session data objects in an transient object container
+instantiated within the "main" ZODB database. If this is not an option, you
+should ensure that objects you store as values or keys in a session data object
+held in a mounted session data container are instantiated "from scratch" (via
+their constructors), as opposed to being "pulled out" of the main ZODB.
+
+Conflict Errors
++++++++++++++++
+
+This session tracking software stores all session state in Zope's ZODB. The
+ZODB uses an optimistic concurrency strategy to maintain transactional
+integrity for simultaneous writes. This means that if two objects in the ZODB
+are changed at the same time by two different connections (site visitors) that
+a "ConflictError" will be raised. Zope retries requests that raise a
+ConflictError at most 3 times. If your site is extremely busy, you may notice
+ConflictErrors in the Zope debug log (or they may be printed to the console
+from which you run Zope). An example of one of these errors is as follows::
+
+  2001-01-16T04:26:58 INFO(0) Z2 CONFLICT Competing writes at, /getData
+  Traceback (innermost last):
+  File /zope/lib/python/ZPublisher/Publish.py, line 175, in publish
+  File /zope/lib/python/Zope/__init__.py, line 235, in commit
+  File /zope/lib/python/ZODB/Transaction.py, line 251, in commit
+  File /zope/lib/python/ZODB/Connection.py, line 268, in commit
+  ConflictError: '\000\000\000\000\000\000\002/'
+
+Errors like this in your debug log (or console if you've not redirected debug
+logging to a file) are normal to an extent. If your site is undergoing heavy
+load, you can expect to see a ConflictError perhaps every 20 to 30 seconds. The
+requests which experience conflict errors will be retried automatically by
+Zope, and the end user should *never* see one. Generally, session data objects
+attempt to provide application-level conflict resolution to reduce the
+limitations imposed by conflict errors NOTE: to take advantage of this feature,
+you must store your transient object container in a storage such as FileStorage
+or TemporaryStorage which supports application-level conflict resolution.



More information about the Checkins mailing list