Schema-extending Dexterity content types with five.grok and plone.behavior
This is just a quick blog post about using plone.behavior to change the schema of Plone's Dexterity content types. Hopefully it'll help people familiar the Archetypes' schema-extending paradigm, to achieve the same effect with Dexterity types.
Of course, Dexterity is not Archetypes, and plone.behaviors are not schema-extenders (well, not only), but sometimes you want to do the same thing, whether you're using Archetypes or Dexterity, and that is extend the schema of an existing content type created in a different product (that you don't want to or cannot edit) in your own product.
Schema-extending in Archetypes also happened through adapters, but they were not checked against the type's FTI, and were thus not conditional.
Suppose we have a Sector type in another package, and that we want to add another field statistics_level. We don't want to modify this other package, so we'll use plone.behavior to "extend its schema".
In our own package, we create sector.py and add the following code:
from zope import schemafrom plone.directives import dexterity from plone.directives import formfrom plone.autoform.interfaces import IFormFieldProviderfrom plone.app.dexterity.behaviors.metadata import MetadataBasefrom plone.app.dexterity.behaviors.metadata import DCFieldPropertyclass IExtendedSector(form.Schema):""" """statistics_level = schema.Choice(title = _("label_statistics_level", default=u"Statistics Level"),description = _("help_statistics_level",default=u"Level 1: Basic statistics. Level 2: More detailed statistics."),required=True,vocabulary = SimpleVocabulary([SimpleTerm(1, title=u"1"),SimpleTerm(2, title=u"2"),]),default=1,)
The interface IExtendedSector defines the new field statistics_level that we want to add.
We then mark IExtendedSector with IFormFieldProvider to signal that it should be processed for form fields.
class ExtendedSector(MetadataBase):statistics_level = DCFieldProperty(IExtendedSector['statistics_level'])
Next we need a class that implements the MetadataBase behavior adapter and which will act as the adapter factory.
Now we need to wire this adapter in with ZCML:
<plone:behavior title="Extended Sector" description="Provide additional statistics_level field" provides=".sector.IExtendedSector" factory=".sector.ExtendedSector" for="plone.dexterity.interfaces.IDexterityContent" />
Lastly we need the portal_types FTI (Factory Type Information) to be updated with our behavior. Go the the ZMI, then portal_types and then click on the Dexterity type you want to extend. You will see that there is a lines field called Behaviors.
Add the interface of your new extender here, i.e collective.myproduct.sector.IExtendedSector
That's basically it!
You probably want to add the behavior interface to a GenericSetup XML file that defines the type you are extending, to avoid having to manually add it to portal_types every time you create a new site (it's also very easy to forget about this step).
Something like this in your type's GS-XML file:
<!-- List of enabled behaviors --> <property name="behaviors"> <element value="plone.app.content.interfaces.INameFromTitle" /> <element value="collective.myproduct.sector.IExtendedSector" /> </property>
Oh, and lastly... you're going to need five.grok for this. Your package needs to be grokked, otherwise the plone.directives.form form hints won't be activated.
In your configure.zcml:
<grok:grok package="." />
You can do a lot more than just the above with Dexterity and plone.behavior, take a look at Martin's documentation for more info.