[CMF-checkins] CVS: CMF/CMFCore - ActionInformation.py:1.10.2.1 ActionProviderBase.py:1.13.6.1 ActionsTool.py:1.31.2.1

Chris McDonough chrism@zope.com
Wed, 31 Jul 2002 01:32:20 -0400


Update of /cvs-repository/CMF/CMFCore
In directory cvs.zope.org:/tmp/cvs-serv11157

Modified Files:
      Tag: chrism-actions-branch
	ActionInformation.py ActionProviderBase.py ActionsTool.py 
Log Message:
Modified actions handling:

- Actions tool no longer swallows action dictionary elements it doesn't
  understand.

- listFilteredActionsFor refactored.

- ActionInformation objects grew some new methods as utilities.

This is on a branch for now until Tres has a chance to look at it,
as it's a somewhat big patch.


=== CMF/CMFCore/ActionInformation.py 1.10 => 1.10.2.1 ===
 from CMFCorePermissions import ManagePortal
 from utils import _dtmldir
 from utils import getToolByName
+from types import StringType
 
 class ActionInformation( SimpleItem ):
 
@@ -38,57 +39,36 @@
     __allow_access_to_unprotected_subobjects__ = 1
 
     security = ClassSecurityInfo()
+    _action_info = None
 
-    def __init__( self
-                , id
-                , title=''
-                , description=''
-                , category='object'
-                , condition=''
-                , permissions=()
-                , priority=10
-                , visible=1
-                , action=''
-                ):
+    def __init__( self, id, **kw):
         """ Set up an instance.
         """
-        if condition and type( condition ) == type( '' ):
-            condition = Expression( condition )
-
-        if action and type( action ) == type( '' ):
-            action = Expression( action )
-
-        self.id = id
-        self.title = title
-        self.description = description
-        self.category = category 
-        self.condition = condition
-        self.permissions = permissions
-        self.priority = priority 
-        self.visible = visible
-        self.action = action
+        kw['id'] = id
+        self.updateActionInfoDict(kw)
 
     security.declareProtected( View, 'Title' )
     def Title(self):
 
         """ Return the Action title.
         """
-        return self.title or self.getId()
+        return self._action_info['title'] or self.getId()
 
     security.declareProtected( View, 'Description' )
     def Description( self ):
 
         """ Return a description of the action.
         """
-        return self.description
+        return self._action_info['description']
 
     security.declarePrivate( 'testCondition' )
     def testCondition( self, ec ):
 
         """ Evaluate condition using context, 'ec', and return 0 or 1.
         """
-        if self.condition:
-            return self.condition(ec)
+        cond = self._action_info['condition']
+        if cond:
+            return cond(ec)
         else:
             return 1
 
@@ -99,6 +79,7 @@
             info about the action.
         """
         info = {}
+        info.update(self._action_info)
         info['id'] = self.id
         info['name'] = self.Title()
         action_obj = self._getActionObject()
@@ -106,22 +87,14 @@
         info['permissions'] = self.getPermissions()
         info['category'] = self.getCategory()
         info['visible'] = self.getVisibility()
-        return info 
+        return info
 
     security.declarePrivate( '_getActionObject' )
     def _getActionObject( self ):
 
         """ Find the action object, working around name changes.
         """
-        action = getattr( self, 'action', None )
-
-        if action is None:  # Forward compatibility, used to be '_action'
-            action = getattr( self, '_action', None )
-            if action is not None:
-                self.action = self._action
-                del self._action
-
-        return action
+        return self._action_info.get('action', None )
 
     security.declarePublic( 'getActionExpression' )
     def getActionExpression( self ):
@@ -136,7 +109,10 @@
 
         """ Return the text of the TALES expression for our condition.
         """
-        return getattr( self, 'condition', None ) and self.condition.text or ''
+        cond = self._action_info['condition']
+        if hasattr(cond, 'text'):
+            return cond.text
+        return ''
 
     security.declarePublic( 'getPermission' )
     def getPermissions( self ):
@@ -145,37 +121,73 @@
 
         Return an empty tuple if no permission is required.
         """
