[CMF-checkins] CVS: CMF/CMFCore - ActionsTool.py:1.38.2.1 PortalFolder.py:1.43.18.1 TypesTool.py:1.49.2.1 utils.py:1.36.4.1

Tres Seaver tseaver@zope.com
Mon, 10 Mar 2003 18:50:26 -0500


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

Modified Files:
      Tag: tseaver-ti_apb_redux-branch
	ActionsTool.py PortalFolder.py TypesTool.py utils.py 
Log Message:


  Landing 'tseaver-typeinfo_as_apb-branch' against current HEAD, using
  a new integration branch:

  - Merged the branch and fixed up all conflicts;  all tests pass.

  - Updated all the 'factory_type_information' structures to make the
    'action' key a TALES expression (e.g., "document_view" ->
    "string:document_view").

  Remaining TODOs:

  - Write an external method for converting existing type info objects
    (they all have dictionaries, instead of ActionInformation objects,
    stored in the ZODB).  Otherwise, all existing type info objects are
    *toast*.

  - Perhaps someone can think of a clever way to avoid the need for this
    conversion?


=== CMF/CMFCore/ActionsTool.py 1.38 => 1.38.2.1 ===
--- CMF/CMFCore/ActionsTool.py:1.38	Thu Feb 13 02:44:07 2003
+++ CMF/CMFCore/ActionsTool.py	Mon Mar 10 18:49:53 2003
@@ -70,6 +70,7 @@
     action_providers = ( 'portal_membership'
                        , 'portal_actions'
                        , 'portal_registration'
+                       , 'portal_types'
                        , 'portal_discussion'
                        , 'portal_undo'
                        , 'portal_syndication'
@@ -176,6 +177,7 @@
         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)
@@ -184,27 +186,6 @@
         # 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)
 


=== CMF/CMFCore/PortalFolder.py 1.43 => 1.43.18.1 ===
--- CMF/CMFCore/PortalFolder.py:1.43	Sat Aug  3 23:51:56 2002
+++ CMF/CMFCore/PortalFolder.py	Mon Mar 10 18:49:53 2003
@@ -32,38 +32,37 @@
 from DynamicType import DynamicType
 from utils import getToolByName, _checkPermission
 
-factory_type_information = ( { 'id'             : 'Folder'
-                             , 'meta_type'      : 'Portal Folder'
-                             , 'description'    : """\
-Use folders to put content in categories."""
-                             , 'icon'           : 'folder_icon.gif'
-                             , 'product'        : 'CMFCore'
-                             , 'factory'        : 'manage_addPortalFolder'
-                             , 'filter_content_types' : 0
-                             , 'immediate_view' : 'folder_edit_form'
-                             , 'actions'        :
-                                ( { 'id'            : 'view'
-                                  , 'name'          : 'View'
-                                  , 'action'        : ''
-                                  , 'permissions'   : (View,)
-                                  , 'category'      : 'folder'
-                                  }
-                                , { 'id'            : 'edit'
-                                  , 'name'          : 'Edit'
-                                  , 'action'        : 'folder_edit_form'
-                                  , 'permissions'   : (ManageProperties,)
-                                  , 'category'      : 'folder'
-                                  }
-                                , { 'id'            : 'localroles'
-                                  , 'name'          : 'Local Roles'
-                                  , 'action'        : 'folder_localrole_form'
-                                  , 'permissions'   : (ManageProperties,)
-                                  , 'category'      : 'folder'
-                                  }
-                                )
-                             }
-                           ,
-                           )
+factory_type_information = (
+  { 'id'             : 'Folder'
+  , 'meta_type'      : 'Portal Folder'
+  , 'description'    : """ Use folders to put content in categories."""
+  , 'icon'           : 'folder_icon.gif'
+  , 'product'        : 'CMFCore'
+  , 'factory'        : 'manage_addPortalFolder'
+  , 'filter_content_types' : 0
+  , 'immediate_view' : 'folder_edit_form'
+  , 'actions'        : ( { 'id'            : 'view'
+                         , 'name'          : 'View'
+                         , 'action'        : 'string:'
+                         , 'permissions'   : (View,)
+                         , 'category'      : 'folder'
+                         }
+                       , { 'id'            : 'edit'
+                         , 'name'          : 'Edit'
+                         , 'action'        : 'string:folder_edit_form'
+                         , 'permissions'   : (ManageProperties,)
+                         , 'category'      : 'folder'
+                         }
+                       , { 'id'            : 'localroles'
+                         , 'name'          : 'Local Roles'
+                         , 'action'        : 'string:folder_localrole_form'
+                         , 'permissions'   : (ManageProperties,)
+                         , 'category'      : 'folder'
+                         }
+                       )
+  }
+,
+)
 
 
 class PortalFolder(DynamicType, CMFCatalogAware, Folder):


