Back to index

plone3  3.1.7
CMFCatalogAware.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 """ Base class for catalog aware content items.
00014 
00015 $Id: CMFCatalogAware.py 82471 2007-12-27 18:03:03Z yuppie $
00016 """
00017 
00018 import logging
00019 
00020 from AccessControl import ClassSecurityInfo
00021 from Acquisition import aq_base
00022 from ExtensionClass import Base
00023 from Globals import DTMLFile
00024 from Globals import InitializeClass
00025 from OFS.interfaces import IObjectClonedEvent
00026 from OFS.interfaces import IObjectWillBeMovedEvent
00027 from zope.app.container.interfaces import IObjectAddedEvent
00028 from zope.app.container.interfaces import IObjectMovedEvent
00029 from zope.component import subscribers
00030 
00031 from interfaces import ICallableOpaqueItem
00032 from interfaces.IOpaqueItems \
00033         import ICallableOpaqueItem as z2ICallableOpaqueItem
00034 from permissions import AccessContentsInformation
00035 from permissions import ManagePortal
00036 from permissions import ModifyPortalContent
00037 from utils import _dtmldir
00038 from utils import _getAuthenticatedUser
00039 from utils import getToolByName
00040 
00041 logger = logging.getLogger('CMFCore.CMFCatalogAware')
00042 
00043 
00044 class CMFCatalogAware(Base):
00045 
00046     """Mix-in for notifying portal_catalog and portal_workflow
00047     """
00048 
00049     security = ClassSecurityInfo()
00050 
00051     # The following methods can be overriden using inheritence so that
00052     # it's possible to specifiy another catalog tool or workflow tool
00053     # for a given content type
00054 
00055     def _getCatalogTool(self):
00056         return getToolByName(self, 'portal_catalog', None)
00057 
00058     def _getWorkflowTool(self):
00059         return getToolByName(self, 'portal_workflow', None)
00060 
00061     # Cataloging methods
00062     # ------------------
00063 
00064     security.declareProtected(ModifyPortalContent, 'indexObject')
00065     def indexObject(self):
00066         """
00067             Index the object in the portal catalog.
00068         """
00069         catalog = self._getCatalogTool()
00070         if catalog is not None:
00071             catalog.indexObject(self)
00072 
00073     security.declareProtected(ModifyPortalContent, 'unindexObject')
00074     def unindexObject(self):
00075         """
00076             Unindex the object from the portal catalog.
00077         """
00078         catalog = self._getCatalogTool()
00079         if catalog is not None:
00080             catalog.unindexObject(self)
00081 
00082     security.declareProtected(ModifyPortalContent, 'reindexObject')
00083     def reindexObject(self, idxs=[]):
00084         """
00085             Reindex the object in the portal catalog.
00086             If idxs is present, only those indexes are reindexed.
00087             The metadata is always updated.
00088 
00089             Also update the modification date of the object,
00090             unless specific indexes were requested.
00091         """
00092         if idxs == []:
00093             # Update the modification date.
00094             if hasattr(aq_base(self), 'notifyModified'):
00095                 self.notifyModified()
00096         catalog = self._getCatalogTool()
00097         if catalog is not None:
00098             catalog.reindexObject(self, idxs=idxs)
00099 
00100     _cmf_security_indexes = ('allowedRolesAndUsers',)
00101 
00102     security.declareProtected(ModifyPortalContent, 'reindexObjectSecurity')
00103     def reindexObjectSecurity(self, skip_self=False):
00104         """Reindex security-related indexes on the object.
00105 
00106         Recurses in the children to reindex them too.
00107 
00108         If skip_self is True, only the children will be reindexed. This
00109         is a useful optimization if the object itself has just been
00110         fully reindexed, as there's no need to reindex its security twice.
00111         """
00112         catalog = self._getCatalogTool()
00113         if catalog is None:
00114             return
00115         path = '/'.join(self.getPhysicalPath())
00116 
00117         # XXX if _getCatalogTool() is overriden we will have to change
00118         # this method for the sub-objects.
00119         for brain in catalog.unrestrictedSearchResults(path=path):
00120             brain_path = brain.getPath()
00121             if brain_path == path and skip_self:
00122                 continue
00123             # Get the object
00124             ob = brain._unrestrictedGetObject()
00125             if ob is None:
00126                 # BBB: Ignore old references to deleted objects.
00127                 # Can happen only when using
00128                 # catalog-getObject-raises off in Zope 2.8
00129                 logger.warning("reindexObjectSecurity: Cannot get %s from "
00130                                "catalog", brain_path)
00131                 continue
00132             # Recatalog with the same catalog uid.
00133             s = getattr(ob, '_p_changed', 0)
00134             catalog.reindexObject(ob, idxs=self._cmf_security_indexes,
00135                                   update_metadata=0, uid=brain_path)
00136             if s is None: ob._p_deactivate()
00137 
00138     # Workflow methods
00139     # ----------------
00140 
00141     security.declarePrivate('notifyWorkflowCreated')
00142     def notifyWorkflowCreated(self):
00143         """
00144             Notify the workflow that self was just created.
00145         """
00146         wftool = self._getWorkflowTool()
00147         if wftool is not None:
00148             wftool.notifyCreated(self)
00149 
00150     # Opaque subitems
00151     # ---------------
00152 
00153     security.declareProtected(AccessContentsInformation, 'opaqueItems')
00154     def opaqueItems(self):
00155         """
00156             Return opaque items (subelements that are contained
00157             using something that is not an ObjectManager).
00158         """
00159         items = []
00160 
00161         # Call 'talkback' knowing that it is an opaque item.
00162         # This will remain here as long as the discussion item does
00163         # not implement ICallableOpaqueItem (backwards compatibility).
00164         if hasattr(aq_base(self), 'talkback'):
00165             talkback = self.talkback
00166             if talkback is not None:
00167                 items.append((talkback.id, talkback))
00168 
00169         # Other opaque items than 'talkback' may have callable
00170         # manage_after* and manage_before* hooks.
00171         # Loop over all attributes and add those to 'items'
00172         # implementing 'ICallableOpaqueItem'.
00173         self_base = aq_base(self)
00174         for name in self_base.__dict__.keys():
00175             obj = getattr(self, name)
00176             if ICallableOpaqueItem.providedBy(obj) \
00177                     or z2ICallableOpaqueItem.isImplementedBy(obj):
00178                 items.append((obj.getId(), obj))
00179 
00180         return tuple(items)
00181 
00182     security.declareProtected(AccessContentsInformation, 'opaqueIds')
00183     def opaqueIds(self):
00184         """
00185             Return opaque ids (subelements that are contained
00186             using something that is not an ObjectManager).
00187         """
00188         return [t[0] for t in self.opaqueItems()]
00189 
00190     security.declareProtected(AccessContentsInformation, 'opaqueValues')
00191     def opaqueValues(self):
00192         """
00193             Return opaque values (subelements that are contained
00194             using something that is not an ObjectManager).
00195         """
00196         return [t[1] for t in self.opaqueItems()]
00197 
00198     # ZMI
00199     # ---
00200 
00201     manage_options = ({'label': 'Workflows',
00202                        'action': 'manage_workflowsTab',
00203                        },
00204                        )
00205 
00206     _manage_workflowsTab = DTMLFile('zmi_workflows', _dtmldir)
00207 
00208     security.declareProtected(ManagePortal, 'manage_workflowsTab')
00209     def manage_workflowsTab(self, REQUEST, manage_tabs_message=None):
00210         """
00211             Tab displaying the current workflows for the content object.
00212         """
00213         ob = self
00214         wftool = self._getWorkflowTool()
00215         # XXX None ?
00216         if wftool is not None:
00217             wf_ids = wftool.getChainFor(ob)
00218             states = {}
00219             chain = []
00220             for wf_id in wf_ids:
00221                 wf = wftool.getWorkflowById(wf_id)
00222                 if wf is not None:
00223                     # XXX a standard API would be nice
00224                     if hasattr(wf, 'getReviewStateOf'):
00225                         # Default Workflow
00226                         state = wf.getReviewStateOf(ob)
00227                     elif hasattr(wf, '_getWorkflowStateOf'):
00228                         # DCWorkflow
00229                         state = wf._getWorkflowStateOf(ob, id_only=1)
00230                     else:
00231                         state = '(Unknown)'
00232                     states[wf_id] = state
00233                     chain.append(wf_id)
00234         return self._manage_workflowsTab(
00235             REQUEST,
00236             chain=chain,
00237             states=states,
00238             management_view='Workflows',
00239             manage_tabs_message=manage_tabs_message)
00240 
00241 InitializeClass(CMFCatalogAware)
00242 
00243 
00244 def handleContentishEvent(ob, event):
00245     """ Event subscriber for (IContentish, IObjectEvent) events.
00246     """
00247     if IObjectAddedEvent.providedBy(event):
00248         if event.newParent is not None:
00249             ob.indexObject()
00250 
00251     elif IObjectClonedEvent.providedBy(event):
00252         ob.notifyWorkflowCreated()
00253 
00254     elif IObjectMovedEvent.providedBy(event):
00255         if event.newParent is not None:
00256             ob.indexObject()
00257 
00258     elif IObjectWillBeMovedEvent.providedBy(event):
00259         if event.oldParent is not None:
00260             ob.unindexObject()
00261 
00262 def handleDynamicTypeCopiedEvent(ob, event):
00263     """ Event subscriber for (IDynamicType, IObjectCopiedEvent) events.
00264     """
00265     # Make sure owner local role is set after pasting
00266     # The standard Zope mechanisms take care of executable ownership
00267     current_user = _getAuthenticatedUser(ob)
00268     if current_user is None:
00269         return
00270 
00271     current_user_id = current_user.getId()
00272     if current_user_id is not None:
00273         local_role_holders = [ x[0] for x in ob.get_local_roles() ]
00274         ob.manage_delLocalRoles(local_role_holders)
00275         ob.manage_setLocalRoles(current_user_id, ['Owner'])
00276 
00277 def dispatchToOpaqueItems(ob, event):
00278     """Dispatch an event to opaque sub-items of a given object.
00279     """
00280     for opaque in ob.opaqueValues():
00281         s = getattr(opaque, '_p_changed', 0)
00282         for ignored in subscribers((opaque, event), None):
00283             pass # They do work in the adapter fetch
00284         if s is None:
00285             opaque._p_deactivate()
00286 
00287 def handleOpaqueItemEvent(ob, event):
00288     """ Event subscriber for (ICallableOpaqueItemEvents, IObjectEvent) events.
00289     """
00290     if IObjectAddedEvent.providedBy(event):
00291         if event.newParent is not None:
00292             ob.manage_afterAdd(ob, event.newParent)
00293 
00294     elif IObjectClonedEvent.providedBy(event):
00295         ob.manage_afterClone(ob)
00296 
00297     elif IObjectMovedEvent.providedBy(event):
00298         if event.newParent is not None:
00299             ob.manage_afterAdd(ob, event.newParent)
00300 
00301     elif IObjectWillBeMovedEvent.providedBy(event):
00302         if event.oldParent is not None:
00303             ob.manage_beforeDelete(ob, event.oldParent)