If you put the template code from the beginning of this section into a file named template.dtmland save it to the same place as where your code is then you can usethe script like this: i
Trang 1Method Description
setBase(base) Sets the base URL for the returned document.
expireCookie(name, **kw) Causes an HTTP cookie to be removed from
an expiration date that has already passed.
Note that some clients require a path to be specified This path must exactly match the path given when creating the cookie The path can be specified as a keyword argument.
appendCookie(name, value) Returns an HTTP header that sets a cookie on
cookie-enabled browsers with a key “name”
and value (“value”) If a value for the cookie has previously been set in the response object, the new value is appended to the old one, separated by a colon.
redirect(location, lock=0) Causes a redirection without raising an error If
the “lock” keyword argument is passed with a true value, the HTTP redirect response code will not be changed even if an error occurs later in request processing (after redirect() has been called).
Create Dynamic Text with DocumentTemplates
In Chapter 4 we showed you how to create dynamic Web pages using DTML ods and documents In this section we will show you how to use and extend theunderlying class library that generates dynamic text documents based on tem-plates The examples in this section will be used to generate HTML Although DTMLisn’t limited to the use of HTML, it’s just such a natural fit that it’s almost difficult toimagine better examples So we’re going to take the path of least resistance for thissection We’ll leave it up to you, if desired, to find other uses for DTML
meth-The name of the library is DocumentTemplate It can be found under
lib/python/DocumentTemplateof your Zope installation With this library you cancreate callable template objects
Trang 2A callable object is a Python object that implements the call hook Objectsthat implement this hook can be treated as functions within Python code.
Let’s jump right in and create one of these template objects and we’ll show you how
Once you have created an a template instance you can now call it
results = template_method(title=”This Document!”,
content=”Oh lookey, I’ve been “ + \
“dynamically generated by” +\
in while calling the template
See Chapter 4 for a reference of all the tags and what they can do
Cross-Reference
Note
Trang 3Initializing templates with default arguments
You have already seen how a basic template is instantiated and used from the ous example Optionally a template can be instantiated with a mapping object,named arguments, or both to create a default namespace that is searched if a valuecan’t be found from the arguments that are passed in when the template is called
previ-Using the same example as previously (one you should be quite familiar with bynow!) we can change it to use some default values via named arguments
template_method = HTML(template_source, Æ{title:”Missing Title”}, content=”Missing Content”)
Now you can call the template without any arguments
template_methodas the first argument, the results would look like this:
>>> class k: pass
Trang 4Working with templates stored in files
Instead of cluttering your source code with long format strings you can put yourtemplates into individual files In fact, this is such a convenient way of working withtemplates that the DocumentTemplate library provides a class that can be instanti-ated with a filename instead of the source of the template
If you put the template code from the beginning of this section into a file named
template.dtmland save it to the same place as where your code is then you can usethe script like this:
import syssys.path.append(“c:\\program files\\website\\lib\\python”)from DocumentTemplate import HTMLFile
template_method = HTMLFile(“template.dtml”,
title=”No Title”,content=”No Content”
)
Document template security
DocumentTemplates have basic security checks that prevent attributes that startwith the underscore from being used in templates If desired you can extend yourtemplates to perform other security checks while rendering
In the following script we subclass the HTML object and provide the guarded_ getattr()hook The hook takes two arguments (besides “self”) the object, and thename of the attribute that is being accessed in the DTML So back to the script In itwe’ve implemented the hook and check to see whether “bob” is the one attempting
to render the attribute If it is “bob,” we return the value of the attribute, if it’s body else, we raise a RunTime exception
any-import syssys.path.append(“c:\\program files\\website\\lib\\python”)from DocumentTemplate import HTML
Trang 5There’s one other hook that you can define named gaurded_getitem() that will becalled when using mapping objects within DTML expressions Using a modified ver-sion of the preceding script we’ve defined the hook and changed the DTML used inthe template to show you how it works.
import syssys.path.append(“c:\\program files\\website\\lib\\python”)from DocumentTemplate import HTML
dict = {‘title’:’Title inside a key’}
user = raw_input(“Enter your user name: “)print template_method(dict=dict)
The key thing that we should point out in the previous example is that you had toexplicitly pass your mapping object in as a named argument This is because DTMLdoes not search mapping objects if you pass them in like an object So to getaround this, we call the function as follows, template_method(dict=dict)
Trang 6Creating your own tags
To finish this section on DTML, we’ll show you how to create your own tags
Creating tags can lead to some exciting possibilities For instance there’s theCalendar tag (originally created by Ty Sarna, now maintained by the Zope commu-nity, and available at http://www.zope.org/Members/jdavid/Calendar) that renders
an HTML representation of a calendar This is great way to generate a dynamic endar with little HTML coding effort
cal-We’re not going to create anything so complex in this chapter, but we will show youthe basics of creating tags so that, if you like, you can impress the world with thenext innovative tag
There are two types of tags that can be created: block and singleton tags Block tags
are two-part tags that consist of an opening and a closing tag Examples of thesetags are the inand iftags The other type of tag is the singleton tag, such as the var
and the calltags that don’t have a closing counterpart
Creating a tag is a simple matter of implementing all of the required interfaces andregistering it as an available command We’ll start with the singleton tag
Creating a simple singleton tag
We’ll start with the equivalent of a hello world example, and then show how tomake more complicated aspects of tags like using arguments and expressions, andworking with values from a templates namespace
Here’s the most basic of tags that simply inserts the phrase “Hi Mom!” when dered It can be used in a template by inserting <dtml-himom>into a templatessource Singleton tags need to implement three things in their class:
ren-✦ A nameattribute, which is used to match your class to the tag when it is used
in a template For instance in the example that follows, we set name equal to
“himom”.
✦ A constructor ( init ) method that takes one attribute called “args.” Fornow we’ll pass on this method since we don’t need it for our first example
✦ A render()method, which is hook that is called by the template when the tag
is rendered This hook needs to take one argument, usually called “md,” which
is a TemplateDictionary (your guess is as good as ours as to why it’s calledmd) The Method Dictionary is the namespace passed to the template We’llignore the TemplateDictionary for now since we’re only going to insert the “HiMom!” phrase
Without further ado here’s a Python script that creates our “himom” tag, registers
it, and then builds a sample template to test it:
import syssys.path.append(“c:\\program files\\website\\lib\\python”)from DocumentTemplate.DT_String import String
Trang 7# Create a class for our tagclass HiMomTag:
name = ‘himom’
def init (self, args):
passdef render(self, md):
return “Hi Mom!”
call = render
# Register our new tag so we can use it in a templateString.commands[‘himom’] = HiMomTag
# Test it out by creating a template
from DocumentTemplate import HTMLtemplate_src = “<dtml-himom>”
template_method = HTML(template_src)print template_method()
If you save all this code into file and run it with Python, it will produce the followingresults:
Hi Mom!
Using arguments in tags
Let’s put the “Dynamic” in DTML by using arguments in your tag so that you caninsert a message to your mother
It’s now time to talk about a tag’s constructor It’s important to note that your tag’s
init method is only called when the template is instantiated In other wordswhen you run the following code:
template_method =HTML(template_source)
When this happens the tag class is passed its argsas a string You need to parsethe string and set the appropriate attributes on your tag instance so that you canuse the values when the tag is rendered later The value of argsis everythingbetween the beginning and end of the tag (<dtml-nameand >) For example if yourtemplate’s source contained:
<dtml-himom msg=”the dogs dead.” btw=”Oh, and I won’t be home for dinner!”>
then the value of argswill be equal to msg=”the dogs dead.” btw=”Oh, and I won’t
be home for dinner!” You could attempt to parse this string yourself (if you’re a masochist) or you coulduse a handy method provided as part of the DocumentTemplate library called
Trang 8parsed_params() This method takes a string as its first argument, and then a series
of named arguments that specify what values should be present in the argsstringand what they should default to if not present Knowing this, we can update are tag
to parse the argswhen the object is instantiated and modify our tag’s rendermethod to use the attributes if they’re present
import syssys.path.append(“c:\\program files\\website\\lib\\python”)from DocumentTemplate.DT_String import String
from DocumentTemplate.DT_Util import parse_params
# Create a class for our tagclass HiMomTag:
name = ‘himom’
def init (self, args):
args = parse_params(args,msg=””,
btw=””)self.msg = args[‘msg’]
template_method = HTML(template_src)print template_method()
Running this script we get:
Hi Mom, the dog’s dead Oh, and I won’t be home for dinner!
Getting values and rendering Python expressions
All right you got us The last example wasn’t very dynamic because it was based onthe values directly inserted into the templates source To make it truly dynamic wewant to interact with those values that you pass to the template when you render it(or use the defaults)
Trang 9This is done using the mdparameter of your tag’s render method This dictionary is
an aggregated representation of all the namespaces passed to the template when it
is rendered Imagine calling a Template Method and passing it an object thatexpects to insert the value for “title” into a document it’s creating To get this valuefrom within the render method of your tag you would simply write:
results = template_method(title=”The title”)
One other thing that you’d likely want to do is evaluate a Python expression (thesame way the vartag does) using values that are part of the template_method Youcan accomplish this with the help of the Evalclass, which provides a method forsafely evaluating Python expressions while working with the md The Evalclass willfollow all the security precautions that DTML follows, even the custom ones youprovide
To use the Evalclass import it from the DT_Utilmodule that’s inside theDocumentTemplate library Then instantiate an Eval instance with your pythonexpression This instance has a method named “eval” that returns the results ofyour Python expression using a TemplateDictionary
We’ve taken the himom example as far as we can so to demonstrate using valuesand expressions in a template Let’s build a tag that can print out a pretty version ofPython’s types
import syssys.path.append(“c:\\program files\\website\\lib\\python”)from DocumentTemplate.DT_String import String
from DocumentTemplate.DT_Util import parse_params, Eval,html_quote
#from pprint import PrettyPrentimport pprint
# Create a class for our tagclass PPrintTag:
name = ‘pprint’
Trang 10def init (self, args):
get = parse_params(args,name=None,
expr=None).getself.name = get(‘name’)
self.expr = get(‘expr’)
if self.expr is not None:
self.expr = Eval(self.expr).evaldef render(self, md):print template_method(o)
if self.name is not None:
ret = md[self.name]
else:
ret = self.expr(md)ret = pprint.pformat(ret)return html_quote(ret) call = render
# Register our new tag so we can use it in a templateString.commands[PPrintTag.name] = PPrintTag
# An example that inserts a title attribut into
# a documentfrom DocumentTemplate import HTMLtemplate_src=”””<dtml-pprint name=”title”>”””
template_method = HTML(template_src)class k: pass
o = k()o.title = “I’m a title”
print template_method(o)
# Here’s an exampl using an expresiontemplate_src = “””<dtml-pprint expr=”x+ 20”>”””
o.x = 10template_method = HTML(template_src)print template_method(o)
Block tags
Block tags have an opening and closing tag that usually surround data and othertags In the previous example we showed you how to create a tag whose attributesuse Python expressions What expressions can’t do is evaluate tags, whereas blocktags can
Trang 11To create a block tag, define an attribute at the class level named
“blockContinuations” and set it to an empty tuple This tells the DocumentTemplatelibrary that this is a block tag This attribute is also used to let the
DocumentTemplate library know what other special tags there are that can existonly within the opening and closing of this block tag We’ll explain more about this
in a moment
So for now, here’s an example of a note tag that produces the HTML equivalent of ayellow sticky note
import syssys.path.append(“c:\\program files\\website\\lib\\python”)from DocumentTemplate.DT_String import String
self.note = blocks[0][2]
def render(self, md):
note = self.note(md)return note_format % note call = render
# Register the tagString.commands[NoteTag.name] = NoteTagfrom DocumentTemplate import HTMLtemplate_src=”””<dtml-note>My first note!</dtml-note>”””
template_method = HTML(template_src)print template_method()
Running this script produces the following HTML:
<table border=1 bgcolor=”#FFFE7B” width=”250” height=”250”>
<tr height=10><td valign=”top”>Note:</td></tr>
<tr><td valign=”top”>My first note!</td></tr>
</table>
Trang 12The biggest difference between a block tag and a singleton tag is in the constructor.Instead of taking a single string argument, it takes a two-dimensional list, named
“blocks.” Each item in the list represents a block in the tag Each block has threeelements:
✦ The first element is the name of the tag
✦ The second element is the argument string for the block It’s in the same mat as the argument string that is passed to the singleton constructor
for-✦ The third element is a template method, the same type that you create whenyou make a method with the HTML class This method was built using thesource of all the text that was in between the <dtml-note>and </dtml-note>
tags
Block tags can have speacil tags that are only allowed inside of the block tag Agood example of this is the <dtml-else>tag that is only allowed between a <dtml- if>and </dtml-if>tag To tell the DocumentTemplate library that your block taghas one of these special tags you add its name to the blockContinuationtuple ofyour tag’s class
The following code has been modified so that your note tag can have a subject and
a body block The subject will be all the text between the <dtml-note>and body>tag The body will be everything between the <dtml-body>and </dtml-note>
<dtml-tags If the <dtml-body>tag doesn’t exist then there will be no subject
import syssys.path.append(“c:\\program files\\website\\lib\\python”)from DocumentTemplate.DT_String import String
# Create a class for our tagnote_format = “””
<table border=1 bgcolor=”#FFFE7B” width=”250” height=”250”>
Trang 13self.header = blocks[0][2]
del blocks[0]
else:
self.header = Noneself.note = blocks[0][2]
def render(self, md):
if self.header is not None:
header = self.header(md)else:
header = “”
note = self.note(md)return note_format % (header, note) call = render
# Register the tagString.commands[NoteTag.name] = NoteTagfrom DocumentTemplate import HTMLtemplate_src=”””<dtml-note>Read This<dtml-body>I have written asecond note!</dtml-note>”””
template_method = HTML(template_src)print template_method()
Trang 15Scripting Zope
In previous chapters, you learned about DTML, a simple but
powerful method of creating dynamic content, andProducts, a more complex and thorough way of writing appli-cations in Zope to handle business logic Sometimes, however,
a problem is too large to be handled elegantly in a DTML ument, yet not quite large enough to warrant building anentire application around it Sometimes, all you really want isthe ability to write a small chunk of code to do something thatwould otherwise be cumbersome in DTML This is where
doc-scripts come in.
Consider the following problem Suppose you have an area ofyour Web site that needs to display the directory hierarchy upuntil the point of the page you are on, with each level being alink to that level This would give you an effect similar to theobject listing at the top of the management interfaces editingscreens, a sort of “breadcrumbs” trail leading you from thetop of your structure to where you currently are You start bygetting the folder that the document being viewed is in; thenyou get each parent of that object until you reach the Zopeapplication or some arbitrary stopping point After you haveall of the objects, you take each one in turn and build a URLfor it and join them all together for displaying If youattempted to do this in DTML, you’d have a host of unwieldypieces of code What you really want is to be able to hand thistask off to a small program written in Python Simply pass it
an object and it returns a series of URLs To do that, you use ascript
Zope comes bundled with two powerful scripting solutions:
Python Scripts and External Methods A third option, PerlScripts, is available as an add-on product
Under the hood of aPython script
Calling Python-basedscripts
External methods
Perl-based scripts
Trang 16Jumping in with Python Scripts
The following sections assume at least a rudimentary knowledge of the Python guage Please see Chapter 5 for a primer
lan-Creating a Python-based script
To create a Python-based script, select Script (Python) from the Add menu in themanagement interface Enter an id and click Add and Edit You’re presented with anediting screen like the one shown in Figure 15-1
Figure 15-1: Editing a Python script
The Parameter List is just like the parameter list in a normal Python method It is acomma-separated list of variables to be passed into the function Default values arespecified with the syntax <parameter>=<value> Ways of passing arguments to thesescripts will be described in more detail later
The large text area is the body of the script and is where the actual Python code isplaced Almost anything you can do in a normal Python method can be done here
A few restrictions are explained later in this chapter
When you first create a Python Script, it contains some example code that strates several useful functions Let’s take a moment to examine these before youmove on to one of your own
Trang 17demon-# Example code:
# Import a standard function, and get the HTML request and response objects.
from Products.PythonScripts.standard import html_quote request = container.REQUEST
RESPONSE = request.RESPONSE
# Return a string identifying this script.
print “This is the”, script.meta_type, ‘“%s”’ % script.getId(),
if script.title:
print “(%s)” % html_quote(script.title), print “in”, container.absolute_url()
return printed
The first thing the script does is to import a function from a module inProducts.PythonScripts.standard This module provides access to several usefulfunctions for use in Python Scripts and can be examined in closer detail by opening
up the standard.py file located in your Products/PythonScripts directory This ticular function, html_quote, converts characters that have special meaning inHTML to special syntax that can be displayed without being interpreted The nextthing we do is get the REQUEST object from the scripts container This is a usefulshortcut that can be used in place of passing the REQUEST to the script as an argument
par-See “Calling scripts” later in this chapter for more information on passing ters to scripts
parame-Notice that the script is actually printing values Unlike a Python module run fromthe command line, or code run through Python’s interactive shell, the valuesprinted here don’t go to the terminal where you started Zope Instead, they are putinto a special variable called printedthat this script eventually returns This can be
a useful tool for returning complicated, formatted output to a calling object, or even
to print out an entire HTML page, although we don’t recommend doing that
As a final note, you’ll see that the code uses a variable called scriptand accessesseveral properties This is a special bound variable and is explained in greaterdetail later on in this chapter under the section “Binding variables.”
Now let’s create a small script, the classic “Hello world!” Create a Script (Python)object with an id of helloand edit it If the script has code in it already, go aheadand erase it Fill out the fields as shown in Figure 15-2 and save
To test your script, simply click the Test tab at the top of the form You should get ascreen with the text “Hello World!”on it Easy as that!
But the script doesn’t do much good if you can’t pass it a parameter Edit the scriptagain to be like the one shown in Figure 15-3 Notice that we added a parameter tothe script and supplied a default value of World This means you can pass in thename that you would like the script to greet, but you may optionally leave it out
Cross-Reference
Trang 18and the default value of Worldwill be used Click the Test tab again This time, younotice that it brings up a screen with an input box for the name parameter we speci-fied Go ahead and enter your name and click the Run Script button It should sayhello to you now! Try it again without the parameter and verify that the defaultworks.
Figure 15-2: A simple script
Figure 15-3: Passing parameters
Trang 19ArithmeticError AssertionError AttributeError EOFError EnvironmentError FloatingPointError IOError
ImportError IndexError KeyError LookupError NameError None OSError OverflowError RuntimeError StandardError SyntaxError
TypeError ValueError ZeroDivisionError abs
apply callable chr cmp complex delattr divmod filter float getattr hasattr hash hex int
isinstance issubclass len list long map max min oct ord pow range repr round setattr str tuple
Most of these functions behave as you would expect them to in normal Python
However, rangeand poware limited from producing overly large numbers andsequences Of the missing functions, most deal with the filesystem (open, for exam-ple) or have the capability to affect the Zope process (such as exit)
In addition to the standard built-ins, Zope also provides a few bonus built-ins
DateTime, test, namespaceand renderare DTML utility functions And to make up
Trang 20for removing the typefunction from the built-ins, Zope provides an alternative,
same_type, which enables you to compare the type of two objects in much the sameway as type
Besides restrictions to standard built-ins, Zope also restricts what you may importinto your scripts Python-based scripts have access to only the following modules:
Products.PythonScripts.standard, AccessControl, string, random, and math Zopealso monitors scripts for excessive looping and raises an error if it detects a potentialinfinite loop This is to prevent a Python-based script from monopolizing CPU timeand possibly reducing performance of the rest of the server or even locking it up
And, finally, like all objects in Zope, Python-based scripts adhere to the standardsecurity policies The scripts themselves cannot access objects that the user call-ing the script does not have access to unless a Proxy Role has been placed on thescript Also, as in DTML, Python-based scripts cannot access variables whosenames begin with an underscore Zope considers these private and raises an error
if you attempt it
Binding variables
You notice an area between the Parameter List and the body of the script labeledBound Names with a list of variables These are hooks back into Zope’s framework,giving you access to traversal and other useful information Click the Bindings tab of the management interface You should get a screen like the one shown inFigure 15-4
Figure 15-4: The Bindings screen
Trang 21When you create a Python-based script, Zope automatically creates default valuesfor four of the five available bound variables Here is a list of the variables and whatthey are used for:
✦ Context This is the object on which the script is called Usually, this is the
same as the Container, but as we learned in the section about Acquisition inChapter 14, through the trickeries of Traversal, we can essentially make anyscript, anywhere, be called on any other object in the Zope Hierarchy Thedefault value for this variable is context
✦ Container This is the actual container (usually a Zope folder) where the
script resides This is useful for calling other methods stored in the samefolder The default value is container
✦ Script This refers to the script object itself and defaults to script
✦ Namespace By default, this variable is left blank, but its recommended value
is an underscore character If this script is called from DTML, this variable isset to be the namespace of the caller Normally, a script searches for itsparameters in the REQUESTobject if no parameters are passed in (see “Callingscripts from DTML,” later in this chapter.) If this variable is set, it insteadsearches through the namespace (eventually getting to the request object)
✦ Subpath If this script was traversed to in a URL, Subpath contains all the
ele-ments of the URL that occur after the script If a script named some-scriptwascalled in the URL /foo/some-script/bar/spam, Subpath would be a list contain-ing the strings barand spam This variable defaults to traverse_subpath
A simple script probably won’t use any of these variables, but they can come invery handy for more complex problems They’re also quite helpful for debuggingyour scripts
Under the Hood of a Python Script
A Zope Python script is, in essence, a Python method defined through Zope Zopetakes the parameters and body you supply and converts it into a callable pythonmethod, which runs just like you would expect it to A lot of trickery is involvedbehind the scenes in order to get your function to be called on the right object andobey Zope’s security restrictions, but if you think of it as a normal python methodwith limited access, you’ll do fine
Calling Python-Based Scripts
You can call a Python-based script just like you would any other object in Zope Thetwo primary methods are, of course, calling it from inside of another Zope object(including other scripts and DTML methods), and calling it directly via a URL Formost Zope Web applications, the former will probably be the most used way
Trang 22However, for advanced functions, usually where another client that understandsHTTP wants to talk to your server, calling a script from the URL can be a powerfultool We’ll look at both of these methods in the following sections.
Calling scripts from DTML
Calling your scripts from inside DTML is done in much the same way as you wouldcall a DTML method If you just need to run the code but aren’t interested in dis-playing any returned results, simply use the dtml-callsyntax To display thereturned results, use the dtml-varsyntax It becomes interesting when you mustdecide how to pass parameters to your scripts
Parameters can be passed two ways, explicitly or implicitly Explicitly passing one
or more parameters to a script requires you to treat it exactly as you would a mal python function call Suppose you had a Python-based script called retrieve CustomerInfothat took the parameter customerId, processed some information, didsome formatting, and returned some information about that customer Now sup-pose you had a DTML document that looped through a list of customerIdand dis-played their information It would probably look something like this:
nor-<dtml-in customerIds>
<dtml-var item’])”><BR>
expr=”retrieveCustomerInfo(customerId=_[‘sequence-</dtml-in>
Simple enough, right? But there are shortcuts — ways to get your parameters to bepassed without explicitly specifying them If you don’t pass any parameters to ascript that requires one or more, Zope examines the REQUESTobject to see if it canfind the required parameters there This behavior also works if you have a formthat submits directly to a Python-based script Any form variables that matchparameters of the script will be matched up Any form variables that don’t have acounterpart in the Parameter List will be ignored and unavailable in the script
As mentioned earlier, if the Namespacevariable is bound, the script also attempts tofind the needed variables in calling objects Namespace These are usually otherobjects, such as a folder
See Chapter 4 for details about Zope’s Namespaceand what might be foundthere
Let’s test this out using our Hello World script created earlier in the chapter Create
a DTML Document called helloDoc in the same folder as your script with the ing bit of code in its body:
follow-<dtml-var expr=”hello(name=’John’)”>
Now view the page You should see the value returned by the script displayed Younotice that the name ‘John’was passed explicitly to the script Now let’s alter it alittle bit Change your page to look like the following:
Cross-Reference
Trang 23<dtml-call “REQUEST.set(‘name’, ‘John’)”>
<dtml-var hello>
View the page again You now see that even though we didn’t explicitly pass theparameter namein, Zope searched the request and matched the value up anyway
Now, let’s do one final example to make it all a bit more interactive Here we create
a form that enables a user to input the name to be passed to the script We also use
a common programming technique of having the form post back to itself and dealwith the displaying of the results Edit helloDoc to contain the following code:
<form action=”./” method=”POST”>
Name: <input type=”text” name=”name” value=””>
<input type=”submit” value=”Submit”>
Note that we could have changed the action of the form to submit directly to thehello script, but the results would not have been formatted with your header andfooter There are times when this may not be such a bad thing — for example, ifyou call a RESPONSE.redirect() at the end of the script But in general, you’ll still
be calling it from another object
Calling scripts from a URL
Navigating to a script via URL is fairly simple The interesting thing is that you canchange the Context that a script is called in, and thus the object that it is called on,via the intricacies of Zope’s acquisition framework Suppose you have a directorystructure similar to the one in the following list:
Trang 24To call the Waterscript on the ficusobject, you would call /plants/tasks/
ficus/water Because of the way Zope does Acquisition, it is able to properly findall the objects in the URL, and because of the way we’ve called our script, the con-text that wateroperates in is the ficusobject To call feedon the dogswould behandled the same way by calling /animals/tasks/feed/dogs
For more information on acquisition, see Chapter 14
A practical example
Let’s take our breadcrumbs example from the beginning of the chapter and actuallyimplement it Create a Python script called breadcrumbswith the following code inthe body:
# Take the object that this script is called on (the context)
# and traverse up to the root, getting the ID of each object.object = context
path = [] # A list of id’s, one for each object
while 1:
# The root application doesn’t have an ID attribute, instead it
# has a method that returns the id So we check to see if the
# objects id is a method or a string If it is a method we’ve
# reached the top of our trail
if same_type(object.id, ‘’):
path.append(object.id)object = object.aq_parent # Get the parent of this objectand loop
else:
break
# Because we started at the bottom and went up, we’ll
# want to reverse the order of the trail so that the
# first item is at the top most object
path.reverse()
# Since we didn’t include the root object in our list of
# breadcrumbs we’ll want to prepopulate it
breadcrumbs = “<a href=’/’>/</a>”
for i in range(0, len(path)):
Cross-Reference
Trang 25Now, edit your standard_html_headerto include the following code somewherebelow the body:
<dtml-var breadcrumbs><BR>
Create a directory structure a few layers deep with an index_htmlthat includes the
standard_html_headerin each one View the page at the bottom of the tree Youshould see a page similar to the one shown in Figure 15-5 Each link at the topshould take you back to the particular directory as named
Figure 15-5: Breadcrumbs
External Methods
Zope provides another method of scripting with Python, that of External Methods
They’re called External Methods because the code that you write lies outside of theZODB and instead resides in the filesystem An external method is simply a method,written in Python, whose module is in the /Extensionsfolder of the Zope installa-tion To create one, you need access to the filesystem directly, or at least enough toFTP/SCP your file to the appropriate location
Open up an editor and create a file called Hello.pywith the following contents:
def helloMethod(name=”World”):
return “Hello %s!” % name
Save the file to the /Extensionsdirectory under your Zope installation If you don’thave access to the filesystem where your Zope installation is running, you need totalk to your system administrator
Trang 26After the module is on the filesystem, you need to create a Zope External Method
object From the Zope Add menu, select External Method Enter externalHello for the Id, Hello for the Module Name, and helloMethod for the Function Name Click
add You should now have a Zope object called externalHello You can call thisfrom DTML or traverse it from the Web just like you would a normal Python-basedscript It behaves in much the same way
Why external methods?
So, the big question is, why would you want to go through all the trouble of anExternal Method when you could just create a Python-based script? As we saw inthe previous section, Python-based scripts are powerful, but they are also limited Ifyou have a particular module you want to import, chances are it won’t be available
in a simple Python-based script External methods have far fewer restrictions Withthem, you can access arbitrary packages, the filesystem, or the network This less-ening of security in the script itself is offset by the fact that you must have access
to the filesystem
A practical example
In order to perform the following example, you need access to an FTP server If youdon’t have access to one, you can use Zope’s built-in FTP server to upload the file
to the root of your Zope instance
Suppose that each day you need to upload some data via FTP to a remote server.The file is a simple comma-delimited text file created from some data pulled from aSQL database and formatted in a python script The exact contents are unimpor-tant, as long as it’s a simple text file
Open up a text editor and create a file called dailyUpload.pythat looks like the lowing, replacing the values for HOST, PORT, USER, and PASSWORD with your FTPinformation:
fol-from ftplib import FTPfrom StringIO import StringIOfrom string import joinfrom DateTime import DateTime
# Replace the contents of the following
# variables with your own FTP values
HOST = ‘##Your FTP Server##’
PORT = ‘##The Port it connects to Blank for default##’
USER = ‘##User name##’
PASSWORD = ‘##Password##’
Trang 27def ftpData(self, data):
# Create the file object from the string data suppliedfile = StringIO(data)
# Next, login to the FTP server
ftp = FTP()ftp.connect(HOST, PORT)ftp.login(USER, PASSWORD)
# DateStamp the data then upload it
date = DateTime()dateStamp = join([str(date.year()),
str(date.month()),str(date.day())], ‘-’)filename = ‘file_%s’ % dateStamp
command = “STOR %s” % filenameftp.storlines(command, file)
# Logoutftp.close()
Place the file in the /Extensionsdirectory of your Zope installation Now create anExternal Method object with an id of dailyUpload, a module name of dailyUpload,and a Function name of ftpData Create a simple text file in your editor and upload
it to a File object called datain the same directory as your External Method Thecontents don’t matter Now create a Python script like the one shown in Figure 15-6
Figure 15-6: uploadData script
Trang 28Test the script and then log in to your FTP server to verify that the data was cessfully transferred.
suc-Changes to External Methods are not immediately available to Zope If you edit thecode of an External Method, you need to edit Zopes External Method object.Restarting the server will also refresh the code
Perl-Based Scripts
Because Zope is itself written in Python, it’s relatively easy to provide Python-basedScripts and External Methods But for all its power, Python just doesn’t do somethings as well as other languages Perl is a powerful language, similar to Python,that many Web developers are already familiar with As such, there are Perl ver-sions of both the Zope embedded scripts and External methods
This section assumes you are installing in a Linux environment
Unlike Python scripts, Perl scripts don’t yet have the restrictions that limit theamount of Memory or CPU time taken up by them As such, it’s possible for some-one to write a Perl script that will monopolize server time and could bring yourZope installation to a halt
Before installing Script (Perl)
Before you can install Perl-based scripts, you need to make sure that Perl isinstalled Installing Perl is beyond the scope of this book, but the rest of the setuprequires that you have at least Perl 5.6.0 installed You can get Perl from theComprehensive Perl Archive Network at www.cpan.org
Perl needs to be compiled with Multi Threading support Most binary distributionsare compiled without threads This means you will probably have to install andcompile it from source For the CPAN source version, make sure you run Configurewith the -Dusethreadsflag
After Perl is installed, you need to install the pyperlmodule In a nutshell, thisenables you to embed Perl inside of Python None of it is Zope specific, but it isneeded by the Zoperl product later on Pyperl and Zoperl can both be downloadedfrom http://downloads.activestate.com//Zope-Perl/.
Note
Caution Note Note
Trang 31While Zope comes with a rich variety of built-in object
types such as Images, Files, Documents, Folders, andMethods, occasionally you may need to define a new objecttype that will work within Zope
New object types are defined within Products There are twoways to define new object types within Zope:
✦ File system–based Python Products
✦ Through-the-Web Products using ZClassesPython product development is covered extensively in Part II
of this book In this chapter, we explain when and how to useZClasses to define new object types
What are ZClasses? OOP and Classes
In standard OOP (object-oriented programming) parlance, a
class is a blueprint or prototype that defines the variables and
the methods common to all objects of a certain kind, which
are also known as instances of the class.
So, all Folder objects within Zope are really instances of theFolder class Everything about how Folders behave is definedwithin that class For example, the functionality that enablesFolders to contain other objects and list the objects withinthem
However, not all folders are identical Folders can contain ferent objects and they can have different properties Theseattributes are not defined in the class, which is only con-cerned with those things that instances have in common
ZClasses andproperty sheets
Automaticallygenerating ZClassviews
Creating simpleapplications usingZClasses
ZClasses and security
CreatingCatalogAwareZClasses
Subclassing ZClassesfrom Python baseclasses
Distributing ZClassproducts
Trang 32lan-a Python Product for Zope Therefore, Zope hlan-as lan-a wlan-ay for users to define new
object types through the Web by creating what are called ZClasses.
ZClasses enable you to create Products and new object types (classes) entirelythrough the Web, without resorting to creating files on the file system
ZClass-based products can be redistributed to other Zope users, and can generally
be modified after they’ve been installed if the user has management privileges onthe Root Folder, unless the developer has chosen to disallow such modification
It’s expected that in future versions of Zope, the distinction between file based products and through-the-Web products will diminish, or even disappear, butfor the moment, ZClasses and Python Products are still distinct
system-Creating a Simple ZClass
In this section, we show you step-by-step how to create a simple ZClass All of theinteresting parts will happen inside the special Products folder, which is in theControl_Panel, as you can see in Figure 16-1
As you can see, the Products folder contains a number of listings If you haven’tadded or created any through-the-Web products to your Zope installation yet, thenall of the products are indicated by “closed box” icons As we shall soon see,through-the-Web products have “open box” icons