Chapter 4: Dynamic Content with DTML

DTML (Document Template Markup Language) is Zope's tag-based presentation and scripting language. DTML dynamically generates, controls, and formats content. DTML is commonly used to build modular and dynamic web interfaces for your web applications.

DTML is a server side scripting language, like SSI, PHP, ASP, and JSP. This means that DTML commands are executed by Zope at the server, and the result of that execution is sent to your web browser. By contrast, client-side scripting languages like Javascript are not processed by the server, but are rather sent to and executed by your web browser.

You can use DTML scripting in two types of Zope objects, DTML Documents and DTML Methods.

Who is DTML For?

DTML is designed for people familiar with HTML and basic web scripting, not for application programmers. In fact, if you want to do programming with Zope you shouldn't use DTML. In Chapter 9, "Advanced Zope Scripting", we'll cover advanced programming using Python and Perl.

DTML is for presentation and should be managed by web designers. Zope encourages you to keep your presentation and logic separate by providing different objects for presentation (DTML), and logic (Python, Perl, and others). You will find a host of benefits resulting from keeping your presentation in DTML and your logic in other types of Zope objects. Some of those benefits include:

What is DTML Good for?

DTML is good for creating dynamic web interfaces. It supports reusing content and layout, formatting heterogeneous data, and separating presentation from logic and data.

For example with DTML you can reuse shared web page headers and footers:

      <dtml-var standard_html_header>

      <p>Hello world.</p>

      <dtml-var standard_html_footer>

This web page mixes HTML and DTML together. DTML commands are written as tags that begin with dtml-. This example builds a web page by inserting a standard header and footer into an HTML page. The resulting HTML page might look something like this:

      <html>
      <body bgcolor="#FFFFFF">

      <p>Hello world.</p>

      <hr>
      <p>Last modified 2000/10/16 by AmosL</p>
      </body>
      </html>

As you can see the standard header defined a white background color and the standard footer added a note at the bottom of the page telling when the page was last modified and by whom.

In addition to reusing content, DTML lets you easily and powerfully format all kinds of data. You can use DTML to call methods, query databases, introspect Zope objects, process forms, and more.

For example when you query a database with a SQL Method it typically returns a list of results. Here's how you might use DTML to format each result from a database query:

      <ul>
      <dtml-in frogQuery>
        <li><dtml-var animal_name></li>
      </dtml-in>
      </ul>

The DTML in tag iterates over the results of the database query and formats each result. Suppose four results are returned by frogQuery. Here's what the resulting HTML might look like:

      <ul>
        <li>Fire-bellied toad</li>
        <li>African clawed frog</li>
        <li>Lake Nabu reed frog</li>
        <li>Chilean four-eyed frog</li>
      </ul>

The results of the database query are formatted as an HTML bulleted list.

Note that you don't have to tell DTML that you are querying a database and you don't have to tell it where to find the arguments to call the database query. You just tell it what object to call, it will do the work of figuring out how to call the object and pass it appropriate arguments. If you replace the frogQuery SQL Method with some other kind of object, like a Script, a ZCatalog, or even another DTML Method, you won't have to change the way you format the results.

This ability to format all kinds of data makes DTML a powerful presentation tool, and lets you modify your business logic without changing your presentation.

When Not to Use DTML

DTML is not a general purpose programming language. For example, DTML does not allow you to create variables very easily. While it may be possible to implement complex algorithms in DTML, it is painful and not recommended. If you want to implement programming logic, use Python or Perl (for more information on these subjects, see Chapter 9, "Advanced Zope Scripting").

For example, let's suppose you were writing a simple web page for a group of math students, and on that page you wanted to illustrate a simple calculation. You would not want to write the program that made this calculation in DTML. It could be done in DTML, but it would be difficult to understand. DTML would be perfect for describing the page that this calculation is inserted into, but it would be awful to do this calculation in DTML, whereas it may be very simple and trivial in Python or Perl.

String processing is another area where DTML is not the best choice. If you want to manipulate input from a user in a complex way, but using functions that manipulate strings, you are better off doing it in Python or Perl, both of which have much more powerful string processing abilities than DTML.

