<html> <head> <title>Chapter 4: Dynamic Content with DTML</title> </head> <body bgcolor="#FFFFFF"> <h1>Chapter 4: Dynamic Content with DTML</h1> <p> <em>DTML</em> (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.</p><p> 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.</p><p> You can use DTML scripting in two types of Zope objects, <em>DTML Documents</em> and <em>DTML Methods</em>.</p><h2> Who is DTML For?</h2> <p> 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.</p><p> 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:<ul> <li>Keeping logic and presentation separate makes it easy to vary either component without disrupting the other.</li> <li>Often you will have different people in charge of maintaining logic and presentation. By using different objects for these tasks you make it easier for people to collaborate without disrupting each other.</li> <li>It's easier to reuse existing presentation and logic components if they are not intermingled.</li> </ul> </p><h2> What is DTML Good for?</h2> <p> DTML is good for creating dynamic web interfaces. It supports reusing content and layout, formatting heterogeneous data, and separating presentation from logic and data.<p> For example with DTML you can reuse shared web page headers and footers:<pre> <dtml-var standard_html_header> <p>Hello world.</p> <dtml-var standard_html_footer></pre> </p></p><p> This web page mixes HTML and DTML together. DTML commands are written as tags that begin with <em>dtml-</em>. 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:<pre> <html> <body bgcolor="#FFFFFF"> <p>Hello world.</p> <hr> <p>Last modified 2000/10/16 by AmosL</p> </body> </html></pre> </p><p> 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.</p><p> 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.</p><p> 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:<pre> <ul> <dtml-in frogQuery> <li><dtml-var animal_name></li> </dtml-in> </ul></pre> </p><p> The DTML <em>in</em> tag iterates over the results of the database query and formats each result. Suppose four results are returned by <em>frogQuery</em>. Here's what the resulting HTML might look like:<pre> <ul> <li>Fire-bellied toad</li> <li>African clawed frog</li> <li>Lake Nabu reed frog</li> <li>Chilean four-eyed frog</li> </ul></pre> </p><p> The results of the database query are formatted as an HTML bulleted list.</p><p> 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 <em>frogQuery</em> 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.</p><p> 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.</p><h2> When Not to Use DTML</h2> <p> 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").</p><p> 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 <em>done</em> 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.</p><p> 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.</p><p> 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.</p><h2> DTML Tag Syntax</h2> <p> 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:<pre> <dtml-var standard_html_header> <h1>Hello World!</h1> <dtml-var standard_html_footer></pre> </p><p> This DTML code contains two DTML <em>var</em> tags and some HTML. The <em>h1</em> 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.</p><p> DTML contains two kinds of tags, <em>singleton</em> and <em>block</em> tags. Singleton tags consist of one tag enclosed by less-than (<) and greater-than (>) symbols. The <em>var</em> tag is an example of a singleton tag:<pre> <dtml-var parrot></pre> </p><p> There's no need to close the <em>var</em> tag. </p><p> Block tags consist of two tags, one that opens the block and one that closes the block, and content that goes between them:<pre> <dtml-in mySequence> <!-- this is an HTML comment inside the in tag block --> </dtml-in></pre> </p><p> 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.</p><h3> Using DTML Tag Attributes</h3> <p> All DTML tags have attributes. An attribute provides information about how the tag is supposed to work. Some attributes are optional. For example, the <em>var</em> tag inserts the value of a variable. It has an optional <em>missing</em> attribute that specifies a default value in case the variable can't be found:<pre> <dtml-var wingspan missing="unknown wingspan"></pre> </p><p> If the <em>wingspan</em> variable is not found then <code>unknown wingspan</code> is inserted instead.</p><p> Some attributes don't have values. For example, you can convert an inserted variable to upper case with the <em>upper</em> attribute:<pre> <dtml-var exclamation upper></pre> </p><p> Notice that the <em>upper</em> attribute, unlike the <em>missing</em> attribute doesn't need a value.</p><p> Different tags have different attributes. See Appendix A, "DTML Reference", for more information on the syntax of different DTML tags.</p><h2> Inserting Variables with DTML</h2> <p> 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 <em>var</em> 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.</p><p> Suppose you have a folder whose id is <em>Feedbags</em> that has the title "Bob's Fancy Feedbags". Inside the folder create a DTML Method with an id of <em>pricelist</em>. Then change the contents of the DTML Method to the following:<pre> <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></pre> </p><p> Now view the DTML Method by clicking the <em>View</em> tab. You should see an HTML page whose source looks something like this:<pre> <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></pre> </p><p> 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 <em>var</em> 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.</p><p> Let's follow this DTML code step by step to see where the variables are found. First Zope looks for <em>standard_html_header</em> in the current object, which is the <em>pricelist</em> DTML Method. Next, Zope looks for the header in the current object's containers. The <em>Feedbags</em> folder doesn't have any methods or properties or sub-objects by that name either. Next Zope examines the <em>Feedbags</em> folder's container, and so on until it gets to the root folder. The root folder does have a sub-object named <em>standard_html_header</em>. The header object is a DTML Method. So Zope calls the header method and inserts the results.</p><p> Next Zope looks for the <em>title</em> variable. Here, the search is a little shorter. First, it looks in the <em>pricelist</em> DTML Method, which does not have a title, so Zope moves on and finds the <em>Feedbags</em> folder's title and inserts it.</p><p> Finally Zope looks for the <em>standard_html_footer</em> variable. It has to search all the way up to the root folder to find it, just like it looked for <em>standard_html_header</em>.</p><p> 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.</p><h2> Processing Input from Forms</h2> <p> 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.</p><p> Create two DTML Documents, one with the id <em>infoForm</em> and the other with the id <em>infoAction</em>. Now edit the contents of the documents. Here's the contents of the <em>infoForm</em> document:<pre> <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></pre> </p><p> Now view this document. It is a web form that asks for information and sends it to the <em>infoAction</em> document when you submit the form.</p><p> Now edit the contents of the <em>infoAction</em> document to make it process the form:<pre> <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></pre> </p><p> This document displays a thanks message which includes name and email information gathered from the web form.</p><p> Now go back to the <em>infoForm</em> 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.</p><p> The <em>infoAction</em> document found the form information from the web request that happened when you clicked the submit button on the <em>infoForm</em>. 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.</p><p> Let's perform an experiment. What happens if you try to view the <em>infoAction</em> document directly, as opposed to getting to it from the <em>infoForm</em> document. Click on the <em>infoAction</em> document and then click the View tab, as shown in <a href="#4-1">Figure 4-1</a>.</p><p> <a name="4-1"></a> <img src="Figures/4-1.png" alt="DTML error resulting from a failed variable lookup."> <p><b>Figure 4-1</b> DTML error resulting from a failed variable lookup.</p> </p><p> Zope couldn't find the <em>user_name</em> 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 <code>missing</code> attribute on the var tag as described above:<pre> <h1>Thanks <dtml-var user_name missing="Anonymous User"></h1></pre> </p><p> 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 <em>infoForm</em> in order to provide variables to be inserted in the output.</p><h2> Dynamically Acquiring Content</h2> <p> 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 <em>acquisition</em> to refer to this dynamic use of content and behavior.</p><p> 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.</p><p> 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 <em>var</em> tag:<pre> <dtml-var standard_html_header></pre> </p><p> It doesn't matter where your DTML Method or Document is located. Zope will search upwards until it finds the <em>standard_html_header</em> that is defined in the root folder.</p><p> You can take advantage of how Zope looks up variables to customize your header in different parts of your site. Just create a new <em>standard_html_header</em> in a folder and it will override global header for all web pages in your folder and below it.</p><p> Create a folder in the root folder with an id of <em>Green</em>. Enter the <em>Green</em> folder and create a DTML Document with an id of <em>welcome</em>. Edit the <em>welcome</em> document to have these contents:<pre> <dtml-var standard_html_header> <p>Welcome</p> <dtml-var standard_html_footer></pre> </p><p> Now view the <em>welcome</em> document. It should look like a simple web page with the word <em>welcome</em>, as shown in <a href="#4-2">Figure 4-2</a>.</p><p> <a name="4-2"></a> <img src="Figures/4-2.png" alt="Welcome document."> <p><b>Figure 4-2</b> Welcome document.</p> </p><p> Now let's customize the header for the <em>Green</em> folder. Create a DTML Method in the <em>Green</em> folder with an id of <em>standard_html_header</em>. Then edit the contents of the header to the following:<pre> <html> <head> <style type="text/css"> body {color: #00FF00;} p {font-family: sans-serif;} </style> </head> <body></pre> </p><p> 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 <a href="http://www.w3.org/Style/CSS">CSS</a> (Cascading Style Sheets) to make some changes to the look and feel of web pages.</p><p> Now go back to the <em>welcome</em> document and view it again, as shown in <a href="#4-3">Figure 4-3</a>.</p><p> <a name="4-3"></a> <img src="Figures/4-3.png" alt="Welcome document with custom header."> <p><b>Figure 4-3</b> Welcome document with custom header.</p> </p><p> The document now looks quite different. This is because it is now using the new header we introduced in the <em>Green</em> folder. This header will be used by all web pages in the <em>Green</em> folder and its sub-folders.</p><p> You can continue this process of overriding default content by creating another folder inside the <em>Green</em> folder and creating a <em>standard_html_header</em> DTML Method there. Now web pages in the sub-folder will use their local header rather than the <em>Green</em> 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.</p><h2> Using Python Expressions from DTML</h2> <p> So far we've looked at simple DTML tags. Here's an example:<pre> <dtml-var getHippo></pre> </p><p> This will insert the value of the variable named <em>getHippo</em>, 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 <em>name</em> syntax to differentiate it from <em>expression</em> syntax.</p><p> 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 <em>getHippo</em>, we can use an expression to explicitly pass arguments:<pre> <dtml-var expr="getHippo('with a large net')"></pre> </p><p> Here we've used a Python expression to explicitly call the <em>getHippo</em> method with the string argument, <code>with a large net</code>. To find out more about Python's syntax, see the <a href="http://www.python.org/doc/current/tut">Python Tutorial</a> at the Python.org web site. Many DTML tags can use expression attributes.</p><p> Expressions make DTML pretty powerful. For example, using Python expressions, you can easily test conditions:<pre> <dtml-if expr="foo < bar"> Foo is less than bar. </dtml-if></pre> </p><p> 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.</p><p> 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.</p><h3> DTML Expression Gotchas</h3> <p> Using Python expressions can be tricky. One common mistake is to confuse expressions with basic tag syntax. For example:<pre> <dtml-var objectValues></pre> </p><p> and:<pre> <dtml-var expr="objectValues"></pre> </p><p> will end up giving you two completely different results. The first example of the DTML <em>var</em> 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".</p><p> In an expression, you have complete control over the variable rendering. In the case of our example, <em>objectValues</em> is a method. So:<pre> <dtml-var objectValues></pre> </p><p> will call the method. But:<pre> <dtml-var expr="objectValues"></pre> </p><p> will <em>not</em> call the method, it will just try to insert it. The result will be not a list of objects but a string such as <code><Python Method object at 8681298></code>. If you ever see results like this, there is a good chance that you're returning a method, rather than calling it.</p><p> To call a method from an expression, you must use standard Python calling syntax by using parenthesis:<pre> <dtml-var expr="objectValues()"></pre> </p><p> 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.</p><p> 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 <em>please</em> don't do this. It is far too easy to confuse:<pre> <dtml-var aName></pre> </p><p> with:<pre> <dtml-var "aName"></pre> </p><p> 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.</p><h2> The <em>Var</em> Tag</h2> <p> The <em>var</em> tag inserts variables into DTML Methods and Documents. We've already seen many examples of how the <em>var</em> tag can be used to insert strings into web pages.</p><p> As you've seen, the <em>var</em> tag looks up variables first in the current object, then in its containers and finally in the web request.</p><p> The <em>var</em> tag can also use Python expressions to provide more control in locating and calling variables.</p><h3> <em>Var</em> Tag Attributes</h3> <p> You can control the behavior of the <em>var</em> tag using its attributes. The <em>var</em> tag has many attributes that help you in common formatting situations. The attributes are summarized in Appendix A. Here's a sampling of <em>var</em> tag attributes.<dl> <dt> html_quote</dt> <dd>This attribute causes the inserted values to be HTML quoted. This means that <code><</code>, <code>></code> and <code>&</code> are escaped.</dd> <dt> missing</dt> <dd>The missing attribute allows you to specify a default value to use in case Zope can't find the variable. For example:<pre> <dtml-var bananas missing="We have no bananas"></pre> </dd> <dt> fmt</dt> <dd>The fmt attribute allows you to control the format of the <em>var</em> tags output. There are many possible formats which are detailed in Appendix A.<p> One use of the <em>fmt</em> attribute is to format monetary values. For example, create a <em>float</em> property in your root folder called <em>adult_rate</em>. This property will represent the cost for one adult to visit the Zoo. Give this property the value <code>2.2</code>.</p><p> You can display this cost in a DTML Document or Method like so:<pre> One Adult pass: <dtml-var adult_rate fmt=dollars-and-cents></pre> </p><p> This will correctly print "$2.20". It will round more precise decimal numbers to the nearest penny.</p></dd> </dl> </p><h3> <em>Var</em> Tag Entity Syntax</h3> <p> Zope provides a shortcut DTML syntax just for the simple <em>var</em> tag. Because the <em>var</em> tag is a singleton, it can be represented with an <em>HTML entity</em> like syntax:<pre> &dtml-cockatiel;</pre> </p><p> This is equivalent to:<pre> <dtml-var name="cockatiel" html_quote></pre> </p><p> The main reason to use the entity syntax is to avoid putting DTML tags inside HTML tags. For example, instead of writing:<pre> <input type="text" value="<dtml-var name="defaultValue">"></pre> </p><p> You can use the entity syntax to make things more readable for you and your text editor:<pre> <input type="text" value="&dtml-defaultValue;"></pre> </p><p> The <em>var</em> 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 <em>var</em> tag entity syntax.</p><h2> The <em>If</em> Tag</h2> <p> One of DTML's important benefits is to let you customize your web pages. Often customization means testing conditions are responding appropriately. This <em>if</em> tag lets you evaluate a condition and carry out different actions based on the result.</p><p> 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.</p><h3> Here's an example condition:</h3> <dl> <dt> objectValues</dt> <dd>True if the variable <em>objectValues</em> exists and is true. That is to say, when found and rendered <em>objectValues</em> is not 0, None, an empty sequence, or an empty string.</dd> </dl> <p> As with the <em>var</em> tag, you can use both name syntax and expression syntax. Here are some conditions expressed as DTML expressions.<dl> <dt> expr="1"</dt> <dd>Always true.</dd> <dt> expr="rhino"</dt> <dd>True if the rhino variable is true.</dd> <dt> expr="x < 5"</dt> <dd>True if x is less than 5.</dd> <dt> expr="objectValues('File')"</dt> <dd>True if calling the <em>objectValues</em> method with an argument of <em>File</em> returns a true value. This method is explained in more detail in this chapter.</dd> </dl> </p><p> The <em>if</em> tag is a block tag. The block inside the <em>if</em> tag is executed if the condition is true.</p><p> Here's how you might use a variable expression with the <em>if</em> tag to test a condition:<pre> <p>How many monkeys are there?</p> <dtml-if expr="monkeys > monkey_limit"> <p>There are too many monkeys!</p> </dtml-if></pre> </p><p> In the above example, if the Python expression <code>monkeys > monkey_limit</code> 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.</p><p> <em>If</em> tags be nested to any depth, for example, you could have:<pre> <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></pre> </p><p> Nested if tags work by evaluating the first condition, and if that condition is true, then evaluating the second. In general, DTML <em>if</em> tags work very much like Python <em>if</em> statements..</p><h3> Name and Expression Syntax Differences</h3> <p> The name syntax checks for the <em>existence</em> of a name, as well as its value. For example:<pre> <dtml-if monkey_house> <p>There <em>is</em> a monkey house Mom!</p> </dtml-if> </pre> </p><p> If the <em>monkey_house</em> variable does not exist, then this condition is false. If there is a <em>monkey_house</em> variable but it is false, then this condition is also false. The condition is only true is there is a <em>monkey_house</em> variable and it is not 0, None, an empty sequence or an empty string.</p><p> The Python expression syntax does not check for variable existence. This is because the expression must be valid Python. For example:<pre> <dtml-if expr="monkey_house"> <p>There <em>is</em> a monkey house, Mom!</p> </dtml-if></pre> </p><p> This will work as expected as long as <em>monkey_house</em> exists. If the <em>monkey_house</em> variable does not exist, Zope will raise a <em>KeyError</em> exception when it tries to find the variable.</p><h3> <em>Else</em> and <em>Elif</em> Tags</h3> <p> The <em>if</em> 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 <em>else</em> tag. The <em>if</em> block can also contain an <em>else</em> singleton tag. For example:<pre> <dtml-if expr="monkeys > monkey_limit"> <p>There are too many monkeys!</p> <dtml-else> <p>The monkeys are happy!</p> </dtml-if></pre> </p><p> The <em>else</em> tag splits the <em>if</em> tag block into two blocks, the first is executed if the condition is true, the second is executed if the condition is not true.</p><p> A <em>if</em> tag block can also contain a <em>elif</em> singleton tag. The <em>elif</em> tag specifies another condition just like an addition <em>if</em> tag. This lets you specify multiple conditions in one block:<pre> <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></pre> </p><p> An <em>if</em> tag block can contain any number of <em>elif</em> tags but only one <em>else</em> tag. The <em>else</em> tag must always come after the <em>elif</em> tags. <em>Elif</em> tags can test for condition using either the name or expression syntax.</p><h2> Using Cookies with the <em>If</em> Tag</h2> <p> Let's look at a more meaty <em>if</em> 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.</p><p> 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.</p><p> 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 <em>hasVisitedZoo</em> variable and displays the price according to whether a user is new or a repeat visitor:<pre> <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> </pre> </p><p> This fragment tests for the <em>hasVisitedZoo</em> 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.</p><p> Just for completeness sake, here's an implementation of the <em>hasVisitedZoo</em> method as a Python-based Script:<pre> ## 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</pre> </p><p> 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 <em>else</em> tags. DTML's <em>if</em> and <em>else</em> tags are based on Python's. In fact Python also has an elif statement, just like DTML.</p><h2> The <em>In</em> Tag</h2> <p> The DTML <em>in</em> 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 <em>iteration</em>, or <em>looping</em>.</p><p> The <em>in</em> tag is a block tag like the <em>if</em> tag. The content of the <em>in</em> tag block is executed once for every iteration in the <em>in</em> tag loop. For example:<pre> <dtml-in todo_list> <p><dtml-var description></p> </dtml-in></pre> </p><p> This example loops over a list of objects named <em>todo_list</em>. For each item, it inserts an HTML paragraph with a description of the to do item.</p><p> 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.</p><p> In a way, the contents of an <em>in</em> tag block is a kind of <em>template</em> that is applied once for each item in a sequence.</p><h3> Iterating over Folder Contents</h3> <p> 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:<pre> <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></pre> </p><p> This code displayed the following file listing, as shown in <a href="#4-4">Figure 4-4</a>.</p><p> <a name="4-4"></a> <img src="Figures/4-4.png" alt="Iterating over a list of files."> <p><b>Figure 4-4</b> Iterating over a list of files.</p> </p><p> Let's look at this DTML example step by step. First, the <em>var</em> 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 <em>ul</em> HTML tag.</p><p> Then there is the <em>in</em> tag. The tag has an expression that is calling the Zope API method called <em>objectValues</em>. 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.</p><p> The <em>in</em> tag will loop over every item in this sequence. If there are four file objects in the current folder, then the <em>in</em> tag will execute the code in its block four times; once for each object in the sequence.</p><p> During each iteration, the <em>in</em> 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.</p><p> For example, this <em>in</em> tag iterates over a collection of File objects and uses the <em>var</em> tag to look up variables in each file:<pre> <dtml-in expr="objectValues('File')"> <li><a href="&dtml-absolute_url;"><dtml-var title_or_id></a></li> </dtml-in></pre> </p><p> The first <em>var</em> tag is an entity and the second is a normal DTML <em>var</em> tag. When the <em>in</em> tag loops over the first object its <em>absolute_url</em> and <em>title_or_id</em> variables will be inserted in the first bulleted list item:<pre> <ul> <li><a href="http://localhost:8080/FirstFile">FirstFile</a></li></pre> </p><p> During the second iteration the second object's <em>absolute_url</em> and <em>title_or_id</em> variables are inserted in the output:<pre> <ul> <li><a href="http://localhost:8080/FirstFile">FirstFile</a></li> <li><a href="http://localhost:8080/SecondFile">SecondFile</a></li></pre> </p><p> This process will continue until the <em>in</em> tag has iterated over every file in the current folder. After the <em>in</em> tag you finally close your HTML bulleted list with a closing <em>ul</em> HTML tag and the <em>standard_html_footer</em> is inserted to close the document.</p><h3> <em>In</em> Tag Special Variables</h3> <p> The <em>in</em> 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 <a href="#4-5">Figure 4-5</a>.</p><p> <a name="4-5"></a> <img src="Figures/4-5.png" alt="File listing with alternating row colors."> <p><b>Figure 4-5</b> File listing with alternating row colors.</p> </p><p> The <em>in</em> tag makes this easy. Change your file library method a bit to look like this:<pre> <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></pre> </p><p> Here an <em>if</em> tag is used to test for a special variable called <code>sequence-even</code>. The <em>in</em> 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.</p><p> The result of this test is that a <em>tr</em> 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 <code>sequence-odd</code> that always has the opposite value of <code>sequence-even</code>.</p><p> There are many special variables that the <em>in</em> tag defines for you. Here are the most common and useful:<dl> <dt> sequence-item</dt> <dd>This special variable is the current item in the iteration.<p> 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.</p></dd> <dt> sequence-index</dt> <dd>the current number, starting from 0, of iterations completed so far. If this number is even, <code>sequence-even</code> is true and <code>sequence-odd</code> is false.</dd> <dt> sequence-number</dt> <dd>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, <code>sequence-even</code> is false and <code>sequence-odd</code> is true.</dd> <dt> sequence-start</dt> <dd>This variable is true for the very first iteration.</dd> <dt> sequence-end</dt> <dd>This variable is true for the very last iteration.</dd> </dl> </p><p> These special variables are detailed more thoroughly in Appendix A.</p><p> 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.</p></body> </html>