[CMF-checkins] CVS: CMF/CMFCore - ActionInformation.py:1.18 ActionProviderBase.py:1.21 ActionsTool.py:1.42 DiscussionTool.py:1.14 Expression.py:1.6 TypesTool.py:1.63 WorkflowTool.py:1.42

Yvo Schubbe cvs-admin at zope.org
Thu Nov 27 10:07:20 EST 2003


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

Modified Files:
	ActionInformation.py ActionProviderBase.py ActionsTool.py 
	DiscussionTool.py Expression.py TypesTool.py WorkflowTool.py 
Log Message:
Merged yuppie-listActionInfos-branch:
- refactored listFilteredActionsFor()
- added listActionInfos() and getActionInfo() to Action providers
- added helper methods getOAI() and getExprContext()


=== CMF/CMFCore/ActionInformation.py 1.17 => 1.18 ===
--- CMF/CMFCore/ActionInformation.py:1.17	Mon Sep  1 11:18:34 2003
+++ CMF/CMFCore/ActionInformation.py	Thu Nov 27 10:06:49 2003
@@ -15,20 +15,22 @@
 $Id$
 """
 
+from types import StringType
+
 from AccessControl import ClassSecurityInfo
 from Globals import InitializeClass
-from Acquisition import aq_inner, aq_parent
+from Acquisition import aq_base, aq_inner, aq_parent
 from OFS.SimpleItem import SimpleItem
 
 from Expression import Expression
 from CMFCorePermissions import View
 from utils import getToolByName
-from types import StringType
+
 
 class ActionInformation( SimpleItem ):
 
     """ Represent a single selectable action.
-    
+
     Actions generate links to views of content, or to specific methods
     of the site.  They can be filtered via their conditions.
     """
@@ -59,10 +61,10 @@
         self.id = id
         self.title = title
         self.description = description
-        self.category = category 
+        self.category = category
         self.condition = condition
         self.permissions = permissions
-        self.priority = priority 
+        self.priority = priority
         self.visible = visible
         self.setActionExpression(action)
 
@@ -106,7 +108,7 @@
         info['permissions'] = self.getPermissions()
         info['category'] = self.getCategory()
         info['visible'] = self.getVisibility()
-        return info 
+        return info
 
     security.declarePrivate( '_getActionObject' )
     def _getActionObject( self ):
@@ -192,6 +194,35 @@
 
 InitializeClass( ActionInformation )
 
+
+def getOAI(context, object=None):
+    request = getattr(context, 'REQUEST', None)
+    if request:
+        cache = request.get('_oai_cache', None)
+        if cache is None:
+            request['_oai_cache'] = cache = {}
+        info = cache.get( str(object), None )
+    else:
+        info = None
+    if info is None:
+        if object is None or not hasattr(object, 'aq_base'):
+            folder = None
+        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))
+        info = oai(context, folder, object)
+        if request:
+            cache[ str(object) ] = info
+    return info
+
+
 class oai:
     #Provided for backwards compatability
     # Provides information that may be needed when constructing the list of
@@ -223,4 +254,3 @@
         if hasattr(self, name):
             return getattr(self, name)
         raise KeyError, name
-


=== CMF/CMFCore/ActionProviderBase.py 1.20 => 1.21 ===
--- CMF/CMFCore/ActionProviderBase.py:1.20	Sat Oct 18 04:33:53 2003
+++ CMF/CMFCore/ActionProviderBase.py	Thu Nov 27 10:06:49 2003
@@ -15,15 +15,22 @@
 $Id$
 """
 
+from types import StringType
+
 from Globals import DTMLFile, InitializeClass
 from AccessControl import ClassSecurityInfo
 
 from ActionInformation import ActionInformation
+from ActionInformation import getOAI
 from CMFCorePermissions import ManagePortal
 from Expression import Expression
-from utils import _dtmldir
-
+from Expression import getExprContext
 from interfaces.portal_actions import ActionProvider as IActionProvider
+from interfaces.portal_actions \
+        import OldstyleActionProvider as IOldstyleActionProvider
+from utils import _checkPermission
+from utils import _dtmldir
+from utils import getToolByName
 
 
 class ActionProviderBase:
@@ -47,12 +54,74 @@
     #
     #   ActionProvider interface
     #
