Adding custom event handlers for your type
So far, we have mainly been concerned with content types’ schemata and forms created from these. However, we often want to add more dynamic functionality, reacting when something happens to objects of our type. In Zope, that usually means writing event subscribers.
Zope’s event model is synchronous. When an event is
broadcast (via the
function from the
package), for example from the
action of an add form, all registered event handlers will be
called. There is no guarantee of which order the event
handlers will be called in, however.
Each event is described by an interface, and will typically
carry some information about the event. Some events are
known as object events, and provide
zope.component.interfaces.IObjectEvent. These have an
attribute giving access to the (content) object that the
event relates to. Object events allow event handlers to be
registered for a specific type of object as well as a
specific type of event.
Some of the most commonly used event types in Plone are shown below. They are all object events.
fired by the standard add form just after an object has
been created, but before it has been added on the
container. Note that it is often easier to write a handler
IObjectAddedEvent(see below), because at this point the object has a proper acquisition context.
- fired by the standard edit form when an object has been modified.
fired when an object has been added to its container. The
container is available as the
newParentattribute, and the name the new item holds in the container is available as
fired when an object has been removed from its container.
The container is available as the
oldParentattribute, and the name the item held in the container is available as
fired when an object is added to, removed from, renamed
in, or moved between containers. This event is a
IObjectRemovedEvent, shown above, so an event handler registered for this interface will be invoked for the ‘added’ and ‘removed’ cases as well. When an object is moved or renamed, all of
newNamewill be set.
fired when a workflow event has completed. The
workflowattribute holds the workflow instance involved, and the
actionattribute holds the action (transition) invoked.
Event handlers can be registered using ZCML with the
directive, but when working with Dexterity types, we’ll more
commonly use the
in Python code.
As an example, let’s add an event handler to the
type that tries to find users with matching names matching
the presenter id, and send these users an email.
First, we require a few additional imports at the top of
from zope.lifecycleevent.interfaces import IObjectAddedEvent from Products.CMFCore.utils import getToolByName
Then, we’ll add the following event subscriber after the schema definition:
@grok.subscribe(IPresenter, IObjectAddedEvent) def notifyUser(presenter, event): acl_users = getToolByName(presenter, 'acl_users') mail_host = getToolByName(presenter, 'MailHost') portal_url = getToolByName(presenter, 'portal_url') portal = portal_url.getPortalObject() sender = portal.getProperty('email_from_address') if not sender: return subject = "Is this you?" message = "A presenter called %s was added here %s" % (presenter.title, presenter.absolute_url(),) matching_users = acl_users.searchUsers(fullname=presenter.title) for user_info in matching_users: email = user_info.get('email', None) if email is not None: mail_host.secureSend(message, email, sender, subject)
There are many ways to improve this rather simplistic event
handler, but it illustrates how events can be used. The
first argument to
is an interface describing the object type. For non-object
events, this is omitted. The second argument is the event
type. The arguments to the function reflects these two, so
the first argument is the
instance and the second is an