[Zope-Checkins] SVN: Zope/trunk/ Moved general OFS related ZCML directives from Products.Five into the OFS package itself.

Hanno Schlichting hannosch at hannosch.eu
Fri Jan 1 17:46:02 EST 2010


Log message for revision 107521:
  Moved general OFS related ZCML directives from Products.Five into the OFS package itself.
  

Changed:
  U   Zope/trunk/doc/CHANGES.rst
  U   Zope/trunk/src/OFS/configure.zcml
  A   Zope/trunk/src/OFS/deprecated.zcml
  A   Zope/trunk/src/OFS/event.txt
  A   Zope/trunk/src/OFS/meta.zcml
  A   Zope/trunk/src/OFS/metaconfigure.py
  A   Zope/trunk/src/OFS/metadirectives.py
  A   Zope/trunk/src/OFS/tests/event.txt
  U   Zope/trunk/src/OFS/tests/testCopySupportHooks.py
  U   Zope/trunk/src/OFS/tests/testObjectManager.py
  A   Zope/trunk/src/OFS/tests/test_event.py
  A   Zope/trunk/src/OFS/tests/test_registerclass.py
  A   Zope/trunk/src/OFS/tests/test_registerpackage.py
  U   Zope/trunk/src/Products/Five/configure.zcml
  D   Zope/trunk/src/Products/Five/deprecated.zcml
  A   Zope/trunk/src/Products/Five/deprecated.zcml
  D   Zope/trunk/src/Products/Five/doc/event.txt
  U   Zope/trunk/src/Products/Five/eventconfigure.py
  U   Zope/trunk/src/Products/Five/fiveconfigure.py
  U   Zope/trunk/src/Products/Five/fivedirectives.py
  U   Zope/trunk/src/Products/Five/meta.zcml
  U   Zope/trunk/src/Products/Five/sizeconfigure.py
  D   Zope/trunk/src/Products/Five/tests/event.txt
  D   Zope/trunk/src/Products/Five/tests/test_event.py
  D   Zope/trunk/src/Products/Five/tests/test_registerclass.py
  D   Zope/trunk/src/Products/Five/tests/test_registerpackage.py

-=-
Modified: Zope/trunk/doc/CHANGES.rst
===================================================================
--- Zope/trunk/doc/CHANGES.rst	2010-01-01 22:37:20 UTC (rev 107520)
+++ Zope/trunk/doc/CHANGES.rst	2010-01-01 22:46:01 UTC (rev 107521)
@@ -11,6 +11,9 @@
 Restructuring
 +++++++++++++
 
+- Moved general OFS related ZCML directives from Products.Five into the OFS
+  package itself.
+
 - Ported the lazy expression into zope.tales and require a new version of it.
 
 - Updated Five documentation to clarify its role in regard to Zope packages.

Modified: Zope/trunk/src/OFS/configure.zcml
===================================================================
--- Zope/trunk/src/OFS/configure.zcml	2010-01-01 22:37:20 UTC (rev 107520)
+++ Zope/trunk/src/OFS/configure.zcml	2010-01-01 22:46:01 UTC (rev 107521)
@@ -1,5 +1,6 @@
 <configure xmlns="http://namespaces.zope.org/zope">
 
+  <include file="deprecated.zcml"/>
   <include file="event.zcml"/>
 
 </configure>

Copied: Zope/trunk/src/OFS/deprecated.zcml (from rev 107512, Zope/trunk/src/Products/Five/deprecated.zcml)
===================================================================
--- Zope/trunk/src/OFS/deprecated.zcml	                        (rev 0)
+++ Zope/trunk/src/OFS/deprecated.zcml	2010-01-01 22:46:01 UTC (rev 107521)
@@ -0,0 +1,39 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+           xmlns:five="http://namespaces.zope.org/five">
+
+  <!-- deprecated in core Zope, should be fixed there -->
+
+  <five:deprecatedManageAddDelete
+      class="AccessControl.User.BasicUserFolder"/>
+
+  <five:deprecatedManageAddDelete
+      class="App.Permission.Permission"/>
+
+  <five:deprecatedManageAddDelete
+      class="HelpSys.HelpTopic.HelpTopicBase"/>
+
+  <five:deprecatedManageAddDelete
+      class="OFS.Cache.CacheManager"/>
+
+  <five:deprecatedManageAddDelete
+      class="Products.PythonScripts.PythonScript.PythonScript"/>
+
+  <five:deprecatedManageAddDelete
+      class="Products.Sessions.BrowserIdManager.BrowserIdManager"/>
+  <five:deprecatedManageAddDelete
+      class="Products.Sessions.SessionDataManager.SessionDataManager"/>
+
+  <five:deprecatedManageAddDelete
+      class="Products.SiteAccess.VirtualHostMonster.VirtualHostMonster"/>
+  <five:deprecatedManageAddDelete
+      class="Products.SiteAccess.SiteRoot.Traverser"/>
+
+  <five:deprecatedManageAddDelete
+      class="Products.SiteErrorLog.SiteErrorLog.SiteErrorLog"/>
+
+  <five:deprecatedManageAddDelete
+      class="Products.ZCatalog.CatalogAwareness.CatalogAware"/>
+  <five:deprecatedManageAddDelete
+      class="Products.ZCatalog.CatalogPathAwareness.CatalogAware"/>
+
+</configure>

Copied: Zope/trunk/src/OFS/event.txt (from rev 107512, Zope/trunk/src/Products/Five/doc/event.txt)
===================================================================
--- Zope/trunk/src/OFS/event.txt	                        (rev 0)
+++ Zope/trunk/src/OFS/event.txt	2010-01-01 22:46:01 UTC (rev 107521)
@@ -0,0 +1,283 @@
+Events in Zope 2
+================
+
+Zope 2 supports zope.lifecycleevent style events including container events.
+
+With container events, you finally have the ability to react to things
+happening to objects without have to subclass ``manage_afterAdd``,
+``manage_beforeDelete`` or ``manage_afterClone``. Instead, you just have
+to register a subscriber for the appropriate event, for instance
+IObjectAddedEvent, and make it do the work.
+
+Indeed, the old methods like ``manage_afterAdd`` are now discouraged, you
+shouldn't use them anymore.
+
+Let's see how to migrate your products.
+
+Old product
+-----------
+
+Suppose that in an old product you have code that needs to register
+through a central tool whenever a document is created. Or it could be
+indexing itself. Or it could initialize an attribute according to its
+current path. Code like::
+
+    class CoolDocument(...):
+        ...
+        def manage_afterAdd(self, item, container):
+            self.mangled_path = mangle('/'.join(self.getPhysicalPath()))
+            getToolByName(self, 'portal_cool').registerCool(self)
+            super(CoolDocument, self).manage_afterAdd(item, container)
+
+        def manage_afterClone(self, item):
+            self.mangled_path = mangle('/'.join(self.getPhysicalPath()))
+            getToolByName(self, 'portal_cool').registerCool(self)
+            super(CoolDocument, self).manage_afterClone(item)
+
+        def manage_beforeDelete(self, item, container):
+            super(CoolDocument, self).manage_beforeDelete(item, container)
+            getToolByName(self, 'portal_cool').unregisterCool(self)
+
+This had been the best practice in old Zope 2 versions. Note the use of
+``super()`` to call the base class, which is often omitted because people
+"know" that SimpleItem for instance doesn't do anything in these methods.
+
+If you run this code today, you will get deprecation warnings,
+telling you that::
+
+    Calling Products.CoolProduct.CoolDocument.CoolDocument.manage_afterAdd
+    is discouraged. You should use event subscribers instead.
+
+Using five:deprecatedManageAddDelete
+------------------------------------
+
+The simplest thing you can do to deal with the deprecation warnings, and
+have correct behavior, is to add in your products a ``configure.zcml``
+file containing::
+
+    <configure
+        xmlns="http://namespaces.zope.org/zope"
+        xmlns:five="http://namespaces.zope.org/five">
+
+      <five:deprecatedManageAddDelete
+          class="Products.CoolProduct.CoolDocument.CoolDocument"/>
+
+    </configure>
+
+This tells Zope that you acknowledge that your class contains deprecated
+methods, and ask it to still call them in the proper manner. So Zope
+will be sending events when an object is added, for instance, and in
+addition call your old ``manage_afterAdd`` method.
+
+One subtlety here is that you may have to modify your methods to just do
+their work, and not call their super class. This is necessary because
+proper events are already dispatched to all relevant classes, and the
+work of the super class will be done trough events, you must not redo it
+by hand. If you call the super class, you will get a warning, saying for
+instance::
+
+    CoolDocument.manage_afterAdd is discouraged. You
+    should use an IObjectAddedEvent subscriber instead.
+
+The fact that you must "just do your work" is especially important for
+the rare cases where people subclass the ``manage_afterAdd`` of object
+managers like folders, and decided to reimplement recursion into the
+children themselves. If you do that, then there will be two recursions
+going on in parallel, the one done by events, and the one done by your
+code. This would be bad.
+
+Using subscribers
+-----------------
+
+In the long run, you will want to use proper subscribers.
+
+First, you'll have to write a subscriber that "does the work", for
+instance::
+
+    def addedCoolDocument(ob, event):
+        """A Cool Document was added to a container."""
+        self.mangled_path = mangle('/'.join(self.getPhysicalPath()))
+
+Note that we're not calling the ``portal_cool`` tool anymore, because
+presumably this tool will also be modified to do its work through
+events, and will have a similar subscriber doing the necessary
+``registerCool``. Note also that here we don't care about the event, but
+in more complex cases we would.
+
+Now we have to register our subscriber for our object. To do that, we
+need to "mark" our object through an interface. We can define in our
+product's ``interfaces.py``::
+
+    from zope.interface import Interface, Attribute
+
+    class ICoolDocument(Interface):
+        """Cool Document."""
+        mangled_path = Attribute("Our mangled path.")
+        ...
+
+Then the class CoolDocument is marked with this interface::
+
+    from zope.interface import implements
+    from Products.CoolProduct.interfaces import ICoolDocument
+    class CoolDocument(...):
+        implements(ICoolDocument)
+        ...
+
+Finally we must link the event and the interface to the subscriber using
+zcml, so in ``configure.zcml`` we'll add::
+
+    ...
+      <subscriber
+          for="Products.CoolProduct.interfaces.ICoolDocument
+               zope.lifecycleevent.interfaces.IObjectAddedEvent"
+          handler="Products.CoolProduct.CoolDocument.addedCoolDocument"
+          />
+    ...
+
+And that's it, everything is plugged. Note that IObjectAddedEvent takes
+care of both ``manage_afterAdd`` and ``manage_afterClone``, as it's sent
+whenever a new object is placed into a container. However this won't
+take care of moves and renamings, we'll see below how to do that.
+
+Event dispatching
+-----------------
+
+When an IObjectEvent (from which all the events we're talking here
+derive) is initially sent, it concerns one object. For instance, a
+specific object is removed. The ``event.object`` attribute is this
+object.
+
+To be able to know about removals, we could just subscribe to the
+appropriate event using a standard event subscriber. In that case, we'd
+have to filter "by hand" to check if the object removed is of the type
+we're interested in, which would be a chore. In addition, any subobjects
+of the removed object wouldn't know what happens to them, and for
+instance they wouldn't have any way of doing some cleanup before they
+disappear.
+
+To solve these two problems, Zope has an additional mechanism by which
+any IObjectEvent is redispatched using multi-adapters of the form ``(ob,
+event)``, so that a subscriber can be specific about the type of object
+it's interested in. Furthermore, this is done recursively for all
+sublocations ``ob`` of the initial object. The ``event`` won't change
+though, and ``event.object`` will still be the original object for which
+the event was initially sent (this corresponds to ``self`` and ``item``
+in the ``manage_afterAdd`` method -- ``self`` is ``ob``, and ``item`` is
+``event.object``).
+
+Understanding the hierarchy of events is important to see how to
+subscribe to them.
+
+ * IObjectEvent is the most general. Any event focused on an object
+   derives from this.
+
+ * IObjectMovedEvent is sent when an object changes location or is
+   renamed. It is quite general, as it also encompasses the case where
+   there's no old location (addition) or no new location (removal).
+
+ * IObjectAddedEvent and IObjectRemovedEvent both derive from
+   IObjectMovedEvent.
+
+ * IObjectCopiedEvent is sent just after an object copy is made, but
+   this doesn't mean the object has been put into its new container yet,
+   so it doesn't have a location.
+
+There are only a few basic use cases about what one wants to do with
+respect to events (but you might want to read the full story in
+Five/tests/event.txt).
+
+The first use case is the one where the object has to be aware of its
+path, like in the CoolDocument example above.
+
+In Zope 2 an object has a new path through creation, copy or move
+(rename is a kind of move). The events sent during these three
+operations are varied: creation sends IObjectAddedEvent, copy sends
+IObjectCopiedEvent then IObjectAddedEvent, and move sends
+IObjectMovedEvent.
+
+So to react to new paths, we have to subscribe to IObjectMovedEvent, but
+this will also get us any IObjectRemovedEvent, which we'll have to
+filter out by hand (this is unfortunate, and due to the way the Zope
+interface hierarchy is organized). So to fix the CoolDocument
+configuration we have to add::
+
+    def movedCoolDocument(ob, event):
+        """A Cool Document was moved."""
+        if not IObjectRemovedEvent.providedBy(event):
+            addedCoolDocument(ob, event)
+
+And replace the subscriber with::
+
+    ...
+      <subscriber
+          for="Products.CoolProduct.interfaces.ICoolDocument
+               zope.lifecycleevent.interfaces.IObjectMovedEvent"
+          handler="Products.CoolProduct.CoolDocument.movedCoolDocument"
+          />
+    ...
+
+The second use case is when the object has to do some cleanup when it is
+removed from its parent. This used to be in ``manage_beforeDelete``, now
+we can do the work in a ``removedCoolDocument`` method and just
+subscribe to IObjectRemovedEvent. But wait, this won't take into account
+moves... So in the same vein as above, we would have to write::
+
+    def movedCoolDocument(ob, event):
+        """A Cool Document was moved."""
+        if not IObjectRemovedEvent.providedBy(event):
+            addedCoolDocument(ob, event)
+        if not IObjectAddedEvent.providedBy(event):
+            removedCoolDocument(ob, event)
+
+The third use case is when your object has to stay registered with some
+tool, for instance indexed in a catalog, or as above registered with
+``portal_cool``. Here we have to know the old object's path to
+unregister it, so we have to be called *before* it is removed. We'll use
+``IObjectWillBe...`` events, that are sent before the actual operations
+take place::
+
+    from OFS.interfaces import IObjectWillBeAddedEvent
+    def beforeMoveCoolDocument(ob, event):
+        """A Cool Document will be moved."""
+        if not IObjectWillBeAddedEvent.providedBy(event):
+            getToolByName(ob, 'portal_cool').unregisterCool(ob)
+
+    def movedCoolDocument(ob, event):
+        """A Cool Document was moved."""
+        if not IObjectRemovedEvent.providedBy(event):
+            getToolByName(ob, 'portal_cool').registerCool(ob)
+        ...
+
+And use an additional subscriber::
+
+    ...
+      <subscriber
+          for="Products.CoolProduct.interfaces.ICoolDocument
+               OFS.interfaces.IObjectWillBeMovedEvent"
+          handler="Products.CoolProduct.CoolDocument.beforeMoveCoolDocument"
+          />
+    ...
+
+This has to be done if the tool cannot react by itself to objects being
+added and removed, which obviously would be better as it's ultimately
+the tool's responsibility and not the object's.
+
+Note that if having tests like::
+
+    if not IObjectWillBeAddedEvent.providedBy(event):
+    if not IObjectRemovedEvent.providedBy(event):
+
+seems cumbersome (and backwards), it is also possible to check what kind
+of event you're dealing with using::
+
+    if event.oldParent is not None:
+    if event.newParent is not None:
+
+(However be careful, the ``oldParent`` and ``newParent`` are the old and
+new parents *of the original object* for which the event was sent, not
+of the one to which the event was redispatched using the
+multi-subscribers we have registered.)
+
+The ``IObjectWillBe...`` events are specific to Zope 2 (and imported
+from ``OFS.interfaces``). zope.lifecycleevent doesn't really need them, as
+object identity is often enough.

