kopia lustrzana https://github.com/wagtail/wagtail
Add breadcrumbs and new AJAX-based filtering to built-in report views
The export buttons, while working correctly on initial load, is still broken upon filtering on the client-side. This is because the base listing results template has yet to include the re-rendered header buttons. As a result, the export links won't be updated on filter. This will be fixed in the next commit. Also, the "by task" and "by workflow" links in the workflow reports are gone because the slim header doesn't support passing arbitrary actions fragment. This will be fixed in another commit using header_buttons.pull/11987/head
rodzic
105df51ae3
commit
7ab8e6932f
|
@ -7,6 +7,7 @@ It is possible to create your own custom reports in the Wagtail admin. Two base
|
|||
`wagtail.admin.views.reports.ReportView`, which provides basic listing and spreadsheet export functionality, and
|
||||
`wagtail.admin.views.reports.PageReportView`, which additionally provides a default set of fields suitable for page listings.
|
||||
For this example, we'll add a report which shows any pages with unpublished changes.
|
||||
We will register this view using the `unpublished_changes_report` name for the URL pattern.
|
||||
|
||||
```python
|
||||
# <project>/views.py
|
||||
|
@ -14,7 +15,8 @@ from wagtail.admin.views.reports import PageReportView
|
|||
|
||||
|
||||
class UnpublishedChangesReportView(PageReportView):
|
||||
pass
|
||||
index_url_name = "unpublished_changes_report"
|
||||
index_results_url_name = "unpublished_changes_report_results"
|
||||
```
|
||||
|
||||
## Defining your report
|
||||
|
@ -70,7 +72,7 @@ displaying action buttons, as well as the title, time of the last update, status
|
|||
In this example, we'll change this to a new template in a later section.
|
||||
|
||||
.. versionadded:: 6.2
|
||||
The ``results_template_name`` attribute was added to support the use of the ``wagtail.admin.ui.tables`` framework.
|
||||
The ``results_template_name`` attribute was added to support updating the listing via AJAX upon filtering and to allow the use of the ``wagtail.admin.ui.tables`` framework.
|
||||
|
||||
.. attribute:: title
|
||||
|
||||
|
@ -86,6 +88,21 @@ The name of your report, which will be displayed in the header. For our example,
|
|||
The name of the icon, using the standard Wagtail icon names. For example, the locked pages view uses ``"locked"``,
|
||||
and for our example report, we'll set it to ``'doc-empty-inverse'``.
|
||||
|
||||
.. attribute:: index_url_name
|
||||
|
||||
(string)
|
||||
|
||||
The name of the URL pattern registered for the report view.
|
||||
|
||||
.. attribute:: index_results_url_name
|
||||
|
||||
(string)
|
||||
|
||||
The name of the URL pattern registered for the results view (the report view with ``.as_view(results_only=True)``).
|
||||
|
||||
.. versionadded:: 6.2
|
||||
The ``index_results_url_name`` attribute was added to support updating the listing via AJAX upon filtering.
|
||||
|
||||
```
|
||||
|
||||
## Spreadsheet exports
|
||||
|
@ -197,6 +214,8 @@ def register_unpublished_changes_report_menu_item():
|
|||
def register_unpublished_changes_report_url():
|
||||
return [
|
||||
path('reports/unpublished-changes/', UnpublishedChangesReportView.as_view(), name='unpublished_changes_report'),
|
||||
# Add a results-only view to add support for AJAX-based filtering
|
||||
path('reports/unpublished-changes/results/', UnpublishedChangesReportView.as_view(results_only=True), name='unpublished_changes_report_results'),
|
||||
]
|
||||
```
|
||||
|
||||
|
@ -225,7 +244,8 @@ from wagtail.admin.views.reports import PageReportView
|
|||
from wagtail.models import Page
|
||||
|
||||
class UnpublishedChangesReportView(PageReportView):
|
||||
|
||||
index_url_name = "unpublished_changes_report"
|
||||
index_results_url_name = "unpublished_changes_report_results"
|
||||
header_icon = 'doc-empty-inverse'
|
||||
results_template_name = 'reports/unpublished_changes_report_results.html'
|
||||
title = "Pages with unpublished changes"
|
||||
|
@ -260,6 +280,7 @@ def register_unpublished_changes_report_menu_item():
|
|||
def register_unpublished_changes_report_url():
|
||||
return [
|
||||
path('reports/unpublished-changes/', UnpublishedChangesReportView.as_view(), name='unpublished_changes_report'),
|
||||
path('reports/unpublished-changes/results/', UnpublishedChangesReportView.as_view(results_only=True), name='unpublished_changes_report_results'),
|
||||
]
|
||||
```
|
||||
|
||||
|
|
|
@ -1,16 +1 @@
|
|||
{% extends "wagtailadmin/generic/listing.html" %}
|
||||
{% load wagtailadmin_tags %}
|
||||
|
||||
{% block main_header %}
|
||||
{% fragment as report_actions %}
|
||||
<div class="report__actions">
|
||||
{% block actions %}
|
||||
{% if view.list_export %}
|
||||
{% include view.export_buttons_template_name %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% endfragment %}
|
||||
|
||||
{% include "wagtailadmin/shared/header.html" with title=title icon=header_icon merged=1 extra_actions=report_actions %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -31,9 +31,26 @@ from wagtail.test.testapp.models import (
|
|||
SimplePage,
|
||||
)
|
||||
from wagtail.test.utils import WagtailTestUtils
|
||||
from wagtail.test.utils.template_tests import AdminTemplateTestUtils
|
||||
|
||||
|
||||
class TestLockedPagesView(WagtailTestUtils, TestCase):
|
||||
class BaseReportViewTestCase(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
|
||||
url_name = None
|
||||
|
||||
def assertActiveFilter(self, soup, filter_name, filter_value):
|
||||
# Should render the export buttons inside the header "more" dropdown
|
||||
# with the filtered URL
|
||||
links = soup.select("#w-slim-header-buttons .w-dropdown a")
|
||||
unfiltered_url = reverse(self.url_name)
|
||||
filtered_url = f"{unfiltered_url}?{filter_name}={filter_value}"
|
||||
self.assertEqual(len(links), 2)
|
||||
self.assertEqual(
|
||||
[link.get("href") for link in links],
|
||||
[f"{filtered_url}&export=xlsx", f"{filtered_url}&export=csv"],
|
||||
)
|
||||
|
||||
|
||||
class TestLockedPagesView(BaseReportViewTestCase):
|
||||
def setUp(self):
|
||||
self.user = self.login()
|
||||
|
||||
|
@ -52,19 +69,21 @@ class TestLockedPagesView(WagtailTestUtils, TestCase):
|
|||
response,
|
||||
"wagtailadmin/reports/locked_pages_results.html",
|
||||
)
|
||||
self.assertBreadcrumbsItemsRendered(
|
||||
[{"url": "", "label": "Locked pages"}],
|
||||
response.content,
|
||||
)
|
||||
|
||||
# Initially there should be no locked pages
|
||||
self.assertContains(response, "No locked pages found.")
|
||||
|
||||
# No user locked anything
|
||||
self.assertInHTML(
|
||||
"""
|
||||
<select name="locked_by" id="id_locked_by">
|
||||
<option value="" selected>---------</option>
|
||||
</select>
|
||||
""",
|
||||
response.content.decode(),
|
||||
)
|
||||
# Should render the filter inside the drilldown
|
||||
soup = self.get_soup(response.content)
|
||||
locked_by_options = soup.select(".w-drilldown select[name='locked_by'] option")
|
||||
# No user locked anything, so there should be no option for the filter
|
||||
self.assertEqual(len(locked_by_options), 1)
|
||||
self.assertEqual(locked_by_options[0].text, "---------")
|
||||
self.assertEqual(locked_by_options[0].get("value"), "")
|
||||
|
||||
parent_page = Page.objects.first()
|
||||
parent_page.add_child(
|
||||
|
@ -96,19 +115,23 @@ class TestLockedPagesView(WagtailTestUtils, TestCase):
|
|||
response,
|
||||
"wagtailadmin/reports/locked_pages_results.html",
|
||||
)
|
||||
self.assertBreadcrumbsItemsRendered(
|
||||
[{"url": "", "label": "Locked pages"}],
|
||||
response.content,
|
||||
)
|
||||
self.assertNotContains(response, "No locked pages found.")
|
||||
self.assertContains(response, "First locked page")
|
||||
self.assertContains(response, "Second locked page")
|
||||
|
||||
self.assertInHTML(
|
||||
f"""
|
||||
<select name="locked_by" id="id_locked_by">
|
||||
<option value="" selected>---------</option>
|
||||
<option value="{self.user.pk}">{self.user}</option>
|
||||
</select>
|
||||
""",
|
||||
response.content.decode(),
|
||||
)
|
||||
# Should render the filter inside the drilldown
|
||||
soup = self.get_soup(response.content)
|
||||
locked_by_options = soup.select(".w-drilldown select[name='locked_by'] option")
|
||||
# The options should only display users who have locked pages
|
||||
self.assertEqual(len(locked_by_options), 2)
|
||||
self.assertEqual(locked_by_options[0].text, "---------")
|
||||
self.assertIsNone(locked_by_options[0].value)
|
||||
self.assertEqual(locked_by_options[1].text, str(self.user))
|
||||
self.assertEqual(locked_by_options[1].get("value"), str(self.user.pk))
|
||||
|
||||
# Locked by current user shown in indicator
|
||||
self.assertContains(response, "locked-indicator indicator--is-inverse")
|
||||
|
@ -234,8 +257,9 @@ class TestLockedPagesView(WagtailTestUtils, TestCase):
|
|||
self.assertEqual(worksheet["E2"].number_format, ExcelDateFormatter().get())
|
||||
|
||||
|
||||
class TestFilteredLockedPagesView(WagtailTestUtils, TestCase):
|
||||
class TestFilteredLockedPagesView(BaseReportViewTestCase):
|
||||
fixtures = ["test.json"]
|
||||
url_name = "wagtailadmin_reports:locked_pages"
|
||||
|
||||
def setUp(self):
|
||||
self.user = self.login()
|
||||
|
@ -269,6 +293,9 @@ class TestFilteredLockedPagesView(WagtailTestUtils, TestCase):
|
|||
self.assertNotContains(response, "My locked page")
|
||||
self.assertNotContains(response, "Christmas")
|
||||
|
||||
soup = self.get_soup(response.content)
|
||||
self.assertActiveFilter(soup, "live", "false")
|
||||
|
||||
def test_filter_by_user(self):
|
||||
response = self.get(params={"locked_by": self.user.pk})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
@ -277,8 +304,9 @@ class TestFilteredLockedPagesView(WagtailTestUtils, TestCase):
|
|||
self.assertNotContains(response, "My locked page")
|
||||
|
||||
|
||||
class TestFilteredLogEntriesView(WagtailTestUtils, TestCase):
|
||||
class TestFilteredLogEntriesView(BaseReportViewTestCase):
|
||||
fixtures = ["test.json"]
|
||||
url_name = "wagtailadmin_reports:site_history"
|
||||
|
||||
def setUp(self):
|
||||
self.user = self.login()
|
||||
|
@ -361,15 +389,20 @@ class TestFilteredLogEntriesView(WagtailTestUtils, TestCase):
|
|||
self.assertSetEqual(actual, set(expected))
|
||||
|
||||
def assert_filter_actions(self, response, expected):
|
||||
soup = self.get_soup(response.content)
|
||||
actual = {
|
||||
choice[0]
|
||||
for choice in response.context["filters"].filters["action"].extra["choices"]
|
||||
choice.get("value")
|
||||
for choice in soup.select(".w-drilldown select[name='action'] option")
|
||||
}
|
||||
self.assertSetEqual(actual, set(expected))
|
||||
self.assertSetEqual(actual, set(expected) | {""})
|
||||
|
||||
def test_unfiltered(self):
|
||||
response = self.get()
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertBreadcrumbsItemsRendered(
|
||||
[{"url": "", "label": "Site history"}],
|
||||
response.content,
|
||||
)
|
||||
self.assert_log_entries(
|
||||
response,
|
||||
[
|
||||
|
@ -449,6 +482,9 @@ class TestFilteredLogEntriesView(WagtailTestUtils, TestCase):
|
|||
],
|
||||
)
|
||||
|
||||
soup = self.get_soup(response.content)
|
||||
self.assertActiveFilter(soup, "action", "wagtail.edit")
|
||||
|
||||
def test_hide_commenting_actions(self):
|
||||
response = self.get(params={"hide_commenting_actions": "on"})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
@ -547,7 +583,7 @@ class TestExcelDateFormatter(TestCase):
|
|||
self.assertEqual(formatter.format("m/d/Y g:i A"), "mm/dd/yyyy h:mm AM/PM")
|
||||
|
||||
|
||||
class TestAgingPagesView(WagtailTestUtils, TestCase):
|
||||
class TestAgingPagesView(BaseReportViewTestCase):
|
||||
def setUp(self):
|
||||
self.user = self.login()
|
||||
self.root = Page.objects.first()
|
||||
|
@ -571,6 +607,10 @@ class TestAgingPagesView(WagtailTestUtils, TestCase):
|
|||
response,
|
||||
"wagtailadmin/reports/aging_pages_results.html",
|
||||
)
|
||||
self.assertBreadcrumbsItemsRendered(
|
||||
[{"url": "", "label": "Aging pages"}],
|
||||
response.content,
|
||||
)
|
||||
|
||||
def test_displays_only_published_pages(self):
|
||||
response = self.get()
|
||||
|
@ -766,8 +806,9 @@ class TestAgingPagesViewPermissions(WagtailTestUtils, TestCase):
|
|||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class TestFilteredAgingPagesView(WagtailTestUtils, TestCase):
|
||||
class TestFilteredAgingPagesView(BaseReportViewTestCase):
|
||||
fixtures = ["test.json"]
|
||||
url_name = "wagtailadmin_reports:aging_pages"
|
||||
|
||||
def setUp(self):
|
||||
self.user = self.login()
|
||||
|
@ -790,14 +831,23 @@ class TestFilteredAgingPagesView(WagtailTestUtils, TestCase):
|
|||
self.assertNotContains(response, self.aboutus_page.title)
|
||||
|
||||
def test_filter_by_content_type(self):
|
||||
response = self.get(
|
||||
params={"content_type": self.aboutus_page.specific.content_type.pk}
|
||||
)
|
||||
ct_pk = self.aboutus_page.specific.content_type.pk
|
||||
response = self.get(params={"content_type": ct_pk})
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, self.aboutus_page.title)
|
||||
self.assertNotContains(response, self.home_page.title)
|
||||
|
||||
soup = self.get_soup(response.content)
|
||||
self.assertActiveFilter(soup, "content_type", ct_pk)
|
||||
|
||||
# Should render the filter inside the drilldown component
|
||||
ct_select = soup.select_one(".w-drilldown select[name='content_type']")
|
||||
self.assertIsNotNone(ct_select)
|
||||
selected_option = ct_select.select_one("option[selected]")
|
||||
self.assertIsNotNone(selected_option)
|
||||
self.assertEqual(selected_option.get("value"), str(ct_pk))
|
||||
|
||||
def test_filter_by_last_published_at(self):
|
||||
self.home_page.last_published_at = timezone.now()
|
||||
self.home_page.save()
|
||||
|
@ -808,7 +858,7 @@ class TestFilteredAgingPagesView(WagtailTestUtils, TestCase):
|
|||
self.assertNotContains(response, self.home_page.title)
|
||||
|
||||
|
||||
class PageTypesUsageReportViewTest(WagtailTestUtils, TestCase):
|
||||
class PageTypesUsageReportViewTest(BaseReportViewTestCase):
|
||||
fixtures = ["test.json"]
|
||||
|
||||
def setUp(self):
|
||||
|
@ -829,6 +879,10 @@ class PageTypesUsageReportViewTest(WagtailTestUtils, TestCase):
|
|||
response,
|
||||
"wagtailadmin/reports/page_types_usage_results.html",
|
||||
)
|
||||
self.assertBreadcrumbsItemsRendered(
|
||||
[{"url": "", "label": "Page types usage"}],
|
||||
response.content,
|
||||
)
|
||||
|
||||
def test_displays_only_page_types(self):
|
||||
"""Asserts that the correct models are included in the queryset."""
|
||||
|
@ -1009,6 +1063,14 @@ class PageTypesReportFiltersTests(WagtailTestUtils, TestCase):
|
|||
self.assertEqual(event_page_row.last_edited_page.locale, self.fr_locale)
|
||||
self.assertEqual(simple_page_row.last_edited_page.locale, self.fr_locale)
|
||||
|
||||
# Should render the filter inside the drilldown component
|
||||
soup = self.get_soup(response.content)
|
||||
locale_select = soup.select_one(".w-drilldown select[name='page_locale']")
|
||||
self.assertIsNotNone(locale_select)
|
||||
selected_option = locale_select.select_one("option[selected]")
|
||||
self.assertIsNotNone(selected_option)
|
||||
self.assertEqual(selected_option.get("value"), "fr")
|
||||
|
||||
def test_site_filtering_with_single_site(self):
|
||||
"""Asserts that the site filter is not displayed when there is only one site."""
|
||||
sites = Site.objects.all()
|
||||
|
|
|
@ -1493,7 +1493,7 @@ class TestEditTaskView(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
|
|||
self.assertEqual(moderator_url_finder.get_edit_url(self.task), expected_url)
|
||||
|
||||
|
||||
class BasePageWorkflowTests(WagtailTestUtils, TestCase):
|
||||
class BasePageWorkflowTests(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
|
||||
model_name = "page"
|
||||
|
||||
def setUp(self):
|
||||
|
@ -2927,10 +2927,18 @@ class TestPageWorkflowReport(BasePageWorkflowTests):
|
|||
self.assertContains(response, "test_workflow")
|
||||
self.assertContains(response, "Sebastian Mitter")
|
||||
self.assertContains(response, "March 31, 2020")
|
||||
self.assertBreadcrumbsItemsRendered(
|
||||
[{"url": "", "label": "Workflows"}],
|
||||
response.content,
|
||||
)
|
||||
|
||||
response = self.client.get(reverse("wagtailadmin_reports:workflow_tasks"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "Hello world!")
|
||||
self.assertBreadcrumbsItemsRendered(
|
||||
[{"url": "", "label": "Workflow tasks"}],
|
||||
response.content,
|
||||
)
|
||||
|
||||
def test_workflow_report_filtered(self):
|
||||
# the moderator can review the task, so the workflow state should show up even when reports are filtered by reviewable
|
||||
|
@ -2943,12 +2951,52 @@ class TestPageWorkflowReport(BasePageWorkflowTests):
|
|||
self.assertContains(response, "Sebastian Mitter")
|
||||
self.assertContains(response, "March 31, 2020")
|
||||
|
||||
# Should render the export buttons inside the header "more" dropdown
|
||||
# with the filtered URL
|
||||
soup = self.get_soup(response.content)
|
||||
links = soup.select("#w-slim-header-buttons .w-dropdown a")
|
||||
unfiltered_url = reverse("wagtailadmin_reports:workflow")
|
||||
filtered_url = f"{unfiltered_url}?reviewable=true"
|
||||
self.assertEqual(len(links), 2)
|
||||
self.assertEqual(
|
||||
[link.get("href") for link in links],
|
||||
[f"{filtered_url}&export=xlsx", f"{filtered_url}&export=csv"],
|
||||
)
|
||||
|
||||
# Should render the filter inside the drilldown component
|
||||
inputs = soup.select(".w-drilldown input[name='reviewable'][type='radio']")
|
||||
self.assertEqual(len(inputs), 2)
|
||||
self.assertEqual(inputs[0].get("value"), "")
|
||||
self.assertIsNone(inputs[0].get("checked"))
|
||||
self.assertEqual(inputs[1].get("value"), "true")
|
||||
self.assertEqual(inputs[1].get("checked"), "")
|
||||
|
||||
response = self.client.get(
|
||||
reverse("wagtailadmin_reports:workflow_tasks"), {"reviewable": "true"}
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "Hello world!")
|
||||
|
||||
# Should render the export buttons inside the header "more" dropdown
|
||||
# with the filtered URL
|
||||
soup = self.get_soup(response.content)
|
||||
links = soup.select("#w-slim-header-buttons .w-dropdown a")
|
||||
unfiltered_url = reverse("wagtailadmin_reports:workflow_tasks")
|
||||
filtered_url = f"{unfiltered_url}?reviewable=true"
|
||||
self.assertEqual(len(links), 2)
|
||||
self.assertEqual(
|
||||
[link.get("href") for link in links],
|
||||
[f"{filtered_url}&export=xlsx", f"{filtered_url}&export=csv"],
|
||||
)
|
||||
|
||||
# Should render the filter inside the drilldown component
|
||||
inputs = soup.select(".w-drilldown input[name='reviewable'][type='radio']")
|
||||
self.assertEqual(len(inputs), 2)
|
||||
self.assertEqual(inputs[0].get("value"), "")
|
||||
self.assertIsNone(inputs[0].get("checked"))
|
||||
self.assertEqual(inputs[1].get("value"), "true")
|
||||
self.assertEqual(inputs[1].get("checked"), "")
|
||||
|
||||
# the submitter cannot review the task, so the workflow state shouldn't show up when reports are filtered by reviewable
|
||||
self.login(self.submitter)
|
||||
response = self.client.get(
|
||||
|
@ -3325,7 +3373,7 @@ class TestSnippetNotificationPreferencesHTML(TestSnippetNotificationPreferences)
|
|||
pass
|
||||
|
||||
|
||||
class TestDisableViews(AdminTemplateTestUtils, BasePageWorkflowTests):
|
||||
class TestDisableViews(BasePageWorkflowTests):
|
||||
def test_disable_workflow(self):
|
||||
"""Test that deactivating a workflow sets it to inactive and cancels in progress states"""
|
||||
self.login(self.submitter)
|
||||
|
|
|
@ -11,11 +11,43 @@ from wagtail.admin.views.reports.workflows import WorkflowTasksView, WorkflowVie
|
|||
app_name = "wagtailadmin_reports"
|
||||
urlpatterns = [
|
||||
path("locked/", LockedPagesView.as_view(), name="locked_pages"),
|
||||
path(
|
||||
"locked/results/",
|
||||
LockedPagesView.as_view(results_only=True),
|
||||
name="locked_pages_results",
|
||||
),
|
||||
path("workflow/", WorkflowView.as_view(), name="workflow"),
|
||||
path(
|
||||
"workflow/results/",
|
||||
WorkflowView.as_view(results_only=True),
|
||||
name="workflow_results",
|
||||
),
|
||||
path("workflow_tasks/", WorkflowTasksView.as_view(), name="workflow_tasks"),
|
||||
path(
|
||||
"workflow_tasks/results/",
|
||||
WorkflowTasksView.as_view(results_only=True),
|
||||
name="workflow_tasks_results",
|
||||
),
|
||||
path("site-history/", LogEntriesView.as_view(), name="site_history"),
|
||||
path(
|
||||
"site-history/results/",
|
||||
LogEntriesView.as_view(results_only=True),
|
||||
name="site_history_results",
|
||||
),
|
||||
path("aging-pages/", AgingPagesView.as_view(), name="aging_pages"),
|
||||
path(
|
||||
"page-types-usage/", PageTypesUsageReportView.as_view(), name="page_types_usage"
|
||||
"aging-pages/results/",
|
||||
AgingPagesView.as_view(results_only=True),
|
||||
name="aging_pages_results",
|
||||
),
|
||||
path(
|
||||
"page-types-usage/",
|
||||
PageTypesUsageReportView.as_view(),
|
||||
name="page_types_usage",
|
||||
),
|
||||
path(
|
||||
"page-types-usage/results/",
|
||||
PageTypesUsageReportView.as_view(results_only=True),
|
||||
name="page_types_usage_results",
|
||||
),
|
||||
]
|
||||
|
|
|
@ -33,6 +33,8 @@ class AgingPagesView(PageReportView):
|
|||
title = _("Aging pages")
|
||||
header_icon = "time"
|
||||
filterset_class = AgingPagesReportFilterSet
|
||||
index_url_name = "wagtailadmin_reports:aging_pages"
|
||||
index_results_url_name = "wagtailadmin_reports:aging_pages_results"
|
||||
export_headings = {
|
||||
"status_string": _("Status"),
|
||||
"last_published_at": _("Last published at"),
|
||||
|
|
|
@ -108,6 +108,8 @@ class LogEntriesView(ReportView):
|
|||
title = _("Site history")
|
||||
header_icon = "history"
|
||||
filterset_class = SiteHistoryReportFilterSet
|
||||
index_url_name = "wagtailadmin_reports:site_history"
|
||||
index_results_url_name = "wagtailadmin_reports:site_history_results"
|
||||
|
||||
export_headings = {
|
||||
"object_id": _("ID"),
|
||||
|
|
|
@ -9,6 +9,12 @@ class ReportView(SpreadsheetExportMixin, BaseListingView):
|
|||
results_template_name = "wagtailadmin/reports/base_report_results.html"
|
||||
title = ""
|
||||
paginate_by = 50
|
||||
_show_breadcrumbs = True
|
||||
|
||||
def get_breadcrumbs_items(self):
|
||||
return super().get_breadcrumbs_items() + [
|
||||
{"url": "", "label": self.get_page_title()}
|
||||
]
|
||||
|
||||
def get_page_title(self):
|
||||
# WagtailAdminTemplateMixin uses `page_title`, but the documented approach
|
||||
|
|
|
@ -42,6 +42,8 @@ class LockedPagesView(PageReportView):
|
|||
"locked_by",
|
||||
]
|
||||
filterset_class = LockedPagesReportFilterSet
|
||||
index_url_name = "wagtailadmin_reports:locked_pages"
|
||||
index_results_url_name = "wagtailadmin_reports:locked_pages_results"
|
||||
|
||||
def get_filename(self):
|
||||
return "locked-pages-report-{}".format(
|
||||
|
|
|
@ -100,6 +100,8 @@ class PageTypesUsageReportView(ReportView):
|
|||
title = _("Page types usage")
|
||||
header_icon = "doc-empty-inverse"
|
||||
filterset_class = PageTypesUsageReportFilterSet
|
||||
index_url_name = "wagtailadmin_reports:page_types_usage"
|
||||
index_results_url_name = "wagtailadmin_reports:page_types_usage_results"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
|
|
@ -140,6 +140,8 @@ class WorkflowView(ReportView):
|
|||
title = _("Workflows")
|
||||
header_icon = "tasks"
|
||||
filterset_class = WorkflowReportFilterSet
|
||||
index_url_name = "wagtailadmin_reports:workflow"
|
||||
index_results_url_name = "wagtailadmin_reports:workflow_results"
|
||||
|
||||
export_headings = {
|
||||
"content_object.pk": _("Page/Snippet ID"),
|
||||
|
@ -213,6 +215,8 @@ class WorkflowTasksView(ReportView):
|
|||
title = _("Workflow tasks")
|
||||
header_icon = "thumbtack"
|
||||
filterset_class = WorkflowTasksReportFilterSet
|
||||
index_url_name = "wagtailadmin_reports:workflow_tasks"
|
||||
index_results_url_name = "wagtailadmin_reports:workflow_tasks_results"
|
||||
|
||||
export_headings = {
|
||||
"workflow_state.content_object.pk": _("Page/Snippet ID"),
|
||||
|
|
Ładowanie…
Reference in New Issue