vendor/CMF/1.6.3/CMFCore

view CMFCatalogAware.py @ 2:4c712d7bd1d7

Added tag 1.6.3 for changeset 1babb9d61518
author Georges Racinet on purity.racinet.fr <georges@racinet.fr>
date Fri, 09 Sep 2011 12:44:00 +0200
parents
children
line source
1 ##############################################################################
2 #
3 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
4 #
5 # This software is subject to the provisions of the Zope Public License,
6 # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10 # FOR A PARTICULAR PURPOSE.
11 #
12 ##############################################################################
13 """ Base class for catalog aware content items.
15 $Id$
16 """
18 import logging
19 from AccessControl import ClassSecurityInfo
20 from Acquisition import aq_base
21 from ExtensionClass import Base
22 from Globals import DTMLFile
23 from Globals import InitializeClass
25 from permissions import AccessContentsInformation
26 from permissions import ManagePortal
27 from permissions import ModifyPortalContent
28 from utils import _dtmldir
29 from utils import _getAuthenticatedUser
30 from utils import getToolByName
32 from interfaces.IOpaqueItems import ICallableOpaqueItem
35 logger = logging.getLogger('CMFCore.CMFCatalogAware')
38 class CMFCatalogAware(Base):
39 """Mix-in for notifying portal_catalog and portal_workflow
40 """
42 security = ClassSecurityInfo()
44 # The following methods can be overriden using inheritence so that
45 # it's possible to specifiy another catalog tool or workflow tool
46 # for a given content type
48 def _getCatalogTool(self):
49 return getToolByName(self, 'portal_catalog', None)
51 def _getWorkflowTool(self):
52 return getToolByName(self, 'portal_workflow', None)
54 # Cataloging methods
55 # ------------------
57 security.declareProtected(ModifyPortalContent, 'indexObject')
58 def indexObject(self):
59 """
60 Index the object in the portal catalog.
61 """
62 catalog = self._getCatalogTool()
63 if catalog is not None:
64 catalog.indexObject(self)
66 security.declareProtected(ModifyPortalContent, 'unindexObject')
67 def unindexObject(self):
68 """
69 Unindex the object from the portal catalog.
70 """
71 catalog = self._getCatalogTool()
72 if catalog is not None:
73 catalog.unindexObject(self)
75 security.declareProtected(ModifyPortalContent, 'reindexObject')
76 def reindexObject(self, idxs=[]):
77 """
78 Reindex the object in the portal catalog.
79 If idxs is present, only those indexes are reindexed.
80 The metadata is always updated.
82 Also update the modification date of the object,
83 unless specific indexes were requested.
84 """
85 if idxs == []:
86 # Update the modification date.
87 if hasattr(aq_base(self), 'notifyModified'):
88 self.notifyModified()
89 catalog = self._getCatalogTool()
90 if catalog is not None:
91 catalog.reindexObject(self, idxs=idxs)
93 _cmf_security_indexes = ('allowedRolesAndUsers',)
95 security.declareProtected(ModifyPortalContent, 'reindexObjectSecurity')
96 def reindexObjectSecurity(self, skip_self=False):
97 """Reindex security-related indexes on the object.
99 Recurses in the children to reindex them too.
101 If skip_self is True, only the children will be reindexed. This
102 is a useful optimization if the object itself has just been
103 fully reindexed, as there's no need to reindex its security twice.
104 """
105 catalog = self._getCatalogTool()
106 if catalog is None:
107 return
108 path = '/'.join(self.getPhysicalPath())
110 # XXX if _getCatalogTool() is overriden we will have to change
111 # this method for the sub-objects.
112 for brain in catalog.unrestrictedSearchResults(path=path):
113 brain_path = brain.getPath()
114 if brain_path == path and skip_self:
115 continue
116 # Get the object
117 if hasattr(aq_base(brain), '_unrestrictedGetObject'):
118 ob = brain._unrestrictedGetObject()
119 else:
120 # BBB: Zope 2.7
121 ob = self.unrestrictedTraverse(brain_path, None)
122 if ob is None:
123 # BBB: Ignore old references to deleted objects.
124 # Can happen only in Zope 2.7, or when using
125 # catalog-getObject-raises off in Zope 2.8
126 logger.warning("reindexObjectSecurity: Cannot get %s from "
127 "catalog", brain_path)
128 continue
129 # Recatalog with the same catalog uid.
130 s = getattr(ob, '_p_changed', 0)
131 catalog.reindexObject(ob, idxs=self._cmf_security_indexes,
132 update_metadata=0, uid=brain_path)
133 if s is None: ob._p_deactivate()
135 # Workflow methods
136 # ----------------
138 security.declarePrivate('notifyWorkflowCreated')
139 def notifyWorkflowCreated(self):
140 """
141 Notify the workflow that self was just created.
142 """
143 wftool = self._getWorkflowTool()
144 if wftool is not None:
145 wftool.notifyCreated(self)
147 # Opaque subitems
148 # ---------------
150 security.declareProtected(AccessContentsInformation, 'opaqueItems')
151 def opaqueItems(self):
152 """
153 Return opaque items (subelements that are contained
154 using something that is not an ObjectManager).
155 """
156 items = []
158 # Call 'talkback' knowing that it is an opaque item.
159 # This will remain here as long as the discussion item does
160 # not implement ICallableOpaqueItem (backwards compatibility).
161 if hasattr(aq_base(self), 'talkback'):
162 talkback = self.talkback
163 if talkback is not None:
164 items.append((talkback.id, talkback))
166 # Other opaque items than 'talkback' may have callable
167 # manage_after* and manage_before* hooks.
168 # Loop over all attributes and add those to 'items'
169 # implementing 'ICallableOpaqueItem'.
170 self_base = aq_base(self)
171 for name in self_base.__dict__.keys():
172 obj = getattr(self_base, name)
173 if ICallableOpaqueItem.isImplementedBy(obj):
174 items.append((obj.getId(), obj))
176 return tuple(items)
178 security.declareProtected(AccessContentsInformation, 'opaqueIds')
179 def opaqueIds(self):
180 """
181 Return opaque ids (subelements that are contained
182 using something that is not an ObjectManager).
183 """
184 return [t[0] for t in self.opaqueItems()]
186 security.declareProtected(AccessContentsInformation, 'opaqueValues')
187 def opaqueValues(self):
188 """
189 Return opaque values (subelements that are contained
190 using something that is not an ObjectManager).
191 """
192 return [t[1] for t in self.opaqueItems()]
194 # Hooks
195 # -----
197 def manage_afterAdd(self, item, container):
198 """
199 Add self to the catalog.
200 (Called when the object is created or moved.)
201 """
202 self.indexObject()
203 self.__recurse('manage_afterAdd', item, container)
205 def manage_afterClone(self, item):
206 """
207 Add self to the workflow.
208 (Called when the object is cloned.)
209 """
210 self.notifyWorkflowCreated()
211 self.__recurse('manage_afterClone', item)
213 # Make sure owner local role is set after pasting
214 # The standard Zope mechanisms take care of executable ownership
215 current_user = _getAuthenticatedUser(self)
216 if current_user is not None:
217 local_role_holders = [x[0] for x in self.get_local_roles()]
218 self.manage_delLocalRoles(local_role_holders)
219 self.manage_setLocalRoles(current_user.getId(), ['Owner'])
221 def manage_beforeDelete(self, item, container):
222 """
223 Remove self from the catalog.
224 (Called when the object is deleted or moved.)
225 """
226 self.__recurse('manage_beforeDelete', item, container)
227 self.unindexObject()
229 def __recurse(self, name, *args):
230 """
231 Recurse in both normal and opaque subobjects.
232 """
233 values = self.objectValues()
234 opaque_values = self.opaqueValues()
235 for subobjects in values, opaque_values:
236 for ob in subobjects:
237 s = getattr(ob, '_p_changed', 0)
238 if hasattr(aq_base(ob), name):
239 getattr(ob, name)(*args)
240 if s is None: ob._p_deactivate()
242 # ZMI
243 # ---
245 manage_options = ({'label': 'Workflows',
246 'action': 'manage_workflowsTab',
247 },
248 )
250 _manage_workflowsTab = DTMLFile('zmi_workflows', _dtmldir)
252 security.declareProtected(ManagePortal, 'manage_workflowsTab')
253 def manage_workflowsTab(self, REQUEST, manage_tabs_message=None):
254 """
255 Tab displaying the current workflows for the content object.
256 """
257 ob = self
258 wftool = self._getWorkflowTool()
259 # XXX None ?
260 if wftool is not None:
261 wf_ids = wftool.getChainFor(ob)
262 states = {}
263 chain = []
264 for wf_id in wf_ids:
265 wf = wftool.getWorkflowById(wf_id)
266 if wf is not None:
267 # XXX a standard API would be nice
268 if hasattr(wf, 'getReviewStateOf'):
269 # Default Workflow
270 state = wf.getReviewStateOf(ob)
271 elif hasattr(wf, '_getWorkflowStateOf'):
272 # DCWorkflow
273 state = wf._getWorkflowStateOf(ob, id_only=1)
274 else:
275 state = '(Unknown)'
276 states[wf_id] = state
277 chain.append(wf_id)
278 return self._manage_workflowsTab(
279 REQUEST,
280 chain=chain,
281 states=states,
282 management_view='Workflows',
283 manage_tabs_message=manage_tabs_message)
285 InitializeClass(CMFCatalogAware)