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  <ZPT.html>`_. In
this section you'll learn some advanced tricks for inserting
content.

Inserting Structure
%%%%%%%%%%%%%%%%%%%

Normally, the 'tal:replace' and 'tal:content' statements
convert HTML tags and entities in the text that they insert
into an "escaped" form that appears in the resulting document
as plain text rather than HTML markup.
For instance, the '<' character is "escaped" to '&amp;lt;'.
If you want to insert text as part of the HTML structure of
your document, avoiding this conversion , you need to
precede the expression with the 'structure' keyword.

This feature is useful when you are inserting a fragment of
HTML or XML that is stored by an object 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 context/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 'context' refers to the folder in which the template is rendered; See
the "Expressions" section further below in this chapter for more information on
'context'. In this case, we use 'context' 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:context.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.html>`_. 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.

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 context/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.

One 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::

  <table tal:define="objects context/objectValues;
                     sort_on python:(('title', 'nocase', 'asc'),);
                     sorted_objects python:sequence.sort(objects, sort_on)">
    <tr tal:repeat="item sorted_objects">
      <td tal:content="item/title">title</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 context/getLink;
                     class context/getClass">link</a>

You can also define attributes with XML namespaces. For example::

  <Description 
      dc:Creator="creator name"
      tal:attributes="dc:Creator context/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. To have it available on both tags, you can place the definition on
some element that encloses both the 'h4' and the 'ul' for example the 'body'.

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;
                 untitled not:title;
                 tlen python:len(title);">

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="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="prefs context/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 context/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 context/getItems">
    <tal:items repeat="item items">
    <tal:parts repeat="part item">
      <p tal:content="part">Part</p>
    </tal:parts>
    </tal:items>
    <p tal:condition="not:items">No parts!</p>
  </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
~~~~~~~~~~~~~~~

With Zope Page Templates you can use the form/action/response pattern. 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.

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 'context' 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. 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 context/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.

'context'
  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.

'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
  'context' 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 'context' will not refer to the same object.

'modules'
  The collection of Python modules available to
  templates.  See the section on writing Python expressions.

You'll find examples of how to use these variables throughout
this chapter.

String Expressions
~~~~~~~~~~~~~~~~~~

String expressions allow you to easily mix path expressions with
text.  All of the text after the leading 'string:' is taken and
searched for path expressions.  Each path expression must be
preceded by a dollar sign ('$').  Here are some examples::

  "string:Just text. There's no path here."
  "string:copyright $year by Fred Flintstone."

If the path expression has more than one part (if it contains a
slash), or needs to be separated from the text that follows it,
it must be surrounded by braces ('{}'). For example::

  "string:Three ${vegetable}s, please."
  "string:Your name is ${user/getUserName}!"

Notice how in the example above, you need to surround the
'vegetable' path with braces so that Zope doesn't mistake it for
'vegetables'.

Since the text is inside of an attribute value, you can only
include a double quote by using the entity syntax '&quot;'.
Since dollar signs are used to signal path expressions, a
literal dollar sign must be written as two dollar signs
('$$'). For example::

  "string:Please pay $$$dollars_owed"
  "string:She said, &quot;Hello world.&quot;"

Some complex string formatting operations (such as search and
replace or changing capitalization) can't easily be done with
string expressions. For these cases, you should use Python
expressions or Scripts.

Path Expressions
~~~~~~~~~~~~~~~~

Path expressions refer to objects with a path that resembles a
URL path. A path describes a traversal from object to
object. All paths begin with a known object (such as a built-in
variable, a repeat variable, or a user defined variable) and
depart from there to the desired object. Here are some example
paths expressions::

  template/title
  container/files/objectValues
  user/getUserName
  container/master.html/macros/header
  request/form/address
  root/standard_look_and_feel.html

With path expressions you can traverse from an object to its
sub-objects including properties and methods. You can also use
acquisition in path expressions. See the section entitled
"Calling Scripts from the Web" in the chapter entitled `Advanced
Zope Scripting <ScriptingZope.html>`_ for more information on
acquisition and path traversal.

Zope restricts object traversal in path expressions in the same
way that it restricts object access via URLs. You must have
adequate permissions to access an object in order to refer to it
with a path expression. See the chapter entitled `Users and
Security <Security.html>`_ for more information about object access
controls.

Alternate Paths
%%%%%%%%%%%%%%%

The path 'template/title' is guaranteed to exist every time
the template is used, although it may be a blank string.  Some
paths, such as 'request/form/x', may not exist during some
renderings of the template.  This normally causes an error
when Zope evaluates the path expression.

When a path doesn't exist, you may have a fall-back path or
value that you would like to use instead.  For instance, if
'request/form/x' doesn't exist, you might want to use 'context/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 | context/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.

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:context/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 page template into a variable so that
you can refer to its properties, you can't use a normal path
expression because it will render the template 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="page nocall:context/aPage"
        tal:content="string:${page/getId}: ${page/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.html>`_ 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 and and or operators, like this::

  You <span tal:define="name user/getUserName"
       tal:replace="python:name=='Anonymous User' and
                           'need to log in' or 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 ...'.

This operator combinaion works like an if/then/else statement.
Here's another example of how you can use this pattern::

  <tr tal:define="oddrow repeat/item/odd"
      tal:attributes="class python:oddrow and 'oddclass' or '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 this pattern you could also 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('context/%s/thing' % foldername)"
  "python:path(string('context/$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 "context.Control_Panel" will acquire the Control Panel
object from the root Folder.  Object methods are attributes,
as in "context.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:context.penguin.gif"

Instead, you must write::

  "python:getattr(context, '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']
  context['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::

  "context/images/penguin.gif"

instead of::

  "python:getattr(context.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:context.myscript(1, 2)"
  "python:context.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 context/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.

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 variable
for it, like this::

  tal:define="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" namespace package.  In particular, the "standard" module in
this package provides a number of useful formatting functions. 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
or Scripts unless you add Zope security assertions to
them.  See the `Zope Developer's Guide's security
chapter <http://www.zope.org/Documentation/Books/ZDG/current/Security.stx>`_
for more information on making more Python modules available
to your templates and scripts by using "ModuleSecurityInfo".

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(250):
    for j in range(250):
      for k in range(250):
        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="context/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.html>`_.

Page Template Utilities
-----------------------

Zope Page Templates are powerful but simple.
They 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. 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*.

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 context/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 in 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.
