diff --git a/src/pretix/base/models/vouchers.py b/src/pretix/base/models/vouchers.py index b1b05338c1..fc3b06b356 100644 --- a/src/pretix/base/models/vouchers.py +++ b/src/pretix/base/models/vouchers.py @@ -10,10 +10,14 @@ from .items import Item, ItemVariation, Quota from .orders import CartPosition, Order, OrderPosition -def generate_code(): +def _generate_random_code(): charset = list('ABCDEFGHKLMNPQRSTUVWXYZ23456789') + return get_random_string(length=settings.ENTROPY['voucher_code'], allowed_chars=charset) + + +def generate_code(): while True: - code = get_random_string(length=settings.ENTROPY['voucher_code'], allowed_chars=charset) + code = _generate_random_code() if not Voucher.objects.filter(code=code).exists(): return code diff --git a/src/pretix/control/templates/pretixcontrol/vouchers/bulk.html b/src/pretix/control/templates/pretixcontrol/vouchers/bulk.html index e7d5cc4892..a69f70b6dd 100644 --- a/src/pretix/control/templates/pretixcontrol/vouchers/bulk.html +++ b/src/pretix/control/templates/pretixcontrol/vouchers/bulk.html @@ -18,7 +18,7 @@ placeholder="{% trans "Number" %}">
diff --git a/src/pretix/control/urls.py b/src/pretix/control/urls.py index c3ba01fb12..0de92b9668 100644 --- a/src/pretix/control/urls.py +++ b/src/pretix/control/urls.py @@ -63,6 +63,7 @@ urlpatterns = [ url(r'^quotas/add$', item.QuotaCreate.as_view(), name='event.items.quotas.add'), url(r'^vouchers/$', vouchers.VoucherList.as_view(), name='event.vouchers'), url(r'^vouchers/tags/$', vouchers.VoucherTags.as_view(), name='event.vouchers.tags'), + url(r'^vouchers/rng$', vouchers.VoucherRNG.as_view(), name='event.vouchers.rng'), url(r'^vouchers/(?P\d+)/$', vouchers.VoucherUpdate.as_view(), name='event.voucher'), url(r'^vouchers/(?P\d+)/delete$', vouchers.VoucherDelete.as_view(), name='event.voucher.delete'), diff --git a/src/pretix/control/views/vouchers.py b/src/pretix/control/views/vouchers.py index 77ba4c5af3..66a3e3789d 100644 --- a/src/pretix/control/views/vouchers.py +++ b/src/pretix/control/views/vouchers.py @@ -6,15 +6,18 @@ from django.contrib import messages from django.core.urlresolvers import resolve, reverse from django.db import transaction from django.db.models import Count, Q, Sum -from django.http import Http404, HttpResponse, HttpResponseRedirect -from django.utils.formats import date_format +from django.http import ( + Http404, HttpResponse, HttpResponseBadRequest, HttpResponseRedirect, + JsonResponse, +) from django.utils.timezone import now from django.utils.translation import ugettext_lazy as _ from django.views.generic import ( - CreateView, DeleteView, ListView, TemplateView, UpdateView, + CreateView, DeleteView, ListView, TemplateView, UpdateView, View, ) from pretix.base.models import Voucher +from pretix.base.models.vouchers import _generate_random_code from pretix.control.forms.vouchers import VoucherBulkForm, VoucherForm from pretix.control.permissions import EventPermissionRequiredMixin from pretix.control.signals import voucher_form_class @@ -261,3 +264,32 @@ class VoucherBulkCreate(EventPermissionRequiredMixin, CreateView): # TODO: Transform this into an asynchronous call? with request.event.lock(): return super().post(request, *args, **kwargs) + + +class VoucherRNG(EventPermissionRequiredMixin, View): + template_name = 'pretixcontrol/vouchers/bulk.html' + permission = 'can_change_vouchers' + + def get(self, request, *args, **kwargs): + codes = set() + try: + num = int(request.GET.get('num', '5')) + except ValueError: + return HttpResponseBadRequest() + + while len(codes) < num: + new_codes = set() + for i in range(min(num - len(codes), 500)): # Work around SQLite's SQLITE_MAX_VARIABLE_NUMBER + new_codes.add(_generate_random_code()) + new_codes -= set([v['code'] for v in Voucher.objects.filter(code__in=new_codes).values('code')]) + codes |= new_codes + + return JsonResponse({ + 'codes': list(codes) + }) + + def get_success_url(self) -> str: + return reverse('control:event.vouchers', kwargs={ + 'organizer': self.request.event.organizer.slug, + 'event': self.request.event.slug, + }) diff --git a/src/static/pretixcontrol/js/ui/main.js b/src/static/pretixcontrol/js/ui/main.js index c2475ddab5..01f45f5201 100644 --- a/src/static/pretixcontrol/js/ui/main.js +++ b/src/static/pretixcontrol/js/ui/main.js @@ -56,26 +56,11 @@ $(function () { // Vouchers $("#voucher-bulk-codes-generate").click(function () { - var charset = "ABCDEFGHKLMNPQRSTUVWXYZ23456789", - i = 0, j = 0, len = parseInt($(this).attr("data-length")), - num = parseInt($("#voucher-bulk-codes-num").val()), text = ""; - for (j = 0; j < num; j++) { - var key = []; - if (window.crypto && window.crypto.getRandomValues && Uint8Array) { - key = new Uint8Array(len); - window.crypto.getRandomValues(key); - } else { - for (i = 0; i < len; i++) { - key.push(Math.floor(Math.random() * charset.length)); - } - } - if (i > 0) { - text += "\n"; - } - for (i = 0; i < len; i++) { - text += charset.charAt(key[i] % charset.length); - } - } - $("#id_codes").html(text); + var url = $(this).attr("data-rng-url"), + num = $("#voucher-bulk-codes-num").val(); + $("#id_codes").html("Generating..."); + $.getJSON(url + '?num=' + num, function (data) { + $("#id_codes").text(data.codes.join("\n")); + }); }); }); diff --git a/src/tests/control/test_permissions.py b/src/tests/control/test_permissions.py index e07d0c2766..c99aae2cba 100644 --- a/src/tests/control/test_permissions.py +++ b/src/tests/control/test_permissions.py @@ -55,6 +55,7 @@ event_urls = [ "vouchers/2/", "vouchers/add", "vouchers/bulk_add", + "vouchers/rng", "quotas/", "quotas/2/delete", "quotas/2/",