From f421b61001af1b49a1725fa16918b302196c5005 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/backoffice/workflow-status.html | 1 + wcs/workflows.py | 11 +++-- 5 files changed, 96 insertions(+), 5 deletions(-) diff --git a/tests/admin_pages/test_workflow.py b/tests/admin_pages/test_workflow.py index 95a970875..a3c1de72c 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') + st2 = 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/templates/wcs/backoffice/workflow-status.html b/wcs/templates/wcs/backoffice/workflow-status.html index 2f1c4971f..148beaa7b 100644 --- a/wcs/templates/wcs/backoffice/workflow-status.html +++ b/wcs/templates/wcs/backoffice/workflow-status.html @@ -35,6 +35,7 @@ {% with item.get_target_status_url as url %} {% if url %}{% trans "Go to Target" %}{% endif %} {% endwith %} + {% trans "Copy" %} {% if not workflow.is_readonly %} {% trans "Edit" %} {% trans "Delete" %} diff --git a/wcs/workflows.py b/wcs/workflows.py index cfa66a31d..ebe5213d5 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