mirror of
https://github.com/pretix/pretix.git
synced 2026-05-18 17:24:03 +00:00
WIP
This commit is contained in:
@@ -193,13 +193,15 @@ class BaseTicketOutput:
|
||||
pass
|
||||
|
||||
@property
|
||||
def show_settings(self) -> bool:
|
||||
def is_meta(self) -> bool:
|
||||
"""
|
||||
Returns whether or not this output should be shown in the ticket settings.
|
||||
Returns whether or whether not this output is a "meta" output that only works as a settings holder
|
||||
and should never be used directly. This is a trick to implement outputs with multiple formats but
|
||||
unified settings.
|
||||
|
||||
.. note:: If you set this to false, you need to have some other mechanism to decide whether this output is enabled
|
||||
.. note:: You should set is_enabled to False for meta outputs.
|
||||
"""
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def download_button_text(self) -> str:
|
||||
|
||||
@@ -965,7 +965,7 @@ class TicketSettingsPreview(EventPermissionRequiredMixin, View):
|
||||
responses = register_ticket_outputs.send(self.request.event)
|
||||
for receiver, response in responses:
|
||||
provider = response(self.request.event)
|
||||
if provider.identifier == self.kwargs.get('output'):
|
||||
if provider.identifier == self.kwargs.get('output') and not provider.is_meta:
|
||||
return provider
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
@@ -1068,7 +1068,9 @@ class TicketSettings(EventSettingsViewMixin, EventPermissionRequiredMixin, FormV
|
||||
responses = register_ticket_outputs.send(self.request.event)
|
||||
for receiver, response in responses:
|
||||
provider = response(self.request.event)
|
||||
if not provider.show_settings:
|
||||
provider_settings_fields = provider.settings_form_fields
|
||||
provider_settings_content = provider.settings_content_render(self.request)
|
||||
if not provider_settings_fields and not provider_settings_content:
|
||||
continue
|
||||
|
||||
provider.form = ProviderForm(
|
||||
@@ -1080,17 +1082,17 @@ class TicketSettings(EventSettingsViewMixin, EventPermissionRequiredMixin, FormV
|
||||
provider.form.fields = OrderedDict(
|
||||
[
|
||||
('ticketoutput_%s_%s' % (provider.identifier, k), v)
|
||||
for k, v in provider.settings_form_fields.items()
|
||||
for k, v in provider_settings_fields.items()
|
||||
]
|
||||
)
|
||||
provider.settings_content = provider.settings_content_render(self.request)
|
||||
provider.settings_content = provider_settings_content
|
||||
provider.form.prepare_fields()
|
||||
|
||||
provider.evaluated_preview_allowed = True
|
||||
if not provider.preview_allowed:
|
||||
provider.evaluated_preview_allowed = False
|
||||
else:
|
||||
for k, v in provider.settings_form_fields.items():
|
||||
for k, v in provider_settings_fields.items():
|
||||
if v.required and not self.request.event.settings.get('ticketoutput_%s_%s' % (provider.identifier, k)):
|
||||
provider.evaluated_preview_allowed = False
|
||||
break
|
||||
|
||||
@@ -564,6 +564,8 @@ class OrderDetail(OrderView):
|
||||
responses = register_ticket_outputs.send(self.request.event)
|
||||
for receiver, response in responses:
|
||||
provider = response(self.request.event)
|
||||
if provider.is_meta:
|
||||
continue
|
||||
buttons.append({
|
||||
'text': provider.download_button_text or 'Ticket',
|
||||
'icon': provider.download_button_icon or 'fa-download',
|
||||
|
||||
44
src/pretix/plugins/wallet/migrations/0001_initial.py
Normal file
44
src/pretix/plugins/wallet/migrations/0001_initial.py
Normal file
@@ -0,0 +1,44 @@
|
||||
# Generated by Django 4.2.28 on 2026-03-17 16:29
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import pretix.base.models.base
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("pretixbase", "0297_outgoingmail"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="WalletLayout",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True, primary_key=True, serialize=False
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=190)),
|
||||
("platform", models.CharField(max_length=10)),
|
||||
("style", models.CharField(max_length=255)),
|
||||
("layout", models.TextField()),
|
||||
(
|
||||
"event",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="wallet_layouts",
|
||||
to="pretixbase.event",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"ordering": ("name",),
|
||||
},
|
||||
bases=(models.Model, pretix.base.models.base.LoggingMixin),
|
||||
),
|
||||
]
|
||||
0
src/pretix/plugins/wallet/migrations/__init__.py
Normal file
0
src/pretix/plugins/wallet/migrations/__init__.py
Normal file
47
src/pretix/plugins/wallet/models.py
Normal file
47
src/pretix/plugins/wallet/models.py
Normal file
@@ -0,0 +1,47 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-today pretix GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from pretix.base.models import LoggedModel
|
||||
|
||||
|
||||
class WalletLayout(LoggedModel):
|
||||
event = models.ForeignKey(
|
||||
'pretixbase.Event',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='wallet_layouts'
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=190,
|
||||
verbose_name=_('Name')
|
||||
)
|
||||
platform = models.CharField(max_length=10)
|
||||
style = models.CharField(max_length=255)
|
||||
layout = models.TextField()
|
||||
|
||||
class Meta:
|
||||
ordering = ("name",)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
# TODO:ScopedManager
|
||||
@@ -20,22 +20,16 @@
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
from django.dispatch import receiver
|
||||
from pretix.base.signals import register_ticket_outputs
|
||||
from .ticketoutput import OUTPUTS
|
||||
|
||||
def connect_signals():
|
||||
for output in OUTPUTS:
|
||||
# DIY functools.partial to make get_defining_app happy
|
||||
def get_register_func(o):
|
||||
def register(sender, **kwargs):
|
||||
return o
|
||||
return register
|
||||
register_ticket_outputs.connect(get_register_func(output), dispatch_uid=f"output_{output.identifier}")
|
||||
|
||||
@receiver(register_ticket_outputs, dispatch_uid="output_wallet")
|
||||
def register_ticket_wallet(sender, **kwargs):
|
||||
from .ticketoutput import WalletTicketOutput
|
||||
return WalletTicketOutput
|
||||
|
||||
@receiver(register_ticket_outputs, dispatch_uid="output_wallet_apple")
|
||||
def register_ticket_wallet_apple(sender, **kwargs):
|
||||
from .ticketoutput import AppleWalletTicketOutput
|
||||
return AppleWalletTicketOutput
|
||||
|
||||
|
||||
@receiver(register_ticket_outputs, dispatch_uid="output_wallet_google")
|
||||
def register_ticket_wallet_google(sender, **kwargs):
|
||||
from .ticketoutput import GoogleWalletTicketOutput
|
||||
return GoogleWalletTicketOutput
|
||||
connect_signals()
|
||||
|
||||
156
src/pretix/plugins/wallet/styles.py
Normal file
156
src/pretix/plugins/wallet/styles.py
Normal file
@@ -0,0 +1,156 @@
|
||||
from dataclasses import dataclass, field, asdict
|
||||
from typing import Literal
|
||||
import enum
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.core.exceptions import ValidationError
|
||||
from i18nfield.strings import LazyI18nString
|
||||
|
||||
class PlaceholderFieldType(enum.Enum):
|
||||
TEXT = "text"
|
||||
CODE = "qr"
|
||||
IMAGE = "image"
|
||||
PREDEFINED = "predefined"
|
||||
# TODO: POWERED_BY ?
|
||||
|
||||
|
||||
@dataclass
|
||||
class PlaceholderField:
|
||||
type: PlaceholderFieldType
|
||||
label: LazyI18nString
|
||||
value: LazyI18nString
|
||||
|
||||
def asdict(self):
|
||||
return {'type': self.type.value, 'label': self.label, 'value': self.value}
|
||||
|
||||
@dataclass
|
||||
class FieldGroupDefinition:
|
||||
name: str
|
||||
identifier: str
|
||||
entry_type: PlaceholderFieldType
|
||||
min_entries: int | None = None
|
||||
max_entries: int | None = None
|
||||
|
||||
def asdict(self):
|
||||
return {"identifier": self.identifier, "name": self.name, "min_entries": self.min_entries, "max_entries": self.max_entries}
|
||||
|
||||
|
||||
@dataclass
|
||||
class PlaceholderFieldGroup(FieldGroupDefinition):
|
||||
entry_type: PlaceholderFieldType = PlaceholderFieldType.TEXT
|
||||
default_entries: list[PlaceholderField] = field(default_factory=list)
|
||||
|
||||
def asdict(self):
|
||||
asdict = super().asdict()
|
||||
asdict['default_entries'] = [x.asdict() for x in self.default_entries]
|
||||
return asdict
|
||||
|
||||
|
||||
@dataclass
|
||||
class PredefinedFieldGroup(FieldGroupDefinition):
|
||||
entry_type: PlaceholderFieldType = PlaceholderFieldType.PREDEFINED
|
||||
min_entries = 0
|
||||
max_entries = 1
|
||||
|
||||
|
||||
class PassStyle:
|
||||
identifier: str # unique within platform
|
||||
name: str
|
||||
platform: Literal["apple"] | Literal["google"]
|
||||
fields: list[FieldGroupDefinition]
|
||||
# preview_image: str # TODO: preview
|
||||
|
||||
def asdict(self):
|
||||
return {
|
||||
"identifier": self.identifier,
|
||||
"name": self.name,
|
||||
"platform": self.platform,
|
||||
"fields": [x.asdict() for x in self.fields],
|
||||
}
|
||||
|
||||
|
||||
class AppleWalletEventTicket(PassStyle):
|
||||
identifier = "event_1"
|
||||
name = "Event Ticket Layout 1"
|
||||
platform = "apple"
|
||||
# order here limits in what order users can configure field "overspilling" (if too many fields are defined, where should the rest go) -> can only go down in the list
|
||||
# we evaluate the fields in this order, so they overspill in this order as well (fields from primary are appended to the overspilling field before fields from secondary are etc)
|
||||
fields = [
|
||||
PlaceholderFieldGroup(
|
||||
identifier="logo",
|
||||
name=_("Logo"),
|
||||
min_entries=1,
|
||||
max_entries=1,
|
||||
default_entries=[
|
||||
PlaceholderField(PlaceholderFieldType.IMAGE, "logo", "event:image")
|
||||
],
|
||||
entry_type=PlaceholderFieldType.IMAGE,
|
||||
),
|
||||
PlaceholderFieldGroup(identifier="primary", name=_("Primary"), min_entries=1, max_entries=1),
|
||||
PlaceholderFieldGroup(
|
||||
identifier="secondary", name=_("Secondary"), max_entries=4
|
||||
), # TODO: validation of max field count if combined "Coupons, store cards, and generic passes with a square barcode can have a total of up to four secondary and auxiliary fields, combined."
|
||||
PlaceholderFieldGroup(
|
||||
identifier="headers", name=_("Header"), max_entries=3
|
||||
), # TODO: header image
|
||||
PlaceholderFieldGroup(identifier="auxillary", name=_("Auxillary"), max_entries=4),
|
||||
PlaceholderFieldGroup(identifier="back", name=_("Back")),
|
||||
]
|
||||
# preview_image = "apple/event_ticket.svg"
|
||||
|
||||
|
||||
class GoogleWalletEventTicket(PassStyle):
|
||||
identifier = "event"
|
||||
name = "Event Ticket"
|
||||
platform = "google"
|
||||
fields = [
|
||||
PredefinedFieldGroup(identifier="seating", name=_("Seating")),
|
||||
PlaceholderFieldGroup(identifier="qrcode", name=_("QR-Code"), entry_type=PlaceholderFieldType.CODE),
|
||||
]
|
||||
|
||||
|
||||
AVAILABLE_STYLES = [AppleWalletEventTicket(), GoogleWalletEventTicket()]
|
||||
|
||||
def get_platforms_with_styles():
|
||||
platforms_with_styles = {}
|
||||
for style in AVAILABLE_STYLES:
|
||||
platform = style.platform
|
||||
if platform not in platforms_with_styles:
|
||||
platforms_with_styles[platform] = {}
|
||||
platforms_with_styles[platform][style.identifier] = style
|
||||
return platforms_with_styles
|
||||
|
||||
def get_platform_styles(platform):
|
||||
platform_styles = {}
|
||||
for style in AVAILABLE_STYLES:
|
||||
if style.platform == platform:
|
||||
platform_styles[style.identifier] = style
|
||||
return platform_styles
|
||||
|
||||
def get_platforms():
|
||||
return sorted(set(style.platform for style in AVAILABLE_STYLES))
|
||||
|
||||
class PassLayout:
|
||||
style: PassStyle
|
||||
layout: dict
|
||||
|
||||
def __init__(self, style, layout):
|
||||
self.style = style
|
||||
self.layout = layout
|
||||
|
||||
def validate(self):
|
||||
self.validate_fields()
|
||||
|
||||
def validate_fields(self):
|
||||
style_fields = self.style.fields
|
||||
if 'fields' not in self.layout:
|
||||
raise ValidationError(_("Layout did not contain any fields"))
|
||||
layout_fields = self.layout['fields']
|
||||
if not isinstance(layout_fields, dict):
|
||||
raise ValidationError(_("'fields' must be dict"))
|
||||
|
||||
for fieldgroup in style_fields:
|
||||
layout_field_data = layout_fields.get(fieldgroup.identifier, [])
|
||||
if fieldgroup.min_entries and fieldgroup.min_entries < len(layout_field_data):
|
||||
raise ValidationError(_("At least {min_entries} must be specified for {name}").format(min_entries=fieldgroup.min_entries, name=fieldgroup.name))
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
{% extends "pretixcontrol/event/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load money %}
|
||||
{% block title %}{% trans "Wallet layouts" %}{% endblock %}
|
||||
{% block content %}
|
||||
<pre><code>{{ styles }}</code></pre>
|
||||
<pre><code>{{ variables }}</code></pre>
|
||||
{{ styles|json_script:"styles" }}
|
||||
{{ variables|json_script:"variables" }}
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{form}}
|
||||
<button type="submit" class="btn btn-default">
|
||||
{% trans "Submit " %}
|
||||
</button>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,66 @@
|
||||
{% extends "pretixcontrol/event/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load money %}
|
||||
{% block title %}{% trans "Wallet layouts" %}{% endblock %}
|
||||
{% block content %}
|
||||
<h1>{% trans "Wallet layouts" %}</h1>
|
||||
<div class="tabbed-form">
|
||||
{% for platform in platforms %}
|
||||
<fieldset>
|
||||
<legend>{{platform}}</legend>
|
||||
</fieldset>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% comment %} <div class="table-responsive">
|
||||
<table class="table table-hover table-quotas">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Default" %}</th>
|
||||
<th class="action-col-2"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for l in layouts %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if "can_change_event_settings" in request.eventpermset %}
|
||||
<strong><a href="{% url "plugins:ticketoutputpdf:edit" organizer=request.event.organizer.slug event=request.event.slug layout=l.id %}">
|
||||
{{ l.name }}
|
||||
</a></strong>
|
||||
{% else %}
|
||||
<strong>{{ l.name }}</strong>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if l.default %}
|
||||
<span class="text-success">
|
||||
<span class="fa fa-check"></span>
|
||||
{% trans "Default" %}
|
||||
</span>
|
||||
{% elif "can_change_event_settings" in request.eventpermset %}
|
||||
<form class="form-inline" method="post"
|
||||
action="{% url "plugins:ticketoutputpdf:default" organizer=request.event.organizer.slug event=request.event.slug layout=l.id %}">
|
||||
{% csrf_token %}
|
||||
<button class="btn btn-default btn-sm">
|
||||
{% trans "Make default" %}
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
{% if "can_change_event_settings" in request.eventpermset %}
|
||||
<a href="{% url "plugins:ticketoutputpdf:edit" organizer=request.event.organizer.slug event=request.event.slug layout=l.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "plugins:ticketoutputpdf:add" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ l.id }}"
|
||||
class="btn btn-default btn-sm" title="{% trans "Clone" %}" data-toggle="tooltip"><i class="fa fa-copy"></i></a>
|
||||
<a href="{% url "plugins:ticketoutputpdf:delete" organizer=request.event.organizer.slug event=request.event.slug layout=l.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endcomment %}
|
||||
{% include "pretixcontrol/pagination.html" %}
|
||||
{% endblock %}
|
||||
@@ -19,27 +19,39 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from collections import OrderedDict
|
||||
import logging
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from pretix.base.ticketoutput import BaseTicketOutput
|
||||
from django import forms
|
||||
from pretix.base.models import Event
|
||||
from pretix.base.settings import SettingsSandbox
|
||||
|
||||
|
||||
logger = logging.getLogger('pretix.plugins.wallet')
|
||||
|
||||
|
||||
class WalletTicketOutput(BaseTicketOutput):
|
||||
class WalletSettingsHolder(BaseTicketOutput):
|
||||
identifier = 'wallet'
|
||||
verbose_name = _('Wallet output')
|
||||
verbose_name = _('Wallet Output')
|
||||
|
||||
is_enabeld = False
|
||||
preview_allowed = False
|
||||
is_meta = True
|
||||
is_enabled = False
|
||||
preview_allowed = False # TODO: implement own preview view or hide button for meta-outputs
|
||||
|
||||
class GoogleWalletTicketOutput(BaseTicketOutput):
|
||||
identifier = 'google_wallet'
|
||||
verbose_name = _('google')
|
||||
show_settings = False
|
||||
|
||||
class AppleWalletTicketOutput(BaseTicketOutput):
|
||||
identifier = 'apple_wallet'
|
||||
verbose_name = _('apple')
|
||||
show_settings = False
|
||||
class WalletOutput(BaseTicketOutput):
|
||||
settings_form_fields = []
|
||||
|
||||
def __init__(self, event: Event):
|
||||
super().__init__(event)
|
||||
self.settings = SettingsSandbox('ticketoutput', WalletSettingsHolder.identifier, event)
|
||||
|
||||
class GoogleWalletTicketOutput(WalletOutput):
|
||||
identifier = 'wallet_google'
|
||||
verbose_name = _('Google')
|
||||
download_button_text = "Add to Google Wallet"
|
||||
|
||||
class AppleWalletTicketOutput(WalletOutput):
|
||||
identifier = 'wallet_apple'
|
||||
verbose_name = _('Apple')
|
||||
download_button_text = "Add to Apple Wallet"
|
||||
|
||||
OUTPUTS = [WalletSettingsHolder, GoogleWalletTicketOutput, AppleWalletTicketOutput]
|
||||
|
||||
36
src/pretix/plugins/wallet/urls.py
Normal file
36
src/pretix/plugins/wallet/urls.py
Normal file
@@ -0,0 +1,36 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-today pretix GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from django.urls import re_path
|
||||
|
||||
from .views import (
|
||||
EditorView,
|
||||
LayoutListView
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/wallet/$',
|
||||
LayoutListView.as_view(), name='index'),
|
||||
re_path(r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/wallet/edit/(?P<platform>[^/]+)/$',
|
||||
EditorView.as_view(), name='edit'),
|
||||
re_path(r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/wallet/edit/(?P<platform>[^/]+)/(?P<layout>[^/]+)/$',
|
||||
EditorView.as_view(), name='edit'),
|
||||
]
|
||||
118
src/pretix/plugins/wallet/views.py
Normal file
118
src/pretix/plugins/wallet/views.py
Normal file
@@ -0,0 +1,118 @@
|
||||
from typing import Any
|
||||
|
||||
from django.http import Http404
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import reverse
|
||||
from django.views.generic import FormView, ListView, TemplateView
|
||||
from pretix.base.pdf import get_variables
|
||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||
from .styles import PassLayout, get_platform_styles, get_platforms
|
||||
from .models import WalletLayout
|
||||
import json
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django import forms
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
|
||||
class LayoutListView(EventPermissionRequiredMixin, ListView):
|
||||
model = WalletLayout
|
||||
permission = "can_change_event_settings"
|
||||
template_name = "pretixplugins/wallet/layout_list.html"
|
||||
context_object_name = "layouts"
|
||||
|
||||
def get_queryset(self):
|
||||
return self.request.event.wallet_layouts
|
||||
|
||||
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx["platforms"] = get_platforms()
|
||||
return ctx
|
||||
|
||||
|
||||
class EditorForm(forms.Form):
|
||||
name = forms.CharField()
|
||||
style = forms.TypedChoiceField()
|
||||
layout = forms.JSONField(initial={})
|
||||
|
||||
def __init__(self, platform, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.platform = platform
|
||||
self.platform_styles = get_platform_styles(platform)
|
||||
self.fields["style"].choices = [
|
||||
(id, style.name) for id, style in self.platform_styles.items()
|
||||
]
|
||||
self.fields["style"].coerce = self.coerce_style
|
||||
|
||||
def coerce_style(self, value):
|
||||
return self.platform_styles[value]
|
||||
|
||||
def clean_layout(self):
|
||||
layout = self.cleaned_data["layout"]
|
||||
|
||||
if not isinstance(layout, dict):
|
||||
raise ValidationError(_("Layout must be a dict"))
|
||||
return layout
|
||||
|
||||
def clean(self):
|
||||
if "style" in self.cleaned_data and "layout" in self.cleaned_data:
|
||||
layout = PassLayout(
|
||||
style=self.cleaned_data["style"], layout=self.cleaned_data["layout"]
|
||||
)
|
||||
layout.validate()
|
||||
return self.cleaned_data
|
||||
|
||||
|
||||
class EditorView(EventPermissionRequiredMixin, FormView):
|
||||
template_name = "pretixplugins/wallet/edit.html"
|
||||
form_class = EditorForm
|
||||
success_url = ""
|
||||
permission = "can_change_event_settings"
|
||||
|
||||
@property
|
||||
def platform(self):
|
||||
return self.kwargs["platform"]
|
||||
|
||||
def get_form_kwargs(self) -> dict[str, Any]:
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs["platform"] = self.platform
|
||||
return kwargs
|
||||
|
||||
def get_platform_styles(self):
|
||||
if self.platform not in get_platforms():
|
||||
raise Http404(
|
||||
_("Unknown platform '{platform}'").format(platform=self.platform)
|
||||
)
|
||||
return get_platform_styles(self.platform)
|
||||
|
||||
def get_context_data(self, **kwargs) -> dict[str, Any]:
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["styles"] = {
|
||||
id: style.asdict() for id, style in self.get_platform_styles().items()
|
||||
}
|
||||
context["variables"] = {
|
||||
"text": {
|
||||
varname: {"label": var["label"], "editor_sample": var["editor_sample"]}
|
||||
for varname, var in get_variables(self.request.event).items()
|
||||
}
|
||||
}
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
self.object = WalletLayout.objects.create(
|
||||
event=self.request.event,
|
||||
name=form.cleaned_data["name"],
|
||||
platform=self.platform,
|
||||
style=form.cleaned_data["style"],
|
||||
layout=form.cleaned_data["layout"],
|
||||
)
|
||||
return redirect(
|
||||
reverse(
|
||||
"plugins:wallet:edit",
|
||||
kwargs={
|
||||
"organizer": self.request.event.organizer.slug,
|
||||
"event": self.request.event.slug,
|
||||
"platform": self.platform,
|
||||
"layout": self.object.pk,
|
||||
},
|
||||
)
|
||||
)
|
||||
Reference in New Issue
Block a user