You are not logged in Log in Join
You are here: Home » Zope Documentation » Books » The Zope Book Releases » The Zope Book (2.6 Edition) » Creating Basic Zope Applications

Log in
Name

Password

 
Previous Page Up one Level Next Page Creating Basic Zope Applications Comments On/Off Table of Contents

Creating Basic Zope Applications

XXX - this chapter is not done. I got to just before "Factoring Out Stylesheets" and quit for now. The material prior to that needs to be expanded and cleaned up as well. The examples also need to be converted to page templates. -chrism

Anonymous User - Dec. 4, 2003 10:21 am:
 NEW VERSION OF THIS CHAPTER AVAILABLE AT:

 http://zope.org/Members/jwhitener/zopeZoo_2_6

 Use the link above for a completely rewritten version of this chapter which uses Page Templates instead of
 DTML and includes many other updates and improvements.
Anonymous User - Jan. 17, 2004 12:26 pm:
 This tutorial is a very good rewritten version of this chapter but it is little bit to much a "word by word"
 translation.
 For, one of advantages of zpt is to edit and see templates entirely in a wysiwyg editor.
or when you cut your zpt like dtml method you can't see the result wholly, it is for this reason I prefer use
 the metal language with metal:use-macro=".." et metal:fill-slot than
 <span tal:replace="structure here/header">DUMMY HEADER</span>.
 What do you think about my opinion.
 Loïc
Anonymous User - Jan. 26, 2004 2:01 am:
 Rewritten version is excellent! Thanks jwhitener for a most useful intro. With navigation, template
 construction and a basic style sheet under my belt I think I know enough to launch straight into my first
 zope website. (I'll read a few more sections first though, just in case... :-) -sm.

In this chapter you'll learn more about building basic web applications in Zope using Folders, Scripts, and Methods. Another way of terming this is that you'll learn more about creating applications in Zope "instance space".

Building "Instance-Space" Applications

In Zope, there are a few ways to develop a web application. The simplest and fastest way, and the one we've been concentrating on thus far in this book, is to build an application in instance space. To understand the term "instance space", we need to once again put on our "object orientation hats".

When you create Zope objects by selecting them from the Zope "Add" list, you are creating instances of a class defined by someone else (see the Object Orientation chapter if you need to brush up on these terms). For example, when you add a Script (Python) object to your Zope database, you are creating an instance of the Script (Python) class. The Script (Python) class was written by a Zope Corporation engineer. When you select "Script (Python)" from the Add list, and you fill in the form to give an id and title and whatnot, and click the submit button on the form, Zope creates an instance of that class in the Folder of your choosing. Instances such as these are inserted into your Zope database and they live there until you delete them.

In the Zope application server, most object instances serve to perform presentation duties, logic duties, or content duties. You can "glue" these instances together to create basic Zope applications. Since these objects are really instances of a class, the term "instance space" is commonly used to describe the Zope root folder and all of its subfolders. "Building an application in instance space" is defined as the act of creating Zope object instances in this space and modifying them to act a certain way when they are executed.

Instance-space applications are typically created from common Zope objects. Script (Python) objects, Folders, DTML Methods, Page Templates, and other Zope services can be glued together to build simple applications.

Instance-Space Applications vs. Products

In contrast to building applications in instance space, you may also build applications in Zope by builing them as Products. Building an application as a Product differs from creating applications in instance space inasmuch as the act of creating a Product typically allows you to extend Zope with new "addable" objects that appear in Zope's "Add" list. Building a Product also typically allows you to more easily distribute an application to other people, and allows you to build objects that may more closely resemble your "problem space". We explore one way to create Products in the chapter entitled Extending Zope. Building a Product is typically more complicated than building an "instance-space" application, so we get started here by describing how to build instance-space applications. When you find that it becomes difficult to maintain, extend, or distribute an instance-space application you've written, it's probably time to reconsider rewriting it as a Product.

Using A Folder as A Container For Your Instance-Space Application

Folders provide containers for your applications. A natural way to build a simple Zope application is to create a Folder in your Zope root folder to hold objects related to the application. For example, you may have an Invoices folder to hold an invoice application. You could create "logic" objects inside that folder named addInvoice and editInvoice to allow you to add and edit the invoices. The actual invoices themselves could be DTML Documents or File objects, which could also live in the Invoices folder. Your Invoices folder thus becomes a small application.

URLs are used to work with instance-space Zope applications. As you've seen, you can display a Zope object by visiting its URL in your browser, and in object-orientation terms, when you visit an object in a folder, you are "calling a method in the context of the folder". So for example, the URL http://localhost:8080/Invoices/addInvoice calls the addInvoice method of the Invoices folder. This URL would perhaps take you to a screen that allows you to add an invoice. Likewise, the URL http://localhost:8080/Invoices/editInvoice?invoice_number=42 might call the editInvoice method of the Invoices folder, passing it the argument invoice_number with a value of 42. The resulting HTML might allow you to edit invoice number 42.

Anonymous User - Aug. 17, 2005 7:01 am:
 could someone explain how you actually use arguments in a page template that have been passed from another
 page templates using the above method? If I try to implement the above I get an AttributeError for
 invoice_number.
Anonymous User - Aug. 17, 2005 1:26 pm:
 if you want to pass a page template (editInvoice) an argument (invoice_number=42) on the end of a url
 http://localhost:8080/Invoices/editInvoice?invoice_number=42

 you can retrieve this argument by using request, for example, 
<p> the invoice number we are currently working on is <span
tal:content="request/invoice_number"></span></p>

 I think it is also possible to pass an argument to a page template (test2) from a script (python) object you
 could
 return context.test2(var=somevar)

 you can then retrieve this argument using options rather than request, for example,
<p> the invoice number we are currently working on is <span
tal:content="options/invoice_number"></span></p>

Using Objects as Methods Of Folders Via URLs

The invoices example demonstrates a powerful Zope feature. You can execute a Zope object in the context of a folder by visiting a URL that consists of the folder's URL followed by the id of a Zope object. For example, in the URL http://localhost:8080/Invoices/addInvoice, the name Invoices refers to a folder. In object-orientation terms, the "final" object in the URL (addInvoice) is then used as a "method". The object you call which is used as a method may be a Script (Python) object, a DTML Method, a Page Template, or just about any other kind of Zope object.

This facility is used throughout Zope and is a very general design pattern. In fact you are not restricted to using a folder as the context of a method via a URL. You may call objects as methods in the context of many kinds of Zope objects using the same URL technique.

Using Acquisition In Instance-Space Applications

The Zope facility named Acquisition proves useful when creating instance-space applications. Acquisition allows you to share behavior between different parts of the same application. A folder is said to acquire an object by searching for the object in its containers if it cannot find the object by name in itself.

For example, suppose you want to call a method named viewFolder on one of your folders. Perhaps you have many different viewFolder objects which can be used as methods, each of which represents a particular view of a folder. Zope "figures out" which one you want by first looking in the folder which is named by the "rightmost" portion of the URL. For example, if you invoke the URL http://localhost:8080/Invoices/July/viewFolder, and the "Invoices" and "July" objects are folders, the invoices object will be searched for a viewFolder object first. If Zope can't find the object there it looks for an object named viewFolder in the folder's containing folder (July). If the object can't be found there, it goes up another level. This process continues until Zope finds the object or gets to the root folder. If Zope can't find the object in the root it gives up and raises an exception.

Anonymous User - Nov. 10, 2003 11:34 am:
 Example is described backwards - Wi\orking right-to-left the July folder is searched first, then it's
 container folder Invoices - Duke
Anonymous User - July 13, 2004 12:00 pm:
 Yes! The Explanation is wrong!
 What is a Productdocumentation good for if it is wrong.
 This Zope and Plone Projects show that such open source-projects,
 and open documentation with community-development
 is no real alternative for professional use for IT-Projects!!!
golem1 - July 13, 2004 4:01 pm:
 >Yes! The Explanation is wrong!  Yada yada

 Pay no attention to astroturfers.
Anonymous User - Dec. 8, 2004 8:44 am:
 Wrong productdocumentation is good for defining good productdocumentation.

 And we just did ;)

The Special Folder Object index_html

If there is an object in a Zope folder named index_html, the return value of this object will be used as the default view of the folder when the folder's URL is called. This is analogous to how an index.html file provides a default view for a directory in Apache and other web servers. Instead of explicitly including the name index_html in your URL to show default content for a folder, you can omit it. For example, if you create an index_html object in your Invoices folder and view the folder by clicking the View tab or by visiting the URL http://localhost:8080/Invoices/, Zope will call the index_html object in the Invoices folder and display its results. You can also use the more explicit URL http://localhost:8080/Invoices/index_html, and it will display the same content.

A folder can also acquire an index_html object from its parent folders. You can use this behavior to create a default view for a set of folders. To do so, create an index_html object in a folder which contains another set of folders. This default view will be used for all the folders in the set. This behavior is already evident in Zope. If you create a set of empty Folders in the Zope root folder, you will notice that when you view any of the Folders via a URL, the content of the "root" folder's index_html method is displayed. The index_html in the root folder is acquired. Furthermore, if you create more empty folders inside the folders you've just created in the root folder, a visit to these folders' URLs will also show the root folder's index_html. This is acquisition at work. NOTE: We are using the index_html method as an example here, but this will work with any Zope object which acts as a method, it needs not be named "index_html".

If you want a different default view of a given folder, just create a custom index_html object in that particular folder. This allows you to override the default view of a particular folder on a case-by-case basis, while allowing other folders defined at the same level to acquire a common default view.

The index_html object may be a DTML Method, a Page Template, a Script (Python) object, or any other Zope object that is URL-accessible and which returns browser-renderable content. The content is typically HTML, but Zope doesn't care. You can spit out XML or text or whatever you like.

Building the Zope Zoo Website

In this section, we'll create a simple web site in instance space for the "Zope Zoo". As the Zoo webmaster, it is your job to make the web site easy to use and manage. Here are some things you'll need:

  • Zoo users must easily move around the site, just as if they were walking through a real Zoo.
  • All of your shared web layout tools, like a Cascading Style Sheet (CSS), must be in one easy to manage location.
  • You must provide a simple file library of various documents that describe the animals.
  • You need a site map so that users can quickly get an idea of the layout of the entire Zoo.
  • A Guest book must be created so that Zoo visitors can give you feedback and comments about your site.
  • A what's new section must be added to the guest book so that you can see any recent comments that have been added.

Navigating the Zoo

In order for your navigation system to work, you will need to create some basic site structure. We need to create some folders in your Zope system that represent the structure of your site. Let's use a zoo structure made out of Folders, as shown in the figure below.

Zoo folder structure.

Figure 5-1 Zoo folder structure.

You should create a top-level folder named ZopeZoo. Within the ZopeZoo folder, you should create three subfolders, Reptiles, Mammals and Fish. Within the Mammals folder, you should create a folder named Whales. Within the Reptiles folder, you should create two folders, Lizards and Snakes.

To navigate your site, users will visit the default view of the ZopeZoo folder (the "front page") and click on one of the top level folders to enter that particular part of the Zoo. They should also be able to use a very similar interface to keep going deeper into the site. For instance, if the user wishes to visit the "Mammals" section, the view of the Mammals section should have a similar interface to that of the Zoo itself. Also, the user should be able to back out of a section and go up to the parent section.

To provide navigation facilities, in the ZopeZoo folder, create a DTML Method named navigation:


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

When the method you just created is executed, it displays a list of links. Each link targets the default view of a subfolder. The list of subfolders displayed depends on the context in which the method is executed. For example, if the method is executed in the context of the "Mammals" folder, it will display a link to the default view of the Whales folder. If the method is executed in the context of the "ZopeZoo" folder, it will display links to the default views of the "Mammals", "Fish", and "Reptiles" folders. It's important to notice that this method can be used to display the contents of any folder, so we can use it for most of our "default" folder views. Furthermore, since we've placed this method in the ZopeZoo folder, each of the zoo subfolders will acquire and use it.

Now, you need to incorporate the navigation method into the site. Let's create two DTML methods. One will be used as a standard "header" for all pages within the site, the other a standard "footer". Do this by first creating a DTML Method named standard_html_header in the ZopeZoo folder. We will include the navigation links in the display of this method by referencing the navigation method via 'dtml-var':


<html>
<head><title><dtml-var title></title>
  <!-- Changed by: Peter Sabaini, 05-Jan-2004 -->

<body>
<dtml-var navigation>
Anonymous User - Mar. 15, 2004 11:19 am:
 you should close the <head> tag:
 <html>
 <head><title><dtml-var title></title>
   <!-- Changed by: Peter Sabaini, 05-Jan-2004 -->

 </head>
 <body>
 <dtml-var navigation>

 Umberto

Now create a DTML Method named standard_html_footer in your ZopeZoo folder and provide it with this content:


</body>
</html>

We need to add a front page to the Zoo site and then we can view the site and verify that the navigation works correctly.

Adding a Front Page to the Zoo

In order to display our navigation and standard header and footer, we need a front page that serves as the welcome screen for Zoo visitors. In order to do so, create a DTML Method in the ZopeZoo folder named index_html with the following content:


<dtml-var standard_html_header>

  <h1>Welcome to the Zope Zoo</h1>

  <p>Here you will find all kinds of cool animals.  You are in
  the <b><dtml-var getId></b> section.</p>

<dtml-var standard_html_footer>

Take a look at how your site appears by clicking on the View tab of the ZopeZoo folder. The results of doing so are shown in the figure below.

Zope Zoo front page.

Figure 5-2 Zope Zoo front page.

Here you start to see how things come together. At the top of your main page you see a list of links to the various subsections. These links are created by the navigation method that is included by the standard_html_header method.

You can use the navigation links to travel through the various sections of the Zoo. Use this navigation interface to find the reptiles section.

Zope builds this page to display a folder by looking for the default folder view method ,index_html. It walks up the zoo site folder by folder until it finds the index_html method in the ZopeZoo folder. It then calls this method on the Reptiles folder. The index_html method calls the standard_html_header method which in turn calls the navigation method. Finally, the index_html method displays a welcome message and calls the standard_html_footer.

What if you want the reptile page to display something besides the welcome message? You can replace the index_html method in the reptile section with a more appropriate display method and still take advantage of the zoo header and footer including navigation.

In the Reptile folder create a DTML Method named index_html. Give it some content more appropriate to reptiles:


<dtml-var standard_html_header>

<h1>The Reptile House</h1>

<p>Welcome to the Reptile House.</p>

<p>We are open from 6pm to midnight Monday through Friday.</p>

<dtml-var standard_html_footer>

Now take a look at the reptile page by going to the Reptile folder and clicking the View tab.

Since the index_html method in the Reptile folder includes the standard headers and footers, the reptile page still includes your navigation system.

Click on the Snakes link on the reptile page to see what the Snakes section looks like. The snakes page looks like the Reptiles page because the Snakes folder acquires its index_html display method from the Reptiles folder instead of from the ZopeZoo folder.

Improving Navigation

The navigation system for the zoo works pretty well, but it has one big problem. Once you go deeper into the site you need to use your browser's back button to go back. There are no navigation links to allow you to navigate up the folder hierarchy. Let's add a navigation link to allow you to go up the hierarchy. Change the navigation method in the ZopeZoo folder:


<a href="..">Return to parent</a><br>

<ul>
<dtml-in expr="objectValues('Folder')">
  <li><a href="&dtml-absolute_url;"><dtml-var title_or_id></a><br></li>
</dtml-in>
</ul>
Anonymous User - Oct. 19, 2004 10:07 am:
 I don't know if anyone had this problem during this section but when a folder acquired an "index_html" page
 from a higher object, the href to traverse up one directory didn't keep the correct context and would go
 above the ZoperZoo folder. The actual URL of the context would default to the exact location of the acquired
 index_html page. To keep the URL correct, I changed it to this.
 <a href="<dtml-var URL1>/..">Return to Parent</a><br>

Now view the ZopeZoo folder to see how this new link works, as shown in the figure below.

Improved zoo navigation controls.

Figure 5-3 Improved zoo navigation controls.

As you can see, the Return to parent link allows you to go back up from a section of the site to its parent. However, some problems remain; when you are at the top level of the site you still get a Return to parent link which leads nowhere. Let's fix this by changing the navigation method to hide the parent link when you're in the ZopeZoo folder:


<dtml-if expr="id != 'ZopeZoo'">
  <a href="..">Return to parent</a><br>
</dtml-if>

<ul>
<dtml-in expr="objectValues('Folder')">
  <li><a href="&dtml-absolute_url;"><dtml-var title_or_id></a><br></li>
</dtml-in>
</ul>
Anonymous User - July 16, 2004 9:15 am:
 Everywhere I hear that TAL should be used instead of DTML because of design issues, so I conducted to use
 TAL.
 Now I am learning DTML in all the examples, meaning I get used to DTML. If you really want to bring across
 that TAL is the state of the art then you should maybe use it in the examples too.
Anonymous User - Oct. 14, 2004 4:14 am:
 Agree.Why should we learn all these DTML stuff in documents while experts keep telling us you should use TAL
 instead of DTML?

Now the method tests to see if the current context object is named ZopeZoo and declines to display the "Return to parent" link if so. View the ZopeZoo folder to see the result.

There are still some things that could be improved about the navigation system. For example, it's pretty hard to tell what section of the Zoo you're in. You've changed the reptile section, but the rest of the site all looks pretty much the same with the exception of having different navigation links. It would be nice to have each page tell you what part of the Zoo you're in.

Let's change the navigation method once again to display where you are in the Zoo:


<dtml-if expr="id != 'ZopeZoo'">
  <h2><dtml-var title_or_id> Section</h2>
  <a href="..">Return to parent</a><br>
</dtml-if>

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

Now view the ZopeZoo folder again and navigate into the Reptiles section. Notice that within the Reptiles section, you see a header which says "Reptiles Section", as shown in the figure below.

Zoo page with section information.

Figure 5-4 Zoo page with section information.

Factoring out Style Sheets

Zoo pages are built by collections of methods that operate on folders. For example, the header method calls the navigation method to display navigation links on all pages. In addition to factoring out shared behavior such as navigation controls, you can use different Zope objects to factor out shared content.

Suppose you'd like to use CSS (Cascading Style Sheets ) to tailor the look and feel of the zoo site. One way to do this would be to include the CSS tags in the standard_html_header method. This way every page of the site would have the CSS information. This is a good way to reuse content, however, this is not a flexible solution since you may want a different look and feel in different parts of your site. Suppose you want the background of the snakes page to be green, while the rest of the site should have a white background. You'd have to override the standard_html_header in the Snakes folder and make it exactly the same as the normal header with the exception of the style information. This is an inflexible solution since you can't vary the CSS information without changing the entire header.

You can create a more flexible way to define CSS information by factoring it out into a separate object that the header will insert. Create a DTML Document in the ZopeZoo folder named style_sheet. Change the contents of the document to include some style information:


<style type="text/css">
h1{
  font-size: 24pt;
  font-family: sans-serif;
}
p{
  color: #220000;
}
body{
  background: #FFFFDD;
}
</style>

This is a CSS style sheet that defines how to display h1, p and body HTML tags. Now let's include this content into our web site by inserting it into the standard_html_header method:


<html>
<head>
<dtml-var style_sheet>
</head>
<body>
<dtml-var navigation>
Anonymous User - June 16, 2002 6:47 pm:
 The title is missing from the standard_html_header for no obvious reason.
 It was there in the initial code for standard_html_header, so it should be present here as well, IMHO.

Now, when you look at documents on your site, all of their paragraphs will be dark red, and the headers will be in a sans-serif font.

Anonymous User - June 4, 2002 10:04 am:
 Sorry, it didn't work.  I think dtmf-var is broken.
Anonymous User - Nov. 2, 2003 6:49 pm:
 I found out if the file name, "standard_html_header" is copied and pasted for the file name from the Zope
 Book it does not work. But, "standard_html_header" is hand-typed, it works.

To change the style information in a part of the zoo site, just create a new style_sheet document and drop it into a folder. All the pages in that folder and its sub-folders will use the new style sheet.

Creating a File Library

File libraries are common on web sites since many sites distribute files of some sort. The old fashioned way to create a file library is to upload your files, then create a web page that contains links to those files. With Zope you can dynamically create links to files. When you upload, change or delete files, the file library's links can change automatically.

Create a folder in the ZopeZoo folder called Files. This folder contains all of the file you want to distribute to your web visitors.

In the Files folder create some empty file objects with names like DogGrooming or HomeScienceExperiments, just to give you some sample data to work with. Add some descriptive titles to these files.

DTML can help you save time maintaining this library. Create an index_html DTML Method in the Files folder to list all the files in the library:


<dtml-var standard_html_header>

<h1>File Library</h1>

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

<dtml-var standard_html_footer>  

Now view the Files folder. You should see a list of links to the files in the Files folder as shown in [5-5].

Anonymous User - June 2, 2004 5:25 am:
 i think u need to add 

 <html>
 <head>
 <dtml-var style_sheet>
 </head>
 <body>

 in place of <dtml-var standard_html_header>
 so as to see the a similar thing in ur PC as is shown
 Harsh Jain, MKI, Düsseldorf

File library contents page.

Figure 5-5 File library contents page.

Anonymous User - June 16, 2002 6:56 pm:
 This screenshot seems outdated:

 1) The navigation is missing, for me it displays

 Files Section

 Return to parent (as a link)

 Before the content shown on the picture.

 2) The stylesheet just created is in effect, i. e. the heading is in sans-serif and the background color
 yellow.

If you add another file, Zope will dynamically adjust the file library page. You may also want to try changing the titles of the files, uploading new files, or deleting some of the files.

The file library as it stands is functional but Spartan. The library doesn't let you know when a file was created, and it doesn't let you sort the files in any way. Let's make the library a little fancier.

Most Zope objects have a bobobase_modification_time method that returns the time the object was last modified. We can use this method in the file library's index_html method:


<dtml-var standard_html_header>

<h1>File Library</h1>

<table>
  <tr>
    <th>File</th>
    <th>Last Modified</th>
   </tr>

<dtml-in expr="objectValues('File')">
  <tr>
     <td><a href="&dtml-absolute_url;"><dtml-var title_or_id></a></td>
     <td><dtml-var bobobase_modification_time fmt="aCommon"></td>
  </tr>
</dtml-in>

</table>

<dtml-var standard_html_footer>  

The new file library method uses an HTML table to display the files and their modification times.

Finally let's add the ability to sort this list by file name or by modification date. Change the index_html method again:


<dtml-var standard_html_header>

<h1>File Library</h1>

<table>
  <tr>
    <th><a href="&dtml-URL0;?sort=name">File</a></th>
    <th><a href="&dtml-URL0;?sort=date">Last Modified</a></th>
   </tr>

<dtml-if expr="_.has_key('sort') and sort=='date'">
  <dtml-in expr="objectValues('File')" 
           sort="bobobase_modification_time" reverse>
    <tr>
       <td><a href="&dtml-absolute_url;"><dtml-var title_or_id></a></td>
       <td><dtml-var bobobase_modification_time fmt="aCommon"><td>
    </tr>
  </dtml-in>
<dtml-else>      
  <dtml-in expr="objectValues('File')" sort="id">
    <tr>
       <td><a href="&dtml-absolute_url;"><dtml-var title_or_id></a></td>
       <td><dtml-var bobobase_modification_time fmt="aCommon"><td>
    </tr>
  </dtml-in>
</dtml-if>

</table>

<dtml-var standard_html_footer>  

Now view the file library and click on the File and Last Modified links to sort the files. This method works with two sorting loops. One uses the in tag to sort on an object's id. The other does a reverse sort on an object's bobobase_modification_time method. The index_html method decides which loop to use by looking for the sort variable. If there is a sort variable and if it has a value of date then the files are sorted by modification time. Otherwise the files are sorted by id.

Building a Guest Book

A guest book is a common and useful web application that allows visitors to your site to leave messages. Figure [5-6] shows what the guest book you're going to write looks like.

Zoo guest book.

Figure 5-6 Zoo guest book.

Start by creating a folder called GuestBook in the root folder. Give this folder the title The Zope Zoo Guest Book. The GuestBook folder will hold the guest book entries and methods to view and add entries. The folder will hold everything the guest book needs. After the guest book is done you will be able to copy and paste it elsewhere in your site to create new guest books.

You can use Zope to create a guest book several ways, but for this example, you'll use one of the simplest. The GuestBook folder will hold a bunch of Files, one file for each guest book entry. When a new entry is added to the guest book, a new file is created in the GuestBook folder. To delete an unwanted entry, just go into the GuestBook folder and delete the unwanted file using the management interface.

Let's create a method that displays all of the entries. Call this method index_html so that it is the default view of the GuestBook folder:


<dtml-var standard_html_header>

<h2><dtml-var title_or_id></h2>

<!-- Provide a link to add a new entry, this link goes to the
addEntryForm method -->

<p>
  <a href="addEntryForm">Sign the guest book</a>
</p>

<!-- Iterate over each File in the folder starting with
the newest documents first. -->

<dtml-in expr="objectValues('File')"
         sort="bobobase_modification_time" reverse>

<!-- Display the date, author and contents of each file -->

  <p>
  <b>On <dtml-var bobobase_modification_time fmt="aCommon">, 
     <dtml-var guest_name html_quote null="Anonymous"> said:</b><br>

  <dtml-var sequence-item html_quote newline_to_br>

  <!-- Make sure we use html_quote so the users can't sneak any
  HTML onto our page -->

</p>

</dtml-in>

<dtml-var standard_html_footer>

This method loops over all the files in the folder and displays each one. Notice that this method assumes that each file will have a guest_name property. If that property doesn't exist or is empty, then Zope will use Anonymous as the guest name. When you create a entry file you'll have to make sure to set this property.

Next, let's create a form that your site visitors will use to add new guest book entries. In the index_html method above we already created a link to this form. In your GuestBook folder create a new DTML Method named addEntryForm:


<dtml-var standard_html_header>

<p>Type in your name and your comments and we'll add it to the
guest book.</p>

<form action="addEntryAction" method="POST">
<p> Your name: 
  <input type="text" name="guest_name" value="Anonymous">
