Developer

Expanding Zope

In this Daily Drill Down, Vincent Danen takes a look at Document Template Markup Language (DTML)—an extremely advanced markup language that makes Zope dynamic.


In previous articles, I explained how to install the Zope Web site creation and management tool, and how to use objects in Zope. I discussed how objects make up the basic framework of the entire Zope system. I also gave you instructions on how to configure some aspects of Zope and how to start building Web pages.

In this Daily Drill Down, I will take a look at Document Template Markup Language (DTML), the basics of what makes Zope dynamic. Those of you used to PHP will have to look at things in a different light with Zope because unlike PHP, Zope lacks a scripting language. You can use PHP on your Web site outside of the Zope system. DTML has the same end capabilities as PHP; however, it has a remarkably different implementation.

I’ll also look at using External Methods in Zope, which will allow you to access information outside of Zope and import it dynamically. The External Method is an excellent starting place for individuals interested in using Zope to manage their Web site without having to do a lot of initial legwork. Using the External Method, you can get your Web site running quickly within Zope.

Introduction to DTML
DTML is similar to HTML in that it is a markup language. It is an extremely advanced markup language, however. While HTML 4.0 is quite a step forward from previous incarnations of HTML, and XHTML 1.0 is even one step beyond HTML 4.0, DTML is very different and very powerful.

Think of DTML as another form of scripting language, like PHP or Perl, which can be used only with Zope. It’s almost a source of confusion to think that DTML is similar to HTML because of the many things you can do with it. Unlike PHP, however, DTML uses an HTML-like syntax. For instance, the following would print Hello Joe if user Joe had logged in to a Web site using PHP:
<?
 if($username) {
 print ’Hello $username’;
 }
?>

In DTML, you would accomplish the same thing using:
<dtml-if input_username>
 Hello <dtml-var input_username>
</dtml-if>


Quite the difference! The first uses a programming language syntax similar to C or any other programming language. The second looks more like HTML code, but with a few differences. Where $username is the value of the user’s name in PHP, input_username is the value of the user’s name that is obtained via user input from a form in DTML. The if statements vary, as well. In PHP, it looks like normal programming language syntax. In DTML, you see opening and closing tags that are prefixed with dtml.

There are actually three ways you can define DTML within Zope, so you can select the style that suits you best. The first is the DTML method we have already illustrated. The second is a Python syntax, and the third resembles server-side Include statements you may be familiar with already. Let’s take a look at each:
<dtml-name>
%(name)x
<!-?#name -?>


For the rest of this article, we will concentrate strictly on the first DTML method that most closely resembles standard HTML. Let’s take a look at what I did in my previous article concerning inserting your standard_html_header and standard_html_footer objects into new objects you define. Previously, I used SSI syntax like this:
<!?-#var standard_html_header ?->
...
<!-?#var standard_html_footer ?->


With the DTML syntax style, you could convert this to
<dtml-var standard_html_header>
...
<dtml-var standard_html_footer>


and achieve the same results.

DTML is a complex language all on its own. It has the same functions, variable manipulation, and power as any other Web-enabled scripting language. Typically, the syntax is the hardest thing to understand because it is so nonstandard. The DTML syntax is probably the easiest to understand but Python programmers will appreciate the Python method. Unfortunately, the Python syntax is quite different from Perl or PHP, which makes it a little challenging for people used to those languages.

Instead of going in-depth on how to use DTML and the various functions and expressions it contains, I will instead look at some useful DTML examples for various interesting pages you may wish to add to your Web site. For a detailed look at DTML, I encourage you to download the DTML documentation from the official Zope site.

Mail forms in Zope
Probably one of the biggest functions to any Web site is the ability to send e-mail through a simple form. Whether for feedback on the site or for some other purpose, most larger Web sites include an e-mail sending form of some sort. Zope allows you to accomplish this quite easily by simply defining the server’s MailHost object and two DTML pages.

The first thing you need to do is create the MailHost object. You will probably want to do this at the top-level folder so that all objects can access it. When you add the MailHost object, you must define an SMTP server through which your site will send the e-mail. Be sure this is a server to which you have access. On some systems, the mail server may even be on the same computer (localhost).

You will then need to create two objects. The first will be the page that contains the form, and the second will do the actual sending. Create the first object as a DTML document with an ID of Feedback or something similar. (In this example, I will assume you named it Feedback.) The contents of this page should be similar to this:
<dtml_var standard_html_header>
<h2><dtml_var title_or_id></h2>

<form action="SendFeedback" method="post">
Your Name: <input type="text" name="name" size="40"><br>
Your E-mail: <input type="text" name="email" size="40">
<br>
<textarea name="comments" rows="10" cols="40">
Please submit your comments!
</textarea><br>
<input type="submit" value="Send">
</form>

<dtml_var standard_html_footer>


This is the code for the form itself. It’s no different from other HTML forms that use CGI scripts or PHP to send out e-mail; the only difference is this form calls our second object, which is called SendFeedback. This object will be a DTML method with an ID of SendFeedback. Place the following inside the object:
<dtml_var standard_html_header>

<dtml_sendmail mailhost="MailHost">
To: Admin admin@mydomain.com
From: <dtml_var name> <<dtml_var email>>
Subject: Web site feedback

<dtml_var comments>
</dtml_sendmail>

<h2>Thank you for your input!</h2>
<p>Your comments have been sent.</p>

<dtml_var standard_html_footer>


This is a little bit different. Basically, this page is used to send the e-mail, and then print a thank you message to the user indicating that it has been sent. You will notice two DTML tags here that define the e-mail support: <dtml-sendmail> and </dtml-sendmail> which closes the section. Within the two tags is the actual e-mail information. As an addition to the <dtml-sendmail> tag, you must specify the MailHost with the mailhost= keyword. The MailHost should be the defined MailHost object.

Take a look at the structure of the e-mail message itself:
To: Admin admin@mydomain.com
From: <dtml_var name> <<dtml_var email>>
Subject: Web site feedback

<dtml_var comments>


You’re basically writing the headers for the e-mail message here. The first line tells Zope where to send the e-mail. In this case, you send it to the user admin with the e-mail address mailto:admin@mydomain.com. The second line tells who the message is from. In this case, you asked the user for their real name and their e-mail address, so you can use that information here by using <dtml-var> tags. Notice the first tag is <dtml-var name>, which matches the form item name. The second is <dtml-var email>, which matches the form item named email. Also notice the <dtml-var email> tag is surrounded by another set of greater than and less than symbols. This is because the e-mail address must be enclosed so that:
<<dtml-var email>>

becomes
<mailto:<user@userdomain.com>>

once it is translated by Zope. The final step is to give permission to people who will use the form. Because of the default security settings, only users with the Manage role can send mail. This can be changed in one of two ways. The first is to edit the roles for the Anonymous role to be able to use MailHost service. This is defined in the Security tab at the top-level folder. However, if you do this, the Anonymous role will be able to use every single MailHost service you define on your system. This may not be what you want.

The other option is to use the Proxy roles. This means you can change how the object reacts to users accessing it. For instance, by default all pages can be viewed by Anonymous users. Only Managers can use the MailHost services. So, while an Anonymous user can view the SendFeedback object, their mail would not be sent. If we define a Proxy role of Manager for the SendFeedback object, all connections to the page, whether it is a Manager, Owner, Anonymous, or an otherwise defined role, will assume the permissions of Manager for that page alone. Since you want everyone to be able to use the SendFeedback page, you must force everyone who views the page to assume the Manager role for that page alone. When you edit your SendFeedback object, click on the Proxy tab at the top of the right-hand frame. Select the role you wish to be proxied in the Proxy Roles pick list. Click on Change to save your changes and your new SendFeedback object will now be able to send e-mail regardless of who uses it.

External Methods in Zope
External Methods are a way of extending Zope without leaving the Zope framework. Extensions exist to allow Perl- and PHP-driven pages within Zope to browse the local file system with Zope, and so on. These are things that rely on Python to work, and this is where the External Methods come in.

One External Method that may come in very useful to people is the one that can retrieve information from another Web site to be dynamically included into a page. PHP can do this by using the include() function, but Zope offers a different approach. It's a little more complicated, but it works just as well.

Let's assume you want to display the headlines from Linux Today on your Web site. I chose Linux Today because it exports its headlines to a standard HTML file instead of the RDF or RSS format most other Web sites use. The HTML file Linux Today uses can be found on the Linux Today site.

What you need to do is define an External Method that runs a Python script. Typically, your External Methods are stored in a directory called Extensions. If you installed Zope into /home/Zope, your Extensions directory would be /home/Zope/Extensions. For this illustration, let's create a new file in that directory called GetLinuxToday.py, which will include the following:
import httplib

def GetLinuxToday():
 http = httplib.HTTP('linuxtoday.com')
 http.putrequest('GET', '/backend/lthead.inc')
 http.putheader('Accept', 'text/html') # we can handle text/html
 http.putheader('Accept', 'text/plain') # we can handle text/plain
 http.endheaders()
 httpcode, httpmsg, headers = http.getreply() # get response headers
 if httpcode != 200:
 raise "Could not get document" # HTTP error
 doc = http.getfile()
 data = doc.read() # read file
 doc.close()
 return data


In the above Python script, you connect to the defined Web site (linuxtoday.com) and the defined page (/backend/lthead.inc) and retrieve that page. The end result is the script reads the information from the Web site and returns it to the calling program, which in this case is Zope. If you wish to include other HTML pages, you would simply change the appropriate lines of code:
http = httplib.HTTP('linuxtoday.com')
http.putrequest('GET', '/backend/lthead.inc')


to whatever site and page you want to retrieve. Save the file and go back into your Zope management screen. Go to your top-level folder and create a new External Method object. Use GetLinuxToday for the ID, function name, and Python module file. Use anything you like for the title.

Now, to retrieve the information that the GetLinuxToday object obtains and place it into one of your Web pages, you would simply edit the object associated with the Web page and include the following DTML code:
<dtml_var GetLinuxToday>

And that's it! When you next load that page, you will see the Linux Today headlines displayed where you placed your DTML tag calling that object.

Products
Zope also has a very easy way of using external add-ons, or extensions it simply calls Products. These Products have a variety of uses, from allowing users to access POP3 mailboxes via Zope, to allowing users to view Zope source code, to some complex e-commerce utilities. These Products are all available on the Zope Web site.

Installation of these Products is a pretty simple thing. More often than not, they are available as tar.gz or .zip archives. Download the Product you are interested in and unarchive it into your /home/Zope/lib/python/Products directory. This is assuming you installed Zope into /home/Zope. You may want to be wary, as some people package products differently. For instance, some archives may contain the path structure for the product and you would have to unarchive them in the top-level Zope home directory (/home/Zope), while others may simply contain the Products subdirectory alone. In other words, watch the screen when you unarchive these files. To access the Product through Zope, you will need to restart the Zope server, or Zserver. Once this is done, you will be able to make use of your newly installed Product.

Zope also provides an easy way of fixing problems. Periodically, updates, called Hotfixes, are released for Zope that fix potential security issues. It would be a good idea to check the Zope Web site once in a while for new Hotfixes. These are installed into your Products directory because Zope looks at the fixes like they are just another Product.

If you need to remove a Product, simply remove the associated subdirectory for the Product. For example, if you had installed Hotfix_2000-10-11 (recommended for all users of Zope 2.2.2, which is the current version), you would find the files here:
/home/Zope/lib/python/Products/Hotfix_2000-10-11/

When Zope 2.2.3 (or whatever the next version may be) comes out, you will most likely no longer need that particular Hotfix on your system, so you can simply remove it by using
rm -rf /home/Zope/lib/pythong/Products/Hotfix_2000-10-11/

Conclusion
Zope is a remarkably versatile product with some rather amazing capabilities. The ability to simply drop in new modules is probably one of the most gratifying aspects of Zope. The seamless integration of Products makes recompiling source code to introduce a new capability into the core system obsolete. For example, if you wanted PHP to handle PDF documents internally, you would need to recompile it and specifically enable PDF support during the ./configure stage. With Zope, simply install the associated Product and you are able to make use of the Product as soon as you restart your Zserver.

The DTML system that Zope is based on is quite nice as well. Personally, I would have preferred the ability to use a C- or Perl-style syntax as opposed to DTML or Python, but this is because the PHP and Perl syntax and scripting style is what I am accustomed to using. Coming from that type of background, Zope is quite different, and a little difficult to get accustomed to using. If you come from a similar background, you will also find Zope somewhat challenging. The benefits of a Zope-controlled system, however, are quite amazing. With the wide range of Products available (over 220 Products on the Web site), the ability to make use of Python scripts as External Methods, and a huge user-base that is absolutely in love with Zope and who contribute to it through Products, HOWTOs, FAQs, and other sources of useful information, there isn’t much about Zope that you can find fault with. But, be prepared for a few long hours of reading and learning to make the most of this tool.
The authors and editors have taken care in preparation of the content contained herein but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for any damages. Always have a verified backup before making any changes.

About Vincent Danen

Vincent Danen works on the Red Hat Security Response Team and lives in Canada. He has been writing about and developing on Linux for over 10 years and is a veteran Mac user.

Editor's Picks