24. Behaviors

In this part you will:

  • Add another field to talks by using a behavior

Topics covered:

  • Behaviors

You can extend the functionality of your dexterity object by writing an adapter that adapts your dexterity object to add another feature or aspect.

But if you want to use this adapter, you must somehow know that an object implements that. Also, adding more fields to an object would not be easy with such an approach.

24.1. Dexterity Approach

Dexterity has a solution for it, with special adapters that are called and registered by the name behavior.

A behavior can be added to any content type through the web and at runtime.

All default views (e.g. the add- and edit-forms) know about the concept of behaviors and when rendering forms, the views also check whether there are behaviors referenced with the current context and if these behaviors have a schema of their own, these fields get shown in addition.

24.2. Names and Theory

The name behavior is not a standard term in software development. But it is a good idea to think of a behavior as an aspect. You are adding an aspect to your content type and you want to write your aspect in such a way that it works independently of the content type on which the aspect is applied. You should not have dependencies to specific fields of your object or to other behaviors.

Such an object allows you to apply the Open/closed principle to your dexterity objects.

24.3. Practical example

So, let us write our own small behavior.

In the future, we want our presentation to be represented in Lanyrd (a Social Conference Directory - Lanyrd.com) too. For now we will just provide a link so that visitors can collaborate easily with the Lanyrd site.

So for now, our behavior just adds a new field for storing the url to Lanyrd.

We want to keep a clean structure, so we create a behaviors directory first, and include it into the zcml declarations of our configure.zcml.

<include package=".behaviors" />

Then, we add an empty behaviors/__init__.py and a behaviors/configure.zcml containing

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<configure
    xmlns="http://namespaces.zope.org/zope"
    xmlns:plone="http://namespaces.plone.org/plone"
    i18n_domain="ploneconf.site">

  <plone:behavior
      title="Social Behavior"
      description="Adds a link to lanyrd"
      provides=".social.ISocial"
      />

</configure>

And a behaviors/social.py containing:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# -*- coding: utf-8 -*-
from plone.autoform.interfaces import IFormFieldProvider
from plone.supermodel import directives
from plone.supermodel import model
from zope import schema
from zope.interface import alsoProvides


class ISocial(model.Schema):

    directives.fieldset(
        'social',
        label=u'Social',
        fields=('lanyrd',),
    )

    lanyrd = schema.URI(
        title=u"Lanyrd link",
        description=u"Add URL",
        required=False,
    )

alsoProvides(ISocial, IFormFieldProvider)

Let’s go through this step by step.

  1. We register a behavior in behaviors/configure.zcml. We do not say for which content type this behavior is valid. You do this through the web or in the GenericSetup profile.
  2. We create a marker interface in behaviors/social.py for our behavior and make it also a schema containing the fields we want to declare. We could just define schema fields on a zope.interface class, but we use an extended form from plone.supermodel, else we could not use the fieldset features.
  3. We also add a fieldset so that our fields are not mixed with the normal fields of the object.
  4. We add a normal URI schema field to store the URI to lanyrd.
  5. We mark our schema as a class that also implements the IFormFieldProvider interface. This is a marker interface, we do not need to implement anything to provide the interface.

24.4. Adding it to our talk

We could add this behavior now via the plone control panel. But instead, we will do it directly and properly in our GenericSetup profile

We must add the behavior to profiles/default/types/talk.xml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?xml version="1.0"?>
<object name="talk" meta_type="Dexterity FTI" i18n:domain="plone"
   xmlns:i18n="http://xml.zope.org/namespaces/i18n">
   ...
 <property name="behaviors">
  <element value="plone.app.dexterity.behaviors.metadata.IDublinCore"/>
  <element value="plone.app.content.interfaces.INameFromTitle"/>
  <element value="ploneconf.site.behaviors.social.ISocial"/>
 </property>
 ...
</object>