Skip to content

Commit d5ed69d

Browse files
Fix documentation style: ensure one sentence per line
- Split long sentences into individual lines throughout components.md - Fix Concepts section (Interface, Adapter, Events, Registries, Lookup) - Ensure all paragraphs follow one-sentence-per-line style guide - Resolves formatting issues where text appeared as paragraphs instead of lines
1 parent 90678ec commit d5ed69d

3 files changed

Lines changed: 242 additions & 36 deletions

File tree

docs/backend/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ schemas
3333
search
3434
security
3535
sending-email
36+
subscribers
3637
traversal-acquisition
3738
users-groups
3839
vocabularies

docs/backend/subscribers.md

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

0 commit comments

Comments
 (0)