-    security.declarePrivate( 'listActions' )
-    def listActions( self, info=None ):
-        """ Return all the actions defined by a provider.
+    security.declarePrivate('listActions')
+    def listActions(self, info=None, object=None):
+        """ List all the actions defined by a provider.
         """
         return self._actions or ()
 
+    security.declarePublic('listActionInfos')
+    def listActionInfos(self, action_chain=None, object=None,
+                        check_visibility=1, check_permissions=1,
+                        check_condition=1, max=-1):
+        # List Action info mappings.
+        # (method is without docstring to disable publishing)
+        #
+        ec = getExprContext(self, object)
+        actions = self.listActions(object=object)
+
+        if action_chain:
+            filtered_actions = []
+            if isinstance(action_chain, StringType):
+                action_chain = (action_chain,)
+            for action_ident in action_chain:
+                sep = action_ident.rfind('/')
+                category, id = action_ident[:sep], action_ident[sep+1:]
+                for ai in actions:
+                    if id == ai.getId() and category == ai.getCategory():
+                        filtered_actions.append(ai)
+            actions = filtered_actions
+
+        action_infos = []
+        for ai in actions:
+            if check_visibility and not ai.getVisibility():
+                continue
+            if check_permissions:
+                permissions = ai.getPermissions()
+                if permissions:
+                    category = ai.getCategory()
+                    if (object is not None and
+                        (category.startswith('object') or
+                         category.startswith('workflow'))):
+                        context = object
+                    elif (ec.contexts['folder'] is not None and
+                          category.startswith('folder')):
+                        context = ec.contexts['folder']
+                    else:
+                        context = ec.contexts['portal']
+                    for permission in permissions:
+                        allowed = _checkPermission(permission, context)
+                        if allowed:
+                            break
+                    if not allowed:
+                        continue
+            if check_condition and not ai.testCondition(ec):
+                continue
+            action_infos.append( ai.getAction(ec) )
+            if max + 1 and len(action_infos) >= max:
+                break
+        return action_infos
+
+    security.declarePublic('getActionInfo')
+    def getActionInfo(self, action_chain, object=None, check_visibility=0,
+                      check_condition=0):
+        """ Get an Action info mapping specified by a chain of actions.
+        """
+        action_infos = self.listActionInfos(action_chain, object,
+                                       check_visibility=check_visibility,
+                                       check_condition=check_condition, max=1)
+        return action_infos and action_infos[0] or None
+
     #
     #   ZMI methods
     #
@@ -278,3 +347,83 @@
                                 )
 
 InitializeClass(ActionProviderBase)
+
+
+class OldstyleActionProviderBase:
+    """ Base class for ActionProviders with oldstyle Actions.
+    """
+
+    __implements__ = IOldstyleActionProvider
+
+    security = ClassSecurityInfo()
+
+    _actions = ()
+
+    #
+    #   OldstyleActionProvider interface
+    #
+    security.declarePrivate('listActions')
+    def listActions(self, info):
+        """ List all the actions defined by a provider.
+        """
+        return self._actions or ()
+
+    security.declarePublic('listActionInfos')
+    def listActionInfos(self, action_chain=None, object=None,
+                        check_visibility=1, check_permissions=1,
+                        check_condition=1, max=-1):
+        # List Action info mappings.
+        # (method is without docstring to disable publishing)
+        #
+        info = getOAI(self, object)
+        actions = self.listActions(info=info)
+
+        if action_chain:
+            filtered_actions = []
+            if isinstance(action_chain, StringType):
+                action_chain = (action_chain,)
+            for action_ident in action_chain:
+                sep = action_ident.rfind('/')
+                category, id = action_ident[:sep], action_ident[sep+1:]
+                for ai in actions:
+                    if id == ai['id'] and category == ai['category']:
+                        filtered_actions.append(ai)
+            actions = filtered_actions
+
+        action_infos = []
+        for ai in actions:
+            if check_permissions:
+                permissions = ai.get( 'permissions', () )
+                if permissions:
+                    category = ai['category']
+                    if (object is not None and
+                        (category.startswith('object') or
+                         category.startswith('workflow'))):
+                        context = object
+                    elif (info['folder'] is not None and
+                          category.startswith('folder')):
+                        context = info['folder']
+                    else:
+                        context = info['portal']
+                    for permission in permissions:
+                        allowed = _checkPermission(permission, context)
+                        if allowed:
+                            break
+                    if not allowed:
+                        continue
+            action_infos.append(ai)
+            if max + 1 and len(action_infos) >= max:
+                break
+        return action_infos
+
+    security.declarePublic('getActionInfo')
+    def getActionInfo(self, action_chain, object=None, check_visibility=0,
+                      check_condition=0):
+        """ Get an Action info mapping specified by a chain of actions.
+        """
+        action_infos = self.listActionInfos(action_chain, object,
+                                       check_visibility=check_visibility,
+                                       check_condition=check_condition, max=1)
+        return action_infos and action_infos[0] or None
+
+InitializeClass(OldstyleActionProviderBase)


=== CMF/CMFCore/ActionsTool.py 1.41 => 1.42 ===
--- CMF/CMFCore/ActionsTool.py:1.41	Fri Nov 14 03:34:55 2003
+++ CMF/CMFCore/ActionsTool.py	Thu Nov 27 10:06:49 2003
@@ -15,26 +15,29 @@
 $Id$
 """
 
