workflows: add possibility of backoffice info texts for workflows (#7738)

This commit is contained in:
Frédéric Péters 2015-08-26 20:28:07 +02:00
parent c14d3d8f48
commit 99e00744f3
8 changed files with 201 additions and 9 deletions

View File

@ -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'] = '<p>Hello</p>'
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 == '<p>Hello</p>'
def test_workflows_delete():
Workflow.wipe()
workflow = Workflow(name='foo')

View File

@ -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 = '<p>Foo</p>'
workflow.store()
resp = app.get('/backoffice/management/form-title/%s/' % number31)
assert 'backoffice-description' in resp.body
assert '<p>Foo</p>' in resp.body
# add an info text to the button
commentable.backoffice_info_text = '<p>Bar</p>'
workflow.store()
resp = app.get('/backoffice/management/form-title/%s/' % number31)
assert 'backoffice-description' in resp.body
assert '<p>Foo</p>' in resp.body
assert '<p>Bar</p>' 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 '<p>Foo</p>' in resp.body
assert '<p>Bar</p>' in resp.body
# add info text to second button
commentable2.backoffice_info_text = '<p>Baz</p>'
workflow.store()
resp = app.get('/backoffice/management/form-title/%s/' % number31)
assert 'backoffice-description' in resp.body
assert not '<p>Foo</p>' in resp.body
assert '<p>Bar</p>' in resp.body
assert not '<p>Baz</p>' 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.

View File

@ -279,7 +279,6 @@ def test_variables_formdef():
wf2 = assert_import_export_works(wf)
assert wf2.variables_formdef.fields[0].label == 'Test'
def test_wscall_action():
wf = Workflow(name='status')
st1 = wf.add_status('Status1', 'st1')
@ -301,3 +300,19 @@ def test_wscall_action():
assert wscall2.post == False
assert wscall2.request_signature_key == 'key'
assert wscall2.post_data == {'one': '1', 'two': '=2', 'good:name': 'ok'}
def test_backoffice_info_text():
wf = Workflow(name='info texts')
st1 = wf.add_status('Status1', 'st1')
st1.backoffice_info_text = '<p>Foo</p>'
commentable = CommentableWorkflowStatusItem()
commentable.id = '_commentable'
commentable.by = ['_submitter', '_receiver']
commentable.backoffice_info_text = '<p>Bar</p>'
st1.items.append(commentable)
commentable.parent = st1
wf2 = assert_import_export_works(wf)
assert wf2.possible_status[0].backoffice_info_text == '<p>Foo</p>'
assert wf2.possible_status[0].items[0].backoffice_info_text == '<p>Bar</p>'

View File

@ -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('<h2>%s - ') % _('Workflow')
@ -425,6 +427,8 @@ class WorkflowStatusPage(Directory):
r += htmltext('<li><a href="visibility" rel="popup">%s</a></li>') % _('Change Status Visibility')
r += htmltext('<li><a href="endpoint" rel="popup">%s</a></li>') % _('Change Terminal Status')
r += htmltext('<li><a href="colour" rel="popup">%s</a></li>') % _('Change Status Colour')
r += htmltext('<li><a href="backoffice-info-text" rel="popup">%s</a></li>'
) % _('Change Backoffice Information Text')
r += htmltext('<li><a href="delete" rel="popup">%s</a></li>') % _('Delete')
r += htmltext('</ul>')
r += htmltext('<div id="new-field">')
@ -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 = ['']

View File

@ -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('<div class="backoffice-description bo-block">')
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('<div class="action-info-text">')
r += htmltext(button.backoffice_info_text)
r += htmltext('</div>')
r += htmltext('</div>')
r += htmltext('<a href="..">%s</a>') % _('Back to Listing')
return r.getvalue()

View File

@ -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;

View File

@ -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)

View File

@ -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: