ZPTKit 0.3

Contents

ZPTKit is copyright 2005 Imaginary Landscape, LLC.

Downloads and Websites

The repository is a Subversion repository. To check out a copy:

$ svn co http://svn.webwareforpython.org/ZPTKit/trunk ZPTKit

License and Prerequesites

ZPTKit is licensed under an MIT-style license. This gives you permission to most anything you want with ZPTKit.

ZPTKit is a toolkit for Webware For Python. It builds upon several other pieces of code:

Status & News

ZPTKit is used in production environments, but has not had a wide base of users. As such, it should probably be considered beta quality -- while in a certain use case it performs well, it's likely that new users will provide new use cases.

Also, ZPTKit builds on Component, and Component is still at a beta quality. By the Stable Dependencies Principle, ZPTKit can not be more stable than Component.

Changes in ZPTKit 0.3

  • Improve support for templates in subdirectories of the search path
  • Removed special handling of RFC-822 headers in emails; now only the markup form of headers is allowed
  • Improved rendering of tables with htmlrender
  • Make more compatible with setuptools (setuptools is not required)
  • Added Paste Script package template
  • Added .renderTemplate() method, that does normal rendering but returning the result instead of writing it to the output stream
  • Added add_template_path() method to servlet, which allows per-request addition of items to the path
  • Added egg/PackageName/template_name.pt, which allows you to load templates out of other packages (based on Eggs)

Changes in ZPTKit 0.2

(Note: no public APIs were changed.)

  • Fixed problem where macro templates were not being cached.
  • Caches all templates more aggressively. These two fixes should have a considerable impact on performance.
  • Cleanup and simplification.
  • Distutils-based distribution.
  • Some small improvements to the HTML->text renderer.

Features

To-Do

Using ZPTKit

First, make sure you are subclassing from Component.CPage; typically you change your SitePage:

from Component import CPage
from ZPTKit import ZPTComponent

class SitePage(CPage):
    components = [ZPTComponent(zpt_paths)]
    def writeHTML(self):
        self.writeTemplate()
    # Needed for Webware 0.8.1 and earlier:
    def handleAction(self, action):
        CPage.handleAction(self, action)
        self.defaultAction()

CPage is compatible with WebKit.Page. You must fill in zpt_paths on your own, using whatever configuration mechanism you choose. This can be a single path (where all templates are found) or a list of paths; if a list, then the paths will be searched in order for each template.

Your template will now have several new methods and attributes:

writeTemplate():
Renders and writes the template that you've set with setView, and writes it to the response.
template(filename):
Retrieve the named template. You can render it by calling it.
sendMail(filename, to_address, from_address, **options):
Send an email with the given to and from address, and options.
options:
A dictionary of options. You can also set and get values from this custom dictionary with attributes. The values will be made available in your template under options/attribute_name. In Zope you typically pass values into a template as keyword arguments, and they show up as options; this way top-level attributes remain fairly fixed. This is empty until you add values to it.
context:
Another custom dictionary, which has all the values available to the template, including options. This also includes request, a dictionary-like object that holds values from the request, test(cond, if_true, if_false=None) for inline conditionals, and here, which represents the current directory (for use when calling macros in a template).

CPage adds the concept of "view", which ZPTKit interprets as the name of a template (by default CPage interprets it as the name of a servlet method). This way you can have multiple templates for a single servlet, with each template as a view. E.g.:

class Register(SitePage):
    def actions(self):
        return ['save']
    def save(self):
        ... process form ...
        if successful:
            self.setView('RegisterSuccessful.pt')

By default the view matches the servlet's name, so Register.pt is called. In this case if the form is successfully submitted we instead use RegisterSuccessful.pt.

Templates

ZPTKit templates are normal Zope Page Templates. In most cases they will look very similar. You can use macros as normal, for instance.

One difference is that there is no container variable. You can use here to refer to other templates (like here/standard_template.pt), but only to templates (in Zope you can access scripts and functions the same way).

Instead the variable servlet is added, which is a reference to the calling servlet. So if you have a complicated callback, you can refer to python: servlet.method(arg1, ...) and the value will be substituted in (be sure to use structure if that value includes markup).

Providing Template With Eggs

You must add something like to your setup.py file:

setup(....
    entry_points="""
    [zptkit.template_dir]
    main = mypackage.templates:template_location
    """, ...)

Then in mypackage/templates.py put something like:

template_location = 'mypackage/templates/'

Then when someone tries to load egg/MyPackage/foo.pt it will look in mypackage/templates/foo.pt. Also egg/MyPackage/main/foo.pt will work (using the name you used in entry_points) -- main is the default.

ResourceComponent

ResourceComponent is also provided to allow static files (like CSS or images) to go alongside templates. You must turn ExtraPathInfo on in Application.config, and add a component just to your index.py (or Main.py) servlet:

from ZPTKit.resourcecomponent import ResourceComponent

class index(SitePage):
    components = SitePage.components + [
        ResourceComponent(zpt_paths)]

Then zpt_paths will be searched for files if no servlet is found by a given name (and it will produce 404s if no file is found either). The normal processing of the index servlet will be aborted in either case, so long as there is any request.extraURLPath().