From 3d31b952012428440ea8c3c83be5442a6ebaf44a Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Mon, 28 Oct 2019 22:35:16 +0100 Subject: [PATCH] Event list: Autocomplete meta values --- src/pretix/control/forms/filter.py | 12 +++- src/pretix/control/urls.py | 1 + src/pretix/control/views/typeahead.py | 55 ++++++++++++++++++- src/pretix/static/pretixcontrol/js/ui/main.js | 32 +++++++++++ 4 files changed, 98 insertions(+), 2 deletions(-) diff --git a/src/pretix/control/forms/filter.py b/src/pretix/control/forms/filter.py index 719a3e9bfb..d95fe18b8e 100644 --- a/src/pretix/control/forms/filter.py +++ b/src/pretix/control/forms/filter.py @@ -1,4 +1,5 @@ from datetime import datetime, time +from urllib.parse import urlencode from django import forms from django.apps import apps @@ -583,7 +584,16 @@ class EventFilterForm(FilterForm): continue seen.add(p.name) self.fields['meta_{}'.format(p.name)] = forms.CharField( - label=p.name, required=False + label=p.name, + required=False, + widget=forms.TextInput( + attrs={ + 'data-typeahead-url': reverse('control:events.meta.typeahead') + '?' + urlencode({ + 'property': p.name, + 'organizer': self.organizer.slug if self.organizer else '' + }) + } + ) ) if self.organizer: del self.fields['organizer'] diff --git a/src/pretix/control/urls.py b/src/pretix/control/urls.py index 2c185bdf40..1eea954da9 100644 --- a/src/pretix/control/urls.py +++ b/src/pretix/control/urls.py @@ -108,6 +108,7 @@ urlpatterns = [ url(r'^events/$', main.EventList.as_view(), name='events'), url(r'^events/add$', main.EventWizard.as_view(), name='events.add'), url(r'^events/typeahead/$', typeahead.event_list, name='events.typeahead'), + url(r'^events/typeahead/meta/$', typeahead.meta_values, name='events.meta.typeahead'), url(r'^search/orders/$', search.OrderSearch.as_view(), name='search.orders'), url(r'^event/(?P[^/]+)/(?P[^/]+)/', include([ url(r'^$', dashboards.event_index, name='event.index'), diff --git a/src/pretix/control/views/typeahead.py b/src/pretix/control/views/typeahead.py index 02b3b0a3b0..4cbd3b1bb7 100644 --- a/src/pretix/control/views/typeahead.py +++ b/src/pretix/control/views/typeahead.py @@ -6,12 +6,16 @@ from django.core.exceptions import PermissionDenied from django.db.models import Max, Min, Q from django.db.models.functions import Coalesce, Greatest from django.http import JsonResponse +from django.shortcuts import get_object_or_404 from django.urls import reverse from django.utils.formats import get_format from django.utils.timezone import make_aware from django.utils.translation import pgettext, ugettext as _ -from pretix.base.models import Order, Organizer, SubEvent, User, Voucher +from pretix.base.models import ( + EventMetaProperty, EventMetaValue, Order, Organizer, SubEvent, User, + Voucher, +) from pretix.control.forms.event import EventWizardCopyForm from pretix.control.permissions import event_permission_required from pretix.helpers.daterange import daterange @@ -547,3 +551,52 @@ def users_select2(request): } return JsonResponse(doc) + + +def meta_values(request): + q = request.GET.get('q') + propname = request.GET.get('property') + organizer = request.GET.get('organizer') + + matches = EventMetaValue.objects.filter( + value__icontains=q, + property__name=propname + ) + defaults = EventMetaProperty.objects.filter( + name=propname, + default__icontains=q + ) + + if organizer: + organizer = get_object_or_404(Organizer, slug=organizer) + if not request.user.has_organizer_permission(organizer, request=request): + raise PermissionDenied() + + defaults = defaults.filter(organizer_id=organizer.pk) + matches = matches.filter(event__organizer_id=organizer.pk) + all_access = ( + (request and request.user.has_active_staff_session(request.session.session_key)) + or request.user.teams.filter(all_events=True, organizer=organizer).exists() + ) + if not all_access: + matches = matches.filter(event__id__in=request.user.teams.values_list('limit_events__id', flat=True)) + + else: + # We ignore superuser permissions here. This is intentional – we do not want to show super + # users a form with all meta properties ever assigned. + defaults = defaults.filter( + organizer_id__in=request.user.teams.values_list('organizer', flat=True), + ) + + if not (request and request.user.has_active_staff_session(request.session.session_key)): + matches = matches.filter( + Q(event__organizer_id__in=request.user.teams.filter(all_events=True).values_list('organizer', flat=True)) + | Q(event__id__in=request.user.teams.values_list('limit_events__id', flat=True)) + ) + + return JsonResponse({ + 'results': [ + {'name': v, 'id': v} + for v in sorted(set(defaults.values_list('default', flat=True)[:10]) | set(matches.values_list('value', flat=True)[:10])) + ] + }) diff --git a/src/pretix/static/pretixcontrol/js/ui/main.js b/src/pretix/static/pretixcontrol/js/ui/main.js index 38aebf02e7..b46d76d8d4 100644 --- a/src/pretix/static/pretixcontrol/js/ui/main.js +++ b/src/pretix/static/pretixcontrol/js/ui/main.js @@ -373,6 +373,38 @@ var form_handlers = function (el) { language: $("body").attr("data-select2-locale"), }); + el.find('input[data-typeahead-url]').each(function () { + var $inp = $(this); + $inp.typeahead(null, { + minLength: 1, + highlight: true, + source: new Bloodhound({ + datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'), + queryTokenizer: Bloodhound.tokenizers.whitespace, + remote: { + url: $inp.attr("data-typeahead-url"), + prepare: function (query, settings) { + var sep = (settings.url.indexOf('?') > 0) ? '&' : '?'; + settings.url = settings.url + sep + 'q=' + encodeURIComponent(query); + return settings; + }, + transform: function (object) { + var results = object.results; + var suggs = []; + var reslen = results.length; + for (var i = 0; i < reslen; i++) { + suggs.push(results[i]); + } + return suggs; + } + } + }), + display: function (obj) { + return obj.name; + }, + }); + }); + el.find('[data-model-select2=generic]').each(function () { var $s = $(this); $s.select2({