ServerZen.Net

Technology news centering around Python

Entries tagged “zope2”

Zope 3 in Zope 2 Gotcha's: Unicode

written by rocky, on 8/28/07 9:56 AM.

Viewlets

A viewlet’s render() method needs to return unicode objects because the standard zope3 viewlet implementation concatenates the output of various viewlet render()‘s. If the output is a str, then implicit conversion-to-unicode will happen using the default python character encoding which is most often ascii (causing a utf-8 encoded str with non-ascii chars to blow up).

Archetypes Schema Fields

Archetypes (as present in Plone 2.5 and 3.0, and possibly older) stores all StringField fields as utf-8 encoded str’s irrelevant of what the Plone site encoding has been configured as. Safest way to handle this is to always feed StringField field mutators unicode objects instead of str’s. And upon retrieval, be prepared to decode the str’s using utf-8 (ie context.Title().decode(’utf-8′) => u’someval’). Remember, StringField accessors will always return utf-8 encoded str’s and Zope 3 often makes assumptions that the string values it’s dealing with are unicode objects.

Summing Up

Use unicode objects everywhere. If some Zope 2 / Plone API forces you to use str‘s or get str‘s convert them to unicode‘s before doing anything else.

Using zope.formlib With Plone: Part 2

written by rocky, on 6/13/06 7:13 AM.

The first part in this series focused on getting a good consistent package skeleton in place. This second part will attempt to do something useful with formlib in a Plone environment.

Understanding Formlib

zope.formlib is a Zope 3 package designed to ease the development of web-based forms in your Zope applications. In its simplest form you can compare what it does with the auto-generated displays Archetypes provides you for viewing a content type (base_view) and editing a content type (base_edit). For all practical sense formlib based components are really regular Zope view components with some convenient base classes for auto-generating output based on schema’s and other configuration info.

Thankfully beginning with Zope 2.9.3 zope.formlib is now being distributed with Zope 2. Of course Five >= 1.4 is required to make use of this Zope 3 package.

Defining Our First Form

For purposes of this writing we will construct a very simple search form for searching Plone content. This form will be similar to Plone’s built in advanced search form but much simpler.

You can view the working source code of these examples at the updated collective svn browser and updated collective svn repository locations.

The Form Class

We begin by creating a new file, browser.py, which will need to live in ploneexample.formlib/ploneexample/formlib/. The browser.py file will comprise the bulk of the necessary work. Lets start by adding the necessary imports.

from zope import interface, schema
from zope.formlib import form
from Products.CMFCore import utils as cmfutils
from Products.Five.browser import pagetemplatefile
from Products.Five.formlib import formbase

Next we’ll construct our first Zope 3 interface:

class ISearch(interface.Interface):
    text = schema.TextLine(title=u'Search Text',
                           description=u'The text to search for',
                           required=False)

    description = schema.TextLine(title=u'Description',
                                  required=False)

The purpose of the interface in this case is not to describe a particular content object but instead to define the fields that formlib will use. Later on we’ll discover how tradtional interfaces used to describe actual content classes can be used in combination with formlib to autogenerate proper add and edit forms for content.

And now for the form view class itself. We will start with the first part of the class definition.

class SearchForm(formbase.PageForm):
    form_fields = form.FormFields(ISearch)
    result_template = pagetemplatefile.ZopeTwoPageTemplateFile('search-results.pt')

We use the PageForm class as our super class to inherit functionality from formlib itself. By default, PageForm knows how to generate all the HTML that will make up of our finished form. But in order to do this, formlib needs to know what fields we want. We do this by providing the form_fields attribute. FormFields is a formlib helper class that generates the appropriate field items from any Zope 3 schema (in this case, the schema interface we just defined).

The result_template attribute defines a new page template that we will use to iterate over all of the results of our search.

Next we define an action for our form:

@form.action("search")
def action_search(self, action, data):
    catalog = cmfutils.getToolByName(self.context, 'portal_catalog')

    kwargs = {}
    if data['text']:
        kwargs['SearchableText'] = data['text']
    if data['description']:
        kwargs['description'] = data['description']

    self.search_results = catalog(**kwargs)
    self.search_results_count = len(self.search_results)
    return self.result_template()

This is where the real work takes place. A formlib action is generally a handler that will somehow get invoked by submitting an HTML form. In this case we create a new action labeled search, that will be used to handle when a user hits the search button. Our formlib-based class will automatically understand how to hook in an search button on the HTML form itself. This particular action handler will return our result template as a result.

The Result Page Template

In order to display the results of our search form we need to setup a simple page template. We will name this template, search-results.pt. Most the of template is pretty uninteresting. But for purposes of this writing we will demonstrate the result printing portion.

<tal:block tal:repeat="single view/search_results">
<div class="single-result">
  <h4>
    <span tal:replace="repeat/single/number"></span>.
    <a tal:content="single/Title" tal:attributes="href single/getURL" href=""></a>
  </h4>
  <p tal:content="single/Description"></p>
</div>
</tal:block>

