From 327178e3654ea24354ca167187f5753e822e430a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Wed, 26 Aug 2015 20:28:07 +0200 Subject: [PATCH] workflows: add possibility of backoffice info texts for workflows (#7738) --- tests/test_admin_pages.py | 8 ++++ tests/test_backoffice_pages.py | 80 ++++++++++++++++++++++++++++++++++++- tests/test_workflow_import.py | 17 ++++++++ wcs/admin/workflows.py | 28 ++++++++++++- wcs/forms/common.py | 15 +++++++ wcs/qommon/static/css/dc2/admin.css | 13 ++++++ wcs/wf/attachment.py | 9 ++++- wcs/workflows.py | 40 +++++++++++++++++-- 8 files changed, 202 insertions(+), 8 deletions(-) diff --git a/tests/test_admin_pages.py b/tests/test_admin_pages.py index 05010e4..7cf7da7 100644 --- a/tests/test_admin_pages.py +++ b/tests/test_admin_pages.py @@ -1220,6 +1220,14 @@ def test_workflows_edit_status(): resp = resp.follow() assert Workflow.get(1).possible_status[0].colour == 'FF0000' + resp = resp.click('Change Backoffice Information Text') + assert resp.forms[0]['backoffice_info_text'].value == '' + resp.forms[0]['backoffice_info_text'] = '

Hello

' + resp = resp.forms[0].submit() + assert resp.location == 'http://example.net/backoffice/workflows/1/status/1/' + resp = resp.follow() + assert Workflow.get(1).possible_status[0].backoffice_info_text == '

Hello

' + def test_workflows_delete(): Workflow.wipe() workflow = Workflow(name='foo') diff --git a/tests/test_backoffice_pages.py b/tests/test_backoffice_pages.py index 93e75bc..9823b2e 100644 --- a/tests/test_backoffice_pages.py +++ b/tests/test_backoffice_pages.py @@ -12,7 +12,7 @@ from wcs.qommon import errors, sessions from qommon.ident.password_accounts import PasswordAccount from wcs.qommon.http_request import HTTPRequest from wcs.roles import Role -from wcs.workflows import Workflow +from wcs.workflows import Workflow, CommentableWorkflowStatusItem from wcs.formdef import FormDef from wcs import fields @@ -393,6 +393,84 @@ def test_backoffice_handling(pub): assert FormDef.get_by_urlname('form-title').data_class().get(number31).status == 'wf-accepted' assert 'HELLO WORLD' in resp.body +def test_backoffice_info_text(pub): + create_user(pub) + create_environment(pub) + formdef = FormDef.get_by_urlname('form-title') + form_class = formdef.data_class() + + number31 = [x for x in form_class.select() if x.data['1'] == 'FOO BAR 30'][0].id + number31_status = [x for x in form_class.select() if x.data['1'] == 'FOO BAR 30'][0].status + + # attach a custom workflow + workflow = Workflow(name='info texts') + st1 = workflow.add_status('Status1', number31_status.split('-')[1]) + + commentable = CommentableWorkflowStatusItem() + commentable.id = '_commentable' + commentable.by = ['_submitter', '_receiver'] + commentable.button_label = 'CLICK ME!' + st1.items.append(commentable) + commentable.parent = st1 + + commentable2 = CommentableWorkflowStatusItem() + commentable2.id = '_commentable2' + commentable2.by = ['_submitter'] + commentable2.button_label = 'CLICK ME2!' + st1.items.append(commentable2) + commentable2.parent = st1 + + workflow.store() + + formdef.workflow_id = workflow.id + formdef.store() + + app = login(get_app(pub)) + + resp = app.get('/backoffice/management/form-title/%s/' % number31) + assert (' with the number %s.' % number31) in resp.body + assert 'CLICK ME!' in resp.body + assert not 'CLICK ME2!' in resp.body + assert not 'backoffice-description' in resp.body + + # add an info text to the status + st1.backoffice_info_text = '

Foo

' + workflow.store() + resp = app.get('/backoffice/management/form-title/%s/' % number31) + assert 'backoffice-description' in resp.body + assert '

Foo

' in resp.body + + # add an info text to the button + commentable.backoffice_info_text = '

Bar

' + workflow.store() + resp = app.get('/backoffice/management/form-title/%s/' % number31) + assert 'backoffice-description' in resp.body + assert '

Foo

' in resp.body + assert '

Bar

' in resp.body + + # remove info text from the status + st1.backoffice_info_text = None + workflow.store() + resp = app.get('/backoffice/management/form-title/%s/' % number31) + assert 'backoffice-description' in resp.body + assert not '

Foo

' in resp.body + assert '

Bar

' in resp.body + + # add info text to second button + commentable2.backoffice_info_text = '

Baz

' + workflow.store() + resp = app.get('/backoffice/management/form-title/%s/' % number31) + assert 'backoffice-description' in resp.body + assert not '

Foo

' in resp.body + assert '

Bar

' in resp.body + assert not '

Baz

' in resp.body + + # remove info text from first button + commentable.backoffice_info_text = None + workflow.store() + resp = app.get('/backoffice/management/form-title/%s/' % number31) + assert not 'backoffice-description' in resp.body + def test_backoffice_handling_post_dispatch(pub): # check a formdata that has been dispatched to another role is accessible # by an user with that role. diff --git a/tests/test_workflow_import.py b/tests/test_workflow_import.py index 9b7c41a..abac9db 100644 --- a/tests/test_workflow_import.py +++ b/tests/test_workflow_import.py @@ -277,3 +277,20 @@ def test_variables_formdef(): wf.variables_formdef.fields.append(StringField(label='Test', type='string')) wf2 = assert_import_export_works(wf) assert wf2.variables_formdef.fields[0].label == 'Test' + + +def test_backoffice_info_text(): + wf = Workflow(name='info texts') + st1 = wf.add_status('Status1', 'st1') + st1.backoffice_info_text = '

Foo

' + + commentable = CommentableWorkflowStatusItem() + commentable.id = '_commentable' + commentable.by = ['_submitter', '_receiver'] + commentable.backoffice_info_text = '

Bar

' + st1.items.append(commentable) + commentable.parent = st1 + + wf2 = assert_import_export_works(wf) + assert wf2.possible_status[0].backoffice_info_text == '

Foo

' + assert wf2.possible_status[0].items[0].backoffice_info_text == '

Bar

' diff --git a/wcs/admin/workflows.py b/wcs/admin/workflows.py index 214b184..b39f8d1 100644 --- a/wcs/admin/workflows.py +++ b/wcs/admin/workflows.py @@ -316,7 +316,8 @@ class WorkflowItemsDir(Directory): class WorkflowStatusPage(Directory): _q_exports = ['', 'delete', 'newitem', ('items', 'items_dir'), 'update_order', 'edit', 'reassign', 'visibility', - 'endpoint', 'colour'] + 'endpoint', 'colour', + ('backoffice-info-text', 'backoffice_info_text'),] def __init__(self, workflow, status_id, html_top): self.html_top = html_top @@ -332,7 +333,8 @@ class WorkflowStatusPage(Directory): def _q_index(self): self.html_top('%s - %s' % (_('Workflow'), self.workflow.name)) r = TemplateIO(html=True) - get_response().add_javascript(['jquery.js', 'jquery-ui.js', 'biglist.js', 'svg-pan-zoom.js']) + get_response().add_javascript(['jquery.js', 'jquery-ui.js', 'biglist.js', 'svg-pan-zoom.js', + 'ckeditor/ckeditor.js', 'qommon.wysiwyg.js', 'ckeditor/adapters/jquery.js']) get_response().add_javascript_code('$(function () { svgPanZoom("svg", {controlIconsEnabled: true}); });'); r += htmltext('

%s - ') % _('Workflow') @@ -425,6 +427,8 @@ class WorkflowStatusPage(Directory): r += htmltext('
  • %s
  • ') % _('Change Status Visibility') r += htmltext('
  • %s
  • ') % _('Change Terminal Status') r += htmltext('
  • %s
  • ') % _('Change Status Colour') + r += htmltext('
  • %s
  • ' + ) % _('Change Backoffice Information Text') r += htmltext('
  • %s
  • ') % _('Delete') r += htmltext('') r += htmltext('
    ') @@ -677,6 +681,26 @@ class WorkflowStatusPage(Directory): get_response().breadcrumb.append( ('colour', _('Status Colour')) ) return form.render() + def backoffice_info_text(self): + form = Form(enctype = 'multipart/form-data') + form.add(WysiwygTextWidget, 'backoffice_info_text', + title=_('Information text for backoffice'), + value=self.status.backoffice_info_text) + form.add_submit('submit', _('Submit')) + form.add_submit('cancel', _('Cancel')) + if form.get_widget('cancel').parse(): + return redirect('..') + + if form.is_submitted() and not form.has_errors(): + self.status.backoffice_info_text = form.get_widget('backoffice_info_text').parse() + self.workflow.store() + return redirect('.') + + self.html_top(title = _('Edit Backoffice Information Text')) + get_response().breadcrumb.append( ('backoffice_info_text', + _('Backoffice Information Text')) ) + return form.render() + class WorkflowStatusDirectory(Directory): _q_exports = [''] diff --git a/wcs/forms/common.py b/wcs/forms/common.py index 3ae5670..d1a1526 100644 --- a/wcs/forms/common.py +++ b/wcs/forms/common.py @@ -464,6 +464,21 @@ class FormStatusPage(Directory): if form: r += form.render() + if (self.filled.get_status() and self.filled.get_status().backoffice_info_text) or ( + form and any((getattr(button, 'backoffice_info_text', None) + for button in form.get_submit_widgets()))): + r += htmltext('
    ') + if self.filled.get_status().backoffice_info_text: + r += htmltext(self.filled.get_status().backoffice_info_text) + if form: + for button in form.get_submit_widgets(): + if not getattr(button, 'backoffice_info_text', None): + continue + r += htmltext('
    ') + r += htmltext(button.backoffice_info_text) + r += htmltext('
    ') + r += htmltext('
    ') + r += htmltext('%s') % _('Back to Listing') return r.getvalue() diff --git a/wcs/qommon/static/css/dc2/admin.css b/wcs/qommon/static/css/dc2/admin.css index 29809ce..3da0cd1 100644 --- a/wcs/qommon/static/css/dc2/admin.css +++ b/wcs/qommon/static/css/dc2/admin.css @@ -1012,6 +1012,19 @@ a#filter-settings { cursor: pointer; } +.bo-block.backoffice-description:before { + font-family: FontAwesome; + content: "\f05a"; /* info-circle */ + position: absolute; + left: 10px; + font-size: 2em; +} + +.bo-block.backoffice-description { + position: relative; + padding-left: 40px; +} + @media print { div#sidebar { display: none; diff --git a/wcs/wf/attachment.py b/wcs/wf/attachment.py index 0860ce0..721229f 100644 --- a/wcs/wf/attachment.py +++ b/wcs/wf/attachment.py @@ -68,6 +68,7 @@ class AddAttachmentWorkflowStatusItem(WorkflowStatusItem): required = False hint = None by = [] + backoffice_info_text = None def init(cls): FormStatusPage._q_extra_exports.append('attachment') @@ -92,6 +93,7 @@ class AddAttachmentWorkflowStatusItem(WorkflowStatusItem): required=self.required, hint=self.hint) if self.display_button: form.add_submit('button%s' % self.id, self.button_label or _('Upload File')) + 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_widget('attachment%s' % self.id): @@ -103,7 +105,8 @@ class AddAttachmentWorkflowStatusItem(WorkflowStatusItem): evo.add_part(AttachmentEvolutionPart.from_upload(f)) def get_parameters(self): - return ('by', 'required', 'title', 'display_title', 'button_label', 'display_button', 'hint') + return ('by', 'required', 'title', 'display_title', 'button_label', + 'display_button', 'hint', 'backoffice_info_text') def add_parameters_widgets(self, form, parameters, prefix='', formdef=None): if 'by' in parameters: @@ -126,6 +129,10 @@ class AddAttachmentWorkflowStatusItem(WorkflowStatusItem): form.add(CheckboxWidget, '%sdisplay_button' % prefix, title = _('Display Button'), value = self.display_button) if 'hint' in parameters: form.add(StringWidget, '%shint' % prefix, size=40, title=_('Hint'), value=self.hint) + if 'backoffice_info_text' in parameters: + form.add(WysiwygTextWidget, 'backoffice_info_text', + title=_('Information Text for Backoffice'), + value=self.backoffice_info_text) register_item_class(AddAttachmentWorkflowStatusItem) diff --git a/wcs/workflows.py b/wcs/workflows.py index ecfdd3a..53b484a 100644 --- a/wcs/workflows.py +++ b/wcs/workflows.py @@ -549,6 +549,7 @@ class WorkflowStatus(object): visibility = None forced_endpoint = False colour = 'FFFFFF' + backoffice_info_text = None def __init__(self, name = None): self.name = name @@ -722,6 +723,10 @@ class WorkflowStatus(object): if self.forced_endpoint: ET.SubElement(status, 'forced_endpoint').text = 'true' + if self.backoffice_info_text: + ET.SubElement(status, 'backoffice_info_text').text = unicode( + self.backoffice_info_text, charset) + visibility_node = ET.SubElement(status, 'visibility') for role in self.visibility or []: ET.SubElement(visibility_node, 'role').text = str(role) @@ -739,6 +744,8 @@ class WorkflowStatus(object): self.colour = elem.find('colour').text.encode(charset) if elem.find('forced_endpoint') is not None: self.forced_endpoint = (elem.find('forced_endpoint').text == 'true') + if elem.find('backoffice_info_text') is not None: + self.backoffice_info_text = elem.find('backoffice_info_text').text.encode(charset) self.visibility = [] for visibility_role in elem.findall('visibility/role'): @@ -1089,6 +1096,7 @@ class CommentableWorkflowStatusItem(WorkflowStatusItem): button_label = 0 # hack to handle legacy commentable items hint = None by = [] + backoffice_info_text = None def render_as_line(self): if self.by: @@ -1109,6 +1117,7 @@ class CommentableWorkflowStatusItem(WorkflowStatusItem): form.add_submit('button%s' % self.id, _('Add Comment')) elif self.button_label: form.add_submit('button%s' % self.id, self.button_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_widget('comment'): @@ -1129,7 +1138,8 @@ class CommentableWorkflowStatusItem(WorkflowStatusItem): self.add_parameters_widgets(form, self.get_parameters()) def get_parameters(self): - return ('label', 'button_label', 'by', 'hint', 'varname') + return ('label', 'button_label', 'by', 'hint', 'varname', + 'backoffice_info_text') def add_parameters_widgets(self, form, parameters, prefix='', formdef=None): if 'label' in parameters: @@ -1155,6 +1165,10 @@ class CommentableWorkflowStatusItem(WorkflowStatusItem): form.add(VarnameWidget, '%svarname' % prefix, title=_('Variable Name'), value=self.varname, hint=_('This will make the comment available in a variable named comment_varname.')) + if 'backoffice_info_text' in parameters: + form.add(WysiwygTextWidget, 'backoffice_info_text', + title=_('Information Text for Backoffice'), + value=self.backoffice_info_text) def button_label_export_to_xml(self, xml_item, charset, include_id=False): if self.button_label == 0: @@ -1193,6 +1207,7 @@ class ChoiceWorkflowStatusItem(WorkflowStatusJumpItem): label = None by = [] + backoffice_info_text = None def render_as_line(self): if self.label: @@ -1207,6 +1222,7 @@ class ChoiceWorkflowStatusItem(WorkflowStatusJumpItem): def fill_form(self, form, formdata, user): form.add_submit('button%s' % self.id, self.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: @@ -1226,9 +1242,13 @@ class ChoiceWorkflowStatusItem(WorkflowStatusJumpItem): add_element_label = _('Add Role'), element_kwargs={'render_br': False, 'options': [(None, '---')] + self.get_list_of_roles()}) + if 'backoffice_info_text' in parameters: + form.add(WysiwygTextWidget, 'backoffice_info_text', + title=_('Information Text for Backoffice'), + value=self.backoffice_info_text) def get_parameters(self): - return ('by', 'status', 'label') + return ('by', 'status', 'label', 'backoffice_info_text') register_item_class(ChoiceWorkflowStatusItem) @@ -1536,6 +1556,7 @@ class EditableWorkflowStatusItem(WorkflowStatusItem): by = [] status = None label = None + backoffice_info_text = None def render_as_line(self): if self.by: @@ -1548,6 +1569,7 @@ class EditableWorkflowStatusItem(WorkflowStatusItem): if not label: label = _('Edit Form') 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: @@ -1568,9 +1590,13 @@ class EditableWorkflowStatusItem(WorkflowStatusItem): options = [(None, '---')] + [(x.id, x.name) for x in self.parent.parent.possible_status]) if 'label' in parameters: form.add(StringWidget, '%slabel' % prefix, title = _('Button Label'), value = self.label) + if 'backoffice_info_text' in parameters: + form.add(WysiwygTextWidget, 'backoffice_info_text', + title=_('Information Text for Backoffice'), + value=self.backoffice_info_text) def get_parameters(self): - return ('by', 'status', 'label') + return ('by', 'status', 'label', 'backoffice_info_text') register_item_class(EditableWorkflowStatusItem) @@ -1622,6 +1648,7 @@ class ExportToModel(WorkflowStatusItem): attach_to_history = False directory_class = ExportToModelDirectory by = ['_receiver'] + backoffice_info_text = None def render_as_line(self): if self.label: @@ -1642,6 +1669,7 @@ class ExportToModel(WorkflowStatusItem): if not label: label = _('Create Document') 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: @@ -1707,6 +1735,10 @@ class ExportToModel(WorkflowStatusItem): form.add(UploadWidget, widget_name, directory='models', filename=filename, title=_('Model'), hint=hint, validation=self.model_file_validation, value=value) + if 'backoffice_info_text' in parameters: + form.add(WysiwygTextWidget, 'backoffice_info_text', + title=_('Information Text for Backoffice'), + value=self.backoffice_info_text) def get_directory_name(self): return qommon.misc.simplify(self.label or 'export_to_model', space='_') @@ -1729,7 +1761,7 @@ class ExportToModel(WorkflowStatusItem): raise TemplatingError(_('Unknown error in the template: %s') % str(e)) def get_parameters(self): - return ('by', 'label', 'model_file', 'attach_to_history') + return ('by', 'label', 'model_file', 'attach_to_history', 'backoffice_info_text') def model_file_export_to_xml(self, xml_item, charset, include_id=False): if not self.model_file: -- 2.5.1