+from types import DictionaryType
+
 from Globals import InitializeClass, DTMLFile, package_home
-from Acquisition import aq_base, aq_inner, aq_parent
 from AccessControl import ClassSecurityInfo
+from Acquisition import aq_base, aq_inner, aq_parent
 from OFS.Folder import Folder
 from OFS.SimpleItem import SimpleItem
 
-from Expression import Expression, createExprContext
-from ActionInformation import ActionInformation, oai
+from ActionInformation import ActionInformation
+from ActionInformation import getOAI
 from ActionProviderBase import ActionProviderBase
-from TypesTool import TypeInformation
 from CMFCorePermissions import ListFolderContents
 from CMFCorePermissions import ManagePortal
+from Expression import Expression
+from Expression import getExprContext
+from interfaces.portal_actions import portal_actions as IActionsTool
+from TypesTool import TypeInformation
 from utils import _checkPermission
 from utils import _dtmldir
 from utils import getToolByName
 from utils import SimpleItemWithProperties
 from utils import UniqueObject
 
-from interfaces.portal_actions import portal_actions as IActionsTool
-
 
 class ActionsTool(UniqueObject, Folder, ActionProviderBase):
     """
@@ -51,7 +54,7 @@
                                 , action=Expression(
                text='string: ${folder_url}/folder_contents')
                                 , condition=Expression(
-               text='python: folder is not object') 
+               text='python: folder is not object')
                                 , permissions=(ListFolderContents,)
                                 , category='object'
                                 , visible=1
@@ -89,7 +92,7 @@
                          , 'action' : 'manage_overview'
                          }
                      ) + Folder.manage_options
-                     ) 
+                     )
 
     #
     #   ZMI methods
@@ -156,37 +159,25 @@
     def listFilteredActionsFor(self, object=None):
         """ List all actions available to the user.
         """
-        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)
+            if hasattr( aq_base(provider), 'listActionInfos' ):
+                actions.extend( provider.listActionInfos(object=object) )
+            else:
+                # for Action Providers written for CMF versions before 1.5
+                actions.extend( self._listActionInfos(provider, object) )
 
+        # for objects written for CMF versions before 1.5
         # Include actions from object.
         if object is not None:
             base = aq_base(object)
             if hasattr(base, 'listActions'):
-                self._listActions(append,object,info,ec)
+                actions.extend( self._listActionInfos(object, object) )
 
-        # Reorganize the actions by category,
-        # filtering out disallowed actions.
+        # Reorganize the actions by category.
         filtered_actions={'user':[],
                           'folder':[],
                           'object':[],
@@ -195,44 +186,16 @@
                           }
         for action in actions:
             category = action['category']
-            permissions = action.get('permissions', None)
-            visible = action.get('visible', 1)
-            if not visible:
-                continue
-            verified = 0
-            if not permissions:
-                # This action requires no extra permissions.
-                verified = 1
-            else:
-                # startswith() is used so that we can have several
-                # different categories that are checked in the object or
-                # folder context.
-                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.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)
         return filtered_actions
 
     # listFilteredActions() is an alias.
@@ -240,16 +203,65 @@
     listFilteredActions = listFilteredActionsFor
 
     #
-    #   Helper methods
+    #   Helper method for backwards compatibility
     #
-    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))
+    def _listActionInfos(self, provider, object):
+        """ for Action Providers written for CMF versions before 1.5
+        """
+        info = getOAI(self, object)
+        actions = provider.listActions(info)
+
+        action_infos = []
+        if actions and type(actions[0]) is not DictionaryType:
+            ec = getExprContext(self, object)
+            for ai in actions:
+                if not ai.getVisibility():
+                    continue
+                permissions = ai.getPermissions()
+                if permissions:
+                    category = ai.getCategory()
+                    if (object is not None and
+                        (category.startswith('object') or
+                         category.startswith('workflow'))):
+                        context = object
+                    elif (info['folder'] is not None and
+                          category.startswith('folder')):
+                        context = info['folder']
+                    else:
+                        context = info['portal']
+                    for permission in permissions:
+                        allowed = _checkPermission(permission, context)
+                        if allowed:
+                            break
+                    if not allowed:
+                        continue
+                if not ai.testCondition(ec):
+                    continue
+                action_infos.append( ai.getAction(ec) )
         else:
-            for i in a:
-                append(i)
+            for i in actions:
+                if not i.get('visible', 1):
+                    continue
+                permissions = i.get('permissions', None)
+                if permissions:
+                    category = i['category']
+                    if (object is not None and
+                        (category.startswith('object') or
+                         category.startswith('workflow'))):
+                        context = object
+                    elif (info['folder'] is not None and
+                          category.startswith('folder')):
+                        context = info['folder']
+                    else:
+                        context = info['portal']
+
+                    for permission in permissions:
+                        allowed = _checkPermission(permission, context)
+                        if allowed:
+                            break
+                    if not allowed:
+                        continue
+                action_infos.append(i)
+        return action_infos
 
 InitializeClass(ActionsTool)


=== CMF/CMFCore/DiscussionTool.py 1.13 => 1.14 ===
--- CMF/CMFCore/DiscussionTool.py:1.13	Fri Nov 14 03:34:55 2003
+++ CMF/CMFCore/DiscussionTool.py	Thu Nov 27 10:06:49 2003
@@ -20,13 +20,12 @@
 from Acquisition import Implicit
 from AccessControl import ClassSecurityInfo
 
+from ActionProviderBase import OldstyleActionProviderBase
 from CMFCorePermissions import AccessContentsInformation
 from CMFCorePermissions import ManagePortal
 from CMFCorePermissions import ReplyToItem
 from CMFCorePermissions import View
 from interfaces.Discussions import OldDiscussable as IOldDiscussable
-from interfaces.portal_actions \
-        import OldstyleActionProvider as IOldstyleActionProvider
 from interfaces.portal_discussion \
         import oldstyle_portal_discussion as IOldstyleDiscussionTool
 from utils import _dtmldir
@@ -113,9 +112,10 @@
         return ""
 
 
-class DiscussionTool (UniqueObject, SimpleItem):
+class DiscussionTool (UniqueObject, SimpleItem, OldstyleActionProviderBase):
 
-    __implements__ = (IOldstyleDiscussionTool, IOldstyleActionProvider)
+    __implements__ = (IOldstyleDiscussionTool,
+                      OldstyleActionProviderBase.__implements__)
 
     id = 'portal_discussion'
     meta_type = 'Oldstyle CMF Discussion Tool'


=== CMF/CMFCore/Expression.py 1.5 => 1.6 ===
--- CMF/CMFCore/Expression.py:1.5	Thu Aug  1 15:05:11 2002
+++ CMF/CMFCore/Expression.py	Thu Nov 27 10:06:49 2003
@@ -1,14 +1,14 @@
 ##############################################################################
 #
 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
-# 
+#
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE
-# 
+#
 ##############################################################################
 """ Expressions in a web-configurable workflow.
 
