Attention

This document was written for an old version of Plone, Plone 2.5.x, and was last updated 1203 days ago.

To learn how to upgrade to the current version of Plone, read the upgrade manual.

Adding configuration settings using Zope 3 schemas and formlib

by Malthe Borch last modified Feb 04, 2009 03:03 AM
This how-to explains how to add a configlet to Plone's control panel and letting the Zope 3 framework do the work for you.

We're going to put together three components. A local utility where we store the settings, a schema that defines the settings and a view that takes care of the rendering. We glue together the view and the storage by registering a simple adapter.

First, the components

Let's first define the fields that make up our configuration settings. We use zope.schema for this. As part of best practices in Zope development, we support internationalization by wrapping all strings as messages.

Important: If you're using Plone 2.5 you must make sure that your site is registered as a Five local site. That's beyond the scope of this how-to, but you can look over the shoulders of Rocky Burt in the Plone4Artists project. Specifically the p4a.common module.

interfaces.py:
from zope.interface import Interface
from zope import schema
from zope.i18nmessageid import MessageFactory

_ = MessageFactory('some_message_domain')

class ISillyConfiguration(Interface):
  """This interface defines the configlet."""

  favorite_color = schema.TextLine(title=_(u"Enter your favorite color"),
                                  required=True) 

Next we'll set up the configlet view class:

browser/config.py:
from zope.formlib import form
from zope.i18nmessageid import MessageFactory

from Products.Five.formlib import formbase

from interfaces import ISillyConfiguration

_ = MessageFactory('some_message_domain')

class SillyConfigurationForm(formbase.EditFormBase):
    form_fields = form.Fields(ISillyConfiguration)

    label = _(u"A silly settings form")

We need to register this view on the Plone site. Here's the required configuration:

browser/configuration.zcml:
<configure 
    xmlns="http://namespaces.zope.org/zope"
    xmlns:browser="http://namespaces.zope.org/browser">

  <browser:page
     for="Products.CMFPlone.Portal.PloneSite"
     name="silly-configuration"
     class=".config.SillyConfigurationForm"
     permission="cmf.ManagePortal"
     />

</configure>

We don't want to store the settings on the Plone site itself. Let's add a local utility and store it there:

config.py:
from zope.interface import implements
from zope.schema.fieldproperty import FieldProperty

from interfaces import ISillyConfiguration

from OFS.SimpleItem import SimpleItem

class SillyConfiguration(SimpleItem):
    implements(ISillyConfiguration)
    
    contact_email = FieldProperty(ISillyConfiguration['contact_email'])

We'll create and register this local utility when we install our product. Make sure the following method is called as part of the normal product installation routine.

sitesetup.py:
from Products.SillyProduct.interfaces import ISillyConfiguration
from Products.SillyProduct.config import SillyConfiguration

def setup_site(portal):
  sm = portal.getSiteManager()

  if not sm.queryUtility(interfaces.ISillyConfiguration, name='silly_config'):
    sm.registerUtility(SillyConfiguration(),
                       interfaces.ISillyConfiguration,
                       'silly_config')

Note that if you're on Zope 2.9 you need to reverse the first two arguments for the registerUtility call.

Add glue

All that's left now is to add an adapter that tells the configuration form that we it should use our local utility to store the schema attributes.

Instead of registering a class to adapt our components we just use a normal function. The way it works is that formlib will look for an adapter to tell it how to glue together the fields with the context and it uses the resulting object to write the fields to. Since we just want to store the settings in the local utility we look it up and return it.

configure.zcml:
<adapter
   for="Products.CMFPlone.Portal.PloneSite"
   provides=".interfaces.ISillyConfiguration"
   factory=".config.form_adapter" />
config.py:
from zope.component import getUtility

from interfaces import ISillyConfiguration

def form_adapter(context):
    return getUtility(ISillyConfiguration, name='silly_config', context=context)

What's left

You'll probably want to add your new configlet to the control panel by adding it to your controlpanel.xml (see CMFPlone/profiles/default/controlpanel.xml). You can define the icon by adding it to actionicons.xml.


Contribute

Something wrong or out of date? Anybody can edit or create a new article in the knowledge base. Simply create an account on this site, log in, and click the Edit button to contribute.