Personal tools
You are here: Home Documentation How-tos Adding configuration settings using Zope 3 schemas and formlib
Support

Get Help

Join our chat rooms or support forums if you have more specific questions.

Plone Training
Learn how to design, build, and deploy a website in Plone through one of the numerous Plone training sessions around the world.
Find Plone training…
 
Document Actions

Adding configuration settings using Zope 3 schemas and formlib

This How-to applies to: Plone 3.0.x, Plone 2.5.x
This How-to is intended for: Developers

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.

by Malthe Borch last modified August 26, 2007 - 01:07 All content is copyright Plone Foundation and the individual contributors.

error in call of `registerUtility`

Posted by Tom Lazar at August 25, 2007 - 23:58
Malthe, thanks for this tutorial. just what i needed. however, there's one error: you got the order of parameters wrong for `registerUtility`:

instead of `sm.registerUtility(interfaces.ISillyConfiguration, SillyConfiguration(), 'silly_config')` it needs to be `sm.registerUtility(SillyConfiguration(), ISillyConfiguration,'silly_config')`

re: error in call to registerUtility

Posted by Malthe Borch at August 26, 2007 - 01:09
I've changed it Zope 2.10 syntax and made a note for 2.9 users. Thanks!

named parameters?

Posted by Tom Lazar at August 26, 2007 - 02:58
why not just use named parameters, then the order should be of no consequence.

no can do

Posted by Malthe Borch at August 26, 2007 - 11:52
def registerUtility(component, provided=None, name=u'', info=u'') vs.
def registerUtility(interface, utility, name='')

gotcha ;-)

Posted by Tom Lazar at August 26, 2007 - 17:24
next time i'll look it up first... ;)

use plone.app.controlpanel.form.ControlPanelForm

Posted by Tom Lazar at August 26, 2007 - 18:45
If you use Products.Five.formlib.formbase.EditFormBase as base class for the view you end up with a 'vanilla' webform without any plone styles or menus. Instead base it on plone.app.controlpanel.form.ControlPanelForm (but make sure to add a `description` and a `form_name` field, as well, as they're mandatory).

you then have nice controlpanel, just like all the other ones ;)

use `category="Products"` in controlpanel.xml

Posted by Tom Lazar at August 26, 2007 - 21:06
Then your control panel won't be grouped into the default panels but into a separate 'add-on' section.

Register Utility In configure.zcml

Posted by Nathan Van Gheem at June 5, 2008 - 22:58
It is probably better to do it in the configuration zcml like so,

<utility
provides=".interfaces.ISillyConfiguration"
factory=".configlet.SillyConfiguration"
name="sillyconfig-name"
/>

Is not a local utility

Posted by Jan Murre at July 2, 2008 - 19:30
This type of configuration does not work for me. You need a local utility that can only be registered through python code.

Data Not Persisting

Posted by Nathan Van Gheem at June 12, 2008 - 22:38
For some reason with this implementation I cannot get the data to persist on product reinstalls. Is there any way around this?

It's the quickinstaller

Posted by Jan Murre at July 2, 2008 - 19:33
The quickinstaller keeps track of installed utilities. On re-install they are removed and added again.
So you lose any persistent data.

passing context is redundant for getUtility

Posted by Kapil Thangavelu at July 10, 2008 - 17:58
local site semantics determine the nearest component registry used for lookup of components. in fact passing context is arguably an error as it will fail if the context is not a site. where as it will work for any context if context is not passed by looking up the nearest component registry, else the context must implement/adapt to IComponentLookup.

For any issues with the web site functionality, please file a ticket.

Please consult the policy on plone.org content if you want your content published on this site.

Servers and hosting by