Using Zope Page Templates

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. In this chapter, you'll learn the basics features of Page Templates, including how you can use them in your web site to create dynamic web pages easily. In the chapter 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 a valid HTML page.

Zope Page Templates versus DTML

Zope already has DTML, so you may wonder why we need another template language. First of all, DTML is not aimed at HTML designers. Once an HTML page has been "dynamicized" by inserting DTML into it, the resulting page typically becomes invalid HTML, making it difficult to work with outside Zope. Secondly, DTML suffers from a failure to separate presentation, logic, and content (data). This decreases the scalability of content management and website development efforts that use these systems. Finally, DTML's namespace model adds too much "magic" to object lookup, without allowing enough control.

DTML can do things that Page Templates can't, such as dynamically generate email messages (Page Templates can only generate HTML and XML), so DTML is not a "dead end". However, it is probable that Page Templates will be used for almost all HTML/XML presentation by Zope Corporation and many members of the Zope community.

How Page Templates Work

Page Templates use the Template Attribute Language (TAL). TAL 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 text contained by the title tag, and the value "here/title" is an expression providing the text to insert into the tag.

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 Appendix C, "Zope Page Templates Reference", for more information on TAL.

To the HTML designer using a WYSIWYG tool, the dynamic title example is perfectly valid HTML, and shows up in their editor looking like a title 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 title text will act as a placeholder for the dynamic title text. The template provides an example of how generated documents will look.

When this template is saved in Zope and viewed by a user, Zope turns the dummy content into dynamic content, replacing "Page Title" with whatever "here/title" resolves to. In this case, "here/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.

Creating a Page Template

If you design pages, you will probably use FTP or WebDAV instead of the Zope Management Interface (ZMI) to edit Page Templates. See the later section in this chapter named "Remote Editing With FTP and WebDAV" for information on editing Page Templates remotely. For the small examples in this chapter, it is easier to use the ZMI.

Use your web browser to log into the Zope Management Interface as a manager. Create a Folder to work in named "template_test" in the root of your Zope. Visit this folder and choose "Page Template" from Zope's add list (do NOT choose DTML Method or DTML Document, the following examples only work inside a Page Template). 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 let's create a simple dynamic page. Type the words "a Simple Page" in the Title field. Then, edit the template text to look like this:

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

 

Now push the Save Changes button. Zope should show a message confirming that your changes have been saved.

If an HTML comment starting with Page Template Diagnostics is added to the template text, then check to make sure you typed the example correctly and save it again. This comment is an error message telling you that something is wrong. 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 page with, "This is a Simple Page." at the top. Notice that the text is plain; nothing is in bold. This is because the tal:replace statement replaces the entire tag.

Back up, then click on the Browse HTML source link under the content-type field. This will show you the unrendered source of the template. You should see, "This is the Title." The bold text acts as a placeholder for the dynamic title text. Back up again, so that you are ready to edit the example further.

The Content-Type field allows you to specify the content type of your page. Generally you'll use a content type of text/html HTML or text/xml for XML.

If you set the content-type to text/html then Zope parses your template using HTML compatibility mode which allows HTML's "loose" markup. In this mode, it's possible to enter "non-well-formed" HTML into a Page Template. However, if you set your content-type to something other than text/html then Zope assumes that your template is well formed XML. Zope 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">

 

For our purposes, we want to emit "loose" HTML, so we leave the Content-Type form field as text/html and we do not use any XML namespace declarations.

The Expand macros with editing control is explained in the chapter entitled Advanced Page Templates.

Simple Expressions

The expression, "template/title" in your simple Page Template is a path expression. This 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 the Zope Page Templates Reference Appendix.

The "template/title" path expression fetches the title property of the template. Here are some other common path expressions:

  • request/URL: The URL of the current web request.
  • user/getUserName: The authenticated user's login name.
  • 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.

Zope defines a small set of built-in variables such as request and user, which are described in the chapter entitled Advanced Page Templates. You will also learn how to define your own variables in that chapter.

Inserting Text

In your "simple_page" template, you used the tal:replace statement on a bold tag. When you tested it, Zope 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 rather than on a bold tag. For example, add the following lines to your example:

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

 

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 want to insert text into a tag but leave the tag itself alone, you use the tal:content statement. To set the title of your example page to the template's title property, add the following lines between the html and the body tags:

      <head>        <title tal:content="template/title">The Title</title>      </head>

 

If you open the "Test" tab in a new browser window, the window's title will be "a Simple Page". If you view the source of the page you'll see something like this:

      <html>        <head>          <title>a Simple Page</title>        </head>      ...

 

Zope inserted the title of your template into the title tag.

Repeating Structures

Now let's add some context to your simple_page template, 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 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>Number</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/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 container'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. By placing it after the built-in variable 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 a tal:repeat loop 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. You must specify which loop you're interested in by including the loop name.

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 chapter 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. Zope considers 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!).

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. Add this to the end of your simple_page Page Template:

      <table tal:condition="container/objectValues"              border="1" width="100%">        <tr>          <th>Number</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/getId">Id</td>          <td tal:content="item/meta_type">Meta-Type</td>          <td tal:content="item/title">Title</td>        </tr>      </table>

 

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:

      Number          Id          Meta-Type          Title      1               simple_page Page Template      2               1           Folder      3               2           Folder      4               3           Folder

 

Note that if the expressions, container/objectValues is false (for instance if there are no objectValues), the entire table is omitted.

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. Edit the table cell in the meta-type column of the above example 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 img tag with the value of item/icon. The src="/misc_/OFSP/Folder_icon.gif" attribute in the template 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.

Creating a File Library with Page Templates

Here's an example of using Page Templates with Zope to create a simple file library with one template, a little bit of Python code, and some files.

First, create a "temporary" mock up of a file library page using an HTML "WYSIWYG" ("What You See Is What You Get") editor. Macromedia Dreamweaver, Adobe GoLive, and Netscape Composer are examples of WYSIWYG tools. While you are creating the mockup, just save it to a file on your hard disk.

This mock-up doesn't need to "overdo it", it just shows some dummy information. Here's a mock-up of a file library that contains one file:

      <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"                            "http://www.w3.org/TR/html4/loose.dtd">      <html>      <head>        <title>File Library</title>        <style type="text/css">        <!--        .header {          font-weight: bold;          font-family: helvetica;          background: #DDDDDD;        }        h1 {          font-family: helvetica;        }        .filename {          font-family: courier        }        -->        </style>        <meta name="GENERATOR" content="amaya 5.1">      </head>      <body>      <h1>File Library</h1>      <p>Click on a file below to download it.</p>      <table border="1" cellpadding="5" cellspacing="0">        <tbody>          <tr>            <td class="header">Name</td>            <td class="header">Type</td>            <td class="header">Size</td>            <td class="header">Last Modified</td>          </tr>          <tr>            <td><a href="Sample.tgz" class="filename">Sample.tgz</a></td>            <td>application/x-gzip-compressed</td>            <td>22 K</td>            <td>2001/09/17</td>          </tr>        </tbody>      </table>      </body>      </html>

 

Now, log into your Zope's management interface with your browser and create a folder called FileLib. In this folder, create a Page Template called index_html by selecting Page Template from the add menu, specifying the Id index_html in the form, and clicking Add. For information on creating a new Page Template via an external tool (as opposed to creating one in the ZMI and editing it afterwards with an external tool), see PUT_factory in the chapter entitled Using External Tools.

Now, with your HTML editor, using FTP, WebDAV (via the DAV "source port"), or HTTP PUT, save the above HTML to the URL of the index_html Page Template. For example, you may save to the URL http://localhost:8080/FileLib/index_html. Different editors have different mechanisms that you can use to do this. See the chapter entitled 'Using External Tools With Zope':ExternalTools.stx for more information on using WebDAV, FTP and HTTP PUT to communicate with Zope.

*NOTE: If you're trying to save to Zope via an editor like Netscape Composer or Amaya via HTTP PUT (as opposed to FTP or DAV), but you're having problems, try saving the file to http://localhost:8080/FileLib/index_html/source.html instead of the URL specified above. Appending /source.html to the Zope object name is a "hack" which Page Templates support to get around the fact that HTTP PUT attempts to render* the page before doing the PUT, but we actually just want to save the unrendered source. If you're creating an XML file, the "magic" hack-around name is /source.xml instead of /source.html.**

Now that you've saved the template, you can go back to Zope and click on index_html and then click on its Test tab to view the template. It looks just like it the mock-up, so everything is going well.

Now let's tweak the above HTML and add some dynamic magic. First, we want the title of the template to be dynamic. In Zope, you'll notice that the Page Template has a title form field that you can fill in. Instead of being static HTML, we want Zope to dynamically insert the Page Templates title into the rendered version of the template. Here's how:

      <head>        ...        <title tal:content="template/title">File Library</title>        ...      <body>      <h1 tal:content="template/title">File Library</h1>      ...

 

