Commit 98c0980b authored by Julien Muchembled's avatar Julien Muchembled

Bypass some CMF parts related to actions and start defining our API

git-svn-id: https://svn.erp5.org/repos/public/erp5/sandbox/portal_types@29256 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 94150f3a
...@@ -43,9 +43,11 @@ import OFS.History ...@@ -43,9 +43,11 @@ import OFS.History
from OFS.SimpleItem import SimpleItem from OFS.SimpleItem import SimpleItem
from OFS.PropertyManager import PropertyManager from OFS.PropertyManager import PropertyManager
from Products.CMFCore.PortalContent import PortalContent from ZopePatch import ERP5PropertyManager
from Products.CMFCore.PortalContent import PortalContent, _getViewFor
from Products.CMFCore.Expression import Expression from Products.CMFCore.Expression import Expression
from Products.CMFCore.utils import getToolByName, _getViewFor from Products.CMFCore.utils import getToolByName
from Products.CMFCore.WorkflowCore import ObjectDeleted, ObjectMoved from Products.CMFCore.WorkflowCore import ObjectDeleted, ObjectMoved
from Products.CMFCore.CMFCatalogAware import CMFCatalogAware from Products.CMFCore.CMFCatalogAware import CMFCatalogAware
...@@ -67,7 +69,6 @@ from Accessor import WorkflowState ...@@ -67,7 +69,6 @@ from Accessor import WorkflowState
from Products.ERP5Type.Log import log as unrestrictedLog from Products.ERP5Type.Log import log as unrestrictedLog
from Products.ERP5Type.TransactionalVariable import getTransactionalVariable from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
from Products.ERP5Type.Accessor.TypeDefinition import type_definition from Products.ERP5Type.Accessor.TypeDefinition import type_definition
from ZopePatch import ERP5PropertyManager
from CopySupport import CopyContainer, CopyError,\ from CopySupport import CopyContainer, CopyError,\
tryMethodCallWithTemporaryPermission tryMethodCallWithTemporaryPermission
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
# #
############################################################################## ##############################################################################
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo, getSecurityManager
from Acquisition import aq_base from Acquisition import aq_base
from Products.CMFCore.Expression import Expression from Products.CMFCore.Expression import Expression
from Products.CMFCore.ActionInformation import ActionInfo from Products.CMFCore.ActionInformation import ActionInfo
...@@ -60,10 +60,29 @@ class ActionInformation(XMLObject): ...@@ -60,10 +60,29 @@ class ActionInformation(XMLObject):
, PropertySheet.ActionInformation , PropertySheet.ActionInformation
) )
def testCondition(self, ec): security.declarePrivate('test')
"""Evaluate condition using context, 'ec', and return 0 or 1""" def test(self, ec):
if self.isVisible():
permission_list = self.getActionPermissionList()
if permission_list:
category = self.getActionType()
info = ec.vars
if (info['here'] is not None and
(category.startswith('object') or
category.startswith('workflow'))):
context = info['here']
elif (info['folder'] is not None and
category.startswith('folder')):
context = info['folder']
else:
context = info['portal']
has_permission = getSecurityManager().getUser().has_permission
for permission in permission_list:
if not has_permission(permission, context):
return False
condition = self.getCondition() condition = self.getCondition()
return condition is None and 1 or condition(ec) return condition is None or condition(ec)
return False
security.declarePublic('getVisibility') security.declarePublic('getVisibility')
def getVisibility(self): def getVisibility(self):
...@@ -123,44 +142,6 @@ class ActionInformation(XMLObject): ...@@ -123,44 +142,6 @@ class ActionInformation(XMLObject):
self.getConditionText()] self.getConditionText()]
return ' '.join(filter(None, search_source_list)) return ' '.join(filter(None, search_source_list))
# security.declarePrivate('getActionUrl')
# XXX CMF compatibility def getActionUrl(self, ec):
# return self.getActionExpression()(ec)
def _getActionObject(self):
return self.getActionExpression()
security.declarePrivate('getCategory')
def getCategory(self):
return self.getActionType()
security.declarePrivate('getPermissions')
def getPermissions(self):
return self.getActionPermissionList()
#def getActionCategorySelectionList(self):
# return self._getCategoryTool().action_type.objectIds()
#def getPriority(self):
# return self.getFloatIndex()
security.declarePrivate('getMapping')
def getMapping(self):
""" Get a mapping of this object's data.
"""
return { 'id': self.getReference(),
'title': self.getTitle(),
'description': self.getDescription(),
'category': self.getActionType(),
'condition': self.getCondition(),
'permissions': self.getPermissions(), #self.permissions,
'visible': self.getVisibility(), #bool(self.visible),
'action': self.getActionText() }
security.declarePrivate('getAction')
def getAction(self, ec):
""" Compute the action using context, 'ec'; return a mapping of
info about the action.
XXX To be renamed or removed,
so that 'action_expression' property can be renamed to 'action'.
"""
return ActionInfo(self, ec)
...@@ -21,23 +21,15 @@ ...@@ -21,23 +21,15 @@
############################################################################## ##############################################################################
import zope.interface import zope.interface
from Globals import InitializeClass, DTMLFile from Globals import InitializeClass
from AccessControl import ClassSecurityInfo, getSecurityManager from AccessControl import ClassSecurityInfo, getSecurityManager
from Acquisition import aq_base, aq_inner, aq_parent from Acquisition import aq_base, aq_inner, aq_parent
import Products
import Products.CMFCore.TypesTool
from Products.CMFCore.TypesTool import TypeInformation
from Products.CMFCore.TypesTool import FactoryTypeInformation from Products.CMFCore.TypesTool import FactoryTypeInformation
from Products.CMFCore.TypesTool import TypesTool
from Products.CMFCore.interfaces.portal_types import ContentTypeInformation\
as ITypeInformation
from Products.CMFCore.ActionProviderBase import ActionProviderBase
from Products.CMFCore.utils import SimpleItemWithProperties
from Products.CMFCore.Expression import createExprContext, Expression from Products.CMFCore.Expression import createExprContext, Expression
from Products.CMFCore.exceptions import AccessControl_Unauthorized from Products.CMFCore.exceptions import AccessControl_Unauthorized
from Products.CMFCore.utils import _checkPermission from Products.CMFCore.utils import _checkPermission
from Products.ERP5Type import _dtmldir, interfaces, Permissions, PropertySheet from Products.ERP5Type import interfaces, Constraint, Permissions, PropertySheet
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
from Products.ERP5Type.XMLObject import XMLObject from Products.ERP5Type.XMLObject import XMLObject
...@@ -63,6 +55,22 @@ from zLOG import LOG, ERROR ...@@ -63,6 +55,22 @@ from zLOG import LOG, ERROR
from Products.CMFCore.exceptions import zExceptions_Unauthorized from Products.CMFCore.exceptions import zExceptions_Unauthorized
def getExprContext(context, ob=None):
portal = context.getPortalObject()
if ob is None:
folder = portal
else:
folder = aq_parent(ob)
# 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):
break # found it.
else:
folder = aq_parent(folder)
return createExprContext(folder, portal, ob)
class LocalRoleAssignorMixIn(object): class LocalRoleAssignorMixIn(object):
security = ClassSecurityInfo() security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation) security.declareObjectProtected(Permissions.AccessContentsInformation)
...@@ -147,20 +155,7 @@ class LocalRoleAssignorMixIn(object): ...@@ -147,20 +155,7 @@ class LocalRoleAssignorMixIn(object):
security.declarePrivate('getFilteredRoleListFor') security.declarePrivate('getFilteredRoleListFor')
def getFilteredRoleListFor(self, ob=None): def getFilteredRoleListFor(self, ob=None):
"""Return all role generators applicable to the object.""" """Return all role generators applicable to the object."""
portal = self.getPortalObject() ec = getExprContext(self, ob)
if ob is None:
folder = portal
else:
folder = aq_parent(ob)
# 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):
break # found it.
else:
folder = aq_parent(folder)
ec = createExprContext(folder, portal, ob)
for role in self.getRoleInformationList(): for role in self.getRoleInformationList():
if role.testCondition(ec): if role.testCondition(ec):
yield role yield role
...@@ -402,18 +397,6 @@ class ERP5TypeInformation(XMLObject, ...@@ -402,18 +397,6 @@ class ERP5TypeInformation(XMLObject,
return ob return ob
security.declareProtected(Permissions.ManagePortal,
'setPropertySheetList')
def setPropertySheetList(self, property_sheet_list):
# XXX CMF compatibility
self._setTypePropertySheetList(property_sheet_list)
security.declareProtected(Permissions.AccessContentsInformation,
'getHiddenContentTypeList')
def getHiddenContentTypeList(self):
# XXX CMF compatibility
return self.getTypeHiddenContentTypeList(())
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'getInstanceBaseCategoryList') 'getInstanceBaseCategoryList')
def getInstanceBaseCategoryList(self): def getInstanceBaseCategoryList(self):
...@@ -459,10 +442,7 @@ class ERP5TypeInformation(XMLObject, ...@@ -459,10 +442,7 @@ class ERP5TypeInformation(XMLObject,
id = id + "d" id = id + "d"
return factory_method(portal, id).propertyMap() return factory_method(portal, id).propertyMap()
# def edit(self, *args, **kw):
# Helper methods
#
def manage_editProperties(self, REQUEST):
""" """
Method overload Method overload
...@@ -473,11 +453,10 @@ class ERP5TypeInformation(XMLObject, ...@@ -473,11 +453,10 @@ class ERP5TypeInformation(XMLObject,
in order to implement a broadcast update in order to implement a broadcast update
on production hosts on production hosts
""" """
previous_property_sheet_list = self.property_sheet_list property_list = 'factory', 'property_sheet_list', 'base_category_list'
base_category_list = self.base_category_list previous_state = [getattr(aq_base(self), x) for x in property_list]
result = FactoryTypeInformation.manage_editProperties(self, REQUEST) result = XMLObject.edit(self, *args, **kw)
if previous_property_sheet_list != self.property_sheet_list or \ if previous_state != [getattr(aq_base(self), x) for x in property_list]:
base_category_list != self.base_category_list:
from Products.ERP5Type.Base import _aq_reset from Products.ERP5Type.Base import _aq_reset
_aq_reset() _aq_reset()
return result return result
...@@ -494,6 +473,12 @@ class ERP5TypeInformation(XMLObject, ...@@ -494,6 +473,12 @@ class ERP5TypeInformation(XMLObject,
search_source_list += self.getTypeBaseCategoryList(()) search_source_list += self.getTypeBaseCategoryList(())
return ' '.join(filter(None, search_source_list)) return ' '.join(filter(None, search_source_list))
security.declarePrivate('getFilteredActionListFor')
def getFilteredActionListFor(self, ob=None):
ec = getExprContext(self, ob)
return (action for action in self.getActionInformationList()
if action.test(ec))
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'getActionInformationList') 'getActionInformationList')
def getActionInformationList(self): def getActionInformationList(self):
...@@ -511,13 +496,13 @@ class ERP5TypeInformation(XMLObject, ...@@ -511,13 +496,13 @@ class ERP5TypeInformation(XMLObject,
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'getAvailablePropertySheetList') 'getAvailablePropertySheetList')
def getAvailablePropertySheetList(self): def getAvailablePropertySheetList(self):
return sorted(k for k in Products.ERP5Type.PropertySheet.__dict__ return sorted(k for k in PropertySheet.__dict__
if not k.startswith('__')) if not k.startswith('__'))
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'getAvailableConstraintList') 'getAvailableConstraintList')
def getAvailableConstraintList(self): def getAvailableConstraintList(self):
return sorted(k for k in Products.ERP5Type.Constraint.__dict__ return sorted(k for k in Constraint.__dict__
if k != 'Constraint' and not k.startswith('__')) if k != 'Constraint' and not k.startswith('__'))
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
...@@ -531,9 +516,21 @@ class ERP5TypeInformation(XMLObject, ...@@ -531,9 +516,21 @@ class ERP5TypeInformation(XMLObject,
return sorted(self._getCategoryTool().getBaseCategoryList()) return sorted(self._getCategoryTool().getBaseCategoryList())
# #
# Compatibitility code for actions # XXX CMF compatibility
# #
security.declareProtected(Permissions.ManagePortal,
'setPropertySheetList')
def setPropertySheetList(self, property_sheet_list):
self._setTypePropertySheetList(property_sheet_list)
security.declareProtected(Permissions.AccessContentsInformation,
'getHiddenContentTypeList')
def getHiddenContentTypeList(self):
return self.getTypeHiddenContentTypeList(())
# Compatibitility code for actions
security.declareProtected(Permissions.ModifyPortalContent, 'addAction') security.declareProtected(Permissions.ModifyPortalContent, 'addAction')
def addAction(self, id, name, action, condition, permission, category, def addAction(self, id, name, action, condition, permission, category,
icon=None, visible=1, priority=1.0, REQUEST=None, icon=None, visible=1, priority=1.0, REQUEST=None,
......
...@@ -39,6 +39,13 @@ class TypesTool(BaseTool, CMFCore_TypesTool.TypesTool): ...@@ -39,6 +39,13 @@ class TypesTool(BaseTool, CMFCore_TypesTool.TypesTool):
security = ClassSecurityInfo() security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation) security.declareObjectProtected(Permissions.AccessContentsInformation)
security.declarePrivate('getFilteredActionListFor')
def getFilteredActionListFor(self, ob=None):
if ob is not None:
type_info = self.getTypeInfo(ob)
if type_info is not None:
return type_info.getFilteredActionListFor(ob)
return ()
def getTypeInfo(self, *args): def getTypeInfo(self, *args):
if not len(args): if not len(args):
......
...@@ -19,7 +19,7 @@ ActionsTool_listFilteredActionsFor = ActionsTool.listFilteredActionsFor ...@@ -19,7 +19,7 @@ ActionsTool_listFilteredActionsFor = ActionsTool.listFilteredActionsFor
def listFilteredActionsFor(self, object=None): def listFilteredActionsFor(self, object=None):
""" List all actions available to the user. """ List all actions available to the user.
This patch remove inclusion of actions from the object itself. This patch removes inclusion of actions from the object itself.
It was never used and now, it breaks objects inside Types Tool. It was never used and now, it breaks objects inside Types Tool.
""" """
actions = [] actions = []
...@@ -29,6 +29,18 @@ def listFilteredActionsFor(self, object=None): ...@@ -29,6 +29,18 @@ def listFilteredActionsFor(self, object=None):
provider = getattr(self, provider_name) provider = getattr(self, provider_name)
if IActionProvider.isImplementedBy(provider): if IActionProvider.isImplementedBy(provider):
actions.extend( provider.listActionInfos(object=object) ) actions.extend( provider.listActionInfos(object=object) )
elif hasattr(provider, 'getFilteredActionListFor'):
from Products.ERP5Type.ERP5Type import getExprContext
ec = getExprContext(self, object)
actions += sorted(({
'id': action.getReference(),
'name': action.getTitle(),
'description': action.getDescription(),
'category': action.getActionType(),
'priority': action.getFloatIndex(),
'url': action.getActionUrl(ec),
} for action in provider.getFilteredActionListFor(object)),
key=lambda x: x['priority'])
else: else:
# for Action Providers written for CMF versions before 1.5 # for Action Providers written for CMF versions before 1.5
actions.extend( self._listActionInfos(provider, object) ) actions.extend( self._listActionInfos(provider, object) )
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
from warnings import warn from warnings import warn
from Products.CMFCore.exceptions import AccessControl_Unauthorized, NotFound from Products.CMFCore.exceptions import AccessControl_Unauthorized, NotFound
from Products.CMFCore.utils import getActionContext from Products.CMFCore.utils import getActionContext
from Products.CMFCore.utils import _verifyActionPermissions
from Products.CMFCore.Expression import getExprContext from Products.CMFCore.Expression import getExprContext
from Products.CMFCore import PortalContent from Products.CMFCore import PortalContent
...@@ -25,8 +24,8 @@ from zLOG import LOG ...@@ -25,8 +24,8 @@ from zLOG import LOG
This patch is based on Products/CMFCore/utils.py file from CMF 1.5.0 package. This patch is based on Products/CMFCore/utils.py file from CMF 1.5.0 package.
Please update the following file if you update CMF to greater version. Please update the following file if you update CMF to greater version.
The modifications in this method are: The modifications in this method are:
* new filter on default action (= "action.getCategory().endswith('_%s' % view)" statement) * new filter on default action (= ".endswith('_%s' % view)" statement)
* new test on action condition (= "action.testCondition(context)" statement) * use action API to check its visibility in the context
This method was patched to let CMF choose between several default actions according conditions. This method was patched to let CMF choose between several default actions according conditions.
""" """
...@@ -37,38 +36,33 @@ def CMFCoreUtils_getViewFor(obj, view='view'): ...@@ -37,38 +36,33 @@ def CMFCoreUtils_getViewFor(obj, view='view'):
'Aliases.', 'Aliases.',
DeprecationWarning) DeprecationWarning)
ti = obj.getTypeInfo() ti = obj.getTypeInfo()
if ti is None:
raise NotFound('Cannot find default view for %r' % obj.getPath())
if ti is not None: context = getActionContext(obj)
context = getActionContext( obj ) ec = getExprContext(obj, obj)
test_context = getExprContext(obj, obj) # Patch 1: mimic _listActionInfos in ActionsTool best_action = (), None
actions = ti.listActions() for action in ti.getFilteredActionListFor(obj):
for action in actions: if action.getReference() == view:
# portal_types hack if action.test(ec):
action_type = action.getActionType() break
reference = getattr(action, 'reference', action.id) else:
if reference == view or action_type.endswith('_%s' % view): # Patch 2: consider anything ending by _view # In case that "view" action is not present or not allowed,
if _verifyActionPermissions(obj, action): # find something that's allowed.
if action.isVisible() and action.testCondition(test_context): # Patch 3: test actions index = (action.getActionType().endswith('_' + view),
target = action.action(context).strip() -action.getFloatIndex())
if target.startswith('/'): if best_action[0] < index and action.test(ec):
target = target[1:] best_action = index, action
__traceback_info__ = ( ti.getId(), target ) else:
return obj.restrictedTraverse( target ) action = best_action[1]
if action is None:
raise AccessControl_Unauthorized('No accessible views available for %r'
% obj.getPath())
# "view" action is not present or not allowed. target = action.getActionUrl(context).strip()
# Find something that's allowed.
for action in actions:
if _verifyActionPermissions(obj, action):
if action.visible and action.testCondition(test_context): # Patch 3: test actions
target = action.action(context).strip()
if target.startswith('/'): if target.startswith('/'):
target = target[1:] target = target[1:]
__traceback_info__ = ( ti.getId(), target ) __traceback_info__ = ti.getId(), target
return obj.restrictedTraverse( target ) return obj.restrictedTraverse(target)
raise AccessControl_Unauthorized( 'No accessible views available for '
'%s' % '/'.join( obj.getPhysicalPath() ) )
else:
raise NotFound('Cannot find default view for "%s"' %
'/'.join(obj.getPhysicalPath()))
PortalContent._getViewFor = CMFCoreUtils_getViewFor PortalContent._getViewFor = CMFCoreUtils_getViewFor
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment