WalletLayout now contain layouts for all platforms

This commit is contained in:
Kara Engelhardt
2026-05-19 18:04:20 +02:00
parent f6f4c1c56c
commit 97167f75c9
12 changed files with 259 additions and 185 deletions

View File

@@ -1,35 +1,21 @@
from rest_framework import viewsets from rest_framework import viewsets
from django.db import transaction from django.db import transaction
from .styles import PassLayout, AVAILABLE_STYLES_DICT from .styles import PassLayout, AVAILABLE_STYLES_DICT, AVAILABLE_PLATFORMS
from .models import WalletLayout from .models import WalletLayout, WalletPlatformLayout
from pretix.api.serializers.i18n import I18nAwareModelSerializer from pretix.api.serializers.i18n import I18nAwareModelSerializer
import django_filters.rest_framework
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from .views import get_layout_variables from .views import get_layout_variables
from rest_framework import serializers
class WalletLayoutSerializer(I18nAwareModelSerializer): class WalletPlatformLayoutSerializer(I18nAwareModelSerializer):
platform = serializers.ChoiceField(choices=[p.identifier for p in AVAILABLE_PLATFORMS])
style = serializers.CharField(allow_null=True, required=False)
class Meta: class Meta:
model = WalletLayout model = WalletPlatformLayout
fields = ("id", "platform", "name", "style", "layout") fields = ("platform", "style", "layout")
read_only_fields = ("id",)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance:
self.fields['platform'].read_only = True
def save(self, *args, **kwargs):
super().save(*args, **kwargs, event=self.context["event"])
def validate_platform(self, value):
if self.instance and value != self.instance.platform:
raise ValidationError(_("Platform cannot be changed"))
if value not in AVAILABLE_STYLES_DICT:
raise ValidationError(_("Invalid platform"))
return value
def validate_layout(self, value): def validate_layout(self, value):
if not isinstance(value, dict): if not isinstance(value, dict):
@@ -37,11 +23,10 @@ class WalletLayoutSerializer(I18nAwareModelSerializer):
return value return value
def validate(self, data): def validate(self, data):
if self.instance: platform = data.get('platform')
platform = self.instance.platform style = data.get('style')
else: layout = data.get('layout')
platform = data.get('platform', None) if platform and style and layout:
if "style" in data and "layout" in data and platform:
platform_styles = AVAILABLE_STYLES_DICT[platform] platform_styles = AVAILABLE_STYLES_DICT[platform]
if data["style"] not in platform_styles: if data["style"] not in platform_styles:
@@ -54,20 +39,38 @@ class WalletLayoutSerializer(I18nAwareModelSerializer):
return data return data
class WalletLayoutSerializer(I18nAwareModelSerializer):
platform_layouts = WalletPlatformLayoutSerializer(many=True)
class Meta:
model = WalletLayout
fields = ("id", "name", "platform_layouts")
read_only_fields = ("id",)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def save(self, *args, **kwargs):
super().save(*args, **kwargs, event=self.context["event"])
def update(self, instance, validated_data):
platform_layouts = validated_data.pop('platform_layouts')
for layout in platform_layouts:
if layout['style']:
instance.platform_layouts.update_or_create(platform=layout['platform'], defaults=layout)
instance.platform_layouts.exclude(platform__in={layout['platform'] for layout in platform_layouts if layout['style'] is not None}).delete()
return super().update(instance, validated_data)
class WalletLayoutViewSet(viewsets.ModelViewSet): class WalletLayoutViewSet(viewsets.ModelViewSet):
model = WalletLayout model = WalletLayout
queryset = WalletLayout.objects.none() queryset = WalletLayout.objects.none()
serializer_class = WalletLayoutSerializer serializer_class = WalletLayoutSerializer
filter_backends = (django_filters.rest_framework.DjangoFilterBackend,)
filterset_fields = ["platform"]
permission = "event.settings.general:write" permission = "event.settings.general:write"
def get_queryset(self): def get_queryset(self):
return self.request.event.wallet_layouts.all() return self.request.event.wallet_layouts.all()
def get_serializer(self, *args, **kwargs):
return super().get_serializer(*args, **kwargs)
def get_serializer_context(self): def get_serializer_context(self):
ctx = super().get_serializer_context() ctx = super().get_serializer_context()
ctx["event"] = self.request.event ctx["event"] = self.request.event

View File

@@ -1,8 +1,8 @@
# Generated by Django 4.2.28 on 2026-03-17 16:29 # Generated by Django 5.2.13 on 2026-05-19 15:39
from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import pretix.base.models.base import pretix.base.models.base
from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
@@ -10,7 +10,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
("pretixbase", "0297_outgoingmail"), ("pretixbase", "0300_alter_customer_locale_alter_user_locale"),
] ]
operations = [ operations = [
@@ -24,9 +24,6 @@ class Migration(migrations.Migration):
), ),
), ),
("name", models.CharField(max_length=190)), ("name", models.CharField(max_length=190)),
("platform", models.CharField(max_length=10)),
("style", models.CharField(max_length=255)),
("layout", models.TextField()),
( (
"event", "event",
models.ForeignKey( models.ForeignKey(
@@ -37,7 +34,64 @@ class Migration(migrations.Migration):
), ),
], ],
options={ options={
"ordering": ("name",), "abstract": False,
},
bases=(models.Model, pretix.base.models.base.LoggingMixin),
),
migrations.CreateModel(
name="WalletLayoutItem",
fields=[
(
"id",
models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
(
"item",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="walletlayout_assignments",
to="pretixbase.item",
),
),
(
"layout",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="item_assignments",
to="wallet.walletlayout",
),
),
],
options={
"unique_together": {("item", "layout")},
},
),
migrations.CreateModel(
name="WalletPlatformLayout",
fields=[
(
"id",
models.BigAutoField(
auto_created=True, primary_key=True, serialize=False
),
),
("platform", models.CharField(max_length=10)),
("style", models.CharField(max_length=255)),
("layout", models.JSONField(default=dict)),
(
"parent",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="platform_layouts",
to="wallet.walletlayout",
),
),
],
options={
"unique_together": {("parent", "platform")},
}, },
bases=(models.Model, pretix.base.models.base.LoggingMixin), bases=(models.Model, pretix.base.models.base.LoggingMixin),
), ),

View File

@@ -24,6 +24,7 @@ from django.utils.translation import gettext_lazy as _
from pretix.base.models import LoggedModel from pretix.base.models import LoggedModel
from django_scopes import ScopedManager from django_scopes import ScopedManager
from django.core.exceptions import ValidationError
class WalletLayout(LoggedModel): class WalletLayout(LoggedModel):
@@ -36,28 +37,31 @@ class WalletLayout(LoggedModel):
max_length=190, max_length=190,
verbose_name=_('Name') verbose_name=_('Name')
) )
objects = ScopedManager(organizer='event__organizer')
class WalletPlatformLayout(LoggedModel):
parent = models.ForeignKey(WalletLayout, on_delete=models.CASCADE, related_name="platform_layouts")
platform = models.CharField(max_length=10) platform = models.CharField(max_length=10)
style = models.CharField(max_length=255) style = models.CharField(max_length=255)
layout = models.JSONField(default=dict) layout = models.JSONField(default=dict)
objects = ScopedManager(organizer='event__organizer') objects = ScopedManager(organizer='parent__event__organizer')
class Meta: class Meta:
ordering = ("name",) unique_together = (('parent', 'platform'),)
def __str__(self):
return self.name
class WalletLayoutItem(models.Model): class WalletLayoutItem(models.Model):
item = models.ForeignKey('pretixbase.Item', null=True, blank=True, related_name='walletlayout_assignments', item = models.ForeignKey('pretixbase.Item', null=True, blank=True, related_name='walletlayout_assignments',
on_delete=models.CASCADE) on_delete=models.CASCADE)
layout = models.ForeignKey(WalletLayout, on_delete=models.CASCADE, related_name='item_assignments') layout = models.ForeignKey(WalletLayout, on_delete=models.CASCADE, related_name='item_assignments')
sales_channel = models.ForeignKey(
"pretixbase.SalesChannel",
on_delete=models.CASCADE,
)
class Meta: class Meta:
unique_together = (('item', 'layout', 'sales_channel'),) unique_together = (('item', 'layout'),)
ordering = ("id",)
def clean(self):
if self.item.event != self.layout.event:
raise ValidationError("cannot bind layout to item of different event")

View File

