[Checkins] SVN: Products.CMFCore/trunk/Products/CMFCore/ merging catalog adapterization into the HEAD
Miles Waller
miles at jamkit.com
Mon Mar 23 06:18:08 EDT 2009
Log message for revision 98305:
merging catalog adapterization into the HEAD
Changed:
U Products.CMFCore/trunk/Products/CMFCore/CHANGES.txt
U Products.CMFCore/trunk/Products/CMFCore/CatalogTool.py
U Products.CMFCore/trunk/Products/CMFCore/content.zcml
U Products.CMFCore/trunk/Products/CMFCore/interfaces/_tools.py
U Products.CMFCore/trunk/Products/CMFCore/tests/test_CatalogTool.py
U Products.CMFCore/trunk/Products/CMFCore/tests/test_PortalFolder.py
-=-
Modified: Products.CMFCore/trunk/Products/CMFCore/CHANGES.txt
===================================================================
--- Products.CMFCore/trunk/Products/CMFCore/CHANGES.txt 2009-03-23 10:07:51 UTC (rev 98304)
+++ Products.CMFCore/trunk/Products/CMFCore/CHANGES.txt 2009-03-23 10:18:07 UTC (rev 98305)
@@ -4,6 +4,11 @@
2.2.0 (unreleased)
------------------
+- PortalCatalog: Changed to use a multi-adaptor to allow a pluggable
+ IndexableObjectWrapper class. Objects that implement IIndexableObject
+ are not wrapped. The change will assist in integrating with
+ other indexing strategies from third-party packages.
+
- Events: Changed 'handleContentishEvent' behavior for IObjectCopiedEvent.
'WorkflowTool.notifyCreated' no longer resets the workflow state, so the
the event subscriber clears the workflow history instead.
Modified: Products.CMFCore/trunk/Products/CMFCore/CatalogTool.py
===================================================================
--- Products.CMFCore/trunk/Products/CMFCore/CatalogTool.py 2009-03-23 10:07:51 UTC (rev 98304)
+++ Products.CMFCore/trunk/Products/CMFCore/CatalogTool.py 2009-03-23 10:18:07 UTC (rev 98305)
@@ -25,6 +25,8 @@
from Products.PluginIndexes.common import safe_callable
from Products.ZCatalog.ZCatalog import ZCatalog
from zope.interface import implements
+from zope.component import adapts
+from zope.component import queryMultiAdapter
from zope.interface import providedBy
from zope.interface.declarations import getObjectSpecification
from zope.interface.declarations import ObjectSpecification
@@ -33,6 +35,8 @@
from Products.CMFCore.ActionProviderBase import ActionProviderBase
from Products.CMFCore.interfaces import ICatalogTool
from Products.CMFCore.interfaces import IIndexableObjectWrapper
+from Products.CMFCore.interfaces import IIndexableObject
+from Products.CMFCore.interfaces import IContentish
from Products.CMFCore.permissions import AccessInactivePortalContent
from Products.CMFCore.permissions import ManagePortal
from Products.CMFCore.permissions import View
@@ -43,9 +47,11 @@
from Products.CMFCore.utils import getToolByName
from Products.CMFCore.utils import UniqueObject
-
class IndexableObjectSpecification(ObjectSpecificationDescriptor):
+ # This class makes the wrapper transparent, adapter lookup is
+ # carried out based on the interfaces of the wrapped object.
+
def __get__(self, inst, cls=None):
if inst is None:
return getObjectSpecification(cls)
@@ -57,11 +63,17 @@
class IndexableObjectWrapper(object):
- implements(IIndexableObjectWrapper)
+ implements(IIndexableObjectWrapper, IIndexableObject)
+ adapts(IContentish, ICatalogTool)
__providedBy__ = IndexableObjectSpecification()
- def __init__(self, vars, ob):
- self.__vars = vars
+ def __init__(self, ob, catalog):
+ # look up the workflow variables for the object
+ wftool = getToolByName(catalog, 'portal_workflow', None)
+ if wftool is not None:
+ self.__vars = wftool.getCatalogVariablesFor(ob)
+ else:
+ self.__vars = {}
self.__ob = ob
def __str__(self):
@@ -246,12 +258,13 @@
# information just before cataloging.
# XXX: this method violates the rules for tools/utilities:
# it depends on a non-utility tool
- wftool = getToolByName(self, 'portal_workflow', None)
- if wftool is not None:
- vars = wftool.getCatalogVariablesFor(obj)
+ if IIndexableObject.providedBy(obj):
+ w = obj
else:
- vars = {}
- w = IndexableObjectWrapper(vars, obj)
+ w = queryMultiAdapter( (obj, self), IIndexableObject )
+ if w is None:
+ # BBB
+ w = IndexableObjectWrapper(obj, self)
ZCatalog.catalog_object(self, w, uid, idxs, update_metadata,
pghandler)
Modified: Products.CMFCore/trunk/Products/CMFCore/content.zcml
===================================================================
--- Products.CMFCore/trunk/Products/CMFCore/content.zcml 2009-03-23 10:07:51 UTC (rev 98304)
+++ Products.CMFCore/trunk/Products/CMFCore/content.zcml 2009-03-23 10:18:07 UTC (rev 98305)
@@ -29,4 +29,11 @@
name="cmf.folder.btree"
/>
+ <!-- Default wrapper for indexing IContentish objects -->
+ <adapter
+ for=".interfaces.ICatalogAware
+ .interfaces.ICatalogTool"
+ provides=".interfaces.IIndexableObject"
+ factory=".CatalogTool.IndexableObjectWrapper" />
+
</configure>
Modified: Products.CMFCore/trunk/Products/CMFCore/interfaces/_tools.py
===================================================================
--- Products.CMFCore/trunk/Products/CMFCore/interfaces/_tools.py 2009-03-23 10:07:51 UTC (rev 98304)
+++ Products.CMFCore/trunk/Products/CMFCore/interfaces/_tools.py 2009-03-23 10:18:07 UTC (rev 98305)
@@ -406,7 +406,12 @@
a user is not allowed to see.
"""
+class IIndexableObject(Interface):
+ """ Marker interface for objects that can be indexed in
+ the portal catalog
+ """
+
#
# PUT factory handler interfaces
#
Modified: Products.CMFCore/trunk/Products/CMFCore/tests/test_CatalogTool.py
===================================================================
--- Products.CMFCore/trunk/Products/CMFCore/tests/test_CatalogTool.py 2009-03-23 10:07:51 UTC (rev 98304)
+++ Products.CMFCore/trunk/Products/CMFCore/tests/test_CatalogTool.py 2009-03-23 10:18:07 UTC (rev 98305)
@@ -17,9 +17,41 @@
import unittest
+from Acquisition import Implicit
+from zope.interface import implements
+from zope.component import getMultiAdapter
+from Products.CMFCore.interfaces import ICatalogTool
+from Products.CMFCore.tests.base.dummy import DummyContent
+from Products.CMFCore.interfaces import IIndexableObject
+from Products.CMFCore.interfaces import IContentish
+
from Products.CMFCore.tests.base.testcase import SecurityTest
+class FakeFolder(Implicit):
+ id = 'portal'
+class FakeCatalog(Implicit):
+ implements(ICatalogTool)
+ id = 'portal_catalog'
+
+class FakeWorkflowTool(Implicit):
+ id = 'portal_workflow'
+
+ def __init__(self, vars):
+ self._vars = vars
+
+ def getCatalogVariablesFor(self, ob):
+ return self._vars
+
+class CatalogDummyContent(DummyContent):
+
+ """ Dummy content that already provides IIndexableObject
+ and therefore does not need a wrapper to be registered
+ """
+
+ implements(IIndexableObject)
+ allowedRolesAndUsers = ['Manager'] # default value
+
class IndexableObjectWrapperTests(unittest.TestCase):
def _getTargetClass(self):
@@ -27,11 +59,16 @@
return IndexableObjectWrapper
- def _makeOne(self, *args, **kw):
- return self._getTargetClass()(*args, **kw)
+ def _makeOne(self, vars, obj):
+ self.root = FakeFolder()
+ self.root.portal_catalog = FakeCatalog()
+ self.root.portal_workflow = FakeWorkflowTool(vars)
+ catalog = self.root.portal_catalog
+ return self._getTargetClass()(obj, catalog)
def _makeContent(self, *args, **kw):
from Products.CMFCore.tests.base.dummy import DummyContent
+
return DummyContent(*args, **kw)
def test_interfaces(self):
@@ -69,13 +106,23 @@
def test_provided(self):
from Products.CMFCore.interfaces import IContentish
from Products.CMFCore.interfaces import IIndexableObjectWrapper
+ from Products.CMFCore.interfaces import IIndexableObject
obj = self._makeContent()
w = self._makeOne({}, obj)
self.failUnless(IContentish.providedBy(w))
self.failUnless(IIndexableObjectWrapper.providedBy(w))
+ self.failUnless(IIndexableObject.providedBy(w))
+ def test_adapts(self):
+ from zope.component import adaptedBy
+ from Products.CMFCore.interfaces import IContentish
+ from Products.CMFCore.interfaces import ICatalogTool
+ w = self._getTargetClass()
+ adapts = adaptedBy(w)
+ self.assertEqual(adapts, (IContentish, ICatalogTool))
+
class CatalogToolTests(SecurityTest):
def _getTargetClass(self):
@@ -87,8 +134,7 @@
return self._getTargetClass()(*args, **kw)
def _makeContent(self, *args, **kw):
- from Products.CMFCore.tests.base.dummy import DummyContent
- return DummyContent(*args, **kw)
+ return CatalogDummyContent(*args, **kw)
def test_interfaces(self):
from zope.interface.verify import verifyClass
@@ -144,7 +190,7 @@
catalog = self._makeOne()
catalog.addIndex('allowedRolesAndUsers', 'KeywordIndex')
dummy = self._makeContent(catalog=1)
- dummy._View_Permission = ('Blob',)
+ dummy.allowedRolesAndUsers = ('Blob',)
catalog.catalog_object(dummy, '/dummy')
self.loginWithRoles('Blob')
@@ -156,7 +202,7 @@
catalog = self._makeOne()
catalog.addIndex('allowedRolesAndUsers', 'KeywordIndex')
dummy = self._makeContent(catalog=1)
- dummy._View_Permission = ('Blob',)
+ dummy.allowedRolesAndUsers = ('Blob',)
catalog.catalog_object(dummy, '/dummy')
self.loginWithRoles('Blob')
@@ -169,7 +215,7 @@
catalog = self._makeOne()
catalog.addIndex('allowedRolesAndUsers', 'KeywordIndex')
dummy = self._makeContent(catalog=1)
- dummy._View_Permission = ('Blob',)
+ dummy.allowedRoleAndUsers = ('Blob',)
catalog.catalog_object(dummy, '/dummy')
self.loginWithRoles('Waggle')
@@ -181,7 +227,7 @@
catalog = self._makeOne()
catalog.addIndex('allowedRolesAndUsers', 'KeywordIndex')
dummy = self._makeContent(catalog=1)
- dummy._View_Permission = ('Blob',)
+ dummy.allowedRolesAndUsers = ('Blob',)
catalog.catalog_object(dummy, '/dummy')
self.loginWithRoles('Waggle')
@@ -198,7 +244,7 @@
catalog.addIndex('expires', 'DateIndex')
now = DateTime()
dummy = self._makeContent(catalog=1)
- dummy._View_Permission = ('Blob',)
+ dummy.allowedRolesAndUsers = ('Blob',)
self.loginWithRoles('Blob')
@@ -259,7 +305,7 @@
catalog.addIndex('expires', 'DateIndex')
now = DateTime()
dummy = self._makeContent(catalog=1)
- dummy._View_Permission = ('Blob',)
+ dummy.allowedRolesAndUsers = ('Blob',)
self.loginWithRoles('Blob')
@@ -293,7 +339,7 @@
catalog.addIndex('expires', 'DateIndex')
now = DateTime()
dummy = self._makeContent(catalog=1)
- dummy._View_Permission = ('Blob',)
+ dummy.allowedRolesAndUsers = ('Blob',)
self.loginWithRoles('Blob')
@@ -439,7 +485,41 @@
self.failUnless('Blob' in arus)
self.failUnless('user:%s' % user.getId() in arus)
+ def test_wrapping1(self):
+ # DummyContent implements IIndexableObject
+ # so should be indexed
+ dummy = self._makeContent(catalog=1)
+ ctool = self._makeOne()
+ ctool.catalog_object(dummy, '/dummy')
+ self.assertEqual(1, len(ctool._catalog.searchResults()))
+ def test_wrapping2(self):
+ # DummyContent does not implement IIndexableObject
+ # no wrapper registered - should fall back to using
+ # wrapper class directly
+ dummy = DummyContent(catalog=1)
+ ctool = self._makeOne()
+ ctool.catalog_object(dummy, '/dummy')
+ self.assertEqual(1, len(ctool._catalog.searchResults()))
+
+ def test_wrapping3(self):
+ # DummyContent does not implement IIndexableObject
+ # wrapper registered - should look this up
+
+ def FakeWrapper(object, catalog):
+ return object
+
+ from zope.component import getSiteManager
+ self.sm = getSiteManager()
+ self.sm.registerAdapter( FakeWrapper
+ , (IContentish, ICatalogTool)
+ , IIndexableObject )
+
+ dummy =DummyContent(catalog=1)
+ ctool = self._makeOne()
+ ctool.catalog_object(dummy, '/dummy')
+ self.assertEqual(1, len(ctool._catalog.searchResults()))
+
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(IndexableObjectWrapperTests),
Modified: Products.CMFCore/trunk/Products/CMFCore/tests/test_PortalFolder.py
===================================================================
--- Products.CMFCore/trunk/Products/CMFCore/tests/test_PortalFolder.py 2009-03-23 10:07:51 UTC (rev 98304)
+++ Products.CMFCore/trunk/Products/CMFCore/tests/test_PortalFolder.py 2009-03-23 10:18:07 UTC (rev 98305)
@@ -31,7 +31,7 @@
from zope.interface import implements
from zope.interface.verify import verifyClass
-from Products.CMFCore.CatalogTool import CatalogTool
+from Products.CMFCore.interfaces import ICatalogTool
from Products.CMFCore.exceptions import BadRequest
from Products.CMFCore.interfaces import ITypesTool
from Products.CMFCore.testing import ConformsToFolder
@@ -45,17 +45,44 @@
from Products.CMFCore.tests.base.testcase import SecurityTest
from Products.CMFCore.tests.base.tidata import FTIDATA_CMF15
from Products.CMFCore.tests.base.tidata import FTIDATA_DUMMY
-from Products.CMFCore.tests.base.utils import has_path
from Products.CMFCore.TypesTool import FactoryTypeInformation as FTI
from Products.CMFCore.TypesTool import TypesTool
from Products.CMFCore.WorkflowTool import WorkflowTool
+from types import TupleType
-
def extra_meta_types():
return [{'name': 'Dummy', 'action': 'manage_addFolder',
'permission': 'View'}]
+class DummyCatalogTool:
+ implements(ICatalogTool)
+ def __init__(self):
+ self.paths = []
+ self.ids = []
+
+ def indexObject(self, object):
+ self.paths.append( '/'.join(object.getPhysicalPath()) )
+ self.ids.append( object.getId() )
+
+ def unindexObject(self, object):
+ self.paths.remove( '/'.join(object.getPhysicalPath()) )
+ self.ids.append( object.getId() )
+
+ def reindexObject(self, object):
+ pass
+
+ def __len__(self):
+ return len(self.paths)
+
+def has_path(catalog, path):
+ if type(path) is TupleType:
+ path = '/'.join(path)
+ return path in catalog.paths
+
+def has_id(catalog, id):
+ return id in catalog.ids
+
class PortalFolderFactoryTests(SecurityTest):
layer = TraversingEventZCMLLayer
@@ -211,7 +238,7 @@
#
test = self._makeOne('test')
ttool = self.site._setObject( 'portal_types', TypesTool() )
- ctool = self.site._setObject( 'portal_catalog', CatalogTool() )
+ ctool = self.site._setObject( 'portal_catalog', DummyCatalogTool() )
self.assertEqual( len(ctool), 0 )
test._setObject( 'foo', DummyContent( 'foo' , catalog=1 ) )
@@ -232,7 +259,7 @@
# instantiation (Tracker issue 309)
#
ttool = self.site._setObject( 'portal_types', TypesTool() )
- ctool = self.site._setObject( 'portal_catalog', CatalogTool() )
+ ctool = self.site._setObject( 'portal_catalog', DummyCatalogTool() )
wftool = self.site._setObject( 'portal_workflow', WorkflowTool() )
test = self._makeOne('test')
wftool.notifyCreated(test)
@@ -248,7 +275,7 @@
test = self._makeOne('test')
ttool = self.site._setObject( 'portal_types', TypesTool() )
- ctool = self.site._setObject( 'portal_catalog', CatalogTool() )
+ ctool = self.site._setObject( 'portal_catalog', DummyCatalogTool() )
self.assertEqual( len(ctool), 0 )
test._setObject( 'sub', PortalFolder( 'sub', '' ) )
@@ -436,29 +463,28 @@
from Products.CMFCore.PortalFolder import PortalFolder
ttool = self.site._setObject( 'portal_types', TypesTool() )
- ctool = self.site._setObject( 'portal_catalog', CatalogTool() )
- ctool.addIndex('getId', 'FieldIndex')
+ ctool = self.site._setObject( 'portal_catalog', DummyCatalogTool() )
self.assertEqual( len(ctool), 0 )
folder = self._makeOne('folder')
folder._setObject( 'sub', PortalFolder( 'sub', '' ) )
folder.sub._setObject( 'foo', DummyContent( 'foo', catalog=1 ) )
self.assertEqual( len(ctool), 1 )
- self.failUnless( 'foo' in ctool.uniqueValuesFor('getId') )
- self.failUnless( has_path(ctool._catalog,
+ self.failUnless( has_id(ctool, 'foo') )
+ self.failUnless( has_path(ctool,
'/bar/site/folder/sub/foo') )
transaction.savepoint(optimistic=True)
folder.manage_renameObject(id='sub', new_id='new_sub')
self.assertEqual( len(ctool), 1 )
- self.failUnless( 'foo' in ctool.uniqueValuesFor('getId') )
- self.failUnless( has_path(ctool._catalog,
+ self.failUnless( has_id(ctool, 'foo') )
+ self.failUnless( has_path(ctool,
'/bar/site/folder/new_sub/foo') )
folder._setObject( 'bar', DummyContent( 'bar', catalog=1 ) )
self.assertEqual( len(ctool), 2 )
- self.failUnless( 'bar' in ctool.uniqueValuesFor('getId') )
- self.failUnless( has_path(ctool._catalog, '/bar/site/folder/bar') )
+ self.failUnless( has_id(ctool, 'bar') )
+ self.failUnless( has_path(ctool, '/bar/site/folder/bar') )
folder._setObject( 'sub2', PortalFolder( 'sub2', '' ) )
sub2 = folder.sub2
@@ -471,17 +497,17 @@
cookie = folder.manage_cutObjects(ids=['bar'])
sub2.manage_pasteObjects(cookie)
- self.failUnless( 'foo' in ctool.uniqueValuesFor('getId') )
- self.failUnless( 'bar' in ctool.uniqueValuesFor('getId') )
+ self.failUnless( has_id( ctool, 'foo' ) )
+ self.failUnless( has_id( ctool, 'bar' ) )
self.assertEqual( len(ctool), 2 )
- self.failUnless( has_path(ctool._catalog,
+ self.failUnless( has_path(ctool,
'/bar/site/folder/sub2/bar') )
def test_contentPaste(self):
#
# Does copy / paste work?
#
- ctool = self.site._setObject( 'portal_catalog', CatalogTool() )
+ ctool = self.site._setObject( 'portal_catalog', DummyCatalogTool() )
ttool = self.site._setObject( 'portal_types', TypesTool() )
fti = FTIDATA_DUMMY[0].copy()
ttool._setObject( 'Dummy Content', FTI(**fti) )
@@ -497,9 +523,9 @@
self.failIf( 'dummy' in sub2.contentIds() )
self.failIf( 'dummy' in sub3.objectIds() )
self.failIf( 'dummy' in sub3.contentIds() )
- self.failUnless( has_path(ctool._catalog, '/bar/site/sub1/dummy') )
- self.failIf( has_path(ctool._catalog, '/bar/site/sub2/dummy') )
- self.failIf( has_path(ctool._catalog, '/bar/site/sub3/dummy') )
+ self.failUnless( has_path(ctool, '/bar/site/sub1/dummy') )
+ self.failIf( has_path(ctool, '/bar/site/sub2/dummy') )
+ self.failIf( has_path(ctool, '/bar/site/sub3/dummy') )
cookie = sub1.manage_copyObjects( ids = ( 'dummy', ) )
# Waaa! force sub2 to allow paste of Dummy object.
@@ -513,9 +539,9 @@
self.failUnless( 'dummy' in sub2.contentIds() )
self.failIf( 'dummy' in sub3.objectIds() )
self.failIf( 'dummy' in sub3.contentIds() )
- self.failUnless( has_path(ctool._catalog, '/bar/site/sub1/dummy') )
- self.failUnless( has_path(ctool._catalog, '/bar/site/sub2/dummy') )
- self.failIf( has_path(ctool._catalog, '/bar/site/sub3/dummy') )
+ self.failUnless( has_path(ctool, '/bar/site/sub1/dummy') )
+ self.failUnless( has_path(ctool, '/bar/site/sub2/dummy') )
+ self.failIf( has_path(ctool, '/bar/site/sub3/dummy') )
transaction.savepoint(optimistic=True)
cookie = sub1.manage_cutObjects( ids = ('dummy',) )
@@ -530,9 +556,9 @@
self.failUnless( 'dummy' in sub2.contentIds() )
self.failUnless( 'dummy' in sub3.objectIds() )
self.failUnless( 'dummy' in sub3.contentIds() )
- self.failIf( has_path(ctool._catalog, '/bar/site/sub1/dummy') )
- self.failUnless( has_path(ctool._catalog, '/bar/site/sub2/dummy') )
- self.failUnless( has_path(ctool._catalog, '/bar/site/sub3/dummy') )
+ self.failIf( has_path(ctool, '/bar/site/sub1/dummy') )
+ self.failUnless( has_path(ctool, '/bar/site/sub2/dummy') )
+ self.failUnless( has_path(ctool, '/bar/site/sub3/dummy') )
class ContentFilterTests(unittest.TestCase):
More information about the Checkins
mailing list