[Checkins] SVN: zope2book/trunk/ Restify ScriptingZope chapter.

Tres Seaver tseaver at palladion.com
Tue Feb 10 17:44:06 EST 2009


Log message for revision 96427:
  Restify ScriptingZope chapter.

Changed:
  D   zope2book/trunk/ScriptingZope.stx
  A   zope2book/trunk/source/ScriptingZope.rst
  U   zope2book/trunk/source/index.rst

-=-
Deleted: zope2book/trunk/ScriptingZope.stx
===================================================================
--- zope2book/trunk/ScriptingZope.stx	2009-02-10 22:22:14 UTC (rev 96426)
+++ zope2book/trunk/ScriptingZope.stx	2009-02-10 22:44:06 UTC (rev 96427)
@@ -1,1420 +0,0 @@
-Advanced Zope Scripting
-
- 
-  In the chapter entitled "Basic Zope Scripting", you have seen
-  how to manage Zope objects programmatically.  In this chapter,
-  we will explore this topic some more.  Subjects discussed
-  include additional scripting objects, script security, and
-  calling script objects from presentation objects like Page
-  Templates or DTML Methods.  As we have mentioned before,
-  separation of logic and presentation is a key factor in
-  implementing maintainable web applications. 
-  
-  What is *logic* and how does it differ from presentation? Logic
-  provides those actions which change objects, send messages, test
-  conditions and respond to events, whereas presentation formats and
-  displays information and reports. Typically you will use DTML or
-  Page Templates to handle presentation, and Zope scripting to
-  handle logic.
-
-  Zope Scripts
-
-    Zope *Script* objects are objects that encapsulate a small
-    chunk of code written in a programming language. Script
-    objects first appeared in Zope 2.3, and are now the
-    preferred way to write programming logic in Zope. Currently,
-    Zope comes with *Python-based Scripts*, which are written in
-    the Python language.  We have discussed Python-based Scripts
-    in the "Basic Zope Scripting" chapter; below we will present
-    *External Methods*, a filesystem-based scripting object
-    without the security restrictions of *Python-based Scripts*.
-    These are also written in Python, but the code is stored on
-    the filesystem. External Methods allow you to do many things
-    that are restricted from Python-based Scripts.
-
-
-  Calling Scripts
-
-    In the "Basic Zope Scripting" chapter, you learned how to
-    call scripts from the web and, conversely, how to call Page
-    Templates and DTML from Python-based Scripts.  In addition,
-    any type of script may be called by any other type of
-    object; you can call a Python-based Script from a DTML
-    Method, or a built-in method from an External Method.  In
-    fact scripts can call scripts which call other scripts, and
-    so on.  For example, if you're using a Python-based Script
-    to perform a task, but later decide that it would be better
-    done in an External Method, you can usually replace the
-    script with an External Method with the same id.
-    
-    Calling Scripts from Other Objects
-
-      You can call scripts from other objects, whether they are DTML
-      objects, Page Templates, or Scripts (Python). The
-      semantics of each language differ slightly, but the same rules
-      of acquisition apply. You do not necessarily have to know what
-      language is used in the script you are calling; you only need to
-      pass it any parameters that it requires, if any.
-
-      Calling Scripts from Page Templates 
-
-        Calling scripts from Page Templates is much like calling them
-        by URL or from Python. Just use standard TALES path
-        expressions as described in the chapter entitled "Using Zope
-        Page Templates.":ZPT.stx  For example::
-
-          <div tal:replace="here/hippo/feed">
-            Output of feed()
-          </div>
-
-        The inserted value will be HTML-quoted. You can disable
-        quoting by using the *structure* keyword, as described in
-        the chapter entitled "Advanced Page Templates.":AdvZPT.stx
-
-        Page Templates do not really provide an equivalent to DTML's
-        *call* tag. To call a script without inserting a value in the
-        page, you can use *define* and ignore the variable assigned::
-
-          <div tal:define="dummy here/hippo/feed" />
-
-        In a page template, *here* refers to the current context.  It
-        behaves much like the *context* variable in a Python-based
-        Script.  In other words, *hippo* and *feed* will both be
-        looked up by acquisition.
-
-        If the script you call requires arguments, you must use a 
-        TALES python expression in your template, like so::
-
-          <div tal:replace="python:here.hippo.feed(food='spam')">
-            Output of feed(food='spam')
-          </div>
-
-        Just as in Path Expressions, the 'here' variable refers to the
-        acquisition context the Page Template is called in.  
-
-        The python expression above is exactly like a line of
-        code you might write in a Script (Python). One
-        difference is the name of the variable used to get the
-        acquisition context -- 'here' in TALES, 'context' in
-        Script (Python).  Don't be misled by the different
-        terminology: context is context, whatever you call
-        it. Unfortunately, the different names used in ZPT and
-        Python Scripts evolved independently.  (Note that as of
-        this writing, the ZPT variable *here* is planned to
-        become *context* in a future version of Zope, probably
-        Zope 3.). 
-
-        Another difference is the notation used for attribute access --
-        Script (Python) uses the standard Python period notation,
-        whereas in a TALES path expression, a forward slash is
-        used.
-
-        For further reading on using Scripts in Page Templates, refer
-        to the chapter entitled "Advanced Page Templates":AdvZPT.stx.
-
-      Calling Scripts from DTML 
-
-        As you saw in the chapter entitled "Advanced
-        DTML":AdvDTML.stx, you can call Zope scripts from DTML with
-        the *call* tag. For example::
-
-          <dtml-call updateInfo>
-
-        DTML will call the *updateInfo* script, whether it is
-        implemented in a Python-based SWcript, Page Template, or
-        any other object.
-
-        If the *updateInfo* script requires parameters, either your script 
-        must have a name for the DTML namespace binding (see *Binding
-        Variables* in the section "Using Python-based Scripts" below), 
-        so that the parameters will be looked up in the DTML
-        namespace, or you must pass the parameters in an expression.
-        For example:: 
-
-          <dtml-call expr="updateInfo(color='brown', pattern='spotted')">
-
-        You can also pass in any variables that are valid in
-        the current DTML namespace. For example, if *newColor*
-        and *newPattern* are defined using *dtml-let*, you could pass the
-        variables as parameters like this::
-
-          <dtml-call expr="updateInfo(color=newColor, pattern=newPattern)">
-
-        You can also pass variables that are defined automatically
-        by dtml tags such as *dtml-in*. For example::
-
-          <dtml-in all_animals prefix="seq">
-            <dtml-call expr="feed(animal=seq_item)">
-          </dtml-in>
-
-        This assumes that *feed* is a script and has a parameter
-        called *animal*.  The standard names used during DTML
-        loops ('sequence-item', 'sequence-key', et al.) are a
-        bit cumbersome to spell out in a Python *expr*, because
-        'sequence-item' would be interpreted as 'sequence' minus
-        'item'.  To avoid this problem, we use the *prefix*
-        attribute of the dtml-in tag, which then uses the
-        specified value ("seq") and an underscore ("_") instead
-        of the customary "sequence-" string.
-
-      Calling scripts from Python
-
-        Calling scripts from other Python scripts works the same
-        as calling scripts from DTML, except that you must
-        *always* use explicit calling (by using
-        parentheses). For example, here is how you might call
-        the *updateInfo* script from Python::
-
-          new_color='brown'
-          context.updateInfo(color=new_color, 
-                             pattern="spotted")
-
-        Note the use of the *context* variable to tell Zope to find
-        updateInfo by acquisition. 
-
-        Zope locates the scripts you call by using acquisition the
-        same way it does when calling scripts from the web.  Returning
-        to our hippo feeding example of the last section, let's see
-        how to vaccinate a hippo from Python. The figure
-        below shows a slightly updated object hierarchy that contains
-        a script named *vaccinateHippo.py*. 
-
-        "A collection of objects and scripts":img:14-2:Figures/zoo-again.png 
-
-        Here is how you can call the *vaccinate* script on the
-        *hippo* obect from the *vaccinateHippo.py* script::
-
-          context.Vet.LargeAnimals.hippo.vaccinate()
-
-        In other words, you simply access the object by using the same
-        acquisition path as you would use if you called it from the
-        web. The result is the same as if you visited the URL
-        *Zoo/Vet/LargeAnimals/hippo/vaccinate*. Note that in this Python
-        example, we do not bother to specify *Zoo* before *Vet*. We can
-        leave *Zoo* out because all of the objects involved, including
-        the script, are in the Zoo folder, so it is implicitly part
-        of the acquisition chain.
-
-      Calling Scripts: Summary and Comparison
-
-        Let's  recap  the ways  to  call  a hypothetical  *updateInfo*
-        script on a *foo* object, with argument passing: from your web
-        browser,  from Python, from DTML,  and  from Page
-        Templates.
-
-        by
-        URL::
-
-          http://my-zope-server.com:8080/foo/updateInfo?amount=lots
-
-        from a Python
-        script::
-
-          context.foo.updateInfo(amount="lots")
-
-        from a Page
-        Template::
-
-          <span tal:content="here/foo/updateInfo" />
-
-        from a Page Template, with
-        arguments::
-
-          <span tal:content="python:here.foo.updateInfo(amount='lots')" />
-
-        from
-        DTML::
-
-          <dtml-with foo >
-           <dtml-var updateInfo>
-          </dtml-with>
-
-        from DTML, with
-        arguments::
-
-          <dtml-with foo>
-           <dtml-var expr="updateInfo(amount='lots')">
-          </dtml-with>
-
-        another DTML
-        variant::
-
-          <dtml-var expr="_['foo'].updateInfo()">
-
-        Regardless of the language used, this is a very common idiom
-        to find an object, be it a script or any other kind of object:
-        you ask the context for it, and if it exists in this context
-        or can be acquired from it, it will be used.
-
-        Zope will throw a *KeyError* exception if the script you are
-        calling cannot be acquired. If you are not certain that a
-        given script exists in the current context, or if you want to
-        compute the script name at run-time, you can use this Python
-        idiom::
-
-          updateInfo = getattr(context, "updateInfo", None)
-          if updateInfo is not None:
-              updateInfo(color="brown", pattern="spotted")
-          else:
-              # complain about missing script
-              return "error: updateInfo() not found"
-
-        The *getattr* function is a Python built-in. The first
-        argument specifies an object, the second an attribute
-        name.  The *getattr* function will return the named
-        attribute, or the third argument if the attribute cannot be
-        found. So in the next statement we just have to test whether
-        the *updateInfo* variable is None, and if not, we know we can
-        call it.
-
-  Using External Methods
-
-    Sometimes the security constraints imposed by Python-based
-    Scripts, DTML and ZPT get in your way. For example, you
-    might want to read files from disk, or access the network,
-    or use some advanced libraries for things like regular
-    expressions or image processing.  In these cases you can use
-    *External Methods*.   We encountered External Methods briefly
-    in the chapter entitled "Using Basic Zope
-    Objects":BasicObjects.stx .  Now we will explore them in
-    more detail.
-
-    To create and edit External Methods you need access
-    to the filesystem. This makes editing these scripts more
-    cumbersome since you can't edit them right in your web
-    browser. However, requiring access to the server's filesystem
-    provides an important security control. If a user has access
-    to a server's filesystem they already have the ability to harm
-    Zope. So by requiring that unrestricted scripts be edited on
-    the filesystem, Zope ensures that only people who are already
-    trusted have access.
-
-    External Method code is created and edited in files on the Zope
-    server in the *Extensions* directory. This directory is located in
-    the top-level Zope directory. Alternately you can create and edit
-    your External Methods in an *Extensions* directory inside an
-    installed Zope product directory, or in your INSTANCE_HOME
-    directory if you have one. See the chapter entitled "Installing
-    and Starting Zope":InstallingZope.stx for more about
-    INSTANCE_HOME.
-
-    Let's take an example. Create a file named *example.py* in the
-    Zope *Extensions* directory on your server. In the file, enter the
-    following code::
-
-      def hello(name="World"):
-          return "Hello %s." % name 
-
-    You've created a Python function in a Python module. But you have
-    not yet created an External Method from it. To do so, we must add
-    an External Method object in Zope.
-
-    To add an External Method, choose *External Method* from the
-    product add list. You will be taken to a form where you must
-    provide an id. Type "hello" into the *Id* field, type "hello" in
-    the *Function name* field, and type "example" in the *Module name*
-    field. Then click the *Add* button.  You should now see a new
-    External Method object in your folder. Click on it. You should be
-    taken to the *Properties* view of your new External Method as
-    shown in the figure below.
-
-    "External Method *Properties* view":img:14-5:Figures/8-7.png
-
-    Note that if you wish to create several related External
-    Methods, you do not need to create multiple modules on the
-    filesystem.  You can define any number of functions in one
-    module, and add an External Method to Zope for each
-    function. For each of these External Methods, the *module
-    name* would be the same, but *function name* would vary.
-
-    Now test your new script by going to the *Test* view. You should
-    see a greeting. You can pass different names to the script by
-    specifying them in the URL. For example,
-    'hello?name=Spanish+Inquisition'.
-
-    This example is exactly the same as the "hello world" example that
-    you saw for Python-based scripts. In fact, for simple string
-    processing tasks like this, scripts offer a better solution since
-    they are easier to work with.
-
-    The main reasons to use an External Method are to access
-    the filesystem or network, or to use Python packages that are
-    not available to restricted scripts.
-
-    For example, a Script (Python) cannot access environment variables
-    on the host system. One could access them using an External
-    Method, like so::
-
-      def instance_home():
-         import os
-         return os.environ.get('INSTANCE_HOME')
-
-    Regular expressions are another useful tool that are restricted
-    from Scripts.  Let's look at an example.  Assume we want to get
-    the body of an HTML Page (everything between the 'body' and
-    '/body' tags)::
-
-      import re
-      pattern = r"<\s*body.*?>(.*?)</body>"
-      regexp = re.compile(pattern, re.IGNORECASE + re.DOTALL)
-
-      def extract_body(htmlstring):
-          """
-          If htmlstring is a complete HTML page, return the string
-          between (the first) <body> ... </body> tags
-          """
-          matched = regexp.search(htmlpage)
-          if matched is None: return "No match found"
-          body = matched.group(1)
-          return body 
-
-    Note that we import the 're' module and define the regular
-    expression at the module level, instead of in the function itself;
-    the 'extract_body()' function will find it anyway. Thus, the
-    regular expression is compiled once, when Zope first loads the
-    External Method, rather than every time this External Method is
-    called.  This is a common optimization tactic.
-
-    Now put this code in a module called 'my_extensions.py'. Add an
-    'External Method' with an id of 'body_external_m'; specify
-    'my_extensions' for the 'Module Name' to use and, 'extract_body'
-    for 'Function Name'.
-
-    You could call this for example in a 'Script (Python)' called
-    'store_html' like this::
-
-      ## Script (Python) "store_html"
-      ##
-
-      # code to get 'htmlpage' goes here...
-      htmlpage = "some string, perhaps from an uploaded file"
-      # now extract the body
-      body = context.body_external_m(htmlpage)
-      # now do something with 'body' ...
-
-    ... assuming that body_external_m can be acquired by store_html.
-    This is obviously not a complete example; you would want
-    to get a real HTML page instead of a hardcoded one, and you would
-    do something sensible with the value returned by your External
-    Method. 
-
-    Creating Thumbnails from Images
-
-      Here is an example External Method that uses the Python Imaging
-      Library (PIL) to create a thumbnail version of an existing Image
-      object in a Folder.  Enter the following code in a file named
-      *Thumbnail.py* in the *Extensions* directory::
-  
-        def makeThumbnail(self, original_id, size=200):
-            """
-            Makes a thumbnail image given an image Id when called on a Zope
-            folder.
-  
-            The thumbnail is a Zope image object that is a small JPG
-            representation of the original image. The thumbnail has an
-            'original_id' property set to the id of the full size image
-            object.
-            """
-  
-            import PIL 
-            from StringIO import StringIO
-            import os.path 
-            # none of the above imports would be allowed in Script (Python)!
-  
-            # Note that PIL.Image objects expect to get and save data
-            # from the filesystem; so do Zope Images. We can get around 
-            # this and do everything in memory by using StringIO.
-  
-            # Get the original image data in memory.
-            original_image=getattr(self, original_id)
-            original_file=StringIO(str(original_image.data))
-  
-            # create the thumbnail data in a new PIL Image. 
-            image=PIL.Image.open(original_file)
-            image=image.convert('RGB')
-            image.thumbnail((size,size))
-  
-            # get the thumbnail data in memory.
-            thumbnail_file=StringIO()
-            image.save(thumbnail_file, "JPEG") 
-            thumbnail_file.seek(0)
-  
-            # create an id for the thumbnail
-            path, ext=os.path.splitext(original_id)
-            thumbnail_id=path + '.thumb.jpg'
-  
-            # if there's an old thumbnail, delete it
-            if thumbnail_id in self.objectIds():
-                self.manage_delObjects([thumbnail_id])
-  
-            # create the Zope image object for the new thumbnail
-            self.manage_addProduct['OFSP'].manage_addImage(thumbnail_id,
-                                                           thumbnail_file,
-                                                           'thumbnail image')
-  
-            # now find the new zope object so we can modify 
-            # its properties.
-            thumbnail_image=getattr(self, thumbnail_id)
-            thumbnail_image.manage_addProperty('original_id', original_id, 'string')
-  
-      Notice that the first parameter to the above function is called
-      *self*. This parameter is optional. If *self* is the first parameter 
-      to an External Method function definition, it will be assigned 
-      the value of the calling context (in this case, a folder). 
-      It can be used much like the *context* we have seen in 
-      Scripts (Python).
-  
-      You must have PIL installed for this example to work. Installing
-      PIL is beyond the scope of this book, but note that it is
-      important to choose a version of PIL that is compatible with the
-      version of Python that is used by your version of Zope. See the
-      "PythonWorks
-      website":http://www.pythonware.com/products/pil/index.htm for more
-      information on PIL.  
-  
-      To continue our example, create an External Method named
-      *makeThumbnail* that uses the *makeThumbnail* function in the
-      *Thumbnail* module.
-  
-      Now you have a method that will create a thumbnail image. You can
-      call it on a Folder with a URL like
-      *ImageFolder/makeThumbnail?original_id=Horse.gif* This would
-      create a thumbnail image named 'Horse.thumb.jpg'.
-  
-      You can use a script to loop through all the images in a folder and
-      create thumbnail images for them. Create a Script (Python) named
-      *makeThumbnails*::
-  
-        ## Script (Python) "makeThumbnails"
-        ##
-        for image_id in context.objectIds('Image'):
-            context.makeThumbnail(image_id)
-  
-      This will loop through all the images in a folder and create a
-      thumbnail for each one.
-  
-      Now call this script on a folder with images in it. It will create a
-      thumbnail image for each contained image. Try calling the
-      *makeThumbnails* script on the folder again and you'll notice it created
-      thumbnails of your thumbnails. This is no good. You need to change the
-      *makeThumbnails* script to recognize existing thumbnail images and not
-      make thumbnails of them. Since all thumbnail images have an
-      *original_id* property you can check for that property as a way of
-      distinguishing between thumbnails and normal images::
-  
-        ## Script (Python) "makeThumbnails"
-        ##
-        for image in context.objectValues('Image'):
-            if not image.hasProperty('original_id'):
-                context.makeThumbnail(image.getId())
-  
-      Delete all the thumbnail images in your folder and try calling your
-      updated *makeThumbnails* script on the folder. It seems to work
-      correctly now.
-  
-      Now with a little DTML you can glue your script and External Method
-      together. Create a DTML Method called *displayThumbnails*::
-  
-        <dtml-var standard_html_header>
-  
-        <dtml-if updateThumbnails>
-          <dtml-call makeThumbnails>
-        </dtml-if>
-  
-        <h2>Thumbnails</h2>
-  
-        <table><tr valign="top">
-  
-        <dtml-in expr="objectValues('Image')">
-          <dtml-if original_id>
-            <td>
-              <a href="&dtml-original_id;"><dtml-var sequence-item></a>
-              <br />
-              <dtml-var original_id>
-            </td> 
-          </dtml-if>
-        </dtml-in>
-  
-        </tr></table>
-  
-        <form>
-        <input type="submit" name="updateThumbnails"
-               value="Update Thumbnails" />
-        </form>
-  
-        <dtml-var standard_html_footer>
-  
-      When you call this DTML Method on a folder it will loop through all the
-      images in the folder and display all the thumbnail images and link them
-      to the originals as shown in the figure below.
-  
-      "Displaying thumbnail images":img:14-6:Figures/8-8.png
-  
-      This DTML Method also includes a form that allows you to update the
-      thumbnail images. If you add, delete or change the images in your
-      folder you can use this form to update your thumbnails.
-  
-      This example shows a good way to use scripts, External Methods and DTML
-      together. Python takes care of the logic while the DTML handles
-      presentation. Your External Methods handle external packages 
-      such as PIL while your scripts do simple processing of Zope objects.
-      Note that you could just as easily use a Page Template instead of DTML.
-      
-    Processing XML with External Methods
-
-      You can use External Methods to do nearly anything. One interesting
-      thing that you can do is to communicate using XML. You can generate and
-      process XML with External Methods.
-
-      Zope already understands some kinds of XML messages such as
-      XML-RPC and WebDAV. As you create web applications that communicate
-      with other systems you may want to have the ability to receive XML
-      messages. You can receive XML a number of ways: you can read XML files
-      from the file system or over the network, or you can define scripts
-      that take XML arguments which can be called by remote systems.
-
-      Once you have received an XML message you must process the XML to find
-      out what it means and how to act on it.  Let's take a quick look at how
-      you might parse XML manually using Python. Suppose you want to connect
-      your web application to a "Jabber":http://www.jabber.com/ chat
-      server. You might want to allow users to message you and receive
-      dynamic responses based on the status of your web application. For
-      example suppose you want to allow users to check the status of animals
-      using instant messaging. Your application should respond to XML instant
-      messages like this::
-
-        <message to="cage_monitor at zopezoo.org" from="user at host.com">
-          <body>monkey food status</body>
-        </message>
-
-      You could scan the body of the message for commands, call a script
-      and return responses like this::
-
-        <message to="user at host.com" from="cage_monitor at zopezoo.org">
-          <body>Monkeys were last fed at 3:15</body>
-        </message>
-
-      Here is a sketch of how you could implement this XML messaging
-      facility in your web application using an External Method::
-
-        # Uses Python 2.x standard xml processing packages.  See
-        # http://www.python.org/doc/current/lib/module-xml.sax.html for
-        # information about Python's SAX (Simple API for XML) support If
-        # you are using Python 1.5.2 you can get the PyXML package. See
-        # http://pyxml.sourceforge.net for more information about PyXML.
-
-        from xml.sax import parseString
-        from xml.sax.handler import ContentHandler
-
-        class MessageHandler(ContentHandler):
-            """
-            SAX message handler class
-
-            Extracts a message's to, from, and body
-            """
-
-            inbody=0
-            body=""
-
-            def startElement(self, name, attrs):
-                if name=="message":
-                    self.recipient=attrs['to']
-                    self.sender=attrs['from']
-                elif name=="body":
-                    self.inbody=1
-
-            def endElement(self, name):
-                if name=="body":
-                    self.inbody=0
-
-            def characters(self, content):
-                if self.inbody:
-                    self.body=self.body + content
-
-        def receiveMessage(self, message):
-            """
-            Called by a Jabber server
-            """
-            handler=MessageHandler()
-            parseString(message, handler)
-
-            # call a script that returns a response string
-            # given a message body string
-            response_body=self.getResponse(handler.body)
-
-            # create a response XML message
-            response_message="""
-              <message to="%s" from="%s">
-                <body>%s</body>
-              </message>""" % (handler.sender, handler.recipient, response_body)
-
-            # return it to the server
-            return response_message
-
-      The *receiveMessage* External Method uses Python's SAX (Simple API
-      for XML) package to parse the XML message. The *MessageHandler*
-      class receives callbacks as Python parses the message. The handler
-      saves information its interested in. The External Method uses the
-      handler class by creating an instance of it, and passing it to the
-      *parseString* function. It then figures out a response message by
-      calling *getResponse* with the message body. The *getResponse*
-      script (which is not shown here) presumably scans the body for
-      commands, queries the web applications state and returns some
-      response. The *receiveMessage* method then creates an XML message
-      using response and the sender information and returns it.
-
-      The remote server would use this External Method by calling the
-      *receiveMessage* method using the standard HTTP POST
-      command. Voila, you've implemented a custom XML chat server that
-      runs over HTTP.
-
-    External Method Gotchas
-
-      While you are essentially unrestricted in what you can do in an
-      External Method, there are still some things that
-      are hard to do.
-
-      While your Python code can do as it pleases if you want to
-      work with the Zope framework you need to respect its
-      rules. While programming with the Zope framework is too
-      advanced a topic to cover here, there are a few things that
-      you should be aware of.
-
-      Problems can occur if you hand instances of your own
-      classes to Zope and expect them to work like Zope
-      objects. For example, you cannot define a class in your
-      External Method and assign an instance of this class as an
-      attribute of a Zope object. This causes problems with
-      Zope's persistence machinery.  If you need to create new
-      kinds of persistent objects, it's time to learn about
-      writing Zope Products. Writing a Product is beyond the
-      scope of this book. You can learn more by reading the
-      "Zope Developers'
-      Guide":http://www.zope.org/Documentation/Books/ZDG/current
-
-  Advanced Acquisition 
-
-      In the chapter entitled "Acquisition":Acquisition.stx , we
-      introduced acquisition by containment, which we have been using
-      throughout this chapter. In acquisition by containment, Zope
-      looks for an object by going back up the containment heirarchy
-      until it finds an object with the right id. In Chapter 7 we also
-      mentioned *context acquisition*, and warned that it is a tricky
-      subject capable of causing your brain to explode. If you are
-      ready for exploding brains, read on.
-
-      Recall our Zoo example introduced earlier in this chapter. 
-
-      "Zope Zoo Example hierarchy":img:14-7:Figures/zoo.png 
-
-      We have seen how Zope uses URL traversal and acquisition to find
-      objects  in  higher containers.  More  complex arrangements  are
-      possible. Suppose you want to call the *vaccinate* script on the
-      *hippo*  object. What  URL can  you use?  If you  visit  the URL
-      *Zoo/LargeAnimals/hippo/vaccinate* Zope will not be able to find
-      the  *vaccinate* script  since it  isn't in  any of  the *hippo*
-      object's containers.
-
-      The solution is to give the path to the script as part of the
-      URL. Zope allows you to combine two or more URLs into one in
-      order to provide more acquisition context! By using acquisition,
-      Zope will find the script as it backtracks along the URL. The
-      URL to vaccinate the hippo is
-      *Zoo/Vet/LargeAnimals/hippo/vaccinate*. Likewise, if you want to
-      call the *vaccinate* script on the *kargarooMouse* object you
-      should use the URL
-      *Zoo/Vet/SmallAnimals/kargarooMouse/vaccinate*.
-
-      Let's follow along as Zope traverses the URL
-      *Zoo/Vet/LargeAnimals/hippo/vaccinate*. Zope starts in the root
-      folder and looks for an object named *Zoo*. It moves to the
-      *Zoo* folder and looks for an object named *Vet*. It moves to
-      the *Vet* folder and looks for an object named
-      *LargeAnimals*. The *Vet* folder does not contain an object with
-      that name, but it can acquire the *LargeAnimals* folder from its
-      container, *Zoo* folder. So it moves to the *LargeAnimals*
-      folder and looks for an object named *hippo*.  It then moves to
-      the *hippo* object and looks for an object named
-      *vaccinate*. Since the *hippo* object does not contain a
-      *vaccinate* object and neither do any of its containers, Zope
-      backtracks along the URL path trying to find a *vaccinate*
-      object. First it backs up to the *LargeAnimals* folder where
-      *vaccinate* still cannot be found. Then it backs up to the *Vet*
-      folder.  Here it finds a *vaccinate* script in the *Vet*
-      folder. Since Zope has now come to the end of the URL, it calls
-      the *vaccinate* script in the context of the *hippo* object.
-
-      Note that we could also have organized the URL a bit
-      differently. *Zoo/LargeAnimals/Vet/hippo/vaccinate* would also
-      work. The difference is the order in which the context elements
-      are searched. In this example, we only need to get *vaccinate*
-      from *Vet*, so all that matters is that *Vet* appears in the URL
-      after *Zoo* and before *hippo*.
-
-      When Zope looks for a sub-object during URL traversal, it first
-      looks for the sub-object in the current object. If it cannot
-      find it in the current object it looks in the current object's
-      containers. If it still cannot find the sub-object, it backs up
-      along the URL path and searches again. It continues this process
-      until it either finds the object or raises an error if it cannot
-      be found. If several context folders are used in the URL, they
-      will be searched in order from *left to right*.
-
-      Context acquisition can be a very useful mechanism, and it
-      allows you to be quite expressive when you compose URLs. The
-      path you tell Zope to take on its way to an object will
-      determine how it uses acquisition to look up the object's
-      scripts.
-
-      Note that not all scripts will behave differently depending on
-      the traversed URL. For example, you might want your script to
-      acquire names only from its parent containers and not from the
-      URL context. To do so, simply use the *container* variable
-      instead of the *context* variable in the script, as described
-      above in the section "Using Python-based Scripts."
-
-    Context Acquisition Gotchas
-
-      Containment before context
-
-        It is important to realize that context acquisition
-        *supplements* container acquisition. It does not *override*
-        container acquisition.
-
-      One at a time
-
-        Another point that often confuses new users is that each element
-        of a path "sticks" for the duration of the traversal, once it is
-        found. Think of it this way: objects are looked up one at a
-        time, and once an object is found, it will not be looked up
-        again.  For example, imagine this folder structure:
-
-        "Acquisition example folder structure":img:14-8:Figures/acquisition.png
-
-        Now suppose that the *about_penguins* page contains a link to
-        *Images/penguins.png*. Shouldn't this work? Won't
-        */Images/penguins.png* succeed when
-        */Content/Images/penguins.png* fails?  The answer is no. We
-        always traverse from left to right, one item at a time. 
-        First we find *Content*, then *Images* within it; *penguins.png* 
-        appears in neither of those, and we haved searched all 
-        parent containers of every element in the URL, so 
-        there is nothing more to search in this URL.
-        Zope stops there and raises an error. Zope never looks in */Images*
-        because it has already found */Content/Images*.
-
-      Readability
-
-        Context acquisition can make code more difficult to
-        understand. A person reading your script can no longer simply
-        look backwards up one containment heirarchy to see where an
-        acquired object might be; many more places might be searched,
-        all over the zope tree folder. And the order in which objects
-        are searched, though it is consistent, can be confusing.
-
-      Fragility
-
-        Over-use of context acquisition can also lead to fragility. In
-        object-oriented terms, context acquisition can lead to a site
-        with low cohesion and tight coupling. This is generally regarded
-        as a bad thing. More specifically, there are many simple actions
-        by which an unwitting developer could break scripts that rely on
-        context acquisition. These are more likely to occur than with
-        container acquisition, because potentially every part of your
-        site affects every other part, even in parallel folder branches.
-
-        For example, if you write a script that calls another script by
-        a long and torturous path, you are assuming that the folder tree
-        is not going to change. A maintenance decision to reorganize the
-        folder heirarchy could require an audit of scripts in *every*
-        part of the site to determine whether the reorganization will
-        break anything. 
-
-        Recall our Zoo example. There are several ways in which a zope
-        maintainer could break the feed() script:
-
-        Inserting another object with the name of the method -- This
-        is a normal technique for customizing behavior in Zope, but
-        context acquisition makes it more likely to happen by
-        accident. Suppose that giraffe vaccination is controlled by a
-        regularly scheduled script that calls
-        *Zoo/Vet/LargeAnimals/giraffe/feed*. Suppose a content
-        administrator doesn't know about this script and adds a DTML
-        page called *vaccinate* in the giraffe folder, containing
-        information about vaccinating giraffes. This new *vaccinate*
-        object will be acquired before *Zoo/Vet/vaccinate*.  Hopefully
-        you will notice the problem before your giraffes get sick.
-
-        Calling an inappropriate path -- if you visit
-        *Zoo/LargeAnimals/hippo/buildings/visitor_reception/feed*,
-        will the reception area be filled with hippo food?  One would
-        hope not. This might even be possible for someone who has no
-        permissions on the reception object. Such URLs are actually
-        not difficult to construct. For example, using relative URLs
-        in standard_html_header can lead to some quite long
-        combinations of paths.
-
-      Thanks to Toby Dickenson for pointing out these fragility issues
-      on the zope-dev mailing list.
-
-
-  Passing Parameters to Scripts
-
-     All scripts can be passed parameters. A parameter gives a script
-     more information about what to do. When you call a script from the
-     web, Zope will try to find the script's parameters in the web
-     request and pass them to your script. For example, if you have a
-     script with parameters *dolphin* and *REQUEST* Zope will
-     look for *dolphin* in the web request, and will pass the request
-     itself as the *REQUEST* parameter. In practical terms this means
-     that it is easy to do form processing in your script. For example,
-     here is a form::
-
-       <form action="form_action">
-         Name of Hippo <input type="text" name="name" /><br />
-         Age of Hippo <input type="text" name="age" /><br />
-         <input type="submit" />
-       </form>
-
-     You can easily process this form with a script named
-     *form_action* that includes *name* and *age* in its parameter
-     list::
-
-       ## Script (Python) "form_action"
-       ##parameters=name, age
-       ##
-       "Process form"
-       age=int(age)
-       message= 'This hippo is called %s and is %d years old' % (name, age)
-       if age < 18:
-           message += '\n %s is not old enough to drive!' % name
-       return message
-
-     There is no need to process the form manually to extract values
-     from it. Form elements are passed as strings, or lists of
-     strings in the case of checkboxes and multiple-select input.
-
-     In addition to form variables, you can specify any request
-     variables as script parameters. For example, to get access to the
-     request and response objects just include 'REQUEST' and 'RESPONSE'
-     in your list of parameters. Request variables are detailed more
-     fully in "Appendix B":AppendixB.stx .
-
-     In the Python script given above, there is a subtle problem. You
-     are probably expecting an integer rather than a string for age,
-     but all form variables are passed as strings.  You could
-     manually convert the string to an integer using the Python *int*
-     built-in::
-
-       age=int(age)
-
-     But this manual conversion may be inconvenient. Zope provides a
-     way for you to specify form input types in the form, rather than
-     in the processing script. Instead of converting the *age* variable
-     to an integer in the processing script, you can indicate that it
-     is an integer in the form itself::
-
-       Age <input type="text" name="age:int" />
-
-     The ':int' appended to the form input name tells Zope to
-     automatically convert the form input to an integer. This
-     process is called *marshalling*. If the user of
-     your form types something that cannot be converted to an integer
-     (such as "22 going on 23") then Zope will raise an exception as
-     shown in the figure below.
-
-     "Parameter conversion error":img:14-10:Figures/8-3.png
-
-     It's handy to have Zope catch conversion errors, but you may not
-     like Zope's error messages. You should avoid using Zope's
-     converters if you want to provide your own error messages.
-
-     Zope can perform many parameter conversions. Here is a list of Zope's
-     basic parameter converters.
-
-       *boolean* -- Converts a variable to true or false. Variables
-       that are 0, None, an empty string, or an empty sequence are
-       false, all others are true.
-
-       *int* -- Converts a variable to an integer.
-
-       *long* -- Converts a variable to a long integer.
-
-       *float* -- Converts a variable to a floating point number.
-
-       *string* -- Converts a variable to a string. Most variables
-       are strings already so this converter is seldom used.
-
-       *text* -- Converts a variable to a string with normalized line
-       breaks.  Different browsers on various platforms encode line
-       endings differently, so this script makes sure the line endings are
-       consistent, regardless of how they were encoded by the browser.
-
-       *list* -- Converts a variable to a Python list.
-
-       *tuple* -- Converts a variable to a Python tuple. A tuple is
-       like a list, but cannot be modified.
-
-       *tokens* -- Converts a string to a list by breaking it on white
-       spaces.
-
-       *lines* -- Converts a string to a list by breaking it on new
-       lines.
-
-       *date* -- Converts a string to a *DateTime* object. The formats
-       accepted are fairly flexible, for example '10/16/2000',
-       '12:01:13 pm'.
-
-       *required* -- Raises an exception if the variable is not present.
-
-       *ignore_empty* -- Excludes the variable from the request if
-       the variable is an empty string.
-
-     These converters all work in more or less the same way to coerce
-     a form variable, which is a string, into another specific
-     type. You may recognize these converters from the chapter
-     entitled "Using Basic Zope Objects":BasicObjects.stx , in which we
-     discussed properties. These converters are used by Zope's
-     property facility to convert properties to the right type.
-
-     The *list* and *tuple* converters can be used in combination with other
-     converters.  This allows you to apply additional converters to each
-     element of the list or tuple.  Consider this form::
-
-       <form action="processTimes"> 
-
-         <p>I would prefer not to be disturbed at the following
-         times:</p>
-  
-         <input type="checkbox" name="disturb_times:list:date"
-         value="12:00 AM" /> Midnight<br />
-  
-         <input type="checkbox" name="disturb_times:list:date"
-         value="01:00 AM" /> 1:00 AM<br />
-  
-         <input type="checkbox" name="disturb_times:list:date"
-         value="02:00 AM" /> 2:00 AM<br />
-  
-         <input type="checkbox" name="disturb_times:list:date"
-         value="03:00 AM" /> 3:00 AM<br />
-  
-         <input type="checkbox" name="disturb_times:list:date"
-         value="04:00 AM" /> 4:00 AM<br />
-  
-         <input type="submit" />
-       </form>
-
-     By using the *list* and *date* converters together, Zope will
-     convert each selected time to a date and then combine all selected
-     dates into a list named *disturb_times*.
-
-     A more complex type of form conversion is to convert a series of inputs
-     into *records.* Records are structures that have attributes. Using
-     records, you can combine a number of form inputs into one variable with
-     attributes.  The available record converters are:
-
-       *record* -- Converts a variable to a record attribute.
-
-       *records* -- Converts a variable to a record attribute in a list of
-       records.
-
-       *default* -- Provides a default value for a record attribute if the
-       variable is empty.
-
-       *ignore_empty* -- Skips a record attribute if the variable is empty.
-
-     Here are some examples of how these converters are used::
-
-       <form action="processPerson">
-
-         First Name <input type="text" name="person.fname:record" /><br />
-         Last Name <input type="text" name="person.lname:record" /><br />
-         Age <input type="text" name="person.age:record:int" /><br />
-
-         <input type="submit" />
-       </form>
-
-     This form will call the *processPerson* script with one
-     parameter, *person*. The *person* variable will have the attributes
-     *fname*, *lname* and *age*. Here's an example of how you might
-     use the *person* variable in your *processPerson* script::
-
-       ## Script (Python) "processPerson"
-       ##parameters=person
-       ##
-       "Process a person record"
-       full_name="%s %s" % (person.fname, person.lname)
-       if person.age < 21:
-           return "Sorry, %s. You are not old enough to adopt an aardvark." % full_name
-       return "Thanks, %s. Your aardvark is on its way." % full_name
-
-     The *records* converter works like the *record* converter except
-     that it produces a list of records, rather than just one. Here is
-     an example form::
-
-       <form action="processPeople">
-
-         <p>Please, enter information about one or more of your next of
-         kin.</p>
-  
-         <p>
-           First Name <input type="text" name="people.fname:records" />
-           Last Name <input type="text" name="people.lname:records" />
-         </p>
-  
-         <p>
-           First Name <input type="text" name="people.fname:records" />
-           Last Name <input type="text" name="people.lname:records" />
-         </p>
-  
-         <p>
-           First Name <input type="text" name="people.fname:records" />
-           Last Name <input type="text" name="people.lname:records" />
-         </p>
-  
-         <input type="submit" />
-       </form>    
-
-     This form will call the *processPeople* script with a variable
-     called *people* that is a list of records. Each record will have
-     *fname* and *lname* attributes.  Note the difference between the
-     *records* converter and the *list:record* converter: the former
-     would create a list of records, whereas the latter would produce
-     a single record whose attributes *fname* and *lname* would each
-     be a list of values.
-
-     The order of combined modifiers does not matter; for example,
-     *int:list* is identical to *list:int*.
-
-     Another useful parameter conversion uses form variables to
-     rewrite the action of the form. This allows you to submit a form
-     to different scripts depending on how the form is filled
-     out. This is most useful in the case of a form with multiple
-     submit buttons. Zope's action converters are:
-
-       *action* -- Appends the attribute value to the original form
-       action of the form. This is mostly useful for the case in
-       which you have multiple submit buttons on one form.  Each
-       button can be assigned to a script that gets called when that
-       button is clicked to submit the form. A synonym for *action*
-       is *method*.
-
-       *default_action* -- Appends the attribute value to the
-       original action of the form when no other *action* converter
-       is used.
-
-     Here's an example form that uses action converters::
-
-       <form action="employeeHandlers">
-
-         <p>Select one or more employees</p>
-  
-         <input type="checkbox" name="employees:list" value="Larry" /> Larry<br />
-         <input type="checkbox" name="employees:list" value="Simon" /> Simon<br />
-         <input type="checkbox" name="employees:list" value="Rene" /> Rene<br />
- 
-         <input type="submit" name="fireEmployees:action" value="Fire!" /><br />
-  
-         <input type="submit" name="promoteEmployees:action" value="Promote!" />
-
-       </form>
-
-     We assume a folder 'employeeHandlers' containing two
-     scripts named 'fireEmployees' and 'promoteEmployees'.  The
-     form will call either the *fireEmployees* or the
-     *promoteEmployees* script, depending on which of the two
-     submit buttons is used.  Notice also how it builds a list
-     of employees with the *list* converter.  Form converters
-     can be very useful when designing Zope applications.
-
-  Script Security
-
-    All scripts that can be edited through the web are subject to
-    Zope's standard security policies. The only scripts that are not
-    subject to these security restrictions are scripts that must be
-    edited through the filesystem. These unrestricted scripts
-    include *External Methods*.
-
-    The chapter entitled "Users and Security":Security.stx covers
-    security in more detail. You should consult the *Roles of
-    Executable Objects* and *Proxy Roles* sections for more
-    information on how scripts are restricted by Zope security
-    constraints.
-
-    Security Restrictions of Script (Python)
-
-      Scripts are restricted in order to limit their ability
-      to do harm. What could be harmful? In general, scripts
-      keep you from accessing private Zope objects, making harmful
-      changes to Zope objects, hurting the Zope process itself, and
-      accessing the server Zope is running on. These restrictions
-      are implemented through a collection of limits on what your
-      scripts can do.
-
-        Loop limits -- Scripts cannot create infinite loops. If your script
-        loops a very large number of times Zope will raise an error. This
-        restriction covers all kinds of loops including *for* and *while*
-        loops. The reason for this restriction is to limit your ability to
-        hang Zope by creating an infinite loop.
-
-        Import limits -- Scripts cannot import arbitrary
-        packages and modules. You are limited to importing the
-        *Products.PythonScripts.standard* utility module, the
-        *AccessControl* module, those modules available via DTML
-        (*string*, *random*, *math*, *sequence*), and modules
-        which have been specifically made available to scripts
-        by product authors.  See Appendix B, "API
-        Reference":AppendixB.stx for more information on these
-        modules.  If you want to be able to import any Python
-        module, use an External Method, as described in this
-        chapter.
-
-        Access limits -- You are restricted by standard Zope
-        security policies when accessing objects. In other words
-        the user executing the script is checked for
-        authorization when accessing objects. As with all
-        executable objects, you can modify the effective roles a
-        user has when calling a script using *Proxy Roles* (see
-        the chapter entitled "Users and Security":Security.stx
-        for more information). In addition, you cannot access
-        objects whose names begin with an underscore, since Zope
-        considers these objects to be private. Finally, you can
-        define classes in scripts but it is not really practical
-        to do so, because you are not allowed to access
-        attributes of these classes! Even if you were allowed to
-        do so, the restriction against using objects whose names
-        begin with an underscore would prevent you from using
-        your class's __init__ method.  If you need to define
-        classes, use *External Methods* or *Zope Products* (see
-        the "Zope Developers
-        Guide":http://www.zope.org/Documentation/Books/ZDG for
-        more information about creating Products).  You may,
-        however, define functions in scripts, although it is
-        rarely useful or necessary to do so.  In practice, a
-        Script in Zope is treated as if it were a single method
-        of the object you wish to call it on.
-
-        Writing limits -- In general you cannot directly change Zope object
-        attributes using scripts. You should call the appropriate
-        methods from the Zope API instead.
-
-      Despite these limits, a determined user could use large amounts
-      of CPU time and memory using Python-based Scripts. So malicious
-      scripts could constitute a kind of denial of service attack by
-      using lots of resources. These are difficult problems to solve
-      and DTML suffers from the same potential for abuse. As with
-      DTML, you probably should not grant access to scripts to
-      untrusted people.
-
-
-  DTML versus Python versus Page Templates 
-
-    Zope gives you many ways to script. For small scripting
-    tasks the choice of Python-based Scripts, Page Templates or
-    DTML probably doesn't make a big difference.  For larger,
-    logic-oriented tasks you should use Python-based Scripts or
-    External Methods. 
-    
-    For presentation, Python should *not* be used; the choice
-    then becomes whether to use DTML or ZPT.
-
-    Just for the sake of comparison, here is a simple presentational script 
-    suggested by Gisle Aas in four different languages.
-
-    In DTML::
-
-      <dtml-in objectValues>
-        <dtml-var getId>: <dtml-var sequence-item>
-      </dtml-in>
-      done
-
-    In ZPT::
-
-      <div tal:repeat="item here/objectValues" 
-           tal:replace="python:'%s: %s\n' % (item.getId(), str(item))" />
-
-    In Python::
-
-      for item in context.objectValues():
-          print "%s: %s" % (item.getId(), item)
-      print "done"
-      return printed
-
-    Despite the fact that Zope is implemented in Python, it sometimes
-    (for better or worse) follows the Perl philosophy that "there's
-    more than one way to do it".
-
-  Remote Scripting and Network Services
-
-    Web servers are used to serve content to software clients; usually
-    people using web browser software.  The software client can also be
-    another computer that is using your web server to access some kind of
-    service.
-
-    Because Zope exposes objects and scripts on the web, it can be used to
-    provide a powerful, well organized, secure web API to other remote
-    network application clients.
-
-    There are two common ways to remotely script Zope.  The first way
-    is using a simple remote procedure call protocol called
-    *XML-RPC*.  XML-RPC is used to execute a procedure on a remote
-    machine and get a result on the local machine.  XML-RPC is designed
-    to be language neutral, and in this chapter you'll see examples in
-    Python, Perl and Java.
-
-    The second common way to remotely script Zope is with any HTTP
-    client that can be automated with a script.  Many language
-    libraries come with simple scriptable HTTP clients and there are
-    many programs that let you you script HTTP from the command line.
-
-    Using XML-RPC
-
-      XML-RPC is a simple remote procedure call mechanism that works
-      over HTTP and uses XML to encode information. XML-RPC clients
-      have been implemented for many languages including Python, Perl,
-      Java, JavaScript, and TCL.
-
-      In-depth information on XML-RPC can be found at the "XML-RPC
-      website":http://www.xmlrpc.org/. 
-
-      All Zope scripts that can be called from URLs can be called via
-      XML-RPC. Basically XML-RPC provides a system to marshal
-      arguments to scripts that can be called from the web. As you saw
-      earlier in the chapter Zope provides its own marshaling
-      controls that you can use from HTTP. XML-RPC and Zope's own
-      marshaling accomplish much the same thing. The advantage of
-      XML-RPC marshaling is that it is a reasonably supported
-      standard that also supports marshaling of return values as well
-      as argument values.
-
-      Here's a fanciful example that shows you how to remotely script
-      a mass firing of janitors using XML-RPC.
-
-      Here's the code in Python::
-
-        import xmlrpclib
-
-        server = xmlrpclib.Server('http://www.zopezoo.org/')
-        for employee in server.JanitorialDepartment.personnel():
-            server.fireEmployee(employee)
-
-      In Perl::
-
-        use Frontier::Client;
-
-        $server = Frontier::Client->new(url => "http://www.zopezoo.org/");
-
-        $employees = $server->call("JanitorialDepartment.personnel");
-        foreach $employee ( @$employees ) {
-
-          $server->call("fireEmployee",$server->string($employee));
-
-        }
-
-      In Java::
-
-        try {
-            XmlRpcClient server = new XmlRpcClient("http://www.zopezoo.org/");
-            Vector employees = (Vector) server.execute("JanitorialDepartment.personnel");
-
-            int num = employees.size();
-            for (int i = 0; i < num; i++) {
-                Vector args = new Vector(employees.subList(i, i+1));
-                server.execute("fireEmployee", args);
-            }
-
-        } catch (XmlRpcException ex) {
-            ex.printStackTrace();
-        } catch (IOException ioex) {
-            ioex.printStackTrace();
-        }
-
-      Actually the above example will probably not run correctly, since you
-      will most likely want to protect the *fireEmployee* script. This brings
-      up the issue of security with XML-RPC. XML-RPC does not have any
-      security provisions of its own; however, since it runs over HTTP it can
-      leverage existing HTTP security controls. In fact Zope treats an
-      XML-RPC request exactly like a normal HTTP request with respect to
-      security controls. This means that you must provide authentication in
-      your XML-RPC request for Zope to grant you access to protected
-      scripts, eg. by using the user:password URL notation, as
-      in 'http://user:password@server.domain/'
-
-    Remote Scripting with HTTP
-
-      Any HTTP client can be used for remotely 
-      scripting Zope.
-
-      On Unix systems you have a number of tools at your
-      disposal for remotely scripting Zope. One simple example
-      is to use *wget* to call Zope script URLs and use *cron*
-      to schedule the script calls. For example, suppose you
-      have a Zope script that feeds the lions and you would like
-      to call it every morning.  You can use *wget* to call the
-      script like so::
-
-        $ wget --spider http://www.zopezope.org/Lions/feed
-
-      The *spider* option tells *wget* not to save the response as a
-      file. Suppose that your script is protected and requires
-      authorization. You can pass your user name and password with *wget* to
-      access protected scripts::
-
-        $ wget --spider --http-user=ZooKeeper \
-            --http-passwd=SecretPhrase \
-            http://www.zopezope.org/Lions/feed
-
-      Now let's use *cron* to call this command every morning at 8am. Edit
-      your crontab file with the *crontab* command::
-
-        $ crontab -e
-
-      Then add a line to call wget every day at 8 am::
-
-        0 8 * * * wget -nv --spider --http_user=ZooKeeper \
-          --http_pass=SecretPhrase http://www.zopezoo.org/Lions/feed
-
-      (Beware of the linebreak -- the above should be input as
-      one line, minus the backslash).
-
-      The only difference between using *cron* and calling *wget* manually is
-      that you should use the *nv* switch when using *cron* since you don't
-      care about output of the *wget* command.
-
-      For our final example let's get really perverse. Since networking is
-      built into so many different systems, it's easy to find an unlikely
-      candidate to script Zope. If you had an Internet-enabled toaster you
-      would probably be able to script Zope with it. Let's take Microsoft
-      Word as our example Zope client. All that's necessary is to get Word to
-      agree to tickle a URL.
-
-      The easiest way to script Zope with Word is to tell word to open a
-      document and then type a Zope script URL as the file name as shown in
-      [8-9].
-
-      "Calling a URL with Microsoft Word":img:14-12:Figures/8-9.png
-
-      Word will then load the URL and return the results of calling the Zope
-      script. Despite the fact that Word doesn't let you POST arguments this
-      way, you can pass GET arguments by entering them as part of the URL.
-
-      You can even control this behavior using Word's built-in Visual Basic
-      scripting. For example, here's a fragment of Visual Basic that tells
-      Word to open a new document using a Zope script URL::
-
-        Documents.Open FileName:="http://www.zopezoo.org/LionCages/wash?use_soap=1&water_temp=hot" 
-
-      You could use Visual Basic to call Zope script URLs in many different
-      ways.
-
-      Zope's URL to script call translation is the key to remote
-      scripting. Since you can control Zope so easily with simple URLs you
-      can easy script Zope with almost any network-aware system.
-
-  Conclusion
-
-    With scripts you can control Zope objects and glue together your
-    application's logic, data, and presentation. You can
-    programmatically manage objects in your Zope folder hierarchy by
-    using the Zope API.  You can also perform serious programming
-    tasks such as image processing and XML parsing.