@@ -9,8 +9,8 @@ const gettext = (window as any).gettext;
const isLoading = ref<boolean>(true); const isLoading = ref<boolean>(true);
const wallet_layout = ref<Layout | null>(null); const wallet_layout = ref<Layout | null>(null);
const STYLES: Styles = JSON.parse( const PLATFORMS: Platforms = JSON.parse(
document.querySelector("#styles")?.textContent ?? "{}", document.querySelector("#platforms")?.textContent ?? "{}",
); );
const VARIABLES: VariableConfig = JSON.parse( const VARIABLES: VariableConfig = JSON.parse(
document.querySelector("#variables")?.textContent ?? "{}", document.querySelector("#variables")?.textContent ?? "{}",
@@ -55,11 +55,36 @@ function saveLayout(e: SubmitEvent) {
}, },
) )
.then((x) => x.json()) .then((x) => x.json())
.catch((x) => alert(x))
.then((x) => { .then((x) => {
wallet_layout.value = x; wallet_layout.value = x;
isLoading.value = false; isLoading.value = false;
}); });
} }
const currentPlatform = ref(PLATFORMS[0].identifier);
const currentLayout = computed(() => ({}));
const platformStyles = computed(() => {
for (const platform of PLATFORMS) {
if (platform.identifier === currentPlatform.value) {
return platform.styles
}
}
});
const platformLayout = computed(() => {
for (const layout of wallet_layout.value.platform_layouts) {
if (layout.platform === currentPlatform.value) {
return layout
}
}
const newLayout = {platform: currentPlatform, style: null, layout: {}};
wallet_layout.value.platform_layouts.push(newLayout);
return newLayout
});
const platformChoices = computed(() => {
return [[null, "Do not generate pass"], ...Object.values(platformStyles.value).map(x => [x.identifier, x.name])]
});
</script> </script>
<template lang="pug"> <template lang="pug">
@@ -68,24 +93,29 @@ function saveLayout(e: SubmitEvent) {
// TODO: proper spinner // TODO: proper spinner
template(v-if="isLoading") {{ gettext("Loading...") }} template(v-if="isLoading") {{ gettext("Loading...") }}
form(v-else @submit="saveLayout") form(v-else @submit="saveLayout")
.row .form-group
.col-md-8 Input(label="Name" v-model="wallet_layout.name")
.form-group() nav
Input(label="Name" v-model="wallet_layout.name") ul.nav.nav-tabs
li(v-for="platform in PLATFORMS" :class="{'active': currentPlatform === platform.identifier}")
a(role="tab" @click="currentPlatform = platform.identifier") {{ platform.name }}
.tabbed-form.tab-content
.tab-pane.active.row
.col-md-8
Select.form-group(label="Style" v-model="platformLayout.style" :choices="platformChoices")
.form-group() StyleSettings(v-if="platformLayout.style" v-model="platformLayout.layout" :style="platformStyles[platformLayout.style]" :variables="VARIABLES" :locales="LOCALES")
Select(label="Style" v-model="wallet_layout.style" :choices="Object.values(STYLES).map(x => [x.identifier, x.name])") .col-md-4
.panel.panel-default
StyleSettings(v-if="wallet_layout.style" v-model="wallet_layout.layout" :style="STYLES[wallet_layout.style]" :variables="VARIABLES" :locales="LOCALES") .panel-heading Preview
.col-md-4 .panel-body
.panel.panel-default // TODO: Preview
.panel-heading Preview pre
.panel-body code {{ platformLayout }}
// TODO: Preview pre(v-if="wallet_layout.style")
pre code {{ platformStyles[wallet_layout.style] }}
code {{ wallet_layout }} pre
pre(v-if="wallet_layout.style") code {{ wallet_layout }}
code {{ STYLES[wallet_layout.style] }}
.form-group.submit-group .form-group.submit-group
button.btn.btn-primary.btn-save(type="submit") Submit button.btn.btn-primary.btn-save(type="submit") Submit
</template> </template>

View File

@@ -8,7 +8,8 @@ defineOptions({
const props = defineProps<{ const props = defineProps<{
label?: string label?: string
choices: Array<[string, string]> choices: Array<[string, string]>
errors?: string[] errors?: string[],
class: string
}>() }>()
const modelValue = defineModel<string|null>(); const modelValue = defineModel<string|null>();
const id = useId() const id = useId()
@@ -23,7 +24,7 @@ watchEffect(() => {
</script> </script>
<template lang="pug"> <template lang="pug">
template(v-if="choices.length >= 1") template(v-if="choices.length >= 1" :class="props.class")
label.control-label(v-if="props.label" :for="id") {{ props.label }} label.control-label(v-if="props.label" :for="id") {{ props.label }}
select.form-control(:id="id" v-model="modelValue" v-bind="$attrs" required) select.form-control(:id="id" v-model="modelValue" v-bind="$attrs" required)
option(v-for="choice in props.choices" :key="choice[0]" :value="choice[0]") {{ choice[1] }} option(v-for="choice in props.choices" :key="choice[0]" :value="choice[0]") {{ choice[1] }}

View File

@@ -48,10 +48,16 @@ type Variable = {
label: string label: string
}; };
type Platform = {
identifier: string;
name: string;
styles: Styles;
};
type Styles = Record<string, Style>; type Styles = Record<string, Style>;
type Variables = Record<string, Variable>; type Variables = Record<string, Variable>;
type VariableConfig = Record<string, Variables>; type VariableConfig = Record<string, Variables>;
type Platforms = Record<string, Platform>;
type PlaceholderFieldGroupConfig = { type PlaceholderFieldGroupConfig = {

View File

@@ -2,7 +2,8 @@ from .apple import ApplePlatform, AppleWalletEventTicket
from .google import GooglePlatform, GoogleWalletEventTicket from .google import GooglePlatform, GoogleWalletEventTicket
from .base import PassLayout from .base import PassLayout
AVAILABLE_PLATFORMS = {"apple": ApplePlatform, "google": GooglePlatform} AVAILABLE_PLATFORMS = [ApplePlatform, GooglePlatform]
AVAILABLE_STYLES = { AVAILABLE_STYLES = {
"apple": [AppleWalletEventTicket()], "apple": [AppleWalletEventTicket()],
"google": [ "google": [
@@ -14,4 +15,4 @@ AVAILABLE_STYLES_DICT = {
plat: {s.identifier: s for s in styls} for plat, styls in AVAILABLE_STYLES.items() plat: {s.identifier: s for s in styls} for plat, styls in AVAILABLE_STYLES.items()
} }
__all__ = ["AVAILABLE_PLATFORMS", "AVAILABLE_STYLES", "PassLayout"] __all__ = ["AVAILABLE_PLATFORMS", "AVAILABLE_STYLES", "PassLayout"]

View File

@@ -19,7 +19,6 @@ import json
from django.contrib.staticfiles import finders from django.contrib.staticfiles import finders
class ApplePlatform(WalletPlatform): class ApplePlatform(WalletPlatform):
identifier = "apple" identifier = "apple"
name = _("Apple") name = _("Apple")
@@ -236,13 +235,13 @@ class AppleWalletEventTicket(AppleWalletStyle):
raise ValueError("Unknown field group") raise ValueError("Unknown field group")
return fields return fields
def convert_fields(self, strings, fields): def convert_fields(self, strings, fields, prefix):
converted = [] converted = []
for i,f in enumerate(fields): for i,f in enumerate(fields):
converted_field = {**f, "key": f"primary-{i}"} converted_field = {**f, "key": f"{prefix}-{i}"}
if "label" in converted_field and isinstance(converted_field['label'], LazyI18nString): if "label" in converted_field and isinstance(converted_field['label'], LazyI18nString):
strings.add_entry(f"primary-{i}-label", converted_field['label']) strings.add_entry(f"{prefix}-{i}-label", converted_field['label'])
converted_field['label'] = f"primary-{i}-label" converted_field['label'] = f"{prefix}-{i}-label"
converted.append(converted_field) converted.append(converted_field)
return converted return converted
@@ -251,6 +250,6 @@ class AppleWalletEventTicket(AppleWalletStyle):
fields = self.get_pass_fields(layout, context) fields = self.get_pass_fields(layout, context)
return { return {
"eventTicket": { "eventTicket": {
"primaryFields": self.convert_fields(strings, fields['primary']) "primaryFields": self.convert_fields(strings, fields['primary'], 'primary')
} }
} }

View File

@@ -11,7 +11,7 @@
{% block content %} {% block content %}
<h1>{% trans "Edit layout" %} {{ object.name }} </h1> <h1>{% trans "Edit layout" %} {{ object.name }} </h1>
{{ styles|json_script:"styles" }} {{ platforms|json_script:"platforms" }}
{{ variables|json_script:"variables" }} {{ variables|json_script:"variables" }}
{{ locales|json_script:"locales" }} {{ locales|json_script:"locales" }}
<div id="editor" data-layout-id="{{ object.pk }}"></div> <div id="editor" data-layout-id="{{ object.pk }}"></div>

View File

@@ -5,79 +5,70 @@
{% block title %}{% trans "Wallet layouts" %}{% endblock %} {% block title %}{% trans "Wallet layouts" %}{% endblock %}
{% block content %} {% block content %}
<h1>{% trans "Wallet layouts" %}</h1> <h1>{% trans "Wallet layouts" %}</h1>
<div class="tabbed-form"> {% if layouts|length == 0 %}
{% for platform in platforms.values %} <div class="empty-collection">
<fieldset> <p>
<legend>{{platform.name}}</legend> {% blocktrans trimmed %}
{% with platform_layouts=platform|platform_layouts:request.event %} You haven't created any layouts yet.
{% if platform_layouts|length == 0 %} {% endblocktrans %}
<div class="empty-collection"> </p>
<p>
{% blocktrans trimmed %}
You haven't created any layouts yet.
{% endblocktrans %}
</p>
{% if "event.settings.general:write" in request.eventpermset %} {% if "event.settings.general:write" in request.eventpermset %}
<a href="{% url "plugins:wallet:add" organizer=request.event.organizer.slug event=request.event.slug platform=platform.identifier %}" <a href="{% url "plugins:wallet:add" organizer=request.event.organizer.slug event=request.event.slug %}"
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new layout" %} class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new layout" %}
</a> </a>
{% endif %} {% endif %}
</div> </div>
{% else %} {% else %}
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-hover table-quotas"> <table class="table table-hover table-quotas">
<thead> <thead>
<tr> <tr>
<th>{% trans "Name" %}</th> <th>{% trans "Name" %}</th>
<th>{% trans "Default" %}</th> <th>{% trans "Default" %}</th>
<th class="action-col-2"></th> <th class="action-col-2"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for l in platform_layouts %} {% for l in layouts %}
<tr> <tr>
<td> <td>
{% if "can_change_event_settings" in request.eventpermset %} {% if "can_change_event_settings" in request.eventpermset %}
<strong><a href="{% url "plugins:wallet:edit" organizer=request.event.organizer.slug event=request.event.slug layout=l.id %}"> <strong><a href="{% url "plugins:wallet:edit" organizer=request.event.organizer.slug event=request.event.slug layout=l.id %}">
{{ l.name }} {{ l.name }}
</a></strong> </a></strong>
{% else %} {% else %}
<strong>{{ l.name }}</strong> <strong>{{ l.name }}</strong>
{% endif %} {% endif %}
</td> </td>
<td> <td>
{% if l.default %} {% if l.default %}
<span class="text-success"> <span class="text-success">
<span class="fa fa-check"></span> <span class="fa fa-check"></span>
{% trans "Default" %} {% trans "Default" %}
</span> </span>
{% elif "can_change_event_settings" in request.eventpermset %} {% elif "can_change_event_settings" in request.eventpermset %}
<form class="form-inline" method="post" <form class="form-inline" method="post"
action="{% url "plugins:wallet:default" organizer=request.event.organizer.slug event=request.event.slug layout=l.id %}"> action="{% url "plugins:wallet:default" organizer=request.event.organizer.slug event=request.event.slug layout=l.id %}">
{% csrf_token %} {% csrf_token %}
<button class="btn btn-default btn-sm"> <button class="btn btn-default btn-sm">
{% trans "Make default" %} {% trans "Make default" %}
</button> </button>
</form> </form>
{% endif %} {% endif %}
</td> </td>
<td class="text-right flip"> <td class="text-right flip">
{% if "can_change_event_settings" in request.eventpermset %} {% if "can_change_event_settings" in request.eventpermset %}
<a href="{% url "plugins:wallet: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:wallet: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:wallet:add" organizer=request.event.organizer.slug event=request.event.slug platform=platform.identifier %}?copy_from={{ l.id }}" <a href="{% url "plugins:wallet: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> class="btn btn-default btn-sm" title="{% trans "Clone" %}" data-toggle="tooltip"><i class="fa fa-copy"></i></a>
<a href="{% url "plugins:wallet: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> <a href="{% url "plugins:wallet: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 %} {% endif %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
</div> </div>
{% endif %} {% endif %}
{% endwith %}
</fieldset>
{% endfor %}
</div>
{% endblock %} {% endblock %}

View File

@@ -32,7 +32,7 @@ from .api import WalletLayoutViewSet
urlpatterns = [ urlpatterns = [
re_path(r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/wallet/$', re_path(r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/wallet/$',
LayoutListView.as_view(), name='index'), LayoutListView.as_view(), name='index'),
re_path(r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/wallet/add/(?P<platform>[^/]+)/$', re_path(r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/wallet/add/$',
LayoutCreateView.as_view(), name='add'), LayoutCreateView.as_view(), name='add'),
re_path(r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/wallet/edit/(?P<layout>[^/]+)/$', re_path(r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/wallet/edit/(?P<layout>[^/]+)/$',
LayoutEditorView.as_view(), name='edit'), LayoutEditorView.as_view(), name='edit'),

View File

@@ -31,7 +31,6 @@ def get_editor_variables(event):
} }
# TODO: should this even be a list view?
class LayoutListView(EventPermissionRequiredMixin, ListView): class LayoutListView(EventPermissionRequiredMixin, ListView):
model = WalletLayout model = WalletLayout
permission = "can_change_event_settings" permission = "can_change_event_settings"
@@ -39,12 +38,7 @@ class LayoutListView(EventPermissionRequiredMixin, ListView):
context_object_name = "layouts" context_object_name = "layouts"
def get_queryset(self): def get_queryset(self):
return self.request.event.wallet_layouts return self.request.event.wallet_layouts.all()
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
ctx = super().get_context_data(**kwargs)
ctx["platforms"] = AVAILABLE_PLATFORMS
return ctx
class LayoutEditorView(DetailView): class LayoutEditorView(DetailView):
@@ -53,18 +47,19 @@ class LayoutEditorView(DetailView):
permission = "event.settings.general:write" permission = "event.settings.general:write"
pk_url_kwarg = "layout" pk_url_kwarg = "layout"
def get_platform_styles(self):
if self.object.platform not in AVAILABLE_STYLES:
raise Http404(
_("Unknown platform '{platform}'").format(platform=self.object.platform)
)
return AVAILABLE_STYLES[self.object.platform]
def get_context_data(self, **kwargs) -> dict[str, Any]: def get_context_data(self, **kwargs) -> dict[str, Any]:
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context["styles"] = { context['platforms'] = [{
style.identifier: style.asdict() for style in self.get_platform_styles() "identifier": platform.identifier,
} "name": platform.name,
"styles": {
style.identifier: style.asdict() for style in AVAILABLE_STYLES.get(platform.identifier)
}
} for platform in AVAILABLE_PLATFORMS
]
# context["styles"] = {
# style.identifier: style.asdict() for style in self.get_platform_styles()
# }
context["variables"] = get_editor_variables(self.request.event) context["variables"] = get_editor_variables(self.request.event)
context["locales"] = { context["locales"] = {
l: dict(settings.LANGUAGES).get(l, l) l: dict(settings.LANGUAGES).get(l, l)
@@ -79,13 +74,11 @@ class WalletLayoutCreateForm(forms.ModelForm):
model = WalletLayout model = WalletLayout
fields = ("name",) fields = ("name",)
def __init__(self, *args, platform, event, **kwargs): def __init__(self, *args, event, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.platform = platform
self.event = event self.event = event
def save(self, *args, **kwargs) -> Any: def save(self, *args, **kwargs) -> Any:
self.instance.platform = self.platform
self.instance.event = self.event self.instance.event = self.event
return super().save(*args, **kwargs) return super().save(*args, **kwargs)
@@ -95,16 +88,8 @@ class LayoutCreateView(CreateView):
form_class = WalletLayoutCreateForm form_class = WalletLayoutCreateForm
permission = "event.settings.general:write" permission = "event.settings.general:write"
@property
def platform(self):
platform = self.kwargs["platform"]
if platform not in AVAILABLE_PLATFORMS:
raise Http404(_("Unknown platform '{platform}'").format(platform=platform))
return platform
def get_form_kwargs(self) -> dict[str, Any]: def get_form_kwargs(self) -> dict[str, Any]:
kwargs = super().get_form_kwargs() kwargs = super().get_form_kwargs()
kwargs["platform"] = self.platform
kwargs["event"] = self.request.event kwargs["event"] = self.request.event
return kwargs return kwargs