[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 '&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 '"'.
- 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, "Hello world.""
-
- 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>`_.
+"<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`_. 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 '&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 '"'.
+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, "Hello world.""
+
+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