Added: Zope/trunk/src/OFS/meta.zcml
===================================================================
--- Zope/trunk/src/OFS/meta.zcml	                        (rev 0)
+++ Zope/trunk/src/OFS/meta.zcml	2010-01-01 22:46:01 UTC (rev 107521)
@@ -0,0 +1,39 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:meta="http://namespaces.zope.org/meta">
+
+  <meta:directives namespace="http://namespaces.zope.org/five">
+
+    <meta:directive
+       name="loadProducts"
+       schema="zope.configuration.xmlconfig.IInclude"
+       handler=".metaconfigure.loadProducts"
+       />
+
+    <meta:directive
+       name="loadProductsOverrides"
+       schema="zope.configuration.xmlconfig.IInclude"
+       handler=".metaconfigure.loadProductsOverrides"
+       />
+
+    <meta:directive
+       name="deprecatedManageAddDelete"
+       schema=".metadirectives.IDeprecatedManageAddDeleteDirective"
+       handler=".metaconfigure.deprecatedManageAddDelete"
+       />
+
+    <meta:directive
+       name="registerClass"
+       schema=".metadirectives.IRegisterClassDirective"
+       handler=".metaconfigure.registerClass"
+       />
+
+    <meta:directive
+       name="registerPackage"
+       schema=".metadirectives.IRegisterPackageDirective"
+       handler=".metaconfigure.registerPackage"
+       />
+
+  </meta:directives>
+
+</configure>


Property changes on: Zope/trunk/src/OFS/meta.zcml
___________________________________________________________________
Added: svn:eol-style
   + native

Added: Zope/trunk/src/OFS/metaconfigure.py
===================================================================
--- Zope/trunk/src/OFS/metaconfigure.py	                        (rev 0)
+++ Zope/trunk/src/OFS/metaconfigure.py	2010-01-01 22:46:01 UTC (rev 107521)
@@ -0,0 +1,178 @@
+import logging
+import os
+
+from zope.component import getUtility
+from zope.configuration import xmlconfig
+from zope.interface import implementedBy
+from zope.security.interfaces import IPermission
+
+import App.config
+from OFS.subscribers import deprecatedManageAddDeleteClasses
+import Products
+
+debug_mode = App.config.getConfiguration().debug_mode
+logger = logging.getLogger('OFS')
+
+_register_monkies = []
+_meta_type_regs = []
+
+
+def findProducts():
+    from types import ModuleType
+    products = []
+    for name in dir(Products):
+        product = getattr(Products, name)
+        if isinstance(product, ModuleType) and hasattr(product, '__file__'):
+            products.append(product)
+    return products
+
+
+def handleBrokenProduct(product):
+    if debug_mode:
+        # Just reraise the error and let Zope handle it.
+        raise
+    # Not debug mode. Zope should continue to load. Print a log message:
+    logger.exception('Could not import Product %s' % product.__name__)
+
+
+def loadProducts(_context, file=None, files=None, package=None):
+    if file is None:
+        # set the default
+        file = 'configure.zcml'
+
+    if files is not None or package is not None:
+        raise ValueError("Neither the files or package argument is supported.")
+
+    # now load the files if they exist
+    for product in findProducts():
+        zcml = os.path.join(os.path.dirname(product.__file__), file)
+        if os.path.isfile(zcml):
+            try:
+                xmlconfig.include(_context, zcml, package=product)
+            except: # Yes, really, *any* kind of error.
+                handleBrokenProduct(product)
+
+
+def loadProductsOverrides(_context, file=None, files=None, package=None):
+    if file is None:
+        # set the default
+        file = 'overrides.zcml'
+
+    if files is not None or package is not None:
+        raise ValueError("Neither the files or package argument is supported.")
+
+    # now load the files if they exist
+    for product in findProducts():
+        zcml = os.path.join(os.path.dirname(product.__file__), file)
+        if os.path.isfile(zcml):
+            try:
+                xmlconfig.includeOverrides(_context, zcml, package=product)
+            except: # Yes, really, *any* kind of error.
+                handleBrokenProduct(product)
+
+
+def _registerPackage(module_, init_func=None):
+    """Registers the given python package as a Zope 2 style product
+    """
+
+    if not hasattr(module_, '__path__'):
+        raise ValueError("Must be a package and the " \
+                         "package must be filesystem based")
+
+    registered_packages = getattr(Products, '_registered_packages', None)
+    if registered_packages is None:
+        registered_packages = Products._registered_packages = []
+    registered_packages.append(module_)
+
+    # Delay the actual setup until the usual product loading time in
+    # OFS.Application. Otherwise, we may get database write errors in
+    # ZEO, when there's no connection with which to write an entry to
+    # Control_Panel. We would also get multiple calls to initialize().
+    to_initialize = getattr(Products, '_packages_to_initialize', None)
+    if to_initialize is None:
+        to_initialize = Products._packages_to_initialize = []
+    to_initialize.append((module_, init_func,))
+
+
+def registerPackage(_context, package, initialize=None):
+    """ZCML directive function for registering a python package product
+    """
+
+    _context.action(
+        discriminator = ('registerPackage', package),
+        callable = _registerPackage,
+        args = (package,initialize)
+        )
+
+
+def _registerClass(class_, meta_type, permission, addview, icon, global_):
+    setattr(class_, 'meta_type', meta_type)
+
+    permission_obj = getUtility(IPermission, permission)
+
+    if icon:
+        setattr(class_, 'icon', '++resource++%s' % icon)
+
+    interfaces = tuple(implementedBy(class_))
+
+    info = {'name': meta_type,
+            'action': addview and ('+/%s' % addview) or '',
+            'product': 'Five',
+            'permission': str(permission_obj.title),
+            'visibility': global_ and 'Global' or None,
+            'interfaces': interfaces,
+            'instance': class_,
+            'container_filter': None}
+
+    Products.meta_types += (info,)
+
+    _register_monkies.append(class_)
+    _meta_type_regs.append(meta_type)
+
+
+def registerClass(_context, class_, meta_type, permission, addview=None,
+                  icon=None, global_=True):
+    _context.action(
+        discriminator = ('registerClass', meta_type),
+        callable = _registerClass,
+        args = (class_, meta_type, permission, addview, icon, global_)
+        )
+
+def unregisterClass(class_):
+    delattr(class_, 'meta_type')
+    try:
+        delattr(class_, 'icon')
+    except AttributeError:
+        pass
+
+
+def setDeprecatedManageAddDelete(class_):
+    """Instances of the class will still see their old methods called."""
+    deprecatedManageAddDeleteClasses.append(class_)
+
+
+def deprecatedManageAddDelete(_context, class_):
+    _context.action(
+        discriminator=('five:deprecatedManageAddDelete', class_),
+        callable=setDeprecatedManageAddDelete,
+        args=(class_,),
+        )
+
+
+def cleanUp():
+    deprecatedManageAddDeleteClasses[:] = []
+
+    global _register_monkies
+    for class_ in _register_monkies:
+        unregisterClass(class_)
+    _register_monkies = []
+
+    global _meta_type_regs
+    Products.meta_types = tuple([ info for info in Products.meta_types
+                                  if info['name'] not in _meta_type_regs ])
+    _meta_type_regs = []
+
+
+from zope.testing.cleanup import addCleanUp
+addCleanUp(cleanUp)
+del addCleanUp


Property changes on: Zope/trunk/src/OFS/metaconfigure.py
___________________________________________________________________
Added: svn:eol-style
   + native

Added: Zope/trunk/src/OFS/metadirectives.py
===================================================================
--- Zope/trunk/src/OFS/metadirectives.py	                        (rev 0)
+++ Zope/trunk/src/OFS/metadirectives.py	2010-01-01 22:46:01 UTC (rev 107521)
@@ -0,0 +1,81 @@
+from zope.interface import Interface
+from zope.security.zcml import Permission
+from zope.configuration.fields import GlobalObject
+from zope.configuration.fields import Bool
+from zope.schema import ASCII
+
+
+class IDeprecatedManageAddDeleteDirective(Interface):
+    """Call manage_afterAdd & co for these contained content classes.
+    """
+    class_ = GlobalObject(
+        title=u"Class",
+        required=True,
+        )
+
+
+class IRegisterClassDirective(Interface):
+
+    """registerClass directive schema.
+
+    Register content with Zope 2.
+    """
+
+    class_ = GlobalObject(
+        title=u'Instance Class',
+        description=u'Dotted name of the class that is registered.',
+        required=True
+        )
+
+    meta_type = ASCII(
+        title=u'Meta Type',
+        description=u'A human readable unique identifier for the class.',
+        required=True
+        )
+
+    permission = Permission(
+        title=u'Add Permission',
+        description=u'The permission for adding objects of this class.',
+        required=True
+        )
+
+    addview = ASCII(
+        title=u'Add View ID',
+        description=u'The ID of the add view used in the ZMI. Consider this '
+                    u'required unless you know exactly what you do.',
+        default=None,
+        required=False
+        )
+
+    icon = ASCII(
+        title=u'Icon ID',
+        description=u'The ID of the icon used in the ZMI.',
+        default=None,
+        required=False
+        )
+
+    global_ = Bool(
+        title=u'Global scope?',
+        description=u'If "global" is False the class is only available in '
+                    u'containers that explicitly allow one of its interfaces.',
+        default=True,
+        required=False
+        )
+
+
+class IRegisterPackageDirective(Interface):
+    """Registers the given Python package which at a minimum fools zope2 into
+    thinking of it as a zope2 product.
+    """
+
+    package = GlobalObject(
+        title=u'Target package',
+        required=True
+        )
+
+    initialize = GlobalObject(
+        title=u'Initialization function to invoke',
+        description=u'The dotted name of a function that will get invoked '
+                    u'with a ProductContext instance',
+        required=False
+        )


Property changes on: Zope/trunk/src/OFS/metadirectives.py
___________________________________________________________________
Added: svn:eol-style
   + native

