From f7c131532337323b632a89ef6035c2273482b016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Mon, 17 Aug 2020 17:23:53 +0200 Subject: [PATCH] backoffice: add link to tag/save snapshot (#4960) --- tests/test_snapshots.py | 22 ++++++++++++++++++++++ wcs/admin/blocks.py | 1 + wcs/admin/data_sources.py | 1 + wcs/admin/forms.py | 1 + wcs/admin/workflows.py | 1 + wcs/admin/wscalls.py | 1 + wcs/backoffice/cards.py | 1 + wcs/backoffice/snapshots.py | 25 ++++++++++++++++++++++--- wcs/snapshots.py | 8 +++++--- 9 files changed, 55 insertions(+), 6 deletions(-) diff --git a/tests/test_snapshots.py b/tests/test_snapshots.py index 90387a567..933dc498b 100644 --- a/tests/test_snapshots.py +++ b/tests/test_snapshots.py @@ -317,3 +317,25 @@ def test_wscall_snapshot_browse(pub): assert 'This webservice call is readonly' in resp with pytest.raises(IndexError): resp = resp.click('Edit') + + +def test_form_snapshot_save(pub, formdef_with_history): + create_superuser(pub) + create_role() + app = login(get_app(pub)) + + resp = app.get('/backoffice/forms/%s/' % formdef_with_history.id) + resp = resp.click('Save snapshot') + resp.form['label'] = 'test snapshot' + resp = resp.form.submit('submit') + + # add more snapshots + formdef = FormDef.get(id=formdef_with_history.id) + for i in range(10, 15): + formdef.description = 'this is a description (%s)' % i + formdef.store() + + resp = app.get('/backoffice/forms/%s/history/' % formdef_with_history.id) + assert [x.attrib['class'] for x in resp.pyquery.find('ul.snapshots-list li')] == [ + 'new-day', 'collapsed', 'collapsed', 'collapsed', 'collapsed', 'has-label', + 'collapsed', 'collapsed', 'collapsed', 'collapsed', 'collapsed', 'collapsed'] diff --git a/wcs/admin/blocks.py b/wcs/admin/blocks.py index cac3fe71f..bf7c06d23 100644 --- a/wcs/admin/blocks.py +++ b/wcs/admin/blocks.py @@ -90,6 +90,7 @@ class BlockDirectory(FieldsDirectory): r += htmltext('
  • %s
  • ') % _('Delete') r += htmltext('
  • %s
  • ') % _('Export') if get_publisher().snapshot_class: + r += htmltext('
  • %s
  • ') % _('Save snapshot') r += htmltext('
  • %s
  • ') % _('History') r += htmltext('
  • %s
  • ') % _('Settings') r += htmltext('') diff --git a/wcs/admin/data_sources.py b/wcs/admin/data_sources.py index c248f4fea..f0aefeae4 100644 --- a/wcs/admin/data_sources.py +++ b/wcs/admin/data_sources.py @@ -178,6 +178,7 @@ class NamedDataSourcePage(Directory): r += htmltext('
  • %s
  • ') % _('Delete') r += htmltext('
  • %s
  • ') % _('Export') if get_publisher().snapshot_class: + r += htmltext('
  • %s
  • ') % _('Save snapshot') r += htmltext('
  • %s
  • ') % _('History') r += htmltext('') return r.getvalue() diff --git a/wcs/admin/forms.py b/wcs/admin/forms.py index 368a174fa..1930476eb 100644 --- a/wcs/admin/forms.py +++ b/wcs/admin/forms.py @@ -623,6 +623,7 @@ class FormDefPage(Directory): 'Overwrite with new import') r += htmltext('
  • %s
  • ') % _('Export') if get_publisher().snapshot_class: + r += htmltext('
  • %s
  • ') % _('Save snapshot') r += htmltext('
  • %s
  • ') % _('History') r += htmltext('
  • %s
  • ') % _('Anonymise forms') if not get_publisher().is_using_postgresql(): diff --git a/wcs/admin/workflows.py b/wcs/admin/workflows.py index 64c39709f..b62381eaa 100644 --- a/wcs/admin/workflows.py +++ b/wcs/admin/workflows.py @@ -1581,6 +1581,7 @@ class WorkflowPage(Directory): r += htmltext('
  • %s
  • ') % _('Duplicate') r += htmltext('
  • %s
  • ') % _('Export') if get_publisher().snapshot_class: + r += htmltext('
  • %s
  • ') % _('Save snapshot') r += htmltext('
  • %s
  • ') % _('History') r += htmltext('') if not self.workflow.is_readonly(): diff --git a/wcs/admin/wscalls.py b/wcs/admin/wscalls.py index d3d091bf9..b366bf757 100644 --- a/wcs/admin/wscalls.py +++ b/wcs/admin/wscalls.py @@ -117,6 +117,7 @@ class NamedWsCallPage(Directory): r += htmltext('
  • %s
  • ') % _('Export') r += htmltext('
  • %s
  • ') % _('Delete') if get_publisher().snapshot_class: + r += htmltext('
  • %s
  • ') % _('Save snapshot') r += htmltext('
  • %s
  • ') % _('History') r += htmltext('') return r.getvalue() diff --git a/wcs/backoffice/cards.py b/wcs/backoffice/cards.py index c22963d41..487bff4af 100644 --- a/wcs/backoffice/cards.py +++ b/wcs/backoffice/cards.py @@ -184,6 +184,7 @@ class CardDefPage(FormDefPage): 'Overwrite with new import') r += htmltext('
  • %s
  • ') % _('Export') if get_publisher().snapshot_class: + r += htmltext('
  • %s
  • ') % _('Save snapshot') r += htmltext('
  • %s
  • ') % _('History') r += htmltext('') diff --git a/wcs/backoffice/snapshots.py b/wcs/backoffice/snapshots.py index 8c114c324..59f52cc72 100644 --- a/wcs/backoffice/snapshots.py +++ b/wcs/backoffice/snapshots.py @@ -20,12 +20,12 @@ from quixote.html import TemplateIO, htmltext from wcs.qommon import _, errors, misc, template from wcs.qommon.backoffice.menu import html_top -from wcs.qommon.form import Form, RadiobuttonsWidget +from wcs.qommon.form import Form, RadiobuttonsWidget, StringWidget from wcs.qommon.storage import Equal class SnapshotsDirectory(Directory): - _q_exports = [''] + _q_exports = ['', 'save'] do_not_call_in_templates = True def __init__(self, instance): @@ -43,9 +43,28 @@ class SnapshotsDirectory(Directory): templates=['wcs/backoffice/snapshots.html'], context={'view': self}) + def save(self): + form = Form(enctype='multipart/form-data') + label = form.add(StringWidget, 'label', title=_('Label'), required=True) + 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(): + get_publisher().snapshot_class.snap(instance=self.obj, label=label.parse()) + return redirect('../') + + html_top('', _('History')) + r = TemplateIO(html=True) + r += htmltext('

    %s

    ') % _('Save snapshot') + r += form.render() + return r.getvalue() + def snapshots(self): current_date = None - snapshots = get_publisher().snapshot_class.select_object_history(self.obj)[1:] + snapshots = get_publisher().snapshot_class.select_object_history(self.obj) day_snapshot = None for i, snapshot in enumerate(snapshots): if snapshot.timestamp.date() != current_date: diff --git a/wcs/snapshots.py b/wcs/snapshots.py index 066ba2569..bcffb58fb 100644 --- a/wcs/snapshots.py +++ b/wcs/snapshots.py @@ -43,7 +43,7 @@ class Snapshot: _user = None @classmethod - def snap(cls, instance, comment): + def snap(cls, instance, comment=None, label=None): obj = cls() obj.object_type = instance.xml_root_node obj.object_id = instance.id @@ -52,9 +52,11 @@ class Snapshot: obj.user_id = get_session().user obj.serialization = ET.tostring(instance.export_to_xml(include_id=True)).decode('utf-8') obj.comment = comment + obj.label = label latest = cls.get_latest(obj.object_type, obj.object_id) - if latest is None or obj.serialization != latest.serialization: - # save snapshot if there are changes + if label is not None or latest is None or obj.serialization != latest.serialization: + # save snapshot if there are changes or an explicit label was + # given. obj.store() def get_object_class(self):