############################################################################## # # Copyright (c) 2005 Zope Corporation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """ Five subscriber definitions. $Id: subscribers.py 81167 2007-10-28 09:40:32Z hannosch $ """ import warnings from logging import getLogger import OFS.interfaces from Acquisition import aq_base from App.config import getConfiguration from AccessControl import getSecurityManager from ZODB.POSException import ConflictError import zope.component import zope.interface import zope.location.interfaces from zope.app.container.contained import dispatchToSublocations from zope.app.container.interfaces import IObjectMovedEvent from zope.lifecycleevent.interfaces import IObjectCopiedEvent deprecatedManageAddDeleteClasses = [] LOG = getLogger('OFS.subscribers') def compatibilityCall(method_name, *args): """Call a method if events have not been setup yet. This is the case for some unit tests that have not been converted to use the component architecture. """ if deprecatedManageAddDeleteClasses: # Events initialized, don't do compatibility call return if method_name == 'manage_afterAdd': callManageAfterAdd(*args) elif method_name == 'manage_beforeDelete': callManageBeforeDelete(*args) else: callManageAfterClone(*args) def maybeWarnDeprecated(ob, method_name): """Send a warning if a method is deprecated. """ if not deprecatedManageAddDeleteClasses: # Directives not fully loaded return for cls in deprecatedManageAddDeleteClasses: if isinstance(ob, cls): # Already deprecated through zcml return if getattr(getattr(ob, method_name), '__five_method__', False): # Method knows it's deprecated return class_ = ob.__class__ warnings.warn( "%s.%s.%s is discouraged. You should use event subscribers instead." % (class_.__module__, class_.__name__, method_name), DeprecationWarning) ################################################## class ObjectManagerSublocations(object): """Get the sublocations for an ObjectManager. """ zope.component.adapts(OFS.interfaces.IObjectManager) zope.interface.implements(zope.location.interfaces.ISublocations) def __init__(self, container): self.container = container def sublocations(self): for ob in self.container.objectValues(): yield ob # The following subscribers should really be defined in ZCML # but we don't have enough control over subscriber ordering for # that to work exactly right. # (Sometimes IItem comes before IObjectManager, sometimes after, # depending on some of Zope's classes.) # This code can be simplified when Zope is completely rid of # manage_afterAdd & co, then IItem wouldn't be relevant anymore and we # could have a simple subscriber for IObjectManager that directly calls # dispatchToSublocations. @zope.component.adapter(OFS.interfaces.IItem, OFS.interfaces.IObjectWillBeMovedEvent) def dispatchObjectWillBeMovedEvent(ob, event): """Multi-subscriber for IItem + IObjectWillBeMovedEvent. """ # First, dispatch to sublocations if OFS.interfaces.IObjectManager.providedBy(ob): dispatchToSublocations(ob, event) # Next, do the manage_beforeDelete dance callManageBeforeDelete(ob, event.object, event.oldParent) @zope.component.adapter(OFS.interfaces.IItem, IObjectMovedEvent) def dispatchObjectMovedEvent(ob, event): """Multi-subscriber for IItem + IObjectMovedEvent. """ # First, do the manage_afterAdd dance callManageAfterAdd(ob, event.object, event.newParent) # Next, dispatch to sublocations if OFS.interfaces.IObjectManager.providedBy(ob): dispatchToSublocations(ob, event) @zope.component.adapter(OFS.interfaces.IItem, OFS.interfaces.IObjectClonedEvent) def dispatchObjectClonedEvent(ob, event): """Multi-subscriber for IItem + IObjectClonedEvent. """ # First, do the manage_afterClone dance callManageAfterClone(ob, event.object) # Next, dispatch to sublocations if OFS.interfaces.IObjectManager.providedBy(ob): dispatchToSublocations(ob, event) @zope.component.adapter(OFS.interfaces.IItem, IObjectCopiedEvent) def dispatchObjectCopiedEvent(ob, event): """Multi-subscriber for IItem + IObjectCopiedEvent. """ # Dispatch to sublocations if OFS.interfaces.IObjectManager.providedBy(ob): dispatchToSublocations(ob, event) def callManageAfterAdd(ob, item, container): """Compatibility subscriber for manage_afterAdd. """ if container is None: return if getattr(aq_base(ob), 'manage_afterAdd', None) is None: return maybeWarnDeprecated(ob, 'manage_afterAdd') ob.manage_afterAdd(item, container) def callManageBeforeDelete(ob, item, container): """Compatibility subscriber for manage_beforeDelete. """ if container is None: return if getattr(aq_base(ob), 'manage_beforeDelete', None) is None: return maybeWarnDeprecated(ob, 'manage_beforeDelete') import OFS.ObjectManager # avoid circular imports try: ob.manage_beforeDelete(item, container) except OFS.ObjectManager.BeforeDeleteException: raise except ConflictError: raise except: LOG.error('_delObject() threw', exc_info=True) # In debug mode when non-Manager, let exceptions propagate. if getConfiguration().debug_mode: if not getSecurityManager().getUser().has_role('Manager'): raise def callManageAfterClone(ob, item): """Compatibility subscriber for manage_afterClone. """ if getattr(aq_base(ob), 'manage_afterClone', None) is None: return maybeWarnDeprecated(ob, 'manage_afterClone') ob.manage_afterClone(item)