vendor/CMF/1.6.3/CMFCore

view ActionInformation.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) 2002 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 """ Information about customizable actions.
15 $Id$
16 """
18 from UserDict import UserDict
20 from AccessControl import ClassSecurityInfo
21 from Acquisition import aq_base, aq_inner, aq_parent
22 from Globals import InitializeClass
23 from OFS.SimpleItem import SimpleItem
25 from Expression import Expression
26 from interfaces.portal_actions import ActionInfo as IActionInfo
27 from permissions import View
28 from utils import _checkPermission
29 from utils import getToolByName
32 _unchanged = [] # marker
34 class ActionInfo(UserDict):
35 """ A lazy dictionary for Action infos.
36 """
38 __implements__ = IActionInfo
40 __allow_access_to_unprotected_subobjects__ = 1
42 def __init__(self, action, ec):
43 lazy_keys = []
45 if isinstance(action, dict):
46 UserDict.__init__(self, action)
47 self.data.setdefault( 'id', self.data['name'].lower() )
48 self.data.setdefault( 'title', self.data['name'] )
49 self.data.setdefault( 'url', '' )
50 self.data.setdefault( 'permissions', () )
51 self.data.setdefault( 'category', 'object' )
52 self.data.setdefault( 'visible', True )
53 self.data['available'] = True
55 else:
56 self._action = action
57 UserDict.__init__( self, action.getMapping() )
58 self.data['name'] = self.data['title']
59 del self.data['description']
61 if self.data['action']:
62 self.data['url'] = self._getURL
63 lazy_keys.append('url')
64 else:
65 self.data['url'] = ''
66 del self.data['action']
68 if self.data['condition']:
69 self.data['available'] = self._checkCondition
70 lazy_keys.append('available')
71 else:
72 self.data['available'] = True
73 del self.data['condition']
75 if self.data['permissions']:
76 self.data['allowed'] = self._checkPermissions
77 lazy_keys.append('allowed')
78 else:
79 self.data['allowed'] = True
81 self._ec = ec
82 self._lazy_keys = lazy_keys
84 def __getitem__(self, key):
85 value = UserDict.__getitem__(self, key)
86 if key in self._lazy_keys:
87 value = self.data[key] = value()
88 self._lazy_keys.remove(key)
89 return value
91 def __eq__(self, other):
92 # this is expensive, use it with care
93 [ self.__getitem__(key) for key in self._lazy_keys ]
95 if isinstance(other, self.__class__):
96 [ other[key] for key in other._lazy_keys ]
97 return self.data == other.data
98 elif isinstance(other, UserDict):
99 return self.data == other.data
100 else:
101 return self.data == other
103 def copy(self):
104 c = UserDict.copy(self)
105 c._lazy_keys = self._lazy_keys[:]
106 return c
108 def _getURL(self):
109 """ Get the result of the URL expression in the current context.
110 """
111 return self._action._getActionObject()(self._ec)
113 def _checkCondition(self):
114 """ Check condition expression in the current context.
115 """
116 return self._action.testCondition(self._ec)
118 def _checkPermissions(self):
119 """ Check permissions in the current context.
120 """
121 category = self['category']
122 object = self._ec.contexts['object']
123 if object is not None and ( category.startswith('object') or
124 category.startswith('workflow') or
125 category.startswith('document') ):
126 context = object
127 else:
128 folder = self._ec.contexts['folder']
129 if folder is not None and category.startswith('folder'):
130 context = folder
131 else:
132 context = self._ec.contexts['portal']
134 for permission in self['permissions']:
135 if _checkPermission(permission, context):
136 return True
137 return False
140 class ActionInformation( SimpleItem ):
142 """ Represent a single selectable action.
144 Actions generate links to views of content, or to specific methods
145 of the site. They can be filtered via their conditions.
146 """
147 _isActionInformation = 1
148 __allow_access_to_unprotected_subobjects__ = 1
150 security = ClassSecurityInfo()
152 def __init__( self
153 , id
154 , title=''
155 , description=''
156 , category='object'
157 , condition=''
158 , permissions=()
159 , priority=10
160 , visible=True
161 , action=''
162 ):
163 """ Set up an instance.
164 """
165 self.edit( id
166 , title
167 , description
168 , category
169 , condition
170 , permissions
171 , priority
172 , visible
173 , action
174 )
176 security.declarePrivate('edit')
177 def edit( self
178 , id=_unchanged
179 , title=_unchanged
180 , description=_unchanged
181 , category=_unchanged
182 , condition=_unchanged
183 , permissions=_unchanged
184 , priority=_unchanged
185 , visible=_unchanged
186 , action=_unchanged
187 ):
188 """Edit the specified properties.
189 """
191 if id is not _unchanged:
192 self.id = id
193 if title is not _unchanged:
194 self.title = title
195 if description is not _unchanged:
196 self.description = description
197 if category is not _unchanged:
198 self.category = category
199 if condition is not _unchanged:
200 if condition and isinstance(condition, basestring):
201 condition = Expression(condition)
202 self.condition = condition
203 if permissions is not _unchanged:
204 if permissions == ('',):
205 permissions = ()
206 self.permissions = permissions
207 if priority is not _unchanged:
208 self.priority = priority
209 if visible is not _unchanged:
210 self.visible = visible
211 if action is not _unchanged:
212 if action and isinstance(action, basestring):
213 action = Expression(action)
214 self.setActionExpression(action)
216 security.declareProtected( View, 'Title' )
217 def Title(self):
219 """ Return the Action title.
220 """
221 return self.title or self.getId()
223 security.declareProtected( View, 'Description' )
224 def Description( self ):
226 """ Return a description of the action.
227 """
228 return self.description
230 security.declarePrivate( 'testCondition' )
231 def testCondition( self, ec ):
233 """ Evaluate condition using context, 'ec', and return 0 or 1.
234 """
235 if self.condition:
236 return bool( self.condition(ec) )
237 else:
238 return True
240 security.declarePublic( 'getAction' )
241 def getAction( self, ec ):
243 """ Compute the action using context, 'ec'; return a mapping of
244 info about the action.
245 """
246 return ActionInfo(self, ec)
248 security.declarePrivate( '_getActionObject' )
249 def _getActionObject( self ):
251 """ Find the action object, working around name changes.
252 """
253 action = getattr( self, 'action', None )
255 if action is None: # Forward compatibility, used to be '_action'
256 action = getattr( self, '_action', None )
257 if action is not None:
258 self.action = self._action
259 del self._action
261 return action
263 security.declarePublic( 'getActionExpression' )
264 def getActionExpression( self ):
266 """ Return the text of the TALES expression for our URL.
267 """
268 action = self._getActionObject()
269 expr = action and action.text or ''
270 if expr and isinstance(expr, basestring):
271 if ( not expr.startswith('string:')
272 and not expr.startswith('python:') ):
273 expr = 'string:${object_url}/%s' % expr
274 self.action = Expression( expr )
275 return expr
277 security.declarePrivate( 'setActionExpression' )
278 def setActionExpression(self, action):
279 if action and isinstance(action, basestring):
280 if ( not action.startswith('string:')
281 and not action.startswith('python:') ):
282 action = 'string:${object_url}/%s' % action
283 action = Expression( action )
284 self.action = action
286 security.declarePublic( 'getCondition' )
287 def getCondition(self):
289 """ Return the text of the TALES expression for our condition.
290 """
291 return getattr( self, 'condition', None ) and self.condition.text or ''
293 security.declarePublic( 'getPermissions' )
294 def getPermissions( self ):
296 """ Return the permission, if any, required to execute the action.
298 Return an empty tuple if no permission is required.
299 """
300 return self.permissions
302 security.declarePublic( 'getCategory' )
303 def getCategory( self ):
305 """ Return the category in which the action should be grouped.
306 """
307 return self.category or 'object'
309 security.declarePublic( 'getVisibility' )
310 def getVisibility( self ):
312 """ Return whether the action should be visible in the CMF UI.
313 """
314 return bool(self.visible)
316 security.declarePrivate('getMapping')
317 def getMapping(self):
318 """ Get a mapping of this object's data.
319 """
320 return { 'id': self.id,
321 'title': self.title or self.id,
322 'description': self.description,
323 'category': self.category or 'object',
324 'condition': getattr(self, 'condition', None)
325 and self.condition.text or '',
326 'permissions': self.permissions,
327 'visible': bool(self.visible),
328 'action': self.getActionExpression() }
330 security.declarePrivate('clone')
331 def clone( self ):
332 """ Get a newly-created AI just like us.
333 """
334 return self.__class__( priority=self.priority, **self.getMapping() )
336 InitializeClass( ActionInformation )
339 def getOAI(context, object=None):
340 request = getattr(context, 'REQUEST', None)
341 if request:
342 cache = request.get('_oai_cache', None)
343 if cache is None:
344 request['_oai_cache'] = cache = {}
345 info = cache.get( id(object), None )
346 else:
347 info = None
348 if info is None:
349 if object is None or not hasattr(object, 'aq_base'):
350 folder = None
351 else:
352 folder = object
353 # Search up the containment hierarchy until we find an
354 # object that claims it's a folder.
355 while folder is not None:
356 if getattr(aq_base(folder), 'isPrincipiaFolderish', 0):
357 # found it.
358 break
359 else:
360 folder = aq_parent(aq_inner(folder))
361 info = oai(context, folder, object)
362 if request:
363 cache[ id(object) ] = info
364 return info
367 class oai:
368 #Provided for backwards compatability
369 # Provides information that may be needed when constructing the list of
370 # available actions.
371 __allow_access_to_unprotected_subobjects__ = 1
373 def __init__( self, tool, folder, object=None ):
374 self.portal = portal = aq_parent(aq_inner(tool))
375 membership = getToolByName(tool, 'portal_membership')
376 self.isAnonymous = membership.isAnonymousUser()
377 self.user_id = membership.getAuthenticatedMember().getId()
378 self.portal_url = portal.absolute_url()
379 if folder is not None:
380 self.folder_url = folder.absolute_url()
381 self.folder = folder
382 else:
383 self.folder_url = self.portal_url
384 self.folder = portal
386 # The name "content" is deprecated and will go away in CMF 2.0!
387 self.object = self.content = object
388 if object is not None:
389 self.object_url = self.content_url = object.absolute_url()
390 else:
391 self.object_url = self.content_url = None
393 def __getitem__(self, name):
394 # Mapping interface for easy string formatting.
395 if name[:1] == '_':
396 raise KeyError, name
397 if hasattr(self, name):
398 return getattr(self, name)
399 raise KeyError, name