Page Templates

Documentation Status

Page Templates provide an elegant templating mechanism that achieves a clean separation of presentation and application logic while allowing for designers to work with templates in their visual editing tools (FrontPage, Dreamweaver, GoLive, etc.).

This is the documentation for the page template languages, including TAL and METAL.

For detailed documentation, see https://pagetemplates.readthedocs.io

Documentation

Using Zope Page Templates

Page Templates are a web page generation tool. They help programmers and designers collaborate in producing dynamic web pages. Designers can use them to maintain pages without having to abandon their tools, while preserving the work required to embed those pages in an application.

Here, you’ll learn the basic features of Page Templates, including how you can use them in your website to create dynamic web pages easily. In the section entitled Advanced Page Templates, you’ll learn about advanced Page Template features.

The goal of Page Templates is to allow designers and programmers to work together easily. A designer can use a WYSIWYG HTML editor to create a template, then a programmer can edit it to make it part of an application. If required, the designer can load the template back into his editor and make further changes to its structure and appearance. By taking reasonable steps to preserve the changes made by the programmer, the designer will not disrupt the application.

Page Templates aim at this goal by adopting three principles:

  1. Play nicely with editing tools.
  2. What you see is very similar to what you get.
  3. Keep code out of templates, except for structural logic.

A Page Template is like a model of the pages that it will generate. In particular, it is parseable by most HTML tools.

Implementation Note

In this documentation, you will occasionally see boxes like this one. They contain extra information specific to one or more implementations of Page Templates.

Note

When this document refers to the “page template implementation,”, “ZPT implementation” or the “implementation”, it means the software that parses and renders page templates to produce output. This could be a framework like Zope, or it could a separate library like zope.pagetemplate. For more information, see Page Template Implementations.

HTML Page Templates

Page Templates can operate in two modes: HTML Mode and XML Mode. Later in this section we will show you how to use the XML Mode, but in most cases we want to use the HTML Mode which is also the default mode. For the HTML Mode the Content-Type has to be set to ‘text/html’.

HTML isn’t XML-conform and can’t be extended by a template language. So while rendered HTML Page Templates should return valid HTML, their source code isn’t valid HTML or XML. But the Template Attribute Language (TAL) does a good job in hiding itself in HTML tags, so most HTML tools will be able to parse the source of HTML Page Templates and just ignore the TAL attributes.

As you might already know, XHTML is a XML-conform reformulation of HTML and widely used in our days. Nevertheless, generating HTML and XHTML with Page Templates works exactly the same way. While the HTML Mode doesn’t enforce well-formed XML, it’s absolutely fine to use this mode also for XHTML.

How Page Templates Work

Page Templates use the Template Attribute Language (TAL). TAL consists of special tag attributes. For example, a dynamic page headline might look like this:

<h1 tal:content="context/title">Sample Page Title</h1>

The ‘tal:content’ attribute is a TAL statement. Since it has an XML namespace (the ‘tal:’ part) most editing tools will not complain that they don’t understand it, and will not remove it. It will not change the structure or appearance of the template when loaded into a WYSIWYG editor or a web browser. The name content indicates that it will set the text contained by the ‘h1’ tag, and the value ‘context/title’ is an expression providing the text to insert into the tag. Given the text specified by ‘context/title’ resolves to “Susan Jones Home Page”, the generated HTML snippet looks like this:

<h1>Susan Jones Home Page</h1>

All TAL statements consist of tag attributes whose name starts with ‘tal:’ and all TAL statements have values associated with them. The value of a TAL statement is shown inside quotes. See TAL Reference for more information on TAL.

To the HTML designer using a WYSIWYG tool, the dynamic headline example is perfectly parseable HTML, and shows up in their editor looking like a headline should look like. In other words, Page Templates play nicely with editing tools.

This example also demonstrates the principle that “What you see is very similar to what you get”. When you view the template in an editor, the headline text will act as a placeholder for the dynamic headline text. The template provides an example of how generated documents will look.

When this template is saved and viewed by a user, the page template implementation turns the dummy content into dynamic content, replacing “Sample Page Title” with whatever ‘context/title’ resolves to. In this case, ‘context/title’ resolves to the title of the object to which the template is applied. This substitution is done dynamically, when the template is viewed.

There are template statements for replacing entire tags, their contents, or just some of their attributes. You can repeat a tag several times or omit it entirely. You can join parts of several templates together, and specify simple error handling. All of these capabilities are used to generate document structures. Despite these capabilities, you can’t create subroutines or classes, perform complex flow control, or easily express complex algorithms using a Page Template. For these tasks, you should use Python-based Scripts or application components.

The Page Template language is deliberately not as powerful and general-purpose as it could be. It is meant to be used inside of a framework (such as Zope) in which other objects handle business logic and tasks unrelated to page layout.

For instance, template language would be useful for rendering an invoice page, generating one row for each line item, and inserting the description, quantity, price, and so on into the text for each row. It would not be used to create the invoice record in a database or to interact with a credit card processing facility.

TALES Expressions

The expression “context/title” in your simple Page Template is a path expression. This is the most common type of expression. There are several other types of expressions defined by the TAL Expression Syntax (TALES) specification. For more information on TALES see TALES Reference.

Path Expressions

The ‘context/title’ path expression fetches the title attribute of the context. Here are some other common path expressions:

  • ‘context/objectValues’: A list of the sub-objects of the folder on which the template is called.
  • ‘request/URL’: The URL of the current web request.
  • ‘user/getUserName’: The authenticated user’s login name.

Every path expression starts with a variable name. The available variable names refer either to objects like context, request or user that are bound to every Page Template by default or variables defined within the Page Template using TAL. Note that here is an old alias of context and still used in many places.

The small set of built-in variables such as request and user is described in the section entitled Advanced Page Templates. You will also learn how to define your own variables in that section.

If the variable itself returns the value you want, you are done. Otherwise, you add a slash (‘/’) and the name of a sub-object or attribute. You may need to work your way through several sub-objects to get to the value you’re looking for.

Python Expressions

A good rule of thumb is that if you need Python to express your logic, you better factor out the code into a script (Python function or class). But page templates can be used for prototyping and sometimes it would be overkill to write a script for one line of code. And looking at existing templates you will see quite often ‘Python expressions’, so it’s better to know them.

Recall the first example of this section:

<h1 tal:content="context/title">Sample Page Title</h1>

Let’s try to rewrite it using a Python expression:

<h1 tal:content="python: context.title">Sample Page Title</h1>

While path expressions are the default, we need a prefix to indicate other expression types. This expression with the prefix ‘python:’ does (at least here) the same as the path expression above. Path expressions try different ways to access ‘title’, so in general they are more flexible, but less explicit.

There are some simple things you can’t do with path expressions. The most common are comparing values like in:

"python: variable1 == variable2"

or passing arguments to methods, e.g.:

"python: context.objectValues(['Folder'])"
TAL Attributes

Page Templates are example pages or snippets. TAL statements define how to convert them dynamically. Depending on the used TAL attribute they substitute example content or attributes by dynamic values, or remove or repeat example elements depending on dynamic values.

Here’s another example:

<html>
  <body>
    <p>
      This is <b tal:content="template/title">the Title</b>.
    </p>
  </body>
</html>
Inserting Text

In the above template, you used the ‘tal:content’ statement on a bold tag. When you render it, the template implementation replaced the content of the HTML bold element with the title of the template.

Implementation Note

Not all implementations support titles for templates.

This is easy as long as we want to replace the complete content of an HTML element. But what if we want to replace only some words within an element?

In order to place dynamic text inside of other text, you typically use ‘tal:replace’ on an additional ‘span’ tag. For example, add the following lines to your example:

<p>The URL is
  <span tal:replace="request/URL">
    http://www.example.com</span>.</p>

The ‘span’ tag is structural, not visual, so this looks like “The URL is http://www.example.com.” when you view the source in an editor or browser. When you view the rendered version, however, it may look something like:

The URL is http://localhost:8080/template_test/simple_page.

If you look at the source code of the rendered version, the span tags are removed.

To see the difference between ‘tal:replace’ and ‘tal:content’, create a page template and include the following in the body:

<b tal:content="template/title"></b>
<b tal:content="request/URL"></b>
<b tal:content="user/getUserName"></b>
<b tal:replace="template/title"></b>
<b tal:replace="request/URL"></b>
<b tal:replace="user/getUserName"></b>

There are two other ways to add elements that are only needed for TAL attributes and that are removed again in the rendered version:

<p>The URL is
  <span tal:content="request/URL" tal:omit-tag="">
    http://www.example.com</span>.</p>

… which is more useful in other situations and will be discussed there and:

<p>The URL is
  <tal:span tal:content="request/URL">
    http://www.example.com</tal:span>.</p>

While you can get really far by using HTML elements and ‘tal:replace’ or ‘tal:omit-tag’, some people prefer to use TAL elements if the elements are only used to add TAL attributes. TAL is an attribute language and doesn’t define any elements like ‘tal:span’, but it uses a complete XML namespace and allows to use any element name you like. They are silently removed while the Page Template is rendered.

This is useful for using speaking names like ‘tal:loop’, ‘tal:case’ or ‘tal:span’ and to insert additional elements where HTML doesn’t allow elements like ‘span’ or ‘div’. And if her browser or editor also ignores these tags, the designer will have less trouble with TAL elements than with additional HTML elements.

Repeating Structures

Let’s start with a simple three-liner:

<p tal:repeat="number python: range(4)" tal:content="number">
  999
</p>

‘number’ is our repeat variable and ‘range(4)’ is a Python expression that returns the list ‘[0, 1, 2, 3]’. If this code is rendered, the ‘repeat’ statement repeats the paragraph element for each value of the sequence, replacing the variable ‘number’ by the current sequence value. So the rendered page will not show the example number ‘999’, but 4 paragraph elements containing the numbers of our list.

In most cases we want to iterate over more complex sequences. Our next example shows how to use a sequence of (references to) objects. The ‘simple_page’ template could be improved by adding an item list, in the form of a list of the objects that are in the same Folder as the template. You will make a table that has a row for each object, and columns for the id, meta-type and title. Add these lines to the bottom of your example template:

<table border="1" width="100%">
  <tr>
    <th>Id</th>
    <th>Meta-Type</th>
    <th>Title</th>
  </tr>
  <tr tal:repeat="item context/objectValues">
    <td tal:content="item/getId">Id</td>
    <td tal:content="item/meta_type">Meta-Type</td>
    <td tal:content="item/title">Title</td>
  </tr>
</table>

The ‘tal:repeat’ statement on the table row means “repeat this row for each item in my context’s list of object values”. The repeat statement puts the objects from the list into the item variable one at a time (this is called the repeat variable), and makes a copy of the row using that variable. The value of ‘item/getId’ in each row is the Id of the object for that row, and likewise with ‘item/meta_type’ and ‘item/title’.

You can use any name you like for the repeat variable (“item” is only an example), as long as it starts with a letter and contains only letters, numbers, and underscores (‘_’). The repeat variable is only defined in the repeat tag. If you try to use it above or below the tr tag you will get an error.

You can also use the repeat variable name to get information about the current repetition. See Advanced Page Templates.

Now view the page and notice how it lists all the objects in the same folder as the template. Try adding or deleting objects from the folder and notice how the page reflects these changes.

Conditional Elements

Using Page Templates you can dynamically query your environment and selectively insert text depending on conditions. For example, you could display special information in response to a cookie:

<p tal:condition="request/cookies/verbose | nothing">
  Here's the extra information you requested.
</p>

This paragraph will be included in the output only if there is a ‘verbose’ cookie set. The expression, ‘request/cookies/verbose | nothing’ is true only when there is a cookie named ‘verbose’ set. You’ll learn more about this kind of expression in the section entitled Advanced Page Templates.

Using the ‘tal:condition’ statement you can check all kinds of conditions. A ‘tal:condition’ statement leaves the tag and its contents in place if its expression has a true value, but removes them if the value is false. Page templates consider the number zero, a blank string, an empty list, and the built-in variable ‘nothing’ to be false values. Nearly every other value is true, including non-zero numbers, and strings with anything in them (even spaces!).

Implementation Note

In Python, page templates use the standard bool operator to test conditions. Python objects can override the behaviour of boolean tests by implementing the __bool__ (or __nonzero__ on Python 2) method.

Another common use of conditions is to test a sequence to see if it is empty before looping over it. For example in the last section you saw how to draw a table by iterating over a collection of objects. Here’s how to add a check to the page so that if the list of objects is empty no table is drawn.

To allow you to see the effect, we first have to modify that example a bit, showing only Folder objects in the context folder. Because we can’t specify parameters using path expressions like ‘context/objectValues’, we first convert it into the Python expression ‘context.objectValues()’ and then add the argument that tells the ‘objectValues’ method to return only sub-folders:

<tr tal:repeat="item python: context.objectValues(['Folder'])">

If you did not add any sub-folders to the template_test folder so far, you will notice that using the Test tab the table header is still shown even if we have no table body. To avoid this we add a ‘tal:condition’ statement in the table tag. The complete table now looks like this:

<table tal:condition="python: context.objectValues(['Folder'])"
       border="1" width="100%">
  <tr>
    <th>Id</th>
    <th>Meta-Type</th>
    <th>Title</th>
  </tr>
  <tr tal:repeat="item python: context.objectValues(['Folder'])">
    <td tal:content="item/getId">Id</td>
    <td tal:content="item/meta_type">Meta-Type</td>
    <td tal:content="item/title">Title</td>
  </tr>
</table>

If the list of sub-folders is an empty list, the condition is false and the entire table is omitted. You can verify this by using the Test tab again.

Go and add three Folders named ‘1’, ‘2’, and ‘3’ to the template_test folder in which your simple_page template lives. Revisit the simple_page template and view the rendered output via the Test tab. You will see a table that looks much like the below:

Id          Meta-Type          Title
1           Folder
2           Folder
3           Folder
Changing Attributes

Most, if not all, of the objects listed by your template have an icon attribute that contains the path to the icon for that kind of object. In order to show this icon in the meta-type column, you will need to insert this path into the ‘src’ attribute of an ‘img’ tag. Edit the table cell in the meta-type column of the above example to look like this:

<td><img src="file_icon.gif"
         tal:attributes="src item/icon" />
  <span tal:replace="item/meta_type">Meta-Type</span></td>