=== CMF/CMFCore/TypesTool.py 1.49 => 1.49.2.1 ===
--- CMF/CMFCore/TypesTool.py:1.49	Thu Feb 13 02:44:48 2003
+++ CMF/CMFCore/TypesTool.py	Mon Mar 10 18:49:53 2003
@@ -14,52 +14,58 @@
 
 $Id$
 """
+import sys
 
-from Globals import InitializeClass, DTMLFile
-from OFS.Folder import Folder
-
-from utils import _checkPermission
-from utils import _dtmldir
-from utils import cookString
-from utils import SimpleItemWithProperties
-from utils import UniqueObject
-from AccessControl import getSecurityManager, ClassSecurityInfo, Unauthorized
+from Globals import InitializeClass
+from Globals import DTMLFile
+from AccessControl import getSecurityManager
+from AccessControl import ClassSecurityInfo
+from AccessControl import Unauthorized
 from Acquisition import aq_base
+from zLOG import LOG, WARNING, ERROR
+
+from OFS.Folder import Folder
 import Products
+
+from interfaces.portal_types import ContentTypeInformation as ITypeInformation
+from interfaces.portal_types import portal_types as ITypesTool
+
 from ActionProviderBase import ActionProviderBase
 from ActionInformation import ActionInformation
 from Expression import Expression
-from zLOG import LOG, WARNING, ERROR
-import sys
 
 from CMFCorePermissions import View
 from CMFCorePermissions import ManagePortal
 from CMFCorePermissions import AccessContentsInformation
 
-from interfaces.portal_types import ContentTypeInformation as ITypeInformation
-from interfaces.portal_types import portal_types as ITypesTool
+from utils import UniqueObject
+from utils import SimpleItemWithProperties
+from utils import _dtmldir
+from utils import _checkPermission
+from utils import cookString
+from utils import getActionContext
 
 _marker = []  # Create a new marker.
 
-class TypeInformation (SimpleItemWithProperties):
+class TypeInformation (SimpleItemWithProperties, ActionProviderBase):
     """
     Base class for information about a content type.
     """
 
     _isTypeInformation = 1
 
-    manage_options = (SimpleItemWithProperties.manage_options[:1] +
-                      ({'label':'Actions',
-                        'action':'manage_editActionsForm'},) +
-                      SimpleItemWithProperties.manage_options[1:])
+    manage_options = ( SimpleItemWithProperties.manage_options[:1]
+                     + ActionProviderBase.manage_options
+                     + SimpleItemWithProperties.manage_options[1:]
+                     )
 
 
     security = ClassSecurityInfo()
+
     security.declareProtected(ManagePortal, 'manage_editProperties')
     security.declareProtected(ManagePortal, 'manage_changeProperties')
     security.declareProtected(ManagePortal, 'manage_propertiesForm')
 
-
     _basic_properties = (
         {'id':'title', 'type': 'string', 'mode':'w',
          'label':'Title'},
@@ -98,31 +104,50 @@
     allowed_content_types = ()
     allow_discussion = 0
     global_allow = 1
-    _actions = ()
 
     def __init__(self, id, **kw):
+
         self.id = id
+
+        kw = kw.copy()  # Get a modifiable dict.
+
         if kw:
-            kw = kw.copy()  # Get a modifiable dict.
+
             if (not kw.has_key('content_meta_type')
                 and kw.has_key('meta_type')):
                 kw['content_meta_type'] = kw['meta_type']
+
             if (not kw.has_key('content_icon')
                 and kw.has_key('icon')):
                 kw['content_icon'] = kw['icon']
+
             apply(self.manage_changeProperties, (), kw)
-            if kw.has_key('actions'):
-                aa = kw['actions']
-                actions = []
-                for action in aa:
-                    action = action.copy()
-                    # Some backward compatibility stuff.
-                    if not action.has_key('id'):
-                        action['id'] = cookString(action['name'])
-                    if not action.has_key('category'):
-                        action['category'] = 'object'
-                    actions.append(action)
-                self._actions = tuple(actions)
+
+        aa = kw.get( 'actions', () )
+
+        for action in aa:
+
+            action = action.copy()
+
+            # Some backward compatibility stuff.
+            if not action.has_key('id'):
+                action['id'] = cookString(action['name'])
+
+            if not action.has_key('name'):
+                action['name'] = action['id'].capitalize()
+
+            # XXX:  historically, action['action'] is simple string
+
+            self.addAction( id=action['id']
+                          , name=action['name']
+                          , action=action.get( 'action' )
+                          , condition=action.get( 'condition' )
+                          , permission=action.get('permissions', () )
+                          , category=action.get( 'category', 'object' )
+                          , visible=action.get( 'visible', 1 )
+                          )
+
+        
 
     #
     #   Accessors
@@ -200,14 +225,6 @@
         """
         return self.allow_discussion
 