Copied: Zope/trunk/src/OFS/tests/event.txt (from rev 107512, Zope/trunk/src/Products/Five/tests/event.txt)
===================================================================
--- Zope/trunk/src/OFS/tests/event.txt	                        (rev 0)
+++ Zope/trunk/src/OFS/tests/event.txt	2010-01-01 22:46:01 UTC (rev 107521)
@@ -0,0 +1,473 @@
+================
+Container events
+================
+
+Zope container events are used to inform subscribers that an object is
+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 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::
+
+  <five:deprecatedManageAddDelete class="some.content.class"/>
+
+Classes that don't have this directive but still have manage_afterAdd &
+co methods will trigger a warning when they are called (and this is
+strictly a compatibility call, behavior may not be strictly equivalent
+to the original one).
+
+Test setup
+==========
+
+A bit of setup for the tests. Because we'll test copy/paste, we need to
+work inside a database::
+
+  >>> import ZODB.tests.util
+  >>> db = ZODB.tests.util.DB()
+  >>> connection = db.open()
+  >>> root = connection.root()
+
+We'll use a few simple classes (defined in python code for picklability)
+for our tests.
+
+  >>> from OFS.tests.test_event import MyApp, MyContent
+  >>> from OFS.tests.test_event import MyFolder, MyBTreeFolder
+  >>> from OFS.tests.test_event import MyOrderedFolder
+
+  >>> app = MyApp('')
+  >>> root['app'] = app
+  >>> folder = MyFolder('folder')
+  >>> app._setObject('folder', folder) # doctest: +NORMALIZE_WHITESPACE
+  old manage_afterAdd folder folder
+  'folder'
+  >>> folder = app.folder
+  >>> btfolder = MyBTreeFolder('btfolder')
+  >>> app._setObject('btfolder', btfolder) # doctest: +NORMALIZE_WHITESPACE
+  old manage_afterAdd btfolder btfolder
+  'btfolder'
+
+To observe what object events are dispatched, we'll have some
+subscribers print them. We'll actually do that for a specific interface,
+not for (None, IObjectEvent), and register our subscribers before the
+framework's ones, so ours will be called first. This has the effect that
+printed events will be in their "natural" order::
+
+  >>> from zope.component.interfaces import IObjectEvent, IRegistrationEvent
+  >>> from zope.lifecycleevent.interfaces import IObjectMovedEvent
+  >>> from zope.lifecycleevent.interfaces import IObjectCopiedEvent
+  >>> from OFS.interfaces import IObjectWillBeMovedEvent
+  >>> from OFS.interfaces import IObjectClonedEvent
+  >>> from OFS.interfaces import IItem
+  >>> def printObjectEvent(object, event):
+  ...     print event.__class__.__name__, object.getId()
+  >>> def printObjectEventExceptSome(object, event):
+  ...     if (IObjectMovedEvent.providedBy(event) or
+  ...         IObjectCopiedEvent.providedBy(event) or
+  ...         IObjectWillBeMovedEvent.providedBy(event) or
+  ...         IObjectClonedEvent.providedBy(event) or
+  ...         IRegistrationEvent.providedBy(event)):
+  ...         return
+  ...     print event.__class__.__name__, object.getId()
+
+  >>> from zope.component import provideHandler
+  >>> provideHandler(printObjectEvent, (IItem, IObjectMovedEvent))
+  >>> provideHandler(printObjectEvent, (IItem, IObjectCopiedEvent))
+  >>> provideHandler(printObjectEvent, (IItem, IObjectWillBeMovedEvent))
+  >>> provideHandler(printObjectEvent, (IItem, IObjectClonedEvent))
+  >>> provideHandler(printObjectEventExceptSome, (None, IObjectEvent))
+
+Finally we need to load the subscribers configuration::
+
+  >>> import zope.component
+  >>> import OFS.subscribers
+  >>> zope.component.provideAdapter(OFS.subscribers.ObjectManagerSublocations)
+  >>> zope.component.provideHandler(OFS.subscribers.dispatchObjectWillBeMovedEvent)
+  >>> zope.component.provideHandler(OFS.subscribers.dispatchObjectMovedEvent)
+  >>> zope.component.provideHandler(OFS.subscribers.dispatchObjectCopiedEvent)
+  >>> zope.component.provideHandler(OFS.subscribers.dispatchObjectClonedEvent)
+
+We need at least one fake deprecated method to tell the compatibility
+framework that component architecture is initialized::
+
+  >>> from OFS.metaconfigure import setDeprecatedManageAddDelete
+  >>> class C(object): pass
+  >>> setDeprecatedManageAddDelete(C)
+
+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
+called, but with a deprecation warning::
+
+  >>> sub = MyFolder('sub')
+  >>> folder._setObject('sub', sub)
+  ObjectWillBeAddedEvent sub
+  ObjectAddedEvent sub
+  old manage_afterAdd sub sub folder
+  ContainerModifiedEvent folder
+  'sub'
+  >>> sub = folder.sub
+  >>> ob = MyContent('dog')
+  >>> sub._setObject('dog', ob)
+  ObjectWillBeAddedEvent dog
+  ObjectAddedEvent dog
+  old manage_afterAdd dog dog sub
+  ContainerModifiedEvent sub
+  'dog'
+
+And when we rename the subfolder, manage_beforeDelete is also called
+bottom-up and events are sent::
+
+  >>> folder.manage_renameObject('sub', 'marine')
+  ObjectWillBeMovedEvent sub
+  ObjectWillBeMovedEvent dog
+  old manage_beforeDelete dog sub folder
+  old manage_beforeDelete sub sub folder
+  ObjectMovedEvent marine
+  old manage_afterAdd marine marine folder
+  ObjectMovedEvent dog
+  old manage_afterAdd dog marine folder
+  ContainerModifiedEvent folder
+
+Same thing for clone::
+
+  >>> res = folder.manage_clone(folder.marine, 'tank')
+  ObjectCopiedEvent tank
+  ObjectCopiedEvent dog
+  ObjectWillBeAddedEvent tank
+  ObjectWillBeAddedEvent dog
+  ObjectAddedEvent tank
+  old manage_afterAdd tank tank folder
+  ObjectAddedEvent dog
+  old manage_afterAdd dog tank folder
+  ContainerModifiedEvent folder
+  ObjectClonedEvent tank
+  old manage_afterClone tank tank
+  ObjectClonedEvent dog
+  old manage_afterClone dog tank
+  >>> res.getId()
+  'tank'
+
+Old class with deprecatedManageAddDelete
+========================================
+
+We specify that our class is deprecated (using zcml in real life)::
+
+  >>> setDeprecatedManageAddDelete(MyContent)
+  >>> setDeprecatedManageAddDelete(MyFolder)
+  >>> setDeprecatedManageAddDelete(MyOrderedFolder)
+
+Now some events are sent but the old manage_afterAdd method is also
+called correctly::
+
+  >>> ob = MyContent('lassie')
+  >>> folder._setObject('lassie', ob)
+  ObjectWillBeAddedEvent lassie
+  ObjectAddedEvent lassie
+  old manage_afterAdd lassie lassie folder
+  ContainerModifiedEvent folder
+  'lassie'
+
+And when we delete the object, manage_beforeDelete is also called and
+events are sent::
+
+  >>> folder.manage_delObjects('lassie')
+  ObjectWillBeRemovedEvent lassie
+  old manage_beforeDelete lassie lassie folder
+  ObjectRemovedEvent lassie
+  ContainerModifiedEvent folder
+
+The old behavior happens for a move or a copy, with events too.
+For a move::
+
+  >>> ob = MyContent('blueberry')
+  >>> folder._setObject('blueberry', ob)
+  ObjectWillBeAddedEvent blueberry
+  ObjectAddedEvent blueberry
+  old manage_afterAdd blueberry blueberry folder
+  ContainerModifiedEvent folder
+  'blueberry'
+  >>> cp = folder.manage_cutObjects('blueberry')
+  >>> folder.manage_pasteObjects(cp)
+  ObjectWillBeMovedEvent blueberry
+  old manage_beforeDelete blueberry blueberry folder
+  ObjectMovedEvent blueberry
+  old manage_afterAdd blueberry blueberry folder
+  ContainerModifiedEvent folder
+  [{'new_id': 'blueberry', 'id': 'blueberry'}]
+
+Old behavior with events for a copy::
+
+  >>> cp = folder.manage_copyObjects('blueberry')
+  >>> folder.manage_pasteObjects(cp)
+  ObjectCopiedEvent copy_of_blueberry
+  ObjectWillBeAddedEvent copy_of_blueberry
+  ObjectAddedEvent copy_of_blueberry
+  old manage_afterAdd copy_of_blueberry copy_of_blueberry folder
+  ContainerModifiedEvent folder
+  ObjectClonedEvent copy_of_blueberry
+  old manage_afterClone copy_of_blueberry copy_of_blueberry
+  [{'new_id': 'copy_of_blueberry', 'id': 'blueberry'}]
+
+Old behavior with events for a renaming::
+
+  >>> folder.manage_renameObject('copy_of_blueberry', 'myrtille')
+  ObjectWillBeMovedEvent copy_of_blueberry
+  old manage_beforeDelete copy_of_blueberry copy_of_blueberry folder
+  ObjectMovedEvent myrtille
+  old manage_afterAdd myrtille myrtille folder
+  ContainerModifiedEvent folder
+
+Old behavior with events for a clone::
+
+  >>> res = folder.manage_clone(folder.blueberry, 'strawberry')
+  ObjectCopiedEvent strawberry
+  ObjectWillBeAddedEvent strawberry
+  ObjectAddedEvent strawberry
+  old manage_afterAdd strawberry strawberry folder
+  ContainerModifiedEvent folder
+  ObjectClonedEvent strawberry
+  old manage_afterClone strawberry strawberry
+  >>> res.getId()
+  'strawberry'
+
+Events are also sent when we work with a BTreeFolder::
+
+  >>> ob = MyContent('luckyluke')
+  >>> btfolder._setObject('luckyluke', ob)
+  ObjectWillBeAddedEvent luckyluke
+  ObjectAddedEvent luckyluke
+  old manage_afterAdd luckyluke luckyluke btfolder
+  ContainerModifiedEvent btfolder
+  'luckyluke'
+
+  >>> btfolder.manage_delObjects('luckyluke')
+  ObjectWillBeRemovedEvent luckyluke
+  old manage_beforeDelete luckyluke luckyluke btfolder
+  ObjectRemovedEvent luckyluke
+  ContainerModifiedEvent btfolder
+
+Here is what happens for a tree of objects. Let's create a simple one::
+
+  >>> subfolder = MyFolder('subfolder')
+  >>> folder._setObject('subfolder', subfolder)
+  ObjectWillBeAddedEvent subfolder
+  ObjectAddedEvent subfolder
+  old manage_afterAdd subfolder subfolder folder
+  ContainerModifiedEvent folder
+  'subfolder'
+  >>> subfolder = folder.subfolder
+  >>> ob = MyContent('donald')
+  >>> subfolder._setObject('donald', ob)
+  ObjectWillBeAddedEvent donald
+  ObjectAddedEvent donald
+  old manage_afterAdd donald donald subfolder
+  ContainerModifiedEvent subfolder
+  'donald'
+
+Renaming a tree of objects. Note that manage_beforeDelete is called
+bottom-up::
+
+  >>> folder.manage_renameObject('subfolder', 'pluto')
+  ObjectWillBeMovedEvent subfolder
+  ObjectWillBeMovedEvent donald
+  old manage_beforeDelete donald subfolder folder
+  old manage_beforeDelete subfolder subfolder folder
+  ObjectMovedEvent pluto
+  old manage_afterAdd pluto pluto folder
+  ObjectMovedEvent donald
+  old manage_afterAdd donald pluto folder
+  ContainerModifiedEvent folder
+
+Cloning a tree of objects::
+
+  >>> res = folder.manage_clone(folder.pluto, 'mickey')
+  ObjectCopiedEvent mickey
+  ObjectCopiedEvent donald
+  ObjectWillBeAddedEvent mickey
+  ObjectWillBeAddedEvent donald
+  ObjectAddedEvent mickey
+  old manage_afterAdd mickey mickey folder
+  ObjectAddedEvent donald
+  old manage_afterAdd donald mickey folder
+  ContainerModifiedEvent folder
+  ObjectClonedEvent mickey
+  old manage_afterClone mickey mickey
+  ObjectClonedEvent donald
+  old manage_afterClone donald mickey
+  >>> res.getId()
+  'mickey'
+
+New class
+=========
+
+If we use classes that don't have any manage_afterAdd & co method,
+everything happens correctly::
+
+  >>> from OFS.tests.test_event import MyNewFolder, MyNewContent
+  >>> app = MyApp('')
+  >>> root['app'] = app
+  >>> folder = MyNewFolder('folder')
+  >>> app._setObject('folder', folder) # doctest: +NORMALIZE_WHITESPACE
+  ObjectWillBeAddedEvent folder
+  ObjectAddedEvent folder
+  ContainerModifiedEvent
+  'folder'
+  >>> folder = app.folder
+
+  >>> ob = MyNewContent('dogbert')
+  >>> folder._setObject('dogbert', ob)
+  ObjectWillBeAddedEvent dogbert
+  ObjectAddedEvent dogbert
+  ContainerModifiedEvent folder
+  'dogbert'
+  >>> folder.manage_delObjects('dogbert')
+  ObjectWillBeRemovedEvent dogbert
+  ObjectRemovedEvent dogbert
+  ContainerModifiedEvent folder
+
+Now move::
+
+  >>> ob = MyNewContent('dilbert')
+  >>> folder._setObject('dilbert', ob)
+  ObjectWillBeAddedEvent dilbert
+  ObjectAddedEvent dilbert
+  ContainerModifiedEvent folder
+  'dilbert'
+  >>> cp = folder.manage_cutObjects('dilbert')
+  >>> folder.manage_pasteObjects(cp)
+  ObjectWillBeMovedEvent dilbert
+  ObjectMovedEvent dilbert
+  ContainerModifiedEvent folder
+  [{'new_id': 'dilbert', 'id': 'dilbert'}]
+
+And copy::
+
+  >>> cp = folder.manage_copyObjects('dilbert')
+  >>> folder.manage_pasteObjects(cp)
+  ObjectCopiedEvent copy_of_dilbert
+  ObjectWillBeAddedEvent copy_of_dilbert
+  ObjectAddedEvent copy_of_dilbert
+  ContainerModifiedEvent folder
+  ObjectClonedEvent copy_of_dilbert
+  [{'new_id': 'copy_of_dilbert', 'id': 'dilbert'}]
+
+Then rename::
+
+  >>> folder.manage_renameObject('copy_of_dilbert', 'wally')
+  ObjectWillBeMovedEvent copy_of_dilbert
+  ObjectMovedEvent wally
+  ContainerModifiedEvent folder
+
+Or copy using manage_clone::
+
+  >>> res = folder.manage_clone(folder.dilbert, 'phb')
+  ObjectCopiedEvent phb
+  ObjectWillBeAddedEvent phb
+  ObjectAddedEvent phb
+  ContainerModifiedEvent folder
+  ObjectClonedEvent phb
+  >>> res.getId()
+  'phb'
+
+Also on a BTreeFolder::
+
+  >>> ob = MyNewContent('alice')
+  >>> btfolder._setObject('alice', ob)
+  ObjectWillBeAddedEvent alice
+  ObjectAddedEvent alice
+  ContainerModifiedEvent btfolder
+  'alice'
+  >>> btfolder.manage_renameObject('alice', 'rabbit')
+  ObjectWillBeMovedEvent alice
+  ObjectMovedEvent rabbit
+  ContainerModifiedEvent btfolder
+  >>> btfolder.manage_delObjects('rabbit')
+  ObjectWillBeRemovedEvent rabbit
+  ObjectRemovedEvent rabbit
+  ContainerModifiedEvent btfolder
+
+Now for a tree of objects. Let's create a simple one::
+
+  >>> subfolder = MyNewFolder('subfolder')
+  >>> folder._setObject('subfolder', subfolder)
+  ObjectWillBeAddedEvent subfolder
+  ObjectAddedEvent subfolder
+  ContainerModifiedEvent folder
+  'subfolder'
+  >>> subfolder = folder.subfolder
+  >>> ob = MyNewContent('mel')
+  >>> subfolder._setObject('mel', ob)
+  ObjectWillBeAddedEvent mel
+  ObjectAddedEvent mel
+  ContainerModifiedEvent subfolder
+  'mel'
+
+Renaming a tree of objects::
+
+  >>> folder.manage_renameObject('subfolder', 'firefly')
+  ObjectWillBeMovedEvent subfolder
+  ObjectWillBeMovedEvent mel
+  ObjectMovedEvent firefly
+  ObjectMovedEvent mel
+  ContainerModifiedEvent folder
+
+Cloning a tree of objects::
+
+  >>> res = folder.manage_clone(folder.firefly, 'serenity')
+  ObjectCopiedEvent serenity
+  ObjectCopiedEvent mel
+  ObjectWillBeAddedEvent serenity
+  ObjectWillBeAddedEvent mel
+  ObjectAddedEvent serenity
+  ObjectAddedEvent mel
+  ContainerModifiedEvent folder
+  ObjectClonedEvent serenity
+  ObjectClonedEvent mel
+  >>> res.getId()
+  'serenity'
+
+OrderedFolder has the same renaming behavior than before::
+
+  >>> ofolder = MyOrderedFolder('ofolder')
+  >>> app._setObject('ofolder', ofolder) # doctest: +NORMALIZE_WHITESPACE
+  ObjectWillBeAddedEvent ofolder
+  ObjectAddedEvent ofolder
+  old manage_afterAdd ofolder ofolder
+  ContainerModifiedEvent
+  'ofolder'
+  >>> ob1 = MyNewContent('ob1')
+  >>> ofolder._setObject('ob1', ob1)
+  ObjectWillBeAddedEvent ob1
+  ObjectAddedEvent ob1
+  ContainerModifiedEvent ofolder
+  'ob1'
+  >>> ob2 = MyNewContent('ob2')
+  >>> ofolder._setObject('ob2', ob2)
+  ObjectWillBeAddedEvent ob2
+  ObjectAddedEvent ob2
+  ContainerModifiedEvent ofolder
+  'ob2'
+  >>> ofolder.manage_renameObject('ob1', 'ob4')
+  ObjectWillBeMovedEvent ob1
+  ObjectMovedEvent ob4
+  ContainerModifiedEvent ofolder
+  >>> ofolder.objectIds()
+  ['ob4', 'ob2']
+
+When subobjects are reordered, an event about the container is sent::
+
+  >>> ofolder.moveObjectsUp('ob2')
+  ContainerModifiedEvent ofolder
+  1
+  >>> ofolder.objectIds()
+  ['ob2', 'ob4']
+
+Now cleanup::
+
+  >>> import transaction
+  >>> transaction.abort()

