Commit df85ef46 authored by Arnaud Fontaine's avatar Arnaud Fontaine
Browse files

ERP5Workflow: DC Workflows are now ERP5 objects (!1378).

This also moves all Configurator Workflows in workflow_module to portal_workflow
(workflow_module was an implementation of Workflows based on ERP5 objects and
not using DCWorkflow code).

* Workflows are now defined on on portal_workflow._chains_by_type anymore but,
  as everything else, on the Portal Type itself.
* portal_workflow can contain and work at the same time with legacy and new
  Workflows (ERP5Type/patches/DCWorkflow.py monkey-patching DCWorkflow classes
  to provide the same API).
* Existing Workflow Scripts should work as they are and the code can be updated
  later on to take advantage of the new API:
  + With legacy implementation Workflow {Scripts,Transitions,Worklists,States}
    were in a Folder ({scripts,transitions,worklists,states} attribute) but
    all of these are now in the Workflow itself and their IDs are prefixed
    (PropertySheet-style), for example `script_`. Legacy attributes are
    provided in new implementation to call the new API.
  + When calling a Workflow Script, `container` was bound to its parent, namely
    WF.scripts (Folder) and a Workflow Script could call another. Now `container`
    is bound to the WF itself and Workflow Scripts are in a Workflow directly.
    New implementation `scripts` attribute handle such use case.
  + Override portal_workflow.__getattr__ so that a Workflow Script can call
    another one without prefix.
* Worklist are Predicate: Worklist filter objects based on given criterions and
  thus it makes more sense for a Worklist to be a Predicate (albeit a Predicate
  with only Identity Criterion and nothing else).
  + Criterion Properties:
    * state_variable.
    * local_roles (SECURITY_PARAMETER_ID).
    * Any Workflow Variables with for_catalog == 1.

erp5_performance_test:testWorkflowPerformance were ran to compare DCWorkflow
and ERP5Workflow implementations and it seems to be about 4% slower with the
new implementation (legacy: 7.547, 7.593, 7.618, 7.59, 7.514 and new: 7.842,
7.723, 7.902, 7.837, 7.875).

