[Zope3-checkins] CVS: Zope3/src/zope/app/interfaces - copypastemove.py:1.5
Jim Fulton
jim@zope.com
Thu, 12 Jun 2003 07:04:56 -0400
Update of /cvs-repository/Zope3/src/zope/app/interfaces
In directory cvs.zope.org:/tmp/cvs-serv7478
Modified Files:
copypastemove.py
Log Message:
Added license and doc string based on original proposal.
=== Zope3/src/zope/app/interfaces/copypastemove.py 1.4 => 1.5 ===
--- Zope3/src/zope/app/interfaces/copypastemove.py:1.4 Mon Mar 31 09:48:41 2003
+++ Zope3/src/zope/app/interfaces/copypastemove.py Thu Jun 12 07:04:56 2003
@@ -1,3 +1,200 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+#
+##############################################################################
+"""Copy and Move support
+
+XXX The theory is unclear about whether copy and move are about
+ containers or not. Many of the relevent interfaces are in
+ zope.app.interfaces.container, even though they are supposed not
+ to be indepenent of IContainer.
+
+ Perhaps we should just say that this is about containers and move
+ these interfaces there.
+
+
+Problem
+
+ Zope 3 needs to support three kinds of cut/copy/paste behaviour.
+
+ * Renaming an object to a different name in the same container.
+
+ * Moving an object from one place to another.
+
+ * Copying an object to another place.
+
+ The design for this needs to provide the right capabilities and
+ introspection to the user-interface. The appropriate events need
+ to be issued. The appropriate hooks, such as IDeleteNotifiable,
+ need to be called.
+
+
+Approach
+
+ Events and hooks
+
+ First, I need to explain events and hooks.
+
+ When an object is added to a container, if the object has an
+ IAddNotifiable adapter, that adapter's 'manage_afterAdd' method
+ is called. Then, an IObjectAddedEvent is published for the object.
+ Then an IObjectModifiedEvent is published for the container.
+
+ When an object is removed from a container, if the object has an
+ IDeleteNotifiable adapter, that adapter's 'manage_beforeDelete' method
+ is called. Then, an IObjectRemovedEvent is published for the object.
+ Then an IObjectModifiedEvent is published for the container.
+
+ When an object gets moved, it is a bit like being deleted and then added.
+ For many kinds of things you'd want to do in manage_afterAdd and
+ manage_beforeDelete, it is entirely appropriate to do these things
+ when an object is moved. However, in other cases you want special
+ behaviour on a move. So, IMoveNotifiable extends both IDeleteNotifiable
+ and IAddNotifiable, adds no further methods, but extends the method
+ signatures. This avoids the problem of an object that needs to be
+ notified of moves and of deletes, and do something different in each
+ case.
+
+ See zope.app.interrfaces.container.IMoveNotifiable and
+ zope.app.interrfaces.container.ICopyNotifiable.
+
+ The IZopeContainerAdapter is responsible for calling the hooks and
+ sending the events when you add or remove things from a container.
+
+ Renaming
+
+ Renaming an object is performed by moving it.
+
+ The ZopeContainerAdapter should be extended with a 'rename' method
+ for renaming an object in a container. This will be used by the
+ simple UI gesture for renaming something.
+
+ The rename method looks like this::
+
+ def rename(currentKey, newKey):
+ '''Put the object found at 'currentKey' under 'newKey' instead.
+
+ The container can choose different or modified 'newKey'. The
+ 'newKey' that was used is returned.
+
+ If the object at 'currentKey' is IMoveNotifiable, its
+ manage_beforeDelete method is called, with a movingTo
+ argument of the container's path plus the 'newKey'.
+ Otherwise, if the object at 'currentKey' is IDeleteNotifiable,
+ its manage_beforeDelete method is called.
+
+ Then, the object is removed from the container using the
+ container's __del__ method.
+
+ Then, If the object is IMoveNotifiable, its manage_afterAdd
+ method is called, with a movedFrom argument of the container's
+ path plus the 'currentKey'.
+ Otherwise, if the object is IAddNotifiable, its manage_afterAdd
+ method is called.
+
+ Then, an IObjectMovedEvent is published.
+ '''
+
+ Note that zope.app.interfaces.event.IObjectMovedEvent extends
+ both zope.app.interfaces.event.IObjectRemovedEvent and
+ zope.app.interfaces.event.IObjectAddedEvent.
+
+ Similarly zope.app.interfaces.event.IObjectCopiedEvent extends
+ should be made to IObjectAddedEvent.
+
+ Moving and copying
+
+ IObjectMover is what you adapt an object to when you want to move
+ it. The adapter is responsible for calling the manage_beforeDelete and
+ manage_afterAdd methods on an I(Add|Delete|Move)Notifiable adapter of
+ the object, as described above.
+ The IObjectMover adapter is also responsible for publishing an
+ IObjectMoved event in the context of the original container.
+
+ The 'moveTo()' method will get hold of an IMoveSource for
+ the object's container, and an IPasteTarget for the container the object
+ is being moved to.
+
+ Likewise, IObjectCopier is what you adapt an object to when you want to
+ copy it. The IObjectCopier adapter is responsible for calling a
+ manage_afterAdd hook on an I(Add|Copy)Notifiable adapter of the object.
+
+ The zope.app.interrfaces.container.IPasteTarget,
+ zope.app.interrfaces.container.IMoveSource, and
+ zope.app.interrfaces.container.ICopySource interfaces are designed
+ to be independent of IContainer. Adapters will be available for
+ IContainer, though. The idea is that it should be easy for
+ non-container classes to implement copy and paste, without having to
+ be containers.
+
+ A zope.app.interrfaces.container.IPasteTarget adapter must be
+ available for the object you want to copy or move something into.
+
+ A zope.app.interrfaces.container.IMoveSource adapter must be
+ available for an object you want to move something from.
+
+ Similarly, a zope.app.interrfaces.container.ICopySource adapter must
+ be available for an object you want to copy something from.
+
+Stepped out examples
+
+ These examples are simplified, and assume things, such as that an
+ IPasteTarget adapter is unconditionally available, and copying across
+ databases is not supported.
+
+ Copying the object at '/foo/bar/baz' to '/fish/tree/zab'
+
+ Basic application code::
+
+ obj = traverse(context, '/foo/bar/baz')
+ target = traverse(context, '/fish/tree')
+ copier = getAdapter(obj, IObjectCopier)
+ if copier.copyableTo(target, 'zab'):
+ copier.copy(target, 'zab')
+
+ Inside the 'copier.copyableTo()' method::
+
+ def copyableTo(self, target, name=None):
+ obj = self.context
+ if name is None:
+ name = objectName(obj)
+ pastetarget = getAdapter(target, IPasteTarget)
+ return pastetarget.acceptsObject(name, obj)
+
+ Inside the 'copier.copy()' method::
+
+ def copy(self, target, name=None):
+ obj = self.context
+ if name is None:
+ name = objectName(obj)
+ copysource = getAdapter(getParent(obj), ICopySource)
+ obj = copysource.copyObject(name, target)
+ pastetarget = getAdapter(target, IPasteTarget)
+ new_obj = self._pickle_then_unpickle(obj)
+ # publish an ObjectCreatedEvent (perhaps...?)
+ new_name = pastetarget.pasteObject(name, new_obj)
+ # call manage_afterAdd hook
+ # publish ObjectCopiedEvent
+ return new_name
+
+ def _pickle_then_unpickle(self, obj):
+ # Remove proxies from obj, pickle and then unpickle it.
+ # Return the result. Or, something like that
+ ....
+
+
+$Id$
+"""
+
from zope.interface import Interface
class IObjectMover(Interface):