Back to index

plone3  3.1.7
CatalogTool.py
Go to the documentation of this file.
00001 ##############################################################################
00002 #
00003 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
00004 #
00005 # This software is subject to the provisions of the Zope Public License,
00006 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
00007 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
00008 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00009 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
00010 # FOR A PARTICULAR PURPOSE.
00011 #
00012 ##############################################################################
00013 """ Basic portal catalog.
00014 
00015 $Id: CatalogTool.py 77004 2007-06-24 08:57:54Z yuppie $
00016 """
00017 
00018 from AccessControl import ClassSecurityInfo
00019 from AccessControl import getSecurityManager
00020 from AccessControl.PermissionRole import rolesForPermissionOn
00021 from Acquisition import aq_base
00022 from DateTime import DateTime
00023 from Globals import DTMLFile
00024 from Globals import InitializeClass
00025 from Products.PluginIndexes.common import safe_callable
00026 from Products.ZCatalog.ZCatalog import ZCatalog
00027 from zope.interface import implements
00028 from zope.interface import providedBy
00029 from zope.interface.declarations import getObjectSpecification
00030 from zope.interface.declarations import ObjectSpecification
00031 from zope.interface.declarations import ObjectSpecificationDescriptor
00032 
00033 from ActionProviderBase import ActionProviderBase
00034 from interfaces import ICatalogTool
00035 from interfaces import IIndexableObjectWrapper
00036 from interfaces.portal_catalog \
00037         import IndexableObjectWrapper as z2IIndexableObjectWrapper
00038 from interfaces.portal_catalog import portal_catalog as z2ICatalogTool
00039 from permissions import AccessInactivePortalContent
00040 from permissions import ManagePortal
00041 from permissions import View
00042 from utils import _checkPermission
00043 from utils import _dtmldir
00044 from utils import _getAuthenticatedUser
00045 from utils import _mergedLocalRoles
00046 from utils import getToolByName
00047 from utils import UniqueObject
00048 
00049 
00050 class IndexableObjectSpecification(ObjectSpecificationDescriptor):
00051 
00052     def __get__(self, inst, cls=None):
00053         if inst is None:
00054             return getObjectSpecification(cls)
00055         else:
00056             provided = providedBy(inst._IndexableObjectWrapper__ob)
00057             cls = type(inst)
00058             return ObjectSpecification(provided, cls)
00059 
00060 
00061 class IndexableObjectWrapper(object):
00062 
00063     implements(IIndexableObjectWrapper)
00064     __implements__ = z2IIndexableObjectWrapper
00065     __providedBy__ = IndexableObjectSpecification()
00066 
00067     def __init__(self, vars, ob):
00068         self.__vars = vars
00069         self.__ob = ob
00070 
00071     def __str__(self):
00072         try:
00073             # __str__ is used to get the data of File objects
00074             return self.__ob.__str__()
00075         except AttributeError:
00076             return object.__str__(self)
00077 
00078     def __getattr__(self, name):
00079         vars = self.__vars
00080         if vars.has_key(name):
00081             return vars[name]
00082         return getattr(self.__ob, name)
00083 
00084     def allowedRolesAndUsers(self):
00085         """
00086         Return a list of roles and users with View permission.
00087         Used by PortalCatalog to filter out items you're not allowed to see.
00088         """
00089         ob = self.__ob
00090         allowed = {}
00091         for r in rolesForPermissionOn(View, ob):
00092             allowed[r] = 1
00093         localroles = _mergedLocalRoles(ob)
00094         for user, roles in localroles.items():
00095             for role in roles:
00096                 if allowed.has_key(role):
00097                     allowed['user:' + user] = 1
00098         if allowed.has_key('Owner'):
00099             del allowed['Owner']
00100         return list(allowed.keys())
00101 
00102     def cmf_uid(self):
00103         """
00104         Return the CMFUid UID of the object while making sure
00105         it is not accidentally acquired.
00106         """
00107         cmf_uid = getattr(aq_base(self.__ob), 'cmf_uid', '')
00108         if safe_callable(cmf_uid):
00109             return cmf_uid()
00110         return cmf_uid
00111 
00112 
00113 class CatalogTool(UniqueObject, ZCatalog, ActionProviderBase):
00114 
00115     """ This is a ZCatalog that filters catalog queries.
00116     """
00117 
00118     implements(ICatalogTool)
00119     __implements__ = (z2ICatalogTool, ZCatalog.__implements__,
00120                       ActionProviderBase.__implements__)
00121 
00122     id = 'portal_catalog'
00123     meta_type = 'CMF Catalog'
00124 
00125     security = ClassSecurityInfo()
00126 
00127     manage_options = ( ZCatalog.manage_options +
00128                       ActionProviderBase.manage_options +
00129                       ({ 'label' : 'Overview', 'action' : 'manage_overview' }
00130                      ,
00131                      ))
00132 
00133     def __init__(self):
00134         ZCatalog.__init__(self, self.getId())
00135 
00136     #
00137     #   ZMI methods
00138     #
00139     security.declareProtected(ManagePortal, 'manage_overview')
00140     manage_overview = DTMLFile( 'explainCatalogTool', _dtmldir )
00141 
00142     #
00143     #   'portal_catalog' interface methods
00144     #
00145 
00146     def _listAllowedRolesAndUsers(self, user):
00147         effective_roles = user.getRoles()
00148         sm = getSecurityManager()
00149         if sm.calledByExecutable():
00150             eo = sm._context.stack[-1]
00151             proxy_roles = getattr(eo, '_proxy_roles', None)
00152             if proxy_roles is not None:
00153                 effective_roles = proxy_roles
00154         result = list( effective_roles )
00155         result.append( 'Anonymous' )
00156         result.append( 'user:%s' % user.getId() )
00157         return result
00158 
00159     def _convertQuery(self, kw):
00160         # Convert query to modern syntax
00161         for k in 'effective', 'expires':
00162             kusage = k+'_usage'
00163             if not kw.has_key(kusage):
00164                 continue
00165             usage = kw[kusage]
00166             if not usage.startswith('range:'):
00167                 raise ValueError("Incorrect usage %s" % `usage`)
00168             kw[k] = {'query': kw[k], 'range': usage[6:]}
00169             del kw[kusage]
00170 
00171     # searchResults has inherited security assertions.
00172     def searchResults(self, REQUEST=None, **kw):
00173         """
00174             Calls ZCatalog.searchResults with extra arguments that
00175             limit the results to what the user is allowed to see.
00176         """
00177         user = _getAuthenticatedUser(self)
00178         kw[ 'allowedRolesAndUsers' ] = self._listAllowedRolesAndUsers( user )
00179 
00180         if not _checkPermission( AccessInactivePortalContent, self ):
00181             now = DateTime()
00182 
00183             self._convertQuery(kw)
00184 
00185             # Intersect query restrictions with those implicit to the tool
00186             for k in 'effective', 'expires':
00187                 if kw.has_key(k):
00188                     range = kw[k]['range'] or ''
00189                     query = kw[k]['query']
00190                     if not isinstance(query, (tuple, list)):
00191                         query = (query,)
00192                 else:
00193                     range = ''
00194                     query = None
00195                 if range.find('min') > -1:
00196                     lo = min(query)
00197                 else:
00198                     lo = None
00199                 if range.find('max') > -1:
00200                     hi = max(query)
00201                 else:
00202                     hi = None
00203                 if k == 'effective':
00204                     if hi is None or hi > now:
00205                         hi = now
00206                     if lo is not None and hi < lo:
00207                         return ()
00208                 else: # 'expires':
00209                     if lo is None or lo < now:
00210                         lo = now
00211                     if hi is not None and hi < lo:
00212                         return ()
00213                 # Rebuild a query
00214                 if lo is None:
00215                     query = hi
00216                     range = 'max'
00217                 elif hi is None:
00218                     query = lo
00219                     range = 'min'
00220                 else:
00221                     query = (lo, hi)
00222                     range = 'min:max'
00223                 kw[k] = {'query': query, 'range': range}
00224 
00225         return ZCatalog.searchResults(self, REQUEST, **kw)
00226 
00227     __call__ = searchResults
00228 
00229     security.declarePrivate('unrestrictedSearchResults')
00230     def unrestrictedSearchResults(self, REQUEST=None, **kw):
00231         """Calls ZCatalog.searchResults directly without restrictions.
00232 
00233         This method returns every also not yet effective and already expired
00234         objects regardless of the roles the caller has.
00235 
00236         CAUTION: Care must be taken not to open security holes by
00237         exposing the results of this method to non authorized callers!
00238 
00239         If you're in doubt if you should use this method or
00240         'searchResults' use the latter.
00241         """
00242         return ZCatalog.searchResults(self, REQUEST, **kw)
00243 
00244     def __url(self, ob):
00245         return '/'.join( ob.getPhysicalPath() )
00246 
00247     manage_catalogFind = DTMLFile( 'catalogFind', _dtmldir )
00248 
00249     def catalog_object(self, obj, uid=None, idxs=None, update_metadata=1,
00250                        pghandler=None):
00251         # Wraps the object with workflow and accessibility
00252         # information just before cataloging.
00253         # XXX: this method violates the rules for tools/utilities:
00254         # it depends on a non-utility tool
00255         wftool = getToolByName(self, 'portal_workflow', None)
00256         if wftool is not None:
00257             vars = wftool.getCatalogVariablesFor(obj)
00258         else:
00259             vars = {}
00260         w = IndexableObjectWrapper(vars, obj)
00261         ZCatalog.catalog_object(self, w, uid, idxs, update_metadata,
00262                                 pghandler)
00263 
00264     security.declarePrivate('indexObject')
00265     def indexObject(self, object):
00266         """Add to catalog.
00267         """
00268         url = self.__url(object)
00269         self.catalog_object(object, url)
00270 
00271     security.declarePrivate('unindexObject')
00272     def unindexObject(self, object):
00273         """Remove from catalog.
00274         """
00275         url = self.__url(object)
00276         self.uncatalog_object(url)
00277 
00278     security.declarePrivate('reindexObject')
00279     def reindexObject(self, object, idxs=[], update_metadata=1, uid=None):
00280         """Update catalog after object data has changed.
00281 
00282         The optional idxs argument is a list of specific indexes
00283         to update (all of them by default).
00284 
00285         The update_metadata flag controls whether the object's
00286         metadata record is updated as well.
00287 
00288         If a non-None uid is passed, it will be used as the catalog uid
00289         for the object instead of its physical path.
00290         """
00291         if uid is None:
00292             uid = self.__url(object)
00293         if idxs != []:
00294             # Filter out invalid indexes.
00295             valid_indexes = self._catalog.indexes.keys()
00296             idxs = [i for i in idxs if i in valid_indexes]
00297         self.catalog_object(object, uid, idxs, update_metadata)
00298 
00299 InitializeClass(CatalogTool)