Copied: zope2book/trunk/source/ScriptingZope.rst (from rev 96426, zope2book/trunk/ScriptingZope.stx)
===================================================================
--- zope2book/trunk/source/ScriptingZope.rst	                        (rev 0)
+++ zope2book/trunk/source/ScriptingZope.rst	2009-02-10 22:44:06 UTC (rev 96427)
@@ -0,0 +1,1470 @@
+Advanced Zope Scripting
+=======================
+
+In the chapter entitled "Basic Zope Scripting", you have seen
+how to manage Zope objects programmatically.  In this chapter,
+we will explore this topic some more.  Subjects discussed
+include additional scripting objects, script security, and
+calling script objects from presentation objects like Page
+Templates or DTML Methods.  As we have mentioned before,
+separation of logic and presentation is a key factor in
+implementing maintainable web applications. 
+
+What is *logic* and how does it differ from presentation? Logic
+provides those actions which change objects, send messages, test
+conditions and respond to events, whereas presentation formats and
+displays information and reports. Typically you will use DTML or
+Page Templates to handle presentation, and Zope scripting to
+handle logic.
+
+Zope Scripts
+------------
+
+Zope *Script* objects are objects that encapsulate a small
+chunk of code written in a programming language. Script
+objects first appeared in Zope 2.3, and are now the
+preferred way to write programming logic in Zope. Currently,
+Zope comes with *Python-based Scripts*, which are written in
+the Python language.  We have discussed Python-based Scripts
+in the "Basic Zope Scripting" chapter; below we will present
+*External Methods*, a filesystem-based scripting object
+without the security restrictions of *Python-based Scripts*.
+These are also written in Python, but the code is stored on
+the filesystem. External Methods allow you to do many things
+that are restricted from Python-based Scripts.
+
+
+Calling Scripts
+---------------
+
+In the "Basic Zope Scripting" chapter, you learned how to
+call scripts from the web and, conversely, how to call Page
+Templates and DTML from Python-based Scripts.  In addition,
+any type of script may be called by any other type of
+object; you can call a Python-based Script from a DTML
+Method, or a built-in method from an External Method.  In
+fact scripts can call scripts which call other scripts, and
+so on.  For example, if you're using a Python-based Script
+to perform a task, but later decide that it would be better
+done in an External Method, you can usually replace the
+script with an External Method with the same id.
+
+Calling Scripts from Other Objects
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can call scripts from other objects, whether they are DTML
+objects, Page Templates, or Scripts (Python). The
+semantics of each language differ slightly, but the same rules
+of acquisition apply. You do not necessarily have to know what
+language is used in the script you are calling; you only need to
+pass it any parameters that it requires, if any.
+
+Calling Scripts from Page Templates 
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+Calling scripts from Page Templates is much like calling them
+by URL or from Python. Just use standard TALES path
+expressions as described in the chapter entitled "Using Zope
+Page Templates.":ZPT.html>`_  For example::
+
+  <div tal:replace="here/hippo/feed">
+    Output of feed()
+  </div>
+
+The inserted value will be HTML-quoted. You can disable
+quoting by using the *structure* keyword, as described in
+the chapter entitled `Advanced Page Templates <AdvZPT.html>`_
+
+Page Templates do not really provide an equivalent to DTML's
+*call* tag. To call a script without inserting a value in the
+page, you can use *define* and ignore the variable assigned::
+
+  <div tal:define="dummy here/hippo/feed" />
+
+In a page template, *here* refers to the current context.  It
+behaves much like the *context* variable in a Python-based
+Script.  In other words, *hippo* and *feed* will both be
+looked up by acquisition.
+
+If the script you call requires arguments, you must use a 
+TALES python expression in your template, like so::
+
+  <div tal:replace="python:here.hippo.feed(food='spam')">
+    Output of feed(food='spam')
+  </div>
+
+Just as in Path Expressions, the 'here' variable refers to the
+acquisition context the Page Template is called in.  
+
+The python expression above is exactly like a line of
+code you might write in a Script (Python). One
+difference is the name of the variable used to get the
+acquisition context, called 'here' in TALES, 'context' in
+Script (Python).  Don't be misled by the different
+terminology: context is context, whatever you call
+it. Unfortunately, the different names used in ZPT and
+Python Scripts evolved independently.  (Note that as of
+this writing, the ZPT variable *here* is planned to
+become *context* in a future version of Zope, probably
+Zope 3.). 
+
+Another difference is the notation used for attribute access --
+Script (Python) uses the standard Python period notation,
+whereas in a TALES path expression, a forward slash is
+used.
+
+For further reading on using Scripts in Page Templates, refer
+to the chapter entitled `Advanced Page Templates`_.
+
+Calling Scripts from DTML 
+%%%%%%%%%%%%%%%%%%%%%%%%%
+
+As you saw in the chapter entitled `Advanced DTML <AdvDTML.html>`_,
+you can call Zope scripts from DTML with the *call* tag. For example::
+
+  <dtml-call updateInfo>
+
+DTML will call the *updateInfo* script, whether it is
+implemented in a Python-based SWcript, Page Template, or
+any other object.
+
+If the *updateInfo* script requires parameters, either your script 
+must have a name for the DTML namespace binding (see *Binding
+Variables* in the section "Using Python-based Scripts" below), 
+so that the parameters will be looked up in the DTML
+namespace, or you must pass the parameters in an expression.
+For example:: 
+
+  <dtml-call expr="updateInfo(color='brown', pattern='spotted')">
+
+You can also pass in any variables that are valid in
+the current DTML namespace. For example, if *newColor*
+and *newPattern* are defined using *dtml-let*, you could pass the
+variables as parameters like this::
+
+  <dtml-call expr="updateInfo(color=newColor, pattern=newPattern)">
+
+You can also pass variables that are defined automatically
+by dtml tags such as *dtml-in*. For example::
+
+  <dtml-in all_animals prefix="seq">
+    <dtml-call expr="feed(animal=seq_item)">
+  </dtml-in>
+
+This assumes that *feed* is a script and has a parameter
+called *animal*.  The standard names used during DTML
+loops ('sequence-item', 'sequence-key', et al.) are a
+bit cumbersome to spell out in a Python *expr*, because
+'sequence-item' would be interpreted as 'sequence' minus
+'item'.  To avoid this problem, we use the *prefix*
+attribute of the dtml-in tag, which then uses the
+specified value ("seq") and an underscore ("_") instead
+of the customary "sequence-" string.
+
+Calling scripts from Python
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+Calling scripts from other Python scripts works the same
+as calling scripts from DTML, except that you must
+*always* use explicit calling (by using
+parentheses). For example, here is how you might call
+the *updateInfo* script from Python::
+
+  new_color='brown'
+  context.updateInfo(color=new_color, 
+                     pattern="spotted")
+
+Note the use of the *context* variable to tell Zope to find
+updateInfo by acquisition. 
+
+Zope locates the scripts you call by using acquisition the
+same way it does when calling scripts from the web.  Returning
+to our hippo feeding example of the last section, let's see
+how to vaccinate a hippo from Python. The figure
+below shows a slightly updated object hierarchy that contains
+a script named *vaccinateHippo.py*. 
+
+.. figure:: ../Figures/zoo-again.png 
+
+   A collection of objects and scripts
+
+Here is how you can call the *vaccinate* script on the
+*hippo* obect from the *vaccinateHippo.py* script::
+
+  context.Vet.LargeAnimals.hippo.vaccinate()
+
+In other words, you simply access the object by using the same
+acquisition path as you would use if you called it from the
+web. The result is the same as if you visited the URL
+*Zoo/Vet/LargeAnimals/hippo/vaccinate*. Note that in this Python
+example, we do not bother to specify *Zoo* before *Vet*. We can
+leave *Zoo* out because all of the objects involved, including
+the script, are in the Zoo folder, so it is implicitly part
+of the acquisition chain.
+
+Calling Scripts: Summary and Comparison
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+Let's  recap  the ways  to  call  a hypothetical  *updateInfo*
+script on a *foo* object, with argument passing: from your web
+browser,  from Python, from DTML,  and  from Page
+Templates.
+
+- by URL::
+
+   http://my-zope-server.com:8080/foo/updateInfo?amount=lots
+
+- from a Python script::
+
+   context.foo.updateInfo(amount="lots")
+
+- from a Page Template::
+
+   <span tal:content="here/foo/updateInfo" />
+
+- from a Page Template, with arguments::
+
+   <span tal:content="python:here.foo.updateInfo(amount='lots')" />
+
+- from DTML::
+
+   <dtml-with foo >
+    <dtml-var updateInfo>
+   </dtml-with>
+
+- from DTML, with arguments::
+
+   <dtml-with foo>
+    <dtml-var expr="updateInfo(amount='lots')">
+   </dtml-with>
+
+- another DTML variant::
+
+   <dtml-var expr="_['foo'].updateInfo()">
+
+Regardless of the language used, this is a very common idiom
+to find an object, be it a script or any other kind of object:
+you ask the context for it, and if it exists in this context
+or can be acquired from it, it will be used.
+
+Zope will throw a *KeyError* exception if the script you are
+calling cannot be acquired. If you are not certain that a
+given script exists in the current context, or if you want to
+compute the script name at run-time, you can use this Python
+idiom::
+
+  updateInfo = getattr(context, "updateInfo", None)
+  if updateInfo is not None:
+      updateInfo(color="brown", pattern="spotted")
+  else:
+      # complain about missing script
+      return "error: updateInfo() not found"
+
+The *getattr* function is a Python built-in. The first
+argument specifies an object, the second an attribute
+name.  The *getattr* function will return the named
+attribute, or the third argument if the attribute cannot be
+found. So in the next statement we just have to test whether
+the *updateInfo* variable is None, and if not, we know we can
+call it.
+
+Using External Methods
+----------------------
+
+Sometimes the security constraints imposed by Python-based
+Scripts, DTML and ZPT get in your way. For example, you
+might want to read files from disk, or access the network,
+or use some advanced libraries for things like regular
+expressions or image processing.  In these cases you can use
+*External Methods*.   We encountered External Methods briefly
+in the chapter entitled `Using Basic Zope Objects <BasicObjects.html>`_ .
+Now we will explore them in more detail.
+
+To create and edit External Methods you need access
+to the filesystem. This makes editing these scripts more
+cumbersome since you can't edit them right in your web
+browser. However, requiring access to the server's filesystem
+provides an important security control. If a user has access
+to a server's filesystem they already have the ability to harm
+Zope. So by requiring that unrestricted scripts be edited on
+the filesystem, Zope ensures that only people who are already
+trusted have access.
+
+External Method code is created and edited in files on the Zope
+server in the *Extensions* directory. This directory is located in
+the top-level Zope directory. Alternately you can create and edit
+your External Methods in an *Extensions* directory inside an
+installed Zope product directory, or in your INSTANCE_HOME
+directory if you have one. See the chapter entitled "Installing
+and Starting Zope":InstallingZope.html>`_ for more about
+INSTANCE_HOME.
+
+Let's take an example. Create a file named *example.py* in the
+Zope *Extensions* directory on your server. In the file, enter the
+following code::
+
+  def hello(name="World"):
+      return "Hello %s." % name 
+
+You've created a Python function in a Python module. But you have
+not yet created an External Method from it. To do so, we must add
+an External Method object in Zope.
+
+To add an External Method, choose *External Method* from the
+product add list. You will be taken to a form where you must
+provide an id. Type "hello" into the *Id* field, type "hello" in
+the *Function name* field, and type "example" in the *Module name*
+field. Then click the *Add* button.  You should now see a new
+External Method object in your folder. Click on it. You should be
+taken to the *Properties* view of your new External Method as
+shown in the figure below.
+
+.. figure:: ../Figures/8-7.png
+
+   External Method *Properties* view
+
+Note that if you wish to create several related External
+Methods, you do not need to create multiple modules on the
+filesystem.  You can define any number of functions in one
+module, and add an External Method to Zope for each
+function. For each of these External Methods, the *module
+name* would be the same, but *function name* would vary.
+
+Now test your new script by going to the *Test* view. You should
+see a greeting. You can pass different names to the script by
+specifying them in the URL. For example,
+'hello?name=Spanish+Inquisition'.
+
+This example is exactly the same as the "hello world" example that
+you saw for Python-based scripts. In fact, for simple string
+processing tasks like this, scripts offer a better solution since
+they are easier to work with.
+
+The main reasons to use an External Method are to access
+the filesystem or network, or to use Python packages that are
+not available to restricted scripts.
+
+For example, a Script (Python) cannot access environment variables
+on the host system. One could access them using an External
+Method, like so::
+
+  def instance_home():
+     import os
+     return os.environ.get('INSTANCE_HOME')
+
+Regular expressions are another useful tool that are restricted
+from Scripts.  Let's look at an example.  Assume we want to get
+the body of an HTML Page (everything between the 'body' and
+'/body' tags)::
+
+  import re
+  pattern = r"<\s*body.*?>(.*?)</body>"
+  regexp = re.compile(pattern, re.IGNORECASE + re.DOTALL)
+
+  def extract_body(htmlstring):
+      """
+      If htmlstring is a complete HTML page, return the string
+      between (the first) <body> ... </body> tags
+      """
+      matched = regexp.search(htmlpage)
+      if matched is None: return "No match found"
+      body = matched.group(1)
+      return body 
+
+Note that we import the 're' module and define the regular
+expression at the module level, instead of in the function itself;
+the 'extract_body()' function will find it anyway. Thus, the
+regular expression is compiled once, when Zope first loads the
+External Method, rather than every time this External Method is
+called.  This is a common optimization tactic.
+
+Now put this code in a module called 'my_extensions.py'. Add an
+'External Method' with an id of 'body_external_m'; specify
+'my_extensions' for the 'Module Name' to use and, 'extract_body'
+for 'Function Name'.
+
+You could call this for example in a 'Script (Python)' called
+'store_html' like this::
+
+  ## Script (Python) "store_html"
+  ##
+
+  # code to get 'htmlpage' goes here...
+  htmlpage = "some string, perhaps from an uploaded file"
+  # now extract the body
+  body = context.body_external_m(htmlpage)
+  # now do something with 'body' ...
+
+... assuming that body_external_m can be acquired by store_html.
+This is obviously not a complete example; you would want
+to get a real HTML page instead of a hardcoded one, and you would
+do something sensible with the value returned by your External
+Method. 
+
+Creating Thumbnails from Images
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Here is an example External Method that uses the Python Imaging
+Library (PIL) to create a thumbnail version of an existing Image
+object in a Folder.  Enter the following code in a file named
+*Thumbnail.py* in the *Extensions* directory::
+
+  def makeThumbnail(self, original_id, size=200):
+      """
+      Makes a thumbnail image given an image Id when called on a Zope
+      folder.
+
+      The thumbnail is a Zope image object that is a small JPG
+      representation of the original image. The thumbnail has an
+      'original_id' property set to the id of the full size image
+      object.
+      """
+
+      import PIL 
+      from StringIO import StringIO
+      import os.path 
+      # none of the above imports would be allowed in Script (Python)!
+
+      # Note that PIL.Image objects expect to get and save data
+      # from the filesystem; so do Zope Images. We can get around 
+      # this and do everything in memory by using StringIO.
+
+      # Get the original image data in memory.
+      original_image=getattr(self, original_id)
+      original_file=StringIO(str(original_image.data))
+
+      # create the thumbnail data in a new PIL Image. 
+      image=PIL.Image.open(original_file)
+      image=image.convert('RGB')
+      image.thumbnail((size,size))
+
+      # get the thumbnail data in memory.
+      thumbnail_file=StringIO()
+      image.save(thumbnail_file, "JPEG") 
+      thumbnail_file.seek(0)
+
+      # create an id for the thumbnail
+      path, ext=os.path.splitext(original_id)
+      thumbnail_id=path + '.thumb.jpg'
+
+      # if there's an old thumbnail, delete it
+      if thumbnail_id in self.objectIds():
+          self.manage_delObjects([thumbnail_id])
+
+      # create the Zope image object for the new thumbnail
+      self.manage_addProduct['OFSP'].manage_addImage(thumbnail_id,
+                                                     thumbnail_file,
+                                                     'thumbnail image')
+
+      # now find the new zope object so we can modify 
+      # its properties.
+      thumbnail_image=getattr(self, thumbnail_id)
+      thumbnail_image.manage_addProperty('original_id', original_id, 'string')
+
+Notice that the first parameter to the above function is called
+*self*. This parameter is optional. If *self* is the first parameter 
+to an External Method function definition, it will be assigned 
+the value of the calling context (in this case, a folder). 
+It can be used much like the *context* we have seen in 
+Scripts (Python).
+
+You must have PIL installed for this example to work. Installing
+PIL is beyond the scope of this book, but note that it is
+important to choose a version of PIL that is compatible with the
+version of Python that is used by your version of Zope. See the
+"PythonWorks
+website":http://www.pythonware.com/products/pil/index.htm for more
+information on PIL.  
+
+To continue our example, create an External Method named
+*makeThumbnail* that uses the *makeThumbnail* function in the
+*Thumbnail* module.
+
+Now you have a method that will create a thumbnail image. You can
+call it on a Folder with a URL like
+*ImageFolder/makeThumbnail?original_id=Horse.gif* This would
+create a thumbnail image named 'Horse.thumb.jpg'.
+
+You can use a script to loop through all the images in a folder and
+create thumbnail images for them. Create a Script (Python) named
+*makeThumbnails*::
+
+  ## Script (Python) "makeThumbnails"
+  ##
+  for image_id in context.objectIds('Image'):
+      context.makeThumbnail(image_id)
+
+This will loop through all the images in a folder and create a
+thumbnail for each one.
+
+Now call this script on a folder with images in it. It will create a
+thumbnail image for each contained image. Try calling the
+*makeThumbnails* script on the folder again and you'll notice it created
+thumbnails of your thumbnails. This is no good. You need to change the
+*makeThumbnails* script to recognize existing thumbnail images and not
+make thumbnails of them. Since all thumbnail images have an
+*original_id* property you can check for that property as a way of
+distinguishing between thumbnails and normal images::
+
+  ## Script (Python) "makeThumbnails"
+  ##
+  for image in context.objectValues('Image'):
+      if not image.hasProperty('original_id'):
+          context.makeThumbnail(image.getId())
+
+Delete all the thumbnail images in your folder and try calling your
+updated *makeThumbnails* script on the folder. It seems to work
+correctly now.
+
+Now with a little DTML you can glue your script and External Method
+together. Create a DTML Method called *displayThumbnails*::
+
+  <dtml-var standard_html_header>
+
+  <dtml-if updateThumbnails>
+    <dtml-call makeThumbnails>
+  </dtml-if>
+
+  <h2>Thumbnails</h2>
+
+  <table><tr valign="top">
+
+  <dtml-in expr="objectValues('Image')">
+    <dtml-if original_id>
+      <td>
+        <a href="&dtml-original_id;"><dtml-var sequence-item></a>
+        <br />
+        <dtml-var original_id>
+      </td> 
+    </dtml-if>
+  </dtml-in>
+
+  </tr></table>
+
+  <form>
+  <input type="submit" name="updateThumbnails"
+         value="Update Thumbnails" />
+  </form>
+
+  <dtml-var standard_html_footer>
+
+When you call this DTML Method on a folder it will loop through all the
+images in the folder and display all the thumbnail images and link them
+to the originals as shown in the figure below.
+
+.. figure:: ../Figures/8-8.png
+
+   Displaying thumbnail images
+
+This DTML Method also includes a form that allows you to update the
+thumbnail images. If you add, delete or change the images in your
+folder you can use this form to update your thumbnails.
+
+This example shows a good way to use scripts, External Methods and DTML
+together. Python takes care of the logic while the DTML handles
+presentation. Your External Methods handle external packages 
+such as PIL while your scripts do simple processing of Zope objects.
+Note that you could just as easily use a Page Template instead of DTML.
+  
+Processing XML with External Methods
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can use External Methods to do nearly anything. One interesting
+thing that you can do is to communicate using XML. You can generate and
+process XML with External Methods.
+
+Zope already understands some kinds of XML messages such as
+XML-RPC and WebDAV. As you create web applications that communicate
+with other systems you may want to have the ability to receive XML
+messages. You can receive XML a number of ways: you can read XML files
+from the file system or over the network, or you can define scripts
+that take XML arguments which can be called by remote systems.
+
+Once you have received an XML message you must process the XML to find
+out what it means and how to act on it.  Let's take a quick look at how
+you might parse XML manually using Python. Suppose you want to connect
+your web application to a "Jabber":http://www.jabber.com/ chat
+server. You might want to allow users to message you and receive
+dynamic responses based on the status of your web application. For
+example suppose you want to allow users to check the status of animals
+using instant messaging. Your application should respond to XML instant
+messages like this::
+
+  <message to="cage_monitor at zopezoo.org" from="user at host.com">
+    <body>monkey food status</body>
+  </message>
+
+You could scan the body of the message for commands, call a script
+and return responses like this::
+
+  <message to="user at host.com" from="cage_monitor at zopezoo.org">
+    <body>Monkeys were last fed at 3:15</body>
+  </message>
+
+Here is a sketch of how you could implement this XML messaging
+facility in your web application using an External Method::
+
+  # Uses Python 2.x standard xml processing packages.  See
+  # http://www.python.org/doc/current/lib/module-xml.sax.html for
+  # information about Python's SAX (Simple API for XML) support If
+  # you are using Python 1.5.2 you can get the PyXML package. See
+  # http://pyxml.sourceforge.net for more information about PyXML.
+
+  from xml.sax import parseString
+  from xml.sax.handler import ContentHandler
+
+  class MessageHandler(ContentHandler):
+      """
+      SAX message handler class
+
+      Extracts a message's to, from, and body
+      """
+
+      inbody=0
+      body=""
+
+      def startElement(self, name, attrs):
+          if name=="message":
+              self.recipient=attrs['to']
+              self.sender=attrs['from']
+          elif name=="body":
+              self.inbody=1
+
+      def endElement(self, name):
+          if name=="body":
+              self.inbody=0
+
+      def characters(self, content):
+          if self.inbody:
+              self.body=self.body + content
+
+  def receiveMessage(self, message):
+      """
+      Called by a Jabber server
+      """
+      handler=MessageHandler()
+      parseString(message, handler)
+
+      # call a script that returns a response string
+      # given a message body string
+      response_body=self.getResponse(handler.body)
+
+      # create a response XML message
+      response_message="""
+        <message to="%s" from="%s">
+          <body>%s</body>
+        </message>""" % (handler.sender, handler.recipient, response_body)
+
+      # return it to the server
+      return response_message
+
+The *receiveMessage* External Method uses Python's SAX (Simple API
+for XML) package to parse the XML message. The *MessageHandler*
+class receives callbacks as Python parses the message. The handler
+saves information its interested in. The External Method uses the
+handler class by creating an instance of it, and passing it to the
+*parseString* function. It then figures out a response message by
+calling *getResponse* with the message body. The *getResponse*
+script (which is not shown here) presumably scans the body for
+commands, queries the web applications state and returns some
+response. The *receiveMessage* method then creates an XML message
+using response and the sender information and returns it.
+
+The remote server would use this External Method by calling the
+*receiveMessage* method using the standard HTTP POST
+command. Voila, you've implemented a custom XML chat server that
+runs over HTTP.
+
+External Method Gotchas
+~~~~~~~~~~~~~~~~~~~~~~~
+
+While you are essentially unrestricted in what you can do in an
+External Method, there are still some things that
+are hard to do.
+
+While your Python code can do as it pleases if you want to
+work with the Zope framework you need to respect its
+rules. While programming with the Zope framework is too
+advanced a topic to cover here, there are a few things that
+you should be aware of.
+
+Problems can occur if you hand instances of your own
+classes to Zope and expect them to work like Zope
+objects. For example, you cannot define a class in your
+External Method and assign an instance of this class as an
+attribute of a Zope object. This causes problems with
+Zope's persistence machinery.  If you need to create new
+kinds of persistent objects, it's time to learn about
+writing Zope Products. Writing a Product is beyond the
+scope of this book. You can learn more by reading the
+"Zope Developers'
+Guide":http://www.zope.org/Documentation/Books/ZDG/current
+
+Advanced Acquisition 
+--------------------
+
+In the chapter entitled "Acquisition":Acquisition.html>`_ , we
+introduced acquisition by containment, which we have been using
+throughout this chapter. In acquisition by containment, Zope
+looks for an object by going back up the containment heirarchy
+until it finds an object with the right id. In Chapter 7 we also
+mentioned *context acquisition*, and warned that it is a tricky
+subject capable of causing your brain to explode. If you are
+ready for exploding brains, read on.
+
+Recall our Zoo example introduced earlier in this chapter. 
+
+.. figure:: ../Figures/zoo.png 
+
+   Zope Zoo Example hierarchy
+
+We have seen how Zope uses URL traversal and acquisition to find
+objects  in  higher containers.  More  complex arrangements  are
+possible. Suppose you want to call the *vaccinate* script on the
+*hippo*  object. What  URL can  you use?  If you  visit  the URL
+*Zoo/LargeAnimals/hippo/vaccinate* Zope will not be able to find
+the  *vaccinate* script  since it  isn't in  any of  the *hippo*
+object's containers.
+
+The solution is to give the path to the script as part of the
+URL. Zope allows you to combine two or more URLs into one in
+order to provide more acquisition context! By using acquisition,
+Zope will find the script as it backtracks along the URL. The
+URL to vaccinate the hippo is
+*Zoo/Vet/LargeAnimals/hippo/vaccinate*. Likewise, if you want to
+call the *vaccinate* script on the *kargarooMouse* object you
+should use the URL
+*Zoo/Vet/SmallAnimals/kargarooMouse/vaccinate*.
+
+Let's follow along as Zope traverses the URL
+*Zoo/Vet/LargeAnimals/hippo/vaccinate*. Zope starts in the root
+folder and looks for an object named *Zoo*. It moves to the
+*Zoo* folder and looks for an object named *Vet*. It moves to
+the *Vet* folder and looks for an object named
+*LargeAnimals*. The *Vet* folder does not contain an object with
+that name, but it can acquire the *LargeAnimals* folder from its
+container, *Zoo* folder. So it moves to the *LargeAnimals*
+folder and looks for an object named *hippo*.  It then moves to
+the *hippo* object and looks for an object named
+*vaccinate*. Since the *hippo* object does not contain a
+*vaccinate* object and neither do any of its containers, Zope
+backtracks along the URL path trying to find a *vaccinate*
+object. First it backs up to the *LargeAnimals* folder where
+*vaccinate* still cannot be found. Then it backs up to the *Vet*
+folder.  Here it finds a *vaccinate* script in the *Vet*
+folder. Since Zope has now come to the end of the URL, it calls
+the *vaccinate* script in the context of the *hippo* object.
+
+Note that we could also have organized the URL a bit
+differently. *Zoo/LargeAnimals/Vet/hippo/vaccinate* would also
+work. The difference is the order in which the context elements
+are searched. In this example, we only need to get *vaccinate*
+from *Vet*, so all that matters is that *Vet* appears in the URL
+after *Zoo* and before *hippo*.
+
+When Zope looks for a sub-object during URL traversal, it first
+looks for the sub-object in the current object. If it cannot
+find it in the current object it looks in the current object's
+containers. If it still cannot find the sub-object, it backs up
+along the URL path and searches again. It continues this process
+until it either finds the object or raises an error if it cannot
+be found. If several context folders are used in the URL, they
+will be searched in order from *left to right*.
+
+Context acquisition can be a very useful mechanism, and it
+allows you to be quite expressive when you compose URLs. The
+path you tell Zope to take on its way to an object will
+determine how it uses acquisition to look up the object's
+scripts.
+
+Note that not all scripts will behave differently depending on
+the traversed URL. For example, you might want your script to
+acquire names only from its parent containers and not from the
+URL context. To do so, simply use the *container* variable
+instead of the *context* variable in the script, as described
+above in the section "Using Python-based Scripts."
+
+Context Acquisition Gotchas
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Containment before context
+%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+It is important to realize that context acquisition
+*supplements* container acquisition. It does not *override*
+container acquisition.
+
+One at a time
+%%%%%%%%%%%%%
+
+Another point that often confuses new users is that each element
+of a path "sticks" for the duration of the traversal, once it is
+found. Think of it this way: objects are looked up one at a
+time, and once an object is found, it will not be looked up
+again.  For example, imagine this folder structure:
+
+.. figure:: ../Figures/acquisition.png
+
+   Acquisition example folder structure
+
+Now suppose that the *about_penguins* page contains a link to
+*Images/penguins.png*. Shouldn't this work? Won't
+*/Images/penguins.png* succeed when
+*/Content/Images/penguins.png* fails?  The answer is no. We
+always traverse from left to right, one item at a time. 
+First we find *Content*, then *Images* within it; *penguins.png* 
+appears in neither of those, and we haved searched all 
+parent containers of every element in the URL, so 
+there is nothing more to search in this URL.
+Zope stops there and raises an error. Zope never looks in */Images*
+because it has already found */Content/Images*.
+
+Readability
+%%%%%%%%%%%
+
+Context acquisition can make code more difficult to
+understand. A person reading your script can no longer simply
+look backwards up one containment heirarchy to see where an
+acquired object might be; many more places might be searched,
+all over the zope tree folder. And the order in which objects
+are searched, though it is consistent, can be confusing.
+
+Fragility
+%%%%%%%%%
+
+Over-use of context acquisition can also lead to fragility. In
+object-oriented terms, context acquisition can lead to a site
+with low cohesion and tight coupling. This is generally regarded
+as a bad thing. More specifically, there are many simple actions
+by which an unwitting developer could break scripts that rely on
+context acquisition. These are more likely to occur than with
+container acquisition, because potentially every part of your
+site affects every other part, even in parallel folder branches.
+
+For example, if you write a script that calls another script by
+a long and torturous path, you are assuming that the folder tree
+is not going to change. A maintenance decision to reorganize the
+folder heirarchy could require an audit of scripts in *every*
+part of the site to determine whether the reorganization will
+break anything. 
+
+Recall our Zoo example. There are several ways in which a zope
+maintainer could break the feed() script:
+
+Inserting another object with the name of the method
+  This is a normal technique for customizing behavior in Zope, but context
+  acquisition makes it more likely to happen by accident. Suppose that
+  giraffe vaccination is controlled by a regularly scheduled script that
+  calls *Zoo/Vet/LargeAnimals/giraffe/feed*. Suppose a content
+  administrator doesn't know about this script and adds a DTML page called
+  *vaccinate* in the giraffe folder, containing information about
+  vaccinating giraffes. This new *vaccinate* object will be acquired before
+  *Zoo/Vet/vaccinate*.  Hopefully you will notice the problem before your
+  giraffes get sick.
+
+Calling an inappropriate path
+  if you visit *Zoo/LargeAnimals/hippo/buildings/visitor_reception/feed*,
+  will the reception area be filled with hippo food?  One would hope not.
+  This might even be possible for someone who has no permissions on the
+  reception object. Such URLs are actually not difficult to construct. For
+  example, using relative URLs in standard_html_header can lead to some
+  quite long combinations of paths.
+
+Thanks to Toby Dickenson for pointing out these fragility issues
+on the zope-dev mailing list.
+
+
+Passing Parameters to Scripts
+-----------------------------
+
+All scripts can be passed parameters. A parameter gives a script
+more information about what to do. When you call a script from the
+web, Zope will try to find the script's parameters in the web
+request and pass them to your script. For example, if you have a
+script with parameters *dolphin* and *REQUEST* Zope will
+look for *dolphin* in the web request, and will pass the request
+itself as the *REQUEST* parameter. In practical terms this means
+that it is easy to do form processing in your script. For example,
+here is a form::
+
+  <form action="form_action">
+    Name of Hippo <input type="text" name="name" /><br />
+    Age of Hippo <input type="text" name="age" /><br />
+    <input type="submit" />
+  </form>
+
+You can easily process this form with a script named
+*form_action* that includes *name* and *age* in its parameter
+list::
+
+  ## Script (Python) "form_action"
+  ##parameters=name, age
+  ##
+  "Process form"
+  age=int(age)
+  message= 'This hippo is called %s and is %d years old' % (name, age)
+  if age < 18:
+      message += '\n %s is not old enough to drive!' % name
+  return message
+
+There is no need to process the form manually to extract values
+from it. Form elements are passed as strings, or lists of
+strings in the case of checkboxes and multiple-select input.
+
+In addition to form variables, you can specify any request
+variables as script parameters. For example, to get access to the
+request and response objects just include 'REQUEST' and 'RESPONSE'
+in your list of parameters. Request variables are detailed more
+fully in `Appendix B: API Reference <AppendixB.html>`_ .
+
+In the Python script given above, there is a subtle problem. You
+are probably expecting an integer rather than a string for age,
+but all form variables are passed as strings.  You could
+manually convert the string to an integer using the Python *int*
+built-in::
+
+  age = int(age)
+
+But this manual conversion may be inconvenient. Zope provides a
+way for you to specify form input types in the form, rather than
+in the processing script. Instead of converting the *age* variable
+to an integer in the processing script, you can indicate that it
+is an integer in the form itself::
+
+  Age <input type="text" name="age:int" />
+
+The ':int' appended to the form input name tells Zope to
+automatically convert the form input to an integer. This
+process is called *marshalling*. If the user of
+your form types something that cannot be converted to an integer
+(such as "22 going on 23") then Zope will raise an exception as
+shown in the figure below.
+
+.. figure:: ../Figures/8-3.png
+
+   Parameter conversion error
+
+It's handy to have Zope catch conversion errors, but you may not
+like Zope's error messages. You should avoid using Zope's
+converters if you want to provide your own error messages.
+
+Zope can perform many parameter conversions. Here is a list of Zope's
+basic parameter converters.
+
+*boolean*
+  Converts a variable to true or false. Variables
+  that are 0, None, an empty string, or an empty sequence are
+  false, all others are true.
+
+*int*
+  Converts a variable to an integer.
+
+*long*
+  Converts a variable to a long integer.
+
+*float*
+  Converts a variable to a floating point number.
+
+*string*
+  Converts a variable to a string. Most variables
+  are strings already so this converter is seldom used.
+
+*text*
+  Converts a variable to a string with normalized line
+  breaks.  Different browsers on various platforms encode line
+  endings differently, so this script makes sure the line endings are
+  consistent, regardless of how they were encoded by the browser.
+
+*list*
+  Converts a variable to a Python list.
+
+*tuple*
+  Converts a variable to a Python tuple. A tuple is
+  like a list, but cannot be modified.
+
+*tokens*
+  Converts a string to a list by breaking it on white
+  spaces.
+
+*lines*
+  Converts a string to a list by breaking it on new
+  lines.
+
+*date*
+  Converts a string to a *DateTime* object. The formats
+  accepted are fairly flexible, for example '10/16/2000',
+  '12:01:13 pm'.
+
+*required*
+  Raises an exception if the variable is not present.
+
+*ignore_empty*
+  Excludes the variable from the request if
+  the variable is an empty string.
+
+These converters all work in more or less the same way to coerce
+a form variable, which is a string, into another specific
+type. You may recognize these converters from the chapter
+entitled `Using Basic Zope Objects`_ , in which we
+discussed properties. These converters are used by Zope's
+property facility to convert properties to the right type.
+
+The *list* and *tuple* converters can be used in combination with other
+converters.  This allows you to apply additional converters to each
+element of the list or tuple.  Consider this form::
+
+  <form action="processTimes"> 
+
+    <p>I would prefer not to be disturbed at the following
+    times:</p>
+
+    <input type="checkbox" name="disturb_times:list:date"
+    value="12:00 AM" /> Midnight<br />
+
+    <input type="checkbox" name="disturb_times:list:date"
+    value="01:00 AM" /> 1:00 AM<br />
+
+    <input type="checkbox" name="disturb_times:list:date"
+    value="02:00 AM" /> 2:00 AM<br />
+
+    <input type="checkbox" name="disturb_times:list:date"
+    value="03:00 AM" /> 3:00 AM<br />
+
+    <input type="checkbox" name="disturb_times:list:date"
+    value="04:00 AM" /> 4:00 AM<br />
+
+    <input type="submit" />
+  </form>
+
+By using the *list* and *date* converters together, Zope will
+convert each selected time to a date and then combine all selected
+dates into a list named *disturb_times*.
+
+A more complex type of form conversion is to convert a series of inputs
+into *records.* Records are structures that have attributes. Using
+records, you can combine a number of form inputs into one variable with
+attributes.  The available record converters are:
+
+*record*
+  Converts a variable to a record attribute.
+
+*records*
+  Converts a variable to a record attribute in a list of
+  records.
+
+*default*
+  Provides a default value for a record attribute if the
+  variable is empty.
+
+*ignore_empty*
+  Skips a record attribute if the variable is empty.
+
+Here are some examples of how these converters are used::
+
+  <form action="processPerson">
+
+    First Name <input type="text" name="person.fname:record" /><br />
+    Last Name <input type="text" name="person.lname:record" /><br />
+    Age <input type="text" name="person.age:record:int" /><br />
+
+    <input type="submit" />
+  </form>
+
+This form will call the *processPerson* script with one
+parameter, *person*. The *person* variable will have the attributes
+*fname*, *lname* and *age*. Here's an example of how you might
+use the *person* variable in your *processPerson* script::
+
+  ## Script (Python) "processPerson"
+  ##parameters=person
+  ##
+  "Process a person record"
+  full_name="%s %s" % (person.fname, person.lname)
+  if person.age < 21:
+      return "Sorry, %s. You are not old enough to adopt an aardvark." % full_name
+  return "Thanks, %s. Your aardvark is on its way." % full_name
+
+The *records* converter works like the *record* converter except
+that it produces a list of records, rather than just one. Here is
+an example form::
+
+  <form action="processPeople">
+
+    <p>Please, enter information about one or more of your next of
+    kin.</p>
+
+    <p>
+      First Name <input type="text" name="people.fname:records" />
+      Last Name <input type="text" name="people.lname:records" />
+    </p>
+
+    <p>
+      First Name <input type="text" name="people.fname:records" />
+      Last Name <input type="text" name="people.lname:records" />
+    </p>
+
+    <p>
+      First Name <input type="text" name="people.fname:records" />
+      Last Name <input type="text" name="people.lname:records" />
+    </p>
+
+    <input type="submit" />
+  </form>    
+
+This form will call the *processPeople* script with a variable
+called *people* that is a list of records. Each record will have
+*fname* and *lname* attributes.  Note the difference between the
+*records* converter and the *list:record* converter: the former
+would create a list of records, whereas the latter would produce
+a single record whose attributes *fname* and *lname* would each
+be a list of values.
+
+The order of combined modifiers does not matter; for example,
+*int:list* is identical to *list:int*.
+
+Another useful parameter conversion uses form variables to
+rewrite the action of the form. This allows you to submit a form
+to different scripts depending on how the form is filled
+out. This is most useful in the case of a form with multiple
+submit buttons. Zope's action converters are:
+
+*action*
+  Appends the attribute value to the original form
+  action of the form. This is mostly useful for the case in
+  which you have multiple submit buttons on one form.  Each
+  button can be assigned to a script that gets called when that
+  button is clicked to submit the form. A synonym for *action*
+  is *method*.
+
+*default_action*
+  Appends the attribute value to the
+  original action of the form when no other *action* converter
+  is used.
+
+Here's an example form that uses action converters::
+
+  <form action="employeeHandlers">
+
+    <p>Select one or more employees</p>
+
+    <input type="checkbox" name="employees:list" value="Larry" /> Larry<br />
+    <input type="checkbox" name="employees:list" value="Simon" /> Simon<br />
+    <input type="checkbox" name="employees:list" value="Rene" /> Rene<br />
+
+    <input type="submit" name="fireEmployees:action" value="Fire!" /><br />
+
+    <input type="submit" name="promoteEmployees:action" value="Promote!" />
+
+  </form>
+
+We assume a folder 'employeeHandlers' containing two
+scripts named 'fireEmployees' and 'promoteEmployees'.  The
+form will call either the *fireEmployees* or the
+*promoteEmployees* script, depending on which of the two
+submit buttons is used.  Notice also how it builds a list
+of employees with the *list* converter.  Form converters
+can be very useful when designing Zope applications.
+
+Script Security
+---------------
+
+All scripts that can be edited through the web are subject to
+Zope's standard security policies. The only scripts that are not
+subject to these security restrictions are scripts that must be
+edited through the filesystem. These unrestricted scripts
+include *External Methods*.
+
+The chapter entitled `Users and Security <Security.html>`_ covers
+security in more detail. You should consult the *Roles of
+Executable Objects* and *Proxy Roles* sections for more
+information on how scripts are restricted by Zope security
+constraints.
+
+Security Restrictions of Script (Python)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Scripts are restricted in order to limit their ability
+to do harm. What could be harmful? In general, scripts
+keep you from accessing private Zope objects, making harmful
+changes to Zope objects, hurting the Zope process itself, and
+accessing the server Zope is running on. These restrictions
+are implemented through a collection of limits on what your
+scripts can do.
+
+Loop limits
+  Scripts cannot create infinite loops. If your script
+  loops a very large number of times Zope will raise an error. This
+  restriction covers all kinds of loops including *for* and *while*
+  loops. The reason for this restriction is to limit your ability to
+  hang Zope by creating an infinite loop.
+
+Import limits
+  Scripts cannot import arbitrary
+  packages and modules. You are limited to importing the
+  *Products.PythonScripts.standard* utility module, the
+  *AccessControl* module, those modules available via DTML
+  (*string*, *random*, *math*, *sequence*), and modules
+  which have been specifically made available to scripts
+  by product authors.  See `Appendix B: API Reference`_
+  for more information on these
+  modules.  If you want to be able to import any Python
+  module, use an External Method, as described in this
+  chapter.
+
+Access limits
+  You are restricted by standard Zope
+  security policies when accessing objects. In other words
+  the user executing the script is checked for
+  authorization when accessing objects. As with all
+  executable objects, you can modify the effective roles a
+  user has when calling a script using *Proxy Roles* (see
+  the chapter entitled `Users and Security`_
+  for more information). In addition, you cannot access
+  objects whose names begin with an underscore, since Zope
+  considers these objects to be private. Finally, you can
+  define classes in scripts but it is not really practical
+  to do so, because you are not allowed to access
+  attributes of these classes! Even if you were allowed to
+  do so, the restriction against using objects whose names
+  begin with an underscore would prevent you from using
+  your class's __init__ method.  If you need to define
+  classes, use *External Methods* or *Zope Products* (see
+  the "Zope Developers
+  Guide":http://www.zope.org/Documentation/Books/ZDG for
+  more information about creating Products).  You may,
+  however, define functions in scripts, although it is
+  rarely useful or necessary to do so.  In practice, a
+  Script in Zope is treated as if it were a single method
+  of the object you wish to call it on.
+
+Writing limits
+  In general you cannot directly change Zope object
+  attributes using scripts. You should call the appropriate
+  methods from the Zope API instead.
+
+Despite these limits, a determined user could use large amounts
+of CPU time and memory using Python-based Scripts. So malicious
+scripts could constitute a kind of denial of service attack by
+using lots of resources. These are difficult problems to solve
+and DTML suffers from the same potential for abuse. As with
+DTML, you probably should not grant access to scripts to
+untrusted people.
+
+
+DTML versus Python versus Page Templates 
+----------------------------------------
+
+Zope gives you many ways to script. For small scripting
+tasks the choice of Python-based Scripts, Page Templates or
+DTML probably doesn't make a big difference.  For larger,
+logic-oriented tasks you should use Python-based Scripts or
+External Methods. 
+
+For presentation, Python should *not* be used; the choice
+then becomes whether to use DTML or ZPT.
+
+Just for the sake of comparison, here is a simple presentational script 
+suggested by Gisle Aas in four different languages.
+
+In DTML::
+
+  <dtml-in objectValues>
+    <dtml-var getId>: <dtml-var sequence-item>
+  </dtml-in>
+  done
+
+In ZPT::
+
+  <div tal:repeat="item here/objectValues" 
+       tal:replace="python:'%s: %s\n' % (item.getId(), str(item))" />
+
+In Python::
+
+  for item in context.objectValues():
+      print "%s: %s" % (item.getId(), item)
+  print "done"
+  return printed
+
+Despite the fact that Zope is implemented in Python, it sometimes
+(for better or worse) follows the Perl philosophy that "there's
+more than one way to do it".
+
+Remote Scripting and Network Services
+-------------------------------------
+
+Web servers are used to serve content to software clients; usually
+people using web browser software.  The software client can also be
+another computer that is using your web server to access some kind of
+service.
+
+Because Zope exposes objects and scripts on the web, it can be used to
+provide a powerful, well organized, secure web API to other remote
+network application clients.
+
+There are two common ways to remotely script Zope.  The first way
+is using a simple remote procedure call protocol called
+*XML-RPC*.  XML-RPC is used to execute a procedure on a remote
+machine and get a result on the local machine.  XML-RPC is designed
+to be language neutral, and in this chapter you'll see examples in
+Python, Perl and Java.
+
+The second common way to remotely script Zope is with any HTTP
+client that can be automated with a script.  Many language
+libraries come with simple scriptable HTTP clients and there are
+many programs that let you you script HTTP from the command line.
+
+Using XML-RPC
+~~~~~~~~~~~~~
+
+XML-RPC is a simple remote procedure call mechanism that works
+over HTTP and uses XML to encode information. XML-RPC clients
+have been implemented for many languages including Python, Perl,
+Java, JavaScript, and TCL.
+
+In-depth information on XML-RPC can be found at the "XML-RPC
+website":http://www.xmlrpc.org/. 
+
+All Zope scripts that can be called from URLs can be called via
+XML-RPC. Basically XML-RPC provides a system to marshal
+arguments to scripts that can be called from the web. As you saw
+earlier in the chapter Zope provides its own marshaling
+controls that you can use from HTTP. XML-RPC and Zope's own
+marshaling accomplish much the same thing. The advantage of
+XML-RPC marshaling is that it is a reasonably supported
+standard that also supports marshaling of return values as well
+as argument values.
+
+Here's a fanciful example that shows you how to remotely script
+a mass firing of janitors using XML-RPC.
+
+Here's the code in Python::
+
+  import xmlrpclib
+
+  server = xmlrpclib.Server('http://www.zopezoo.org/')
+  for employee in server.JanitorialDepartment.personnel():
+      server.fireEmployee(employee)
+
+In Perl::
+
+  use Frontier::Client;
+
+  $server = Frontier::Client->new(url => "http://www.zopezoo.org/");
+
+  $employees = $server->call("JanitorialDepartment.personnel");
+  foreach $employee ( @$employees ) {
+
+    $server->call("fireEmployee",$server->string($employee));
+
+  }
+
+In Java::
+
+  try {
+      XmlRpcClient server = new XmlRpcClient("http://www.zopezoo.org/");
+      Vector employees = (Vector) server.execute("JanitorialDepartment.personnel");
+
+      int num = employees.size();
+      for (int i = 0; i < num; i++) {
+          Vector args = new Vector(employees.subList(i, i+1));
+          server.execute("fireEmployee", args);
+      }
+
+  } catch (XmlRpcException ex) {
+      ex.printStackTrace();
+  } catch (IOException ioex) {
+      ioex.printStackTrace();
+  }
+
+Actually the above example will probably not run correctly, since you
+will most likely want to protect the *fireEmployee* script. This brings
+up the issue of security with XML-RPC. XML-RPC does not have any
+security provisions of its own; however, since it runs over HTTP it can
+leverage existing HTTP security controls. In fact Zope treats an
+XML-RPC request exactly like a normal HTTP request with respect to
+security controls. This means that you must provide authentication in
+your XML-RPC request for Zope to grant you access to protected
+scripts, eg. by using the user:password URL notation, as
+in 'http://user:password@server.domain/'
+
+Remote Scripting with HTTP
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Any HTTP client can be used for remotely scripting Zope.
+
+On Unix systems you have a number of tools at your
+disposal for remotely scripting Zope. One simple example
+is to use *wget* to call Zope script URLs and use *cron*
+to schedule the script calls. For example, suppose you
+have a Zope script that feeds the lions and you would like
+to call it every morning.  You can use *wget* to call the
+script like so::
+
+  $ wget --spider http://www.zopezope.org/Lions/feed
+
+The *spider* option tells *wget* not to save the response as a
+file. Suppose that your script is protected and requires
+authorization. You can pass your user name and password with *wget* to
+access protected scripts::
+
+  $ wget --spider --http-user=ZooKeeper \
+      --http-passwd=SecretPhrase \
+      http://www.zopezope.org/Lions/feed
+
+Now let's use *cron* to call this command every morning at 8am. Edit
+your crontab file with the *crontab* command::
+
+  $ crontab -e
+
+Then add a line to call wget every day at 8 am::
+
+  0 8 * * * wget -nv --spider --http_user=ZooKeeper \
+    --http_pass=SecretPhrase http://www.zopezoo.org/Lions/feed
+
+(Beware of the linebreak -- the above should be input as
+one line, minus the backslash).
+
+The only difference between using *cron* and calling *wget* manually is
+that you should use the *nv* switch when using *cron* since you don't
+care about output of the *wget* command.
+
+For our final example let's get really perverse. Since networking is
+built into so many different systems, it's easy to find an unlikely
+candidate to script Zope. If you had an Internet-enabled toaster you
+would probably be able to script Zope with it. Let's take Microsoft
+Word as our example Zope client. All that's necessary is to get Word to
+agree to tickle a URL.
+
+The easiest way to script Zope with Word is to tell word to open a
+document and then type a Zope script URL as the file name as shown in
+[8-9].
+
+.. figure:: ../Figures/8-9.png
+
+   Calling a URL with Microsoft Word
+
+Word will then load the URL and return the results of calling the Zope
+script. Despite the fact that Word doesn't let you POST arguments this
+way, you can pass GET arguments by entering them as part of the URL.
+
+You can even control this behavior using Word's built-in Visual Basic
+scripting. For example, here's a fragment of Visual Basic that tells
+Word to open a new document using a Zope script URL::
+
+  Documents.Open FileName:="http://www.zopezoo.org/LionCages/wash?use_soap=1&water_temp=hot" 
+
+You could use Visual Basic to call Zope script URLs in many different
+ways.
+
+Zope's URL to script call translation is the key to remote
+scripting. Since you can control Zope so easily with simple URLs you
+can easy script Zope with almost any network-aware system.
+
+Conclusion
+----------
+
+With scripts you can control Zope objects and glue together your
+application's logic, data, and presentation. You can
+programmatically manage objects in your Zope folder hierarchy by
+using the Zope API.  You can also perform serious programming
+tasks such as image processing and XML parsing.

Modified: zope2book/trunk/source/index.rst
===================================================================
--- zope2book/trunk/source/index.rst	2009-02-10 22:22:14 UTC (rev 96426)
+++ zope2book/trunk/source/index.rst	2009-02-10 22:44:06 UTC (rev 96427)
@@ -25,6 +25,7 @@
    Security.rst
    AdvDTML.rst
    AdvZPT.rst
+   ScriptingZope.rst
    AppendixA.rst
    Contributions.rst
 



More information about the Checkins mailing list