Rohit Sharma 2024-04-27 12:38:59 +00:00 zatwierdzone przez GitHub
commit 4efca19d74
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
13 zmienionych plików z 302 dodań i 208 usunięć

Wyświetl plik

@ -0,0 +1,31 @@
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext
from wagtail.contrib.forms.bulk_actions.form_bulk_action import FormSubmissionBulkAction
from wagtail.contrib.forms.utils import get_forms_for_user
class DeleteBulkAction(FormSubmissionBulkAction):
display_name = _("Delete")
aria_label = _("Delete selected objects")
action_type = "delete"
template_name = "bulk_actions/confirm_bulk_delete.html"
def check_perm(self, obj):
return get_forms_for_user(self.request.user).exists()
@classmethod
def execute_action(cls, objects, **kwargs):
num_forms = 0
for obj in objects:
num_forms = num_forms + 1
obj.delete()
return num_forms, 0
def get_success_message(self, count, num_child_objects):
return ngettext(
"One submission has been deleted.",
"%(count)d submissions have been deleted.",
count,
) % {"count": count}

Wyświetl plik

@ -0,0 +1,10 @@
from wagtail.admin.views.bulk_action import BulkAction
from wagtail.contrib.forms.models import FormSubmission
class FormSubmissionBulkAction(BulkAction):
models = [FormSubmission]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
return context

Wyświetl plik

@ -0,0 +1,41 @@
{% extends 'wagtailadmin/bulk_actions/confirmation/base.html' %}
{% load i18n wagtailadmin_tags %}
{% block titletag %}{% blocktrans count counter=items|length %}Delete 1 item{% plural %}Delete {{ counter }} items{% endblocktrans %}{% endblock %}
{% block header %}
{% trans "Delete" as del_str %}
{% include "wagtailadmin/shared/header.html" with title=del_str icon="doc-empty-inverse" %}
{% endblock header %}
{% block items_with_access %}
{% if items %}
<p>
{% blocktrans trimmed count counter=items|length %}
Are you sure you want to delete this form submission?
{% plural %}
Are you sure you want to delete these form submissions?
{% endblocktrans %}
</p>
{% include 'bulk_actions/list_form_submissions.html' %}
{% endif %}
{% endblock items_with_access %}
{% block items_with_no_access %}
{% blocktranslate trimmed asvar no_access_msg count counter=items_with_no_access|length %}You don't have permission to delete this item{% plural %}You don't have permission to delete these items{% endblocktranslate %}
{% include 'bulk_actions/list_items_with_no_access.html' with items=items_with_no_access %}
{% endblock items_with_no_access %}
{% block form_section %}
{% if items %}
{% trans 'Yes, delete' as action_button_text %}
{% trans "No, don't delete" as no_action_button_text %}
{% include 'wagtailadmin/bulk_actions/confirmation/form.html' with action_button_class="serious" %}
{% else %}
{% include 'wagtailadmin/bulk_actions/confirmation/go_back.html' %}
{% endif %}
{% endblock form_section %}

Wyświetl plik

@ -0,0 +1,29 @@
{% load i18n %}
<table class="listing">
<thead>
<tr>
<th id="submit_time" class="">
Submission date
</th>
{% for heading in data_headings %}
<th>
{{ heading.name }}
</th>
{% endfor %}
</tr>
</thead>
{% for item in items %}
<tbody>
<tr>
<td>
{{item.item.submit_time}}
</td>
{% for label,data in item.item.form_data.items %}
<td>
{{ data }}
</td>
{% endfor %}
</tr>
</tbody>
{% endfor %}
</table>

Wyświetl plik

@ -0,0 +1,6 @@
{% extends 'wagtailadmin/bulk_actions/confirmation/list_items_with_no_access.html' %}
{% load i18n wagtailadmin_tags %}
{% block per_item %}
{{ item }}
{% endblock per_item %}

Wyświetl plik