DTML is one tool among many available in Zope. If you find yourself scratching your head trying to figure out some complicated DTML construct, there's a good chance that things would work better if you broke your DTML script up into a collection of DTML and Python or Perl-based Scripts.

DTML Tag Syntax

DTML's syntax is similar to HTML. DTML is a tag based mark-up language. In other words DTML uses tags to do its work. Here is a simple snippet of DTML:

      <dtml-var standard_html_header>

      <h1>Hello World!</h1>

      <dtml-var standard_html_footer>

This DTML code contains two DTML var tags and some HTML. The h1 tags are HTML, not DTML. You typically mix DTML with other mark-up languages like HTML. Normally DTML is used to generate HTML, but there's nothing keeping you from generating other types of text. As you'll see later you can also use DTML to generate mail messages and other textual information.

DTML contains two kinds of tags, singleton and block tags. Singleton tags consist of one tag enclosed by less-than (<) and greater-than (>) symbols. The var tag is an example of a singleton tag:

      <dtml-var parrot>

There's no need to close the var tag.

Block tags consist of two tags, one that opens the block and one that closes the block, and content that goes between them:

      <dtml-in mySequence>

        <!-- this is an HTML comment inside the in tag block -->

      </dtml-in>

The opening tag starts the block and the closing tag ends it. The closing tag has the same name as the opening tag with a slash preceding it. This is the same convention that HTML and XML use.

Using DTML Tag Attributes

All DTML tags have attributes. An attribute provides information about how the tag is supposed to work. Some attributes are optional. For example, the var tag inserts the value of a variable. It has an optional missing attribute that specifies a default value in case the variable can't be found:

        <dtml-var wingspan missing="unknown wingspan">

If the wingspan variable is not found then unknown wingspan is inserted instead.

Some attributes don't have values. For example, you can convert an inserted variable to upper case with the upper attribute:

        <dtml-var exclamation upper>

Notice that the upper attribute, unlike the missing attribute doesn't need a value.

Different tags have different attributes. See Appendix A, "DTML Reference", for more information on the syntax of different DTML tags.

Inserting Variables with DTML

Inserting a variable is the most basic task that you can perform with DTML. You already saw how DTML inserts a header and footer into a web page with the var tag. Many DTML tags insert variables, and they all do it in a similar way. Let's look more closely at how Zope inserts variables.

Suppose you have a folder whose id is Feedbags that has the title "Bob's Fancy Feedbags". Inside the folder create a DTML Method with an id of pricelist. Then change the contents of the DTML Method to the following:

      <dtml-var standard_html_header>

      <h1>Price list for <dtml-var title></h1>

      <p>Hemp Bag $2.50</p>
      <p>Silk Bag $5.00</p>

      <dtml-var standard_html_footer>

Now view the DTML Method by clicking the View tab. You should see an HTML page whose source looks something like this:

      <html>
      <body>

      <h1>Price list for Bob's Fancy Feedbags</h1>

      <p>Hemp Bag $2.50</p>
      <p>Silk Bag $5.00</p>     

      </body>
      </html>

This is basically what you might expect. Zope inserts a header, a footer, and a title into the web page. DTML gets the values for these variables from a number of different places. First, the var tag tries to find a variable in the current object. Then it looks in the current object's containers. Then it looks in the web request (forms and cookies). If Zope cannot find a variable then it raises an exception, and it stops executing the DTML.

Let's follow this DTML code step by step to see where the variables are found. First Zope looks for standard_html_header in the current object, which is the pricelist DTML Method. Next, Zope looks for the header in the current object's containers. The Feedbags folder doesn't have any methods or properties or sub-objects by that name either. Next Zope examines the Feedbags folder's container, and so on until it gets to the root folder. The root folder does have a sub-object named standard_html_header. The header object is a DTML Method. So Zope calls the header method and inserts the results.

Next Zope looks for the title variable. Here, the search is a little shorter. First, it looks in the pricelist DTML Method, which does not have a title, so Zope moves on and finds the Feedbags folder's title and inserts it.

Finally Zope looks for the standard_html_footer variable. It has to search all the way up to the root folder to find it, just like it looked for standard_html_header.

This exercise may seem a bit tedious, but understanding how Zope looks up variables is very important. For example, some important implications of how Zope looks up variables include how Zope objects can get content and behavior from their parents, and how content defined in one location can be reused by many objects.

Processing Input from Forms

It's easy to do form processing with Zope. DTML looks for variables to insert in a number of locations, including information that comes from submitted HTML forms. You don't need any special objects, DTML Documents and DTML Methods will do.

Create two DTML Documents, one with the id infoForm and the other with the id infoAction. Now edit the contents of the documents. Here's the contents of the infoForm document:

      <dtml-var standard_html_header>

      <p>Please send me information on your aardvark adoption
      program.</p>

      <form action="infoAction">
      name: <input type="text" name="user_name"><br>
      email: <input type="text" name="email"><br>
      <input type="submit">
      </form>

      <dtml-var standard_html_footer>

Now view this document. It is a web form that asks for information and sends it to the infoAction document when you submit the form.

Now edit the contents of the infoAction document to make it process the form:

      <dtml-var standard_html_header>

      <h1>Thanks <dtml-var user_name></h1>

      <p>We received your request for information and will send you
      email at <dtml-var email> describing our aardvark adoption
      program as soon as it receives final governmental approval.
      </p>

      <dtml-var standard_html_footer>

This document displays a thanks message which includes name and email information gathered from the web form.

Now go back to the infoForm document, view it, fill out the form and submit it. If all goes well you should see a thank you message that includes your name and email address.

The infoAction document found the form information from the web request that happened when you clicked the submit button on the infoForm. As we mentioned in the last section, DTML looks for variables in a couple of places, one of which is the web request, so there's nothing special you need to do to enable your documents to process web forms.

Let's perform an experiment. What happens if you try to view the infoAction document directly, as opposed to getting to it from the infoForm document. Click on the infoAction document and then click the View tab, as shown in Figure 4-1.

DTML error resulting from a failed variable lookup.

Figure 4-1 DTML error resulting from a failed variable lookup.

Zope couldn't find the user_name variable since it was not in the current object, its containers or the web request. This is an error that you're likely to see frequently as you learn Zope. Don't fear, it just means that you've tried to insert a variable that Zope can't find. In this example, you need to either insert a variable that Zope can find, or use the missing attribute on the var tag as described above:

      <h1>Thanks <dtml-var user_name missing="Anonymous User"></h1>

Understanding where Zope looks for variables will help you figure out how to fix this kind of problem. In this case, you have viewed a document that needs to be called from an HTML form like infoForm in order to provide variables to be inserted in the output.

Dynamically Acquiring Content

Zope looks for DTML variables in the current object's containers if it can't find the variable first in the current object. This behavior allows your objects to find and use content and behavior defined in their parents. Zope uses the term acquisition to refer to this dynamic use of content and behavior.

Now that you see how site structure fits into the way names are looked up, you can begin to understand that where you place objects you are looking for is very important.

An example of acquisition that you've already seen is how web pages use standard headers and footers. To acquire the standard header just ask Zope to insert it with the var tag:

      <dtml-var standard_html_header>

It doesn't matter where your DTML Method or Document is located. Zope will search upwards until it finds the standard_html_header that is defined in the root folder.

You can take advantage of how Zope looks up variables to customize your header in different parts of your site. Just create a new standard_html_header in a folder and it will override global header for all web pages in your folder and below it.

Create a folder in the root folder with an id of Green. Enter the Green folder and create a DTML Document with an id of welcome. Edit the welcome document to have these contents:

      <dtml-var standard_html_header>

      <p>Welcome</p>

      <dtml-var standard_html_footer>

Now view the welcome document. It should look like a simple web page with the word welcome, as shown in Figure 4-2.

Welcome document.

Figure 4-2 Welcome document.

