vendor/CMF/1.6.1/DCWorkflow

view exportimport.py @ 0:238bab7e7116

CMF 1.6.1 vendor import
author fguillaume
date Tue, 13 Jun 2006 14:57:59 +0000
parents
children
line source
1 ##############################################################################
2 #
3 # Copyright (c) 2005 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 """DCWorkflow export / import support.
15 $Id: exportimport.py 68102 2006-05-12 06:05:10Z rafrombrc $
16 """
18 import re
19 from xml.dom.minidom import parseString
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.GenericSetup.utils import BodyAdapterBase
28 from utils import _xmldir
29 from DCWorkflow import DCWorkflowDefinition
30 from interfaces import IDCWorkflowDefinition
31 from permissions import ManagePortal
34 TRIGGER_TYPES = ( 'AUTOMATIC', 'USER', 'WORKFLOWMETHOD' )
35 _FILENAME = 'workflows.xml'
38 class DCWorkflowDefinitionBodyAdapter(BodyAdapterBase):
40 """Body im- and exporter for DCWorkflowDefinition.
41 """
43 __used_for__ = IDCWorkflowDefinition
45 def _exportBody(self):
46 """Export the object as a file body.
47 """
48 wfdc = WorkflowDefinitionConfigurator(self.context)
49 return wfdc.__of__(self.context).generateWorkflowXML()
51 def _importBody(self, body):
52 """Import the object from the file body.
53 """
54 encoding = 'utf-8'
55 wfdc = WorkflowDefinitionConfigurator(self.context)
57 ( workflow_id
58 , title
59 , state_variable
60 , initial_state
61 , states
62 , transitions
63 , variables
64 , worklists
65 , permissions
66 , scripts
67 ) = wfdc.parseWorkflowXML(body, encoding)
69 _initDCWorkflow( self.context
70 , title
71 , state_variable
72 , initial_state
73 , states
74 , transitions
75 , variables
76 , worklists
77 , permissions
78 , scripts
79 , self.environ
80 )
82 body = property(_exportBody, _importBody)
84 mime_type = 'text/xml'
86 suffix = '/definition.xml'
89 class WorkflowDefinitionConfigurator( Implicit ):
90 """ Synthesize XML description of site's workflows.
91 """
92 security = ClassSecurityInfo()
94 def __init__(self, obj):
95 self._obj = obj
97 security.declareProtected( ManagePortal, 'getWorkflowInfo' )
98 def getWorkflowInfo( self, workflow_id ):
100 """ Return a mapping describing a given workflow.
102 o Keys in the mappings:
104 'id' -- the ID of the workflow within the tool
106 'meta_type' -- the workflow's meta_type
108 'title' -- the workflow's title property
110 o See '_extractDCWorkflowInfo' below for keys present only for
111 DCWorkflow definitions.
113 """
114 workflow = self._obj
116 workflow_info = { 'id' : workflow_id
117 , 'meta_type' : workflow.meta_type
118 , 'title' : workflow.title_or_id()
119 }
121 if workflow.meta_type == DCWorkflowDefinition.meta_type:
122 self._extractDCWorkflowInfo( workflow, workflow_info )
124 return workflow_info
126 security.declareProtected( ManagePortal, 'generateWorkflowXML' )
127 def generateWorkflowXML(self):
128 """ Pseudo API.
129 """
130 return self._workflowConfig(workflow_id=self._obj.getId())
132 security.declareProtected( ManagePortal, 'getWorkflowScripts' )
133 def getWorkflowScripts(self):
134 """ Get workflow scripts information
135 """
136 return self._extractScripts(self._obj)
138 security.declareProtected( ManagePortal, 'parseWorkflowXML' )
139 def parseWorkflowXML( self, xml, encoding=None ):
140 """ Pseudo API.
141 """
142 dom = parseString( xml )
144 root = dom.getElementsByTagName( 'dc-workflow' )[ 0 ]
146 workflow_id = _getNodeAttribute( root, 'workflow_id', encoding )
147 title = _getNodeAttribute( root, 'title', encoding )
148 state_variable = _getNodeAttribute( root, 'state_variable', encoding )
149 initial_state = _getNodeAttribute( root, 'initial_state', encoding )
151 states = _extractStateNodes( root, encoding )
152 transitions = _extractTransitionNodes( root, encoding )
153 variables = _extractVariableNodes( root, encoding )
154 worklists = _extractWorklistNodes( root, encoding )
155 permissions = _extractPermissionNodes( root, encoding )
156 scripts = _extractScriptNodes( root, encoding )
158 return ( workflow_id
159 , title
160 , state_variable
161 , initial_state
162 , states
163 , transitions
164 , variables
165 , worklists
166 , permissions
167 , scripts
168 )
170 security.declarePrivate( '_workflowConfig' )
171 _workflowConfig = PageTemplateFile( 'wtcWorkflowExport.xml'
172 , _xmldir
173 , __name__='workflowConfig'
174 )
176 security.declarePrivate( '_extractDCWorkflowInfo' )
177 def _extractDCWorkflowInfo( self, workflow, workflow_info ):
179 """ Append the information for a 'workflow' into 'workflow_info'
181 o 'workflow' must be a DCWorkflowDefinition instance.
183 o 'workflow_info' must be a dictionary.
185 o The following keys will be added to 'workflow_info':
187 'permissions' -- a list of names of permissions managed
188 by the workflow
190 'state_variable' -- the name of the workflow's "main"
191 state variable
193 'initial_state' -- the name of the state in the workflow
194 in which objects start their lifecycle.
196 'variable_info' -- a list of mappings describing the
197 variables tracked by the workflow (see '_extractVariables').
199 'state_info' -- a list of mappings describing the
200 states tracked by the workflow (see '_extractStates').
202 'transition_info' -- a list of mappings describing the
203 transitions tracked by the workflow (see '_extractTransitions').
205 'worklist_info' -- a list of mappings describing the
206 worklists tracked by the workflow (see '_extractWorklists').
208 'script_info' -- a list of mappings describing the scripts which
209 provide added business logic (see '_extractScripts').
210 """
211 workflow_info[ 'state_variable' ] = workflow.state_var
212 workflow_info[ 'initial_state' ] = workflow.initial_state
213 workflow_info[ 'permissions' ] = workflow.permissions
214 workflow_info[ 'variable_info' ] = self._extractVariables( workflow )
215 workflow_info[ 'state_info' ] = self._extractStates( workflow )
216 workflow_info[ 'transition_info' ] = self._extractTransitions(
217 workflow )
218 workflow_info[ 'worklist_info' ] = self._extractWorklists( workflow )
219 workflow_info[ 'script_info' ] = self._extractScripts( workflow )
221 security.declarePrivate( '_extractVariables' )
222 def _extractVariables( self, workflow ):
224 """ Return a sequence of mappings describing DCWorkflow variables.
226 o Keys for each mapping will include:
228 'id' -- the variable's ID
230 'description' -- a textual description of the variable
232 'for_catalog' -- whether to catalog this variable
234 'for_status' -- whether to ??? this variable (XXX)
236 'update_always' -- whether to update this variable whenever
237 executing a transition (xxX)
239 'default_value' -- a default value for the variable (XXX)
241 'default_expression' -- a TALES expression for the default value
243 'guard_permissions' -- a list of permissions guarding access
244 to the variable
246 'guard_roles' -- a list of roles guarding access
247 to the variable
249 'guard_groups' -- a list of groups guarding the transition
251 'guard_expr' -- an expression guarding access to the variable
252 """
253 result = []
255 items = workflow.variables.objectItems()
256 items.sort()
258 for k, v in items:
260 guard = v.getInfoGuard()
262 default_type = _guessVariableType( v.default_value )
264 info = { 'id' : k
265 , 'description' : v.description
266 , 'for_catalog' : bool( v.for_catalog )
267 , 'for_status' : bool( v.for_status )
268 , 'update_always' : bool( v.update_always )
269 , 'default_value' : v.default_value
270 , 'default_type' : default_type
271 , 'default_expr' : v.getDefaultExprText()
272 , 'guard_permissions' : guard.permissions
273 , 'guard_roles' : guard.roles
274 , 'guard_groups' : guard.groups
275 , 'guard_expr' : guard.getExprText()
276 }
278 result.append( info )
280 return result
282 security.declarePrivate( '_extractStates' )
283 def _extractStates( self, workflow ):
285 """ Return a sequence of mappings describing DCWorkflow states.
287 o Within the workflow mapping, each 'state_info' mapping has keys:
289 'id' -- the state's ID
291 'title' -- the state's title
293 'description' -- the state's description
295 'transitions' -- a list of IDs of transitions out of the state
297 'permissions' -- a list of mappings describing the permission
298 map for the state
300 'groups' -- a list of ( group_id, (roles,) ) tuples describing the
301 group-role assignments for the state
303 'variables' -- a list of mapping for the variables
304 to be set when entering the state.
306 o Within the state_info mappings, each 'permissions' mapping
307 has the keys:
309 'name' -- the name of the permission
311 'roles' -- a sequence of role IDs which have the permission
313 'acquired' -- whether roles are acquired for the permission
315 o Within the state_info mappings, each 'variable' mapping
316 has the keys:
318 'name' -- the name of the variable
320 'type' -- the type of the value (allowed values are:
321 'string', 'datetime', 'bool', 'int')
323 'value' -- the value to be set
324 """
325 result = []
327 items = workflow.states.objectItems()
328 items.sort()
330 for k, v in items:
332 groups = v.group_roles and list( v.group_roles.items() ) or []
333 groups = [ x for x in groups if x[1] ]
334 groups.sort()
336 variables = list( v.getVariableValues() )
337 variables.sort()
339 v_info = []
341 for v_name, value in variables:
342 v_info.append( { 'name' : v_name
343 , 'type' :_guessVariableType( value )
344 , 'value' : value
345 } )
347 info = { 'id' : k
348 , 'title' : v.title
349 , 'description' : v.description
350 , 'transitions' : v.transitions
351 , 'permissions' : self._extractStatePermissions( v )
352 , 'groups' : groups
353 , 'variables' : v_info
354 }
356 result.append( info )
358 return result
360 security.declarePrivate( '_extractStatePermissions' )
361 def _extractStatePermissions( self, state ):
363 """ Return a sequence of mappings for the permissions in a state.
365 o Each mapping has the keys:
367 'name' -- the name of the permission
369 'roles' -- a sequence of role IDs which have the permission
371 'acquired' -- whether roles are acquired for the permission
372 """
373 result = []
374 perm_roles = state.permission_roles
376 if perm_roles:
377 items = state.permission_roles.items()
378 items.sort()
380 for k, v in items:
382 result.append( { 'name' : k
383 , 'roles' : v
384 , 'acquired' : not isinstance( v, tuple )
385 } )
387 return result
390 security.declarePrivate( '_extractTransitions' )
391 def _extractTransitions( self, workflow ):
393 """ Return a sequence of mappings describing DCWorkflow transitions.
395 o Each mapping has the keys:
397 'id' -- the transition's ID
399 'title' -- the transition's ID
401 'description' -- the transition's description
403 'new_state_id' -- the ID of the state into which the transition
404 moves an object
406 'trigger_type' -- one of the following values, indicating how the
407 transition is fired:
409 - "AUTOMATIC" -> fired opportunistically whenever the workflow
410 notices that its guard conditions permit
412 - "USER" -> fired in response to user request
414 'script_name' -- the ID of a script to be executed before
415 the transition
417 'after_script_name' -- the ID of a script to be executed after
418 the transition
420 'actbox_name' -- the name of the action by which the user
421 triggers the transition
423 'actbox_url' -- the URL of the action by which the user
424 triggers the transition
426 'actbox_category' -- the category of the action by which the user
427 triggers the transition
429 'variables' -- a list of ( id, expr ) tuples defining how variables
430 are to be set during the transition
432 'guard_permissions' -- a list of permissions guarding the transition
434 'guard_roles' -- a list of roles guarding the transition
436 'guard_groups' -- a list of groups guarding the transition
438 'guard_expr' -- an expression guarding the transition
440 """
441 result = []
443 items = workflow.transitions.objectItems()
444 items.sort()
446 for k, v in items:
447 guard = v.getGuard()
449 v_info = []
451 for v_name, expr in v.getVariableExprs():
452 v_info.append( { 'name' : v_name, 'expr' : expr } )
454 info = { 'id' : k
455 , 'title' : v.title
456 , 'description' : v.description
457 , 'new_state_id' : v.new_state_id
458 , 'trigger_type' : TRIGGER_TYPES[ v.trigger_type ]
459 , 'script_name' : v.script_name
460 , 'after_script_name' : v.after_script_name
461 , 'actbox_name' : v.actbox_name
462 , 'actbox_url' : v.actbox_url
463 , 'actbox_category' : v.actbox_category
464 , 'variables' : v_info
465 , 'guard_permissions' : guard.permissions
466 , 'guard_roles' : guard.roles
467 , 'guard_groups' : guard.groups
468 , 'guard_expr' : guard.getExprText()
469 }
471 result.append( info )
472 return result
474 security.declarePrivate( '_extractWorklists' )
475 def _extractWorklists( self, workflow ):
477 """ Return a sequence of mappings describing DCWorkflow transitions.
479 o Each mapping has the keys:
481 'id' -- the ID of the worklist
483 'title' -- the title of the worklist
485 'description' -- a textual description of the worklist
487 'var_match' -- a list of ( key, value ) tuples defining
488 the variables used to "activate" the worklist.
490 'actbox_name' -- the name of the "action" corresponding to the
491 worklist
493 'actbox_url' -- the URL of the "action" corresponding to the
494 worklist
496 'actbox_category' -- the category of the "action" corresponding
497 to the worklist
499 'guard_permissions' -- a list of permissions guarding access
500 to the worklist
502 'guard_roles' -- a list of roles guarding access
503 to the worklist
505 'guard_expr' -- an expression guarding access to the worklist
507 """
508 result = []
510 items = workflow.worklists.objectItems()
511 items.sort()
513 for k, v in items:
515 guard = v.getGuard()
517 var_match = [ ( id, v.getVarMatchText( id ) )
518 for id in v.getVarMatchKeys() ]
520 info = { 'id' : k
521 , 'title' : v.title
522 , 'description' : v.description
523 , 'var_match' : var_match
524 , 'actbox_name' : v.actbox_name
525 , 'actbox_url' : v.actbox_url
526 , 'actbox_category' : v.actbox_category
527 , 'guard_permissions' : guard.permissions
528 , 'guard_roles' : guard.roles
529 , 'guard_groups' : guard.groups
530 , 'guard_expr' : guard.getExprText()
531 }
533 result.append( info )
535 return result
537 security.declarePrivate( '_extractScripts' )
538 def _extractScripts( self, workflow ):
540 """ Return a sequence of mappings describing DCWorkflow scripts.
542 o Each mapping has the keys:
544 'id' -- the ID of the script
546 'meta_type' -- the title of the worklist
548 'body' -- the text of the script (only applicable to scripts
549 of type Script (Python))
551 'module' -- The module from where to load the function (only
552 applicable to External Method scripts)
554 'function' -- The function to load from the 'module' given
555 (Only applicable to External Method scripts)
557 'filename' -- the name of the file to / from which the script
558 is stored / loaded (Script (Python) only)
559 """
560 result = []
562 items = workflow.scripts.objectItems()
563 items.sort()
565 for k, v in items:
567 filename = _getScriptFilename( workflow.getId(), k, v.meta_type )
568 module = ''
569 function = ''
571 if v.meta_type == 'External Method':
572 module = v.module()
573 function = v.function()
575 info = { 'id' : k
576 , 'meta_type' : v.meta_type
577 , 'module' : module
578 , 'function' : function
579 , 'filename' : filename
580 }
582 result.append( info )
584 return result
586 InitializeClass( WorkflowDefinitionConfigurator )
589 def _getScriptFilename( workflow_id, script_id, meta_type ):
591 """ Return the name of the file which holds the script.
592 """
593 wf_dir = workflow_id.replace( ' ', '_' )
594 suffix = _METATYPE_SUFFIXES.get(meta_type, None)
596 if suffix is None:
597 return ''
599 return 'workflows/%s/scripts/%s.%s' % ( wf_dir, script_id, suffix )
601 def _extractStateNodes( root, encoding=None ):
603 result = []
605 for s_node in root.getElementsByTagName( 'state' ):
607 info = { 'state_id' : _getNodeAttribute( s_node, 'state_id', encoding )
608 , 'title' : _getNodeAttribute( s_node, 'title', encoding )
609 , 'description' : _extractDescriptionNode( s_node, encoding )
610 }
612 info[ 'transitions' ] = [ _getNodeAttribute( x, 'transition_id'
613 , encoding )
614 for x in s_node.getElementsByTagName(
615 'exit-transition' ) ]
617 info[ 'permissions' ] = permission_map = {}
619 for p_map in s_node.getElementsByTagName( 'permission-map' ):
621 name = _getNodeAttribute( p_map, 'name', encoding )
622 acquired = _getNodeAttributeBoolean( p_map, 'acquired' )
624 roles = [ _coalesceTextNodeChildren( x, encoding )
625 for x in p_map.getElementsByTagName(
626 'permission-role' ) ]
628 if not acquired:
629 roles = tuple( roles )
631 permission_map[ name ] = roles
633 info[ 'groups' ] = group_map = []
635 for g_map in s_node.getElementsByTagName( 'group-map' ):
637 name = _getNodeAttribute( g_map, 'name', encoding )
639 roles = [ _coalesceTextNodeChildren( x, encoding )
640 for x in g_map.getElementsByTagName(
641 'group-role' ) ]
643 group_map.append( ( name, tuple( roles ) ) )
645 info[ 'variables' ] = var_map = {}
647 for assignment in s_node.getElementsByTagName( 'assignment' ):
649 name = _getNodeAttribute( assignment, 'name', encoding )
650 type_id = _getNodeAttribute( assignment, 'type', encoding )
651 value = _coalesceTextNodeChildren( assignment, encoding )
653 var_map[ name ] = { 'name' : name
654 , 'type' : type_id
655 , 'value' : value
656 }
658 result.append( info )
660 return result
662 def _extractTransitionNodes( root, encoding=None ):
664 result = []
666 for t_node in root.getElementsByTagName( 'transition' ):
668 info = { 'transition_id' : _getNodeAttribute( t_node, 'transition_id'
669 , encoding )
670 , 'title' : _getNodeAttribute( t_node, 'title', encoding )
671 , 'description' : _extractDescriptionNode( t_node, encoding )
672 , 'new_state' : _getNodeAttribute( t_node, 'new_state'
673 , encoding )
674 , 'trigger' : _getNodeAttribute( t_node, 'trigger', encoding )
675 , 'before_script' : _getNodeAttribute( t_node, 'before_script'
676 , encoding )
677 , 'after_script' : _getNodeAttribute( t_node, 'after_script'
678 , encoding )
679 , 'action' : _extractActionNode( t_node, encoding )
680 , 'guard' : _extractGuardNode( t_node, encoding )
681 }
683 info[ 'variables' ] = var_map = {}
685 for assignment in t_node.getElementsByTagName( 'assignment' ):
687 name = _getNodeAttribute( assignment, 'name', encoding )
688 expr = _coalesceTextNodeChildren( assignment, encoding )
689 var_map[ name ] = expr
691 result.append( info )
693 return result
695 def _extractVariableNodes( root, encoding=None ):
697 result = []
699 for v_node in root.getElementsByTagName( 'variable' ):
701 info = { 'variable_id' : _getNodeAttribute( v_node, 'variable_id'
702 , encoding )
703 , 'description' : _extractDescriptionNode( v_node, encoding )
704 , 'for_catalog' : _getNodeAttributeBoolean( v_node
705 , 'for_catalog'
706 )
707 , 'for_status' : _getNodeAttributeBoolean( v_node
708 , 'for_status'
709 )
710 , 'update_always' : _getNodeAttributeBoolean( v_node
711 , 'update_always'
712 )
713 , 'default' : _extractDefaultNode( v_node, encoding )
714 , 'guard' : _extractGuardNode( v_node, encoding )
715 }
717 result.append( info )
719 return result
721 def _extractWorklistNodes( root, encoding=None ):
723 result = []
725 for w_node in root.getElementsByTagName( 'worklist' ):
727 info = { 'worklist_id' : _getNodeAttribute( w_node, 'worklist_id'
728 , encoding )
729 , 'title' : _getNodeAttribute( w_node, 'title' , encoding )
730 , 'description' : _extractDescriptionNode( w_node, encoding )
731 , 'match' : _extractMatchNode( w_node, encoding )
732 , 'action' : _extractActionNode( w_node, encoding )
733 , 'guard' : _extractGuardNode( w_node, encoding )
734 }
736 result.append( info )
738 return result
740 def _extractScriptNodes( root, encoding=None ):
742 result = []
744 for s_node in root.getElementsByTagName( 'script' ):
746 try:
747 function = _getNodeAttribute( s_node, 'function' )
748 except ValueError:
749 function = ''
751 try:
752 module = _getNodeAttribute( s_node, 'module' )
753 except ValueError:
754 module = ''
756 info = { 'script_id' : _getNodeAttribute( s_node, 'script_id' )
757 , 'meta_type' : _getNodeAttribute( s_node, 'type' , encoding )
758 , 'function' : function
759 , 'module' : module
760 }
762 filename = _queryNodeAttribute( s_node, 'filename' , None, encoding )
764 if filename is not None:
765 info[ 'filename' ] = filename
767 result.append( info )
769 return result
771 def _extractPermissionNodes( root, encoding=None ):
773 result = []
775 for p_node in root.getElementsByTagName( 'permission' ):
777 result.append( _coalesceTextNodeChildren( p_node, encoding ) )
779 return result
781 def _extractActionNode( parent, encoding=None ):
783 nodes = parent.getElementsByTagName( 'action' )
784 assert len( nodes ) <= 1, nodes
786 if len( nodes ) < 1:
787 return { 'name' : '', 'url' : '', 'category' : '' }
789 node = nodes[ 0 ]
791 return { 'name' : _coalesceTextNodeChildren( node, encoding )
792 , 'url' : _getNodeAttribute( node, 'url', encoding )
793 , 'category' : _getNodeAttribute( node, 'category', encoding )
794 }
796 def _extractGuardNode( parent, encoding=None ):
798 nodes = parent.getElementsByTagName( 'guard' )
799 assert len( nodes ) <= 1, nodes
801 if len( nodes ) < 1:
802 return { 'permissions' : (), 'roles' : (), 'groups' : (), 'expr' : '' }
804 node = nodes[ 0 ]
806 expr_nodes = node.getElementsByTagName( 'guard-expression' )
807 assert( len( expr_nodes ) <= 1 )
809 expr_text = expr_nodes and _coalesceTextNodeChildren( expr_nodes[ 0 ]
810 , encoding
811 ) or ''
813 return { 'permissions' : [ _coalesceTextNodeChildren( x, encoding )
814 for x in node.getElementsByTagName(
815 'guard-permission' ) ]
816 , 'roles' : [ _coalesceTextNodeChildren( x, encoding )
817 for x in node.getElementsByTagName( 'guard-role' ) ]
818 , 'groups' : [ _coalesceTextNodeChildren( x, encoding )
819 for x in node.getElementsByTagName( 'guard-group' ) ]
820 , 'expression' : expr_text
821 }
823 def _extractDefaultNode( parent, encoding=None ):
825 nodes = parent.getElementsByTagName( 'default' )
826 assert len( nodes ) <= 1, nodes
828 if len( nodes ) < 1:
829 return { 'value' : '', 'expression' : '', 'type' : 'n/a' }
831 node = nodes[ 0 ]
833 value_nodes = node.getElementsByTagName( 'value' )
834 assert( len( value_nodes ) <= 1 )
836 value_type = 'n/a'
837 if value_nodes:
838 value_type = value_nodes[ 0 ].getAttribute( 'type' ) or 'n/a'
840 value_text = value_nodes and _coalesceTextNodeChildren( value_nodes[ 0 ]
841 , encoding
842 ) or ''
844 expr_nodes = node.getElementsByTagName( 'expression' )
845 assert( len( expr_nodes ) <= 1 )
847 expr_text = expr_nodes and _coalesceTextNodeChildren( expr_nodes[ 0 ]
848 , encoding
849 ) or ''
851 return { 'value' : value_text
852 , 'type' : value_type
853 , 'expression' : expr_text
854 }
856 _SEMICOLON_LIST_SPLITTER = re.compile( r';[ ]*' )
858 def _extractMatchNode( parent, encoding=None ):
860 nodes = parent.getElementsByTagName( 'match' )
862 result = {}
864 for node in nodes:
866 name = _getNodeAttribute( node, 'name', encoding )
867 values = _getNodeAttribute( node, 'values', encoding )
868 result[ name ] = _SEMICOLON_LIST_SPLITTER.split( values )
870 return result
872 def _guessVariableType( value ):
874 from DateTime.DateTime import DateTime
876 if value is None:
877 return 'none'
879 if isinstance( value, DateTime ):
880 return 'datetime'
882 if isinstance( value, bool ):
883 return 'bool'
885 if isinstance( value, int ):
886 return 'int'
888 if isinstance( value, float ):
889 return 'float'
891 if isinstance( value, basestring ):
892 return 'string'
894 return 'unknown'
896 def _convertVariableValue( value, type_id ):
898 from DateTime.DateTime import DateTime
900 if type_id == 'none':
901 return None
903 if type_id == 'datetime':
905 return DateTime( value )
907 if type_id == 'bool':
909 if isinstance( value, basestring ):
911 value = str( value ).lower()
913 return value in ( 'true', 'yes', '1' )
915 else:
916 return bool( value )
918 if type_id == 'int':
919 return int( value )
921 if type_id == 'float':
922 return float( value )
924 return value
926 from Products.PythonScripts.PythonScript import PythonScript
927 from Products.ExternalMethod.ExternalMethod import ExternalMethod
928 from OFS.DTMLMethod import DTMLMethod
930 _METATYPE_SUFFIXES = \
931 { PythonScript.meta_type : 'py'
932 , DTMLMethod.meta_type : 'dtml'
933 }
935 def _initDCWorkflow( workflow
936 , title
937 , state_variable
938 , initial_state
939 , states
940 , transitions
941 , variables
942 , worklists
943 , permissions
944 , scripts
945 , context
946 ):
947 """ Initialize a DC Workflow using values parsed from XML.
948 """
949 workflow.title = title
950 workflow.state_var = state_variable
951 workflow.initial_state = initial_state
953 permissions = permissions[:]
954 permissions.sort()
955 workflow.permissions = tuple(permissions)
957 _initDCWorkflowVariables( workflow, variables )
958 _initDCWorkflowStates( workflow, states )
959 _initDCWorkflowTransitions( workflow, transitions )
960 _initDCWorkflowWorklists( workflow, worklists )
961 _initDCWorkflowScripts( workflow, scripts, context )
964 def _initDCWorkflowVariables( workflow, variables ):
966 """ Initialize DCWorkflow variables
967 """
968 from Products.DCWorkflow.Variables import VariableDefinition
970 for v_info in variables:
972 id = str( v_info[ 'variable_id' ] ) # no unicode!
973 if not workflow.variables.has_key(id):
974 v = VariableDefinition(id)
975 workflow.variables._setObject(id, v)
976 v = workflow.variables._getOb( id )
978 guard = v_info[ 'guard' ]
979 props = { 'guard_roles' : ';'.join( guard[ 'roles' ] )
980 , 'guard_permissions' : ';'.join( guard[ 'permissions' ] )
981 , 'guard_groups' : ';'.join( guard[ 'groups' ] )
982 , 'guard_expr' : guard[ 'expression' ]
983 }
985 default = v_info[ 'default' ]
986 default_value = _convertVariableValue( default[ 'value' ]
987 , default[ 'type' ] )
989 v.setProperties( description = v_info[ 'description' ]
990 , default_value = default_value
991 , default_expr = default[ 'expression' ]
992 , for_catalog = v_info[ 'for_catalog' ]
993 , for_status = v_info[ 'for_status' ]
994 , update_always = v_info[ 'update_always' ]
995 , props = props
996 )
999 def _initDCWorkflowStates( workflow, states ):
1001 """ Initialize DCWorkflow states
1002 """
1003 from Globals import PersistentMapping
1004 from Products.DCWorkflow.States import StateDefinition
1006 for s_info in states:
1008 id = str( s_info[ 'state_id' ] ) # no unicode!
1009 if not workflow.states.has_key(id):
1010 s = StateDefinition(id)
1011 workflow.states._setObject(id, s)
1012 s = workflow.states._getOb( id )
1014 s.setProperties( title = s_info[ 'title' ]
1015 , description = s_info[ 'description' ]
1016 , transitions = s_info[ 'transitions' ]
1019 for k, v in s_info[ 'permissions' ].items():
1020 s.setPermission( k, isinstance(v, list), v )
1022 gmap = s.group_roles = PersistentMapping()
1024 for group_id, roles in s_info[ 'groups' ]:
1025 gmap[ group_id ] = roles
1027 vmap = s.var_values = PersistentMapping()
1029 for name, v_info in s_info[ 'variables' ].items():
1031 value = _convertVariableValue( v_info[ 'value' ]
1032 , v_info[ 'type' ] )
1034 vmap[ name ] = value
1037 def _initDCWorkflowTransitions( workflow, transitions ):
1039 """ Initialize DCWorkflow transitions
1040 """
1041 from Globals import PersistentMapping
1042 from Products.DCWorkflow.Transitions import TransitionDefinition
1044 for t_info in transitions:
1046 id = str( t_info[ 'transition_id' ] ) # no unicode!
1047 if not workflow.transitions.has_key(id):
1048 t = TransitionDefinition(id)
1049 workflow.transitions._setObject(id, t)
1050 t = workflow.transitions._getOb( id )
1052 trigger_type = list( TRIGGER_TYPES ).index( t_info[ 'trigger' ] )
1054 action = t_info[ 'action' ]
1056 guard = t_info[ 'guard' ]
1057 props = { 'guard_roles' : ';'.join( guard[ 'roles' ] )
1058 , 'guard_permissions' : ';'.join( guard[ 'permissions' ] )
1059 , 'guard_groups' : ';'.join( guard[ 'groups' ] )
1060 , 'guard_expr' : guard[ 'expression' ]
1063 t.setProperties( title = t_info[ 'title' ]
1064 , description = t_info[ 'description' ]
1065 , new_state_id = t_info[ 'new_state' ]
1066 , trigger_type = trigger_type
1067 , script_name = t_info[ 'before_script' ]
1068 , after_script_name = t_info[ 'after_script' ]
1069 , actbox_name = action[ 'name' ]
1070 , actbox_url = action[ 'url' ]
1071 , actbox_category = action[ 'category' ]
1072 , props = props
1075 t.var_exprs = PersistentMapping( t_info[ 'variables' ].items() )
1077 def _initDCWorkflowWorklists( workflow, worklists ):
1079 """ Initialize DCWorkflow worklists
1080 """
1081 from Globals import PersistentMapping
1082 from Products.DCWorkflow.Worklists import WorklistDefinition
1084 for w_info in worklists:
1086 id = str( w_info[ 'worklist_id' ] ) # no unicode!
1087 if not workflow.worklists.has_key(id):
1088 w = WorklistDefinition(id)
1089 workflow.worklists._setObject(id, w)
1090 w = workflow.worklists._getOb( id )
1092 action = w_info[ 'action' ]
1094 guard = w_info[ 'guard' ]
1095 props = { 'guard_roles' : ';'.join( guard[ 'roles' ] )
1096 , 'guard_permissions' : ';'.join( guard[ 'permissions' ] )
1097 , 'guard_groups' : ';'.join( guard[ 'groups' ] )
1098 , 'guard_expr' : guard[ 'expression' ]
1101 w.setProperties( description = w_info[ 'description' ]
1102 , actbox_name = action[ 'name' ]
1103 , actbox_url = action[ 'url' ]
1104 , actbox_category = action[ 'category' ]
1105 , props = props
1108 w.var_matches = PersistentMapping()
1109 for k, v in w_info[ 'match' ].items():
1110 w.var_matches[ str( k ) ] = tuple( [ str(x) for x in v ] )
1112 def _initDCWorkflowScripts( workflow, scripts, context ):
1114 """ Initialize DCWorkflow scripts
1115 """
1116 for s_info in scripts:
1118 id = str( s_info[ 'script_id' ] ) # no unicode!
1119 meta_type = s_info[ 'meta_type' ]
1120 filename = s_info[ 'filename' ]
1121 file = ''
1123 if filename:
1124 file = context.readDataFile( filename )
1126 if meta_type == PythonScript.meta_type:
1127 script = PythonScript( id )
1128 script.write( file )
1130 elif meta_type == ExternalMethod.meta_type:
1131 script = ExternalMethod( id
1132 , ''
1133 , s_info['module']
1134 , s_info['function']
1137 elif meta_type == DTMLMethod.meta_type:
1138 script = DTMLMethod( file, __name__=id )
1140 if workflow.scripts.has_key(id):
1141 workflow.scripts._delObject(id)
1142 workflow.scripts._setObject( id, script )
1145 # deprecated DOM parsing utilities
1147 _marker = object()
1149 def _queryNodeAttribute( node, attr_name, default, encoding=None ):
1151 """ Extract a string-valued attribute from node.
1153 o Return 'default' if the attribute is not present.
1154 """
1155 attr_node = node.attributes.get( attr_name, _marker )
1157 if attr_node is _marker:
1158 return default
1160 value = attr_node.nodeValue
1162 if encoding is not None:
1163 value = value.encode( encoding )
1165 return value
1167 def _getNodeAttribute( node, attr_name, encoding=None ):
1169 """ Extract a string-valued attribute from node.
1170 """
1171 value = _queryNodeAttribute( node, attr_name, _marker, encoding )
1173 if value is _marker:
1174 raise ValueError, 'Invalid attribute: %s' % attr_name
1176 return value
1178 def _queryNodeAttributeBoolean( node, attr_name, default ):
1180 """ Extract a string-valued attribute from node.
1182 o Return 'default' if the attribute is not present.
1183 """
1184 attr_node = node.attributes.get( attr_name, _marker )
1186 if attr_node is _marker:
1187 return default
1189 value = node.attributes[ attr_name ].nodeValue.lower()
1191 return value in ( 'true', 'yes', '1' )
1193 def _getNodeAttributeBoolean( node, attr_name ):
1195 """ Extract a string-valued attribute from node.
1196 """
1197 value = node.attributes[ attr_name ].nodeValue.lower()
1199 return value in ( 'true', 'yes', '1' )
1201 def _coalesceTextNodeChildren( node, encoding=None ):
1203 """ Concatenate all childe text nodes into a single string.
1204 """
1205 from xml.dom import Node
1206 fragments = []
1207 node.normalize()
1208 child = node.firstChild
1210 while child is not None:
1212 if child.nodeType == Node.TEXT_NODE:
1213 fragments.append( child.nodeValue )
1215 child = child.nextSibling
1217 joined = ''.join( fragments )
1219 if encoding is not None:
1220 joined = joined.encode( encoding )
1222 return ''.join( [ line.lstrip() for line in joined.splitlines(True) ] )
1224 def _extractDescriptionNode(parent, encoding=None):
1226 d_nodes = parent.getElementsByTagName('description')
1227 if d_nodes:
1228 return _coalesceTextNodeChildren(d_nodes[0], encoding)
1229 else:
1230 return ''