Modified: Zope/trunk/src/OFS/tests/testCopySupportHooks.py
===================================================================
--- Zope/trunk/src/OFS/tests/testCopySupportHooks.py	2010-01-01 22:37:20 UTC (rev 107520)
+++ Zope/trunk/src/OFS/tests/testCopySupportHooks.py	2010-01-01 22:46:01 UTC (rev 107521)
@@ -57,7 +57,7 @@
         Folder.manage_beforeDelete(self, item, container)
 
 
-from Products.Five.eventconfigure import setDeprecatedManageAddDelete
+from OFS.metaconfigure import setDeprecatedManageAddDelete
 
 class HookLayer:
 

Modified: Zope/trunk/src/OFS/tests/testObjectManager.py
===================================================================
--- Zope/trunk/src/OFS/tests/testObjectManager.py	2010-01-01 22:37:20 UTC (rev 107520)
+++ Zope/trunk/src/OFS/tests/testObjectManager.py	2010-01-01 22:46:01 UTC (rev 107521)
@@ -11,11 +11,11 @@
 from Acquisition import Implicit
 from App.config import getConfiguration
 from logging import getLogger
+from OFS.metaconfigure import setDeprecatedManageAddDelete
 from OFS.ObjectManager import ObjectManager
 from OFS.SimpleItem import SimpleItem
 import Products.Five
 from Products.Five import zcml
-from Products.Five.eventconfigure import setDeprecatedManageAddDelete
 from zExceptions import BadRequest
 
 logger = getLogger('OFS.subscribers')            
@@ -75,8 +75,7 @@
         self.saved_cfg_debug_mode = getConfiguration().debug_mode
         zcml.load_config('meta.zcml', Products.Five)
         import OFS
-        zcml.load_config('event.zcml', OFS)
-        zcml.load_config('deprecated.zcml', Products.Five)
+        zcml.load_config('configure.zcml', OFS)
         setDeprecatedManageAddDelete(ItemForDeletion)
 
     def tearDown( self ):

Copied: Zope/trunk/src/OFS/tests/test_event.py (from rev 107512, Zope/trunk/src/Products/Five/tests/test_event.py)
===================================================================
--- Zope/trunk/src/OFS/tests/test_event.py	                        (rev 0)
+++ Zope/trunk/src/OFS/tests/test_event.py	2010-01-01 22:46:01 UTC (rev 107521)
@@ -0,0 +1,88 @@
+##############################################################################
+#
+# Copyright (c) 2004, 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.
+#
+##############################################################################
+"""Test events triggered by Five
+
+$Id$
+"""
+
+# These classes aren't defined in the doctest because otherwise
+# they wouldn't be picklable, and we need that to test copy/paste.
+
+from OFS.SimpleItem import SimpleItem
+from OFS.Folder import Folder
+from OFS.OrderedFolder import OrderedFolder
+from Products.BTreeFolder2.BTreeFolder2 import BTreeFolder2
+
+from zope.component import testing, eventtesting
+
+def setUp(test):
+    testing.setUp(test)
+    eventtesting.setUp(test)
+
+class DontComplain(object):
+    def _verifyObjectPaste(self, object, validate_src=1):
+        pass
+    def cb_isMoveable(self):
+        return True
+    def cb_isCopyable(self):
+        return True
+
+class NotifyBase(DontComplain):
+    def manage_afterAdd(self, item, container):
+        print 'old manage_afterAdd %s %s %s' % (self.getId(), item.getId(),
+                                                container.getId())
+        super(NotifyBase, self).manage_afterAdd(item, container)
+    manage_afterAdd.__five_method__ = True # Shut up deprecation warnings
+    def manage_beforeDelete(self, item, container):
+        super(NotifyBase, self).manage_beforeDelete(item, container)
+        print 'old manage_beforeDelete %s %s %s' % (self.getId(), item.getId(),
+                                                    container.getId())
+    manage_beforeDelete.__five_method__ = True # Shut up deprecation warnings
+    def manage_afterClone(self, item):
+        print 'old manage_afterClone %s %s' % (self.getId(), item.getId())
+        super(NotifyBase, self).manage_afterClone(item)
+    manage_afterClone.__five_method__ = True # Shut up deprecation warnings
+
+class MyApp(Folder):
+    def getPhysicalRoot(self):
+        return self
+
+class MyFolder(NotifyBase, Folder):
+    pass
+
+class MyOrderedFolder(NotifyBase, OrderedFolder):
+    pass
+
+class MyBTreeFolder(NotifyBase, BTreeFolder2):
+    def _verifyObjectPaste(self, object, validate_src=1):
+        pass
+
+class MyContent(NotifyBase, SimpleItem):
+    def __init__(self, id):
+        self._setId(id)
+
+# These don't have manage_beforeDelete & co methods
+
+class MyNewContent(DontComplain, SimpleItem):
+    def __init__(self, id):
+        self._setId(id)
+
+class MyNewFolder(DontComplain, Folder):
+    pass
+
+
+def test_suite():
+    from zope.testing.doctest import DocFileSuite
+    return DocFileSuite('event.txt', package="OFS.tests",
+                        setUp=setUp, tearDown=testing.tearDown)

Copied: Zope/trunk/src/OFS/tests/test_registerclass.py (from rev 107512, Zope/trunk/src/Products/Five/tests/test_registerclass.py)
===================================================================
--- Zope/trunk/src/OFS/tests/test_registerclass.py	                        (rev 0)
+++ Zope/trunk/src/OFS/tests/test_registerclass.py	2010-01-01 22:46:01 UTC (rev 107521)
@@ -0,0 +1,145 @@
+##############################################################################
+#
+# Copyright (c) 2004, 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.
+#
+##############################################################################
+"""Unit tests for the registerClass directive.
+
+$Id$
+"""
+
+def test_registerClass():
+    """
+    Testing registerClass
+
+      >>> from zope.component.testing import setUp, tearDown
+      >>> setUp()
+      >>> import Products
+      >>> import Products.Five
+      >>> from Products.Five import zcml
+      >>> from Products.Five.tests.testing.simplecontent import SimpleContent
+      >>> from Products.Five.tests.testing.simplecontent import ISimpleContent
+      >>> from persistent.interfaces import IPersistent
+
+    Use the five:registerClass directive::
+
+      >>> configure_zcml = '''
+      ... <configure
+      ...     xmlns="http://namespaces.zope.org/zope"
+      ...     xmlns:five="http://namespaces.zope.org/five"
+      ...     i18n_domain="foo">
+      ...   <permission id="foo.add" title="Add Foo"/>
+      ...   <five:registerClass
+      ...       class="Products.Five.tests.testing.simplecontent.SimpleContent"
+      ...       meta_type="Foo Type"
+      ...       permission="foo.add"
+      ...       addview="addfoo.html"
+      ...       icon="foo_icon.png"
+      ...       global="false"
+      ...       />
+      ... </configure>'''
+      >>> zcml.load_config('meta.zcml', Products.Five)
+      >>> zcml.load_string(configure_zcml)
+
+    Make sure that the class attributes are set correctly::
+
+      >>> SimpleContent.meta_type
+      'Foo Type'
+      >>> SimpleContent.icon
+      '++resource++foo_icon.png'
+
+    And the meta_type is registered correctly::
+
+      >>> for info in Products.meta_types:
+      ...     if info['name'] == 'Foo Type':
+      ...         break
+      >>> info['product']
+      'Five'
+      >>> info['permission']
+      'Add Foo'
+      >>> ISimpleContent in info['interfaces']
+      True
+      >>> IPersistent in info['interfaces']
+      True
+      >>> info['visibility'] is None
+      True
+      >>> info['instance'] is SimpleContent
+      True
+      >>> info['action']
+      '+/addfoo.html'
+      >>> info['container_filter'] is None
+      True
+
+    Now reset everything and see what happens without optional parameters::
+
+      >>> tearDown()
+      >>> setUp()
+
+    Use the five:registerClass directive again::
+
+      >>> configure_zcml = '''
+      ... <configure
+      ...     xmlns="http://namespaces.zope.org/zope"
+      ...     xmlns:five="http://namespaces.zope.org/five"
+      ...     i18n_domain="bar">
+      ...   <permission id="bar.add" title="Add Bar"/>
+      ...   <five:registerClass
+      ...       class="Products.Five.tests.testing.simplecontent.SimpleContent"
+      ...       meta_type="Bar Type"
+      ...       permission="bar.add"
+      ...       />
+      ... </configure>'''
+      >>> zcml.load_config('meta.zcml', Products.Five)
+      >>> zcml.load_string(configure_zcml)
+
+    Make sure that the class attributes are set correctly::
+
+      >>> SimpleContent.meta_type
+      'Bar Type'
+      >>> SimpleContent.icon
+      ''
+
+    And the meta_type is registered correctly::
+
+      >>> for info in Products.meta_types:
+      ...     if info['name'] == 'Bar Type':
+      ...         break
+      >>> info['product']
+      'Five'
+      >>> info['permission']
+      'Add Bar'
+      >>> ISimpleContent in info['interfaces']
+      True
+      >>> IPersistent in info['interfaces']
+      True
+      >>> info['visibility']
+      'Global'
+      >>> info['instance'] is SimpleContent
+      True
+      >>> info['action']
+      ''
+      >>> info['container_filter'] is None
+      True
+
+    Clean up:
+
+      >>> tearDown()
+      >>> SimpleContent.meta_type
+      'simple item'
+      >>> SimpleContent.icon
+      ''
+      >>> [info for info in Products.meta_types if info['name'] == 'Bar Type']
+      []
+    """
+
+def test_suite():
+    from Testing.ZopeTestCase import ZopeDocTestSuite
+    return ZopeDocTestSuite()

Copied: Zope/trunk/src/OFS/tests/test_registerpackage.py (from rev 107512, Zope/trunk/src/Products/Five/tests/test_registerpackage.py)
===================================================================
--- Zope/trunk/src/OFS/tests/test_registerpackage.py	                        (rev 0)
+++ Zope/trunk/src/OFS/tests/test_registerpackage.py	2010-01-01 22:46:01 UTC (rev 107521)
@@ -0,0 +1,86 @@
+##############################################################################
+#
+# Copyright (c) 2006 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.
+#
+##############################################################################
+"""Unit tests for the registerPackage directive.
+
+$Id$
+"""
+import sys
+
+# need to add the testing package to the pythonpath in order to
+# test python-packages-as-products
+from Products.Five.tests import testing
+sys.path.append(testing.__path__[0])
+
+def test_registerPackage():
+    """
+    Testing registerPackage
+
+      >>> from zope.component.testing import setUp, tearDown
+      >>> setUp()
+      >>> import Products
+      >>> import Products.Five
+      >>> from Products.Five import zcml
+      >>> zcml.load_config('meta.zcml', Products.Five)
+
+    Make sure a python package with a valid initialize gets its
+    initialize function called::
+    
+      >>> configure_zcml = '''
+      ... <configure
+      ...     xmlns="http://namespaces.zope.org/zope"
+      ...     xmlns:five="http://namespaces.zope.org/five"
+      ...     i18n_domain="foo">
+      ...   <five:registerPackage
+      ...       package="pythonproduct2"
+      ...       initialize="pythonproduct2.initialize"
+      ...       />
+      ... </configure>'''
+      >>> zcml.load_string(configure_zcml)
+      
+    We need to load the product as well. This would normally happen during 
+    Zope startup, but in the test, we're already too late.
+    
+      >>> import Zope2
+      >>> from OFS.Application import install_products
+      
+      >>> app = Zope2.app()
+      >>> install_products(app)
+      pythonproduct2 initialized
+      
+    Test to see if the pythonproduct2 python package actually gets setup
+    as a zope2 product in the Control Panel.
+
+      >>> product_listing = []
+      >>> try:
+      ...    product_listing = app.Control_Panel.Products.objectIds()
+      ... finally:
+      ...     app._p_jar.close()
+      >>> 'pythonproduct2' in product_listing
+      True
+
+    Make sure it also shows up in ``Products._registered_packages``.
+
+      >>> [x.__name__ for x in getattr(Products, '_registered_packages', [])]
+      ['pythonproduct2']
+
+    Clean up:
+
+      >>> tearDown()
+    """
+
+
+def test_suite():
+    # Must use functional because registerPackage commits
+    from Testing.ZopeTestCase import FunctionalDocTestSuite
+    return FunctionalDocTestSuite()

Modified: Zope/trunk/src/Products/Five/configure.zcml
===================================================================
--- Zope/trunk/src/Products/Five/configure.zcml	2010-01-01 22:37:20 UTC (rev 107520)
+++ Zope/trunk/src/Products/Five/configure.zcml	2010-01-01 22:46:01 UTC (rev 107521)
@@ -3,7 +3,6 @@
 
   <include file="meta.zcml" />
   <include file="permissions.zcml" />
-  <include file="deprecated.zcml"/>
 
   <include package="zope.traversing" />
   <include package="OFS "/>

Deleted: Zope/trunk/src/Products/Five/deprecated.zcml
===================================================================
--- Zope/trunk/src/Products/Five/deprecated.zcml	2010-01-01 22:37:20 UTC (rev 107520)
+++ Zope/trunk/src/Products/Five/deprecated.zcml	2010-01-01 22:46:01 UTC (rev 107521)
@@ -1,39 +0,0 @@
-<configure xmlns="http://namespaces.zope.org/zope"
-           xmlns:five="http://namespaces.zope.org/five">
-
-  <!-- deprecated in core Zope, should be fixed there -->
-
-  <five:deprecatedManageAddDelete
-      class="AccessControl.User.BasicUserFolder"/>
-
-  <five:deprecatedManageAddDelete
-      class="App.Permission.Permission"/>
-
-  <five:deprecatedManageAddDelete
-      class="HelpSys.HelpTopic.HelpTopicBase"/>
-
-  <five:deprecatedManageAddDelete
-      class="OFS.Cache.CacheManager"/>
-
-  <five:deprecatedManageAddDelete
-      class="Products.PythonScripts.PythonScript.PythonScript"/>
-
-  <five:deprecatedManageAddDelete
-      class="Products.Sessions.BrowserIdManager.BrowserIdManager"/>
-  <five:deprecatedManageAddDelete
-      class="Products.Sessions.SessionDataManager.SessionDataManager"/>
-
-  <five:deprecatedManageAddDelete
-      class="Products.SiteAccess.VirtualHostMonster.VirtualHostMonster"/>
-  <five:deprecatedManageAddDelete
-      class="Products.SiteAccess.SiteRoot.Traverser"/>
-
-  <five:deprecatedManageAddDelete
-      class="Products.SiteErrorLog.SiteErrorLog.SiteErrorLog"/>
-
-  <five:deprecatedManageAddDelete
-      class="Products.ZCatalog.CatalogAwareness.CatalogAware"/>
-  <five:deprecatedManageAddDelete
-      class="Products.ZCatalog.CatalogPathAwareness.CatalogAware"/>
-
-</configure>

Added: Zope/trunk/src/Products/Five/deprecated.zcml
===================================================================
--- Zope/trunk/src/Products/Five/deprecated.zcml	                        (rev 0)
+++ Zope/trunk/src/Products/Five/deprecated.zcml	2010-01-01 22:46:01 UTC (rev 107521)
@@ -0,0 +1,5 @@
+<configure xmlns="http://namespaces.zope.org/zope">
+
+  <include package="OFS" file="deprecated.zcml"/>
+
+</configure>


Property changes on: Zope/trunk/src/Products/Five/deprecated.zcml
___________________________________________________________________
Added: svn:eol-style
   + native

Deleted: Zope/trunk/src/Products/Five/doc/event.txt
===================================================================
--- Zope/trunk/src/Products/Five/doc/event.txt	2010-01-01 22:37:20 UTC (rev 107520)
+++ Zope/trunk/src/Products/Five/doc/event.txt	2010-01-01 22:46:01 UTC (rev 107521)
@@ -1,283 +0,0 @@
-Events in Zope 2
-================
-
-Zope 2 supports zope.lifecycleevent style events including container events.
-
-With container events, you finally have the ability to react to things
-happening to objects without have to subclass ``manage_afterAdd``,
-``manage_beforeDelete`` or ``manage_afterClone``. Instead, you just have
-to register a subscriber for the appropriate event, for instance
-IObjectAddedEvent, and make it do the work.
-
-Indeed, the old methods like ``manage_afterAdd`` are now discouraged, you
-shouldn't use them anymore.
-
-Let's see how to migrate your products.
-
-Old product
------------
-
-Suppose that in an old product you have code that needs to register
-through a central tool whenever a document is created. Or it could be
-indexing itself. Or it could initialize an attribute according to its
-current path. Code like::
-
-    class CoolDocument(...):
-        ...
-        def manage_afterAdd(self, item, container):
-            self.mangled_path = mangle('/'.join(self.getPhysicalPath()))
-            getToolByName(self, 'portal_cool').registerCool(self)
-            super(CoolDocument, self).manage_afterAdd(item, container)
-
-        def manage_afterClone(self, item):
-            self.mangled_path = mangle('/'.join(self.getPhysicalPath()))
-            getToolByName(self, 'portal_cool').registerCool(self)
-            super(CoolDocument, self).manage_afterClone(item)
-
-        def manage_beforeDelete(self, item, container):
-            super(CoolDocument, self).manage_beforeDelete(item, container)
-            getToolByName(self, 'portal_cool').unregisterCool(self)
-
-This had been the best practice in old Zope 2 versions. Note the use of
-``super()`` to call the base class, which is often omitted because people
-"know" that SimpleItem for instance doesn't do anything in these methods.
-
-If you run this code today, you will get deprecation warnings,
-telling you that::
-
-    Calling Products.CoolProduct.CoolDocument.CoolDocument.manage_afterAdd
-    is discouraged. You should use event subscribers instead.
-
-Using five:deprecatedManageAddDelete
-------------------------------------
-
-The simplest thing you can do to deal with the deprecation warnings, and
-have correct behavior, is to add in your products a ``configure.zcml``
-file containing::
-
-    <configure
-        xmlns="http://namespaces.zope.org/zope"
-        xmlns:five="http://namespaces.zope.org/five">
-
-      <five:deprecatedManageAddDelete
-          class="Products.CoolProduct.CoolDocument.CoolDocument"/>
-
-    </configure>
-
-This tells Zope that you acknowledge that your class contains deprecated
-methods, and ask it to still call them in the proper manner. So Zope
-will be sending events when an object is added, for instance, and in
-addition call your old ``manage_afterAdd`` method.
-
-One subtlety here is that you may have to modify your methods to just do
-their work, and not call their super class. This is necessary because
-proper events are already dispatched to all relevant classes, and the
-work of the super class will be done trough events, you must not redo it
-by hand. If you call the super class, you will get a warning, saying for
-instance::
-
-    CoolDocument.manage_afterAdd is discouraged. You
-    should use an IObjectAddedEvent subscriber instead.
-
-The fact that you must "just do your work" is especially important for
-the rare cases where people subclass the ``manage_afterAdd`` of object
-managers like folders, and decided to reimplement recursion into the
-children themselves. If you do that, then there will be two recursions
-going on in parallel, the one done by events, and the one done by your
-code. This would be bad.
-
-Using subscribers
------------------
-
-In the long run, you will want to use proper subscribers.
-
-First, you'll have to write a subscriber that "does the work", for
-instance::
-
-    def addedCoolDocument(ob, event):
-        """A Cool Document was added to a container."""
-        self.mangled_path = mangle('/'.join(self.getPhysicalPath()))
-
-Note that we're not calling the ``portal_cool`` tool anymore, because
-presumably this tool will also be modified to do its work through
-events, and will have a similar subscriber doing the necessary
-``registerCool``. Note also that here we don't care about the event, but
-in more complex cases we would.
-
-Now we have to register our subscriber for our object. To do that, we
-need to "mark" our object through an interface. We can define in our
-product's ``interfaces.py``::
-
-    from zope.interface import Interface, Attribute
-
-    class ICoolDocument(Interface):
-        """Cool Document."""
-        mangled_path = Attribute("Our mangled path.")
-        ...
-
-Then the class CoolDocument is marked with this interface::
-
-    from zope.interface import implements
-    from Products.CoolProduct.interfaces import ICoolDocument
-    class CoolDocument(...):
-        implements(ICoolDocument)
-        ...
-
-Finally we must link the event and the interface to the subscriber using
-zcml, so in ``configure.zcml`` we'll add::
-
-    ...
-      <subscriber
-          for="Products.CoolProduct.interfaces.ICoolDocument
-               zope.lifecycleevent.interfaces.IObjectAddedEvent"
-          handler="Products.CoolProduct.CoolDocument.addedCoolDocument"
-          />
-    ...
-
-And that's it, everything is plugged. Note that IObjectAddedEvent takes
-care of both ``manage_afterAdd`` and ``manage_afterClone``, as it's sent
-whenever a new object is placed into a container. However this won't
-take care of moves and renamings, we'll see below how to do that.
-
-Event dispatching
------------------
-
-When an IObjectEvent (from which all the events we're talking here
-derive) is initially sent, it concerns one object. For instance, a
-specific object is removed. The ``event.object`` attribute is this
-object.
-
-To be able to know about removals, we could just subscribe to the
-appropriate event using a standard event subscriber. In that case, we'd
-have to filter "by hand" to check if the object removed is of the type
-we're interested in, which would be a chore. In addition, any subobjects
-of the removed object wouldn't know what happens to them, and for
-instance they wouldn't have any way of doing some cleanup before they
-disappear.
-
-To solve these two problems, Zope has an additional mechanism by which
-any IObjectEvent is redispatched using multi-adapters of the form ``(ob,
-event)``, so that a subscriber can be specific about the type of object
-it's interested in. Furthermore, this is done recursively for all
-sublocations ``ob`` of the initial object. The ``event`` won't change
-though, and ``event.object`` will still be the original object for which
-the event was initially sent (this corresponds to ``self`` and ``item``
-in the ``manage_afterAdd`` method -- ``self`` is ``ob``, and ``item`` is
-``event.object``).
-
-Understanding the hierarchy of events is important to see how to
-subscribe to them.
-
- * IObjectEvent is the most general. Any event focused on an object
-   derives from this.
-
- * IObjectMovedEvent is sent when an object changes location or is
-   renamed. It is quite general, as it also encompasses the case where
-   there's no old location (addition) or no new location (removal).
-
- * IObjectAddedEvent and IObjectRemovedEvent both derive from
-   IObjectMovedEvent.
-
- * IObjectCopiedEvent is sent just after an object copy is made, but
-   this doesn't mean the object has been put into its new container yet,
-   so it doesn't have a location.
-
-There are only a few basic use cases about what one wants to do with
-respect to events (but you might want to read the full story in
-Five/tests/event.txt).
-
-The first use case is the one where the object has to be aware of its
-path, like in the CoolDocument example above.
-
-In Zope 2 an object has a new path through creation, copy or move
-(rename is a kind of move). The events sent during these three
-operations are varied: creation sends IObjectAddedEvent, copy sends
-IObjectCopiedEvent then IObjectAddedEvent, and move sends
-IObjectMovedEvent.
-
-So to react to new paths, we have to subscribe to IObjectMovedEvent, but
-this will also get us any IObjectRemovedEvent, which we'll have to
-filter out by hand (this is unfortunate, and due to the way the Zope
-interface hierarchy is organized). So to fix the CoolDocument
-configuration we have to add::
-
-    def movedCoolDocument(ob, event):
-        """A Cool Document was moved."""
-        if not IObjectRemovedEvent.providedBy(event):
-            addedCoolDocument(ob, event)
-
-And replace the subscriber with::
-
-    ...
-      <subscriber
-          for="Products.CoolProduct.interfaces.ICoolDocument
-               zope.lifecycleevent.interfaces.IObjectMovedEvent"
-          handler="Products.CoolProduct.CoolDocument.movedCoolDocument"
-          />
-    ...
-
-The second use case is when the object has to do some cleanup when it is
-removed from its parent. This used to be in ``manage_beforeDelete``, now
-we can do the work in a ``removedCoolDocument`` method and just
-subscribe to IObjectRemovedEvent. But wait, this won't take into account
-moves... So in the same vein as above, we would have to write::
-
-    def movedCoolDocument(ob, event):
-        """A Cool Document was moved."""
-        if not IObjectRemovedEvent.providedBy(event):
-            addedCoolDocument(ob, event)
-        if not IObjectAddedEvent.providedBy(event):
-            removedCoolDocument(ob, event)
-
-The third use case is when your object has to stay registered with some
-tool, for instance indexed in a catalog, or as above registered with
-``portal_cool``. Here we have to know the old object's path to
-unregister it, so we have to be called *before* it is removed. We'll use
-``IObjectWillBe...`` events, that are sent before the actual operations
-take place::
-
-    from OFS.interfaces import IObjectWillBeAddedEvent
-    def beforeMoveCoolDocument(ob, event):
-        """A Cool Document will be moved."""
-        if not IObjectWillBeAddedEvent.providedBy(event):
-            getToolByName(ob, 'portal_cool').unregisterCool(ob)
-
-    def movedCoolDocument(ob, event):
-        """A Cool Document was moved."""
-        if not IObjectRemovedEvent.providedBy(event):
-            getToolByName(ob, 'portal_cool').registerCool(ob)
-        ...
-
-And use an additional subscriber::
-
-    ...
-      <subscriber
-          for="Products.CoolProduct.interfaces.ICoolDocument
-               OFS.interfaces.IObjectWillBeMovedEvent"
-          handler="Products.CoolProduct.CoolDocument.beforeMoveCoolDocument"
-          />
-    ...
-
-This has to be done if the tool cannot react by itself to objects being
-added and removed, which obviously would be better as it's ultimately
-the tool's responsibility and not the object's.
-
-Note that if having tests like::
-
-    if not IObjectWillBeAddedEvent.providedBy(event):
-    if not IObjectRemovedEvent.providedBy(event):
-
-seems cumbersome (and backwards), it is also possible to check what kind
-of event you're dealing with using::
-
-    if event.oldParent is not None:
-    if event.newParent is not None:
-
-(However be careful, the ``oldParent`` and ``newParent`` are the old and
-new parents *of the original object* for which the event was sent, not
-of the one to which the event was redispatched using the
-multi-subscribers we have registered.)
-
-The ``IObjectWillBe...`` events are specific to Zope 2 (and imported
-from ``OFS.interfaces``). zope.lifecycleevent doesn't really need them, as
-object identity is often enough.

Modified: Zope/trunk/src/Products/Five/eventconfigure.py
===================================================================
--- Zope/trunk/src/Products/Five/eventconfigure.py	2010-01-01 22:37:20 UTC (rev 107520)
+++ Zope/trunk/src/Products/Five/eventconfigure.py	2010-01-01 22:46:01 UTC (rev 107521)
@@ -12,28 +12,13 @@
 #
 ##############################################################################
 """
-Use 'structured monkey patching' to enable zope.container event sending for
-Zope 2 objects.
-
 $Id$
 """
 
-from OFS.subscribers import deprecatedManageAddDeleteClasses
+from zope.deferredimport import deprecated
 
-def setDeprecatedManageAddDelete(class_):
-    """Instances of the class will still see their old methods called."""
-    deprecatedManageAddDeleteClasses.append(class_)
-
-def cleanUp():
-    deprecatedManageAddDeleteClasses[:] = []
-
-def deprecatedManageAddDelete(_context, class_):
-    _context.action(
-        discriminator=('five:deprecatedManageAddDelete', class_),
-        callable=setDeprecatedManageAddDelete,
-        args=(class_,),
-        )
-
-from zope.testing.cleanup import addCleanUp
-addCleanUp(cleanUp)
-del addCleanUp
+deprecated("Please import from OFS.metaconfigure",
+    deprecatedManageAddDelete = 'OFS.metaconfigure:deprecatedManageAddDelete',
+    setDeprecatedManageAddDelete = \
+        'OFS.metaconfigure:setDeprecatedManageAddDelete',
+)

Modified: Zope/trunk/src/Products/Five/fiveconfigure.py
===================================================================
--- Zope/trunk/src/Products/Five/fiveconfigure.py	2010-01-01 22:37:20 UTC (rev 107520)
+++ Zope/trunk/src/Products/Five/fiveconfigure.py	2010-01-01 22:46:01 UTC (rev 107521)
@@ -19,81 +19,16 @@
 """
 import os
 import glob
-import logging
 import warnings
 
-import App.config
-import Products
-import Zope2
-
-from zope.interface import classImplements, implementedBy
-from zope.component import getUtility
+from zope.interface import classImplements
 from zope.component.interface import provideInterface
-from zope.configuration import xmlconfig
 from zope.configuration.exceptions import ConfigurationError
 from zope.publisher.interfaces.browser import IDefaultBrowserLayer
-from zope.security.interfaces import IPermission
 
-from Products.Five import isFiveMethod
 from Products.Five.browser.metaconfigure import page
 
-debug_mode = App.config.getConfiguration().debug_mode
-LOG = logging.getLogger('Five')
 
-def findProducts():
-    import Products
-    from types import ModuleType
-    products = []
-    for name in dir(Products):
-        product = getattr(Products, name)
-        if isinstance(product, ModuleType) and hasattr(product, '__file__'):
-            products.append(product)
-    return products
-
-def handleBrokenProduct(product):
-    if debug_mode:
-        # Just reraise the error and let Zope handle it.
-        raise
-    # Not debug mode. Zope should continue to load. Print a log message:
-    # XXX It would be really cool if we could make this product appear broken
-    # in the control panel. However, all attempts to do so has failed from my 
-    # side. //regebro
-    LOG.error('Could not import Product %s' % product.__name__, exc_info=True)
-
-def loadProducts(_context, file=None, files=None, package=None):
-    if file is None:
-        # set the default
-        file = 'configure.zcml'
-
-    if files is not None or package is not None:
-        raise ValueError("Neither the files or package argument is supported.")
-
-    # now load the files if they exist
-    for product in findProducts():
-        zcml = os.path.join(os.path.dirname(product.__file__), file)
-        if os.path.isfile(zcml):
-            try:
-                xmlconfig.include(_context, zcml, package=product)
-            except: # Yes, really, *any* kind of error.
-                handleBrokenProduct(product)
-
-def loadProductsOverrides(_context, file=None, files=None, package=None):
-    if file is None:
-        # set the default
-        file = 'overrides.zcml'
-
-    if files is not None or package is not None:
-        raise ValueError("Neither the files or package argument is supported.")
-
-    # now load the files if they exist
-    for product in findProducts():
-        zcml = os.path.join(os.path.dirname(product.__file__), file)
-        if os.path.isfile(zcml):
-            try:
-                xmlconfig.includeOverrides(_context, zcml, package=product)
-            except: # Yes, really, *any* kind of error.
-                handleBrokenProduct(product)
-
 def implements(_context, class_, interface):
     warnings.warn('Using <five:implements /> is deprecated. Please use the '
                   '<class class="foo.Bar">'
@@ -133,114 +68,18 @@
              layer=layer, for_=for_, template=fname)
 
 
-_register_monkies = []
-_meta_type_regs = []
-def _registerClass(class_, meta_type, permission, addview, icon, global_):
-    setattr(class_, 'meta_type', meta_type)
+from zope.deferredimport import deprecated
 
-    permission_obj = getUtility(IPermission, permission)
-
-    if icon:
-        setattr(class_, 'icon', '++resource++%s' % icon)
-
-    interfaces = tuple(implementedBy(class_))
-
-    info = {'name': meta_type,
-            'action': addview and ('+/%s' % addview) or '',
-            'product': 'Five',
-            'permission': str(permission_obj.title),
-            'visibility': global_ and 'Global' or None,
-            'interfaces': interfaces,
-            'instance': class_,
-            'container_filter': None}
-
-    Products.meta_types += (info,)
-
-    _register_monkies.append(class_)
-    _meta_type_regs.append(meta_type)
-
-def registerClass(_context, class_, meta_type, permission, addview=None,
-                  icon=None, global_=True):
-    _context.action(
-        discriminator = ('registerClass', meta_type),
-        callable = _registerClass,
-        args = (class_, meta_type, permission, addview, icon, global_)
-        )
-
-def _registerPackage(module_, init_func=None):
-    """Registers the given python package as a Zope 2 style product
-    """
-    
-    if not hasattr(module_, '__path__'):
-        raise ValueError("Must be a package and the " \
-                         "package must be filesystem based")
-    
-    registered_packages = getattr(Products, '_registered_packages', None)
-    if registered_packages is None:
-        registered_packages = Products._registered_packages = []
-    registered_packages.append(module_)
-    
-    # Delay the actual setup until the usual product loading time in
-    # OFS.Application. Otherwise, we may get database write errors in
-    # ZEO, when there's no connection with which to write an entry to
-    # Control_Panel. We would also get multiple calls to initialize().
-    to_initialize = getattr(Products, '_packages_to_initialize', None)
-    if to_initialize is None:
-        to_initialize = Products._packages_to_initialize = []
-    to_initialize.append((module_, init_func,))
-
-def registerPackage(_context, package, initialize=None):
-    """ZCML directive function for registering a python package product
-    """
-
-    _context.action(
-        discriminator = ('registerPackage', package),
-        callable = _registerPackage,
-        args = (package,initialize)
-        )
-
-# clean up code
-
-def killMonkey(class_, name, fallback, attr=None):
-    """Die monkey, die!"""
-    method = getattr(class_, name, None)
-    if isFiveMethod(method):
-        original = getattr(class_, fallback, None)
-        if original is not None:
-            delattr(class_, fallback)
-        if original is None or isFiveMethod(original):
-            try:
-                delattr(class_, name)
-            except AttributeError:
-                pass
-        else:
-            setattr(class_, name, original)
-
-    if attr is not None:
-        try:
-            delattr(class_, attr)
-        except (AttributeError, KeyError):
-            pass
-
-def unregisterClass(class_):
-    delattr(class_, 'meta_type')
-    try:
-        delattr(class_, 'icon')
-    except AttributeError:
-        pass
-
-def cleanUp():
-
-    global _register_monkies
-    for class_ in _register_monkies:
-        unregisterClass(class_)
-    _register_monkies = []
-
-    global _meta_type_regs
-    Products.meta_types = tuple([ info for info in Products.meta_types
-                                  if info['name'] not in _meta_type_regs ])
-    _meta_type_regs = []
-
-from zope.testing.cleanup import addCleanUp
-addCleanUp(cleanUp)
-del addCleanUp
+deprecated("Please import from OFS.metaconfigure",
+    findProducts = 'OFS.metaconfigure:findProducts',
+    handleBrokenProduct = 'OFS.metaconfigure:handleBrokenProduct',
+    loadProducts = 'OFS.metaconfigure:loadProducts',
+    loadProductsOverrides = 'OFS.metaconfigure:loadProductsOverrides',
+    _register_monkies = 'OFS.metaconfigure:_register_monkies',
+    _meta_type_regs = 'OFS.metaconfigure:_meta_type_regs',
+    _registerClass = 'OFS.metaconfigure:_registerClass',
+    registerClass = 'OFS.metaconfigure:registerClass',
+    _registerPackage = 'OFS.metaconfigure:_registerPackage',
+    registerPackage = 'OFS.metaconfigure:registerPackage',
+    unregisterClass = 'OFS.metaconfigure:unregisterClass',
+)

Modified: Zope/trunk/src/Products/Five/fivedirectives.py
===================================================================
--- Zope/trunk/src/Products/Five/fivedirectives.py	2010-01-01 22:37:20 UTC (rev 107520)
+++ Zope/trunk/src/Products/Five/fivedirectives.py	2010-01-01 22:46:01 UTC (rev 107521)
@@ -17,10 +17,8 @@
 """
 from zope.interface import Interface
 from zope.browserresource.metadirectives import IBasicResourceInformation
-from zope.security.zcml import Permission
 from zope.configuration.fields import GlobalObject, Tokens
-from zope.configuration.fields import Bool
-from zope.schema import ASCII, TextLine
+from zope.schema import TextLine
 
 
 # Deprecated, the class directive from zope.security allows the same
@@ -38,8 +36,9 @@
         value_type=GlobalObject()
         )
 
+
 class ISizableDirective(Interface):
-    """Make instances of class send events.
+    """Attach sizable adapters to classes.
     """
 
     class_ = GlobalObject(
@@ -48,15 +47,6 @@
         )
 
 
-class IDeprecatedManageAddDeleteDirective(Interface):
-    """Call manage_afterAdd & co for these contained content classes.
-    """
-    class_ = GlobalObject(
-        title=u"Class",
-        required=True,
-        )
-
-
 class IPagesFromDirectoryDirective(IBasicResourceInformation):
     """Register each file in a skin directory as a page resource
     """
@@ -77,75 +67,16 @@
         required=True
         )
 
-class IRegisterClassDirective(Interface):
 
-    """registerClass directive schema.
-
-    Register Five content with Zope 2.
-    """
-
-    class_ = GlobalObject(
-        title=u'Instance Class',
-        description=u'Dotted name of the class that is registered.',
-        required=True
-        )
-
-    meta_type = ASCII(
-        title=u'Meta Type',
-        description=u'A human readable unique identifier for the class.',
-        required=True
-        )
-
-    permission = Permission(
-        title=u'Add Permission',
-        description=u'The permission for adding objects of this class.',
-        required=True
-        )
-
-    addview = ASCII(
-        title=u'Add View ID',
-        description=u'The ID of the add view used in the ZMI. Consider this '
-                    u'required unless you know exactly what you do.',
-        default=None,
-        required=False
-        )
-
-    icon = ASCII(
-        title=u'Icon ID',
-        description=u'The ID of the icon used in the ZMI.',
-        default=None,
-        required=False
-        )
-
-    global_ = Bool(
-        title=u'Global scope?',
-        description=u'If "global" is False the class is only available in '
-                    u'containers that explicitly allow one of its interfaces.',
-        default=True,
-        required=False
-        )
-
-
-class IRegisterPackageDirective(Interface):
-    """Registers the given python package which at a minimum fools zope2 into
-    thinking of it as a zope2 product.
-    """
-
-    package = GlobalObject(
-        title=u'Target package',
-        required=True
-        )
-
-    initialize = GlobalObject(
-        title=u'Initialization function to invoke',
-        description=u'The dotted name of a function that will get invoked '
-                    u'with a ProductContext instance',
-        required=False
-        )
-
-
 from zope.deferredimport import deprecated
 
+deprecated("Please import from OFS.metadirectives",
+    IRegisterPackageDirective = 'OFS.metadirectives:IRegisterPackageDirective',
+    IRegisterClassDirective = 'OFS.metadirectives:IRegisterClassDirective',
+    IDeprecatedManageAddDeleteDirective = \
+        'OFS.metadirectives:IDeprecatedManageAddDeleteDirective',
+)
+
 deprecated("Please import from zope.configuration.xmlconfig",
     IInclude = 'zope.configuration.xmlconfig:IInclude',
 )

Modified: Zope/trunk/src/Products/Five/meta.zcml
===================================================================
--- Zope/trunk/src/Products/Five/meta.zcml	2010-01-01 22:37:20 UTC (rev 107520)
+++ Zope/trunk/src/Products/Five/meta.zcml	2010-01-01 22:46:01 UTC (rev 107521)
@@ -3,6 +3,7 @@
     xmlns:meta="http://namespaces.zope.org/meta">
 
   <include package="AccessControl" file="meta.zcml" />
+  <include package="OFS" file="meta.zcml" />
   <include package="zope.component" file="meta.zcml" />
   <include package="zope.i18n" file="meta.zcml" />
 
@@ -24,24 +25,6 @@
     <!-- specific to Five -->
 
     <meta:directive
-       name="loadProducts"
-       schema="zope.configuration.xmlconfig.IInclude"
-       handler=".fiveconfigure.loadProducts"
-       />
-
-    <meta:directive
-       name="loadProductsOverrides"
-       schema="zope.configuration.xmlconfig.IInclude"
-       handler=".fiveconfigure.loadProductsOverrides"
-       />
-
-    <meta:directive
-       name="deprecatedManageAddDelete"
-       schema=".fivedirectives.IDeprecatedManageAddDeleteDirective"
-       handler=".eventconfigure.deprecatedManageAddDelete"
-       />
-
-    <meta:directive
        name="sizable"
        schema=".fivedirectives.ISizableDirective"
        handler=".sizeconfigure.sizable"
@@ -53,18 +36,6 @@
         handler=".fiveconfigure.pagesFromDirectory"
         />
 
-    <meta:directive
-       name="registerClass"
-       schema=".fivedirectives.IRegisterClassDirective"
-       handler=".fiveconfigure.registerClass"
-       />
-
-    <meta:directive
-       name="registerPackage"
-       schema=".fivedirectives.IRegisterPackageDirective"
-       handler=".fiveconfigure.registerPackage"
-       />
-
     <!-- Deprecated, use the class directive instead. -->
     <meta:directive
        name="implements"

Modified: Zope/trunk/src/Products/Five/sizeconfigure.py
===================================================================
--- Zope/trunk/src/Products/Five/sizeconfigure.py	2010-01-01 22:37:20 UTC (rev 107520)
+++ Zope/trunk/src/Products/Five/sizeconfigure.py	2010-01-01 22:46:01 UTC (rev 107521)
@@ -50,9 +50,27 @@
         )
 
 # clean up code
-from Products.Five.fiveconfigure import killMonkey
-from zope.testing.cleanup import addCleanUp
+def killMonkey(class_, name, fallback, attr=None):
+    """Die monkey, die!"""
+    method = getattr(class_, name, None)
+    if isFiveMethod(method):
+        original = getattr(class_, fallback, None)
+        if original is not None:
+            delattr(class_, fallback)
+        if original is None or isFiveMethod(original):
+            try:
+                delattr(class_, name)
+            except AttributeError:
+                pass
+        else:
+            setattr(class_, name, original)
 
+    if attr is not None:
+        try:
+            delattr(class_, attr)
+        except (AttributeError, KeyError):
+            pass
+
 def unsizable(class_):
     """Restore class's initial state with respect to being sizable"""
     killMonkey(class_, 'get_size', '__five_original_get_size')
@@ -61,5 +79,6 @@
     for class_ in _monkied:
         unsizable(class_)
 
