Introduction
A fully functional website powered by Zope can be created by sticking DTML, Zope Page Templates, Scripts and other sorts of objects together. However, this arrangement of objects can quickly turn into a mess if you attempt to create special features or a large website. Things become difficult to create, and the result can look unorganized.
Thankfully, there exists an amazing array of Zope Products. These Products can do a number of things, from allowing the creation of a simple blog to allowing the creation of a full-blown content management system. However, what if you can’t find a product that specifically suits your project’s needs? If this is the case, then nothing is stopping you from creating your own Zope Products with a bit of Python, which we’ll take a look at in this article.
A Class Foundation
Zope Products are simply classes that Zope makes available for usage. Using these special classes, we are free to create multiple instances, with each instance possessing its own unique attributes. For example, look at Script. We can create several Script objects, each with its own name, title, code and any number of custom attributes we choose to define.
Knowing this, it’s not hard to figure out that the base of our Zope Product will be a Python class. In this article, we’ll take a look at a simple (and useless except for learning purposes) Product named Person. At the core of this Product will, of course, be a Python class named Person. Besides an id attribute and a title attribute (which we’ll use to store our virtual person’s name), our Person Product will also contain a nickname. Each instance of our Product will have these attributes set to unique values, and the values will be accessible to the outside world in the form of pages.
Go ahead and create a file named person.py. This will store our Python class as well as a few more mechanisms required in our Product (which we’ll take a look at later). Create a folder named Person in the lib/python/Products directory of your Zope installation to house our product.
class Person:
pass
Of course, while the above script is perfectly valid Python, it’s not much use to Zope. Besides, it doesn’t do anything. Before we can call it a Zope Product, we need to do a number of things. We’ll start with adding a documentation string to our class. Every class and function you attach to your Zope Product is required to feature a documentation string that explains what that particular class or function does. This forces code to be a bit more readable and organized.
class Person:
“A virtual person.”
pass
We’ll also need our class to inherit some basic functionality associated with items. This functionality deals with acquisition and other things. However, this can remain behind-the-scenes for our purposes. It isn’t especially important to know everything about what we inherit if we’re only developing a simple Product like this one.
import OFS.SimpleItem
class Person(OFS.SimpleItem.SimpleItem):
“A virtual person.”
As you can see, all that’s required is that we subclass OFS.SimpleItem.SimpleItem. The next step is to define a variable called meta_type. This is simply the name that our Product will appear as (for example, “Script”, “Folder” and “Page Template”). In this case, we’ll set it to “Person”:
import OFS.SimpleItem
class Person(OFS.SimpleItem.SimpleItem):
“A virtual person.”
meta_type = ‘Person’
Now that we got that done, we’re now ready to begin stuffing our class with functionality. This functionality comes in the form of methods.
{mospagebreak title=The Stuffing}
The first thing we’ll need to do is create an __init__ method. The purpose of this is to accept attributes when an instance of our Product is created. In this example, we’ll accept the attributes id, title and nickname:
…
def __init__(self, id, title, nickname):
“Initialization.”
self.id = id
self.title = title
self.nickname = nickname
We can now add actual content to our Product. The methods that we create will be available as separate pages in our Product. These pages can be accessed by attaching the associated method’s name to the id of the Product instance in a request (for example,). We’ll start with index_html, the default page that’s called when nothing is specifically requested:
…
def index_html(self):
“Default page.”
out = ‘<html><head><title>’ + self.title +
‘</title></head><body>’
out = out + ‘Hello, I am ‘ + self.title + ‘, but my friends
call me ‘ + self.nickname + ‘.’
out = out + ‘</body></html>’
return out
In the above method, we return a page that displays the instance’s title and the value of the nickname attribute we accepted in the __init__ method earlier. Let’s add one more method that lists data about an instance:
…
def list_info(self):
“A list of information.”
out = ‘<html><head><title>’ + self.title +
‘</title></head><body>’
out = out + ‘<b>ID:</b> ‘ + self.id
out = out + ‘<br /><b>Name:</b> ‘ + self.title
out = out + ‘<br /><b>Nickname:</b> ‘ + self.nickname
out = out + ‘</body></html>’
return out
{mospagebreak title=Inner Mechanisms}
Now that we’ve described our Product by building a class, we’ll have to work on a few inner mechanisms required to actually use our Product. The first two mechanisms we add to our Product will be responsible for creating instances of that Product. The first one will accept information about the instance to be created, such as the id, title and nickname attributes we make define in __init__. This data will then be passed to the second mechanism. This second mechanism will be responsible for actually adding the instance to Zope based on the data we entered into the first mechanism.
All that’s needed for the first mechanism is a simple form that requests the data we use in __init__. Note that this is not included in our Product class, but it does go into the same file.
…
def addForm(self):
“Accept basic information about the instance.”
out = ‘<html><head><title>Person Product</title></head><body>’
out = out + ‘<form action=”addProduct”>’
out = out + ‘<b>ID:</b> <input type=”text” name=”id” /><br />’
out = out + ‘<b>Name:</b> <input type=”text”
name=”title” /><br />’
out = out + ‘<b>Nickname:</b> <input type=”text”
name=”nickname” /><br />’
out = out + ‘<input type=”submit”></form></body></html>’
return out
As I said above, the addForm method is simply a form that accepts the information we will use to create our instance, id, title and nickname. Once we have this information, we can then use the information to actually create an instance of our Product. To do this, we use the _setObject method:
def addProduct(self, id, title, nickname):
“Add an instance.”
self._setObject(id, Person(id, title, nickname))
return “Object created.”
We pass the _setObject method two arguments. The first is the id of our instance, and the second is an actual instance of our Person class, to which we pass the information obtained in addForm. Finally, we return a message stating that our object has been created.
{mospagebreak title=Finishing Touches}
Though the person.py file is complete, Zope still will not acknowledge our new Zope Product. This is because there is a bit more work left to do. We have to tell Zope exactly where our Zope Product is located and how to go about adding the Product—what methods are required to create the Product and what-not.
Thankfully, though, this is a pretty simple process. Zope looks around in the Products directory for __init__.py files. These files contain instructions on what Zope should do to add the particular Product with which the file is associated. Go ahead and create a file named __init__.py and place it in the directory with the person.py file.
import person
def initialize(context):
“Instructions for creating an object.”
context.registerClass(person.Person, constructors =
(person.addForm,person.addProduct))
If you followed everything closely, then you should now be ready to test out the Product we created. Simply restart Zope and select our new Product from the drop-down list in the Zope Management Interface.
There is one additional thing I would like to explain. Though generating all the required HTML in the Python of a Product is perfectly possible, it’s not completely practical. A better solution might be to create DTML files and then return those to the user instead. We’ll modify our person.py file. The first thing you need to do is import Globals:
import Globals
…
Next, you need to add an attribute to Person and assign it to the DTML page. We’ll use the page dtml_page.dtml here:
…
dtml_page = Globals.HTMLFile(‘dtml_page’, globals())
…
Finally, we must create the DTML page. Put it in the same directory as person.py and name it dtml_page.dtml (even though we don’t use the extension in Globals.HTMLFile, the extension is required):
<html>
<head>
<title><dtml-var title></title>
</head>
<body>
This is a DTML page for <dtml-var title>.
</body>
</html>
Conclusion
From here, there are a lot of paths you can take. Try experimenting with Zope Products a bit more and familiarizing yourself with them. Simple ones aren’t difficult to create, as this article has illustrated with the Person Product. You can use them to simplify complex logic that can’t be organized using traditional methods. I know that many Python scripters feel that creating numerous Zope objects and attempting to force them to interact is a bit cumbersome, but Zope Products can get around this as well.
Of course, this article only explains a very small fraction of the inner workings of Zope Products. Products are pretty complex creatures that allow much more customization and functionality than a simple article like this can explain. However, I do hope that this article has introduced you to the very basics of Products so that you can decide where to go next.