You are not logged in Log in Join
You are here: Home » Zope Documentation » Books » The Zope Book Releases » The Zope Book (2.5 Edition) » Variables and Advanced DTML

Log in
Name

Password

 
Previous Page Up one Level Next Page Variables and Advanced DTML Comments On/Off Table of Contents

Chapter 8: Variables and Advanced DTML

DTML is the kind of language that "does what you mean." That is good, when it does what you actually want it to do, but when it does something you don't want to do, it's bad. This chapter tells you how to make DTML do what you really mean.

It's no lie that DTML has a reputation for complexity. And it's true, DTML is really simple if all you want to do is simple layout, like you've seen so far. However, if you want to use DTML for more advanced tasks, you have to understand where DTML variables come from.

Here's a very tricky error that almost all newbies encounter. Imagine you have a DTML Document called zooName. This document contains an HTML form like the following:


<dtml-var standard_html_header>

  <dtml-if zooName>

    <p><dtml-var zooName></p>

  <dtml-else>

    <form action="<dtml-var URL>" method="GET">
      <input name="zooName">
      <input type="submit" value="What is zooName?">
    </form>

  </dtml-if>  

<dtml-var standard_html_footer>
Anonymous User - June 15, 2002 10:02 am:
 Why wouldn't the form action code use a Zope object name instead of the URL variable? I thought that in one
 of the previous examples form actions were more object-oriented than this.
Anonymous User - Aug. 24, 2002 5:25 am:
 I recieve:
 Zope has encountered an error while publishing this resource.

 Error Type: SystemError
 Error Value: Excessive recursion

 ?
Anonymous User - Sep. 26, 2002 1:35 am:
 :) thats the idea! Cary on reading..

This looks simple enough, the idea is, this is an HTML page that calls itself. This is because the HTML action is the URL variable, which will become the URL of the DTML Document.

If there is a zooName variable, then the page will print it, if there isn't, it shows a form that asks for it. When you click submit, the data you enter will make the "if" evaluate to true, and this code should print what entered in the form.

Anonymous User - May 24, 2002 11:26 am:
 I think you mean: 
 what is entered
Anonymous User - July 18, 2002 9:52 pm:
 ...was entered...

 past tense

But unfortunately, this is one of those instances where DTML will not do what you mean, because the name of the DTML Document that contains this DTML is also named zooName, and it doesn't use the variable out of the request, it uses itself, which causes it call itself and call itself, ad infinitum, until you get an "excessive recursion" error. So instead of doing what you really meant, you got an error. This is what confuses beginners. In the next couple sections, we'll show you how to fix this example to do what you mean.

Anonymous User - Nov. 11, 2002 1:29 am:
 Missing words:

 "causes it call" should be "causes it to call"

 and

 "next couple sections" should be "next couple of sections"

 Hope that helps.
Anonymous User - Aug. 5, 2003 11:57 pm:
 gee..... thanks....

How Variables are Looked up

There are actually two ways to fix the DTML error in the zooName document. The first is that you can rename the document to something like zopeNameFormOrReply and always remember this special exception and never do it; never knowing why it happens. The second is to understand how names are looked up, and to be explicit about where you want the name to come from in the namespace.

The DTML namespace is a collection of objects arranged in a stack. A stack is a list of objects that can be manipulated by pushing and popping objects on to and off of the stack.

When a DTML Document or DTML Method is executed, Zope creates a DTML namespace to resolve DTML variable names. It's important to understand the workings of the DTML namespace so that you can accurately predict how Zope will locate variables. Some of the trickiest problems you will run into with DTML can be resolved by understanding the DTML namespace.

Anonymous User - Sep. 24, 2002 12:14 pm:
 /is executed/called for execution/
The namespace is constructed upon the *event* of a call; execution itself is a *sequence of events* in time*.

When Zope looks for names in the DTML namespace stack it first looks at the very top most object in the stack. If the name can't be found there, then the next item down is looked in. Zope will work its way down the stack, checking each object in turn until it finds the name that it is looking for.

Anonymous User - July 12, 2002 12:11 am:
 topmost
Anonymous User - Aug. 8, 2002 3:03 pm:
 Neither "very top most" (sic) or "topmost" say anything more than a simple "top"
Anonymous User - Sep. 24, 2002 12:37 pm:
 Look up *in* an object: Explain:
 Python objects have a namespace for their components ("__dict__" ?); also the name of the class the object
 was instantiated from; the classes namespace (classes are objects, too) contains the names of its parent
 classes.
 All these objects nested namespaces are looked up, and in that order!

If Zope gets all the way down to the bottom of the stack and can't find what it is looking for, then an error is generated. For example, try looking for the non-existent name, unicorn:


<dtml-var unicorn>
Anonymous User - Jan. 5, 2003 2:47 pm:
 Worth mentioning that you are growing your stack up from the bottom. New names are pushed onto the top while
called routines execute. Later, the names will be popped off the top again, shrinking the stack, later as the
 routine exits.
 (My stacks grow down, from my days assembly programming :) ).

As long as there is no variable named unicorn viewing this DTML will return an error, as shown in [7-1].

DTML error message indicating that it cannot find a
    variable.

Figure 7-1 DTML error message indicating that it cannot find a variable.

But the DTML stack is not all there is to names because DTML doesn't start with an empty stack, before you even begin executing DTML in Zope there are already a number of objects pushed on the namespace stack.

Anonymous User - Sep. 24, 2002 12:41 pm:
 paragraph superficial. 
 BTW, the DTML namespace *is* all there ist to lookup!
Anonymous User - Nov. 11, 2002 1:34 am:
 The paragraph might be superficial, but it does help Newbies.

 Chill a bit.

 :)

DTML Namespaces

DTML namespaces are built dynamically for every request in Zope. When you call a DTML Method or DTML Document through the web, the DTML namespace starts with the same first two stack elements the client object and the request as shown in [7-2]

Anonymous User - July 18, 2002 9:54 pm:
 ..first two stack elements; the client object...
Anonymous User - July 18, 2002 9:55 pm:
 ...the request, as shown in Figure 7-2.
Anonymous User - Sep. 24, 2002 12:29 pm:
 ... starts with two objects: at the bottom the *client* object and next the *REQUEST* object as show in
 Figure 7-2.
Anonymous User - Sep. 24, 2002 12:32 pm:
 error, sorry, should read
 ... at the bottom the *REQUEST* object and next the *client* object ...
 it implies that the REQUEST is the last place where objects are looked up.

Initial DTML namespace stack.

Figure 7-2 Initial DTML namespace stack.

The client object is the first object on the top of the DTML namespace stack. What the client object is depends on whether or not you are executing a DTML Method or a DTML Document. In our example above, this means that the client object is named zooName. Which is why it breaks. The form input that we really wanted comes from the web request, but the client is looked at first.

Anonymous User - Sep. 24, 2002 12:45 pm:
 The client object is the first object on the top of the DTML namespace stack.
 Nope. 
 The client object is topmost only as long as nothing else is pushed onto the namespace stack. The namespace
 stack may grow and shrink during processing of a request.
charlesz - Dec. 1, 2002 6:53 pm:
 "whether or not you are executing a DTML Method or a DTML Document" is just a little ambiguous. Does it mean
 "whether your are executing a DTML Metod or DTML Document"?

The request namespace is always on the bottom of the DTML namespace stack, and is therefore the last namespace to be looked in for names. This means that we must be explicit in our example about which namespace we want. We can do this with the DTML with tag:


<dtml-var standard_html_header>

  <dtml-with REQUEST only>
    <dtml-if zooName>
      <p><dtml-var zooName></p>
    <dtml-else>
      <form action="<dtml-var URL>" method="GET">
        <input name="zooName">
        <input type="submit" value="What is zooName?">
      </form>
    </dtml-if>
  </dtml-with>

<dtml-var standard_html_footer>
Anonymous User - July 18, 2002 9:57 pm:
 Unfortunately, it's also going to look in just the REQUEST namespace for the URL variable. For a URL this is
 probably ok, as it should be part of the REQUEST, but don't get tripped up.
Anonymous User - July 24, 2002 10:47 pm:
 How do you get items into the REQUEST namespace? And is there a complete reference for dealing exclusively
 with this item alone?
mcdonc - July 25, 2002 9:35 am:
 REQUEST.set will work.  See the Zope help system API reference for the REUQUEST object.
Anonymous User - Sep. 24, 2002 12:56 pm:
Well, the basic effect of <dtml-with ...> is to push another object onto the namespace, with
<dtml-with
 object only> restricting the lookup to just this object.
While sufficient to make the example work, this doesn't explain dtml-with (although it makes the impression).
 Forward reference, see *Modifying the DTML Namespace*.

Here, the with tag says to look in the REQUEST namespace, and only the REQUEST namespace, for the name "zooName".

DTML Client Object

The client object in DTML depends on whether or not you are executing a DTML Method or a DTML Document. In the case of a Document, the client object is always the document itself, or in other words, a DTML Document is its own client object.

A DTML Method however can have different kinds of client objects depending on how it is called. For example, if you had a DTML Method that displayed all of the contents of a folder then the client object would be the folder that is being displayed. This client object can change depending on which folder the method in question is displaying. For example, consider the following DTML Method named list in the root folder:


<dtml-var standard_html_header>

<ul>
<dtml-in objectValues>
  <li><dtml-var title_or_id></li>
</dtml-in>
</ul>

<dtml-var standard_html_footer>

Now, what this method displays depends upon how it is used. If you apply this method to the Reptiles folder with the URL http://localhost:8080/Reptiles/list, then you will get something that looks like [7-3].

Applying the list method to the Reptiles
      folder.

Figure 7-3 Applying the list method to the Reptiles folder.

Anonymous User - Sep. 24, 2002 1:12 pm:
 picture too large for the illustrating purpose

But if you were to apply the method to the Birds folder with the URL http://localhost:8080/Birds/list then you would get something different, only two items in the list, Parrot and Raptors.

Anonymous User - Sep. 24, 2002 1:04 pm:
 /, only two items in the list,/, namely/ # "only" implies we would expect more

Same DTML Method, different results. In the first example, the client object of the list method was the Reptiles folder. In the second example, the client object was the Birds folder. When Zope looked up the objectValues variable, in the first case it called the objectValues method of the Reptiles folder, in the second case it called the objectValues method of the Birds folder.

In other words, the client object is where variables such as methods, and properties are looked up first.

Anonymous User - Sep. 24, 2002 1:08 pm:
 "first": only as long as the namespace stack doesn't grow.
 Forward Section "Modifying the DTML Namespace" will modify above sentence also!

As you saw in Chapter 4, "Dynamic Content with DTML", if Zope cannot find a variable in the client object, it searches through the object's containers. Zope uses acquisition to automatically inherit variables from the client object's containers. So when Zope walks up the object hierarchy looking for variables it always starts at the client object, and works its way up from there.

Anonymous User - Sep. 24, 2002 1:46 pm:
 Since the client object is located in the ZODB, it has a location in the ZODB (denoted by its URL); it also
 implies the object is located in a container.
 In that case, if Zope can not find the variable *in* the client object (*context*), Zope will also look up
 the variable in the *container* of the object.
 ? is acquisition used exclusively for the client object only in the namespace?

 ? when i take http://www.zopeonarope.com/Misc/chapters8-10.pdf page 199-211,
 acquisition not only travels upwards the container hierarchy to the root, 
 but also the PARENT hierarchy given by the URL.
 Some aspects of acquisition are still a mystery for me. Since acquisition is so central, if it is not
 explained proper here, where shall i look?
 "Zope uses acquisition to ... *inherit*": Inheritance is AFAIK already used for class derivation.
Anonymous User - Sep. 24, 2002 1:51 pm:
 ...the PARENT hierarchy given by the URL
 the URL which initiated the request. This url is not necessarily the url of the client object! The URL is
 used to *place objects* for one call!

DTML Request Object

The request object is the very bottom most object on the DTML namespace stack. The request contains all of the information specific to the current web request.

Anonymous User - July 12, 2002 12:36 am:
 bottommost
Anonymous User - Aug. 18, 2002 7:45 am:
 ...object on the bottom??? --RAW--
Anonymous User - Sep. 24, 2002 1:56 pm:
 /very // # The request object is at the bottom. Cant go below bottom.
Anonymous User - Sep. 24, 2002 2:01 pm:
 /the very bottom most/at the bottom / 
 # The request object is at the bottom. Cant go below bottom (very most).
 # also: a stack *has* a bottom, the request *is at* the bottom

Just as the client object uses acquisition to look in a number of places for variables, so too the request looks up variables in a number of places. When the request looks for a variable it consults these sources in order:

  1. The CGI environment. The Common Gateway Interface, or CGI interface defines a standard set of environment variables to be used by dynamic web scripts. These variables are provided by Zope in the REQUEST namespace.
  2. Form data. If the current request is a form action, then any form input data that was submitted with the request can be found in the REQUEST object.
  3. Cookies. If the client of the current request has any cookies these can be found in the current REQUEST object.
  4. Additional variables. The REQUEST namespace provides you with lots of other useful information, such as the URL of the current object and all of its parents.

Anonymous User - Sep. 24, 2002 2:11 pm:
 - neither client object nor request object do the lookup, the DTML interpreter does.
 - the request object is *constructed* before the call to include the following sources
 - Form data: see http://www.zope.org/Members/Zen/howto/FormVariableTypes
   and http://www.dieter.handshake.de/pyprojects/zope/book/chap3.html

The request namespace is very useful in Zope since it is the primary way that clients (in this case, web browsers) communicate with Zope by providing form data, cookies and other information about themselves. For more information about the request object, see Appendix B.

A very simple and enlightening example is to simply print the REQUEST out in an HTML page:


<dtml-var standard_html_header>

<dtml-var REQUEST>

<dtml-var standard_html_footer>
Anonymous User - Aug. 18, 2002 7:49 am:
 should HTML page be DTML Document ?? --RAW--
Anonymous User - Sep. 24, 2002 2:14 pm:
 It is also a very useful debugging technique.
 The REQUEST objects __call__-method gives a suitable HTML representation string of itself.
Anonymous User - Sep. 24, 2002 2:16 pm:
dtml-var does not print! it inserts a string. HTML output usually is not printed, but displayed by a browser.

Try this yourself, you should get something that looks like [7-4].

Displaying the request.

Figure 7-4 Displaying the request.

Since the request comes after the client object, if there are names that exist in both the request and the client object, DTML will always find them first in the client object. This can be a problem. Next, let's look at some ways to get around this problem by controlling more directly how DTML looks up variables.

Rendering Variables

When you insert a variable using the var tag, Zope first looks up the variable using the DTML namespace, it then renders it and inserts the results. Rendering means turning an object or value into a string suitable for inserting into the output. Zope renders simple variables by using Python's standard method for coercing objects to strings. For complex objects such as DTML Methods and SQL Methods, Zope will call the object instead of just trying to turn it into a string. This allows you to insert DTML Methods into other DTML Methods.