+from zope.testing.cleanup import addCleanUp
 addCleanUp(cleanUp)
 del addCleanUp

Deleted: Zope/trunk/src/Products/Five/tests/event.txt
===================================================================
--- Zope/trunk/src/Products/Five/tests/event.txt	2010-01-01 22:37:20 UTC (rev 107520)
+++ Zope/trunk/src/Products/Five/tests/event.txt	2010-01-01 22:46:01 UTC (rev 107521)
@@ -1,473 +0,0 @@
-================
-Container events
-================
-
-Zope container events are used to inform subscribers that an object is
-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 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::
-
-  <five:deprecatedManageAddDelete class="some.content.class"/>
-
-Classes that don't have this directive but still have manage_afterAdd &
-co methods will trigger a warning when they are called (and this is
-strictly a compatibility call, behavior may not be strictly equivalent
-to the original one).
-
-Test setup
-==========
-
-A bit of setup for the tests. Because we'll test copy/paste, we need to
-work inside a database::
-
-  >>> import ZODB.tests.util
-  >>> db = ZODB.tests.util.DB()
-  >>> connection = db.open()
-  >>> root = connection.root()
-
-We'll use a few simple classes (defined in python code for picklability)
-for our tests.
-
-  >>> from Products.Five.tests.test_event import MyApp, MyContent
-  >>> from Products.Five.tests.test_event import MyFolder, MyBTreeFolder
-  >>> from Products.Five.tests.test_event import MyOrderedFolder
-
-  >>> app = MyApp('')
-  >>> root['app'] = app
-  >>> folder = MyFolder('folder')
-  >>> app._setObject('folder', folder) # doctest: +NORMALIZE_WHITESPACE
-  old manage_afterAdd folder folder
-  'folder'
-  >>> folder = app.folder
-  >>> btfolder = MyBTreeFolder('btfolder')
-  >>> app._setObject('btfolder', btfolder) # doctest: +NORMALIZE_WHITESPACE
-  old manage_afterAdd btfolder btfolder
-  'btfolder'
-
-To observe what object events are dispatched, we'll have some
-subscribers print them. We'll actually do that for a specific interface,
-not for (None, IObjectEvent), and register our subscribers before the
-framework's ones, so ours will be called first. This has the effect that
-printed events will be in their "natural" order::
-
-  >>> from zope.component.interfaces import IObjectEvent, IRegistrationEvent
-  >>> from zope.lifecycleevent.interfaces import IObjectMovedEvent
-  >>> from zope.lifecycleevent.interfaces import IObjectCopiedEvent
-  >>> from OFS.interfaces import IObjectWillBeMovedEvent
-  >>> from OFS.interfaces import IObjectClonedEvent
-  >>> from OFS.interfaces import IItem
-  >>> def printObjectEvent(object, event):
-  ...     print event.__class__.__name__, object.getId()
-  >>> def printObjectEventExceptSome(object, event):
-  ...     if (IObjectMovedEvent.providedBy(event) or
-  ...         IObjectCopiedEvent.providedBy(event) or
-  ...         IObjectWillBeMovedEvent.providedBy(event) or
-  ...         IObjectClonedEvent.providedBy(event) or
-  ...         IRegistrationEvent.providedBy(event)):
-  ...         return
-  ...     print event.__class__.__name__, object.getId()
-
-  >>> from zope.component import provideHandler
-  >>> provideHandler(printObjectEvent, (IItem, IObjectMovedEvent))
-  >>> provideHandler(printObjectEvent, (IItem, IObjectCopiedEvent))
-  >>> provideHandler(printObjectEvent, (IItem, IObjectWillBeMovedEvent))
-  >>> provideHandler(printObjectEvent, (IItem, IObjectClonedEvent))
-  >>> provideHandler(printObjectEventExceptSome, (None, IObjectEvent))
-
-Finally we need to load the subscribers configuration::
-
-  >>> import zope.component
-  >>> import OFS.subscribers
-  >>> zope.component.provideAdapter(OFS.subscribers.ObjectManagerSublocations)
-  >>> zope.component.provideHandler(OFS.subscribers.dispatchObjectWillBeMovedEvent)
-  >>> zope.component.provideHandler(OFS.subscribers.dispatchObjectMovedEvent)
-  >>> zope.component.provideHandler(OFS.subscribers.dispatchObjectCopiedEvent)
-  >>> zope.component.provideHandler(OFS.subscribers.dispatchObjectClonedEvent)
-
-We need at least one fake deprecated method to tell the compatibility
-framework that component architecture is initialized::
-
-  >>> from Products.Five.eventconfigure import setDeprecatedManageAddDelete
-  >>> class C(object): pass
-  >>> setDeprecatedManageAddDelete(C)
-
-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
-called, but with a deprecation warning::
-
-  >>> sub = MyFolder('sub')
-  >>> folder._setObject('sub', sub)
-  ObjectWillBeAddedEvent sub
-  ObjectAddedEvent sub
-  old manage_afterAdd sub sub folder
-  ContainerModifiedEvent folder
-  'sub'
-  >>> sub = folder.sub
-  >>> ob = MyContent('dog')
-  >>> sub._setObject('dog', ob)
-  ObjectWillBeAddedEvent dog
-  ObjectAddedEvent dog
-  old manage_afterAdd dog dog sub
-  ContainerModifiedEvent sub
-  'dog'
-
-And when we rename the subfolder, manage_beforeDelete is also called
-bottom-up and events are sent::
-
-  >>> folder.manage_renameObject('sub', 'marine')
-  ObjectWillBeMovedEvent sub
-  ObjectWillBeMovedEvent dog
-  old manage_beforeDelete dog sub folder
-  old manage_beforeDelete sub sub folder
-  ObjectMovedEvent marine
-  old manage_afterAdd marine marine folder
-  ObjectMovedEvent dog
-  old manage_afterAdd dog marine folder
-  ContainerModifiedEvent folder
-
-Same thing for clone::
-
-  >>> res = folder.manage_clone(folder.marine, 'tank')
-  ObjectCopiedEvent tank
-  ObjectCopiedEvent dog
-  ObjectWillBeAddedEvent tank
-  ObjectWillBeAddedEvent dog
-  ObjectAddedEvent tank
-  old manage_afterAdd tank tank folder
-  ObjectAddedEvent dog
-  old manage_afterAdd dog tank folder
-  ContainerModifiedEvent folder
-  ObjectClonedEvent tank
-  old manage_afterClone tank tank
-  ObjectClonedEvent dog
-  old manage_afterClone dog tank
-  >>> res.getId()
-  'tank'
-
-Old class with deprecatedManageAddDelete
-========================================
-
-We specify that our class is deprecated (using zcml in real life)::
-
-  >>> setDeprecatedManageAddDelete(MyContent)
-  >>> setDeprecatedManageAddDelete(MyFolder)
-  >>> setDeprecatedManageAddDelete(MyOrderedFolder)
-
-Now some events are sent but the old manage_afterAdd method is also
-called correctly::
-
-  >>> ob = MyContent('lassie')
-  >>> folder._setObject('lassie', ob)
-  ObjectWillBeAddedEvent lassie
-  ObjectAddedEvent lassie
-  old manage_afterAdd lassie lassie folder
-  ContainerModifiedEvent folder
-  'lassie'
-
-And when we delete the object, manage_beforeDelete is also called and
-events are sent::
-
-  >>> folder.manage_delObjects('lassie')
-  ObjectWillBeRemovedEvent lassie
-  old manage_beforeDelete lassie lassie folder
-  ObjectRemovedEvent lassie
-  ContainerModifiedEvent folder
-
-The old behavior happens for a move or a copy, with events too.
-For a move::
-
-  >>> ob = MyContent('blueberry')
-  >>> folder._setObject('blueberry', ob)
-  ObjectWillBeAddedEvent blueberry
-  ObjectAddedEvent blueberry
-  old manage_afterAdd blueberry blueberry folder
-  ContainerModifiedEvent folder
-  'blueberry'
-  >>> cp = folder.manage_cutObjects('blueberry')
-  >>> folder.manage_pasteObjects(cp)
-  ObjectWillBeMovedEvent blueberry
-  old manage_beforeDelete blueberry blueberry folder
-  ObjectMovedEvent blueberry
-  old manage_afterAdd blueberry blueberry folder
-  ContainerModifiedEvent folder
-  [{'new_id': 'blueberry', 'id': 'blueberry'}]
-
-Old behavior with events for a copy::
-
-  >>> cp = folder.manage_copyObjects('blueberry')
-  >>> folder.manage_pasteObjects(cp)
-  ObjectCopiedEvent copy_of_blueberry
-  ObjectWillBeAddedEvent copy_of_blueberry
-  ObjectAddedEvent copy_of_blueberry
-  old manage_afterAdd copy_of_blueberry copy_of_blueberry folder
-  ContainerModifiedEvent folder
-  ObjectClonedEvent copy_of_blueberry
-  old manage_afterClone copy_of_blueberry copy_of_blueberry
-  [{'new_id': 'copy_of_blueberry', 'id': 'blueberry'}]
-
-Old behavior with events for a renaming::
-
-  >>> folder.manage_renameObject('copy_of_blueberry', 'myrtille')
-  ObjectWillBeMovedEvent copy_of_blueberry
-  old manage_beforeDelete copy_of_blueberry copy_of_blueberry folder
-  ObjectMovedEvent myrtille
-  old manage_afterAdd myrtille myrtille folder
-  ContainerModifiedEvent folder
-
-Old behavior with events for a clone::
-
-  >>> res = folder.manage_clone(folder.blueberry, 'strawberry')
-  ObjectCopiedEvent strawberry
-  ObjectWillBeAddedEvent strawberry
-  ObjectAddedEvent strawberry
-  old manage_afterAdd strawberry strawberry folder
-  ContainerModifiedEvent folder
-  ObjectClonedEvent strawberry
-  old manage_afterClone strawberry strawberry
-  >>> res.getId()
-  'strawberry'
-
-Events are also sent when we work with a BTreeFolder::
-
-  >>> ob = MyContent('luckyluke')
-  >>> btfolder._setObject('luckyluke', ob)
-  ObjectWillBeAddedEvent luckyluke
-  ObjectAddedEvent luckyluke
-  old manage_afterAdd luckyluke luckyluke btfolder
-  ContainerModifiedEvent btfolder
-  'luckyluke'
-
-  >>> btfolder.manage_delObjects('luckyluke')
-  ObjectWillBeRemovedEvent luckyluke
-  old manage_beforeDelete luckyluke luckyluke btfolder
-  ObjectRemovedEvent luckyluke
-  ContainerModifiedEvent btfolder
-
-Here is what happens for a tree of objects. Let's create a simple one::
-
-  >>> subfolder = MyFolder('subfolder')
-  >>> folder._setObject('subfolder', subfolder)
-  ObjectWillBeAddedEvent subfolder
-  ObjectAddedEvent subfolder
-  old manage_afterAdd subfolder subfolder folder
-  ContainerModifiedEvent folder
-  'subfolder'
-  >>> subfolder = folder.subfolder
-  >>> ob = MyContent('donald')
-  >>> subfolder._setObject('donald', ob)
-  ObjectWillBeAddedEvent donald
-  ObjectAddedEvent donald
-  old manage_afterAdd donald donald subfolder
-  ContainerModifiedEvent subfolder
-  'donald'
-
-Renaming a tree of objects. Note that manage_beforeDelete is called
-bottom-up::
-
-  >>> folder.manage_renameObject('subfolder', 'pluto')
-  ObjectWillBeMovedEvent subfolder
-  ObjectWillBeMovedEvent donald
-  old manage_beforeDelete donald subfolder folder
-  old manage_beforeDelete subfolder subfolder folder
-  ObjectMovedEvent pluto
-  old manage_afterAdd pluto pluto folder
-  ObjectMovedEvent donald
-  old manage_afterAdd donald pluto folder
-  ContainerModifiedEvent folder
-
-Cloning a tree of objects::
-
-  >>> res = folder.manage_clone(folder.pluto, 'mickey')
-  ObjectCopiedEvent mickey
-  ObjectCopiedEvent donald
-  ObjectWillBeAddedEvent mickey
-  ObjectWillBeAddedEvent donald
-  ObjectAddedEvent mickey
-  old manage_afterAdd mickey mickey folder
-  ObjectAddedEvent donald
-  old manage_afterAdd donald mickey folder
-  ContainerModifiedEvent folder
-  ObjectClonedEvent mickey
-  old manage_afterClone mickey mickey
-  ObjectClonedEvent donald
-  old manage_afterClone donald mickey
-  >>> res.getId()
-  'mickey'
-
-New class
-=========
-
-If we use classes that don't have any manage_afterAdd & co method,
-everything happens correctly::
-
-  >>> from Products.Five.tests.test_event import MyNewFolder, MyNewContent
-  >>> app = MyApp('')
-  >>> root['app'] = app
-  >>> folder = MyNewFolder('folder')
-  >>> app._setObject('folder', folder) # doctest: +NORMALIZE_WHITESPACE
-  ObjectWillBeAddedEvent folder
-  ObjectAddedEvent folder
-  ContainerModifiedEvent
-  'folder'
-  >>> folder = app.folder
-
-  >>> ob = MyNewContent('dogbert')
-  >>> folder._setObject('dogbert', ob)
-  ObjectWillBeAddedEvent dogbert
-  ObjectAddedEvent dogbert
-  ContainerModifiedEvent folder
-  'dogbert'
-  >>> folder.manage_delObjects('dogbert')
-  ObjectWillBeRemovedEvent dogbert
-  ObjectRemovedEvent dogbert
-  ContainerModifiedEvent folder
-
-Now move::
-
-  >>> ob = MyNewContent('dilbert')
-  >>> folder._setObject('dilbert', ob)
-  ObjectWillBeAddedEvent dilbert
-  ObjectAddedEvent dilbert
-  ContainerModifiedEvent folder
-  'dilbert'
-  >>> cp = folder.manage_cutObjects('dilbert')
-  >>> folder.manage_pasteObjects(cp)
-  ObjectWillBeMovedEvent dilbert
-  ObjectMovedEvent dilbert
-  ContainerModifiedEvent folder
-  [{'new_id': 'dilbert', 'id': 'dilbert'}]
-
-And copy::
-
-  >>> cp = folder.manage_copyObjects('dilbert')
-  >>> folder.manage_pasteObjects(cp)
-  ObjectCopiedEvent copy_of_dilbert
-  ObjectWillBeAddedEvent copy_of_dilbert
-  ObjectAddedEvent copy_of_dilbert
-  ContainerModifiedEvent folder
-  ObjectClonedEvent copy_of_dilbert
-  [{'new_id': 'copy_of_dilbert', 'id': 'dilbert'}]
-
-Then rename::
-
-  >>> folder.manage_renameObject('copy_of_dilbert', 'wally')
-  ObjectWillBeMovedEvent copy_of_dilbert
-  ObjectMovedEvent wally
-  ContainerModifiedEvent folder
-
-Or copy using manage_clone::
-
-  >>> res = folder.manage_clone(folder.dilbert, 'phb')
-  ObjectCopiedEvent phb
-  ObjectWillBeAddedEvent phb
-  ObjectAddedEvent phb
-  ContainerModifiedEvent folder
-  ObjectClonedEvent phb
-  >>> res.getId()
-  'phb'
-
-Also on a BTreeFolder::
-
-  >>> ob = MyNewContent('alice')
-  >>> btfolder._setObject('alice', ob)
-  ObjectWillBeAddedEvent alice
-  ObjectAddedEvent alice
-  ContainerModifiedEvent btfolder
-  'alice'
-  >>> btfolder.manage_renameObject('alice', 'rabbit')
-  ObjectWillBeMovedEvent alice
-  ObjectMovedEvent rabbit
-  ContainerModifiedEvent btfolder
-  >>> btfolder.manage_delObjects('rabbit')
-  ObjectWillBeRemovedEvent rabbit
-  ObjectRemovedEvent rabbit
-  ContainerModifiedEvent btfolder
-
-Now for a tree of objects. Let's create a simple one::
-
-  >>> subfolder = MyNewFolder('subfolder')
-  >>> folder._setObject('subfolder', subfolder)
-  ObjectWillBeAddedEvent subfolder
-  ObjectAddedEvent subfolder
-  ContainerModifiedEvent folder
-  'subfolder'
-  >>> subfolder = folder.subfolder
-  >>> ob = MyNewContent('mel')
-  >>> subfolder._setObject('mel', ob)
-  ObjectWillBeAddedEvent mel
-  ObjectAddedEvent mel
-  ContainerModifiedEvent subfolder
-  'mel'
-
-Renaming a tree of objects::
-
-  >>> folder.manage_renameObject('subfolder', 'firefly')
-  ObjectWillBeMovedEvent subfolder
-  ObjectWillBeMovedEvent mel
-  ObjectMovedEvent firefly
-  ObjectMovedEvent mel
-  ContainerModifiedEvent folder
-
-Cloning a tree of objects::
-
-  >>> res = folder.manage_clone(folder.firefly, 'serenity')
-  ObjectCopiedEvent serenity
-  ObjectCopiedEvent mel
-  ObjectWillBeAddedEvent serenity
-  ObjectWillBeAddedEvent mel
-  ObjectAddedEvent serenity
-  ObjectAddedEvent mel
-  ContainerModifiedEvent folder
-  ObjectClonedEvent serenity
-  ObjectClonedEvent mel
-  >>> res.getId()
-  'serenity'
-
-OrderedFolder has the same renaming behavior than before::
-
-  >>> ofolder = MyOrderedFolder('ofolder')
-  >>> app._setObject('ofolder', ofolder) # doctest: +NORMALIZE_WHITESPACE
-  ObjectWillBeAddedEvent ofolder
-  ObjectAddedEvent ofolder
-  old manage_afterAdd ofolder ofolder
-  ContainerModifiedEvent
-  'ofolder'
-  >>> ob1 = MyNewContent('ob1')
-  >>> ofolder._setObject('ob1', ob1)
-  ObjectWillBeAddedEvent ob1
-  ObjectAddedEvent ob1
-  ContainerModifiedEvent ofolder
-  'ob1'
-  >>> ob2 = MyNewContent('ob2')
-  >>> ofolder._setObject('ob2', ob2)
-  ObjectWillBeAddedEvent ob2
-  ObjectAddedEvent ob2
-  ContainerModifiedEvent ofolder
-  'ob2'
-  >>> ofolder.manage_renameObject('ob1', 'ob4')
-  ObjectWillBeMovedEvent ob1
-  ObjectMovedEvent ob4
-  ContainerModifiedEvent ofolder
-  >>> ofolder.objectIds()
-  ['ob4', 'ob2']
-
-When subobjects are reordered, an event about the container is sent::
-
-  >>> ofolder.moveObjectsUp('ob2')
-  ContainerModifiedEvent ofolder
-  1
-  >>> ofolder.objectIds()
-  ['ob2', 'ob4']
-
-Now cleanup::
-
-  >>> import transaction
-  >>> transaction.abort()

