[Checkins] SVN: zope2book/trunk/ Restify AdvDTML chapter.
Tres Seaver
tseaver at palladion.com
Tue Feb 10 16:58:00 EST 2009
Log message for revision 96425:
Restify AdvDTML chapter.
Changed:
D zope2book/trunk/AdvDTML.stx
A zope2book/trunk/source/AdvDTML.rst
U zope2book/trunk/source/index.rst
-=-
Deleted: zope2book/trunk/AdvDTML.stx
===================================================================
--- zope2book/trunk/AdvDTML.stx 2009-02-10 21:44:22 UTC (rev 96424)
+++ zope2book/trunk/AdvDTML.stx 2009-02-10 21:58:00 UTC (rev 96425)
@@ -1,1619 +0,0 @@
-Advanced DTML
-
- DTML is the kind of language that appears to "do 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, well, it's no fun at all.
- This chapter tells you how to make DTML do what you *really* mean.
- When you're done reading this chapter you will be able to write DTML
- that will accomplish a number of complex tasks including:
-
- o Inspect and Modify the REQUEST object
-
- o Modify the current namespace
-
- o Call other scripts from within DTML
-
- o Send email with or without MIME attachments
-
- o Handle exceptions within DTML
-
- % Anonymous User - Jan. 14, 2004 11:08 am:
- After about a week of reading the Zope Book and listening in on irc.freenode.net#zope , I've discovered that,
- culturally, they're trying to move away from DTML anything and use ZPT. Per
- https://www.philikon.de/wiki/ZopeChannelFAQ :
- --------
- I have problem XYZ with DTML. Help!
- If you are scripting markup such as (X)HTML, use Zope Page Templates (ZPT) instead and slap the person who
- taught you DTML. Read http://zope.org/Documentation/Books/ZopeBook/2_6Edition/AppendixC.stx for more
- information.
- --------
- Don't frustrate yourself by learning too much about DTML. ZPT is more powerful and works with third-party
- HTML editors, GUI and non-GUI.
-
- A few of caveats before getting
- started:
-
- o It's a good idea to know something about Python before diving into
- advanced DTML or any other advanced area of Zope.
-
- o Understand the Zope acquisition model and how it works.
-
- o If you are writing very complex functionality in DTML, consider
- using a Python Script. This will ease maintenance, not to mention
- readability.
-
- o Understand the difference between a DTML Document and a DTML
- Method before embarking on building a huge site. See the explanation
- included in this chapter.
-
- It's no lie that DTML has a reputation for complexity. While it is true
- that DTML is really simple if all you want to do is simple layout,
- using DTML for more advanced tasks requires an understanding of 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>
-
- 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 was entered in the form.
-
- 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 to 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 of sections, we'll show you how to fix this
- example to do what you mean.
-
- 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.
-
- When Zope looks for names in the DTML namespace stack it first looks at
- the topmost object in the stack. If the name can't be found
- there, then the next item down is introspected. Zope will work its way
- down the stack, checking each object in turn until it finds the name
- that it is looking for.
-
- 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>
-
- As long as there is no variable named *unicorn* viewing this
- DTML will return an error, as shown in the figure below.
-
- "DTML error message indicating that it cannot find a
- variable.":img:7-1:Figures/7-1.png
-
- 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.
-
- 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 the figure below.
-
- "Initial DTML namespace stack.":img:7-2:Figures/7-2.png
-
- The client object is the first object on the top of the DTML namespace
- stack when entering a transaction (note: commands exist to push
- additional parameters onto the namespace stack during a thread of
- execution). What the client object is depends on whether 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.
-
- 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>
-
- 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 the figure below.
-
- "Applying the *list* method to the *Reptiles*
- folder.":img:7-3:Figures/7-3.png
-
- 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*.
-
- 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.
-
- As you saw in "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.
-
- DTML Method vs. DTML Document
-
- One of the most potentially confusing choices to make for Zope
- newbies is the choice between a DTML Method and a DTML Document.
- Unfortunately, many Zope newbies develop entire sites using one
- type of object only to discover that they should have used the
- other type. In general, keep the following items in mind when
- deciding upon which type to use:
-
- o **Does the object require properties of its own?** If so,
- use a DTML Document since DTML Methods have no inherent
- properties.
-
- o **Does the object need to be called as a "page"?** If so,
- consider using a DTML Document since it will be easier
- to control such items as page title by using properties.
-
- o **Does the object need transparency to its context?** If so,
- you should probably use a DTML Method since these objects
- act as though they are directly attached to their calling,
- or containing object.
-
- DTML Request Object
-
- The request object is the bottom object on the DTML
- namespace stack. The request contains all of the information
- specific to the current web request.
-
- 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":http://www.w3.org/CGI/, 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.
-
- 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 render the REQUEST
- object in a DTML Document or Method::
-
- <dtml-var standard_html_header>
-
- <dtml-var REQUEST>
-
- <dtml-var standard_html_footer>
-
- Try this yourself, you should get something that looks like
- the figure below.
-
- "Displaying the request.":img:7-4:Figures/7-4.png
-
- 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 know the DTML namespace is a stack, you may
- be wondering how, or even why, new objects get pushed onto it.
-
- 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.
-
- Additional Notes
-
- To be more accurate, the *in* tag pushes a number of items
- onto the namespace stack. These include sequence variables,
- grouping variables, and batch variables in addition to the
- object itself. Some of those variables are:
-
- o sequence-item: The current item within the iteration.
-
- o sequence-start: True if the current item is the first item
- in the sequence.
-
- o sequence-end: True if the current item is the last item in
- the sequence.
-
- o sequence-length: The length of the sequence.
-
- o previous-sequence: True on the first iteration if the
- current batch is not the first one. Batch size is set with the
- size attribute.
-
- o next-sequence: True on the last iteration if the current
- batch is not the last batch.
-
- There are many more variables available when using the *in*
- tag. See "Appendix A":AppendixA.stx for more detail.
-
- The *With* Tag
-
- The *with* tag pushes an object that you specify onto
- 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>
-
- 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.
-
- 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
- pushed onto the namespace stack::
-
- <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.
-
- 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 name-value pairs 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.
-
- 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 the chapter entitled "Advanced Zope
- Scripting":ScriptingZope.stx .
-
- 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 alter
- the namespace in order to obtain references to the objects you
- need.
-
- DTML Namespace Utility Functions
-
- Like all things in Zope, the DTML namespace is an object, and it can
- be accessed directly in DTML with the *_* (underscore) object. The
- *_* namespace is often referred to 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,10,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 number of built-in names. The Zope
- philosophy can be thought of as having a large, complex array of
- built-in names.
-
- The under namespace can also be used to explicitly control variable
- look up. There is a very common usage of this syntax. As mentioned
- above 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.
-
- 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')">
-
- The *getitem* function takes the name to look up as its first
- argument. Now, the DTML Method will correctly display the square 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.
-
- 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)">
-
- Notice in the above example that *selectedDoc* is not in
- quotes. We don't want to insert the text *selectedDoc*
- we want to insert the value of the variable named *selectedDoc*. For
- example, the value of *selectedDoc* might be 'chapterOne'. Using this
- method, you can look up an item using a dynamic value instead of
- static text.
-
- If you are 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 the chapter entitled "Advanced Zope
- Scripting":ScriptingZope.stx . Using Python sidesteps many of the
- issues in DTML.
-
- DTML Security
-
- Zope can be used by many different kinds of users. For example, the
- Zope site, "Zope.org":http://www.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, then the
- security system will raise an *Unauthorized* error and the user
- will be asked to present more privileged authentication
- credentials.
-
- In the chapter entitled "Users and Security":Security.stx , 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.
-
- 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 website, along with an interesting set
- of contributed DTML tags.
-
- 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.
-
- 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>
-
- 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.
-
- Another possibility for use of the *call* tag would be to call a
- ZSQL Method or or preprocess the REQUEST. Two examples of calling
- a ZSQL method::
-
- <dtml-call "insertLogEntry(REQUEST)">
-
- or::
-
- <dtml-call "insertLogEntry(logInfo=REQUEST.get('URL0'), severity=1)">
-
- To call a python script that might do any number of things,
- including preprocessing the REQUEST::
-
- <dtml-call "preprocess(REQUEST)">
-
- 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>
-
- 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.
-
- 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.":img:7-5:Figures/7-5.png
-
- 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>
-
- 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.":img:7-6:Figures/7-6.png
-
- 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.
-
- 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.
-
- Here's an example of how to send an email message with the
- *sendmail* tag::
-
- <dtml-sendmail>
- To: <dtml-var recipient>
- From: <dtml-var sender>
- 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>
-
- 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="jobs at yahoo.com">
- Yahoo<br>
-
- <input type="checkbox" name="send_to:list" value="jobs at microsoft.com">
- Microsoft<br>
-
- <input type="checkbox" name="send_to:list" value="jobs at mcdonalds.com">
- McDonalds</p>
-
- <input type=submit value="Send Resume">
- </form>
-
- <dtml-var standard_html_footer>
-
- Note: The text *:list* added to the name of the input fields directs
- Zope to treat the received information as a list type. For example if
- the first two checkboxes were selected in the above upload form, the
- REQUEST variable send_to would have the value [jobs at yahoo.com, jobs at microsoft.com]
-
- 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>
-
- 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.
-
- 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>
-
- 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.
-
- 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.
-
- 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.
-
- o 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.
-
- o Because smaller batches of results are being used, often less
- memory is consumed by Zope.
-
- o *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>
-
- 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.
-
- 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.
-
- 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>
- <!-- Changed by: Peter Sabaini, 30-Jan-2004 -->
-</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>
-
- Another example utilizes the commonly accepted navigation scheme
- of presenting the the user page numbers from which to select::
-
- <dtml-in "_.range(1,101) "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 URL><dtml-var sequence-query>start=<dtml-var "_['batch-start-index']+1">">
- <dtml-var sequence-number></a>
- <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>
- <a href="<dtml-var 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>
-
- <dtml-in "_.range(1,101) "size=10 start=start>
- <br><dtml-var sequence-item>
- </dtml-in>
-
- This quick and easy method to display pages is a nice navigational tool
- for larger batches. It does present the drawback of having to utilize
- an additional *dtml-in* tag to iterate through the actual items, however.
-
- 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 the chapter entitled "Searching
- and Categorizing Content":SearchingZCatalog.stx.
-
- 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":http://www.python.org/doc/current/tut/node10.html.
-
- 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 are ignored. So in
- addition to reporting errors, exceptions allow you to back out
- changes if a problem crops up.
-
- 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>
-
- 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":http://www.python.org/doc/current/lib/module-exceptions.html
- for a list of Python exceptions and their subclasses. An
- *except* tag can catch multiple exceptions by listing them all
- in the same tag.
-
- 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.
-
- Other useful examples
-
- In this section are several useful examples of dtml code. While
- many of these are most often better done in Python scripts, there
- are occasions when knowing how to accomplish this in dtml is
- worthwhile.
-
- Forwarding a REQUEST
-
- We have seen how to redirect the user's browser to another page
- with the help of the *call* directive. However, there are times
- when a redirection is not necessary and a simple forwarding of a
- REQUEST from one dtml-method to another would suffice. In this
- example, the dtml-method shown obtains a variable named *type*
- from the REQUEST object. A lookup table is reference to obtain
- the name of the dtml-method to which the REQUEST should be
- forwarded. The code below accomplishes this::
-
- <dtml-let lookup="{'a' : 'form15', 'b' : 'form75', 'c' : 'form88'}">
- <dtml-return "_[lookup[REQUEST.get('type')]]">
- </dtml-let>
-
- This code looks up the name of the desired dtml-method in the
- lookup table (contained in the *let* statement) and in turn,
- looks up the name of this dtml-method in the current namespace.
- As long as the dtml-method exists, control will be passed to the
- method directly. This example could be made more complete with
- the addition of exception handling which was discussed above.
-
- Sorting with the '<dtml-in>' tag
-
- There are many times when sorting a result set is necessary.
- The *dtml-in* tag has some very interesting sort capabilities
- for both static and dynamic sorting. In the example below, a
- ZSQL method is called that returns results from a log table.
- The columns returned are logTime, logType, and userName. The
- dtml-method or document that contains this code will generate
- links back to itself to re-sort the query based upon certain
- search criteria::
-
- <dtml-comment>
-
- The sorting is accomplished by looking up a sort type
- variable in the REQUEST that is comprised of two parts. All
- but the last character indicate the name of the column on
- which to sort. The last character of the sort type indicates
- whether the sort should be ascending or descending.
-
- </dtml-comment>
-
- <table>
- <tr>
- <td>Time <a href="<dtml-var URL>?st=logTimea">A</a> <a href="<dtml-var URL>?st=logTimed">D</a></td>
- <td>Type <a href="<dtml-var URL>?st=logTypea">A</a> <a href="<dtml-var URL>?st=logTyped">D</a></td>
- <td>User <a href="<dtml-var URL>?st=userNamea">A</a> <a href="<dtml-var URL>?st=userNamed">D</a></td>
- </tr>
-
- <dtml-comment>The line below sets the default sort</dtml-comment>
- <dtml-if "REQUEST.get('st')==None"><dtml-call "REQUEST.set('st', 'logTimed')"></dtml-if>
- <dtml-in getLogData sort_expr="REQUEST.get('st')[0:-1]" reverse_expr="REQUEST.get('st')[-1]=='d'">
- <tr>
- <td><dtml-var logTime></td>
- <td><dtml-var logType></td>
- <td><dtml-var userName></td>
- </tr>
- </dtml-in>
- </table>
-
- Calling a DTML object from a Python Script
-
- Although calling a DTML method from a Python script isn't really
- an advanced DTML technique, it deals with DTML, so it's being
- included here. To call a DTML Method or DTML Document from a
- Python script, the following code is used::
-
- dtmlMethodName = 'index_html'
- return context[dtmlMethodName](container, container.REQUEST)
-
- It's as simple as that. Often this is very useful if you wish
- to forward a request and significant processing is needed to
- determine which dtml object is the target.
-
- Explicit Lookups
-
- Occasionally it is useful to "turn off" acquisition when looking
- up an attribute. In this example, you have a folder which
- contains sub-folders. Each sub-folder contains Images. The
- top-level folder, each subfolder, and each image contain a
- property named *desc*.
-
- If you were to query the Image for its *desc* property it would
- return the *desc* property of it's parent folder if the Image
- did not have the property. This could cause confusion as the
- Image would appear to have the *desc* property when it really
- belonged to the parent folder. In most cases, this behavior is
- desired. However, in this case, the user would like to see
- which images have the *desc* property and which don't. This is
- accomplished by utilizing *aq_explicit* in the call to the
- object in question.
-
- Given the following structure::
-
- Folder
- |
- |- Folder1 (desc='Folder one')
- |- Folder2 (desc='Folder two')
- |- Image1 (desc='Photo one')
- |- Image2
- |- Image3 (desc='Photo three')
-
- when the second image is asked for its *desc* property it will
- return 'Folder two' based on acquisition rules::
-
- <dtml-var "Image2.desc">
-
- However, utilizing *aq_explicit* will cause Zope to look only
- in the desired location for the property::
-
- <dtml-var "Image2.aq_explicit.desc">
-
- This will, of course, raise an exception when the *desc*
- property does not exist. A safer way to do this is::
-
- <dtml-if "_.hasattr(Image2.aq_explicit, 'desc')">
- <dtml-var "Image2.aq_explicit.desc">
- <dtml-else>
- No desc property.
- </dtml-if>
-
- As you can see, this can be very useful.
-
- 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.
Copied: zope2book/trunk/source/AdvDTML.rst (from rev 96419, zope2book/trunk/AdvDTML.stx)
===================================================================
--- zope2book/trunk/source/AdvDTML.rst (rev 0)
+++ zope2book/trunk/source/AdvDTML.rst 2009-02-10 21:58:00 UTC (rev 96425)
@@ -0,0 +1,1664 @@
+Advanced DTML
+=============
+
+DTML is the kind of language that appears to "do 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, well, it's no fun at all.
+This chapter tells you how to make DTML do what you *really* mean.
+When you're done reading this chapter you will be able to write DTML
+that will accomplish a number of complex tasks including:
+
+- Inspect and Modify the REQUEST object
+
+- Modify the current namespace
+
+- Call other scripts from within DTML
+
+- Send email with or without MIME attachments
+
+- Handle exceptions within DTML
+
+A few of caveats before getting started:
+
+- It's a good idea to know something about Python before diving into
+ advanced DTML or any other advanced area of Zope.
+
+- Understand the Zope acquisition model and how it works.
+
+- If you are writing very complex functionality in DTML, consider
+ using a Python Script. This will ease maintenance, not to mention
+ readability.
+
+- Understand the difference between a DTML Document and a DTML
+ Method before embarking on building a huge site. See the explanation
+ included in this chapter.
+
+It's no lie that DTML has a reputation for complexity. While it is true
+that DTML is really simple if all you want to do is simple layout,
+using DTML for more advanced tasks requires an understanding of 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>
+
+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 was entered in the form.
+
+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 to 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 of sections, we'll show you how to fix this
+example to do what you mean.
+
+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.
+
+When Zope looks for names in the DTML namespace stack it first looks at
+the topmost object in the stack. If the name can't be found
+there, then the next item down is introspected. Zope will work its way
+down the stack, checking each object in turn until it finds the name
+that it is looking for.
+
+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>
+
+As long as there is no variable named *unicorn* viewing this
+DTML will return an error, as shown in the figure below.
+
+.. figure:: ../Figures/7-1.png
+
+ 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.
+
+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 the figure below.
+
+.. figure:: ../Figures/7-2.png
+
+ Initial DTML namespace stack
+
+The client object is the first object on the top of the DTML namespace
+stack when entering a transaction (note: commands exist to push
+additional parameters onto the namespace stack during a thread of
+execution). What the client object is depends on whether 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.
+
+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>
+
+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 the figure below.
+
+.. figure:: ../Figures/7-3.png
+
+ Applying the *list* method to the *Reptiles* folder
+
+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*.
+
+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.
+
+As you saw in "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.
+
+DTML Method vs. DTML Document
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+One of the most potentially confusing choices to make for Zope
+newbies is the choice between a DTML Method and a DTML Document.
+Unfortunately, many Zope newbies develop entire sites using one
+type of object only to discover that they should have used the
+other type. In general, keep the following items in mind when
+deciding upon which type to use:
+
+- **Does the object require properties of its own?** If so,
+ use a DTML Document since DTML Methods have no inherent
+ properties.
+
+- **Does the object need to be called as a "page"?** If so,
+ consider using a DTML Document since it will be easier
+ to control such items as page title by using properties.
+
+- **Does the object need transparency to its context?** If so,
+ you should probably use a DTML Method since these objects
+ act as though they are directly attached to their calling,
+ or containing object.
+
+DTML Request Object
+~~~~~~~~~~~~~~~~~~~
+
+The request object is the bottom object on the DTML
+namespace stack. The request contains all of the information
+specific to the current web request.
+
+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
+ <http://www.w3.org/CGI/>`_, 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.
+
+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 render the REQUEST
+object in a DTML Document or Method::
+
+ <dtml-var standard_html_header>
+
+ <dtml-var REQUEST>
+
+ <dtml-var standard_html_footer>
+
+Try this yourself, you should get something that looks like
+the figure below.
+
+.. figure:: ../Figures/7-4.png
+
+ 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 know the DTML namespace is a stack, you may
+be wondering how, or even why, new objects get pushed onto it.
+
+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.
+
+Additional Notes
+%%%%%%%%%%%%%%%%
+
+To be more accurate, the *in* tag pushes a number of items
+onto the namespace stack. These include sequence variables,
+grouping variables, and batch variables in addition to the
+object itself. Some of those variables are:
+
+- sequence-item: The current item within the iteration.
+
+- sequence-start: True if the current item is the first item
+ in the sequence.
+
+- sequence-end: True if the current item is the last item in
+ the sequence.
+
+- sequence-length: The length of the sequence.
+
+- previous-sequence: True on the first iteration if the
+ current batch is not the first one. Batch size is set with the
+ size attribute.
+
+- next-sequence: True on the last iteration if the current
+ batch is not the last batch.
+
+There are many more variables available when using the *in*
+tag. See `Appendix A <AppendixA.html>`_ for more detail.
+
+The *With* Tag
+~~~~~~~~~~~~~~
+
+The *with* tag pushes an object that you specify onto
+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>
+
+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.
+
+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
+pushed onto the namespace stack::
+
+ <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.
+
+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 name-value pairs 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.
+
+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 the chapter entitled `Advanced Zope Scripting
+<ScriptingZope.html>`_ .
+
+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 alter
+the namespace in order to obtain references to the objects you
+need.
+
+DTML Namespace Utility Functions
+--------------------------------
+
+Like all things in Zope, the DTML namespace is an object, and it can
+be accessed directly in DTML with the *_* (underscore) object. The
+*_* namespace is often referred to 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,10,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 number of built-in names. The Zope
+philosophy can be thought of as having a large, complex array of
+built-in names.
+
+The under namespace can also be used to explicitly control variable
+look up. There is a very common usage of this syntax. As mentioned
+above 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.
+
+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')">
+
+The *getitem* function takes the name to look up as its first
+argument. Now, the DTML Method will correctly display the square 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.
+
+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)">
+
+Notice in the above example that *selectedDoc* is not in
+quotes. We don't want to insert the text *selectedDoc*
+we want to insert the value of the variable named *selectedDoc*. For
+example, the value of *selectedDoc* might be 'chapterOne'. Using this
+method, you can look up an item using a dynamic value instead of
+static text.
+
+If you are 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 the chapter entitled `Advanced Zope Scripting`_.
+Using Python sidesteps many of the issues in DTML.
+
+DTML Security
+-------------
+
+Zope can be used by many different kinds of users. For example, the
+Zope site, `Zope.org <http://www.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, then the
+security system will raise an *Unauthorized* error and the user
+will be asked to present more privileged authentication
+credentials.
+
+In the chapter entitled `Users and Security <Security.html>`_ , 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.
+
+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 website, along with an interesting set
+of contributed DTML tags.
+
+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.
+
+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>
+
+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.
+
+Another possibility for use of the *call* tag would be to call a
+ZSQL Method or or preprocess the REQUEST. Two examples of calling
+a ZSQL method::
+
+ <dtml-call "insertLogEntry(REQUEST)">
+
+or::
+
+ <dtml-call "insertLogEntry(logInfo=REQUEST.get('URL0'), severity=1)">
+
+To call a python script that might do any number of things,
+including preprocessing the REQUEST::
+
+ <dtml-call "preprocess(REQUEST)">
+
+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>
+
+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.
+
+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.
+
+.. figure:: ../Figures/7-5.png
+
+ 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>
+
+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.
+
+.. figure:: ../Figures/7-6.png
+
+ 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.
+
+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.
+
+Here's an example of how to send an email message with the
+*sendmail* tag::
+
+ <dtml-sendmail>
+ To: <dtml-var recipient>
+ From: <dtml-var sender>
+ 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>
+
+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="jobs at yahoo.com">
+ Yahoo<br>
+
+ <input type="checkbox" name="send_to:list" value="jobs at microsoft.com">
+ Microsoft<br>
+
+ <input type="checkbox" name="send_to:list" value="jobs at mcdonalds.com">
+ McDonalds</p>
+
+ <input type=submit value="Send Resume">
+ </form>
+
+ <dtml-var standard_html_footer>
+
+Note: The text *:list* added to the name of the input fields directs
+Zope to treat the received information as a list type. For example if
+the first two checkboxes were selected in the above upload form, the
+REQUEST variable send_to would have the value [jobs at yahoo.com, jobs at microsoft.com]
+
+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>
+
+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.
+
+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>
+
+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.
+
+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.
+
+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.
+
+ o 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.
+
+ o Because smaller batches of results are being used, often less
+ memory is consumed by Zope.
+
+ o *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>
+
+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.
+
+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.
+
+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>
+
+Another example utilizes the commonly accepted navigation scheme
+of presenting the the user page numbers from which to select::
+
+ <dtml-in "_.range(1,101) "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 URL><dtml-var sequence-query>start=<dtml-var "_['batch-start-index']+1">">
+ <dtml-var sequence-number></a>
+ <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>
+ <a href="<dtml-var 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>
+
+ <dtml-in "_.range(1,101) "size=10 start=start>
+ <br><dtml-var sequence-item>
+ </dtml-in>
+
+This quick and easy method to display pages is a nice navigational tool
+for larger batches. It does present the drawback of having to utilize
+an additional *dtml-in* tag to iterate through the actual items, however.
+
+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 the chapter entitled `Searching
+and Categorizing Content <SearchingZCatalog.html>`_.
+
+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 <http://www.python.org/doc/current/tut/node10.html>`_.
+
+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 are ignored. So in
+addition to reporting errors, exceptions allow you to back out
+changes if a problem crops up.
+
+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>
+
+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
+<http://www.python.org/doc/current/lib/module-exceptions.html>`_
+for a list of Python exceptions and their subclasses. An
+*except* tag can catch multiple exceptions by listing them all
+in the same tag.
+
+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.
+
+Other useful examples
+---------------------
+
+In this section are several useful examples of dtml code. While
+many of these are most often better done in Python scripts, there
+are occasions when knowing how to accomplish this in dtml is
+worthwhile.
+
+Forwarding a REQUEST
+~~~~~~~~~~~~~~~~~~~~
+
+We have seen how to redirect the user's browser to another page
+with the help of the *call* directive. However, there are times
+when a redirection is not necessary and a simple forwarding of a
+REQUEST from one dtml-method to another would suffice. In this
+example, the dtml-method shown obtains a variable named *type*
+from the REQUEST object. A lookup table is reference to obtain
+the name of the dtml-method to which the REQUEST should be
+forwarded. The code below accomplishes this::
+
+ <dtml-let lookup="{'a' : 'form15', 'b' : 'form75', 'c' : 'form88'}">
+ <dtml-return "_[lookup[REQUEST.get('type')]]">
+ </dtml-let>
+
+This code looks up the name of the desired dtml-method in the
+lookup table (contained in the *let* statement) and in turn,
+looks up the name of this dtml-method in the current namespace.
+As long as the dtml-method exists, control will be passed to the
+method directly. This example could be made more complete with
+the addition of exception handling which was discussed above.
+
+Sorting with the '<dtml-in>' tag
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are many times when sorting a result set is necessary.
+The *dtml-in* tag has some very interesting sort capabilities
+for both static and dynamic sorting. In the example below, a
+ZSQL method is called that returns results from a log table.
+The columns returned are logTime, logType, and userName. The
+dtml-method or document that contains this code will generate
+links back to itself to re-sort the query based upon certain
+search criteria::
+
+ <dtml-comment>
+
+ The sorting is accomplished by looking up a sort type
+ variable in the REQUEST that is comprised of two parts. All
+ but the last character indicate the name of the column on
+ which to sort. The last character of the sort type indicates
+ whether the sort should be ascending or descending.
+
+ </dtml-comment>
+
+ <table>
+ <tr>
+ <td>Time <a href="<dtml-var URL>?st=logTimea">A</a> <a href="<dtml-var URL>?st=logTimed">D</a></td>
+ <td>Type <a href="<dtml-var URL>?st=logTypea">A</a> <a href="<dtml-var URL>?st=logTyped">D</a></td>
+ <td>User <a href="<dtml-var URL>?st=userNamea">A</a> <a href="<dtml-var URL>?st=userNamed">D</a></td>
+ </tr>
+
+ <dtml-comment>The line below sets the default sort</dtml-comment>
+ <dtml-if "REQUEST.get('st')==None"><dtml-call "REQUEST.set('st', 'logTimed')"></dtml-if>
+ <dtml-in getLogData sort_expr="REQUEST.get('st')[0:-1]" reverse_expr="REQUEST.get('st')[-1]=='d'">
+ <tr>
+ <td><dtml-var logTime></td>
+ <td><dtml-var logType></td>
+ <td><dtml-var userName></td>
+ </tr>
+ </dtml-in>
+ </table>
+
+Calling a DTML object from a Python Script
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Although calling a DTML method from a Python script isn't really
+an advanced DTML technique, it deals with DTML, so it's being
+included here. To call a DTML Method or DTML Document from a
+Python script, the following code is used::
+
+ dtmlMethodName = 'index_html'
+ return context[dtmlMethodName](container, container.REQUEST)
+
+It's as simple as that. Often this is very useful if you wish
+to forward a request and significant processing is needed to
+determine which dtml object is the target.
+
+Explicit Lookups
+~~~~~~~~~~~~~~~~
+
+Occasionally it is useful to "turn off" acquisition when looking
+up an attribute. In this example, you have a folder which
+contains sub-folders. Each sub-folder contains Images. The
+top-level folder, each subfolder, and each image contain a
+property named *desc*.
+
+If you were to query the Image for its *desc* property it would
+return the *desc* property of it's parent folder if the Image
+did not have the property. This could cause confusion as the
+Image would appear to have the *desc* property when it really
+belonged to the parent folder. In most cases, this behavior is
+desired. However, in this case, the user would like to see
+which images have the *desc* property and which don't. This is
+accomplished by utilizing *aq_explicit* in the call to the
+object in question.
+
+Given the following structure::
+
+ Folder
+ |
+ |- Folder1 (desc='Folder one')
+ |- Folder2 (desc='Folder two')
+ |- Image1 (desc='Photo one')
+ |- Image2
+ |- Image3 (desc='Photo three')
+
+when the second image is asked for its *desc* property it will
+return 'Folder two' based on acquisition rules::
+
+ <dtml-var "Image2.desc">
+
+However, utilizing *aq_explicit* will cause Zope to look only
+in the desired location for the property::
+
+ <dtml-var "Image2.aq_explicit.desc">
+
+This will, of course, raise an exception when the *desc*
+property does not exist. A safer way to do this is::
+
+ <dtml-if "_.hasattr(Image2.aq_explicit, 'desc')">
+ <dtml-var "Image2.aq_explicit.desc">
+ <dtml-else>
+ No desc property.
+ </dtml-if>
+
+As you can see, this can be very useful.
+
+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.
Modified: zope2book/trunk/source/index.rst
===================================================================
--- zope2book/trunk/source/index.rst 2009-02-10 21:44:22 UTC (rev 96424)
+++ zope2book/trunk/source/index.rst 2009-02-10 21:58:00 UTC (rev 96425)
@@ -23,6 +23,7 @@
ZPT.rst
SimpleExamples.rst
Security.rst
+ AdvDTML.rst
AppendixA.rst
Contributions.rst
More information about the Checkins
mailing list