The ‘tal:attributes’ statement replaces the ‘src’ attribute of the ‘img’ tag with the value of ‘item/icon’. The ‘src` attribute in the template (whose value is “file_icon.gif”) acts as a placeholder.

Notice that we’ve replaced the ‘tal:content’ attribute on the table cell with a ‘tal:replace’ statement on a ‘span’ tag. This change allows you to have both an image and text in the table cell.

XML Page Templates

Creating XML with Page Templates is almost exactly like creating HTML. You switch to XML Mode by setting the content-type field to ‘text/xml’ or whatever the content-type for your XML should be.

In XML Mode no “loose” markup is allowed. Page templates assumes that your template is well-formed XML. Page templates also requires an explicit TAL and METAL XML namespace declarations in order to emit XML. For example, if you wish to emit XHTML, you might put your namespace declarations on the ‘html’ tag:

<html xmlns:tal="http://xml.zope.org/namespaces/tal"
  xmlns:metal="http://xml.zope.org/namespaces/metal">

To browse the source of an XML template you go to ‘source.xml’ rather than ‘source.html’.

Debugging and Testing

Implementation Note

This section refers specifically to the Zope2/Zope page template implementation. This behaviour is basically shared by zope.pagetemplate; see zope.pagetemplate.interfaces.IPageTemplateProgram.

Zope helps you find and correct problems in your Page Templates. Zope notices problems at two different times: when you’re editing a Page Template, and when you’re viewing a Page Template. Zope catches different types of problems when you’re editing and than when you’re viewing a Page Template.

You may have already seen the trouble-shooting comments that Zope inserts into your Page Templates when it runs into problems. These comments tell you about problems that Zope finds while you’re editing your templates. The sorts of problems that Zope finds when you’re editing are mostly errors in your TAL statements. For example:

<!-- Page Template Diagnostics
 Compilation failed
 TAL.TALDefs.TALError: bad TAL attribute: 'contents', at line 10, column 1
-->

This diagnostic message lets you know that you mistakenly used ‘tal:contents’ rather than ‘tal:content’ on line 10 of your template. Other diagnostic messages will tell you about problems with your template expressions and macros.

When you’re using the Zope management interface to edit Page Templates it’s easy to spot these diagnostic messages, because they are shown in the “Errors” header of the management interface page when you save the Page Template.

If you don’t notice the diagnostic message and try to render a template with problems you’ll see a message like this:

Error Type: PTRuntimeError
Error Value: Page Template hello.html has errors.

That’s your signal to reload the template and check out the diagnostic message.

In addition to diagnostic messages when editing, you’ll occasionally get regular Zope errors when viewing a Page Template. These problems are usually due to problems in your template expressions. For example, you might get an error if an expression can’t locate a variable:

Error Type: KeyError
Error Value: 'unicorn'

This error message tells you that it cannot find the unicorn variable. To help you figure out what went wrong, Zope includes information about the environment in the traceback. This information will be available in your error_log (in your Zope root folder). The traceback will include information about the place where the error occurred and the environment:

URL: /sandbox/demo
Line 1, Column 14
Expression: standard:'context/unicorn'
Names:
  {'container': <Folder instance at 019AC4D0>,
   'context': <Application instance at 01736F78>,
   'default': <Products.PageTemplates.TALES.Default instance at 0x012F9D00>,
   ...
   'root': <Application instance at 01736F78>,
   'template': <ZopePageTemplate at /sandbox/demo>,
   'traverse_subpath': [],
   'user': admin}

This information is a bit cryptic, but with a little detective work it can help you figure out what went wrong. In this case, it tells us that the ‘context’ variable is an “Application instance”. This means that it is the top-level Zope folder (notice how ‘root’ variable is the same “Application instance”). Perhaps the problem is that you wanted to apply the template to a folder that had a unicorn property, but the root on which you called the template hasn’t such a property.

Macros

So far, you’ve seen how Page Templates can be used to add dynamic behavior to individual web pages. Another feature of page templates is the ability to reuse look and feel elements across many pages.

For example, with Page Templates, you can have a site that has a standard look and feel. No matter what the “content” of a page, it will have a standard header, side-bar, footer, and/or other page elements. This is a very common requirement for websites.

You can reuse presentation elements across pages with macros. Macros define a section of a page that can be reused in other pages. A macro can be an entire page, or just a chunk of a page such as a header or footer. After you define one or more macros in one Page Template, you can use them in other Page Templates.

Using Macros

You can define macros with tag attributes similar to TAL statements. Macro tag attributes are called Macro Expansion Tag Attribute Language (METAL) statements. Here’s an example macro definition:

<p metal:define-macro="copyright">
  Copyright 2009, <em>Foo, Bar, and Associates</em> Inc.
</p>

This ‘metal:define-macro’ statement defines a macro named “copyright”. The macro consists of the ‘p’ element (including all contained elements, ending with the closing ‘p’ tag).

Macros defined in a Page Template are stored in the template’s macros attribute. You can use macros from other Page Templates by referring to them through the macros attribute of the Page Template in which they are defined. For example, suppose the copyright macro is in a Page Template called “master_page”. Here’s how to use the copyright macro from another Page Template:

<hr />
<b metal:use-macro="container/master_page/macros/copyright">
  Macro goes here
</b>

In this Page Template, the ‘b’ element will be completely replaced by the macro when the implementation renders the page:

<hr />
<p>
  Copyright 2009, <em>Foo, Bar, and Associates</em> Inc.
</p>

If you change the macro (for example, if the copyright holder changes) then all Page Templates that use the macro will automatically reflect the change.

Notice how the macro is identified by a path expression using the ‘metal:use-macro’ statement. The ‘metal:use-macro’ statement replaces the statement element with the named macro.

Macro Details

The ‘metal:define-macro’ and ‘metal:use-macro’ statements are pretty simple. However there are a few subtleties to using them which are worth mentioning.

A macro’s name must be unique within the Page Template in which it is defined. You can define more than one macro in a template, but they all need to have different names.

Normally you’ll refer to a macro in a ‘metal:use-macro’ statement with a path expression. However, you can use any expression type you wish so long as it returns a macro. For example:

<p metal:use-macro="python:context.getMacro()">
  Replaced with a dynamically determined macro,
  which is located by the getMacro script.
</p>

In this case the path expression returns a macro defined dynamically by the ‘getMacro’ script. Using Python expressions to locate macros lets you dynamically vary which macro your template uses. An example of the body of a “getMacro” Script (Python) is as follows:

return container.ptMacros.macros['amacroname']

You can use the ‘default’ variable with the ‘metal:use-macro’ statement:

<p metal:use-macro="default">
  This content remains - no macro is used
</p>

The result is the same as using default with ‘tal:content’ and ‘tal:replace’. The “default” content in the tag doesn’t change when it is rendered. This can be handy if you need to conditionally use a macro or fall back on the default content if it doesn’t exist.

If you try to use the ‘nothing’ variable with ‘metal:use-macro’ you will get an error, since ‘nothing’ is not a macro. If you want to use ‘nothing’ to conditionally include a macro, you should instead enclose the ‘metal:use-macro’ statement with a ‘tal:condition’ statement.

ZPT implementations handle macros first when rendering your templates. Then the implementation evaluates TAL expressions. For example, consider this macro:

<p metal:define-macro="title"
   tal:content="template/title">
  template's title
</p>

When you use this macro it will insert the title of the template in which the macro is used, not the title of the template in which the macro is defined. In other words, when you use a macro, it’s like copying the text of a macro into your template and then rendering your template.

If you check the Expand macros when editing option on the Page Template Edit view, then any macros that you use will be expanded in your template’s source.

Using Slots

Macros are much more useful if you can override parts of them when you use them. You can do this by defining slots in the macro that you can fill in when you use the template. For example, consider a side bar macro:

<div metal:define-macro="sidebar">
  Links
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/products">Products</a></li>
    <li><a href="/support">Support</a></li>
    <li><a href="/contact">Contact Us</a></li>
  </ul>
</div>

This macro is fine, but suppose you’d like to include some additional information in the sidebar on some pages. One way to accomplish this is with slots:

<div metal:define-macro="sidebar">
  Links
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/products">Products</a></li>
    <li><a href="/support">Support</a></li>
    <li><a href="/contact">Contact Us</a></li>
  </ul>
  <span metal:define-slot="additional_info"></span>
</div>

When you use this macro you can choose to fill the slot like so:

<p metal:use-macro="container/master.html/macros/sidebar">
  <b metal:fill-slot="additional_info">
    Make sure to check out our <a href="/specials">specials</a>.
  </b>
</p>

When you render this template the side bar will include the extra information that you provided in the slot:

<div>
  Links
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/products">Products</a></li>
    <li><a href="/support">Support</a></li>
    <li><a href="/contact">Contact Us</a></li>
  </ul>
  <b>
    Make sure to check out our <a href="/specials">specials</a>.
  </b>
</div>

Notice how the ‘span’ element that defines the slot is replaced with the ‘b’ element that fills the slot.

Customizing Default Presentation

A common use of slot is to provide default presentation which you can customize. In the slot example in the last section, the slot definition was just an empty ‘span’ element. However, you can provide default presentation in a slot definition. For example, consider this revised sidebar macro:

<div metal:define-macro="sidebar">
  <div metal:define-slot="links">
  Links
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/products">Products</a></li>
    <li><a href="/support">Support</a></li>
    <li><a href="/contact">Contact Us</a></li>
  </ul>
  </div>
  <span metal:define-slot="additional_info"></span>
</div>

Now the sidebar is fully customizable. You can fill the ‘links’ slot to redefine the sidebar links. However, if you choose not to fill the ‘links’ slot then you’ll get the default links, which appear inside the slot.

You can even take this technique further by defining slots inside of slots. This allows you to override default presentation with a fine degree of precision. Here’s a sidebar macro that defines slots within slots:

<div metal:define-macro="sidebar">
  <div metal:define-slot="links">
  Links
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/products">Products</a></li>
    <li><a href="/support">Support</a></li>
    <li><a href="/contact">Contact Us</a></li>
    <span metal:define-slot="additional_links"></span>
  </ul>
  </div>
  <span metal:define-slot="additional_info"></span>
</div>

If you wish to customize the sidebar links you can either fill the ‘links’ slot to completely override the links, or you can fill the ‘additional_links’ slot to insert some extra links after the default links. You can nest slots as deeply as you wish.

Combining METAL and TAL

You can use both METAL and TAL statements on the same elements. For example:

<ul metal:define-macro="links"
    tal:repeat="link context/getLinks">
  <li>
    <a href="link url"
       tal:attributes="href link/url"
       tal:content="link/name">link name</a>
  </li>
</ul>

In this case, ‘getLinks’ is an (imaginary) Script that assembles a list of link objects, possibly using a Catalog query.

Since METAL statements are evaluated before TAL statements, there are no conflicts. This example is also interesting since it customizes a macro without using slots. The macro calls the ‘getLinks’ Script to determine the links. You can thus customize your site’s links by redefining the ‘getLinks’ Script at different locations within your site.

It’s not always easy to figure out the best way to customize look and feel in different parts of your site. In general you should use slots to override presentation elements, and you should use Scripts to provide content dynamically. In the case of the links example, it’s arguable whether links are content or presentation. Scripts probably provide a more flexible solution, especially if your site includes link content objects.

Whole Page Macros

Rather than using macros for chunks of presentation shared between pages, you can use macros to define entire pages. Slots make this possible. Here’s an example macro that defines an entire page:

<html metal:define-macro="page">
  <head>
    <title tal:content="context/title">The title</title>
  </head>

  <body>
    <h1 metal:define-slot="headline"
        tal:content="context/title">title</h1>

    <p metal:define-slot="body">
      This is the body.
    </p>

    <span metal:define-slot="footer">
      <p>Copyright 2009 Fluffy Enterprises</p>
    </span>

  </body>
</html>

This macro defines a page with three slots, ‘headline’, ‘body’, and ‘footer’. Notice how the ‘headline’ slot includes a TAL statement to dynamically determine the headline content.

You can then use this macro in templates for different types of content, or different parts of your site. For example here’s how a template for news items might use this macro:

<html metal:use-macro="container/master.html/macros/page">

  <h1 metal:fill-slot="headline">
    Press Release:
    <span tal:replace="context/getHeadline">Headline</span>
  </h1>

  <p metal:fill-slot="body"
     tal:content="context/getBody">
    News item body goes here
  </p>

</html>

This template redefines the ‘headline’ slot to include the words “Press Release” and call the ‘getHeadline’ method on the current object. It also redefines the ‘body’ slot to call the ‘getBody’ method on the current object.

The powerful thing about this approach is that you can now change the ‘page’ macro and the press release template will be automatically updated. For example you could put the body of the page in a table and add a sidebar on the left and the press release template would automatically use these new presentation elements.

Advanced Page Templates

In the section Using Zope Page Templates you learned the basic features of Page Templates. In this section you’ll learn about advanced techniques including new types of expressions.

Implementation Note

In this documentation, you will occasionally see boxes like this one. They contain extra information specific to one or more implementations of Page Templates.

Note

When this document refers to the “page template implementation,”, “ZPT implementation” or the “implementation”, it means the software that parses and renders page templates to produce output. This could be a framework like Zope, or it could a separate library like zope.pagetemplate. For more information, see Page Template Implementations.

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 TAL Reference.

In this chapter, the terms ‘tag’ and ‘element’ are used in the sense laid out by the XHTML spec. <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 ‘&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 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 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>
Advanced Repetition

You’ve already seen most of what you can do with the ‘tal:repeat’ statement in the section 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. For a full list of the available attributes, see tal:repeat documentation.

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 section.

One useful feature that isn’t supplied by ‘tal:repeat’ is sorting. If you want to sort a list you can do so i n a Python expression:

<table tal:define="objects context/objectValues;
                   sorted_objects python:sorted(objects)">
  <tr tal:repeat="item sorted_objects">
    <td tal:content="item/title">title</td>
  </tr>
</table>
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 the template implementation 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, page templates 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.

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.

See Built-in Names for more information.

‘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.

Zope Implementation Note

The following names are optional names supported by Zope, but are not required by the TALES standard.

‘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 the implementation 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.

Zope Implementation Note

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.

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 the zpt implementation 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 TAL 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 Python, zero, empty strings, empty sequences, nothing, and None are considered false, while everything else is true (unless it implements a custom __bool__ method). 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.

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’.

Zope Implementation Note

In addition, Zope imposes some security restrictions to keep you from accessing protected information, changing secured data, and creating problems such as infinite loops.

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.

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.

Conclusion

This section 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.

Attribute Languages

An attribute language is a programming language designed to mesh in a particular way with documents written using tag-structured markup, such as HTML and XML. The statements of the language are document tags with special attributes, and look like this:

<p ns:command="argument">Some Text</p>

The attribute ns:command="argument" is the statement attribute, and the entire paragraph tag is the statement element, the portion of the document on which this statement operates. Each statement attribute has three parts: the namespace prefix, the name, and the argument. The prefix identifies the language, and must be introduced by an XML namespace declaration in XML and XHTML documents, like this:

xmlns:ns="http://language.creator.com/namespaces/MyAttributeLanguage"

HTML should include these declarations, but an implementation may allow you to omit them. The statement name determines which language statement to apply to the statement element, and the argument determines how it is applied. The name is always a simple identifier, but the argument can contain just about anything, depending on the statement.

How to Specify an Attribute Language

If you create an attribute language, you need to define the following:

  • The URI of the XML namespace, and (optionally) a recommended alias.
  • The names, argument syntax, and meaning of the possible statements.
  • The order in which statements on the same element are processed, and
    restrictions on which statements can share the same element.

Why use Attribute Languages

A central concept of Page Template design is the proper embedding of programming code into HTML and XML. This is not a completely new idea; template systems such as ASP, PHP, and DTML have defined various new tags to hold code. Tools must be modified to work properly with the resulting mixed documents, since they are often invalid.

Presentation Templates take advantage of XML namespaces and the tendency of HTML tools to preserve and ignore attributes that they don’t recognize. They do this by using attribute languages. Attribute language statements conform to the XML 1.0 syntax and may appear in XML, XHTML or HTML documents.

Important

Statements in HTML documents must be within well formed elements (even if the rest of the document is not well formed).

Drawbacks

There are several restrictions imposed on attribute languages by the use of attributes as statements. The most obvious is that statements are tied to tags. This makes it slightly awkward to operate on other attributes or on the contents of elements. A tag cannot contain more than one attribute with the same name, the value must be quoted, and the arrangement of attributes on a tag is not dependable. Also, parsing or writing a DTD for attribute languages is hard if the name of any statement attribute contains “data”. Examples:

Bad: <div ns:set="foo=1" ns:set="bar=2">
Bad: <div ns:set-foo="1" ns:set-bar="2">
Good: <div ns:set="foo=1, bar=2">

The attribute languages TAL Reference and METAL Reference used in Page Templates both take these restrictions into account. They do not depend on the ordering of attributes within a tag, and some commands allow arguments with multiple parts.

Page Template Implementations

This document will mention some implementations of page templates.

  • zope.pagetemplate is the reference implementation for page templates in Python. It implements TAL and METAL using the packages zope.tal and TALES using zope.tales.
  • Chameleon is a Python implementation with a focus on speed (it compiles templates into Python bytecode). It differs from the reference and specifications in a few ways, most notably by using Python expressions as the default and not providing an implementation of path expressions. If you add the z3c.pt package, you can combine path expressions and Chameleon templates.
  • SimpleTAL is an independent Python implementation.
  • PHPTAL is an independent implementation in PHP 5.
  • ZPT-JS is an independent implementation in Javascript.

An older list is at Non-Zope Implementations.

Specifications

These documents are a reference to Zope Page Templates standards: Template Attribute Language (TAL), TAL Expression Syntax (TALES), and Macro Expansion TAL (METAL). It also describes some ZPT-specific behaviors that are not part of the standards.

TAL Reference

The Template Attribute Language (TAL) standard is an attribute language used to create dynamic templates. It allows elements of a document to be replaced, repeated, or omitted.

Implementation Note

In this documentation, you will occasionally see boxes like this one. They contain extra information specific to one or more implementations of Page Templates.

Note

When this document refers to the “page template implementation,”, “ZPT implementation” or the “implementation”, it means the software that parses and renders page templates to produce output. This could be a framework like Zope, or it could a separate library like zope.pagetemplate. For more information, see Page Template Implementations.

Introduction

The statements of TAL are XML attributes from the TAL namespace. These attributes can be applied to an XML or HTML document in order to make it act as a template.

A TAL statement has a name (the attribute name) and a body (the attribute value). For example, an content statement might look like:

tal:content="string:Hello"

The element on which a statement is defined is its statement element. Most TAL statements require expressions, but the syntax and semantics of these expressions are not part of TAL. TALES is recommended for this purpose.

TAL Namespace

The TAL namespace URI and recommended alias are currently defined as:

xmlns:tal="http://xml.zope.org/namespaces/tal"

This is not a URL, but merely a unique identifier. Do not expect a browser to resolve it successfully.

Implementations should not require an XML namespace declaration when creating templates with a content-type of text/html. However, they should require an XML namespace declaration for all other content-types.

TAL Statements

These are the tal statements:

tal:attributes
dynamically change element attributes.
tal:define
define variables.
tal:condition
test conditions.
tal:content
replace the content of an element.
tal:omit-tag
remove an element, leaving the content of the element.
tal:on-error
handle errors.
tal:repeat
repeat an element.
tal:replace
replace the content of an element and remove the element leaving the content.

Expressions used in statements may return values of any type, although most statements will only accept strings, or will convert values into a string representation. The expression language must define a value named nothing that is not a string. In particular, this value is useful for deleting elements or attributes.

Order of Operations

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 is visited, in order, to do the same.

Any combination of statements may appear on the same elements, except that the content and replace statements may not appear together.

Due to the fact that TAL sees statements as XML attributes, even in HTML documents, it cannot use the order in which statements are written in the tag to determine the order in which they are executed. TAL must also forbid multiples of the same kind of statement on a single element, so it is sufficient to arrange the kinds of statement in a precedence list.

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 on-error statement is only invoked when an error occurs, it does not appear in the list.

It may not be apparent that there needs to be an ordering. The reason that there must be one is that TAL is XML based. The XML specification specifically states that XML processors are free to rewrite the terms. In particular, you cannot assume that attributes of an XML statement will be processed in the order written, particularly if there is another preprocessor involved. To avoid needless proliferation of tags, and still permit unambiguous execution of complex TAL, a precedence order was chosen according to the following rationale.

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 be able to replace various parts of an element with different values on each iteration of a repeat, so repeat is next. It makes no sense to replace attributes and then throw them away, so attributes is last. The remaining statements clash, because they each replace or edit the statement element.

attributes: Replace element attributes

Syntax

tal:attributes syntax:

argument             ::= attribute_statement [';' attribute_statement]*
attribute_statement  ::= attribute_name expression
attribute_name       ::= [namespace-prefix ':'] Name
namespace-prefix     ::= Name

Note: If you want to include a semi-colon (;) in an ``expression``, it must be escaped by doubling it (;;).

Description

The tal:attributes statement replaces the value of an attribute (or creates an attribute) with a dynamic value. You can qualify an attribute name with a namespace prefix, for example:

html:table

if you are generating an XML document with multiple namespaces. The value of each expression is converted to a string, if necessary.

If the expression associated with an attribute assignment evaluates to nothing, then that attribute is deleted from the statement element. If the expression evaluates to default, then that attribute is left unchanged. Each attribute assignment is independent, so attributes may be assigned in the same statement in which some attributes are deleted and others are left alone.

If you use tal:attributes on an element with an active tal:replace command, the tal:attributes statement is ignored.

If you use tal:attributes on an element with a tal:repeat statement, the replacement is made on each repetition of the element, and the replacement expression is evaluated fresh for each repetition.

Examples

Replacing a link:

<a href="/sample/link.html"
   tal:attributes="href context/sub/absolute_url">

Replacing two attributes:

<textarea
  rows="80" cols="20"
  tal:attributes="rows request/rows;cols request/cols">

condition: Conditionally insert or remove an element

Syntax

tal:condition syntax:

argument ::= expression
Description

The tal:condition statement includes the statement element in the template only if the condition is met, and omits it otherwise. If its expression evaluates to a true value, then normal processing of the element continues, otherwise the statement element is immediately removed from the template. For these purposes, the value nothing is false, and default has the same effect as returning a true value.

See TALES Not expressions for information on what the default expression language considers to be true or false.

Examples

Test a variable before inserting it (the first example tests for existence and truth, while the second only tests for existence):

<p tal:condition="request/message | nothing"
   tal:content="request/message">message goes here</p>

<p tal:condition="exists:request/message"
   tal:content="request/message">message goes here</p>

Test for alternate conditions:

<div tal:repeat="item python:range(10)">
  <p tal:condition="repeat/item/even">Even</p>
  <p tal:condition="repeat/item/odd">Odd</p>
</div>

content: Replace the content of an element

Syntax

tal:content syntax:

argument ::= (['text'] | 'structure') expression
Description

Rather than replacing an entire element, you can insert text or structure in place of its children with the tal:content statement. The statement argument is exactly like that of tal:replace, and is interpreted in the same fashion. If the expression evaluates to nothing, the statement element is left childless. If the expression evaluates to default, then the element’s contents are unchanged.

The default replacement behavior is text, which replaces angle-brackets and ampersands with their HTML entity equivalents. The structure keyword passes the replacement text through unchanged, allowing HTML/XML markup to be inserted. This can break your page if the text contains unanticipated markup (e.g.. text submitted via a web form), which is the reason that it is not the default.

Examples

Inserting the user name:

<p tal:content="user/getUserName">Fred Farkas</p>

Inserting HTML/XML:

<p tal:content="structure context/getStory">
  marked <b>up</b> content goes here.
</p>

define: Define variables

Syntax

tal:define syntax:

argument       ::= define_scope [';' define_scope]*
define_scope   ::= (['local'] | 'global') define_var
define_var     ::= variable_name expression
variable_name  ::= Name

Note: If you want to include a semi-colon (;) in an ``expression``, it must be escaped by doubling it (;;).

Description

The tal:define statement defines variables. You can define two different kinds of TAL variables: local and global. When you define a local variable in a statement element, you can only use that variable in that element and the elements it contains. If you redefine a local variable in a contained element, the new definition hides the outer element’s definition within the inner element. When you define a global variables, you can use it in any element processed after the defining element. If you redefine a global variable, you replace its definition for the rest of the template.

Note: local variables are the default

If the expression associated with a variable evaluates to nothing, then that variable has the value nothing, and may be used as such in further expressions. Likewise, if the expression evaluates to default, then the variable has the value default, and may be used as such in further expressions.

Examples

Defining a global variable:

tal:define="global company_name string:Zope Corp, Inc."

Defining two variables, where the second depends on the first:

tal:define="mytitle template/title; tlen python:len(mytitle)"

omit-tag: Remove an element, leaving its contents

Syntax

tal:omit-tag syntax:

argument ::= [ expression ]
Description

The tal:omit-tag statement leaves the contents of an element in place while omitting the surrounding start and end tags.

If the expression evaluates to a false value, then normal processing of the element continues and the tags are not omitted. If the expression evaluates to a true value, or no expression is provided, the statement element is replaced with its contents. The default value is considered to be true.

See TALES Not expressions for information on what the default expression language considers to be true or false.

Examples

Unconditionally omitting a tag:

<div tal:omit-tag="" comment="This tag will be removed">
  <i>...but this text will remain.</i>
</div>

Conditionally omitting a tag:

<b tal:omit-tag="not:bold">
  I may be bold.
</b>

The above example will omit the b tag if the variable bold is false.

Creating ten paragraph tags, with no enclosing tag:

<span tal:repeat="n python:range(10)"
      tal:omit-tag="">
  <p tal:content="n">1</p>
</span>

on-error: Handle errors

Syntax

tal:on-error syntax:

argument ::= (['text'] | 'structure') expression
Description

The tal:on-error statement provides error handling for your template. When a TAL statement produces an error, the TAL interpreter searches for a tal:on-error statement on the same element, then on the enclosing element, and so forth. The first tal:on-error found is invoked. It is treated as a tal:content statement.

A local variable error is set. This variable has these attributes:

type:the exception type
value:the exception instance
traceback:the traceback object

The simplest sort of tal:on-error statement has a literal error string or nothing for an expression. A more complex handler may call a script that examines the error and either emits error text or raises an exception to propagate the error outwards.

Examples

Simple error message:

<b tal:on-error="string: Username is not defined!"
   tal:content="context/getUsername">Ishmael</b>

Removing elements with errors:

<b tal:on-error="nothing"
   tal:content="context/getUsername">Ishmael</b>

Calling an error-handling script:

<div tal:on-error="structure context/errorScript">
...
</div>

Here’s what the error-handling script might look like:

## Script (Python) "errHandler"
##bind namespace=_
##
error=_['error']
if error.type==ZeroDivisionError:
    return "<p>Can't divide by zero.</p>"
else
    return """<p>An error ocurred.</p>
    <p>Error type: %s</p>
    <p>Error value: %s</p>""" % (error.type, error.value)

repeat: Repeat an element

Syntax

tal:repeat syntax:

argument      ::= variable_name expression
variable_name ::= Name
Description

The tal:repeat statement replicates a sub-tree of your document once for each item in a sequence. The expression should evaluate to a sequence. If the sequence is empty, then the statement element is deleted, otherwise it is repeated for each value in the sequence. If the expression is default, then the element is left unchanged, and no new variables are defined.

The variable_name is used to define a local variable and a repeat variable. For each repetition, the local variable is set to the current sequence element, and the repeat variable is set to an iteration object.

Repeat Variables

You use repeat variables to access information about the current repetition (such as the repeat index). The repeat variable has the same name as the local variable, but is only accessible through the built-in variable named repeat.

Repeat Variable Attributes
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.
first:true for the first item in a group - see note below
last:true for the last item in a group - see note below
length:length of the sequence, which will be the total number of repetitions.
letter:repetition number as a lower-case letter: “a” - “z”, “aa” - “az”, “ba” - “bz”, …, “za” - “zz”, “aaa” - “aaz”, and so forth.
Letter:upper-case version of letter.
roman:repetition number as a lower-case roman numeral: “i”, “ii”, “iii”, “iv”, “v”, etc.
Roman:upper-case version of roman.

You can access the contents of the 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”.

With the exception of start, end, and index, all of the attributes of a repeat variable are methods. Thus, when you use a Python expression to access them, you must call them, as in “python:repeat[‘item’].length()”.

Note

first and last are intended for use with sorted sequences. They try to divide the sequence into group of items with the same value. If you provide a path, then the value obtained by following that path from a sequence item is used for grouping, otherwise the value of the item is used. You can provide the path by passing it as a parameter, as in:

python:repeat['item'].first(color)

or by appending it to the path from the repeat variable, as in “repeat/item/first/color”.

Zope Implementation Note

first and last are Zope 2 extensions and are not available in other implementations.

Examples

Iterating over a sequence of strings:

<p tal:repeat="txt python: ('one', 'two', 'three')">
  <span tal:replace="txt" />
</p>

Inserting a sequence of table rows, and using the repeat variable to number the rows:

<table>
  <tr tal:repeat="item context/cart">
    <td tal:content="repeat/item/number">1</td>
    <td tal:content="item/description">Widget</td>
    <td tal:content="item/price">$1.50</td>
  </tr>
</table>

Nested repeats:

<table border="1">
  <tr tal:repeat="row python:range(10)">
    <td tal:repeat="column python:range(10)">
      <span tal:define="x repeat/row/number;
                        y repeat/column/number;
                        z python:x*y"
            tal:replace="string:$x * $y = $z">
          1 * 1 = 1
      </span>
    </td>
  </tr>
</table>

Insert objects. Separate groups of objects by meta-type by drawing a rule between them:

<div tal:repeat="object objects">
  <h2 tal:condition="repeat/object/first/meta_type"
      tal:content="object/meta_type">Meta Type</h2>
  <p tal:content="object/getId">Object ID</p>
  <hr tal:condition="repeat/object/last/meta_type" />
</div>

Note, the objects in the above example should already be sorted by meta-type.

replace: Replace an element

Syntax

tal:replace syntax:

argument ::= (['text'] | 'structure') expression
Description

The tal:replace statement replaces an element with dynamic content. It replaces the statement element with either text or a structure (unescaped markup). The body of the statement is an expression with an optional type prefix. The value of the expression is converted into an escaped string if you prefix the expression with text or omit the prefix, and is inserted unchanged if you prefix it with structure. Escaping consists of converting “&amp;” to “&amp;amp;”, “&lt;” to “&amp;lt;”, and “&gt;” to “&amp;gt;”.

If the value is nothing, then the element is simply removed. If the value is default, then the element is left unchanged.

Examples

The two ways to insert the title of a template:

<span tal:replace="template/title">Title</span>
<span tal:replace="text template/title">Title</span>

Inserting HTML/XML:

<div tal:replace="structure table" />

Inserting nothing:

<div tal:replace="nothing">
  This element is a comment.
</div>

TALES Reference

The Template Attribute Language Expression Syntax (TALES) standard describes expressions that supply TAL and METAL with data. TALES is one possible expression syntax for these languages, but they are not bound to this definition. Similarly, TALES could be used in a context having nothing to do with TAL or METAL.

Implementation Note

In this documentation, you will occasionally see boxes like this one. They contain extra information specific to one or more implementations of Page Templates.

Note

When this document refers to the “page template implementation,”, “ZPT implementation” or the “implementation”, it means the software that parses and renders page templates to produce output. This could be a framework like Zope, or it could a separate library like zope.pagetemplate. For more information, see Page Template Implementations.

Introduction

TALES expressions are described below with any delimiter or quote markup from higher language layers removed. Here is the basic definition of TALES syntax:

Expression  ::= [type_prefix ':'] String
type_prefix ::= Name

Here are some simple examples:

a/b/c
path:a/b/c
nothing
path:nothing
python: 1 + 2
string:Hello, ${user/getUserName}

The optional type prefix determines the semantics and syntax of the expression string that follows it. A given implementation of TALES can define any number of expression types, with whatever syntax you like. It also determines which expression type is indicated by omitting the prefix.

Implementation Note

If you do not specify a prefix, Zope and zope.pagetemplate assumes that the expression is a path expression. Other implementations may choose a different default.

TALES Expression Types

These are the TALES expression types defined in the standard:

path expressions
locate a value by its path.
exists expressions
test whether a path is valid.
nocall expressions
locate an object by its path.
not expressions
negate an expression
string expressions
format a string
python expressions
execute a Python expression
Built-in Names

These are the names always available to TALES expressions:

nothing:special value used by to represent a non-value (e.g. void, None, Nil, NULL).
default:special value used to specify that existing text should not be replaced. See the documentation for individual TAL statements for details on how they interpret default.
options:the keyword arguments passed to the template. These are generally available when a template is called from Methods and Scripts, rather than from the web.
repeat:the repeat variables; see the tal:repeat documentation.
attrs:a dictionary containing the initial values of the attributes of the current statement tag.
CONTEXTS:the list of standard names (this list). This can be used to access a built-in variable that has been hidden by a local or global variable with the same name.

Zope Implementation Note

The following names are optional names supported by Zope, but are not required by the TALES standard.

root:the system’s top-most object: the Zope root folder.
context:the object to which the template is being applied.
container:The folder in which the template is located.
template:the template itself.
request:the publishing request object.
user:the authenticated user object.
modules:a collection through which Python modules and packages can be accessed. Only modules which are approved by the Zope security policy can be accessed.

zope.pagetemplate provides template and modules by default. z3c.pt tries to provide those in addition to request and context. For both system, other values may be provided by the calling framework.

TALES Exists expressions

Syntax

Exists expression syntax:

exists_expressions ::= 'exists:' path_expression
Description

Exists expressions test for the existence of paths. An exists expression returns true when the path expressions following it expression returns a value. It is false when the path expression cannot locate an object.

Examples

Testing for the existence of a form variable:

<p tal:condition="not:exists:request/form/number">
  Please enter a number between 0 and 5
</p>

Note that in this case you can’t use the expression, not:request/form/number, since that expression will be true if the number variable exists and is zero.

TALES Nocall expressions

Syntax

Nocall expression syntax:

nocall_expression ::= 'nocall:' path_expression
Description

Nocall expressions avoid rendering the results of a path expression.

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

Examples

Using nocall to get the properties of a document:

<span tal:define="doc nocall:context/aDoc"
      tal:content="string:${doc/getId}: ${doc/title}">
  Id: Title
</span>

Using nocall expressions on a functions:

<p tal:define="join nocall:modules/string/join">

This example defines a variable:: join which is bound to the string.join function.

TALES Not expressions

Syntax

Not expression syntax:

not_expression ::= 'not:' expression
Description

Not expression evaluates the expression string (recursively) as a full expression, and returns the boolean negation of its value. If the expression supplied does not evaluate to a boolean value, not will issue a warning and coerce the expression’s value into a boolean type based on the following rules:

  1. the number 0 is false
  2. positive and negative numbers are true
  3. an empty string or other sequence is false
  4. a non-empty string or other sequence is true
  5. a non-value (e.g. nothing, void, None, Nil, NULL, etc) is false
  6. all other values are implementation-dependent.

If no expression string is supplied, an error should be generated.

Implementation Note

Python implementations use the bool type to check for truth.

Typically this means all objects not specifically listed above as false are true, but individual object classes can customize that.

Examples

Testing a sequence:

<p tal:condition="not:context/objectIds">
  There are no contained objects.
</p>

TALES Path expressions

Syntax

Path expression syntax:

PathExpr    ::= Path [ '|' Expression ]
Path        ::= variable [ '/' PathSegment ]*
variable    ::= Name
PathSegment ::= ( '?' variable ) | PathChar+
PathChar    ::= AlphaNumeric | ' ' | '_' | '-' | '.' | ',' | '~'
Description

A path expression consists of a path optionally followed by a vertical bar (|) and alternate expression. A path consists of one or more non-empty strings separated by slashes. The first string must be a variable name (a built-in variable or a user defined variable), and the remaining strings, the path segments, may contain letters, digits, spaces, and the punctuation characters underscore, dash, period, comma, and tilde.

A limited amount of indirection is possible by using a variable name prefixed with ? as a path segment. The variable must contain a string, which replaces that segment before the path is traversed.

For example:

request/cookies/oatmeal
nothing
context/some-file 2009_02.html.tar.gz/foo
root/to/branch | default
request/name | string:Anonymous Coward
context/?tname/macros/?mname

When a path expression is evaluated, the implementation attempts to traverse the path, from left to right, until it succeeds or runs out of paths segments. To traverse a path, it first fetches the object stored in the variable. For each path segment, it traverses from the current object to the sub-object named by the path segment.

Implementation Note

In Zope, zope.pagetemplate and z3c.pt, sub-objects are located according to standard traversal rules (via getattr, getitem, or traversal hooks). Note that traversal hooks are used last and only if fetching an attribute failed.

Once a path has been successfully traversed, the resulting object is the value of the expression. If it is a callable object, such as a method or template, it is called.

If a traversal step fails, and no alternate expression has been specified, an error results. Otherwise, the alternate expression is evaluated.

The alternate expression can be any TALES expression. For example:

request/name | string:Anonymous Coward

is a valid path expression. This is useful chiefly for providing default values, such as strings and numbers, which are not expressible as path expressions. Since the alternate expression can be a path expression, it is possible to “chain” path expressions, as in:

first | second | third | nothing

If no path is given the result is nothing.

Since every path must start with a variable name, you need a set of starting variables that you can use to find other objects and values. See the TALES overview for a list of built-in variables. Variable names are looked up first in locals, then in globals, then in the built-in list, so the built-in variables act just like built-ins in Python; They are always available, but they can be shadowed by a global or local variable declaration. You can always access the built-in names explicitly by prefixing them with CONTEXTS. (e.g. CONTEXTS/root, CONTEXTS/nothing, etc).

Examples

Inserting a cookie variable or a property:

<span tal:replace="request/cookies/pref | context/pref">
  preference
</span>

Inserting the user name:

<p tal:content="user/getUserName">
  User name
</p>

TALES Python expressions

Syntax

Python expression syntax:

Any valid Python language expression
Description

Python expressions evaluate Python code in a security-restricted environment. Python expressions offer the same facilities as those available in Python-based Scripts and DTML variable expressions.

Security Restrictions

Zope Implementation Note

This entire section applies to the Zope framework. zope.pagetemplate also provides optional security restrictions.

Python expressions are subject to the same security restrictions as Python-based scripts. These restrictions include:

access limits
Python expressions are subject to Zope permission and role security restrictions. In addition, expressions cannot access objects whose names begin with underscore.
write limits
Python expressions cannot change attributes of Zope objects.

Despite these limits malicious Python expressions can cause problems.

Built-in Functions

These standard Python built-ins are available:

  • None
  • abs
  • apply
  • callable
  • chr
  • cmp
  • complex
  • delattr
  • divmod
  • filter
  • float
  • getattr
  • hash
  • hex
  • int
  • isinstance
  • issubclass
  • list
  • len
  • long
  • map
  • max
  • min
  • oct
  • ord
  • repr
  • round
  • setattr
  • str
  • tuple

Zope Implementation Note

Python expressions have the same built-ins as Python-based Scripts with a few additions.

The range and pow functions are available and work the same way they do in standard Python; however, they are limited to keep them from generating very large numbers and sequences. This limitation helps to avoid accidental long execution times.

These functions are available in Python expressions, but not in Python-based scripts:

path(string)
Evaluate a TALES path expression.
string(string)
Evaluate a TALES string expression.
exists(string)
Evaluates a TALES exists expression.
nocall(string)
Evaluates a TALES nocall expression.
Python Modules

A number of Python modules are available by default. You can make more modules available. You can access modules either via path expressions (for example modules/string/join) or in Python with the modules mapping object (for example modules["string"].join). Here are the default modules:

string
The standard Python string module Note: most of the functions in the module are also available as methods on string objects.
random
The standard Python random module.
math
The standard Python math module.

Zope Implementation Note

Zope makes these additional modules available.

sequence
A module with a powerful sorting function.
Products.PythonScripts.standard
Various HTML formatting functions available in DTML.
ZTUtils
Batch processing facilities similar to those offered by dtml-in.
AccessControl
Security and access checking facilities. See AccessControl for more
information.

Implementation Note

Some implementations simply make the entire contents of sys.modules available, importing new modules on demand. This includes zope.pagetemplate and Zope. z3c.pt does not import on demand, but does provide access to all currently imported modules.

Examples

Using a module usage (pick a random choice from a list):

<span tal:replace="python:modules['random'].choice(
                       ['one', 'two', 'three', 'four', 'five'])">
  a random number between one and five
</span>

String processing (capitalize the user name):

<p tal:content="python:user.getUserName().capitalize()">
  User Name
</p>

Basic math (convert an image size to megabytes):

<p tal:content="python:image.getSize() / 1048576.0">
  12.2323
</p>

String formatting (format a float to two decimal places):

<p tal:content="python:'%0.2f' % size">
  13.56
</p>

TALES String expressions

Syntax

String expression syntax:

string_expression ::= ( plain_string | [ varsub ] )*
varsub            ::= ( '$' Path ) | ( '${' Path '}' )
plain_string      ::= ( '$$' | non_dollar )*
non_dollar        ::= any character except '$'
Description

String expressions interpret the expression string as text. If no expression string is supplied the resulting string is empty. The string can contain variable substitutions of the form $name or ${path}, where name is a variable name, and path is a path expression. The escaped string value of the path expression is inserted into the string. To prevent a $ from being interpreted this way, it must be escaped as $$.

Examples

Basic string formatting:

<span tal:replace="string:$this and $that">
  Spam and Eggs
</span>

Using paths:

<p tal:content="string:total: ${request/form/total}">
  total: 12
</p>

Including a dollar sign:

<p tal:content="string:cost: $$$cost">
  cost: $42.00
</p>

METAL Reference

The Macro Expansion Template Attribute Language (METAL) standard is a facility for HTML/XML macro preprocessing. It can be used in conjunction with or independently of TAL and TALES.

Introduction

Macros provide a way to define a chunk of presentation in one template, and share it in others, so that changes to the macro are immediately reflected in all of the places that share it. Additionally, macros are always fully expanded, even in a template’s source text, so that the template appears very similar to its final rendering

METAL Namespace

The METAL namespace URI and recommended alias are currently defined as:

xmlns:metal="http://xml.zope.org/namespaces/metal"

Just like the TAL namespace URI, this URI is not attached to a web page; it’s just a unique identifier.

Zope does not require an XML namespace declaration when creating templates with a content-type of text/html. However, it does require an XML namespace declaration for all other content-types.

METAL Statements

METAL defines a number of statements:

metal:define-macro
Define a macro.
metal:use-macro
Use a macro.
metal:define-slot
Define a macro customization point.
metal:fill-slot
Customize a macro.

Although METAL does not define the syntax of expression non-terminals, leaving that up to the implementation, a canonical expression syntax for use in METAL arguments is described in TALES Specification.

define-macro: Define a macro

Syntax

metal:define-macro syntax:

argument ::= Name
Description

The metal:define-macro statement defines a macro. The macro is named by the statement expression, and is defined as the element and its sub-tree.

In Zope, a macro definition is available as a sub-object of a template’s macros object. For example, to access a macro named header in a template named master.html, you could use the path expression:

master.html/macros/header
Examples

Simple macro definition:

<p metal:define-macro="copyright">
  Copyright 2009, <em>Foobar</em> Inc.
</p>

define-slot: Define a macro customization point

Syntax

metal:define-slot syntax:

argument ::= Name
Description

The metal:define-slot statement defines a macro customization point or slot. When a macro is used, its slots can be replaced, in order to customize the macro. Slot definitions provide default content for the slot. You will get the default slot contents if you decide not to customize the macro when using it.

The metal:define-slot statement must be used inside a metal:define-macro statement.

Slot names must be unique within a macro.

Examples

Simple macro with slot:

<p metal:define-macro="hello">
  Hello <b metal:define-slot="name">World</b>
</p>

This example defines a macro with one slot named name. When you use this macro you can customize the b element by filling the name slot.

fill-slot: Customize a macro

Syntax

metal:fill-slot syntax:

argument ::= Name
Description

The metal:fill-slot statement customizes a macro by replacing a slot in the macro with the statement element (and its content).

The metal:fill-slot statement must be used inside a metal:use-macro statement. Slot names must be unique within a macro.

If the named slot does not exist within the macro, the slot contents will be silently dropped.

Examples

Given this macro:

<p metal:define-macro="hello">
  Hello <b metal:define-slot="name">World</b>
</p>

You can fill the name slot like so:

<p metal:use-macro="container/master.html/macros/hello">
  Hello <b metal:fill-slot="name">Kevin Bacon</b>
</p>

use-macro: Use a macro

Syntax

metal:use-macro syntax:

argument ::= expression
Description

The metal:use-macro statement replaces the statement element with a macro. The statement expression describes a macro definition.

In Zope the expression will generally be a path expression referring to a macro defined in another template. See “metal:define-macro” for more information.

The effect of expanding a macro is to graft a subtree from another document (or from elsewhere in the current document) in place of the statement element, replacing the existing sub-tree. Parts of the original subtree may remain, grafted onto the new subtree, if the macro has slots. See metal:define-slot for more information. If the macro body uses any macros, they are expanded first.

When a macro is expanded, its metal:define-macro attribute is replaced with the metal:use-macro attribute from the statement element. This makes the root of the expanded macro a valid use-macro statement element.

Examples

Basic macro usage:

<p metal:use-macro="container/other.html/macros/header">
  header macro from defined in other.html template
</p>

This example refers to the header macro defined in the other.html template which is in the same folder as the current template. When the macro is expanded, the p element and its contents will be replaced by the macro. Note: there will still be a metal:use-macro attribute on the replacement element.

Extensions

ZPT-specific Behaviors

The behavior of Zope Page Templates is almost completely described by the TAL, TALES, and METAL specifications. ZPTs do, however, have a few additional features that are not described in the standards.

HTML Support Features

When the content-type of a Page Template is set to text/html, Zope processes the template somewhat differently than with any other content-type. As mentioned under TAL Namespace, HTML documents are not required to declare namespaces, and are provided with tal and metal namespaces by default.

HTML documents are parsed using a non-XML parser that is somewhat more forgiving of malformed markup. In particular, elements that are often written without closing tags, such as paragraphs and list items, are not treated as errors when written that way, unless they are statement elements. This laxity can cause a confusing error in at least one case; a <div> element is block-level, and therefore technically not allowed to be nested in a <p> element, so it will cause the paragraph to be implicitly closed. The closing </p> tag will then cause a NestingError, since it is not matched up with the opening tag. The solution is to use <span> instead.

Unclosed statement elements are always treated as errors, so as not to cause subtle errors by trying to infer where the element ends. Elements which normally do not have closing tags in HTML, such as image and input elements, are not required to have a closing tag, or to use the XHTML <tag /> form.

Certain boolean attributes, such as checked and selected, are treated differently by tal:attributes. The value is treated as true or false (as defined by tal:condition). The attribute is set to attr="attr" in the true case and omitted otherwise. If the value is default, then it is treated as true if the attribute already exists, and false if it does not. For example, each of the following lines:

<input type="checkbox" checked tal:attributes="checked default">
<input type="checkbox" tal:attributes="checked string:yes">
<input type="checkbox" tal:attributes="checked python:42">

will render as:

<input type="checkbox" checked="checked">

while each of these:

<input type="checkbox" tal:attributes="checked default">
<input type="checkbox" tal:attributes="checked string:">
<input type="checkbox" tal:attributes="checked nothing">

will render as:

<input type="checkbox">

This works correctly in all browsers in which it has been tested.

Changes

1.6+ (unreleased)

Historical Documents

These documents are from the wiki site where ZPT was originally developed. They are preserved here for historical context.

ZPT - Zope Page Templates (Front Page)

General documentation on ZPT for Zope 2 users can be found in the Zope Book:

http://docs.zope.org/zope2/zope2book/ZPT.html

and for advanced ZPT topics:

http://docs.zope.org/zope2/zope2book/AdvZPT.html

The specification documents continue to provide value, particularly: TAL Reference, TALES Reference, METAL Reference

The ZPT - Zope Page Templates (Original Front Page) contains all of the historical project links.

http://wiki.zope.org/zope2/PageTemplates has a more complete list of docs.

Non-Zope Implementations

Page Templates, or subsets such as TAL, have now been implemented in several languages. There are also Python re-implementations that are independent of Zope:

Articles

This free article from ZopeMag is an introduction for Beginners. http://www.zopemag.com/Issue003/Section_Articles/article_ZPTintro.html

ZPT - Zope Page Templates (Original Front Page)

This is the home page for the Zope Page Template project. This project seeks to establish website production harmony through the seamless integration of the three separate domains: Presentation, Content and Logic.

Project Vision

Use Cases

Use cases describe the functionality of a system from the point of view of users of the system. Actors describe the unique kinds of roles that users play when interacting with the system.

Actors

Presentation Designer

The Presentation Designer is responsible for the look and feel (Presentation) of the site. This may include graphic icons, typography, page layout, javascript coding and general layout logic controls.

Zope Programmer

The Zope Programmer is responsible for providing the site with logic.

Site Architect

The Site Architect is the designer and maintainer of the Zope site. This person is fluent in Zope (but not necessarily python) and is responsible for the overall structure and conceptual integrity of the website.

A Site Architect may direct the Zope Programmer and Presentation Designer tasking. She may be fluent in DTML, HTML or XML, although this is not a prerequisite.

GoLive

Information on GoLive can be found at http://www.adobe.com/

Use cases

Web Site Creation Story

Zozoban Incorporated designs corporate business cards. Well, that’s a bit of an understatement–they are the premier providers of very trendy business cards for Fortune 1000 companies. In particular, they are experts in quick turnaround orders for companies that like to spin their image every few months.

Recently, Zozoban has noticed that demand for new card designs surge just before major industry conferences. This is especially noticable in the technology industry, where each new conference demands a new identity (new buzzwords are unveiled and must be embraced).

In the past year, the conference demand has made it difficult for Zozoban to keep up. So, they finally caught a clue and decided to bring their business more fully to the web. Unfortunately, Internet World was only 2 months away. Part of the Internet World hype was to have the flashiest, most time relevant business cards available to catch the eye of potential clients and partners. New businesses and old, they all greased up their marketing machinery for the show. Cards were a big part of this.

Zozoban had no problem generating the cards and delivering them in a matter of a few days, but working with corporations that wanted minor tweaks (add a new employee, change the catchphrase or color) took a toll on their rather small staff. Sue, their innovative new Web SiteArchitect, suggested that most of this could easily be done through the Web. Little did Sue realize that they would simply say “Do it.”. Unfortunately, they added “..and have it ready 1 month before Internet World”.

ZPT To The Rescue

Sue enlisted her resident Presentation Designer, Jeff, to do the mock ups. She didn’t want to wait weeks before getting feedback from her bosses. Jeff used GoLive to generate an overall look and feel page that would be the model for all pages on the site. Jeff had no idea what ZPT was, but he was willing to learn as he went. Sue told him that she would do the initial structuring, all she wanted from Jeff was a single page representing the general layout.

Sue downloaded ZPT and installed it into her departmental Zope instance. She told Jeff to save his page (using WebDAV) into http::/internal/mastertemps/masterlookandfeel.html. She had previously created it as an empty page and checked off the ‘notify me on changes’ option.

A couple of hours later, Sue was notified by email (by Zope) that Jeff had modified masterlookandfeel.html. Sue used her own favorite editor, emacs, to modify the file. She turned the page into a METAL macro by adding the attribute metal:define-macro to the html tag. Jeff had inserted divs and spans into the areas where they expected the business card application to show through. Sue paramatized these areas as slots. This is where the content would plug in. This also reminded Sue that she need to get Ulrich, her ZopeProgrammer, started on developing Zope methods to access the corporate database which held the card design information as well as client contacts.

Sue’s overall design was to have corporate clients log into the site, look at their current battery of cards (previously designed for them by Zozoban), and annotate them with the changes they needed. Perhaps she would also throw in the capability to show them new stock designs for their casual perusal. Maybe she would even allow potential clients browse such designs without having a corporate login. But, no matter any new features she could dream of, they would amount to nothing if she couldn’t get a prototype of the site up and running. Ulrich would work on the back end logic, she could work out the requirements with him later. Right now, she needed to focus on the prototype.

Before calling Jeff, Sue created a general page to contain the application. She called this page, simply enough, “main.html”. This page was wrapped in a metal:use-macro attribute that would bring in the master look and feel template. She wrote metal:fill-slot attributes on tables and text that she wanted to supply to the template (so that they would show through). This would eventually be populated with the results of Ulrich’s code. But, that wasn’t anywhere near ready, so Sue provided some sample content.

When Jeff got Sue’s email, he used GoLive to download “main.html”. He was a bit confused. It looked like Sue had copied his master layout to this file. Didn’t she realize what a waste of duplication that would be? Glancing back at her email, he noticed she had written that he should edit the layout of her sample content here, making adjustments as needed. Her tables were badly formed, and the fonts were all wrong. Jeff made changes and saved them back to Zope. Jeff also needed to change the master layout a bit to adjust to the oddly sized table that would be needed to show the card samples. He brought up “masterlookandfeel.html” and tweaked the layout. He closed “main.html” and opened it up again. The master look and feel changes appeared.

to be continued…

Comments
smw (Sep 21, 2001 2:43 pm; Comment #1) Editor Remark Requested
Please consider finishing this, it’s great!
Designer Mocks Up Site Look And Feel
Architect Assigns Functionality
Programmer Adds Code
Comments
peterbe (Jul 20, 2001 4:29 am; Comment #1) –

Here’s how I described it to a friend. My point was that one can reverse the development cycle around the design

  • the designer designs something stupid as usual and gives it to the developer and says “make this HTML dynamic” and the developer changes stuff and inserts <dtml- …> or <% response.write …%> or <cf …>

    The developer cuts tables into 10 pieces so that they depend on loops and stuff. Also, to be smart he cuts his code into many files/objects hence header.inc bodytag.inc index.php footer.inc

    Doing this the beginning of a table <table> ends up in the header.inc and the end </table> ends up in footer.inc

    After a while when there is empirical evidence that the design was too artistic, the HTML is messed up by geeky DTML or ASP or PHP code that nobody dares to touch. Project goes to hell!

  • the web application developer prepares a bunch of Python Scripts and uses TAL to print it through a template with very simple design. he then asks the designer “make this simple HTML use some nice colours and CSS and feel free to fuck up the simplicity of it if you want to”.

  • The designer changes things with the template given from the application developer in Dreamweaver and can during his development hit F12 to view it in an external browser.

  • After a while when there is empirical evidence that the design was too artistic, the boss can ask the designer to redesign the template. He can work with the template as if it was plain HTML and does not even have to look at the source (if he’s careful).

cchoi (Dec 13, 2001 4:52 am; Comment #3) Editor Remark Requested

Here’s my two bits on this:

Actor

  • IntranetEditor - An authorized user allowed to edit content on an intranet who does not necessarily know HTML, but can use wysiwyg HTML editors that are WebDAV compliant.

Use Cases

  • IntranetEditorWritesContentUsingOffice2000

    The idea here is that rank and file users should be able to create and edit ZPT files using Office2000 tools via WebDAV. Basic text, table entry, and linking is what is desired here, not full-blown page layout editing.

    I’ve tried using MS Word 2000 to read in a ZPT file, edit it, and write it back out via WebDAV. And as expected, MS Word writes tags in the file that ZPT processing yaks on, in this case tags with the characters ‘[‘ and ‘]’ in them. Removing such tags got the page to render though, and the happy part is, there weren’t that many tags. Getting ZPT to work seamlessly with Office2000 (in particular Word and Excel) would be a huge win.

    Issues

    • How to get IntranetEditors to understand slot and use tags when they don’t even want to understand HTML
    • Is the many-naive-editors model for an intranet a good idea in the first place?
cchoi (Dec 13, 2001 5:27 am; Comment #4) –

More thoughts

  • Zope’s WebDAV implementation adopts Zope’s security model. WebDAV as I currently understand it does not incorporate a security model, as Greg Stein relates:

    “The DAV spec (RFC 2518) does not incorporate a security model. It relies on any web server and file system security that the administrator configures.”:

Observation

ZPT, Office2000, WebDAV, and Zope’s security model almost suggests Zope as an Internet accessible file system where many users can do work. Is ZODB up for this?
cchoi (Dec 13, 2001 5:43 am; Comment #5) –

yet another thought

How well can ZPT files withstand importing and exporting through multiple HTML editors? We know that in theory yes, but in practice with the existing HTML editors out there the answer is less clear. Has anybody tested this?

For example,

  • Write a ZPT file in Web Browser
  • Read and write back out ZPT file using the following apps in different sequences:
    • MS Word 97 and 2000
    • Netscape Navigator
    • Mozilla
    • Dreamweaver
    • GoLive
    • [
Risk Factors
Speed
Macro expansion + TAL/TALES could be expensive. Especially if Macro expansion must occur everytime before a document is processed with TAL/TALES.
User Complexity

TAL must be easy for Presentation Designers to grok. Early usage tests must be performed (using real designers!).

TALES must be easy for programmers to grok. The community should take care of this ;-)

Problem

Presentation Designers like to work with sophisticated visually oriented web page tools where they can sculpt presentation in the context of the prototypical content. DTML uses its own non-HTML markup which is counter to the way most web page editors work. Through the extensive use of DTML, content is dynamically generated and prototypical proxies (for said content) is not available to the designer. In addtion, content, presentation and logic are all blended into one place and each respective party is expected to keep their hands off of the other’s domain.

Proposed Solution

Adopt a consistent, XML compliant, markup language as a target, embed all logic in namespaced attributes (where they are ignored by the web page tools) and provide a means to supply prototypical content to give the presentation designer a sense of context.

The solution shall have the following characteristics:

  • Everything is explicit. There are no implicit contexts or magic side effects.
  • Separates Presentation from Content and Business Logic.
  • Work with plain old HTML as well as XML.
  • XML compliant.
  • Allows example content in PageTemplates to allow designers to see/edit their work in context (idea courtesy of HiperDOM).
  • Macro based template facility. You can create paramaterized chunks of Presentation that can be applied as macros in pages.
  • Many of these ideas are universal. Things that are not Zope specific shouldn’t be made Zope specific.
Risk Factors

See Risk Factors

Scope

This markup language will replace DTML for most general functionality and will be accompanied by tools that will help the Presentation Designer’s tools import and export pages to and from Zope.

This markup language will not be Zope specific in design, but may be so in implementation.

See the Frequently Asked Questions for additional information.

Deliverables
  1. The Template Attribute Language (TAL Reference)
  2. The Template Attribute Language Expression Syntax (TALES Reference)
  3. The Template Attribute Language Macros (METAL Reference)
  4. PageTemplates
Comments
jeske (Apr 3, 2002 6:05 pm; Comment #2) –
I recommend you take a look at Clearsilver.net. Clearsilver is an extremely fast template system which separates presentation from application logic. It is based on the template system we used internally for eGroups.com and which is still used for Yahoo Groups.

Release Plan

ZPT 1.4.x bugfixes will be released as necessary.

ZPT 1.5 was released in October, 2002.

New releases will eventually be made using the implementation of page templates from Zope 3.

Closed Proposals

These are proposals which have either been implemented or rejected.

Disappearing Tags
Problem

Several people don’t like the fact that their pages end up littered with <span> tags that they had to add in order to hold tal:repeat. Not all (not many?) XML schemas provide a “neutral” tag like <span> and <div>, so it might be impossible to cleanly generate documents in those schemas.

Proposal

These proposals aren’t mutually exclusive, and #2 will not solve the problem for XML, so both may be implemented.

  1. Define a <tal:block> tag that is always replaced by its contents.

    Also, since the default namespace of its attributes is tal:, we can omit it from the attributes. We would get 1 2 3 from:

    <tal:block repeat="digit python:1,2,3">
      <tal:block replace="digit">1</tal:block>
    </tal:block>
    

    Testing has shown that Dreamweaver and GoLive, at least, have no problem with tal:block tags.

  2. Add a statement called tal:remove or tal:no_tag that replaces the statement’s tag with its children, after other TAL processing is done. The attribute value could be a boolean expression that controls whether the tag is removed, or omitted for the default behaviour. For example:

    <span tal:repeat="digit python:1,2,3"
          tal:remove="">
          <span tal:replace="digit">1</span>
    </span>
    
    <span tal:repeat="digit python:1,2,3"
          tal:no_tag="options/no_tags">
                  <span tal:replace="digit">1</span>
    </span>
    
Comments

(Earlier comments consolidated)

mindlace (Jun 17, 2001 4:02 am; Comment #1) –

Note that the block syntax would also allow me to use ZPT for things like CSS. I think the “big problem” with your own tags in DTML was things like if else /if , where the “middle” tag was a singleton, and no editor would be able to know if it should keep else.

From a pedantic perspective, having a block tag also lets TAL be a real schema as opposed to this floating attribute namespace.

byron (Jun 18, 2001 6:00 pm; Comment #9) –

Is there a missing double quote after tal:replace=”digit in the examples, or is that the syntax?

Evan - whoops, no, it’s a typo. Fixed now

richard (Jul 14, 2001 10:39 pm; Comment #10) –
+1
peterbe (Jul 16, 2001 3:28 am; Comment #11) Editor Remark Requested
+1 This is cool and necessary for survival for those XML Schemas you’re talking about. Please explain/discuss the possible values of the tal:remove attribute a bit more.
mindlace (Jul 18, 2001 1:40 pm; Comment #12) –
+6 :)
mindlace (Jul 18, 2001 1:42 pm; Comment #13) –
er… that goes for proposal 1. I’m indifferent about #2.
n.larosa (Jul 30, 2001 9:05 am; Comment #14) –

+1 for proposal #2. I would avoid the introduction of new tags, everything in attributes is cleaner.

This would be clean up the generated pages a lot. Also, often there are <div></div> empty tags that cannot be avoided, so I would like to see three values for the tal:remove attribute: yes, no and empty. The empty value would instruct the parser to only erase the tag if it is empty.

poster (Jul 30, 2001 10:41 pm; Comment #15) –
Proposal 1: +1
this seems necessary for a robust templating system (XML, as noted above), and acknowledges that it is structural logic and not a display tag; I would suggest this to be the last-resort method, preferring my alternate proposal below.
Proposal 2: -1
Pure structural logic masquerading as actual document markup is not only redundant but potentially confusing, and somewhat against the “What you see is very similar to what you get” ZPT principle. Of course, this argument could also be made against tal:replace as an attribute and not a tag… In any case, tal:remove would also significantly overlap tal:replace in functionality.
Alternate proposal to proposal 2: tal:repeat_internal

The situation in which the noted problem arises is in repeats. If a tag attribute is available that repeats only its internal contents, I believe many situations could be resolved more gracefully. In an example modified from one in Evan’s “Getting Started” tutorial, the tbody tag would not be endlessly and pointlessly repeated in the produced code, for instance:

<table><tr><th>Titles</th></tr>
  <tbody tal:repeat_internal="item container/objectValues">
  <tr bgcolor="#EEEEEE" tal:condition="repeat/item/even">
    <td tal:content="item/title">a title</td></tr>
    <tr tal:condition="repeat/item/odd">
    <td tal:content="item/title">a title</td></tr>
  </tbody>
</table>
bernt (Aug 12, 2001 9:13 am; Comment #17) –
See Default Content Variable Problem and Proposal for a related problem and an alternate solution.
Default Content Variable

The problem is resolved by the new TAL Specification Version 1.4

Forget it…
Problem

When using Page Templates for templating just some inner part of a html page, you need to replace some elements by their content:

<html>
  <body>
    The template should render as this text,
    without any tags before and behind.
  </body>
</html>

This is not possible with the default variable, because it adapts to replace and content dynamicaly.

Proposal

Add a variable defaultcontent to Tales, that represents the tag’s content.

With Tales having a variable like defaultcontent, one could replace:

<sometag tal:replace="defaultcontent">Original content</sometag>

by simply

Original content

Remark: There is surely a better (shorter) name for such a variable.

Did I miss an easy way to do so with the current language definition?

  • Yes, I saw METAL Reference. The proposal is what I expected to exist in the Tales language. I find it more intuitive.
  • The second proposal from the Disappearing Tags page would resolve my problem too.
Eliminate Path Modifiers

The path modifiers of TALES 1.2 were introduced in an attempt to provide simple spellings for two common capabilities:

  1. Choose between a path value and the default text, based on a boolean test.
  2. Deal well with invalid paths.

Several people (including the implementor) have found path modifiers to be confusing and inadequate. In a three hour meeting between Jim Fulton, Guido van Rossum, and Evan Simpson, the following proposed changes were worked out.

Features That Have Been Implemented
  • Add the alternation operator | to path expressions.

    A path expression can now contain multiple path separated by |. The value of the expression is the value of the first path that exists (proceeding from left to right). Spaces surrounding a | are not significant. If none of the paths exist, then the result is the same as if only the final path appeared in the expression.

    For example:

    <title tal:replace="request/title | here/title">Untitled</title>
    
  • Add new builtin variable default that can be used to keep default text.

    The default variable, like the nothing variable, contains a special value that affects TAL statements. Where nothing causes the replace, content, and attributes statements to delete their targets, default causes them to leave the existing target text alone. It is most useful when combined with the alternation operator.

  • Add path and exists functions to Python expressions.

    We decided to relegate more complex logic that tests for path existence to Python expressions, and make Python expressions more capable of dealing with paths.

    The path function takes a string, evaluates it in exactly the same way that the path expression type would, and returns the result. If the string doesn’t resolve to an existing path, the result is an error object.

    The exists function takes a string, tries to treat it as a path, and returns true if it is a valid path, false if it isn’t.

    Examples:

    <div tal:condition="python:exists('a/b/c')"
         tal:replace="a/b/c/d" />
    <div tal:replace="python:path('a/b') + 1" />
    
Features That Are Not Implemented

The following items have not yet been implemented:

  • Eliminate path modifiers.

  • Spell nocall by adding a trailing slash to a path.

    This means that modules/string/join/ will get you the join function, instead of an error about calling it with too few arguments.

  • Provide an alternation operator || which works like |, except that it treats the preceding path as non-existent if its value is false. Whether this use-case is significant enough to merit its own syntax is unknown.

As alternatives to these, we could keep only the path modifiers if and nocall. With if, we can write “(if) a | b” instead of a || b.

Comments

wleftwich (May 17, 2001 1:04 pm; Comment #2) Editor Remark Requested

Giving the trailing slash a special meaning seems like asking for trouble, since the mental model (mine anyway) is a filesystem path. (I have a problem with the double slash in XSLT also.)

How about moving nocall into a python function instead?

gotcha (May 18, 2001 3:48 am; Comment #3) –

I totally support the comment on the trailing slash…

I think also it would be far too easy not to see a missing trailing slash…

Page Properties
Problem

Some of the data associated with a page, such as its Title, should be available for inspection by other objects.

Proposal

Give Page Templates a property sheet just like Folders and DTML Documents.

Comments
Lynn Walton (Jun 12, 2001 11:14 pm; Comment #1) –

I definitely think properties for PageTemplates would be an incredibly useful feature. And also as part of it, an idea which Evan suggested in response to a mailing list post I made, having a global variable page too, where global variables would automatically be included in the page as though a <div tal:define=”….”></div> was done.

Here is a quote from Evan’s post that gives the idea more: (Hope Evan doesn’t mind me quoting this here.)

--------- QUOTE -----------------------
It does make me think, though, that rather than the
standard property sheet such as Folders and Documents have (or in
addition to such as sheet), it might be valuable to have a global
variable definition sheet, which would take a form like this (please
excuse ASCII art):

Variable    Expression
______    _____________
|x_____|  |here/foo_______|
______    _____________
|name__|  |string:Fred_____|
______    _____________
|four___|  |python:2+2____|

...and implicitly execute it at the start of page rendering, as though
the first line of the template were:

<div tal:define="global x here/foo;global name string:Fred;global four
python:2+2"></div>
-------------- END QUOTE ---------------------------------

So, I hope this idea get’s implemented too!

peterbe (Jul 16, 2001 4:04 am; Comment #2) –

+1

Wohoo! I’ve wanted this. Since ZPT has the template namespace we can use ZPT as both a DTML Method and DTML Document at the same time.

jmr (Jul 19, 2001 5:25 pm; Comment #3) –

+1

ZPT Properties would be great! (I didn’t see the original on the “global properties” mail, and can’t understand what is meant because the wiki has mangled the ascii art… so no comment on that comment.:)

Extending Macros

It’s often useful to override or elaborate one or more slots from a macro, or to provide additional slots, possibly as replacements for a single slot, without having to redefine all the slots of the “base” macro. This is especially useful with complex macros that may change frequently. This can almost be thought of as subclassing a macro.

Without the ability to extend macros directly, it becomes necessary for the macro that wants to extend another to duplicate each of the slots from the base macro. This is usually done with a stanza of the form:

<metal:block fill-slot="slotname">
  <metal:block define-slot="slotname">
    slot filler goes here
  </metal:block>
</metal:block>

While this isn’t the prettiest construct, it works to allow a template that uses this macro to fill a slot provided by a macro used by the macro providing this.

If there are many such slots in the “base” macro, however, this creates a great deal of bloat in the derived macro. Likewise, if the set of slots defined in the base macro, this is fragile and imposes an additional burden on the maintainer of the derived macro.

These problems can be avoided by providing an explicit way for macros to express that they extend other macros.

Desired Semantics

A macro has an interface for clients (either other macros or pages) defined by the set of slots that can be filled. When a macro is defined without the extension mechanism, the set of slots is defined entirely by elements with ‘define-slot’ attributes in the body of the macro.

A derived macro’s interface is determined a bit differently; the set of slots includes those provided directly by the macro and the slots of the base macro that are not filled by the derived macro. Clients can then fill any of the slots, and, if the client is a macro, define their own slots as well for their clients.

Note that each slot can only be filled once; slots cannot be filled by both the derived macro and the client; the client can only provide a filler if the derived macro provides a new slot to replace the one that was filled in; it may have the same name, but it has a unique identity.

Let’s look at some examples. Consider the following templates:

T1

<div metal:define-macro="base">
  <div metal:define-slot="A">
    default A filler
  </div>
  <div metal:define-slot="B">
    default B filler
  </div>
  <div metal:define-slot="C">
    default C filler
  </div>
</div>

T2

<div metal:define-macro="extension"
     metal:extend-macro="T1/macros/base">
  <div metal:fill-slot="A">
    slot A decoration from T2
    <div metal:define-slot="A">
      overridden A filler
      <div metal:define-slot="D">
        default D filler
      </block>
    </div>
  </div>
</block>

T3

<div metal:use-macro="T2/macros/extension">
  <div metal:fill-slot="A">
    final slot A filler
  </div>
  <div metal:fill-slot="C">
    final slot C filler
  </div>
  <div metal:fill-slot="D">
    final slot D filler
  </div>
</div>

If T3 is the template to be rendered, we expect to see T3’s filler for T2’s slots A and D, T2’s filler for T1’s A (with T3’s filler for T2’s A embedded within it), and T3’s filler for T1’s slot C:

<div>
  <div>
    slot A decoration from T2
    <div>
      final slot A filler
    </div>
    <div>
      final slot D filler
    </div>
  </div>
  <div>
    default B filler
  </block>
  <div>
    final slot C filler
  </block>
</block>

(The indentation here is illustrative, not necessarily what a processor will provide.)

Syntax

A new attribute is used to indicate that one macro extends another. This attribute, ‘extend-macro’, takes an expression that identifies the base macro as the value. The macro identified by that expression is treated as a source for slots not provided by the derived macro.

The derived macro must provide a name for itself using the ‘define-macro’ attribute.

The ‘use-macro’ attribute may not be used in conjunction with either ‘define-macro’ or ‘extend-macro’.

Implementation

This will be implemented in the ‘zope.tal’ package and included in Zope 3.1.0.

Module Access In TALES
Problem

DTML allows access to standard modules such as ‘string’ and ‘math’. Scripts can import and module that has security restrictions. TALES expressions (especially Python expressions) should have similar access.

Proposal

Add a builtin variable ‘modules’ that can be used to access Python modules that have security restrictions. In Python, this would be used with mapping notation:

python:modules['string'].split(x)
python:modules['Products.PythonScripts.standard'].html_quote(x)

The variable would be usable in path expressions also:

tal:define="join nocall modules/string/join"
Comments
richard (Apr 9, 2001 7:11 pm; Comment #1) –
I can’t help thinking that it’d be more user-friendly for the python: case to be able to use an attribute interface rather than a mapping interface…
evan (Apr 20, 2001 3:03 pm; Comment #2) –
Unfortunately, Python overloads dotted notation for module import and for attribute access. ‘modules[“a”].b’ is not semantically identical to ‘modules[“a.b”]’, since “import a; x=a.b” and “import a.b; x=a.b” mean two different things if ‘a’ is a package.
Act Only If Path Exists

Note

This has been superceded by Eliminate Path Modifiers.

Problem

Often you want to replace part of a template with information from the environment that may not always be present. For example, you might want to fill in data from the ‘request’ object, but provide default content if ‘request’ doesn’t contain the data.

Proposal

Allow a path expression to be marked so that it catches traversal exceptions, and cancels the TAL or METAL action in which it is used. If no exception is raised, continue normally.

When a statement is cancelled, the effect is the same as if the statement did not exist. In the case of ‘define’ and ‘attribute’ statements with more than one part, only the part containing the cancelling expression is affected, but if all parts are cancelled, the entire statment is cancelled.

A cancelled ‘replace’, ‘attributes’, ‘content’, ‘repeat’, or ‘use-macro’ statement leaves the text that would have been overwritten alone. A cancelled ‘define’ statement does not define a variable.

Given the following statements:

<span tal:replace="if exists request/x">No X</span>
<span tal:condition="if exists request/x">X</span>
<a href="/" tal:attribues="href if exists request/x;
   target if exists request/y">

A request without an ‘x’ or ‘y’ would produce:

<span>No X</span>

<a href="/">

A request with ‘x’ containing the string “X!” would produce:

X!
<span>X</span>
<a href="X!">

Without the ‘if exists’, each statement would produce the same result in the second case, but cause an error in the first.

Implemented
Disappearing Tags
Tags that aren’t rendered
Page Properties
Supply a page with data or meta-data.

Proposed Changes

If you have an idea for a new feature, please add a link to it here. Once there has been sufficient discussion, we’ll have a straw poll and add the features that have enough support. See Closed Proposals for proposals which have either been implemented or rejected.

TALES (Expressions)
Variables Expansion In Paths
Problem

TALES expression syntax provides variable path expansion in strings using the ${path/to/variable} syntax. However, this syntax is not allowed when constructing paths. (While this behavior can be simulated using python: strings, it is not intuitive why variable expansion should work in one case but not in another.)

For example, this works currently:

<li tal:repeat="item here/queryIds">
  <a tal:attributes="href string:here/subfolder/${item/id}">Link</a>
</li>

whereas this does not:

<li tal:repeat="item here/queryIds">
  <span tal:content="path:here/subfolder/${item/id}/method" />
</li>
Proposal

Allow for variable path expansion to occur when constructing paths. The most flexible way to do this might be to simply treat paths as strings, perform any expansions, and then traverse the resulting expression. The new syntax for path would be expressed as:

PathExpr  ::= Path [ '|' Path ]*
Path      ::= string_expression

where string_expression is already defined in TAL Reference as:

string_expression ::= ( plain_string | [ varsub ] )*
varsub            ::= ( '$' Path ) | ( '${' Path '}' )
plain_string      ::= ( '$$' | non_dollar )*
non_dollar        ::= any character except '$'

This would allow nested expansions:

<span tal:replace="path:here/${folder${item/id}}/method" />

as well as allowing the initial path variable to be, well, a variable:

<span tal:replace="${where}/object" />

Although if deemed undesirable, this usage could be disallowed while still allowing path expansion generally.

Rationale for Rejection

(Explanation from JimFulton.)

First of all, I see no justification for the generality of full string interpolation, with interpolated values treated as sub-expressions. The example in the proposal doesn’t require this generality. No one has given any convincing use cases why this (full string interpolation) is needed.

I do support making the current undocumented syntax official. The current syntax allows individual path segments to be retrieved from variables, as in:

here/folder/?id

Characters in the path segment variable are given no special meaning. This is especially useful if the object being traversed is a mapping object. A mapping object may have keys that contain special characters, like ‘/’s and ‘|’s. It’s important that these characters be used as data. Otherwise the mapping items would be inaccessible.

Expression Aliases
Problem

You can access other expression types from Python expressions by using the type name as a function, for example:

tal:replace="python:path(string('here/$x'))"

but ‘string’ conflicts with the string module, which would normally allow you to write expressions such as:

tal:replace="python:string.split(x, ':')[0]"
Proposal

Place the TALES expression functions into a builtin ‘tales’ namespace. This is more verbose, but more explicit:

tal:replace="python:tales.path(tales.string('here/$x'))"
Old Proposal

Note

Reaction to this was divided. The new proposal is more Pythonic.

Make capitalized versions (‘Path’, ‘Nocall’, ‘String’) of these functions, don’t replace ‘string’, and promote the capitalized version in docs. Then, you could use both, as in:

tal:replace="string.split(Path(String('here/$x')), ':')[0]"
Comments
pje (Aug 19, 2001 8:12 am; Comment #8) –

> but ‘string’ conflicts with the string module, which would normally allow > you to write expressions such as:: > > tal:replace=”python:string.split(x, ‘:’)[0]”

Um, now that Zope is on Python 2.1, can’t you do the above with:

tal:replace="python:x.split(':')[0]"

I know that’s just one example, but AFAICT the string module is all but deprecated in 2.1. The “tales.foo” approach seems awfully verbose to me, if string is the only conflict being worried about here. If a prefix is preferable to capitalization, how about “as”? e.g. “python:asPath(asString(‘here/$x’))”, or “python:as_path(as_string(‘here/$x’))”. Just a thought.

tone (Aug 20, 2001 12:21 am; Comment #9) –
I agree with pje. If ZPT is going to be core in 2.5, it’s important to keep things ‘clean’ and not too verbose. +1 for using 2.1 string-type methods.
User Variable

Give TALES a “user” variable. This would have the same value as DTML’s ‘_.SecurityGetUser()’.

Path Expression Variants

In TALES 1.0, there are two optional expression types which are variations on the path expression type. The ‘exists’ type returns a boolean value indicating whether the path is defined, and ‘nocall’ resolves the path without trying to render the resulting value.

These don’t need to clutter up the expression type namespace. Instead, since the root of a path is always a simple name and cannot contain spaces, we can make variant path expressions by prefixing the path with the modifier name, separated by a space. For example:

exists request/arg
nocall modules/ZTUtils/TreeMaker
if exists request/opt_arg

These can also be written with an explicit type, of course, as in:

path:exists request/arg
TAL (Statements)
Remove Insane Constructs
Proposal

The TAL 1.4 specification provides a number of loopholes for confusion. These appear to be issues where specific combinations of TAL statements weren’t considered when the specification was written. As TAL has been more broadly used, and especially as it’s seen adoption in Zope 3, some less carefully crafted corners of the specification have been identified.

This proposal suggests some specific improvements that should be made to the specification, which can be folded in to create a new revision. It is expected that at least the Zope 3 TAL interpreter can be improved as a result.

Specific Changes
  • ‘tal:replace’ and ‘tal:content’ can insert new markup structures into the output using the ‘structure’ keyword. The TAL 1.4 specification does not take a stance on whether this structure should be parsed and interpreted in any way; implementations are allowed to ignore embedded TAL constructs, but are permitted to interpret them.

    In the case when interpretation is permitted, attribute replacements specified using ‘tal:attributes’ are made to the first element found in the replacement. It is not clear that this behavior is useful, but it is very fragile.

    The Zope 3 implementation attempts to support the insertion of attributes, and does so by honoring all TAL within the replacement, but in the absence of ‘tal:attributes’ does not respect TAL embedded in the replacement structure.

    PETAL does not support interpretation of TAL in the replacement structure.

    The specification should be modified to disallow the interpretation of replacement structure.

  • If TAL in a replacement structure can never be interpreted, there is no value to ever combine ‘tal:replace’ and ‘tal:attributes’. This should be explicitly identified as an error in the template. ‘tal:content’ and ‘tal:attributes’ still makes sense.

References

structure and TAL interpretation was a thread on the ZPT mailing list where it was generally agreed that parsing a replacement structure was undesirable.

Comments
tseaver Thu Oct 21 15:04:00 US/Eastern 2004
+1 on disallowing ‘tal:replace’ with ‘tal:attributes’ as a programming error.
lalo Thu Oct 21 16:16:00 US/Eastern 2004

+1

IIRC OpenTAL ignores all other directives when a replace is executed, and doesn’t parse the generated structure either (which would be a rather tough proposition given OpenTAL’s model).

slinkp Thu Oct 21 18:21:00 US/Eastern 2004
In however many years I’ve been reading and writing zpt, it has never even occurred to me to want to do that. It could even be a security issue to interpret the structure. Maybe you are inserting some HTML written by some content jockey who is not allowed to write code. Let’s get rid of this.
efge Mon Oct 25 11:01:00 US/Eastern 2004
+1 on both
Replace Inner Text
Problem

When you use ‘tal:content’ or ‘tal:replace’ on a tag, then change the style of tag’s content with an editor, it is easy to accidentally insert markup tags into the content. This markup will then be lost when the page is rendered. For example:

<i><span tal:replace="x"><b><font size="+1">Text</font></b></span></i>

The ‘<i>’ tag is correctly placed, but the others will be lost.

Proposal

Add a TAL statement that replaces the content of its innermost simply nested tag. Starting with TAL statement tag, if the current element has only one child element (not counting whitespace), examine that element. When you reach an element with zero or more than one child, replace the content of that element.

In each of the following examples, ‘<b>’ is the innermost simply nested tag:

<b>Text</b>
<b>Text and <i>Markup</i></b>
<i><b>Text</b></i>
<span> <i> <b></b> </i> </span>

These are just examples of what it means for a tag to be “innermost simply nested”. I haven’t got a proposed spelling for the new statement, except that the value would presumably work the same as those of ‘tal:replace’ and ‘tal:content’.

Comments
peterbe (Jul 16, 2001 3:32 am; Comment #1) Editor Remark Requested

+1 But I don’t get it! With the propopsal, what would happen to this:

<i>&lt;span tal:replace="x"><b><font size="+1">Text</font></b></span></i>

This feature proposal is very important for the GoLive/Dreamweaver argument part of ZPT.

mindlace (Jul 18, 2001 1:47 pm; Comment #2) –
-1 DWIMpy! It’s my understanding that at least goliath can be set to work in “css mode” where it doesn’t do things like this to achieve presentation effects.
magnus (Jul 31, 2001 1:41 am; Comment #3) –
-1 If <i> <b> etc are the only reason, use CSS!
interra (Jul 31, 2001 3:06 am; Comment #4) –
-1 This kind of behavior is confusing. What if I want to mark all
fields to be replaced it template with Bold Italic Yellow text? I want them to be stripped out of result!
poster (Jul 31, 2001 11:43 am; Comment #5) –
</i></b>+1 (as a new separate attribute only): CSS is the right thing to do but production-line assembly with many designers I’ve worked with is under very little programmatic control, and CSS is something that neither the designers nor the marketers who foot their bills care much about. Having “replace inner text” as an optional tag (not changing tal:replace current functionality) will make programmers in these kind of designer-driven shops more sane, I think. Those of you not in this kind of designer-driven shop (be thankful) can still use tal:replace and justifiably insist on CSS.
Search and Replace
Problem

There are a number of cases in which it is not possible to put a tag at a place in a page where you want to insert text. These include the inside of ‘<script>’, ‘<style>’, and ‘<![CDATA[‘ tags. Most of these cases can probably be handled by dynamically including the entire tag, since the contents are likely to either be irrelevant to structured editing or very hard to mark for dynamic replacement without ruining structured editing. For example, most editors will not parse (much less execute) scripts, and while they may use a stylesheet, it will be hard to make a valid stylesheet that is also dynamic.

Proposal
  • Case-by-case: Make a TAL statement that specifies placeholder/expression pairs, and replaces all contained instances of the placeholder text with the value of the expression.

    For example:

    <script tal:put="IMGDIR here/images/absolute_url">
    for (var i=0; i < elements.length; i++)
      if (elements[i].type == 'img')
        elements[i].src = 'IMGDIR' + elements[i].src;
    </script>
    
  • Notation: Make a TAL statement that specifies placeholder delimiters, and replaces all contained path expressions with the specified delimiters:

    <script tal:paths="${ }">
    for (var i=0; i < elements.length; i++)
      if (elements[i].type == 'img')
        elements[i].src = '${here/images/absolute_url}' + elements[i].src;
    </script>
    
Comments
peterbe (Jul 16, 2001 3:49 am; Comment #1) Editor Remark Requested

-1 Why not something like this:

<script tal:replace="peter here/title">
 alert("You are in the peter page");
</script>

Which is rendered as:

<script>
 alert("You are in the My Title page");
</script>

Disabling the script and style tag will make it impossible to test (as in to test it with the script) offline zpt files in Dreamweaver or GoLive.

Keeps the number of TAL operators down. Will it confuse with ‘tal:replace=”here/title”’ ??

d.maurer (Jul 16, 2001 3:50 am; Comment #2) –
In the example
for (var i=0; i < elements.length; i++)
if (elements[i].type == ‘img’)
elements[i].src = ‘IMGDIR’ + elements[i].src;

I would prefer, if replacement were not done based on literal text but would use the variable syntax used elsewhere in TALES, e.g. $IMGDIR would be replaced by the value of “IMGDIR”.

Also note, that such replacements will often require context specific quoting. Probably, a set of quoting functions should be made available, including “url_quote”, “html_quote”, “string_quote”.

d.maurer (Jul 16, 2001 3:51 am; Comment #3) –
About this:

o Notation: Make a TAL statement that specifies placeholder delimiters, and replaces all contained path expressions with the specified delimiters:

<script tal:paths="${ }">
for (var i=0; i < elements.length; i++)
  if (elements[i].type == 'img')
    elements[i].src = '${here/images/absolute_url}' + elements[i].src;
</script>

I do not understand what is proposed here.

peterbe (Jul 16, 2001 3:52 am; Comment #4) –

oh fk, my structured text was mangled. I hope you see what it was meant to be.
mindlace (Jul 18, 2001 1:51 pm; Comment #5) –
+1 “dummy” text can be real for offline purposes.
poster (Jul 31, 2001 11:51 am; Comment #6) –
+1 for both proposals
Bring Back Dummy

I think that:

<tr tal:dummy="">.....</tr>

is more obvious than:

<tr tal:replace="nothing">.....</tr>

That is, I think a dedicated tag would make dummy or example (I’d be happy with tal:example.) elements easier to spot.

Comments
peterbe (Jun 12, 2001 6:24 am; Comment #1) –

I’m just a user, but my instinctive feel is that ‘dummy’ or ‘example’ should not be implemented:

  <tr tal:replace="nothing">.....</tr>

*does* feel very natural.
n.larosa (Jun 13, 2001 4:29 am; Comment #2) –
Well, this overlaps with the Comment Directive proposal. Is tal:comment more pleasant than tal:dummy ? I hope so.
peterbe (Jul 16, 2001 3:53 am; Comment #3) –

-1

go tal:comment !

mindlace (Jul 18, 2001 2:04 pm; Comment #4) –
-1 but how about tal:replace=”structure nothing”?
n.larosa (Jul 30, 2001 8:43 am; Comment #6) –

I guess I now understand the difference between the Comment Directive and Bring Back Dummy.

A tal:comment attribute would be erased by itself, leaving the tag in place, while a tal:dummy attribute would make the whole tag disappear.

Is this correct? If so, I see the need for something dummy-like. tal:replace="nothing" is too verbose, and does not clearly express the fact that this is a comment. I would use the tal:comment in place of the tal:dummy one, though.

On the other hand, the standard HTML comment syntax leaks to the generated HTML pages, something to be avoided. And there’s no such option in XML, generally speaking.

A <span tal:comment="SQL data here"> tag would not impact the designer’s layout, while <`span tal:comment="">Jim, please no italics. Thanks.</span> would be visible.

Comment Directive

It would be nice, and obviously trivial to provide a do-nothing comment directive. I could see providing one in both the tal and metal namespaces:

<span tal:content="here/splat"
      tal:comment="Jim was here"> ...
Comments
peterbe (Jun 12, 2001 6:30 am; Comment #1) –

I’m in on that!

A quick check (did not visit www.w3c.org) proved that Golive have no problem with:

<div tal:comment="

On many
Lines
">Commented DIV</div>

It would be be very nice to be able to write comments on many lines. Can this be done?

n.larosa (Jun 13, 2001 4:27 am; Comment #2) –
Yeah! Go for it! I proposed this on the mailing list a while ago, but GvR was not impressed. Let’s help him see the light! ;^)
fdrake (Jul 13, 2001 3:06 pm; Comment #3) –

Why would this need to be a TAL attribute? What about:

<div my:comment="
   This is my long comment.
   I could go on for pages and pages...
  ">Commented DIV</div>

Is it a matter of filtering out the attribute in the rendering? I can understand that you might not want the comments public.

mindlace (Jul 18, 2001 2:05 pm; Comment #5) –
-1 what’s the difference between this and existing comment syntax or the ability to just use a bogus tag?
chaironome Tue Aug 31 21:50:00 US/Eastern 2004
I think this is a fantastic idea. One could use tal:comment=”this is my comment”. It would be the perfect way to explain what you are trying to accomplish just by glancing at the page template. Best of all, if Zope recognized this tag, the server could assure that these comments are always filtered out.
Empty Attribute Options

If we had attributes (e.g. tal:dummy) whos values must always be empty, it would be nice, in HTML mode, to allow simple option syntax:

<tr tal:dummy> ... </tr>
Comments
peterbe (Jul 16, 2001 3:55 am; Comment #1) Editor Remark Requested
Please elaborate more here. The word “dummy” annoys me. It feel unserious and a bit too funky.
n.larosa (Jul 30, 2001 9:20 am; Comment #2) –

Well, if you do not like “dummy”, think “comment” or something else, that’s not the point here.

My vote is 0 on this, saving three chars does not seem like much. On the other hand, it would not validate as XHTML 1.0.

Optional In and As
Problem
No problem :-)
Proposal

Allow noise particles ‘in’ and ‘as’ in ‘tal:repeat’ and ‘tal:define’:

tal:repeat="item in here/items"

tal:define="x as request/form/x"

Don’t require them, though.

Comments
n.larosa (Jun 17, 2001 4:50 am; Comment #1) –
Yes, please! My Templates are getting a little dense, and those repeats are getting difficult to (visually) decode in a hurry. (Yes, I do am careful to delegate all logic I can to separate scripts.) Gimme gimme gimme. ;^)
peterbe (Jul 16, 2001 3:58 am; Comment #2) –

0

Not a scientist my self so I don’t know what laws and principles apply to templating syntaxes such as ZPT.

One thing I really like about Python is that there is only one way of writing things and that isn’t too hard to learn. More alternatives will confuse users (especially new ones) Smells like the confusion ‘<dtml-var name=”peter”>’ and ‘<dtml-var peter>’

mindlace (Jul 18, 2001 2:07 pm; Comment #3) –
+1 if it is promoted as the canonical way, and the other is eventually deprecated. Otherwise, keep the original syntax.
tanghus (Jul 29, 2001 6:58 am; Comment #4) –
+1 (As comment #3)
poster (Jul 31, 2001 11:30 am; Comment #5) –
+1 (“Me too” with mindlace’s reservation)
dracvl (Oct 25, 2001 4:21 am; Comment #6) –
Yes, please. The syntax looks much better this way, and will surely help newcomers to Page Templates.
Plural Synonyms
Problem

It’s easy to forget whether the correct spelling of some of the TAL attributes is plural or not. In particular, a prominent member of the ZPT community used ‘tal:contents’ instead of ‘tal:content’.

Proposal

Make synonyms for all of the TAL statements for which it is reasonable to have both the singular and plural forms. In particular:

  • content / contents
  • attribute / attributes
  • define / defines
  • on-error / on-errors

We would never have given any of these new names a different meaning for fear of confusing users, so it seems reasonable to me to give them the same meaning. I omitted ‘conditions’, since that implies (to me) that it allows multiple conditions. I don’t have a strong reason for leaving out ‘replaces’ and ‘repeats’, just a vague feeling that they’re unlikely to suffer from the same confusion, since they are verbs and don’t accept multiple parts.

Comments
peterbe (Jul 16, 2001 4:01 am; Comment #1) –

-1 NO!

I agree with myself that this will only cause confusion. The Python “only one” is better. It isn’t that difficult to remember these things.

mindlace (Jul 18, 2001 2:10 pm; Comment #2) –
-1 TOOWTDI
poster (Jul 31, 2001 11:31 am; Comment #3) –
-1
Structure As Default

Somebody wants to make structure mode the default.

Comments
Dwonis Tue Dec 21 00:16:00 US/Eastern 2004

I disagree. Structure mode should absolutely not be the default.

In cases where template developers don’t understand the difference between text and structure mode, it is much safer to default to text mode than to structure mode.

The current default (text mode) is fail-safe. If the user doesn’t need structure mode, ZPT will handle things as expected. If the user does need structure mode, the mistake is obvious

METAL (Macros)
Whole Page Macros
Problem

If you want to re-use an entire page as a macro, you can’t if the page has elements outside of the outermost tag. In particular, ‘<!DOCTYPE>’ must precede the main tag and ‘<?xml?>’ must precede everything else if they are used.

It is trivial to expose the entire text of a Page Templates as a macro through a ‘macro’ attribute. Unfortunately, there is no place to put a ‘metal:use-macro’ statement that uses a full-page macro. If you wrote ‘<html metal:use-macro=”here/other.zpt/macro”>’, for instance, the ‘<html>’ tag would be replaced by the contents of ‘other.zpt’. The resulting macro expansion would not have ‘metal:use-macro’ in it, so further changes would break the connection.

Proposals
  • Add a ‘master_macro’ property to Page Templates. Since it would not be part of the text of the page, it would not be broken by the macro expansion process. It wouldn’t be visible in (or editable through) the source, though, which could be a problem.

  • Add a processing instruction ‘<?metal use-macro=””?>’ that would act like the ‘master_macro’ property, except that it would be re-inserted into the expanded macro at the top, or just below the ‘<?xml?>’, if there is one. The line below might expand into the text that follows it:

    <?metal use-macro="here/other.zpt/macro"?>
    
    <?xml version="1.0"?>
    <?metal use-macro="here/other.zpt/macro"?>
    <records tal:repeat="record options/records">
      <record tal:content="structure record"></record>
    </records>
    
Comments
mindlace (Jul 18, 2001 2:12 pm; Comment #1) –
+1 for the second option.
ElviX (Aug 6, 2001 5:17 am; Comment #2) –

+1 for both

Never use wysiwyg editors anyway, so the first one would never pose a problem..

dracvl (Oct 25, 2001 4:25 am; Comment #3) –
I think both would be useful, the master macro to quickly apply the same doctype to the whole site, and the other one to have selected parts of the site have a specific doctype. Number two should then be able to override the master macro? (I’m tired and don’t think clearly, just think that a rule is needed if we want to keep both)
Editing Expanded Macros

I’ve been playing with macros a bit, and it occurs to me that while it is a great safety feature to ignore edits to expanded macros in the page templates that use them, this really ought to be a switchable behaviour.

In other words, I’d like to be able to add a metal:edit-expanded="allow" statement to the macro definition in order to facilitate rapid development (edits to the expanded macro are applied to the macro definition), and then remove the statement when I want to lock the macro down.

Alternatively, this could be a checkbox in the ZMI, but I think I like the attribute better.

When I’m designing an overall page layout, I find that I need to evaluate it in different contexts and tweak it in different ways depending on the specifics of the page. All page layouts end up being compromises between the needs of the different contexts in which the layout is used. Developing consistent navigation systems for sites seems to be the hardest balancing act.

It’s annoying to have to constantly switch back and forth between the context that I am currently evaluating and the ‘master’ macro template in order to edit it. There is little productivity improvement over using DTML in this case.

Being able to edit the expanded macro and have those changes applied to the macro definition would save a lot of time and annoyance, and make it easier and quicker to find the balance between the different contexts’ needs.

I want to emphasize again that I would not advocate that this statement be left on the macro beyond the prototyping stage, or that it be used in situations where one person is doing the design, and another does the programming.

However, Zope is used by many people who are a ‘one-man-show’ so to speak, and making their life easier only makes sense, especially as this will not compromise any of ZPT’s goals for other audiences.

Expanded Macro Indentation

I think it would be nice if an expanded macro was automatically indented as far as the use-macro element that it was replacing. Currently, this is only true for the starting element of the expanded macro, other elements are only indented as far as they are in the macro definition.

This would make the page template with the expanded macros easier to understand and edit.

Comments
alzhimer (Mar 22, 2002 5:50 am; Comment #1) –

even nicer would be to have an option to automagically format the whole PT according to the structure of the HTML. or at least, at a start, keep the indenting as is (now, with “expand macros” enabled it would justify every line to the left which makes the PT very hard to read).

The auto-formatted HTML might look something like:

<html>
<head>
  <title>The title</title>
</head>
<body>
  <h1>Cool Page</h1>
  <p>
    Some Text here...
  </p>
</body>
</html>
Page Templates
New Icons

I find myself really disliking the ZPT icon, so I designed two replacement icons .The difference between them is subtle, but intended to suggest other possible color variations.

Caution

The links here don’t seem to work.

Here are links to the two icons:

http://216.32.129.50/zpt1.gif

http://216.32.129.50/zpt2.gif

The icons try to symbolize the concept of a ‘document template’ by adding a dashed line (as in ‘stuff goes here’) inside a document icon. In zpt2.gif, the area defined by the dashed line is made slightly more distinct by changing its color to a pale yellow.

I’ve created two more variations:

http://216.32.129.50/zpt3.gif

http://216.32.129.50/zpt4.gif

The change I made (besides the color variations) was to take the ‘template’ area (surrounded by a dashed line), make it a bit smaller and offset it a bit off the page icon to indicate that ‘stuff comes in from somewhere else’. If anyone has suggestions for further variations, please let me know.

I’m also wondering if it’s possible to display slightly different icons depending on whether the Page Template defines a macro or not? This would only be a small usability improvement, but worthwhile, I think.

Comments
dracvl (Oct 25, 2001 4:29 am; Comment #1) –
I like the first one. It’s clean and understandable, and represent Page Templates nicely. The others are a bit too complicated.
tal:on-error=”default”
Make it the default behavior to save typing

IRC Chat Sessions

From time to time, DigitalCreations will host a live IRC chat. Each chat will have its own page here, with relevent info and logs (after the fact, of course).

Sessions

First IRC Chat Session for ZPT

Held: Friday, April 6 at 1pm EST

Hosts: Evan Simpson and Ethan Fremen

Log: http://www.zope.org/Documentation/Chats/zpt_chat_log

Intro

The purpose of this chat is to introduce Zope Page Templates to members of the Zope community who would like to learn more about them, and to discuss the features and future development of ZPT.

Preamble

Zope Page Templates were created with a very specific audience in mind: teams of designers and programmers who work closely and iteratively to design sites.

ZPT is not a replacement for DTML in any sense. It is an alternative that is meant to be attractive to designers who use WYSIWYG editors, and the programmers who work with them.

Obsolete Template Attribute Language Expression Syntax

Note

This is the former syntax. See TALES Reference for the new syntax.

TALES is the recommended syntax for TAL Reference expressions. It is an extensible syntax that allows you to define any number of expression types and use them together. A TALES implementation has a set of expression types with associated handlers. It is the handlers that determine the form and meaning of each type of expression.

A TALES expression is a string with an optional type prefix, which is a simple name followed by a colon. A simple name is a string containing only letters, numbers, and underscores (‘_’). If an expression has a type prefix, the simple name is the expression type, otherwise the expression type is ‘standard’. Examples:

python: 1 + 2
standard:/a/b/c
/a/b/c
str:Hello, ${username}

To evaluate a TALES expression, you determine the expression type (removing the type prefix, if any), look up the handler, and pass the expression to the handler along with a reference to the evaluator. The handler can use this reference to evaluate subexpressions.

Presentation Templates

The implementation of Presentation Templates uses TAL with TALES, and defines a standard set of expression types. The ‘standard’ type of expression is the Path Expression.

Path Expressions

A path expression consists of one or more strings separated by slashes. The first string is the context name, and must be a simple name. The remaining path segments may contain any character except slash (‘/’). For example:

request/cookies/oatmeal
template
here/Oh, my &@#*^! What a terrible name!/foo
/path/from/the/root

When a Presentation Template evaluates a path expression, it looks up the context name in a registry to get a starting object. If there are path segments, it traverses from the current object to the next object using each path segment in turn. If the resulting object is callable, it is called. There will be a syntax (prefixing the path with ‘nocall:’, for instance) for returning the raw object.

Since context names are the starting places from which you can find various objects and values, they are the fundamental building block of path expressions.

Context Names in Presentation Templates

PresentationTemplates define the following context names:

  • The null context name (when a path starts with a slash) refers to the Zope root object.
  • here - the object to which the template is being applied.
  • container - the template’s container object.
  • template - the template itself.
  • request - the zope publishing request object.
  • node - an object representing the current XML node. This can be used to access attributes, or children.
  • nothing - special TAL singleton value (Python ‘None’).
Other Expression Types
  • local - treat the text as a path expression, looking the context name up in the local variable namespace.
  • global - treat the text as a path expression, looking the context name up in the global variable namespace.
  • var - treat the text as a path expression, looking the context name up in first the local, then the global variable namespaces.
  • loop - treat the text as a path expression, looking the context name up in the loop variable namespace.
  • python - interpret the text as restricted Python code.
  • str - interpret the text as a literal string.
  • not - interpret the text as a full expression, and return the boolean negation of its value. In other words, ‘not:’ can be followed by any expression, for example ‘not:var:x’.
Potential Future Expression Types
  • perl
  • xpath
Comments
gvanrossum (Jan 29, 2001 8:19 pm; Comment #1) –
  • var - treat the text as a path expression, looking the context name up in first the local, then the global variable namespaces.

Do we really need this as well as local? I’d suggest to use these semantics for local. These are the semantics used by Python (first look up locals, then globals) and they work very well.

Note: I believe that the specs here don’t clarify that local variables defined in outer scopes remain visible in inner scopes unless redefined in an inner scope. That is of course an essential property – and it is the semantics chosen by Python 2.1 for nested scopes. (Making the globals the outermost scope also suggests that having both var and local is redundant.)

evan (Jan 30, 2001 3:07 pm; Comment #3) –
Do we really need this as well as local?

local allows us to explicitly state that we expect a local variable, and want a name error if it isn’t there. var will presumably be the most commonly used namespace. Global are the outermost scope, but they are also explicitly accessible through global regardless of intervening local definitions.

Presentation Templates

See Page Templates

Python Expression Reference

This is a quick reference guide for using TALES Python expressions in Zope Page Templates.

Variables and Object Access

All TALES variables, including builtin, global, and local variables, can be used in Python expressions. See TALES Reference for more information. Builtin variable uses are described below:

  • root: Get properties or sub-objects of the Zope root object.
  • here: If your template is being applied to a content object, use this to access it. You can also use this to acquire objects or properties from the current acquisition context.
  • container: Access objects in the same location as the template, without regard to where it is being used.
  • template: Get the template’s title, or use the template’s own macro definitions elsewhere in itself.
  • request: Read submitted form contents, environment information, and the RESPONSE object. Set request variables for use by other objects.
  • user: Check the user’s access rights, or get their login name.
  • modules: Call Python functions stored in filesystem modules and packages. Use subitem notation, with the full dotted module access path as the key, like this: “modules![‘package.module’]”.
  • attrs: Get the default (original) values of the statement element’s attributes. This is a dictionary, with the attribute name as the key.
  • options: Get keyword arguments passed to the template, when it is called from a Method or Script. This is a dictionary.
  • repeat: Access the iterator information for enclosing TAL repeat statements. Use subitem notation, with the repeat variable name as the key.
  • default and nothing: Return these as the value of the expression to control TAL statements.

You can also use the same builtin functions as in Python Scripts and DTML namespaces, such as ‘str()’, ‘getattr()’, and ‘DateTime()’. See “Appendix A of the Zope Book”, http://www.zope.org/Members/michel/ZB/AppendixA.dtml for more information. A few of them deserve special mention here:

  • test: This allows you to choose among two or more alternate value based on boolean expressions. This can be very useful for operations such as giving different styles to alternating lines of a table:

    tal:define="oddrow repeat/item/odd"
    tal:attributes="class python:test(oddrow, 'odd', 'even')"
    
  • DateTime: Get the current date and time, or construct a DateTime object from a string or other data.

Path and String Expressions in Python

Each of the TALES expression types can be invoked from Python by a function with the same name. Here are some situations in which other expression types are useful inside of Python expressions:

  • path: Since the path function returns a special false value when the path cannot be traversed, boolean logic can be used to select the first of several paths which both exists and has a true value, like this:

    path('here/a') or path('here/b')
    
  • path: Fetch an object with a dynamic path:

    path('container/%s/index_html' % folder_name)
    
  • string: Fetch an object with a complex dynamic path:

    path(string('here/${options/foo}/${request/bar}'))
    
Formatting DateTimes

DateTime objects have dozens of methods that you can use to extract information such as the month, day of the week, etc. See the “DateTime API Reference”, http://www.zope.org/Members/michel/ZB/AppendixB.dtml for more information. The most general of these is the ‘strftime()’ method, which uses a format string with special placeholders, like this:

"%b %e, %Y" produces "Jul  4, 2001"
"%m/%d/%y" produces "07/04/01"
Formatting Numbers and Strings

Python’s formatting operator (‘%’) can be used for simple numeric formatting and string concatenation; It is very efficient. See the “Python Reference”, http://www.python.org/doc/current/lib/typesseq-strings.html for details. A simple example:

"Hello %s, you have %s tries and $%.2f left." % (
  name, attempts, dollars)

There is a library of commonly-used formatting functions in the PythonScripts Product that duplicate format options available in DTML. You can get the module with a variable definition like this:

tal:define="pss modules/Products/PythonScripts/standard"

Then you can convert newlines to HTML break tags:

tal:replace="structure python:pss.newline_to_br(lines)"

format monetary and numeric values:

tal:replace="python:pss.whole_dollars(money)"
tal:replace="python:pss.dollars_and_cents(money)"
tal:replace="python:pss.thousands_commas(n)"

quote strings for safe use in !URLs, or in SQL:

tal:replace="python:pss.url_quote(anURL)"
tal:replace="python:pss.url_quote_plus(anURL)"
tal:replace="python:pss.sql_quote(sql_text)"

or convert structured text into HTML:

tal:replace="structure python:pss.structured_text(txt)"

Macro Expansion for TAL (or METAL)

METAL Specification Version 1.0

The Macro Expansion Template Attribute Language is an Attribute Languages for structured macro preprocessing. It can be used in conjunction with or independently of Template Attribute Language, Template Attribute Language Expression Syntax and ZPT.

Macros provide a way to define a chunk of presentation in one template, and share it in others, so that changes to the macro are immediately reflected in all of the places that share it. Additionally, macros are always fully expanded, even in a template’s source text, so that the template appears very similar to its final rendering.

The METAL namespace URI and recommended alias are currently defined as:

xmlns:metal="http://xml.zope.org/namespaces/metal"
METAL Statements

See EBNF for rules and terminals. See Attribute Languages for a description of attribute language statements.

The following are the names of the required TAL 1.0 statements:

  • define-macro
  • use-macro
  • define-slot
  • use-slot

Each statement is described below, along with its argument syntax.

Although METAL does not define the syntax of ‘expression’ non-terminals, leaving that up to the implementation, a canonical expression syntax for use in METAL arguments is described in TALES Specification Version 1.0.

define-macro

Syntax:

argument ::= Name

To define a macro, choose a name for the macro and add a ‘define-macro’ attribute to a document element with the name as the argument. The element’s subtree is the macro body. Example:

<p metal:define-macro="copyright">
 Copyright 2001, <em>Foobar</em> Inc.
</p>
use-macro

Syntax:

argument ::= expression

To use a macro, first choose the document element that you want to replace. Add a ‘use-macro’ attribute to it, and set the value of the attribute to an expression that will return a macro definition. This will usually be some kind of path or template id and a macro name. It is important to remember that this expression will be evaluated separately from any Template Attribute Language commands in the template, so it cannot depend on any such commands.

Note

PageTemplates use Template Attribute Language Expression Syntax for the expression, and you can use any of the standard context names in path expressions. Since they expose their collection of macro definitions through a ‘macros’ attribute, you can access individual macros easily.* For example:

<hr />
 <p metal:use-macro="here/master_page/macros/copyright">
<hr />
Macro Expansion

The effect of expanding a macro is to graft a subtree from another document (or from elsewhere in the current document) in place of the statement element, replacing the existing subtree. Parts of the original subtree may remain, grafted onto the new subtree, if the macro has slots. If the macro body uses any macros, they are expanded first.

When a macro is expanded, its ‘define-macro’ attribute is replaced with the ‘use-macro’ attribute from the statement element. This makes the root of the expanded macro a valid ‘use-macro’ statement element.

define-slot and fill-slot

Syntax:

argument ::= Name

Macros are much more useful if you can override parts of them when you use them. For example, you might want to reuse a complex table, but provide different contents for one of the table cells in every place that you use it. It would be possible to place the contents somewhere on each Tempate, define a variable for it, and ‘insert’ that variable into the cell in the macro body. This, however, would violate the presentation structure, preventing you from seeing the table with the cell contents in place.

To make elements of the macro body overridable, add ‘define-slot’ attributes with the value set to a slot name. Wherever the macro is used, choose corresponding sub-elements of the statement element and add ‘fill-slot’ attributes with the value set to the slot name. When the macro is expanded, ‘fill-slot’ elements will replace the ‘define-slot’ elements in the macro body that use the same slot name.

Slot names must be unique within a single macro, and within a single macro use. It is legal, however, to define a slot in a macro and not fill it. This will simply cause the default contents of the slot definition to be copied into the expanded macro. If a ‘fill-slot’ element names a slot that is not found in the macro body, it causes an error.

Examples:

In doc1:
 <table metal:define-macro="sidebar">
   <tr><th>Links</th></tr>
   <tr><td metal:define-slot="links">
     <a href="/">A Link</a>
   </td></tr>
 </table>

In doc2:
 <table metal:use-macro="here/doc1/macros/sidebar">
   <tr><th>Links</th></tr>
   <tr><td metal:fill-slot="links">
     <a href="http://www.goodplace.com">Good Place</a><br>
     <a href="http://www.badplace.com">Bad Place</a><br>
     <a href="http://www.otherplace.com">Other Place</a>
   </td></tr>
 </table>

Notice that ‘doc2’, which uses the macro defined in ‘doc1’, contains the entire text of the ‘sidebar’ macro except for the ‘links’ slot. This is because the macro is inserted every time the source of ‘doc2’ is edited.

Comments
gotcha (Mar 16, 2001 5:15 am; Comment #1) Editor Remark Requested

Two questions:

I have found no Use Cases for METAL so I would like to state one of them :

UseCase

I create a new template (NT) and I want to reuse some presentation code from a previous template (PT) I have written.

1 I must edit PT and add ‘metal:define-macro’ attribute in the element I want to reuse

2 I can then add in NT a ‘metal:use-macro’ attribute to an element I want to be replaced by the corresponding excerpt of PT

3 when i later access NT, NT element with ‘metal:use-macro’ is replaced by the (recursive) expansion of PT element

4 later, I can change PT ‘metal:define-macro’ element content

5 following access to NT reflects changes in PT

PS please excuse poor english

Are there any need to keep both this page and METAL page ?

METAL Specification Version 1.1

The Macro Expansion Template Attribute Language is an Attribute Languages for structured macro preprocessing. It can be used in conjunction with or independently of Template Attribute Language, Template Attribute Language Expression Syntax and ZPT.

Macros provide a way to define a chunk of presentation in one template, and share it in others, so that changes to the macro are immediately reflected in all of the places that share it. Additionally, macros are always fully expanded, even in a template’s source text, so that the template appears very similar to its final rendering.

The METAL namespace URI and recommended alias are currently defined as:

xmlns:metal="http://xml.zope.org/namespaces/metal"
METAL Statements

See EBNF for rules and terminals. See Attribute Languages for a description of attribute language statements.

The following are the names of the required TAL 1.0 statements:

  • define-macro
  • extend-macro
  • use-macro
  • define-slot
  • use-slot

Each statement is described below, along with its argument syntax.

Although METAL does not define the syntax of ‘expression’ non-terminals, leaving that up to the implementation, a canonical expression syntax for use in METAL arguments is described in TALES Specification Version 1.0.

define-macro

Syntax:

argument ::= Name

To define a macro, choose a name for the macro and add a ‘define-macro’ attribute to a document element with the name as the argument. The element’s subtree is the macro body. ‘define-macro’ may not be combined with ‘use-macro’. Example:

<p metal:define-macro="copyright">
 Copyright 2001, <em>Foobar</em> Inc.
</p>
extend-macro

Syntax:

argument ::= expression

To extend an existing macro, choose a name for the macro and add a ‘define-macro’ attribute to a document element with the name as the argument. Add an ‘extend-macro’ attribute to the document element with an expression referencing the base macro as the argument. The ‘extend-macro’ must be used in conjunction with ‘define-macro’, and must not be used with ‘use-macro’. The element’s subtree is the macro body. Example:

<div metal:define-macro="page-header"
     metal:extend-macro="standard_macros/macros/page-header">
  <div metal:fill-slot="breadcrumbs">
    You are here:
    <div metal:define-slot="breadcrumbs"/>
  </div>
</div>

The rationale for the Extending Macros feature is given elsewhere.

use-macro

Syntax:

argument ::= expression

To use a macro, first choose the document element that you want to replace. Add a ‘use-macro’ attribute to it, and set the value of the attribute to an expression that will return a macro definition. This will usually be some kind of path or template id and a macro name. It is important to remember that this expression will be evaluated separately from any [TAL] commands in the template, so it cannot depend on any such commands. ‘use-macro’ may not be combined with ‘define-macro’ or ‘extend-macro’.

Note

PageTemplates use Template Attribute Language Expression Syntax for the expression, and you can use any of the standard context names in path expressions. Since they expose their collection of macro definitions through a ‘macros’ attribute, you can access individual macros easily.* For example:

<hr />
 <p metal:use-macro="here/master_page/macros/copyright">
<hr />
Macro Expansion

The effect of expanding a macro is to graft a subtree from another document (or from elsewhere in the current document) in place of the statement element, replacing the existing subtree. Parts of the original subtree may remain, grafted onto the new subtree, if the macro has slots. If the macro body uses any macros, they are expanded first.

When a macro is expanded, its ‘define-macro’ attribute is replaced with the ‘use-macro’ attribute from the statement element. This makes the root of the expanded macro a valid ‘use-macro’ statement element.

define-slot and fill-slot

Syntax:

argument ::= Name

Macros are much more useful if you can override parts of them when you use them. For example, you might want to reuse a complex table, but provide different contents for one of the table cells in every place that you use it. It would be possible to place the contents somewhere on each Tempate, define a variable for it, and ‘insert’ that variable into the cell in the macro body. This, however, would violate the presentation structure, preventing you from seeing the table with the cell contents in place.

To make elements of the macro body overridable, add ‘define-slot’ attributes with the value set to a slot name. Wherever the macro is used, choose corresponding sub-elements of the statement element and add ‘fill-slot’ attributes with the value set to the slot name. When the macro is expanded, ‘fill-slot’ elements will replace the ‘define-slot’ elements in the macro body that use the same slot name.

Slot names must be unique within a single macro, and within a single macro use. It is legal, however, to define a slot in a macro and not.. fill it. This will simply cause the default contents of the slot definition to be copied into the expanded macro. If a ‘fill-slot’ element names a slot that is not found in the macro body, it causes an error.

Examples:

In doc1:
 <table metal:define-macro="sidebar">
   <tr><th>Links</th></tr>
   <tr><td metal:define-slot="links">
     <a href="/">A Link</a>
   </td></tr>
 </table>

In doc2:
 <table metal:use-macro="here/doc1/macros/sidebar">
   <tr><th>Links</th></tr>
   <tr><td metal:fill-slot="links">
     <a href="http://www.goodplace.com">Good Place</a><br>
     <a href="http://www.badplace.com">Bad Place</a><br>
     <a href="http://www.otherplace.com">Other Place</a>
   </td></tr>
 </table>

Notice that ‘doc2’, which uses the macro defined in ‘doc1’, contains the entire text of the ‘sidebar’ macro except for the ‘links’ slot. This is because the macro is inserted every time the source of ‘doc2’ is edited.

When the ‘extend-macro’ statement is being used, the set of ‘define-slot’ statements that apply is determined slightly differently. Slots defined by the base macro remain available for the template to fill, unless the derived macro fills them itself. Each slot can be filled at most once. The derived macro is allowed to offer a replacement slot that may be filled in lieu of a slot from the base macro. The template has no way to determine which macro is providing the slot. If a template attempts to fill a slot which is not available because a derived macro filled it already but did not offer a new slot of the same name, the slot-filler from the template will be ignored, just like any filler for an undefined slot.

Macros provide a way to define a chunk of presentation in one template, and share it in others, so that changes to the macro are immediately reflected in all of the places that share it.

Macros are a special set of TAL statements that are processed separately from other statements. There are statements to define macros and macro slots, and to use macros and provide macro arguments. When a template which uses METAL macros is rendered, it expands them before processing other TAL statements.

PageTemplates implement METAL, and expand all of the macros used in a template when generating its source, as well as when rendering it. This allows macros to be viewed in-place.

See the METAL specifications for details of what METAL provides:

Comments
emiliano (Mar 13, 2001 4:27 am; Comment #3) –
If the macros are unfolded in full it is essential that it be very clear to the user which parts are editable, especially if you’re editing a macro that includes static parts (non-slots) or that calls other macros itself. Assuming that there’s some kind of access control scheme in place, this can get quite tricky.

Template Attribute Language

The Template Attribute Language is an Attribute Languages used to create dynamic templates. By marking elements of your HTML or XML document with TAL statement attributes, you can replace parts of the document with dynamically computed content. You can insert text or portions of other documents, repeat elements such as list tags or table rows, or choose whether or not to include optional parts.

See TAL Reference for the current implementation specification.

See Proposed Changes for discussion of the next TAL specification.

Error Handling proposals exist in Error Handling Strategies.

Template Attribute Language Expression Syntax

TALES is the recommended syntax for Template Attribute Language expressions. It is an extensible syntax that allows you to define any number of expression types and use them together.

See TALES Reference for the current implementation specification.

See Discuss TALES for discussion of proposals for the next specification.

EBNF

Here is the initial cut at an EBNF syntax (work still in progress)!

EBNF Syntax:

[ ... ] apply zero or one times;
[ ... ]* apply zero or more times;
[ ... ]+ apply one or more times;
... | ... choose one of the alternatives;
'...' use the literal characters enclosed;
( ... ) used for grouping.

EBNF Terminals:

String      ::=
Name        ::=
URL_Segment ::=

Letter      ::=
Number     ::=
NonAlphaId ::=
Comments
hansa (Jul 6, 2001 12:15 pm; Comment #1) Editor Remark Requested
it would be nice, to have EBNF for String..NonAlphaId specified and please consistent w a)the specs b) the tutorial

Discuss TALES

This is the main page for proposing changes to TALES. Please add a sub-page for each proposal, and discuss it in comments to that page.

Proposals

Implemented in ZPT 1.3

Implemented in ZPT 1.2

  • Module Access In TALES - Provide access to imported Python modules through a builtin variable.
  • Act Only If Path Exists - Provide a convenient way to perform a TAL action if a path resolves, but do nothing if it does not.
  • Path Expression Variants - Change ‘exists’ and ‘nocall’ from TALES expression types into optional path expression prefixes.

Repeat Variable

The repeat variable is a Template Attribute Language Expression Syntax builtin variable that contains a dictionary. This dictionary has an entry for each enclosing Template Attribute Language ‘repeat’ statement, with a key equal to the variable name defined in the statement. The value of the entry is a ZTUtils Iterator, which keeps track of the state of the ‘repeat’ statement.

You can access the contents of the 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. In Python expressions, you use normal dictionary notation to get the Iterator, then attribute access to get the information. For example:

<p tal:repeat="thing things">
   <span tal:replace="repeat/thing/index">1</span>.
   <span tal:replace="thing">The Thing</span>
</p>

<p tal:repeat="thing things">
  <span tal:replace="python:repeat['thing'].index">1</span>.
  <span tal:replace="thing">The Thing</span>
</p>

<table border="1">
  <tr tal:repeat="row python:range(10)">
    <td tal:repeat="column python:range(10)">
<span tal:define="x repeat/row/number; y repeat/column/number; z python:x*y"
      tal:replace="string:$x * $y = $z">1 * 1 = 1</span>
  </td></tr>
</table>

If the same variable name is reused in a nested ‘repeat’, the inner one’s entry hides the entry for the outer one’s for all expressions inside of the inner ‘repeat’ statement.

The following information is available from an Iterator:

  • 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.

First time here? Please start with Project Vision. Things evolve quickly, so keep an eye of RecentChanges.

Project leader: fred@zope.com

Frequently Asked Questions

Q1: What are Page Templates (ZPT)?
See Project Vision for an overview.
Q2: Will ZPT replace DTML?
ZPT is an alternative to DTML and may replace most if not all of its usage for a particular audience. If you are a PresentationDesigner, you may find ZPT more useful than DTML.
Q3: Is DTML being deprecated?
No, we encourage new projects (and developers) to take a look at ZPT and see if it fits. At DC, we will probably use ZPT as much as possible on new projects. However, if DTML is a better fit…
Q4: Can I use ZPT without knowing XML?
Yes, you can use it without knowing anything about XML. ZPT’s AttributeLanguage was designed for compatibility with XML markup syntax, but this mostly means that it is simple and very well-defined.
Q4a: So, Why XML?
As the Web moves to XHTML, XML will be the common underlying markup format. There is enough flexibility through the usage of namespaces, that we can work within the structure of XML to do the DTMLish things we wish to do. Also, we expect more HTML editors, in the future, to support XML than DTML ;-)
Q4b: Okay, but why be restricted to working within attributes?
Defining our own elements was certainly a possibility. Unfortunately, we expect that most HTML editors will continue to want to deal with HTML, not arbitrary XML. Defining your own element set (through a DTD or Schema) requires a lot of processing overhead on the editor side in order to do anything useful with that markup. Part of the core idea of ZPT is to allow [PresentationDesigner]s to concentrate on the presentation (the HTML) rather than some newly defined elements that don’t visually render themselves in the editor.
Q5: Does ZPT require XHTML?
ZPT will work within plain old HTML. The ZPT markup exists in attributes of your HTML tags, so if your HTML editor leaves unfamiliar attributes alone, it should work with ZPT templates.
Q6: What is XHTML (briefly, please)?
Loosely speaking, XHTML is HTML with stricter syntax and semantics. As a rule, all tags must have end tags. And, all attributes must follow a syntax of ‘attributename = “something always in quotes”’. Also, certain elements cannot contain other elements as children. Thats pretty much it (with apologies to XML language lawyers).
Q7: My favorite editor produces plain old HTML. Can I use ZPT?

Yes, you have a couple of choices. The best is to use an editor that supports XML (meaning: it doesn’t muck with your elements or use of namespaced attributes). If your editor doesn’t mess with stuff like this:

<span tal:define="x str:1" />

Then you can probably use ZPT.

However, you can also use a tool like HTML Tidy to convert your HTML to XHTML (which ZPT naturally speaks). The side effect of this is that your output doesn’t match your original source! This is not an optimal solution.

Q8: What HTML editors work well with ZPT?
We are still compiling a list, but on the commercial (industrial strength) front, GoLive and Dreamweaver 4 work well with ZPT. Recent versions of Amaya work well in HTML mode.
Q9: Ideally, how does one use an HTML editor with ZPT?
ZPT wants to be fairly tightly integrated with your editing process. Right now, this means using the ZPT HTML edit form (yuck) or using a HTML editor that works with FTP or WebDAV. Since ZPT expects you to retrieve your editable sources from Zope, this is the most natural way to work: Zope has the canonical sources for the site.
Q10: Why does ZPT talk about TAL and METAL being non-Zope specific?
We thought that these ideas were so cool, that we shouldn’t hog them all to ourselves. We, of course, expect to have the most butt kicking implementation of the specs, but we wanted to make sure we had a general enough specification so that people who steal the idea (and all good ideas should be stolen!), will not produce widely different implementations. We need convergence, not divergence!
Q11: Does this mean that they will be proposed as standards to a body such as the W3C?
Currently, we have no plans to propose TAL and METAL as a standard. As a practical specification, TAL and METAL need to be able to evolve as needed for applications using template systems based on TAL and METAL, and there’s no need to wait for outside standards bodies to approve new versions of the specifications.
Q12: How do I run the tests? Should they all pass?
The TAL README.txt file describes how to run the test suite for TAL. If you do not have Expat installed, the XML tests will not pass. (All recent versions of Python include Expat as a matter of course.) The ZPT tests can be run by executing run.py from the tests directory.
Q13: Does acquisition work with these path expressions in TALES?
Path expressions such as here/master_page/macros/copyright must always start with a builtin or defined variable (“here”, in this case). Each step in the path will take advantage of acquisition if the current object supports it, so “master_page” may be acquired from “here”. This is different from DTML, where every name is searched for in a large, diverse set of locations many of which support acquisition. locations

Q14: How do I write comments that won’t be included in the rendered page?

Use ‘tal:replace=”nothing”’ or ‘tal:condition=”nothing”’, like this:

<span tal:replace="nothing">A Comment</span>
<span tal:replace="nothing"><-- A Commented Comment --></span>

Q15: When I use tal:attributes to set the ‘src’ or ‘href’ attribute of a tag, it turns my ampersands into &amp;amp;s! How can I stop it?

You can’t, and shouldn’t. According to the standard, escaping is the proper thing to do here. It works properly with every browser we’ve tried.

Comments

flight (May 15, 2001 7:52 am; Comment #7) Editor Remark Requested

Regarding Q8/Q9 (HTML editors with ZPT): Somebody should fill in some tips how to use (X)Emacs with PageTemplates.

How do I set up a PageTemplate to startup the correct Emacs mode ? Which Emacs modes are preferable for editing PageTemplates (elaborate on psgml, html-mode, hm–html-mode, xml-mode etc.pp.) ? Is it possible to augment the mode’s definitions with TAL, METAL and perhaps TALES descriptions ?

Anonymous User (Jun 11, 2001 4:53 am; Comment #8) Editor Remark Requested
In my view, a good FAQ should provide links to the current specifications.
Anonymous User (Jan 7, 2002 9:40 pm; Comment #10) –

I’ve been using HTML_Kit from http://www.chami.org, its shareware, Windows only. Has FTP built-in and Tidy.

Would be handy if ZPT’s would return a long FTP response code with the error message if there’s a problem in the template during a STOR.

Anonymous User (Mar 20, 2002 8:58 pm; Comment #11) Editor Remark Requested
A comment on Q15: while I agree that ampersands etc. should be quoted, there is a slight problem when one tries to place quoted characters themselves in a ‘src’ or ‘href’ attribute. For example, while &amp; is handled correctly, the equally valid &#38; is not, and gets turned into &amp;#38; and won’t be properly recognized by any browser. The code should probably ignore ampersands that are already part of escape sequences.

Page Templates

A Page Template is a Zope object intended to hold presentation and apply it to content. It holds presentation in the form of an XML or HTML document. The application of this presentation to content is controlled by attributes in the document called Template Attributes. The possible attributes constitute the Template Attribute Language (TAL Reference). Most of these attributes’ values contain expressions referring to objects or perform computations. In Page Templates, these expressions are written in the TAL Expression Syntax (TALES Reference).

A separation of user roles is possible because TAL describes dynamic presentation elements and the inclusion of content, while the expressions encapsulate the (sometimes) complex logic of addressing Zope objects and computing values.

Presentation reuse is addressed through METAL Reference, which allow you to define and use chunks of presentations as parametric macros.

A Presentation Designer uses Page Templates, and may need to know TAL, but doesn’t need to know the expression syntax (TALES). TAL is designed to be comprehensible to a non-programmer.

Simple Tutorial

This is a very introductory guide to Zope Page Templates, making no assumptions about prior DTML or Python knowlege. Please comment!

Tutorial: Getting Started

Introduction

Page Templates are a web page generation tool. They help programmers and designers collaborate in producing dynamic web pages for Zope web applications. Designers can use them to maintain pages without having to abandon their tools, while preserving the work required to embed those pages in an application.

The goal is natural workflow. A designer will use a WYSIWYG HTML editor to create a template, then a programmer will edit it to make it part of an application. As required, the designer can load the template back into his editor and make further changes to its structure and appearance. By taking reasonable steps to preserve the changes made by the programmer, he will not disrupt her application.

Page Templates aim at this goal by adopting three principles:

  1. Play nicely with editing tools.
  2. What you see is very similar to what you get.
  3. Keep code out of templates, except for structural logic.

A Page Template is a model of the pages that it will generate. In particular, it is a valid HTML page. Since HTML is highly structured, and WYSIWYG editors carefully preserve this structure, there are strict limits on the ways in which the programmer can change a page and still respect the first principle.

Who Are They For?

Page Templates are for programmers and designers who need to work together to create dynamic web pages. If you create and edit all of your web pages with a text editor, you might not care for Page Templates. Then again, they can be simpler to use and understand than the alternatve, DTML.

Why Yet Another Template Language?

There are plenty of template systems out there, some of them quite popular, such as ASP, JSP, and PHP. Why invent another?

First, none of these template systems are aimed at HTML designers. Once a page has been converted into a template, it is invalid HTML, making it difficult to work with outside of the application. Each of them violates the first or second principle of Zope Page Templates to one degree or another. Programmers should not “hijack” the work of the designers and turn HTML into software. XMLC, part of the Enhydra project, shares our goal, but requires the programmer to write substantial amounts of Java support code for each template.

Second, all of these systems suffer from failure to separate presentation, logic, and content (data). Their violations of the third principle decrease the scalability of content management and website development efforts that use these systems.

Applying The Principles

Page Templates have a template language (TAL) that consists of special tag attributes. For example, a dynamic page title might look like this:

<title tal:content="here/title">Page Title</title>

The ‘tal:content’ attribute is a TAL statement. Since it has an XML namespace (the ‘tal:’ part) most editing tools will not complain that they don’t understand it, and will not remove it. It will not change the structure or appearance of the template when loaded into a WYSIWYG editor or a web browser. The name ‘content’ indicates that it will set the content of the ‘title’ tag, and the value “here/title” is an expression providing the text to insert into the tag.

This example also demonstrates the second principle; When you view the template in an editor, the title text will act as a placeholder for the dynamic title text. The template acts like an example of how generated documents should look.

There are template commands for replacing entire tags, their contents, or just some of their attributes. You can repeat a tag several times or omit it entirely. You can join parts of several templates together, and specify simple error handling. All of these capabilities are used to generate document structures. You can’t create subroutines or classes, write loops or multi-way tests, or easily express complex algorithms.

The template language is deliberately not as powerful and general-purpose as it could be. It is meant to be used inside of a framework (such as Zope) in which other objects handle business logic and tasks unrelated to page layout.

For instance, template language would be useful for rendering an invoice page, generating one row for each line item, and inserting the description, quantity, price, and so on into the text for each row. It would not be used to create the invoice record in a database or to interact with a credit card processing facility.

Tutorial: Template Language

Creating a Page Template

If you design pages, you will probably use FTP or WebDAV instead of the Zope Management Interface (ZMI) to create and edit Page Templates. See your Zope adminstrator for instructions. For the very small examples in this introduction, it is much easier to use the ZMI.

If you are a Zope administrator or a programmer, you will probably use the ZMI at least occasionally. You may also use emacs, cadaver, or some other client. See the Zope Administrator’s Guide for instructions on setting up Zope to to work with various clients.

Use your web browser to log into the Zope management interface. Choose a Folder (the root is fine) and pick “Page Template” from the drop-down add list. Type “simple_page” in the add form’s ‘Id’ field, then push the “Add and Edit” button.

You should now see the main editing page for the new Page Template. The title is blank, the content-type is ‘text/html’, and the default template text is in the editing area.

Now you will create a very simple dynamic page. Type the words “a Simple Page” in the ‘Title’ field. Edit the template text to look like this:

This is <b tal:replace="template/title">the Title</b>.

Now push the “Save Changes” button. The edit page should show a message confirming that your changes have been saved. If some text starting with ‘<!– Page Template Diagnostics’ is added to the template, then check to make sure you typed the example correctly and save it again. You don’t need to erase the error comment: once the error is corrected it will go away.

Click on the ‘Test’ tab. You should see a mostly blank page with “This is a Simple Page.” at the top.

Back up, then click on the “Browse HTML source” link under the content-type field. You should see “This is the Title.” Back up again, so that you are ready to edit the example further.

Simple Expressions

The text “template/title” in your simple Page Template is a path expression. This the most commonly used of the expression types defined by the TAL Expression Syntax (TALES). It fetches the ‘title’ property of the template. Here are some other common path expressions:

o request/URL: The URL of the current web request.

o user/getUserName: The authenticated user’s login name.

o container/objectIds: A list of Ids of the objects in the same Folder as the template.

Every path starts with a variable name. If the variable contains the value you want, you stop there. Otherwise, you add a slash (‘/’) and the name of a sub-object or property. You may need to work your way through several sub-objects to get to the value you’re looking for.

There is a small built in set of variables, such as ‘request’ and ‘user’, that will be listed and described later. You will also learn how to define your own variables.

Inserting Text

In your “simple_page” template, you used the ‘tal:replace’ statement on a bold tag. When you tested it, it replaced the entire tag with the title of the template. When you browsed the source, you saw the template text in bold. We used a bold tag in order to highlight the difference.

In order to place dynamic text inside of other text, you typically use ‘tal:replace’ on a ‘span’ tag. Add the following lines to your example:

<br>
The URL is <span tal:replace="request/URL">URL</span>.

The ‘span’ tag is structural, not visual, so this looks like “The URL is URL.” when you view the source in an editor or browser. Remember to take care when editing not to destroy the ‘span’ or place formatting tags such as ‘b’ or ‘font’ inside of it, since they would also be replaced.

If you want to insert text into a tag but leave the tag itself alone, you use ‘tal:content’. To set the title of your example page to the template’s title property, add the following lines above the other text:

<head>
<title tal:content="template/title">The Title</title>
</head>

If you open the “Test” tab in a new window, the window’s title will be “a Simple Page”.

Repeating Structures

Now you will add some context to your page, in the form of a list of the objects that are in the same Folder. You will make a table that has a numbered row for each object, and columns for the id, meta-type, and title. Add these lines to the bottom of your example template:

<table border="1" width="100%">
  <tr>
    <th>#</th><th>Id</th><th>Meta-Type</th><th>Title</th>
  </tr>
  <tr tal:repeat="item container/objectValues">
    <td tal:content="repeat/item/number">#</td>
    <td tal:content="item/id">Id</td>
    <td tal:content="item/meta_type">Meta-Type</td>
    <td tal:content="item/title">Title</td>
  </tr>
</table>

The ‘tal:repeat’ statement on the table row means “repeat this row for each item in my container’s list of object values”. The repeat statement puts the objects from the list into the ‘item’ variable one at a time, and makes a copy of the row using that variable. The value of “item/id” in each row is the Id of the object for that row.

The name ‘item’ is not special; You can use any name you like for the variable, as long as it starts with a letter and contains only letters, numbers, and underscores (‘_’). It only exists in the tag that the ‘tal:repeat’ statement is in, in this case the ‘tr’ tag; If you tried to use it above or below that tag you would get an error.

Each row in the example is numbered, which requires knowing the number of the current repetition. You might also like to know whether a particular row is the first or last row, or similar information. In each copy of the row, the variable ‘item’ contains an object from the list. This object doesn’t know anything about the repetition, so you can’t use ‘item/somename’ to get this sort of information. This is what the builtin ‘repeat’ variable is for. By placing the name of the repeating variable after ‘repeat’ in a path, you can access the repetition count from zero (‘index’), from one (‘number’), from “A” (‘Letter’), and in several other ways. So, the expression ‘repeat/item/number’ is ‘1’ in the first row, ‘2’ in the second row, and so on.

Since one ‘tal:repeat’ can be placed inside of another, more than one can be active at the same time. This is why you must write ‘repeat/item/number’ instead of just ‘repeat/number’ to get information about the ‘tal:repeat’ that creates the ‘item’ variable.

Conditional Elements

View the template, and you’ll notice that the table is very dull looking. Let’s improve it by shading alternate rows. Copy the second row of the table, then edit it so that it looks like this:

<table border="1" width="100%">
  <tr>
    <th>#</th><th>Id</th><th>Meta-Type</th><th>Title</th>
  </tr>
  <tbody tal:repeat="item container/objectValues">
    <tr bgcolor="#EEEEEE" tal:condition="repeat/item/even">
      <td tal:content="repeat/item/number">#</td>
      <td tal:content="item/id">Id</td>
      <td tal:content="item/meta_type">Meta-Type</td>
      <td tal:content="item/title">Title</td>
    </tr>
    <tr tal:condition="repeat/item/odd">
      <td tal:content="repeat/item/number">#</td>
      <td tal:content="item/id">Id</td>
      <td tal:content="item/meta_type">Meta-Type</td>
      <td tal:content="item/title">Title</td>
    </tr>
  </tbody>
</table>

The ‘tal:repeat’ has not changed, you have just moved it onto the new ‘tbody’ tag. This is a standard HTML tag meant to group together the body rows of a table, which is how you are using it. There are two rows in the body, with identical columns, and one has a grey background.

View the template’s source, and you see both rows. If you had not added the ‘tal:condition’ statements to the rows, then the template would generate both rows for every item, which is not what you want. The ‘tal:condition’ statement on the first row ensures that it is only included on even- indexed repetitions, while the second row’s condition only lets it appear in odd-indexed repetitions.

A ‘tal:condition’ statement does nothing if its expression has a true value, but removes the entire statement tag, including its contents, if the value is false. The ‘odd’ and ‘even’ properties of ‘repeat/item’ are either zero or one. The number zero, a blank string, an empty list, and the builtin variable ‘nothing’ are all false values. Nearly every other value is true, including non-zero numbers, and strings with anything in them (even spaces!).

Defining Variables

Your template will always show at least one row, since the template itself is one of the objects listed. In other circumstances, you might want to account for the possibility that the table will be empty. Suppose you want to simply omit the entire table in this case. You can do this by adding a ‘tal:condition’ to the table:

<table border="1" width="100%"
       tal:condition="container/objectValues"

Now, when there are no objects, no part of the table will be included in the output. When there are objects, though, the expression “container/objectValues” will be evaluated twice, which is mildly inefficient. Also, if you wanted to change the expression, you would have to change it in both places.

To avoid these problems, you can define a variable to hold the list, and then use it in both the ‘tal:condition’ and the ‘tal:repeat’. Change the first few lines of the table to look like this:

<table border="1" width="100%"
       tal:define="items container/objectValues"
       tal:condition="items">
  <tr>
    <th>#</th><th>Id</th><th>Meta-Type</th><th>Title</th>
  </tr>
  <tbody tal:repeat="item items">

The ‘tal:define’ statement creates the variable ‘items’, and you can use it anywhere in the table tag.

Now, suppose that instead of simply leaving the table out when there are no items, you want to show a message. To do this, you place the following above the table:

<h4 tal:condition="not:container/objectValues">There
Are No Items</h4>

You can’t use your ‘items’ variable here, because it isn’t defined yet. If you move the definition to the ‘h4’ tag, you can’t use it in the ‘table’ tag any more, because it becomes a local variable of the ‘h4’ tag. You could place the definition on some tag that enclosed both the ‘h4’ and the ‘table’, 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 ‘h4’ tag to the bottom of the template:

<h4 tal:define="global items container/objectValues"
    tal:condition="not:items">There Are No Items</h4>
<table border="1" width="100%" tal:condition="items">

The ‘not:’ in the first ‘tal:condition’ is an expression type prefix that can be placed in front of any expression. If the expression is true, ‘not:’ is false, and vice versa.

Changing Attributes

Most, if not all, of the objects listed by your template have an ‘icon’ property, that contains the path to the icon for that kind of object. In order to show this icon in the meta- type column, you will need to insert this path into the ‘src’ attribute of an ‘img’ tag, by editing the meta-type column in both rows to look like this:

<td><img src="/misc_/OFSP/Folder_icon.gif"
         tal:attributes="src item/icon">
    <span tal:replace="item/meta_type">Meta-Type</span>
</td>

The ‘tal:attributes’ statement replaces the ‘src’ attribute of the image with the value of ‘item/icon’. The value of ‘src’ in the template acts as a placeholder, so that the image is not broken, and is the correct size.

Since the ‘tal:content’ attribute on the table cell would have replaced the entire contents of the cell, including the image, with the meta-type text, it had to be removed. Instead, you insert the meta-type inline in the same fashion as the URL at the top of the page.

Comments
peterbe (Jun 25, 2001 11:21 am; Comment #3) Editor Remark Requested

Perhaps also mention how to use (‘parameters’)

tal:define="items container/objectValues"

becomes:

tal:define="items python:container.objectValues('Photo')"

or if the objectpath containes . dots:

tal:define="items container/folder.peter/objectValues"

becomes:

tal:define="itemsobj container/folder.peter/objectValues/; items python:itemsobj('Photo')"

Stop and correct me before I make a fool out of myself.

evan (Jun 25, 2001 1:29 pm; Comment #4) –
These are good, but belong in part 4, where Python expressions are discussed.

Tutorial: More TAL

Mixing and Matching Statements

As you have seen in the example template, you can put more than one TAL statement on the same tag. There are three limits you should be aware of, however.

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, 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 following order: ‘define’, ‘condition’, ‘repeat’, ‘content’ / ‘replace’, ‘attributes’.

To get around these limits, you can add another tag and split up the statements between the tags. If there is no obvious tag type that would fit, you can use ‘span’ or ‘div’ if it is appropriate for the output to contain an element, or use a ‘tal:block’ element to have the start and end tags omitted from the document.

For example, if you want to define a variable for each repetition of a paragraph, you can’t place the ‘tal:define’ on the same tag as the ‘tal:repeat’, since the definition would happen before all of the repetitions. Instead, you would write either of the following:

<div tal:repeat="p phrases">
  <p tal:define="n repeat/p/number">
  Phrase number <span tal:replace="n">1</span> is
  "<span tal:replace="p">Phrase</span>".</p>
</div>

<p tal:repeat="p phrases">
  <span tal:define="n repeat/p/number">
  Phrase number <span tal:replace="n">1</span> is
  "<span tal:replace="p">Phrase</span>".</span>
</p>

Statements with Multiple Parts

If you need to set multiple attributes on a tag, you can’t do it by placing multiple ‘tal:attributes’ statements on the tag, and splitting them across tags is useless.

Both the ‘tal:attributes’ and ‘tal:define’ statements can have multiple parts in a single statement. You separate the parts with semicolons (‘;’), so any semicolon appearing in an expression in one of these statements must be escaped by doubling it (‘;;’). Here is an example of setting both the ‘src’ and ‘alt’ attributes of an image:

<img src="default.jpg"
     tal:attributes="src item/icon; alt item/id">

Here is a mixture of variable definitions:

<span tal:define="global logo here/logo.gif; ids here/objectIds">
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 (‘$’). If it has more than one part, or needs to be separated from the text that follows it, it must be surrounded by braces (‘{}’). 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:Just text."
"string:&copy; $year, by Me."
"string:Three ${vegetable}s, please."
"string:Your name is ${user/getUserName}!"
Nocall Path 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 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/id}: ${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.

Python Expressions

A Python expression starts with ‘python:’, followed by an expression written in the Python language. See the section on writing Python expressions for more information.

Other Builtin Variables

You have already seen some examples of the builtin variables ‘template’, ‘user’, ‘repeat’, and ‘request’. Here is a complete list of the other builtin variables and their uses:

  • nothing: a false value, similar to a blank string, that you can use in ‘tal:replace’ or ‘tal:content’ to erase a tag or its contents. If you set an attribute to ‘nothing’, the attribute is removed from the tag (or not inserted), unlike a blank string.
  • 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.
  • 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.
  • 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.
  • 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.
  • modules: the collection of Python modules available to templates. See the section on writing Python expressions.
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 the path is evaluated.

When a path doesn’t exist, you often have a fallback 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’. Use ‘nothing’ to blank the target if none of the paths is found, or ‘default’ to leave the example text in place.

You can also test the existence of a path directly with the ‘exists:’ expression type prefix. A path expression with ‘exists:’ in front of it is true if the path exists, false otherwise. These examples both 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>

<h4 tal:condition="exists:request/form/errmsg"
    tal:content="request/form/errmsg">Error!</h4>
Dummy Elements

You can include page elements that are visible in the template but not in generated text by using the builtin 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 take up more of the generated page than of the template. 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.

Inserting Structure

Normally, the ‘tal:replace’ and ‘tal:content’ statements quote the text that they insert, converting ‘<’ to ‘&lt;’, for instance. If you actually want to insert the unquoted text, you need to precede the expression with the ‘structure’ keyword. Given a variable ‘copyright’, the following two lines:

<span tal:replace="copyright">Copyright 2000</span>
<span tal:replace="structure copyright">Copyright 2000</span>

might generate “&amp;copy; 2001 By &lt;b&gt;Me&lt;/b&gt;” and “&copy; 2001 By <b>Me</b>” respectively.

This feature is especially useful when you are inserting a fragment of HTML 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="article topnewsitems"
   tal:content="structure article">A News Article</p>
Comments
jwm (Aug 8, 2001 7:12 pm; Comment #2) Editor Remark Requested

Multiple Defines:

&lt;span tal:define="global logo here/logo.gif; ids here/objectIds">

‘global’ is a keyword used per variable definition, so in this case ‘logo’ becomes global, will ‘ids’ stays local to the span. A bit like you’d expect, really :-)

Strings:

How about some examples of them in action straight after the construction examples. I find when I hit something I don’t quite understand I tend to stall, and in this case I didn’t see where I’d use a string construct until I read the fairly unrelated example for ‘nocall’ below it.

So we’d have something like this:

"string:Just text."
"string:� $year, by Me."
"string:Three ${vegetable}s, please."
"string:Your name is ${user/getUserName}!"

Which are used like this:

<span tal:replace="string:Interesing text">Boring text</span>
<p tal:content="string:You are logged in as ${user/getUserName}">You are logged in as someone.</p>

Actually on further reading, I think just mentioning that we’re talking about ‘expresion type prefixes’ that are things that appear inside the attribute strings created when you use a ‘statement’ (ie, any of tal:define, etc) just before statements would clear this up.

benster (Jun 29, 2002 11:00 pm; Comment #4) Editor Remark Requested

Under Mixing and Matching Statements:

> For example, if you want to define a variable for each
> repetition of a paragraph ...

Where is the paragraph defined? Is it a string variable? It’s not a string expression, judging from the examples. It would be helpful to see this paragraph being defined.

FredDrake –
The paragraph is the ‘p’ element shown in the example. There’s nothing magical about this.

mux_07 Fri Feb 2 13:29:02 -0800 2007

Perhaps a good example for tal:attributes would be the style attribute, which demonstrates a need to double the semi-colon e.g. <span tal:attributes="style string:font-size:${item/size}em;;font-size-adjust:${item/sizeAdjust};;">Sized Font</span>

Tutorial: Advanced Concepts

Basic Python Expressions

The Python language is a 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 them, you have to know how to get access to them.

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, instead of writing “here.penguin.gif”, you must write “getattr(here, ‘penguin.gif’)”.

Some objects, such as ‘request’, ‘modules’, and Zope Folders support item access. Some examples of this are:

request['URL'], modules['math'], and 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 tradeoff 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 “request/form/get” will evaluate to the “get” method of the form dictionary.

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.

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

"python:here.myscript(1, 2)"
"python:here.myscript('arg', foo=request.form['x'])"

If you want to return more than a single bit 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 bit. For example:

getPerson returns this: {'name': 'Fred', 'age': 25}

<span tal:define="person here/getPerson"
      tal:replace="string:${person/name} is ${person/age}">
Name is 30</span> years old.
Calling DTML

Unlike Scripts, DTML Methods don’t have an explicit parameter list. Instead, they expect to be passed a client, a mapping, and keyword arguments. They use these to construct a namespace.

When the ZPublisher publishes a DTML object, 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.

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. In this case, you would use “modules/math/pi”. Please refer to the Zope Book or a DTML reference guide for more information about these modules.

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, ':')"

As long as you’re using Python 2.0 or newer, you can avoid this in many cases using string methods:

tal:replace="python:':'.join(slist)"

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="pps modules/Products.PythonScripts.standard"
tal:define="pps python:modules['Products.PythonScripts.standard']"

Most Python modules cannot be accessed from Page Templates, DTML, or Scripts unless you add Zope security assertions to them. That’s outside the scope of this document, and is covered by the Zope Security Guide.

Comments

d.maurer (Jul 3, 2001 11:11 am; Comment #1) Editor Remark Requested
I would appreciate a section on METAL.
d.maurer (Jul 3, 2001 11:15 am; Comment #3) Editor Remark Requested
Links to the specifications would be helpful
weitzman (Sep 10, 2001 12:08 pm; Comment #4) –
forward and backward links when working through the tutorial would be nice … i have simple needs.
dfavor (Nov 19, 2001 7:28 am; Comment #8) –
It would be nice to have popups for other parts of the tutorial, like the listings in part1.
dfavor (Nov 19, 2001 7:28 am; Comment #9) –
It would be nice to have popups for other parts of tutorial, like the listings in part1.
dfavor (Nov 19, 2001 7:29 am; Comment #10) –
It would be nice to have popup example pages for tutorials 2-4, like the listings in tutorial 1.

A DTML user’s Introduction to Page Templates

DTML is good

DTML solves a lot of problems, and solves them well. You can use it to generate XML, HTML, RDF, SQL, or any other sort of text your heart desires. You have access to the full power of Zope, through the namespace, and to Python, through expressions.

DTML has problems

For all that you may be familiar with DTML now, and love it to death, you can probably remember a time when it made you tear your hair out. Its syntax is quirky and unpredictable. Some tag attributes use names, some use literal strings, and some can use Python expressions, but there’s not much rhyme or reason to it. Access to names in the namespace that contain punctuation, or names which must be reached via a path, are painful to use.

If you do all of your page design in vi, emacs, or some other text-based editor, you’re probably content to replace large, arbitrary chunks of your page with <dtml-var foo>. On the other hand, if you use a visual editor or work with someone who does, you’ve exerienced the pain of having to reconcile DTML’s dynamicism with the visual representation of a page (or page fragment).

DTML will go on

Nobody who uses DTML wants to see it disappear. We will continue to mai`ntain and improve it, and may well incorporate the more successful ideas from Page Templates. It will continue to be the language of choice for generating unstructured text (like SQL or CSV).

Page Templates are better (where they apply)

Template Attribute Language, Template Attribute Language Expression Syntax and Macro Expansion for TAL (or METAL) are our simplest, most straighforward attempt to design templates for HTML that still have enough expressiveness to get the job done. Each of them covers a single layer of template functionality in a cleanly extensible fashion. Forget the cutesy acronyms for a moment, and follow the reasoning:

Expressions

Just as in DTML, most of the grunt work of Page Templates involves expressions; You need to perform calculations and comparisons, and refer to objects. When you refer to a Zope object, you want to be able to use its Id, or a path of Ids, including Ids with odd characters in them. You want to be able to use simple Python expressions. You want to be able to write literal strings, and interpolate variables into a string. Perhaps at some point you’ll discover that there’s a whole new notation that you would like to be able to use (eg. Perl). These are the requirements that produced Template Attribute Language Expression Syntax

Referring to Zope objects by name is one of the clunkier bits of DTML, and it is made worse by the “everything in one pile” namespace DTML uses. In TALES, the namespace is replaced by paths. There are a small number of “contexts” that you use as starting points for referring to objects. They are similar to Script bindings, and include “root”, “here”, “container”, and “template”, among others. You pick a context, then write a slash-separated path from the context to the object. For example, you refer to object “big-wig” in the same Folder as the Template by the path “container/big-wig”. You can acquire “dead parrot” from “shop”, and “shop” from the object on which the Template was called with “here/shop/dead parrot”. Any character that is valid in a Zope Id is allowed in a path. Contexts are just builtin variables, and a path can start with any variable, so if you have defined variable “ick” as the path “here/shop/dead parrot”, you can use “ick/name” to get the parrot’s “name” property.

In DTML, sometimes you can use a Python expression in an attribute, sometimes you must, and sometimes you can’t. Every place that you can use a TALES expression, you can use Python. You simply prefix the Python expression with python:, as in python:2 + 2 or python:string.split(x, '','').

Similarly, you can always provide a literal string by using the expression type prefix string:. Thus, string:This isn't bad is a valid TALES expression. You can insert variables values into string expressions using $varname (or ${varname}) syntax. If variable name refers to “Evan” and thing refers to “cat”, then string:$name likes ${thing}s produces “Evan likes cats”.

TALES is designed to be extended by simply defining new expression types. If you wanted to use XPath notation, you could write an XPath expression engine, register it with TALES, and start writing expressions like xpath:....

Statements

Here is a simple example of how you might use statements from DTML and from Page Templates to insert the value of an expression:

<p>Ten times X is <b><dtml-var expr="x * 10"></b></p>

<p>Ten times X is <b z:insert="python: x * 10">(x * 10)</b></p>

In the DTML version, you use the made-up tag dtml-var with an expr attribute to hold the expression. In the PT version, you add TAL attribute z:replace to the HTML b tag. If you loaded a page containing each of these into a WYSIWYG editor, the DTML tag would probably not show up, or would be replaced with some kind of icon, while the PT version would look like: <b>(x * 10)</b>.

DTML Tags are the statements of the DTML language. They are completely independent of the text in which they are embedded, and they don’t pay any attention to any structure or grammar that the text may possess. If you mark up HTML with DTML carefully, you can produce a composite document that an HTML editing tool can tolerate, or can be made to tolerate. On the other hand, it’s easy to write DTML that an editing tool considers complete gibberish, but that renders into perfectly good HTML. For this reason (among others) it is highly unlikely that many HTML tools will be adapted to deal well with DTML.

Page Templates’ TAL statements are attributes of ordinary HTML (or XML) tags. This means that you can only use TAL with HTML or XML text, but Zope generates a lot of HTML. Most of the popular HTML tools ignore and preserve tag attributes that they don’t recognize. Since TAL attributes have their own XML namespace (a prefix like z: or tal: that you choose), they don’t interfere with the validity of XML documents. Thus, HTML or XML that you have marked up with TAL is likely to be perfectly compatible with tools that know nothing about Zope.

Write an HTML document as XHTML, or at least very tidily, and it has a nice easy-to-parse tree structure. This allows us to frame template operations in terms of repeating, replacing, removing, or changing attributes of tags. You can handle almost all cases where this model doesn’t fit the dynamicism of your document by adding the odd <span> or <div>, just as you might when applying CSS classes.

Variables and Scoping

Much of the difficulty of DTML lies in using the DTML namespace. This is a powerful, difficult to control tool, especially when combined with acquisition. It is all too easy to accidentally obscure a name, and often hard to access names deep in the stack. There are also security issues posed by the fact that DTML code cannot be sure that any name in the namespace is bound to what it expected to find.

Page Templates solve this, by providing your TALES expressions with a set of builtin variables. If there is an object named ‘helper’ in the same container as your template, you can reliably access it as container/helper.

You can define your own global or local variables with the define="var expr" statement. Local variables are like the names bound by a <dtml-let>; Their scope is the statement tag and its content. Global variables are more like REQUEST.set names, since they are in scope from the start of the statement tag to the end of the template.

Template Operations

Just as <dtml-var foo> is the most fundamental statement of DTML, replace="expr" and content="expr" are the basic workhorses of TAL. The first replaces the tag on which you place it with the value of the expression “expr”, while the other inserts the value in place of the tag’s contents. As you saw above, the TALES expression “expr” can evaluate to any Zope object, string, or Python-computed value.

You can write comments that don’t show up in a rendered page, or comment out part of a page, by putting replace="nothing" on a tag. TAL inside of the tag will not be executed.

Rather than replacing an entire tag, you can set tag attributes with attributes="attrname expr". This allows you to have reasonable dummy values for the attributes that a WYSIWYG editor can deal with. For example:

<textarea name="fred" rows="10" cols="60"
  z:attributes="rows request/my_rows; cols request/my_cols">

Instead of <dtml-in>, TAL uses repeat="var expr". This repeats the tag on which you place it once for each element of the sequence “expr”. In each repetition, the local variable “var” is bound to the current element of the sequence, and repeat variable “var” gives you access to the TAL equivalent of sequence-number. For example:

<p z:repeat="thing here/collection">
  Paragraph #<span z:replace="repeat/thing/number">1</span>:
  <span z:replace="thing">something</span>.
</p>

The condition="expr" statement is similar to <dtml-if>, but more basic. If the expression is true, the statement does nothing. If the expression is false, the tag and its contents is skipped. There is no equivalent to <dtml-else> or <dtml-elif>.

The Macros Saga

There are times when the power of <dtml-var> can be a problem. You can’t tell, just by inspecting DTML source, whether <dtml-var x> inserts a string, performs a catalog search, sends mail, or even restarts Zope! Templates often include other templates, or parts of them, and it can be useful to know when that is only thing happening.

If you have some way to tell that <dtml-var sidebar> inserts a sidebar template, then you can solve the problem faced by designers who use WYSIWYG tools. When they load a template page into their editor, insert the sidebar template so that it appears normally in the page, instead of as a little placeholder symbol. When they save it back to Zope, convert the sidebar back into a reference.

Macro Expansion for TAL (or METAL) is the language that allows this sort of behavior. It is similar to TAL, in that it consists of tag attributes in an XML namespace. You can use it to define macros, by placing a define-macro="name" attribute on a tag that you want to re-use. METAL affects template rendering, of course, inserting any macros that the template uses just before TAL executes, it also inserts macros whenever you get the source code for the template. You can create libraries of macros, with all of the advantages of code reuse, yet still see everything in a page when you edit it.

Macros can only be changed by editing the definition. If you load a template into an editor, and change a tag that has a use-macro="expr" attribute, these changes will have no effect.

Only whole tags can be macros, so you can’t make a standard_html_header and standard_html_footer macro. Instead, you would create a standard_page template, make the entire html tag a macro, and place a define-slot="main" METAL attribute on the body tag. Slots are tags within a macro that can be replaced when the macro is inserted. They are replaced if the use-macro statement tag contains a fill-slot="name" statement tag, where name matches the name of a slot in the macro. To use your standard_page template, you place use-macro="here/standard_page/macros/page" on your page’s html tag, and fill-slot="main" on its body tag. Now the html tag and all of its contents other than the body tag will be replaced with the macro body. To see what this looks like, check out the Examples.

Comments

peterbe (May 10, 2001 5:09 am; Comment #2) –

This doesn’t work:

<p z:repeat="thing here/collection">
   Paragraph #<span z:replace="loop:thing/number">1</span>:
   <span z:replace="thing">something</span>.
</p>

Products.PageTemplates.TALES.CompilerError: Unrecognized expression type “loop”.

And where do I find more info about doing TAL what DTML-IN does?

evan (May 10, 2001 9:37 am; Comment #3) –
I have fixed the example. There isn’t any good documentation written yet on the equivalent of dtml-in in TAL, although one of the examples in PageTemplates 1.2 should be informative.
peterbe (May 15, 2001 5:21 am; Comment #4) –
As announced before (but then was broken), I have started to compile a little list of examples of how to go from DTML to ZPT. http://www.zope.org/Members/peterbe/DTML2ZPT_Conversion_examples

Examples

This page collects exemplar usages of ZPT.

Standard Look And Feel Example

This macro provides the capability of the out of the box ‘standard_header’ and ‘standard_footer’. The macro’s name is ‘master’; it defines just one slot to be filled (‘main’). An example template that uses this macro can be found in Simple Welcome Example.

<html xmlns:tal="http://xml.zope.org/namespaces/tal"
      xmlns:metal="http://xml.zope.org/namespaces/metal"
      metal:define-macro="master">
  <head>
    <title tal:content="here/title">The title</title>
  </head>
  <body bgcolor="#999999">
    <div metal:define-slot="main">
    If you supply a tag with a 'fill-slot="main"' attribute
    when using this macro, that tag will replace this text.
    </div>
    <p tal:content="structure here/ZopeAttributionButton"></p>

Simple Welcome Example

This template provides a simple example for constructing a page that uses the Standard Look And Feel Example macro. The only retained portion of this template is the content provided to fill-slot="main".

Original Source:

<html xmlns:tal="http://xml.zope.org/namespaces/tal"
      xmlns:metal="http://xml.zope.org/namespaces/metal"
      metal:use-macro="here/StandardLookAndFeel/macros/master">
  <body>
    <div metal:fill-slot="main">
      <p>Welcome to the machine</p>
    </div>
  </body>
</html>

Expanded Results:

<html xmlns:tal="http://xml.zope.org/namespaces/tal"
      xmlns:metal="http://xml.zope.org/namespaces/metal"
      metal:use-macro="here/StandardLookAndFeel/macros/master">
  <head>
    <title tal:content="here/title">The title</title>
  </head>
  <body bgcolor="#999999">
    <div metal:fill-slot="main">
      <p>Welcome to the machine</p>
    </div>
    <p tal:content="structure here/ZopeAttributionButton"></p>
  </body>
</html>
Comments
peterbe (Apr 25, 2001 9:53 am; Comment #1) Editor Remark Requested

Can’t get this to work!

metal:use-macro="here/StandardLookAndFeel/macros/master">
Initially:
Win2k Zope 2.3.0 & PageTemplates-1.1.1.tar.gz & TAL-1.1.0.tar.gz No ParsedXML product installed (is this the reason?)

‘here’ is a protected word right? ‘here’ is like ‘self’ in py modules and ‘context’ in python scripts. Right?

So I create a PageTemplate in /test with id: “standard_template” and this ‘metal:define-macro=”master”>’ Another PageTemplate to use the ‘master’ macro with id: index_html “metal:use-macro=”here/standard_lookandfeel/master”>”

Here’s the Traceback I get inside “index_html”:

Macro expansion failed
Products.PageTemplates.TALES.TALESError: exceptions.AttributeError on __getitem__ in "<PathExpr standard:here/standard_lookandfeel/master>"
peterbe (Apr 25, 2001 11:58 am; Comment #2) –

Ahh… Got the syntax right finally.

metal:use-macro="here/standard_lookandfeel/macros/master">

Where ‘standard_lookandfeel’ is a PageTemplate in the same folder or inherited from a folder “below”.

This was tricky. It might be documented and all but it bloody well keep me occupied for some long time.

Loop Examples

Here is a template which present a list of URLs to the user. Each URL is constructed by ‘str’ and its base name is displayed in an unordered list.

Note

This examples uses the StandardLookAndFeel macro.

<?xml version="1.0" ?>
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
      xmlns:metal="http://xml.zope.org/namespaces/metal"
      metal:use-macro="here/StandardLookAndFeel/macros/master">
<head>
 <title>Untitled Document</title>
 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
</head>
  <body>
    <div metal:fill-slot="main">
      <p>Welcome to the machine</p>
      <p>Choose your type:</p>
       <ul>
         <li tal:repeat="type python:'digital', 'analog', 'organic'">
          <a href="dummy"
             tal:attributes="href string:/mach/$type"
             tal:content="type">selection</a>
         </li>
       </ul>
    </div>
  </body>
</html>

Produces (something like):

<p>Welcome to the machine</p>
<p>Choose your type:
  <ul>
    <li tal:repeat="type python:'digital', 'analog', 'organic'">
     <a href="/mach/digital"
        tal:attributes="href string:/mach/$type"
        tal:content="type">digital</a>
    </li>
    <li tal:repeat="type python:'digital', 'analog', 'organic'">
     <a href="/mach/analog"
        tal:attributes="href string:/mach/$type"
        tal:content="type">analog</a></li>
    <li tal:repeat="type python:'digital', 'analog', 'organic'">
     <a href="/mach/organic"
        tal:attributes="href string:/mach/$type"
        tal:content="type">organic</a>
    </li>

  </ul>
</p>
Comments
richard (Apr 6, 2001 1:27 am; Comment #6) –

A much simpler example: displaying information from a ZSQL method:

<table>
 <tr tal:repeat="row here/my_zsql_method">
    <td tal:content="row/column1">Column 1 dummy data</td>
    <td tal:content="row/column2">Column 2 dummy data</td>
 </tr>
</table>

There’s a trio of example Page Templates (with support data) that you can try out by copying ‘PageTemplates/examples/zpt_examples.zexp’ into your ‘import’ directory and importing it somewhere. One of them shows off batching, another shows how to make a tree widget, and the third calls DTML Methods from ZPT.

Standard Look And Feel Example
A master template replacement for standard_header and standard_footer.
Simple Welcome Example
A simple example template using StandardLookAndFeel.
Loop Examples
Looping

Some “DTML to ZPT”, http://www.zope.org/Members/peterbe/DTML2ZPT_Conversion_examples conversion examples

Comments

weitzman (Sep 20, 2001 8:11 pm; Comment #2) Editor Remark Requested
I reviewed the included example on batching but the code is a bit overwhelming. A brief doc on batching would be most appreciated.
teyc (Oct 16, 2001 8:58 pm; Comment #3) Editor Remark Requested
The Fulton presentation suggested Scripts can be used as URL targets instead of PageTemplates themselves. Could someone furnish an example of this?

Architecture

Error Handling Strategies

TAL errors can occur at two different times:

  1. Edit Time
  2. Render Time

Edit-time Error Handling

Here is a proposal for dealing with TAL errors in saved templates.

Context

A Presentation Designer is creating or editing a template, not through the Zope Management Inferface, but through client software like GoLive or Dreamweaver.

Problem

The HTML file saved to Zope may have METAL/TAL syntax errors. Such a file is no longer a valid TAL template. However, the file is most likely okay HTML (as verified by the client software). What do we do when the saved file is broken?

Forces
  • Refusing to save the file (WebDAV or FTP PUT error) is very misleading. There is no consistent way to send back the intent of the rejection, so the user thinks that Zope is broken.
  • Silently ignoring the error means bad pages. Bad pages mean bad sites. Bad sites mean bad press.
  • The Presentation Designer has a workflow. TAL is already disruptive enough to the workflow. In addition, using ZPT is also a change in the way things are done. The potential for more errors will cause the designer to question how useful doing templates and Zope is to begin with.
  • The ZPT management interface can do fancy logging and presentation of the TAL syntax errors but a Presentation Designer may not want to leave his/her neat little GoLive world to wrangle with HTML forms (and the synchronization problem that editing in two places can introduce).
  • The workflow of the PresentationDesigner must be minimally impacted
    by the adoption of ZPT. In particular, TAL syntax errors will constantly remind the designer that they are working in a new paradigm. A paradigm not seamlessly supported by their HTML editing tool.
Solution

Mark the saved page as broken. When the page is rendered through a browser (from Zope), it should display detailed information (an error log) about what is wrong.

In addition, embed the error log in the original page. This way, the next time the page is loaded into the editor, the error will be evident. The designer will need to adopt a new process in which he reloads a page from Zope (through WebDAV or FTP) in order to make sure that a saved page has no errors.

A page with an error should contain a block of comments (after the DOCTYPE, XML declaration or the first place where a comment is legal) with text containing representing the error log, presented in such a manner that it is very clear that something is wrong. The log should be as verbose as necessary to point out exactly where the error occurs, the nature of the error and, perhaps, hints about how to fix it.

Note

21march2001 - This has been reworked to address some of the concerns raised by Comments #1, #2 and #3*

Comments
Duncan (Mar 13, 2001 9:49 am; Comment #1) –

One option to consider would be to allow the client software to always succeed when it attempts to save some text, whether or not it is valid XML.

If the saved page is valid, then it is compiled and everything works as usual. If the saved text is invalid, then the actual text is saved and returned to the user on request, but any attempt to use the template will throw an exception with details of the error. Ideally the error message should also be displayed to the user when the invalid text is saved.

My reason for suggesting this is quite simple. If a user can get in a situation where they cannot save their work, and potentially cannot spot their mistake, this is a bad thing. But if the user is allowed to save at any point, then they or someone else can come back and fix the problem later.

evan (Mar 14, 2001 5:22 pm; Comment #2) –

I agree with Duncan. Saves should almost always succeed, except where the document is clearly nothing like HTML (eg. user accidentally tries to save a .jpeg to a template). If the page contains errors that prevent compilation, the list of errors is saved, and an indication is returned along with the success code (if possible). Now the template is in a broken state, and attempts to render it produce an error report instead, with complete descriptions of what needs to be fixed. Also, whenever the source is loaded, an HTML comment block is prepended. This would require scanning for and removing this block on each save.

Now, we have two strategies. After each save you reload the page into either your editor, or into a browser window open to the page on which you’re working. In the editor, a comment will appear right at the start, and in the browser a complete error report will appear.

gvanrossum (Mar 20, 2001 12:28 pm; Comment #3) –

The main text is inconsistent with Evan’s last comment. Is the error inserted in the source as an HTML comment or as an HTML ‘div’ section?

If we use a ‘div’, I question where to insert it. Right after ‘html’ would mean just before the ‘head’ element which might make the ‘head’ section placed invalid according to the DTD (?). Maybe a comment is safer? That could go almost right at the top (but after <?xml…?> and after <!DOCTYPE …>).

[Gosh I hate structured text.]

tcoram (Mar 21, 2001 9:46 am; Comment #4) –

Oops. Not after ‘html’. That was a mistake, I meant after ‘body’… But, anyway, it probably makes more since to include the errors in comments. For visual editors, like Dreamweaver, my concern was that comments are hidden in visual mode and the designer wouldn’t notice the erorr log. However, Dreamweaver (at least DW4) shows a yellow-highlighted question marks where comments should be (which can be expanded on demand).

Commments make a lot of sense. That way we aren’t mucking with the visual structure of the document: If they want to live with an error (to be caught later?) they can.

Strategies for Error-handling at Render time

As of version 1.2, TAL has the ability to handle render-time errors using the on-error statement. Multiple on-error statements can be nested, and information about the error is provided to the handler’s expression. You can create a single error handler on the top element, or provide individual handlers for several parts of a template, or both.

Note that compile-time errors, such as TAL or TALES syntax errors, are not handled by on-error statements.

Each on-error statement is akin to a Python try:except: block with a single except: handler. If you want to handle exception selectively, you will need to write a script that examines the error information and decides whether to return text, re-raise the exception, or raise a new exception.

The simplest strategy for handling errors, apart from not handling them, is to use a literal string. For example, tal:on-error="string:Server Error" indicates that an error has occured, while allowing the rest of the page to render normally.

You can also use an expression that cancels the action. For example, tal:on-error="(if) nothing" will leave the template text unchanged, allowing it to serve as error text.

A more complex handler could insert an error icon, with a Javascript link to pop up error diagnostics for sophisticated users. Alternatively, it could insert an HTML comment or element attributes that give error details.

Guido recommends that on-error be used very sparingly, only to catch errors that are caused by external things such as failing database retrievals; it should also be used very selectively, meaning to put the on-error around just the element that may fail.

Very common “errors”, such as missing variables in a form submission, can be handled by a pre-processing script, or by TALES expressions that test for variable existence.

Zope Template Language Collaborations

A collaboration is a sequence of messages sent between objects that describe an implementation of some feature of software. It is helpful for identifying what kinds of objects are needed to solve a problem and what sorts of methods they need to provide.

Compile Source Collaboration

In this collaboration, we update template source and prepare create a TAL object to object to render the template.

Assumptions:

  • Our initial assumption is that we start from an object that implements the DOMInterface. An alternative might be to input an XML source string.
  • We want to separate the implementation of TAL and TALES
  • The implementation of TAL also implements METAL.
Collaboration

Objects:

PT
The page template
TI
An object that implements the TALImplementation interface.
EC
An object that implements the ExpressionCompiler interface.
DOM
an object that implements the DOM Interface.

Sequence of calls

  • PT calls TI.compileFromDOM(DOM, EC)

    When TAL expressions are encountered:

    • TI calls EC.compile(src)

      where src is the expression source string.

      The compiled expression is returned and saved by the TAL implementation for later use. The value returned is opaque and could be the original source of the expression compiler doesn’t do anything.

      The TAL compile method returns an opaque data structue that is used later to render output. Note that the compiled data contains any compiled expressions.

Render Template Collaboration

In this collaboration, we use a compiled TAL to render text.

Objects

PT – The PageTemplate

PT2 – Another PageTemplate

TI – An object that implements the TALImplementation interface.

CTAL – A compiled TAL data structure returned from a TAL compile
call.

CTAL2 – ditto.

EEF – An object that implements the ExpressionEngineFactory interface.

EE – An object that implements the ExpressionEngine interface.

cexpr – A compiled expression. This will be context-dependent. For
example, when we are executing a define. this will be a different expression for each definition.

Sequence of calls

  • PT calls EEF.create(data)

    to create an expression engine. The expression is passed names to be included in the standard namespace. The call returns an expression engine, EE.

  • PT calls TI.renderText(CTAL, EE)

    As the TALImplementation renders the TAL, it will make calls to EE as different TAL statements are executed:

    When a node containing a define or a repeat is encountered (and a condition, if present, if true):

    • TI calls EE.beginScope()

    After processing node containing a define or a loop:

    • TI calls EE.endScope()

    when a z:define is executed:

    For each define (in order), either:

    • TI calls EE.setLocal(name, cexpr)

    or

    • TI calls EE.setGlobal(name, cexpr)

    For each condition:

    • TI calls EE.isTrue(cexpr)

    For each repeat:

    • TI calls EE.startRepeat(name, cexpr)

    while 1:

    • TI calls EE.repeat()

      The return value is a boolean. If the value is false, we break out of the loop. Otherwise, we render the output node and continue.

    For each insert or replace:

    • TI calls EE.text(cexpr)

      To compute the text to be used. The return value is either a string, None, or an object that satisfies the DOM text-node interface.

    or

    • TI calls EE.structure(cexpr)

      To compute the structure to be inserted. The result should be a DOM Fragment or None.

    For each attribute to be set:

    • TI calls EE.text(cexpr)

      to compute the attribute text. The result should be a string or None.

    When a use-macro is encountered,

    • TI calls EE.macro(cexpr)

      The expression engine evaluates the expression. This is likely to involve traversal into a PageTemplate, which will ask the TAL implementation for the macro.

      PT2 calls TI.findMacro(CTAL2, name)

Get Template Source Collaboration

When we get a templates source, we want to expand macros references.

Objects

PT – The page template

PT2 – Another page template

TI – An object that implements the TALImplementation interface.

CTAL – A compiled TAL data structure returned from a TAL compile
call.

CTAL2 – ditto.

EEF – An object that implements the ExpressionEngineFactory interface.

EE – An object that implements the ExpressionEngine interface.

cexpr – A compiled expression. This will be context-dependent. For
example, when we are executing a define. this will be a different expression for each definition.

Sequence

  • PT calls EEF.create(data)

    to create an expression engine. The expression is passed names to be included in the standard namespace. The call returns an expression engine, EE.

  • PT calls TI.getSource(CTAL, EE)

    When a use-macro is encountered,

    • TI calls EE.macro(cexpr)

      The expression engine evaluates the expression. This is likely to involve traversal into a page template, which will ask the TAL implementation for the macro.

      PT2 calls TI.findMacro(CTAL2, name)

ZPT Interfaces

ExpressionCompiler
The expression compiler interface provides the ability to compile expressions to speed later rendering. A trivial expression compiler could simply return uncompiled expression source.
ExpressionContextFactory
An interface for creating expression contexts.
ExpressionContext
An interfaces for objects that evaluate previously compiled expressions, while maintaining the evaluation context.
TALImplementation
This is the interface provided by TAL and METAL implementations.

This document (and the documents it links to) will describe the architecture for the Zope PageTemplates. It is a work in progress.

By an architecture, we mean a subset of the design that documents the design decisions made to address the Major Use Cases and Risk Factors. This will typically include a high-level design overview. The design will typically be expressed as prose, often supplemented with UML diagrams.

ZPT consists of 4 major components:

  1. The Template Attribute Language (TAL Reference) implementation
  2. The Template Attribute Language Expression Syntax (TALES Reference) implementation
  3. The Macro Expansion Template Attribute Language (METAL Reference) implementation
  4. PageTemplates

Before we can harden the design and implementation of these components, we need to agree on the ways in which these components interact. We’ll begin by defining relevant ZPT Interfaces that the various components will need to provide. We’ll decide what these interfaces need to look like through Zope Template Language Collaborations.

ZPT has complex interactions to deal with. The HTML editing tool (whether it be emacs, vi, GoLive or whatever) will have to communicate with Zope (usually through WebDAV or FTP) in order to save and load PageTemplates. Since the templates may have TAL syntax errors in them, we need to develop Error Handling Strategies.

Can you trip up TAL, TALES, and METAL?

My test programs are intended to break the system, to push it to its extreme limits, to pile complication on complication, in ways that the system programmer never consciously anticipated. To prepare such test data, I get into the meanest, nastiest frame of mind that I can manage, and I write the cruelest code I can think of; then I turn around and embed that in even nastier constructions that are almost obscene.

– Donald Knuth, The Errors of TeX (1989)

Things that should work

Here are a few things that should work. If they don’t, then there’s either a bug in the implementation or the specifications have changed out from under this document.

This one may confuse you, but it shouldn’t confuse TAL/TALES:

<span z:define="global global str:0; local global str:1;;one">
  <span z:define="local local local:global; global global str:2">
   <ul>
    <li z:insert="var:local">1;one</li>
    <li z:insert="var:global">1;one</li>
    <li z:insert="local:global">1;one</li>
    <li z:insert="global:global">2</li>
   </ul>
  </span>
</span>

This snippet defines a simple macro:

<table z:define-macro="useless" z:define="local one python:1"
       z:attributes="border var:defaultborder">
  <tr><th>One line table</th></tr>
  <tr z:define-slot="firstrow"><td>firstrow</td></tr>
</table>

This snippet uses the above macro:

<table z:use-macro="template/useless" z:define="local one str:one">
  <tr z:fill-slot="firstrow"><td z:insert="var:one">1 or one?</td></tr>
</table>

Things that should generate errors

There are a few things that should generate errors. We should write them down.

Developer Notes

This is a semi-random collection of information that may be useful to you if you’re developing Products or otherwise hacking Zope at the filesystem level.

PageTemplateFile

Just as the DTMLFile class can be used to create object UI pages from DTML on the filesystem, the PageTemplateFile class allows you to make UI from filesystem Page Templates. For example:

from Products.PageTemplates.PageTemplateFile import PageTemplateFile

class FooClass(SimpleItem):
    # stuff
    manage_main = PageTemplateFile('www/manage', globals())

When this class is created, the file ‘manage.zpt’ will be read from the ‘www’ subdirectory of the package in which the class is defined. The ‘.zpt’ extension is always added to the name that you specify.

Unlike DTMLFiles, PageTemplateFiles operate under the Zope security restrictions.

If you use the same PageTemplateFile object under more than one name in a class, you should pass the keyword argument __name__ to specify the name to use for security declarations. For example:

class FooClass(SimpleItem):
    ...
    security.declareProtected('View Management Screens', 'manage_main')
    manage_main = PageTemplateFile('www/manage', globals(),
                                    __name__='manage_main')
    foo_view = manage_main
    ...
Comments
peterbe (Sep 10, 2001 9:37 am; Comment #1) –
If you care, NetSend was written with PageTemplates in an python product. Fun to look at perhaps if you want to see some examples. It’s easy and clear.

Other Uses of Tales

TALES can be used outside of Zope Page Templates. Zope 3 uses TALES expressions embedded in ZCML in a few instances where some sort of filter expression is needed. One example is the ‘filter’ attribute of the ‘browser:menuItem’ element:

<menuItem
    title="Show details"
    action="show_details.html"
    permission="zope.View"
    filter="not:context/metadata_is_missing"
    />

Ulrick Eck has written code to use TALES expressions in his own Products. Here is sample Zope 2 code that he contributed to the mailing list:

import Globals
from Globals import Persistent
from Acquisition import aq_inner, aq_parent
from AccessControl import getSecurityManager, ClassSecurityInfo

from Products.PageTemplates.Expressions import getEngine, _SecureModuleImporter

SecureModuleImporter = _SecureModuleImporter()

class Expression (Persistent):
    method_name = ''
    _expr = None

    security = ClassSecurityInfo()

    def __init__(self, method_name):
        self.method_name = method_name
        e = getEngine()
        self._expr = e.compile(method_name)

    security.declarePrivate('validate')
    def validate(self, inst, parent, name, value, md):
        # Zope 2.3.x
        return getSecurityManager().validate(inst, parent, name, value)

    def __call__(self, context):
       e = getEngine()
       if self._expr is None:
             self._expr = e.compile(self.method_name)
       c = e.getContext(context)
       return c.evaluate(self._expr)

Globals.InitializeClass(Expression)


def exprNamespace(client,form, field=None, value=None, object=None):
    root = client.getPhysicalRoot()
    c = {'form': form,
         'field': field,
         'value': value,
         'here': object,
         'nothing': None,
         'options': {},
         'root': root,
         'request': getattr(root, 'REQUEST', None),
         'modules': SecureModuleImporter,
         }
    return c

Use it like this:

myExpr = Expression('string:this is a text')
ns = exprNamespace(...)
result = myExpr(ns)

TAL Specification Version 1.0

The Template Attribute Language is an Attribute Languages used to create dynamic templates. It allows elements of a document to be replaced, repeated, or omitted.

The statements of TAL are XML attributes from the TAL namespace. These attributes can be applied to an XML or HTML document in order to make it act as a template.

A TAL statement has a name (the attribute name) and a body (the attribute value). For example, an ‘content’ statement might look like ‘tal:content=”string:Hello”’. The element on which a statement is defined is its statement element. Most TAL statements require expressions, but the syntax and semantics of these expressions are not part of TAL. Template Attribute Language Expression Syntax is recommended for this purpose.

The TAL namespace URI and recommended alias are currently defined as:

xmlns:tal="http://xml.zope.org/namespaces/tal"

TAL Statements

See :doc:`EBNF` for rules and terminals. See :doc:`../AttributeLanguage` for a description of attribute language statements.

The following are the TAL 1.0 statements:

define, attributes, condition, content, replace, repeat

Each statement is described below, along with its argument syntax. Although Template Attribute Language does not define the syntax of ‘expression’ non-terminals, leaving that up to the implementation, a canonical expression syntax for use in Template Attribute Language arguments is described in [TALES Specification 1.0].

Define

Syntax:

argument       ::= define_scope [';' define_scope]*
define_scope   ::= (['local'] | 'global') define_var
define_var     ::= variable_name expression
variable_name  ::= Name

Note: If you want to include a semi-colon (;) in an ‘expression’, it must be escaped by doubling it (;;).

You can define two different kinds of TAL variables: local and global. When you define a local variable in a statement element, you can only use that variable in that element and the elements it contains. If you redefine a local variable in a contained element, the new definition hides the outer element’s definition within the inner element. When you define a global variables, you can use it in any element processed after the defining element. If you redefine a global variable, you replace its definition for the rest of the Template.

Examples:

tal:define="mytitle template/title; tlen python:len(mytitle)"
tal:define="global company_name string:Digital Creations, Inc."
Attributes

Syntax:

argument             ::= attribute_statement [';' attribute_statement]*
attribute_statement  ::= attribute_name expression
attribute_name       ::= [namespace ':'] Name
namespace            ::= Name

Note: If you want to include a semi-colon (;) in an ‘expression’, it must be escaped by doubling it (;;).

If you want to replace the value of an attribute (or create an attribute) with a dynamic value, you need the ‘attributes’ command. You can qualify an attribute name with a namespace prefix, for example ‘html:table’. The value of each expression is converted to a string, if necessary. If the value is nothing (see [TALES Specification 1.0]), the attribute is deleted. Examples:

<a href="/sample/link.html"
   tal:attributes="href here/sub/absolute_url">
<textarea rows="80" cols="20"
   tal:attributes="rows request/rows;cols request/cols">

When this is used on a node with a ‘replace’ command, the replacement must use the ‘structure’ type, the structure returned by the expression must yield at least one element node, and the attributes are replaced on the first such element node only.

When this is used on a node with a ‘repeat’ command, the replacement is made on each repetition of the element, and the replacement expression is evaluated fresh for each repetition.

Condition

Syntax:

argument ::= expression

To include a particular part of a Template only under certain conditions, and omit it otherwise, use the ‘condition’ command. Its body is an expression. If the expression evaluates to a true value, then normal processing of the element continues, otherwise the command element is immediately removed from the document. It is up to the interface between TAL and the expression engine to determine the value of true and false.

Example:

<p tal:condition="here/copyright"
   tal:content="here/copyright">(c) 2000</p>
Replace

Syntax:

argument ::= (['text'] | 'structure') expression

To replace an element with dynamic content, use the ‘replace’ statement. This replaces the current element with either text or a structure (unescaped ML). The body of the statement is an expression with an optional type prefix. The value of the expression is converted into an escaped string if you prefix the expression with ‘text’ or omit the prefix, and is inserted raw if you prefix it with ‘structure’. If the value is *nothing (see Template Attribute Language Expression Syntax), then the element is simply removed.

Note: The default replacement behavior is ‘text’.

Examples:

<span tal:replace="template/title">Title</span>
<span tal:replace="text template/title">Title</span>
<span tal:replace="structure table" />
Content

Syntax:

argument ::= (['text'] | 'structure') expression

Rather than replacing an entire element, you can insert text or structure in place of its children with the ‘content’ statement. The statement argument is exactly like that of ‘replace’, and is interpreted in the same fashion. If the expression evaluates to nothing, the statement element is left childless.

Note: The default replacement behavior is ‘text’.

Example:

<p tal:content="user/name">Fred Farkas</p>
Repeat

Syntax:

argument      ::= variable_name expression
variable_name ::= Name

When you want to replicate a subtree of your document once for each item in a sequence, you use ‘repeat’. The expression should evaluate to a sequence. The ‘variable_name’ is used to define a local variable and a repeat variable. For each iteration, the local variable is set to the current sequence element, and the repeat variable is set to an iteration object. You use iteration objects to access information about the current repeat iteration (such as the repeat index). (Iteration objects are more properly the domain of Template Attribute Language Expression Syntax.) The repeat variable has the same name as the local variable, but is only accessible through the ‘repeat’ variable. Examples:

<p tal:repeat="txt python:'one', 'two', 'three'">
   <span tal:replace="txt" />
</p>
<table>
  <tr tal:repeat="item here/cart">
      <td tal:content="repeat/item/index">1</td>
      <td tal:content="item/description">Widget</td>
      <td tal:content="item/price">$1.50</td>
  </tr>
</table>

Order of Operations

When there is only one TAL statement per element, the order in which they are executed is simple. Starting with the root node, each node executes its command, then tells each of its child elements, in order, to do the same.

Only one command out of ‘content’, ‘replace’, and ‘repeat’ is allowed on a single element. Any one of these may appear on the same element as a ‘define’, ‘attributes’, and ‘condition’ command.

When an element has multiple commands, they are executed in this order:

  • ‘define’
  • ‘condition’
  • ‘content’, ‘replace’, or ‘repeat’
  • ‘attributes’

The reasoning behind this ordering goes like this: You often want to set up variables for use in other commands, so ‘define’ comes first. The very next thing to do is decide whether this node will be included at all, so ‘condition’ is next; since the condition may depend on variables you just set, it comes after ‘define’. Setting attributes doesn’t make sense if the next step throws the entire node away, and we will probably want to be able to fill in different attribute values on each iteration of a repeat, so ‘attributes’ is last. All of the remaining commands clash, because they each replace or edit the command element.

If you want to override this ordering, you must do so by enclosing the element in another element, possibly ‘div’ or ‘span’, and placing some of the commands on this new element. Examples:

<p tal:define="x /a/long/path/from/the/root"
   tal:condition="x"
   tal:content="x/txt"
   tal:attributes="class x/class">Ex Text</p>

TAL Specification Version 1.2

This specification supercedes TAL Specification Version 1.0.

The Template Attribute Language is an Attribute Languages used to create dynamic templates. It allows elements of a document to be replaced, repeated, or omitted.

The statements of TAL are XML attributes from the TAL namespace. These attributes can be applied to an XML or HTML document in order to make it act as a template.

A TAL statement has a name (the attribute name) and a body (the attribute value). For example, an ‘content’ statement might look like ‘tal:content=”string:Hello”’. The element on which a statement is defined is its statement element. Most TAL statements require expressions, but the syntax and semantics of these expressions are not part of TAL. Template Attribute Language Expression Syntax is recommended for this purpose.

The TAL namespace URI and recommended alias are currently defined as:

xmlns:tal="http://xml.zope.org/namespaces/tal"

TAL Statements

See EBNF for rules and terminals. See Attribute Languages for a description of attribute language statements.

The following are the TAL 1.2 statements:

  • define
  • attributes
  • condition
  • content
  • replace
  • repeat
  • on-error

Each statement is described below, along with its argument syntax. Also, the order of operations is described.

Although Template Attribute Language does not define the syntax of ‘expression’ non-terminals, leaving that up to the implementation, a canonical expression syntax for use in Template Attribute Language arguments is described in Template Attribute Language Expression Syntax.

Expressions used in statements may return values of any type, although most statements will only accept strings, or will convert values into a string representation. The expression language must define a value named nothing (see Template Attribute Language Expression Syntax) that is not a string. In particular, this value is useful for deleting elements or attributes.

Evaluation of an expression may cause the action (the effect of the statement) to be cancelled. In this case, it is as though the statement (or part of the statement, in some cases) did not exist.

define

Syntax:

argument       ::= define_scope [';' define_scope]*
define_scope   ::= (['local'] | 'global') define_var
define_var     ::= variable_name expression
variable_name  ::= Name

Note: If you want to include a semi-colon (;) in an ‘expression’, it must be escaped by doubling it (;;).

You can define two different kinds of TAL variables: local and global. When you define a local variable in a statement element, you can only use that variable in that element and the elements it contains. If you redefine a local variable in a contained element, the new definition hides the outer element’s definition within the inner element. When you define a global variables, you can use it in any element processed after the defining element. If you redefine a global variable, you replace its definition for the rest of the Template.

If the expression associated with a variable evaluates to nothing, then that variable has the value nothing, and may be used as such in further expressions. If the expression cancels the action, then that variable is not (re)defined. This means that if the variable exists in an enclosing scope, its value is unchanged, but if it does not, it is not created. Each variable definition is independent, so variables may be defined in the same statement in which some variable definitions are cancelled.

Examples:

tal:define="mytitle template/title; tlen python:len(mytitle)"
tal:define="global company_name string:Digital Creations, Inc."
attributes

Syntax:

argument             ::= attribute_statement [';' attribute_statement]*
attribute_statement  ::= attribute_name expression
attribute_name       ::= [namespace ':'] Name
namespace            ::= Name

Note: If you want to include a semi-colon (;) in an ‘expression’, it must be escaped by doubling it (;;).

If you want to replace the value of an attribute (or create an attribute) with a dynamic value, you need the ‘attributes’ statement. You can qualify an attribute name with a namespace prefix, for example ‘html:table’, if you are generating an XML document with multiple namespaces. The value of each expression is converted to a string, if necessary.

If the expression associated with an attribute assignment evaluates to nothing, then that attribute is deleted from the statement element. If the expression cancels the action, then that attribute is left unchanged. Each attribute assignment is independent, so attributes may be assigned in the same statement in which some attributes are deleted and others are left alone due to cancellation. Examples:

<a href="/sample/link.html"
   tal:attributes="href here/sub/absolute_url">
<textarea rows="80" cols="20"
   tal:attributes="rows request/rows;cols request/cols">

When this statement is used on an element with an active ‘replace’ command, the implementation may ignore the ‘attributes’ statement. If it does not, the replacement must use the ‘structure’ type, the structure returned by the expression must yield at least one element, and the attributes will be replaced on the first such element only.

When this is used on an element with a ‘repeat’ statement, the replacement is made on each repetition of the element, and the replacement expression is evaluated fresh for each repetition.

condition

Syntax:

argument ::= expression

To include a particular part of a Template only under certain conditions, and omit it otherwise, use the ‘condition’ statement. If its expression evaluates to a true value, then normal processing of the element continues, otherwise the statement element is immediately removed from the document. It is up to the interface between TAL and the expression engine to determine the value of true and false. For these purposes, the value nothing is false, and cancellation of the action has the same effect as returning a true value.

Example:

<p tal:condition="here/copyright"
   tal:content="here/copyright">(c) 2000</p>
replace

Syntax:

argument ::= (['text'] | 'structure') expression

To replace an element with dynamic content, use the ‘replace’ statement. This replaces the statement element with either text or a structure (unescaped markup). The body of the statement is an expression with an optional type prefix. The value of the expression is converted into an escaped string if you prefix the expression with ‘text’ or omit the prefix, and is inserted unchanged if you prefix it with ‘structure’. Escaping consists of converting “&amp;” to “&amp;amp;”, “&lt;” to “&amp;lt;”, and “&gt;” to “&amp;gt;”.

If the value is nothing, then the element is simply removed. If the action is cancelled, then the element is left unchanged (see the TALES default value).

Note: The default replacement behavior is ‘text’.

Examples:

<span tal:replace="template/title">Title</span>
<span tal:replace="text template/title">Title</span>
<span tal:replace="structure table" />
<span tal:replace="nothing">This element is a comment.</span>
content

Syntax:

argument ::= (['text'] | 'structure') expression

Rather than replacing an entire element, you can insert text or structure in place of its children with the ‘content’ statement. The statement argument is exactly like that of ‘replace’, and is interpreted in the same fashion. If the expression evaluates to nothing, the statement element is left childless. If the action is cancelled, then the element’s contents are unchanged.

Note: The default replacement behavior is ‘text’.

Example:

<p tal:content="user/name">Fred Farkas</p>
repeat

Syntax:

argument      ::= variable_name expression
variable_name ::= Name

When you want to replicate a subtree of your document once for each item in a sequence, you use ‘repeat’. The expression should evaluate to a sequence. If the sequence is empty, then the statement element is deleted, otherwise it is repeated for each value in the sequence. If the action is cancelled, then the element is left unchanged, and no new variables are defined.

The ‘variable_name’ is used to define a local variable and a repeat variable. For each repetition, the local variable is set to the current sequence element, and the repeat variable is set to an iteration object. You use iteration objects to access information about the current repetition (such as the repeat index). (Iteration objects are more properly the domain of Template Attribute Language Expression Syntax.) The repeat variable has the same name as the local variable, but is only accessible through the builtin variable named ‘repeat’ (see RepeatVariable). Examples:

<p tal:repeat="txt python:'one', 'two', 'three'">
   <span tal:replace="txt" />
</p>
<table>
  <tr tal:repeat="item here/cart">
      <td tal:content="repeat/item/index">1</td>
      <td tal:content="item/description">Widget</td>
      <td tal:content="item/price">$1.50</td>
  </tr>
</table>

on-error

Syntax:

argument ::= (['text'] | 'structure') expression

You can provide error handling for your document using ‘on-error’. When a TAL statement produces an error, the TAL interpreter searches for an ‘on-error’ statement on the same element, then on the enclosing element, and so forth. The first ‘on-error’ found is invoked. It is treated as a ‘content’ statement.

The simplest sort of ‘on-error’ statement has a literal error string or nothing for an expression. A more complex handler may call a script that examines the error and either emits error text or raises an exception to propagate the error outwards. See Strategies for Error-handling at Render time for further information. Example:

<p tal:on-error="string: Error! This paragraph is buggy!">
My name is <span tal:replace="here/SlimShady" />.<br />
(My login name is
<b tal:on-error="string: Username is not defined!"
   tal:content="user">Unknown</b>)
</p>

In the above example, if ‘here/SlimShady’ results in an error, the ‘on-error’ statement catches it and replaces the paragraph with the string ‘“Error! This paragraph is buggy!”’. If ‘here/SlimShady’ evaluates correctly, but there is an error evaluating ‘user’, then ‘“Username is not defined!”’ replaces ‘Unknown’, but the rest of the paragraph is processed normally.

Order of Operations

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 is visited, in order, to do the same.

Any combination of statements may appear on the same elements, except that the ‘content’ and ‘replace’ statements may not appear together.

When an element has multiple statements, they are executed in this order:

  • ‘define’
  • ‘condition’
  • ‘repeat’
  • ‘content’ or ‘replace’
  • ‘attributes’

Since the ‘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 be able to replace various parts of an element with different values on each iteration of a repeat, so ‘repeat’ is next. It makes no sense to replace attributes and then throw them away, so ‘attributes’ is last. The remaining statements clash, because they each replace or edit the statement element.

If you want to override this ordering, you must do so by enclosing the element in another element, possibly ‘div’ or ‘span’, and placing some of the statements on this new element. Examples:

<p tal:define="x /a/long/path/from/the/root"
   tal:condition="x"
   tal:content="x/txt"
   tal:attributes="class x/class">Ex Text</p>

Comments

hansa (Jul 10, 2001 11:59 am; Comment #2) Editor Remark Requested
  • my browser is unable to locate the server xml.zope.org
  • typo … See AttributeLanguage for a
  • variable redefinition: case ‘global’ redefs ‘local’ only implicit.
  • “When this statement is used on a node with an active ‘replace’ command,” perhaps the example should contain a ‘replace’ (ditto ‘repeat’). X-effects when ‘attributes’ and ‘replace’/’repeat’ on the same statement element.
  • …”the implementation may ignore”… hmm. This is a spec, used only for ZPT. But even if not, where is it documented for ZPT? or do i have to experiment and the language is “what the machine accepts”?
  • …”interface between TAL and the expression engine”… This interface has a)namespaces passed to METAL, b)exceptions returned to TAL, c)agreements abt common variable names ‘nothing’, d) and objects nothing, false etc. i would like to have it explicitly described and also, if there is leeway in implementing the interface, where. TALES does more than TAL describes and in the tutorials plenty more is mentioned.
  • Also, if examples are given, they should be more like before/after TAL
  • change/the element is left/the statement element is left/
Evan –

The namespace URN “http://xml.zope.org/namespaces/tal” is not a URL, but merely a unique identifier. The “http://” is misleading, and probably shouldn’t be there.

I don’t see what the typo is, in item 2. I don’t understand item 3.

This spec is meant to be independent of Zope and ZPT. Some of the optional behavior in the spec isn’t properly documented for ZPT, as you noted.

I’ll address the other items as time allows.

hansa (Jul 11, 2001 6:45 am; Comment #3) –
  • the typo: in my browser the asterisks appear, when by looking at the source the intention was italics

  • “When this statement…” appears in section abt ‘attributes’ right after the example and explains cross-effects between ‘attributes’ and ‘replace’. It took some time to figure out an example with both statements, like:

    <a href="dummy" tal:replace="<a>gaga</a><a>dodo</a>"
       tal:attributes="href here/sub/absolute_url">
    

    the interpretation of which is implementation dependent and should give either:

    <a href="result of here/sub/absolute/url">gaga</a><a>dodo</a>
    

    or:

    <a>gaga</a><a>dodo</a>.
    
  • a description of the full ZPT would be very welcome. Thanks for the patience anyway

TAL Specification Version 1.4

This specification supercedes TAL Specification Version 1.2.

The Template Attribute Language is an Attribute Languages used to create dynamic templates. It allows elements of a document to be replaced, repeated, or omitted.

The statements of TAL are XML attributes from the TAL namespace. These attributes can be applied to an XML or HTML document in order to make it act as a template.

A TAL statement has a name (the attribute name) and a body (the attribute value). For example, an ‘content’ statement might look like ‘tal:content=”string:Hello”’. The element on which a statement is defined is its statement element. Most TAL statements require expressions, but the syntax and semantics of these expressions are not part of TAL. Template Attribute Language Expression Syntax is recommended for this purpose.

The TAL namespace URI and recommended alias are currently defined as:

xmlns:tal="http://xml.zope.org/namespaces/tal"

This is not a URL, but merely a unique identifier. Do not expect a browser to resolve it successfully.

TAL Statements

See EBNF for rules and terminals. See Attribute Languages for a description of attribute language statements.

The following are the TAL 1.4 statements:

  • define
  • attributes
  • condition
  • content
  • replace
  • repeat
  • on-error
  • omit-tag

Each statement is described below, along with its argument syntax. Also, the order of operations< is described.

Although Template Attribute Language does not define the syntax of ‘expression’ non-terminals, leaving that up to the implementation, a canonical expression syntax for use in Template Attribute Language arguments is described in Template Attribute Language Expression Syntax.

Expressions used in statements may return values of any type, although most statements will only accept strings, or will convert values into a string representation. The expression language must define a value named nothing (see Template Attribute Language Expression Syntax) that is not a string. In particular, this value is useful for deleting elements or attributes.

Evaluation of an expression may cause the action (the effect of the statement) to be cancelled. In this case, it is as though the statement (or part of the statement, in some cases) did not exist.

define

Syntax:

argument       ::= define_scope [';' define_scope]*
define_scope   ::= (['local'] | 'global') define_var
define_var     ::= variable_name expression
variable_name  ::= Name

Note: If you want to include a semi-colon (;) in an ‘expression’, it must be escaped by doubling it (;;).

You can define two different kinds of TAL variables: local and global. When you define a local variable in a statement element, you can only use that variable in that element and the elements it contains. If you redefine a local variable in a contained element, the new definition hides the outer element’s definition within the inner element. When you define a global variables, you can use it in any element processed after the defining element. If you redefine a global variable, you replace its definition for the rest of the Template.

If the expression associated with a variable evaluates to nothing, then that variable has the value nothing, and may be used as such in further expressions. If the expression cancels the action, then that variable is not (re)defined. This means that if the variable exists in an enclosing scope, its value is unchanged, but if it does not, it is not created. Each variable definition is independent, so variables may be defined in the same statement in which some variable definitions are cancelled.

Examples:

tal:define="mytitle template/title; tlen python:len(mytitle)"
tal:define="global company_name string:Digital Creations, Inc."
attributes

Syntax:

argument             ::= attribute_statement [';' attribute_statement]*
attribute_statement  ::= attribute_name expression
attribute_name       ::= [namespace ':'] Name
namespace            ::= Name

Note: If you want to include a semi-colon (;) in an ‘expression’, it must be escaped by doubling it (;;).

If you want to replace the value of an attribute (or create an attribute) with a dynamic value, you need the ‘attributes’ statement. You can qualify an attribute name with a namespace prefix, for example ‘html:table’, if you are generating an XML document with multiple namespaces. The value of each expression is converted to a string, if necessary.

If the expression associated with an attribute assignment evaluates to nothing, then that attribute is deleted from the statement element. If the expression cancels the action, then that attribute is left unchanged. Each attribute assignment is independent, so attributes may be assigned in the same statement in which some attributes are deleted and others are left alone due to cancellation. Examples:

<a href="/sample/link.html"
   tal:attributes="href here/sub/absolute_url">
<textarea rows="80" cols="20"
   tal:attributes="rows request/rows;cols request/cols">

When this statement is used on an element with an active ‘replace’ command, the implementation may ignore the ‘attributes’ statement. If it does not, the replacement must use the ‘structure’ type, the structure returned by the expression must yield at least one element, and the attributes will be replaced on the first such element only. For example, for the first line below, either of the two outcomes that follow it is acceptable:

<span tal:replace="structure an_image" tal:attributes="border string:1">
<img src="foo.png">
<img src="foo.png" border="1">

When this is used on an element with a ‘repeat’ statement, the replacement is made on each repetition of the element, and the replacement expression is evaluated fresh for each repetition.

condition

Syntax:

argument ::= expression

To include a particular part of a Template only under certain conditions, and omit it otherwise, use the ‘condition’ statement. If its expression evaluates to a true value, then normal processing of the element continues, otherwise the statement element is immediately removed from the document. It is up to the interface between TAL and the expression engine to determine the value of true and false. For these purposes, the value nothing is false, and cancellation of the action has the same effect as returning a true value.

Example:

<p tal:condition="here/copyright"
   tal:content="here/copyright">(c) 2000</p>
replace

Syntax:

argument ::= (['text'] | 'structure') expression

To replace an element with dynamic content, use the ‘replace’ statement. This replaces the statement element with either text or a structure (unescaped markup). The body of the statement is an expression with an optional type prefix. The value of the expression is converted into an escaped string if you prefix the expression with ‘text’ or omit the prefix, and is inserted unchanged if you prefix it with ‘structure’. Escaping consists of converting “&amp;” to “&amp;amp;”, “&lt;” to “&amp;lt;”, and “&gt;” to “&amp;gt;”.

If the value is nothing, then the element is simply removed. If the action is cancelled, then the element is left unchanged (see the TALES default value).

Note: The default replacement behavior is ‘text’.

Examples:

<span tal:replace="template/title">Title</span>
<span tal:replace="text template/title">Title</span>
<span tal:replace="structure table" />
<span tal:replace="nothing">This element is a comment.</span>
content

Syntax:

argument ::= (['text'] | 'structure') expression

Rather than replacing an entire element, you can insert text or structure in place of its children with the ‘content’ statement. The statement argument is exactly like that of ‘replace’, and is interpreted in the same fashion. If the expression evaluates to nothing, the statement element is left childless. If the action is cancelled, then the element’s contents are unchanged.

Note: The default replacement behavior is ‘text’.

Example:

<p tal:content="user/name">Fred Farkas</p>
repeat

Syntax:

argument      ::= variable_name expression
variable_name ::= Name

When you want to replicate a subtree of your document once for each item in a sequence, you use ‘repeat’. The expression should evaluate to a sequence. If the sequence is empty, then the statement element is deleted, otherwise it is repeated for each value in the sequence. If the action is cancelled, then the element is left unchanged, and no new variables are defined.

The ‘variable_name’ is used to define a local variable and a repeat variable. For each repetition, the local variable is set to the current sequence element, and the repeat variable is set to an iteration object. You use iteration objects to access information about the current repetition (such as the repeat index). (Iteration objects are more properly the domain of Template Attribute Language Expression Syntax.) The repeat variable has the same name as the local variable, but is only accessible through the builtin variable named ‘repeat’ (see Repeat Variable). Examples:

<p tal:repeat="txt python:'one', 'two', 'three'">
   <span tal:replace="txt" />
</p>
<table>
  <tr tal:repeat="item here/cart">
      <td tal:content="repeat/item/index">1</td>
      <td tal:content="item/description">Widget</td>
      <td tal:content="item/price">$1.50</td>
  </tr>
</table>
on-error

Syntax:

argument ::= (['text'] | 'structure') expression

You can provide error handling for your document using ‘on-error’. When a TAL statement produces an error, the TAL interpreter searches for an ‘on-error’ statement on the same element, then on the enclosing element, and so forth. The first ‘on-error’ found is invoked. It is treated as a ‘content’ statement.

The simplest sort of ‘on-error’ statement has a literal error string or nothing for an expression. A more complex handler may call a script that examines the error and either emits error text or raises an exception to propagate the error outwards. See Strategies for Error-handling at Render time for further information. Example:

<p tal:on-error="string: Error! This paragraph is buggy!">
My name is <span tal:replace="here/SlimShady" />.<br />
(My login name is
<b tal:on-error="string: Username is not defined!"
   tal:content="user">Unknown</b>)
</p>

In the above example, if ‘here/SlimShady’ results in an error, the ‘on-error’ statement catches it and replaces the paragraph with the string ‘“Error! This paragraph is buggy!”’. If ‘here/SlimShady’ evaluates correctly, but there is an error evaluating ‘user’, then ‘“Username is not defined!”’ replaces ‘Unknown’, but the rest of the paragraph is processed normally.

omit-tag

Syntax:

argument ::= ![expression]

To leave the contents of a tag in place while omitting the surrounding start and end tag, use the ‘omit-tag’ statement. If its expression evaluates to a false value, then normal processing of the element continues. If the expression evaluates to a true value, or there is no expression, the statement tag is replaced with its contents. It is up to the interface between TAL and the expression engine to determine the value of true and false. For these purposes, the value nothing is false, and cancellation of the action has the same effect as returning a false value.

Examples:

<div tal:omit-tag="" comment="This tag will be removed">
<i>...but this text will remain.</i>
</div>

<b tal:omit-tag="not:bold">I may not be bold.</b>

Order of Operations

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 is visited, in order, to do the same.

Any combination of statements may appear on the same elements, except that the ‘content’ and ‘replace’ statements may not appear together.

When an element has multiple statements, they are executed in this order:

  • ‘define’
  • ‘condition’
  • ‘repeat’
  • ‘content’ or ‘replace’
  • ‘attributes’
  • ‘omit-tag’

Since the ‘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 be able to replace various parts of an element with different values on each iteration of a repeat, so ‘repeat’ is next. It makes no sense to replace attributes and then throw them away, so ‘attributes’ is last. The remaining statements clash, because they each replace or edit the statement element.

If you want to override this ordering, you must do so by enclosing the element in another element, possibly ‘div’ or ‘span’, and placing some of the statements on this new element. Examples:

<p tal:define="x /a/long/path/from/the/root"
   tal:condition="x"
   tal:content="x/txt"
   tal:attributes="class x/class">Ex Text</p>

Comments

“the implementation may ignore.” hmm. This is a spec, used only for ZPT. But even if not, where is it documented for ZPT? or do i have to experiment and the language is “what the machine accepts”?

…”interface between TAL and the expression engine”… This interface has a)namespaces passed to METAL, b)exceptions returned to TAL, c)agreements abt common variable names ‘nothing’, d) and objects nothing, false etc. i would like to have it explicitly described and also, if there is leeway in implementing the interface, where. TALES does more than TAL describes and in the tutorials plenty more is mentioned.

change/the element is left/the statement element is left/

MHudson (Feb 17, 2002 12:21 pm; Comment #1) Editor Remark Requested

TAL 1.4 includes several references to: If the expression _cancels_ the action… _cancellation_ of the action has the same effect as…

The only reference I can find to the mechanism for cancellation is from TALES 1.2 where the path modifier (if) could _cancel_ an action. Since TALES 1.2 has been superceded by TALES 1.4 are there other mechanisms for cancellation or are these clauses now superceded?

MHudson (Feb 17, 2002 12:23 pm; Comment #2) –
TALES 1.4 mentioned above should be TALES 1.3 which removed the path modifers.
gotcha (Mar 22, 2002 11:10 am; Comment #3) Editor Remark Requested
It says:
If you want to override this ordering, you must do so by enclosing the element in another element, possibly ‘div’ or ‘span’, and placing some of the statements on this new element.

This sentence should be removed and replaced by something like:

‘tal:’ prefixed elements are allowed and interpreted. Further, any ‘tal:’ prefixed element has a default ‘tal:omit-tag’ like behaviour.

If you want to override this ordering, you can use ‘tal:’ prefixed elements.

Example:

<tal:formfields repeat="field python:form.get_fields()">
<tal:fieldid define="fieldId python:field.getId()">
  <tal:m_formaterrorbyfield
      metal:use-macro="here/PT_macros/macros/formatErrorByField"
      condition="python:errorMap.has_key(fieldId)">
      <p class="errorMessage"
         tal:content="python:errorMap[fieldId].error_text"></p>
  </tal:m_formaterrorbyfield>
</tal:fieldid>
</tal:formfields>

Tobias Sun Feb 17 15:57:03 +0000 2008

The text says, “It makes no sense to replace attributes and then throw them away, so attributes is last”; but in the list, “omit-tag” appears after “attributes”. Wouldn’t it make sense to note “omit-tag” before “attributes” (after define and repeat, possibly conflicting with replace)? If the omit condition is true, there wouldn’t be any tag which could bear the attributes, right? (it might perfectly make sense to have attributes when the tag is not omitted).

FredDrake Fri May 7 08:59:51 -0400 2010

This is a bug in the specification above (it’s inconsistent):

https://bugs.launchpad.net/zope.tal/+bug/430662

A new version of the spec needs to be created and the implementations can be adjusted to suite should they determine that the new spec is acceptable. Some implementations may already support the recommended change.

TALES Specification Version 1.0

The Template Attribute Language Expression Syntax describes expressions that may be used to supply Template Attribute Language and Macro Expansion for TAL (or METAL) with data. TALES is one possible expression syntax for these languages, but they are not bound to this definition. Similarly, TALES could be used in a context having nothing to do with Template Attribute Language or Macro Expansion for TAL (or METAL).

TALES expressions are described below with any delimiter or quote markup from higher language layers removed, since this is how expression strings must be passed to the TALES engine. Here is the basic definition of TALES syntax:

Expression  ::= [type_prefix ':'] String
type_prefix ::= Name

See :doc:`EBNF` for rules and terminals. Here are some simple examples:

a/b/c
path:a/b/c
nothing
path:nothing
python: 1 + 2
string:Hello, ${username}

The optional type prefix determines the semantics and syntax of the expression string that follows it. A given implementation of TALES can define any number of expression types, with whatever syntax you like. It also determines which expression type is indicated by omitting the prefix.

Several expression types are required:

Required Type Prefixes

  • not - evaluate the expression string (recursively) as a full expression, and returns the boolean negation of its value. If the expression supplied does not evaluate to a boolean value, not will issue a warning and coerce the expression’s value into a boolean type based on the following rules:

    1. integer 0 is false
    2. integer > 0 is true
    3. an empty string or other sequence is false
    4. a non-empty string or other sequence is true
    5. a non-value (e.g. void, None, Nil, NULL, etc) is false
    6. all other values are implementation-dependent.

    If no expression string is supplied, an error should be generated.

  • path - interpret the expression string as the path to some object. The syntax and semantics of paths warrant their own section and thus are described below (see Path Expressions) If no expression string is supplied, the result is interpreted as the value nothing.

  • string - interpret the expression string as text. If no expression string is supplied the resulting string is empty. The string can contain variable substitutions of the form ‘$name’ or ‘${name}’, where ‘name’ is an expression of type ‘path’. The escaped string value of the path expression is inserted into the string. To prevent a ‘$’ from being interpreted this way, it must be escaped as ‘$$’.

    Syntax:

    string_expression ::= ( plain_string | [varsub] )*
    varsub            ::= ( '$' Path ) | ( '${' Path '}' )
    plain_string      ::= ( '$$' | non_dollar )*
    non_dollar        ::= any character except '$'
    

Optional Type Prefixes

  • python - interpret the expression string as restricted Python code. This code must be a legitimate python expression.
  • nocall - interpret the expression string as a path expression, but do not attempt to call the result. This is used for returning an object rather than a value, such as in TAL “define” commands where you may want to defer evaluation. For example, if the path ‘modules/util/counter’ evaluates to an iterator that produces the numbers from one to infinity in sequence when called, then ‘tal:define=”ctr nocall:modules/util/counter”’ creates such an iterator, while ‘tal:define=”ctr modules/util/counter”’ simply evaluates to one.
  • exists - interpret the expression string as a path expression, but return a boolean value indicating whether the path can be resolved successfully.

Path Expressions

A path expression consists of one or more non-empty strings separated by slashes. The first string is a variable name, and the remaining strings, the path segments, may contain letters, digits, spaces, and the punctuation characters underscore, dash, period, comma, and tilde.

Here is the syntax:

Path     ::= variable [ '/' URL_Segment ]*
variable ::= Name

For example:

request/cookies/oatmeal
template
nothing
here/some-file 2001_02.html.tar.gz/foo
root/to/branch

When a TALES path expression is evaluated, it first looks up the variable name. If there are path segments, it traverses from the current object (starting with the variable value) to the next object using each path segment in turn. If the resulting object is callable, it is called. The semantics of traversal (and what it means to be callable) are implementation-dependent.

Since every path must start with a variable name, you need a set of starting variables that you can use to find other objects and values. PresentationTemplates define the variable names listed below. Since variable names are looked up first in locals, then in globals, then in this list, these names act just like builtins in Python; They are always available, but they can be shadowed by a global or local variable declaration. You can always access the builtin names explicitely by prefixing them with standard. (e.g. standard/root, standard/standard, etc).

Builtin Names in Presentation Templates

  • nothing - special singleton value used by TAL to represent a non-value (e.g. void, None, Nil, NULL).
  • options - the keyword arguments passed to the template.
  • repeat - the ‘repeat’ variables (see Template Attribute Language).
  • standard - the list of standard names (this list).

Optional Names in Presentation Templates

  • root - the system’s top-most object.
  • here - the object to which the template is being applied.
  • container - the template’s container object.
  • template - the template itself.
  • request - the publishing request object.

TALES Specification Version 1.2

This specification supercedes TALES Specification Version 1.0. It incorporates Module Access In TALES, Act Only If Path Exists, and Path Expression Variants.

The Template Attribute Language Expression Syntax describes expressions that may be used to supply Template Attribute Language and Macro Expansion for TAL (or METAL) with data. TALES is one possible expression syntax for these languages, but they are not bound to this definition. Similarly, TALES could be used in a context having nothing to do with Template Attribute Language or Macro Expansion for TAL (or METAL).

TALES expressions are described below with any delimiter or quote markup from higher language layers removed, since this is how expression strings must be passed to the TALES engine. Here is the basic definition of TALES syntax:

Expression  ::= [type_prefix ':'] String
type_prefix ::= Name

See [EBNF] for rules and terminals. Here are some simple examples:

a/b/c
path:a/b/c
nothing
path:nothing
python: 1 + 2
string:Hello, ${username}

The optional type prefix determines the semantics and syntax of the expression string that follows it. A given implementation of TALES can define any number of expression types, with whatever syntax you like. It also determines which expression type is indicated by omitting the prefix.

Several expression types are required:

Required Type Prefixes

  • not - evaluate the expression string (recursively) as a full expression, and returns the boolean negation of its value. If the expression supplied does not evaluate to a boolean value, not will issue a warning and coerce the expression’s value into a boolean type based on the following rules:

    1. integer 0 is false
    2. integer > 0 is true
    3. an empty string or other sequence is false
    4. a non-empty string or other sequence is true
    5. a non-value (e.g. void, None, Nil, NULL, etc) is false
    6. all other values are implementation-dependent.

    If no expression string is supplied, an error should be generated.

  • path - interpret the expression string as the path to some object. The syntax and semantics of paths warrant their own section and thus are described below (see Path Expressions) If no expression string is supplied, the result is interpreted as the value nothing.

  • string - interpret the expression string as text. If no expression string is supplied the resulting string is empty. The string can contain variable substitutions of the form ‘$name’ or ‘${name}’, where ‘name’ is an expression of type ‘path’. The escaped string value of the path expression is inserted into the string. To prevent a ‘$’ from being interpreted this way, it must be escaped as ‘$$’.

    Syntax:

    string_expression ::= ( plain_string | [varsub] )*
    varsub            ::= ( '$' Path ) | ( '${' Path '}' )
    plain_string      ::= ( '$$' | non_dollar )*
    non_dollar        ::= any character except '$'
    

Optional Type Prefixes

  • python - interpret the expression string as restricted Python code. This code must be a legitimate python expression.

Path Expressions

A path expression consists of a path, preceded by an optional set of path modifiers in parenthesis. A path consists of one or more non-empty strings separated by slashes. The first string must be a variable name, and the remaining strings, the path segments, may contain letters, digits, spaces, and the punctuation characters underscore, dash, period, comma, and tilde.

The path modifiers may include ‘if’, ‘exists’, and ‘nocall’ in any combination.

Here is the syntax:

Path      ::= [ modifiers ] variable [ '/' URL_Segment ]*
modifiers ::= '(' [ 'if' | 'exists' | 'nocall' ]* ')'
variable  ::= Name

For example:

request/cookies/oatmeal
template
nothing
(exists) here/some-file 2001_02.html.tar.gz/foo
(if nocall) root/to/branch

When a TALES path expression is evaluated, it first looks up the variable name. If there are path segments, it traverses from the current object (starting with the variable value) to the next object using each path segment in turn. If the resulting object is callable, and the path modifier ‘nocall’ was not used, the object is called. The semantics of traversal (and what it means to be callable) are implementation-dependent.

If a traversal step fails, a traversal error is raised unless the ‘exists’ modifier is used. When a path expression has both the ‘if’ and ‘exists’ modifier, as in ‘(if exists) a/b/c’, the only effect is not cancel the action (see Template Attribute Language) in case of a traversal error. Used by itself, ‘exists’ causes the expression to evaluate to ‘1’ if traversal succeeds, or ‘0’ if it fails. The ‘if’ modifier can also be used alone, in which case it does not handle traversal errors, but cancels the action if the path value is false.

Since every path must start with a variable name, you need a set of starting variables that you can use to find other objects and values. PresentationTemplates define the variable names listed below. Since variable names are looked up first in locals, then in globals, then in this list, these names act just like builtins in Python; They are always available, but they can be shadowed by a global or local variable declaration. You can always access the builtin names explicitely by prefixing them with standard. (e.g. standard/root, standard/standard, etc).

Builtin Names in Presentation Templates

  • nothing - special singleton value used by TAL to represent a non-value (e.g. void, None, Nil, NULL).
  • options - the keyword arguments passed to the template.
  • repeat - the ‘repeat’ variables (see Template Attribute Language).
  • standard - the list of standard names (this list).

Optional Names in Presentation Templates

  • root - the system’s top-most object.
  • here - the object to which the template is being applied.
  • container - the template’s container object.
  • template - the template itself.
  • request - the publishing request object.
  • modules - a collection of modules providing additional functions.

TALES Specification Version 1.3

This specification supercedes TALES Specification Version 1.2.

The Template Attribute Language Expression Syntax describes expressions that may be used to supply Template Attribute Language and Macro Expansion for TAL (or METAL) with data. TALES is one possible expression syntax for these languages, but they are not bound to this definition. Similarly, TALES could be used in a context having nothing to do with Template Attribute Language or Macro Expansion for TAL (or METAL).

TALES expressions are described below with any delimiter or quote markup from higher language layers removed, since this is how expression strings must be passed to the TALES engine. Here is the basic definition of TALES syntax:

Expression  ::= [type_prefix ':'] String
type_prefix ::= Name

See :doc:`EBNF` for rules and terminals.

Here are some simple examples:

a/b/c
path:a/b/c
nothing
path:nothing
python: 1 + 2
string:Hello, ${username}

The optional type prefix determines the semantics and syntax of the expression string that follows it. A given implementation of TALES can define any number of expression types, with whatever syntax you like. It also determines which expression type is indicated by omitting the prefix.

Several expression types are required:

Required Type Prefixes

  • not - evaluate the expression string (recursively) as a full expression, and returns the boolean negation of its value. If the expression supplied does not evaluate to a boolean value, not will issue a warning and coerce the expression’s value into a boolean type based on the following rules:

    1. integer 0 is false
    2. integer > 0 is true
    3. an empty string or other sequence is false
    4. a non-empty string or other sequence is true
    5. a non-value (e.g. void, None, Nil, NULL, etc) is false
    6. all other values are implementation-dependent.

    If no expression string is supplied, an error should be generated.

  • path - interpret the expression string as the path to some object. The syntax and semantics of paths warrant their own section and thus are described below (see [Path Expressions]) If no expression string is supplied, the result is interpreted as the value nothing.

  • string - interpret the expression string as text. If no expression string is supplied the resulting string is empty. The string can contain variable substitutions of the form ‘$name’ or ‘${name}’, where ‘name’ is an expression of type ‘path’. The escaped string value of the path expression is inserted into the string. To prevent a ‘$’ from being interpreted this way, it must be escaped as ‘$$’. Note that leading and trailing spaces in the expression are included in the value.

    Syntax:

    string_expression ::= ( plain_string | [ varsub ] )*
    varsub            ::= ( '$' Path ) | ( '${' Path '}' )
    plain_string      ::= ( '$$' | non_dollar )*
    non_dollar        ::= any character except '$'
    

Optional Type Prefixes

  • python - interpret the expression string as restricted Python code. This code must be a legitimate python expression.

Path Expressions

A path expression consists of one or more paths separated by vertical bars (|). A path consists of one or more non-empty strings separated by slashes. The first string must be a variable name, and the remaining strings, the path segments, may contain letters, digits, spaces, and the punctuation characters underscore, dash, period, comma, and tilde.

Here is the syntax:

PathExpr  ::= Path [ '|' Path ]*
Path      ::= variable [ '/' URL_Segment ]*
variable  ::= Name

For example:

request/cookies/oatmeal
nothing
here/some-file 2001_02.html.tar.gz/foo
root/to/branch | default

When a TALES path expression is evaluated, it attempts to traverse each path, from left to right, until it succeeds or runs out of paths. To traverse a path, it first fetches the object stored in the variable. For each path segment, it traverses from the current object to the subobject named by the path segment.

Once a path has been successfully traversed, the resulting object is the value of the expression. If it is a callable object, such as a method or class, it is called. The semantics of traversal (and what it means to be callable) are implementation-dependent.

If a traversal step fails, evaluation immediately proceeds to the next path. If there are no further paths, an error results.

Since every path must start with a variable name, you need a set of starting variables that you can use to find other objects and values. PageTemplates define the variable names listed below. Since variable names are looked up first in locals, then in globals, then in this list, these names act just like builtins in Python; They are always available, but they can be shadowed by a global or local variable declaration. You can always access the builtin names explicitely by prefixing them with CONTEXTS. (e.g. CONTEXTS/root, CONTEXTS/nothing, etc).

Builtin Names in Page Templates

  • nothing - special singleton value used by TAL to represent a non-value (e.g. void, None, Nil, NULL).
  • default - special singleton value used by TAL to specify that existing text should not be replaced.
  • options - the keyword arguments passed to the template.
  • repeat - the ‘repeat’ variables (see RepeatVariable).
  • attrs - a dictionary containing the initial values of the attributes of the current statement tag.
  • CONTEXTS - the list of standard names (this list). This can be used to access a builtin variable that has been hidden by a local or global variable with the same name.

Optional Names in Page Templates (used in Zope Page Templates)

  • root - the system’s top-most object.
  • here - the object to which the template is being applied.
  • container - the template’s container object.
  • template - the template itself.
  • request - the publishing request object.
  • user - the authenticated user object.
  • modules - a collection through which all Python modules and packages can be accessed. Some or many of these may not be usable in TALES, however, depending on the security policies of the template’s implementation.

Comments

jschinnerer (Nov 2, 2001 3:59 am; Comment #2) –

Relative Paths

Deceptively simple but not explicitly spelled out in the Path Expressions section. A very common filesystem command form doesn’t work in ZPT paths and confused me for a bit. Key phrase is this one:

Since every path must start with a variable name, …

so this works (to go up a node, then out another branch):

‘container/../down_one/got_it’

but this does not:

‘../down_one/got-it’

hathawsh (Apr 19, 2002 3:24 pm; Comment #3) –

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 associated with ‘myvar’. For example, if ‘myvar’ is set to “title”, ‘here/?myvar’ is equivalent to ‘here/title’.

bwarsaw (Jun 3, 2002 1:29 pm; Comment #4) –

  1. integer 0 is false
  2. integer > 0 is true

what about integers < 0? :)

chrisw (Jul 18, 2002 3:04 am; Comment #5) –
where is the ‘structure’ prefix documented? I reckon it should probably be in here…
From djmaze Sun Nov 8 17:45:59 -0500 2009

“path segments, may contain letters, digits, spaces, and the punctuation characters underscore, dash, period, comma, and tilde.” Which dash are you talking about? http://en.wikipedia.org/wiki/Dash

Or are you talking about Hyphen-minus (U+002D)?

From FredDrake Mon Nov 9 07:33:48 -0500 2009
The hyphen-minus, which is what most programmers refer to as the dash.

Historical Change Log for TAL, Page Templates, and ZTUtils

2001-08-13: PageTemplates and TAL 1.4.0, ZTUtils 1.1.0

  • ZPTs are now cache-enabled

  • Added property sheet to ZPT

  • Added TAL statement: omit_tag="[<boolean expr>]" replaces the statement tag with its contents if the boolean expression is true or omitted.

  • The TAL and METAL namespaces can be applied to tag names, tags in these namespaces are removed from rendered output (leaving the contents in place, as with omit_tag) whenever attributes in these namespaces would be, and tag attributes without explicit namespaces default to the tag’s namespace (per XML spec).

  • TreeMakers have a setChildAccess() method that you can use to control tree construction. Child nodes can be accessed through either an attribute name or callback function. Children fetched by attribute name can be filtered through a callback function.

  • A new LazyFilter class allows you to filter a sequence using Zope security and an optional filter callback function. The security and filter tests are lazy, meaning they are performed as late as possible.

    The optional ‘skip’ argument determines the reaction when access to a sequence element is refused by the Zope security policy. The default (None) is to raise the ‘Unauthorized’ exception. If a string is passed, such elements are skipped. If the string is non-empty, it is treated as a permission name, and the element is skipped if the user doesn’t have that permission on the element.

  • The Zope versions of TreeMaker, SimpleTreeMaker, and Batch now use LazyFilter. The TreeMakers have a setSkip() method that can be used to set the ‘skip’ value. Batch has an optional ‘skip_unauthorized’ argument that is passed to LazyFilter as ‘skip’.

  • Utility functions make_query(), url_query(), and make_hidden_input() have been added.

  • FIXED: Expressions with embedded newlines were broken

  • FIXED: History comparison tried to expand macros

  • FIXED: Iterator exceptions weren’t converted

  • FIXED: ‘Unauthorized’ exception couldn’t be handled by on-error

2001-07-18: PageTemplates and TAL 1.3.3

  • Allow any false value in tal:repeat to act as an empty sequence.
  • manage_addPageTemplate accepts optional title and text arguments, and returns the new object if REQUEST is None.
  • FIXED: The various Python modules failed to import CompilerError.
  • FIXED: Security machinery changes in Zope 2.4 broke ZRPythonExpr
  • FIXED: tal:atributes was creating stray attributes in METAL expansion, and there was no unit test for this behavior.
  • FIXED: tal:attributes parsing was not catching badly malformed values, and used “print” instead of raising exceptions.

2001-05-25: PageTemplates 1.3.1

  • Added error logging to PageTemplateFiles.
  • Refactored PythonExpr, and added support for Zope 2.4

2001-05-21: TAL and PageTemplates 1.3.0

  • Guido fixed use of nested macros.
  • New builtin variables default, user, and attrs.
  • Removed path modifiers.
  • Added | operator for paths.
  • Tweaked parameters passed when calling DTML.
  • Expression types now have corresponding builtin functions in Python expressions.

2001-05-07: TAL and PageTemplates 1.2.1

  • Bugfixes for ‘repeat’ variable access

2001-04-27: TAL and PageTemplates 1.2.0, ZTUtils 1.0.0

  • Depends on the new ZTUtils package, which adds batching and tree widget capabilities.
  • Path expressions now have optional path modifiers. These appear in parenthesis before the path, and include if, exists, and nocall.
  • Changed nocall: and exists: expressions types into path modifiers.
  • tal:attributes no longer inserts empty attributes into source.
  • The if path modifier can cancel any TAL action.

2001-04-10: TAL and PageTemplates 1.1.0

  • Bug fixes
  • Tell TAL not to parse replacement structural text
  • Change tests to match TAL’s omitted attributes

2001-03-30: TAL and PageTemplates 1.0.0

  • Minor bugs fixed

2001-03-27: TAL and PageTemplates 1.0.0b1

  • All functionality described in the Project Wiki is implemented

Indices and tables