mirror of
https://github.com/pretix/pretix.git
synced 2026-06-11 01:25:13 +00:00
WIP: first successful pass creation
This commit is contained in:
@@ -21,7 +21,7 @@ const selection = computed({
|
||||
get() {
|
||||
if (entry.value.type === 'placeholder') {
|
||||
return entry.value.content
|
||||
} else if (entry.value.type === 'text') {
|
||||
} else if (entry.value.type === 'custom') {
|
||||
return "other"
|
||||
} else {
|
||||
throw new Error(`Unknown entry type "${entry.value.type}"`);
|
||||
@@ -29,7 +29,7 @@ const selection = computed({
|
||||
},
|
||||
set(newValue) {
|
||||
if (newValue == "other") {
|
||||
entry.value.type = "text"
|
||||
entry.value.type = "custom"
|
||||
entry.value.content = {};
|
||||
} else {
|
||||
entry.value.type = "placeholder"
|
||||
@@ -42,7 +42,7 @@ const textContent = computed({
|
||||
get() {
|
||||
if (entry.value.type === 'placeholder') {
|
||||
return ""
|
||||
} else if (entry.value.type === 'text') {
|
||||
} else if (entry.value.type === 'custom') {
|
||||
return entry.value.content
|
||||
} else {
|
||||
throw new Error(`Unknown entry type "${entry.value.type}"`);
|
||||
|
||||
@@ -111,10 +111,10 @@ class SignedZipFile:
|
||||
class AppleWalletStyle(PassStyle):
|
||||
platform = ApplePlatform
|
||||
|
||||
def pass_content(self, layout, context, strings):
|
||||
def pass_content(self, fields, strings):
|
||||
raise NotImplementedError()
|
||||
|
||||
def generate_pass_json(self, layout, context, strings):
|
||||
def generate_pass_json(self, fields, context, strings):
|
||||
def add_from_context(key):
|
||||
value = context.get(key)
|
||||
if not value:
|
||||
@@ -128,7 +128,7 @@ class AppleWalletStyle(PassStyle):
|
||||
"passTypeIdentifier": add_from_context("passTypeIdentifier"),
|
||||
"teamIdentifier": add_from_context("teamIdentifier"),
|
||||
"serialNumber": add_from_context("serialNumber"),
|
||||
**self.pass_content(layout, context, strings),
|
||||
**self.pass_content(fields, strings),
|
||||
}
|
||||
return pass_json
|
||||
|
||||
@@ -136,6 +136,9 @@ class AppleWalletStyle(PassStyle):
|
||||
for key in ["ca_certificate", "certificate", "key", "password", "locales"]:
|
||||
if key not in context:
|
||||
raise ValueError(f"{key} missing from context")
|
||||
|
||||
fields = self.get_pass_fields(layout, context)
|
||||
|
||||
pkpass = SignedZipFile(
|
||||
context["ca_certificate"],
|
||||
context["certificate"],
|
||||
@@ -144,12 +147,23 @@ class AppleWalletStyle(PassStyle):
|
||||
)
|
||||
strings = StringResource(locales=context['locales'])
|
||||
|
||||
pass_json = self.generate_pass_json(layout, context, strings)
|
||||
pass_json = self.generate_pass_json(fields, context, strings)
|
||||
print(pass_json)
|
||||
pkpass.add_file(
|
||||
"icon.png", open(finders.find("pretix_passbook/icon.png"), "rb").read()
|
||||
)
|
||||
if fields['logo']:
|
||||
logo = fields['logo'][0]['value']
|
||||
else:
|
||||
logo = open(finders.find("pretix_passbook/logo.png"), "rb")
|
||||
|
||||
if fields['icon']:
|
||||
icon = fields['icon'][0]['value']
|
||||
else:
|
||||
icon = open(finders.find("pretix_passbook/icon.png"), "rb")
|
||||
|
||||
pkpass.add_file("icon.png", icon.read())
|
||||
pkpass.add_file("logo.png", logo.read())
|
||||
|
||||
for lang, content in strings.generate().items():
|
||||
pkpass.add_file(f"{lang}.lproj/pass.strings", content)
|
||||
pkpass.add_file("pass.json", json.dumps(pass_json))
|
||||
return pkpass.finish()
|
||||
|
||||
@@ -158,6 +172,18 @@ class AppleWalletEventTicket(AppleWalletStyle):
|
||||
identifier = "event_1"
|
||||
name = _("Event Ticket Layout 1")
|
||||
fieldgroups = [
|
||||
ImageFieldGroup(
|
||||
identifier="icon",
|
||||
name=_("Icon"),
|
||||
min_entries=0,
|
||||
max_entries=1,
|
||||
labels=False,
|
||||
default_entries=[
|
||||
PlaceholderFieldEntry(
|
||||
content="poweredby",
|
||||
)
|
||||
],
|
||||
),
|
||||
ImageFieldGroup(
|
||||
identifier="logo",
|
||||
name=_("Logo"),
|
||||
@@ -194,47 +220,6 @@ class AppleWalletEventTicket(AppleWalletStyle):
|
||||
]
|
||||
# preview_image = "apple/event_ticket.svg"
|
||||
|
||||
def get_pass_fields(self, layout, context):
|
||||
fields = {}
|
||||
for group in self.fieldgroups:
|
||||
if isinstance(group, PredefinedFieldGroup):
|
||||
pass
|
||||
elif isinstance(group, PlaceholderFieldGroup):
|
||||
group_fields = []
|
||||
if group.identifier in layout["fieldgroups"]:
|
||||
for field in layout["fieldgroups"][group.identifier]["entries"]:
|
||||
field_entry = {}
|
||||
if group.labels:
|
||||
field_entry["label"] = LazyI18nString(field["label"])
|
||||
if field["type"] == FieldEntryType.PLACEHOLDER.value:
|
||||
placeholder = (
|
||||
context.get("placeholders")
|
||||
.get(group.content_type.value, {})
|
||||
.get(field["content"])
|
||||
)
|
||||
if placeholder:
|
||||
placeholder_value = placeholder["evaluate"](
|
||||
*context.get("evaluation_context", [])
|
||||
)
|
||||
if placeholder_value:
|
||||
field_entry["value"] = placeholder_value
|
||||
elif field["type"] == FieldEntryType.TEXT.value:
|
||||
placeholder_value = LazyI18nString(field["content"])
|
||||
elif field["type"] == FieldEntryType.IMAGE.value:
|
||||
raise NotImplementedError(
|
||||
"Image placeholders not implemented"
|
||||
)
|
||||
if "value" in field_entry and field_entry["value"]:
|
||||
group_fields.append(field_entry)
|
||||
if group.min_entries and len(group_fields) < group.min_entries:
|
||||
raise ValueError(
|
||||
f"Group {group.identifier} needs at least {group.min_entries} entries, but only {len(group_fields)} were provided"
|
||||
)
|
||||
fields[group.identifier] = group_fields[: group.max_entries]
|
||||
else:
|
||||
raise ValueError("Unknown field group")
|
||||
return fields
|
||||
|
||||
def convert_fields(self, strings, fields, prefix):
|
||||
converted = []
|
||||
for i,f in enumerate(fields):
|
||||
@@ -243,13 +228,18 @@ class AppleWalletEventTicket(AppleWalletStyle):
|
||||
strings.add_entry(f"{prefix}-{i}-label", converted_field['label'])
|
||||
converted_field['label'] = f"{prefix}-{i}-label"
|
||||
|
||||
if isinstance(converted_field['value'], LazyI18nString):
|
||||
strings.add_entry(f"{prefix}-{i}-value", converted_field['value'])
|
||||
converted_field['value'] = f"{prefix}-{i}-value"
|
||||
converted.append(converted_field)
|
||||
return converted
|
||||
|
||||
def pass_content(self, layout, context, strings):
|
||||
fields = self.get_pass_fields(layout, context)
|
||||
def pass_content(self, fields, strings):
|
||||
return {
|
||||
"eventTicket": {
|
||||
"primaryFields": self.convert_fields(strings, fields['primary'], 'primary')
|
||||
"primaryFields": self.convert_fields(strings, fields['primary'], 'primary'),
|
||||
"secondaryFields": self.convert_fields(strings, fields['secondary'], 'secondary'),
|
||||
"auxillaryFields": self.convert_fields(strings, fields['auxillary'], 'auxillary'),
|
||||
"backFields": self.convert_fields(strings, fields['back'], 'back'),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,8 +49,7 @@ class FieldContentType(enum.Enum):
|
||||
|
||||
|
||||
class FieldEntryType(enum.Enum):
|
||||
IMAGE = "image"
|
||||
TEXT = "text"
|
||||
CUSTOM = "custom"
|
||||
PLACEHOLDER = "placeholder"
|
||||
|
||||
|
||||
@@ -191,7 +190,7 @@ class PlaceholderFieldGroup(FieldGroup):
|
||||
{
|
||||
"properties": {
|
||||
**baseprops,
|
||||
"type": {"const": self.content_type.value},
|
||||
"type": {"const": "custom"},
|
||||
"content": {"$ref": "#/$defs/I18nString"},
|
||||
}
|
||||
},
|
||||
@@ -276,6 +275,49 @@ class PassStyle:
|
||||
|
||||
def generate(self, layout, context):
|
||||
raise NotImplementedError()
|
||||
|
||||
def render_placeholder(self, context, content_type, content):
|
||||
placeholder = (
|
||||
context.get("placeholders")
|
||||
.get(content_type, {})
|
||||
.get(content)
|
||||
)
|
||||
if placeholder:
|
||||
placeholder_value = placeholder["evaluate"](
|
||||
*context.get("evaluation_context", [])
|
||||
)
|
||||
if placeholder_value:
|
||||
return placeholder_value
|
||||
def get_pass_fields(self, layout, context):
|
||||
fields = {}
|
||||
for group in self.fieldgroups:
|
||||
if isinstance(group, PredefinedFieldGroup):
|
||||
pass
|
||||
elif isinstance(group, PlaceholderFieldGroup):
|
||||
group_fields = fields.get(group.identifier, [])
|
||||
if group.identifier in layout["fieldgroups"]:
|
||||
for field in layout["fieldgroups"][group.identifier]["entries"]:
|
||||
field_entry = {}
|
||||
if group.labels:
|
||||
field_entry["label"] = LazyI18nString(field["label"])
|
||||
if field["type"] == FieldEntryType.PLACEHOLDER.value:
|
||||
field_entry["value"] = self.render_placeholder(context, group.content_type.value, field['content'])
|
||||
elif field["type"] == FieldEntryType.CUSTOM.value:
|
||||
field_entry["value"] = LazyI18nString(field["content"])
|
||||
if "value" in field_entry and field_entry["value"]:
|
||||
group_fields.append(field_entry)
|
||||
if group.min_entries and len(group_fields) < group.min_entries:
|
||||
raise ValueError(
|
||||
f"Group {group.identifier} needs at least {group.min_entries} entries, but only {len(group_fields)} were provided"
|
||||
)
|
||||
fields[group.identifier] = group_fields[: group.max_entries]
|
||||
if (overflow_group := layout["fieldgroups"][group.identifier]['overflow']):
|
||||
fields.setdefault(overflow_group, [])
|
||||
fields[overflow_group] += group_fields[group.max_entries:]
|
||||
else:
|
||||
raise ValueError("Unknown field group")
|
||||
return fields
|
||||
|
||||
|
||||
class PassLayout:
|
||||
style: PassStyle
|
||||
|
||||
@@ -27,6 +27,8 @@ from pretix.base.settings import SettingsSandbox
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
from .styles import AVAILABLE_STYLES_DICT
|
||||
from .styles.apple import ApplePlatform
|
||||
from .styles.google import GooglePlatform
|
||||
|
||||
from .models import WalletLayout
|
||||
from .views import get_layout_variables
|
||||
@@ -65,12 +67,14 @@ class GoogleWalletTicketOutput(WalletOutput):
|
||||
identifier = "wallet_google"
|
||||
verbose_name = _("Google")
|
||||
download_button_text = "Add to Google Wallet"
|
||||
platform = GooglePlatform
|
||||
|
||||
|
||||
class AppleWalletTicketOutput(WalletOutput):
|
||||
identifier = "wallet_apple"
|
||||
verbose_name = _("Apple")
|
||||
download_button_text = "Add to Apple Wallet"
|
||||
platform = ApplePlatform
|
||||
|
||||
def generate(self, op):
|
||||
order = op.order
|
||||
@@ -87,6 +91,7 @@ class AppleWalletTicketOutput(WalletOutput):
|
||||
# )
|
||||
# )
|
||||
layout = WalletLayout.objects.get(pk=1)
|
||||
platform_layout = layout.platform_layouts.get(platform=self.platform.identifier)
|
||||
|
||||
ticket = str(op.item.name)
|
||||
if op.variation:
|
||||
@@ -121,9 +126,9 @@ class AppleWalletTicketOutput(WalletOutput):
|
||||
"serialNumber": serialNumber,
|
||||
"locales": event.settings.locales
|
||||
}
|
||||
assert layout.platform == "apple"
|
||||
data = AVAILABLE_STYLES_DICT[layout.platform][layout.style].generate(
|
||||
layout.layout, context
|
||||
|
||||
data = AVAILABLE_STYLES_DICT[self.platform.identifier][platform_layout.style].generate(
|
||||
platform_layout.layout, context
|
||||
)
|
||||
return filename, "application/vnd.apple.pkpass", data
|
||||
|
||||
|
||||
@@ -12,12 +12,14 @@ from django.conf import settings
|
||||
from .models import WalletLayout
|
||||
from .styles import AVAILABLE_STYLES, AVAILABLE_PLATFORMS
|
||||
|
||||
from django.contrib.staticfiles import finders
|
||||
|
||||
def get_layout_variables(event):
|
||||
return {
|
||||
"text": get_variables(event),
|
||||
"image": get_images(event)
|
||||
| {"poweredby": {"label": _("pretix-Logo")}}, # TODO: image upload
|
||||
| {"poweredby": {"label": _("pretix-Logo"), "evaluate": lambda *_: open(finders.find("pretix_passbook/logo.png"), "rb")},
|
||||
"poweredby_icon": {"label": _("pretix-Icon"), "evaluate": lambda *_: open(finders.find("pretix_passbook/icon.png"), "rb")}}, # TODO: image upload
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user