Add ability to export redirects using reports ()

pull/7013/head
Martin Sandström 2020-08-07 15:14:38 +02:00 zatwierdzone przez Matt Westcott
rodzic 7423d72efe
commit c0a84975e7
9 zmienionych plików z 162 dodań i 1 usunięć
docs/releases
wagtail/contrib/redirects
static/wagtailredirects/css
templates/wagtailredirects

Wyświetl plik

@ -21,6 +21,7 @@ Changelog
* Reinstate submitter's name on moderation notification email (Matt Westcott)
* Add a new switch input widget as an alternative to checkboxes (Karl Hobley)
* Allow `{% pageurl %}` fallback to be a direct URL or an object with a `get_absolute_url` method (Andy Babic)
* Add support for exporting redirects (Martin Sandström)
* Fix: StreamField required status is now consistently handled by the `blank` keyword argument (Matt Westcott)
* Fix: Show 'required' asterisks for blocks inside required StreamFields (Matt Westcott)
* Fix: Make image chooser "Select format" fields translatable (Helen Chapman, Thibaud Colas)

Wyświetl plik

@ -44,6 +44,7 @@ Other features
* Reinstate submitter's name on moderation notification email (Matt Westcott)
* Add a new switch input widget as an alternative to checkboxes (Karl Hobley)
* Allow ``{% pageurl %}`` fallback to be a direct URL or an object with a ``get_absolute_url`` method (Andy Babic)
* Add support for exporting redirects (Martin Sandström)
Bug fixes
~~~~~~~~~

Wyświetl plik

@ -0,0 +1,25 @@
import django_filters
from django.utils.translation import gettext as _
from wagtail.admin.filters import WagtailFilterSet
from wagtail.admin.widgets import ButtonSelect
from wagtail.core.models import Site
class RedirectsReportFilterSet(WagtailFilterSet):
is_permanent = django_filters.ChoiceFilter(
label=_("Type"),
method="filter_type",
choices=((True, _("Permanent")), (False, _("Temporary")),),
empty_label=_("All"),
widget=ButtonSelect,
)
site = django_filters.ModelChoiceFilter(
field_name="site", queryset=Site.objects.all()
)
def filter_type(self, queryset, name, value):
if value and self.request and self.request.user:
queryset = queryset.filter(is_permanent=value)
return queryset

Wyświetl plik

