Browse Source

Enable breadcrumbs in revisions compare view (#12675)

Sage Abdullah 3 tháng trước cách đây
mục cha
commit
286d7cbd6b

+ 1 - 0
CHANGELOG.txt

@@ -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)

+ 1 - 0
docs/releases/6.4.md

@@ -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
 

+ 63 - 76
wagtail/admin/templates/wagtailadmin/generic/revisions/compare.html

@@ -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 />
+        <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 %}
 
-            <thead>
+                                    <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>
-                    <th>{% trans "Fields" %}</th>
-                    <th>{% trans "Changes" %}</th>
+                    <td colspan="2" class="no-results-message">
+                        <p>{% trans "There are no differences between these two versions" %}</p>
+                    </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>
+            {% endfor %}
+        </tbody>
+    </table>
 {% endblock %}

+ 30 - 1
wagtail/admin/tests/pages/test_revisions.py

@@ -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",

+ 38 - 4
wagtail/admin/views/generic/models.py

@@ -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,

+ 3 - 4
wagtail/admin/views/pages/revisions.py

@@ -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):

+ 27 - 1
wagtail/snippets/tests/test_snippets.py

@@ -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)

+ 1 - 1
wagtail/snippets/tests/test_viewset.py

@@ -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"),
         ]

+ 0 - 12
wagtail/snippets/views/snippets.py

@@ -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"