-    security.declarePrivate('getActions')
-    def getActions(self):
-        """
-        Returns the customizable user actions.
-        """
-        # Private because this returns the actual structure.
-        return self._actions
-
     security.declarePublic('globalAllow')
     def globalAllow(self):
         """
@@ -220,15 +237,15 @@
         """
             Return the URL of the action whose ID is id.
         """
-        for action in self.getActions():
+        context = getActionContext( self )
+        for action in self.listActions() or ():
 
-            if action.has_key('id'):
-                if action['id'] == id:
-                    return action['action']
+            if action.getId() == id:
+                return action.action( context )
             else:
                 # Temporary backward compatibility.
-                if action['name'].lower() == id:
-                    return action['action']
+                if action.Title().lower() == id:
+                    return action.action( context )
 
         if default is _marker:
             raise TypeError, ( 'No action "%s" for type "%s"'
@@ -237,181 +254,44 @@
         else:
             return default
 
-    #
-    #  Action editing interface
-    #
-    _actions_form = DTMLFile( 'editActions', _dtmldir )
-    
-    security.declareProtected(ManagePortal, 'manage_editActionsForm')
-    def manage_editActionsForm(self, REQUEST, manage_tabs_message=None):
-        """
-        Shows the 'Actions' management tab.
-        """
-        actions = []
-        for a in self.getActions():
-            a = a.copy()
-            p = a['permissions']
-            if p:
-                a['permission'] = p[0]
-            else:
-                a['permission'] = ''
-            if not a.has_key('category'):
-                a['category'] = 'object'
-            if not a.has_key('id'):
-                a['id'] = cookString(a['name'])
-            if not a.has_key( 'visible' ):
-                a['visible'] = 1
-            actions.append(a)
-        # possible_permissions is in AccessControl.Role.RoleManager.
-        pp = self.possible_permissions()
-        return self._actions_form(self, REQUEST,
-                                  actions=actions,
-                                  possible_permissions=pp,
-                                  management_view='Actions',
-                                  manage_tabs_message=manage_tabs_message)
-
-    security.declareProtected(ManagePortal, 'addAction')
-    def addAction( self
-                 , id
-                 , name
-                 , action
-                 , permission
-                 , category
-                 , visible=1
-                 , REQUEST=None
-                 ):
-        """
-        Adds an action to the list.
-        """
-        if not name:
-            raise ValueError('A name is required.')
-
-        new_actions = self._cloneActions()
-
-        new_actions.append( { 'id'          : str(id)
-                            , 'name'        : str(name)
-                            , 'action'      : str(action)
-                            , 'permissions' : (str(permission),)
-                            , 'category'    : str(category)
-                            , 'visible'     : int(visible) 
-                            } )
-
-        self._actions = tuple( new_actions )
-
-        if REQUEST is not None:
-            return self.manage_editActionsForm(
-                REQUEST, manage_tabs_message='Added.')
-    
-    security.declareProtected(ManagePortal, 'changeActions')
-    def changeActions(self, properties=None, REQUEST=None):
-        """
-        Changes the _actions.
-        """
-        if properties is None:
-            properties = REQUEST
-        actions = []
-        for idx in range(len(self._actions)):
-            s_idx = str(idx)
-            action = self._actions[idx].copy()
-            action.update( {
-                    'id': str(properties.get('id_' + s_idx, '')),
-                    'name': str(properties.get('name_' + s_idx, '')),
-                    'action': str(properties.get('action_' + s_idx, '')),
-                    'permissions':
-                    (properties.get('permission_' + s_idx, ()),),
-                    'category': str(properties.get('category_' + s_idx, '')),
-                    'visible': not not properties.get('visible_' + s_idx, 0),
-                    } )
-            if not action['name']:
-                raise ValueError('A name is required.')
-            actions.append( action )
-        self._actions = tuple( actions )
-        if REQUEST is not None:
-            return self.manage_editActionsForm(REQUEST, manage_tabs_message=
-                                               'Actions changed.')
-
-    security.declareProtected(ManagePortal, 'deleteActions')
-    def deleteActions(self, selections=(), REQUEST=None):
-        """
-        Deletes actions.
-        """
-        sels = list(map(int, selections))  # Convert to a list of integers.
-        sels.sort()
-        sels.reverse()
-
-        new_actions = self._cloneActions()
-
-        for idx in sels:
-            del new_actions[idx]
-
-        self._actions = tuple(new_actions)
-
-        if REQUEST is not None:
-            return self.manage_editActionsForm(
-                REQUEST, manage_tabs_message=(
-                'Deleted %d action(s).' % len(sels)))
-
-    security.declareProtected(ManagePortal, 'moveUpActions')
-    def moveUpActions(self, selections=(), REQUEST=None):
-        """
-        Moves the specified actions up one slot.
-        """
-        sels = list(map(int, selections))  # Convert to a list of integers.
-        sels.sort()
-
-        new_actions = self._cloneActions()
-
-        for idx in sels:
-            idx2 = idx - 1
-            if idx2 < 0:
-                # Wrap to the bottom.
-                idx2 = len(new_actions) - 1
-            # Swap.
-            a = new_actions[idx2]
-            new_actions[idx2] = new_actions[idx]
-            new_actions[idx] = a
-
-        self._actions = tuple(new_actions)
-
-        if REQUEST is not None:
-            return self.manage_editActionsForm(
-                REQUEST, manage_tabs_message=(
-                'Moved up %d action(s).' % len(sels)))
-
-    security.declareProtected(ManagePortal, 'moveDownActions')
-    def moveDownActions(self, selections=(), REQUEST=None):
-        """
-        Moves the specified actions down one slot.
-        """
-        sels = list(map(int, selections))  # Convert to a list of integers.
-        sels.sort()
-        sels.reverse()
-
-        new_actions = self._cloneActions()
-
-        for idx in sels:
-            idx2 = idx + 1
-            if idx2 >= len(new_actions):
-                # Wrap to the top.
-                idx2 = 0
-            # Swap.
-            a = new_actions[idx2]
-            new_actions[idx2] = new_actions[idx]
-            new_actions[idx] = a
-
-        self._actions = tuple(new_actions)
-
-        if REQUEST is not None:
-            return self.manage_editActionsForm(
-                REQUEST, manage_tabs_message=(
-                'Moved down %d action(s).' % len(sels)))
-
-    security.declarePrivate( '_cloneActions' )
-    def _cloneActions( self ):
+    security.declarePrivate( '_convertActions' )
+    def _convertActions( self ):
         """
