[z3-checkins] r19300 - in z3/Five/trunk: . tests

efge at codespeak.net efge at codespeak.net
Mon Oct 31 20:59:07 CET 2005


Author: efge
Date: Mon Oct 31 20:59:07 2005
New Revision: 19300

Added:
   z3/Five/trunk/subscribers.py   (contents, props changed)
Modified:
   z3/Five/trunk/CHANGES.txt
   z3/Five/trunk/TODO.txt
   z3/Five/trunk/configure.zcml
   z3/Five/trunk/event.py
   z3/Five/trunk/event.zcml
   z3/Five/trunk/eventconfigure.py
   z3/Five/trunk/tests/event.txt
Log:
Moved all event monkey-patches to Zope itself (in Zope
philikon-zope32-integration branch at the time of this writing, soon in
Zope trunk and Zope 2.9).

Moved subscribers to a separate file to mitigate circular import
problems. This change will have to be backported to Five 1.2 so that
subscriber paths are the same in 1.2 and 1.3.



Modified: z3/Five/trunk/CHANGES.txt
==============================================================================
--- z3/Five/trunk/CHANGES.txt	(original)
+++ z3/Five/trunk/CHANGES.txt	Mon Oct 31 20:59:07 2005
@@ -24,6 +24,8 @@
   renamed to ``SimpleLocalUtilityRegistry``.  The old names are still
   available for a short period of time.
 
+* Event support: ``<five:containerEvents/>`` is the default.
+
 * XXX ObjectWidget (to be used instead of the Zope 3.2 one)
 
 * XXX mention interface BBB? Let yuppie decide
@@ -50,8 +52,13 @@
   to convert it's portal tools into local utilities.  See
   doc/localsite.txt for more information
 
-* XXX Florent's event stuff (better put this under restructuring?
-  Florent decides.)
+* Event support: When ``<five:containerEvents/>`` is specified, Five
+  makes the standard Zope 2 containers send events instead of using
+  manage_afterAdd, manage_beforeDelete and manage_afterClone. These
+  methods are still called for a class declared
+  ``<five:deprecatedManageAddDelete class=.../>``, and are called in
+  compatibility mode with a deprecation warning for classes that don't
+  use this directive.
 
 Restructuring
 -------------

Modified: z3/Five/trunk/TODO.txt
==============================================================================
--- z3/Five/trunk/TODO.txt	(original)
+++ z3/Five/trunk/TODO.txt	Mon Oct 31 20:59:07 2005
@@ -48,6 +48,9 @@
 
 * revisit the test_localsite/test_{get|query}NextSiteManager tests
 
+* correctly treat ZCatalog.Catalog(Path)Awareness.CatalogAware w.r.t
+  events (efge)
+
 v1.4
 ----
 

Modified: z3/Five/trunk/configure.zcml
==============================================================================
--- z3/Five/trunk/configure.zcml	(original)
+++ z3/Five/trunk/configure.zcml	Mon Oct 31 20:59:07 2005
@@ -4,9 +4,7 @@
   <include file="meta.zcml" />
   <include file="permissions.zcml" />
   <include file="i18n.zcml" />
-  <!-- XXX in Five 1.3, include this -->
-  <!-- <include file="event.zcml"/> -->
-  <!-- XXX in Five 1.3, fix these deprecations in core Zope instead -->
+  <include file="event.zcml"/>
   <include file="deprecated.zcml"/>
   <include package=".site" />
   <include package=".browser" />

Modified: z3/Five/trunk/event.py
==============================================================================
--- z3/Five/trunk/event.py	(original)
+++ z3/Five/trunk/event.py	Mon Oct 31 20:59:07 2005
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
+# Copyright (c) 2005 Zope Corporation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
@@ -14,29 +14,15 @@
 """
 Five event definitions.
 
-May eventually be folded back into Zope 3 proper.
-
 $Id$
 """
 
 import warnings
 
-from zope.event import notify
 from zope.interface import implements
 from zope.interface import Attribute
-
 from zope.app.event.interfaces import IObjectEvent
-from zope.app.container.interfaces import IObjectAddedEvent
-from zope.app.container.interfaces import IObjectRemovedEvent
-
 from zope.app.event.objectevent import ObjectEvent
-from zope.app.container.contained import ObjectMovedEvent
-from zope.app.container.contained import ObjectAddedEvent
-from zope.app.container.contained import ObjectRemovedEvent
-from zope.app.event.objectevent import ObjectCopiedEvent
-from zope.app.container.contained import dispatchToSublocations
-
-from Products.Five.fiveconfigure import isFiveMethod
 
 
 class IObjectWillBeMovedEvent(IObjectEvent):
@@ -102,718 +88,3 @@
 class FiveObjectClonedEvent(ObjectEvent):
     implements(IFiveObjectClonedEvent)
 
