diff --git a/product/ERP5/tests/testERP5Interfaces.py b/product/ERP5/tests/testERP5Interfaces.py index 48dbb3a029bd1ca4bf37cccef944666f8f43a854..2f7c602db91adafb1587db601cbc5ca49d077337 100644 --- a/product/ERP5/tests/testERP5Interfaces.py +++ b/product/ERP5/tests/testERP5Interfaces.py @@ -34,7 +34,10 @@ import unittest # manually and treated as reference to what implements what implements_tuple_list = [ ## ERP5Type + ('ERP5Type.Tool.TypesTool.TypesTool', 'IActionProvider'), + ('ERP5Type.ERP5Type.ERP5TypeInformation', 'IActionProvider'), ('ERP5Type.ERP5Type.ERP5TypeInformation', 'ILocalRoleAssignor'), + ('ActionInformation', 'IAction'), ('RoleInformation', 'ILocalRoleGenerator'), ## ERP5 ('BusinessPath', 'IArrowBase'), diff --git a/product/ERP5Type/Core/ActionInformation.py b/product/ERP5Type/Core/ActionInformation.py index d20cf5ecc151697fc62357e9f1249929cd227dd9..ec328990507ee19ffe54ccdf10ff336194b78c40 100644 --- a/product/ERP5Type/Core/ActionInformation.py +++ b/product/ERP5Type/Core/ActionInformation.py @@ -28,21 +28,19 @@ # ############################################################################## +import zope.interface from AccessControl import ClassSecurityInfo, getSecurityManager from Acquisition import aq_base from Products.CMFCore.Expression import Expression -from Products.CMFCore.ActionInformation import ActionInfo -from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface +from Products.ERP5Type import interfaces, Constraint, Permissions, PropertySheet from Products.ERP5Type.Permissions import AccessContentsInformation from Products.ERP5Type.XMLObject import XMLObject -from zLOG import LOG + class ActionInformation(XMLObject): """ ActionInformation is an ERP5 type which will eventually replace respective ActionInformation from CMF. """ - # XXX 'icon' property is not used. We can problably drop it. - meta_type = 'ERP5 Action Information' portal_type = 'Action Information' add_permission = Permissions.AddPortalContent @@ -54,14 +52,17 @@ class ActionInformation(XMLObject): security = ClassSecurityInfo() security.declareObjectProtected(AccessContentsInformation) + zope.interface.implements(interfaces.IAction) + # Declarative properties property_sheets = ( PropertySheet.CategoryCore , PropertySheet.DublinCore , PropertySheet.ActionInformation ) - security.declarePrivate('test') + security.declareProtected(AccessContentsInformation, 'test') def test(self, ec): + """Test if the action should be displayed or not for the given context""" if self.isVisible(): permission_list = self.getActionPermissionList() if permission_list: @@ -84,10 +85,19 @@ class ActionInformation(XMLObject): return condition is None or condition(ec) return False - security.declarePrivate('getActionUrl') - def getActionUrl(self, ec): + security.declareProtected(AccessContentsInformation, 'getActionInfo') + def getActionInfo(self, ec): + """Return a dict with values required to display the action""" action = self.getAction() - return action is not None and action(ec) or '' + icon = self.getIcon() + return {'id': self.getReference(), + 'name': self.getTitle(), + 'description': self.getDescription(), + 'category': self.getActionType(), + 'priority': self.getFloatIndex(), + 'icon': icon is not None and icon(ec) or '', + 'url': action is not None and action(ec) or '', + } def _setAction(self, value): """Overridden setter for 'action' to accept strings and clean null values diff --git a/product/ERP5Type/ERP5Type.py b/product/ERP5Type/ERP5Type.py index 188c26ba5d9a4b5faa954488227d0531561fe9e5..3eb8dc22d2b8088cf47a9e1a0d06e4704d24d7c7 100644 --- a/product/ERP5Type/ERP5Type.py +++ b/product/ERP5Type/ERP5Type.py @@ -230,6 +230,8 @@ class ERP5TypeInformation(XMLObject, security = ClassSecurityInfo() security.declareObjectProtected(Permissions.AccessContentsInformation) + zope.interface.implements(interfaces.IActionProvider) + # Declarative properties property_sheets = ( PropertySheet.BaseType, ) @@ -475,6 +477,7 @@ class ERP5TypeInformation(XMLObject, security.declarePrivate('getFilteredActionListFor') def getFilteredActionListFor(self, ob=None): + """Return all actions applicable to the object""" ec = getExprContext(self, ob) return (action for action in self.getActionInformationList() if action.test(ec)) diff --git a/product/ERP5Type/PropertySheet/ActionInformation.py b/product/ERP5Type/PropertySheet/ActionInformation.py index c6c3643e1c439a74785201521e54a3078f375836..5df0ad7246f9ee6e251f10cbff781fe4ba4f358e 100644 --- a/product/ERP5Type/PropertySheet/ActionInformation.py +++ b/product/ERP5Type/PropertySheet/ActionInformation.py @@ -66,7 +66,7 @@ class ActionInformation: 'description': 'TALES Expression to define the URL of the action', 'mode': 'w', }, - { 'id': 'icon', # XXX not used + { 'id': 'icon', 'type': 'object', 'description': 'TALES Expression to define the URL of the icon of the' \ ' action', diff --git a/product/ERP5Type/Tool/TypesTool.py b/product/ERP5Type/Tool/TypesTool.py index fea55da398b81b96e37ea7f312316225e9af6188..799ad73e2e876df9fc6e2c5a31dd1d729ee557b9 100644 --- a/product/ERP5Type/Tool/TypesTool.py +++ b/product/ERP5Type/Tool/TypesTool.py @@ -16,6 +16,7 @@ ############################################################################## import imp, sys +import zope.interface from Acquisition import aq_base from AccessControl import ClassSecurityInfo from OFS.Folder import Folder as OFSFolder @@ -23,7 +24,7 @@ import transaction from Products.CMFCore import TypesTool as CMFCore_TypesTool from Products.ERP5Type.Tool.BaseTool import BaseTool from Products.ERP5Type.Cache import CachingMethod -from Products.ERP5Type import Permissions +from Products.ERP5Type import interfaces, Permissions from Products.ERP5Type.ERP5Type import ERP5TypeInformation from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod from zLOG import LOG, WARNING, PANIC @@ -39,8 +40,11 @@ class TypesTool(BaseTool, CMFCore_TypesTool.TypesTool): security = ClassSecurityInfo() security.declareObjectProtected(Permissions.AccessContentsInformation) + zope.interface.implements(interfaces.IActionProvider) + security.declarePrivate('getFilteredActionListFor') def getFilteredActionListFor(self, ob=None): + """Return all actions applicable to the object""" if ob is not None: type_info = self.getTypeInfo(ob) if type_info is not None: diff --git a/product/ERP5Type/patches/ActionsTool.py b/product/ERP5Type/patches/ActionsTool.py index 6cf31a13893136ed8287d6cb15be5aafd99d53c7..84ea437d16a0a40f598075d263d7da4bb3bcfc5f 100644 --- a/product/ERP5Type/patches/ActionsTool.py +++ b/product/ERP5Type/patches/ActionsTool.py @@ -32,14 +32,9 @@ def listFilteredActionsFor(self, object=None): 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)), + actions += sorted( + (action.getActionInfo(ec) + for action in provider.getFilteredActionListFor(object)), key=lambda x: x['priority']) else: # for Action Providers written for CMF versions before 1.5 diff --git a/product/ERP5Type/patches/CMFCoreUtils.py b/product/ERP5Type/patches/CMFCoreUtils.py index 2f0d653725a1dce023be1686ced3f035d0f19bcb..b24d9e2b49411589a8ecf0a5177dd5ae85df0744 100644 --- a/product/ERP5Type/patches/CMFCoreUtils.py +++ b/product/ERP5Type/patches/CMFCoreUtils.py @@ -47,8 +47,8 @@ def CMFCoreUtils_getViewFor(obj, view='view'): if action.test(ec): break else: - # In case that "view" action is not present or not allowed, - # find something that's allowed. + # In case that "view" (or "list") action is not present or not allowed, + # find something that's allowed (of the same category, if possible). index = (action.getActionType().endswith('_' + view), -action.getFloatIndex()) if best_action[0] < index and action.test(ec): @@ -59,7 +59,7 @@ def CMFCoreUtils_getViewFor(obj, view='view'): raise AccessControl_Unauthorized('No accessible views available for %r' % obj.getPath()) - target = action.getActionUrl(context).strip() + target = action.getAction()(context).strip() if target.startswith('/'): target = target[1:] __traceback_info__ = ti.getId(), target