Allow users with can_checkin_orders permission to use the bulk actions of the check-in list view

This commit is contained in:
Raphael Michel
2023-01-04 18:12:58 +01:00
parent 4747a4c480
commit a2f2d25169
5 changed files with 41 additions and 8 deletions

View File

@@ -28,7 +28,7 @@ from celery import states
from celery.result import AsyncResult
from django.conf import settings
from django.contrib import messages
from django.core.exceptions import ValidationError
from django.core.exceptions import PermissionDenied, ValidationError
from django.http import HttpResponse, JsonResponse, QueryDict
from django.shortcuts import redirect, render
from django.test import RequestFactory
@@ -149,6 +149,8 @@ class AsyncMixin:
return redirect(self.get_success_url(value))
def error(self, exception):
if isinstance(exception, PermissionDenied):
raise exception
messages.error(self.request, self.get_error_message(exception))
if "ajax" in self.request.POST or "ajax" in self.request.GET:
return JsonResponse({
@@ -337,8 +339,8 @@ class AsyncPostView(AsyncMixin, View):
depend on the request object unless specifically supported by this class. File upload is currently also
not supported.
"""
known_errortypes = ['ValidationError']
expected_exceptions = (ValidationError,)
known_errortypes = ['ValidationError', 'PermissionDenied']
expected_exceptions = (ValidationError, PermissionDenied)
task_base = ProfiledEventTask
def async_set_progress(self, percentage):

View File

@@ -82,8 +82,10 @@
<thead>
<tr>
<th>
<label aria-label="{% trans "select all rows for batch-operation" %}"
class="batch-select-label"><input type="checkbox" data-toggle-table/></label>
{% if "can_change_orders" in request.eventpermset or "can_checkin_orders" in request.eventpermset %}
<label aria-label="{% trans "select all rows for batch-operation" %}"
class="batch-select-label"><input type="checkbox" data-toggle-table/></label>
{% endif %}
</th>
<th>{% trans "Order code" %} <a href="?{% url_replace request 'ordering' '-code'%}"><i class="fa fa-caret-down"></i></a>
<a href="?{% url_replace request 'ordering' 'code'%}"><i class="fa fa-caret-up"></i></a></th>
@@ -125,7 +127,7 @@
{% for e in entries %}
<tr>
<td>
{% if "can_change_orders" in request.eventpermset %}
{% if "can_change_orders" in request.eventpermset or "can_checkin_orders" in request.eventpermset %}
<input type="checkbox" name="checkin" id="id_checkin" class="" value="{{ e.pk }}"/>
{% endif %}
</td>
@@ -198,7 +200,7 @@
</tbody>
</table>
</div>
{% if "can_change_orders" in request.eventpermset %}
{% if "can_change_orders" in request.eventpermset or "can_checkin_orders" in request.eventpermset %}
<button type="submit" class="btn btn-primary btn-save">
<span class="fa fa-sign-in" aria-hidden="true"></span>
{% trans "Check-In selected attendees" %}
@@ -207,6 +209,8 @@
<span class="fa fa-sign-out" aria-hidden="true"></span>
{% trans "Check-Out selected attendees" %}
</button>
{% endif %}
{% if "can_change_orders" in request.eventpermset %}
<button type="submit" class="btn btn-danger btn-save" name="revert" value="true">
<span class="fa fa-trash" aria-hidden="true"></span>
{% trans "Delete all check-ins of selected attendees" %}

View File

@@ -34,6 +34,7 @@
import dateutil.parser
from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.db import transaction
from django.db.models import Exists, Max, OuterRef, Prefetch, Subquery
from django.http import Http404, HttpResponseRedirect
@@ -171,7 +172,7 @@ class CheckInListShow(EventPermissionRequiredMixin, PaginationMixin, CheckInList
class CheckInListBulkActionView(CheckInListQueryMixin, EventPermissionRequiredMixin, AsyncPostView):
template_name = 'pretixcontrol/organizers/device_bulk_edit.html'
permission = 'can_change_orders'
permission = ('can_change_orders', 'can_checkin_orders')
context_object_name = 'device'
def dispatch(self, request, *args, **kwargs):
@@ -181,11 +182,16 @@ class CheckInListBulkActionView(CheckInListQueryMixin, EventPermissionRequiredMi
def get_queryset(self):
return super().get_queryset().prefetch_related(None).order_by()
def get_error_url(self):
return self.get_success_url(None)
@transaction.atomic()
def async_post(self, request, *args, **kwargs):
self.list = get_object_or_404(request.event.checkin_lists.all(), pk=kwargs.get("list"))
positions = self.get_queryset()
if request.POST.get('revert') == 'true':
if not request.user.has_event_permission(request.organizer, request.event, 'can_change_orders', request=request):
raise PermissionDenied()
for op in positions:
if op.order.status == Order.STATUS_PAID or (self.list.include_pending and op.order.status == Order.STATUS_PENDING):
Checkin.objects.filter(position=op, list=self.list).delete()

View File

@@ -307,6 +307,26 @@ def test_manual_checkins(client, checkin_list_env):
).exists()
@pytest.mark.django_db
def test_manual_checkins_revert_requires_order_change_permission(client, checkin_list_env):
client.login(email='dummy@dummy.dummy', password='dummy')
with scopes_disabled():
assert not checkin_list_env[5][3].checkins.exists()
Team.objects.update(can_change_orders=False, can_checkin_orders=True)
client.post('/control/event/dummy/dummy/checkinlists/{}/bulk_action'.format(checkin_list_env[6].pk), {
'checkin': [checkin_list_env[5][3].pk]
})
with scopes_disabled():
assert checkin_list_env[5][3].checkins.exists()
r = client.post('/control/event/dummy/dummy/checkinlists/{}/bulk_action'.format(checkin_list_env[6].pk), {
'checkin': [checkin_list_env[5][3].pk],
'revert': 'true'
})
assert r.status_code == 403
with scopes_disabled():
assert checkin_list_env[5][3].checkins.exists()
@pytest.mark.django_db
def test_manual_checkins_revert(client, checkin_list_env):
client.login(email='dummy@dummy.dummy', password='dummy')

View File

@@ -386,6 +386,7 @@ event_permission_urls = [
("can_view_orders", "checkinlists/", 200, HTTP_GET),
("can_view_orders", "checkinlists/1/", 404, HTTP_GET),
("can_change_orders", "checkinlists/1/bulk_action", 404, HTTP_POST),
("can_checkin_orders", "checkinlists/1/bulk_action", 404, HTTP_POST),
("can_change_event_settings", "checkinlists/add", 200, HTTP_GET),
("can_change_event_settings", "checkinlists/1/change", 404, HTTP_GET),
("can_change_event_settings", "checkinlists/1/delete", 404, HTTP_GET),