mirror of
https://github.com/pretix/pretix.git
synced 2026-05-19 17:34:03 +00:00
WalletLayout now contain layouts for all platforms
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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] }}
|
||||||
|
|||||||
@@ -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 = {
|
||||||
|
|||||||
@@ -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"]
|
||||||
|
|||||||
@@ -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')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -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'),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user