From b44a06f97ce8a433fcc7fde0af6b23e4fc002977 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Thu, 7 Sep 2017 23:36:57 +0200 Subject: [PATCH] wf: add DispatchUserWorkflowStatusItem --- tests/test_backoffice_pages.py | 84 +++++++++++++++++++++++++++++++++++++++++- tests/test_workflows.py | 2 +- wcs/backoffice/management.py | 4 ++ wcs/formdef.py | 7 ++++ wcs/forms/backoffice.py | 2 + wcs/forms/common.py | 3 +- wcs/wf/dispatch.py | 81 ++++++++++++++++++++++++++++++++++++++++ wcs/workflows.py | 9 ++++- 8 files changed, 187 insertions(+), 5 deletions(-) diff --git a/tests/test_backoffice_pages.py b/tests/test_backoffice_pages.py index 780b5f1..2ee9ccf 100644 --- a/tests/test_backoffice_pages.py +++ b/tests/test_backoffice_pages.py @@ -26,7 +26,7 @@ from wcs.workflows import (Workflow, CommentableWorkflowStatusItem, DisplayMessageWorkflowStatusItem, JumpOnSubmitWorkflowStatusItem, WorkflowCriticalityLevel, WorkflowBackofficeFieldsFormDef) -from wcs.wf.dispatch import DispatchWorkflowStatusItem +from wcs.wf.dispatch import DispatchWorkflowStatusItem, DispatchUserWorkflowStatusItem from wcs.wf.form import FormWorkflowStatusItem, WorkflowFormFieldsFormDef from wcs.wf.jump import JumpWorkflowStatusItem from wcs.wf.wscall import WebserviceCallStatusItem @@ -3780,3 +3780,85 @@ query_string_allowed_vars = foo,bar assert resp.location == 'http://example.net/backoffice/submission/test/' resp = resp.follow() assert '

XbarY

' in resp.body + +def test_backoffice_dispatch_user(pub): + user = create_user(pub) + create_environment(pub) + + target_role = Role(name='target') + target_role.allows_backoffice_access = True + target_role.store() + members = [] + for i in range(10): + member = pub.user_class(name='member%s' % i) + member.roles = [str(target_role.id)] + member.store() + account = PasswordAccount(id=member.name) + account.set_password(member.name) + account.user_id = member.id + account.store() + members.append(member) + + wf = Workflow(name='resubmit') + st1 = wf.add_status('Status1') + st2 = wf.add_status('Status2') + st3 = wf.add_status('Status3') + wf.roles['_responsible'] = 'Responsible' + + dispatch = DispatchUserWorkflowStatusItem() + dispatch.id = '_dispatch_user' + dispatch.by = user.roles[:] + dispatch.role_key = '_responsible' + dispatch.members_of_role = str(target_role.id) + + st1.items.append(dispatch) + dispatch.parent = st1 + + jump = JumpOnSubmitWorkflowStatusItem() + jump.id = '_jump' + jump.status = st2.id + st1.items.append(jump) + jump.parent = st1 + + jump1 = ChoiceWorkflowStatusItem() + jump1.id = '_jump_responible' + jump1.label = 'jump for responsible' + jump1.by = ['_responsible'] + jump1.status = st3.id + st2.items.append(jump1) + jump1.parent = st2 + + jump2 = ChoiceWorkflowStatusItem() + jump2.id = '_jump_submitter' + jump2.label = 'jump for submitter' + jump2.by = ['_submitter'] + jump2.status = st3.id + st2.items.append(jump2) + jump2.parent = st2 + + wf.store() + + formdef = FormDef.get_by_urlname('form-title') + formdef.fields[0].varname = 'foo' + formdef.workflow_id = wf.id + formdef.store() + + formdata = formdef.data_class()() + formdata.just_created() + formdata.data = {'1': 'XXX'} + formdata.store() + + member = members[1] + app = login(get_app(pub)) + resp = app.get(formdata.get_url(backoffice=True)) + resp = app.get(formdata.get_url(backoffice=True)) + resp.form['user'].value = str(member.id) + resp = resp.form.submit('button_dispatch_user') + resp = resp.follow() + app.get('/logout') + app = login(get_app(pub), username=member.name, password=member.name) + resp = app.get(formdata.get_url(backoffice=True)) + + assert 'jump for responsible' in resp + assert 'jump for submitter' not in resp + diff --git a/tests/test_workflows.py b/tests/test_workflows.py index 8768653..3de4e9d 100644 --- a/tests/test_workflows.py +++ b/tests/test_workflows.py @@ -27,7 +27,7 @@ from wcs.workflows import (Workflow, WorkflowStatusItem, AttachmentEvolutionPart, WorkflowBackofficeFieldsFormDef) from wcs.wf.anonymise import AnonymiseWorkflowStatusItem from wcs.wf.criticality import ModifyCriticalityWorkflowStatusItem, MODE_INC, MODE_DEC, MODE_SET -from wcs.wf.dispatch import DispatchWorkflowStatusItem +from wcs.wf.dispatch import DispatchWorkflowStatusItem, DispatchUserWorkflowStatusItem from wcs.wf.form import FormWorkflowStatusItem, WorkflowFormFieldsFormDef from wcs.wf.jump import JumpWorkflowStatusItem, _apply_timeouts from wcs.wf.timeout_jump import TimeoutWorkflowStatusItem diff --git a/wcs/backoffice/management.py b/wcs/backoffice/management.py index 8383772..e21c880 100644 --- a/wcs/backoffice/management.py +++ b/wcs/backoffice/management.py @@ -212,6 +212,7 @@ class UserViewDirectory(Directory): # display list of open formdata for the user formdefs = [x for x in FormDef.select() if not x.skip_from_360_view] user_roles = set([logged_users_role().id] + (get_request().user.roles or [])) + user_roles.add('_user-%s' % get_request().user.id) criterias = [Equal('is_at_endpoint', False), Equal('user_id', str(self.user.id)), Contains('formdef_id', [x.id for x in formdefs]), @@ -461,6 +462,7 @@ class ManagementDirectory(Directory): user = get_request().user user_roles = [logged_users_role().id] + (user.roles or []) + user_roles.add('_user-%s' % user.id) forms_without_pending_stuff = [] forms_with_pending_stuff = [] @@ -733,6 +735,7 @@ class ManagementDirectory(Directory): def get_global_listing_criterias(self): parsed_values = {} user_roles = [logged_users_role().id] + (get_request().user.roles or []) + user_roles.add('_user-%s' % user.id) criterias = get_global_criteria(get_request(), parsed_values) query_parameters = (get_request().form or {}).copy() query_parameters.pop('callback', None) # when using jsonp @@ -2043,6 +2046,7 @@ class FormBackOfficeStatusPage(FormStatusPage): if formdata.user_id and get_publisher().is_using_postgresql(): # display list of open formdata for the same user user_roles = [logged_users_role().id] + (get_request().user.roles or []) + user_roles.add('_user-%s' % user.id) criterias = [Equal('is_at_endpoint', False), Equal('user_id', str(formdata.user_id)), Intersects('concerned_roles_array', user_roles), diff --git a/wcs/formdef.py b/wcs/formdef.py index 9c7136b..c2761aa 100644 --- a/wcs/formdef.py +++ b/wcs/formdef.py @@ -1186,6 +1186,9 @@ class FormDef(StorableObject): for role_id in formdata.workflow_roles.values(): if role_id in (user.roles or []): return True + # if user was directly attributed a function + if ('_user-%s' % user.id) in formdata.workflow_roles.values(): + return True # if no formdata was given, lookup if there are some existing formdata # where the user has access. @@ -1194,6 +1197,8 @@ class FormDef(StorableObject): for role_id in user.roles or []: if data_class.get_ids_with_indexed_value('workflow_roles', role_id): return True + if data_class.get_ids_with_indexed_value('workflow_roles', '_user-%s' % user.id): + return True return False @@ -1210,6 +1215,7 @@ class FormDef(StorableObject): user_roles = set(user.roles) else: user_roles = set([]) + user_roles.add('_user-%s' % user.id) user_roles.add(logged_users_role().id) def ensure_role_are_strings(roles): @@ -1243,6 +1249,7 @@ class FormDef(StorableObject): user_roles = set(user.roles) else: user_roles = set([]) + user_roles.add('_user-%s' % user.id) if not self.workflow_roles: self.workflow_roles = {} diff --git a/wcs/forms/backoffice.py b/wcs/forms/backoffice.py index b738931..cb7a49b 100644 --- a/wcs/forms/backoffice.py +++ b/wcs/forms/backoffice.py @@ -126,6 +126,7 @@ class FormDefUI(object): item_ids = [x for x in item_ids if x not in drafts] elif selected_filter == 'waiting': user_roles = [logged_users_role().id] + (user.roles or []) + user_roles.append('_user-%s' % user.id) item_ids = formdata_class.get_actionable_ids(user_roles) else: applied_filters = [] @@ -156,6 +157,7 @@ class FormDefUI(object): # to consider treating roles. if not user.is_admin: user_roles = set(user.roles or []) + user_roles.add('_user-%s' % user.id) concerned_ids = set() for role in user_roles: concerned_ids |= set(formdata_class.get_ids_with_indexed_value( diff --git a/wcs/forms/common.py b/wcs/forms/common.py index 3f008e5..14bf8d0 100644 --- a/wcs/forms/common.py +++ b/wcs/forms/common.py @@ -495,7 +495,8 @@ class FormStatusPage(Directory): r += form.render() self.filled.mark_as_being_visited() related_user_forms = getattr(self.filled, 'related_user_forms', None) or [] - user_roles = set(get_request().user.roles) + user_roles = set(get_request().user.roles or []) + user_roles.append('_user-%s' % get_request().user.id) for user_formdata in related_user_forms: if user_roles.intersection(user_formdata.actions_roles): user_formdata.mark_as_being_visited() diff --git a/wcs/wf/dispatch.py b/wcs/wf/dispatch.py index 9c75839..7577175 100644 --- a/wcs/wf/dispatch.py +++ b/wcs/wf/dispatch.py @@ -17,10 +17,12 @@ import collections import xml.etree.ElementTree as ET +from quixote import get_publisher from qommon import _ from qommon.form import * from qommon import get_logger from wcs.roles import Role, get_user_roles +from wcs.users import User from wcs.workflows import XmlSerialisable, WorkflowStatusItem, register_item_class @@ -208,3 +210,82 @@ class DispatchWorkflowStatusItem(WorkflowStatusItem): formdata.store() register_item_class(DispatchWorkflowStatusItem) + + +class DispatchUserWorkflowStatusItem(WorkflowStatusItem): + description = N_('Dispatch user') + key = 'dispatch-user' + endpoint = False + waitpoint = True + ok_in_global_action = False + + by = [] + label = None + role_key = None + members_of_role = None + backoffice_info_text = None + + def get_parameters(self): + return ('by', 'label', 'role_key', 'role_key', 'members_of_role', 'backoffice_info_text') + + def render_as_line(self): + function = self.parent.parent.roles[self.role_key] + if self.by: + return _('Dispatch user to function %s by %s') % ( + function, + self.render_list_of_roles(self.by)) + else: + return _('Dispatch user to function %s (not completed)') % function + + def fill_form(self, form, formdata, user): + function = self.parent.parent.roles[self.role_key] + label = self.label + if not label: + label = _('Dispatch user to function %s') % function + members = get_publisher().user_class.get_users_with_role(self.members_of_role) + users = [(None, '---', None)] + users += [(str(member.id), member.display_name, str(member.id)) for member in members] + form.add(SingleSelectWidget, 'user', title=_('User'), + required=True, options=users) + form.add_submit('button%s' % self.id, label) + form.get_widget('button%s' % self.id).backoffice_info_text = self.backoffice_info_text + + def submit_form(self, form, formdata, user, evo): + if form.get_submit() != 'button%s' % self.id: + return + + user_id = form.get_widget('user').parse() + + if not formdata.workflow_roles: + formdata.workflow_roles = {} + formdata.workflow_roles[self.role_key] = '_user-%s' % user_id + formdata.store() + + def add_parameters_widgets(self, form, parameters, prefix='', formdef=None): + if 'by' in parameters: + form.add(WidgetList, '%sby' % prefix, title=_('By'), element_type=SingleSelectWidget, + value=self.by, + add_element_label=_('Add Role'), + element_kwargs={ + 'render_br': False, + 'options': [(None, '---', None)] + self.get_list_of_roles()}) + if 'label' in parameters: + form.add(StringWidget, '%slabel' % prefix, title=_('Button Label'), + value=self.label or _('Resubmit')) + if 'role_key' in parameters: + if not self.parent.parent.roles: + self.parent.parent.roles = {} + form.add(SingleSelectWidget, '%srole_key' % prefix, + title=_('Function to Set'), value=self.role_key, + options=[(None, '----')] + self.parent.parent.roles.items()) + if 'members_of_role' in parameters: + form.add(SingleSelectWidget, '%smember_of_roles' % prefix, + title=_('Members of role'), value=self.members_of_role, + options=[(None, '----')] + self.get_list_of_roles()) + if 'backoffice_info_text' in parameters: + form.add(WysiwygTextWidget, '%sbackoffice_info_text' % prefix, + title=_('Information Text for Backoffice'), + value=self.backoffice_info_text) + + +register_item_class(DispatchUserWorkflowStatusItem) diff --git a/wcs/workflows.py b/wcs/workflows.py index 41717c9..df559e8 100644 --- a/wcs/workflows.py +++ b/wcs/workflows.py @@ -403,7 +403,7 @@ class Workflow(StorableObject): if isinstance(trigger, WorkflowGlobalActionManualTrigger): roles = [get_role_translation(formdata, x) for x in (trigger.roles or [])] - if set(roles).intersection(user.roles or []): + if set(roles).intersection(user.roles or []) or ('_user-%s' % user.id) in roles: actions.append(action) break return actions @@ -1334,6 +1334,8 @@ class WorkflowStatus(object): role = get_role_translation(filled, role) if role in (user.roles or []): break + if role == '_user-%s' % user.id: + break else: continue next_url = item.submit_form(form, filled, user, evo) @@ -1381,6 +1383,7 @@ class WorkflowStatus(object): if user: user_roles = set(user.roles or []) user_roles.add(logged_users_role().id) + user_roles.add('_user-%s' % user.id) else: user_roles = set([]) @@ -1536,6 +1539,8 @@ class WorkflowStatusItem(XmlSerialisable): role = get_role_translation(formdata, role) if role in (user.roles or []): return True + if user and role == '_user-%s' % user.id: + return True return False @@ -2310,7 +2315,7 @@ class DisplayMessageWorkflowStatusItem(WorkflowStatusItem): break elif user: role = get_role_translation(filled, role) - if role in (user.roles or []): + if role in (user.roles or []) or '_user-%s' % user == role: break else: return '' -- 2.1.4