Compare commits

..
Author SHA1 Message Date
Maximilian Richt c00cebb724 Show url of public organizer page on organizer overview 2026-10-13 14:52:40 +02:00
10 changed files with 97 additions and 22 deletions
-1
View File
@@ -316,7 +316,6 @@ def mail(email: Union[str, Sequence[str]], subject: Union[str, FormattedString],
body_html = None
m = OutgoingMail.objects.create(
guid=guid,
organizer=organizer,
event=event,
order=order,
+1 -2
View File
@@ -359,8 +359,7 @@ def mark_order_refunded(order, user=None, auth=None, api_token=None):
def mark_order_expired(order, user=None, auth=None):
"""
Mark this order as expired. This sets the order status, generates an invoice cancellation
and returns the order object.
Mark this order as expired. This sets the payment status and returns the order object.
:param order: The order to change
:param user: The user that performed the change
"""
-5
View File
@@ -752,11 +752,6 @@ def base_placeholders(sender, **kwargs):
lambda position_or_address: concatenation_for_salutation(get_best_name(position_or_address, parts=True)),
lambda event: concatenation_for_salutation(name_scheme['sample']),
))
ph.append(SimpleFunctionalTextPlaceholder(
"attendee_name_for_salutation", ["position"],
lambda position: concatenation_for_salutation(position.attendee_name_parts),
lambda event: concatenation_for_salutation(name_scheme['sample']),
))
for f, l, w in name_scheme['fields']:
if f == 'full_name':
@@ -1,11 +1,27 @@
{% extends "pretixcontrol/organizers/base.html" %}
{% load i18n %}
{% load eventurl %}
{% load bootstrap3 %}
{% load static %}
{% block inner %}
<h1>
{% blocktrans with name=request.organizer.name %}Organizer: {{ name }}{% endblocktrans %}
</h1>
<div class="helper-space-below">
{% trans "Public profile" %}:
<span id="organizer_url" class="text-muted">{% abseventurl request.organizer "presale:organizer.index" %}</span>
<button type="button" class="btn btn-default btn-xs btn-clipboard js-only" data-clipboard-target="#organizer_url">
<i class="fa fa-clipboard" aria-hidden="true"></i>
<span class="sr-only">{% trans "Copy to clipboard" %}</span>
</button>
<div class="btn-group helper-display-inline-block">
<button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown" title="{% trans "Create QR code" %}" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-qrcode" aria-hidden="true"></i>
</button>
{% include "pretixcontrol/organizers/fragment_qr_dropdown.html" with url=0 %}
</div>
<div class="clearfix"></div>
</div>
{% if events|length == 0 and not filter_form.filtered %}
{% if "organizer.events:create" in request.orgapermset %}
<p>
@@ -0,0 +1,27 @@
{% load i18n %}
<ul class="dropdown-menu dropdown-menu-right">
<li>
<a href="{% url "control:organizer.qrcode" organizer=request.organizer.slug filetype="png" %}{% if url %}?url={{ url|urlencode }}{% endif %}"
target="_blank" download>
{% blocktrans with filetype="PNG" %}Download QR code as {{ filetype }} image{% endblocktrans %}
</a>
</li>
<li>
<a href="{% url "control:organizer.qrcode" organizer=request.organizer.slug filetype="svg" %}{% if url %}?url={{ url|urlencode }}{% endif %}"
target="_blank" download>
{% blocktrans with filetype="SVG" %}Download QR code as {{ filetype }} image{% endblocktrans %}
</a>
</li>
<li>
<a href="{% url "control:organizer.qrcode" organizer=request.organizer.slug filetype="jpeg" %}{% if url %}?url={{ url|urlencode }}{% endif %}"
target="_blank" download>
{% blocktrans with filetype="JPG" %}Download QR code as {{ filetype }} image{% endblocktrans %}
</a>
</li>
<li>
<a href="{% url "control:organizer.qrcode" organizer=request.organizer.slug filetype="gif" %}{% if url %}?url={{ url|urlencode }}{% endif %}"
target="_blank" download>
{% blocktrans with filetype="GIF" %}Download QR code as {{ filetype }} image{% endblocktrans %}
</a>
</li>
</ul>
+1
View File
@@ -263,6 +263,7 @@ urlpatterns = [
name='organizer.export.scheduled.delete'),
re_path(r'^organizer/(?P<organizer>[^/]+)/ticket_select2$', typeahead.ticket_select2, name='organizer.ticket_select2'),
re_path(r'^organizer/(?P<organizer>[^/]+)/datasync/failedjobs/$', datasync.OrganizerFailedSyncJobsView.as_view(), name='organizer.datasync.failedjobs'),
re_path(r'^organizer/(?P<organizer>[^/]+)/qrcode.(?P<filetype>(png|jpeg|gif|svg))$', organizer.OrganizerQRCode.as_view(), name='organizer.qrcode'),
re_path(r'^nav/typeahead/$', typeahead.nav_context_list, name='nav.typeahead'),
re_path(r'^events/$', main.EventList.as_view(), name='events'),
re_path(r'^events/add$', main.EventWizard.as_view(), name='events.add'),
+41
View File
@@ -40,11 +40,14 @@ from collections import Counter
from datetime import time, timedelta
from decimal import Decimal
from hashlib import sha1
from io import BytesIO
from itertools import groupby
from json import JSONDecodeError
import bleach
import dateutil
import qrcode
import qrcode.image.svg
from django import forms
from django.conf import settings
from django.contrib import messages
@@ -3758,3 +3761,41 @@ def reorder_channels(request, organizer):
)
return HttpResponse()
class OrganizerQRCode(OrganizerPermissionRequiredMixin, View):
permission = None
def get(self, request, *args, filetype, **kwargs):
url = eventreverse_absolute(request.organizer, 'presale:organizer.index')
if "url" in request.GET:
if request.GET["url"].startswith(url):
url = request.GET["url"]
else:
raise PermissionDenied("Untrusted URL")
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 = qr.make_image(image_factory=factory)
r = HttpResponse(img.to_string(), content_type='image/svg+xml')
r['Content-Disposition'] = f'inline; filename="qrcode-{request.organizer.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.organizer.slug}.{filetype}"'
return r
+1 -1
View File
@@ -342,7 +342,7 @@ def nav_context_list(request):
max_to=Max('subevents__date_to'),
max_fromto=Greatest(Max('subevents__date_to'), Max('subevents__date_from'))
).annotate(
order_from=Coalesce('max_from', 'date_from'),
order_from=Coalesce('min_from', 'date_from'),
).order_by('-order_from', 'slug')
if request.user.has_active_staff_session(request.session.session_key):
+10 -9
View File
@@ -138,7 +138,7 @@ logger = logging.getLogger('pretix.plugins.stripe')
# - BLIK: ✗
# - Pay By Bank: ✓
# - TWINT: ✓
# - Wero: ✓
# - Wero: ✓ (No settings UI yet)
# - giropay: ✗ (deprecated)
# - Sofort: ✗ (deprecated)
#
@@ -529,14 +529,15 @@ class StripeSettingsHolder(BasePaymentProvider):
'before they work properly.'),
required=False,
)),
('method_wero',
forms.BooleanField(
label=_('Wero'),
disabled=self.event.currency not in 'EUR',
help_text=_('Some payment methods might need to be enabled in the settings of your Stripe account '
'before they work properly.'),
required=False,
)),
# Disabled for now, since still in closed Beta and only available to dedicated boarded accounts.
# ('method_wero',
# forms.BooleanField(
# label=_('Wero'),
# disabled=self.event.currency not in 'EUR',
# help_text=_('Some payment methods might need to be enabled in the settings of your Stripe account '
# 'before they work properly.'),
# required=False,
# )),
] + extra_fields + list(super().settings_form_fields.items()) + moto_settings
)
if not self.settings.connect_client_id or self.settings.secret_key:
-4
View File
@@ -247,10 +247,6 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
context['has_addon_choices'] = any(cp.has_addon_choices for cp in get_cart(self.request))
templating_context = PlaceholderContext(event_or_subevent=self.subevent or self.request.event, event=self.request.event)
for field in ('presale_has_ended_text',):
context[field] = templating_context.format(str(self.request.event.settings[field]))
if self.subevent:
context['frontpage_text'] = templating_context.format(str(self.subevent.frontpage_text))
else: