Skip to content.

plope

Personal tools
You are here: Home » Books » 2_7Edition » Creating Basic Zope Applications
 
 
Views
  • State: visible
Previous Page Up one Level Next Page Creating Basic Zope Applications Comments On/Off Table of Contents Sections Index

Creating Basic Zope Applications

XXX: add new screen shots

XXX: convert guest book to zpt

This chapter will take you, step by step, through building a basic web application in Zope. As we go through the chapter, we will examine some of Zope's main concepts at work. Using Zope Folder, Script (Python), and Page Template objects, we'll create a simple web site for an imaginary zoo: the "Zope Zoo", of course!

We will develop the web site as a Zope "instance-space" application. A discussion of instance space is at the end of this chapter, but for now it is enough to know that instance-space applications are the easiest and fastest kind to build, because we can do everything in our favorite web browser.

Goals for the Zope Zoo Web Site

As with any project, we first need to clarify our goals for the Zope Zoo application. The application's primary goal is to create a web site for the world-renowned Zope Zoo. Furthermore, we want to make the web site easy to use and manage. Here are some things we'll do:

  • Enable web users to navigate the site easily, as if they were moving around a real zoo.
  • Keep all our shared web layout tools, like a Cascading Style Sheet (CSS), in a single, easy-to-manage location.
  • Design the web site so that future site-wide changes are quick and easy to implement.
  • Take advantage of Zope to create a dynamic web site in which web pages build themselves "on the fly" when requested so that they are always up to date.
  • Provide a simple file library of various documents that describe the animals.
  • A guest book must be created so that zoo visitors can give you feedback and comments about your site.

Beginning with a Folder

Zope Folder objects provide natural containers and organizers for web applications. A good way to start building an application is to create a new Folder to hold all the objects and subfolders related to the application.

Consider, for example, a Zope folder named Invoices to hold an application for managing invoices through the Web. The Invoices folder could contain both the logic objects - or "methods" - which allow you to add and edit invoices, as well as the actual data of the invoices. The Invoices folder thus becomes a small Zope application.

We begin building our Zope Zoo web site application by creating a Zope Folder object to hold it all together in one place.

Step 1: Create ZopeZoo Folder

If you haven't already, start your Zope installation and log into the Zope Management Interface (ZMI) using your favorite browser. (If you are not familiar with the ZMI, refer to the Installing and Starting Zope chapter.)

  1. Navigate to Zope's top-level root folder.
  2. Use the Add list to create a new Folder.
  3. Give the new folder the Id ZopeZoo.
  4. Check Create public interface.
  5. Click Add.

(For now, we will ignore the optional Title fields.)

Designing a Navigable Zoo

One of our goals is to enable easy user movement around the web site. A key to this easy movement is a navigation interface that is consistent among the site's pages. In other words, every web page in the site should present a similar set of hyperlinks, in a similar place on the page, on which users can rely to guide them through the site.

We also want to make sure the navigation links are always correct, regardless of how the structure of the site changes. The solution is to design a meaningful site structure, and then create the Zope methods that will dynamically present the current structure to web users in the form of navigation links.

First, let's define the site structure. If the Zope Zoo was real, we might model the web site's organization on the zoo's physical or logical design. For our purposes, we will pretend that the zoo houses three classes of animals. We'll organize the web site by adding folders inside our ZopeZoo folder.

Step 2: Create Site Organization

(NOTE: Do not create public interfaces for the folders in this step.)

  1. Enter the ZopeZoo folder and create three subfolders with Ids: Reptiles, Mammals and Fish.
  2. Inside the Mammals folder, add one folder named Whales.
  3. Navigate to the Reptiles folder and create two folders there: Lizards and Snakes.

In Zope's Navigator frame on the left side, you should see an icon for the ZopeZoo folder. (If you don't see it, click Refresh in the Navigator). To view the ZopeZoo folder hierarchy - i.e. our nascent web site's structure - expand the ZopeZoo folder by clicking the little plus sign next to the icon. Similarly expand the zoo subfolders. You'll see something like the figure below.

Zoo folder structure.

Figure 5-1 Zoo folder structure.

Now we create the basic presentation objects: The main template and the style sheet z_zoo.css. To get started, we ask a web designer to create a HTML mockup and a CSS file that together represent the web page layout shared throughout the site.

For the style sheet we create a simple File object in Zope. No need to make it dynamic.

Step 3: Create the Style Sheet

  1. Go to the top level of our zoo web site, the ZopeZoo folder.
  2. Select File from the Add list.
  3. Give the file an Id of z_zoo.css.
  4. Click Add.
  5. Select z_zoo.css to get its Edit view.
  6. Copy and paste these style definitions into the File Data area:
    
    body, p, th, td {
      font-family: Verdana, Arial, Helvetica, sans-serif;
      font-size: 10pt;
    }
    h1 {
      color: #6699cc;
      font-family: Verdana, Arial, Helvetica, sans-serif;
      font-size: 18pt;
      font-weight: bold;
    }
    p {
      color: #660000;
    }
    .status_message{
      background: #ffffaa;
      border-style: solid;
      border-width: thin;
      font-weight: bold;
      padding: 4pt;
    }
    th {
      background: #dee7ec;
      text-align: left;
    }
    

At this stage, the HTML page the web designer created for us is valid XHTML 1.0 Strict and could also live in a static File object. But in the next steps we will convert the page into a dynamic template by adding TAL and METAL statements, so we need a Page Template object. For now we use the index_html method already added by selecting Create public interface in step 1.

Step 4: Create the Main Template

  1. Select index_html to get its Edit view.
  2. Replace all of the stock template code with this:
    
    <!DOCTYPE html PUBLIC
        "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
    <html>
    <head>
    
    <title>PAGE TITLE OR ID</title>
    <link rel="stylesheet" href="z_zoo.css" type="text/css" />
    
    </head>
    <body>
    
    <div>&gt; <a href="ABSOLUTE_URL">PARENT TITLE OR ID</a> </div>
    
    <ul>
      <li><a href="ABSOLUTE_URL">SUB-OBJECT TITLE OR ID</a></li>
    </ul>
    
    <h1>PAGE TITLE OR ID</h1>
    
    <p class="status_message">STATUS MESSAGE</p>
    
    <p>THIS IS WHERE THE PAGE'S CONTENT GOES.</p>
    
    </body>
    </html>
    

Our web designer marked placeholders for dynamic elements with UPPERCASE letters. Using the Test tab of the new template, we can see the static HTML page. Don't blame the web designer for the spartan layout. It's for the sake of an easy example. If you don't understand the XHTML and CSS code you might want to learn more about HTML first. This chapter shows you how to make that code dynamic.

Step 5: Dynamic Title and Headline

  1. Go to the Edit tab of index_html.
  2. Find these two lines:
    
    <title>PAGE TITLE OR ID</title>
    ...
    <h1>PAGE TITLE OR ID</h1>
    
  3. Change them to look like that:
    
    <title tal:content="context/title_or_id">PAGE TITLE OR ID</title>
    ...
    <h1 tal:content="context/title_or_id">PAGE TITLE OR ID</h1>
    

The path expression context/title_or_id returns the title of the context object or - if that doesn't exist - its id. We work in the context of the ZopeZoo folder, which has no title. So clicking again on the Test tab you'll see that title and headline are replaced by the id ZopeZoo. (You might want to open the Test tab in a new window to see the title of the browser window.) After completing the next step you'll be able to navigate to subfolders and see title and headline change depending on the context.

Step 6: Generate Subfolder Menu Dynamically

  1. Find the example menu item:
    
    <ul>
      <li><a href="ABSOLUTE_URL">SUB-OBJECT TITLE OR ID</a></li>
    </ul>
    
  2. Extend it like this:
    
    <ul tal:condition="python: context.objectValues(['Folder'])">
      <li tal:repeat="item python: context.objectValues(['Folder'])">
        <a href="ABSOLUTE_URL"
           tal:attributes="href item/absolute_url"
           tal:content="item/title_or_id">SUB-OBJECT TITLE OR ID</a></li>
    </ul>
    

The Python expression 'context.objectValues(['Folder'])' returns all the subfolders in our context. The tal:condition statement checks if any subfolders exist. If not, the complete ul element is removed. That means we have reached a leaf of the navigation tree and don't need a subfolder menu.

Otherwise, the same expression in the tal:repeat statement of the li element will return a list of subfolders. The li element will be repeated for each item of this list. In step 3 we created three subfolders in the ZopeZoo folder, so using again the Test tab we will see three list items, each with the correct id and link URL. For now there are no links back, so use the back button of your browser if you can't wait exploring the site.

Step 7: Generate Breadcrumbs Dynamically

  1. Look for this line:
    
    <div>&gt; <a href="ABSOLUTE_URL">PARENT TITLE OR ID</a> </div>
    
  2. Replace it by:
    
    <div><tal:loop tal:repeat="item python: request.PARENTS[-2::-1]">&gt;
      <a href="ABSOLUTE_URL"
         tal:attributes="href item/absolute_url"
         tal:content="item/title_or_id">PARENT TITLE OR
                                        ID</a> </tal:loop></div>
    

Using a trail of bread crumbs for navigation is quite an old idea, you might remember Hansel and Gretel tried that to find their way home. In our days, breadcrumbs are used for site navigation and show the path back to the root (or home) of the site.

The folder that contains the current object is also called its parent. As long as we have not reached the root object, each folder has again a parent folder. request.PARENTS is a list of all these parents from the current object down to the root object of the Zope application. request.PARENTS[-2::-1] returns a copy of that list in reverse order, starting with the second last element. We don't need the last value because ZopeZoo is located in the second level of our Zope application and we just want to navigate within the zoo.

We use again a tal:repeat statement to display the list. Because we don't want to repeat the div element, we add a dummy TAL element that doesn't show up in the rendered HTML page. Now our site navigation is complete and you can explore the sections of the zoo.

Step 8: Dynamic Status Bar

  1. Go to this line:
    
    <p class="status_message">STATUS MESSAGE</p>
    
  2. Extend it by two tal attributes:
    
    <p class="status_message"
       tal:condition="options/status_message | nothing"
       tal:content="options/status_message">STATUS MESSAGE</p>
    

We need the status bar later in this chapter. For now all we need is to make it invisible. options/status_message will later be used for some messages. But most pages don't have that variable at all and this path expression would raise an error. options/status_message | nothing catches that error and falls back to the special value nothing. This is a common pattern to test if a value exists and is true.

Step 9: Improve Style Sheet Link

  1. Find this line in the HTML head:
    
    <link rel="stylesheet" href="z_zoo.css" type="text/css" />
    
  2. Replace it by:
    
    <link rel="stylesheet" href="z_zoo.css" type="text/css"
          tal:attributes="href context/z_zoo.css/absolute_url" />
    

While the relative URI of the href attribute works thanks to acquisition, this isn't a good solution. Using the index_html method for different folders, the browser can't know that all the z_zoo.css files are in fact one and the same. Besides the CSS file the basic layout often contains a logo and other images, so making sure they are requested only once makes your site faster and you waste less bandwidth. The path expression context/z_zoo.css/absolute_url returns the absolute url of the CSS file. Using it in the href attribute we have a unique URI independent of the current context.

Step 10: Factor out Basic Look and Feel

  1. Rename index_html to z_zoo.pt.
  2. Wrap a metal:define-macro statement around the whole page and add two metal:define-slot statements for headline and content. After all these changes our main template - now called z_zoo.pt - looks like this:
    
    <metal:macro metal:define-macro="page"><!DOCTYPE html PUBLIC
        "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
    <html>
    <head>
    
    <title tal:content="context/title_or_id">PAGE TITLE OR ID</title>
    <link rel="stylesheet" href="z_zoo.css" type="text/css"
          tal:attributes="href context/z_zoo.css/absolute_url" />
    
    </head>
    <body>
    
    <div><tal:loop tal:repeat="item python: request.PARENTS[-2::-1]">&gt;
      <a href="ABSOLUTE_URL"
         tal:attributes="href item/absolute_url"
         tal:content="item/title_or_id">PARENT TITLE OR
                                        ID</a> </tal:loop></div>
    
    <ul tal:condition="python: context.objectValues(['Folder'])">
      <li tal:repeat="item python: context.objectValues(['Folder'])">
        <a href="ABSOLUTE_URL"
           tal:attributes="href item/absolute_url"
           tal:content="item/title_or_id">SUB-OBJECT TITLE OR ID</a></li>
    </ul>
    
    <metal:slot metal:define-slot="headline">
    
      <h1 tal:content="context/title_or_id">PAGE TITLE OR ID</h1>
    
    </metal:slot>
    
    <p class="status_message"
       tal:condition="options/status_message | nothing"
       tal:content="options/status_message">STATUS MESSAGE</p>
    
    <metal:slot metal:define-slot="content">
    
      <p>THIS IS WHERE THE PAGE'S CONTENT GOES.</p>
    
    </metal:slot>
    
    </body>
    </html>
    </metal:macro>
    
  3. Add again a new Page Template with the id index_html.
  4. Replace the example code of index_html with these two lines:
    
    <metal:macro metal:use-macro="context/z_zoo.pt/macros/page">
    </metal:macro>
    

Transforming our main template into an external macro and including it again using the metal:use-macro statement doesn't change the resulting HTML page in any way. But in the next step we can add code we only want to use in index_html without changing the main template.

The metal:define-macro statement in z_zoo.pt marks the complete template as reuseable macro, giving it the id page. The expression context/z_zoo.pt/macros/page in index_html points to that macro.

For later use we also added two metal:define-slot statements within the macro. That allows to override headline and body while reusing the rest of the macro.

Step 11: Add Special Front Page Code

  1. Go to the Edit tab of the new index_html.
  2. Replace it by this code:
    
    <metal:macro metal:use-macro="context/z_zoo.pt/macros/page">
    <metal:slot metal:fill-slot="headline">
    
      <h1>Welcome to the Zope Zoo</h1>
    
    </metal:slot>
    <metal:slot metal:fill-slot="content">
    
      <p>Here you will find all kinds of cool animals. You are in the
        <b tal:content="context/title_or_id">TITLE OR ID</b> section.</p>
    
    </metal:slot>
    </metal:macro>
    

The index_html should serve as the welcome screen for zoo visitors. In order to do so, we override the default slots. Take a look at how your site appears by clicking on the View tab of the ZopeZoo folder.

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.

Modifying a Subsection of the Site

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 main template including navigation.

Step 12: Create index_html for the Reptile House

  1. Go to the Reptile folder.
  2. Add a new Page Template named index_html.
  3. Give it some content more appropriate to reptiles:
    
    <metal:macro metal:use-macro="context/z_zoo.pt/macros/page">
    <metal:slot metal:fill-slot="headline">
    
      <h1>The Reptile House</h1>
    
    </metal:slot>
    <metal:slot metal:fill-slot="content">
    
      <p>Welcome to the Reptile House.</p>
    
      <p>We are open from 6pm to midnight Monday through Friday.</p>
    
    </metal:slot>
    </metal:macro>
    

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 uses the same macro as the main index_html, 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.

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.

Step 13: Creating Library Folder and some Files

  1. Add a new Folder to ZopeZoo with Id Files and Title File Library.
  2. Within that folder, add two File objects called DogGrooming and HomeScienceExperiments.

We don't need any content within the files to test the library. Feel free to add some more files and upload some content.

Step 14: Adding index_html Script and Template

  1. Within the Files folder, add this new Script (Python) with the Id 'index_html':
    
    ## Script (Python) "index_html"
    ##parameters=
    ##
    library_items = []
    items = context.objectValues(['File'])
    for item in items:
        library_items.append(
                { 'title': item.title_or_id(),
                  'url': item.absolute_url(),
                  'modified': item.bobobase_modification_time().aCommon()
                  } )
    
    options = { 'library_items': tuple(library_items) }
    
    return options
    
  2. Also add a new Page Template named index_html.pt with this content:
    
    <metal:macro metal:use-macro="context/z_zoo.pt/macros/page">
    <metal:slot metal:fill-slot="content">
    
      <table>
        <tr>
          <th width="300">File</th>
          <th>Last Modified</th>
        </tr>
        <tr>
          <td><a href="URL">TITLE</a></td>
          <td>MON DD, YYYY H:MM AM</td>
        </tr>
      </table>
    
    </metal:slot>
    </metal:macro>
    

This time the logic for our index_html method will be more complex, so we should separate logic from presentation. We start with two unconnected objects: A Script (Python) to generate the results and a Page Template to present them as HTML page.

The script loops over 'context.objectValues(['File'])', a list of all File objects in our Files folder, and appends for each file the needed values to the library_items list. Again the dynamic values are UPPERCASE in our mockup, so what we need are the file title, the url and the last modified date in a format like this: Mar 1, 1997 1:45 pm. Most Zope objects have the bobobase_modification_time method that returns a DateTime object. Looking at the API of DateTime, you'll find that the aCommon method returns the format we want.

