From 451b9a95b58eb8d35d546988e5796a8e962b9cc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= Date: Fri, 24 Jun 2022 08:47:23 +0200 Subject: [PATCH] snapshot: compare inspect of 2 versions (#66565) --- debian/control | 1 + setup.py | 2 + tests/test_snapshots.py | 264 ++++++++++++------ wcs/admin/blocks.py | 6 +- wcs/admin/forms.py | 6 +- wcs/admin/workflows.py | 6 +- wcs/backoffice/snapshots.py | 144 ++++++++-- wcs/qommon/static/css/dc2/admin.scss | 31 +- .../wcs/backoffice/block-inspect.html | 2 +- .../wcs/backoffice/formdef-inspect.html | 6 +- .../wcs/backoffice/snapshots_compare.html | 34 ++- .../wcs/backoffice/workflow-inspect.html | 60 ++-- 12 files changed, 405 insertions(+), 157 deletions(-) diff --git a/debian/control b/debian/control index 1be2da0a6..0ea9d2a1d 100644 --- a/debian/control +++ b/debian/control @@ -24,6 +24,7 @@ Depends: graphviz, python3-dnspython, python3-hobo, python3-lasso, + python3-lxml, python3-pil, python3-psycopg2, python3-pyproj, diff --git a/setup.py b/setup.py index 23a2a2f85..e10e0259a 100644 --- a/setup.py +++ b/setup.py @@ -178,7 +178,9 @@ setup( 'XStatic-Leaflet-GestureHandling', 'XStatic-Select2', 'pyproj', + 'pyquery', 'unidecode', + 'lxml', ], package_dir={'wcs': 'wcs'}, packages=find_packages(), diff --git a/tests/test_snapshots.py b/tests/test_snapshots.py index 3dcde6c7e..9a8c1ce48 100644 --- a/tests/test_snapshots.py +++ b/tests/test_snapshots.py @@ -149,94 +149,6 @@ def test_snapshot_basics(pub): assert [int(f.id) for f in snapshot6.instance.fields] == list(range(0, 12)) -def test_snapshot_diff(pub): - create_superuser(pub) - create_role(pub) - - formdef = FormDef() - formdef.name = 'testform' - formdef.fields = [] - formdef.store() - assert pub.snapshot_class.count() == 1 - snapshot1 = pub.snapshot_class.get_latest('formdef', formdef.id) - - formdef.fields = [StringField(id=1, label='Test', type='string')] - formdef.store() - assert pub.snapshot_class.count() == 2 - snapshot2 = pub.snapshot_class.get_latest('formdef', formdef.id) - - formdef.fields += [StringField(id=2, label='Test bis', type='string')] - formdef.store() - assert pub.snapshot_class.count() == 3 - snapshot3 = pub.snapshot_class.get_latest('formdef', formdef.id) - - app = login(get_app(pub)) - resp = app.get('/backoffice/forms/%s/history/' % formdef.id) - assert 'name="version1" value="%s"' % snapshot3.id in resp - assert 'name="version2" value="%s"' % snapshot3.id not in resp - assert 'name="version1" value="%s"' % snapshot2.id in resp - assert 'name="version2" value="%s"' % snapshot2.id in resp - assert 'name="version1" value="%s"' % snapshot1.id not in resp - assert 'name="version2" value="%s"' % snapshot1.id in resp - - resp = app.get( - '/backoffice/forms/%s/history/compare?version1=%s&version2=%s' - % (formdef.id, snapshot1.id, snapshot3.id) - ) - assert 'Snapshot %s' % (snapshot1.id, snapshot1.id) in resp - assert 'Snapshot %s' % (snapshot3.id, snapshot3.id) in resp - assert resp.text.count('diff_sub') == 1 - assert resp.text.count('diff_add') == 24 - - resp = app.get( - '/backoffice/forms/%s/history/compare?version1=%s&version2=%s' - % (formdef.id, snapshot3.id, snapshot1.id) - ) - assert 'Snapshot %s' % (snapshot1.id, snapshot1.id) in resp - assert 'Snapshot %s' % (snapshot3.id, snapshot3.id) in resp - assert resp.text.count('diff_sub') == 1 - assert resp.text.count('diff_add') == 24 - - resp = app.get( - '/backoffice/forms/%s/history/compare?version1=%s&version2=%s' - % (formdef.id, snapshot2.id, snapshot3.id) - ) - assert 'Snapshot %s' % (snapshot2.id, snapshot2.id) in resp - assert 'Snapshot %s' % (snapshot3.id, snapshot3.id) in resp - assert resp.text.count('diff_sub') == 0 - assert resp.text.count('diff_add') == 11 - - formdef.fields = [StringField(id=1, label='Test', type='string')] - formdef.store() - assert pub.snapshot_class.count() == 4 - snapshot4 = pub.snapshot_class.get_latest('formdef', formdef.id) - - resp = app.get( - '/backoffice/forms/%s/history/compare?version1=%s&version2=%s' - % (formdef.id, snapshot3.id, snapshot4.id) - ) - assert 'Snapshot %s' % (snapshot3.id, snapshot3.id) in resp - assert 'Snapshot %s' % (snapshot4.id, snapshot4.id) in resp - assert resp.text.count('diff_sub') == 11 - assert resp.text.count('diff_add') == 0 - - resp = app.get('/backoffice/forms/%s/history/compare' % (formdef.id), status=404) - resp = app.get( - '/backoffice/forms/%s/history/compare?version1=%s' % (formdef.id, snapshot4.id), status=404 - ) - resp = app.get( - '/backoffice/forms/%s/history/compare?version2=%s' % (formdef.id, snapshot4.id), status=404 - ) - resp = app.get( - '/backoffice/forms/%s/history/compare?version1=%s&version2=%s' % (formdef.id, snapshot3.id, 0), - status=404, - ) - resp = app.get( - '/backoffice/forms/%s/history/compare?version1=%s&version2=%s' % (formdef.id, 0, snapshot4.id), - status=404, - ) - - def test_snapshot_instance(pub): formdef = FormDef() formdef.name = 'testform' @@ -296,6 +208,100 @@ def test_snapshot_user(pub): assert str(snapshot.user) == 'unknown user' +def test_form_snapshot_diff(pub): + create_superuser(pub) + create_role(pub) + + formdef = FormDef() + formdef.name = 'testform' + formdef.fields = [] + formdef.store() + assert pub.snapshot_class.count() == 1 + snapshot1 = pub.snapshot_class.get_latest('formdef', formdef.id) + + formdef.fields = [StringField(id=1, label='Test', type='string')] + formdef.store() + assert pub.snapshot_class.count() == 2 + snapshot2 = pub.snapshot_class.get_latest('formdef', formdef.id) + + formdef.fields += [StringField(id=2, label='Test bis', type='string')] + formdef.store() + assert pub.snapshot_class.count() == 3 + snapshot3 = pub.snapshot_class.get_latest('formdef', formdef.id) + + app = login(get_app(pub)) + resp = app.get('/backoffice/forms/%s/history/' % formdef.id) + assert 'name="version1" value="%s"' % snapshot3.id in resp + assert 'name="version2" value="%s"' % snapshot3.id not in resp + assert 'name="version1" value="%s"' % snapshot2.id in resp + assert 'name="version2" value="%s"' % snapshot2.id in resp + assert 'name="version1" value="%s"' % snapshot1.id not in resp + assert 'name="version2" value="%s"' % snapshot1.id in resp + + resp = app.get( + '/backoffice/forms/%s/history/compare?version1=%s&version2=%s' + % (formdef.id, snapshot1.id, snapshot3.id) + ) + assert 'Snapshot %s' % (snapshot1.id, snapshot1.id) in resp + assert 'Snapshot %s' % (snapshot3.id, snapshot3.id) in resp + assert resp.text.count('diff_sub') == 1 + assert resp.text.count('diff_add') == 24 + resp = resp.click('Compare inspect') + assert 'Snapshot %s' % (snapshot1.id, snapshot1.id) in resp + assert 'Snapshot %s' % (snapshot3.id, snapshot3.id) in resp + assert 'http://example.net/backoffice/forms/%s/fields/1/' % formdef.id in resp + assert 'http://example.net/backoffice/forms/%s/fields/2/' % formdef.id in resp + + resp = app.get( + '/backoffice/forms/%s/history/compare?version1=%s&version2=%s' + % (formdef.id, snapshot3.id, snapshot1.id) + ) + assert 'Snapshot %s' % (snapshot1.id, snapshot1.id) in resp + assert 'Snapshot %s' % (snapshot3.id, snapshot3.id) in resp + assert resp.text.count('diff_sub') == 1 + assert resp.text.count('diff_add') == 24 + + resp = app.get( + '/backoffice/forms/%s/history/compare?version1=%s&version2=%s' + % (formdef.id, snapshot2.id, snapshot3.id) + ) + assert 'Snapshot %s' % (snapshot2.id, snapshot2.id) in resp + assert 'Snapshot %s' % (snapshot3.id, snapshot3.id) in resp + assert resp.text.count('diff_sub') == 0 + assert resp.text.count('diff_add') == 11 + + formdef.fields = [StringField(id=1, label='Test', type='string')] + formdef.store() + assert pub.snapshot_class.count() == 4 + snapshot4 = pub.snapshot_class.get_latest('formdef', formdef.id) + + resp = app.get( + '/backoffice/forms/%s/history/compare?version1=%s&version2=%s' + % (formdef.id, snapshot3.id, snapshot4.id) + ) + assert 'Snapshot %s' % (snapshot3.id, snapshot3.id) in resp + assert 'Snapshot %s' % (snapshot4.id, snapshot4.id) in resp + assert resp.text.count('diff_sub') == 11 + assert resp.text.count('diff_add') == 0 + + app.get('/backoffice/forms/%s/history/compare' % (formdef.id), status=404) + app.get('/backoffice/forms/%s/history/compare?version1=%s' % (formdef.id, snapshot4.id), status=404) + app.get('/backoffice/forms/%s/history/compare?version2=%s' % (formdef.id, snapshot4.id), status=404) + app.get( + '/backoffice/forms/%s/history/compare?version1=%s&version2=%s' % (formdef.id, snapshot3.id, 0), + status=404, + ) + app.get( + '/backoffice/forms/%s/history/compare?version1=%s&version2=%s' % (formdef.id, 0, snapshot4.id), + status=404, + ) + app.get( + '/backoffice/forms/%s/history/compare?version1=%s&version2=%s&mode=foobar' + % (formdef.id, snapshot1.id, snapshot3.id), + status=404, + ) + + def test_form_snapshot_comments(pub): create_superuser(pub) create_role(pub) @@ -587,6 +593,70 @@ def test_form_snapshot_browse_with_import_error(pub): assert 'Can not display snapshot (Unknown referenced objects [Unknown field types: foobar])' in resp +def test_workflow_snapshot_diff(pub): + create_superuser(pub) + create_role(pub) + + Workflow.wipe() + workflow = Workflow(name='test') + workflow.store() + assert pub.snapshot_class.count() == 1 + snapshot1 = pub.snapshot_class.get_latest('workflow', workflow.id) + + workflow.add_status('Status1', 'st1') + workflow.store() + assert pub.snapshot_class.count() == 2 + snapshot2 = pub.snapshot_class.get_latest('workflow', workflow.id) + + ac1 = workflow.add_global_action('Action', 'ac1') + trigger = ac1.triggers[0] + assert trigger.key == 'manual' + trigger.roles = ['foobar'] + workflow.store() + assert pub.snapshot_class.count() == 3 + snapshot3 = pub.snapshot_class.get_latest('workflow', workflow.id) + + workflow.global_actions = [] + workflow.store() + assert pub.snapshot_class.count() == 4 + snapshot4 = pub.snapshot_class.get_latest('workflow', workflow.id) + + app = login(get_app(pub)) + resp = app.get( + '/backoffice/workflows/%s/history/compare?version1=%s&version2=%s&mode=inspect' + % (workflow.id, snapshot1.id, snapshot2.id) + ) + assert 'Snapshot %s' % (snapshot1.id, snapshot1.id) in resp + assert 'Snapshot %s' % (snapshot2.id, snapshot2.id) in resp + assert 'id="tab-statuses"' in resp + assert 'id="tab-global-actions"' not in resp + + resp = app.get( + '/backoffice/workflows/%s/history/compare?version1=%s&version2=%s&mode=inspect' + % (workflow.id, snapshot2.id, snapshot3.id) + ) + assert 'Snapshot %s' % (snapshot2.id, snapshot2.id) in resp + assert 'Snapshot %s' % (snapshot3.id, snapshot3.id) in resp + assert 'http://example.net/backoffice/workflows/%s/global-actions/ac1/' % (workflow.id) in resp + assert 'http://example.net/backoffice/workflows/%s/status/st1/' % workflow.id in resp + assert 'id="tab-statuses"' in resp + assert 'id="tab-global-actions"' in resp + + resp = app.get( + '/backoffice/workflows/%s/history/compare?version1=%s&version2=%s&mode=inspect' + % (workflow.id, snapshot3.id, snapshot4.id) + ) + assert 'id="tab-statuses"' in resp + assert 'id="tab-global-actions"' in resp + + resp = app.get( + '/backoffice/workflows/%s/history/compare?version1=%s&version2=%s&mode=inspect' + % (workflow.id, snapshot1.id, snapshot4.id) + ) + assert 'id="tab-statuses"' in resp + assert 'id="tab-global-actions"' not in resp + + def test_workflow_snapshot_browse(pub): create_superuser(pub) create_role(pub) @@ -1003,6 +1073,24 @@ def test_mail_template_snapshot_restore(pub): mail_template2 = MailTemplate.get(resp.location.split('/')[-2]) assert mail_template2.id == mail_template.id + snapshot1 = pub.snapshot_class.select_object_history(mail_template)[0] + snapshot2 = pub.snapshot_class.select_object_history(mail_template)[1] + app.get( + '/backoffice/workflows/mail-templates/%s/history/compare?version1=%s&version2=%s&mode=xml' + % (mail_template.id, snapshot1.id, snapshot2.id), + status=200, + ) + app.get( + '/backoffice/workflows/mail-templates/%s/history/compare?version1=%s&version2=%s&mode=inspect' + % (mail_template.id, snapshot1.id, snapshot2.id), + status=404, + ) + app.get( + '/backoffice/workflows/mail-templates/%s/history/compare?version1=%s&version2=%s&mode=foobar' + % (mail_template.id, snapshot1.id, snapshot2.id), + status=404, + ) + def test_mail_template_snapshot_browse(pub): create_superuser(pub) diff --git a/wcs/admin/blocks.py b/wcs/admin/blocks.py index b8e9ba625..6ca116287 100644 --- a/wcs/admin/blocks.py +++ b/wcs/admin/blocks.py @@ -58,7 +58,8 @@ class BlockDirectory(FieldsDirectory): fields_count_total_hard_limit = 60 def __init__(self, section='forms', *args, **kwargs): - if kwargs.pop('component', None): # snapshot + kwargs.pop('component', None) # snapshot + if 'instance' in kwargs: kwargs['objectdef'] = kwargs.pop('instance') self.section = section super().__init__(*args, **kwargs) @@ -211,6 +212,9 @@ class BlockDirectory(FieldsDirectory): def inspect(self): self.html_top(self.objectdef.name) get_response().breadcrumb.append(('inspect', _('Inspector'))) + return self.render_inspect() + + def render_inspect(self): context = {'blockdef': self.objectdef, 'view': self} return template.QommonTemplateResponse( templates=['wcs/backoffice/block-inspect.html'], context=context diff --git a/wcs/admin/forms.py b/wcs/admin/forms.py index b29d4cc98..9cb91aaef 100644 --- a/wcs/admin/forms.py +++ b/wcs/admin/forms.py @@ -647,7 +647,8 @@ class FormDefPage(Directory): except KeyError: raise TraversalError() self.formdefui = self.formdef_ui_class(self.formdef) - get_response().breadcrumb.append((component + '/', self.formdef.name)) + if component: + get_response().breadcrumb.append((component + '/', self.formdef.name)) self.fields = self.fields_directory_class(self.formdef) self.fields.html_top = self.html_top self.role = WorkflowRoleDirectory(self.formdef) @@ -1647,6 +1648,9 @@ class FormDefPage(Directory): def inspect(self): self.html_top(self.formdef.name) get_response().breadcrumb.append(('inspect', _('Inspector'))) + return self.render_inspect() + + def render_inspect(self): context = {'formdef': self.formdef, 'view': self} if self.formdef.workflow.variables_formdef: context['workflow_options'] = {} diff --git a/wcs/admin/workflows.py b/wcs/admin/workflows.py index 8e69a312e..39cb7cc00 100644 --- a/wcs/admin/workflows.py +++ b/wcs/admin/workflows.py @@ -1602,7 +1602,8 @@ class WorkflowPage(Directory): self.criticality_levels_dir = CriticalityLevelsDirectory(self.workflow) self.logged_errors_dir = LoggedErrorsDirectory(parent_dir=self, workflow_id=self.workflow.id) self.snapshots_dir = SnapshotsDirectory(self.workflow) - get_response().breadcrumb.append((component + '/', self.workflow.name)) + if component: + get_response().breadcrumb.append((component + '/', self.workflow.name)) def html_top(self, title): return html_top('workflows', title) @@ -1707,6 +1708,9 @@ class WorkflowPage(Directory): def inspect(self): self.html_top(self.workflow.name) get_response().breadcrumb.append(('inspect', _('Inspector'))) + return self.render_inspect() + + def render_inspect(self): context = {'workflow': self.workflow, 'view': self} return template.QommonTemplateResponse( templates=['wcs/backoffice/workflow-inspect.html'], context=context diff --git a/wcs/backoffice/snapshots.py b/wcs/backoffice/snapshots.py index d209ad675..24194cd8c 100644 --- a/wcs/backoffice/snapshots.py +++ b/wcs/backoffice/snapshots.py @@ -15,8 +15,11 @@ # along with this program; if not, see . import difflib +import re from django.utils.module_loading import import_string +from lxml.html.diff import htmldiff # pylint: disable=no-name-in-module +from pyquery import PyQuery as pq from quixote import get_publisher, get_request, get_response, get_session, redirect from quixote.directory import Directory from quixote.html import TemplateIO, htmltext @@ -70,10 +73,14 @@ class SnapshotsDirectory(Directory): get_response().breadcrumb.append(('compare/', _('Compare'))) html_top('', _('Compare')) + mode = get_request().form.get('mode') or 'xml' + id1 = get_request().form.get('version1') id2 = get_request().form.get('version2') if not id1 or not id2: raise errors.TraversalError() + if mode not in ['xml', 'inspect']: + raise errors.TraversalError() snapshot1 = get_publisher().snapshot_class.get(id1, ignore_errors=True) snapshot2 = get_publisher().snapshot_class.get(id2, ignore_errors=True) @@ -82,37 +89,128 @@ class SnapshotsDirectory(Directory): if snapshot1.timestamp > snapshot2.timestamp: snapshot1, snapshot2 = snapshot2, snapshot1 - def snapshot_desc(snapshot): - label_or_comment = '' - if snapshot.label: - label_or_comment = snapshot.label - elif snapshot.comment: - label_or_comment = snapshot.comment - return '{name} {pk}
{label_or_comment}
({user}{timestamp})'.format( - name=_('Snapshot'), - pk=snapshot.id, - label_or_comment=label_or_comment, - user='%s ' % snapshot.user if snapshot.user_id else '', - timestamp=misc.strftime(misc.datetime_format(), snapshot.timestamp), - ) + klass = snapshot1.get_object_class() + backoffice_class = import_string(klass.backoffice_class) + has_inspect = hasattr(backoffice_class, 'render_inspect') + if mode == 'inspect' and not has_inspect: + raise errors.TraversalError() + + context = getattr(self, 'get_compare_%s_context' % mode)(snapshot1, snapshot2) + context.update( + { + 'mode': mode, + 'has_inspect': has_inspect, + 'snapshot1': snapshot1, + 'snapshot2': snapshot2, + } + ) + return template.QommonTemplateResponse( + templates=['wcs/backoffice/snapshots_compare.html'], + context=context, + ) + + def snapshot_desc(self, snapshot): + label_or_comment = '' + if snapshot.label: + label_or_comment = snapshot.label + elif snapshot.comment: + label_or_comment = snapshot.comment + return '{name} {pk} - {label_or_comment} ({user}{timestamp})'.format( + name=_('Snapshot'), + pk=snapshot.id, + label_or_comment=label_or_comment, + user='%s ' % snapshot.user if snapshot.user_id else '', + timestamp=misc.strftime(misc.datetime_format(), snapshot.timestamp), + ) + + def get_compare_xml_context(self, snapshot1, snapshot2): serialization1 = snapshot1.get_serialization(indented=True) serialization2 = snapshot2.get_serialization(indented=True) diff_serialization = difflib.HtmlDiff(wrapcolumn=160).make_table( fromlines=serialization1.splitlines(True), tolines=serialization2.splitlines(True), - fromdesc=snapshot_desc(snapshot1), - todesc=snapshot_desc(snapshot2), ) - return template.QommonTemplateResponse( - templates=['wcs/backoffice/snapshots_compare.html'], - context={ - 'snapshot1': snapshot1, - 'snapshot2': snapshot2, - 'diff_serialization': diff_serialization, - }, - ) + return { + 'fromdesc': self.snapshot_desc(snapshot1), + 'todesc': self.snapshot_desc(snapshot2), + 'diff_serialization': diff_serialization, + } + + def get_compare_inspect_context(self, snapshot1, snapshot2): + klass = snapshot1.get_object_class() + backoffice_class = import_string(klass.backoffice_class) + + def clean_panel(tab): + panel = pq(tab) + # remove quicknavs + panel.find('.inspect--quicknav').remove() + # remove page & field counters, for formdef + panel.find('.page-field-counters').remove() + # remove status colors + panel.find('.inspect-status--colour').remove() + return panel.html().strip('\n') + + def fix_result(panel_diff): + if not panel_diff: + return panel_diff + panel = pq(panel_diff) + # remove "Link" added by htmldiff + for link in panel.find('a'): + d = pq(link) + text = d.html() + new_text = re.sub(r' Link: .*$', '', text) + d.html(new_text) + # remove empty ins and del tags + for elem in panel.find('ins, del'): + d = pq(elem) + if not d.html().strip(): + d.remove() + # prevent auto-closing behaviour of pyquery .html() method + for elem in panel.find('span, ul, div'): + d = pq(elem) + if not d.html(): + d.html(' ') + # sometimes status section are misplaced by htmldiff, fix it + for elem in panel.find('div.section.status'): + d = pq(elem) + parents = d.parents('div.section.status') + if parents: + pq(parents[0]).after(d.remove()) + return panel.html() + + inspect1 = backoffice_class(component=None, instance=snapshot1.instance).render_inspect() + inspect1 = template.render(inspect1.templates, inspect1.context) + d1 = pq(str(inspect1)) + inspect2 = backoffice_class(component=None, instance=snapshot2.instance).render_inspect() + inspect2 = template.render(inspect2.templates, inspect2.context) + d2 = pq(str(inspect2)) + panels_attrs = [tab.attrib for tab in d1('[role="tabpanel"]')] + panels1 = [clean_panel(tab) for tab in d1('[role="tabpanel"]')] + panels2 = [clean_panel(tab) for tab in d2('[role="tabpanel"]')] + + # build tab list (merge version 1 and version2) + tabs1 = d1.find('[role="tab"]') + tabs2 = d2.find('[role="tab"]') + tabs_order = [t.get('id') for t in panels_attrs] + tabs = {} + for tab in tabs1 + tabs2: + tab_id = pq(tab).attr('aria-controls') + tabs[tab_id] = pq(tab).outer_html() + tabs = [tabs[k] for k in tabs_order if k in tabs] + + # build diff of each panel + panels_diff = list(map(htmldiff, panels1, panels2)) + panels_diff = [fix_result(t) for t in panels_diff] + + return { + 'fromdesc': self.snapshot_desc(snapshot1), + 'todesc': self.snapshot_desc(snapshot2), + 'tabs': tabs, + 'panels': zip(panels_attrs, panels_diff), + 'tab_class_names': d1('.pk-tabs').attr('class'), + } def snapshots(self): current_date = None diff --git a/wcs/qommon/static/css/dc2/admin.scss b/wcs/qommon/static/css/dc2/admin.scss index 4c84bbaa5..871093dc8 100644 --- a/wcs/qommon/static/css/dc2/admin.scss +++ b/wcs/qommon/static/css/dc2/admin.scss @@ -969,7 +969,7 @@ div.full-screen-link { text-align: right; } -p.last-modification { +p.last-modification, p.snapshot-description { font-size: 80%; margin: 0; } @@ -2374,8 +2374,8 @@ div.timetable-widget { min-width: 4em; } -.section.diff { - background: transparent; +div.diff { + margin: 1em 0; } table.diff { @@ -2396,6 +2396,7 @@ table.diff { */ overflow: hidden; text-overflow: ellipsis; + vertical-align: top; } .diff_header { background: #f7f7f7; @@ -2419,6 +2420,30 @@ table.diff { } } +ins { + text-decoration: none; + background-color: #d4fcbc; +} + +del { + text-decoration: line-through; + background-color: #fbb6c2; + color: #555; +} + +.inspect-tabs h3 { + del, ins { + font-weight: bold; + background-color: transparent; + } + del, del a { + color: #fbb6c2 !important; + } + ins, ins a { + color: #d4fcbc !important; + } +} + #sidebar .operator-and-value-widget { .title-and-operator { display: flex; diff --git a/wcs/templates/wcs/backoffice/block-inspect.html b/wcs/templates/wcs/backoffice/block-inspect.html index 33f5bf930..4e3a9fcb2 100644 --- a/wcs/templates/wcs/backoffice/block-inspect.html +++ b/wcs/templates/wcs/backoffice/block-inspect.html @@ -22,7 +22,7 @@ diff --git a/wcs/templates/wcs/backoffice/formdef-inspect.html b/wcs/templates/wcs/backoffice/formdef-inspect.html index 9450caa27..fbe90a82d 100644 --- a/wcs/templates/wcs/backoffice/formdef-inspect.html +++ b/wcs/templates/wcs/backoffice/formdef-inspect.html @@ -29,7 +29,7 @@
  • {% trans "Options" %}{% trans ":" %} {% if not workflow_options %}-{% else %}
      {% for label, value in workflow_options.items %} {% if value == '__title__' or value == '__subtitle__' %}
    • {{ label }}
    • - {% elif value == '__comment__' %}
    • <{{ label }}
    • + {% elif value == '__comment__' %}
    • {{ label }}
    • {% else %}
    • {{ label }} → {{ value|safe|default:"-" }}
    • {% endif %} @@ -73,12 +73,12 @@ diff --git a/wcs/templates/wcs/backoffice/snapshots_compare.html b/wcs/templates/wcs/backoffice/snapshots_compare.html index d4c971277..89b2aef43 100644 --- a/wcs/templates/wcs/backoffice/snapshots_compare.html +++ b/wcs/templates/wcs/backoffice/snapshots_compare.html @@ -1,11 +1,33 @@ +{% extends "wcs/backoffice/base.html" %} {% load i18n %} -{% block body %} -
      -

      {% trans "Compare snapshots" %}

      -
      +{% block appbar-title %}{% trans "Compare snapshots" %}{% if has_inspect %} ({% if mode == 'xml' %}{% trans "XML" %}{% else %}{% trans "Inspect" %}{% endif %}){% endif %}{% endblock %} +{% block appbar-actions %} +{% if has_inspect %} +{% trans "Compare inspect" %} +{% trans "Compare XML" %} +{% endif %} +{% endblock %} -
      - {{ diff_serialization|safe }} +{% block content %} +

      {{ fromdesc|safe }} ➔ {{ todesc|safe }}

      +
      +{% if mode == 'xml' %} + {{ diff_serialization|safe }} +{% else %} +
      +
      + {% for tab in tabs %}{{ tab|safe }}{% endfor %} + {{ tab_list|safe }} +
      +
      + {% for attrs, panel in panels %} + + {{ panel|safe }} +
      + {% endfor %} +
      +
      +{% endif %}
      {% endblock %} diff --git a/wcs/templates/wcs/backoffice/workflow-inspect.html b/wcs/templates/wcs/backoffice/workflow-inspect.html index bc1d27be4..90353a014 100644 --- a/wcs/templates/wcs/backoffice/workflow-inspect.html +++ b/wcs/templates/wcs/backoffice/workflow-inspect.html @@ -24,32 +24,6 @@
      - - - - - - - -
      {% endblock %}