@ -1,23 +0,0 @@
{% extends "wagtailadmin/base.html" %}
{% load i18n %}
{% block titletag %}{% blocktrans trimmed with title=page.title %}Delete form data {{ title }}{% endblocktrans %}{% endblock %}
{% block bodyclass %}menu-explorer{% endblock %}
{% block content %}
{% trans "Delete form data" as del_str %}
{% include "wagtailadmin/shared/header.html" with title=del_str subtitle=page.title icon="doc-empty-inverse" %}
<div class="nice-padding">
<p>
{% blocktrans trimmed count counter=submissions.count %}
Are you sure you want to delete this form submission?
{% plural %}
Are you sure you want to delete these form submissions?
{% endblocktrans %}
</p>
<form action="{% url 'wagtailforms:delete_submissions' page.id %}?{{ request.GET.urlencode }}" method="POST">
{% csrf_token %}
<input type="submit" value="{% trans 'Delete' %}" class="button serious">
</form>
</div>
{% endblock %}

Wyświetl plik

@ -1,14 +1,14 @@
{% extends "wagtailadmin/generic/index_results.html" %}
{% load i18n %}
{% load i18n l10n wagtailadmin_tags %}
{% block results %}
<form class="w-overflow-auto" data-controller="w-bulk" data-w-bulk-action-inactive-class="w-invisible" action="{% url 'wagtailforms:delete_submissions' form_page.id %}" method="get">
<form class="w-overflow-auto" data-controller="w-bulk" data-w-bulk-action-inactive-class="w-invisible">
<table class="listing">
<col />
<col />
<col />
<thead>
<tr>
<th><input type="checkbox" data-action="w-bulk#toggleAll" data-w-bulk-target="all" /></th>
{% include 'wagtailadmin/bulk_actions/select_all_checkbox_cell.html' %}
{% for heading in data_headings %}
<th id="{{ heading.name }}" class="{% if heading.order %}ordered icon {% if heading.order == 'ascending' %}icon-arrow-up-after{% else %}icon-arrow-down-after{% endif %}{% endif %}">
{% if heading.order %}<a href="?order_by={% if heading.order == 'ascending' %}-{% endif %}{{ heading.name }}">{{ heading.label }}</a>{% else %}{{ heading.label }}{% endif %}
@ -17,10 +17,16 @@
</tr>
</thead>
<tbody>
{% trans "Select response" as checkbox_aria_label %}
{% for row in data_rows %}
<tr>
<td>
<input type="checkbox" name="selected-submissions" class="select-submission" value="{{ row.model_id }}" data-action="w-bulk#toggle" data-w-bulk-target="item" />
<td class="bulk-action-checkbox-cell">
<input type="checkbox"
{% if obj_type == 'data_rows' %}data-page-status="{% if instance.live %}live{% else %}draft{% endif %}"{% endif %}
data-object-id="{{ row.id|unlocalize|admin_urlquote }}" data-bulk-action-checkbox class="bulk-action-checkbox"
aria-label="{% trans "Select" %}"
{% if aria_describedby %}aria-describedby="{{ aria_describedby }}"{% endif %}
/>
</td>
{% for cell in row.fields %}
<td>
@ -31,11 +37,6 @@
{% endfor %}
</tbody>
</table>
<div class="nice-padding">
<button class="button no w-invisible" data-w-bulk-target="action">
{% trans "Delete selected submissions" %}
</button>
</div>
</form>
{% endblock %}

Wyświetl plik

@ -1,3 +1,15 @@
{% extends "wagtailadmin/generic/listing.html" %}
{% load i18n %}
{% load i18n wagtailadmin_tags %}
{% block titletag %}{% blocktrans trimmed with form_title=form_page.title|capfirst %}Submissions of {{ form_title }}{% endblocktrans %}{% endblock %}
{% block extra_js %}
<script>
window.wagtailConfig.BULK_ACTION_ITEM_TYPE = 'SNIPPET';
</script>
<script defer src="{% versioned_static 'wagtailadmin/js/bulk-actions.js' %}"></script>
{{ block.super }}
{% endblock %}
{% block bulk_actions %}
{% trans "Select all documents in listing" as select_all_text %}
{% include 'wagtailadmin/bulk_actions/footer.html' with select_all_obj_text=select_all_text app_label=app_label model_name=model_name objects=data_rows %}
{% endblock %}

