global action trigger confirmation (#58122) #776

Merged
fpeters merged 1 commits from wip/58122-global-action-button-confirmation into main 2023-10-27 08:34:00 +02:00
3 changed files with 91 additions and 2 deletions

View File

@ -835,6 +835,45 @@ def test_backoffice_multi_actions_some_status(pub):
assert len(resp.pyquery('[data-status_accepted]')) == 5
def test_backoffice_multi_actions_confirmation(pub):
create_superuser(pub)
Workflow.wipe()
FormDef.wipe()
workflow = Workflow.get_default_workflow()
workflow.id = '2'
action = workflow.add_global_action('FOOBAR')
jump = action.add_action('jump')
jump.status = 'finished'
trigger = action.triggers[0]
trigger.statuses = ['new']
trigger.roles = ['_receiver']
trigger.require_confirmation = True
workflow.store()
formdef = FormDef()
formdef.name = 'form title'
formdef.workflow_roles = {'_receiver': 1}
formdef.workflow_id = workflow.id
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.jump_status('accepted')
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/?filter=all')
assert resp.pyquery('#multi-actions button').attr('data-ask-for-confirmation') == 'true'
trigger.confirmation_text = 'Ok?'
workflow.store()
resp = app.get('/backoffice/management/form-title/?filter=all')
assert (
resp.pyquery('#multi-actions button[ data-ask-for-confirmation]').attr('data-ask-for-confirmation')
== 'Ok?'
)
def test_backoffice_multi_actions_jump(pub):
create_superuser(pub)
FormDef.wipe()
@ -903,6 +942,14 @@ def test_backoffice_multi_actions_jump(pub):
assert len(resp.pyquery('[data-status_finished]')) == 7
assert len(resp.pyquery('[data-status_accepted]')) == 8
workflow.get_status('new').items[2].confirmation_text = 'Ok?'
workflow.store()
resp = app.get('/backoffice/management/form-title/?filter=all')
assert (
resp.pyquery('#multi-actions button[data-ask-for-confirmation]').attr('data-ask-for-confirmation')
== 'Ok?'
)
def test_backoffice_multi_actions_jump_same_identifier(pub):
create_superuser(pub)

View File

@ -2338,7 +2338,9 @@ class FormPage(Directory, TempfileDirectoryMixin):
else:
attrs['data-visible_all_status'] = 'true'
if getattr(action['action'], 'require_confirmation', False):
attrs['data-ask-for-confirmation'] = 'true'
attrs['data-ask-for-confirmation'] = (
getattr(action['action'], 'confirmation_text', None) or 'true'
)
multi_form.add_submit(
'button-action-%s' % action['action'].id, action['action'].name, attrs=attrs
)

View File

@ -966,6 +966,8 @@ class Workflow(StorableObject):
statuses.extend(trigger.statuses or [])
functions = [x for x in roles if x in self.roles]
roles = [x for x in roles if x not in self.roles]
action.require_confirmation = trigger.require_confirmation
action.confirmation_text = trigger.confirmation_text
if functions or roles:
actions.append(
{'action': action, 'roles': roles, 'functions': functions, 'statuses': statuses}
@ -980,6 +982,7 @@ class Workflow(StorableObject):
self.name = action.get_label()
self.status_action = True
self.require_confirmation = action.require_confirmation
self.confirmation_text = action.confirmation_text
self.action = action
def is_interactive(self):
@ -1625,9 +1628,11 @@ class WorkflowGlobalActionManualTrigger(WorkflowGlobalActionTrigger):
roles = None
statuses = None
allow_as_mass_action = True
require_confirmation = False
confirmation_text = None
def get_parameters(self):
return ('roles', 'statuses', 'allow_as_mass_action')
return ('roles', 'statuses', 'allow_as_mass_action', 'require_confirmation', 'confirmation_text')
def render_as_line(self):
parts = [_('Manual')]
@ -1650,6 +1655,18 @@ class WorkflowGlobalActionManualTrigger(WorkflowGlobalActionTrigger):
_(':'),
_('Yes') if self.allow_as_mass_action else _('No'),
)
r += htmltext('<li>%s%s %s</li>') % (
_('Require confirmation'),
_(':'),
_('Yes') if self.require_confirmation else _('No'),
)
if self.require_confirmation and self.confirmation_text:
r += htmltext('<li>%s%s %s</li>') % (
_('Custom text for confirmation popup'),
_(':'),
self.confirmation_text,
)
r += htmltext('</ul>')
return r.getvalue()
@ -1683,6 +1700,25 @@ class WorkflowGlobalActionManualTrigger(WorkflowGlobalActionTrigger):
title=_('Allow as mass action'),
value=self.allow_as_mass_action,
)
form.add(
CheckboxWidget,
'require_confirmation',
title=_('Require confirmation'),
value=self.require_confirmation,
attrs={'data-dynamic-display-parent': 'true'},
)
form.add(
StringWidget,
'confirmation_text',
title=_('Custom text for confirmation popup'),
size=100,
value=self.confirmation_text,
attrs={
'data-dynamic-display-child-of': 'require_confirmation',
'data-dynamic-display-checked': 'true',
},
)
return form
def roles_export_to_xml(self, item, charset, include_id=False):
@ -2376,6 +2412,7 @@ class WorkflowGlobalAction(SerieOfActionsMixin):
# check action is executable for given formdata and user (appropriate status and roles)
current_status_id = (formdata.status or '').removeprefix('wf-')
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:
continue
@ -2454,6 +2491,9 @@ class WorkflowStatus(SerieOfActionsMixin):
widget.backoffice_info_text = action.backoffice_info_text
widget.ignore_form_errors = True
widget.attrs['formnovalidate'] = 'formnovalidate'
if action.trigger.require_confirmation:
get_response().add_javascript(['jquery.js', '../../i18n.js', 'qommon.js'])
widget.attrs = {'data-ask-for-confirmation': action.trigger.confirmation_text or 'true'}
if form.widgets or form.submit_widgets:
return form