backoffice: add multi-action support for status manual jump action (#37983)
This commit is contained in:
parent
6b69a5a1b6
commit
5cf979eb02
|
@ -317,8 +317,8 @@ def test_backoffice_listing(pub):
|
|||
|
||||
# check status filter <select>
|
||||
resp = app.get('/backoffice/management/form-title/')
|
||||
resp.forms[0]['filter'] = 'all'
|
||||
resp = resp.forms[0].submit()
|
||||
resp.forms['listing-settings']['filter'] = 'all'
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
if pub.is_using_postgresql():
|
||||
assert resp.text.count('data-link') == 20
|
||||
else:
|
||||
|
@ -327,8 +327,8 @@ def test_backoffice_listing(pub):
|
|||
|
||||
# check status filter <select>
|
||||
resp = app.get('/backoffice/management/form-title/')
|
||||
resp.forms[0]['filter'] = 'done'
|
||||
resp = resp.forms[0].submit()
|
||||
resp.forms['listing-settings']['filter'] = 'done'
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
if pub.is_using_postgresql():
|
||||
assert resp.text.count('data-link') == 20
|
||||
resp = resp.click('Next Page')
|
||||
|
@ -361,8 +361,8 @@ def test_backoffice_listing(pub):
|
|||
|
||||
resp = app.get('/backoffice/management/form-title/')
|
||||
assert resp.text.count('data-link') == 9
|
||||
resp.forms[0]['filter'] = 'pending'
|
||||
resp = resp.forms[0].submit()
|
||||
resp.forms['listing-settings']['filter'] = 'pending'
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
assert resp.text.count('data-link') == 17
|
||||
|
||||
# check status forced as endpoints are not part of the "actionable" list.
|
||||
|
@ -392,8 +392,8 @@ def test_backoffice_listing(pub):
|
|||
|
||||
resp = app.get('/backoffice/management/form-title/')
|
||||
assert resp.text.count('data-link') == 17
|
||||
resp.forms[0]['filter'] = 'pending'
|
||||
resp = resp.forms[0].submit()
|
||||
resp.forms['listing-settings']['filter'] = 'pending'
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
assert resp.text.count('data-link') == 17
|
||||
|
||||
# mark status as an endpoint
|
||||
|
@ -403,8 +403,8 @@ def test_backoffice_listing(pub):
|
|||
|
||||
resp = app.get('/backoffice/management/form-title/')
|
||||
assert resp.text.count('data-link') == 9
|
||||
resp.forms[0]['filter'] = 'pending'
|
||||
resp = resp.forms[0].submit()
|
||||
resp.forms['listing-settings']['filter'] = 'pending'
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
assert resp.text.count('data-link') == 9
|
||||
|
||||
|
||||
|
@ -542,10 +542,10 @@ def test_backoffice_columns(pub):
|
|||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/management/form-title/')
|
||||
assert resp.text.count('</th>') == 8 # six columns
|
||||
resp.forms[0]['1'].checked = False
|
||||
assert not 'submission_channel' in resp.forms[0].fields
|
||||
assert 'last_update_time' in resp.forms[0].fields
|
||||
resp = resp.forms[0].submit()
|
||||
resp.forms['listing-settings']['1'].checked = False
|
||||
assert not 'submission_channel' in resp.forms['listing-settings'].fields
|
||||
assert 'last_update_time' in resp.forms['listing-settings'].fields
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
assert resp.text.count('</th>') == 7 # fixe columns
|
||||
assert resp.text.count('data-link') == 17 # 17 rows
|
||||
assert resp.text.count('FOO BAR') == 0 # no field 1 column
|
||||
|
@ -570,8 +570,8 @@ def test_backoffice_channel_column(pub):
|
|||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/management/form-title/')
|
||||
assert resp.text.count('</th>') == 8 # six columns
|
||||
resp.forms[0]['submission_channel'].checked = True
|
||||
resp = resp.forms[0].submit()
|
||||
resp.forms['listing-settings']['submission_channel'].checked = True
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
assert resp.text.count('</th>') == 9 # seven columns
|
||||
assert resp.text.count('data-link') == 17 # 17 rows
|
||||
assert resp.text.count('<td>Web</td>') == 17
|
||||
|
@ -640,42 +640,42 @@ def test_backoffice_filter(pub):
|
|||
create_environment(pub)
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/management/form-title/')
|
||||
assert resp.forms[0]['filter-status'].checked == True
|
||||
resp.forms[0]['filter-status'].checked = False
|
||||
resp.forms[0]['filter-2'].checked = True
|
||||
resp = resp.forms[0].submit()
|
||||
assert resp.forms['listing-settings']['filter-status'].checked == True
|
||||
resp.forms['listing-settings']['filter-status'].checked = False
|
||||
resp.forms['listing-settings']['filter-2'].checked = True
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
assert '<select name="filter">' not in resp.text
|
||||
|
||||
resp.forms[0]['filter-2-value'] = 'baz'
|
||||
resp = resp.forms[0].submit()
|
||||
resp.forms['listing-settings']['filter-2-value'] = 'baz'
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
assert resp.text.count('<td>baz</td>') == 8
|
||||
assert resp.text.count('<td>foo</td>') == 0
|
||||
assert resp.text.count('<td>bar</td>') == 0
|
||||
|
||||
resp.forms[0]['filter-start'].checked = True
|
||||
resp = resp.forms[0].submit()
|
||||
resp.forms[0]['filter-start-value'] = datetime.datetime(2015, 2, 1).strftime('%Y-%m-%d')
|
||||
resp = resp.forms[0].submit()
|
||||
resp.forms['listing-settings']['filter-start'].checked = True
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
resp.forms['listing-settings']['filter-start-value'] = datetime.datetime(2015, 2, 1).strftime('%Y-%m-%d')
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
assert resp.text.count('<td>baz</td>') == 0
|
||||
resp.forms[0]['filter-start-value'] = datetime.datetime(2014, 2, 1).strftime('%Y-%m-%d')
|
||||
resp = resp.forms[0].submit()
|
||||
resp.forms['listing-settings']['filter-start-value'] = datetime.datetime(2014, 2, 1).strftime('%Y-%m-%d')
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
assert resp.text.count('<td>baz</td>') == 8
|
||||
|
||||
# check there's no crash on invalid date values
|
||||
resp.forms[0]['filter-start-value'] = 'whatever'
|
||||
resp = resp.forms[0].submit()
|
||||
resp.forms['listing-settings']['filter-start-value'] = 'whatever'
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
assert resp.text.count('<td>baz</td>') == 8
|
||||
|
||||
# check two-digit years are handled correctly
|
||||
resp.forms[0]['filter-start-value'] = datetime.datetime(2014, 2, 1).strftime('%y-%m-%d')
|
||||
resp = resp.forms[0].submit()
|
||||
resp.forms['listing-settings']['filter-start-value'] = datetime.datetime(2014, 2, 1).strftime('%y-%m-%d')
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
assert resp.text.count('<td>baz</td>') == 8
|
||||
|
||||
# check it's also ok for end filter
|
||||
resp.forms[0]['filter-end'].checked = True
|
||||
resp = resp.forms[0].submit()
|
||||
resp.forms[0]['filter-end-value'] = datetime.datetime(2014, 2, 2).strftime('%y-%m-%d')
|
||||
resp = resp.forms[0].submit()
|
||||
resp.forms['listing-settings']['filter-end'].checked = True
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
resp.forms['listing-settings']['filter-end-value'] = datetime.datetime(2014, 2, 2).strftime('%y-%m-%d')
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
assert resp.text.count('<td>baz</td>') == 0
|
||||
|
||||
|
||||
|
@ -1447,7 +1447,7 @@ def test_backoffice_multi_actions(pub):
|
|||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/management/form-title/')
|
||||
assert not 'id="multi-actions"' in resp.text
|
||||
assert 'id="multi-actions"' in resp.text # always there
|
||||
|
||||
workflow = Workflow.get_default_workflow()
|
||||
workflow.id = '2'
|
||||
|
@ -1462,7 +1462,7 @@ def test_backoffice_multi_actions(pub):
|
|||
formdef.store()
|
||||
|
||||
resp = app.get('/backoffice/management/form-title/')
|
||||
assert not 'id="multi-actions"' in resp.text
|
||||
assert 'id="multi-actions"' in resp.text
|
||||
|
||||
trigger.roles = [x.id for x in Role.select() if x.name == 'foobar']
|
||||
workflow.store()
|
||||
|
@ -1550,6 +1550,51 @@ def test_backoffice_multi_actions(pub):
|
|||
assert formdata.status != 'wf-accepted'
|
||||
|
||||
|
||||
def test_backoffice_multi_actions_jump(pub):
|
||||
create_superuser(pub)
|
||||
create_environment(pub)
|
||||
formdef = FormDef.get_by_urlname('form-title')
|
||||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/management/form-title/')
|
||||
|
||||
workflow = Workflow.get_default_workflow()
|
||||
workflow.id = '2'
|
||||
workflow.store()
|
||||
formdef.workflow_id = workflow.id
|
||||
formdef.store()
|
||||
|
||||
resp = app.get('/backoffice/management/form-title/')
|
||||
assert 'select[]' not in resp.forms['multi-actions'].fields
|
||||
|
||||
resp.forms['listing-settings']['filter'] = 'new'
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
assert 'select[]' not in resp.forms['multi-actions'].fields
|
||||
|
||||
# add identifier to jumps
|
||||
workflow.get_status('new').items[1].identifier = 'accept'
|
||||
workflow.get_status('new').items[2].identifier = 'reject'
|
||||
workflow.get_status('new').items[2].require_confirmation = True
|
||||
workflow.store()
|
||||
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
assert 'select[]' in resp.forms['multi-actions'].fields
|
||||
assert len(resp.pyquery.find('#multi-actions div.buttons button')) == 2
|
||||
assert len(resp.pyquery.find('#multi-actions div.buttons button[data-ask-for-confirmation]')) == 1
|
||||
|
||||
ids = []
|
||||
for checkbox in resp.forms[0].fields['select[]'][1:6]:
|
||||
ids.append(checkbox._value)
|
||||
checkbox.checked = True
|
||||
resp = resp.forms['multi-actions'].submit('button-action-st-accept')
|
||||
assert '?job=' in resp.location
|
||||
resp = resp.follow()
|
||||
assert 'Executing task "Accept" on forms' in resp.text
|
||||
assert '>completed<' in resp.text
|
||||
for id in ids:
|
||||
assert formdef.data_class().get(id).status == 'wf-accepted'
|
||||
|
||||
|
||||
def test_backoffice_statistics_with_no_formdefs(pub):
|
||||
create_user(pub)
|
||||
create_environment(pub)
|
||||
|
@ -6442,7 +6487,7 @@ def test_backoffice_custom_view(pub):
|
|||
resp.forms['listing-settings']['user-label'].checked = False
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
# filters
|
||||
resp.forms[0]['filter-2'].checked = True
|
||||
resp.forms['listing-settings']['filter-2'].checked = True
|
||||
resp = resp.forms['listing-settings'].submit()
|
||||
|
||||
resp.forms['listing-settings']['filter-2-value'] = 'baz'
|
||||
|
|
|
@ -1646,19 +1646,32 @@ class FormPage(Directory):
|
|||
def listing_top_actions(self):
|
||||
return ''
|
||||
|
||||
def get_multi_actions(self, user):
|
||||
# filter global manual actions to get those that can be run by the
|
||||
# user, either because of actual roles, or because the action is
|
||||
# accessible to functions.
|
||||
def get_multi_actions(self, user, status_filter):
|
||||
global_actions = self.formdef.workflow.get_global_manual_actions()
|
||||
|
||||
if status_filter not in ('open', 'waiting', 'done', 'all'):
|
||||
# when the listing is filtered on a specific status, include
|
||||
# manual jumps with identifiers
|
||||
try:
|
||||
status = self.formdef.workflow.get_status(status_filter)
|
||||
except KeyError:
|
||||
status = None
|
||||
else:
|
||||
global_actions.extend(status.get_status_manual_actions())
|
||||
|
||||
mass_actions = []
|
||||
for action_dict in global_actions:
|
||||
action_dict['roles'] = [x for x in user.get_roles() if x in action_dict.get('roles') or []]
|
||||
# filter actions to get those that can be run by the user,
|
||||
# either because of actual roles, or because the action is
|
||||
# accessible to functions.
|
||||
if not logged_users_role().id in action_dict.get('roles') or []:
|
||||
action_dict['roles'] = [x for x in user.get_roles() if x in action_dict.get('roles') or []]
|
||||
if action_dict['roles']:
|
||||
# action is accessible with user roles, remove mentions of functions
|
||||
action_dict['functions'] = []
|
||||
if action_dict['functions'] or action_dict['roles']:
|
||||
mass_actions.append(action_dict)
|
||||
|
||||
return mass_actions
|
||||
|
||||
def _q_index(self):
|
||||
|
@ -1692,19 +1705,22 @@ class FormPage(Directory):
|
|||
if get_request().get_query():
|
||||
qs = '?' + get_request().get_query()
|
||||
|
||||
multi_actions = self.get_multi_actions(get_request().user)
|
||||
multi_actions = self.get_multi_actions(get_request().user,
|
||||
status_filter=selected_filter)
|
||||
multi_form = Form(id='multi-actions')
|
||||
for action in multi_actions:
|
||||
attrs = {}
|
||||
if action.get('functions'):
|
||||
for function in action.get('functions'):
|
||||
# dashes are replaced by underscores to prevent HTML5
|
||||
# normalization to CamelCase.
|
||||
attrs['data-visible_for_%s' % function.replace('-', '_')] = 'true'
|
||||
else:
|
||||
attrs['data-visible_for_all'] = 'true'
|
||||
if getattr(action['action'], 'require_confirmation', False):
|
||||
attrs['data-ask-for-confirmation'] = 'true'
|
||||
multi_form.add_submit('button-action-%s' % action['action'].id, action['action'].name, attrs=attrs)
|
||||
if not get_request().form.get('ajax') == 'true':
|
||||
multi_form = Form(id='multi-actions')
|
||||
for action in multi_actions:
|
||||
attrs = {}
|
||||
if action.get('functions'):
|
||||
for function in action.get('functions'):
|
||||
# dashes are replaced by underscores to prevent HTML5
|
||||
# normalization to CamelCase.
|
||||
attrs['data-visible_for_%s' % function.replace('-', '_')] = 'true'
|
||||
else:
|
||||
attrs['data-visible_for_all'] = 'true'
|
||||
multi_form.add_submit('button-action-%s' % action['action'].id, action['action'].name, attrs=attrs)
|
||||
if multi_form.is_submitted() and get_request().form.get('select[]'):
|
||||
for action in multi_actions:
|
||||
if multi_form.get_submit() == 'button-action-%s' % action['action'].id:
|
||||
|
@ -1719,13 +1735,17 @@ class FormPage(Directory):
|
|||
limit=int(limit), offset=int(offset), query=query,
|
||||
order_by=order_by, criterias=criterias,
|
||||
include_checkboxes=bool(multi_actions))
|
||||
|
||||
if get_response().status_code == 302:
|
||||
# catch early redirect
|
||||
return table
|
||||
|
||||
multi_form.widgets.append(HtmlWidget(table))
|
||||
if not multi_actions:
|
||||
multi_form.widgets.append(HtmlWidget('<div class="buttons"></div>'))
|
||||
if get_request().form.get('ajax') == 'true':
|
||||
get_response().filter = {'raw': True}
|
||||
return table
|
||||
return multi_form.render()
|
||||
|
||||
view_name = self.view.title if self.view else _('Listing')
|
||||
html_top('management', '%s - %s' % (view_name, self.formdef.name))
|
||||
|
@ -1735,11 +1755,7 @@ class FormPage(Directory):
|
|||
r += get_session().display_message()
|
||||
r += self.listing_top_actions()
|
||||
r += htmltext('</div>')
|
||||
if multi_actions:
|
||||
multi_form.widgets.append(HtmlWidget(table))
|
||||
r += multi_form.render()
|
||||
else:
|
||||
r += table
|
||||
r += multi_form.render()
|
||||
|
||||
get_response().filter['sidebar'] = self.get_formdata_sidebar(qs) + \
|
||||
self.get_fields_sidebar(selected_filter, fields, limit=limit,
|
||||
|
@ -1765,7 +1781,13 @@ class FormPage(Directory):
|
|||
publisher.substitutions.feed(publisher)
|
||||
publisher.substitutions.feed(self.formdef)
|
||||
publisher.substitutions.feed(formdata)
|
||||
formdata.perform_global_action(self.action['action'].id, self.user)
|
||||
if getattr(self.action['action'], 'status_action', False):
|
||||
# manual jump action
|
||||
from wcs.wf.jump import jump_and_perform
|
||||
jump_and_perform(formdata, self.action['action'].action)
|
||||
else:
|
||||
# global action
|
||||
formdata.perform_global_action(self.action['action'].id, self.user)
|
||||
|
||||
item_ids = get_request().form['select[]']
|
||||
if '_all' in item_ids:
|
||||
|
|
|
@ -151,9 +151,19 @@ $(document).on('backoffice-filter-change', function(event, listing_settings) {
|
|||
beforeSend: function() { $('#more-user-links, #listing, #statistics').addClass('activity'); },
|
||||
complete: function() { $('#more-user-links, #listing, #statistics').removeClass('activity'); },
|
||||
success: function(html) {
|
||||
$('#page-links').remove();
|
||||
$('#listing').replaceWith(html);
|
||||
$('#statistics').replaceWith(html);
|
||||
var $html = $(html);
|
||||
var $listing = $html;
|
||||
if ($listing.is('form')) {
|
||||
// mass action
|
||||
$listing = $listing.find('#listing');
|
||||
$('#multi-actions div.buttons').replaceWith($html.find('div.buttons'));
|
||||
$('#listing').replaceWith($listing);
|
||||
$('#page-links').replaceWith($html.find('#page-links'));
|
||||
} else {
|
||||
$('#page-links').remove();
|
||||
$('#listing').replaceWith($listing);
|
||||
}
|
||||
$('#statistics').replaceWith($html);
|
||||
if (typeof(wcs_draw_graphs) !== 'undefined') {
|
||||
wcs_draw_graphs();
|
||||
}
|
||||
|
|
|
@ -469,7 +469,6 @@ class Workflow(StorableObject):
|
|||
actions.append({'action': action, 'roles': roles, 'functions': functions})
|
||||
return actions
|
||||
|
||||
|
||||
def get_global_actions_for_user(self, formdata, user):
|
||||
if not user:
|
||||
return []
|
||||
|
@ -1650,6 +1649,32 @@ class WorkflowStatus(object):
|
|||
waitpoint = item.waitpoint or waitpoint
|
||||
return bool(endpoint or waitpoint)
|
||||
|
||||
def get_status_manual_actions(self):
|
||||
actions = []
|
||||
status_id = self.id
|
||||
|
||||
class StatusAction:
|
||||
def __init__(self, action):
|
||||
self.id = 'st-%s' % action.identifier
|
||||
self.status_id = status_id
|
||||
self.action_id = action.identifier
|
||||
self.name = action.get_label()
|
||||
self.status_action = True
|
||||
self.require_confirmation = action.require_confirmation
|
||||
self.action = action
|
||||
|
||||
for action in self.items or []:
|
||||
if not isinstance(action, ChoiceWorkflowStatusItem):
|
||||
continue
|
||||
if not action.identifier:
|
||||
continue
|
||||
roles = action.by
|
||||
functions = [x for x in roles if x in self.parent.roles]
|
||||
roles = [x for x in roles if x not in self.parent.roles]
|
||||
if functions or roles:
|
||||
actions.append({'action': StatusAction(action), 'roles': roles, 'functions': functions})
|
||||
return actions
|
||||
|
||||
def get_contrast_color(self):
|
||||
colour = self.colour or 'ffffff'
|
||||
return misc.get_foreground_colour(colour)
|
||||
|
|
Loading…
Reference in New Issue