From e12269bc2ba86fc0609b61d1e960b0c72c7d62bb Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Tue, 14 Dec 2021 10:05:02 +0100 Subject: [PATCH] workflows: copy action from one status to another (#57894) --- tests/admin_pages/test_workflow.py | 41 +++++++++++++++++++ tests/test_snapshots.py | 7 ++++ wcs/admin/workflows.py | 41 ++++++++++++++++++- wcs/qommon/static/css/dc2/admin.scss | 4 +- .../wcs/backoffice/workflow-status.html | 1 + wcs/workflows.py | 11 +++-- 6 files changed, 98 insertions(+), 7 deletions(-) diff --git a/tests/admin_pages/test_workflow.py b/tests/admin_pages/test_workflow.py index 72f094299..3136c7fe8 100644 --- a/tests/admin_pages/test_workflow.py +++ b/tests/admin_pages/test_workflow.py @@ -470,6 +470,47 @@ def test_workflows_order_status(pub): assert Workflow.get(workflow.id).possible_status[2].id == status1_id +def test_workflows_copy_status_item(pub): + create_superuser(pub) + Workflow.wipe() + workflow = Workflow(name='foo') + st1 = workflow.add_status('Status1') + workflow.add_status('Status2') + st3 = workflow.add_status('Status3') + + item = SendmailWorkflowStatusItem() + item.to = ['_submitter'] + item.subject = 'bla' + st1.items.append(item) + item.parent = st1 + workflow.store() + + create_superuser(pub) + app = login(get_app(pub)) + + resp = app.get('/backoffice/workflows/%s/status/%s/' % (workflow.id, st1.id)) + resp = resp.click('Copy') + resp.form['status'] = 'Status3' + resp = resp.form.submit() + assert resp.location == 'http://example.net/backoffice/workflows/%s/status/%s/' % (workflow.id, st1.id) + + resp = app.get('/backoffice/workflows/%s/status/%s/' % (workflow.id, st3.id)) + resp = resp.click('Email') + assert resp.form['to$element0$choice'].value == '_submitter' + assert resp.form['subject'].value == 'bla' + + resp = app.get('/backoffice/workflows/%s/status/%s/' % (workflow.id, st3.id)) + resp = resp.click('Copy') + resp.form['status'] = 'Status1' + resp = resp.form.submit() + assert resp.location == 'http://example.net/backoffice/workflows/%s/status/%s/' % (workflow.id, st3.id) + + resp = app.get('/backoffice/workflows/%s/status/%s/' % (workflow.id, st1.id)) + assert len(resp.pyquery('#items-list li')) == 2 + assert 'items/1/' in resp.text + assert 'items/2/' in resp.text + + def test_workflows_delete(pub): Workflow.wipe() workflow = Workflow(name='foo') diff --git a/tests/test_snapshots.py b/tests/test_snapshots.py index d49e2ebe5..0ee7e9982 100644 --- a/tests/test_snapshots.py +++ b/tests/test_snapshots.py @@ -867,6 +867,7 @@ def test_snaphost_workflow_status_item_comments(pub): Workflow.wipe() workflow = Workflow(name='test') workflow.add_status(name='baz') + workflow.add_status(name='hop') workflow.store() app = login(get_app(pub)) @@ -889,6 +890,11 @@ def test_snaphost_workflow_status_item_comments(pub): resp = resp.follow() resp = resp.follow() + resp = resp.click(href='items/1/copy') + resp.form['status'] = 'hop' + resp = resp.forms[0].submit('submit') + resp = resp.follow() + resp = resp.click(href='items/1/delete') resp = resp.form.submit('submit') @@ -899,6 +905,7 @@ def test_snaphost_workflow_status_item_comments(pub): ] assert comments == [ 'Deletion of action "Webservice (foo)" in status "baz"', + 'Copy of action "Webservice (foo)" from status "baz" to status "hop"', 'Change in action "Webservice (foo)" in status "baz"', 'Change in action "Webservice" in status "baz"', 'New action "Webservice" in status "baz"', diff --git a/wcs/admin/workflows.py b/wcs/admin/workflows.py index bf384d3f5..dc8a677ce 100644 --- a/wcs/admin/workflows.py +++ b/wcs/admin/workflows.py @@ -14,6 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see . +import copy import io import textwrap import time @@ -358,7 +359,7 @@ class WorkflowUI: class WorkflowItemPage(Directory): - _q_exports = ['', 'delete'] + _q_exports = ['', 'delete', 'copy'] def __init__(self, workflow, parent, component, html_top): try: @@ -437,6 +438,44 @@ class WorkflowItemPage(Directory): ) return redirect('../../') + def copy(self): + form = Form(enctype='multipart/form-data') + destinations = [(x.id, x.name) for x in self.workflow.possible_status] + + form.add(SingleSelectWidget, 'status', title=_('Target status'), options=destinations) + + form.add_submit('copy', _('Copy')) + form.add_submit('cancel', _('Cancel')) + if form.get_widget('cancel').parse(): + return redirect('../../') + if not form.is_submitted() or form.has_errors(): + get_response().breadcrumb.append(('copy', _('Copy'))) + self.html_top(title=_('Copy Item')) + r = TemplateIO(html=True) + r += htmltext('

%s

') % _('Copy Item') + r += form.render() + return r.getvalue() + else: + status_id = form.get_widget('status').parse() + destination_status = self.workflow.get_status(status_id) + + new_item = copy.deepcopy(self.item) + new_item.id = destination_status.get_new_item_id() + new_item.parent = destination_status + destination_status.items.append(new_item) + + self.workflow.store( + comment=_( + 'Copy of action "%(description)s" from status "%(from_status)s" to status "%(destination_status)s"' + ) + % { + 'description': self.item.render_as_line(), + 'from_status': self.parent.name, + 'destination_status': destination_status.name, + } + ) + return redirect('../../') + def _q_lookup(self, component): t = self.item.q_admin_lookup(self.workflow, self.parent, component, self.html_top) if t: diff --git a/wcs/qommon/static/css/dc2/admin.scss b/wcs/qommon/static/css/dc2/admin.scss index 190ae83c4..efde04aa1 100644 --- a/wcs/qommon/static/css/dc2/admin.scss +++ b/wcs/qommon/static/css/dc2/admin.scss @@ -1,7 +1,7 @@ $primary-color: #386ede; $secondary-color: #00d6eb; $string-color: str-slice($primary-color + '', 2); -$actions: add, duplicate, edit, jump, remove; +$actions: add, duplicate, edit, jump, remove, copy; @mixin clearfix { &::after { @@ -2066,7 +2066,7 @@ div#side { // w.c.s. steps in backoffice submission &.view { margin-top: 2px; } - &.remove, &.add, &.edit, &.duplicate, &.jump { + &.remove, &.add, &.edit, &.duplicate, &.jump, &.copy { padding: 0; border: 0; background: transparent; diff --git a/wcs/templates/wcs/backoffice/workflow-status.html b/wcs/templates/wcs/backoffice/workflow-status.html index 2f1c4971f..1c7c7d4ae 100644 --- a/wcs/templates/wcs/backoffice/workflow-status.html +++ b/wcs/templates/wcs/backoffice/workflow-status.html @@ -37,6 +37,7 @@ {% endwith %} {% if not workflow.is_readonly %} {% trans "Edit" %} + {% trans "Copy" %} {% trans "Delete" %} {% endif %}

diff --git a/wcs/workflows.py b/wcs/workflows.py index 3e3e8350c..b4d0a9963 100644 --- a/wcs/workflows.py +++ b/wcs/workflows.py @@ -1787,15 +1787,18 @@ class WorkflowStatus: for klass in item_classes: if klass.key == type: o = klass() - if self.items: - o.id = str(max(lax_int(x.id) for x in self.items) + 1) - else: - o.id = '1' + o.id = self.get_new_item_id() self.items.append(o) break else: raise KeyError() + def get_new_item_id(self): + if self.items: + return str(max(lax_int(x.id) for x in self.items) + 1) + else: + return '1' + def get_item(self, id): for item in self.items: if item.id == id: -- 2.30.2