Skip to content

Commit 379ef2e

Browse files
Migrate Subscribers (event handlers) (#1918)
* pick migrate docs * Enhance introduction with term definitions from https://zopeevent.readthedocs.io/en/latest/usage.html * - Clean up grammar - Enhance with MyST syntax `{file}` and `{lineno-start=1}` * remove event handlers --------- Co-authored-by: Steve Piercy <[email protected]>
1 parent 68e85be commit 379ef2e

File tree

1 file changed

+158
-4
lines changed

1 file changed

+158
-4
lines changed

docs/backend/subscribers.md

+158-4
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,162 @@ myst:
1111

1212
# Subscribers (event handlers)
1313

14-
```{todo}
15-
Move content from:
16-
- https://5.docs.plone.org/external/plone.app.dexterity/docs/advanced/event-handlers.html
17-
- https://5.docs.plone.org/develop/addons/components/events.html
14+
A _subscriber_ is a callable object that takes one argument, an object that we call the _event_.
15+
16+
_Events_ are objects that represent something happening in a system.
17+
They are used to extend processing by providing processing plug points.
18+
19+
A _notification_ alerts subscribers that an event has occurred.
20+
21+
The {term}`Zope Component Architecture`'s [`zope.event`](https://zopeevent.readthedocs.io/en/latest/) package is used to manage subscribable events in Plone.
22+
23+
The Plone event system has some notable characteristics:
24+
25+
- It's simple.
26+
- The calling order of subscribers is random.
27+
You can't set the order in which event handlers are called.
28+
- Events can't be cancelled.
29+
All handlers will always get the event.
30+
- Event handlers can't have return values.
31+
- Exceptions raised in an event handler will interrupt the request processing.
32+
33+
34+
## Register an event handler
35+
36+
Plone events can be scoped:
37+
38+
- globally (no scope)
39+
- per content type
40+
- per behavior or marker interface
41+
42+
43+
### Register an event handler on content type creation
44+
45+
The following example demonstrates how to register an event handler when a content type is created.
46+
47+
In your {file}`.product/your/product/configure.zcml` insert the following code.
48+
49+
{lineno-start=1}
50+
```xml
51+
<subscriber
52+
for=".interfaces.IMyContentTypeClass
53+
zope.lifecycleevent.IObjectCreatedEvent"
54+
handler=".your_python_file.your_method"
55+
/>
56+
```
57+
58+
The second line defines to which interface you want to bind the execution of your code.
59+
Here, the event handler code will only be executed if the object is a content type providing the interface `.interfaces.IMyContentTypeClass`.
60+
If you want this to be interface agnostic, insert an asterix `*` as a wildcard instead.
61+
62+
The third line defines the event on which this should happen, which is `IObjectCreatedEvent`.
63+
For more available possible events to use as a trigger, see {ref}`subscribers-event-handlers`.
64+
65+
The fourth line gives the path to the callable function to be executed.
66+
67+
Create your {file}`.product/your/product/your_python_file.py` and insert the following code.
68+
69+
```python
70+
def your_subscriber(object, event):
71+
# do something with your created content type
72+
```
73+
74+
75+
### Subscribe to an event using ZCML
76+
77+
Subscribe to a global event using {term}`ZCML` by inserting the following code in your {file}`.product/your/product/configure.zcml`.
78+
79+
```xml
80+
<subscriber
81+
for="Products.PlonePAS.events.UserLoggedOutEvent"
82+
handler=".smartcard.clear_extra_cookies_on_logout"
83+
/>
84+
```
85+
86+
For this event, the Python code in {file}`smartcard.py` would be the following.
87+
88+
```python
89+
def clear_extra_cookies_on_logout(event):
90+
# What event contains depends on the
91+
# triggerer of the event and event class
92+
request = event.object.REQUEST
93+
```
94+
95+
The following example for a custom event subscribes content types to all `IMyEvents` when fired by `IMyObject`.
96+
97+
```xml
98+
<subscriber
99+
for=".interfaces.IMyObject
100+
.interfaces.IMyEvent"
101+
handler=".content.MyObject.myEventHandler"
102+
/>
18103
```
104+
105+
The following example shows how to subscribe a content type to the life cycle event.
106+
107+
```xml
108+
<subscriber
109+
zcml:condition="installed zope.lifecycleevent"
110+
for=".interfaces.ISitsPatient
111+
zope.lifecycleevent.IObjectModifiedEvent"
112+
handler=".content.SitsPatient.objectModified"
113+
/>
114+
```
115+
116+
117+
## Fire an event
118+
119+
Use `zope.event.notify()` to fire event objects to their subscribers.
120+
121+
The following code shows how to fire an event in unit tests.
122+
123+
```python
124+
import zope.event
125+
from plone.postpublicationhook.event import AfterPublicationEvent
126+
127+
event = AfterPublicationEvent(self.portal, self.portal.REQUEST)
128+
zope.event.notify(event)
129+
```
130+
131+
132+
(subscribers-event-types-label)=
133+
134+
## Event types
135+
136+
Plone has the following types of events.
137+
138+
139+
### Creation events
140+
141+
`zope.lifecycleevent.IObjectCreatedEvent` is fired for all Zope-ish objects when they are created, or copied via `IObjectCopiedEvent`.
142+
They don't have to be content objects.
143+
144+
### Modified events
145+
146+
`zope.lifecycleevent.IObjectModifiedEvent` is called for creation stage events as well, unlike the previous event type.
147+
148+
### Delete events
149+
150+
Delete events can be fired several times for the same object.
151+
Some delete event transactions are rolled back.
152+
153+
### Copy events
154+
155+
`zope.lifecycleevent.IObjectCopiedEvent` is triggered when an object is copied.
156+
It will also fire `IObjectCreatedEvent` event code.
157+
158+
### Workflow events
159+
160+
`Products.DCWorkflow.interfaces.IBeforeTransitionEvent` is triggered before a workflow transition is executed.
161+
162+
`Products.DCWorkflow.interfaces.IAfterTransitionEvent` is triggered after a workflow transition has been executed.
163+
164+
The DCWorkflow events are low-level events that can tell you a lot about the previous and current states.
165+
166+
`Products.CMFCore.interfaces.IActionSucceededEvent` is a higher level event that is more commonly used to react after a workflow action has completed.
167+
168+
### Zope startup events
169+
170+
`zope.processlifetime.IProcessStarting` is triggered after the component registry has been loaded and Zope is starting up.
171+
172+
`zope.processlifetime.IDatabaseOpened` is triggered after the main ZODB database has been opened.

0 commit comments

Comments
 (0)