Later we will have more return values, so we store them in the options dictionary. Using the Test tab of the script you will see the returned dictionary contains all the dynamic content needed by our template.

The template uses again the page macro of z_zoo.pt. Unlike before there is only one metal:fill-slot statement because we don't want to override the headline slot. Go to the Test tab of the template to see how our file library will look like.

Step 15: Bringing Things Together

  1. Replace the last line of the index_html script by this one:
    
    return getattr(context, 'index_html.pt')(**options)
    
  2. Look for this example table row in index_html.pt:
    
    <tr>
      <td><a href="URL">TITLE</a></td>
      <td>MON DD, YYYY H:MM AM</td>
    </tr>
    
  3. Replace it by that code:
    
    <tr tal:repeat="item options/library_items">
      <td><a href="URL"
             tal:attributes="href item/url"
             tal:content="item/title">TITLE</a></td>
      <td tal:content="item/modified">MON DD, YYYY H:MM AM</td>
    </tr>
    

Now our script calls the index_html.pt after doing all the computing and passes the resulting options dictionary to the template, which creates the HTML presentation of options. The Test tab of the template no longer works because it now depends on the script. Go to the Test tab of the script to see the result: The file library!

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.

Step 16: Making the Library Sortable

  1. Find the table headers in index_html.pt:
    
    <th width="300">File</th>
    <th>Last Modified</th>
    
  2. Replace them with these dynamic table headers:
    
    <th width="300"><a href="SORT_TITLE_URL"
           tal:omit-tag="not: options/sort_title_url"
           tal:attributes="href options/sort_title_url"
           >File</a></th>
    <th><a href="SORT_MODIFIED_URL"
           tal:omit-tag="not: options/sort_modified_url"
           tal:attributes="href options/sort_modified_url"
           >Last Modified</a></th>
    
  3. Extend index_html to make it look like this:
    
    ## Script (Python) "index_html"
    ##parameters=sort='title'
    ##
    library_items = []
    items = context.objectValues(['File'])
    if sort == 'title':
        sort_on = ( ('title_or_id', 'cmp', 'asc'), )
        sort_title_url = ''
        sort_modified_url = '%s?sort=modified' % context.absolute_url()
    else:
        sort_on = ( ('bobobase_modification_time', 'cmp', 'desc'), )
        sort_title_url = '%s?sort=title' % context.absolute_url()
        sort_modified_url = ''
    items = sequence.sort(items, sort_on)
    for item in items:
        library_items.append(
                { 'title': item.title_or_id(),
                  'url': item.absolute_url(),
                  'modified': item.bobobase_modification_time().aCommon()
                  } )
    
    options = { 'sort_title_url': sort_title_url,
                'sort_modified_url': sort_modified_url,
                'library_items': tuple(library_items) }
    
    return getattr(context, 'index_html.pt')(**options)
    

The changes in the template are quite simple. If an url is provided, the column header becomes a link. If not, the not: expression of the tal:omit-tag statement is true and the a tag is omitted. The script will always provide an url for the column that isn't currently sorted.

Basically we have to extend the logic, so most changes are in the script. First of all we define an optional parameter sort. By default it is title, so if no value is passed in we sort by title. Sort criteria and urls depend on the sort parameter. We use the sort function of the built in sequence module to apply the sort criteria to the items list.

Now view the file library and click on the File and Last Modified links to sort the files. If there is a sort variable and if it has a value of modified then the files are sorted by modification time. Otherwise the files are sorted by title.

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>

XXX: show how to send the form data straight to the form and then return to the guestbook page with the validated entry

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')

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.

XXX: also explain objectIds, OFSP

The guest book is now almost finished. To use the simple guest book, just visit http://localhost:8080/ZopeZoo/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].

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:


<?xml version="1.0"?>
<guestbook>
  <entry>
    <author>Tom</author>
    <comments>My comments</comments>
  </entry>
  <entry>
    <author>Anonymous</author>
    <comments>I like your web page</comments>
  </entry>
  <entry>
    <author>Laura</author>
    <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:


<?xml version="1.0"?>
<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.

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 building 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.

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