workflows : add an identifier to automatic and on submit jumps (#74723) #161
|
@ -2373,8 +2373,8 @@ def test_backoffice_wscall_attachment(http_requests, pub):
|
|||
assert resp.text == '<?xml version="1.0"><foo/>'
|
||||
|
||||
formdata = formdef.data_class().get(number31.id)
|
||||
assert formdata.evolution[-1].parts[0].orig_filename == 'xxx.xml'
|
||||
assert formdata.evolution[-1].parts[0].content_type == 'text/xml'
|
||||
assert formdata.evolution[-1].parts[1].orig_filename == 'xxx.xml'
|
||||
assert formdata.evolution[-1].parts[1].content_type == 'text/xml'
|
||||
assert formdata.get_substitution_variables()['attachments'].xxx.filename == 'xxx.xml'
|
||||
resp = app.get(formdata.get_substitution_variables()['attachments'].xxx.url)
|
||||
resp = resp.follow()
|
||||
|
@ -6104,3 +6104,90 @@ def test_status_visibility(pub):
|
|||
wf.store()
|
||||
resp = app.get(formdata.get_backoffice_url())
|
||||
assert resp.pyquery('.status').text() == 'st1 st2 st3'
|
||||
|
||||
|
||||
def test_workflow_track_jumps(pub):
|
||||
create_user(pub)
|
||||
create_environment(pub)
|
||||
|
||||
wf = Workflow(name='blah')
|
||||
st1 = wf.add_status('One')
|
||||
st1.id = 'one'
|
||||
st2 = wf.add_status('Two')
|
||||
st2.id = 'two'
|
||||
|
||||
st3 = wf.add_status('Three')
|
||||
st3.id = 'three'
|
||||
st4 = wf.add_status('Four')
|
||||
st4.id = 'four'
|
||||
st5 = wf.add_status('Five')
|
||||
st5.id = 'five'
|
||||
|
||||
commentable = st1.add_action('commentable', id='_commentable')
|
||||
commentable.by = ['_submitter', '_receiver']
|
||||
commentable.button_label = 'CLICK ME!'
|
||||
|
||||
jump = st1.add_action('jumponsubmit', id='_jump')
|
||||
jump.status = st2.id
|
||||
jump.identifier = 'to_two'
|
||||
|
||||
to_three = st2.add_action('choice', id='_tothree')
|
||||
to_three.label = 'ToThree'
|
||||
to_three.by = ['_receiver']
|
||||
to_three.status = 'three'
|
||||
to_three.identifier = 'to_three'
|
||||
|
||||
to_four = st3.add_action('choice', id='_tofour')
|
||||
to_four.label = 'ToFour'
|
||||
to_four.by = ['_receiver']
|
||||
to_four.status = 'four'
|
||||
to_four.identifier = 'to_four'
|
||||
|
||||
to_five = st4.add_action('jump', id='_jump')
|
||||
to_five.status = 'five'
|
||||
to_five.identifier = 'to_five'
|
||||
|
||||
wf.store()
|
||||
|
||||
formdef = FormDef.get_by_urlname('form-title')
|
||||
formdef.data_class().wipe()
|
||||
formdef.workflow = wf
|
||||
formdef.store()
|
||||
|
||||
formdata = formdef.data_class()()
|
||||
formdata.data = {}
|
||||
formdata.just_created()
|
||||
formdata.store()
|
||||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/management/form-title/%s/' % formdata.id)
|
||||
resp.form['comment'] = 'HELLO WORLD'
|
||||
resp = resp.form.submit('button_commentable')
|
||||
resp = resp.follow()
|
||||
|
||||
formdata.refresh_from_storage()
|
||||
assert formdata.status == 'wf-two'
|
||||
substitution_variables = formdata.get_substitution_variables()
|
||||
assert substitution_variables['form_jumps'] == ['to_two']
|
||||
assert substitution_variables['form_latest_jump'] == 'to_two'
|
||||
|
||||
resp = resp.form.submit('button_tothree')
|
||||
resp = resp.follow()
|
||||
formdata.refresh_from_storage()
|
||||
assert formdata.status == 'wf-three'
|
||||
substitution_variables = formdata.get_substitution_variables()
|
||||
assert substitution_variables['form_jumps'] == ['to_two', 'to_three']
|
||||
assert substitution_variables['form_latest_jump'] == 'to_three'
|
||||
|
||||
resp = resp.form.submit('button_tofour')
|
||||
resp = resp.follow()
|
||||
formdata.refresh_from_storage()
|
||||
assert formdata.status == 'wf-five'
|
||||
substitution_variables = formdata.get_substitution_variables()
|
||||
assert substitution_variables['form_jumps'] == [
|
||||
'to_two',
|
||||
'to_three',
|
||||
'to_four',
|
||||
'to_five',
|
||||
]
|
||||
assert substitution_variables['form_latest_jump'] == 'to_five'
|
||||
|
|
|
@ -5438,7 +5438,7 @@ def test_rich_commentable_action(pub):
|
|||
assert '<p>hello <i>world</i></p>' in resp.text
|
||||
|
||||
formdata = formdef.data_class().select()[0]
|
||||
assert formdata.evolution[-1].parts[-1].comment == '<p>hello <i>world</i></p>'
|
||||
assert formdata.evolution[-1].parts[-2].comment == '<p>hello <i>world</i></p>'
|
||||
|
||||
# check link
|
||||
resp.form['comment'] = '<p>hello <a href="http://localhost/">link</a>.</p>'
|
||||
|
@ -5446,7 +5446,7 @@ def test_rich_commentable_action(pub):
|
|||
assert '<p>hello <a href="http://localhost/" rel="nofollow">link</a>.</p>' in resp.text
|
||||
formdata = formdef.data_class().select()[0]
|
||||
assert (
|
||||
formdata.evolution[-1].parts[-1].comment
|
||||
formdata.evolution[-1].parts[-2].comment
|
||||
== '<p>hello <a href="http://localhost/" rel="nofollow">link</a>.</p>'
|
||||
)
|
||||
|
||||
|
@ -5455,7 +5455,7 @@ def test_rich_commentable_action(pub):
|
|||
resp = resp.form.submit('button_x1').follow()
|
||||
assert '<p>hello evil</p>' in resp.text
|
||||
formdata = formdef.data_class().select()[0]
|
||||
assert formdata.evolution[-1].parts[-1].comment == '<p>hello evil</p>'
|
||||
assert formdata.evolution[-1].parts[-2].comment == '<p>hello evil</p>'
|
||||
|
||||
resp.form['comment'] = '<p></p>' # left empty
|
||||
resp = resp.form.submit('button_x1')
|
||||
|
@ -5486,7 +5486,7 @@ def test_rich_commentable_action(pub):
|
|||
resp.form['comment'] = '<p>hello<br>world</p>'
|
||||
resp = resp.form.submit('button_x1').follow()
|
||||
formdata = formdef.data_class().select()[0]
|
||||
assert formdata.evolution[-1].parts[-1].comment == '<p>hello<br>world</p>'
|
||||
assert formdata.evolution[-1].parts[-2].comment == '<p>hello<br>world</p>'
|
||||
pub.substitutions.feed(formdata)
|
||||
context = pub.substitutions.get_context_variables(mode='lazy')
|
||||
tmpl = Template('{{form_comment}}')
|
||||
|
|
|
@ -168,12 +168,14 @@ def test_trigger_jumps(pub):
|
|||
formdata.id = 1
|
||||
formdata.data = {'0': 'Alice', '1': 'alice@example.net'}
|
||||
formdata.status = 'wf-%s' % st1.id
|
||||
|
||||
formdata.just_created()
|
||||
formdata.store()
|
||||
id1 = formdata.id
|
||||
formdata = formdef.data_class()()
|
||||
formdata.id = 2
|
||||
formdata.data = {'0': 'Bob', '1': 'bob@example.net'}
|
||||
formdata.status = 'wf-%s' % st1.id
|
||||
formdata.just_created()
|
||||
formdata.store()
|
||||
id2 = formdata.id
|
||||
select_and_jump_formdata(formdef, trigger, rows)
|
||||
|
|
|
@ -790,7 +790,7 @@ class FormData(StorableObject):
|
|||
return None
|
||||
|
||||
def jump_status(self, status_id, user_id=None):
|
||||
from wcs.workflows import ContentSnapshotPart
|
||||
from wcs.workflows import ContentSnapshotPart, JumpEvolutionPart
|
||||
|
||||
if status_id == '_previous':
|
||||
previous_status = self.pop_previous_marked_status()
|
||||
|
@ -811,7 +811,11 @@ class FormData(StorableObject):
|
|||
self.status == status
|
||||
and self.evolution[-1].status == status
|
||||
and not self.evolution[-1].comment
|
||||
and not [x for x in self.evolution[-1].parts or [] if not isinstance(x, ContentSnapshotPart)]
|
||||
and not [
|
||||
x
|
||||
for x in self.evolution[-1].parts or []
|
||||
if not isinstance(x, (ContentSnapshotPart, JumpEvolutionPart))
|
||||
]
|
||||
):
|
||||
# if status do not change and last evolution is empty,
|
||||
# just update last jump time on last evolution, do not add one
|
||||
|
|
|
@ -1011,6 +1011,29 @@ class LazyFormData(LazyFormDef):
|
|||
data = self._formdata.workflow_data or {}
|
||||
return {k: data[k] for k in data if not k.startswith('_')}
|
||||
|
||||
@property
|
||||
def jumps(self):
|
||||
from wcs.workflows import JumpEvolutionPart
|
||||
|
||||
jump_parts = []
|
||||
for part in self._formdata.iter_evolution_parts():
|
||||
if not isinstance(part, JumpEvolutionPart):
|
||||
continue
|
||||
jump_parts.append(part.identifier or 'undefined')
|
||||
return jump_parts
|
||||
|
||||
@property
|
||||
def latest_jump(self):
|
||||
from wcs.workflows import JumpEvolutionPart
|
||||
|
||||
if self._formdata.evolution:
|
||||
for evolution in reversed(self._formdata.evolution or []):
|
||||
if evolution.parts:
|
||||
for part in reversed(evolution.parts):
|
||||
if isinstance(part, JumpEvolutionPart):
|
||||
return part.identifier or 'undefined'
|
||||
return ''
|
||||
|
||||
def export_to_json(self, include_files=True):
|
||||
# this gets used to generate an email attachment :/
|
||||
return self._formdata.export_to_json(include_files=include_files)
|
||||
|
|
|
@ -21,7 +21,6 @@ from wcs.qommon.form import (
|
|||
CheckboxWidget,
|
||||
ComputedExpressionWidget,
|
||||
SingleSelectWidget,
|
||||
VarnameWidget,
|
||||
WidgetList,
|
||||
WysiwygTextWidget,
|
||||
)
|
||||
|
@ -35,7 +34,6 @@ class ChoiceWorkflowStatusItem(WorkflowStatusJumpItem):
|
|||
ok_in_global_action = True
|
||||
|
||||
label = None
|
||||
identifier = None
|
||||
by = []
|
||||
backoffice_info_text = None
|
||||
require_confirmation = False
|
||||
|
@ -116,6 +114,7 @@ class ChoiceWorkflowStatusItem(WorkflowStatusJumpItem):
|
|||
formdata.record_workflow_event('button', action_item_id=self.id)
|
||||
evo.status = 'wf-%s' % wf_status[0].id
|
||||
self.handle_markers_stack(formdata)
|
||||
self.add_jump_part(formdata, evo)
|
||||
ecazenave
commented
On passe l'evolution qui vient d'être créée sinon pas de persistance de la JumpEvolutionPart (voir plus bas). On passe l'evolution qui vient d'être créée sinon pas de persistance de la JumpEvolutionPart (voir plus bas).
|
||||
form.clear_errors()
|
||||
return True # get out of processing loop
|
||||
|
||||
|
@ -156,14 +155,6 @@ class ChoiceWorkflowStatusItem(WorkflowStatusJumpItem):
|
|||
title=_('Information Text for Backoffice'),
|
||||
value=self.backoffice_info_text,
|
||||
)
|
||||
if 'identifier' in parameters:
|
||||
form.add(
|
||||
VarnameWidget,
|
||||
'%sidentifier' % prefix,
|
||||
title=_('Identifier'),
|
||||
value=self.identifier,
|
||||
advanced=True,
|
||||
)
|
||||
if 'ignore_form_errors' in parameters:
|
||||
form.add(
|
||||
CheckboxWidget,
|
||||
|
|
|
@ -42,6 +42,7 @@ JUMP_TIMEOUT_INTERVAL = max((60 // int(os.environ.get('WCS_JUMP_TIMEOUT_CHECKS',
|
|||
|
||||
def jump_and_perform(formdata, action, workflow_data=None):
|
||||
action.handle_markers_stack(formdata)
|
||||
action.add_jump_part(formdata)
|
||||
if workflow_data:
|
||||
formdata.update_workflow_data(workflow_data)
|
||||
formdata.store()
|
||||
|
@ -186,7 +187,7 @@ class JumpWorkflowStatusItem(WorkflowStatusJumpItem):
|
|||
def get_parameters(self):
|
||||
if hasattr(self, 'parent') and isinstance(self.parent, WorkflowGlobalAction):
|
||||
return ('status', 'condition', 'set_marker_on_status')
|
||||
return ('status', 'condition', 'trigger', 'by', 'timeout', 'set_marker_on_status')
|
||||
return ('status', 'condition', 'trigger', 'by', 'timeout', 'set_marker_on_status', 'identifier')
|
||||
|
||||
def add_parameters_widgets(self, form, parameters, prefix='', formdef=None, **kwargs):
|
||||
super().add_parameters_widgets(form, parameters, prefix, formdef, **kwargs)
|
||||
|
@ -260,7 +261,9 @@ class JumpWorkflowStatusItem(WorkflowStatusJumpItem):
|
|||
wf_status = self.get_target_status(formdata)
|
||||
if wf_status:
|
||||
self.handle_markers_stack(formdata)
|
||||
self.add_jump_part(formdata)
|
||||
formdata.status = 'wf-%s' % wf_status[0].id
|
||||
formdata.store()
|
||||
|
||||
def check_condition(self, formdata, *args, trigger=None, **kwargs):
|
||||
result = super().check_condition(formdata, *args, **kwargs)
|
||||
|
|
|
@ -45,9 +45,10 @@ class JumpOnSubmitWorkflowStatusItem(WorkflowStatusJumpItem):
|
|||
if wf_status:
|
||||
evo.status = 'wf-%s' % wf_status[0].id
|
||||
self.handle_markers_stack(formdata)
|
||||
self.add_jump_part(formdata, evo)
|
||||
|
||||
def get_parameters(self):
|
||||
return ('status', 'set_marker_on_status', 'condition')
|
||||
return ('status', 'set_marker_on_status', 'condition', 'identifier')
|
||||
|
||||
|
||||
register_item_class(JumpOnSubmitWorkflowStatusItem)
|
||||
|
|
|
@ -55,6 +55,7 @@ from .qommon.form import (
|
|||
SingleSelectWidget,
|
||||
SingleSelectWidgetWithOther,
|
||||
StringWidget,
|
||||
VarnameWidget,
|
||||
WidgetList,
|
||||
WidgetListOfRoles,
|
||||
)
|
||||
|
@ -3230,11 +3231,17 @@ class WorkflowStatusItem(XmlSerialisable):
|
|||
return '<%s>' % ' '.join(parts)
|
||||
|
||||
|
||||
class JumpEvolutionPart(EvolutionPart):
|
||||
def __init__(self, identifier):
|
||||
self.identifier = identifier
|
||||
|
||||
|
||||
class WorkflowStatusJumpItem(WorkflowStatusItem):
|
||||
status = None
|
||||
endpoint = False
|
||||
set_marker_on_status = False
|
||||
category = 'status-change'
|
||||
identifier = None
|
||||
|
||||
def add_parameters_widgets(self, form, parameters, prefix='', formdef=None, **kwargs):
|
||||
super().add_parameters_widgets(form, parameters, prefix=prefix, formdef=formdef, **kwargs)
|
||||
|
@ -3256,9 +3263,23 @@ class WorkflowStatusJumpItem(WorkflowStatusItem):
|
|||
advanced=True,
|
||||
)
|
||||
|
||||
if 'identifier' in parameters:
|
||||
form.add(
|
||||
VarnameWidget,
|
||||
'%sidentifier' % prefix,
|
||||
title=_('Identifier'),
|
||||
value=self.identifier,
|
||||
advanced=True,
|
||||
)
|
||||
|
||||
def get_parameters(self):
|
||||
return ('status', 'set_marker_on_status', 'condition')
|
||||
|
||||
def add_jump_part(self, formdata, evo=None):
|
||||
if evo is None:
|
||||
evo = formdata.evolution[-1]
|
||||
evo.add_part(JumpEvolutionPart(self.identifier))
|
||||
|
||||
|
||||
class NoLongerAvailableAction(WorkflowStatusItem):
|
||||
pass # marker class, loadable from pickle files but removed in migrate()
|
||||
|
|
Loading…
Reference in New Issue
Sinon le tests crash sur add_jump_part qui ne trouve pas d'évolution.
L'absence d'evolution me parait être une situation artificielle du test, d'où l'ajout du just_created.