workflows: add generic status options to global action manual trigger (#77926)
gitea/wcs/pipeline/head This commit looks good Details

This commit is contained in:
Frédéric Péters 2024-04-13 19:01:23 +02:00
parent ccc87a959c
commit a07ac2a8b3
2 changed files with 174 additions and 7 deletions

View File

@ -856,6 +856,144 @@ def test_backoffice_multi_actions_some_status(pub):
assert len(resp.pyquery('[data-status_accepted]')) == 5
def test_backoffice_multi_actions_generic_status(pub):
create_superuser(pub)
Workflow.wipe()
FormDef.wipe()
workflow = Workflow.get_default_workflow()
workflow.id = '2'
action1 = workflow.add_global_action('FOOBAR')
register_comment = action1.add_action('register-comment')
register_comment.comment = 'hello'
trigger = action1.triggers[0]
trigger.statuses = ['_endpoint_status']
trigger.roles = ['_receiver']
assert set(trigger.get_statuses_ids()) == {'rejected', 'finished'}
assert trigger.render_as_line() == 'Manual, from final status, by Recipient'
action2 = workflow.add_global_action('FOOBAR2')
register_comment = action2.add_action('register-comment')
register_comment.comment = 'hello2'
trigger = action2.triggers[0]
trigger.statuses = ['_waitpoint_status']
trigger.roles = ['_receiver']
assert set(trigger.get_statuses_ids()) == {'new', 'accepted'}
assert trigger.render_as_line() == 'Manual, from pause status, by Recipient'
action3 = workflow.add_global_action('FOOBAR3')
register_comment = action3.add_action('register-comment')
register_comment.comment = 'hello3'
trigger = action3.triggers[0]
trigger.statuses = ['_transition_status']
trigger.roles = ['_receiver']
assert set(trigger.get_statuses_ids()) == {'just_submitted'}
assert trigger.render_as_line() == 'Manual, from transition status, by Recipient'
trigger.statuses = ['_transition_status', 'xxx'] # check with invalid status
assert trigger.render_as_line() == 'Manual, from transition status, by Recipient'
workflow.store()
formdef = FormDef()
formdef.name = 'form title'
formdef.workflow_roles = {'_receiver': 1}
formdef.workflow_id = workflow.id
formdef.store()
for i in range(15):
formdata = formdef.data_class()()
formdata.just_created()
if i < 5:
formdata.jump_status('accepted')
elif i % 3 == 0:
formdata.jump_status('new')
else:
formdata.jump_status('finished')
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/?filter=all')
ids = []
for checkbox in resp.forms[0].fields['select[]']:
if checkbox._value == '_all':
continue
# check them all
ids.append(checkbox._value)
checkbox.checked = True
assert len(resp.pyquery('[data-status_new]')) == 3
assert len(resp.pyquery('[data-status_finished]')) == 7
assert len(resp.pyquery('[data-status_accepted]')) == 5
assert resp.pyquery(
f'form#multi-actions button[name="button-action-{action1.id}"]'
'[data-visible_status_finished]'
'[data-visible_status_rejected]'
':not([data-visible_status_new])'
':not([data-visible_status_accepted])'
':not([data-visible_status_just_submitted])'
)
assert resp.pyquery(
f'form#multi-actions button[name="button-action-{action2.id}"]'
':not([data-visible_status_finished])'
':not([data-visible_status_rejected])'
'[data-visible_status_new]'
'[data-visible_status_accepted]'
':not([data-visible_status_just_submitted])'
)
assert resp.pyquery(
f'form#multi-actions button[name="button-action-{action3.id}"]'
':not([data-visible_status_finished])'
':not([data-visible_status_rejected])'
':not([data-visible_status_new])'
':not([data-visible_status_accepted])'
'[data-visible_status_just_submitted]'
)
resp = resp.forms[0].submit(f'button-action-{action1.id}')
assert '?job=' in resp.location
resp = resp.follow()
assert 'Executing task &quot;FOOBAR&quot; on forms' in resp.text
assert '>completed<' in resp.text
for id in ids:
formdata = formdef.data_class().get(id)
comments = [x.content for x in formdata.iter_evolution_parts(JournalEvolutionPart)]
# check action was only executed on "finished"
if formdata.status == 'wf-finished':
assert comments == ['<p>hello</p>']
else:
assert comments == []
# check not end point status
resp = app.get('/backoffice/management/form-title/?filter=all')
ids = []
for checkbox in resp.forms[0].fields['select[]']:
if checkbox._value == '_all':
continue
# check them all
ids.append(checkbox._value)
checkbox.checked = True
assert len(resp.pyquery('[data-status_new]')) == 3
assert len(resp.pyquery('[data-status_finished]')) == 7
assert len(resp.pyquery('[data-status_accepted]')) == 5
resp = resp.forms[0].submit(f'button-action-{action2.id}')
assert '?job=' in resp.location
resp = resp.follow()
assert 'Executing task &quot;FOOBAR2&quot; on forms' in resp.text
assert '>completed<' in resp.text
for id in ids:
formdata = formdef.data_class().get(id)
comments = [x.content for x in formdata.iter_evolution_parts(JournalEvolutionPart)]
# check action was only executed on not final status
if formdata.status in ('wf-finished', 'wf-rejected'):
assert comments == ['<p>hello</p>']
else:
assert comments == ['<p>hello2</p>']
def test_backoffice_multi_actions_confirmation(pub):
create_superuser(pub)
Workflow.wipe()

View File

@ -1098,7 +1098,7 @@ class Workflow(StorableObject):
if not trigger.allow_as_mass_action:
continue
roles.extend(trigger.roles or [])
statuses.extend(trigger.statuses or [])
statuses.extend(trigger.get_statuses_ids())
action.require_confirmation = trigger.require_confirmation
action.confirmation_text = trigger.confirmation_text
functions = [x for x in roles if x in self.roles]
@ -1786,12 +1786,35 @@ class WorkflowGlobalActionManualTrigger(WorkflowGlobalActionTrigger):
def get_parameters(self):
return ('roles', 'statuses', 'allow_as_mass_action', 'require_confirmation', 'confirmation_text')
def get_statuses_ids(self):
statuses = self.statuses or []
for status in self.get_workflow().possible_status or []:
if status in statuses:
yield status.id
if '_endpoint_status' in statuses and status.is_endpoint():
yield status.id
if '_waitpoint_status' in statuses and (status.is_waitpoint() and not status.is_endpoint()):
yield status.id
if '_transition_status' in statuses and not (status.is_waitpoint() or status.is_endpoint()):
yield status.id
def get_status_option_label(self, status_id):
if status_id == '_endpoint_status':
return _('from final status')
if status_id == '_waitpoint_status':
return _('from pause status')
if status_id == '_transition_status':
return _('from transition status')
try:
return _('from status "%s"') % self.get_workflow().get_status(status_id).name
except KeyError:
return None
def render_as_line(self):
parts = [_('Manual')]
if self.statuses:
labels = [x.name for x in self.get_workflow().possible_status if x.id in self.statuses]
if labels:
parts.append(_('from status %s') % _(' or ').join([_('"%s"') % x for x in labels]))
status_labels = [self.get_status_option_label(x) for x in self.statuses or []]
if status_labels:
parts.append(_(' or ').join([str(x) for x in status_labels if x]))
if self.roles:
parts.append(_('by %s') % render_list_of_roles(self.get_workflow(), self.roles))
else:
@ -1838,7 +1861,13 @@ class WorkflowGlobalActionManualTrigger(WorkflowGlobalActionTrigger):
add_element_label=workflow.get_add_role_label(),
element_kwargs={'render_br': False, 'options': options},
)
status_options = [(None, '---', None)]
status_options = [
(None, '---', ''),
('_waitpoint_status', _('Pause status'), '_wait_status'),
('_endpoint_status', _('Final status'), '_endpoint_status'),
('_transition_status', _('Transition status'), '_transition_status'),
(None, '---', ''),
]
status_options += [(str(x.id), x.name, str(x.id)) for x in self.get_workflow().possible_status]
form.add(
WidgetList,
@ -2635,7 +2664,7 @@ class WorkflowGlobalAction(SerieOfActionsMixin):
for trigger in self.triggers or []:
self.trigger = trigger # attach trigger to action, to have trigger options available in form
if trigger.key == 'manual':
if trigger.statuses and current_status_id not in trigger.statuses:
if trigger.statuses and current_status_id not in trigger.get_statuses_ids():
continue
if '_submitter' in (trigger.roles or []) and formdata.is_submitter(user):
return True