123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- 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"
- context_object_name = "submissions"
- form_page = None
- default_ordering = ("-submit_time",)
- ordering_csv = ("submit_time",)
- orderable_fields = (
- "id",
- "submit_time",
- )
- page_title = gettext_lazy("Form data")
- paginate_by = 20
- filterset_class = SubmissionsListFilterSet
- 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()
-
- 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:
-
- 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
- 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_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:
-
- 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})
-
- 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]
- else:
- order_label = "orderable"
- 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
|