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
from OFS.SimpleItem import SimpleItem
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.utils import getToolByName, _getViewFor
from Products.CMFCore.utils import getToolByName
from Products.CMFCore.WorkflowCore import ObjectDeleted, ObjectMoved
from Products.CMFCore.CMFCatalogAware import CMFCatalogAware
......@@ -67,7 +69,6 @@ from Accessor import WorkflowState
from Products.ERP5Type.Log import log as unrestrictedLog
from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
from Products.ERP5Type.Accessor.TypeDefinition import type_definition
from ZopePatch import ERP5PropertyManager
from CopySupport import CopyContainer, CopyError,\
tryMethodCallWithTemporaryPermission
......
......@@ -28,7 +28,7 @@
#
##############################################################################
from AccessControl import ClassSecurityInfo
from AccessControl import ClassSecurityInfo, getSecurityManager
from Acquisition import aq_base
from Products.CMFCore.Expression import Expression
from Products.CMFCore.ActionInformation import ActionInfo
......@@ -60,10 +60,29 @@ class ActionInformation(XMLObject):
, PropertySheet.ActionInformation
)
def testCondition(self, ec):
"""Evaluate condition using context, 'ec', and return 0 or 1"""
condition = self.getCondition()
return condition is None and 1 or condition(ec)
security.declarePrivate('test')
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()
return condition is None or condition(ec)
return False
security.declarePublic('getVisibility')
def getVisibility(self):
......@@ -123,44 +142,6 @@ class ActionInformation(XMLObject):
self.getConditionText()]
return ' '.join(filter(None, search_source_list))
#
# XXX CMF compatibility
#
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)
security.declarePrivate('getActionUrl')
def getActionUrl(self, ec):
return self.getActionExpression()(ec)
......@@ -21,23 +21,15 @@
##############################################################################
import zope.interface
from Globals import InitializeClass, DTMLFile
from Globals import InitializeClass
from AccessControl import ClassSecurityInfo, getSecurityManager
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 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.exceptions import AccessControl_Unauthorized
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.XMLObject import XMLObject
......@@ -63,6 +55,22 @@ from zLOG import LOG, ERROR
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):
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
......@@ -147,20 +155,7 @@ class LocalRoleAssignorMixIn(object):
security.declarePrivate('getFilteredRoleListFor')
def getFilteredRoleListFor(self, ob=None):
"""Return all role generators applicable to the object."""
portal = self.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)
ec = createExprContext(folder, portal, ob)
ec = getExprContext(self, ob)
for role in self.getRoleInformationList():
if role.testCondition(ec):
yield role
......@@ -402,18 +397,6 @@ class ERP5TypeInformation(XMLObject,
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,
'getInstanceBaseCategoryList')
def getInstanceBaseCategoryList(self):
......@@ -459,10 +442,7 @@ class ERP5TypeInformation(XMLObject,
id = id + "d"
return factory_method(portal, id).propertyMap()
#
# Helper methods
#
def manage_editProperties(self, REQUEST):
def edit(self, *args, **kw):
"""
Method overload
......@@ -473,11 +453,10 @@ class ERP5TypeInformation(XMLObject,
in order to implement a broadcast update
on production hosts
"""
previous_property_sheet_list = self.property_sheet_list
base_category_list = self.base_category_list
result = FactoryTypeInformation.manage_editProperties(self, REQUEST)
if previous_property_sheet_list != self.property_sheet_list or \
base_category_list != self.base_category_list:
property_list = 'factory', 'property_sheet_list', 'base_category_list'
previous_state = [getattr(aq_base(self), x) for x in property_list]
result = XMLObject.edit(self, *args, **kw)
if previous_state != [getattr(aq_base(self), x) for x in property_list]:
from Products.ERP5Type.Base import _aq_reset
_aq_reset()
return result
......@@ -494,6 +473,12 @@ class ERP5TypeInformation(XMLObject,
search_source_list += self.getTypeBaseCategoryList(())
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,
'getActionInformationList')
def getActionInformationList(self):
......@@ -511,13 +496,13 @@ class ERP5TypeInformation(XMLObject,
security.declareProtected(Permissions.AccessContentsInformation,
'getAvailablePropertySheetList')
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('__'))
security.declareProtected(Permissions.AccessContentsInformation,
'getAvailableConstraintList')
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('__'))
security.declareProtected(Permissions.AccessContentsInformation,
......@@ -531,9 +516,21 @@ class ERP5TypeInformation(XMLObject,
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')
def addAction(self, id, name, action, condition, permission, category,
icon=None, visible=1, priority=1.0, REQUEST=None,
......
......@@ -39,6 +39,13 @@ class TypesTool(BaseTool, CMFCore_TypesTool.TypesTool):
security = ClassSecurityInfo()
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):
if not len(args):
......
......@@ -19,7 +19,7 @@ ActionsTool_listFilteredActionsFor = ActionsTool.listFilteredActionsFor
def listFilteredActionsFor(self, object=None):
""" 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.
"""
actions = []
......@@ -29,6 +29,18 @@ def listFilteredActionsFor(self, object=None):
provider = getattr(self, provider_name)
if IActionProvider.isImplementedBy(provider):
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:
# for Action Providers written for CMF versions before 1.5
actions.extend( self._listActionInfos(provider, object) )
......
......@@ -15,7 +15,6 @@
from warnings import warn
from Products.CMFCore.exceptions import AccessControl_Unauthorized, NotFound
from Products.CMFCore.utils import getActionContext
from Products.CMFCore.utils import _verifyActionPermissions
from Products.CMFCore.Expression import getExprContext
from Products.CMFCore import PortalContent
......@@ -25,8 +24,8 @@ from zLOG import LOG
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.
The modifications in this method are:
* new filter on default action (= "action.getCategory().endswith('_%s' % view)" statement)
* new test on action condition (= "action.testCondition(context)" statement)
* new filter on default action (= ".endswith('_%s' % view)" 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.
"""
......@@ -37,38 +36,33 @@ def CMFCoreUtils_getViewFor(obj, view='view'):
'Aliases.',
DeprecationWarning)
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 )
test_context = getExprContext(obj, obj) # Patch 1: mimic _listActionInfos in ActionsTool
actions = ti.listActions()
for action in actions:
# portal_types hack
action_type = action.getActionType()
reference = getattr(action, 'reference', action.id)
if reference == view or action_type.endswith('_%s' % view): # Patch 2: consider anything ending by _view
if _verifyActionPermissions(obj, action):
if action.isVisible() and action.testCondition(test_context): # Patch 3: test actions
target = action.action(context).strip()
if target.startswith('/'):
target = target[1:]
__traceback_info__ = ( ti.getId(), target )
return obj.restrictedTraverse( target )
# "view" action is not present or not allowed.
# 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('/'):
target = target[1:]
__traceback_info__ = ( ti.getId(), target )
return obj.restrictedTraverse( target )
raise AccessControl_Unauthorized( 'No accessible views available for '
'%s' % '/'.join( obj.getPhysicalPath() ) )
context = getActionContext(obj)
ec = getExprContext(obj, obj)
best_action = (), None
for action in ti.getFilteredActionListFor(obj):
if action.getReference() == view:
if action.test(ec):
break
else:
# In case that "view" action is not present or not allowed,
# find something that's allowed.
index = (action.getActionType().endswith('_' + view),
-action.getFloatIndex())
if best_action[0] < index and action.test(ec):
best_action = index, action
else:
raise NotFound('Cannot find default view for "%s"' %
'/'.join(obj.getPhysicalPath()))
action = best_action[1]
if action is None:
raise AccessControl_Unauthorized('No accessible views available for %r'
% obj.getPath())
target = action.getActionUrl(context).strip()
if target.startswith('/'):
target = target[1:]
__traceback_info__ = ti.getId(), target
return obj.restrictedTraverse(target)
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