Eventi Plone: intercettarli e notificarli
un esempio di utilizzo del sistema di eventi di Zope3 (su Plone)
Plone ha acquisito da Zope3 un magnifico sistema di eventi basato sul
Gli eventi sono quindi intercettabili dall'interfaccia che adattano. Facciamo un esempio:
Mi sono ritrovato con l'esigenza di dover effettuare, su un oggetto di tipo folder, un operazione scatenata dall'aggiunta/rimozione e riordinameno degli oggetti contenuti.
Nella documentazione ho trovato l'interfaccia IContainerModifiedEvent e pensavo di aver risolto i miei problemi: infatti la documentazione riporta che viene notificato l'evento relativo a questa interfaccia proprio quando mi serve.
Ho quindi sottoscritto l'evento (ho candidato la mia funzione ad essere eseguita in corrispondenza di quell'evento): (configure.zcml)
<subscriber handler=".product_reindex.update_image" />
La funzione che ho definito è questa:
from zope.app.container.interfaces import IContainerModifiedEvent
from redomino.recatalog.interfaces import IProductForm
from zope.component import adapter
from zope.lifecycleevent.interfaces import IObjectModifiedEvent
@adapter(IProductForm, IContainerModifiedEvent)
def update_image(context, event):
"""this function reindex an object to reload the first image in metadata"""
context.reindexObject()Come potete vedere viene eseguita solo per gli oggetti di tipo IProductForm che vengono reinidicizzati quando viene modificato il loro contenuto.
In seguito ho provato la mia modifica e .... SORPRESA !!!! funziona per aggiunte e rimozioni ma non per riordinamento dei contenuti.
Dopo un po' di debugging ho scoperto che, in caso di riordinamento, Plone non emette l'evento (contrariamente a quanto documentato).
Cercando nel codice ho scoperto che lo spostamento di oggetti in un folder avviene richiamando il metodo moveObjectsByDelta della classe mix-in OrderedContainer. Ricordo che una classe mix-in è una classe che viene applicata attraverso l'ereditarietà multipla e che aggiunge dei metodi ad un altra classe.
Come soluzione ho quindi dovuto reimplementare questo metodo nella classe ProductForm
from zope.app.container.contained import notifyContainerModified
def moveObjectsByDelta(self, ids, delta, subset_ids=None):
out = super(ProductForm, self).moveObjectsByDelta(ids, delta, subset_ids)
notifyContainerModified(self)
return out
Con notifyContainerObject ho forzato la notifica dell'evento che mi serviva e super mi ha permesso di richiamare la funzione originaria.
Vi lascio con un paio di avvisi:
1 - gli eventi sono sincroni (non aprono thread o processi)
2 - il metodo notifyContainerModified è un metodo di comodo per emettere l'evento IContainerModifiedEvent usando la funzione standard notify
