A talk I gave at the European Plone Symposium 2010 in Sorrento.
Event Driven Programming in Plone - or how to extend Plone the lazy way
Ever wanted to extend Plone somehow but not wanted to change the existing code for a product? Want to learn the easy way to extend Plone's functionality?
Events!
I will show you some examples of how to use Plone's event subscriber and workflow systems to extend Plone without having to change any existing products. Using a number of common Plone products and the events system to combine them you can come up with unlimited possibilities. I'll use examples from two websites I've been working on recently to show you how you can make authors have to pay (using GetPaid) to publish content on a site, and how to check the comments are not spam on your discussion board.
Why Teams call analytics are critical to your entire business
Plone: Event Driven Programming
1. Event Driven Programming in
Plone
Matt Hamilton
...or how to extend Plone the lazy way
Matt Hamilton
Technical Director, Netsight
2. Who am I?
- Matt Hamilton
- Technical Director of
Netsight Internet Solutions
- A Plone ‘integrator’
matth@netsight.co.uk
@hammertoe
Matt Hamilton European Plone Symposium 2010, Sorrento 2
3. Who is this talk for?
- Integrators
- Those newish to Plone, they can
assemble together a site from a
number of products, but don't really
want to alter them
- I consider myself 'experienced' but this
was a bit of an epiphany for me
Matt Hamilton European Plone Symposium 2010, Sorrento 3
4. What is the problem?
?
Matt Hamilton European Plone Symposium 2010, Sorrento 4
5. What is the problem?
I want to change the functionality of an
existing product, but don't want to
change the guts of it.
Matt Hamilton European Plone Symposium 2010, Sorrento 5
6. What is the problem?
Came from a real life problem.
Developing Netsight's new website.
Wanted to use plone.app.discussion, but
needed to add spam checking on
comments
Matt Hamilton European Plone Symposium 2010, Sorrento 6
7. What is the problem?
Matt Hamilton European Plone Symposium 2010, Sorrento 7
8. What is the problem?
plone.app.discussion has captcha
support already, but I wanted to add
Akismet support, but no easy extension
point
Matt Hamilton European Plone Symposium 2010, Sorrento 8
9. Admittedly, I could have offered to
refactor the whole p.a.discussion code ;)
Matt Hamilton European Plone Symposium 2010, Sorrento 9
10. Considered Approaches
- I could subclass the product and
override it
➡ A lot of boiler-plate for small change
- I could use an adapter and adapt the
behaviour
➡ No adapter lookup where I needed it
- I could 'just hack it in the original code'
➡ Yuck! Maintainability nightmare
Matt Hamilton European Plone Symposium 2010, Sorrento 10
12. Eureka!
Use Events!
Matt Hamilton European Plone Symposium 2010, Sorrento 12
13. Eureka!
- I realised I could leave p.a.discussion
alone, and just listen for an event for
when a comment is added and then
check it for spam
- I can listen for an event from Plone and
then do the behaviour afterwards
Matt Hamilton European Plone Symposium 2010, Sorrento 13
14. Advantages
- Leave the existing code alone
- Work around lack of suitable extension
point
- Very little boilerplate code
- All happens in same transaction still
Matt Hamilton European Plone Symposium 2010, Sorrento 14
15. Zope's Event System
- Events
- Subscribers
Matt Hamilton European Plone Symposium 2010, Sorrento 15
16. A Simple Event
in configure.zcml:
<subscriber
for=".interfaces.IMyObject
.interfaces.IMyEvent"
handler=".events.myEventHandler"
/>
in events.py:
def myEventHandler( object, event):
object.doSomeThing()
Matt Hamilton European Plone Symposium 2010, Sorrento 16
17. Event Simpler Event
in events.py:
from five import grok
from interfaces import IMyObject, IMyEvent
@@grok.subscribe(IMyObject, IMyEvent)
def myEventHandler( object, event):
object.doSomeThing()
Matt Hamilton European Plone Symposium 2010, Sorrento 17
18. Example - Spam Checking
in events.py:
from five import grok
from plone.app.discussion.interfaces
import IComment
from zope.lifecycleevent.interfaces
import IObjectAddedEvent
@@grok.subscribe(IComment,
IObjectAddedEvent)
def checkForSpam( comment, event):
wf = getToolByName(comment,
'portal_workflow')
if is_spam(comment.text):
wf.doActionFor(comment, ‘spam’)
Matt Hamilton European Plone Symposium 2010, Sorrento 18
19. Next Example - GetPaid
GetPaid is an eCommerce add-on to
Plone
➡ Allows you to mark any piece of
content as ‘buyable’
Matt Hamilton European Plone Symposium 2010, Sorrento 19
20. PloneConf 2010 Registration
User clicks ‘register’
User fills in ‘add attendee’ form, and hits submit
Event fired indicating object added to container
Event subscriber marks item as buyable, adds to
shopping cart, and then redirects to cart view
Matt Hamilton European Plone Symposium 2010, Sorrento 20
21. GetPaid Example
from Products.CMFCore.utils import getToolByName
from getpaid.core.interfaces import workflow_states
import interfaces
def handlePaymentReceived( order, event ):
if event.destination !=workflow_states.order.finance.CHARGED:
return
for item in order.shopping_cart.values():
ob = item.resolve()
workflow = getToolByName( ob, 'portal_workflow')
state = workflow.getInfoFor( ob, 'review_state' )
if state == 'published':
return
workflow.doActionFor( ob, 'publish')
Matt Hamilton European Plone Symposium 2010, Sorrento 21
22. Other Ideas
- Whenever a Folder is created, add
some default content to it
- When a Page is added, set some
metadata fields
- When an Event is added, check that an
expiry date has been set no more than
12 months in the future
Matt Hamilton European Plone Symposium 2010, Sorrento 22