[Zope3-checkins] CVS: Zope3/lib/python/Zope/App/index - subscribers.py:1.2

Guido van Rossum guido@python.org
Fri, 6 Dec 2002 08:20:16 -0500


Update of /cvs-repository/Zope3/lib/python/Zope/App/index
In directory cvs.zope.org:/tmp/cvs-serv8171

Modified Files:
	subscribers.py 
Log Message:
Refactored a bit.
Added module comments.
Added registerExisting() method which traverses the content tree from
the folder containing the current service manager and registers
everything it finds (ignoring registration errors).


=== Zope3/lib/python/Zope/App/index/subscribers.py 1.1 => 1.2 ===
--- Zope3/lib/python/Zope/App/index/subscribers.py:1.1	Thu Dec  5 08:47:43 2002
+++ Zope3/lib/python/Zope/App/index/subscribers.py	Fri Dec  6 08:20:15 2002
@@ -11,20 +11,36 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""XXX short summary goes here.
+"""A stupid registration thingie.
 
-XXX longer description goes here.
+In order to do indexing, objects need to be registered with the object
+hub.  A real site should define a policy for when objects are to be
+registered.  This particular implementation subscribes to
+IObjectAddedEvent events from the event service, and registers
+absolutely everything.   It also has a way of registering all
+pre-existing objects.
+
+XXX This is really just an example!  There are no unit tests, and it
+hardcodes all the policy decisions.  Also, it has some "viewish"
+properties.  The traversal code in registerExisting could be useful
+for creating a general "Find" facility like the Zope2 Find tab.
 
 $Id$
 """
 __metaclass__ = type
 
 from Interface import Interface
+from Persistence import Persistent
+
 from Zope.Event.ISubscriber import ISubscriber
 from Zope.Event.IObjectEvent import IObjectAddedEvent
+from Zope.App.OFS.Content.Folder.Folder import IFolder
 from Zope.ContextWrapper import ContextMethod
-from Persistence import Persistent
-from Zope.ComponentArchitecture import getService
+from Zope.ComponentArchitecture import getService, queryAdapter
+
+from Zope.App.Traversing import traverse, traverseName, \
+     getPhysicalPath, getPhysicalRoot
+from Zope.App.OFS.Services.ObjectHub.IObjectHub import ObjectHubError
 
 class ISubscriptionControl(Interface):
     def subscribe():
@@ -36,13 +52,16 @@
     def isSubscribed():
         """Return whether we are currently subscribed."""
 
+    def registerExisting():
+        """Register all existing objects (for some definition of all)."""
+
 class Registration(Persistent):
 
     __implements__ = ISubscriptionControl, ISubscriber
 
     def notify(wrapped_self, event):
         """An event occured. Perhaps register this object with the hub."""
-        getService(wrapped_self, "ObjectHub").register(event.object)
+        self._registerObject(event.object)
     notify = ContextMethod(notify)
 
     currentlySubscribed = False # Default subscription state
@@ -66,9 +85,69 @@
     def isSubscribed(self):
         return self.currentlySubscribed
 
+    def registerExisting(wrapped_self):
+        object = findContentObject(wrapped_self)
+        wrapped_self._registerTree(object)
+    registerExisting = ContextMethod(registerExisting)
+
+    def _registerTree(wrapped_self, object):
+        wrapped_self._registerObject(object)
+        # XXX Policy decision: only traverse into folders
+        if not IFolder.isImplementedBy(object):
+            return
+        # Register subobjects
+        names = object.keys()
+        for name in names:
+            # XXX Once traverseName is refactored, should get an
+            #     ITraversable from object and pass it to traverseName
+            sub_object = traverseName(object, name)
+            wrapped_self._registerTree(sub_object)
+    _registerTree = ContextMethod(_registerTree)
+
+    def _registerObject(wrapped_self, object):
+        # XXX Policy decision: register absolutely everything
+        try:
+            getService(wrapped_self, "ObjectHub").register(object)
+        except ObjectHubError:
+            # Probably already registered
+            # XXX It would be more convenient if register() returned
+            #     a bool indicating whether the object was already
+            #     registered, we wouldn't have to catch this exception.
+            pass
+    _registerObject = ContextMethod(_registerObject)
+
     def _getChannel(wrapped_self, channel):
         if channel is None:
             channel = getService(wrapped_self, "ObjectHub")
         return channel
     _getChannel = ContextMethod(_getChannel)
 
+def findContentObject(context):
+    # We want to find the (content) Folder in whose service manager we
+    # live.  There are man y way to do this.  Perhaps the simplest is
+    # looking for '++etc++...' in the location.  We could also walk up
+    # the path looking for something that implements IFolder; the
+    # service manager and packages don't implement this.  Or (perhaps
+    # better, because a service manager might be attached to a
+    # non-folder container) assume we're in service space, and walk up
+    # until we find a service manager, and then go up one more step.
+    # Walking up the path could be done by stripping components from
+    # the end of the path one at a time and doing a lookup each time,
+    # or more directly by traversing the context.  Traversing the
+    # context can be done by getting the context and following the
+    # chain back; there's a convenience class, ContainmentIterator to
+    # do that.  Use the version of ContainmentIterator from
+    # Zope.Proxy, which is aware of the complications caused by
+    # security proxies.
+
+    # For now, we pick the first approach.
+    location = getPhysicalPath(context)
+    # Location is a tuple of strings, starting with '' (for the root)
+    for i in range(len(location)):
+        if location[i].startswith("++etc++"):
+            location = location[:i]
+            break
+    else:
+        raise ValueError, "can't find ++etc++ in path"
+    root = getPhysicalRoot(context)
+    return traverse(root, location)