[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