[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