0001-workflows-add-possibility-of-backoffice-info-texts-f.patch
tests/test_admin_pages.py | ||
---|---|---|
1220 | 1220 |
resp = resp.follow() |
1221 | 1221 |
assert Workflow.get(1).possible_status[0].colour == 'FF0000' |
1222 | 1222 | |
1223 |
resp = resp.click('Change Backoffice Information Text') |
|
1224 |
assert resp.forms[0]['backoffice_info_text'].value == '' |
|
1225 |
resp.forms[0]['backoffice_info_text'] = '<p>Hello</p>' |
|
1226 |
resp = resp.forms[0].submit() |
|
1227 |
assert resp.location == 'http://example.net/backoffice/workflows/1/status/1/' |
|
1228 |
resp = resp.follow() |
|
1229 |
assert Workflow.get(1).possible_status[0].backoffice_info_text == '<p>Hello</p>' |
|
1230 | ||
1223 | 1231 |
def test_workflows_delete(): |
1224 | 1232 |
Workflow.wipe() |
1225 | 1233 |
workflow = Workflow(name='foo') |
tests/test_backoffice_pages.py | ||
---|---|---|
12 | 12 |
from qommon.ident.password_accounts import PasswordAccount |
13 | 13 |
from wcs.qommon.http_request import HTTPRequest |
14 | 14 |
from wcs.roles import Role |
15 |
from wcs.workflows import Workflow |
|
15 |
from wcs.workflows import Workflow, CommentableWorkflowStatusItem
|
|
16 | 16 |
from wcs.formdef import FormDef |
17 | 17 |
from wcs import fields |
18 | 18 | |
... | ... | |
393 | 393 |
assert FormDef.get_by_urlname('form-title').data_class().get(number31).status == 'wf-accepted' |
394 | 394 |
assert 'HELLO WORLD' in resp.body |
395 | 395 | |
396 |
def test_backoffice_info_text(pub): |
|
397 |
create_user(pub) |
|
398 |
create_environment(pub) |
|
399 |
formdef = FormDef.get_by_urlname('form-title') |
|
400 |
form_class = formdef.data_class() |
|
401 | ||
402 |
number31 = [x for x in form_class.select() if x.data['1'] == 'FOO BAR 30'][0].id |
|
403 |
number31_status = [x for x in form_class.select() if x.data['1'] == 'FOO BAR 30'][0].status |
|
404 | ||
405 |
# attach a custom workflow |
|
406 |
workflow = Workflow(name='info texts') |
|
407 |
st1 = workflow.add_status('Status1', number31_status.split('-')[1]) |
|
408 | ||
409 |
commentable = CommentableWorkflowStatusItem() |
|
410 |
commentable.id = '_commentable' |
|
411 |
commentable.by = ['_submitter', '_receiver'] |
|
412 |
commentable.button_label = 'CLICK ME!' |
|
413 |
st1.items.append(commentable) |
|
414 |
commentable.parent = st1 |
|
415 | ||
416 |
commentable2 = CommentableWorkflowStatusItem() |
|
417 |
commentable2.id = '_commentable2' |
|
418 |
commentable2.by = ['_submitter'] |
|
419 |
commentable2.button_label = 'CLICK ME2!' |
|
420 |
st1.items.append(commentable2) |
|
421 |
commentable2.parent = st1 |
|
422 | ||
423 |
workflow.store() |
|
424 | ||
425 |
formdef.workflow_id = workflow.id |
|
426 |
formdef.store() |
|
427 | ||
428 |
app = login(get_app(pub)) |
|
429 | ||
430 |
resp = app.get('/backoffice/management/form-title/%s/' % number31) |
|
431 |
assert (' with the number %s.' % number31) in resp.body |
|
432 |
assert 'CLICK ME!' in resp.body |
|
433 |
assert not 'CLICK ME2!' in resp.body |
|
434 |
assert not 'backoffice-description' in resp.body |
|
435 | ||
436 |
# add an info text to the status |
|
437 |
st1.backoffice_info_text = '<p>Foo</p>' |
|
438 |
workflow.store() |
|
439 |
resp = app.get('/backoffice/management/form-title/%s/' % number31) |
|
440 |
assert 'backoffice-description' in resp.body |
|
441 |
assert '<p>Foo</p>' in resp.body |
|
442 | ||
443 |
# add an info text to the button |
|
444 |
commentable.backoffice_info_text = '<p>Bar</p>' |
|
445 |
workflow.store() |
|
446 |
resp = app.get('/backoffice/management/form-title/%s/' % number31) |
|
447 |
assert 'backoffice-description' in resp.body |
|
448 |
assert '<p>Foo</p>' in resp.body |
|
449 |
assert '<p>Bar</p>' in resp.body |
|
450 | ||
451 |
# remove info text from the status |
|
452 |
st1.backoffice_info_text = None |
|
453 |
workflow.store() |
|
454 |
resp = app.get('/backoffice/management/form-title/%s/' % number31) |
|
455 |
assert 'backoffice-description' in resp.body |
|
456 |
assert not '<p>Foo</p>' in resp.body |
|
457 |
assert '<p>Bar</p>' in resp.body |
|
458 | ||
459 |
# add info text to second button |
|
460 |
commentable2.backoffice_info_text = '<p>Baz</p>' |
|
461 |
workflow.store() |
|
462 |
resp = app.get('/backoffice/management/form-title/%s/' % number31) |
|
463 |
assert 'backoffice-description' in resp.body |
|
464 |
assert not '<p>Foo</p>' in resp.body |
|
465 |
assert '<p>Bar</p>' in resp.body |
|
466 |
assert not '<p>Baz</p>' in resp.body |
|
467 | ||
468 |
# remove info text from first button |
|
469 |
commentable.backoffice_info_text = None |
|
470 |
workflow.store() |
|
471 |
resp = app.get('/backoffice/management/form-title/%s/' % number31) |
|
472 |
assert not 'backoffice-description' in resp.body |
|
473 | ||
396 | 474 |
def test_backoffice_handling_post_dispatch(pub): |
397 | 475 |
# check a formdata that has been dispatched to another role is accessible |
398 | 476 |
# by an user with that role. |
tests/test_workflow_import.py | ||
---|---|---|
277 | 277 |
wf.variables_formdef.fields.append(StringField(label='Test', type='string')) |
278 | 278 |
wf2 = assert_import_export_works(wf) |
279 | 279 |
assert wf2.variables_formdef.fields[0].label == 'Test' |
280 | ||
281 | ||
282 |
def test_backoffice_info_text(): |
|
283 |
wf = Workflow(name='info texts') |
|
284 |
st1 = wf.add_status('Status1', 'st1') |
|
285 |
st1.backoffice_info_text = '<p>Foo</p>' |
|
286 | ||
287 |
commentable = CommentableWorkflowStatusItem() |
|
288 |
commentable.id = '_commentable' |
|
289 |
commentable.by = ['_submitter', '_receiver'] |
|
290 |
commentable.backoffice_info_text = '<p>Bar</p>' |
|
291 |
st1.items.append(commentable) |
|
292 |
commentable.parent = st1 |
|
293 | ||
294 |
wf2 = assert_import_export_works(wf) |
|
295 |
assert wf2.possible_status[0].backoffice_info_text == '<p>Foo</p>' |
|
296 |
assert wf2.possible_status[0].items[0].backoffice_info_text == '<p>Bar</p>' |
wcs/admin/workflows.py | ||
---|---|---|
316 | 316 |
class WorkflowStatusPage(Directory): |
317 | 317 |
_q_exports = ['', 'delete', 'newitem', ('items', 'items_dir'), |
318 | 318 |
'update_order', 'edit', 'reassign', 'visibility', |
319 |
'endpoint', 'colour'] |
|
319 |
'endpoint', 'colour', |
|
320 |
('backoffice-info-text', 'backoffice_info_text'),] |
|
320 | 321 | |
321 | 322 |
def __init__(self, workflow, status_id, html_top): |
322 | 323 |
self.html_top = html_top |
... | ... | |
332 | 333 |
def _q_index(self): |
333 | 334 |
self.html_top('%s - %s' % (_('Workflow'), self.workflow.name)) |
334 | 335 |
r = TemplateIO(html=True) |
335 |
get_response().add_javascript(['jquery.js', 'jquery-ui.js', 'biglist.js', 'svg-pan-zoom.js']) |
|
336 |
get_response().add_javascript(['jquery.js', 'jquery-ui.js', 'biglist.js', 'svg-pan-zoom.js', |
|
337 |
'ckeditor/ckeditor.js', 'qommon.wysiwyg.js', 'ckeditor/adapters/jquery.js']) |
|
336 | 338 |
get_response().add_javascript_code('$(function () { svgPanZoom("svg", {controlIconsEnabled: true}); });'); |
337 | 339 | |
338 | 340 |
r += htmltext('<h2>%s - ') % _('Workflow') |
... | ... | |
425 | 427 |
r += htmltext('<li><a href="visibility" rel="popup">%s</a></li>') % _('Change Status Visibility') |
426 | 428 |
r += htmltext('<li><a href="endpoint" rel="popup">%s</a></li>') % _('Change Terminal Status') |
427 | 429 |
r += htmltext('<li><a href="colour" rel="popup">%s</a></li>') % _('Change Status Colour') |
430 |
r += htmltext('<li><a href="backoffice-info-text" rel="popup">%s</a></li>' |
|
431 |
) % _('Change Backoffice Information Text') |
|
428 | 432 |
r += htmltext('<li><a href="delete" rel="popup">%s</a></li>') % _('Delete') |
429 | 433 |
r += htmltext('</ul>') |
430 | 434 |
r += htmltext('<div id="new-field">') |
... | ... | |
677 | 681 |
get_response().breadcrumb.append( ('colour', _('Status Colour')) ) |
678 | 682 |
return form.render() |
679 | 683 | |
684 |
def backoffice_info_text(self): |
|
685 |
form = Form(enctype = 'multipart/form-data') |
|
686 |
form.add(WysiwygTextWidget, 'backoffice_info_text', |
|
687 |
title=_('Information text for backoffice'), |
|
688 |
value=self.status.backoffice_info_text) |
|
689 |
form.add_submit('submit', _('Submit')) |
|
690 |
form.add_submit('cancel', _('Cancel')) |
|
691 |
if form.get_widget('cancel').parse(): |
|
692 |
return redirect('..') |
|
693 | ||
694 |
if form.is_submitted() and not form.has_errors(): |
|
695 |
self.status.backoffice_info_text = form.get_widget('backoffice_info_text').parse() |
|
696 |
self.workflow.store() |
|
697 |
return redirect('.') |
|
698 | ||
699 |
self.html_top(title = _('Edit Backoffice Information Text')) |
|
700 |
get_response().breadcrumb.append( ('backoffice_info_text', |
|
701 |
_('Backoffice Information Text')) ) |
|
702 |
return form.render() |
|
703 | ||
680 | 704 | |
681 | 705 |
class WorkflowStatusDirectory(Directory): |
682 | 706 |
_q_exports = [''] |
wcs/forms/common.py | ||
---|---|---|
464 | 464 |
if form: |
465 | 465 |
r += form.render() |
466 | 466 | |
467 |
if (self.filled.get_status() and self.filled.get_status().backoffice_info_text) or ( |
|
468 |
form and any((getattr(button, 'backoffice_info_text', None) |
|
469 |
for button in form.get_submit_widgets()))): |
|
470 |
r += htmltext('<div class="backoffice-description bo-block">') |
|
471 |
if self.filled.get_status().backoffice_info_text: |
|
472 |
r += htmltext(self.filled.get_status().backoffice_info_text) |
|
473 |
if form: |
|
474 |
for button in form.get_submit_widgets(): |
|
475 |
if not getattr(button, 'backoffice_info_text', None): |
|
476 |
continue |
|
477 |
r += htmltext('<div class="action-info-text">') |
|
478 |
r += htmltext(button.backoffice_info_text) |
|
479 |
r += htmltext('</div>') |
|
480 |
r += htmltext('</div>') |
|
481 | ||
467 | 482 |
r += htmltext('<a href="..">%s</a>') % _('Back to Listing') |
468 | 483 |
return r.getvalue() |
469 | 484 |
wcs/qommon/static/css/dc2/admin.css | ||
---|---|---|
1012 | 1012 |
cursor: pointer; |
1013 | 1013 |
} |
1014 | 1014 | |
1015 |
.bo-block.backoffice-description:before { |
|
1016 |
font-family: FontAwesome; |
|
1017 |
content: "\f05a"; /* info-circle */ |
|
1018 |
position: absolute; |
|
1019 |
left: 10px; |
|
1020 |
font-size: 2em; |
|
1021 |
} |
|
1022 | ||
1023 |
.bo-block.backoffice-description { |
|
1024 |
position: relative; |
|
1025 |
padding-left: 40px; |
|
1026 |
} |
|
1027 | ||
1015 | 1028 |
@media print { |
1016 | 1029 |
div#sidebar { |
1017 | 1030 |
display: none; |
wcs/wf/attachment.py | ||
---|---|---|
68 | 68 |
required = False |
69 | 69 |
hint = None |
70 | 70 |
by = [] |
71 |
backoffice_info_text = None |
|
71 | 72 | |
72 | 73 |
def init(cls): |
73 | 74 |
FormStatusPage._q_extra_exports.append('attachment') |
... | ... | |
92 | 93 |
required=self.required, hint=self.hint) |
93 | 94 |
if self.display_button: |
94 | 95 |
form.add_submit('button%s' % self.id, self.button_label or _('Upload File')) |
96 |
form.get_widget('button%s' % self.id).backoffice_info_text = self.backoffice_info_text |
|
95 | 97 | |
96 | 98 |
def submit_form(self, form, formdata, user, evo): |
97 | 99 |
if form.get_widget('attachment%s' % self.id): |
... | ... | |
103 | 105 |
evo.add_part(AttachmentEvolutionPart.from_upload(f)) |
104 | 106 | |
105 | 107 |
def get_parameters(self): |
106 |
return ('by', 'required', 'title', 'display_title', 'button_label', 'display_button', 'hint') |
|
108 |
return ('by', 'required', 'title', 'display_title', 'button_label', |
|
109 |
'display_button', 'hint', 'backoffice_info_text') |
|
107 | 110 | |
108 | 111 |
def add_parameters_widgets(self, form, parameters, prefix='', formdef=None): |
109 | 112 |
if 'by' in parameters: |
... | ... | |
126 | 129 |
form.add(CheckboxWidget, '%sdisplay_button' % prefix, title = _('Display Button'), value = self.display_button) |
127 | 130 |
if 'hint' in parameters: |
128 | 131 |
form.add(StringWidget, '%shint' % prefix, size=40, title=_('Hint'), value=self.hint) |
132 |
if 'backoffice_info_text' in parameters: |
|
133 |
form.add(WysiwygTextWidget, 'backoffice_info_text', |
|
134 |
title=_('Information Text for Backoffice'), |
|
135 |
value=self.backoffice_info_text) |
|
129 | 136 | |
130 | 137 | |
131 | 138 |
register_item_class(AddAttachmentWorkflowStatusItem) |
wcs/workflows.py | ||
---|---|---|
549 | 549 |
visibility = None |
550 | 550 |
forced_endpoint = False |
551 | 551 |
colour = 'FFFFFF' |
552 |
backoffice_info_text = None |
|
552 | 553 | |
553 | 554 |
def __init__(self, name = None): |
554 | 555 |
self.name = name |
... | ... | |
722 | 723 |
if self.forced_endpoint: |
723 | 724 |
ET.SubElement(status, 'forced_endpoint').text = 'true' |
724 | 725 | |
726 |
if self.backoffice_info_text: |
|
727 |
ET.SubElement(status, 'backoffice_info_text').text = unicode( |
|
728 |
self.backoffice_info_text, charset) |
|
729 | ||
725 | 730 |
visibility_node = ET.SubElement(status, 'visibility') |
726 | 731 |
for role in self.visibility or []: |
727 | 732 |
ET.SubElement(visibility_node, 'role').text = str(role) |
... | ... | |
739 | 744 |
self.colour = elem.find('colour').text.encode(charset) |
740 | 745 |
if elem.find('forced_endpoint') is not None: |
741 | 746 |
self.forced_endpoint = (elem.find('forced_endpoint').text == 'true') |
747 |
if elem.find('backoffice_info_text') is not None: |
|
748 |
self.backoffice_info_text = elem.find('backoffice_info_text').text.encode(charset) |
|
742 | 749 | |
743 | 750 |
self.visibility = [] |
744 | 751 |
for visibility_role in elem.findall('visibility/role'): |
... | ... | |
1089 | 1096 |
button_label = 0 # hack to handle legacy commentable items |
1090 | 1097 |
hint = None |
1091 | 1098 |
by = [] |
1099 |
backoffice_info_text = None |
|
1092 | 1100 | |
1093 | 1101 |
def render_as_line(self): |
1094 | 1102 |
if self.by: |
... | ... | |
1109 | 1117 |
form.add_submit('button%s' % self.id, _('Add Comment')) |
1110 | 1118 |
elif self.button_label: |
1111 | 1119 |
form.add_submit('button%s' % self.id, self.button_label) |
1120 |
form.get_widget('button%s' % self.id).backoffice_info_text = self.backoffice_info_text |
|
1112 | 1121 | |
1113 | 1122 |
def submit_form(self, form, formdata, user, evo): |
1114 | 1123 |
if form.get_widget('comment'): |
... | ... | |
1129 | 1138 |
self.add_parameters_widgets(form, self.get_parameters()) |
1130 | 1139 | |
1131 | 1140 |
def get_parameters(self): |
1132 |
return ('label', 'button_label', 'by', 'hint', 'varname') |
|
1141 |
return ('label', 'button_label', 'by', 'hint', 'varname', |
|
1142 |
'backoffice_info_text') |
|
1133 | 1143 | |
1134 | 1144 |
def add_parameters_widgets(self, form, parameters, prefix='', formdef=None): |
1135 | 1145 |
if 'label' in parameters: |
... | ... | |
1155 | 1165 |
form.add(VarnameWidget, '%svarname' % prefix, |
1156 | 1166 |
title=_('Variable Name'), value=self.varname, |
1157 | 1167 |
hint=_('This will make the comment available in a variable named comment_varname.')) |
1168 |
if 'backoffice_info_text' in parameters: |
|
1169 |
form.add(WysiwygTextWidget, 'backoffice_info_text', |
|
1170 |
title=_('Information Text for Backoffice'), |
|
1171 |
value=self.backoffice_info_text) |
|
1158 | 1172 | |
1159 | 1173 |
def button_label_export_to_xml(self, xml_item, charset, include_id=False): |
1160 | 1174 |
if self.button_label == 0: |
... | ... | |
1193 | 1207 | |
1194 | 1208 |
label = None |
1195 | 1209 |
by = [] |
1210 |
backoffice_info_text = None |
|
1196 | 1211 | |
1197 | 1212 |
def render_as_line(self): |
1198 | 1213 |
if self.label: |
... | ... | |
1207 | 1222 | |
1208 | 1223 |
def fill_form(self, form, formdata, user): |
1209 | 1224 |
form.add_submit('button%s' % self.id, self.label) |
1225 |
form.get_widget('button%s' % self.id).backoffice_info_text = self.backoffice_info_text |
|
1210 | 1226 | |
1211 | 1227 |
def submit_form(self, form, formdata, user, evo): |
1212 | 1228 |
if form.get_submit() == 'button%s' % self.id: |
... | ... | |
1226 | 1242 |
add_element_label = _('Add Role'), |
1227 | 1243 |
element_kwargs={'render_br': False, |
1228 | 1244 |
'options': [(None, '---')] + self.get_list_of_roles()}) |
1245 |
if 'backoffice_info_text' in parameters: |
|
1246 |
form.add(WysiwygTextWidget, 'backoffice_info_text', |
|
1247 |
title=_('Information Text for Backoffice'), |
|
1248 |
value=self.backoffice_info_text) |
|
1229 | 1249 | |
1230 | 1250 |
def get_parameters(self): |
1231 |
return ('by', 'status', 'label') |
|
1251 |
return ('by', 'status', 'label', 'backoffice_info_text')
|
|
1232 | 1252 | |
1233 | 1253 |
register_item_class(ChoiceWorkflowStatusItem) |
1234 | 1254 | |
... | ... | |
1536 | 1556 |
by = [] |
1537 | 1557 |
status = None |
1538 | 1558 |
label = None |
1559 |
backoffice_info_text = None |
|
1539 | 1560 | |
1540 | 1561 |
def render_as_line(self): |
1541 | 1562 |
if self.by: |
... | ... | |
1548 | 1569 |
if not label: |
1549 | 1570 |
label = _('Edit Form') |
1550 | 1571 |
form.add_submit('button%s' % self.id, label) |
1572 |
form.get_widget('button%s' % self.id).backoffice_info_text = self.backoffice_info_text |
|
1551 | 1573 | |
1552 | 1574 |
def submit_form(self, form, formdata, user, evo): |
1553 | 1575 |
if form.get_submit() == 'button%s' % self.id: |
... | ... | |
1568 | 1590 |
options = [(None, '---')] + [(x.id, x.name) for x in self.parent.parent.possible_status]) |
1569 | 1591 |
if 'label' in parameters: |
1570 | 1592 |
form.add(StringWidget, '%slabel' % prefix, title = _('Button Label'), value = self.label) |
1593 |
if 'backoffice_info_text' in parameters: |
|
1594 |
form.add(WysiwygTextWidget, 'backoffice_info_text', |
|
1595 |
title=_('Information Text for Backoffice'), |
|
1596 |
value=self.backoffice_info_text) |
|
1571 | 1597 | |
1572 | 1598 |
def get_parameters(self): |
1573 |
return ('by', 'status', 'label') |
|
1599 |
return ('by', 'status', 'label', 'backoffice_info_text')
|
|
1574 | 1600 | |
1575 | 1601 |
register_item_class(EditableWorkflowStatusItem) |
1576 | 1602 | |
... | ... | |
1622 | 1648 |
attach_to_history = False |
1623 | 1649 |
directory_class = ExportToModelDirectory |
1624 | 1650 |
by = ['_receiver'] |
1651 |
backoffice_info_text = None |
|
1625 | 1652 | |
1626 | 1653 |
def render_as_line(self): |
1627 | 1654 |
if self.label: |
... | ... | |
1642 | 1669 |
if not label: |
1643 | 1670 |
label = _('Create Document') |
1644 | 1671 |
form.add_submit('button%s' % self.id, label) |
1672 |
form.get_widget('button%s' % self.id).backoffice_info_text = self.backoffice_info_text |
|
1645 | 1673 | |
1646 | 1674 |
def submit_form(self, form, formdata, user, evo): |
1647 | 1675 |
if form.get_submit() == 'button%s' % self.id: |
... | ... | |
1707 | 1735 |
form.add(UploadWidget, widget_name, directory='models', |
1708 | 1736 |
filename=filename, title=_('Model'), hint=hint, |
1709 | 1737 |
validation=self.model_file_validation, value=value) |
1738 |
if 'backoffice_info_text' in parameters: |
|
1739 |
form.add(WysiwygTextWidget, 'backoffice_info_text', |
|
1740 |
title=_('Information Text for Backoffice'), |
|
1741 |
value=self.backoffice_info_text) |
|
1710 | 1742 | |
1711 | 1743 |
def get_directory_name(self): |
1712 | 1744 |
return qommon.misc.simplify(self.label or 'export_to_model', space='_') |
... | ... | |
1729 | 1761 |
raise TemplatingError(_('Unknown error in the template: %s') % str(e)) |
1730 | 1762 | |
1731 | 1763 |
def get_parameters(self): |
1732 |
return ('by', 'label', 'model_file', 'attach_to_history') |
|
1764 |
return ('by', 'label', 'model_file', 'attach_to_history', 'backoffice_info_text')
|
|
1733 | 1765 | |
1734 | 1766 |
def model_file_export_to_xml(self, xml_item, charset, include_id=False): |
1735 | 1767 |
if not self.model_file: |
1736 |
- |