Now go to Zope and change the title of the index_html page template to "My File Library". After saving that change, click the Test tab. As you can see, the Page Template dynamically inserted the "My File Library" title of the template object in the output of the template.

Notice the new content tag attribute. This attribute says to "replace the content of this tag (the text between the h1 tags) with the variable 'template/title'". In this case, template/title is the title of the index_html Page Template.

The next bit of magic is to build a dynamic file list that shows you all the File objects in the FileLib folder.

To start, you need to write just one line of Python. Go to the FileLib folder and create a Script (Python) in that folder. Give the script the id files and click Add and Edit. Edit the script to contain the following Python code:

      ## Script (Python) "files"      ##       return container.objectValues(['File'])

 

This will return a list of any File objects in the FileLib folder. Now, edit your index_html Page Template and add some more tal attributes to your mock-up:

      ...      <tr tal:repeat="item container/files">        <td><a href="Sample.tgz" class="filename"               tal:attributes="href item/getId"               tal:content="item/getId">Sample.tgz</a></td>        <td tal:content="item/getContentType">application/x-gzip-compressed</td>        <td tal:content="item/getSize">22 K</td>        <td tal:content="item/bobobase_modification_time">2001/09/17</td>      </tr>      ...

 

The interesting part is the tal:repeat attribute on the tr HTML tag. This attribute tells the template to iterate over the values returned by "container/files", which is the Python script you created in the current folder (the "container"), which returns a list of Zope objects. The repeat tag causes Zope to create a new table row with columns representing a bit of metadata about each of those objects. During each iteration, the current file object being iterated over is assigned the name item.

The cells of each row all have tal:content attributes that describe the data that should go in each cell. During each iteration through the table row loop, the id, the content type, the size, and modification time replace the dummy data in the rows. Also notice how the anchor link dynamically points to the current file using tal:attributes to rewrite the href attribute.

This data comes from the item object by calling Zope API methods on what we know is a file object. The methods item/getId, item/getContentType, item/getSize, item/bobobase_modification_time are all standard API functions that are documented in Zope's online help system as well as in the various appendices to this book.

Go to Zope and test this script by first uploading some Files into the FileLib folder. This is done by selecting File from the add menu and clicking on the upload form button on the next screen. After uploading your file, you can just click Add. If you do not specify an id, then the filename of the file you are uploading will be used.

After uploading some files, go to the index_html Page Template and click the Test tab. Now, you can see the Page Template has rendered a very simple file library with just a few HTML tag attribute changes.

There are a few cosmetic problems with the file library as it stands. The size and date displays of the default content are very pretty, but the values returned from Zope don't match the format of the dummy content. Instead, they are "raw" numbers. You would like the size of the files to be displayed in K or MB rather than bytes. Here's a Python-based script that you can use for this:

      ## Script (Python) "file_size"      ##      """      Return a string describing the size of a file.      """      bytes=context.getSize()      k=bytes/1024.0      mb=bytes/1048576.0      if mb > 1:          return "%.2f MB" % mb      if k > 1:          return "%d K" % k      return "%d bytes" % bytes

 

Create this script with the Id file_size in your FileLib folder. It calculates a file's size in kilobytes and megabytes and returns an appropriate string describing the size of the file. Now you can use the script in place of the item/getSize expression:

      ...      <td tal:content="item/file_size">22 K</td>      ...

 

Replacing this bit of TAL in your file_size template causes Zope to call the 'file_size" script on each object, returning the file's size. When the script runs during the loop, the "context" of the script is "item", which is a File object. This is an example of Zope's acquisition in action, as the file_size script is actually a sibling of the items in the folder, although it can be used as a method of the items in the folder. The expression item/file_size translates to "find a method of the object item named file_size. If the object named file_size has no "real" method named file_size, use acquisition to find a file_size method." Zope finds the Script (Python) file_size script and use it as a method of the item object.

You can also fix the date formatting problems with a little Python. Create a script named file_date in your FileLib folder:

      ## Script (Python) "file_date"      ##      """      Return modification date as string YYYY/MM/DD      """      date=context.bobobase_modification_time()      return "%s/%s/%s" % (date.year(), date.month(), date.day())

 

Now replace the item/bobobase_modification_time expression with a reference to this script:

      ...      <td tal:content="item/file_date">2001/9/17</td>      ...

 

Congratulations, you've successfully taken a mock-up and turned it into a dynamic Page Template. This example illustrates how Page Templates work well as the "presentation layer" to your applications. The Page Templates present the application logic (the Python-based scripts) and the application logic works with the data in your site (the files).