Since our previous formlib based class was a regular view, it gets treated that way inside the page templates. And we are able to assign simple attributes to our view that can get picked up within the template.

Tying It All Together With ZCML

Now that we’ve defined the form class and the result page template to go along with it we need to glue this all into Zope. We do this in configure.zcml.

So we need to add the appropriate ZCML snippet:

<browser:page
    name="search.html"
    for="Products.CMFPlone.Portal.PloneSite"
    class=".browser.SearchForm"
    permission="zope.Public"
    />

Again, since formlib is all based on regular Zope 3 view components, we register them the same way in the ZCML.

Our First zope.formlib Example In Summary

The example demonstrated here shows the simplest form that could be created with formlib and how to hook in a simple action. It should be obvious from this example how you could use formlib to replace simple CMFFormController based logic. Of course formlib can do many other advanced things such as provide sub-form functionality and autogenerated add and edit forms for content classes.

The bottom line is that zope.formlib is ready for use inside Plone today. And since formlib is so easy to work with, the author recommends all Plone application developers give it a try.

Using zope.formlib With Plone: Part 1

written by rocky, on 6/8/06 9:15 PM.

The primary purpose of this writing is to display the use of the Zope 3 technology, zope.formlib, in a Zope 2 + Plone based environment. A side goal is to help demonstrate some new practices with building new Plone based applications.

Part 1 specfically focuses on getting the initial skeleton product in place.

Required Stack Components

The example built by this writing is designed to run on the following stack:

  • Python >= 2.4.3
  • Zope >= 2.9.3
  • Plone >= 2.5
  • Five >= 1.4

The example described in this writing may work on different versions but is untested by the author so your mileage may vary.

Some Things To Note Before Starting

We will begin by working with a product by the name of ploneexample.formlib. The finished code for this example can be browsed using the collective svn browser or checked out from Subversion using at the collective svn repository.

The first thing the keen Plone application developer will notice is the unusual naming convention of the product. Traditionally Plone products use CamelCase as the convention for naming products with no additional periods (ie PloneExampleFormlib). But as one of the goals of this writing, best practices will be demonstrated where we try to be as pythonic as possible which means using all lowercase for package names.

The Importance Of Pythonic

Many of you may be asking yourself, “but why do we really care about keeping this pythonic?” The single biggest reason for keeping things pythonic is to keep things as simple and easy to understand for people who are already familiar with Python. When developing standard Zope 2 products and placing them into the Products directory of your Zope 2 instance, Zope magically places them within the Products namespace package (ie so the full package path of CMFPlone actually becomes Products.CMFPlone). Living in PYTHONPATH like any other package and being reusable in general is A Good Thing TM.

Also, by keeping products pythonic, we can now use generic Python tools such as easy_install and setuptools (both great package management components) to work with these products.

Creating The Product

For the initial creation of the product we will use a small component of the Python Paste project. The rest of this writing will assume the reader has installed Phillip J. Eby’s setuptools and easy_install products. As well as the necessary paster component along with the ZopeSkel template package. For instructions on how to setup paster and ZopeSkel, please see Daniel Nouri’s excellent article on ZopeSkel.

So lets get started:

$ paster create -t plone_core ploneexample.formlib
Selected and implied templates:
  ZopeSkel#plone_core  A Plone Core project

Variables:
  package:  ploneexampleformlib
  project:  ploneexample.formlib
Creating template plone_core
Enter namespace_package (Namespace package) ['plone']: ploneexample
Enter package (The package contained namespace package (like i18n)) ['']: formlib
Enter pythonproducts (Are you making a productsless Zope 2 Product?) [False]: True
Enter version (Version) ['0.1']:
Enter description (One-line description of the package) ['']: A Plone product for demonstrating zope.formlib usage
Enter long_description (Multi-line description (in reST)) ['']:
Enter author (Author name) ['Plone Foundation']: Rocky Burt
Enter author_email (Author email) ['plone-developers@lists.sourceforge.net']: rocky@serverzen.com
Enter keywords (Space-separated keywords/tags) ['']:
Enter url (URL of homepage) ['http://svn.plone.org/svn/plone/plone.i18n']: http://dev.plone.org/collective/browser/examples/ploneexample.formlib
Enter license_name (License name) ['GPL']:
Enter zip_safe (True/False: if the package can be distributed as a .zip file) [False]:
  Recursing into +namespace_package+
    Creating ./ploneexample.formlib/ploneexample/
    Recursing into +package+
      Creating ./ploneexample.formlib/ploneexample/formlib/
      Copying HISTORY.txt_tmpl to ./ploneexample.formlib/ploneexample/formlib/HISTORY.txt
      Copying LICENSE.GPL to ./ploneexample.formlib/ploneexample/formlib/LICENSE.GPL
      Copying LICENSE.txt_tmpl to ./ploneexample.formlib/ploneexample/formlib/LICENSE.txt
      Copying README.txt_tmpl to ./ploneexample.formlib/ploneexample/formlib/README.txt
      Copying __init__.py_tmpl to ./ploneexample.formlib/ploneexample/formlib/__init__.py
      Copying configure.zcml to ./ploneexample.formlib/ploneexample/formlib/configure.zcml
      Copying version.txt_tmpl to ./ploneexample.formlib/ploneexample/formlib/version.txt
    Copying __init__.py_tmpl to ./ploneexample.formlib/ploneexample/__init__.py
  Copying setup.cfg to ./ploneexample.formlib/setup.cfg
  Copying setup.py_tmpl to ./ploneexample.formlib/setup.py
Running /usr/bin/python2.4 setup.py egg_info

Setting Up Development

After this has completed running you should now have a ploneexample.formlib directory in your current working directory. For purposes of development we will now use setuptools to setup the ploneexample.formlib project on the PYTHONPATH in order to make it available to Zope. So make sure you make the ploneexample.formlib directory your current directory and do the following (remember to run this with sudo if you’re in a UNIX environment and don’t have permission to write to the system python site-packages directory):

$ python2.4 setup.py develop

This will yield some output similar to the following:

[snip output...]
Installed /home/rocky/Documents/developing/projects/ploneexample.formlib
Processing dependencies for ploneexample.formlib==0.1dev

Hooking Up The New Product Into Zope 2

Now that we have a fully runnable Zope 2 product we will proceed with hooking this up to an existing Zope 2 instance. Until Zope and CMF/Plone catch up (Zope 2.10 already includes the necessary changes) we will need to use the pythonproducts product to enable Zope 2 products to exist outside of the instance Products directory.

So be sure to download and install the pythonproducts product into your Zope 2 instance by following the install instructions provided by pythonproducts. For those in a hurry, setting up pythonproducts is as simple as downloading the pythonproducts tarball, extracting the contents into some temporary directory, and running python setup.py install –home $INSTANCE_HOME.

Now you need to tell your Zope 2 instance to “activate” the new ploneexample.formlib package as a Zope 2 product. You do this by going to the etc/package-includes directory in your Zope 2 instance and create a new file there called ploneexample.formlib-configure.zcml with its only contents being:

<include package="ploneexample.formlib" />

You should now test your Zope 2 instance to confirm that the new ploneexample.formlib product is available. You can do this by starting your Zope 2 instance as you normally would and going to the Products section of the Control Panel via the ZMI. Towards the bottom of the list you should see:

ploneexample.formlib (Installed product ploneexample.formlib)

Stay tuned for the next part of this series!

Plone And Zope 3

written by rocky, on 2/3/06 12:03 PM.

For a little while now Plone development has been moving towards Zope 3 development styles, conventions, and techniques via the Five product on Zope 2. There is much documentation available on the internet today for following this path (at least from the perspective of Zope 3). One of the largest benefits of this is being able to use the Zope 3 Component Architecture (CA) which promises to provide us with more manageable and reusable components.

The repercussions of such a system is that developers have a more robust framework for developing applications and when components are developed by one developer, there is a higher chance that this component can be reused by another developer in another project (and of course by the same developer in other projects).

Business Impact

Benefits

Of course further development, refactoring, and refining the base architecture generally provides a more robust solution to such an extent that businesses have to spend less time and effort managing and supporting deployed solutions. Having reusable components means less time spent developing new components and more time spent plugging in existing components which proves to take less time and thus less expense.

Risks

Migration of existing sites can get quite complicated. Also having to deal with switching frameworks such as making it difficult to maintain backwards compatibility with third-party products. Of course this gets easier as more and more third-party products adopt the same Zope 3 develop techniques which means education of this process moving forward is vitally important.

Random Non-Plone Impacts

From the perspective of a traditional Zope 2 developer deciding whether a new project should be developed and deployed on Zope 2 VS Zope 3 there are two different types of people:

  1. Zope 2 developer who is accustomed and familiar with Zope 2 development techniques with no interest to move outside of the Zope 2 development model.
  2. Zope 2 developer who is interested in Zope 3 development techniques but due to missing functionality (Zope 2 has obviously been around a lot longer than Zope 3) in Zope 3 has to develop and deploy on Zope 2.

Obviously the move to Zope 3 development techniques for #1 Zope developer doesn’t make any sense. But developer #2 would be very interested in getting equivalent (in capability) functionality from Zope 2 into Zope 3. And once enough of the required Zope 2 functionality has found a capable equivalent in Zope 3, #2 developers will have a path to fully migrate to developing and deploying on Zope 3.

In order for a full set of Plone components to deploy and run on Zope 3 a lot of this traditional Zope 2 functionality will have to have equivalents on Zope 3. So in the process of providing this functionality for Plone on Zope 3, Zope 2 developers will have much of their wanted/needed functionality migrated as well. This means non-Plone Zope 2 developers will have a higher likelihood of being able to deploy on Zope 3.

Conclusion

It is the recommendation of this author that moving to Zope 3 development techniques is a required manner of evolution for Plone and the Plone software stack (which is in agreement with the Goldegg effort of course). And related to this, the development process and attitude should be to build Zope 3 components on Zope 3 and build them so they (for the some finite time period) can be deployed on Zope 2 until the convergence of Zope 2 and Zope 3 is complete.