Deleted: Zope/trunk/src/Products/Five/tests/test_event.py
===================================================================
--- Zope/trunk/src/Products/Five/tests/test_event.py	2010-01-01 22:37:20 UTC (rev 107520)
+++ Zope/trunk/src/Products/Five/tests/test_event.py	2010-01-01 22:46:01 UTC (rev 107521)
@@ -1,88 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004, 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.
-#
-##############################################################################
-"""Test events triggered by Five
-
-$Id$
-"""
-
-# These classes aren't defined in the doctest because otherwise
-# they wouldn't be picklable, and we need that to test copy/paste.
-
-from OFS.SimpleItem import SimpleItem
-from OFS.Folder import Folder
-from OFS.OrderedFolder import OrderedFolder
-from Products.BTreeFolder2.BTreeFolder2 import BTreeFolder2
-
-from zope.component import testing, eventtesting
-
-def setUp(test):
-    testing.setUp(test)
-    eventtesting.setUp(test)
-
-class DontComplain(object):
-    def _verifyObjectPaste(self, object, validate_src=1):
-        pass
-    def cb_isMoveable(self):
-        return True
-    def cb_isCopyable(self):
-        return True
-
-class NotifyBase(DontComplain):
-    def manage_afterAdd(self, item, container):
-        print 'old manage_afterAdd %s %s %s' % (self.getId(), item.getId(),
-                                                container.getId())
-        super(NotifyBase, self).manage_afterAdd(item, container)
-    manage_afterAdd.__five_method__ = True # Shut up deprecation warnings
-    def manage_beforeDelete(self, item, container):
-        super(NotifyBase, self).manage_beforeDelete(item, container)
-        print 'old manage_beforeDelete %s %s %s' % (self.getId(), item.getId(),
-                                                    container.getId())
-    manage_beforeDelete.__five_method__ = True # Shut up deprecation warnings
-    def manage_afterClone(self, item):
-        print 'old manage_afterClone %s %s' % (self.getId(), item.getId())
-        super(NotifyBase, self).manage_afterClone(item)
-    manage_afterClone.__five_method__ = True # Shut up deprecation warnings
-
-class MyApp(Folder):
-    def getPhysicalRoot(self):
-        return self
-
-class MyFolder(NotifyBase, Folder):
-    pass
-
-class MyOrderedFolder(NotifyBase, OrderedFolder):
-    pass
-
-class MyBTreeFolder(NotifyBase, BTreeFolder2):
-    def _verifyObjectPaste(self, object, validate_src=1):
-        pass
-
-class MyContent(NotifyBase, SimpleItem):
-    def __init__(self, id):
-        self._setId(id)
-
-# These don't have manage_beforeDelete & co methods
-
-class MyNewContent(DontComplain, SimpleItem):
-    def __init__(self, id):
-        self._setId(id)
-
-class MyNewFolder(DontComplain, Folder):
-    pass
-
-
-def test_suite():
-    from zope.testing.doctest import DocFileSuite
-    return DocFileSuite('event.txt', package="Products.Five.tests",
-                        setUp=setUp, tearDown=testing.tearDown)

Deleted: Zope/trunk/src/Products/Five/tests/test_registerclass.py
===================================================================
--- Zope/trunk/src/Products/Five/tests/test_registerclass.py	2010-01-01 22:37:20 UTC (rev 107520)
+++ Zope/trunk/src/Products/Five/tests/test_registerclass.py	2010-01-01 22:46:01 UTC (rev 107521)
@@ -1,145 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004, 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.
-#
-##############################################################################
-"""Unit tests for the registerClass directive.
-
-$Id$
-"""
-
-def test_registerClass():
-    """
-    Testing registerClass
-
-      >>> from zope.component.testing import setUp, tearDown
-      >>> setUp()
-      >>> import Products
-      >>> import Products.Five
-      >>> from Products.Five import zcml
-      >>> from Products.Five.tests.testing.simplecontent import SimpleContent
-      >>> from Products.Five.tests.testing.simplecontent import ISimpleContent
-      >>> from persistent.interfaces import IPersistent
-
-    Use the five:registerClass directive::
-
-      >>> configure_zcml = '''
-      ... <configure
-      ...     xmlns="http://namespaces.zope.org/zope"
-      ...     xmlns:five="http://namespaces.zope.org/five"
-      ...     i18n_domain="foo">
-      ...   <permission id="foo.add" title="Add Foo"/>
-      ...   <five:registerClass
-      ...       class="Products.Five.tests.testing.simplecontent.SimpleContent"
-      ...       meta_type="Foo Type"
-      ...       permission="foo.add"
-      ...       addview="addfoo.html"
-      ...       icon="foo_icon.png"
-      ...       global="false"
-      ...       />
-      ... </configure>'''
-      >>> zcml.load_config('meta.zcml', Products.Five)
-      >>> zcml.load_string(configure_zcml)
-
-    Make sure that the class attributes are set correctly::
-
-      >>> SimpleContent.meta_type
-      'Foo Type'
-      >>> SimpleContent.icon
-      '++resource++foo_icon.png'
-
-    And the meta_type is registered correctly::
-
-      >>> for info in Products.meta_types:
-      ...     if info['name'] == 'Foo Type':
-      ...         break
-      >>> info['product']
-      'Five'
-      >>> info['permission']
-      'Add Foo'
-      >>> ISimpleContent in info['interfaces']
-      True
-      >>> IPersistent in info['interfaces']
-      True
-      >>> info['visibility'] is None
-      True
-      >>> info['instance'] is SimpleContent
-      True
-      >>> info['action']
-      '+/addfoo.html'
-      >>> info['container_filter'] is None
-      True
-
-    Now reset everything and see what happens without optional parameters::
-
-      >>> tearDown()
-      >>> setUp()
-
-    Use the five:registerClass directive again::
-
-      >>> configure_zcml = '''
-      ... <configure
-      ...     xmlns="http://namespaces.zope.org/zope"
-      ...     xmlns:five="http://namespaces.zope.org/five"
-      ...     i18n_domain="bar">
-      ...   <permission id="bar.add" title="Add Bar"/>
-      ...   <five:registerClass
-      ...       class="Products.Five.tests.testing.simplecontent.SimpleContent"
-      ...       meta_type="Bar Type"
-      ...       permission="bar.add"
-      ...       />
-      ... </configure>'''
-      >>> zcml.load_config('meta.zcml', Products.Five)
-      >>> zcml.load_string(configure_zcml)
-
-    Make sure that the class attributes are set correctly::
-
-      >>> SimpleContent.meta_type
-      'Bar Type'
-      >>> SimpleContent.icon
-      ''
-
-    And the meta_type is registered correctly::
-
-      >>> for info in Products.meta_types:
-      ...     if info['name'] == 'Bar Type':
-      ...         break
-      >>> info['product']
-      'Five'
-      >>> info['permission']
-      'Add Bar'
-      >>> ISimpleContent in info['interfaces']
-      True
-      >>> IPersistent in info['interfaces']
-      True
-      >>> info['visibility']
-      'Global'
-      >>> info['instance'] is SimpleContent
-      True
-      >>> info['action']
-      ''
-      >>> info['container_filter'] is None
-      True
-
-    Clean up:
-
-      >>> tearDown()
-      >>> SimpleContent.meta_type
-      'simple item'
-      >>> SimpleContent.icon
-      ''
-      >>> [info for info in Products.meta_types if info['name'] == 'Bar Type']
-      []
-    """
-
-def test_suite():
-    from Testing.ZopeTestCase import ZopeDocTestSuite
-    return ZopeDocTestSuite()

Deleted: Zope/trunk/src/Products/Five/tests/test_registerpackage.py
===================================================================
--- Zope/trunk/src/Products/Five/tests/test_registerpackage.py	2010-01-01 22:37:20 UTC (rev 107520)
+++ Zope/trunk/src/Products/Five/tests/test_registerpackage.py	2010-01-01 22:46:01 UTC (rev 107521)
@@ -1,86 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2006 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.
-#
-##############################################################################
-"""Unit tests for the registerPackage directive.
-
-$Id$
-"""
-import sys
-
-# need to add the testing package to the pythonpath in order to
-# test python-packages-as-products
-from Products.Five.tests import testing
-sys.path.append(testing.__path__[0])
-
-def test_registerPackage():
-    """
-    Testing registerPackage
-
-      >>> from zope.component.testing import setUp, tearDown
-      >>> setUp()
-      >>> import Products
-      >>> import Products.Five
-      >>> from Products.Five import zcml
-      >>> zcml.load_config('meta.zcml', Products.Five)
-
-    Make sure a python package with a valid initialize gets its
-    initialize function called::
-    
-      >>> configure_zcml = '''
-      ... <configure
-      ...     xmlns="http://namespaces.zope.org/zope"
-      ...     xmlns:five="http://namespaces.zope.org/five"
-      ...     i18n_domain="foo">
-      ...   <five:registerPackage
-      ...       package="pythonproduct2"
-      ...       initialize="pythonproduct2.initialize"
-      ...       />
-      ... </configure>'''
-      >>> zcml.load_string(configure_zcml)
-      
-    We need to load the product as well. This would normally happen during 
-    Zope startup, but in the test, we're already too late.
-    
-      >>> import Zope2
-      >>> from OFS.Application import install_products
-      
-      >>> app = Zope2.app()
-      >>> install_products(app)
-      pythonproduct2 initialized
-      
-    Test to see if the pythonproduct2 python package actually gets setup
-    as a zope2 product in the Control Panel.
-
-      >>> product_listing = []
-      >>> try:
-      ...    product_listing = app.Control_Panel.Products.objectIds()
-      ... finally:
-      ...     app._p_jar.close()
-      >>> 'pythonproduct2' in product_listing
-      True
-
-    Make sure it also shows up in ``Products._registered_packages``.
-
-      >>> [x.__name__ for x in getattr(Products, '_registered_packages', [])]
-      ['pythonproduct2']
-
-    Clean up:
-
-      >>> tearDown()
-    """
-
-
-def test_suite():
-    # Must use functional because registerPackage commits
-    from Testing.ZopeTestCase import FunctionalDocTestSuite
-    return FunctionalDocTestSuite()



More information about the Zope-Checkins mailing list