You are here: Home / What do you need? / Help and documentation / Plone Info / dynamic vocabularies, control panel configlets and

dynamic vocabularies, control panel configlets and

by Darrell Kingsley last modified Mar 13, 2014 01:00 PM
Describes how to set up a configlet in site settings to allow managers to maintain persistent configuration data for products. Specific case deals with maintaining lists of values to provide dynamic vocabularies for selection drop-down (Choice) fields in the Member data schema. provides ui integration for plone.registry which in turn provides the local utility (per site) data store and interfaces. They are core in plone 4. NB this only appears to be true for a root zeocluster install, and not for a rootless standalone install. I don't know whether it is the root, or the zeo which is important, or both.

This documentation is based on

However this casued a ComponentLookupError at startup when getting the registry utility similar to the one described at - the reason for this is explained better Essentially as the registry is a local utility the python startup code to create schemas for products does not have the context of the registry as it is local to the plone site and is not available at top-level Zope. This would not be a problem if we are just accessing configuration data in standard product scripts but is an issue for start-up glue (in my case The solution is mentioned in the first link - use grok (or five.grok for Plone 4.0.5 and Zope.2.12) to provide a special interface IContextSourceBinder which is documented This interface allows the context of the form to be passed to the registry access code which is hidden in a method at run-time. Once this was done it just worked. Other Zope-based frameworks use it to. For example in the Silva CMS documentation on Zope Schema fields

To create the registry and control panel form:

A walk through ignoring some bits that just weren't needed as we already had and plone.registry available. But see note above about zeo/root install.

1) Define the fields that make up the control panel form by creating a zope schema interface in our products

class IEnhancedUserDataSettings(interface.Interface):
    """ Global settings. Definition of plone.registry fields to be maintained in Control Panel.
    profession_vocabulary = schema.List(title=u"Professions drop-down",

2) Set up the control panel form and the view it uses by using the same interface in

from bb.nhs.userdata.browser.interfaces import IEnhancedUserDataSettings
from import controlpanel
class EnhancedUserDataSettingsEditForm(controlpanel.RegistryEditForm):
    schema = IEnhancedUserDataSettings
    label = u"Enhanced User Data settings"
    description = u"Drop-down field values"
    def updateFields(self):
        super(EnhancedUserDataSettingsEditForm, self).updateFields()
    def updateWidgets(self):
        super(EnhancedUserDataSettingsEditForm, self).updateWidgets()
class EnhancedUserDataSettingsControlPanel(controlpanel.ControlPanelFormWrapper):
    form = EnhancedUserDataSettingsEditForm

3) Register this control panel view in the configure.zcml slug...

    <!-- Control panel -->

4) Define a configlet to use this view in yout products control panel using generic setup - profiles/default/controlpanel.xml...

<?xml version="1.0"?>
        title="NHS Userdata"
            <permission>Manage portal</permission>

5) Tell generic setup to use in profiles/default/metadata.xml...

<?xml version="1.0"?>

6) Now define the registry store itself in profiles/default/registry.xml. Again this can be fast-tracked by using the schema interface...

<?xml version="1.0"?>
 <records interface="bb.nhs.userdata.browser.interfaces.IEnhancedUserDataSettings" />

You should now have a working control panel configlet usable by managers.


More about five.grok

Probably only need to do this if using registry in product start-up code to avoid the ComponentLookupError described above, though most likely it'll have other uses too.

As we wanted to access the registry to provide vocabularies at run-time for drop-downs in (and avoid the ComponentLookupError) five.grok was installed using the pinned versions detailed at for five.grok 1.2.0 which is for Zope 2.12. 

1) Install five.grok

In buildout.cfg you'll need...

eggs = 

which wasn't mentioned in instructions and to be safe also added the version pinning too...

# grok framework
# five.grok = 1.2.0
grokcore.annotation = 1.2
grokcore.component = 1.8
grokcore.formlib = 1.5 = 1.4 = 1.2
grokcore.view = 1.12.2
grokcore.viewlet = 1.4.1
five.localsitemanager = 2.0.3
martian = 0.11.2

2) Now we can make a one-off source using a grok context source binder to do its magic in Firstly by defining a method to return the vocabulary that uses the context of the form by way of this special IContextSourceBinder interface... 

from five import grok
from zope.component import queryUtility
from zope.schema.interfaces import IContextSourceBinder
from zope.schema.vocabulary import SimpleVocabulary
from plone.registry.interfaces import IRegistry
from bb.nhs.userdata.browser.interfaces import IEnhancedUserDataSettings
def availableProfessions(context):
    registry = queryUtility(IRegistry)"registry: %s" % registry)
    terms = []
    if registry is not None:
        settings = registry.forInterface(IEnhancedUserDataSettings)"settings: %s" % settings)"profession_vocabulary: %s" % settings.profession_vocabulary)
        for profession in settings.profession_vocabulary:
 #           # create a term - the arguments are the value, the token, and
  #          # the title (optional)
            terms.append(SimpleVocabulary.createTerm(profession, profession.encode('utf-8'), profession))
    return SimpleVocabulary(terms)

and further on using this vocabulary in the schema...

   profession = schema.Choice(source=availableProfessions,

The documentation (link below) shows the use of schema.Set() wrapping shema.Choice() but couldn't get this to work (error on writing data) and schema.Choice worked. Maybe its for the parameterised and named versions below. Not checked this out yet. describes this in more detail as well as using parameterised sources (one method for multiple vocabularies whereas the one-off source described requires a method for each vocabulary stored in the registry) and named vocabularies for using distributed components. 

Accessing the registry

In the form used to select the values from the registry we access an external method so

<select name="profession" id="select_profession">
      <option value="">
         None specified
      <tal:block tal:repeat="profession context/getProfessions">
         <option value="None"
           tal:attributes="value profession"
              profession value


def getProfessions(self):
    from zope.component import getUtility
    from plone.registry.interfaces import IRegistry
    registry = getUtility(IRegistry)
    from bb.nhs.userdata.browser.interfaces import IEnhancedUserDataSettings
    settings = registry.forInterface(IEnhancedUserDataSettings)
#    self.plone_log('Hello from me!')

Logging for Debugging

context.plone_log may well not work for component architecture scripts so logging may need setting up...

from logging import getLogger
log = getLogger('MARKER ')
..."message: %s" % variable)
Search the event.log(s) for 'MARKER' and bob's...