Files
old-saburly-wagtail-web/env/lib/python3.10/site-packages/wagtail/contrib/forms/views.py
2024-08-27 20:33:44 +02:00

323 lines
12 KiB
Python

import datetime
from collections import OrderedDict
from django.contrib.admin.utils import quote
from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse
from django.utils.translation import gettext, gettext_lazy, ngettext
from django.views.generic import TemplateView
from django_filters import DateFromToRangeFilter
from wagtail.admin import messages
from wagtail.admin.filters import DateRangePickerWidget, WagtailFilterSet
from wagtail.admin.ui.tables import Column, TitleColumn
from wagtail.admin.views import generic
from wagtail.admin.views.generic.base import BaseListingView
from wagtail.admin.views.mixins import SpreadsheetExportMixin
from wagtail.contrib.forms.utils import get_forms_for_user
from wagtail.models import Page
def get_submissions_list_view(request, *args, **kwargs):
"""Call the form page's list submissions view class"""
page_id = kwargs.get("page_id")
form_page = get_object_or_404(Page, id=page_id).specific
return form_page.serve_submissions_list_view(request, *args, **kwargs)
class ContentTypeColumn(Column):
edit_url_name = "wagtailadmin_pages:edit"
cell_template_name = "wagtailforms/content_type_column.html"
def get_url(self, instance):
return reverse(self.edit_url_name, args=(quote(instance.pk),))
def get_cell_context_data(self, instance, parent_context):
context = super().get_cell_context_data(instance, parent_context)
context["url"] = self.get_url(instance)
return context
class FormPagesListView(generic.IndexView):
"""Lists the available form pages for the current user"""
template_name = "wagtailforms/index.html"
results_template_name = "wagtailforms/index_results.html"
context_object_name = "form_pages"
paginate_by = 20
page_kwarg = "p"
index_url_name = "wagtailforms:index"
index_results_url_name = "wagtailforms:index_results"
page_title = gettext_lazy("Forms")
header_icon = "form"
_show_breadcrumbs = True
columns = [
TitleColumn(
"title",
classname="title",
label=gettext_lazy("Title"),
width="50%",
url_name="wagtailforms:list_submissions",
sort_key="title",
),
ContentTypeColumn(
"content_type",
label=gettext_lazy("Origin"),
width="50%",
sort_key="content_type",
),
]
model = Page
is_searchable = False
def get_breadcrumbs_items(self):
return self.breadcrumbs_items + [
{"url": "", "label": self.page_title, "sublabel": gettext("Pages")},
]
def get_base_queryset(self):
"""Return the queryset of form pages for this view"""
return get_forms_for_user(self.request.user).select_related("content_type")
class DeleteSubmissionsView(TemplateView):
"""Delete the selected submissions"""
template_name = "wagtailforms/confirm_delete.html"
page = None
submissions = None
success_url = "wagtailforms:list_submissions"
def get_queryset(self):
"""Returns a queryset for the selected submissions"""
submission_ids = self.request.GET.getlist("selected-submissions")
submission_class = self.page.get_submission_class()
return submission_class._default_manager.filter(id__in=submission_ids)
def handle_delete(self, submissions):
"""Deletes the given queryset"""
count = submissions.count()
submissions.delete()
messages.success(
self.request,
ngettext(
"One submission has been deleted.",
"%(count)d submissions have been deleted.",
count,
)
% {"count": count},
)
def get_success_url(self):
"""Returns the success URL to redirect to after a successful deletion"""
return self.success_url
def dispatch(self, request, *args, **kwargs):
"""Check permissions, set the page and submissions, handle delete"""
page_id = kwargs.get("page_id")
if not get_forms_for_user(self.request.user).filter(id=page_id).exists():
raise PermissionDenied
self.page = get_object_or_404(Page, id=page_id).specific
self.submissions = self.get_queryset()
if self.request.method == "POST":
self.handle_delete(self.submissions)
return redirect(self.get_success_url(), page_id)
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
"""Get the context for this view"""
context = super().get_context_data(**kwargs)
context.update(
{
"page": self.page,
"submissions": self.submissions,
}
)
return context
class SubmissionsListFilterSet(WagtailFilterSet):
date = DateFromToRangeFilter(
label=gettext_lazy("Submission date"),
field_name="submit_time",
widget=DateRangePickerWidget,
)
class SubmissionsListView(SpreadsheetExportMixin, BaseListingView):
"""Lists submissions for the provided form page"""
template_name = "wagtailforms/submissions_index.html"
results_template_name = "wagtailforms/list_submissions.html"
context_object_name = "submissions"
form_page = None
default_ordering = ("-submit_time",)
ordering_csv = ("submit_time",) # keep legacy CSV ordering
orderable_fields = (
"id",
"submit_time",
) # used to validate ordering in URL
page_title = gettext_lazy("Form data")
header_icon = "form"
paginate_by = 20
filterset_class = SubmissionsListFilterSet
forms_index_url_name = "wagtailforms:index"
index_url_name = "wagtailforms:list_submissions"
index_results_url_name = "wagtailforms:list_submissions_results"
_show_breadcrumbs = True
show_export_buttons = True
def dispatch(self, request, *args, **kwargs):
"""Check permissions and set the form page"""
self.form_page = kwargs.get("form_page")
if not get_forms_for_user(request.user).filter(pk=self.form_page.id).exists():
raise PermissionDenied
if self.is_export:
data_fields = self.form_page.get_data_fields()
# Set the export fields and the headings for spreadsheet export
self.list_export = [field for field, label in data_fields]
self.export_headings = dict(data_fields)
return super().dispatch(request, *args, **kwargs)
def get_filterset_kwargs(self):
kwargs = super().get_filterset_kwargs()
kwargs["queryset"] = self.get_base_queryset()
return kwargs
def get_base_queryset(self):
"""Return queryset of form submissions"""
submission_class = self.form_page.get_submission_class()
queryset = submission_class._default_manager.filter(page=self.form_page)
return queryset
def get_validated_ordering(self):
"""Return a dict of field names with ordering labels if ordering is valid"""
orderable_fields = self.orderable_fields or ()
ordering = {}
if self.is_export:
# Revert to CSV order_by submit_time ascending for backwards compatibility
default_ordering = self.ordering_csv or ()
else:
default_ordering = self.default_ordering or ()
if isinstance(default_ordering, str):
default_ordering = (default_ordering,)
ordering_strs = self.request.GET.getlist("order_by") or list(default_ordering)
for order in ordering_strs:
try:
_, prefix, field_name = order.rpartition("-")
if field_name in orderable_fields:
ordering[field_name] = (
prefix,
"descending" if prefix == "-" else "ascending",
)
except (IndexError, ValueError):
continue # invalid ordering specified, skip it
return ordering
def get_ordering(self):
"""Return the field or fields to use for ordering the queryset"""
ordering = self.get_validated_ordering()
return [values[0] + name for name, values in ordering.items()]
def get_filename(self):
"""Returns the base filename for the generated spreadsheet data file"""
return "{}-export-{}".format(
self.form_page.slug, datetime.datetime.today().strftime("%Y-%m-%d")
)
def render_to_response(self, context, **response_kwargs):
if self.is_export:
return self.as_spreadsheet(
context["submissions"], self.request.GET.get("export")
)
return super().render_to_response(context, **response_kwargs)
def to_row_dict(self, item):
"""Orders the submission dictionary for spreadsheet writing"""
row_dict = OrderedDict(
(field, item.get_data().get(field)) for field in self.list_export
)
return row_dict
def get_index_url(self):
return reverse(self.index_url_name, args=(self.form_page.id,))
def get_index_results_url(self):
return reverse(self.index_results_url_name, args=(self.form_page.id,))
def get_page_subtitle(self):
return self.form_page.get_admin_display_title()
def get_breadcrumbs_items(self):
return self.breadcrumbs_items + [
{
"url": reverse(self.forms_index_url_name),
"label": gettext("Forms"),
},
{
"url": "",
"label": self.get_page_title(),
"sublabel": self.get_page_subtitle(),
},
]
def get_context_data(self, **kwargs):
"""Return context for view"""
context = super().get_context_data(**kwargs)
submissions = context[self.context_object_name]
data_fields = self.form_page.get_data_fields()
data_rows = []
context["submissions"] = submissions
if not self.is_export:
# Build data_rows as list of dicts containing model_id and fields
for submission in submissions:
form_data = submission.get_data()
data_row = []
for name, label in data_fields:
val = form_data.get(name)
if isinstance(val, list):
val = ", ".join(val)
data_row.append(val)
data_rows.append({"model_id": submission.id, "fields": data_row})
# Build data_headings as list of dicts containing model_id and fields
ordering_by_field = self.get_validated_ordering()
orderable_fields = self.orderable_fields
data_headings = []
for name, label in data_fields:
order_label = None
if name in orderable_fields:
order = ordering_by_field.get(name)
if order:
order_label = order[1] # 'ascending' or 'descending'
else:
order_label = "orderable" # not ordered yet but can be
data_headings.append(
{
"name": name,
"label": label,
"order": order_label,
}
)
context.update(
{
"form_page": self.form_page,
"data_headings": data_headings,
"data_rows": data_rows,
}
)
return context