[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