Now let's customize the header for the Green folder. Create a DTML Method in the Green folder with an id of standard_html_header. Then edit the contents of the header to the following:

      <html>
      <head>
        <style type="text/css">
        body {color: #00FF00;}
        p {font-family: sans-serif;}
        </style>
      </head>
      <body>

Notice that this is not a complete web page. This is just a fragment of HTML that will be used as a header. This header uses CSS (Cascading Style Sheets) to make some changes to the look and feel of web pages.

Now go back to the welcome document and view it again, as shown in Figure 4-3.

Welcome document with custom header.

Figure 4-3 Welcome document with custom header.

The document now looks quite different. This is because it is now using the new header we introduced in the Green folder. This header will be used by all web pages in the Green folder and its sub-folders.

You can continue this process of overriding default content by creating another folder inside the Green folder and creating a standard_html_header DTML Method there. Now web pages in the sub-folder will use their local header rather than the Green folder's header. Using this pattern you can quickly change the look and feel of different parts of your web site. If you later decide that an area of the site needs a different header, just create one. You don't have to change the DTML in any of the web pages; they'll automatically find the closest header and use it.

Using Python Expressions from DTML

So far we've looked at simple DTML tags. Here's an example:

      <dtml-var getHippo>

This will insert the value of the variable named getHippo, whatever that may be. DTML will automatically take care of the details, like finding the variable and calling it. We call this basic tag syntax name syntax to differentiate it from expression syntax.

DTML expressions allow you to be more explicit about how to find and call variables. Expressions are tag attributes that contain snippets of code in the Python language. For example, instead of letting DTML find and call getHippo, we can use an expression to explicitly pass arguments:

      <dtml-var expr="getHippo('with a large net')">

Here we've used a Python expression to explicitly call the getHippo method with the string argument, with a large net. To find out more about Python's syntax, see the Python Tutorial at the Python.org web site. Many DTML tags can use expression attributes.

Expressions make DTML pretty powerful. For example, using Python expressions, you can easily test conditions:

        <dtml-if expr="foo < bar">
          Foo is less than bar.
        </dtml-if>

Without expressions, this very simple task would have to be broken out into a separate method and would add a lot of overhead for something this trivial.

Before you get carried away with expressions, take care. Expressions can make your DTML hard to understand. Code that is hard to understand is more likely to contain errors and is harder to maintain. Expressions can also lead to mixing logic in your presentation. If you find yourself staring blankly at an expression for more than five seconds, stop. Rewrite the DTML without the expression and use a Script to do your logic. Just because you can do complex things with DTML doesn't mean you should.

DTML Expression Gotchas

Using Python expressions can be tricky. One common mistake is to confuse expressions with basic tag syntax. For example:

        <dtml-var objectValues>

and:

        <dtml-var expr="objectValues">

will end up giving you two completely different results. The first example of the DTML var tag will automatically render variables. In other words it will try to do the right thing to insert your variable, no matter what that variable may be. In general this means that if the variable is a method it will be called with appropriate arguments. This process is covered more thoroughly in Chapter 8, "Variables and Advanced DTML".

In an expression, you have complete control over the variable rendering. In the case of our example, objectValues is a method. So:

        <dtml-var objectValues>

will call the method. But:

        <dtml-var expr="objectValues">

will not call the method, it will just try to insert it. The result will be not a list of objects but a string such as <Python Method object at 8681298>. If you ever see results like this, there is a good chance that you're returning a method, rather than calling it.

To call a method from an expression, you must use standard Python calling syntax by using parenthesis:

        <dtml-var expr="objectValues()">

The lesson is that if you use Python expressions you must know what kind of variable you are inserting and must use the proper Python syntax to appropriately render the variable.

Before we leave the subject of variable expressions we should mention that there is a deprecated form of the expression syntax. You can leave out the "expr=" part on a variable expression tag. But please don't do this. It is far too easy to confuse:

        <dtml-var aName>

with:

        <dtml-var "aName">

and get two completely different results. These "shortcuts" were built into DTML long ago, but we do not encourage you to use them now unless you are prepared to accept the confusion and debugging problems that come from this subtle difference in syntax.

The Var Tag

The var tag inserts variables into DTML Methods and Documents. We've already seen many examples of how the var tag can be used to insert strings into web pages.

As you've seen, the var tag looks up variables first in the current object, then in its containers and finally in the web request.

The var tag can also use Python expressions to provide more control in locating and calling variables.

Var Tag Attributes

You can control the behavior of the var tag using its attributes. The var tag has many attributes that help you in common formatting situations. The attributes are summarized in Appendix A. Here's a sampling of var tag attributes.

html_quote
This attribute causes the inserted values to be HTML quoted. This means that <, > and & are escaped.
missing
The missing attribute allows you to specify a default value to use in case Zope can't find the variable. For example:
            <dtml-var bananas missing="We have no bananas">
fmt
The fmt attribute allows you to control the format of the var tags output. There are many possible formats which are detailed in Appendix A.

One use of the fmt attribute is to format monetary values. For example, create a float property in your root folder called adult_rate. This property will represent the cost for one adult to visit the Zoo. Give this property the value 2.2.

You can display this cost in a DTML Document or Method like so:

            One Adult pass: <dtml-var adult_rate fmt=dollars-and-cents>

This will correctly print "$2.20". It will round more precise decimal numbers to the nearest penny.

Var Tag Entity Syntax

Zope provides a shortcut DTML syntax just for the simple var tag. Because the var tag is a singleton, it can be represented with an HTML entity like syntax:

        &dtml-cockatiel;

This is equivalent to:

        <dtml-var name="cockatiel" html_quote>

The main reason to use the entity syntax is to avoid putting DTML tags inside HTML tags. For example, instead of writing:

        <input type="text" value="<dtml-var name="defaultValue">">

You can use the entity syntax to make things more readable for you and your text editor:

        <input type="text" value="&dtml-defaultValue;">

The var tag entity syntax is very limited. You can't use Python expressions and some tag attributes with it. See Appendix A for more information on var tag entity syntax.

The If Tag

One of DTML's important benefits is to let you customize your web pages. Often customization means testing conditions are responding appropriately. This if tag lets you evaluate a condition and carry out different actions based on the result.

What is a condition? A condition is either a true or false value. In general all objects are considered true unless they are 0, None, an empty sequence or an empty string.

Here's an example condition:

objectValues
True if the variable objectValues exists and is true. That is to say, when found and rendered objectValues is not 0, None, an empty sequence, or an empty string.

As with the var tag, you can use both name syntax and expression syntax. Here are some conditions expressed as DTML expressions.

expr="1"
Always true.
expr="rhino"
True if the rhino variable is true.
expr="x < 5"
True if x is less than 5.
expr="objectValues('File')"
True if calling the objectValues method with an argument of File returns a true value. This method is explained in more detail in this chapter.

The if tag is a block tag. The block inside the if tag is executed if the condition is true.

Here's how you might use a variable expression with the if tag to test a condition:

      <p>How many monkeys are there?</p>

      <dtml-if expr="monkeys > monkey_limit">
        <p>There are too many monkeys!</p>
      </dtml-if>

In the above example, if the Python expression monkeys > monkey_limit is true then you will see the first and the second paragraphs of HTML. If the condition is false, you will only see the first.

If tags be nested to any depth, for example, you could have:

      <p>Are there too many blue monkeys?</p>

      <dtml-if "monkeys.color == 'blue'">
        <dtml-if expr="monkeys > monkey_limit">
          <p>There are too many blue monkeys!</p>
        </dtml-if>
      </dtml-if>

Nested if tags work by evaluating the first condition, and if that condition is true, then evaluating the second. In general, DTML if tags work very much like Python if statements..

Name and Expression Syntax Differences

The name syntax checks for the existence of a name, as well as its value. For example:

        <dtml-if monkey_house>
          <p>There <em>is</em> a monkey house Mom!</p>
        </dtml-if>  

If the monkey_house variable does not exist, then this condition is false. If there is a monkey_house variable but it is false, then this condition is also false. The condition is only true is there is a monkey_house variable and it is not 0, None, an empty sequence or an empty string.

The Python expression syntax does not check for variable existence. This is because the expression must be valid Python. For example:

        <dtml-if expr="monkey_house">
          <p>There <em>is</em> a monkey house, Mom!</p>
        </dtml-if>

This will work as expected as long as monkey_house exists. If the monkey_house variable does not exist, Zope will raise a KeyError exception when it tries to find the variable.

Else and Elif Tags

The if tag only lets you take an action if a condition is true. You may also want to take a different action if the condition is false. This can be done with the DTML else tag. The if block can also contain an else singleton tag. For example:

        <dtml-if expr="monkeys > monkey_limit">
          <p>There are too many monkeys!</p>
        <dtml-else>
          <p>The monkeys are happy!</p>
        </dtml-if>

The else tag splits the if tag block into two blocks, the first is executed if the condition is true, the second is executed if the condition is not true.

A if tag block can also contain a elif singleton tag. The elif tag specifies another condition just like an addition if tag. This lets you specify multiple conditions in one block:

        <dtml-if expr="monkeys > monkey_limit">
          <p>There are too many monkeys!</p>
        <dtml-elif expr="monkeys < minimum_monkeys">
          <p>There aren't enough monkeys!</p>
        <dtml-else>
          <p>There are just enough monkeys.</p>
        </dtml-if>

An if tag block can contain any number of elif tags but only one else tag. The else tag must always come after the elif tags. Elif tags can test for condition using either the name or expression syntax.

Using Cookies with the If Tag

Let's look at a more meaty if tag example. Often when you have visitors to your site you want to give them a cookie to identify them with some kind of special value. Cookies are used frequently all over the Internet, and when they are used properly they are quite useful.

Suppose we want to differentiate new visitors from folks who have already been to our site. When a user visits the site we can set a cookie. Then we can test for the cookie when displaying pages. If the user has already been to the site they will have the cookie. If they don't have the cookie yet, it means that they're new.

Suppose we're running a special. First time zoo visitors get in for half price. Here's a DTML fragment that tests for a cookie using the hasVisitedZoo variable and displays the price according to whether a user is new or a repeat visitor:

      <dtml-if hasVisitedZoo>
        <p>Zoo admission <dtml-var adult_rate fmt="dollars-and-cents">.</p>
      <dtml-else>
        <b>Zoo admission for first time visitors
             <dtml-var expr="adult_rate/2" fmt="dollars-and-cents"></p>
      </dtml-if>  

This fragment tests for the hasVisitedZoo variable. If the user has visited the zoo before it displays the normal price for admission. If the visitor is here for the first time they get in for half-price.

Just for completeness sake, here's an implementation of the hasVisitedZoo method as a Python-based Script:

      ## Script(Python) "hasVisitedZoo"
      ##parameters=REQUEST, RESPONSE
      ##
      """
      Returns true if the user has previously visited
      the Zoo. Uses cookies to keep track of zoo visits.
      """
      if REQUEST.has_key('zooVisitCookie'):
          return 1
      else:
          RESPONSE.setCookie('zooVisitCookie', '1')
          return 0

In Chapter 10, "Advanced Zope Scripting" we'll look more closely at how to script business logic with Python and Perl. For now it is sufficient to see that the method looks for a cookie and returns a true or false value depending on whether the cookie is found or not. Notice how Python uses if and else statements just like DTML uses if and else tags. DTML's if and else tags are based on Python's. In fact Python also has an elif statement, just like DTML.

The In Tag

The DTML in tag iterates over a sequence of objects, carrying out one block of execution for each item in the sequence. In programming, this is often called iteration, or looping.

The in tag is a block tag like the if tag. The content of the in tag block is executed once for every iteration in the in tag loop. For example:

      <dtml-in todo_list>
        <p><dtml-var description></p>
      </dtml-in>

This example loops over a list of objects named todo_list. For each item, it inserts an HTML paragraph with a description of the to do item.

Iteration is very useful in many web tasks. Consider a site that display houses for sale. Users will search your site for houses that match certain criteria. You will want to format all of those results in a consistent way on the page, therefore, you will need to iterate over each result one at a time and render a similar block of HTML for each result.

In a way, the contents of an in tag block is a kind of template that is applied once for each item in a sequence.

Iterating over Folder Contents

Here's an example of how to iterate over the contents of a folder. This DTML will loop over all the files in a folder and display a link to each one. This example shows you how to display all the "File" objects in a folder, so in order to run this example you will need to upload some files into Zope as explained in the previous chapter:

        <dtml-var standard_html_header>
        <ul>
        <dtml-in expr="objectValues('File')">
          <li><a href="&dtml-absolute_url;"><dtml-var title_or_id></a></li>
        </dtml-in>
        </ul>
        <dtml-var standard_html_footer>

This code displayed the following file listing, as shown in Figure 4-4.

Iterating over a list of files.

Figure 4-4 Iterating over a list of files.

Let's look at this DTML example step by step. First, the var tag is used to insert your common header into the document. Next, to indicate that you want the browser to draw an HTML bulleted list, you have the ul HTML tag.

Then there is the in tag. The tag has an expression that is calling the Zope API method called objectValues. This method returns a sequence of objects in the current folder that match a given criteria. In this case, the objects must be files. This method call will return a list of files in the current folder.

The in tag will loop over every item in this sequence. If there are four file objects in the current folder, then the in tag will execute the code in its block four times; once for each object in the sequence.

During each iteration, the in tag looks for variables in the current object, first. In Chapter 8, "Variables and Advanced DTML" we'll look more closely at how DTML looks up variables.

For example, this in tag iterates over a collection of File objects and uses the var tag to look up variables in each file:

        <dtml-in expr="objectValues('File')">
          <li><a href="&dtml-absolute_url;"><dtml-var title_or_id></a></li>
        </dtml-in>

The first var tag is an entity and the second is a normal DTML var tag. When the in tag loops over the first object its absolute_url and title_or_id variables will be inserted in the first bulleted list item:

        <ul>
          <li><a href="http://localhost:8080/FirstFile">FirstFile</a></li>

During the second iteration the second object's absolute_url and title_or_id variables are inserted in the output:

        <ul>
          <li><a href="http://localhost:8080/FirstFile">FirstFile</a></li>
          <li><a href="http://localhost:8080/SecondFile">SecondFile</a></li>

This process will continue until the in tag has iterated over every file in the current folder. After the in tag you finally close your HTML bulleted list with a closing ul HTML tag and the standard_html_footer is inserted to close the document.

In Tag Special Variables

The in tag provides you with some useful information that lets you customize your HTML while you are iterating over a sequence. For example, you can make your file library easier to read by putting it in an HTML table and making every other table row an alternating color, like this, as shown in Figure 4-5.

File listing with alternating row colors.

Figure 4-5 File listing with alternating row colors.

The in tag makes this easy. Change your file library method a bit to look like this:

        <dtml-var standard_html_header>

        <table>
        <dtml-in expr="objectValues('File')">
          <dtml-if sequence-even>
            <tr bgcolor="grey">
          <dtml-else>
            <tr>
          </dtml-if>    
          <td>
          <a href="&dtml-absolute_url;"><dtml-var title_or_id></a>
          </td></tr>
        </dtml-in>
        </table>

        <dtml-var standard_html_footer>

Here an if tag is used to test for a special variable called sequence-even. The in tag sets this variable to a true or false value each time through the loop. If the current iteration number is even, then the value is true, if the iteration number is odd, it is false.

The result of this test is that a tr tag with either a gray background or no background is inserted into the document for every other object in the sequence. As you might expect, there is a sequence-odd that always has the opposite value of sequence-even.

There are many special variables that the in tag defines for you. Here are the most common and useful:

sequence-item
This special variable is the current item in the iteration.

In the case of the file library example, each time through the loop the current file of the iteration is assigned to sequence-item. It is often useful to have a reference to the current object in the iteration.

sequence-index
the current number, starting from 0, of iterations completed so far. If this number is even, sequence-even is true and sequence-odd is false.
sequence-number
The current number, starting from 1, of iterations completed so far. This can be thought of as the cardinal position (first, second, third, etc.) of the current object in the loop. If this number is even, sequence-even is false and sequence-odd is true.
sequence-start
This variable is true for the very first iteration.
sequence-end
This variable is true for the very last iteration.

These special variables are detailed more thoroughly in Appendix A.

DTML is a powerful tool for creating dynamic content. It allows you to perform fairly complex calculations. In Chapter 8, "Variables and Advanced DTML", you'll find out about many more DTML tags, and more powerful ways to use the tags you already have seen. Despite its power, you should resist the temptation to use DTML for complex scripting. In Chapter 10, "Advanced Zope Scripting" you'll find out about how to use Python and Perl for scripting business logic.