diff --git a/src/pretix/control/forms/filter.py b/src/pretix/control/forms/filter.py index ad6dd2cd3a..1a609daeec 100644 --- a/src/pretix/control/forms/filter.py +++ b/src/pretix/control/forms/filter.py @@ -2202,6 +2202,18 @@ class CheckinFilterForm(FilterForm): super().__init__(*args, **kwargs) self.fields['device'].queryset = self.event.organizer.devices.all().order_by('device_id') + self.fields['device'].widget = Select2( + attrs={ + 'data-model-select2': 'generic', + 'data-select2-url': reverse('control:organizer.devices.select2', kwargs={ + 'organizer': self.event.organizer.slug, + }), + 'data-placeholder': _('All devices'), + } + ) + self.fields['device'].widget.choices = self.fields['device'].choices + self.fields['device'].label = _('Device') + self.fields['gate'].queryset = self.event.organizer.gates.all() self.fields['checkin_list'].queryset = self.event.checkin_lists.all() diff --git a/src/pretix/control/urls.py b/src/pretix/control/urls.py index cc3d89b7df..91045a97fd 100644 --- a/src/pretix/control/urls.py +++ b/src/pretix/control/urls.py @@ -177,6 +177,7 @@ urlpatterns = [ re_path(r'^organizer/(?P[^/]+)/webhook/(?P[^/]+)/logs$', organizer.WebHookLogsView.as_view(), name='organizer.webhook.logs'), re_path(r'^organizer/(?P[^/]+)/devices$', organizer.DeviceListView.as_view(), name='organizer.devices'), + re_path(r'^organizer/(?P[^/]+)/devices/select2$', typeahead.devices_select2, name='organizer.devices.select2'), re_path(r'^organizer/(?P[^/]+)/device/add$', organizer.DeviceCreateView.as_view(), name='organizer.device.add'), re_path(r'^organizer/(?P[^/]+)/device/bulk_edit$', organizer.DeviceBulkUpdateView.as_view(), diff --git a/src/pretix/control/views/typeahead.py b/src/pretix/control/views/typeahead.py index 55e4bda6f7..e5840dfb3c 100644 --- a/src/pretix/control/views/typeahead.py +++ b/src/pretix/control/views/typeahead.py @@ -831,3 +831,41 @@ def item_meta_values(request, organizer, event): ) ] }) + + +@organizer_permission_required(("can_view_orders", "can_change_organizer_settings")) +# This decorator is a bit of a hack since this is not technically an organizer permission, but it does the job here -- +# anyone who can see orders for any event can see the check-in log view where this is used as a filter +def devices_select2(request, **kwargs): + query = request.GET.get('query', '') + try: + page = int(request.GET.get('page', '1')) + except ValueError: + page = 1 + + qq = ( + Q(name__icontains=query) | Q(hardware_brand__icontains=query) | Q(hardware_model__icontains=query) | + Q(unique_serial__istartswith=query) + ) + try: + qq |= Q(device_id=int(query)) + except ValueError: + pass + qs = request.organizer.devices.filter(qq).order_by('device_id') + + total = qs.count() + pagesize = 20 + offset = (page - 1) * pagesize + doc = { + 'results': [ + { + 'id': e.pk, + 'text': str(e), + } + for e in qs[offset:offset + pagesize] + ], + 'pagination': { + "more": total >= (offset + pagesize) + } + } + return JsonResponse(doc) diff --git a/src/tests/control/test_permissions.py b/src/tests/control/test_permissions.py index 2ff80659f7..242c95b195 100644 --- a/src/tests/control/test_permissions.py +++ b/src/tests/control/test_permissions.py @@ -513,6 +513,7 @@ organizer_permission_urls = [ ("can_change_organizer_settings", "organizer/dummy/settings/email", 200), ("can_change_organizer_settings", "organizer/dummy/settings/email/setup", 200), ("can_change_organizer_settings", "organizer/dummy/devices", 200), + ("can_change_organizer_settings", "organizer/dummy/devices/select2", 200), ("can_change_organizer_settings", "organizer/dummy/device/add", 200), ("can_change_organizer_settings", "organizer/dummy/device/1/edit", 404), ("can_change_organizer_settings", "organizer/dummy/device/1/connect", 404),