[Checkins] SVN: Products.DCWorkflow/trunk/Products/DCWorkflow/ Fix LP #707927:
Tres Seaver
tseaver at palladion.com
Mon Feb 14 11:17:49 EST 2011
Log message for revision 120334:
Fix LP #707927:
- Hardened XML import parsing against missing boolean attributes.
- Ensured that emitted XML export has a valid encoding, even when passed
'None'.
Changed:
U Products.DCWorkflow/trunk/Products/DCWorkflow/CHANGES.txt
U Products.DCWorkflow/trunk/Products/DCWorkflow/exportimport.py
U Products.DCWorkflow/trunk/Products/DCWorkflow/tests/test_exportimport.py
U Products.DCWorkflow/trunk/Products/DCWorkflow/xml/wtcWorkflowExport.xml
-=-
Modified: Products.DCWorkflow/trunk/Products/DCWorkflow/CHANGES.txt
===================================================================
--- Products.DCWorkflow/trunk/Products/DCWorkflow/CHANGES.txt 2011-02-14 11:42:30 UTC (rev 120333)
+++ Products.DCWorkflow/trunk/Products/DCWorkflow/CHANGES.txt 2011-02-14 16:17:48 UTC (rev 120334)
@@ -4,8 +4,14 @@
2.3.0-alpha (unreleased)
------------------------
-- Fixed Chameleon compatibility in `state_groups.pt`.
+- Hardened XML import parsing against missing boolean attributes.
+ (https://bugs.launchpad.net/zope-cmf/+bug/707927)
+- Ensured that emitted XML export has a valid encoding, even when passed
+ 'None'. (https://bugs.launchpad.net/zope-cmf/+bug/707927)
+
+- Fixed Chameleon compatibility in 'state_groups.pt'.
+
- Workflow states cannot be renamed through the ZMI.
(https://bugs.launchpad.net/zope-cmf/+bug/625722)
Modified: Products.DCWorkflow/trunk/Products/DCWorkflow/exportimport.py
===================================================================
--- Products.DCWorkflow/trunk/Products/DCWorkflow/exportimport.py 2011-02-14 11:42:30 UTC (rev 120333)
+++ Products.DCWorkflow/trunk/Products/DCWorkflow/exportimport.py 2011-02-14 16:17:48 UTC (rev 120334)
@@ -678,7 +678,7 @@
for p_map in s_node.getElementsByTagName( 'permission-map' ):
name = _getNodeAttribute( p_map, 'name', encoding )
- acquired = _getNodeAttributeBoolean( p_map, 'acquired' )
+ acquired = _queryNodeAttributeBoolean( p_map, 'acquired', False )
roles = [ _coalesceTextNodeChildren( x, encoding )
for x in p_map.getElementsByTagName(
@@ -760,15 +760,18 @@
info = { 'variable_id' : _getNodeAttribute( v_node, 'variable_id'
, encoding )
, 'description' : _extractDescriptionNode( v_node, encoding )
- , 'for_catalog' : _getNodeAttributeBoolean( v_node
- , 'for_catalog'
- )
- , 'for_status' : _getNodeAttributeBoolean( v_node
- , 'for_status'
- )
- , 'update_always' : _getNodeAttributeBoolean( v_node
- , 'update_always'
+ , 'for_catalog' : _queryNodeAttributeBoolean( v_node
+ , 'for_catalog'
+ , False
)
+ , 'for_status' : _queryNodeAttributeBoolean( v_node
+ , 'for_status'
+ , False
+ )
+ , 'update_always' : _queryNodeAttributeBoolean( v_node
+ , 'update_always'
+ , False
+ )
, 'default' : _extractDefaultNode( v_node, encoding )
, 'guard' : _extractGuardNode( v_node, encoding )
}
Modified: Products.DCWorkflow/trunk/Products/DCWorkflow/tests/test_exportimport.py
===================================================================
--- Products.DCWorkflow/trunk/Products/DCWorkflow/tests/test_exportimport.py 2011-02-14 11:42:30 UTC (rev 120333)
+++ Products.DCWorkflow/trunk/Products/DCWorkflow/tests/test_exportimport.py 2011-02-14 16:17:48 UTC (rev 120334)
@@ -730,6 +730,58 @@
elif isinstance( exp_value, basestring ):
self.assertEqual( v_info[ 'type' ], 'string' )
+ def test_parseWorkflowXML_state_w_missing_acquired( self ):
+
+ WF_ID = 'missing_acquired'
+ WF_TITLE = 'DCWorkflow w/o acquired on state'
+ WF_DESCRIPTION = WF_TITLE
+ WF_INITIAL_STATE = 'closed'
+
+ site = self._initSite()
+
+ configurator = self._makeOne( site ).__of__( site )
+
+ ( workflow_id
+ , title
+ , state_variable
+ , initial_state
+ , states
+ , transitions
+ , variables
+ , worklists
+ , permissions
+ , scripts
+ , description
+ , manager_bypass
+ , creation_guard
+ ) = configurator.parseWorkflowXML(
+ _WORKFLOW_EXPORT_WO_ACQUIRED
+ % { 'workflow_id' : WF_ID
+ , 'title' : WF_TITLE
+ , 'description' : WF_DESCRIPTION
+ , 'initial_state' : WF_INITIAL_STATE
+ , 'workflow_filename' : WF_ID.replace(' ', '_')
+ } )
+
+ self.assertEqual( len( states ), len( _WF_STATES_MISSING_ACQUIRED ) )
+
+ for state in states:
+
+ state_id = state[ 'state_id' ]
+ self.failUnless( state_id in _WF_STATES_MISSING_ACQUIRED )
+
+ expected = _WF_STATES_MISSING_ACQUIRED[ state_id ]
+
+ self.assertEqual( state[ 'title' ], expected[ 0 ] )
+
+ description = ''.join( state[ 'description' ] )
+ self.failUnless( expected[ 1 ] in description )
+
+ self.assertEqual( tuple( state[ 'transitions' ] ), expected[ 2 ] )
+ self.assertEqual( state[ 'permissions' ], expected[ 3 ] )
+ self.assertEqual( tuple( state[ 'groups' ] )
+ , tuple( expected[ 4 ] ) )
+
def test_parseWorkflowXML_normal_transitions( self ):
from Products.DCWorkflow.exportimport import TRIGGER_TYPES
@@ -883,6 +935,86 @@
, expected[ 8 ] )
self.assertEqual( guard.get( 'expression', '' ), expected[ 9 ] )
+ def test_parseWorkflowXML_w_variables_missing_attrs( self ):
+
+ WF_ID = 'normal'
+ WF_TITLE = 'DCWorkflow w/ missing attrs'
+ WF_DESCRIPTION = WF_TITLE
+ WF_INITIAL_STATE = 'closed'
+
+ site = self._initSite()
+
+ configurator = self._makeOne( site ).__of__( site )
+
+ ( workflow_id
+ , title
+ , state_variable
+ , initial_state
+ , states
+ , transitions
+ , variables
+ , worklists
+ , permissions
+ , scripts
+ , description
+ , manager_bypass
+ , creation_guard
+ ) = configurator.parseWorkflowXML(
+ _WORKFLOW_EXPORT_W_MISSING_VARIABLE_ATTRS
+ % { 'workflow_id' : WF_ID
+ , 'title' : WF_TITLE
+ , 'description' : WF_DESCRIPTION
+ , 'initial_state' : WF_INITIAL_STATE
+ , 'workflow_filename' : WF_ID.replace(' ', '_')
+ } )
+
+ self.assertEqual( len( variables ), len( _WF_VARIABLES_MISSING_ATTRS ) )
+
+ for variable in variables:
+
+ variable_id = variable[ 'variable_id' ]
+ self.failUnless( variable_id in _WF_VARIABLES_MISSING_ATTRS )
+
+ expected = _WF_VARIABLES_MISSING_ATTRS[ variable_id ]
+
+ description = ''.join( variable[ 'description' ] )
+ self.failUnless( expected[ 0 ] in description )
+
+ default = variable[ 'default' ]
+ self.assertEqual( default[ 'value' ], expected[ 1 ] )
+
+ exp_type = 'n/a'
+
+ if expected[ 1 ]:
+ exp_value = expected[ 1 ]
+
+ if isinstance( exp_value, bool ):
+ exp_type = 'bool'
+ elif isinstance( exp_value, int ):
+ exp_type = 'int'
+ elif isinstance( exp_value, float ):
+ exp_type = 'float'
+ elif isinstance( exp_value, basestring ):
+ exp_type = 'string'
+ else:
+ exp_type = 'XXX'
+
+ self.assertEqual( default[ 'type' ], exp_type )
+ self.assertEqual( default[ 'expression' ], expected[ 2 ] )
+
+ self.assertEqual( variable[ 'for_catalog' ], expected[ 3 ] )
+ self.assertEqual( variable[ 'for_status' ], expected[ 4 ] )
+ self.assertEqual( variable[ 'update_always' ], expected[ 5 ] )
+
+ guard = variable[ 'guard' ]
+ self.assertEqual( tuple( guard.get( 'permissions', () ) )
+ , expected[ 6 ] )
+ self.assertEqual( tuple( guard.get( 'roles', () ) )
+ , expected[ 7 ] )
+ self.assertEqual( tuple( guard.get( 'groups', () ) )
+ , expected[ 8 ] )
+ self.assertEqual( guard.get( 'expression', '' ), expected[ 9 ] )
+
def test_parseWorkflowXML_normal_worklists( self ):
WF_ID = 'normal'
@@ -1088,6 +1220,42 @@
)
}
+_WF_VARIABLES_MISSING_ATTRS = \
+{ 'when_opened': ( 'Opened when'
+ , ''
+ , "python:None"
+ , False
+ , False
+ , False
+ , ( 'Query history', 'Open content for modifications' )
+ , ()
+ , ()
+ , ""
+ )
+, 'when_expired': ( 'Expired when'
+ , ''
+ , "nothing"
+ , False
+ , False
+ , False
+ , ( 'Query history', 'Open content for modifications' )
+ , ()
+ , ()
+ , ""
+ )
+, 'killed_by': ( 'Killed by'
+ , 'n/a'
+ , ""
+ , False
+ , False
+ , False
+ , ()
+ , ( 'Hangman', 'Sherrif' )
+ , ()
+ , ""
+ )
+}
+
_WF_STATES = \
{ 'closed': ( 'Closed'
, 'Closed for modifications'
@@ -1119,6 +1287,37 @@
)
}
+_WF_STATES_MISSING_ACQUIRED = \
+{ 'closed': ( 'Closed'
+ , 'Closed for modifications'
+ , ( 'open', 'kill', 'expire' )
+ , { 'Modify content': () }
+ , ()
+ , { 'is_opened': False, 'is_closed': True }
+ )
+, 'opened': ( 'Opened'
+ , 'Open for modifications'
+ , ( 'close', 'kill', 'expire' )
+ , { 'Modify content': ( 'Owner', 'Manager' ) }
+ , [ ( 'Content_owners', ( 'Owner', ) ) ]
+ , { 'is_opened': True, 'is_closed': False }
+ )
+, 'killed': ( 'Killed'
+ , 'Permanently unavailable'
+ , ()
+ , {}
+ , ()
+ , {}
+ )
+, 'expired': ( 'Expired'
+ , 'Expiration date has passed'
+ , ( 'open', )
+ , { 'Modify content': ( 'Owner', 'Manager' ) }
+ , ()
+ , { 'is_opened': False, 'is_closed': False }
+ )
+}
+
_WF_TRANSITIONS = \
{ 'open': ( 'Open'
, 'Open the object for modifications'
@@ -1829,6 +2028,494 @@
</dc-workflow>
"""
+_WORKFLOW_EXPORT_WO_ACQUIRED = """\
+<?xml version="1.0"?>
+<dc-workflow
+ workflow_id="%(workflow_id)s"
+ title="%(title)s"
+ description="%(description)s"
+ state_variable="state"
+ initial_state="%(initial_state)s"
+ manager_bypass="False">
+ <permission>Open content for modifications</permission>
+ <permission>Modify content</permission>
+ <permission>Query history</permission>
+ <permission>Restore expired content</permission>
+ <state
+ state_id="closed"
+ title="Closed">
+ <description>Closed for modifications</description>
+ <exit-transition
+ transition_id="open"/>
+ <exit-transition
+ transition_id="kill"/>
+ <exit-transition
+ transition_id="expire"/>
+ <permission-map
+ name="Modify content">
+ </permission-map>
+ <assignment
+ name="is_closed"
+ type="bool">True</assignment>
+ <assignment
+ name="is_opened"
+ type="bool">False</assignment>
+ </state>
+ <state
+ state_id="expired"
+ title="Expired">
+ <description>Expiration date has passed</description>
+ <exit-transition
+ transition_id="open"/>
+ <permission-map
+ name="Modify content">
+ <permission-role>Owner</permission-role>
+ <permission-role>Manager</permission-role>
+ </permission-map>
+ <assignment
+ name="is_closed"
+ type="bool">False</assignment>
+ <assignment
+ name="is_opened"
+ type="bool">False</assignment>
+ </state>
+ <state
+ state_id="killed"
+ title="Killed">
+ <description>Permanently unavailable</description>
+ </state>
+ <state
+ state_id="opened"
+ title="Opened">
+ <description>Open for modifications</description>
+ <exit-transition
+ transition_id="close"/>
+ <exit-transition
+ transition_id="kill"/>
+ <exit-transition
+ transition_id="expire"/>
+ <permission-map
+ name="Modify content">
+ <permission-role>Owner</permission-role>
+ <permission-role>Manager</permission-role>
+ </permission-map>
+ <group-map name="Content_owners">
+ <group-role>Owner</group-role>
+ </group-map>
+ <assignment
+ name="is_closed"
+ type="bool">False</assignment>
+ <assignment
+ name="is_opened"
+ type="bool">True</assignment>
+ </state>
+ <transition
+ transition_id="close"
+ title="Close"
+ trigger="USER"
+ new_state="closed"
+ before_script=""
+ after_script="after_close">
+ <description>Close the object for modifications</description>
+ <action
+ category="workflow"
+ url="string:${object_url}/close_for_modifications"
+ icon="string:${portal_url}/close.png">Close</action>
+ <guard>
+ <guard-role>Owner</guard-role>
+ <guard-role>Manager</guard-role>
+ </guard>
+ </transition>
+ <transition
+ transition_id="expire"
+ title="Expire"
+ trigger="AUTOMATIC"
+ new_state="expired"
+ before_script="before_expire"
+ after_script="">
+ <description>Retire objects whose expiration is past.</description>
+ <guard>
+ <guard-expression>python: object.expiration() <= object.ZopeTime()</guard-expression>
+ </guard>
+ <assignment
+ name="when_expired">object/ZopeTime</assignment>
+ </transition>
+ <transition
+ transition_id="kill"
+ title="Kill"
+ trigger="USER"
+ new_state="killed"
+ before_script=""
+ after_script="after_kill">
+ <description>Make the object permanently unavailable.</description>
+ <action
+ category="workflow"
+ url="string:${object_url}/kill_object"
+ icon="string:${portal_url}/kill.png">Kill</action>
+ <guard>
+ <guard-group>Content_assassins</guard-group>
+ </guard>
+ <assignment
+ name="killed_by">string:${user/getId}</assignment>
+ </transition>
+ <transition
+ transition_id="open"
+ title="Open"
+ trigger="USER"
+ new_state="opened"
+ before_script="before_open"
+ after_script="">
+ <description>Open the object for modifications</description>
+ <action
+ category="workflow"
+ url="string:${object_url}/open_for_modifications"
+ icon="string:${portal_url}/open.png">Open</action>
+ <guard>
+ <guard-permission>Open content for modifications</guard-permission>
+ </guard>
+ <assignment
+ name="when_opened">object/ZopeTime</assignment>
+ </transition>
+ <worklist
+ worklist_id="alive_list"
+ title="Alive">
+ <description>Worklist for content not yet expired / killed</description>
+ <action
+ category="workflow"
+ url="string:${portal_url}/expired_items"
+ icon="string:${portal_url}/alive.png">Expired items</action>
+ <guard>
+ <guard-permission>Restore expired content</guard-permission>
+ </guard>
+ <match name="state" values="open; closed"/>
+ </worklist>
+ <worklist
+ worklist_id="expired_list"
+ title="Expired">
+ <description>Worklist for expired content</description>
+ <action
+ category="workflow"
+ url="string:${portal_url}/expired_items"
+ icon="string:${portal_url}/expired.png">Expired items</action>
+ <guard>
+ <guard-permission>Restore expired content</guard-permission>
+ </guard>
+ <match name="state" values="expired"/>
+ </worklist>
+ <variable
+ variable_id="killed_by"
+ for_catalog="True"
+ for_status="False"
+ update_always="True">
+ <description>Killed by</description>
+ <default>
+ <value type="string">n/a</value>
+ </default>
+ <guard>
+ <guard-role>Hangman</guard-role>
+ <guard-role>Sherrif</guard-role>
+ </guard>
+ </variable>
+ <variable
+ variable_id="when_expired"
+ for_catalog="True"
+ for_status="False"
+ update_always="True">
+ <description>Expired when</description>
+ <default>
+ <expression>nothing</expression>
+ </default>
+ <guard>
+ <guard-permission>Query history</guard-permission>
+ <guard-permission>Open content for modifications</guard-permission>
+ </guard>
+ </variable>
+ <variable
+ variable_id="when_opened"
+ for_catalog="True"
+ for_status="False"
+ update_always="True">
+ <description>Opened when</description>
+ <default>
+ <expression>python:None</expression>
+ </default>
+ <guard>
+ <guard-permission>Query history</guard-permission>
+ <guard-permission>Open content for modifications</guard-permission>
+ </guard>
+ </variable>
+ <script
+ script_id="after_close"
+ type="Script (Python)"
+ filename="workflows/%(workflow_filename)s/scripts/after_close.py"
+ module=""
+ function=""
+ />
+ <script
+ script_id="after_kill"
+ type="Script (Python)"
+ filename="workflows/%(workflow_filename)s/scripts/after_kill.py"
+ module=""
+ function=""
+ />
+ <script
+ script_id="before_expire"
+ type="External Method"
+ filename=""
+ module="DCWorkflow.test_method"
+ function="test"
+ />
+ <script
+ script_id="before_open"
+ type="Script (Python)"
+ filename="workflows/%(workflow_filename)s/scripts/before_open.py"
+ module=""
+ function=""
+ />
+</dc-workflow>
+"""
+
+_WORKFLOW_EXPORT_W_MISSING_VARIABLE_ATTRS = """\
+<?xml version="1.0"?>
+<dc-workflow
+ workflow_id="%(workflow_id)s"
+ title="%(title)s"
+ description="%(description)s"
+ state_variable="state"
+ initial_state="%(initial_state)s"
+ manager_bypass="False">
+ <permission>Open content for modifications</permission>
+ <permission>Modify content</permission>
+ <permission>Query history</permission>
+ <permission>Restore expired content</permission>
+ <state
+ state_id="closed"
+ title="Closed">
+ <description>Closed for modifications</description>
+ <exit-transition
+ transition_id="open"/>
+ <exit-transition
+ transition_id="kill"/>
+ <exit-transition
+ transition_id="expire"/>
+ <permission-map
+ acquired="False"
+ name="Modify content">
+ </permission-map>
+ <assignment
+ name="is_closed"
+ type="bool">True</assignment>
+ <assignment
+ name="is_opened"
+ type="bool">False</assignment>
+ </state>
+ <state
+ state_id="expired"
+ title="Expired">
+ <description>Expiration date has passed</description>
+ <exit-transition
+ transition_id="open"/>
+ <permission-map
+ acquired="True"
+ name="Modify content">
+ <permission-role>Owner</permission-role>
+ <permission-role>Manager</permission-role>
+ </permission-map>
+ <assignment
+ name="is_closed"
+ type="bool">False</assignment>
+ <assignment
+ name="is_opened"
+ type="bool">False</assignment>
+ </state>
+ <state
+ state_id="killed"
+ title="Killed">
+ <description>Permanently unavailable</description>
+ </state>
+ <state
+ state_id="opened"
+ title="Opened">
+ <description>Open for modifications</description>
+ <exit-transition
+ transition_id="close"/>
+ <exit-transition
+ transition_id="kill"/>
+ <exit-transition
+ transition_id="expire"/>
+ <permission-map
+ acquired="True"
+ name="Modify content">
+ <permission-role>Owner</permission-role>
+ <permission-role>Manager</permission-role>
+ </permission-map>
+ <group-map name="Content_owners">
+ <group-role>Owner</group-role>
+ </group-map>
+ <assignment
+ name="is_closed"
+ type="bool">False</assignment>
+ <assignment
+ name="is_opened"
+ type="bool">True</assignment>
+ </state>
+ <transition
+ transition_id="close"
+ title="Close"
+ trigger="USER"
+ new_state="closed"
+ before_script=""
+ after_script="after_close">
+ <description>Close the object for modifications</description>
+ <action
+ category="workflow"
+ url="string:${object_url}/close_for_modifications"
+ icon="string:${portal_url}/close.png">Close</action>
+ <guard>
+ <guard-role>Owner</guard-role>
+ <guard-role>Manager</guard-role>
+ </guard>
+ </transition>
+ <transition
+ transition_id="expire"
+ title="Expire"
+ trigger="AUTOMATIC"
+ new_state="expired"
+ before_script="before_expire"
+ after_script="">
+ <description>Retire objects whose expiration is past.</description>
+ <guard>
+ <guard-expression>python: object.expiration() <= object.ZopeTime()</guard-expression>
+ </guard>
+ <assignment
+ name="when_expired">object/ZopeTime</assignment>
+ </transition>
+ <transition
+ transition_id="kill"
+ title="Kill"
+ trigger="USER"
+ new_state="killed"
+ before_script=""
+ after_script="after_kill">
+ <description>Make the object permanently unavailable.</description>
+ <action
+ category="workflow"
+ url="string:${object_url}/kill_object"
+ icon="string:${portal_url}/kill.png">Kill</action>
+ <guard>
+ <guard-group>Content_assassins</guard-group>
+ </guard>
+ <assignment
+ name="killed_by">string:${user/getId}</assignment>
+ </transition>
+ <transition
+ transition_id="open"
+ title="Open"
+ trigger="USER"
+ new_state="opened"
+ before_script="before_open"
+ after_script="">
+ <description>Open the object for modifications</description>
+ <action
+ category="workflow"
+ url="string:${object_url}/open_for_modifications"
+ icon="string:${portal_url}/open.png">Open</action>
+ <guard>
+ <guard-permission>Open content for modifications</guard-permission>
+ </guard>
+ <assignment
+ name="when_opened">object/ZopeTime</assignment>
+ </transition>
+ <worklist
+ worklist_id="alive_list"
+ title="Alive">
+ <description>Worklist for content not yet expired / killed</description>
+ <action
+ category="workflow"
+ url="string:${portal_url}/expired_items"
+ icon="string:${portal_url}/alive.png">Expired items</action>
+ <guard>
+ <guard-permission>Restore expired content</guard-permission>
+ </guard>
+ <match name="state" values="open; closed"/>
+ </worklist>
+ <worklist
+ worklist_id="expired_list"
+ title="Expired">
+ <description>Worklist for expired content</description>
+ <action
+ category="workflow"
+ url="string:${portal_url}/expired_items"
+ icon="string:${portal_url}/expired.png">Expired items</action>
+ <guard>
+ <guard-permission>Restore expired content</guard-permission>
+ </guard>
+ <match name="state" values="expired"/>
+ </worklist>
+ <variable
+ variable_id="killed_by">
+ <description>Killed by</description>
+ <default>
+ <value type="string">n/a</value>
+ </default>
+ <guard>
+ <guard-role>Hangman</guard-role>
+ <guard-role>Sherrif</guard-role>
+ </guard>
+ </variable>
+ <variable
+ variable_id="when_expired">
+ <description>Expired when</description>
+ <default>
+ <expression>nothing</expression>
+ </default>
+ <guard>
+ <guard-permission>Query history</guard-permission>
+ <guard-permission>Open content for modifications</guard-permission>
+ </guard>
+ </variable>
+ <variable
+ variable_id="when_opened">
+ <description>Opened when</description>
+ <default>
+ <expression>python:None</expression>
+ </default>
+ <guard>
+ <guard-permission>Query history</guard-permission>
+ <guard-permission>Open content for modifications</guard-permission>
+ </guard>
+ </variable>
+ <script
+ script_id="after_close"
+ type="Script (Python)"
+ filename="workflows/%(workflow_filename)s/scripts/after_close.py"
+ module=""
+ function=""
+ />
+ <script
+ script_id="after_kill"
+ type="Script (Python)"
+ filename="workflows/%(workflow_filename)s/scripts/after_kill.py"
+ module=""
+ function=""
+ />
+ <script
+ script_id="before_expire"
+ type="External Method"
+ filename=""
+ module="DCWorkflow.test_method"
+ function="test"
+ />
+ <script
+ script_id="before_open"
+ type="Script (Python)"
+ filename="workflows/%(workflow_filename)s/scripts/before_open.py"
+ module=""
+ function=""
+ />
+</dc-workflow>
+"""
+
_CREATION_GUARD_WORKFLOW_EXPORT = """\
<?xml version="1.0"?>
<dc-workflow
Modified: Products.DCWorkflow/trunk/Products/DCWorkflow/xml/wtcWorkflowExport.xml
===================================================================
--- Products.DCWorkflow/trunk/Products/DCWorkflow/xml/wtcWorkflowExport.xml 2011-02-14 11:42:30 UTC (rev 120333)
+++ Products.DCWorkflow/trunk/Products/DCWorkflow/xml/wtcWorkflowExport.xml 2011-02-14 16:17:48 UTC (rev 120334)
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<dc-workflow
xmlns:tal="http://xml.zope.org/namespaces/tal"
workflow_id="dcworkflow"
More information about the checkins
mailing list