Enable breadcrumbs in revisions compare view (#12675)

pull/12712/head
Sage Abdullah 2024-12-06 18:18:16 +00:00 zatwierdzone przez Matt Westcott
rodzic 01e7291486
commit 286d7cbd6b
9 zmienionych plików z 165 dodań i 100 usunięć

Wyświetl plik

@ -18,6 +18,7 @@ Changelog
* Add `get_avatar_url` hook to customise user avatars (jhrr)
* Set content security policy (CSP) headers to block embedded content when serving images and documents (Jake Howard, with thanks to Ali İltizar for the initial report)
* Add `page` as a third parameter to the `construct_wagtail_userbar` hook (claudobahn)
* Enable breadcrumbs in revisions compare view (Sage Abdullah)
* Fix: Improve handling of translations for bulk page action confirmation messages (Matt Westcott)
* Fix: Ensure custom rich text feature icons are correctly handled when provided as a list of SVG paths (Temidayo Azeez, Joel William, LB (Ben) Johnston)
* Fix: Ensure manual edits to `StreamField` values do not throw an error (Stefan Hammer)

Wyświetl plik

@ -27,6 +27,7 @@ depth: 1
* Add [`get_avatar_url`](get_avatar_url) hook to customise user avatars (jhrr)
* Set content security policy (CSP) headers to block embedded content when serving images and documents (Jake Howard, with thanks to Ali İltizar for the initial report)
* Add `page` as a third parameter to the [`construct_wagtail_userbar`](construct_wagtail_userbar) hook (claudobahn)
* Enable breadcrumbs in revisions compare view (Sage Abdullah)
### Bug fixes

Wyświetl plik

@ -1,84 +1,71 @@
{% extends "wagtailadmin/base.html" %}
{% extends "wagtailadmin/generic/base.html" %}
{% load i18n wagtailadmin_tags %}
{% block titletag %}{% blocktrans trimmed with title=page_subtitle %}Comparing {{ title }}{% endblocktrans %}{% endblock %}
{% block main_content %}
<table class="listing w-mt-8">
<col width="15%" />
<col />
{% block content %}
{% include "wagtailadmin/shared/header.html" with title=_("Comparing") subtitle=page_subtitle icon=header_icon %}
<thead>
<tr>
<th>{% trans "Fields" %}</th>
<th>{% trans "Changes" %}</th>
</tr>
</thead>
<div class="nice-padding">
<p>
<a href="{{ history_url }}" class="button button-small">{{ history_label|capfirst }}</a>
<a href="{{ edit_url }}" class="button button-small button-secondary">{{ edit_label|capfirst }}</a>
</p>
<table class="listing">
<col width="15%" />
<col />
<thead>
<tbody>
{% for comp in comparison %}
<tr>
<th>{% trans "Fields" %}</th>
<th>{% trans "Changes" %}</th>
<td class="title" valign="top">
<div class="title-wrapper">{{ comp.field_label }}:</div>
</td>
<td class="comparison{% if not comp.is_field %} no-padding{% endif %}">
{% if comp.is_field %}
{{ comp.htmldiff }}
{% elif comp.is_child_relation %}
{% for child_comp in comp.get_child_comparisons %}
<div class="comparison__child-object {% if child_comp.is_addition %}addition{% elif child_comp.is_deletion %}deletion{% endif %}">
{% with child_comp.get_position_change as move %}
{% if move %}
<div class="help-block help-info">
{% icon name='help' %}
<p>
{% if move > 0 %}
{% blocktrans trimmed count counter=move %}
Moved down 1 place.
{% plural %}
Moved down {{ counter }} places.
{% endblocktrans %}
{% elif move < 0 %}
{% blocktrans trimmed count counter=move|abs %}
Moved up 1 place.
{% plural %}
Moved up {{ counter }} places.
{% endblocktrans %}
{% endif %}
</p>
</div>
{% endif %}
{% endwith %}
<dl class="comparison__list">
{% for field_comp in child_comp.get_field_comparisons %}
<dt>{{ field_comp.field_label }}</dt>
<dd>{{ field_comp.htmldiff }}</dd>
{% endfor %}
</dl>
</div>
{% endfor %}
{% endif %}
</td>
</tr>
</thead>
<tbody>
{% for comp in comparison %}
<tr>
<td class="title" valign="top">
<div class="title-wrapper">{{ comp.field_label }}:</div>
</td>
<td class="comparison{% if not comp.is_field %} no-padding{% endif %}">
{% if comp.is_field %}
{{ comp.htmldiff }}
{% elif comp.is_child_relation %}
{% for child_comp in comp.get_child_comparisons %}
<div class="comparison__child-object {% if child_comp.is_addition %}addition{% elif child_comp.is_deletion %}deletion{% endif %}">
{% with child_comp.get_position_change as move %}
{% if move %}
<div class="help-block help-info">
{% icon name='help' %}
<p>
{% if move > 0 %}
{% blocktrans trimmed count counter=move %}
Moved down 1 place.
{% plural %}
Moved down {{ counter }} places.
{% endblocktrans %}
{% elif move < 0 %}
{% blocktrans trimmed count counter=move|abs %}
Moved up 1 place.
{% plural %}
Moved up {{ counter }} places.
{% endblocktrans %}
{% endif %}
</p>
</div>
{% endif %}
{% endwith %}
<dl class="comparison__list">
{% for field_comp in child_comp.get_field_comparisons %}
<dt>{{ field_comp.field_label }}</dt>
<dd>{{ field_comp.htmldiff }}</dd>
{% endfor %}
</dl>
</div>
{% endfor %}
{% endif %}
</td>
</tr>
{% empty %}
<tr>
<td colspan="2" class="no-results-message">
<p>{% trans "There are no differences between these two versions" %}</p>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% empty %}
<tr>
<td colspan="2" class="no-results-message">
<p>{% trans "There are no differences between these two versions" %}</p>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

Wyświetl plik

@ -14,6 +14,7 @@ from wagtail.test.testapp.models import (
SecretPage,
)
from wagtail.test.utils import WagtailTestUtils
from wagtail.test.utils.template_tests import AdminTemplateTestUtils
from wagtail.test.utils.timestamps import local_datetime
@ -199,9 +200,10 @@ class TestStreamRevisions(WagtailTestUtils, TestCase):
)
class TestCompareRevisions(WagtailTestUtils, TestCase):
class TestCompareRevisions(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
# Actual tests for the comparison classes can be found in test_compare.py
base_breadcrumb_items = []
fixtures = ["test.json"]
def setUp(self):
@ -246,6 +248,33 @@ class TestCompareRevisions(WagtailTestUtils, TestCase):
html=True,
)
history_url = reverse(
"wagtailadmin_pages:history", args=(self.christmas_event.id,)
)
self.assertBreadcrumbsItemsRendered(
[
{
"url": reverse("wagtailadmin_explore_root")
if page.is_root()
else reverse("wagtailadmin_explore", args=(page.pk,)),
"label": page.get_admin_display_title(),
}
for page in self.christmas_event.get_ancestors(inclusive=True)
]
+ [
{"url": history_url, "label": "History"},
{"url": "", "label": "Compare", "sublabel": "This Christmas"},
],
response.content,
)
soup = self.get_soup(response.content)
edit_url = reverse("wagtailadmin_pages:edit", args=(self.christmas_event.id,))
edit_button = soup.select_one(f"a.w-header-button[href='{edit_url}']")
self.assertIsNotNone(edit_button)
self.assertEqual(edit_button.text.strip(), "Edit")
def test_compare_revisions_earliest(self):
compare_url = reverse(
"wagtailadmin_pages:revisions_compare",

Wyświetl plik

@ -1183,13 +1183,47 @@ class InspectView(PermissionCheckedMixin, WagtailAdminTemplateMixin, TemplateVie
class RevisionsCompareView(WagtailAdminTemplateMixin, TemplateView):
edit_handler = None
index_url_name = None
edit_url_name = None
history_url_name = None
edit_label = gettext_lazy("Edit")
history_label = gettext_lazy("History")
page_title = gettext_lazy("Compare")
template_name = "wagtailadmin/generic/revisions/compare.html"
_show_breadcrumbs = True
model = None
def get_breadcrumbs_items(self):
items = []
if (index_url := self.get_index_url()) and self.model:
items.append(
{
"url": index_url,
"label": capfirst(self.model._meta.verbose_name_plural),
}
)
if edit_url := self.get_edit_url():
items.append({"url": edit_url, "label": self.get_page_subtitle()})
if history_url := self.get_history_url():
items.append({"url": history_url, "label": self.history_label})
items.append(
{
"url": "",
"label": self.get_page_title(),
"sublabel": self.get_page_subtitle(),
}
)
return self.breadcrumbs_items + items
@cached_property
def header_buttons(self):
buttons = []
if edit_url := self.get_edit_url():
buttons.append(
HeaderButton(self.edit_label, url=edit_url, icon_name="edit")
)
return buttons
def setup(self, request, pk, revision_id_a, revision_id_b, *args, **kwargs):
super().setup(request, *args, **kwargs)
self.pk = pk
@ -1208,6 +1242,10 @@ class RevisionsCompareView(WagtailAdminTemplateMixin, TemplateView):
def get_page_subtitle(self):
return str(self.object)
def get_index_url(self):
if self.index_url_name:
return reverse(self.index_url_name)
def get_history_url(self):
if self.history_url_name:
return reverse(self.history_url_name, args=(quote(self.object.pk),))
@ -1269,10 +1307,6 @@ class RevisionsCompareView(WagtailAdminTemplateMixin, TemplateView):
context.update(
{
"object": self.object,
"history_label": self.history_label,
"edit_label": self.edit_label,
"history_url": self.get_history_url(),
"edit_url": self.get_edit_url(),
"revision_a": revision_a,
"revision_a_heading": revision_a_heading,
"revision_b": revision_b,

Wyświetl plik

@ -8,7 +8,6 @@ from django.urls import reverse
from django.utils.decorators import method_decorator
from django.utils.safestring import mark_safe
from django.utils.translation import gettext as _
from django.utils.translation import gettext_lazy
from wagtail.admin import messages
from wagtail.admin.action_menu import PageActionMenu
@ -25,6 +24,7 @@ from wagtail.admin.views.generic.models import (
RevisionsUnscheduleView,
)
from wagtail.admin.views.generic.preview import PreviewRevision
from wagtail.admin.views.pages.utils import GenericPageBreadcrumbsMixin
from wagtail.models import Page
from wagtail.utils.timestamps import render_timestamp
@ -163,12 +163,11 @@ class RevisionsView(PreviewRevision):
return page
class RevisionsCompare(RevisionsCompareView):
history_label = gettext_lazy("Page history")
edit_label = gettext_lazy("Edit this page")
class RevisionsCompare(GenericPageBreadcrumbsMixin, RevisionsCompareView):
history_url_name = "wagtailadmin_pages:history"
edit_url_name = "wagtailadmin_pages:edit"
header_icon = "doc-empty-inverse"
breadcrumbs_items_to_take = 2
@method_decorator(user_passes_test(user_has_any_page_permission))
def dispatch(self, request, *args, **kwargs):

Wyświetl plik

@ -4870,7 +4870,7 @@ class TestSnippetRevisions(WagtailTestUtils, TestCase):
self.assertEqual(self.snippet.live_revision, self.snippet.latest_revision)
class TestCompareRevisions(WagtailTestUtils, TestCase):
class TestCompareRevisions(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
# Actual tests for the comparison classes can be found in test_compare.py
def setUp(self):
@ -4908,6 +4908,32 @@ class TestCompareRevisions(WagtailTestUtils, TestCase):
html=True,
)
index_url = reverse("wagtailsnippets_tests_revisablemodel:list", args=[])
edit_url = reverse(
"wagtailsnippets_tests_revisablemodel:edit",
args=(self.snippet.id,),
)
history_url = reverse(
"wagtailsnippets_tests_revisablemodel:history",
args=(self.snippet.id,),
)
self.assertBreadcrumbsItemsRendered(
[
{"url": reverse("wagtailsnippets:index"), "label": "Snippets"},
{"url": index_url, "label": "Revisable models"},
{"url": edit_url, "label": str(self.snippet)},
{"url": history_url, "label": "History"},
{"url": "", "label": "Compare", "sublabel": str(self.snippet)},
],
response.content,
)
soup = self.get_soup(response.content)
edit_button = soup.select_one(f"a.w-header-button[href='{edit_url}']")
self.assertIsNotNone(edit_button)
self.assertEqual(edit_button.text.strip(), "Edit")
def test_compare_revisions_earliest(self):
response = self.get("earliest", self.edit_revision.pk)
self.assertEqual(response.status_code, 200)

Wyświetl plik

@ -95,7 +95,7 @@ class TestCustomIcon(BaseSnippetViewSetTests):
(
"revisions_compare",
[pk, self.revision_1.id, self.revision_2.id],
"header.html",
"headers/slim_header.html",
),
("revisions_unschedule", [pk, self.revision_2.id], "header.html"),
]

Wyświetl plik

@ -358,18 +358,6 @@ class PreviewRevisionView(PermissionCheckedMixin, PreviewRevision):
class RevisionsCompareView(PermissionCheckedMixin, generic.RevisionsCompareView):
permission_required = "change"
@property
def edit_label(self):
return _("Edit this %(model_name)s") % {
"model_name": self.model._meta.verbose_name
}
@property
def history_label(self):
return _("%(model_name)s history") % {
"model_name": self.model._meta.verbose_name
}
class UnpublishView(PermissionCheckedMixin, generic.UnpublishView):
permission_required = "publish"