-        return self.permissions
+        return self._action_info['permissions']
 
     security.declarePublic( 'getCategory' )
     def getCategory( self ):
 
         """ Return the category in which the action should be grouped.
         """
-        return self.category or 'object'
+        return self._action_info.get('category') or 'object'
 
     security.declarePublic( 'getVisibility' )
     def getVisibility( self ):
 
         """ Return whether the action should be visible in the CMF UI.
         """
-        return self.visible
+        return self._action_info['visible']
 
     security.declarePrivate( 'clone' )
     def clone( self ):
 
         """ Return a newly-created AI just like us.
         """
-        return self.__class__( id=self.id
-                             , title=self.title
-                             , description=self.description
-                             , category =self.category
-                             , condition=self.getCondition()
-                             , permissions=self.permissions
-                             , priority =self.priority
-                             , visible=self.visible
-                             , action=self.getActionExpression()
-                             )
+        kw = self._action_info.copy()
+        id = kw['id']
+        del kw['id']
+        return apply(self.__class__, (id,), kw)
+
+    security.declarePrivate('getActionInfo')
+    def getActionInfoDict( self ):
+        return self._action_info.copy()
+
+    security.declarePrivate('updateActionInfo')
+    def updateActionInfoDict( self, kw ):
+        if self._action_info is None:
+            self._action_info = {}
+        kw['id'] = kw.get('id', getattr(self, 'id'))
+        kw['title'] = kw.get('title', '')
+        kw['description'] = kw.get('description', '')
+        kw['category'] = kw.get('category', 'object')
+        kw['condition'] = kw.get('condition', '')
+        kw['permissions'] = kw.get('permissions', ())
+        kw['priority'] = kw.get('priority', 10)
+        kw['visible'] = kw.get('visible', 1)
+        kw['action'] = kw.get('action', '')
+        condition = kw['condition'] = kw.get('condition', '')
+        if condition and isinstance(condition, StringType):
+            condition = kw['condition'] = Expression( condition )
+
+        action = kw['action'] = kw.get('action', '')
+        if action and isinstance(action, StringType):
+            action = kw['action'] = Expression( action )
+        self._action_info.update(kw)
+        self.id = kw['id']
+
+    security.declarePublic('query')
+    def query(self, name, default=None):
+        """ returns the item named by name in _action_info_dict or default """
+        return self._action_info.get(name, default)
+
+    def __setstate__(self, state):
+        if not state.has_key('_action_info'):
+            kw = {}
+            for name in ('id', 'title', 'description', 'category', 'condition',
+                         'permissions', 'priority', 'visible', 'action'):
+                kw[name] = state.get(name)
+                del state[name]
+            self._action_info = kw
+            self.__dict__.update(state)
 
 InitializeClass( ActionInformation )
 


=== CMF/CMFCore/ActionProviderBase.py 1.13 => 1.13.6.1 ===
 
             for a in self.listActions():
 
-                a1 = {}
+                a1 = a.getActionInfoDict()
                 a1['id'] = a.getId()
                 a1['name'] = a.Title()
                 p = a.getPermissions()
@@ -138,7 +138,10 @@
         actions = []
 
         for index in range( len( self._actions ) ):
-            actions.append( self._extractAction( properties, index ) )
+            action = self._actions[index].clone()
+            new_action = self._extractAction( properties, index )
+            action.updateActionInfoDict(new_action.getActionInfoDict())
+            actions.append( action )
 
         self._actions = tuple( actions )
 


=== CMF/CMFCore/ActionsTool.py 1.31 => 1.31.2.1 ===
 from ActionInformation import ActionInformation, oai
 from ActionProviderBase import ActionProviderBase
 from TypesTool import TypeInformation
+from types import DictionaryType
 
 class ActionsTool(UniqueObject, OFS.Folder.Folder, ActionProviderBase):
     """
@@ -159,119 +160,126 @@
     #   'portal_actions' interface methods
     #
 
-    def _listActions(self,append,object,info,ec):
-        a = object.listActions(info)
-        if a and type(a[0]) is not type({}):
-            for ai in a:
-                if ai.testCondition(ec):
-                    append(ai.getAction(ec))
-        else:
-            for i in a:
-                append(i)
+    security.declarePrivate('getProviderActions')
+    def getProviderActions(self, info=None):
+        # Return actions from specific tools.
+        l = []
+        for provider_name in self.listActionProviders():
+            provider = getattr(self, provider_name)
+            actions = provider.listActions(info)
+            l.extend(actions)
+        return l
+
+    security.declarePrivate('getTypeActions')
+    def getTypeActions(self, object, info=None):
+        # Return actions from object.
+        if object is None:
+            return []
         
+        l = []
+        types_tool = getToolByName( self, 'portal_types' )
+        # we might get None back from getTypeInfo.  We construct
+        # a dummy TypeInformation object here in that case (the 'or'
+        # case).  This prevents us from needing to check the condition.
+        ti = types_tool.getTypeInfo( object ) or TypeInformation('Dummy')
+        defs = ti.getActions()
+        url = object_url = object.absolute_url()
+        for d in defs:
+            # we can't modify or expose the original actionsd... this
+            # stems from the fact that getActions returns a ref to the
+            # actual dictionary used to store actions instead of a
+            # copy.  We copy it here to prevent it from being modified.
+            d = d.copy()
+            d['id'] = d.get('id', None)
+            if d['action']:
+                url = '%s/%s' % (object_url, d['action'])
+            d['url'] = url
+            d['category'] = d.get('category', 'object')
+            d['visible'] = d.get('visible', 1)
+            l.append(d)
+
+        if hasattr(aq_base(object), 'listActions'):
+            l.extend(object.listActions())
+
+        return l
+
+    def _findParentFolder(self, object):
+        # Search up the object's containment hierarchy until we find an
+        # object that claims it's a folder.
+        context = object
+        while not hasattr(aq_base(context), 'isPrincipiaFolderish'):
+            new_context = aq_parent(aq_inner(context))
+            if context is new_context:
+                break
+            context = new_context
+        return context
+
     security.declarePublic('listFilteredActionsFor')
     def listFilteredActionsFor(self, object=None):
         '''Gets all actions available to the user and returns a mapping
         containing user actions, object actions, and global actions.
         '''
-        portal = aq_parent(aq_inner(self))
-        if object is None or not hasattr(object, 'aq_base'):
-            folder = portal
-        else:
-            folder = object
-            # Search up the containment hierarchy until we find an
-            # object that claims it's a folder.
-            while folder is not None:
-                if getattr(aq_base(folder), 'isPrincipiaFolderish', 0):
-                    # found it.
-                    break
-                else:
-                    folder = aq_parent(aq_inner(folder))
-        ec = createExprContext(folder, portal, object)
         actions = []
-        append = actions.append
-        info = oai(self, folder, object)
-        # Include actions from specific tools.
-        for provider_name in self.listActionProviders():
-            provider = getattr(self, provider_name)
-            self._listActions(append,provider,info,ec)
+        portal = folder = aq_parent(aq_inner(self))
 
-        # Include actions from object.
-        if object is not None:
-            base = aq_base(object)
-            types_tool = getToolByName( self, 'portal_types' )
-            # we might get None back from getTypeInfo.  We construct
-            # a dummy TypeInformation object here in that case (the 'or'
-            # case).  This prevents us from needing to check the condition.
-            ti = types_tool.getTypeInfo( object ) or TypeInformation('Dummy')
-            defs = ti.getActions()
-            url = object_url = object.absolute_url()
-            for d in defs:
-                # we can't modify or expose the original actionsd... this
-                # stems from the fact that getActions returns a ref to the
-                # actual dictionary used to store actions instead of a
-                # copy.  We copy it here to prevent it from being modified.
-                d = d.copy()
-                d['id'] = d.get('id', None)
-                if d['action']:
-                    url = '%s/%s' % (object_url, d['action'])
-                d['url'] = url
-                d['category'] = d.get('category', 'object')
-                d['visible'] = d.get('visible', 1)
-                actions.append(d)
-
-            if hasattr(base, 'listActions'):
-                self._listActions(append,object,info,ec)
-
-        # Reorganize the actions by category,
-        # filtering out disallowed actions.
-        filtered_actions={'user':[],
-                          'folder':[],
-                          'object':[],
-                          'global':[],
-                          'workflow':[],
+        if object is not None and hasattr(object, 'aq_base'):
+            folder = self._findParentFolder(object)
+
+        # include actions from action providers
+        actions.extend(self.getProviderActions(oai(self, folder, object)))
+
+        # include actions from types tool
+        actions.extend(self.getTypeActions(object))
+
+        # Reorganize the actions by category, filtering out disallowed actions.
+        filtered_actions={
+            'user':[], 'folder':[], 'object':[], 'global':[], 'workflow':[],
                           }
+        expr_context = createExprContext(folder, portal, object)
         for action in actions:
-            category = action['category']
-            permissions = action.get('permissions', None)
-            visible = action.get('visible', 1)
-            if not visible:
+            action, category = organizeAction(
+                action, portal, folder, object, expr_context
+                )
+            if None in (action, category):
                 continue
-            verified = 0
-            if not permissions:
-                # This action requires no extra permissions.
-                verified = 1
-            else:
-                if (object is not None and
-                    (category.startswith('object') or
-                     category.startswith('workflow'))):
-                    context = object
-                elif (folder is not None and
-                      category.startswith('folder')):
-                    context = folder
-                else:
-                    context = portal
-                for permission in permissions:
-                    # The user must be able to match at least one of
-                    # the listed permissions.
-                    if _checkPermission(permission, context):
-                        verified = 1
-                        break
-            if verified:
-                catlist = filtered_actions.get(category, None)
-                if catlist is None:
-                    filtered_actions[category] = catlist = []
-                # Filter out duplicate actions by identity...
-                if not action in catlist:
-                    catlist.append(action)
-                # ...should you need it, here's some code that filters
-                # by equality (use instead of the two lines above)
-                #if not [a for a in catlist if a==action]:
-                #    catlist.append(action)
+            catlist = filtered_actions.setdefault(category, [])
+            if not action in catlist:
+                # no dupes
+                catlist.append(action)
+
         return filtered_actions
 
-    # listFilteredActions() is an alias.
+    # listFilteredActions is an alias for listFilteredActionsFor.
     security.declarePublic('listFilteredActions')
     listFilteredActions = listFilteredActionsFor
+
+def organizeAction(action, portal, folder, object, expr_context):
+    """ Organize the action into a category and expand the action
+    into a dictionary if it is an ActionInformation object """
+    if not isinstance(action, DictionaryType):
+        # this is an ActionInformation object
+        if not action.testCondition(expr_context):
+            # it did not pass the condition
+            return None, None
+        action = action.getAction(expr_context)
+
+    if action.get('visible', 1):
+        category = action.get('category', 'object')
+        permissions = action.get('permissions', None)
+        # context will be one of object, folder, or portal
+        context = ((category in ('object', 'workflow') and object) or
+                   (category == 'folder' and folder)) or portal
+        if permissions and not checkPermissions(permissions, context):
+            # inadequate permissions to see the action
+            return None, None
+
+    return action, category
+
+def checkPermissions(permissions, context):
+    for permission in permissions:
+        # The user must be able to match at least one of
+        # the listed permissions.
+        if _checkPermission(permission, context):
+            return 1
 
 InitializeClass(ActionsTool)