Wyświetl plik

@ -1201,63 +1201,84 @@ class TestDeleteFormSubmission(WagtailTestUtils, TestCase):
fixtures = ["test.json"]
def setUp(self):
self.form_model = FormSubmission
self.login(username="siteeditor", password="password")
self.form_page = Page.objects.get(url_path="/home/contact-us/")
def test_delete_submission_show_confirmation(self):
response = self.client.get(
reverse("wagtailforms:delete_submissions", args=(self.form_page.id,))
+ f"?selected-submissions={FormSubmission.objects.first().id}"
reverse(
"wagtail_bulk_action",
args=(
self.form_model._meta.app_label,
self.form_model._meta.model_name,
"delete",
),
)
+ f"?&id={FormSubmission.objects.first().id}"
)
# Check show confirm page when HTTP method is GET
self.assertTemplateUsed(response, "wagtailforms/confirm_delete.html")
self.assertTemplateUsed(response, "bulk_actions/confirm_bulk_delete.html")
# Check that the deletion has not happened with GET request
self.assertEqual(FormSubmission.objects.count(), 2)
def test_delete_submission_with_permissions(self):
response = self.client.post(
reverse("wagtailforms:delete_submissions", args=(self.form_page.id,))
+ f"?selected-submissions={FormSubmission.objects.first().id}"
reverse(
"wagtail_bulk_action",
args=(
self.form_model._meta.app_label,
self.form_model._meta.model_name,
"delete",
),
)
+ f"?&id={FormSubmission.objects.first().id}"
)
# Check that the submission is gone
self.assertEqual(FormSubmission.objects.count(), 1)
# Should be redirected to list of submissions
self.assertRedirects(
response,
reverse("wagtailforms:list_submissions", args=(self.form_page.id,)),
)
def test_delete_multiple_submissions_with_permissions(self):
response = self.client.post(
reverse("wagtailforms:delete_submissions", args=(self.form_page.id,))
+ "?selected-submissions={}&selected-submissions={}".format(
reverse(
"wagtail_bulk_action",
args=(
self.form_model._meta.app_label,
self.form_model._meta.model_name,
"delete",
),
)
+ "?&id={}&id={}".format(
FormSubmission.objects.first().id, FormSubmission.objects.last().id
)
)
# Check that both submissions are gone
self.assertEqual(FormSubmission.objects.count(), 0)
# Should be redirected to list of submissions
self.assertRedirects(
response,
reverse("wagtailforms:list_submissions", args=(self.form_page.id,)),
)
def test_delete_submission_bad_permissions(self):
self.login(username="eventeditor", password="password")
response = self.client.post(
reverse("wagtailforms:delete_submissions", args=(self.form_page.id,))
+ f"?selected-submissions={FormSubmission.objects.first().id}"
response = self.client.get(
reverse(
"wagtail_bulk_action",
args=(
self.form_model._meta.app_label,
self.form_model._meta.model_name,
"delete",
),
)
+ f"?&id={FormSubmission.objects.first().id}"
)
# Check that the user received a permission denied response
self.assertRedirects(response, "/admin/")
self.assertEqual(response.status_code, 200)
# Check that the deletion has not happened
self.assertEqual(FormSubmission.objects.count(), 2)
html = response.content.decode()
self.assertInHTML(
f"<p>You don't have permission to delete this item</p>",
html,
)
def test_delete_submission_after_filter_form_submissions_for_user_hook(self):
# Hook forbids to delete form submissions for everyone
@ -1267,26 +1288,24 @@ class TestDeleteFormSubmission(WagtailTestUtils, TestCase):
with self.register_hook(
"filter_form_submissions_for_user", construct_forms_for_user
):
response = self.client.post(
reverse("wagtailforms:delete_submissions", args=(self.form_page.id,))
+ f"?selected-submissions={FormSubmission.objects.first().id}"
response = self.client.get(
reverse(
"wagtail_bulk_action",
args=(
self.form_model._meta.app_label,
self.form_model._meta.model_name,
"delete",
),
)
+ f"?&id={FormSubmission.objects.first().id}"
)
# An user can't delete a from submission with the hook
self.assertRedirects(response, "/admin/")
self.assertEqual(FormSubmission.objects.count(), 2)
# An user can delete a form submission without the hook
response = self.client.post(
reverse("wagtailforms:delete_submissions", args=(self.form_page.id,))
+ "?selected-submissions={}".format(
CustomFormPageSubmission.objects.first().id
)
)
self.assertEqual(FormSubmission.objects.count(), 1)
self.assertRedirects(
response,
reverse("wagtailforms:list_submissions", args=(self.form_page.id,)),
self.assertEqual(response.status_code, 200)
html = response.content.decode()
self.assertInHTML(
f"<p>You don't have permission to delete this item</p>",
html,
)
@ -1294,43 +1313,56 @@ class TestDeleteCustomFormSubmission(WagtailTestUtils, TestCase):
fixtures = ["test.json"]
def setUp(self):
self.form_model = CustomFormPageSubmission
self.login(username="siteeditor", password="password")
self.form_page = Page.objects.get(url_path="/home/contact-us-one-more-time/")
def test_delete_submission_show_confirmation(self):
response = self.client.get(
reverse("wagtailforms:delete_submissions", args=(self.form_page.id,))
+ "?selected-submissions={}".format(
CustomFormPageSubmission.objects.first().id
reverse(
"wagtail_bulk_action",
args=(
self.form_model._meta.app_label,
self.form_model._meta.model_name,
"delete",
),
)
+ "?&id={}".format(CustomFormPageSubmission.objects.first().id)
)
# Check show confirm page when HTTP method is GET
self.assertTemplateUsed(response, "wagtailforms/confirm_delete.html")
self.assertTemplateUsed(response, "bulk_actions/confirm_bulk_delete.html")
# Check that the deletion has not happened with GET request
self.assertEqual(CustomFormPageSubmission.objects.count(), 2)
def test_delete_submission_with_permissions(self):
response = self.client.post(
reverse("wagtailforms:delete_submissions", args=(self.form_page.id,))
+ "?selected-submissions={}".format(
CustomFormPageSubmission.objects.first().id
reverse(
"wagtail_bulk_action",
args=(
self.form_model._meta.app_label,
self.form_model._meta.model_name,
"delete",
),
)
+ "?&id={}".format(CustomFormPageSubmission.objects.first().id)
)
# Check that the submission is gone
self.assertEqual(CustomFormPageSubmission.objects.count(), 1)
# Should be redirected to list of submissions
self.assertRedirects(
response,
reverse("wagtailforms:list_submissions", args=(self.form_page.id,)),
)
def test_delete_multiple_submissions_with_permissions(self):
response = self.client.post(
reverse("wagtailforms:delete_submissions", args=(self.form_page.id,))
+ "?selected-submissions={}&selected-submissions={}".format(
reverse(
"wagtail_bulk_action",
args=(
self.form_model._meta.app_label,
self.form_model._meta.model_name,
"delete",
),
)
+ "?&id={}&id={}".format(
CustomFormPageSubmission.objects.first().id,
CustomFormPageSubmission.objects.last().id,
)
@ -1338,27 +1370,28 @@ class TestDeleteCustomFormSubmission(WagtailTestUtils, TestCase):
# Check that both submissions are gone
self.assertEqual(CustomFormPageSubmission.objects.count(), 0)
# Should be redirected to list of submissions
self.assertRedirects(
response,
reverse("wagtailforms:list_submissions", args=(self.form_page.id,)),
)
def test_delete_submission_bad_permissions(self):
self.login(username="eventeditor", password="password")
response = self.client.post(
reverse("wagtailforms:delete_submissions", args=(self.form_page.id,))
+ "?selected-submissions={}".format(
CustomFormPageSubmission.objects.first().id
response = self.client.get(
reverse(
"wagtail_bulk_action",
args=(
self.form_model._meta.app_label,
self.form_model._meta.model_name,
"delete",
),
)
+ "?&id={}".format(CustomFormPageSubmission.objects.first().id)
)
# Check that the user received a permission denied response
self.assertRedirects(response, "/admin/")
# Check that the deletion has not happened
self.assertEqual(CustomFormPageSubmission.objects.count(), 2)
self.assertEqual(response.status_code, 200)
html = response.content.decode()
self.assertInHTML(
f"<p>You don't have permission to delete this item</p>",
html,
)
class TestFormsWithCustomSubmissionsList(WagtailTestUtils, TestCase):

Wyświetl plik

@ -1,7 +1,6 @@
from django.urls import path
from wagtail.contrib.forms.views import (
DeleteSubmissionsView,
FormPagesListView,
get_submissions_list_view,
)
@ -21,9 +20,4 @@ urlpatterns = [
{"results_only": True},
name="list_submissions_results",
),
path(
"submissions/<int:page_id>/delete/",
DeleteSubmissionsView.as_view(),
name="delete_submissions",
),
]

Wyświetl plik

@ -45,3 +45,44 @@ def get_forms_for_user(user):
editable_forms = fn(user, editable_forms)
return editable_forms
def get_form_submissions_as_data(
data_fields={}, submissions=[], orderable_fields=[], ordering_by_field={}
):
"""
Build data_rows as list of dicts containing id and fields and
build data_headings as list of dicts containing id and fields
"""
data_rows = []
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({"id": submission.id, "fields": data_row})
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,
}
)
return (
data_headings,
data_rows,
)

Wyświetl plik

@ -3,19 +3,17 @@ 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.shortcuts import get_object_or_404
from django.urls import reverse
from django.utils.translation import gettext, gettext_lazy, ngettext
from django.views.generic import TemplateView
from django.utils.translation import gettext, gettext_lazy
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.contrib.forms.utils import get_form_submissions_as_data, get_forms_for_user
from wagtail.models import Page
@ -81,69 +79,6 @@ class FormPagesListView(generic.IndexView):
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"),
@ -280,43 +215,22 @@ class SubmissionsListView(SpreadsheetExportMixin, BaseListingView):
data_fields = self.form_page.get_data_fields()
data_rows = []
context["submissions"] = submissions
context["page_title"] = self.page_title
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,
}
)
(data_headings, data_rows) = get_form_submissions_as_data(
data_fields=data_fields,
submissions=submissions,
orderable_fields=self.orderable_fields,
ordering_by_field=self.get_validated_ordering(),
)
context.update(
{
"app_label": "wagtailforms",
"model_name": "formsubmission",
"form_page": self.form_page,
"data_headings": data_headings,
"data_rows": data_rows,
}
)
return context
return context

Wyświetl plik

@ -4,6 +4,7 @@ from django.utils.translation import gettext_lazy as _
from wagtail import hooks
from wagtail.admin.menu import MenuItem
from wagtail.contrib.forms import urls
from wagtail.contrib.forms.bulk_actions.delete import DeleteBulkAction
from wagtail.contrib.forms.utils import get_forms_for_user
@ -29,3 +30,7 @@ def register_forms_menu_item():
icon_name="form",
order=700,
)
for action_class in [DeleteBulkAction]:
hooks.register("register_bulk_action", action_class)