diff --git a/src/pretix/control/templates/pretixcontrol/event/index.html b/src/pretix/control/templates/pretixcontrol/event/index.html
index 1c9c46e0ba..88f5fd3748 100644
--- a/src/pretix/control/templates/pretixcontrol/event/index.html
+++ b/src/pretix/control/templates/pretixcontrol/event/index.html
@@ -16,6 +16,42 @@
{% endif %}
+
+ {% trans "Shop URL:" %}
+
{% abseventurl request.event "presale:event.index" %}
+
+
+
+
+
+
+
{% if has_overpaid_orders %}
{% blocktrans trimmed %}
diff --git a/src/pretix/control/urls.py b/src/pretix/control/urls.py
index e4e95546c0..845e7b656e 100644
--- a/src/pretix/control/urls.py
+++ b/src/pretix/control/urls.py
@@ -231,6 +231,7 @@ urlpatterns = [
re_path(r'^search/payments/$', search.PaymentSearch.as_view(), name='search.payments'),
re_path(r'^event/(?P[^/]+)/(?P[^/]+)/', include([
re_path(r'^$', dashboards.event_index, name='event.index'),
+ re_path(r'^qrcode.(?P(png|jpeg|gif|svg))$', event.EventQRCode.as_view(), name='event.qrcode'),
re_path(r'^widgets.json$', dashboards.event_index_widgets_lazy, name='event.index.widgets'),
re_path(r'^logs/embed$', dashboards.event_index_log_lazy, name='event.index.logs'),
re_path(r'^live/$', event.EventLive.as_view(), name='event.live'),
diff --git a/src/pretix/control/views/event.py b/src/pretix/control/views/event.py
index 635ae8f0e5..7696e7c22c 100644
--- a/src/pretix/control/views/event.py
+++ b/src/pretix/control/views/event.py
@@ -38,10 +38,13 @@ import operator
import re
from collections import OrderedDict
from decimal import Decimal
+from io import BytesIO
from itertools import groupby
from urllib.parse import urlsplit
import bleach
+import qrcode
+import qrcode.image.svg
from django.apps import apps
from django.conf import settings
from django.contrib import messages
@@ -86,7 +89,7 @@ from pretix.control.permissions import EventPermissionRequiredMixin
from pretix.control.views.mailsetup import MailSettingsSetupView
from pretix.control.views.user import RecentAuthenticationRequiredMixin
from pretix.helpers.database import rolledback_transaction
-from pretix.multidomain.urlreverse import get_event_domain
+from pretix.multidomain.urlreverse import build_absolute_uri, get_event_domain
from pretix.plugins.stripe.payment import StripeSettingsHolder
from pretix.presale.style import regenerate_css
@@ -1504,3 +1507,35 @@ class QuickSetupView(FormView):
},
] if self.request.method != "POST" else []
)
+
+
+class EventQRCode(EventPermissionRequiredMixin, View):
+ permission = 'can_change_event_settings'
+
+ def get(self, request, *args, filetype, **kwargs):
+ url = build_absolute_uri(request.event, 'presale:event.index')
+
+ qr = qrcode.QRCode(
+ version=1,
+ error_correction=qrcode.constants.ERROR_CORRECT_M,
+ box_size=10,
+ border=4,
+ )
+ qr.add_data(url)
+ qr.make(fit=True)
+
+ if filetype == 'svg':
+ factory = qrcode.image.svg.SvgPathImage
+ img = qrcode.make('Some data here', image_factory=factory)
+ r = HttpResponse(img.to_string(), content_type='image/svg+xml')
+ r['Content-Disposition'] = f'inline; filename="qrcode-{request.event.slug}.{filetype}"'
+ return r
+ elif filetype in ('jpeg', 'png', 'gif'):
+ img = qr.make_image(fill_color="black", back_color="white")
+
+ byte_io = BytesIO()
+ img.save(byte_io, filetype.upper())
+ byte_io.seek(0)
+ r = HttpResponse(byte_io.read(), content_type='image/' + filetype)
+ r['Content-Disposition'] = f'inline; filename="qrcode-{request.event.slug}.{filetype}"'
+ return r
diff --git a/src/pretix/static/pretixcontrol/scss/main.scss b/src/pretix/static/pretixcontrol/scss/main.scss
index 3a0218b643..d0c561d8b1 100644
--- a/src/pretix/static/pretixcontrol/scss/main.scss
+++ b/src/pretix/static/pretixcontrol/scss/main.scss
@@ -139,12 +139,18 @@ p.bigger {
font-size: 16px;
}
+.helper-position-relative {
+ position: relative;
+}
.helper-display-block {
display: block !important;
}
.helper-display-inline {
display: inline !important;
}
+.helper-display-inline-block {
+ display: inline-block !important;
+}
.helper-display-none-soft {
display: none;
}
diff --git a/src/setup.py b/src/setup.py
index 49fbab57bd..267c0b0639 100644
--- a/src/setup.py
+++ b/src/setup.py
@@ -218,6 +218,7 @@ setup(
'python-u2flib-server==4.*',
'pytz',
'pyuca',
+ 'qrcode==7.4.*',
'redis==4.5.*,>=4.5.4',
'reportlab==3.6.*',
'requests==2.28.*',
diff --git a/src/tests/control/test_views.py b/src/tests/control/test_views.py
index 113a3d7986..bfa33f5832 100644
--- a/src/tests/control/test_views.py
+++ b/src/tests/control/test_views.py
@@ -142,6 +142,10 @@ def logged_in_client(client, event):
('/control/events/add', 200),
('/control/event/{orga}/{event}/', 200),
+ ('/control/event/{orga}/{event}/qrcode.png', 200),
+ ('/control/event/{orga}/{event}/qrcode.jpeg', 200),
+ ('/control/event/{orga}/{event}/qrcode.svg', 200),
+ ('/control/event/{orga}/{event}/qrcode.gif', 200),
('/control/event/{orga}/{event}/live/', 200),
('/control/event/{orga}/{event}/dangerzone/', 200),
('/control/event/{orga}/{event}/cancel/', 200),