In general Zope renders variables in the way you would expect. It's only when you start doing more advanced tricks that you become aware of the rendering process. Later in this chapter we'll look at some examples of how to control rendering using the getitem DTML utility function.

Modifying the DTML Namespace

Now that you have seen that the DTML namespace is a stack, you may be wondering how, or even why, new objects get pushed onto it.

Anonymous User - Sep. 24, 2002 2:24 pm:
 /Now that you have seen that /Since [we said that]/ # aint seen yet
Anonymous User - Sep. 24, 2002 2:26 pm:
 Twice before there were implicit (=non obvious) references to here!

Some DTML tags modify the DTML namespace while they are executing. A tag may push some object onto the namespace stack during the course of execution. These tags include the in tag, the with tag, and the let tag.

In Tag Namespace Modifications

When the in tag iterates over a sequence it pushes the current item in the sequence onto the top of the namespace stack:


<dtml-var getId> <!-- This is the id of the client object -->

<dtml-in objectValues>

  <dtml-var getId> <!-- this is the id of the current item in the 
                     objectValues sequence -->
</dtml-in>

You've seen this many times throughout the examples in this book. While the in tag is iterating over a sequence, each item is pushed onto the namespace stack for the duration of the contents of the in tag block. When the block is finished executing, the current item in the sequence is popped off the DTML namespace stack and the next item in the sequence is pushed on.

htrd - Apr. 24, 2002 9:03 am:
 (Re the changes from http://collector.zope.org/Zope/358 that I am about to merge) 

 Use the optional "no_push_item" to inhibit "dtml-in" pushing each item onto the stack in this way. 
 You may want to do this to access names that are already in the DTML namespace, without worrying 
 about whether the same name is also provided by the objects in the sequence.
Anonymous User - Sep. 24, 2002 2:32 pm:
 More exactly: an iteration object is pushed. This iteration object contains the current object in the
 iteration along with a plethora of variables describing the iteration state (sequence-index and the like).

The With Tag

The with tag pushes an object that you specify onto the top of the namespace stack for the duration of the with block. This allows you to specify where variables should be looked up first. When the with block closes, the object is popped off the namespace stack.

Consider a folder that contains a bunch of methods and properties that you are interested in. You could access those names with Python expressions like this:


<dtml-var standard_html_header>

<dtml-var expr="Reptiles.getReptileInfo()">
<dtml-var expr="Reptiles.reptileHouseMaintainer">

<dtml-in expr="Reptiles.getReptiles()">
  <dtml-var species>
</dtml-in>

<dtml-var standard_html_footer>

Notice that a lot of complexity is added to the code just to get things out of the Reptiles folder. Using the with tag you can make this example much easier to read:


<dtml-var standard_html_header>

<dtml-with Reptiles>

  <dtml-var getReptileInfo>
  <dtml-var reptileHouseMaintainer>

  <dtml-in getReptiles>
    <dtml-var species>
  </dtml-in>

</dtml-with>

<dtml-var standard_html_footer>
Anonymous User - Aug. 21, 2002 10:59 am:
 Somehow the get()-function is not interpreted correctly. I always get the following error message:
 Error Type: KeyError
 Error Value: getReptiles
 Why??
krump - Aug. 27, 2002 1:54 pm:
It is not a working example. If there were a function called 'getReptileInfo()' and say a string property for
 the Reptiles folder called 'reptileHouseMaintainer' it might work.

Another reason you might want to use the with tag is to put the request, or some part of the request on top of the namespace stack. For example suppose you have a form that includes an input named id. If you try to process this form by looking up the id variable like so:


<dtml-var id>

You will not get your form's id variable, but the client object's id. One solution is to push the web request's form on to the top of the DTML namespace stack using the with tag:


<dtml-with expr="REQUEST.form">
  <dtml-var id>
</dtml-with>

This will ensure that you get the form's id first. See Appendix B for complete API documentation of the request object.

Anonymous User - Sep. 24, 2002 2:36 pm:
 /for complete/for a complete/

If you submit your form without supplying a value for the id input, the form on top of the namespace stack will do you no good, since the form doesn't contain an id variable. You'll still get the client object's id since DTML will search the client object after failing to find the id variable in the form. The with tag has an attribute that lets you trim the DTML namespace to only include the object you specify:


<dtml-with expr="REQUEST.form" only>
  <dtml-if id>
    <dtml-var id>
  <dtml-else>
    <p>The form didn't contain an "id" variable.</p>
  </dtml-if>
</dtml-with>

Using the only attribute allows you to be sure about where your variables are being looked up.

Anonymous User - Sep. 24, 2002 2:41 pm:
 ... restricts the lookup to just the object pushed.
 # i dont use *only* for my private feelings, but for its effect in name loookup

The Let Tag

The let tag lets you push a new namespace onto the namespace stack. This namespace is defined by the tag attributes to the let tag:


<dtml-let person="'Bob'" relation="'uncle'">
  <p><dtml-var person>'s your <dtml-var relation>.</p>
</dtml-let>

This would display:


<p>Bob's your uncle.</p>

The let tag accomplishes much of the same goals as the with tag. The main advantage of the let tag is that you can use it to define multiple variables to be used in a block. The let tag creates one or more new variables and their values and pushes a namespace object containing those variables and their values on to the top of the DTML namespace stack. In general the with tag is more useful to push existing objects onto the namespace stack, while the let tag is better suited for defining new variables for a block.

Anonymous User - Sep. 24, 2002 2:50 pm:
 /creates one or more new variables and their values/creates one or more new variables from the name=value
 pairs given/
 /and their values//
 # A variable is a pair (name, value)

When you find yourself writing complex DTML that requires things like new variables, there's a good chance that you could do the same thing better with Python or Perl. Advanced scripting is covered in Chapter 10, "Advanced Zope Scripting".

The DTML namespace is a complex place, and this complexity evolved over a lot of time. Although it helps to understand where names come from, it is much more helpful to always be specific about where you are looking for a name. The with and let tags let you control the namespace to look exactly in the right place for the name you are looking for.

Anonymous User - Sep. 24, 2002 2:54 pm:
 /the namespace/the DTML interpreter/
 # the namespace doesnt (actively) look up, it is (passively) looked up

DTML Namespace Utility Functions

Like all things in Zope, the DTML namespace is an object, and it can can be accessed directly in DTML with the _ (underscore) object. The _ namespace is often referred to as as "the under namespace".

Anonymous User - July 23, 2002 4:28 am:
 can can
Anonymous User - July 24, 2002 3:32 pm:
 as as
Anonymous User - Sep. 24, 2002 3:03 pm:
 # As i understand it, '_' is the name for the _-object.
 # Then better:

 Like all things in Zope, the DTML namespace is an object. It has the special name of "_" (underscore) and it
can be accessed directly in DTML by this name to look up other names. The _ namespace is often referred to as
 as "the under namespace".

The under namespace provides you with many useful methods for certain programming tasks. Let's look at a few of them.

Say you wanted to print your name three times. This can be done with the in tag, but how do you explicitly tell the in tag to loop three times? Just pass it a sequence with three items:


<dtml-var standard_html_header>

<ul>
<dtml-in expr="_.range(3)">
  <li><dtml-var sequence-item>: My name is Bob.</li>
</dtml-in>
</ul>

<dtml-var standard_html_footer>

The

_.range(3)

Python expression will return a sequence of the first three integers, 0, 1, and 2. The

range

function is a

standard Python built-in

and many of Python's built-in functions can be accessed through the

_

namespace, including:

range([start,], stop, [step])
Returns a list of integers from start to stop counting step integers at a time. start defaults to 0 and step defaults to 1. For example:

'_.range(3,9,2)' -- gives '[3,5,7,9]'.

'len(sequence)' -- 'len' returns the size of *sequence* as an integer.

Many of these names come from the Python language, which contains a set of special functions called built-ins. The Python philosophy is to have a small, set number of built-in names. The Zope philosphy can be thought of as having a large, complex array of built-in names.

Anonymous User - Apr. 17, 2002 3:34 am:
 mispelling. should be: philosophy
Anonymous User - June 27, 2002 7:40 pm:
 '_.len(sequence)' instead of 'len(sequence)' -VS
Anonymous User - Sep. 24, 2002 4:05 pm:
 /small, set number/ # typo? do you mean /small, fixed number/?
Anonymous User - Nov. 28, 2002 6:15 pm:
 Or just "small number"  its neither fixed nor set for all time the philosophy is just to keep it small.

The under namespace can also be used to explicitly control variable look up. There is a very common usage of this syntax. You've seen that the in tag defines a number of special variables, like sequence-item and sequence-key that you can use inside a loop to help you display and control it. What if you wanted to use one of these variables inside a Python expression?:


<dtml-var standard_html_header>

<h1>The squares of the first three integers:</h1>
<ul>
<dtml-in expr="_.range(3)">
  <li>The square of <dtml-var sequence-item> is: 
    <dtml-var expr="sequence-item * sequence-item">
  </li>
</dtml-in>  
</ul>  

<dtml-var standard_html_footer>

Try this, does it work? No! Why not? The problem lies in this var tag:


<dtml-var expr="sequence-item * sequence-item">

Remember, everything inside a Python expression attribute must be a valid Python expression. In DTML, sequence-item is the name of a variable, but in Python this means "The object sequence minus the object item". This is not what you want.

Anonymous User - Sep. 24, 2002 4:20 pm:
 To make a long explanation short:
 Python names have the familiar structure excluding special characters, 
 ("start with letter, follow with letter or digit where '_' counts as letter")
 while DTML names may contain any character, including the hyphen which in python is the minus-operator.

What you really want is to look up the variable sequence-item. One way to solve this problem is to use the in tag prefix attribute. For example:


<dtml-var standard_html_header>

<h1>The squares of the first three integers:</h1>
<ul>
<dtml-in prefix="loop" expr="_.range(3)">
  <li>The square of <dtml-var loop_item> is: 
    <dtml-var expr="loop_item * loop_item">
  </li>
</dtml-in>  
</ul>  

<dtml-var standard_html_footer>   

The prefix attribute causes in tag variables to be renamed using the specified prefix and underscores, rather than using "sequence" and dashes. So in this example, "sequence-item" becomes "loop_item". See Appendix A for more information on the prefix attribute.

Another way to look up the variable sequence-item in a DTML expression is to use the getitem utility function to explicitly look up a variable:


The square of <dtml-var sequence-item> is:
<dtml-var expr="_.getitem('sequence-item') * 
                _.getitem('sequence-item')">
Anonymous User - Sep. 24, 2002 4:29 pm:
 What abt the often seen

 <dtml-var expr="_['sequence-item']">

The getitem function takes the name to look up as its first argument. Now, the DTML Method will correctly display the sum of the first three integers. The getitem method takes an optional second argument which specifies whether or not to render the variable. Recall that rendering a DTML variable means turning it into a string. By default the getitem function does not render a variable.

Anonymous User - Aug. 22, 2002 5:10 am:
 It's not the sum but rather the square of the first three integers.

Here's how to insert a rendered variable named myDoc:


<dtml-var expr="_.getitem('myDoc', 1)">

This example is in some ways rather pointless, since it's the functional equivalent to:


<dtml-var myDoc>

However, suppose you had a form in which a user got to select which document they wanted to see from a list of choices. Suppose the form had an input named selectedDoc which contained the name of the document. You could then display the rendered document like so:


<dtml-var expr="_.getitem(selectedDoc, 1)">
Anonymous User - Aug. 22, 2002 7:04 am:
 Does this work for subFOLDERS as well?

Notice in the above example that selectedDoc is not in quotes. We don't want to insert the variable named selectedDoc we want to insert the variable named by selectedDoc. For example, the value of selectedDoc might be chapterOne. Using indirect variable insertion you can insert the chapterOne variable. This way you can insert a variable whose name you don't know when you are authoring the DTML.

If you a python programmer and you begin using the more complex aspects of DTML, consider doing a lot of your work in Python scripts that you call from DTML. This is explained more in Chapter 10, "Advanced Zope Scripting". Using Python sidesteps many of the issues in DTML.

Anonymous User - Apr. 28, 2002 8:48 pm:
 If I be a ...

DTML Security

Zope can be used by many different kinds of users. For example, the Zope site, Zope.org, has over 11,000 community members at the time of this writing. Each member can log into Zope, add objects and news items, and manage their own personal area.

Because DTML is a scripting language, it is very flexible about working with objects and their properties. If there were no security system that constrained DTML then a user could potentially create malicious or privacy-invading DTML code.

DTML is restricted by standard Zope security settings. So if you don't have permission to access an object by going to its URL you also don't have permission to access it via DTML. You can't use DTML to trick the Zope security system.

For example, suppose you have a DTML Document named Diary which is private. Anonymous users can't access your diary via the web. If an anonymous user views DTML that tries to access your diary they will be denied:


<dtml-var Diary>

DTML verifies that the current user is authorized to access all DTML variables. If the user does not have authorization, than the security system will raise an Unauthorized error and the user will be asked to present more privileged authentication credentials.

Anonymous User - July 5, 2002 12:50 pm:
 "If the user ..., than" should be "If the user ..., then"
 "than" is a comparison (this is greater than that)
 "then" is temporal (if this happens then this other thing happens next)

In Chapter 7, "Users and Security" you read about security rules for executable content. There are ways to tailor the roles of a DTML Document or Method to allow it to access restricted variables regardless of the viewer's roles.

Safe Scripting Limits

DTML will not let you gobble up memory or execute infinite loops and recursions. Because the restrictions on looping and memory use are relatively tight, DTML is not the right language for complex, expensive programming logic. For example, you cannot create huge lists with the _.range utility function. You also have no way to access the filesystem directly in DTML.

Anonymous User - Sep. 24, 2002 4:43 pm:
 The _*_.range*_ function is a restricted version of the (python) range function.

Keep in mind however that these safety limits are simple and can be outsmarted by a determined user. It's generally not a good idea to let anyone you don't trust write DTML code on your site.

Advanced DTML Tags

In the rest of this chapter we'll look at the many advanced DTML tags. These tags are summarized in Appendix A. DTML has a set of built-in tags, as documented in this book, which can be counted on to be present in all Zope installations and perform the most common kinds of things. However, it is also possible to add new tags to a Zope installation. Instructions for doing this are provided at the Zope.org web site, along with an interesting set of contributed DTML tags.

Anonymous User - Sep. 24, 2002 5:02 pm:
 Although it goes a little deeper than previous chapters still stays in shallow waters. Dont waste time, goto
 Appendix A. As to expanding zope with new tags,
 at least some hrefs would be ok here, if you dont want to burden readers of this "Advanced" chapter w
 advanced knowledge.

This section covers what could be referred to as Zope miscellaneous tags. These tags don't really fit into any broad categories except for one group of tags, the exception handling DTML tags which are discussed at the end of this chapter.

The Call Tag

The var tag can call methods, but it also inserts the return value. Using the call tag you can call methods without inserting their return value into the output. This is useful if you are more interested in the effect of calling a method rather than its return value.

Anonymous User - Sep. 24, 2002 5:05 pm:
 The *side effect*

For example, when you want to change the value of a property, animalName, you are more interested in the effect of calling the manage_changeProperties method than the return value the method gives you. Here's an example:


<dtml-if expr="REQUEST.has_key('animalName')">
  <dtml-call expr="manage_changeProperties(animalName=REQUEST['animalName'])">
  <h1>The property 'animalName' has changed</h1>
<dtml-else>
  <h1>No properties were changed</h1>
</dtml-if>
Anonymous User - Jan. 22, 2003 11:25 am:
 aix� no funciona, ninot !

In this example, the page will change a property depending on whether a certain name exists. The result of the manage_changeProperties method is not important and does not need to be shown to the user.

Another common usage of the call tag is calling methods that affect client behavior, like the RESPONSE.redirect method. In this example, you make the client redirect to a different page, to change the page that gets redirected, change the value for the "target" variable defined in the let tag:


<dtml-var standard_html_header>

<dtml-let target="'http://example.com/new_location.html'">

  <h1>This page has moved, you will now be redirected to the
  correct location.  If your browser does not redirect, click <a
  href="<dtml-var target>"><dtml-var target></a>.</h1>

  <dtml-call expr="RESPONSE.redirect(target)">

</dtml-let>

<dtml-var standard_html_footer>  

In short, the call tag works exactly like the var tag with the exception that it doesn't insert the results of calling the variable.

Anonymous User - Aug. 10, 2002 1:58 am:
 How would you use &lt;dtml-call&gt; to call a ZSQL Method and pass it some input?
Anonymous User - Sep. 24, 2002 5:12 pm:
 Or call a Python Script to preprocess a REQUEST 
 (say, setting defaults, checking previous form values)?

 So far in this book i have not seen a complete discussion of calling callables including parameter passing.
Anonymous User - Dec. 19, 2002 4:30 pm:
 For the ZSQL Method:
 <dtml-call "ZSQL_methodName(
              variableName1=variableValue1
             ,variableName2=variableValue2
            )">

 For calling a python script to preset a variable in the REQUEST object:
 <dtml-call "REQUEST.set('variableName',pythonScriptName(arguments))">

The Comment Tag

DTML can be documented with comments using the comment tag:


<dtml-var standard_html_header>

<dtml-comment>

  This is a DTML comment and will be removed from the DTML code
  before it is returned to the client.  This is useful for
  documenting DTML code.  Unlike HTML comments, DTML comments
  are NEVER sent to the client.

</dtml-comment>

<!-- 

  This is an HTML comment, this is NOT DTML and will be treated
  as HTML and like any other HTML code will get sent to the
  client.  Although it is customary for an HTML browser to hide
  these comments from the end user, they still get sent to the
  client and can be easily seen by 'Viewing the Source' of a
  document.

-->

<dtml-var standard_html_footer>        
Anonymous User - Apr. 29, 2002 1:57 pm:
 It doesnt render the HTML, but Zope does validate the code, so commenting on partial DTML *produces errors*
even though it is commented out. Zope tries to validate all the code, even though it does not render it. This
 is counter-intuitive and cripples good documentation practices.
 Zope collector Issue 370
 http://collector.zope.org/Zope/370

The comment block is removed from DTML output.

In addition to documenting DTML you can use the comment tag to temporarily comment out other DTML tags. Later you can remove the comment tags to re-enable the DTML.

gl03 - Apr. 26, 2002 9:31 am:
 commented dtml is still parsed to the effect that you cannot save bad dtml even if it's commented out!
 (wouldn't mind seeing this changed...)
Anonymous User - July 19, 2002 4:01 am:
 Especially as progressively commenting less and less of a document is a well understood and oft-used method
 for debugging
Anonymous User - Sep. 24, 2002 5:16 pm:
 Hint: since browsers hide <...> when they dont see a tag, 
 commenting out single dtml tags just put an x before "dtml" like

 <xdtml-var blah>.

The Tree Tag

The tree tag lets you easily build dynamic trees in HTML to display hierarchical data. A tree is a graphical representation of data that starts with a "root" object that has objects underneath it often referred to as "branches". Branches can have their own branches, just like a real tree. This concept should be familiar to anyone who has used a file manager program like Microsoft Windows Explorer to navigate a file system. And, in fact, the left hand "navigation" view of the Zope management interface is created using the tree tag.

For example here's a tree that represents a collection of folders and sub-folders.

HTML tree generated by the tree tag.

Figure 7-5 HTML tree generated by the tree tag.

Here's the DTML that generated this tree display:


<dtml-var standard_html_header>

<dtml-tree>

  <dtml-var getId>

</dtml-tree>

<dtml-var standard_html_footer>

The tree tag queries objects to find their sub-objects and takes care of displaying the results as a tree. The tree tag block works as a template to display nodes of the tree.

Now, since the basic protocol of the web, HTTP, is stateless, you need to somehow remember what state the tree is in every time you look at a page. To do this, Zope stores the state of the tree in a cookie. Because this tree state is stored in a cookie, only one tree can appear on a web page at a time, otherwise they will confusingly use the same cookie.

You can tailor the behavior of the

tree

tag quite a bit with

tree

tag attributes and special variables. Here is a sampling of

tree

tag attributes.

branches
The name of the method used to find sub-objects. This defaults to tpValues, which is a method defined by a number of standard Zope objects.

leaves
The name of a method used to display objects that do not have sub-object branches.

nowrap
Either 0 or 1. If 0, then branch text will wrap to fit in available space, otherwise, text may be truncated. The default value is 0.

sort
Sort branches before text insertion is performed. The attribute value is the name of the attribute that items should be sorted on.

assume_children
Either 0 or 1. If 1, then all objects are assumed to have sub-objects, and will therefore always have a plus sign in front of them when they are collapsed. Only when an item is expanded will sub-objects be looked for. This could be a good option when the retrieval of sub-objects is a costly process. The defalt value is 0.

single
Either 0 or 1. If 1, then only one branch of the tree can be expanded. Any expanded branches will collapse when a new branch is expanded. The default value is 0.

skip_unauthorized
Either 0 or 1. If 1, then no errors will be raised trying to display sub-objects for which the user does not have sufficient access. The protected sub-objects are not displayed. The default value is 0.

Suppose you want to use the tree tag to create a dynamic site map. You don't want every page to show up in the site map. Let's say that you put a property on folders and documents that you want to show up in the site map.

Let's first define a Script with the id of publicObjects that returns public objects:


## Script (Python) "publicObjects"
##
"""
Returns sub-folders and DTML documents that have a
true 'siteMap' property.
"""
results=[]
for object in context.objectValues(['Folder', 'DTML Document']):
    if object.hasProperty('siteMap') and object.siteMap:
        results.append(object)
return results

Now we can create a DTML Method that uses the tree tag and our Scripts to draw a site map:


<dtml-var standard_html_header>

<h1>Site Map</h1>

<p><a href="&dtml-URL0;?expand_all=1">Expand All</a> |
   <a href="&dtml-URL0;?collapse_all=1">Collapse All</a>
</p>

<dtml-tree branches="publicObjects" skip_unauthorized="1">
  <a href="&dtml-absolute_url;"><dtml-var title_or_id></a>
</dtml-tree>

<dtml-var standard_html_footer>
Anonymous User - Sep. 21, 2002 4:32 am:
 <dtml-with AnyRootFolder>
   <dtml-tree branches="getSubFolders">
     <a href="&dtml-absolute_url;"><dtml-var title_or_id></a>
   </dtml-tree>
 </dtml-with>

 The tree is rooted in current Folder, but not in AnyRootFolder.
 Why ???
Anonymous User - Sep. 26, 2002 5:32 am:
 (Zope 2.5.1 (source release, python 2.1, linux2), python 2.1.3, linux2)
 i needed:

 <dtml-tree branches_expr="publicObjects()" skip_unauthorized="1">

 to make it work.
Saman - Nov. 17, 2002 7:31 am:
 this dose not work..I can't figre it out

 the only thin I get is:

 Site Map

 Expand All  Collapse All

 it seems that the script is never called????
Anonymous User - Jan. 1, 2003 4:56 am:
 How can I do if I want elements of my array to display as tree?  For example,

 -Quotation
 | |-+Add Quotation
 | |-+Edit Quotation
 -Invoice
 | |-+Add Invoice
 | |-+Approve Invoice
 -Exit
Anonymous User - Jan. 1, 2003 5:08 am:
 And is it possible to parse a file with XML content as tree?  And how?

This DTML Method draws a link to all public resources and displays them in a tree. Here's what the resulting site map looks like.

Dynamic site map using the tree tag.

Figure 7-6 Dynamic site map using the tree tag.

For a summary of the tree tag arguments and special variables see Appendix A.

The Return Tag

In general DTML creates textual output. You can however, make DTML return other values besides text. Using the return tag you can make a DTML Method return an arbitrary value just like a Python or Perl-based Script.

Here's an example:


<p>This text is ignored.</p>

<dtml-return expr="42">

This DTML Method returns the number 42.

Another upshot of using the return tag is that DTML execution will stop after the return tag.

Anonymous User - Sep. 24, 2002 4:51 pm:
 This allows to avoid expensive computations after RESPONSE.redirect like

 <dtml-call "RERSPONSE.redirect( some_url )">
 <dtml-return>

If you find yourself using the return tag, you almost certainly should be using a Script instead. The return tag was developed before Scripts, and is largely useless now that you can easily write scripts in Python and Perl.

The Sendmail Tag

The sendmail tag formats and sends a mail messages. You can use the sendmail tag to connect to an existing Mail Host, or you can manually specify your SMTP host.

Anonymous User - Aug. 26, 2002 1:18 pm:
 Yes, but how do you specify which existing mail host - or how do you specify SMTP manually???

Here's an example of how to send an email message with the sendmail tag:


<dtml-sendmail>
To: <dtml-var recipient>
Subject: Make Money Fast!!!!

Take advantage of our exciting offer now! Using our exclusive method
you can build unimaginable wealth very quickly. Act now!
</dtml-sendmail>
Anonymous User - May 13, 2002 4:49 pm:
 I guess there is a missing 'From:' prompt after 'To:'.
Anonymous User - July 19, 2002 4:09 am:
 From: is probably not necessary

in most cases (I don't know whether this is the case with zope, but it would make sense) from defaults to the
 user which the webserver runs as (e.g. nobody/httpd/apache/whatever)

Notice that there is an extra blank line separating the mail headers from the body of the message.

A common use of the sendmail tag is to send an email message generated by a feedback form. The sendmail tag can contain any DTML tags you wish, so it's easy to tailor your message with form data.

The Mime Tag

The mime tag allows you to format data using MIME (Multipurpose Internet Mail Extensions). MIME is an Internet standard for encoding data in email message. Using the mime tag you can use Zope to send emails with attachments.

Suppose you'd like to upload your resume to Zope and then have Zope email this file to a list of potential employers.

Here's the upload form:


<dtml-var standard_html_header>

<p>Send you resume to potential employers</p>

<form method=post action="sendresume" ENCTYPE="multipart/form-data">
<p>Resume file: <input type="file" name="resume_file"></p>
<p>Send to:</p>
<p>
<input type="checkbox" name="send_to:list" value="[email protected]">
  Yahoo<br>

<input type="checkbox" name="send_to:list" value="[email protected]">
  Microsoft<br>

<input type="checkbox" name="send_to:list" value="[email protected]">
  McDonalds</p>

<input type=submit value="Send Resume">
</form>

<dtml-var standard_html_footer>
Anonymous User - Oct. 16, 2002 8:53 pm:
 Why does the name field of the checkbox contain the ":list" part? What does that do?
Anonymous User - Jan. 5, 2003 3:53 pm:
 This is standard HTML, to tell the browser that all these options belong to the SAME check box widget on
 screen.

Create another DTML Method called sendresume to process the form and send the resume file:


<dtml-var standard_html_header>

<dtml-if send_to>

  <dtml-in send_to> 

    <dtml-sendmail smtphost="my.mailserver.com">
    To: <dtml-var sequence-item>
    Subject: Resume
    <dtml-mime type=text/plain encode=7bit>

    Hi, please take a look at my resume.

    <dtml-boundary type=application/octet-stream disposition=attachment 
    encode=base64><dtml-var expr="resume_file.read()"></dtml-mime>
    </dtml-sendmail>

  </dtml-in>

  <p>Your resume was sent.</p>

<dtml-else>

  <p>You didn't select any recipients.</p>

</dtml-if>

<dtml-var standard_html_footer>    
Anonymous User - June 27, 2002 8:13 am:
 I've experencied problem without 'From:' prompt. Without it, the error was :

 "Error Type: TypeError
 Error Value: len() of unsized object"
m2ic - June 27, 2002 10:45 am:
 Be care also with trailing spaces

This method iterates over the sendto variable and sends one email for each item.

Notice that there is no blank line between the To: header and the starting mime tag. If a blank line is inserted between them then the message will not be interpreted as a multipart message by the receiving mail reader.

Also notice that there is no newline between the boundary tag and the var tag, or the end of the var tag and the closing mime tag. This is important, if you break the tags up with newlines then they will be encoded and included in the MIME part, which is probably not what you're after.

As per the MIME spec, mime tags may be nested within mime tags arbitrarily.

Anonymous User - Nov. 26, 2002 9:07 am:
How is this done for multiple file attachments? Looping over the boundary tag seems not to be allowed.
Looping
 inside the boundary tag causes all attachments to be placed in a single file. Looping over the mime or
 sendmail tags causes separate messages to be sent. Also, how do you specify the filenames of the attachments
 - they are given internally generated names by default.

The Unless Tag

The unless tag executes a block of code unless the given condition is true. The unless tag is the opposite of the if tag. The DTML code:


<dtml-if expr="not butter">
  I can't believe it's not butter.
</dtml-if>

is equivalent to:


<dtml-unless expr="butter">
  I can't believe it's not butter.
</dtml-unless>

What is the purpose of the unless tag? It is simply a convenience tag. The unless tag is more limited than the if tag, since it cannot contain an else or elif tag.

Like the if tag, calling the unless tag by name does existence checking, so:


<dtml-unless the_easter_bunny>
  The Easter Bunny does not exist or is not true.
</dtml-unless>
Anonymous User - Sep. 24, 2002 5:28 pm:
 This comes nice to provide the REQUEST with default variables
 (if they were not set in a previous html form) like

 <dtml-unless "REQUEST.has_key('the_easter_bunny')">
   <dtml-call "REQUEST.set('the_easter_bunny', 0)">
 </dtml-unless>

Checks for the existence of the_easter_bunny as well as its truth. While this example only checks for the truth of the_easter_bunny:


<dtml-unless expr="the_easter_bunny">
  The Easter Bunny is not true.
</dtml-unless>

This example will raise an exception if the_easter_bunny does not exist.

Anonymous User - Sep. 24, 2002 5:40 pm:
 The differentiation between dtml name attributes and expr attributes
 is smeared over chapter 4:
 http://www.zope.org/Documentation/Books/ZopeBook/current/DTML.stx

Anything that can be done by the unless tag can be done by the if tag. Thus, its use is totally optional and a matter of style.

Anonymous User - Sep. 24, 2002 5:43 pm:
 /totally optional and// # totalitarian options, and freedom is slavery

Batch Processing With The In Tag

Often you want to present a large list of information but only show it to the user one screen at a time. For example, if a user queried your database and got 120 results, you will probably only want to show them to the user a small batch, say 10 or 20 results per page. Breaking up large lists into parts is called

batching

. Batching has a number of benefits.

  • The user only needs to download a reasonably sized document rather than a potentially huge document. This makes pages load faster since they are smaller.
  • Because smaller batches of results are being used, often less memory is consumed by Zope.
  • Next and Previous navigation interfaces makes scanning large batches relatively easy.

The in tag provides several variables to facilitate batch processing. Let's look at a complete example that shows how to display 100 items in batches of 10 at a time:


<dtml-var standard_html_header>

  <dtml-in expr="_.range(100)" size=10 start=query_start>

    <dtml-if sequence-start>

      <dtml-if previous-sequence>
        <a href="<dtml-var URL><dtml-var sequence-query
           >query_start=<dtml-var previous-sequence-start-number>">
           (Previous <dtml-var previous-sequence-size> results)
        </a>
      </dtml-if>

      <h1>These words are displayed at the top of a batch:</h1>
      <ul>

    </dtml-if>

      <li>Iteration number: <dtml-var sequence-item></li>

    <dtml-if sequence-end>

      </ul>
      <h4>These words are displayed at the bottom of a batch.</h4>

      <dtml-if next-sequence>
         <a href="<dtml-var URL><dtml-var sequence-query
            >query_start=<dtml-var
            next-sequence-start-number>">
         (Next <dtml-var next-sequence-size> results)
         </a>

      </dtml-if>

    </dtml-if>

  </dtml-in>

<dtml-var standard_html_footer>
Saman - Nov. 17, 2002 8:17 am:
 this must be a DTNL document in order to function,If you shoose to implemented as Method it will not
 function?
 Why is That????

Let's take a look at the DTML to get an idea of what's going on. First we have an in tag that iterates over 100 numbers that are generated by the range utility function. The size attribute tells the in tag to display only 10 items at a time. The start attribute tells the in tag which item number to display first.

Inside the in tag there are two main if tags. The first one tests special variable sequence-start. This variable is only true on the first pass through the in block. So the contents of this if tag will only be executed once at the beginning of the loop. The second if tag tests for the special variable sequence-end. This variable is only true on the last pass through the in tag. So the second if block will only be executed once at the end. The paragraph between the if tags is executed each time through the loop.

Anonymous User - Apr. 28, 2002 7:15 pm:
 This is a HORRIBLE example for usablity and should never be implemented.
Instead, this should show how-to present <Previous and Next> links at the top and bottom of the batch.
Then
the example would need to show NOT to use <dtml-if previous-sequence> or <dtml-if next-sequence>
because they
 dont work.
 Using <dtml-if previous-sequence-start-number>
and <dtml-if next-sequence-start-number> is much more flexible and can be placed in any of the
if-blocks at
 the top or bottom of the batch.
 And then it is still missing an explanantion on how to do batch navigation using the accepted standard of
 "Page" results. Page: 1 2 3 4 Next>
kaleissin - May 16, 2002 3:31 pm:
 One way is to use *two* dtml-ins... One for the header, one for the actual listing of the results. Which
means everything is accessed/done twice. Then maybe you'd like to add the "<Previous 1 2 *3* 4 Next>"
blurb
 *after* the results also, and you use a *third* dtml-in etc... nice excuse to buy a bigger server/faster cpu
 in the end, and lots of money going to your therapist ;)
Anonymous User - Sep. 11, 2002 12:40 pm:
 Here is an example how to do batch navigation using the 
 page 1 2 3 4 ...   Method
 I'm a Zope newbie, so it could possibly done more elegant.

 <dtml-call "REQUEST.set('save_url',_['absolute_url'])">     
 <dtml-in sqlMethod size=10 start=start>
   <dtml-if sequence-start>
     <p>Pages: 
     <dtml-call "REQUEST.set('actual_page',1)">
     <dtml-in previous-batches mapping>   
       <a href="<dtml-var save_url><dtml-var sequence-query>
                start=<dtml-var "_['batch-start-index']+1">">
                <dtml-var sequence-number></a>&nbsp;
       <dtml-call "REQUEST.set('actual_page',_['sequence-number']+1)">     
     </dtml-in>
     <b><dtml-var "_['actual_page']"></b>  
   </dtml-if>

   <dtml-if sequence-end>
     <dtml-in next-batches mapping>&nbsp;
        <a href="<dtml-var save_url><dtml-var sequence-query>
               start=<dtml-var "_['batch-start-index']+1">">
               <dtml-var "_['sequence-number']+_['actual_page']"></a>
      </dtml-in>
   </dtml-if>
  </dtml-in>

Inside each if tag there is another if tag that check for the special variables previous-sequence and next-sequence. The variables are true when the current batch has previous or further batches respectively. In other words previous-sequence is true for all batches except the first, and next-sequence is true for all batches except the last. So the DTML tests to see if there are additional batches available, and if so it draws navigation links.

Anonymous User - Apr. 28, 2002 7:26 pm:
 These nested if's dont work if you use ...

 <dtml-if sequence-start>
             <dtml-if next-sequence>

 or 

 <dtml-if sequence-end>
             <dtml-if previous-sequence>

 Therefore the above paragraph is a bad reccomendation and is very limiting in capability.

The batch navigation consists of links back to the document with a query_start variable set which indicates where the in tag should start when displaying the batch. To better get a feel for how this works, click the previous and next links a few times and watch how the URLs for the navigation links change.

Finally some statistics about the previous and next batches are displayed using the next-sequence-size and previous-sequence-size special variables. All of this ends up generating the following HTML code:


<html><head><title>Zope</title></head><body bgcolor="#FFFFFF">

  <h1>These words are displayed at the top of a batch:</h1>
  <ul>
    <li>Iteration number: 0</li>
    <li>Iteration number: 1</li>
    <li>Iteration number: 2</li>
    <li>Iteration number: 3</li>
    <li>Iteration number: 4</li>
    <li>Iteration number: 5</li>
    <li>Iteration number: 6</li>
    <li>Iteration number: 7</li>
    <li>Iteration number: 8</li>
    <li>Iteration number: 9</li>
  </ul>
  <h4>These words are displayed at the bottom of a batch.</h4>

     <a href="http://pdx:8090/batch?query_start=11">
       (Next 10 results)
     </a>

</body></html>

Batch processing can be complex. A good way to work with batches is to use the Searchable Interface object to create a batching search report for you. You can then modify the DTML to fit your needs. This is explained more in Chapter 11, "Searching and Categorizing Content".

Anonymous User - Apr. 28, 2002 7:22 pm:
 This reference to Chapter 11 is misleading, because it never gets into "batch navigation". The question on
how to present standard search results that include Page numbers is never answered and never again addressed.
 Example:  
 Search Results
 Page: 1 2 3 4 5    Next 10 Results>

Exception Handling Tags

Zope has extensive exception handling facilities. You can get access to these facilities with the raise and try tags. For more information on exceptions and how they are raised and handled see a book on Python or you can read the online Python Tutorial.

The Raise Tag

You can raise exceptions with the raise tag. One reason to raise exceptions is to signal an error. For example you could check for a problem with the if tag, and in case there was something wrong you could report the error with the raise tag.

The raise tag has a type attribute for specifying an error type. The error type is a short descriptive name for the error. In addition, there are some standard error types, like Unauthorized and Redirect that are returned as HTTP errors. Unauthorized errors cause a log-in prompt to be displayed on the user's browser. You can raise HTTP errors to make Zope send an HTTP error. For example:


<dtml-raise type="404">Not Found</dtml-raise>

This raises an HTTP 404 (Not Found) error. Zope responds by sending the HTTP 404 error back to the client's browser.

The raise tag is a block tag. The block enclosed by the raise tag is rendered to create an error message. If the rendered text contains any HTML markup, then Zope will display the text as an error message on the browser, otherwise a generic error message is displayed.

Here is a raise tag example:


<dtml-if expr="balance >= debit_amount">

  <dtml-call expr="debitAccount(account, debit_amount)">

  <p><dtml-var debit_amount> has been deducted from your
  account <dtml-var account>.</p>

<dtml-else>

  <dtml-raise type="Insufficient funds">

    <p>There is not enough money in account <dtml-account> 
    to cover the requested debit amount.</p>

  </dtml-raise>

</dtml-if>

There is an important side effect to raising an exception, exceptions cause the current transaction to be rolled back. This means any changes made by a web request to be ignored. So in addition to reporting errors, exceptions allow you to back out changes if a problem crops up.

Anonymous User - Sep. 24, 2002 5:50 pm:
 /to be/are/
 /back out/back out of/ # not sure

The Try Tag

If an exception is raised either manually with the raise tag, or as the result of some error that Zope encounters, you can catch it with the try tag.

Exceptions are unexpected errors that Zope encounters during the execution of a DTML document or method. Once an exception is detected, the normal execution of the DTML stops. Consider the following example:


Cost per unit: <dtml-var
                     expr="_.float(total_cost/total_units)" 
                     fmt=dollars-and-cents>

This DTML works fine if total_units is not zero. However, if total_units is zero, a ZeroDivisionError exception is raised indicating an illegal operation. So rather than rendering the DTML, an error message will be returned.

You can use the try tag to handle these kind of problems. With the try tag you can anticipate and handle errors yourself, rather than getting a Zope error message whenever an exception occurs.

The try tag has two functions. First, if an exception is raised, the try tag gains control of execution and handles the exception appropriately, and thus avoids returning a Zope error message. Second, the try tag allows the rendering of any subsequent DTML to continue.

Within the try tag are one or more except tags that identify and handle different exceptions. When an exception is raised, each except tag is checked in turn to see if it matches the exception's type. The first except tag to match handles the exception. If no exceptions are given in an except tag, then the except tag will match all exceptions.

Here's how to use the try tag to avoid errors that could occur in the last example:


<dtml-try>

  Cost per unit: <dtml-var 
                       expr="_.float(total_cost/total_units)"
                       fmt="dollars-and-cents">

<dtml-except ZeroDivisionError> 

  Cost per unit: N/A 

</dtml-try> 
Saman - Nov. 17, 2002 8:39 am:
 dont you need to close the except tag: <dtml-except  since you refer to it as a block???

If a ZeroDivisionError is raised, control goes to the except tag, and "Cost per unit: N/A" is rendered. Once the except tag block finishes, execution of DTML continues after the try block.

DTML's except tags work with Python's class-based exceptions. In addition to matching exceptions by name, the except tag will match any subclass of the named exception. For example, if ArithmeticError is named in a except tag, the tag can handle all ArithmeticError subclasses including, ZeroDivisionError. See a Python reference such as the online Python Library Reference for a list of Python exceptions and their subclasses. An except tag can catch multiple exceptions by listing them all in the same tag.

Anonymous User - Nov. 23, 2002 11:43 am:
 You gave a link to the python exceptions, but what's about the Zope's Built-in Exceptions? (ie: The one that
 comes with the authorized errors)

Inside the body of an

except

tag you can access information about the handled exception through several special variables.

error_type
The type of the handled exception.

error_value
The value of the handled exception.

error_tb
The traceback of the handled exception.

You can use these variables to provide error messages to users or to take different actions such as sending email to the webmaster or logging errors depending on the type of error.

The Try Tag Optional Else Block

The try tag has an optional else block that is rendered if an exception didn't occur. Here's an example of how to use the else tag within the try tag:


<dtml-try> 

  <dtml-call feedAlligators>

<dtml-except NotEnoughFood WrongKindOfFood>

  <p>Make sure you have enough alligator food first.</p>

<dtml-except NotHungry> 

  <p>The alligators aren't hungry yet.</p>

<dtml-except> 

  <p>There was some problem trying to feed the alligators.<p>
  <p>Error type: <dtml-var error_type></p>
  <p>Error value: <dtml-var error_value></p>

<dtml-else> 

  <p>The alligator were successfully fed.</p>

</dtml-try> 

The first except block to match the type of error raised is rendered. If an except block has no name, then it matches all raised errors. The optional else block is rendered when no exception occurs in the try block. Exceptions in the else block are not handled by the preceding except blocks.

The Try Tag Optional Finally Block

You can also use the try tag in a slightly different way. Instead of handling exceptions, the try tag can be used not to trap exceptions, but to clean up after them.

The finally tag inside the try tag specifies a cleanup block to be rendered even when an exception occurs.

The finally block is only useful if you need to clean up something that will not be cleaned up by the transaction abort code. The finally block will always be called, whether there is an exception or not and whether a return tag is used or not. If you use a return tag in the try block, any output of the finally block is discarded. Here's an example of how you might use the finally tag:


<dtml-call acquireLock>  
<dtml-try>
    <dtml-call useLockedResource>
<dtml-finally>
    <!-- this always gets done even if an exception is raised -->
    <dtml-call releaseLock>
</dtml-try>

In this example you first acquire a lock on a resource, then try to perform some action on the locked resource. If an exception is raised, you don't handle it, but you make sure to release the lock before passing control off to an exception handler. If all goes well and no exception is raised, you still release the lock at the end of the try block by executing the finally block.

The try/finally form of the try tag is seldom used in Zope. This kind of complex programming control is often better done in Python or Perl.

Anonymous User - July 4, 2002 5:10 am:
 Could you be clear please: does the use of try/except/finally *cancel* the rollback effect mentioned above,
 where the changes made by a request that generates exceptions are cancelled?
 For example: if my page executes an SQL query, but then further down, throws an exception of some sort, my
 query might be rolled back. If I encase the exception-generating code in a try block, and it throws an
 exception, does this mean that my SQL query will *not* be rolled back?

Conclusion

DTML provides some very powerful functionality for designing web applications. In this chapter, we looked at the more advanced DTML tags and some of their options. A more complete reference can be found in Appendix A.

The next chapter teaches you how to become a Page Template wizard. While DTML is a powerful tool, Page Templates provide a more elegant solution to HTML generation.

Previous Page Up one Level Next Page Variables and Advanced DTML Comments On/Off Table of Contents