@@ -17,7 +17,7 @@
 
 import Globals
 from Globals import Persistent
-from Acquisition import aq_inner, aq_parent
+from Acquisition import aq_base, aq_inner, aq_parent
 from AccessControl import getSecurityManager, ClassSecurityInfo
 
 from utils import getToolByName
@@ -25,6 +25,7 @@
 from Products.PageTemplates.TALES import SafeMapping
 from Products.PageTemplates.Expressions import SecureModuleImporter
 
+
 class Expression (Persistent):
     text = ''
     _v_compiled = None
@@ -48,6 +49,36 @@
         return res
 
 Globals.InitializeClass(Expression)
+
+
+def getExprContext(context, object=None):
+    request = getattr(context, 'REQUEST', None)
+    if request:
+        cache = request.get('_ec_cache', None)
+        if cache is None:
+            request['_ec_cache'] = cache = {}
+        ec = cache.get( str(object), None )
+    else:
+        ec = None
+    if ec is None:
+        utool = getToolByName(context, 'portal_url')
+        portal = utool.getPortalObject()
+        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)
+        if request:
+            cache[ str(object) ] = ec
+    return ec
 
 
 def createExprContext(folder, portal, object):


=== CMF/CMFCore/TypesTool.py 1.62 => 1.63 ===
--- CMF/CMFCore/TypesTool.py:1.62	Fri Nov 14 03:34:55 2003
+++ CMF/CMFCore/TypesTool.py	Thu Nov 27 10:06:49 2003
@@ -252,8 +252,7 @@
         return self.global_allow
 
     security.declarePublic('listActions')
