From 8bcd1d253516f4060cafed0ab32ab0fc98a24bbb 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 | 1 +
tests/test_snapshots.py | 226 ++++++++++++------
wcs/admin/blocks.py | 6 +-
wcs/admin/forms.py | 6 +-
wcs/admin/workflows.py | 6 +-
wcs/backoffice/snapshots.py | 149 ++++++++++--
wcs/qommon/static/css/dc2/admin.scss | 17 +-
.../wcs/backoffice/block-inspect.html | 2 +-
.../wcs/backoffice/formdef-inspect.html | 4 +-
.../wcs/backoffice/snapshots_compare.html | 34 ++-
.../wcs/backoffice/workflow-inspect.html | 60 ++---
12 files changed, 375 insertions(+), 137 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..4fa9f67d6 100644
--- a/setup.py
+++ b/setup.py
@@ -179,6 +179,7 @@ setup(
'XStatic-Select2',
'pyproj',
'unidecode',
+ 'lxml',
],
package_dir={'wcs': 'wcs'},
packages=find_packages(),
diff --git a/tests/test_snapshots.py b/tests/test_snapshots.py
index e3c942ea2..fd20148f9 100644
--- a/tests/test_snapshots.py
+++ b/tests/test_snapshots.py
@@ -152,7 +152,66 @@ def test_snapshot_basics(pub):
assert [int(f.id) for f in snapshot6.instance.fields] == list(range(0, 12))
-def test_snapshot_diff(pub):
+def test_snapshot_instance(pub):
+ formdef = FormDef()
+ formdef.name = 'testform'
+ formdef.fields = []
+ formdef.store()
+
+ carddef = CardDef()
+ carddef.name = 'testcard'
+ carddef.fields = []
+ carddef.store()
+
+ # remove existing snapshots as they may be duplicated if table_name was
+ # generated in a different second.
+ pub.snapshot_class.wipe()
+
+ carddef.name = 'testcard2'
+ carddef.store()
+
+ for i in range(10):
+ formdef.name = 'testform %s' % i
+ formdef.store()
+
+ assert pub.snapshot_class.count() == 11
+
+ snapshots = pub.snapshot_class.select_object_history(formdef)
+ assert len(snapshots) == 10
+ for i in range(10):
+ assert snapshots[i].serialization is None # not loaded
+ assert snapshots[i].patch is None # not loaded
+ assert pub.snapshot_class.get(snapshots[i].id).instance.name == 'testform %s' % (9 - i)
+
+ snapshots = pub.snapshot_class.select_object_history(carddef)
+ assert len(snapshots) == 1
+
+
+def test_snapshot_user(pub):
+ user = pub.user_class()
+ user.name = 'User Name'
+ user.email = 'foo@localhost'
+ user.store()
+
+ carddef = CardDef()
+ carddef.name = 'testcard'
+ carddef.fields = []
+ carddef.store()
+ snapshot = pub.snapshot_class.select_object_history(carddef)[0]
+ assert snapshot.user is None
+
+ snapshot.user_id = user.id
+ snapshot.store()
+ snapshot = pub.snapshot_class.select_object_history(carddef)[0]
+ assert str(snapshot.user) == 'User Name'
+
+ snapshot.user_id = 'nope'
+ snapshot.store()
+ snapshot = pub.snapshot_class.select_object_history(carddef)[0]
+ assert str(snapshot.user) == 'unknown user'
+
+
+def test_form_snapshot_diff(pub):
create_superuser(pub)
create_role(pub)
@@ -190,6 +249,11 @@ def test_snapshot_diff(pub):
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'
@@ -223,80 +287,22 @@ def test_snapshot_diff(pub):
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(
+ 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,
)
- resp = app.get(
+ 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'
- formdef.fields = []
- formdef.store()
-
- carddef = CardDef()
- carddef.name = 'testcard'
- carddef.fields = []
- carddef.store()
-
- # remove existing snapshots as they may be duplicated if table_name was
- # generated in a different second.
- pub.snapshot_class.wipe()
-
- carddef.name = 'testcard2'
- carddef.store()
-
- for i in range(10):
- formdef.name = 'testform %s' % i
- formdef.store()
-
- assert pub.snapshot_class.count() == 11
-
- snapshots = pub.snapshot_class.select_object_history(formdef)
- assert len(snapshots) == 10
- for i in range(10):
- assert snapshots[i].serialization is None # not loaded
- assert snapshots[i].patch is None # not loaded
- assert pub.snapshot_class.get(snapshots[i].id).instance.name == 'testform %s' % (9 - i)
-
- snapshots = pub.snapshot_class.select_object_history(carddef)
- assert len(snapshots) == 1
-
-
-def test_snapshot_user(pub):
- user = pub.user_class()
- user.name = 'User Name'
- user.email = 'foo@localhost'
- user.store()
-
- carddef = CardDef()
- carddef.name = 'testcard'
- carddef.fields = []
- carddef.store()
- snapshot = pub.snapshot_class.select_object_history(carddef)[0]
- assert snapshot.user is None
-
- snapshot.user_id = user.id
- snapshot.store()
- snapshot = pub.snapshot_class.select_object_history(carddef)[0]
- assert str(snapshot.user) == 'User Name'
-
- snapshot.user_id = 'nope'
- snapshot.store()
- snapshot = pub.snapshot_class.select_object_history(carddef)[0]
- assert str(snapshot.user) == 'unknown user'
+ 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):
@@ -588,6 +594,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)
@@ -1004,6 +1074,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 724715414..cd444bb96 100644
--- a/wcs/admin/blocks.py
+++ b/wcs/admin/blocks.py
@@ -58,7 +58,8 @@ class BlockDirectory(FieldsDirectory):
fields_count_total_hard_limit = 30
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 9ad492841..3e03c272c 100644
--- a/wcs/admin/forms.py
+++ b/wcs/admin/forms.py
@@ -650,7 +650,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)
@@ -1753,6 +1754,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 a5f674764..4c790e410 100644
--- a/wcs/admin/workflows.py
+++ b/wcs/admin/workflows.py
@@ -1586,7 +1586,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)
@@ -1679,6 +1680,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..c68d942be 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
+import lxml.html.diff as lxmldiff
from django.utils.module_loading import import_string
+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,133 @@ 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),
+ fromdesc=None,
+ todesc=None,
)
- 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 htmldiff(panel1, panel2):
+ if not panel1 or not panel2:
+ return lxmldiff.htmldiff(panel1, panel2)
+
+ d1 = pq(panel1)
+ d2 = pq(panel2)
+ if d1.find('.inspect--quicknav'):
+ # diff nav and statuses separately
+ nav1 = d1.find('.inspect--quicknav')
+ nav2 = d2.find('.inspect--quicknav')
+ statuses1 = d1.find('.expanded-statuses')
+ statuses2 = d2.find('.expanded-statuses')
+ return '%s %s
' % (
+ lxmldiff.htmldiff(nav1.html(), nav2.html()),
+ lxmldiff.htmldiff(statuses1.html(), statuses2.html()),
+ )
+
+ return lxmldiff.htmldiff(panel1, panel2)
+
+ 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(' ')
+ 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 = [pq(tab).html().strip('\n') for tab in d1('[role="tabpanel"]')]
+ panels2 = [pq(tab).html().strip('\n') 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 6359084a5..558932088 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;
}
@@ -2370,8 +2370,8 @@ div.timetable-widget {
min-width: 4em;
}
-.section.diff {
- background: transparent;
+div.diff {
+ margin: 1em 0;
}
table.diff {
@@ -2392,6 +2392,7 @@ table.diff {
*/
overflow: hidden;
text-overflow: ellipsis;
+ vertical-align: top;
}
.diff_header {
background: #f7f7f7;
@@ -2414,6 +2415,16 @@ table.diff {
background-color: #ffaaaa;
}
}
+ins {
+ text-decoration: none;
+ background-color: #d4fcbc;
+}
+
+del {
+ text-decoration: line-through;
+ background-color: #fbb6c2;
+ color: #555;
+}
#sidebar .operator-and-value-widget {
.title-and-operator {
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 @@
{% for field in blockdef.fields %}
-{% include "wcs/backoffice/includes/inspect-field.html" %}
+{% include "wcs/backoffice/includes/inspect-field.html" with path=blockdef.get_admin_url %}
{% endfor %}
diff --git a/wcs/templates/wcs/backoffice/formdef-inspect.html b/wcs/templates/wcs/backoffice/formdef-inspect.html
index 9450caa27..fe3763fe5 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 %}
@@ -78,7 +78,7 @@
{% blocktrans count fields_count=formdef.fields|count %}{{ fields_count }} field{% plural %}{{ fields_count }} fields.{% endblocktrans %}
{% for field in formdef.fields %}
-{% include "wcs/backoffice/includes/inspect-field.html" %}
+{% include "wcs/backoffice/includes/inspect-field.html" with path=formdef.get_admin_url|add:"fields/" %}
{% endfor %}
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 @@
-
-
-{% for label in workflow.roles.values %}
-{{ label }}
-{% endfor %}
-
-
-
-
-{% for field in workflow.variables_formdef.fields %}
-{% include "wcs/backoffice/includes/inspect-field.html" with path="variables/fields/" %}
-{% endfor %}
-
-
-
-{% for field in workflow.backoffice_fields_formdef.fields %}
-{% include "wcs/backoffice/includes/inspect-field.html" with path="backoffice-fields/fields/" %}
-{% endfor %}
-
-
-
-
-{% for level in workflow.criticality_levels %}{{ level.name }} {% endfor %}
-
-
-
{% trans "Jump to:" %}
{% for status in workflow.possible_status %}
@@ -60,12 +34,12 @@
{% for status in workflow.possible_status %}
{% if status.backoffice_info_text %}
{{ status.backoffice_info_text|safe }}
{% endif %}
{% for item in status.items %}
-
+
{{ item.get_parameters_view|safe }}
{% empty %}
{% trans "No actions in this status." %}
@@ -84,11 +58,11 @@
{% for action in workflow.global_actions %}
-
+
{% trans "Triggers" %}
{% for trigger in action.triggers %}{{ trigger.render_as_line }} {% endfor %}
{% for item in action.items %}
-
+
{{ item.get_parameters_view|safe }}
{% endfor %}
@@ -96,6 +70,32 @@
+
+
+{% for label in workflow.roles.values %}
+{{ label }}
+{% endfor %}
+
+
+
+
+{% for field in workflow.variables_formdef.fields %}
+{% include "wcs/backoffice/includes/inspect-field.html" with path=workflow.get_admin_url|add:"variables/fields/" %}
+{% endfor %}
+
+
+
+{% for field in workflow.backoffice_fields_formdef.fields %}
+{% include "wcs/backoffice/includes/inspect-field.html" with path=workflow.get_admin_url|add:"backoffice-fields/fields/" %}
+{% endfor %}
+
+
+
+
+{% for level in workflow.criticality_levels %}{{ level.name }} {% endfor %}
+
+
+
{% endblock %}
--
2.35.1