LJ Archive

Zope Page Templates

Reuven M. Lerner

Issue #98, June 2002

Discover how ZPT attributes can work with HTML to create useful and compatible dynamic web sites.

In the early days of the Web, nearly everyone working on a web site was a programmer of some sort. You could be sure that every webmaster had installed his or her own web server, knew how to hand-code HTML and could write rudimentary CGI programs on their own.

Over time many editors, designers and other nonprogrammers became involved in the creation of web sites. Although it was often possible to teach these people HTML, it was HTML that was only good enough for static web pages.

Dynamically generated pages, which were then created almost exclusively by CGI programs, are another story altogether. After all, if the designer wants to change a site's background color on a static site, then he or she can simply modify the appropriate HTML files (or site-wide stylesheet, in today's world). But if such designs sit within a CGI program, the designer then must ask a programmer to make that change. This situation is bad for everyone; the designer cannot easily experiment with new ideas, the programmer is forced to make small and annoying changes and other programming work is put on hold while the programmer makes the changes.

For a number of years, the mainstream solution to this problem has been the use of templates, which mix HTML and a programming language. Perhaps the best-known commercial implementation of such templates is Microsoft's Active Server Pages (ASP), but Sun's JavaServer Pages (JSPs) is also quite popular. Open-source software developers have produced many high-quality template implementations of their own, including HTML::Mason (which works best with mod_perl), PHP (a programming language used for web templates) and ADP (available with AOLServer).

The idea behind such templates is quite simple: everything is assumed to be static HTML, except what is placed within a special set of brackets. When working with such templates, the designer is basically told, “You can modify everything that doesn't appear inside of <% and %>.” And indeed, this often can work well.

But over time, the drawbacks of such templates become increasingly apparent. For starters, what happens when you want to loop through a number of items that you have retrieved from a database, displaying each item in a different background color depending on its content? In such a case, you cannot ask the designer to ignore the code because the code and HTML are so intertwined.

Zope, the application server that we have been looking at the last few months, tried to solve this problem using something they call DTML (Dynamic Template Markup Language). As we saw several months ago, DTML is a programming language with an HTML-like syntax that allows developers and designers alike to create dynamic pages. DTML is powerful, flexible and easy to understand (at least if you're a programmer).

But if you're not a programmer, then even DTML can be difficult to understand, particularly when it is retrieving results from a database (as we saw last month). Moreover, HTML editors don't know what to do with the DTML tags, which can lead to the inadvertent mangling of DTML pages.

For all of these reasons, Zope Corporation (formerly Digital Creations), which develops the open-source Zope application server, is now encouraging developers to look at Zope Page Templates (ZPT), a new approach to templates meant to solve most of these problems. This month we examine ZPT, looking at ways in which we can use it to create dynamic sites.

What Is ZPT?

ZPT takes advantage of three facts: HTML can be expressed in XML syntax, often known as XHTML; XML allows you to mix tags and attributes from different document definitions by using separate namespaces; and WYSIWYG HTML editors generally ignore (but preserve) attributes they do not recognize.

ZPT thus modifies the core HTML syntax by adding new attributes that sit in a separate namespace. Because these attributes are in a separate namespace, they are legal XML and XHTML. Because they are attributes and not tags, most HTML editors will ignore them, rendering the tag as if the attribute did not exist. And because web browsers ignore attributes that they do not understand, they can render ZPT pages without having to go through Zope.

In other words, ZPT makes it possible for a programmer to mock up a page so that a designer can then edit and view it using any tools they like. Of course, the dynamic elements of the template go into effect only when the template is viewed using Zope.

In HTML, each attribute has a name and a value, e.g., in <a href="http://www.lerner.co.il/">Reuven's site</a>, the attribute href had the following value: http://www.lerner.co.il/. The name and value are separated by an equal sign (=), and the value sits inside of double quotation marks. None of this changes when we work with ZPT, except that the attribute name is defined by TAL (Template Attribute Language). TAL defines a number of different possible attributes names, each of which directs Zope to modify the template in a different way when it is displayed.

TAL defines the attribute names, but what defines the attribute values? For that, we use TALES (TAL Expression Syntax). TALES defines a number of sources for data, including Python expressions and values from the surrounding Python namespace.

The combination of an attribute name from TAL (which tells Zope how to handle the surrounding tag) and an attribute value from TALES (which tells Zope what value to use in this TAL expression) gives us amazing flexibility in building page templates. In addition, the fact that TAL and TALES are published and have open-source specifications means that you can add to them if you have specific needs that they don't cover.

Some Simple Examples

Now that we've discussed the theory behind ZPT, let's look at some examples. The first example is the skeleton document that Zope creates when we start a new page template. To do this, go to the /manage URL on your Zope server, and choose page template from the Add product list in the upper right-hand corner. (If you don't see page template on the menu, it could be that ZPT is not an installed product. You may have to upgrade your copy of Zope to a more modern version in order to use ZPT.) As is usual in the Zope world, you will then be asked to enter an ID (i.e., a unique character string that will appear in the URL) for this new product. Enter a short name and click “Add and edit”.

When you follow these instructions for a DTML document or method, you are brought to an editing screen that contains skeleton DTML. The same is true for a page template, except that the skeleton document obviously contains TAL and TALES expressions:

<html>
  <head>
     <title tal:content="template/title">
      The title</title>
  </head>
  <body>
     <h2><span tal:replace="here/title_or_id">
          content title or id</span>
         <span tal:condition="template/title"
               tal:replace="template/title">
               optional template id</span></h2>
     This is Page Template
     <em tal:content="template/id">template id</em>.
  </body>
</html>

This document might be short, but it effectively displays TAL and TALES and how they might be used in actual documents. Here are the TAL attributes that this skeleton uses:

  • The tal:content attribute replaces the contents of the tag with the value of the TALES expression. So in this example document, the words “The title” will be replaced by the value of the TALES expression template/title, which we will discuss below. The <title> and </title> tags, along with their attributes, are left alone, but the text between these tags is changed when the template is presented by Zope.

  • The tal:replace attribute is similar to tal:content, except that it replaces the content and the surrounding tags. This is frequently used with the <span> tag, which is largely used as a placeholder for such markup in any event. Thus everything from the first <span> tag to the first </span> tag, including the tags themselves, will be replaced by the value of the TALES expression here/title_or_id.

  • The tal:condition attribute only displays its contents if its value is a true value, which is anything other than 0, the empty string, an empty list or the built-in ZPT “nothing” variable.

You might notice that the second <span> tag contains two TAL attributes, tal:condition and tal:replace. When Zope encounters multiple TAL attributes in a given tag, it first evaluates definitions, then conditions, then repeat loops, then content and replace tags. (You cannot have both content and replace attributes in the same tag, because they are mutually exclusive.) In the second <span> tag, Zope inserts the optional template ID only if the template/title TALES expression is true.

In addition to content, replace and condition, TAL defines three other attributes:

  • tal:repeat allows us to loop over a list of items. If your template will display a list of search results, rows from a database or files in a folder, then you can loop over the contents and display the contents in a variety of ways.

  • tal:attributes allows us to replace (or add to) an attribute of the enclosing tag. For example, we can set the href attribute of an anchor (<a>) tag at runtime by adding a tal:attributes attribute in that <a> tag. The value of the TALES expression will be used to set that attribute when it is evaluated at runtime.

  • tal:define allows you to set a new variable, which is then accessible from within enclosed tags. This variable value can be passed either along to other TAL tags or displayed.

TALES

Now that we have covered TAL, let's look at the TALES expressions that we can assign to a TAL attribute.

The simplest sort of TALES expression is a “path expression”, which describes a Zope object relative to the template, its container or the request. Path expressions are similar to URLs, except that they begin with a name rather than the root object /. The final element of the path expression typically will be a property that can be displayed (using tal:content), tested (using tal:condition) or iterated over (using tal:repeat).

The ZPT skeleton document uses four TALES path expressions:

  • here/title_or_id, which returns the value of the optional “title” property (if it exists) or the mandatory “id” property (if there is no title) to a tal:replace attribute.

  • template/title, which returns the (optional) title of the current template to tal:condition followed by tal:replace.

  • template/id, which returns the “id” property of the current template.

A TALES path expression that begins with “template” refers to the template object itself, while one that begins with “here” refers to the object to which the template is being applied—it may be the template itself or another object entirely. Some other relative markers are “request” (the Zope HTTP request object), “repeat” (information about current tal:repeat loops), “options” (for the parameters passed to the template) and “container” (for the folder of the current object).

TALES path expressions are great but are not as flexible as we might sometimes need. TALES thus allows you to return Python code by beginning your TALES expression with “python:”. For example, we can include the result of a simple calculation in a template with the following code:

<p>2 + 2 = <span tal:replace="python:2+2">
            number</span></p>

There is also a “string:” TALES prefix that indicates that the value should be treated as a text string. String expressions may contain interpolated variable values by prefixing the variable name with $, as in Perl and shell scripts. You may optionally surround the variable name with curly braces ({ and }), as in ${x}.

Standard Look and Feel

What we have seen so far is very nice for dynamic content generation. But one of the best things about DTML (or any other sophisticated server-side macro language) is the ability to define menus in one document, headers in a second and footers in a third, and then for each page to import them as necessary.

One way to handle this situation is to create three separate templates (menu, header and footer) in the current folder, importing them with TAL expressions such as:

<span tal:replace="container/menu">
 menu goes here</span>

TALES looks at the current template's container, retrieves the menu object (which happens to be a page template itself) and inserts its contents into the current document in place of the <span> tag.

Another way to approach DTML's flexibility is with the use of macros. Macros are common in many programming languages and allow us to create functionality that expands at runtime. The ZPT macro language is called METAL, and like TAL and TALES, it is defined and invoked within HTML attributes, placed in the “metal:” XML namespace. METAL macros can define “slots”, or parameters, into which parameter values can be bound. It's easy to imagine how you could create a macro that handles the overall site design, with each document fitting into the slot that this macro provides. Changing the macro definition would effectively change the design of the entire site.

Conclusion

When I first heard about ZPT, I was sure that it was yet another new way to create templates that are incompatible with other techniques and technologies. But over time, I have become convinced that ZPT is indeed a clever and elegant idea, and one that offers advantages to developers and designers alike. Although it is not a complete replacement for DTML, I believe that most of my DTML usage can now be replaced by a combination of TAL, TALES and METAL. I look forward to seeing how these technologies improve over time and how they are integrated more fully into Zope in the coming months and years.

Resources

email: reuven@lerner.co.il

Reuven M. Lerner is a consultant specializing in web/database applications and open-source software. His book, Core Perl, was published in January by Prentice Hall. Reuven lives in Modi'in, Israel, with his wife and daughter.

LJ Archive