-    def listActions( self, info=None ):
-
+    def listActions(self, info=None, object=None):
         """ Return a sequence of the action info objects for this type.
         """
         if self._actions and type( self._actions[0] ) == type( {} ):
@@ -888,16 +887,15 @@
             RESPONSE.redirect( immediate_url )
 
     security.declarePrivate( 'listActions' )
-    def listActions( self, info=None ):
-        """
-            List type-related actions.
+    def listActions(self, info=None, object=None):
+        """ List all the actions defined by a provider.
         """
         actions = list( self._actions )
 
-        if info is not None:
-
-            type_info = self.getTypeInfo( info.content )
-
+        if object is None and info is not None:
+            object = info.content
+        if object is not None:
+            type_info = self.getTypeInfo(object)
             if type_info is not None:
                 actions.extend( type_info.listActions() )
 


=== CMF/CMFCore/WorkflowTool.py 1.41 => 1.42 ===
--- CMF/CMFCore/WorkflowTool.py:1.41	Fri Nov 14 03:34:55 2003
+++ CMF/CMFCore/WorkflowTool.py	Thu Nov 27 10:06:49 2003
@@ -22,9 +22,8 @@
 from AccessControl import ClassSecurityInfo
 from Acquisition import aq_base, aq_inner, aq_parent
 
+from ActionProviderBase import OldstyleActionProviderBase
 from CMFCorePermissions import ManagePortal
-from interfaces.portal_actions \
-        import OldstyleActionProvider as IOldstyleActionProvider
 from interfaces.portal_workflow import portal_workflow as IWorkflowTool
 from utils import _dtmldir
 from utils import getToolByName
@@ -53,13 +52,13 @@
         raise KeyError, name
 
 
-class WorkflowTool (UniqueObject, Folder):
-
+class WorkflowTool(UniqueObject, Folder, OldstyleActionProviderBase):
     """ Mediator tool, mapping workflow objects
     """
     id = 'portal_workflow'
     meta_type = 'CMF Workflow Tool'
-    __implements__ = (IWorkflowTool, IOldstyleActionProvider)
+    __implements__ = (IWorkflowTool,
+                      OldstyleActionProviderBase.__implements__)
 
     _chains_by_type = None  # PersistentMapping
     _default_chain = ('default_workflow',)
@@ -344,7 +343,7 @@
                         'Requested workflow definition not found.')
                 else:
                     return default
-        res = apply(wf.getInfoFor, (ob, name, default) + args, kw)
+        res = wf.getInfoFor(ob, name, default, *args, **kw)
         if res is _marker:
             raise WorkflowException('Could not get info: %s' % name)
         return res
@@ -590,7 +589,7 @@
             wfs = ()
         if wf is None:
             # No workflow wraps this method.
-            return apply(func, args, kw)
+            return func(*args, **kw)
         return self._invokeWithNotification(
             wfs, ob, method_id, wf.wrapWorkflowMethod,
             (ob, method_id, func, args, kw), {})
@@ -619,7 +618,7 @@
         for w in wfs:
             w.notifyBefore(ob, action)
         try:
-            res = apply(func, args, kw)
+            res = func(*args, **kw)
         except ObjectDeleted, ex:
             res = ex.getResult()
             reindex = 0




More information about the CMF-checkins mailing list