Remote Editing with FTP and WebDAV

You can edit Page Templates remotely with FTP and WebDAV, as well as HTTP PUT publishing. Using these methods, you can use Page Templates without leaving advanced WYSIWYG editors such as Macromedia Dreamweaver.

The previous section showed you how to edit a page remotely using Amaya, which uses HTTP PUT to upload pages. You can do the same thing with FTP and WebDAV using the same steps.

  1. Create a Page Template in the Zope Management interface. You can name it with whatever file extension you wish. Many folks prefer .html, while others prefer .zpt. Note, some names such as index_html have special meanings to Zope.
  2. Edit your file with your editor and then save it. When you save it you should use the same URL you used to retrieve it.
  3. Optionally reload your page after you edit it, to check for error comments. See the next section for more details on debugging.

 

You can create new Page Templates without using the Zope Management Interface. See the PUT_factory section of the chapter entitled Using External Tools for more information.

Debugging and Testing

Zope helps you find and correct problems in your Page Templates. Zope notices problem 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 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. However, if you're using WebDAV or FTP it's easy to miss these messages. For example, if you save a template to Zope with FTP, you won't get an FTP error telling you about the problem. In fact, you'll have to reload the template from Zope to see the diagnostic message. When using FTP and WebDAV it's a good idea to reload templates after you edit them to make sure that they don't contain diagnostic messages.

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: Undefined      Error Value: "unicorn" not found in "here/unicorn"

 

This error message tells you that it cannot find the unicorn variable which is referenced in the expression, here/unicorn. 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 environment:

      ...      'here': <Application instance at 01736F78>,      'modules': <Products.PageTemplates.ZRPythonExpr._SecureModuleImporter instance at 016E77FC>,      'nothing': None,      'options': {'args': ()},      'request': ...      'root': <Application instance at 01736F78>,      'template': <ZopePageTemplate instance at 01732978>,      'traverse_subpath': [],      'user': amos})      ...

 

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 here 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 folder to which you uploaded the template hasn't such a property.

XML Templates

Another example of the flexibility of Page Templates is that they can dynamically render XML as well as HTML. For example, in a chapter within this book entitled Creating Basic Zope Applications, you create the following XML:

      <guestbook>        <entry>          <comments>My comments</comments>        </entry>        <entry>          <comments>I like your web page</comments>        </entry>        <entry>          <comments>Please no blink tags</comments>        </entry>      </guestbook>

 

This XML is created by looping over all the DTML Documents in a folder and inserting their source into comment elements. In this section, we'll show you how to use Page Templates to generate this same XML.

Create a new Page Template called "entries.xml" in your guest book folder with the following contents:

      <guestbook xmlns:tal="http://xml.zope.org/namespaces/tal">        <entry tal:repeat="entry python:here.objectValues('DTML Document')">          <comments tal:content="entry/document_src">Comment goes here...</comments>        </entry>      </guestbook>

 

Make sure you set the content type to text/xml. Now, click Save Changes and click the Test tab. If you're using Netscape, it will prompt you to download an XML document, if you are using MSIE 5 or higher, you will be able to view the XML document in the browser.

Notice how the tal:repeat statement loops over all the DTML Documents. The tal:content statement inserts the source of each document into the comments element. The xmlns:tal attribute is an XML namespace declaration. It tells Zope that names that start with tal are Page Template commands. See Appendix C, "Zope Page Templates Reference" for more information about TAL and TALES XML namespaces.

Creating XML with Page Templates is almost exactly like creating HTML. The most important difference is that you must use "explicit" XML namespace declarations in the template text itself. Another difference is that you should set the content type to text/xml or whatever the content-type for your XML should be. The final difference is that you can browse the source of an XML template by going to source.xml rather than source.html.

Using Templates with Content

In general Zope supports content, presentation, and logic components. Page Templates are presentation components and they can be used to display content components.

Zope 2.5 ships with several content components: ZSQL Methods, Files, and Images. DTML Documents and methods are not really pure content components since they can hold content and execute DTML code. You can use Files for textual content since you can edit the contents of Files if the file is less than 64K and contains text. However, the File object is fairly basic and may not provide all of the features or metadata that you need.

Zope's Content Management Framework (CMF) solves this problem by providing an assortment of rich content components. The CMF is Zope's content management add on. It introduces all kinds of enhancements including work-flow, skins, and content objects. The CMF makes a lot of use of Page Templates. A later release of Zope will probably include technologies from and inspired by the CMF.