@ -15,3 +15,7 @@ header .has-multiple-actions {
header .has-multiple-actions .actionbutton {
margin-left: 10px;
}
header .has-multiple-actions .dropdown {
margin-left: 10px;
}

Wyświetl plik

@ -23,7 +23,9 @@
{% url "wagtailredirects:add" as add_link %}
{% trans "Add redirect" as add_str %}
{% url "wagtailredirects:start_import" as import_link %}
{% url "wagtailredirects:report" as report_link %}
{% trans "Import redirects" as import_str %}
{% trans "Export redirects" as export_str %}
<header class="hasform">
{% block breadcrumb %}{% endblock %}
@ -45,8 +47,13 @@
<div class="actionbutton">
<a href="{{ add_link }}" class="button bicolor button--icon">{% icon name="plus" wrapped=1 %}{{ add_str }}</a>
</div>
<div class="actionbutton">
<div class="dropdown dropdown-button match-width">
<a href="{{ import_link }}" class="button bicolor button--icon">{% icon name="doc-full-inverse" wrapped=1 %}{{ import_str }}</a>
<div class="dropdown-toggle">{% icon name="arrow-down" %}</div>
<ul>
<li><a class="button bicolor button--icon" href="{{ report_link }}">{% icon name="download" wrapped=1 %}{{ export_str }}</a></li>
</ul>
</div>
</div>
</div>

Wyświetl plik

@ -0,0 +1,14 @@
{% extends 'wagtailadmin/reports/base_report.html' %}
{% load i18n wagtailadmin_tags %}
{% block results %}
{% if object_list %}
{% include "wagtailredirects/list.html" with redirects=object_list %}
{% else %}
<p>{% trans "No redirects found." %}</p>
{% endif %}
{% endblock %}
{% block no_results %}
<p>{% trans "No redirects found." %}</p>
{% endblock %}

Wyświetl plik

@ -0,0 +1,82 @@
from io import BytesIO
from django.test import TestCase
from django.urls import reverse
from openpyxl import load_workbook
from wagtail.contrib.redirects.models import Redirect
from wagtail.core.models import Site
from wagtail.tests.utils import WagtailTestUtils
class TestRedirectReport(TestCase, WagtailTestUtils):
def setUp(self):
self.user = self.login()
def get(self, params={}):
return self.client.get(reverse("wagtailredirects:report"), params)
def test_empty(self):
response = self.get()
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(
response, "wagtailredirects/reports/redirects_report.html"
)
self.assertContains(response, "No redirects found.")
def test_listing_contains_redirect(self):
redirect = Redirect.add_redirect("/from", "/to", False)
response = self.get()
self.assertEqual(response.status_code, 200)
self.assertContains(response, redirect.old_path)
def test_filtering_by_type(self):
temp_redirect = Redirect.add_redirect("/from", "/to", False)
perm_redirect = Redirect.add_redirect("/cat", "/dog", True)
response = self.get(params={"is_permanent": "True"})
self.assertContains(response, perm_redirect.old_path)
self.assertNotContains(response, temp_redirect.old_path)
def test_filtering_by_site(self):
site = Site.objects.first()
site_redirect = Redirect.add_redirect("/cat", "/dog")
site_redirect.site = site
site_redirect.save()
nosite_redirect = Redirect.add_redirect("/from", "/to")
response = self.get(params={"site": site.pk})
self.assertContains(response, site_redirect.old_path)
self.assertNotContains(response, nosite_redirect.old_path)
def test_csv_export(self):
Redirect.add_redirect("/from", "/to", False)
response = self.get(params={"export": "csv"})
self.assertEqual(response.status_code, 200)
csv_data = response.getvalue().decode().split("\n")
csv_header = csv_data[0]
csv_entries = csv_data[1:]
csv_entries = csv_entries[:-1] # Drop empty last line
self.assertEqual(csv_header, "From,Site,To,Type\r")
self.assertEqual(len(csv_entries), 1)
self.assertEqual(csv_entries[0], "/from,None,/to,temporary\r")
def test_xlsx_export(self):
Redirect.add_redirect("/from", "/to", True)
response = self.get(params={"export": "xlsx"})
self.assertEqual(response.status_code, 200)
workbook_data = response.getvalue()
worksheet = load_workbook(filename=BytesIO(workbook_data))["Sheet1"]
cell_array = [[cell.value for cell in row] for row in worksheet.rows]
self.assertEqual(cell_array[0], ["From", "Site", "To", "Type"])
self.assertEqual(len(cell_array), 2)
self.assertEqual(cell_array[1], ["/from", "None", "/to", "permanent"])

Wyświetl plik

@ -11,4 +11,5 @@ urlpatterns = [
path('<int:redirect_id>/delete/', views.delete, name='delete'),
path('import/', views.start_import, name="start_import"),
path('import/process/', views.process_import, name="process_import"),
path('report', views.RedirectsReportView.as_view(), name="report"),
]

Wyświetl plik

@ -15,8 +15,10 @@ from django.views.decorators.vary import vary_on_headers
from wagtail.admin import messages
from wagtail.admin.auth import PermissionPolicyChecker
from wagtail.admin.forms.search import SearchForm
from wagtail.admin.views.reports import ReportView
from wagtail.contrib.redirects import models
from wagtail.contrib.redirects.base_formats import DEFAULT_FORMATS
from wagtail.contrib.redirects.filters import RedirectsReportFilterSet
from wagtail.contrib.redirects.forms import ConfirmImportForm, ImportForm, RedirectForm
from wagtail.contrib.redirects.permissions import permission_policy
from wagtail.contrib.redirects.utils import (
@ -348,3 +350,27 @@ def to_readable_errors(error):
errors = [x.lstrip('* ') for x in errors]
errors = ", ".join(errors)
return errors
class RedirectsReportView(ReportView):
header_icon = "redirect"
title = _("Export Redirects")
template_name = "wagtailredirects/reports/redirects_report.html"
filterset_class = RedirectsReportFilterSet
list_export = [
"old_path",
"site",
"link",
"get_is_permanent_display",
]
export_headings = {
"old_path": _("From"),
"site": _("Site"),
"link": _("To"),
"get_is_permanent_display": _("Type"),
}
def get_queryset(self):
return models.Redirect.objects.all().order_by("old_path")