kopia lustrzana https://github.com/wagtail/wagtail
Enable breadcrumbs in revisions compare view (#12675)
rodzic
01e7291486
commit
286d7cbd6b
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"),
|
||||
]
|
||||
|
|
|
@ -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"
|
||||
|
|
Ładowanie…
Reference in New Issue