</p>
<p> Your comments: <br>
  <textarea name="comments" rows="10" cols="60"></textarea>
</p>

<p>
  <input type="submit" value="Send Comments">
</p>  
</form>

<dtml-var standard_html_footer>

Now when you click on the Sign Guest Book link on the guest book page you'll see a form allowing you to type in your comments. This form collects the user's name and comments and submits this information to a method named addEntryAction.

Now create an addEntryAction DTML Method in the GuestBook folder to handle the form. This form will create a new entry document and return a confirmation message:


<dtml-var standard_html_header>

<dtml-call expr="addEntry(guest_name, comments)">

<h1>Thanks for signing our guest book!</h1>

<p><a href="<dtml-var URL1>">Return</a>
to the guest book.</p>

<dtml-var standard_html_footer>
Anonymous User - May 9, 2002 4:48 pm:
 URL1? Not working for me!?
Anonymous User - June 3, 2002 2:24 am:
 I've tried to send some comments to the guestbook, files of comments have been created but those comments
 didn't list out like the above pic.
Anonymous User - June 13, 2002 5:01 am:
 is it possible to send the form data straight to the form and then return to the guestbook page with the
 validated entry?
Anonymous User - Mar. 22, 2005 3:48 pm:
 http://viagra.intelisearch.org

This method creates a new entry by calling the addEntry method and returns a message letting the user know that their entry has been added.

The last remaining piece of the puzzle is to write the script that will create a file and sets its contents and properties. We'll do this in Python since it is much clearer than doing it in DTML. Create a Python-based Script in the GuestBook folder called addEntry with parameters guest_name and comments:


## Script (Python) "addEntry"
##parameters=guest_name, comments
##
"""
Create a guest book entry.
"""
# create a unique file id
id='entry_%d' % len(context.objectIds())

# create the file
context.manage_addProduct['OFSP'].manage_addFile(id,
                                         title="", file=comments)

# add a guest_name string property
doc=getattr(context, id)
doc.manage_addProperty('guest_name', guest_name, 'string')
Anonymous User - May 22, 2002 11:06 am:
 Where does the ['OFSP'] come from?
 After digging around and doing a few searches,  I found it's a core part of
 Zope,   but no explainations.
Anonymous User - June 12, 2002 12:38 pm:
 I get an error that states:
 Error Type: TypeError
 Error Value: addEntry() takes no arguments (2 given)

 What did I do wrong?
Anonymous User - June 15, 2002 9:54 am:
 You forgot to include the parameters for the Python script in the Parameters field when pasting the script
 into a new Python Script object. You need to explicitly state what parameter your python 'function' takes
 when creating the script.
Anonymous User - Feb. 10, 2005 9:33 pm:
 how about a little explanation of this script here? it is very important to the functionality but is
 completely glossed over - there's no way someone would be able to actually roll their own, so why even have
 the guestbook component at all in this chapter?
Anonymous User - Mar. 14, 2005 12:52 pm:
 did not work for me. just coming back to index page with no entries.

This script uses Zope API calls to create a File and to create a property on it. This script performs the same sort of actions in a script that you could do manually; it creates a file, edits it and sets a property.

The guest book is now almost finished. To use the simple guest book, just visit http://localhost:8080/GuestBook/.

One final thing is needed to make the guest book complete. More than likely your security policy will not allow anonymous site visitors to create files. However the guest book application should be able to be used by anonymous visitors. In Chapter 7, User and Security, we'll explore this scenario more fully. The solution is to grant special permission to the addEntry method to allow it to do its work of creating a file. You can do this by setting the Proxy role of the script to Manager. This means that when the script runs it will work as though it was run by a manager regardless of who is actually running the method. To change the proxy roles go to the Proxy view of the addEntry script, as shown in [5-7].

Anonymous User - July 16, 2004 9:44 am:
 Users and security is chapter 12 not 7
Anonymous User - Apr. 12, 2005 12:12 pm:
 when i post the guest_name and comment i have a identification popup but no user (even the admin) can be
 authenticated by this popup
 At the end an authentification error occure...
Anonymous User - May 27, 2005 11:58 am:
 I managed to redo the above example using ZPT rather than DTML like so

 index_html:

 <html>
   <head>
     <title tal:content="template/title">The title</title>
   </head>
   <body>

     <h2 tal:content="template/title_or_id">content title or id</h2>

     <p>
       <a href="addEntryForm">Sign the guest book</a>
     </p>

     <!-- Display the date, author and contents of each file -->

     <tal:block tal:repeat="item python:container.objectValues(['File'])">
<p> <b>On <b tal:replace="python:DateTime.aCommon(item.bobobase_modification_time())" />,
<b tal:define="anonymous string:Anonymous" tal:replace="item/guest_name | anonymous" />
said:</b><br>

       <span tal:replace="item" />
       </p>

     </tal:block>

   </body>
 </html>

 addEntryForm:

 <html>
   <head>
     <title tal:content="template/title">The title</title>
   </head>
   <body>
     <p>Type in your name and your comments and we'll add it to the guest book.</p>

     <form action="addEntryAction" method="POST">
       <p> Your name: 
         <input type="text" name="guest_name" value="Anonymous">
       </p>
       <p> Your comments: <br>
         <textarea name="comments" rows="10" cols="60"></textarea>
       </p>

       <p>
         <input type="submit" value="Send Comments">
       </p>  
     </form>
   </body>
 </html>

 thanks:

 <html>
   <head>
     <title tal:content="template/title">The title</title>
   </head>
   <body>

   <h1>Thanks for signing our guest book!</h1>

   <p><a tal:attributes="href request/URL1">Return</a> to the guest book.</p>

   </body>
 </html>

 The python script as is I had no luck with (same problem as previous Anonymous User), so I changed it as
 follows
 addEntryAction:

 """
 Create a guest book entry.
 """
 # create a unique file id
 id='entry_%d' % len(container.objectValues(['File']))

 # create the file 
 context.manage_addFile(id, file=comments, content_type="text/plain")

 # add a guest_name string property
 doc=getattr(context, id)
 doc.manage_addProperty('guest_name', guest_name, 'string')

 page=container['thanks']
 return page()

Setting proxy roles for the addEntry script.

Figure 5-7 Setting proxy roles for the addEntry script.

Now select Manager from the list of proxy roles and click Change.

Congratulations, you've just completed a functional web application. The guest book is complete and can be copied to different sites if you want.

Extending the Guest Book to Generate XML

All Zope objects can create XML. It's fairly easy to create XML with DTML. XML is just a way of describing information. The power of XML is that it lets you easily exchange information across the network. Here's a simple way that you could represent your guest book in XML:


<guestbook>
  <entry>
    <comments>My comments</comments>
  </entry>
  <entry>
    <comments>I like your web page</comments>
  </entry>
  <entry>
    <comments>Please no blink tags</comments>
  </entry>
</guestbook>

This XML document may not be that complex but it's easy to generate. Create a DTML Method named "entries.xml" in your guest book folder with the following contents:


<guestbook>
  <dtml-in expr="objectValues('DTML Document')">
  <entry>
    <comments><dtml-var document_src html_quote></comments>
  </entry>
  </dtml-in>
</guestbook>
Anonymous User - May 4, 2002 1:12 am:
 The guestbook entries are not 'DTML Document' type objects, but 'File' type objects in the above examples.
 Thus, I think the following codes are more suitable.
 <?xml version="1.0" encoding="EUC-KR" ?>

 <guestbook>
   <dtml-in expr="objectValues('File')">
   <entry>
    <author><dtml-var guest_name null="Anonymous"></author>
    <comments><dtml-var sequence-item html_quote></comments>
   </entry>
   </dtml-in>
 </guestbook>

As you can see, DTML is equally adept at creating XML as it is at creating HTML. Simply embed DTML tags among XML tags and you're set. The only tricky thing that you may wish to do is to set the content-type of the response to text/xml, which can be done with this DTML code:


<dtml-call expr="RESPONSE.setHeader('content-type', 'text/xml')">

The whole point of generating XML is producing data in a format that can be understood by other systems. Therefore you will probably want to create XML in an existing format understood by the systems you want to communicate with. In the case of the guest book a reasonable format may be the RSS (Rich Site Summary) XML format. RSS is a format developed by Netscape for its my.netscape.com site, which has since gained popularity among other web logs and news sites. The Zope.org web site uses DTML to build a dynamic RSS document.

Congratulations! You've XML-enabled your guest book in just a couple minutes. Pat yourself on the back. If you want extra credit, research RSS enough to figure out how to change entries.xml to generate RSS.

The Next Step

This chapter shows how simple web applications can be made. Zope has many more features in addition to these, but these simple examples should get you started on create well managed, complex web sites.

In the next chapter, we'll see how the Zope security system lets Zope work with many different users at the same time and allows them to collaborate together on the same projects.

Previous Page Up one Level Next Page Creating Basic Zope Applications Comments On/Off Table of Contents