-            Return a "deep copy" of our list of actions.
+            Upgrade dictionary-based actions.
         """
-        return map( lambda x: x.copy(), list( self._actions ) )
+        if not self._actions:
+            return
+
+        if type( self._actions[0] ) == type( {} ):
+
+            aa, self._actions = self._actions, ()
+
+            for action in aa:
+
+                # XXX:  historically, action['action'] is simple string
+
+                self.addAction( id=action['id']
+                            , name=action['name']
+                            , action=action.get( 'action' )
+                            , condition=action.get( 'condition' )
+                            , permission=action.get('permissions', () )
+                            , category=action.get( 'category', 'object' )
+                            , visible=action.get( 'visible', 1 )
+                            )
+        else:
+
+            new_actions = []
+            for clone in self._cloneActions():
+
+                a_expr = clone.getActionExpression()
+
+                # XXX heuristic, may miss
+                if a_expr and ':' not in a_expr and '/' not in a_expr:
+                    clone.action = Expression( 'string:%s' % a_expr )
+
+                new_actions.append( clone )
+
+            self._actions = tuple( new_actions )
 
     security.declarePrivate('_finishConstruction')
     def _finishConstruction(self, ob):
@@ -617,7 +497,6 @@
 
     id = 'portal_types'
     meta_type = 'CMF Types Tool'
-    _actions = ()
 
     security = ClassSecurityInfo()
 
@@ -834,5 +713,21 @@
             immediate_url = '%s/%s' % ( ob.absolute_url()
                                       , info.immediate_view )
             RESPONSE.redirect( immediate_url )
+
+    security.declarePrivate( 'listActions' )
+    def listActions( self, info=None ):
+        """
+            List type-related actions.
+        """
+        actions = list( self._actions )
+
+        if info is not None:
+
+            type_info = self.getTypeInfo( info.content )
+
+            if type_info is not None:
+                actions.extend( type_info.listActions( info ) )
+
+        return actions
 
 InitializeClass( TypesTool )


=== CMF/CMFCore/utils.py 1.36 => 1.36.4.1 ===
--- CMF/CMFCore/utils.py:1.36	Tue Jan 21 12:53:59 2003
+++ CMF/CMFCore/utils.py	Mon Mar 10 18:49:53 2003
@@ -17,6 +17,12 @@
 import operator
 from types import StringType
 
+from Globals import package_home
+from Globals import HTMLFile
+from Globals import ImageFile
+from Globals import InitializeClass
+from Globals import MessageDialog
+
 from ExtensionClass import Base
 from Acquisition import aq_get, aq_inner, aq_parent
 
@@ -27,12 +33,6 @@
 from AccessControl.PermissionRole import rolesForPermissionOn
 from AccessControl.Role import gather_permissions
 
-from Globals import package_home
-from Globals import InitializeClass
-from Globals import HTMLFile
-from Globals import ImageFile
-from Globals import MessageDialog
-
 from OFS.PropertyManager import PropertyManager
 from OFS.SimpleItem import SimpleItem
 from OFS.PropertySheets import PropertySheets
@@ -42,6 +42,8 @@
     from OFS.ObjectManager import UNIQUE
 except ImportError:
     UNIQUE = 2
+from Products.PageTemplates.Expressions import getEngine
+from Products.PageTemplates.Expressions import SecureModuleImporter
 
 
 security = ModuleSecurityInfo( 'Products.CMFCore.utils' )
@@ -119,7 +121,7 @@
 
 security.declarePrivate('_verifyActionPermissions')
 def _verifyActionPermissions(obj, action):
-    pp = action.get('permissions', ())
+    pp = action.getPermissions()
     if not pp:
         return 1
     for p in pp:
@@ -127,20 +129,42 @@
             return 1
     return 0
 
+security.declarePublic( 'getActionContext' )
+def getActionContext( self ):
+    data = { 'object_url'   : ''
+           , 'folder_url'   : ''
+           , 'portal_url'   : ''
+           , 'object'       : None
+           , 'folder'       : None
+           , 'portal'       : None
+           , 'nothing'      : None
+           , 'request'      : getattr( self, 'REQUEST', None )
+           , 'modules'      : SecureModuleImporter
+           , 'member'       : None
+           }
+    return getEngine().getContext( data )
+
 security.declarePrivate('_getViewFor')
 def _getViewFor(obj, view='view'):
     ti = obj.getTypeInfo()
+
     if ti is not None:
-        actions = ti.getActions()
+
+        context = getActionContext( obj )
+        actions = ti.listActions()
+
         for action in actions:
-            if action.get('id', None) == view:
-                if _verifyActionPermissions(obj, action):
-                    return obj.restrictedTraverse(action['action'])
+
+            if action.getId() == view:
+                if _verifyActionPermissions( obj, action ):
+                    return obj.restrictedTraverse( action.action( context ) )
+
         # "view" action is not present or not allowed.
         # Find something that's allowed.
         for action in actions:
             if _verifyActionPermissions(obj, action):
-                return obj.restrictedTraverse(action['action'])
+                return obj.restrictedTraverse( action.action( context ) )
+
         raise 'Unauthorized', ('No accessible views available for %s' %
                                '/'.join(obj.getPhysicalPath()))
     else:
@@ -364,7 +388,6 @@
             tool.icon = 'misc_/%s/%s' % (self.product_name, self.icon)
 
 InitializeClass( ToolInit )
-
 
 addInstanceForm = HTMLFile('dtml/addInstance', globals())