Work done by Wenjie Zheng, Isabelle Vallet, Sebastien Robin and myself.
parent 9c73f05b
......@@ -35,7 +35,7 @@ from Products.CMFCore.utils import _checkPermission
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from Products.ERP5Type.tests.utils import reindex
from Products.DCWorkflow.DCWorkflow import ValidationFailed
from Products.ERP5Type.Core.Workflow import ValidationFailed
from AccessControl.SecurityManagement import newSecurityManager
from Products.ERP5Type.tests.Sequence import SequenceList
from Products.ERP5Form.PreferenceTool import Priority
......
from Products.DCWorkflow.DCWorkflow import ValidationFailed
from Products.ERP5Type.Core.Workflow import ValidationFailed
from Products.ERP5Type.Message import translateString
closing_period = state_change['object']
......
from Products.DCWorkflow.DCWorkflow import ValidationFailed
from Products.ERP5Type.Core.Workflow import ValidationFailed
from Products.ERP5Type.Message import translateString
period = state_change['object']
......
......@@ -2,7 +2,7 @@
XXX why proxy role ???
"""
from Products.DCWorkflow.DCWorkflow import ValidationFailed
from Products.ERP5Type.Core.Workflow import ValidationFailed
from Products.ERP5Type.Message import translateString
transaction = state_change['object']
......
......@@ -3,7 +3,7 @@
XXX why proxy role ???
"""
from Products.DCWorkflow.DCWorkflow import ValidationFailed
from Products.ERP5Type.Core.Workflow import ValidationFailed
from Products.ERP5Type.Message import translateString
transaction = state_change['object']
......
from Products.DCWorkflow.DCWorkflow import ValidationFailed
from Products.ERP5Type.Core.Workflow import ValidationFailed
from Products.ERP5Type.Message import translateString
internal_invoice = state_change['object']
......
......@@ -23,7 +23,6 @@ def dumpWorkflowChain(self, ignore_default=False,
if ignore_id_set is None:
ignore_id_set = set()
workflow_tool = self.getPortalObject().portal_workflow
cbt = workflow_tool._chains_by_type
ti = workflow_tool._listTypeInfo()
types_info = []
for t in ti:
......@@ -32,15 +31,14 @@ def dumpWorkflowChain(self, ignore_default=False,
if title == id_:
title = None
chain = None
if cbt is not None and cbt.has_key(id_):
cbt_list = [x for x in cbt[id_] if not(x in ignore_id_set)]
if keep_order:
chain = cbt_list
else:
chain = sorted(cbt_list)
else:
cbt_list = [x for x in t.getTypeWorkflowList() if not(x in ignore_id_set)]
if not cbt_list:
if not(ignore_default):
chain = ['(Default)']
elif keep_order:
chain = cbt_list
else:
chain = sorted(cbt_list)
if chain:
types_info.append({'id': id_,
'title': title,
......
from Products.ERP5Type.Message import Message
from Products.DCWorkflow.DCWorkflow import ValidationFailed
from Products.ERP5Type.Core.Workflow import ValidationFailed
inventory = state_change['object']
......
from Products.DCWorkflow.DCWorkflow import ValidationFailed
from Products.ERP5Type.Core.Workflow import ValidationFailed
from Products.ERP5Type.Message import Message
# Check new catalog or catalog is the same as previous archive
......
......@@ -32,7 +32,7 @@ import unittest
from DateTime import DateTime
from Products.DCWorkflow.DCWorkflow import ValidationFailed
from Products.ERP5Type.Core.Workflow import ValidationFailed
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5ReportTestCase
from erp5.component.test.testAccounting import AccountingTestCase
......
......@@ -35,7 +35,7 @@ from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
from erp5.component.mixin.EncryptedPasswordMixin import EncryptedPasswordMixin
from erp5.component.mixin.LoginAccountProviderMixin import LoginAccountProviderMixin
from erp5.component.mixin.ERP5UserMixin import ERP5UserMixin
from Products.DCWorkflow.DCWorkflow import ValidationFailed
from Products.ERP5Type.Core.Workflow import ValidationFailed
from Products.CMFCore.utils import _checkPermission
from Products.CMFCore.exceptions import AccessControl_Unauthorized
......
......@@ -5,7 +5,7 @@
# In this case we want to be sure that open assignments share the same site category.
# XXX
from Products.DCWorkflow.DCWorkflow import ValidationFailed
from Products.ERP5Type.Core.Workflow import ValidationFailed
# Get the assignment object and its parent
assignment_object = state_change['object']
......
# XXX: Duplicates Base_checkConsistency so proxy role is really effective.
from Products.DCWorkflow.DCWorkflow import ValidationFailed
from Products.ERP5Type.Core.Workflow import ValidationFailed
message_list = [x.getTranslatedMessage() for x in state_change['object'].checkConsistency()]
if message_list:
raise ValidationFailed(message_list)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_view</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_view</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>configurator_settings</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>10.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Configurator</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/WorkflowTransition_viewConfigurator</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
##############################################################################
#
# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved.
# Romain Courteaud <romain@nexedi.com>
# Ivan Tyagov <ivan@nexedi.com>
......@@ -32,6 +31,7 @@ from AccessControl import ClassSecurityInfo
from Persistence import PersistentMapping
from Acquisition import aq_base
from Products.ERP5Type import Permissions, PropertySheet
from zLOG import LOG, ERROR
from erp5.component.tool.ConfiguratorTool import _validateFormToRequest
from erp5.component.document.Item import Item
......@@ -41,6 +41,203 @@ INITIAL_STATE_TITLE = 'Start'
DOWNLOAD_STATE_TITLE = 'Download'
END_STATE_TITLE = 'End'
## Methods defined in Configurator Workflow (workflow_module) implementation
## but not needed for generic ERP5 Workflow (portal_workflow) implementation
def initializeDocument(workflow, document):
"""
Set initial state on the Document
"""
state_bc_id = workflow.getStateBaseCategory()
document.setCategoryMembership(state_bc_id, workflow.getSource())
obj = workflow.getStateChangeInformation(document, workflow.getSourceValue())
# Initialize workflow history
status_dict = {state_bc_id: workflow.getSource()}
for variable in workflow.getVariableValueList():
status_dict[variable.getTitle()] = variable.getVariableDefaultExpression(object=obj)
_updateWorkflowHistory(workflow, document, status_dict)
def _generateHistoryKey(workflow):
"""
Generate a key used in the workflow history.
"""
return workflow.getReference()
def getWorkflowHistory(state, document, remove_undo=0, remove_not_displayed=0):
"""
Return history tuple
"""
wh = document.workflow_history[_generateHistoryKey(state.getParentValue())]
result = []
# Remove undo
if not remove_undo:
result = [x.copy() for x in wh]
else:
result = []
for x in wh:
if x.has_key('undo') and x['undo'] == 1:
result.pop()
else:
result.append(x.copy())
return result
def _updateWorkflowHistory(workflow, document, status_dict):
"""
Change the state of the object.
"""
if workflow.getPortalType() in ('Interaction Workflow', 'InteractionWorkflowDefinition'):
return
# Create history attributes if needed
if getattr(aq_base(document), 'workflow_history', None) is None:
document.workflow_history = PersistentMapping()
# XXX this _p_changed is apparently not necessary
document._p_changed = 1
# Add an entry for the workflow in the history
workflow_key = workflow.getReference()
if not document.workflow_history.has_key(workflow_key):
document.workflow_history[workflow_key] = ()
# Update history
document.workflow_history[workflow_key] += (status_dict,)
# XXX this _p_changed marks the document modified, but only the
# PersistentMapping is modified
# document._p_changed = 1
# XXX this _p_changed is apparently not necessary
#document.workflow_history._p_changed = 1
def undoTransition(state, document):
"""
Reverse previous transition
"""
workflow = state.getParentValue()
wh = getWorkflowHistory(state, document, remove_undo=1)
status_dict = wh[-2]
# Update workflow state
state_bc_id = state.getParentValue().getStateBaseCategory()
document.setCategoryMembership(
state_bc_id,
workflow.getStateValueByReference(status_dict[state_bc_id]).getRelativeUrl())
# Update workflow history
status_dict['undo'] = 1
_updateWorkflowHistory(workflow, document, status_dict)
# XXX
LOG("State, undo", ERROR, "Variable (like DateTime) need to be updated!")
def _checkPermission(transition, document):
"""
Check if transition is allowed.
"""
expr_value = transition.getGuardExpression(evaluate=0)
if expr_value is not None:
# do not use 'getGuardExpression' to calculate tales because
# it caches value which is bad. Instead do it manually
from Products.ERP5Type.Accessor.Base import _evaluateTales
value = _evaluateTales(document, expr_value)
else:
value = True
#print "CALC", expr_value, '-->', value
return value
def getAvailableTransitionList(state, document):
"""
Return available transitions only if they are accessible for the current document.
"""
result_list = []
for transition in state.getDestinationValueList():
value = _checkPermission(transition, document)
if value:
result_list.append(transition)
return result_list
class StateError(Exception):
"""
Must call only an available transition
"""
pass
def executeTransition(state, transition, document, form_kw=None):
"""
Execute transition on the object.
"""
if transition not in getAvailableTransitionList(state, document):
raise StateError
else:
execute(transition, document, form_kw=form_kw)
def _executeBeforeScript(self, document, form_kw=None):
"""
Execute pre transition script.
"""
if form_kw is None:
form_kw = {}
script_id = getattr(self.aq_base, 'before_script_id', None)
if script_id is not None:
script = getattr(document, script_id)
script(**form_kw)
def _changeState(self, document):
"""
Change the state of the object.
"""
state = self.getDestination()
if state is not None:
# Some transitions don't update the state
state_bc_id = self.getParentValue().getStateBaseCategory()
document.setCategoryMembership(state_bc_id, state)
def _executeAfterScript(self, document, form_kw=None):
"""
Execute post transition script.
"""
if form_kw is None:
form_kw = {}
script_id = getattr(self.aq_base, 'after_script_id', None)
if script_id is not None:
script = getattr(document, script_id)
script(**form_kw)
def execute(self, document, form_kw=None):
"""
Execute transition.
"""
workflow = self.getParentValue()
# Call the before script
_executeBeforeScript(self, document)
# Modify the state
_changeState(self, document)
# Get variable values
status_dict = workflow.getCurrentStatusDict(document)
status_dict['undo'] = 0
# Modify workflow history
state_bc_id = workflow.getStateBaseCategory()
state_object = document.unrestrictedTraverse(document.getCategoryMembershipList(state_bc_id)[0])
status_dict[state_bc_id] = state_object.getReference()
object_ = workflow.getStateChangeInformation(document, state_object, transition=self)
# Update all variables
expression_context = None
for variable in workflow.getVariableValueList():
if variable.getAutomaticUpdate():
# if we have it in form get it from there
# otherwise use default
variable_title = variable.getTitle()
if variable_title in form_kw:
status_dict[variable_title] = form_kw[variable_title]
else:
expression = variable.getVariableDefaultExpressionInstance()
if expression is not None:
if expression_context is None:
from Products.ERP5Type.Core.Workflow import createExpressionContext
from Products.DCWorkflow.Expression import StateChangeInfo
expression_context = createExpressionContext(StateChangeInfo(document, self, status_dict))
status_dict[variable_title] = expression(expression_context)
# Update all transition variables
if form_kw is not None:
object_.REQUEST.other.update(form_kw)
for variable in self.getTransitionVariableValueList():
status_dict[variable.getCausalityTitle()] = variable.getInitialValue(object=object_)
_updateWorkflowHistory(workflow, document, status_dict)
# Call the after script
_executeAfterScript(self, document, form_kw=form_kw)
class BusinessConfiguration(Item):
"""
BusinessConfiguration store the values enter by the wizard.
......@@ -93,15 +290,28 @@ class BusinessConfiguration(Item):
def initializeWorkflow(self):
""" Initialize Related Workflow"""
workflow = self.getResourceValue()
workflow_history = getattr(self, 'workflow_history', {})
if workflow is None:
return
if self.getResource() not in workflow_history:
workflow_history = getattr(self, 'workflow_history', {})
if workflow.getReference() not in workflow_history:
if len(self.objectValues("ERP5 Configuration Save")) > 0:
raise ValueError("Business Configuration Cannot be initialized, \
it contains one or more Configurator Save")
workflow.initializeDocument(self)
# XXX: `state_base_category` points to the state category (usually
# 'current_state'). Without this, getCurrentState() is None and nothing
# happens because getNextTransition() returns None in such case.
#
# But does the state category really need to be configured, or should
# state_base_category be removed and `current_state` used by default?
# * No field to modify `state_base_category`.
# * In erp5.git, it is always `current_state`.
# * Some code using directly `getCurrentState()`.
self.setCategoryMembership(workflow.getStateBaseCategory(),
workflow.getSource())
workflow.notifyCreated(self)
security.declareProtected(Permissions.View, 'getNextTransition')
def getNextTransition(self):
......@@ -109,7 +319,7 @@ class BusinessConfiguration(Item):
current_state = self.getCurrentStateValue()
if current_state is None:
return None
transition_list = current_state.getAvailableTransitionList(self)
transition_list = getAvailableTransitionList(current_state, self)
transition_number = len(transition_list)
if transition_number > 1:
raise TypeError("More than one transition is available.")
......@@ -150,7 +360,7 @@ class BusinessConfiguration(Item):
## Add some variables so we can get use them in workflow after scripts
form_kw['configuration_save_url'] = configuration_save.getRelativeUrl()
form_kw['transition'] = transition.getRelativeUrl()
current_state.executeTransition(transition, self, form_kw=form_kw)
executeTransition(current_state, transition, self, form_kw=form_kw)
security.declarePrivate('_displayNextForm')
def _displayNextForm(self, \
......@@ -240,16 +450,18 @@ class BusinessConfiguration(Item):
security.declarePrivate('_displayPreviousForm')
def _displayPreviousForm(self):
""" Render previous form using workflow history. """
workflow_history = self.getCurrentStateValue().getWorkflowHistory(self, remove_undo=1)
workflow_history = getWorkflowHistory(self.getCurrentStateValue(), self, remove_undo=1)
workflow_history.reverse()
for wh in workflow_history:
## go one step back
current_state = self.getCurrentStateValue()
current_state.undoTransition(self)
undoTransition(current_state, self)
if not wh['transition']:
raise ValueError("Empty URL for transition in workflow history.")
transition = self.unrestrictedTraverse(wh['transition'])
conf_save = self.unrestrictedTraverse(wh['configuration_save_url'])
## check if this transition can be shown to user ...
if transition._checkPermission(self) and \
if _checkPermission(transition, self) and \
transition.getTransitionFormId() is not None:
return self._displayNextForm(context=conf_save, transition=transition)
......@@ -267,19 +479,22 @@ class BusinessConfiguration(Item):
current_state = self.getCurrentStateValue()
transition = self.getNextTransition()
next_state = self.unrestrictedTraverse(transition.getDestination())
for wh in current_state.getWorkflowHistory(self):
if next_state == self.unrestrictedTraverse(wh['current_state']):
workflow = current_state.getParentValue()
for wh in getWorkflowHistory(current_state, self):
if next_state == workflow.getStateValueByReference(wh['current_state']):
configuration_save = self.unrestrictedTraverse(wh['configuration_save_url'])
return configuration_save
security.declarePrivate('_isAlreadyConfSaveInWorkflowHistory')
def _isAlreadyConfSaveInWorkflowHistory(self, transition):
""" check if we have an entry in worklow history for this state """
workflow_history = self.getCurrentStateValue().getWorkflowHistory(self, remove_undo=1)
current_state = self.getCurrentStateValue()
workflow = current_state.getParentValue()
workflow_history = getWorkflowHistory(current_state, self, remove_undo=1)
workflow_history.reverse()
for wh in workflow_history:
wh_state = self.unrestrictedTraverse(wh['current_state'])
for wh_transition in wh_state.getAvailableTransitionList(self):
wh_state = workflow.getStateValueByReference(wh['current_state'])
for wh_transition in getAvailableTransitionList(wh_state, self):
if wh_transition.getTransitionFormId() is not None and \
wh_transition != transition:
return True
......
def titleToReference(title):
reference = title.replace(' ', '_').lower()
return reference
def migrateToERP5Workflow(portal_workflow, configurator_workflow):
"""
Convert Configurator Workflow (workflow_module/*) to ERP5 Workflow.
"""
workflow_id = configurator_workflow.getId()
relative_url = "%s/%s" % (portal_workflow.getRelativeUrl(), workflow_id)
def getCategoryList(prefix, category_value_list):
return ['%s/%s%s' % (relative_url, prefix, titleToReference(o.getTitle()))
for o in category_value_list ]
workflow = portal_workflow.newContent(
portal_type='Workflow',
reference=configurator_workflow.getId(),
comment=configurator_workflow.getComment(),
description=configurator_workflow.getDescription(),
state_base_category=configurator_workflow.getProperty('state_base_category'),
state_variable=configurator_workflow.getProperty('state_variable_name'),
source_list=getCategoryList('state_', configurator_workflow.getSourceValueList()),
# ConfiguratorWorkflow PropertySheet
configuration_after_script_id=configurator_workflow.getConfigurationAfterScriptId())
for business_configuration in configurator_workflow.getRelatedValueList(
portal_type='Business Configuration'):
business_configuration.setResourceValue(workflow)
for subobject in configurator_workflow.objectValues():
title = subobject.getTitle()
reference = titleToReference(title)
if subobject.getPortalType() == 'State':
state = workflow.newContent(
portal_type='State',
reference=reference,
title=title,
destination_list=getCategoryList('transition_', subobject.getDestinationValueList()),
comment=subobject.getComment(),
description=subobject.getDescription())
for business_configuration in subobject.getRelatedValueList(
portal_type='Business Configuration'):
business_configuration.setCurrentStateValue(state)
elif subobject.getPortalType() == 'Workflow Transition':
# XXX_1: Workflows only call Workflow Script and do not call Python Script in
# portal_skins but Configurator Workflows do. For now leave them as they
# ({before,after}_script_id property) but they should be migrated later on.
#
# def addWorkflowScript(script_id_property):
# old_script_id = getattr(subobject.aq_base, script_id_property, None)
# if old_script_id:
# old_script = portal_workflow.getPortalObject().unrestrictedTraverse(old_script_id)
# script = workflow.newContent(id=workflow.getScriptIdByReference(old_script.id),
# portal_type='Workflow Script')
# script.defeault_reference = old_script.id
# script.setTitle(old_script.title)
# script.setParameterSignature('state_change')
# script.setBody(old_script._body)
# script.setProxyRole(old_script._proxy_roles)
# return script
# before_script_value = addWorkflowScript('before_script_id')
# after_script_value = addWorkflowScript('after_script_id')
transition = workflow.newContent(
portal_type='Workflow Transition',
reference=reference,
title=title,
destination_list=getCategoryList('state_', subobject.getDestinationValueList()),
comment=subobject.getComment(),
description=subobject.getDescription(),
# XXX_1: before_script_value=before_script_value,
# after_script_value=after_script_value,
guard_expression=subobject.getProperty('guard_expression'),
# ConfiguratorWorkflowTransition Property Sheet
transition_form_id=subobject.getProperty('transition_form_id'))
# XXX_1: Should use the normal {before,after}_script Workflow Property.
try:
transition.before_script_id = subobject.aq_base.before_script_id
except AttributeError:
pass
try:
transition.after_script_id = subobject.aq_base.after_script_id
except AttributeError:
pass
# XXX: Transition Variable: Not used in erp5.git, used elsewhere?
elif subobject.getPortalType() == 'Variable':
if reference in ('action',
'actor',
'comment',
'error_message',
'history',
'portal_type',
'time'):
continue
workflow.newContent(
portal_type='Workflow Variable',
reference=reference,
title=title,
description=subobject.getDescription(),
automatic_update=subobject.getAutomaticUpdate(),
variable_default_expression=subobject.aq_base.initial_value,
comment=subobject.getComment())
# default_image
elif subobject.getPortalType() == 'Embedded File':
copy_data = configurator_workflow.manage_copyObjects([subobject.getId()])
workflow.manage_pasteObjects(copy_data)
else:
raise NotImplementedError(
"%s: Portal Type '%s'" % (subobject.getRelativeUrl(),
subobject.getPortalType()))
return workflow
def migrateWorkflowModuleToPortalWorkflow(self, recreate=False):
"""
Migrate workflow_module to new-style ERP5Workflows: workflow_module was implemented
for Configurators, based on DCWorkflow (portal_workflow), and a step towards migrating
DCWorkflows to ERP5 Objects. Now that portal_workflow is a real ERP5 Object, these must
be migrated to portal_workflow.
"""
portal = self.getPortalObject()
try:
workflow_module = portal.workflow_module
except AttributeError:
return "Nothing to do as workflow_module does not exist."
portal_workflow = portal.portal_workflow
assert portal_workflow.getPortalType() == 'Workflow Tool'
from zLOG import LOG
for configurator_workflow in workflow_module.objectValues():
id_ = configurator_workflow.getId()
if id_ in portal_workflow:
if not recreate:
continue
portal_workflow._delOb(id_)
new_workflow = migrateToERP5Workflow(portal_workflow, configurator_workflow)
LOG("migrateWorkflowModuleToPortalWorkflow", 0,
"Migrated %s to %s" % (configurator_workflow.getRelativeUrl(),
new_workflow.getRelativeUrl()))
return 'Done'
\ No newline at end of file
......@@ -14,7 +14,7 @@
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>ERP5UpgraderUtils</string> </value>
<value> <string>migrateWorkflowModuleToPortalWorkflow</string> </value>
</item>
<item>
<key> <string>description</string> </key>
......@@ -24,7 +24,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>extension.erp5.ERP5UpgraderUtils</string> </value>
<value> <string>extension.erp5.migrateWorkflowModuleToPortalWorkflow</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
......
......@@ -86,7 +86,6 @@ class TestLiveConfiguratorWorkflowMixin(SecurityTestCase):
return ('erp5_core_proxy_field_legacy',
'erp5_full_text_mroonga_catalog',
'erp5_base',
'erp5_workflow',
'erp5_configurator',
'erp5_configurator_standard',)
......
<property_sheet_list>
<portal_type id="Workflow Transition">
<item>ConfiguratorWorkflowTransition</item>
</portal_type>
<portal_type id="Workflow">
<item>ConfiguratorWorkflow</item>
<item>DefaultImage</item>
<item>WorkflowConfigurator</item>
</portal_type>
</property_sheet_list>
\ No newline at end of file
......@@ -32,7 +32,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>WorkflowConfigurator</string> </value>
<value> <string>ConfiguratorWorkflow</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment