Commit e4cf8f6a authored by Jens Vagelpohl's avatar Jens Vagelpohl

- exportimport: Support for instance creation guards and manager

  bypass added.
  (https://bugs.launchpad.net/zope-cmf/+bug/308947)
parent 78560857
...@@ -4,6 +4,10 @@ Products.DCWorkflow Changelog ...@@ -4,6 +4,10 @@ Products.DCWorkflow Changelog
2.2.0 (unreleased) 2.2.0 (unreleased)
------------------ ------------------
- exportimport: Support for instance creation guards and manager
bypass added.
(https://bugs.launchpad.net/zope-cmf/+bug/308947)
- Cleaned up / normalized imports: - Cleaned up / normalized imports:
o Don't import from Globals; instead, use real locations. o Don't import from Globals; instead, use real locations.
......
...@@ -27,6 +27,7 @@ from zope.component import adapts ...@@ -27,6 +27,7 @@ from zope.component import adapts
from Products.CMFCore.Expression import Expression from Products.CMFCore.Expression import Expression
from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition
from Products.DCWorkflow.Guard import Guard
from Products.DCWorkflow.interfaces import IDCWorkflowDefinition from Products.DCWorkflow.interfaces import IDCWorkflowDefinition
from Products.DCWorkflow.permissions import ManagePortal from Products.DCWorkflow.permissions import ManagePortal
from Products.DCWorkflow.utils import _xmldir from Products.DCWorkflow.utils import _xmldir
...@@ -67,11 +68,15 @@ class DCWorkflowDefinitionBodyAdapter(BodyAdapterBase): ...@@ -67,11 +68,15 @@ class DCWorkflowDefinitionBodyAdapter(BodyAdapterBase):
, permissions , permissions
, scripts , scripts
, description , description
, manager_bypass
, creation_guard
) = wfdc.parseWorkflowXML(body, encoding) ) = wfdc.parseWorkflowXML(body, encoding)
_initDCWorkflow( self.context _initDCWorkflow( self.context
, title , title
, description , description
, manager_bypass
, creation_guard
, state_variable , state_variable
, initial_state , initial_state
, states , states
...@@ -157,6 +162,8 @@ class WorkflowDefinitionConfigurator( Implicit ): ...@@ -157,6 +162,8 @@ class WorkflowDefinitionConfigurator( Implicit ):
except ValueError: except ValueError:
# Don't fail on export files that do not have the description field! # Don't fail on export files that do not have the description field!
description = '' description = ''
manager_bypass = _getNodeAttributeBoolean(root, 'manager_bypass')
creation_guard = _extractCreationGuard(root, encoding)
state_variable = _getNodeAttribute( root, 'state_variable', encoding ) state_variable = _getNodeAttribute( root, 'state_variable', encoding )
initial_state = _getNodeAttribute( root, 'initial_state', encoding ) initial_state = _getNodeAttribute( root, 'initial_state', encoding )
...@@ -178,6 +185,8 @@ class WorkflowDefinitionConfigurator( Implicit ): ...@@ -178,6 +185,8 @@ class WorkflowDefinitionConfigurator( Implicit ):
, permissions , permissions
, scripts , scripts
, description , description
, manager_bypass
, creation_guard
) )
security.declarePrivate( '_workflowConfig' ) security.declarePrivate( '_workflowConfig' )
...@@ -197,9 +206,11 @@ class WorkflowDefinitionConfigurator( Implicit ): ...@@ -197,9 +206,11 @@ class WorkflowDefinitionConfigurator( Implicit ):
o The following keys will be added to 'workflow_info': o The following keys will be added to 'workflow_info':
'creation_guard' -- the guard of 'Instance creation conditions'
'permissions' -- a list of names of permissions managed 'permissions' -- a list of names of permissions managed
by the workflow by the workflow
'state_variable' -- the name of the workflow's "main" 'state_variable' -- the name of the workflow's "main"
state variable state variable
...@@ -221,6 +232,8 @@ class WorkflowDefinitionConfigurator( Implicit ): ...@@ -221,6 +232,8 @@ class WorkflowDefinitionConfigurator( Implicit ):
'script_info' -- a list of mappings describing the scripts which 'script_info' -- a list of mappings describing the scripts which
provide added business logic (see '_extractScripts'). provide added business logic (see '_extractScripts').
""" """
workflow_info[ 'manager_bypass' ] = workflow.manager_bypass
workflow_info[ 'creation_guard' ] = self._extractCreationGuard(workflow)
workflow_info[ 'state_variable' ] = workflow.state_var workflow_info[ 'state_variable' ] = workflow.state_var
workflow_info[ 'initial_state' ] = workflow.initial_state workflow_info[ 'initial_state' ] = workflow.initial_state
workflow_info[ 'permissions' ] = workflow.permissions workflow_info[ 'permissions' ] = workflow.permissions
...@@ -231,6 +244,20 @@ class WorkflowDefinitionConfigurator( Implicit ): ...@@ -231,6 +244,20 @@ class WorkflowDefinitionConfigurator( Implicit ):
workflow_info[ 'worklist_info' ] = self._extractWorklists( workflow ) workflow_info[ 'worklist_info' ] = self._extractWorklists( workflow )
workflow_info[ 'script_info' ] = self._extractScripts( workflow ) workflow_info[ 'script_info' ] = self._extractScripts( workflow )
security.declarePrivate('_extractCreationGuard')
def _extractCreationGuard(self, workflow):
""" Return a mapping describing 'Instance creation conditions'
if 'creation_guard' is initialized or None
"""
guard = workflow.creation_guard
if guard is not None :
info = { 'guard_permissions' : guard.permissions
, 'guard_roles' : guard.roles
, 'guard_groups' : guard.groups
, 'guard_expr' : guard.getExprText()
}
return info
security.declarePrivate( '_extractVariables' ) security.declarePrivate( '_extractVariables' )
def _extractVariables( self, workflow ): def _extractVariables( self, workflow ):
...@@ -621,6 +648,15 @@ def _getScriptFilename( workflow_id, script_id, meta_type ): ...@@ -621,6 +648,15 @@ def _getScriptFilename( workflow_id, script_id, meta_type ):
return 'workflows/%s/scripts/%s.%s' % ( wf_dir, script_id, suffix ) return 'workflows/%s/scripts/%s.%s' % ( wf_dir, script_id, suffix )
def _extractCreationGuard(root, encoding=None) :
icc = root.getElementsByTagName('instance-creation-conditions')
assert len(icc) <= 1
if icc :
parent = icc[0]
return _extractGuardNode(parent, encoding)
else :
return None
def _extractStateNodes( root, encoding=None ): def _extractStateNodes( root, encoding=None ):
result = [] result = []
...@@ -959,6 +995,8 @@ _METATYPE_SUFFIXES = \ ...@@ -959,6 +995,8 @@ _METATYPE_SUFFIXES = \
def _initDCWorkflow( workflow def _initDCWorkflow( workflow
, title , title
, description , description
, manager_bypass
, creation_guard
, state_variable , state_variable
, initial_state , initial_state
, states , states
...@@ -973,6 +1011,7 @@ def _initDCWorkflow( workflow ...@@ -973,6 +1011,7 @@ def _initDCWorkflow( workflow
""" """
workflow.title = title workflow.title = title
workflow.description = description workflow.description = description
workflow.manager_bypass = manager_bypass
workflow.state_var = state_variable workflow.state_var = state_variable
workflow.initial_state = initial_state workflow.initial_state = initial_state
...@@ -980,6 +1019,7 @@ def _initDCWorkflow( workflow ...@@ -980,6 +1019,7 @@ def _initDCWorkflow( workflow
permissions.sort() permissions.sort()
workflow.permissions = tuple(permissions) workflow.permissions = tuple(permissions)
_initDCWorkflowCreationGuard( workflow, creation_guard )
_initDCWorkflowVariables( workflow, variables ) _initDCWorkflowVariables( workflow, variables )
_initDCWorkflowStates( workflow, states ) _initDCWorkflowStates( workflow, states )
_initDCWorkflowTransitions( workflow, transitions ) _initDCWorkflowTransitions( workflow, transitions )
...@@ -987,6 +1027,21 @@ def _initDCWorkflow( workflow ...@@ -987,6 +1027,21 @@ def _initDCWorkflow( workflow
_initDCWorkflowScripts( workflow, scripts, context ) _initDCWorkflowScripts( workflow, scripts, context )
def _initDCWorkflowCreationGuard( workflow, guard ):
"""Initialize Instance creation conditions guard
"""
if guard is None :
workflow.creation_guard = None
else :
props = { 'guard_roles' : ';'.join( guard[ 'roles' ] )
, 'guard_permissions' : ';'.join( guard[ 'permissions' ] )
, 'guard_groups' : ';'.join( guard[ 'groups' ] )
, 'guard_expr' : guard[ 'expression' ]
}
g = Guard()
g.changeFromProperties(props)
workflow.creation_guard = g
def _initDCWorkflowVariables( workflow, variables ): def _initDCWorkflowVariables( workflow, variables ):
""" Initialize DCWorkflow variables """ Initialize DCWorkflow variables
......
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
<dc-workflow workflow_id="default_workflow" <dc-workflow workflow_id="default_workflow"
title="CMF default workflow [Revision 2]" title="CMF default workflow [Revision 2]"
state_variable="review_state" state_variable="review_state"
initial_state="visible"> initial_state="visible"
manager_bypass="False">
<permission>Access contents information</permission> <permission>Access contents information</permission>
<permission>Modify portal content</permission> <permission>Modify portal content</permission>
<permission>View</permission> <permission>View</permission>
......
...@@ -29,6 +29,7 @@ from Products.CMFCore.exportimport.tests.test_workflow \ ...@@ -29,6 +29,7 @@ from Products.CMFCore.exportimport.tests.test_workflow \
import _WorkflowSetup as WorkflowSetupBase import _WorkflowSetup as WorkflowSetupBase
from Products.CMFCore.testing import DummyWorkflow from Products.CMFCore.testing import DummyWorkflow
from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition
from Products.DCWorkflow.Guard import Guard
from Products.DCWorkflow.testing import ExportImportZCMLLayer from Products.DCWorkflow.testing import ExportImportZCMLLayer
from Products.DCWorkflow.Transitions import TRIGGER_USER_ACTION from Products.DCWorkflow.Transitions import TRIGGER_USER_ACTION
from Products.DCWorkflow.Transitions import TRIGGER_AUTOMATIC from Products.DCWorkflow.Transitions import TRIGGER_AUTOMATIC
...@@ -189,6 +190,12 @@ class _WorkflowSetup(WorkflowSetupBase): ...@@ -189,6 +190,12 @@ class _WorkflowSetup(WorkflowSetupBase):
raise ValueError, 'Unknown script type: %s' % v[ 0 ] raise ValueError, 'Unknown script type: %s' % v[ 0 ]
dcworkflow.scripts._setObject( k, script ) dcworkflow.scripts._setObject( k, script )
def _initCreationGuard(self, dcworkflow) :
props = self._genGuardProps(*_CREATION_GUARD)
g = Guard()
g.changeFromProperties(props)
dcworkflow.creation_guard = g
class WorkflowDefinitionConfiguratorTests( _WorkflowSetup, _GuardChecker ): class WorkflowDefinitionConfiguratorTests( _WorkflowSetup, _GuardChecker ):
...@@ -465,6 +472,13 @@ class WorkflowDefinitionConfiguratorTests( _WorkflowSetup, _GuardChecker ): ...@@ -465,6 +472,13 @@ class WorkflowDefinitionConfiguratorTests( _WorkflowSetup, _GuardChecker ):
, expected[ 2 ] % WF_ID ) , expected[ 2 ] % WF_ID )
else: else:
self.assertEqual( info[ 'filename' ], expected[ 2 ] ) self.assertEqual( info[ 'filename' ], expected[ 2 ] )
def test_getWorkflowInfo_dcworkflow_creation_guard(self) :
WF_ID = 'dcworkflow_creation_guard'
site = self._initSite()
dcworkflow = self._initDCWorkflow( WF_ID )
self._initCreationGuard(dcworkflow)
def test_generateXML_empty( self ): def test_generateXML_empty( self ):
...@@ -596,6 +610,8 @@ class WorkflowDefinitionConfiguratorTests( _WorkflowSetup, _GuardChecker ): ...@@ -596,6 +610,8 @@ class WorkflowDefinitionConfiguratorTests( _WorkflowSetup, _GuardChecker ):
, permissions , permissions
, scripts , scripts
, description , description
, manager_bypass
, creation_guard
) = configurator.parseWorkflowXML( _EMPTY_WORKFLOW_EXPORT ) = configurator.parseWorkflowXML( _EMPTY_WORKFLOW_EXPORT
% ( WF_ID % ( WF_ID
, WF_TITLE , WF_TITLE
...@@ -633,6 +649,8 @@ class WorkflowDefinitionConfiguratorTests( _WorkflowSetup, _GuardChecker ): ...@@ -633,6 +649,8 @@ class WorkflowDefinitionConfiguratorTests( _WorkflowSetup, _GuardChecker ):
, permissions , permissions
, scripts , scripts
, description , description
, manager_bypass
, creation_guard
) = configurator.parseWorkflowXML( ) = configurator.parseWorkflowXML(
_NORMAL_WORKFLOW_EXPORT _NORMAL_WORKFLOW_EXPORT
% { 'workflow_id' : WF_ID % { 'workflow_id' : WF_ID
...@@ -670,6 +688,8 @@ class WorkflowDefinitionConfiguratorTests( _WorkflowSetup, _GuardChecker ): ...@@ -670,6 +688,8 @@ class WorkflowDefinitionConfiguratorTests( _WorkflowSetup, _GuardChecker ):
, permissions , permissions
, scripts , scripts
, description , description
, manager_bypass
, creation_guard
) = configurator.parseWorkflowXML( ) = configurator.parseWorkflowXML(
_NORMAL_WORKFLOW_EXPORT _NORMAL_WORKFLOW_EXPORT
% { 'workflow_id' : WF_ID % { 'workflow_id' : WF_ID
...@@ -736,6 +756,8 @@ class WorkflowDefinitionConfiguratorTests( _WorkflowSetup, _GuardChecker ): ...@@ -736,6 +756,8 @@ class WorkflowDefinitionConfiguratorTests( _WorkflowSetup, _GuardChecker ):
, permissions , permissions
, scripts , scripts
, description , description
, manager_bypass
, creation_guard
) = configurator.parseWorkflowXML( ) = configurator.parseWorkflowXML(
_NORMAL_WORKFLOW_EXPORT _NORMAL_WORKFLOW_EXPORT
% { 'workflow_id' : WF_ID % { 'workflow_id' : WF_ID
...@@ -805,6 +827,8 @@ class WorkflowDefinitionConfiguratorTests( _WorkflowSetup, _GuardChecker ): ...@@ -805,6 +827,8 @@ class WorkflowDefinitionConfiguratorTests( _WorkflowSetup, _GuardChecker ):
, permissions , permissions
, scripts , scripts
, description , description
, manager_bypass
, creation_guard
) = configurator.parseWorkflowXML( ) = configurator.parseWorkflowXML(
_NORMAL_WORKFLOW_EXPORT _NORMAL_WORKFLOW_EXPORT
% { 'workflow_id' : WF_ID % { 'workflow_id' : WF_ID
...@@ -883,6 +907,8 @@ class WorkflowDefinitionConfiguratorTests( _WorkflowSetup, _GuardChecker ): ...@@ -883,6 +907,8 @@ class WorkflowDefinitionConfiguratorTests( _WorkflowSetup, _GuardChecker ):
, permissions , permissions
, scripts , scripts
, description , description
, manager_bypass
, creation_guard
) = configurator.parseWorkflowXML( ) = configurator.parseWorkflowXML(
_NORMAL_WORKFLOW_EXPORT _NORMAL_WORKFLOW_EXPORT
% { 'workflow_id' : WF_ID % { 'workflow_id' : WF_ID
...@@ -946,6 +972,8 @@ class WorkflowDefinitionConfiguratorTests( _WorkflowSetup, _GuardChecker ): ...@@ -946,6 +972,8 @@ class WorkflowDefinitionConfiguratorTests( _WorkflowSetup, _GuardChecker ):
, permissions , permissions
, scripts , scripts
, description , description
, manager_bypass
, creation_guard
) = configurator.parseWorkflowXML( ) = configurator.parseWorkflowXML(
_NORMAL_WORKFLOW_EXPORT _NORMAL_WORKFLOW_EXPORT
% { 'workflow_id' : WF_ID % { 'workflow_id' : WF_ID
...@@ -983,6 +1011,8 @@ class WorkflowDefinitionConfiguratorTests( _WorkflowSetup, _GuardChecker ): ...@@ -983,6 +1011,8 @@ class WorkflowDefinitionConfiguratorTests( _WorkflowSetup, _GuardChecker ):
, permissions , permissions
, scripts , scripts
, description , description
, manager_bypass
, creation_guard
) = configurator.parseWorkflowXML( ) = configurator.parseWorkflowXML(
_NORMAL_WORKFLOW_EXPORT _NORMAL_WORKFLOW_EXPORT
% { 'workflow_id' : WF_ID % { 'workflow_id' : WF_ID
...@@ -1251,6 +1281,13 @@ _WF_SCRIPTS = \ ...@@ -1251,6 +1281,13 @@ _WF_SCRIPTS = \
) )
} }
_CREATION_GUARD = \
(('Add portal content', 'Manage portal')
,('Owner', 'Manager')
,('group_readers', 'group_members')
,'python:len(here.objectIds() <= 10)'
)
_NORMAL_TOOL_EXPORT = """\ _NORMAL_TOOL_EXPORT = """\
<?xml version="1.0"?> <?xml version="1.0"?>
<object name="portal_workflow" meta_type="Dummy Workflow Tool"> <object name="portal_workflow" meta_type="Dummy Workflow Tool">
...@@ -1293,7 +1330,8 @@ _EMPTY_WORKFLOW_EXPORT = """\ ...@@ -1293,7 +1330,8 @@ _EMPTY_WORKFLOW_EXPORT = """\
title="%s" title="%s"
description="%s" description="%s"
state_variable="state" state_variable="state"
initial_state="%s"> initial_state="%s"
manager_bypass="0">
</dc-workflow> </dc-workflow>
""" """
...@@ -1305,7 +1343,8 @@ _OLD_WORKFLOW_EXPORT = """\ ...@@ -1305,7 +1343,8 @@ _OLD_WORKFLOW_EXPORT = """\
workflow_id="%(workflow_id)s" workflow_id="%(workflow_id)s"
title="%(title)s" title="%(title)s"
state_variable="state" state_variable="state"
initial_state="%(initial_state)s"> initial_state="%(initial_state)s"
manager_bypass="0">
<permission>Open content for modifications</permission> <permission>Open content for modifications</permission>
<permission>Modify content</permission> <permission>Modify content</permission>
<permission>Query history</permission> <permission>Query history</permission>
...@@ -1549,7 +1588,8 @@ _NORMAL_WORKFLOW_EXPORT = """\ ...@@ -1549,7 +1588,8 @@ _NORMAL_WORKFLOW_EXPORT = """\
title="%(title)s" title="%(title)s"
description="%(description)s" description="%(description)s"
state_variable="state" state_variable="state"
initial_state="%(initial_state)s"> initial_state="%(initial_state)s"
manager_bypass="0">
<permission>Open content for modifications</permission> <permission>Open content for modifications</permission>
<permission>Modify content</permission> <permission>Modify content</permission>
<permission>Query history</permission> <permission>Query history</permission>
...@@ -1791,6 +1831,26 @@ _NORMAL_WORKFLOW_EXPORT = """\ ...@@ -1791,6 +1831,26 @@ _NORMAL_WORKFLOW_EXPORT = """\
</dc-workflow> </dc-workflow>
""" """
_CREATION_GUARD_WORKFLOW_EXPORT = """\
<?xml version="1.0"?>
<dc-workflow
workflow_id="%s"
title="%s"
description="%s"
state_variable="state"
initial_state="%s"
manager_bypass="1">
<instance-creation-conditions>
<guard>
<guard-permission>Add portal content</guard-permission>
<guard-role>Owner</guard-role>
<guard-group>group_members</guard-group>
<guard-expression>python:len(here.objectIds() &lt;= 10)</guard-expression>
</guard>
</instance-creation-conditions>
</dc-workflow>
"""
class Test_exportWorkflow(_WorkflowSetup, _GuardChecker): class Test_exportWorkflow(_WorkflowSetup, _GuardChecker):
......
...@@ -11,8 +11,29 @@ ...@@ -11,8 +11,29 @@
tal:attributes="workflow_id info/id; tal:attributes="workflow_id info/id;
title info/title; title info/title;
description info/description; description info/description;
manager_bypass info/manager_bypass;
state_variable info/state_variable; state_variable info/state_variable;
initial_state info/initial_state"> initial_state info/initial_state">
<instance-creation-conditions tal:define="creation_guard info/creation_guard" tal:condition="creation_guard">
<guard
><tal:case tal:condition="creation_guard/guard_permissions">
<guard-permission
tal:repeat="permission creation_guard/guard_permissions"
tal:content="permission">PERMISSION</guard-permission></tal:case
><tal:case tal:condition="creation_guard/guard_roles">
<guard-role
tal:repeat="role creation_guard/guard_roles"
tal:content="role">ROLE</guard-role></tal:case
><tal:case tal:condition="creation_guard/guard_groups">
<guard-group
tal:repeat="group creation_guard/guard_groups"
tal:content="group">GROUP</guard-group></tal:case
><tal:case tal:condition="creation_guard/guard_expr">
<guard-expression
tal:content="creation_guard/guard_expr">EXPRESSION</guard-expression
></tal:case>
</guard>
</instance-creation-conditions>
<permission <permission
tal:repeat="permission info/permissions" tal:repeat="permission info/permissions"
tal:content="permission">PERMISSION</permission> tal:content="permission">PERMISSION</permission>
......
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