-
-##################################################
-
-import sys
-from cgi import escape
-from zLOG import LOG, ERROR
-from Acquisition import aq_base, aq_parent, aq_inner
-from App.config import getConfiguration
-from App.Dialogs import MessageDialog
-from AccessControl import getSecurityManager
-from ZODB.POSException import ConflictError
-from OFS.ObjectManager import BeforeDeleteException
-from OFS import Moniker
-from OFS.CopySupport import CopyError # Yuck, a string exception
-from OFS.CopySupport import eNoData, eNotFound, eInvalid, eNotSupported
-from OFS.CopySupport import cookie_path, sanity_check, _cb_decode
-from webdav.Lockable import ResourceLockedError
-FIVE_ORIGINAL_PREFIX = '__five_original_'
-
-
-hasContainerEvents = False
-deprecatedManageAddDeleteClasses = []
-
-
-def hasDeprecatedMethods(ob):
-    """Do we need to call the deprecated methods?
-    """
-    for class_ in deprecatedManageAddDeleteClasses:
-        if isinstance(ob, class_):
-            return True
-    return False
-
-def maybeCallDeprecated(method_name, ob, *args):
-    """Call a deprecated method, if the framework doesn't call it already.
-    """
-    if hasDeprecatedMethods(ob):
-        # Already deprecated through zcml
-        return
-    method = getattr(ob, method_name)
-    if isFiveMethod(method):
-        # Monkey-patched method
-        return
-    if deprecatedManageAddDeleteClasses:
-        # Not deprecated through zcml and directives fully loaded
-        class_ = ob.__class__
-        warnings.warn(
-            "Calling %s.%s.%s is deprecated when using Five, "
-            "instead use event subscribers or "
-            "mark the class with <five:deprecatedManageAddDelete/>"
-            % (class_.__module__, class_.__name__, method_name),
-            DeprecationWarning)
-    # Note that calling the method can lead to incorrect behavior
-    # but in the most common case that's better than not calling it.
-    method(ob, *args)
-
-
-##################################################
-# Adapters and subscribers
-
-from OFS.interfaces import IObjectManager
-from zope.app.location.interfaces import ISublocations
-
-class ObjectManagerSublocations(object):
-    """Get the sublocations for an ObjectManager.
-    """
-    #__used_for__ = IObjectManager
-    #implements(ISublocations)
-
-    def __init__(self, container):
-        self.container = container
-
-    def sublocations(self):
-        for ob in self.container.objectValues():
-            yield ob
-        # XXX also want to provide opaqueItems's values
-
-# 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.
-
-def dispatchObjectWillBeMovedEvent(ob, event):
-    """Multi-subscriber for IItem + IObjectWillBeMovedEvent.
-    """
-    # First, dispatch to sublocations
-    if IObjectManager.providedBy(ob):
-        dispatchToSublocations(ob, event)
-    # Next, do the manage_beforeDelete dance
-    if hasDeprecatedMethods(ob):
-        callManageBeforeDelete(ob, event)
-
-def dispatchObjectMovedEvent(ob, event):
-    """Multi-subscriber for IItem + IObjectMovedEvent.
-    """
-    # First, do the manage_afterAdd dance
-    if hasDeprecatedMethods(ob):
-        callManageAfterAdd(ob, event)
-    # Next, dispatch to sublocations
-    if IObjectManager.providedBy(ob):
-        dispatchToSublocations(ob, event)
-
-def dispatchFiveObjectClonedEvent(ob, event):
-    """Multi-subscriber for IItem + IFiveObjectClonedEvent.
-    """
-    # First, do the manage_afterClone dance
-    if hasDeprecatedMethods(ob):
-        callManageAfterClone(ob, event)
-    # Next, dispatch to sublocations
-    if IObjectManager.providedBy(ob):
-        dispatchToSublocations(ob, event)
-
-
-def callManageAfterAdd(ob, event):
-    """Compatibility subscriber for manage_afterAdd.
-    """
-    container = event.newParent
-    if container is None:
-        # this is a remove
-        return
-    ob.manage_afterAdd(event.object, container)
-
-def callManageBeforeDelete(ob, event):
-    """Compatibility subscriber for manage_beforeDelete.
-    """
-    container = event.oldParent
-    if container is None:
-        # this is an add
-        return
-    try:
-        ob.manage_beforeDelete(event.object, container)
-    except BeforeDeleteException:
-        raise
-    except ConflictError:
-        raise
-    except:
-        LOG('Zope', ERROR, '_delObject() threw', error=sys.exc_info())
-        # In debug mode when non-Manager, let exceptions propagate.
-        if getConfiguration().debug_mode:
-            if not getSecurityManager().getUser().has_role('Manager'):
-                raise
-
-def callManageAfterClone(ob, event):
-    """Compatibility subscriber for manage_afterClone.
-    """
-    ob.manage_afterClone(event.object)
-
-
-##################################################
-# Monkey patches
-
-_marker = object()
-
-# From ObjectManager / Item
-def manage_afterAdd(self, item, container):
-    # Don't do recursion anymore, a subscriber does that.
-    pass
-
-# From ObjectManager / Item
-def manage_beforeDelete(self, item, container):
-    # Don't do recursion anymore, a subscriber does that.
-    pass
-
-# From ObjectManager / Item
-def manage_afterClone(self, item):
-    # Don't do recursion anymore, a subscriber does that.
-    pass
-
-# From CatalogAware / CatalogPathAware
-def CA_manage_afterAdd(self, item, container):
-    # Don't do recursion anymore, a subscriber does that.
-    self.index_object()
-
-# From CatalogAware / CatalogPathAware
-def CA_manage_beforeDelete(self, item, container):
-    # Don't do recursion anymore, a subscriber does that.
-    self.unindex_object()
-
-# From CatalogAware / CatalogPathAware
-def CA_manage_afterClone(self, item):
-    # Don't do recursion anymore, a subscriber does that.
-    self.index_object()
-
-
-# From ObjectManager
-def _setObject(self, id, object, roles=None, user=None, set_owner=1,
-               suppress_events=False):
-    """Set an object into this container.
-
-    Also sends IObjectAddedEvent.
-    """
-    ob = object # better name, keep original function signature
-    v = self._checkId(id)
-    if v is not None:
-        id = v
-    t = getattr(ob, 'meta_type', None)
-
-    # If an object by the given id already exists, remove it.
-    for object_info in self._objects:
-        if object_info['id'] == id:
-            self._delObject(id)
-            break
-
-    if not suppress_events:
-        notify(ObjectWillBeAddedEvent(ob, self, id))
-
-    self._objects = self._objects + ({'id': id, 'meta_type': t},)
-    self._setOb(id, ob)
-    ob = self._getOb(id)
-
-    if set_owner:
-        # TODO: eventify manage_fixupOwnershipAfterAdd
-        # This will be called for a copy/clone, or a normal _setObject.
-        ob.manage_fixupOwnershipAfterAdd()
-
-    if set_owner:
-        # Try to give user the local role "Owner", but only if
-        # no local roles have been set on the object yet.
-        if getattr(ob, '__ac_local_roles__', _marker) is None:
-            user = getSecurityManager().getUser()
-            if user is not None:
-                userid = user.getId()
-                if userid is not None:
-                    ob.manage_setLocalRoles(userid, ['Owner'])
-
-    if not suppress_events:
-        notify(ObjectAddedEvent(ob, self, id))
-
-    maybeCallDeprecated('manage_afterAdd', ob, self)
-
-    return id
-
-
-# From BTreeFolder2
-def BT_setObject(self, id, object, roles=None, user=None, set_owner=1,
-                 suppress_events=False):
-    ob = object # better name, keep original function signature
-    v = self._checkId(id)
-    if v is not None:
-        id = v
-
-    # If an object by the given id already exists, remove it.
-    if self.has_key(id):
-        self._delObject(id)
-
-    if not suppress_events:
-        notify(ObjectWillBeAddedEvent(ob, self, id))
-
-    self._setOb(id, ob)
-    ob = self._getOb(id)
-
-    if set_owner:
-        # TODO: eventify manage_fixupOwnershipAfterAdd
-        # This will be called for a copy/clone, or a normal _setObject.
-        ob.manage_fixupOwnershipAfterAdd()
-
-    if set_owner:
-        # Try to give user the local role "Owner", but only if
-        # no local roles have been set on the object yet.
-        if getattr(ob, '__ac_local_roles__', _marker) is None:
-            user = getSecurityManager().getUser()
-            if user is not None:
-                userid = user.getId()
-                if userid is not None:
-                    ob.manage_setLocalRoles(userid, ['Owner'])
-
-    if not suppress_events:
-        notify(ObjectAddedEvent(ob, self, id))
-
-    maybeCallDeprecated('manage_afterAdd', ob, self)
-
-    return id
-
-
-# From ObjectManager
-def _delObject(self, id, dp=1, suppress_events=False):
-    """Delete an object from this container.
-
-    Also sends IObjectRemovedEvent.
-    """
-    ob = self._getOb(id)
-
-    maybeCallDeprecated('manage_beforeDelete', ob, self)
-
-    if not suppress_events:
-        notify(ObjectWillBeRemovedEvent(ob, self, id))
-
-    self._objects = tuple([i for i in self._objects
-                           if i['id'] != id])
-    self._delOb(id)
-
-    # Indicate to the object that it has been deleted. This is
-    # necessary for object DB mount points. Note that we have to
-    # tolerate failure here because the object being deleted could
-    # be a Broken object, and it is not possible to set attributes
-    # on Broken objects.
-    try:
-        ob._v__object_deleted__ = 1
-    except:
-        pass
-
-    if not suppress_events:
-        notify(ObjectRemovedEvent(ob, self, id))
-
-
-# From BTreeFolder2
-def BT_delObject(self, id, dp=1, suppress_events=False):
-    ob = self._getOb(id)
-
-    maybeCallDeprecated('manage_beforeDelete', ob, self)
-
-    if not suppress_events:
-        notify(ObjectWillBeRemovedEvent(ob, self, id))
-
-    self._delOb(id)
-
-    if not suppress_events:
-        notify(ObjectRemovedEvent(ob, self, id))
-
-# From CopyContainer
-def manage_renameObject(self, id, new_id, REQUEST=None):
-    """Rename a particular sub-object.
-    """
-    try:
-        self._checkId(new_id)
-    except:
-        raise CopyError, MessageDialog(
-            title='Invalid Id',
-            message=sys.exc_info()[1],
-            action ='manage_main')
-
-    ob = self._getOb(id)
-
-    if ob.wl_isLocked():
-        raise ResourceLockedError, ('Object "%s" is locked via WebDAV'
-                                    % ob.getId())
-    if not ob.cb_isMoveable():
-        raise CopyError, eNotSupported % escape(id)
-    self._verifyObjectPaste(ob)
-
-    try:
-        ob._notifyOfCopyTo(self, op=1)
-    except ConflictError:
-        raise
-    except:
-        raise CopyError, MessageDialog(
-            title="Rename Error",
-            message=sys.exc_info()[1],
-            action ='manage_main')
-
-    notify(ObjectWillBeMovedEvent(ob, self, id, self, new_id))
-
-    self._delObject(id, suppress_events=True)
-    ob = aq_base(ob)
-    ob._setId(new_id)
-
-    # Note - because a rename always keeps the same context, we
-    # can just leave the ownership info unchanged.
-    self._setObject(new_id, ob, set_owner=0, suppress_events=True)
-    ob = self._getOb(new_id)
-
-    notify(ObjectMovedEvent(ob, self, id, self, new_id))
-
-    ob._postCopy(self, op=1)
-
-    if REQUEST is not None:
-        return self.manage_main(self, REQUEST, update_menu=1)
-    return None
-
-
-# From CopyContainer
-def manage_pasteObjects(self, cb_copy_data=None, REQUEST=None):
-    """Paste previously copied objects into the current object.
-
-    If calling manage_pasteObjects from python code, pass the result of a
-    previous call to manage_cutObjects or manage_copyObjects as the first
-    argument.
-
-    Also sends IObjectCopiedEvent or IObjectMovedEvent.
-    """
-    if cb_copy_data is not None:
-        cp = cb_copy_data
-    elif REQUEST is not None and REQUEST.has_key('__cp'):
-        cp = REQUEST['__cp']
-    else:
-        cp = None
-    if cp is None:
-        raise CopyError, eNoData
-
-    try:
-        op, mdatas = _cb_decode(cp)
-    except:
-        raise CopyError, eInvalid
-
-    oblist = []
-    app = self.getPhysicalRoot()
-    for mdata in mdatas:
-        m = Moniker.loadMoniker(mdata)
-        try:
-            ob = m.bind(app)
-        except ConflictError:
-            raise
-        except:
-            raise CopyError, eNotFound
-        self._verifyObjectPaste(ob, validate_src=op+1)
-        oblist.append(ob)
-
-    result = []
-    if op == 0:
-        # Copy operation
-        for ob in oblist:
-            orig_id = ob.getId()
-            if not ob.cb_isCopyable():
-                raise CopyError, eNotSupported % escape(orig_id)
-
-            try:
-                ob._notifyOfCopyTo(self, op=0)
-            except ConflictError:
-                raise
-            except:
-                raise CopyError, MessageDialog(
-                    title="Copy Error",
-                    message=sys.exc_info()[1],
-                    action='manage_main')
-
-            id = self._get_id(orig_id)
-            result.append({'id': orig_id, 'new_id': id})
-
-            ob = ob._getCopy(self)
-            ob._setId(id)
-            notify(ObjectCopiedEvent(ob))
-
-            self._setObject(id, ob)
-            ob = self._getOb(id)
-            ob.wl_clearLocks()
-
-            ob._postCopy(self, op=0)
-
-            maybeCallDeprecated('manage_afterClone', ob)
-
-            notify(FiveObjectClonedEvent(ob))
-
-        if REQUEST is not None:
-            return self.manage_main(self, REQUEST, update_menu=1,
-                                    cb_dataValid=1)
-
-    elif op == 1:
-        # Move operation
-        for ob in oblist:
-            orig_id = ob.getId()
-            if not ob.cb_isMoveable():
-                raise CopyError, eNotSupported % escape(orig_id)
-
-            try:
-                ob._notifyOfCopyTo(self, op=1)
-            except ConflictError:
-                raise
-            except:
-                raise CopyError, MessageDialog(
-                    title="Move Error",
-                    message=sys.exc_info()[1],
-                    action='manage_main')
-
-            if not sanity_check(self, ob):
-                raise CopyError, "This object cannot be pasted into itself"
-
-            orig_container = aq_parent(aq_inner(ob))
-            if aq_base(orig_container) is aq_base(self):
-                id = orig_id
-            else:
-                id = self._get_id(orig_id)
-            result.append({'id': orig_id, 'new_id': id})
-
-            notify(ObjectWillBeMovedEvent(ob, orig_container, orig_id,
-                                          self, id))
-
-            # try to make ownership explicit so that it gets carried
-            # along to the new location if needed.
-            ob.manage_changeOwnershipType(explicit=1)
-
-            orig_container._delObject(orig_id, suppress_events=True)
-            ob = aq_base(ob)
-            ob._setId(id)
-
-            self._setObject(id, ob, set_owner=0, suppress_events=True)
-            ob = self._getOb(id)
-
-            notify(ObjectMovedEvent(ob, orig_container, orig_id, self, id))
-
-            ob._postCopy(self, op=1)
-            # try to make ownership implicit if possible
-            ob.manage_changeOwnershipType(explicit=0)
-
-        if REQUEST is not None:
-            REQUEST['RESPONSE'].setCookie('__cp', 'deleted',
-                                path='%s' % cookie_path(REQUEST),
-                                expires='Wed, 31-Dec-97 23:59:59 GMT')
-            REQUEST['__cp'] = None
-            return self.manage_main(self, REQUEST, update_menu=1,
-                                    cb_dataValid=0)
-
-    return result
-
-# From CopyContainer
-def manage_clone(self, ob, id, REQUEST=None):
-    """Clone an object, creating a new object with the given id.
-    """
-    if not ob.cb_isCopyable():
-        raise CopyError, eNotSupported % escape(ob.getId())
-    try:
-        self._checkId(id)
-    except:
-        raise CopyError, MessageDialog(
-            title='Invalid Id',
-            message=sys.exc_info()[1],
-            action ='manage_main')
-
-    self._verifyObjectPaste(ob)
-
-    try:
-        ob._notifyOfCopyTo(self, op=0)
-    except ConflictError:
-        raise
-    except:
-        raise CopyError, MessageDialog(
-            title="Clone Error",
-            message=sys.exc_info()[1],
-            action='manage_main')
-
-    ob = ob._getCopy(self)
-    ob._setId(id)
-    notify(ObjectCopiedEvent(ob))
-
-    self._setObject(id, ob)
-    ob = self._getOb(id)
-
-    ob._postCopy(self, op=0)
-
-    maybeCallDeprecated('manage_afterClone', ob)
-
-    notify(FiveObjectClonedEvent(ob))
-
-    return ob
-
-##################################################
-# Fix OFS.Application's creation of some objects.
-#
-# The application object creates root objects like error_log,
-# browser_id_manager, session_data_manager
-#
-# They all expects their manage_afterAdd to be called, but they are
-# created before Five 1.2 is initialized and has had a chance to do its
-# patches. So we call manage_afterAddd by hand.
-#
-# Remove this in Five 1.3, where subscribers and implements()
-# will be setup correctly earlier.
-
-def install_errorlog(self):
-    app = self.getApp()
-    if app._getInitializerFlag('error_log'):
-        # do nothing if we've already installed one
-        return
-    # Install an error_log
-    if not hasattr(app, 'error_log'):
-        from Products.SiteErrorLog.SiteErrorLog import SiteErrorLog
-        error_log = SiteErrorLog()
-        app._setObject('error_log', error_log)
-        # Added for Five 1.2:
-        error_log = app.error_log
-        error_log.manage_afterAdd(error_log, app)
-        # End added
-        app._setInitializerFlag('error_log')
-        self.commit('Added site error_log at /error_log')
-
-def install_browser_id_manager(self):
-    app = self.getApp()
-    if app._getInitializerFlag('browser_id_manager'):
-        # do nothing if we've already installed one
-        return
-    # Ensure that a browser ID manager exists
-    if not hasattr(app, 'browser_id_manager'):
-        from Products.Sessions.BrowserIdManager import BrowserIdManager
-        bid = BrowserIdManager('browser_id_manager', 'Browser Id Manager')
-        app._setObject('browser_id_manager', bid)
-        # Added for Five 1.2:
-        browser_id_manager = app.browser_id_manager
-        browser_id_manager.manage_afterAdd(browser_id_manager, app)
-        # End added
-        app._setInitializerFlag('browser_id_manager')
-        self.commit('Added browser_id_manager')
-
-def install_session_data_manager(self):
-    app = self.getApp()
-    if app._getInitializerFlag('session_data_manager'):
-        # do nothing if we've already installed one
-        return
-    # Ensure that a session data manager exists
-    if not hasattr(app, 'session_data_manager'):
-        from Products.Sessions.SessionDataManager import SessionDataManager
-        sdm = SessionDataManager('session_data_manager',
-            title='Session Data Manager',
-            path='/temp_folder/session_data',
-            requestName='SESSION')
-        app._setObject('session_data_manager', sdm)
-        # Added for Five 1.2:
-        session_data_manager = app.session_data_manager
-        session_data_manager.manage_afterAdd(session_data_manager, app)
-        # End added
-        app._setInitializerFlag('session_data_manager')
-        self.commit('Added session_data_manager')
-
-##################################################
-# Structured monkey-patching
-
-import Products.Five
-from Products.Five import zcml
-from Products.Five.fiveconfigure import killMonkey
-from zope.testing.cleanup import addCleanUp
-
-_monkied = []
-
-from OFS.SimpleItem import Item
-from OFS.ObjectManager import ObjectManager
-from OFS.CopySupport import CopyContainer
-from OFS.OrderSupport import OrderSupport
-from Products.BTreeFolder2.BTreeFolder2 import BTreeFolder2Base
-from OFS.Application import AppInitializer
-from Products.ZCatalog import CatalogAwareness, CatalogPathAwareness
-
-def doMonkies(transitional, register_cleanup=True):
-    """Monkey patch various methods to provide container events.
-    """
-    global hasContainerEvents
-    hasContainerEvents = True
-
-    patchMethod(ObjectManager, '_setObject',
-                _setObject)
-    patchMethod(ObjectManager, '_delObject',
-                _delObject)
-    patchMethod(ObjectManager, 'manage_afterAdd',
-                manage_afterAdd)
-    patchMethod(ObjectManager, 'manage_beforeDelete',
-                manage_beforeDelete)
-    patchMethod(ObjectManager, 'manage_afterClone',
-                manage_afterClone)
-
-    patchMethod(Item, 'manage_afterAdd',
-                manage_afterAdd)
-    patchMethod(Item, 'manage_beforeDelete',
-                manage_beforeDelete)
-    patchMethod(Item, 'manage_afterClone',
-                manage_afterClone)
-
-    patchMethod(BTreeFolder2Base, '_setObject',
-                BT_setObject)
-    patchMethod(BTreeFolder2Base, '_delObject',
-                BT_delObject)
-
-    patchMethod(CopyContainer, 'manage_renameObject',
-                manage_renameObject)
-    patchMethod(CopyContainer, 'manage_pasteObjects',
-                manage_pasteObjects)
-    patchMethod(CopyContainer, 'manage_clone',
-                manage_clone)
-
-    patchMethod(OrderSupport, '_old_manage_renameObject',
-                manage_renameObject)
-
-    patchMethod(AppInitializer, 'install_errorlog',
-                install_errorlog)
-    patchMethod(AppInitializer, 'install_browser_id_manager',
-                install_browser_id_manager)
-    patchMethod(AppInitializer, 'install_session_data_manager',
-                install_session_data_manager)
-
-    patchMethod(CatalogAwareness.CatalogAware, 'manage_afterAdd',
-                CA_manage_afterAdd)
-    patchMethod(CatalogAwareness.CatalogAware, 'manage_beforeDelete',
-                CA_manage_beforeDelete)
-    patchMethod(CatalogAwareness.CatalogAware, 'manage_afterClone',
-                CA_manage_afterClone)
-    patchMethod(CatalogPathAwareness.CatalogAware, 'manage_afterAdd',
-                CA_manage_afterAdd)
-    patchMethod(CatalogPathAwareness.CatalogAware, 'manage_beforeDelete',
-                CA_manage_beforeDelete)
-    patchMethod(CatalogPathAwareness.CatalogAware, 'manage_afterClone',
-                CA_manage_afterClone)
-
-    # XXX remove this for Five 1.3, and put it in configure.zcml
-    zcml.load_config('event.zcml', Products.Five)
-
-    if register_cleanup:
-        addCleanUp(undoMonkies)
-
-def patchMethod(class_, name, new_method):
-    method = getattr(class_, name, None)
-    if isFiveMethod(method):
-        return
-    setattr(class_, FIVE_ORIGINAL_PREFIX + name, method)
-    setattr(class_, name, new_method)
-    new_method.__five_method__ = True
-    _monkied.append((class_, name))
-
-def undoMonkies():
-    """Undo monkey patches.
-    """
-    global hasContainerEvents
-    for class_, name in _monkied:
-        killMonkey(class_, name, FIVE_ORIGINAL_PREFIX + name)
-    hasContainerEvents = False
-    deprecatedManageAddDeleteClasses[:] = []

