vendor/CMF/1.5.3/CMFSetup

view workflow.py @ 0:3ed006215eb6

Vendor import of CMF 1.5.3
author fguillaume
date Tue, 09 Aug 2005 10:47:34 +0000
parents
children
line source
1 ##############################################################################
2 #
3 # Copyright (c) 2004 Zope Corporation and Contributors. All Rights Reserved.
4 #
5 # This software is subject to the provisions of the Zope Public License,
6 # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10 # FOR A PARTICULAR PURPOSE.
11 #
12 ##############################################################################
13 """ Classes: WorkflowConfigurator
15 $Id: workflow.py 37114 2005-07-07 15:33:13Z anahide $
16 """
18 import re
19 from xml.dom.minidom import parseString as domParseString
21 from AccessControl import ClassSecurityInfo
22 from Acquisition import Implicit
23 from Globals import InitializeClass
24 from Products.PageTemplates.PageTemplateFile import PageTemplateFile
26 from Products.CMFCore.utils import getToolByName
27 from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition
29 from permissions import ManagePortal
30 from utils import _coalesceTextNodeChildren
31 from utils import _extractDescriptionNode
32 from utils import _getNodeAttribute
33 from utils import _getNodeAttributeBoolean
34 from utils import _queryNodeAttribute
35 from utils import _xmldir
36 from utils import ConfiguratorBase
37 from utils import CONVERTER, DEFAULT, KEY
40 TRIGGER_TYPES = ( 'AUTOMATIC', 'USER', 'WORKFLOW_METHOD' )
42 #
43 # Configurator entry points
44 #
45 _FILENAME = 'workflows.xml'
47 def importWorkflowTool( context ):
49 """ Import worflow tool and contained workflow definitions.
51 o 'context' must implement IImportContext.
53 o Register via Python:
55 registry = site.portal_setup.getImportStepRegistry()
56 registry.registerStep( 'importWorkflowTool'
57 , '20040602-01'
58 , Products.CMFSetup.workflow.importWorkflowTool
59 , ()
60 , 'Workflow import'
61 , 'Import worflow tool and contained workflow '
62 'definitions.'
63 )
65 o Register via XML:
67 <setup-step id="importWorkflowTool"
68 version="20040602-01"
69 handler="Products.CMFSetup.workflow.importWorkflowTool"
70 title="Workflow import"
71 >Import worflow tool and contained workflow definitions.</setup-step>
73 """
74 site = context.getSite()
75 encoding = context.getEncoding()
76 tool = getToolByName( site, 'portal_workflow' )
78 if context.shouldPurge():
80 tool.setDefaultChain( '' )
81 if tool._chains_by_type is not None:
82 tool._chains_by_type.clear()
84 for workflow_id in tool.getWorkflowIds():
85 tool._delObject( workflow_id )
87 text = context.readDataFile( _FILENAME )
89 if text is not None:
91 wftc = WorkflowToolConfigurator( site, encoding )
92 tool_info = wftc.parseXML( text )
94 wfdc = WorkflowDefinitionConfigurator( site )
96 for info in tool_info[ 'workflows' ]:
98 if info[ 'meta_type' ] == DCWorkflowDefinition.meta_type:
100 filename = info[ 'filename' ]
101 sep = filename.rfind( '/' )
102 if sep == -1:
103 wf_text = context.readDataFile( filename )
104 else:
105 wf_text = context.readDataFile( filename[sep+1:],
106 filename[:sep] )
108 ( workflow_id
109 , title
110 , state_variable
111 , initial_state
112 , states
113 , transitions
114 , variables
115 , worklists
116 , permissions
117 , scripts
118 ) = wfdc.parseWorkflowXML( wf_text, encoding )
120 workflow_id = str( workflow_id ) # No unicode!
122 tool._setObject( workflow_id
123 , DCWorkflowDefinition( workflow_id ) )
125 workflow = tool._getOb( workflow_id )
127 _initDCWorkflow( workflow
128 , title
129 , state_variable
130 , initial_state
131 , states
132 , transitions
133 , variables
134 , worklists
135 , permissions
136 , scripts
137 , context
138 )
139 else:
140 pass # TODO: handle non-DCWorkflows
142 for type_id, workflow_ids in tool_info[ 'bindings' ].items():
144 chain = ','.join( workflow_ids )
145 if type_id is None:
146 tool.setDefaultChain( chain )
147 else:
148 tool.setChainForPortalTypes( ( type_id, ), chain )
150 return 'Workflows imported.'
153 def exportWorkflowTool( context ):
155 """ Export worflow tool and contained workflow definitions as an XML file.
157 o 'context' must implement IExportContext.
159 o Register via Python:
161 registry = site.portal_setup.getExportStepRegistry()
162 registry.registerStep( 'exportWorkflowTool'
163 , Products.CMFSetup.workflow.exportWorkflowTool
164 , 'Workflow export'
165 , 'Export worflow tool and contained workflow '
166 'definitions.'
167 )
169 o Register via XML:
171 <export-script id="exportWorkflowTool"
172 version="20040518-01"
173 handler="Products.CMFSetup.workflow.exportWorkflowTool"
174 title="Workflow export"
175 >Export worflow tool and contained workflow definitions.</export-script>
177 """
178 site = context.getSite()
179 wftc = WorkflowToolConfigurator( site ).__of__( site )
180 wfdc = WorkflowDefinitionConfigurator( site ).__of__( site )
181 wf_tool = getToolByName( site, 'portal_workflow' )
182 text = wftc.generateXML()
184 context.writeDataFile( _FILENAME, text, 'text/xml' )
186 for wf_id in wf_tool.getWorkflowIds():
188 wf_dirname = wf_id.replace( ' ', '_' )
189 wf_xml = wfdc.generateWorkflowXML( wf_id )
190 wf_scripts = wfdc.getWorkflowScripts(wf_id)
192 if wf_xml is not None:
193 context.writeDataFile( 'definition.xml'
194 , wf_xml
195 , 'text/xml'
196 , 'workflows/%s' % wf_dirname
197 )
198 for script_info in wf_scripts:
199 context.writeDataFile(script_info['filename'],
200 script_info['body'],
201 'text/plain')
203 return 'Workflows exported.'
206 class WorkflowToolConfigurator(ConfiguratorBase):
207 """ Synthesize XML description of site's workflow tool.
208 """
209 security = ClassSecurityInfo()
211 security.declareProtected(ManagePortal, 'getWorkflowInfo')
212 def getWorkflowInfo(self, workflow_id):
213 """ Return a mapping describing a given workflow.
214 """
215 workflow_tool = getToolByName( self._site, 'portal_workflow' )
216 workflow = workflow_tool.getWorkflowById( workflow_id )
218 workflow_info = { 'id' : workflow_id
219 , 'meta_type' : workflow.meta_type
220 , 'title' : workflow.title_or_id()
221 }
223 if workflow.meta_type == DCWorkflowDefinition.meta_type:
224 workflow_info['filename'] = _getWorkflowFilename(workflow_id)
226 return workflow_info
228 security.declareProtected( ManagePortal, 'listWorkflowInfo' )
229 def listWorkflowInfo( self ):
231 """ Return a sequence of mappings for each workflow in the tool.
233 o See 'getWorkflowInfo' for definition of the mappings.
234 """
235 workflow_tool = getToolByName( self._site, 'portal_workflow' )
236 return [ self.getWorkflowInfo( workflow_id )
237 for workflow_id in workflow_tool.getWorkflowIds() ]
239 security.declareProtected( ManagePortal, 'listWorkflowChains' )
240 def listWorkflowChains( self ):
242 """ Return a sequence of tuples binding workflows to each content type.
244 o Tuples are in the format, '( type_id, [ workflow_id ] )'.
246 o The default chain will be first in the list, with None for the
247 'type_id'.
249 o The list will only include type-specific chains for types which
250 do not use the default chain.
251 """
252 workflow_tool = getToolByName( self._site, 'portal_workflow' )
254 result = [ ( None, workflow_tool._default_chain ) ]
255 if workflow_tool._chains_by_type is None:
256 overrides = []
257 else:
258 overrides = workflow_tool._chains_by_type.items()
259 overrides.sort()
261 result.extend( overrides )
263 return result
265 def _getExportTemplate(self):
267 return PageTemplateFile('wtcToolExport.xml', _xmldir)
269 def _getImportMapping(self):
271 return {
272 'workflow-tool':
273 { 'workflow': {KEY: 'workflows', DEFAULT: (),
274 CONVERTER: self._convertWorkflows},
275 'bindings': {CONVERTER: self._convertBindings} },
276 'workflow':
277 { 'workflow_id': {},
278 'meta_type': {DEFAULT: '%(workflow_id)s'},
279 'filename': {DEFAULT: '%(workflow_id)s'} },
280 'bindings':
281 { 'default': {KEY: 'bindings'},
282 'type': {KEY: 'bindings'} },
283 'default':
284 { 'type_id': {DEFAULT: None},
285 'bound-workflow': {KEY: 'bound_workflows', DEFAULT: ()} },
286 'type':
287 { 'type_id': {DEFAULT: None},
288 'bound-workflow': {KEY: 'bound_workflows', DEFAULT: ()} },
289 'bound-workflow':
290 { 'workflow_id': {KEY: None} } }
292 def _convertWorkflows(self, val):
294 for wf in val:
295 if wf['meta_type'] == DCWorkflowDefinition.meta_type:
296 if wf['filename'] == wf['workflow_id']:
297 wf['filename'] = _getWorkflowFilename( wf['filename'] )
298 else:
299 wf['filename'] = None
301 return val
303 def _convertBindings(self, val):
305 result = {}
307 for binding in val[0]['bindings']:
308 result[ binding['type_id'] ] = binding['bound_workflows']
310 return result
312 InitializeClass(WorkflowToolConfigurator)
315 class WorkflowDefinitionConfigurator( Implicit ):
316 """ Synthesize XML description of site's workflows.
317 """
318 security = ClassSecurityInfo()
320 def __init__( self, site ):
321 self._site = site
323 security.declareProtected( ManagePortal, 'getWorkflowInfo' )
324 def getWorkflowInfo( self, workflow_id ):
326 """ Return a mapping describing a given workflow.
328 o Keys in the mappings:
330 'id' -- the ID of the workflow within the tool
332 'meta_type' -- the workflow's meta_type
334 'title' -- the workflow's title property
336 o See '_extractDCWorkflowInfo' below for keys present only for
337 DCWorkflow definitions.
339 """
340 workflow_tool = getToolByName( self._site, 'portal_workflow' )
341 workflow = workflow_tool.getWorkflowById( workflow_id )
343 workflow_info = { 'id' : workflow_id
344 , 'meta_type' : workflow.meta_type
345 , 'title' : workflow.title_or_id()
346 }
348 if workflow.meta_type == DCWorkflowDefinition.meta_type:
349 self._extractDCWorkflowInfo( workflow, workflow_info )
351 return workflow_info
354 security.declareProtected( ManagePortal, 'generateWorkflowXML' )
355 def generateWorkflowXML( self, workflow_id ):
357 """ Pseudo API.
358 """
359 info = self.getWorkflowInfo( workflow_id )
361 if info[ 'meta_type' ] != DCWorkflowDefinition.meta_type:
362 return None
364 return self._workflowConfig( workflow_id=workflow_id )
366 security.declareProtected( ManagePortal, 'generateWorkflowScripts' )
367 def getWorkflowScripts( self, workflow_id ):
368 """ Get workflow scripts inforation
369 """
370 workflow_tool = getToolByName( self._site, 'portal_workflow' )
371 workflow = workflow_tool.getWorkflowById( workflow_id )
373 if workflow.meta_type != DCWorkflowDefinition.meta_type:
374 return []
376 scripts = self._extractScripts(workflow)
377 return scripts
380 security.declareProtected( ManagePortal, 'parseWorkflowXML' )
381 def parseWorkflowXML( self, xml, encoding=None ):
383 """ Pseudo API.
384 """
385 dom = domParseString( xml )
387 root = dom.getElementsByTagName( 'dc-workflow' )[ 0 ]
389 workflow_id = _getNodeAttribute( root, 'workflow_id', encoding )
390 title = _getNodeAttribute( root, 'title', encoding )
391 state_variable = _getNodeAttribute( root, 'state_variable', encoding )
392 initial_state = _getNodeAttribute( root, 'initial_state', encoding )
394 states = _extractStateNodes( root, encoding )
395 transitions = _extractTransitionNodes( root, encoding )
396 variables = _extractVariableNodes( root, encoding )
397 worklists = _extractWorklistNodes( root, encoding )
398 permissions = _extractPermissionNodes( root, encoding )
399 scripts = _extractScriptNodes( root, encoding )
401 return ( workflow_id
402 , title
403 , state_variable
404 , initial_state
405 , states
406 , transitions
407 , variables
408 , worklists
409 , permissions
410 , scripts
411 )
413 security.declarePrivate( '_workflowConfig' )
414 _workflowConfig = PageTemplateFile( 'wtcWorkflowExport.xml'
415 , _xmldir
416 , __name__='workflowConfig'
417 )
419 security.declarePrivate( '_extractDCWorkflowInfo' )
420 def _extractDCWorkflowInfo( self, workflow, workflow_info ):
422 """ Append the information for a 'workflow' into 'workflow_info'
424 o 'workflow' must be a DCWorkflowDefinition instance.
426 o 'workflow_info' must be a dictionary.
428 o The following keys will be added to 'workflow_info':
430 'permissions' -- a list of names of permissions managed
431 by the workflow
433 'state_variable' -- the name of the workflow's "main"
434 state variable
436 'initial_state' -- the name of the state in the workflow
437 in which objects start their lifecycle.
439 'variable_info' -- a list of mappings describing the
440 variables tracked by the workflow (see '_extractVariables').
442 'state_info' -- a list of mappings describing the
443 states tracked by the workflow (see '_extractStates').
445 'transition_info' -- a list of mappings describing the
446 transitions tracked by the workflow (see '_extractTransitions').
448 'worklist_info' -- a list of mappings describing the
449 worklists tracked by the workflow (see '_extractWorklists').
451 'script_info' -- a list of mappings describing the scripts which
452 provide added business logic (wee '_extractScripts').
453 """
454 workflow_info[ 'filename' ] = _getWorkflowFilename( workflow.getId() )
455 workflow_info[ 'state_variable' ] = workflow.state_var
456 workflow_info[ 'initial_state' ] = workflow.initial_state
457 workflow_info[ 'permissions' ] = workflow.permissions
458 workflow_info[ 'variable_info' ] = self._extractVariables( workflow )
459 workflow_info[ 'state_info' ] = self._extractStates( workflow )
460 workflow_info[ 'transition_info' ] = self._extractTransitions(
461 workflow )
462 workflow_info[ 'worklist_info' ] = self._extractWorklists( workflow )
463 workflow_info[ 'script_info' ] = self._extractScripts( workflow )
465 security.declarePrivate( '_extractVariables' )
466 def _extractVariables( self, workflow ):
468 """ Return a sequence of mappings describing DCWorkflow variables.
470 o Keys for each mapping will include:
472 'id' -- the variable's ID
474 'description' -- a textual description of the variable
476 'for_catalog' -- whether to catalog this variable
478 'for_status' -- whether to ??? this variable (XXX)
480 'update_always' -- whether to update this variable whenever
481 executing a transition (xxX)
483 'default_value' -- a default value for the variable (XXX)
485 'default_expression' -- a TALES expression for the default value
487 'guard_permissions' -- a list of permissions guarding access
488 to the variable
490 'guard_roles' -- a list of roles guarding access
491 to the variable
493 'guard_groups' -- a list of groups guarding the transition
495 'guard_expr' -- an expression guarding access to the variable
496 """
497 result = []
499 items = workflow.variables.objectItems()
500 items.sort()
502 for k, v in items:
504 guard = v.getInfoGuard()
506 default_type = _guessVariableType( v.default_value )
508 info = { 'id' : k
509 , 'description' : v.description
510 , 'for_catalog' : bool( v.for_catalog )
511 , 'for_status' : bool( v.for_status )
512 , 'update_always' : bool( v.update_always )
513 , 'default_value' : v.default_value
514 , 'default_type' : default_type
515 , 'default_expr' : v.getDefaultExprText()
516 , 'guard_permissions' : guard.permissions
517 , 'guard_roles' : guard.roles
518 , 'guard_groups' : guard.groups
519 , 'guard_expr' : guard.getExprText()
520 }
522 result.append( info )
524 return result
526 security.declarePrivate( '_extractStates' )
527 def _extractStates( self, workflow ):
529 """ Return a sequence of mappings describing DCWorkflow states.
531 o Within the workflow mapping, each 'state_info' mapping has keys:
533 'id' -- the state's ID
535 'title' -- the state's title
537 'description' -- the state's description
539 'transitions' -- a list of IDs of transitions out of the state
541 'permissions' -- a list of mappings describing the permission
542 map for the state
544 'groups' -- a list of ( group_id, (roles,) ) tuples describing the
545 group-role assignments for the state
547 'variables' -- a list of mapping for the variables
548 to be set when entering the state.
550 o Within the state_info mappings, each 'permissions' mapping
551 has the keys:
553 'name' -- the name of the permission
555 'roles' -- a sequence of role IDs which have the permission
557 'acquired' -- whether roles are acquired for the permission
559 o Within the state_info mappings, each 'variable' mapping
560 has the keys:
562 'name' -- the name of the variable
564 'type' -- the type of the value (allowed values are:
565 'string', 'datetime', 'bool', 'int')
567 'value' -- the value to be set
568 """
569 result = []
571 items = workflow.states.objectItems()
572 items.sort()
574 for k, v in items:
576 groups = v.group_roles and list( v.group_roles.items() ) or []
577 groups = [ x for x in groups if x[1] ]
578 groups.sort()
580 variables = list( v.getVariableValues() )
581 variables.sort()
583 v_info = []
585 for v_name, value in variables:
586 v_info.append( { 'name' : v_name
587 , 'type' :_guessVariableType( value )
588 , 'value' : value
589 } )
591 info = { 'id' : k
592 , 'title' : v.title
593 , 'description' : v.description
594 , 'transitions' : v.transitions
595 , 'permissions' : self._extractStatePermissions( v )
596 , 'groups' : groups
597 , 'variables' : v_info
598 }
600 result.append( info )
602 return result
604 security.declarePrivate( '_extractStatePermissions' )
605 def _extractStatePermissions( self, state ):
607 """ Return a sequence of mappings for the permissions in a state.
609 o Each mapping has the keys:
611 'name' -- the name of the permission
613 'roles' -- a sequence of role IDs which have the permission
615 'acquired' -- whether roles are acquired for the permission
616 """
617 result = []
619 items = state.permission_roles.items()
620 items.sort()
622 for k, v in items:
624 result.append( { 'name' : k
625 , 'roles' : v
626 , 'acquired' : not isinstance( v, tuple )
627 } )
629 return result
632 security.declarePrivate( '_extractTransitions' )
633 def _extractTransitions( self, workflow ):
635 """ Return a sequence of mappings describing DCWorkflow transitions.
637 o Each mapping has the keys:
639 'id' -- the transition's ID
641 'title' -- the transition's ID
643 'description' -- the transition's description
645 'new_state_id' -- the ID of the state into which the transition
646 moves an object
648 'trigger_type' -- one of the following values, indicating how the
649 transition is fired:
651 - "AUTOMATIC" -> fired opportunistically whenever the workflow
652 notices that its guard conditions permit
654 - "USER" -> fired in response to user request
656 - "WORKFLOW_METHOD" -> fired as the result of execting a
657 WorkflowMethod
659 'script_name' -- the ID of a script to be executed before
660 the transition
662 'after_script_name' -- the ID of a script to be executed after
663 the transition
665 'actbox_name' -- the name of the action by which the user
666 triggers the transition
668 'actbox_url' -- the URL of the action by which the user
669 triggers the transition
671 'actbox_category' -- the category of the action by which the user
672 triggers the transition
674 'variables' -- a list of ( id, expr ) tuples defining how variables
675 are to be set during the transition
677 'guard_permissions' -- a list of permissions guarding the transition
679 'guard_roles' -- a list of roles guarding the transition
681 'guard_groups' -- a list of groups guarding the transition
683 'guard_expr' -- an expression guarding the transition
685 """
686 result = []
688 items = workflow.transitions.objectItems()
689 items.sort()
691 for k, v in items:
693 guard = v.getGuard()
695 v_info = []
697 for v_name, expr in v.getVariableExprs():
698 v_info.append( { 'name' : v_name, 'expr' : expr } )
700 info = { 'id' : k
701 , 'title' : v.title
702 , 'description' : v.description
703 , 'new_state_id' : v.new_state_id
704 , 'trigger_type' : TRIGGER_TYPES[ v.trigger_type ]
705 , 'script_name' : v.script_name
706 , 'after_script_name' : v.after_script_name
707 , 'actbox_name' : v.actbox_name
708 , 'actbox_url' : v.actbox_url
709 , 'actbox_category' : v.actbox_category
710 , 'variables' : v_info
711 , 'guard_permissions' : guard.permissions
712 , 'guard_roles' : guard.roles
713 , 'guard_groups' : guard.groups
714 , 'guard_expr' : guard.getExprText()
715 }
717 result.append( info )
719 return result
721 security.declarePrivate( '_extractWorklists' )
722 def _extractWorklists( self, workflow ):
724 """ Return a sequence of mappings describing DCWorkflow transitions.
726 o Each mapping has the keys:
728 'id' -- the ID of the worklist
730 'title' -- the title of the worklist
732 'description' -- a textual description of the worklist
734 'var_match' -- a list of ( key, value ) tuples defining
735 the variables used to "activate" the worklist.
737 'actbox_name' -- the name of the "action" corresponding to the
738 worklist
740 'actbox_url' -- the URL of the "action" corresponding to the
741 worklist
743 'actbox_category' -- the category of the "action" corresponding
744 to the worklist
746 'guard_permissions' -- a list of permissions guarding access
747 to the worklist
749 'guard_roles' -- a list of roles guarding access
750 to the worklist
752 'guard_expr' -- an expression guarding access to the worklist
754 """
755 result = []
757 items = workflow.worklists.objectItems()
758 items.sort()
760 for k, v in items:
762 guard = v.getGuard()
764 var_match = [ ( id, v.getVarMatchText( id ) )
765 for id in v.getVarMatchKeys() ]
767 info = { 'id' : k
768 , 'title' : v.title
769 , 'description' : v.description
770 , 'var_match' : var_match
771 , 'actbox_name' : v.actbox_name
772 , 'actbox_url' : v.actbox_url
773 , 'actbox_category' : v.actbox_category
774 , 'guard_permissions' : guard.permissions
775 , 'guard_roles' : guard.roles
776 , 'guard_groups' : guard.groups
777 , 'guard_expr' : guard.getExprText()
778 }
780 result.append( info )
782 return result
784 security.declarePrivate( '_extractScripts' )
785 def _extractScripts( self, workflow ):
787 """ Return a sequence of mappings describing DCWorkflow scripts.
789 o Each mapping has the keys:
791 'id' -- the ID of the script
793 'meta_type' -- the title of the worklist
795 'body' -- the text of the script
797 'filename' -- the name of the file to / from which the script
798 is stored / loaded
799 """
800 result = []
802 items = workflow.scripts.objectItems()
803 items.sort()
805 for k, v in items:
807 filename = _getScriptFilename( workflow.getId(), k, v.meta_type )
809 info = { 'id' : k
810 , 'meta_type' : v.meta_type
811 , 'body' : v.read()
812 , 'filename' : filename
813 }
815 result.append( info )
817 return result
819 InitializeClass( WorkflowDefinitionConfigurator )
822 def _getWorkflowFilename( workflow_id ):
824 """ Return the name of the file which holds info for a given workflow.
825 """
826 return 'workflows/%s/definition.xml' % workflow_id.replace( ' ', '_' )
828 def _getScriptFilename( workflow_id, script_id, meta_type ):
830 """ Return the name of the file which holds the script.
831 """
832 wf_dir = workflow_id.replace( ' ', '_' )
833 suffix = _METATYPE_SUFFIXES[ meta_type ]
834 return 'workflows/%s/scripts/%s.%s' % ( wf_dir, script_id, suffix )
836 def _extractStateNodes( root, encoding=None ):
838 result = []
840 for s_node in root.getElementsByTagName( 'state' ):
842 info = { 'state_id' : _getNodeAttribute( s_node, 'state_id', encoding )
843 , 'title' : _getNodeAttribute( s_node, 'title', encoding )
844 , 'description' : _extractDescriptionNode( s_node, encoding )
845 }
847 info[ 'transitions' ] = [ _getNodeAttribute( x, 'transition_id'
848 , encoding )
849 for x in s_node.getElementsByTagName(
850 'exit-transition' ) ]
852 info[ 'permissions' ] = permission_map = {}
854 for p_map in s_node.getElementsByTagName( 'permission-map' ):
856 name = _getNodeAttribute( p_map, 'name', encoding )
857 acquired = _getNodeAttributeBoolean( p_map, 'acquired' )
859 roles = [ _coalesceTextNodeChildren( x, encoding )
860 for x in p_map.getElementsByTagName(
861 'permission-role' ) ]
863 if not acquired:
864 roles = tuple( roles )
866 permission_map[ name ] = roles
868 info[ 'groups' ] = group_map = []
870 for g_map in s_node.getElementsByTagName( 'group-map' ):
872 name = _getNodeAttribute( g_map, 'name', encoding )
874 roles = [ _coalesceTextNodeChildren( x, encoding )
875 for x in g_map.getElementsByTagName(
876 'group-role' ) ]
878 group_map.append( ( name, tuple( roles ) ) )
880 info[ 'variables' ] = var_map = {}
882 for assignment in s_node.getElementsByTagName( 'assignment' ):
884 name = _getNodeAttribute( assignment, 'name', encoding )
885 type_id = _getNodeAttribute( assignment, 'type', encoding )
886 value = _coalesceTextNodeChildren( assignment, encoding )
888 var_map[ name ] = { 'name' : name
889 , 'type' : type_id
890 , 'value' : value
891 }
893 result.append( info )
895 return result
897 def _extractTransitionNodes( root, encoding=None ):
899 result = []
901 for t_node in root.getElementsByTagName( 'transition' ):
903 info = { 'transition_id' : _getNodeAttribute( t_node, 'transition_id'
904 , encoding )
905 , 'title' : _getNodeAttribute( t_node, 'title', encoding )
906 , 'description' : _extractDescriptionNode( t_node, encoding )
907 , 'new_state' : _getNodeAttribute( t_node, 'new_state'
908 , encoding )
909 , 'trigger' : _getNodeAttribute( t_node, 'trigger', encoding )
910 , 'before_script' : _getNodeAttribute( t_node, 'before_script'
911 , encoding )
912 , 'after_script' : _getNodeAttribute( t_node, 'after_script'
913 , encoding )
914 , 'action' : _extractActionNode( t_node, encoding )
915 , 'guard' : _extractGuardNode( t_node, encoding )
916 }
918 info[ 'variables' ] = var_map = {}
920 for assignment in t_node.getElementsByTagName( 'assignment' ):
922 name = _getNodeAttribute( assignment, 'name', encoding )
923 expr = _coalesceTextNodeChildren( assignment, encoding )
924 var_map[ name ] = expr
926 result.append( info )
928 return result
930 def _extractVariableNodes( root, encoding=None ):
932 result = []
934 for v_node in root.getElementsByTagName( 'variable' ):
936 info = { 'variable_id' : _getNodeAttribute( v_node, 'variable_id'
937 , encoding )
938 , 'description' : _extractDescriptionNode( v_node, encoding )
939 , 'for_catalog' : _getNodeAttributeBoolean( v_node
940 , 'for_catalog'
941 )
942 , 'for_status' : _getNodeAttributeBoolean( v_node
943 , 'for_status'
944 )
945 , 'update_always' : _getNodeAttributeBoolean( v_node
946 , 'update_always'
947 )
948 , 'default' : _extractDefaultNode( v_node, encoding )
949 , 'guard' : _extractGuardNode( v_node, encoding )
950 }
952 result.append( info )
954 return result
956 def _extractWorklistNodes( root, encoding=None ):
958 result = []
960 for w_node in root.getElementsByTagName( 'worklist' ):
962 info = { 'worklist_id' : _getNodeAttribute( w_node, 'worklist_id'
963 , encoding )
964 , 'title' : _getNodeAttribute( w_node, 'title' , encoding )
965 , 'description' : _extractDescriptionNode( w_node, encoding )
966 , 'match' : _extractMatchNode( w_node, encoding )
967 , 'action' : _extractActionNode( w_node, encoding )
968 , 'guard' : _extractGuardNode( w_node, encoding )
969 }
971 result.append( info )
973 return result
975 def _extractScriptNodes( root, encoding=None ):
977 result = []
979 for s_node in root.getElementsByTagName( 'script' ):
982 info = { 'script_id' : _getNodeAttribute( s_node, 'script_id' )
983 , 'meta_type' : _getNodeAttribute( s_node, 'type' , encoding )
984 }
986 filename = _queryNodeAttribute( s_node, 'filename' , None, encoding )
988 if filename is not None:
989 info[ 'filename' ] = filename
991 result.append( info )
993 return result
995 def _extractPermissionNodes( root, encoding=None ):
997 result = []
999 for p_node in root.getElementsByTagName( 'permission' ):
1001 result.append( _coalesceTextNodeChildren( p_node, encoding ) )
1003 return result
1005 def _extractActionNode( parent, encoding=None ):
1007 nodes = parent.getElementsByTagName( 'action' )
1008 assert len( nodes ) <= 1, nodes
1010 if len( nodes ) < 1:
1011 return { 'name' : '', 'url' : '', 'category' : '' }
1013 node = nodes[ 0 ]
1015 return { 'name' : _coalesceTextNodeChildren( node, encoding )
1016 , 'url' : _getNodeAttribute( node, 'url', encoding )
1017 , 'category' : _getNodeAttribute( node, 'category', encoding )
1020 def _extractGuardNode( parent, encoding=None ):
1022 nodes = parent.getElementsByTagName( 'guard' )
1023 assert len( nodes ) <= 1, nodes
1025 if len( nodes ) < 1:
1026 return { 'permissions' : (), 'roles' : (), 'groups' : (), 'expr' : '' }
1028 node = nodes[ 0 ]
1030 expr_nodes = node.getElementsByTagName( 'guard-expression' )
1031 assert( len( expr_nodes ) <= 1 )
1033 expr_text = expr_nodes and _coalesceTextNodeChildren( expr_nodes[ 0 ]
1034 , encoding
1035 ) or ''
1037 return { 'permissions' : [ _coalesceTextNodeChildren( x, encoding )
1038 for x in node.getElementsByTagName(
1039 'guard-permission' ) ]
1040 , 'roles' : [ _coalesceTextNodeChildren( x, encoding )
1041 for x in node.getElementsByTagName( 'guard-role' ) ]
1042 , 'groups' : [ _coalesceTextNodeChildren( x, encoding )
1043 for x in node.getElementsByTagName( 'guard-group' ) ]
1044 , 'expression' : expr_text
1047 def _extractDefaultNode( parent, encoding=None ):
1049 nodes = parent.getElementsByTagName( 'default' )
1050 assert len( nodes ) <= 1, nodes
1052 if len( nodes ) < 1:
1053 return { 'value' : '', 'expression' : '', 'type' : 'n/a' }
1055 node = nodes[ 0 ]
1057 value_nodes = node.getElementsByTagName( 'value' )
1058 assert( len( value_nodes ) <= 1 )
1060 value_type = 'n/a'
1061 if value_nodes:
1062 value_type = value_nodes[ 0 ].getAttribute( 'type' ) or 'n/a'
1064 value_text = value_nodes and _coalesceTextNodeChildren( value_nodes[ 0 ]
1065 , encoding
1066 ) or ''
1068 expr_nodes = node.getElementsByTagName( 'expression' )
1069 assert( len( expr_nodes ) <= 1 )
1071 expr_text = expr_nodes and _coalesceTextNodeChildren( expr_nodes[ 0 ]
1072 , encoding
1073 ) or ''
1075 return { 'value' : value_text
1076 , 'type' : value_type
1077 , 'expression' : expr_text
1080 _SEMICOLON_LIST_SPLITTER = re.compile( r';[ ]*' )
1082 def _extractMatchNode( parent, encoding=None ):
1084 nodes = parent.getElementsByTagName( 'match' )
1086 result = {}
1088 for node in nodes:
1090 name = _getNodeAttribute( node, 'name', encoding )
1091 values = _getNodeAttribute( node, 'values', encoding )
1092 result[ name ] = _SEMICOLON_LIST_SPLITTER.split( values )
1094 return result
1096 def _guessVariableType( value ):
1098 from DateTime.DateTime import DateTime
1100 if value is None:
1101 return 'none'
1103 if isinstance( value, DateTime ):
1104 return 'datetime'
1106 if isinstance( value, bool ):
1107 return 'bool'
1109 if isinstance( value, int ):
1110 return 'int'
1112 if isinstance( value, float ):
1113 return 'float'
1115 if isinstance( value, basestring ):
1116 return 'string'
1118 return 'unknown'
1120 def _convertVariableValue( value, type_id ):
1122 from DateTime.DateTime import DateTime
1124 if type_id == 'none':
1125 return None
1127 if type_id == 'datetime':
1129 return DateTime( value )
1131 if type_id == 'bool':
1133 if isinstance( value, basestring ):
1135 value = str( value ).lower()
1137 return value in ( 'true', 'yes', '1' )
1139 else:
1140 return bool( value )
1142 if type_id == 'int':
1143 return int( value )
1145 if type_id == 'float':
1146 return float( value )
1148 return value
1150 from Products.PythonScripts.PythonScript import PythonScript
1151 from Products.ExternalMethod.ExternalMethod import ExternalMethod
1152 from OFS.DTMLMethod import DTMLMethod
1154 _METATYPE_SUFFIXES = \
1155 { PythonScript.meta_type : 'py'
1156 , ExternalMethod.meta_type : 'em'
1157 , DTMLMethod.meta_type : 'dtml'
1160 def _initDCWorkflow( workflow
1161 , title
1162 , state_variable
1163 , initial_state
1164 , states
1165 , transitions
1166 , variables
1167 , worklists
1168 , permissions
1169 , scripts
1170 , context
1171 ):
1172 """ Initialize a DC Workflow using values parsed from XML.
1173 """
1174 workflow.title = title
1175 workflow.state_var = state_variable
1176 workflow.initial_state = initial_state
1178 permissions = permissions[:]
1179 permissions.sort()
1180 workflow.permissions = tuple(permissions)
1182 _initDCWorkflowVariables( workflow, variables )
1183 _initDCWorkflowStates( workflow, states )
1184 _initDCWorkflowTransitions( workflow, transitions )
1185 _initDCWorkflowWorklists( workflow, worklists )
1186 _initDCWorkflowScripts( workflow, scripts, context )
1189 def _initDCWorkflowVariables( workflow, variables ):
1191 """ Initialize DCWorkflow variables
1192 """
1193 from Products.DCWorkflow.Variables import VariableDefinition
1195 for v_info in variables:
1197 id = str( v_info[ 'variable_id' ] ) # no unicode!
1198 v = VariableDefinition( id )
1199 workflow.variables._setObject( id, v )
1200 v = workflow.variables._getOb( id )
1202 guard = v_info[ 'guard' ]
1203 props = { 'guard_roles' : ';'.join( guard[ 'roles' ] )
1204 , 'guard_permissions' : ';'.join( guard[ 'permissions' ] )
1205 , 'guard_groups' : ';'.join( guard[ 'groups' ] )
1206 , 'guard_expr' : guard[ 'expression' ]
1209 default = v_info[ 'default' ]
1210 default_value = _convertVariableValue( default[ 'value' ]
1211 , default[ 'type' ] )
1213 v.setProperties( description = v_info[ 'description' ]
1214 , default_value = default_value
1215 , default_expr = default[ 'expression' ]
1216 , for_catalog = v_info[ 'for_catalog' ]
1217 , for_status = v_info[ 'for_status' ]
1218 , update_always = v_info[ 'update_always' ]
1219 , props = props
1223 def _initDCWorkflowStates( workflow, states ):
1225 """ Initialize DCWorkflow states
1226 """
1227 from Globals import PersistentMapping
1228 from Products.DCWorkflow.States import StateDefinition
1230 for s_info in states:
1232 id = str( s_info[ 'state_id' ] ) # no unicode!
1233 s = StateDefinition( id )
1234 workflow.states._setObject( id, s )
1235 s = workflow.states._getOb( id )
1237 s.setProperties( title = s_info[ 'title' ]
1238 , description = s_info[ 'description' ]
1239 , transitions = s_info[ 'transitions' ]
1242 for k, v in s_info[ 'permissions' ].items():
1243 s.setPermission( k, isinstance(v, list), v )
1245 gmap = s.group_roles = PersistentMapping()
1247 for group_id, roles in s_info[ 'groups' ]:
1248 gmap[ group_id ] = roles
1250 vmap = s.var_values = PersistentMapping()
1252 for name, v_info in s_info[ 'variables' ].items():
1254 value = _convertVariableValue( v_info[ 'value' ]
1255 , v_info[ 'type' ] )
1257 vmap[ name ] = value
1260 def _initDCWorkflowTransitions( workflow, transitions ):
1262 """ Initialize DCWorkflow transitions
1263 """
1264 from Globals import PersistentMapping
1265 from Products.DCWorkflow.Transitions import TransitionDefinition
1267 for t_info in transitions:
1269 id = str( t_info[ 'transition_id' ] ) # no unicode!
1270 t = TransitionDefinition( id )
1271 workflow.transitions._setObject( id, t )
1272 t = workflow.transitions._getOb( id )
1274 trigger_type = list( TRIGGER_TYPES ).index( t_info[ 'trigger' ] )
1276 action = t_info[ 'action' ]
1278 guard = t_info[ 'guard' ]
1279 props = { 'guard_roles' : ';'.join( guard[ 'roles' ] )
1280 , 'guard_permissions' : ';'.join( guard[ 'permissions' ] )
1281 , 'guard_groups' : ';'.join( guard[ 'groups' ] )
1282 , 'guard_expr' : guard[ 'expression' ]
1285 t.setProperties( title = t_info[ 'title' ]
1286 , description = t_info[ 'description' ]
1287 , new_state_id = t_info[ 'new_state' ]
1288 , trigger_type = trigger_type
1289 , script_name = t_info[ 'before_script' ]
1290 , after_script_name = t_info[ 'after_script' ]
1291 , actbox_name = action[ 'name' ]
1292 , actbox_url = action[ 'url' ]
1293 , actbox_category = action[ 'category' ]
1294 , props = props
1297 t.var_exprs = PersistentMapping( t_info[ 'variables' ].items() )
1299 def _initDCWorkflowWorklists( workflow, worklists ):
1301 """ Initialize DCWorkflow worklists
1302 """
1303 from Globals import PersistentMapping
1304 from Products.DCWorkflow.Worklists import WorklistDefinition
1306 for w_info in worklists:
1308 id = str( w_info[ 'worklist_id' ] ) # no unicode!
1309 w = WorklistDefinition( id )
1310 workflow.worklists._setObject( id, w )
1312 w = workflow.worklists._getOb( id )
1314 action = w_info[ 'action' ]
1316 guard = w_info[ 'guard' ]
1317 props = { 'guard_roles' : ';'.join( guard[ 'roles' ] )
1318 , 'guard_permissions' : ';'.join( guard[ 'permissions' ] )
1319 , 'guard_groups' : ';'.join( guard[ 'groups' ] )
1320 , 'guard_expr' : guard[ 'expression' ]
1323 w.setProperties( description = w_info[ 'description' ]
1324 , actbox_name = action[ 'name' ]
1325 , actbox_url = action[ 'url' ]
1326 , actbox_category = action[ 'category' ]
1327 , props = props
1330 w.var_matches = PersistentMapping()
1331 for k, v in w_info[ 'match' ].items():
1332 w.var_matches[ str( k ) ] = tuple( [ str(x) for x in v ] )
1334 def _initDCWorkflowScripts( workflow, scripts, context ):
1336 """ Initialize DCWorkflow scripts
1337 """
1338 for s_info in scripts:
1340 id = str( s_info[ 'script_id' ] ) # no unicode!
1341 meta_type = s_info[ 'meta_type' ]
1342 filename = s_info[ 'filename' ]
1344 file = context.readDataFile( filename )
1346 if meta_type == PythonScript.meta_type:
1347 script = PythonScript( id )
1348 script.write( file )
1350 #elif meta_type == ExternalMethod.meta_type:
1351 # script = ExternalMethod( id, title, module, function )
1353 elif meta_type == DTMLMethod.meta_type:
1354 script = DTMLMethod( file, __name__=id )
1356 workflow.scripts._setObject( id, script )