[Checkins] SVN: zope2book/trunk/ Restify AdvZPT chapter.

Tres Seaver tseaver at palladion.com
Tue Feb 10 17:22:14 EST 2009


Log message for revision 96426:
  Restify AdvZPT chapter.

Changed:
  D   zope2book/trunk/AdvZPT.stx
  A   zope2book/trunk/source/AdvZPT.rst
  U   zope2book/trunk/source/index.rst

-=-
Deleted: zope2book/trunk/AdvZPT.stx
===================================================================
--- zope2book/trunk/AdvZPT.stx	2009-02-10 21:58:00 UTC (rev 96425)
+++ zope2book/trunk/AdvZPT.stx	2009-02-10 22:22:14 UTC (rev 96426)
@@ -1,1471 +0,0 @@
-Advanced Page Templates
-
-  In the chapter entitled "Using Zope Page Templates":ZPT.stx you
-  learned the basic features of Page Templates. In this chapter
-  you'll learn about advanced techniques including new types of
-  expressions.
-
-  Advanced TAL
-
-    In this section we'll go over all TAL statements and their various
-    options in depth.  This material is covered more concisely in
-    Appendix C, "Zope Page Templates Reference":AppendixC.stx.
-
-    In this chapter, the terms 'tag' and 'element' are used in the
-    sense laid out by the "XHTML
-    spec":http://www.w3.org/TR/2000/REC-xhtml1-20000126/#defs.
-    "<p>" is a *tag*, while the entire block
-    "<p>stuff</p>" from opening tag through the closing
-    tag is an *element*.
-
-    Advanced Content Insertion
-
-      You've already seen how 'tal:content' and 'tal:replace' work in
-      the chapter entitled "Using Zope Page Templates":ZPT.stx. In
-      this section you'll learn some advanced tricks for inserting
-      content.
-
-      Inserting Structure
-
-        Normally, the 'tal:replace' and 'tal:content' statements
-        convert HTML tags and entities in the text that they insert
-        into an "escaped" form that appears in the resulting document
-        as plain text rather than HTML markup.
-        For instance, the '<' character is "escaped" to '&amp;lt;'.
-        If you want to insert text as part of the HTML structure of
-        your document, avoiding this conversion , you need to
-        precede the expression with the 'structure' keyword.
-
-        This feature is useful when you are inserting a fragment of
-        HTML or XML that is stored in a property or generated by
-        another Zope object.  For instance, you may have news items
-        that contain simple HTML markup such as bold and italic text
-        when they are rendered, and you want to preserve this when
-        inserting them into a "Top News" page.  In this case, you
-        might write::
-
-          <p tal:repeat="newsItem here/topNews"
-             tal:content="structure newsItem">
-            A news item with<code>HTML</code> markup.
-          </p>
-
-        This will insert the news items' HTML into a series of
-        paragraphs.  The built-in variable 'here' refers to the folder
-        in which the template is rendered; See the "Expressions"
-        section further below in this chapter for more information on
-        'here'.  In this case, we use 'here' as the starting point for
-        finding the Zope object 'topNews', which is presumably a list
-        of news items or a Script which fetches such a list.
-
-        The 'structure' keyword prevents the text of each newsItem
-        value from being escaped.  It doesn't matter whether the text
-        actually contains any HTML markup, since 'structure' really
-        means "leave this text alone".  This behavior
-        is not the default because most of the text that you insert
-        into a template will *not* contain HTML, but may contain
-        characters that would interfere with the structure of your page.
-
-      Dummy Elements
-
-        You can include page elements that are visible in the template
-        but not in generated text by using the built-in variable
-        'nothing', like this::
-
-          <tr tal:replace="nothing">
-            <td>10213</td><td>Example Item</td><td>$15.34</td>
-          </tr>
-
-        This can be useful for filling out parts of the page that will
-        be populated with dynamic content.  For instance, a table that
-        usually has ten rows will only have one row in the template.
-        By adding nine dummy rows, the template's layout will look
-        more like the final result.
-
-      Default Content
-
-        You can leave the contents of an element alone by using the
-        'default' expression with 'tal:content' or 'tal:replace'. For
-        example::
-
-          <p tal:content="default">Spam</p>
-
-        This renders to::
-
-          <p>Spam</p>
-
-        Most often you will want to selectively include default
-        content, rather than always including it. For example::
-
-          <p tal:content="python:here.getFood() or default">Spam</p>
-
-        Note: Python expressions are explained later in the
-        chapter. If the 'getFood' method returns a true value then its
-        result will be inserted into the paragraph, otherwise it's
-        Spam for dinner.
-
-    Advanced Repetition
-
-      You've already seen most of what you can do with the
-      'tal:repeat' statement in the chapter entitled "Using Zope Page
-      Templates":ZPT.stx. This section covers a few advanced features
-      of the 'tal:repeat' statement.
-
-      Repeat Variables
-
-        One topic that bears more explanation are repeat
-        variables. Repeat variables provide information about the
-        current repetition. The following attributes are available on
-        'repeat' variables:
-
-          o *index* - repetition number, starting from zero.
-
-          o *number* - repetition number, starting from one.
-
-          o *even* - true for even-indexed repetitions (0, 2, 4, ...).
-
-          o *odd* - true for odd-indexed repetitions (1, 3, 5, ...).
-
-          o *start* - true for the starting repetition (index 0).
-
-          o *end* - true for the ending, or final, repetition.
-
-          o *length* - length of the sequence, which will be the total number
-            of repetitions.
-
-          o *letter* - count reps with lower-case letters: "a" - "z",
-          "aa" - "az", "ba" - "bz", ..., "za" - "zz", "aaa" - "aaz", and
-          so forth.
-
-          o *Letter* - upper-case version of *letter*.
-
-        You can access the contents of a repeat variable using path
-        expressions or Python expressions.  In path expressions, you
-        write a three-part path consisting of the name 'repeat', the
-        statement variable's name, and the name of the information you
-        want, for example, 'repeat/item/start'.  In Python expressions,
-        you use normal dictionary notation to get the repeat variable,
-        then attribute access to get the information, for example,
-        'python:repeat['item'].start'.  The reason that you can't
-        simply write 'repeat/start' is that 'tal:repeat' statements
-        can be nested, so you need to be able to specify which one you
-        want information about.
-
-      Repetition Tips
-
-        Here are a couple practical tips that you may find
-        useful. Sometimes you'd like to repeat part of your template,
-        but there is no naturally enclosing element.  In this case,
-        you must add an enclosing element, but you want to prevent
-        it from appearing in the rendered page. You can do this with
-        the 'tal:omit-tag' statement::
-
-          <div tal:repeat="section here/getSections"
-               tal:omit-tag="">
-            <h4 tal:content="section/title">Title</h4>
-            <p tal:content="section/text">quotation</p>
-          </div>
-
-        This is not just a matter of saving a few characters in the
-        rendered output.  Including the 'div' tags in the output could
-        affect the page layout, especially if it has stylesheets. We
-        use the tal 'omit-tag' statement to disinclude the 'div' tag
-        (and its pair closing tag) while leaving its contents
-        unmolested.  The 'tal:omit-tag' statement is described in more
-        detail later in this chapter.
-
-        While it's been mentioned before, it's worth saying again: you
-        can nest 'tal:repeat' statements inside each other. Each
-        'tal:repeat' statement must have a different repeat variable
-        name. Here's an example that shows a math times-table::
-
-          <table border="1">
-            <tr tal:repeat="x python:range(1, 13)">
-              <td tal:repeat="y python:range(1, 13)"
-                  tal:content="python:'%d x %d = %d' % (x, y, x*y)">
-                  X x Y = Z
-              </td>
-            </tr>
-          </table>
-
-        This example uses Python expressions, which are covered later
-        in this chapter.
-
-        If you've done much work with the 'dtml-in' DTML repetition
-        statement, you will have encountered batching. Batching is the
-        process of chopping up a large list into smaller lists. You
-        typically use it to display a small number of items from a
-        large list on a web page. Think of how a search engine batches
-        its search results. The 'tal:repeat' statement does not
-        support batching, but Zope comes with a batching utility. See
-        the section, "Batching" later in this chapter.
-
-        Another useful feature that isn't supplied by 'tal:repeat' is
-        sorting. If you want to sort a list you can either write
-        your own sorting script (which is quite easy in Python) or you
-        can use the 'sequence.sort' utility function. Here's an
-        example of how to sort a list of objects by title, and then by
-        modification date::
-
-          <table tal:define="objects here/objectValues;
-                             sort_on python:(('title', 'nocase', 'asc'),
-                                             ('bobobase_modification_time', 'cmp', 'desc'));
-                             sorted_objects python:sequence.sort(objects, sort_on)">
-            <tr tal:repeat="item sorted_objects">
-              <td tal:content="item/title">title</td>
-              <td tal:content="item/bobobase_modification_time">
-                modification date</td>  
-            </tr>
-          </table>
-
-      This example tries to make things clearer by defining the sort
-      arguments outside the 'sort' function.  The 'sequence.sort'
-      function takes a sequence and a description of how to sort
-      it. In this example the description of how to sort the sequence
-      is defined in the 'sort_on' variable.  See Appendix B, "API
-      Reference":AppendixB.stx for more information on the powerful
-      'sequence.sort' function.
-
-    Advanced Attribute Control
-
-      You've already met the 'tal:attributes' statement. You can use
-      it to dynamically replace tag attributes, for example, the
-      'href' attribute on an 'a' element. You can replace more than
-      one attribute on a tag by separating attributes with
-      semicolons. For example, the code below will generate an
-      "href" and a "class" attribute::
-
-        <a href="link"
-           tal:attributes="href here/getLink;
-                           class here/getClass">link</a>
-
-      You can also define attributes with XML namespaces. For example::
-
-        <Description 
-            dc:Creator="creator name"
-            tal:attributes="dc:Creator here/owner/getUserName">
-          Description</Description>
-
-      Simply put the XML namespace prefix before the attribute name
-      and you can create attributes with XML namespaces.
-
-    Defining Variables
-
-      You can define your own variable using the 'tal:define'
-      attribute. There are several reasons that you might want to do
-      this. One reason is to avoid having to write long expressions
-      repeatedly in a template. Another is to avoid having to call
-      expensive methods repeatedly. You can define a variable once
-      within an element on a tag and then use it many times within
-      elements which are enclosed by this tag. For example, here's a
-      list that defines a variable and later tests it and repeats over
-      it::
-
-        <ul tal:define="items container/objectIds"
-            tal:condition="items">
-          <li tal:repeat="item items">
-            <p tal:content="item">id</p>
-          </li>
-        </ul>
-
-      The 'tal:define' statement creates the variable 'items', which
-      you can use anywhere in the 'ul' element.  Notice also how you
-      can have two TAL statements on the same 'ul' tag.  See the
-      section "Interactions Between TAL Statements" later in this
-      chapter for more information about using more than one statement
-      on a tag.  In this case the first statement assigns the variable
-      'items' and the second uses 'items' in a condition to see
-      whether it is false (in this case, an empty sequence) or
-      true. If the 'items' variable is false, then the 'ul' element is not
-      shown.
-
-      Now, suppose that instead of simply removing the list when there
-      are no items, you want to show a message.  To do this, place the
-      following before the list::
-
-        <h4 tal:condition="not:container/objectIds">There
-        Are No Items</h4>
-
-      The expression, 'not:container/objectIds' is true when
-      'container/objectIds' is false, and vice versa. See the section,
-      "Not Expressions" later in this chapter for more information.
-
-      You can't use your 'items' variable here, because it isn't
-      defined yet.  If you move the definition of 'items' to the 'h4'
-      element, then you can't use it in the 'ul' element any more, because it
-      becomes a *local* variable of the 'h4' element.  You could place the
-      definition on some element that enclosed both the 'h4' and the 'ul',
-      but there is a simpler solution.  By placing the keyword
-      'global' in front of the variable name, you can make the
-      definition last from the 'span' tag to the bottom of the
-      template::
-
-        <span tal:define="global items container/objectIds"></span>
-        <h4 tal:condition="not:items">There Are No Items</h4>
-
-      You can define more than one variable using 'tal:define' by
-      separating them with semicolons. For example::
-
-        <p tal:define="ids container/objectIds; 
-                       title container/title">
-
-      You can define as many variables as you wish. Each variable can
-      have its own global or local scope. You can also refer to
-      earlier defined variables in later definitions. For example::
-
-        <p tal:define="title template/title;
-                       global untitled not:title;
-                       tlen python:len(title);">
-
-      In this case, both 'title' and 'tlen' are local to the
-      paragraph, but 'untitled' is global. With judicious use of
-      'tal:define' you can improve the efficiency and readability of
-      your templates.
-
-    Omitting Tags
-
-      You can remove tags with the 'tal:omit-tag' statement. You will
-      seldom need to use this TAL statement, but occasionally it's
-      useful. The omit-tag attribute removes opening and closing tags,
-      but does not affect the contents of the element. For example::
-
-        <b tal:omit-tag=""><i>this</i> stays</b>
-
-      Renders to::
-
-        <i>this</i> stays
-
-      At this level of usage, 'tal:omit-tag' operates almost like
-      'tal:replace="default"'. However, 'tal:omit-tag' can also be
-      used with a true/false expression, in which case it only removes
-      the tags if the expression is true. For example::
-
-        Friends: <span tal:repeat="friend friends">
-          <b tal:omit-tag="not:friend/best"
-             tal:content="friend/name">Fred</b>
-        </span>
-
-      This will produce a list of friends, with our "best" friend's
-      name in bold.
-
-    Error Handling  
-
-      If an error occurs in your page template, you can catch that
-      error and show a useful error message to your user.  For
-      example, suppose your template defines a
-      variable using form data::
-
-        ...
-        <span tal:define="global prefs request/form/prefs"
-              tal:omit-tag="" />
-        ...
-
-      If Zope encounters a problem, like not being able to find the
-      'prefs' variable in the form data, the entire page will break;
-      you'll get an error page instead. Happily, you can avoid this
-      kind of thing with limited error handling using the
-      'tal:on-error' statement::
-
-        ...
-        <span tal:define="global prefs here/scriptToGetPreferences"
-              tal:omit-tag=""
-              tal:on-error="string:An error occurred">
-        ...
-
-      When an error is raised while rendering a template, Zope looks
-      for a 'tal:on-error' statement to handle the error. It first
-      looks in the current element, then on its enclosing element, and so on
-      until it reaches the top-level element. When it finds an error
-      handler, it replaces the contents of that element with the error
-      handling expression. In this case, the 'span' element will contain
-      an error message.
-
-      Typically you'll define an error handler on an element that encloses
-      a logical page element, for example a table. If an error crops
-      up drawing the table, then the error handler can simply omit the
-      table from the page, or else replace it with an error message of
-      some sort.
-
-      For more flexible error handling you can call a script. For
-      example::
-
-        <div tal:on-error="structure here/handleError">
-        ...
-        </div>
-
-      Any error that occurs inside the 'div' will call the
-      'handleError' script. Note that the 'structure' option allows
-      the script to return HTML. Your error handling script can
-      examine the error and take various actions depending on the
-      error. Your script gets access to the error through the 'error'
-      variable in the namespace. For example::
-
-        ## Script (Python) "handleError"
-        ##bind namespace=_
-        ##
-        error=_['error']
-        if error.type==ZeroDivisionError:
-            return "<p>Can't divide by zero.</p>"
-        else:
-            return """<p>An error occurred.</p>
-                      <p>Error type: %s</p>
-                      <p>Error value: %s</p>""" % (error.type,
-                                                   error.value)
-
-      Your error handling script can take all kinds of actions, for
-      example, it might log the error by sending email.
-
-      The 'tal:on-error' statement is not meant for general purpose
-      exception handling. For example, you shouldn't validate form
-      input with it. You should use a script for that, since scripts
-      allow you to do powerful exception handling. The 'tal:on-error'
-      statement is for dealing with unusual problems that can occur
-      when rendering templates.
-
-    Interactions Between TAL Statements
-
-      When there is only one TAL statement per element, the order in
-      which they are executed is simple. Starting with the root
-      element, each element's statements are executed, then each of
-      its child elements are visited, in order, and their statements
-      are executed, and so on.
-
-      However, it's possible to have more than one TAL statement on
-      the same element. Any combination of statements may appear on
-      the same element, except that the 'tal:content' and
-      'tal:replace' statements may not appear together.
-
-      When an element has multiple statements, they are executed in
-      this order:
-
-        1. define
-
-        2. condition
-
-        3. repeat
-
-        4. content or replace
-
-        5. attributes
-
-        6. omit-tag
-
-      Since the 'tal:on-error' statement is only invoked when an error
-      occurs, it does not appear in the list.
-
-      The reasoning behind this ordering goes like this: you often
-      want to set up variables for use in other statements, so define
-      comes first. The very next thing to do is decide whether this
-      element will be included at all, so condition is next; since the
-      condition may depend on variables you just set, it comes after
-      define. It is valuable to be able to replace various parts of an
-      element with different values on each iteration of a repeat, so
-      repeat comes before content, replace and attributes. Content and
-      replace can't both be used on the same element so they occur at
-      the same place. Omit-tag comes last since no other statements are
-      likely to depend on it and since it should come after define and
-      repeat.
-
-      Here's an example element that includes several TAL 
-      statements::
-
-        <p tal:define="x /root/a/long/path/x | nothing"
-           tal:condition="x"
-           tal:content="x/txt"
-           tal:attributes="class x/class">Ex Text</p>
-
-      Notice how the 'tal:define' statement is executed first, and the
-      other statements rely on its results.
-
-      There are three limits you should be aware of when combining TAL
-      statements on elements:
-
-      1. Only one of each kind of statement can be used on a single
-      tag.  Since HTML does not allow multiple attributes with the
-      same name. For example, you can't have two 'tal:define' on the
-      same tag.
-
-      2. Both of 'tal:content' and 'tal:replace' cannot be used on
-      the same tag, since their functions conflict.
-
-      3. The order in which you write TAL attributes on a tag does
-      not affect the order in which they execute.  No matter how
-      you arrange them, the TAL statements on a tag always execute
-      in the fixed order described earlier.
-
-      If you want to override the ordering of TAL statements, you must
-      do so by enclosing the element in another element and placing
-      some of the statements on this new element. For example suppose
-      you want to loop over a series of items but skip some. Here's an
-      attempt to write a template that loops over the numbers zero to
-      nine and skips three::
-
-        <!-- broken template -->
-        <ul>
-          <li tal:repeat="n python:range(10)"
-              tal:condition="python:n != 3"
-              tal:content="n"> 
-            1
-          </li>
-        </ul>
-
-      This template doesn't work due to TAL statement execution order.
-      Despite the order in which they are written, the condition is
-      always tested before the repeat is executed. This results in a
-      situation in which the 'n' variable is not defined until after
-      it is tested, which ultimately causes an error when you attempt
-      to test or otherwise view the template. Here's a way around this
-      problem::
-
-        <ul>
-          <div tal:repeat="n python:range(10)"
-               tal:omit-tag="">
-            <li tal:condition="python:n != 3"
-                tal:content="n"> 
-              1
-            </li>
-          </div>
-        </ul>
-
-      This template solves the problem by defining the 'n' variable on
-      an enclosing 'div' element. Notice that the 'div' tag will not
-      appear in the output due to its 'tal:omit-tag' statement.
-
-      Although 'span' and 'div' are natural choices for this in HTML,
-      there is, in general, no equivalent natural element in XML.  In
-      this case, you can use TAL's namespace in a new way: while TAL
-      does not define any tags, it doesn't prohibit any either.  You
-      can make up any tag name you like within the TAL namespace, and
-      use it to make an element, like so::
-
-        <tal:series define="items here/getItems">
-          <tal:items repeat="item items">
-          <tal:parts repeat="part item">
-            <part tal:content="part">Part</part>
-          </tal:parts>
-          </tal:items>
-          <noparts tal:condition="not:items" />
-        </tal:series>
-
-      The 'tal:series', 'tal:items', and 'tal:parts' tags in this
-      example should be acceptable to tools that handle XML namespaces
-      properly, and to many HTML tools.  This method has two
-      additional advantages over a 'div'.  First, TAL tags are omitted
-      just like TAL attributes, so no 'tal:omit-tag' is necessary.
-      Second, TAL attributes in these tags don't require their
-      own 'tal:' prefix, since they inherit the namespace of the tag.
-      The METAL namespace can be used in exactly the same fashion.
-
-    Form Processing
-
-      You can process forms in DTML using a common pattern called the
-      "form/action pair".  A form/action pair consists of two DTML
-      methods or documents: one that contains a form that collects
-      input from the user, and one that contains an action that is
-      taken on that input and returns the user a response.  The form
-      calls the action.  See the chapter entitled "Dynamic Content
-      with DTML":DTML.stx for more information on the form/action
-      pattern.
-
-      Zope Page Templates don't work particularly well with the
-      form/action pattern since it assumes that input processing and
-      response presentation are handled by the same object (the
-      action). Instead of the form/action pattern you should use
-      form/action/response pattern with Page Templates. The form and
-      response should be Page Templates and the action should be a
-      script. The form template gathers the input and calls the action
-      script. The action script should process the input and return a
-      response template. This pattern is more flexible than the
-      form/action pattern since it allows the script to return any of
-      a number of different response objects.
-
-      For example here's a part of a form template::
-
-        ...
-        <form action="action">
-          <input type="text" name="name">
-          <input type="text" name="age:int">
-          <input type="submit">
-        </form>
-        ...
-
-      This form could be processed by this script::
-
-        ## Script (Python) "action"
-        ##parameters=name, age
-        ##
-        container.addPerson(name, age)
-        return container.responseTemplate()
-
-      This script calls a method to process the input and then
-      returns another template, the response. You can render a Page
-      Template from Python by calling it. The response template
-      typically contains an acknowledgment that the form has been
-      correctly processed.
-
-      The action script can do all kinds of things. It can validate
-      input, handle errors, send email, or whatever it needs to do to
-      "get the job done".  Here's a sketch of how to validate input
-      with a script::
-
-        ## Script (Python) "action"
-        ##
-        if not context.validateData(request):
-            # if there's a problem return the form page template
-            # along with an error message
-            return context.formTemplate(error_message='Invalid data')
-
-        # otherwise return the thanks page
-        return context.responseTemplate()
-
-      This script validates the form input and returns the form
-      template with an error message if there's a problem. The
-      Script's 'context' variable is equivalent to 'here' in
-      TALES. You can pass Page Templates extra information with
-      keyword arguments. The keyword arguments are available to the
-      template via the 'options' built-in variable. So the form
-      template in this example might include a section like this::
-
-        <span tal:condition="options/error_message | nothing">
-        Error: <b tal:content="options/error_message">
-          Error message goes here.
-        </b></span>
-
-      This example shows how you can display an error message that is
-      passed to the template via keyword arguments. Notice the use of
-      '| nothing' to handle the case where no 'error_message' argument
-      has been passed to the template.
-
-      Depending on your application you may choose to redirect the
-      user to a response Page Template instead of returning it
-      directly. This results in twice as much network activity, but
-      might be useful because it changes the URL displayed in the
-      user's browser to the URL of the Page Template, rather than that
-      of the action script.
-
-      If you need to set up a quick-and-dirty form, you can always
-      create a version of the form-action pair using Page Templates
-      alone. You should only do this when you don't care about error
-      handling and when the response will always be the same, no
-      matter what the user submits. Since Page Templates don't have an
-      equivalent of 'dtml-call', you can use one of any number of
-      hacks to call an input processing method without inserting its
-      results. For example::
-
-        <span tal:define="unused here/processInputs" 
-              tal:omit-tag=""/>
-
-      This sample calls the 'processInputs' method and assigns the
-      result to the 'unused' variable.
-
-  Expressions
-
-    You've already encountered Page Template expressions. Expressions
-    provide values to template statements. For example, in the TAL
-    statement '<td tal:content="request/form/age">Age</td>', the
-    expression of the statement is 'request/form/age'.
-    'request/form/age' is an example of a *path expression*.  Path
-    expressions describe objects by giving them paths such as
-    'request/form/age', or 'user/getUserName'. Expressions only work
-    in the context of a TAL statement; they do not work in "normal"
-    HTML inserted in your page templates.  In this section you'll
-    learn about all the different types of expressions, and variables.
-
-    Built-in Page Template Variables
-
-      Variables are names that you can use in expressions. You have
-      already seen some examples of the built-in variables such as
-      'template', 'user', 'repeat', and 'request'.  Here is the
-      complete list of the other built-in variables and their uses.
-      Note that these variables are different than the built-in
-      variables that you would use in a Script (Python), they are only
-      effective for Page Templates::
-
-      'nothing' -- A false value, similar to a blank string, that you
-      can use in 'tal:replace' or 'tal:content' to erase an element or
-      its contents.  If you set an attribute to 'nothing', the
-      attribute is removed from the tag (or not inserted).  A blank
-      string, on the other hand, would insert the tag with an empty
-      value, as in 'alt=""'.
-
-      'default' -- A special value that doesn't change anything when
-      used in 'tal:replace', 'tal:content', or 'tal:attributes'.  It
-      leaves the template text in place.
-
-      'options' -- The keyword arguments, if any, that were passed to
-      the template. When a template is rendered from the web, no
-      options are present. Options are only available when a template
-      is called from Python or by similarly complex means.  For
-      example, when the template 't' is called by the Python expression
-      't(foo=1)', the path 'options/foo' equals '1'.
-
-      'attrs' -- A dictionary of attributes of the current tag in the
-      template.  The keys are the attributes names, and the values are
-      the original values of the attributes in the template. This
-      variable is rarely needed.
-
-      'root' -- The root Zope object.  Use this to get Zope objects
-      from fixed locations, no matter where your template is placed or
-      called.
-
-      'here' -- The object on which the template is being called.
-      This is often the same as the *container*, but can be different
-      if you are using acquisition.  Use this to get Zope objects that
-      you expect to find in different places depending on how the
-      template is called. The 'here' variable is analogous to the
-      'context' variable in Python-based scripts.
-
-      'container' -- The container (usually a Folder) in which the
-      template is kept.  Use this to get Zope objects from locations
-      relative to the template's permanent home. The 'container' and
-      'here' variables refer to the same object when a template is
-      called from its normal location. However, when a template is
-      applied to another object (for example, a ZSQL Method) the
-      'container' and 'here' will not refer to the same object.
-
-      'modules' -- The collection of Python modules available to
-      templates.  See the section on writing Python expressions.
-
-      You'll find examples of how to use these variables throughout
-      this chapter.
-
-    String Expressions
-
-      String expressions allow you to easily mix path expressions with
-      text.  All of the text after the leading 'string:' is taken and
-      searched for path expressions.  Each path expression must be
-      preceded by a dollar sign ('$').  Here are some examples::
-
-        "string:Just text. There's no path here."
-        "string:copyright $year by Fred Flintstone."
-
-      If the path expression has more than one part (if it contains a
-      slash), or needs to be separated from the text that follows it,
-      it must be surrounded by braces ('{}'). For example::
-
-        "string:Three ${vegetable}s, please."
-        "string:Your name is ${user/getUserName}!"
-
-      Notice how in the example above, you need to surround the
-      'vegetable' path with braces so that Zope doesn't mistake it for
-      'vegetables'.
-
-      Since the text is inside of an attribute value, you can only
-      include a double quote by using the entity syntax '&quot;'.
-      Since dollar signs are used to signal path expressions, a
-      literal dollar sign must be written as two dollar signs
-      ('$$'). For example::
-
-        "string:Please pay $$$dollars_owed"
-        "string:She said, &quot;Hello world.&quot;"
-
-       Some complex string formatting operations (such as search and
-       replace or changing capitalization) can't easily be done with
-       string expressions. For these cases, you should use Python
-       expressions or Scripts.
-
-    Path Expressions
-
-      Path expressions refer to objects with a path that resembles a
-      URL path. A path describes a traversal from object to
-      object. All paths begin with a known object (such as a built-in
-      variable, a repeat variable, or a user defined variable) and
-      depart from there to the desired object. Here are some example
-      paths expressions::
-
-        template/title
-        container/files/objectValues
-        user/getUserName
-        container/master.html/macros/header
-        request/form/address
-        root/standard_look_and_feel.html
-
-      With path expressions you can traverse from an object to its
-      sub-objects including properties and methods. You can also use
-      acquisition in path expressions. See the section entitled
-      "Calling Scripts from the Web" in the chapter entitled "Advanced
-      Zope Scripting":ScriptingZope.stx for more information on
-      acquisition and path traversal.
-
-      Zope restricts object traversal in path expressions in the same
-      way that it restricts object access via URLs. You must have
-      adequate permissions to access an object in order to refer to it
-      with a path expression. See the chapter entitled "Users and
-      Security":Security.stx for more information about object access
-      controls.
-
-      Alternate Paths
-
-        The path 'template/title' is guaranteed to exist every time
-        the template is used, although it may be a blank string.  Some
-        paths, such as 'request/form/x', may not exist during some
-        renderings of the template.  This normally causes an error
-        when Zope evaluates the path expression.
-
-        When a path doesn't exist, you may have a fall-back path or
-        value that you would like to use instead.  For instance, if
-        'request/form/x' doesn't exist, you might want to use 'here/x'
-        instead.  You can do this by listing the paths in order of
-        preference, separated by vertical bar characters ('|')::
-
-          <h4 tal:content="request/form/x | here/x">Header</h4>
-
-        Two variables that are very useful as the last path in a list
-        of alternates are 'nothing' and 'default'.  For example,
-        'default' tells 'tal:content' to leave the dummy
-        content. Different TAL statements interpret 'default' and
-        'nothing' differently. See Appendix C, "Zope Page Templates
-        Reference" for more information.
-
-        You can also use a non-path expression as the final part in an
-        alternate-path expression. For example::
-
-          <p tal:content="request/form/age|python:18">age</p>
-
-        In this example, if the 'request/form/age' path doesn't exist,
-        then the value is the number 18. This form allows you to
-        specify default values to use which can't be expressed as
-        paths. Note, you can only use a non-path expression as the
-        last alternative.
-
-       xxx-todo:
-         This spec should describe the following path expression syntax:
-
-         <span tal:content="here/?myvar" />
-
-         The "here/?myvar" expression is evaluated by traversing from
-         "here" to the name given by the value of "myvar". For
-         example, if "myvar" is set to "title", "here/?myvar" is
-         equivalent to "here/title".
-
-        You can also test the existence of a path directly with the
-        *exists* expression type prefix. See the section "Exists
-        Expressions" below for more information on exists expressions.
-
-    Not Expressions
-
-      Not expressions let you negate the value of other
-      expressions. For example::
-
-        <p tal:condition="not:here/objectIds">
-          There are no contained objects.
-        </p>
-
-      Not expressions return true when the expression they are applied
-      to is false, and vice versa. In Zope, zero, empty strings, empty
-      sequences, nothing, and None are considered false, while
-      everything else is true.  Non-existent paths are neither true
-      nor false, and applying a 'not:' to such a path will fail.
-
-      There isn't much reason to use not expressions with Python
-      expressions since you can use the Python 'not' keyword instead.
-
-    Nocall Expressions
-
-      An ordinary path expression tries to render the object
-      that it fetches.  This means that if the object is a function,
-      Script, Method, or some other kind of executable thing, then
-      the expression will evaluate to the result of calling the object.
-      This is usually what you want, but not always.  For example,
-      if you want to put a DTML Document into a variable so that
-      you can refer to its properties, you can't use a normal path
-      expression because it will render the Document into a string.
-
-      If you put the 'nocall:' expression type prefix in front of a
-      path, it prevents the rendering and simply gives you the
-      object.  For example::
-
-        <span tal:define="doc nocall:here/aDoc"
-              tal:content="string:${doc/getId}: ${doc/title}">
-        Id: Title</span>
-
-      This expression type is also valuable when you want to define
-      a variable to hold a function or class from a module, for use
-      in a Python expression.
-
-      Nocall expressions can also be used on functions, rather than
-      objects::
-
-        <p tal:define="join nocall:modules/string/join">
-
-      This expression defines the 'join' variable as a function
-      ('string.join'), rather than the result of calling a function.
-
-    Exists Expressions
-
-      An exists expression is true if its path exists, and otherwise
-      is false.  For example here's one way to display an error
-      message only if it is passed in the request::
-
-        <h4 tal:define="err request/form/errmsg | nothing"
-            tal:condition="err" 
-            tal:content="err">Error!</h4>
-
-      You can do the same thing more easily with an exists
-      expression::
-
-        <h4 tal:condition="exists:request/form/errmsg"
-            tal:content="request/form/errmsg">Error!</h4>
-
-      You can combine exists expressions with not expressions, for
-      example::
-
-        <p tal:condition="not:exists:request/form/number">Please enter
-        a number between 0 and 5</p>
-
-      Note that in this example you can't use the expression,
-      "not:request/form/number", since that expression will be true if
-      the 'number' variable exists and is zero.
-
-    Python Expressions
-
-      The Python programming language is a simple and expressive one.
-      If you have never encountered it before, you should read one of
-      the excellent tutorials or introductions available at the
-      "Python website":http://www.python.org.
-
-      A Page Template Python expression can contain anything that the
-      Python language considers an expression.  You can't use
-      statements such as 'if' and 'while'. In addition, Zope imposes
-      some security restrictions to keep you from accessing protected
-      information, changing secured data, and creating problems such
-      as infinite loops. See the chapter entitled "Advanced Zope
-      Scripting":ScriptingZope.stx for more information on Python
-      security restrictions.
-
-      Comparisons
-
-        One place where Python expressions are practically necessary
-        is in 'tal:condition' statements.  You usually want to compare
-        two strings or numbers, and there is no support in TAL to do
-        this without Python expressions.  In Python expressions, you
-        can use the comparison operators '<' (less than), '>' (greater
-        than), '==' (equal to), and '!=' (not equal to).  You can also
-        use the boolean operators 'and', 'not', and 'or'.  For
-        example::
-
-          <p tal:repeat="widget widgets">
-            <span tal:condition="python:widget.type == 'gear'">
-            Gear #<span tal:replace="repeat/widget/number>1</span>:
-            <span tal:replace="widget/name">Name</span>
-            </span>
-          </p>
-
-        This example loops over a collection of objects, printing
-        information about widgets which are of type 'gear'.
-
-        Sometimes you want to choose different values inside a single
-        statement based on one or more conditions.  You can do this
-        with the 'test' function, like this::
-
-          You <span tal:define="name user/getUserName"
-               tal:replace="python:test(name=='Anonymous User',
-                                       'need to log in', default)">
-                are logged in as
-                <span tal:replace="name">Name</span>
-              </span>
-
-        If the user is 'Anonymous', then the 'span' element is
-        replaced with the text 'need to log in'.  Otherwise, the
-        default content is used, which is in this case 'are logged in
-        as ...'.
-
-        The 'test' function works like an if/then/else statement. See
-        Appendix A, "DTML Reference":AppendixA.stx for more
-        information on the 'test' function. Here's another example of
-        how you can use the 'test' function::
-
-          <tr tal:define="oddrow repeat/item/odd"
-              tal:attributes="class python:test(oddrow, 'oddclass',
-                                                'evenclass')">
-
-        This assigns 'oddclass' and 'evenclass' class attributes to
-        alternate rows of the table, allowing them to be styled
-        differently in HTML output, for example.
-
-        Without the 'test' function you'd have to write two 'tr'
-        elements with different conditions, one for even rows,
-        and the other for odd rows.
-
-      Using other Expression Types
-
-        You can use other expression types inside of a Python
-        expression.  Each expression type has a corresponding function
-        with the same name, including: 'path()', 'string()',
-        'exists()', and 'nocall()'.  This allows you to write
-        expressions such as::
-
-          "python:path('here/%s/thing' % foldername)"
-          "python:path(string('here/$foldername/thing'))"
-          "python:path('request/form/x') or default"
-
-        The final example has a slightly different meaning than the
-        path expression, "request/form/x | default", since it will use
-        the default text if "request/form/x" doesn't exists *or* if it
-        is false.
-
-      Getting at Zope Objects
-
-        Much of the power of Zope involves tying together specialized
-        objects.  Your Page Templates can use Scripts, SQL Methods,
-        Catalogs, and custom content objects.  In order to use these
-        objects you have to know how to get access to them within Page
-        Templates.
-
-        Object properties are usually attributes, so you can get a
-        template's title with the expression "template.title". Most
-        Zope objects support acquisition, which allows you to get
-        attributes from "parent" objects.  This means that the Python
-        expression "here.Control_Panel" will acquire the Control Panel
-        object from the root Folder.  Object methods are attributes,
-        as in "here.objectIds" and "request.set".  Objects contained
-        in a Folder can be accessed as attributes of the Folder, but
-        since they often have Ids that are not valid Python
-        identifiers, you can't use the normal notation.  For example,
-        you cannot access the 'penguin.gif' object with the following
-        Python expression::
-
-          "python:here.penguin.gif"
-
-        Instead, you must write::
-
-          "python:getattr(here, 'penguin.gif')"
-
-        since Python doesn't support attribute names with periods.
-
-        Some objects, such as 'request', 'modules', and Zope Folders
-        support Python item access, for example::
-
-          request['URL']
-          modules['math']
-          here['thing']
-
-        When you use item access on a Folder, it doesn't try to
-        acquire the name, so it will only succeed if there is actually
-        an object with that Id contained in the Folder.
-
-        As shown in previous chapters, path expressions allow you to
-        ignore details of how you get from one object to the next.
-        Zope tries attribute access, then item access.  You can
-        write::
-
-          "here/images/penguin.gif"
-
-        instead of::
-
-          "python:getattr(here.images, 'penguin.gif')"
-
-        and::
-
-          "request/form/x" 
-
-        instead of::
-
-          "python:request.form['x']"
-
-        The trade-off is that path expressions don't allow you to
-        specify those details.  For instance, if you have a form
-        variable named "get", you must write::
-
-          "python:request.form['get']"
-
-        since this path expression::
-
-          "request/form/get" 
-
-        will evaluate to the "get" *method* of the form dictionary.
-
-        If you prefer you can use path expressions inside Python
-        expressions using the 'path()' function, as described above.
-
-      Using Scripts
-
-        Script objects are often used to encapsulate business logic
-        and complex data manipulation.  Any time that you find
-        yourself writing lots of TAL statements with complicated
-        expressions in them, you should consider whether you could do
-        the work better in a Script. If you have trouble understanding your
-        template statements and expressions, then it's better to
-        simplify your Page Template and use Scripts for the complex
-        stuff.
-
-        Each Script has a list of parameters that it expects to be
-        given when it is called.  If this list is empty, then you can
-        use the Script by writing a path expression.  Otherwise, you
-        will need to use a Python expression in order to supply the
-        argument, like this::
-
-          "python:here.myscript(1, 2)"
-          "python:here.myscript('arg', foo=request.form['x'])"
-
-        If you want to return more than one item of data from a Script
-        to a Page Template, it is a good idea to return it in a
-        dictionary.  That way, you can define a variable to hold all
-        the data, and use path expressions to refer to each item.  For
-        example, suppose the 'getPerson' script returns a dictionary
-        with 'name' and 'age' keys::
-
-          <span tal:define="person here/getPerson"
-                tal:replace="string:${person/name} is ${person/age}">
-          Name is 30</span> years old.
-
-        Of course, it's fine to return Zope objects and Python lists
-        as well.
-
-      Calling DTML
-
-        Unlike Scripts, DTML Methods and Documents don't have an
-        explicit parameter list.  Instead, they expect to be passed a
-        client, a mapping, and keyword arguments.  They use these
-        parameters to construct a namespace.  See the chapter entitled
-        "Variables and Advanced DTML":AdvDTML.stx for more information
-        on explicitly calling DTML.
-
-        When Zope publishes a DTML object through the web, it
-        passes the context of the object as the client, and the
-        REQUEST as the mapping.  When one DTML object calls another,
-        it passes its own namespace as the mapping, and no client.
-
-        If you use a path expression to render a DTML object, it will
-        pass a namespace with 'request', 'here', and the template's
-        variables already on it.  This means that the DTML object will
-        be able to use the same names as if it were being published in
-        the same context as the template, plus the variable names
-        defined in the template.  For example, here is a template that
-        uses a DTML Method to generate JavaScript::
-
-          <head tal:define="items here/getItems.sql">
-            <title tal:content="template/title">Title</title>
-            <script tal:content="structure here/jsItems"></script>
-          </head>
-          ...etc...
-
-        ...and here is the DTML Method 'jsItems'::
-
-          <dtml-let prefix="template.id">
-          <dtml-in items>
-          &dtml-prefix;_&dtml-name; = &dtml-value; ;
-          </dtml-in>
-          </dtml-let>
-
-        The DTML uses the template's 'id', and the 'items' variable
-        that it defined just before the call.
-
-      Python Modules
-
-        The Python language comes with a large number of modules,
-        which provide a wide variety of capabilities to Python
-        programs.  Each module is a collection of Python functions,
-        data, and classes related to a single purpose, such as
-        mathematical calculations or regular expressions.
-
-        Several modules, including "math" and "string", are available
-        in Python expressions by default.  For example, you can get
-        the value of pi from the math module by writing
-        "python:math.pi".  To access it from a path expression,
-        however, you need to use the 'modules' variable,
-        "modules/math/pi".
-
-        The "string" module is hidden in Python expressions by the
-        "string" expression type function, so you need to access it
-        through the 'modules' variable.  You can do this directly in
-        an expression in which you use it, or define a global variable
-        for it, like this::
-
-          tal:define="global mstring modules/string"
-          tal:replace="python:mstring.join(slist, ':')"
-
-        In practice you'll rarely need to do this since you can use
-        string methods most of the time rather than having to rely on
-        functions in the string module.
-
-        Modules can be grouped into packages, which are simply a way
-        of organizing and naming related modules.  For instance,
-        Zope's Python-based Scripts are provided by a collection of
-        modules in the "PythonScripts" subpackage of the Zope
-        "Products" package.  In particular, the "standard" module in
-        this package provides a number of useful formatting functions
-        that are standard in the DTML "var" tag.  The full name of
-        this module is "Products.PythonScripts.standard", so you could
-        get access to it using either of the following statements::
-
-          tal:define="global pps modules/Products.PythonScripts.standard"
-          tal:define="global pps python:modules['Products.PythonScripts.standard']"
-
-        Many Python modules cannot be accessed from Page Templates,
-        DTML, or Scripts unless you add Zope security assertions to
-        them.  See the "Zope Developer's Guide's security
-        chapter":http://www.zope.org/Documentation/ZDG/Security.stx
-        for more information on making more Python modules available
-        to your templates and scripts by using "ModuleSecurityInfo".
-
-  Remote Editing with FTP and WebDAV
-
-    You can edit Page Templates remotely with FTP and WebDAV, as well
-    as HTTP PUT publishing.  Using these methods, you can use Page
-    Templates without leaving advanced WYSIWYG editors such as
-    Macromedia Dreamweaver.  Follow these steps:
-
-      1. Make sure your server and editor are configured correctly.  See
-      "Using External Tools":ExternalTools.stx for instructions.
-
-      2. If you want to create a new *Page Template* with your editor, add a
-      '.pt' suffix to the file name before saving it.  This way Zope adds a
-      *Page Template* object. To add a template with an other suffix like
-      '.html' or '.zpt' or without a suffix like 'index_html', either rename
-      it after adding it with a '.pt' suffix are create it through the ZMI.
-
-      3. Edit your file with your editor and then save it.  When you save it
-      you should use the same URL you used to retrieve it.
-
-      4. Optionally reload your page after you edit it, to check for error
-      comments.
-
-    If you want to create files without the '.pt' suffix, it's also possible
-    to customize the *PUT factory*.  See the *PUT_factory* section of the
-    chapter entitled "Using External Tools":ExternalTools.stx for more
-    information.
-
-  Caching Templates
-
-    While rendering Page Templates normally is quite fast, sometimes
-    it's not fast enough. For frequently accessed pages, or pages that
-    take a long time to render, you may want to trade some dynamic
-    behavior for speed. Caching lets you do this. For more information
-    on caching see the "Cache Manager" section of the chapter entitled
-    "Zope Services":ZopeServices.stx.
-
-    You can cache Page Templates using a cache manager in the same way
-    that you cache other objects. To cache a Page Template, you must
-    associate it with a cache manager. You can either do this by going
-    to the *Cache* view of your Page Template and selecting the cache
-    manager (there must be one in the acquisition path of the template
-    for the *Cache* view to appear), or by going to the *Associate*
-    view of your cache manager and locating your Page Template.
-
-    Here's an example of how to cache a Page Template. First create a
-    Python-based script name 'long.py' with these contents::
-
-      ## Script (Python) "long.py"
-      ##
-      for i in range(500):
-        for j in range(500):
-          for k in range(5):
-            pass
-      return 'Done'
-
-    The purpose of this script is to take up a noticeable amount of
-    execution time. Now create a Page Template that uses this script,
-    for example::
-
-      <html>
-        <body>
-          <p tal:content="here/long.py">results</p>
-        </body>
-      </html>
-
-    Now view this page. Notice how it takes a while to render. Now
-    let's radically improve its rendering time with caching.  Create a
-    Ram Cache Manager if you don't already have one. Make sure to
-    create it within the same folder as your Page Template, or in a
-    higher level. Now visit the *Cache* view of your Page
-    Template. Choose the Ram Cache Manager you just created and click
-    *Save Changes*.  Click the *Cache Settings* link to see how your
-    Ram Cache Manager is configured.  By default, your cache stores
-    objects for one hour (3600 seconds). You may want to adjust this
-    number depending on your application. Now return to your Page
-    Template and view it again. It should take a while for it to
-    render. Now reload the page, and watch it render immediately. You
-    can reload the page again and again, and it will always render
-    immediately since the page is now cached.
-
-    If you change your Page Template, then it will be removed from the
-    cache. So the next time you view it, it will take a while to
-    render. But after that it will render quickly since it will be
-    cached again.
-
-    Caching is a simple but very powerful technique for improving
-    performance. You don't have to be a wizard to use caching, and it
-    can provide great speed-ups. It's well worth your time to use
-    caching for performance-critical applications.
-
-    For more information on caching in the context of Zope, see the
-    chapter entitled "Zope Services":ZopeServices.stx.
-
-  Page Template Utilities
-
-    Zope Page Templates are powerful but simple. Unlike DTML, Page
-    Templates don't give you a lot of convenience features for things
-    like batching, drawing trees, sorting, etc. The creators of Page
-    Templates wanted to keep them simple. However, you may miss some
-    of the built-in features that DTML provides. To address these
-    needs, Zope comes with utilities designed to enhance Page
-    Templates.
-
-    Batching Large Sets of Information
-
-      When a user queries a database and gets hundreds of results, it's
-      often better to show them several pages with only twenty results
-      per page, rather than putting all the results on one
-      page. Breaking up large lists into smaller lists is called
-      *batching*.
-
-      Unlike DTML, which provides batching built into the language,
-      Page Templates support batching by using a special 'Batch'
-      object that comes from the 'ZTUtils' utility module.  See
-      Appendix B, "API Reference":AppendixB.stx, for more information
-      on the 'ZTUtils' Python module.
-
-      Here's a simple example, showing how to create a 'Batch'
-      object::
-
-        <ul tal:define="lots python:range(100);
-                        batch python:modules['ZTUtils'].Batch(lots, 
-                                                              size=10,
-                                                              start=0)">
-          <li tal:repeat="num batch"
-              tal:content="num">0
-          </li>
-        </ul>
-
-      This example renders a list with 10 items (in this case, the
-      numbers 0 through 9). The 'Batch' object chops a long list up
-      into groups or batches. In this case it broke a one hundred item
-      list up into batches of ten items.
-
-      You can display a different batch of ten items by passing a
-      different start number::
-
-        <ul tal:define="lots python:range(100);
-                        batch python:modules['ZTUtils'].Batch(lots, 
-                                                              size=10,
-                                                              start=13)">
-
-      This batch starts with the fourteenth item and ends with the
-      twenty third item. In other words, it displays the numbers 13
-      through 22. It's important to notice that the batch 'start'
-      argument is the *index* of the first item. Indexes count from
-      zero, rather than from one. So index 13 points to the fourteenth
-      item in the sequence. Python uses indexes to refer to list
-      items. 
-
-      Normally when you use batches you'll want to include navigation
-      elements on the page to allow users to go from batch to batch.
-      Here's a full-blow batching example that shows how to navigate
-      between batches::
-
-        <html>
-          <head>
-            <title tal:content="template/title">The title</title>
-          </head>
-          <body tal:define="employees here/getEmployees;
-                 start python:int(path('request/start | nothing') or 0);
-                 batch python:modules['ZTUtils'].Batch(employees, 
-                                                       size=3, 
-                                                       start=start);
-                 previous python:batch.previous;
-                 next python:batch.next">
-
-          <p>
-            <a tal:condition="previous"
-               tal:attributes="href string:${request/URL0}?start:int=${previous/first}"
-               href="previous_url">previous</a>
-            <a tal:condition="next"
-               tal:attributes="href string:${request/URL0}?start:int=${next/first}"
-               href="next_url">next</a>
-          </p>
-
-          <ul tal:repeat="employee batch" >
-            <li>
-              <span tal:replace="employee/name">Bob Jones</span>
-              makes $<span tal:replace="employee/salary">100,000</span>
-              a year.
-            </li>
-          </ul>
-
-          </body>
-        </html>
-
-      Define a Script (Python) with the name getEmployees in the same
-      folder with the following body (no parameters are necessary)::
-
-        return [  {'name': 'Chris McDonough', 'salary':'5'},
-                  {'name': 'Guido van Rossum', 'salary': '10'},
-                  {'name': 'Casey Duncan', 'salary':'20' },
-                  {'name': 'Andrew Sawyers', 'salary':'30' },
-                  {'name': 'Evan Simpson', 'salary':'35' }, 
-                  {'name': 'Stephanie Hand', 'salary':'40' }, ]
-
-      This example iterates over batches of results from the
-      'getEmployees' method. It draws a *previous* and a *next* link
-      as necessary to allow you to page through all the results a
-      batch at a time.  The batch size in this case is 3.
-
-      Take a look at the 'tal:define' statement on the 'body'
-      element. It defines a bunch of batching variables. The
-      'employees' variable is a list of employee objects returned by
-      the 'getEmployees' Script.  It is not very big now, but it could
-      grow fairly large (especially if it were a call into a SQL
-      Method of *real* employees). The second variable, 'start', is
-      either set to the value of 'request/start' or to zero if there
-      is no 'start' variable in the request.  The 'start' variable
-      keeps track of where you are in the list of employees. The
-      'batch' variable is a batch of ten items from the lists of
-      employees. The batch starts at the location specified by the
-      'start' variable. The 'previous' and 'next' variables refer to
-      the previous and next batches (if any). Since all these
-      variables are defined on the 'body' element, they are available
-      to all elements inside the body.
-
-      Next let's look at the navigation links. They create hyper links
-      to browse previous and next batches. The 'tal:condition'
-      statement first tests to see if there is a previous and next
-      batch. If there is a previous or next batch, then the link is
-      rendered, otherwise there is no link. The 'tal:attributes'
-      statement creates a link to the previous and next batches. The
-      link is simply the URL or the current page ('request/URL0')
-      along with a query string indicating the start index of the
-      batch. For example, if the current batch starts with index 10,
-      then the previous batch will start with an index of 0. The
-      'first' variable of a batch gives its starting index, so in this
-      case, 'previous.start' would be 0.
-
-      It's not important to fully understand the workings of this
-      example. Simply copy it, or use a batching example created by
-      the *Z Search Interface*. Later when you want to do more complex
-      batching you can experiment by changing the example code. Don't
-      forget to consult Appendix B, "API Reference":AppendixB.stx for
-      more information on the 'ZTUtils' module and 'Batch' objects.
-
-    Miscellaneous Utilities
-
-      Zope provides a couple Python modules which may come in handy
-      when using Page Templates. The 'string', 'math', and 'random'
-      modules can be used in Python expressions for string formatting,
-      math function, and pseudo-random number generation. These same
-      modules are available from DTML and Python-based scripts. 
-
-      The 'Products.PythonScripts.standard' module is designed to
-      provide utilities to Python-based scripts, but it's also useful
-      for Page Templates. It includes various string and number
-      formatting functions.
-
-      As mentioned earlier in the chapter, the 'sequence' module
-      provides a handy 'sort' function.
-
-      Finally the 'AccessControl' module includes a function and a
-      class which you'll need if you want to test access and to get
-      the authenticated user.
-
-      See Appendix B, "API Reference":AppendixB.stx for more
-      information on these utilities.
-
-  Conclusion
-
-    This chapter covers some useful and some obscure nooks and
-    crannies of Page Templates, and after reading it you may feel a
-    bit overwhelmed. Don't worry, you don't need to know everything
-    in this chapter to effectively use Page Templates. You should
-    understand the different path types and macros, but you can come
-    back to the rest of the material when you need it. The advanced
-    features that you've learned about in this chapter are there for
-    you if and when you need them.
\ No newline at end of file

Copied: zope2book/trunk/source/AdvZPT.rst (from rev 96424, zope2book/trunk/AdvZPT.stx)
===================================================================
--- zope2book/trunk/source/AdvZPT.rst	                        (rev 0)
+++ zope2book/trunk/source/AdvZPT.rst	2009-02-10 22:22:14 UTC (rev 96426)
@@ -0,0 +1,1519 @@
+Advanced Page Templates
+=======================
+
+In the chapter entitled `Using Zope Page Templates <ZPT.html>`_ you
+learned the basic features of Page Templates. In this chapter
+you'll learn about advanced techniques including new types of
+expressions.
+
+Advanced TAL
+------------
+
+In this section we'll go over all TAL statements and their various
+options in depth.  This material is covered more concisely in
+`Appendix C: Zope Page Templates Reference <AppendixC.html>`_.
+
+In this chapter, the terms 'tag' and 'element' are used in the
+sense laid out by the `XHTML spec
+<http://www.w3.org/TR/2000/REC-xhtml1-20000126/#defs>`_.
+"&lt;p&gt;" is a *tag*, while the entire block
+"&lt;p&gt;stuff&lt;/p&gt;" from opening tag through the closing
+tag is an *element*.
+
+Advanced Content Insertion
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You've already seen how 'tal:content' and 'tal:replace' work in
+the chapter entitled `Using Zope Page Templates`_. In
+this section you'll learn some advanced tricks for inserting
+content.
+
+Inserting Structure
+%%%%%%%%%%%%%%%%%%%
+
+Normally, the 'tal:replace' and 'tal:content' statements
+convert HTML tags and entities in the text that they insert
+into an "escaped" form that appears in the resulting document
+as plain text rather than HTML markup.
+For instance, the '<' character is "escaped" to '&amp;lt;'.
+If you want to insert text as part of the HTML structure of
+your document, avoiding this conversion , you need to
+precede the expression with the 'structure' keyword.
+
+This feature is useful when you are inserting a fragment of
+HTML or XML that is stored in a property or generated by
+another Zope object.  For instance, you may have news items
+that contain simple HTML markup such as bold and italic text
+when they are rendered, and you want to preserve this when
+inserting them into a "Top News" page.  In this case, you
+might write::
+
+  <p tal:repeat="newsItem here/topNews"
+     tal:content="structure newsItem">
+    A news item with<code>HTML</code> markup.
+  </p>
+
+This will insert the news items' HTML into a series of
+paragraphs.  The built-in variable 'here' refers to the folder
+in which the template is rendered; See the "Expressions"
+section further below in this chapter for more information on
+'here'.  In this case, we use 'here' as the starting point for
+finding the Zope object 'topNews', which is presumably a list
+of news items or a Script which fetches such a list.
+
+The 'structure' keyword prevents the text of each newsItem
+value from being escaped.  It doesn't matter whether the text
+actually contains any HTML markup, since 'structure' really
+means "leave this text alone".  This behavior
+is not the default because most of the text that you insert
+into a template will *not* contain HTML, but may contain
+characters that would interfere with the structure of your page.
+
+Dummy Elements
+%%%%%%%%%%%%%%
+
+You can include page elements that are visible in the template
+but not in generated text by using the built-in variable
+'nothing', like this::
+
+  <tr tal:replace="nothing">
+    <td>10213</td><td>Example Item</td><td>$15.34</td>
+  </tr>
+
+This can be useful for filling out parts of the page that will
+be populated with dynamic content.  For instance, a table that
+usually has ten rows will only have one row in the template.
+By adding nine dummy rows, the template's layout will look
+more like the final result.
+
+Default Content
+%%%%%%%%%%%%%%%
+
+You can leave the contents of an element alone by using the
+'default' expression with 'tal:content' or 'tal:replace'. For
+example::
+
+  <p tal:content="default">Spam</p>
+
+This renders to::
+
+  <p>Spam</p>
+
+Most often you will want to selectively include default
+content, rather than always including it. For example::
+
+  <p tal:content="python:here.getFood() or default">Spam</p>
+
+.. note:
+   
+   Python expressions are explained later in the chapter. If the
+   'getFood' method returns a true value then its result will be
+   inserted into the paragraph, otherwise it's Spam for dinner.
+
+Advanced Repetition
+~~~~~~~~~~~~~~~~~~~
+
+You've already seen most of what you can do with the
+'tal:repeat' statement in the chapter entitled `Using Zope Page
+Templates`_. This section covers a few advanced features
+of the 'tal:repeat' statement.
+
+Repeat Variables
+%%%%%%%%%%%%%%%%
+
+One topic that bears more explanation are repeat
+variables. Repeat variables provide information about the
+current repetition. The following attributes are available on
+'repeat' variables:
+
+- *index* - repetition number, starting from zero.
+
+- *number* - repetition number, starting from one.
+
+- *even* - true for even-indexed repetitions (0, 2, 4, ...).
+
+- *odd* - true for odd-indexed repetitions (1, 3, 5, ...).
+
+- *start* - true for the starting repetition (index 0).
+
+- *end* - true for the ending, or final, repetition.
+
+- *length* - length of the sequence, which will be the total number
+  of repetitions.
+
+- *letter* - count reps with lower-case letters: "a" - "z",
+  "aa" - "az", "ba" - "bz", ..., "za" - "zz", "aaa" - "aaz", and
+  so forth.
+
+- *Letter* - upper-case version of *letter*.
+
+You can access the contents of a repeat variable using path
+expressions or Python expressions.  In path expressions, you
+write a three-part path consisting of the name 'repeat', the
+statement variable's name, and the name of the information you
+want, for example, 'repeat/item/start'.  In Python expressions,
+you use normal dictionary notation to get the repeat variable,
+then attribute access to get the information, for example,
+'python:repeat['item'].start'.  The reason that you can't
+simply write 'repeat/start' is that 'tal:repeat' statements
+can be nested, so you need to be able to specify which one you
+want information about.
+
+Repetition Tips
+%%%%%%%%%%%%%%%
+
+Here are a couple practical tips that you may find
+useful. Sometimes you'd like to repeat part of your template,
+but there is no naturally enclosing element.  In this case,
+you must add an enclosing element, but you want to prevent
+it from appearing in the rendered page. You can do this with
+the 'tal:omit-tag' statement::
+
+  <div tal:repeat="section here/getSections"
+       tal:omit-tag="">
+    <h4 tal:content="section/title">Title</h4>
+    <p tal:content="section/text">quotation</p>
+  </div>
+
+This is not just a matter of saving a few characters in the
+rendered output.  Including the 'div' tags in the output could
+affect the page layout, especially if it has stylesheets. We
+use the tal 'omit-tag' statement to disinclude the 'div' tag
+(and its pair closing tag) while leaving its contents
+unmolested.  The 'tal:omit-tag' statement is described in more
+detail later in this chapter.
+
+While it's been mentioned before, it's worth saying again: you
+can nest 'tal:repeat' statements inside each other. Each
+'tal:repeat' statement must have a different repeat variable
+name. Here's an example that shows a math times-table::
+
+  <table border="1">
+    <tr tal:repeat="x python:range(1, 13)">
+      <td tal:repeat="y python:range(1, 13)"
+          tal:content="python:'%d x %d = %d' % (x, y, x*y)">
+          X x Y = Z
+      </td>
+    </tr>
+  </table>
+
+This example uses Python expressions, which are covered later
+in this chapter.
+
+If you've done much work with the 'dtml-in' DTML repetition
+statement, you will have encountered batching. Batching is the
+process of chopping up a large list into smaller lists. You
+typically use it to display a small number of items from a
+large list on a web page. Think of how a search engine batches
+its search results. The 'tal:repeat' statement does not
+support batching, but Zope comes with a batching utility. See
+the section, "Batching" later in this chapter.
+
+Another useful feature that isn't supplied by 'tal:repeat' is
+sorting. If you want to sort a list you can either write
+your own sorting script (which is quite easy in Python) or you
+can use the 'sequence.sort' utility function. Here's an
+example of how to sort a list of objects by title, and then by
+modification date::
+
+  <table tal:define="objects here/objectValues;
+                     sort_on python:(('title', 'nocase', 'asc'),
+                                     ('bobobase_modification_time', 'cmp', 'desc'));
+                     sorted_objects python:sequence.sort(objects, sort_on)">
+    <tr tal:repeat="item sorted_objects">
+      <td tal:content="item/title">title</td>
+      <td tal:content="item/bobobase_modification_time">
+        modification date</td>  
+    </tr>
+  </table>
+
+This example tries to make things clearer by defining the sort
+arguments outside the 'sort' function.  The 'sequence.sort'
+function takes a sequence and a description of how to sort
+it. In this example the description of how to sort the sequence
+is defined in the 'sort_on' variable.  See `Appendix B: API
+Reference <AppendixB.html>`_ for more information on the powerful
+'sequence.sort' function.
+
+Advanced Attribute Control
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You've already met the 'tal:attributes' statement. You can use
+it to dynamically replace tag attributes, for example, the
+'href' attribute on an 'a' element. You can replace more than
+one attribute on a tag by separating attributes with
+semicolons. For example, the code below will generate an
+"href" and a "class" attribute::
+
+  <a href="link"
+     tal:attributes="href here/getLink;
+                     class here/getClass">link</a>
+
+You can also define attributes with XML namespaces. For example::
+
+  <Description 
+      dc:Creator="creator name"
+      tal:attributes="dc:Creator here/owner/getUserName">
+    Description</Description>
+
+Simply put the XML namespace prefix before the attribute name
+and you can create attributes with XML namespaces.
+
+Defining Variables
+~~~~~~~~~~~~~~~~~~
+
+You can define your own variable using the 'tal:define'
+attribute. There are several reasons that you might want to do
+this. One reason is to avoid having to write long expressions
+repeatedly in a template. Another is to avoid having to call
+expensive methods repeatedly. You can define a variable once
+within an element on a tag and then use it many times within
+elements which are enclosed by this tag. For example, here's a
+list that defines a variable and later tests it and repeats over
+it::
+
+  <ul tal:define="items container/objectIds"
+      tal:condition="items">
+    <li tal:repeat="item items">
+      <p tal:content="item">id</p>
+    </li>
+  </ul>
+
+The 'tal:define' statement creates the variable 'items', which
+you can use anywhere in the 'ul' element.  Notice also how you
+can have two TAL statements on the same 'ul' tag.  See the
+section "Interactions Between TAL Statements" later in this
+chapter for more information about using more than one statement
+on a tag.  In this case the first statement assigns the variable
+'items' and the second uses 'items' in a condition to see
+whether it is false (in this case, an empty sequence) or
+true. If the 'items' variable is false, then the 'ul' element is not
+shown.
+
+Now, suppose that instead of simply removing the list when there
+are no items, you want to show a message.  To do this, place the
+following before the list::
+
+  <h4 tal:condition="not:container/objectIds">There
+  Are No Items</h4>
+
+The expression, 'not:container/objectIds' is true when
+'container/objectIds' is false, and vice versa. See the section,
+"Not Expressions" later in this chapter for more information.
+
+You can't use your 'items' variable here, because it isn't
+defined yet.  If you move the definition of 'items' to the 'h4'
+element, then you can't use it in the 'ul' element any more, because it
+becomes a *local* variable of the 'h4' element.  You could place the
+definition on some element that enclosed both the 'h4' and the 'ul',
+but there is a simpler solution.  By placing the keyword
+'global' in front of the variable name, you can make the
+definition last from the 'span' tag to the bottom of the
+template::
+
+  <span tal:define="global items container/objectIds"></span>
+  <h4 tal:condition="not:items">There Are No Items</h4>
+
+You can define more than one variable using 'tal:define' by
+separating them with semicolons. For example::
+
+  <p tal:define="ids container/objectIds; 
+                 title container/title">
+
+You can define as many variables as you wish. Each variable can
+have its own global or local scope. You can also refer to
+earlier defined variables in later definitions. For example::
+
+  <p tal:define="title template/title;
+                 global untitled not:title;
+                 tlen python:len(title);">
+
+In this case, both 'title' and 'tlen' are local to the
+paragraph, but 'untitled' is global. With judicious use of
+'tal:define' you can improve the efficiency and readability of
+your templates.
+
+Omitting Tags
+~~~~~~~~~~~~~
+
+You can remove tags with the 'tal:omit-tag' statement. You will
+seldom need to use this TAL statement, but occasionally it's
+useful. The omit-tag attribute removes opening and closing tags,
+but does not affect the contents of the element. For example::
+
+  <b tal:omit-tag=""><i>this</i> stays</b>
+
+Renders to::
+
+  <i>this</i> stays
+
+At this level of usage, 'tal:omit-tag' operates almost like
+'tal:replace="default"'. However, 'tal:omit-tag' can also be
+used with a true/false expression, in which case it only removes
+the tags if the expression is true. For example::
+
+  Friends: <span tal:repeat="friend friends">
+    <b tal:omit-tag="not:friend/best"
+       tal:content="friend/name">Fred</b>
+  </span>
+
+This will produce a list of friends, with our "best" friend's
+name in bold.
+
+Error Handling  
+~~~~~~~~~~~~~~
+
+If an error occurs in your page template, you can catch that
+error and show a useful error message to your user.  For
+example, suppose your template defines a
+variable using form data::
+
+  ...
+  <span tal:define="global prefs request/form/prefs"
+        tal:omit-tag="" />
+  ...
+
+If Zope encounters a problem, like not being able to find the
+'prefs' variable in the form data, the entire page will break;
+you'll get an error page instead. Happily, you can avoid this
+kind of thing with limited error handling using the
+'tal:on-error' statement::
+
+  ...
+  <span tal:define="global prefs here/scriptToGetPreferences"
+        tal:omit-tag=""
+        tal:on-error="string:An error occurred">
+  ...
+
+When an error is raised while rendering a template, Zope looks
+for a 'tal:on-error' statement to handle the error. It first
+looks in the current element, then on its enclosing element, and so on
+until it reaches the top-level element. When it finds an error
+handler, it replaces the contents of that element with the error
+handling expression. In this case, the 'span' element will contain
+an error message.
+
+Typically you'll define an error handler on an element that encloses
+a logical page element, for example a table. If an error crops
+up drawing the table, then the error handler can simply omit the
+table from the page, or else replace it with an error message of
+some sort.
+
+For more flexible error handling you can call a script. For
+example::
+
+  <div tal:on-error="structure here/handleError">
+  ...
+  </div>
+
+Any error that occurs inside the 'div' will call the
+'handleError' script. Note that the 'structure' option allows
+the script to return HTML. Your error handling script can
+examine the error and take various actions depending on the
+error. Your script gets access to the error through the 'error'
+variable in the namespace. For example::
+
+  ## Script (Python) "handleError"
+  ##bind namespace=_
+  ##
+  error=_['error']
+  if error.type==ZeroDivisionError:
+      return "<p>Can't divide by zero.</p>"
+  else:
+      return """<p>An error occurred.</p>
+                <p>Error type: %s</p>
+                <p>Error value: %s</p>""" % (error.type,
+                                             error.value)
+
+Your error handling script can take all kinds of actions, for
+example, it might log the error by sending email.
+
+The 'tal:on-error' statement is not meant for general purpose
+exception handling. For example, you shouldn't validate form
+input with it. You should use a script for that, since scripts
+allow you to do powerful exception handling. The 'tal:on-error'
+statement is for dealing with unusual problems that can occur
+when rendering templates.
+
+Interactions Between TAL Statements
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When there is only one TAL statement per element, the order in
+which they are executed is simple. Starting with the root
+element, each element's statements are executed, then each of
+its child elements are visited, in order, and their statements
+are executed, and so on.
+
+However, it's possible to have more than one TAL statement on
+the same element. Any combination of statements may appear on
+the same element, except that the 'tal:content' and
+'tal:replace' statements may not appear together.
+
+When an element has multiple statements, they are executed in
+this order:
+
+1. define
+
+2. condition
+
+3. repeat
+
+4. content or replace
+
+5. attributes
+
+6. omit-tag
+
+Since the 'tal:on-error' statement is only invoked when an error
+occurs, it does not appear in the list.
+
+The reasoning behind this ordering goes like this: you often
+want to set up variables for use in other statements, so define
+comes first. The very next thing to do is decide whether this
+element will be included at all, so condition is next; since the
+condition may depend on variables you just set, it comes after
+define. It is valuable to be able to replace various parts of an
+element with different values on each iteration of a repeat, so
+repeat comes before content, replace and attributes. Content and
+replace can't both be used on the same element so they occur at
+the same place. Omit-tag comes last since no other statements are
+likely to depend on it and since it should come after define and
+repeat.
+
+Here's an example element that includes several TAL 
+statements::
+
+  <p tal:define="x /root/a/long/path/x | nothing"
+     tal:condition="x"
+     tal:content="x/txt"
+     tal:attributes="class x/class">Ex Text</p>
+
+Notice how the 'tal:define' statement is executed first, and the
+other statements rely on its results.
+
+There are three limits you should be aware of when combining TAL
+statements on elements:
+
+1. Only one of each kind of statement can be used on a single
+   tag.  Since HTML does not allow multiple attributes with the
+   same name. For example, you can't have two 'tal:define' on the
+   same tag.
+
+2. Both of 'tal:content' and 'tal:replace' cannot be used on
+   the same tag, since their functions conflict.
+
+3. The order in which you write TAL attributes on a tag does
+   not affect the order in which they execute.  No matter how
+   you arrange them, the TAL statements on a tag always execute
+   in the fixed order described earlier.
+
+If you want to override the ordering of TAL statements, you must
+do so by enclosing the element in another element and placing
+some of the statements on this new element. For example suppose
+you want to loop over a series of items but skip some. Here's an
+attempt to write a template that loops over the numbers zero to
+nine and skips three::
+
+  <!-- broken template -->
+  <ul>
+    <li tal:repeat="n python:range(10)"
+        tal:condition="python:n != 3"
+        tal:content="n"> 
+      1
+    </li>
+  </ul>
+
+This template doesn't work due to TAL statement execution order.
+Despite the order in which they are written, the condition is
+always tested before the repeat is executed. This results in a
+situation in which the 'n' variable is not defined until after
+it is tested, which ultimately causes an error when you attempt
+to test or otherwise view the template. Here's a way around this
+problem::
+
+  <ul>
+    <div tal:repeat="n python:range(10)"
+         tal:omit-tag="">
+      <li tal:condition="python:n != 3"
+          tal:content="n"> 
+        1
+      </li>
+    </div>
+  </ul>
+
+This template solves the problem by defining the 'n' variable on
+an enclosing 'div' element. Notice that the 'div' tag will not
+appear in the output due to its 'tal:omit-tag' statement.
+
+Although 'span' and 'div' are natural choices for this in HTML,
+there is, in general, no equivalent natural element in XML.  In
+this case, you can use TAL's namespace in a new way: while TAL
+does not define any tags, it doesn't prohibit any either.  You
+can make up any tag name you like within the TAL namespace, and
+use it to make an element, like so::
+
+  <tal:series define="items here/getItems">
+    <tal:items repeat="item items">
+    <tal:parts repeat="part item">
+      <part tal:content="part">Part</part>
+    </tal:parts>
+    </tal:items>
+    <noparts tal:condition="not:items" />
+  </tal:series>
+
+The 'tal:series', 'tal:items', and 'tal:parts' tags in this
+example should be acceptable to tools that handle XML namespaces
+properly, and to many HTML tools.  This method has two
+additional advantages over a 'div'.  First, TAL tags are omitted
+just like TAL attributes, so no 'tal:omit-tag' is necessary.
+Second, TAL attributes in these tags don't require their
+own 'tal:' prefix, since they inherit the namespace of the tag.
+The METAL namespace can be used in exactly the same fashion.
+
+Form Processing
+~~~~~~~~~~~~~~~
+
+You can process forms in DTML using a common pattern called the
+"form/action pair".  A form/action pair consists of two DTML
+methods or documents: one that contains a form that collects
+input from the user, and one that contains an action that is
+taken on that input and returns the user a response.  The form
+calls the action.  See the chapter entitled `Dynamic Content
+with DTML <DTML.html>`_ for more information on the form/action
+pattern.
+
+Zope Page Templates don't work particularly well with the
+form/action pattern since it assumes that input processing and
+response presentation are handled by the same object (the
+action). Instead of the form/action pattern you should use
+form/action/response pattern with Page Templates. The form and
+response should be Page Templates and the action should be a
+script. The form template gathers the input and calls the action
+script. The action script should process the input and return a
+response template. This pattern is more flexible than the
+form/action pattern since it allows the script to return any of
+a number of different response objects.
+
+For example here's a part of a form template::
+
+  ...
+  <form action="action">
+    <input type="text" name="name">
+    <input type="text" name="age:int">
+    <input type="submit">
+  </form>
+  ...
+
+This form could be processed by this script::
+
+  ## Script (Python) "action"
+  ##parameters=name, age
+  ##
+  container.addPerson(name, age)
+  return container.responseTemplate()
+
+This script calls a method to process the input and then
+returns another template, the response. You can render a Page
+Template from Python by calling it. The response template
+typically contains an acknowledgment that the form has been
+correctly processed.
+
+The action script can do all kinds of things. It can validate
+input, handle errors, send email, or whatever it needs to do to
+"get the job done".  Here's a sketch of how to validate input
+with a script::
+
+  ## Script (Python) "action"
+  ##
+  if not context.validateData(request):
+      # if there's a problem return the form page template
+      # along with an error message
+      return context.formTemplate(error_message='Invalid data')
+
+  # otherwise return the thanks page
+  return context.responseTemplate()
+
+This script validates the form input and returns the form
+template with an error message if there's a problem. The
+Script's 'context' variable is equivalent to 'here' in
+TALES. You can pass Page Templates extra information with
+keyword arguments. The keyword arguments are available to the
+template via the 'options' built-in variable. So the form
+template in this example might include a section like this::
+
+  <span tal:condition="options/error_message | nothing">
+  Error: <b tal:content="options/error_message">
+    Error message goes here.
+  </b></span>
+
+This example shows how you can display an error message that is
+passed to the template via keyword arguments. Notice the use of
+'| nothing' to handle the case where no 'error_message' argument
+has been passed to the template.
+
+Depending on your application you may choose to redirect the
+user to a response Page Template instead of returning it
+directly. This results in twice as much network activity, but
+might be useful because it changes the URL displayed in the
+user's browser to the URL of the Page Template, rather than that
+of the action script.
+
+If you need to set up a quick-and-dirty form, you can always
+create a version of the form-action pair using Page Templates
+alone. You should only do this when you don't care about error
+handling and when the response will always be the same, no
+matter what the user submits. Since Page Templates don't have an
+equivalent of 'dtml-call', you can use one of any number of
+hacks to call an input processing method without inserting its
+results. For example::
+
+  <span tal:define="unused here/processInputs" 
+        tal:omit-tag=""/>
+
+This sample calls the 'processInputs' method and assigns the
+result to the 'unused' variable.
+
+Expressions
+-----------
+
+You've already encountered Page Template expressions. Expressions
+provide values to template statements. For example, in the TAL
+statement '<td tal:content="request/form/age">Age</td>', the
+expression of the statement is 'request/form/age'.
+'request/form/age' is an example of a *path expression*.  Path
+expressions describe objects by giving them paths such as
+'request/form/age', or 'user/getUserName'. Expressions only work
+in the context of a TAL statement; they do not work in "normal"
+HTML inserted in your page templates.  In this section you'll
+learn about all the different types of expressions, and variables.
+
+Built-in Page Template Variables
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Variables are names that you can use in expressions. You have
+already seen some examples of the built-in variables such as
+'template', 'user', 'repeat', and 'request'.  Here is the
+complete list of the other built-in variables and their uses.
+Note that these variables are different than the built-in
+variables that you would use in a Script (Python), they are only
+effective for Page Templates:
+
+'nothing'
+  A false value, similar to a blank string, that you
+  can use in 'tal:replace' or 'tal:content' to erase an element or
+  its contents.  If you set an attribute to 'nothing', the
+  attribute is removed from the tag (or not inserted).  A blank
+  string, on the other hand, would insert the tag with an empty
+  value, as in 'alt=""'.
+
+'default'
+  A special value that doesn't change anything when
+  used in 'tal:replace', 'tal:content', or 'tal:attributes'.  It
+  leaves the template text in place.
+
+'options'
+  The keyword arguments, if any, that were passed to
+  the template. When a template is rendered from the web, no
+  options are present. Options are only available when a template
+  is called from Python or by similarly complex means.  For
+  example, when the template 't' is called by the Python expression
+  't(foo=1)', the path 'options/foo' equals '1'.
+
+'attrs'
+  A dictionary of attributes of the current tag in the
+  template.  The keys are the attributes names, and the values are
+  the original values of the attributes in the template. This
+  variable is rarely needed.
+
+'root'
+  The root Zope object.  Use this to get Zope objects
+  from fixed locations, no matter where your template is placed or
+  called.
+
+'here'
+  The object on which the template is being called.
+  This is often the same as the *container*, but can be different
+  if you are using acquisition.  Use this to get Zope objects that
+  you expect to find in different places depending on how the
+  template is called. The 'here' variable is analogous to the
+  'context' variable in Python-based scripts.
+
+'container'
+  The container (usually a Folder) in which the
+  template is kept.  Use this to get Zope objects from locations
+  relative to the template's permanent home. The 'container' and
+  'here' variables refer to the same object when a template is
+  called from its normal location. However, when a template is
+  applied to another object (for example, a ZSQL Method) the
+  'container' and 'here' will not refer to the same object.
+
+'modules'
+  The collection of Python modules available to
+  templates.  See the section on writing Python expressions.
+
+You'll find examples of how to use these variables throughout
+this chapter.
+
+String Expressions
+~~~~~~~~~~~~~~~~~~
+
+String expressions allow you to easily mix path expressions with
+text.  All of the text after the leading 'string:' is taken and
+searched for path expressions.  Each path expression must be
+preceded by a dollar sign ('$').  Here are some examples::
+
+  "string:Just text. There's no path here."
+  "string:copyright $year by Fred Flintstone."
+
+If the path expression has more than one part (if it contains a
+slash), or needs to be separated from the text that follows it,
+it must be surrounded by braces ('{}'). For example::
+
+  "string:Three ${vegetable}s, please."
+  "string:Your name is ${user/getUserName}!"
+
+Notice how in the example above, you need to surround the
+'vegetable' path with braces so that Zope doesn't mistake it for
+'vegetables'.
+
+Since the text is inside of an attribute value, you can only
+include a double quote by using the entity syntax '&quot;'.
+Since dollar signs are used to signal path expressions, a
+literal dollar sign must be written as two dollar signs
+('$$'). For example::
+
+  "string:Please pay $$$dollars_owed"
+  "string:She said, &quot;Hello world.&quot;"
+
+Some complex string formatting operations (such as search and
+replace or changing capitalization) can't easily be done with
+string expressions. For these cases, you should use Python
+expressions or Scripts.
+
+Path Expressions
+~~~~~~~~~~~~~~~~
+
+Path expressions refer to objects with a path that resembles a
+URL path. A path describes a traversal from object to
+object. All paths begin with a known object (such as a built-in
+variable, a repeat variable, or a user defined variable) and
+depart from there to the desired object. Here are some example
+paths expressions::
+
+  template/title
+  container/files/objectValues
+  user/getUserName
+  container/master.html/macros/header
+  request/form/address
+  root/standard_look_and_feel.html
+
+With path expressions you can traverse from an object to its
+sub-objects including properties and methods. You can also use
+acquisition in path expressions. See the section entitled
+"Calling Scripts from the Web" in the chapter entitled `Advanced
+Zope Scripting <ScriptingZope.html>`_ for more information on
+acquisition and path traversal.
+
+Zope restricts object traversal in path expressions in the same
+way that it restricts object access via URLs. You must have
+adequate permissions to access an object in order to refer to it
+with a path expression. See the chapter entitled `Users and
+Security <Security.html>`_ for more information about object access
+controls.
+
+Alternate Paths
+%%%%%%%%%%%%%%%
+
+The path 'template/title' is guaranteed to exist every time
+the template is used, although it may be a blank string.  Some
+paths, such as 'request/form/x', may not exist during some
+renderings of the template.  This normally causes an error
+when Zope evaluates the path expression.
+
+When a path doesn't exist, you may have a fall-back path or
+value that you would like to use instead.  For instance, if
+'request/form/x' doesn't exist, you might want to use 'here/x'
+instead.  You can do this by listing the paths in order of
+preference, separated by vertical bar characters ('|')::
+
+  <h4 tal:content="request/form/x | here/x">Header</h4>
+
+Two variables that are very useful as the last path in a list
+of alternates are 'nothing' and 'default'.  For example,
+'default' tells 'tal:content' to leave the dummy
+content. Different TAL statements interpret 'default' and
+'nothing' differently. See `Appendix C: Zope Page Templates
+Reference`_ for more information.
+
+You can also use a non-path expression as the final part in an
+alternate-path expression. For example::
+
+  <p tal:content="request/form/age|python:18">age</p>
+
+In this example, if the 'request/form/age' path doesn't exist,
+then the value is the number 18. This form allows you to
+specify default values to use which can't be expressed as
+paths. Note, you can only use a non-path expression as the
+last alternative.
+
+.. note::
+
+   TODO:
+
+   This spec should describe the following path expression syntax::
+
+    <span tal:content="here/?myvar" />
+
+   The "here/?myvar" expression is evaluated by traversing from
+   "here" to the name given by the value of "myvar". For
+   example, if "myvar" is set to "title", "here/?myvar" is
+   equivalent to "here/title".
+
+You can also test the existence of a path directly with the
+*exists* expression type prefix. See the section "Exists
+Expressions" below for more information on exists expressions.
+
+Not Expressions
+~~~~~~~~~~~~~~~
+
+Not expressions let you negate the value of other
+expressions. For example::
+
+  <p tal:condition="not:here/objectIds">
+    There are no contained objects.
+  </p>
+
+Not expressions return true when the expression they are applied
+to is false, and vice versa. In Zope, zero, empty strings, empty
+sequences, nothing, and None are considered false, while
+everything else is true.  Non-existent paths are neither true
+nor false, and applying a 'not:' to such a path will fail.
+
+There isn't much reason to use not expressions with Python
+expressions since you can use the Python 'not' keyword instead.
+
+Nocall Expressions
+~~~~~~~~~~~~~~~~~~
+
+An ordinary path expression tries to render the object
+that it fetches.  This means that if the object is a function,
+Script, Method, or some other kind of executable thing, then
+the expression will evaluate to the result of calling the object.
+This is usually what you want, but not always.  For example,
+if you want to put a DTML Document into a variable so that
+you can refer to its properties, you can't use a normal path
+expression because it will render the Document into a string.
+
+If you put the 'nocall:' expression type prefix in front of a
+path, it prevents the rendering and simply gives you the
+object.  For example::
+
+  <span tal:define="doc nocall:here/aDoc"
+        tal:content="string:${doc/getId}: ${doc/title}">
+  Id: Title</span>
+
+This expression type is also valuable when you want to define
+a variable to hold a function or class from a module, for use
+in a Python expression.
+
+Nocall expressions can also be used on functions, rather than
+objects::
+
+  <p tal:define="join nocall:modules/string/join">
+
+This expression defines the 'join' variable as a function
+('string.join'), rather than the result of calling a function.
+
+Exists Expressions
+~~~~~~~~~~~~~~~~~~
+
+An exists expression is true if its path exists, and otherwise
+is false.  For example here's one way to display an error
+message only if it is passed in the request::
+
+  <h4 tal:define="err request/form/errmsg | nothing"
+      tal:condition="err" 
+      tal:content="err">Error!</h4>
+
+You can do the same thing more easily with an exists
+expression::
+
+  <h4 tal:condition="exists:request/form/errmsg"
+      tal:content="request/form/errmsg">Error!</h4>
+
+You can combine exists expressions with not expressions, for
+example::
+
+  <p tal:condition="not:exists:request/form/number">Please enter
+  a number between 0 and 5</p>
+
+Note that in this example you can't use the expression,
+"not:request/form/number", since that expression will be true if
+the 'number' variable exists and is zero.
+
+Python Expressions
+~~~~~~~~~~~~~~~~~~
+
+The Python programming language is a simple and expressive one.
+If you have never encountered it before, you should read one of
+the excellent tutorials or introductions available at the
+`Python website <http://www.python.org>`_.
+
+A Page Template Python expression can contain anything that the
+Python language considers an expression.  You can't use
+statements such as 'if' and 'while'. In addition, Zope imposes
+some security restrictions to keep you from accessing protected
+information, changing secured data, and creating problems such
+as infinite loops. See the chapter entitled `Advanced Zope
+Scripting`_ for more information on Python
+security restrictions.
+
+Comparisons
+%%%%%%%%%%%
+
+One place where Python expressions are practically necessary
+is in 'tal:condition' statements.  You usually want to compare
+two strings or numbers, and there is no support in TAL to do
+this without Python expressions.  In Python expressions, you
+can use the comparison operators '<' (less than), '>' (greater
+than), '==' (equal to), and '!=' (not equal to).  You can also
+use the boolean operators 'and', 'not', and 'or'.  For
+example::
+
+  <p tal:repeat="widget widgets">
+    <span tal:condition="python:widget.type == 'gear'">
+    Gear #<span tal:replace="repeat/widget/number>1</span>:
+    <span tal:replace="widget/name">Name</span>
+    </span>
+  </p>
+
+This example loops over a collection of objects, printing
+information about widgets which are of type 'gear'.
+
+Sometimes you want to choose different values inside a single
+statement based on one or more conditions.  You can do this
+with the 'test' function, like this::
+
+  You <span tal:define="name user/getUserName"
+       tal:replace="python:test(name=='Anonymous User',
+                               'need to log in', default)">
+        are logged in as
+        <span tal:replace="name">Name</span>
+      </span>
+
+If the user is 'Anonymous', then the 'span' element is
+replaced with the text 'need to log in'.  Otherwise, the
+default content is used, which is in this case 'are logged in
+as ...'.
+
+The 'test' function works like an if/then/else statement. See
+`Appendix A: DTML Reference <AppendixA.html>`_ for more
+information on the 'test' function. Here's another example of
+how you can use the 'test' function::
+
+  <tr tal:define="oddrow repeat/item/odd"
+      tal:attributes="class python:test(oddrow, 'oddclass',
+                                        'evenclass')">
+
+This assigns 'oddclass' and 'evenclass' class attributes to
+alternate rows of the table, allowing them to be styled
+differently in HTML output, for example.
+
+Without the 'test' function you'd have to write two 'tr'
+elements with different conditions, one for even rows,
+and the other for odd rows.
+
+Using other Expression Types
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+You can use other expression types inside of a Python
+expression.  Each expression type has a corresponding function
+with the same name, including: 'path()', 'string()',
+'exists()', and 'nocall()'.  This allows you to write
+expressions such as::
+
+  "python:path('here/%s/thing' % foldername)"
+  "python:path(string('here/$foldername/thing'))"
+  "python:path('request/form/x') or default"
+
+The final example has a slightly different meaning than the
+path expression, "request/form/x | default", since it will use
+the default text if "request/form/x" doesn't exists *or* if it
+is false.
+
+Getting at Zope Objects
+%%%%%%%%%%%%%%%%%%%%%%%
+
+Much of the power of Zope involves tying together specialized
+objects.  Your Page Templates can use Scripts, SQL Methods,
+Catalogs, and custom content objects.  In order to use these
+objects you have to know how to get access to them within Page
+Templates.
+
+Object properties are usually attributes, so you can get a
+template's title with the expression "template.title". Most
+Zope objects support acquisition, which allows you to get
+attributes from "parent" objects.  This means that the Python
+expression "here.Control_Panel" will acquire the Control Panel
+object from the root Folder.  Object methods are attributes,
+as in "here.objectIds" and "request.set".  Objects contained
+in a Folder can be accessed as attributes of the Folder, but
+since they often have Ids that are not valid Python
+identifiers, you can't use the normal notation.  For example,
+you cannot access the 'penguin.gif' object with the following
+Python expression::
+
+  "python:here.penguin.gif"
+
+Instead, you must write::
+
+  "python:getattr(here, 'penguin.gif')"
+
+since Python doesn't support attribute names with periods.
+
+Some objects, such as 'request', 'modules', and Zope Folders
+support Python item access, for example::
+
+  request['URL']
+  modules['math']
+  here['thing']
+
+When you use item access on a Folder, it doesn't try to
+acquire the name, so it will only succeed if there is actually
+an object with that Id contained in the Folder.
+
+As shown in previous chapters, path expressions allow you to
+ignore details of how you get from one object to the next.
+Zope tries attribute access, then item access.  You can
+write::
+
+  "here/images/penguin.gif"
+
+instead of::
+
+  "python:getattr(here.images, 'penguin.gif')"
+
+and::
+
+  "request/form/x" 
+
+instead of::
+
+  "python:request.form['x']"
+
+The trade-off is that path expressions don't allow you to
+specify those details.  For instance, if you have a form
+variable named "get", you must write::
+
+  "python:request.form['get']"
+
+since this path expression::
+
+  "request/form/get" 
+
+will evaluate to the "get" *method* of the form dictionary.
+
+If you prefer you can use path expressions inside Python
+expressions using the 'path()' function, as described above.
+
+Using Scripts
+%%%%%%%%%%%%%
+
+Script objects are often used to encapsulate business logic
+and complex data manipulation.  Any time that you find
+yourself writing lots of TAL statements with complicated
+expressions in them, you should consider whether you could do
+the work better in a Script. If you have trouble understanding your
+template statements and expressions, then it's better to
+simplify your Page Template and use Scripts for the complex
+stuff.
+
+Each Script has a list of parameters that it expects to be
+given when it is called.  If this list is empty, then you can
+use the Script by writing a path expression.  Otherwise, you
+will need to use a Python expression in order to supply the
+argument, like this::
+
+  "python:here.myscript(1, 2)"
+  "python:here.myscript('arg', foo=request.form['x'])"
+
+If you want to return more than one item of data from a Script
+to a Page Template, it is a good idea to return it in a
+dictionary.  That way, you can define a variable to hold all
+the data, and use path expressions to refer to each item.  For
+example, suppose the 'getPerson' script returns a dictionary
+with 'name' and 'age' keys::
+
+  <span tal:define="person here/getPerson"
+        tal:replace="string:${person/name} is ${person/age}">
+  Name is 30</span> years old.
+
+Of course, it's fine to return Zope objects and Python lists
+as well.
+
+Calling DTML
+%%%%%%%%%%%%
+
+Unlike Scripts, DTML Methods and Documents don't have an
+explicit parameter list.  Instead, they expect to be passed a
+client, a mapping, and keyword arguments.  They use these
+parameters to construct a namespace.  See the chapter entitled
+`Variables and Advanced DTML <AdvDTML.html>`_ for more information
+on explicitly calling DTML.
+
+When Zope publishes a DTML object through the web, it
+passes the context of the object as the client, and the
+REQUEST as the mapping.  When one DTML object calls another,
+it passes its own namespace as the mapping, and no client.
+
+If you use a path expression to render a DTML object, it will
+pass a namespace with 'request', 'here', and the template's
+variables already on it.  This means that the DTML object will
+be able to use the same names as if it were being published in
+the same context as the template, plus the variable names
+defined in the template.  For example, here is a template that
+uses a DTML Method to generate JavaScript::
+
+  <head tal:define="items here/getItems.sql">
+    <title tal:content="template/title">Title</title>
+    <script tal:content="structure here/jsItems"></script>
+  </head>
+  ...etc...
+
+...and here is the DTML Method 'jsItems'::
+
+  <dtml-let prefix="template.id">
+  <dtml-in items>
+  &dtml-prefix;_&dtml-name; = &dtml-value; ;
+  </dtml-in>
+  </dtml-let>
+
+The DTML uses the template's 'id', and the 'items' variable
+that it defined just before the call.
+
+Python Modules
+%%%%%%%%%%%%%%
+
+The Python language comes with a large number of modules,
+which provide a wide variety of capabilities to Python
+programs.  Each module is a collection of Python functions,
+data, and classes related to a single purpose, such as
+mathematical calculations or regular expressions.
+
+Several modules, including "math" and "string", are available
+in Python expressions by default.  For example, you can get
+the value of pi from the math module by writing
+"python:math.pi".  To access it from a path expression,
+however, you need to use the 'modules' variable,
+"modules/math/pi".
+
+The "string" module is hidden in Python expressions by the
+"string" expression type function, so you need to access it
+through the 'modules' variable.  You can do this directly in
+an expression in which you use it, or define a global variable
+for it, like this::
+
+  tal:define="global mstring modules/string"
+  tal:replace="python:mstring.join(slist, ':')"
+
+In practice you'll rarely need to do this since you can use
+string methods most of the time rather than having to rely on
+functions in the string module.
+
+Modules can be grouped into packages, which are simply a way
+of organizing and naming related modules.  For instance,
+Zope's Python-based Scripts are provided by a collection of
+modules in the "PythonScripts" subpackage of the Zope
+"Products" package.  In particular, the "standard" module in
+this package provides a number of useful formatting functions
+that are standard in the DTML "var" tag.  The full name of
+this module is "Products.PythonScripts.standard", so you could
+get access to it using either of the following statements::
+
+  tal:define="global pps modules/Products.PythonScripts.standard"
+  tal:define="global pps python:modules['Products.PythonScripts.standard']"
+
+Many Python modules cannot be accessed from Page Templates,
+DTML, or Scripts unless you add Zope security assertions to
+them.  See the `Zope Developer's Guide's security
+chapter <http://www.zope.org/Documentation/ZDG/Security.stx>`_
+for more information on making more Python modules available
+to your templates and scripts by using "ModuleSecurityInfo".
+
+Remote Editing with FTP and WebDAV
+----------------------------------
+
+You can edit Page Templates remotely with FTP and WebDAV, as well
+as HTTP PUT publishing.  Using these methods, you can use Page
+Templates without leaving advanced WYSIWYG editors such as
+Macromedia Dreamweaver.  Follow these steps:
+
+1. Make sure your server and editor are configured correctly.  See
+   `Using External Tools <ExternalTools.html>`_ for instructions.
+
+2. If you want to create a new *Page Template* with your editor, add a
+   '.pt' suffix to the file name before saving it.  This way Zope adds a
+   *Page Template* object. To add a template with an other suffix like
+   '.html' or '.zpt' or without a suffix like 'index_html', either rename
+   it after adding it with a '.pt' suffix are create it through the ZMI.
+
+3. Edit your file with your editor and then save it.  When you save it
+   you should use the same URL you used to retrieve it.
+
+4. Optionally reload your page after you edit it, to check for error
+   comments.
+
+If you want to create files without the '.pt' suffix, it's also possible
+to customize the *PUT factory*.  See the *PUT_factory* section of the
+chapter entitled `Using External Tools`_ for more
+information.
+
+Caching Templates
+-----------------
+
+While rendering Page Templates normally is quite fast, sometimes
+it's not fast enough. For frequently accessed pages, or pages that
+take a long time to render, you may want to trade some dynamic
+behavior for speed. Caching lets you do this. For more information
+on caching see the "Cache Manager" section of the chapter entitled
+`Zope Services <ZopeServices.html>`_.
+
+You can cache Page Templates using a cache manager in the same way
+that you cache other objects. To cache a Page Template, you must
+associate it with a cache manager. You can either do this by going
+to the *Cache* view of your Page Template and selecting the cache
+manager (there must be one in the acquisition path of the template
+for the *Cache* view to appear), or by going to the *Associate*
+view of your cache manager and locating your Page Template.
+
+Here's an example of how to cache a Page Template. First create a
+Python-based script name 'long.py' with these contents::
+
+  ## Script (Python) "long.py"
+  ##
+  for i in range(500):
+    for j in range(500):
+      for k in range(5):
+        pass
+  return 'Done'
+
+The purpose of this script is to take up a noticeable amount of
+execution time. Now create a Page Template that uses this script,
+for example::
+
+  <html>
+    <body>
+      <p tal:content="here/long.py">results</p>
+    </body>
+  </html>
+
+Now view this page. Notice how it takes a while to render. Now
+let's radically improve its rendering time with caching.  Create a
+Ram Cache Manager if you don't already have one. Make sure to
+create it within the same folder as your Page Template, or in a
+higher level. Now visit the *Cache* view of your Page
+Template. Choose the Ram Cache Manager you just created and click
+*Save Changes*.  Click the *Cache Settings* link to see how your
+Ram Cache Manager is configured.  By default, your cache stores
+objects for one hour (3600 seconds). You may want to adjust this
+number depending on your application. Now return to your Page
+Template and view it again. It should take a while for it to
+render. Now reload the page, and watch it render immediately. You
+can reload the page again and again, and it will always render
+immediately since the page is now cached.
+
+If you change your Page Template, then it will be removed from the
+cache. So the next time you view it, it will take a while to
+render. But after that it will render quickly since it will be
+cached again.
+
+Caching is a simple but very powerful technique for improving
+performance. You don't have to be a wizard to use caching, and it
+can provide great speed-ups. It's well worth your time to use
+caching for performance-critical applications.
+
+For more information on caching in the context of Zope, see the
+chapter entitled `Zope Services`_.
+
+Page Template Utilities
+-----------------------
+
+Zope Page Templates are powerful but simple. Unlike DTML, Page
+Templates don't give you a lot of convenience features for things
+like batching, drawing trees, sorting, etc. The creators of Page
+Templates wanted to keep them simple. However, you may miss some
+of the built-in features that DTML provides. To address these
+needs, Zope comes with utilities designed to enhance Page
+Templates.
+
+Batching Large Sets of Information
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When a user queries a database and gets hundreds of results, it's
+often better to show them several pages with only twenty results
+per page, rather than putting all the results on one
+page. Breaking up large lists into smaller lists is called
+*batching*.
+
+Unlike DTML, which provides batching built into the language,
+Page Templates support batching by using a special 'Batch'
+object that comes from the 'ZTUtils' utility module.  See
+`Appendix B: API Reference`_, for more information
+on the 'ZTUtils' Python module.
+
+Here's a simple example, showing how to create a 'Batch'
+object::
+
+  <ul tal:define="lots python:range(100);
+                  batch python:modules['ZTUtils'].Batch(lots, 
+                                                        size=10,
+                                                        start=0)">
+    <li tal:repeat="num batch"
+        tal:content="num">0
+    </li>
+  </ul>
+
+This example renders a list with 10 items (in this case, the
+numbers 0 through 9). The 'Batch' object chops a long list up
+into groups or batches. In this case it broke a one hundred item
+list up into batches of ten items.
+
+You can display a different batch of ten items by passing a
+different start number::
+
+  <ul tal:define="lots python:range(100);
+                  batch python:modules['ZTUtils'].Batch(lots, 
+                                                        size=10,
+                                                        start=13)">
+
+This batch starts with the fourteenth item and ends with the
+twenty third item. In other words, it displays the numbers 13
+through 22. It's important to notice that the batch 'start'
+argument is the *index* of the first item. Indexes count from
+zero, rather than from one. So index 13 points to the fourteenth
+item in the sequence. Python uses indexes to refer to list
+items. 
+
+Normally when you use batches you'll want to include navigation
+elements on the page to allow users to go from batch to batch.
+Here's a full-blow batching example that shows how to navigate
+between batches::
+
+  <html>
+    <head>
+      <title tal:content="template/title">The title</title>
+    </head>
+    <body tal:define="employees here/getEmployees;
+           start python:int(path('request/start | nothing') or 0);
+           batch python:modules['ZTUtils'].Batch(employees, 
+                                                 size=3, 
+                                                 start=start);
+           previous python:batch.previous;
+           next python:batch.next">
+
+    <p>
+      <a tal:condition="previous"
+         tal:attributes="href string:${request/URL0}?start:int=${previous/first}"
+         href="previous_url">previous</a>
+      <a tal:condition="next"
+         tal:attributes="href string:${request/URL0}?start:int=${next/first}"
+         href="next_url">next</a>
+    </p>
+
+    <ul tal:repeat="employee batch" >
+      <li>
+        <span tal:replace="employee/name">Bob Jones</span>
+        makes $<span tal:replace="employee/salary">100,000</span>
+        a year.
+      </li>
+    </ul>
+
+    </body>
+  </html>
+
+Define a Script (Python) with the name getEmployees in the same
+folder with the following body (no parameters are necessary)::
+
+  return [  {'name': 'Chris McDonough', 'salary':'5'},
+            {'name': 'Guido van Rossum', 'salary': '10'},
+            {'name': 'Casey Duncan', 'salary':'20' },
+            {'name': 'Andrew Sawyers', 'salary':'30' },
+            {'name': 'Evan Simpson', 'salary':'35' }, 
+            {'name': 'Stephanie Hand', 'salary':'40' }, ]
+
+This example iterates over batches of results from the
+'getEmployees' method. It draws a *previous* and a *next* link
+as necessary to allow you to page through all the results a
+batch at a time.  The batch size in this case is 3.
+
+Take a look at the 'tal:define' statement on the 'body'
+element. It defines a bunch of batching variables. The
+'employees' variable is a list of employee objects returned by
+the 'getEmployees' Script.  It is not very big now, but it could
+grow fairly large (especially if it were a call into a SQL
+Method of *real* employees). The second variable, 'start', is
+either set to the value of 'request/start' or to zero if there
+is no 'start' variable in the request.  The 'start' variable
+keeps track of where you are in the list of employees. The
+'batch' variable is a batch of ten items from the lists of
+employees. The batch starts at the location specified by the
+'start' variable. The 'previous' and 'next' variables refer to
+the previous and next batches (if any). Since all these
+variables are defined on the 'body' element, they are available
+to all elements inside the body.
+
+Next let's look at the navigation links. They create hyper links
+to browse previous and next batches. The 'tal:condition'
+statement first tests to see if there is a previous and next
+batch. If there is a previous or next batch, then the link is
+rendered, otherwise there is no link. The 'tal:attributes'
+statement creates a link to the previous and next batches. The
+link is simply the URL or the current page ('request/URL0')
+along with a query string indicating the start index of the
+batch. For example, if the current batch starts with index 10,
+then the previous batch will start with an index of 0. The
+'first' variable of a batch gives its starting index, so in this
+case, 'previous.start' would be 0.
+
+It's not important to fully understand the workings of this
+example. Simply copy it, or use a batching example created by
+the *Z Search Interface*. Later when you want to do more complex
+batching you can experiment by changing the example code. Don't
+forget to consult `Appendix B: API Reference`_ for
+more information on the 'ZTUtils' module and 'Batch' objects.
+
+Miscellaneous Utilities
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Zope provides a couple Python modules which may come in handy
+when using Page Templates. The 'string', 'math', and 'random'
+modules can be used in Python expressions for string formatting,
+math function, and pseudo-random number generation. These same
+modules are available from DTML and Python-based scripts. 
+
+The 'Products.PythonScripts.standard' module is designed to
+provide utilities to Python-based scripts, but it's also useful
+for Page Templates. It includes various string and number
+formatting functions.
+
+As mentioned earlier in the chapter, the 'sequence' module
+provides a handy 'sort' function.
+
+Finally the 'AccessControl' module includes a function and a
+class which you'll need if you want to test access and to get
+the authenticated user.
+
+See `Appendix B: API Reference`_ for more
+information on these utilities.
+
+Conclusion
+----------
+
+This chapter covers some useful and some obscure nooks and
+crannies of Page Templates, and after reading it you may feel a
+bit overwhelmed. Don't worry, you don't need to know everything
+in this chapter to effectively use Page Templates. You should
+understand the different path types and macros, but you can come
+back to the rest of the material when you need it. The advanced
+features that you've learned about in this chapter are there for
+you if and when you need them.

Modified: zope2book/trunk/source/index.rst
===================================================================
--- zope2book/trunk/source/index.rst	2009-02-10 21:58:00 UTC (rev 96425)
+++ zope2book/trunk/source/index.rst	2009-02-10 22:22:14 UTC (rev 96426)
@@ -24,6 +24,7 @@
    SimpleExamples.rst
    Security.rst
    AdvDTML.rst
+   AdvZPT.rst
    AppendixA.rst
    Contributions.rst
 



More information about the Checkins mailing list