Modified: z3/Five/trunk/event.zcml
==============================================================================
--- z3/Five/trunk/event.zcml	(original)
+++ z3/Five/trunk/event.zcml	Mon Oct 31 20:59:07 2005
@@ -10,7 +10,7 @@
   <adapter
       for="OFS.interfaces.IObjectManager"
       provides="zope.app.location.interfaces.ISublocations"
-      factory=".event.ObjectManagerSublocations"
+      factory=".subscribers.ObjectManagerSublocations"
       />
 
   <!-- dispatch IObjectWillBeMovedEvent with "bottom-up" semantics -->
@@ -18,7 +18,7 @@
   <subscriber
       for="OFS.interfaces.IItem
            .event.IObjectWillBeMovedEvent"
-      handler=".event.dispatchObjectWillBeMovedEvent"
+      handler=".subscribers.dispatchObjectWillBeMovedEvent"
       />
 
   <!-- dispatch IObjectMovedEvent with "top-down" semantics -->
@@ -26,7 +26,7 @@
   <subscriber
       for="OFS.interfaces.IItem
            zope.app.container.interfaces.IObjectMovedEvent"
-      handler=".event.dispatchObjectMovedEvent"
+      handler=".subscribers.dispatchObjectMovedEvent"
       />
 
   <!-- dispatch IFiveObjectClonedEvent with "top-down" semantics -->
@@ -34,7 +34,7 @@
   <subscriber
       for="OFS.interfaces.IItem
            .event.IFiveObjectClonedEvent"
-      handler=".event.dispatchFiveObjectClonedEvent"
+      handler=".subscribers.dispatchFiveObjectClonedEvent"
       />
 
 </configure>

Modified: z3/Five/trunk/eventconfigure.py
==============================================================================
--- z3/Five/trunk/eventconfigure.py	(original)
+++ z3/Five/trunk/eventconfigure.py	Mon Oct 31 20:59:07 2005
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
+# Copyright (c) 2005 Zope Corporation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
@@ -18,8 +18,13 @@
 $Id$
 """
 
-from event import doMonkies
-from event import deprecatedManageAddDeleteClasses
+import warnings
+from subscribers import deprecatedManageAddDeleteClasses
+
+def setContainerEvents():
+    warnings.warn("Using <five:containerEvents/> is deprecated (it is now "
+                  "the default), it will be removed in Zope 2.11",
+                  DeprecationWarning)
 
 def setDeprecatedManageAddDelete(class_):
     """Instances of the class will still see their old methods called."""
@@ -28,7 +33,7 @@
 def containerEvents(_context):
     _context.action(
         discriminator=None,
-        callable=doMonkies,
+        callable=setContainerEvents,
         args=(),
         )
 

Added: z3/Five/trunk/subscribers.py
==============================================================================
--- (empty file)
+++ z3/Five/trunk/subscribers.py	Mon Oct 31 20:59:07 2005
@@ -0,0 +1,157 @@
+##############################################################################
+#
+# 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$
+"""
+
+import warnings
+import sys
+
+from zLOG import LOG, ERROR
+from App.config import getConfiguration
+from AccessControl import getSecurityManager
+from ZODB.POSException import ConflictError
+import OFS.ObjectManager
+import OFS.interfaces
+
+from zope.interface import implements
+from zope.app.container.contained import dispatchToSublocations
+from zope.app.location.interfaces import ISublocations
+
+
+deprecatedManageAddDeleteClasses = []
+
+
+def hasDeprecatedMethods(ob):
+    """Do we need to call the deprecated methods?
+    """
+    for class_ in deprecatedManageAddDeleteClasses:
+        if isinstance(ob, class_):
+            return True
+    return False
+
+def maybeCallDeprecated(method_name, ob, *args):
+    """Call a deprecated method, if the framework doesn't call it already.
+    """
+    if hasDeprecatedMethods(ob):
+        # Already deprecated through zcml
+        return
+    method = getattr(ob, method_name)
+    if getattr(method, '__five_method__', False):
+        # Method knows it's deprecated
+        return
+    if deprecatedManageAddDeleteClasses:
+        # Not deprecated through zcml and directives fully loaded
+        class_ = ob.__class__
+        warnings.warn(
+            "Calling %s.%s.%s is deprecated when using Five, "
+            "instead use event subscribers or "
+            "mark the class with <five:deprecatedManageAddDelete/>"
+            % (class_.__module__, class_.__name__, method_name),
+            DeprecationWarning)
+    # Note that calling the method can lead to incorrect behavior
+    # but in the most common case that's better than not calling it.
+    method(ob, *args)
+
+##################################################
+
+class ObjectManagerSublocations(object):
+    """Get the sublocations for an ObjectManager.
+    """
+    __used_for__ = OFS.interfaces.IObjectManager
+    implements(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.
+
+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
+    if hasDeprecatedMethods(ob):
+        callManageBeforeDelete(ob, event)
+
+def dispatchObjectMovedEvent(ob, event):
+    """Multi-subscriber for IItem + IObjectMovedEvent.
+    """
+    # First, do the manage_afterAdd dance
+    if hasDeprecatedMethods(ob):
+        callManageAfterAdd(ob, event)
+    # Next, dispatch to sublocations
+    if OFS.interfaces.IObjectManager.providedBy(ob):
+        dispatchToSublocations(ob, event)
+
+def dispatchFiveObjectClonedEvent(ob, event):
+    """Multi-subscriber for IItem + IFiveObjectClonedEvent.
+    """
+    # First, do the manage_afterClone dance
+    if hasDeprecatedMethods(ob):
+        callManageAfterClone(ob, event)
+    # Next, dispatch to sublocations
+    if OFS.interfaces.IObjectManager.providedBy(ob):
+        dispatchToSublocations(ob, event)
+
+
+def callManageAfterAdd(ob, event):
+    """Compatibility subscriber for manage_afterAdd.
+    """
+    container = event.newParent
+    if container is None:
+        # this is a remove
+        return
+    ob.manage_afterAdd(event.object, container)
+
+def callManageBeforeDelete(ob, event):
+    """Compatibility subscriber for manage_beforeDelete.
+    """
+    container = event.oldParent
+    if container is None:
+        # this is an add
+        return
+    try:
+        ob.manage_beforeDelete(event.object, container)
+    except OFS.ObjectManager.BeforeDeleteException:
+        raise
+    except ConflictError:
+        raise
+    except:
+        LOG('Zope', ERROR, '_delObject() threw', error=sys.exc_info())
+        # In debug mode when non-Manager, let exceptions propagate.
+        if getConfiguration().debug_mode:
+            if not getSecurityManager().getUser().has_role('Manager'):
+                raise
+
+def callManageAfterClone(ob, event):
+    """Compatibility subscriber for manage_afterClone.
+    """
+    ob.manage_afterClone(event.object)

Modified: z3/Five/trunk/tests/event.txt
==============================================================================
--- z3/Five/trunk/tests/event.txt	(original)
+++ z3/Five/trunk/tests/event.txt	Mon Oct 31 20:59:07 2005
@@ -6,12 +6,8 @@
 about to be added/removed from a container, and also after it has been
 done. This is used for bookkeeping and cleaning up in subobjects.
 
-These events replace the Zope 2 manage_afterAdd, manage_beforeDelete and
-manage_afterClone methods.
-
-To enable events, use::
-
-  <five:containerEvents/>
+These events replace the old Zope 2 manage_afterAdd, manage_beforeDelete
+and manage_afterClone methods.
 
 All standard Zope containers will only call manage_afterAdd & co on
 classes specified with the directive::
@@ -82,112 +78,14 @@
   >>> provideHandler(printObjectEvent, (IItem, IFiveObjectClonedEvent))
   >>> provideHandler(printObjectEventExceptSome, (None, IObjectEvent))
 
-Original behavior
-=================
-
-Let's test standard folder operations. The methods manage_afterAdd and
-manage_beforeDelete, and several others, are called::
-
-  >>> ob = MyContent('milou')
-  >>> folder._setObject('milou', ob)
-  old manage_afterAdd milou milou folder
-  'milou'
-  >>> folder.manage_delObjects('milou')
-  old manage_beforeDelete milou milou folder
-
-We can also move objects::
-
-  >>> ob = MyContent('tintin')
-  >>> folder._setObject('tintin', ob)
-  old manage_afterAdd tintin tintin folder
-  'tintin'
-  >>> cp = folder.manage_cutObjects('tintin')
-  >>> folder.manage_pasteObjects(cp)
-  old manage_beforeDelete tintin tintin folder
-  old manage_afterAdd tintin tintin folder
-  [{'new_id': 'tintin', 'id': 'tintin'}]
-
-And we can copy them::
-
-  >>> cp = folder.manage_copyObjects('tintin')
-  >>> folder.manage_pasteObjects(cp)
-  old manage_afterAdd copy_of_tintin copy_of_tintin folder
-  old manage_afterClone copy_of_tintin copy_of_tintin
-  [{'new_id': 'copy_of_tintin', 'id': 'tintin'}]
-
-We can rename objects::
-
-  >>> folder.manage_renameObject('copy_of_tintin', 'haddock')
-  old manage_beforeDelete copy_of_tintin copy_of_tintin folder
-  old manage_afterAdd haddock haddock folder
-
-We can also call manage_clone by hand::
-
-  >>> res = folder.manage_clone(folder.tintin, 'tournesol')
-  old manage_afterAdd tournesol tournesol folder
-  old manage_afterClone tournesol tournesol
-  >>> res.getId()
-  'tournesol'
-
-Let's also test with a BTreeFolder::
-
-  >>> ob = MyContent('castafiore')
-  >>> btfolder._setObject('castafiore', ob)
-  old manage_afterAdd castafiore castafiore btfolder
-  'castafiore'
-  >>> btfolder.manage_delObjects('castafiore')
-  old manage_beforeDelete castafiore castafiore btfolder
-
-When a tree of objects is affected, the methods are called for all
-levels::
-
-  >>> subfolder = MyFolder('subfolder')
-  >>> folder._setObject('subfolder', subfolder)
-  old manage_afterAdd subfolder subfolder folder
-  'subfolder'
-  >>> subfolder = folder.subfolder
-  >>> ob = MyContent('riri')
-  >>> subfolder._setObject('riri', ob)
-  old manage_afterAdd riri riri subfolder
-  'riri'
-
-Renaming a tree of objects. Note that manage_beforeDelete is called
-bottom-up::
-
-  >>> folder.manage_renameObject('subfolder', 'bob')
-  old manage_beforeDelete riri subfolder folder
-  old manage_beforeDelete subfolder subfolder folder
-  old manage_afterAdd bob bob folder
-  old manage_afterAdd riri bob folder
-
-Cloning a tree of objects::
-
-  >>> res = folder.manage_clone(folder.bob, 'loulou')
-  old manage_afterAdd loulou loulou folder
-  old manage_afterAdd riri loulou folder
-  old manage_afterClone loulou loulou
-  old manage_afterClone riri loulou
-  >>> res.getId()
-  'loulou'
-
-
-With events
-===========
-
-We need some of the Five setup, to get proper five:interfaces declared
-for Zope 2 classes::
+Finally we need to load the subscribers configuration::
 
   >>> from Products.Five import zcml
   >>> import Products.Five
-  >>> zcml.load_config('meta.zcml', Products.Five)
-
-Now execute the directive enabling events::
-
-  >>> from Products.Five.event import doMonkies, undoMonkies
-  >>> doMonkies(transitional=False)
+  >>> zcml.load_config('event.zcml', Products.Five)
 
 Old class
----------
+=========
 
 If we use an instance of an old class for which we haven't specified
 anything, events are sent and the manage_afterAdd & co methods are
@@ -213,7 +111,7 @@
   ObjectRemovedEvent dog
 
 Old class with deprecatedManageAddDelete
-----------------------------------------
+========================================
 
 We specifiy that our class is deprecated (using zcml in real life)::
 
@@ -349,7 +247,7 @@
   'mickey'
 
 New class
----------
+=========
 
 If we use classes that don't have any manage_afterAdd & co method,
 everything happens correctly::
@@ -486,9 +384,8 @@
   ['ob4', 'ob2']
 
 
-Now cleanup all the monkey patches::
+Now cleanup::
 
   >>> import transaction
   >>> transaction.abort()
-  >>> undoMonkies()
   >>> tearDown()


More information about the z3-checkins mailing list