mirror of
https://github.com/pretix/pretix.git
synced 2026-11-10 01:17:07 +00:00
localization; bugfixes; google event ticket
This commit is contained in:
1 parent
2d0b22243c
commit
c8e65e5db0
9 files changed
+389
-228
No files matched your search
@@ -21,24 +21,27 @@
|
||||
#
|
||||
import logging
|
||||
import os
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from decimal import Decimal
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from django.core.files.base import ContentFile
|
||||
from django.db import transaction
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext as _
|
||||
from django_scopes import scopes_disabled
|
||||
from i18nfield.strings import LazyI18nString
|
||||
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import (
|
||||
CachedCombinedTicket, CachedTicket, Event, InvoiceAddress, Order,
|
||||
OrderPosition, CachedFile
|
||||
CachedCombinedTicket, CachedFile, CachedTicket, Event, InvoiceAddress,
|
||||
Order, OrderPosition,
|
||||
)
|
||||
from pretix.base.services.tasks import EventTask, ProfiledTask
|
||||
from pretix.base.settings import PERSON_NAME_SCHEMES
|
||||
from pretix.base.signals import register_ticket_outputs
|
||||
from pretix.celery_app import app
|
||||
from pretix.helpers.database import rolledback_transaction
|
||||
from django.db import transaction
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -64,10 +67,11 @@ def generate_orderposition(order_position: int, provider: str, cf: CachedFile |
|
||||
for ct in CachedTicket.objects.filter(order_position=order_position, provider=provider):
|
||||
ct.delete()
|
||||
ct = CachedTicket.objects.create(order_position=order_position, provider=provider,
|
||||
extension=ext, type=ttype, file=None)
|
||||
extension=ext, type=ttype, file=None)
|
||||
ct.file.save(filename, ContentFile(data))
|
||||
return ct.pk
|
||||
|
||||
|
||||
def generate_order(order: int, provider: str, cf: CachedFile | None = None):
|
||||
order = Order.objects.select_related('event').get(id=order)
|
||||
|
||||
@@ -89,10 +93,11 @@ def generate_order(order: int, provider: str, cf: CachedFile | None = None):
|
||||
for ct in CachedCombinedTicket.objects.filter(order=order, provider=provider):
|
||||
ct.delete()
|
||||
ct = CachedCombinedTicket.objects.create(order=order, provider=provider, extension=ext,
|
||||
type=ttype, file=None)
|
||||
type=ttype, file=None)
|
||||
ct.file.save(filename, ContentFile(data))
|
||||
return ct.pk
|
||||
|
||||
|
||||
@app.task(base=ProfiledTask)
|
||||
def generate(model: str, pk: int, provider: str, cachedfile_pk: int | None = None):
|
||||
with scopes_disabled():
|
||||
@@ -109,18 +114,19 @@ def generate(model: str, pk: int, provider: str, cachedfile_pk: int | None = Non
|
||||
class DummyRollbackException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def get_preview_position(event):
|
||||
connection = transaction.get_connection()
|
||||
if not connection.in_atomic_block:
|
||||
raise RuntimeError("get_preview_position needs to be called in a rolledback_transaction")
|
||||
|
||||
item = event.items.create(name=_("Sample product"), default_price=Decimal('42.23'),
|
||||
description=_("Sample product description"))
|
||||
item2 = event.items.create(name=_("Sample workshop"), default_price=Decimal('23.40'))
|
||||
item = event.items.create(name=LazyI18nString.from_gettext("Sample product"), default_price=Decimal('42.23'),
|
||||
description=_("Sample product description"))
|
||||
item2 = event.items.create(name=LazyI18nString.from_gettext("Sample workshop"), default_price=Decimal('23.40'))
|
||||
item.program_times.create(item=item, start=datetime(2017, 12, 27, 0, 0, 0, tzinfo=timezone.utc),
|
||||
end=datetime(2017, 12, 28, 0, 0, 0, tzinfo=timezone.utc))
|
||||
end=datetime(2017, 12, 28, 0, 0, 0, tzinfo=timezone.utc))
|
||||
|
||||
from pretix.base.models import Order
|
||||
from pretix.base.models import Order, SeatingPlan
|
||||
order = event.orders.create(status=Order.STATUS_PENDING, datetime=now(),
|
||||
email='sample@pretix.eu',
|
||||
locale=event.settings.locale,
|
||||
@@ -129,7 +135,16 @@ def get_preview_position(event):
|
||||
|
||||
scheme = PERSON_NAME_SCHEMES[event.settings.name_scheme]
|
||||
sample = {k: str(v) for k, v in scheme['sample'].items()}
|
||||
position = order.positions.create(item=item, attendee_name_parts=sample, price=item.default_price)
|
||||
|
||||
SeatingPlan.objects.create(
|
||||
name="Plan", organizer=event.organizer, layout="{}"
|
||||
)
|
||||
event.seat_category_mappings.create(
|
||||
layout_category='Stalls', product=item
|
||||
)
|
||||
seat = event.seats.create(seat_number="1", row_label="C", product=item, seat_guid=uuid.uuid4())
|
||||
|
||||
position = order.positions.create(item=item, attendee_name_parts=sample, price=item.default_price, seat=seat)
|
||||
s = event.subevents.first()
|
||||
order.positions.create(item=item2, attendee_name_parts=sample, price=item.default_price, addon_to=position, subevent=s)
|
||||
order.positions.create(item=item2, attendee_name_parts=sample, price=item.default_price, addon_to=position, subevent=s)
|
||||
@@ -137,6 +152,7 @@ def get_preview_position(event):
|
||||
InvoiceAddress.objects.create(order=order, name_parts=sample, company=_("Sample company"))
|
||||
return position
|
||||
|
||||
|
||||
def preview(event: int, provider: str, provider_arguments: dict = {}):
|
||||
event = Event.objects.get(id=event)
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
from typing import Protocol, runtime_checkable
|
||||
|
||||
from django.utils import translation
|
||||
from django.utils.formats import date_format
|
||||
|
||||
type MaybeTranslatedString = str | Localizable
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class Localizable(Protocol):
|
||||
def localize(self, lng: str) -> str:
|
||||
...
|
||||
|
||||
|
||||
class LocalizableBase:
|
||||
def localize(self, lng):
|
||||
with translation.override(lng):
|
||||
return str(self)
|
||||
|
||||
|
||||
class FormattingLocalizableString(LocalizableBase):
|
||||
def __init__(
|
||||
self,
|
||||
base_str: MaybeTranslatedString,
|
||||
*format_args: MaybeTranslatedString,
|
||||
**format_kwargs: MaybeTranslatedString,
|
||||
):
|
||||
self.base_str = base_str
|
||||
self.format_args = format_args
|
||||
self.format_kwargs = format_kwargs
|
||||
|
||||
def __str__(self):
|
||||
return str(self.base_str).format(*self.format_args, **self.format_kwargs)
|
||||
|
||||
|
||||
class GettextLocalizableString(LocalizableBase):
|
||||
def __init__(self, base):
|
||||
self.base = base
|
||||
|
||||
@classmethod
|
||||
def gettext(cls, base):
|
||||
"""Fake function so makemessages picks up these strings."""
|
||||
return cls(base)
|
||||
|
||||
def __str__(self):
|
||||
return translation.gettext(self.base)
|
||||
|
||||
|
||||
class LocalizableDatetime(LocalizableBase):
|
||||
def __init__(self, datetime, format):
|
||||
self.datetime = datetime
|
||||
self.format = format
|
||||
|
||||
def __str__(self):
|
||||
return date_format(self.datetime, self.format)
|
||||
|
||||
|
||||
def localizable_to_dict(string: MaybeTranslatedString, locales: list[str]):
|
||||
translated = {}
|
||||
|
||||
for locale in locales:
|
||||
with translation.override(locale):
|
||||
translated[locale] = str(string)
|
||||
|
||||
return translated
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
# Generated by Django 5.2.17 on 2026-10-06 14:29
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
(
|
||||
"pretixbase",
|
||||
"0312_alter_customer_locale_alter_devicelastseen_device_and_more",
|
||||
),
|
||||
("wallet", "0003_walletlayoutfilesetting"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="walletlayoutitem",
|
||||
name="layout",
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="item_assignments",
|
||||
to="wallet.walletlayout",
|
||||
),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="GoogleWalletInstance",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True, primary_key=True, serialize=False
|
||||
),
|
||||
),
|
||||
("type", models.CharField()),
|
||||
("identifier", models.CharField()),
|
||||
("data", models.JSONField()),
|
||||
(
|
||||
"event",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="pretixbase.event",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"unique_together": {("type", "identifier")},
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -6,11 +6,14 @@ from django.contrib.staticfiles import finders
|
||||
from django.core.files import File
|
||||
from django.dispatch import receiver
|
||||
from django.templatetags.static import static
|
||||
from django.utils.formats import date_format
|
||||
from i18nfield.strings import LazyI18nString
|
||||
|
||||
from pretix.base.models.event import Event
|
||||
from pretix.base.templatetags.money import money_filter
|
||||
from pretix.multidomain.urlreverse import eventreverse_absolute
|
||||
from pretix.plugins.wallet.i18n import (
|
||||
FormattingLocalizableString, GettextLocalizableString, LocalizableDatetime,
|
||||
MaybeTranslatedString,
|
||||
)
|
||||
|
||||
from .signals import register_wallet_placeholders
|
||||
|
||||
@@ -38,12 +41,12 @@ class BaseWalletPlaceholder:
|
||||
raise NotImplementedError()
|
||||
|
||||
@property
|
||||
def label(self) -> LazyI18nString:
|
||||
def label(self) -> MaybeTranslatedString:
|
||||
"""The human readable name of this placeholder"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@property
|
||||
def control_label(self) -> LazyI18nString:
|
||||
def control_label(self) -> MaybeTranslatedString:
|
||||
"""
|
||||
The human readable name of this placeholder shown in the backend.
|
||||
|
||||
@@ -63,7 +66,7 @@ class BaseWalletTextPlaceholder(BaseWalletPlaceholder):
|
||||
def content_type(self) -> str:
|
||||
return "text"
|
||||
|
||||
def render(self, **context) -> LazyI18nString | str | None:
|
||||
def render(self, **context) -> MaybeTranslatedString | None:
|
||||
"""
|
||||
This method is called to generate the text that is being shown on the pass.
|
||||
You will be passed the keyword arguments specified in ``required_context``.
|
||||
@@ -71,7 +74,7 @@ class BaseWalletTextPlaceholder(BaseWalletPlaceholder):
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def render_sample(self, **context) -> LazyI18nString | str:
|
||||
def render_sample(self, **context) -> MaybeTranslatedString:
|
||||
"""
|
||||
This method is called to generate a text to be used in previews.
|
||||
|
||||
@@ -79,8 +82,8 @@ class BaseWalletTextPlaceholder(BaseWalletPlaceholder):
|
||||
If those instances contain all data needed, you do not need to implement this.
|
||||
"""
|
||||
sample = self.render(**context)
|
||||
if sample is None:
|
||||
raise RuntimeError("`render` returned None when rendering a sample")
|
||||
if not sample:
|
||||
raise RuntimeError("`render` returned falsy value when rendering a sample")
|
||||
return sample
|
||||
|
||||
|
||||
@@ -111,11 +114,11 @@ class FunctionalWalletTextPlaceholder(BaseWalletTextPlaceholder):
|
||||
def __init__(
|
||||
self,
|
||||
identifier: str,
|
||||
label: LazyI18nString,
|
||||
label: MaybeTranslatedString,
|
||||
args: set[str],
|
||||
func: Callable[..., LazyI18nString | str | None],
|
||||
func: Callable[..., MaybeTranslatedString | None],
|
||||
sample: (
|
||||
None | LazyI18nString | str | Callable[..., str | LazyI18nString]
|
||||
None | MaybeTranslatedString | Callable[..., MaybeTranslatedString]
|
||||
) = None,
|
||||
):
|
||||
self._identifier = identifier
|
||||
@@ -124,6 +127,9 @@ class FunctionalWalletTextPlaceholder(BaseWalletTextPlaceholder):
|
||||
self.render = func
|
||||
self._sample = sample
|
||||
|
||||
def __repr__(self):
|
||||
return f"FunctionalWalletTextPlaceholder(identifier={self.identifier!r}, label={self.label!r})"
|
||||
|
||||
@property
|
||||
def identifier(self):
|
||||
return self._identifier
|
||||
@@ -136,7 +142,7 @@ class FunctionalWalletTextPlaceholder(BaseWalletTextPlaceholder):
|
||||
def required_context(self) -> set[str]:
|
||||
return self._args
|
||||
|
||||
def render_sample(self, **context) -> LazyI18nString | str:
|
||||
def render_sample(self, **context) -> MaybeTranslatedString:
|
||||
if isinstance(self._sample, Callable):
|
||||
return self._sample(**context)
|
||||
elif self._sample:
|
||||
@@ -149,7 +155,7 @@ class FunctionalWalletImagePlaceholder(BaseWalletImagePlaceholder):
|
||||
def __init__(
|
||||
self,
|
||||
identifier: str,
|
||||
label: LazyI18nString,
|
||||
label: MaybeTranslatedString,
|
||||
args: set[str],
|
||||
func: Callable[..., File | None],
|
||||
sample: None | str | Callable[..., str] = None,
|
||||
@@ -334,13 +340,7 @@ def get_wallet_placeholder_renderer(**kwargs):
|
||||
"seat",
|
||||
{"order_position"},
|
||||
lambda order_position: (
|
||||
order_position.seat
|
||||
if order_position.seat_id
|
||||
else (
|
||||
order_position.addon_to.seat
|
||||
if order_position.addon_to_id
|
||||
else None
|
||||
)
|
||||
order_position.seat if order_position.seat_id else None
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -364,15 +364,19 @@ def get_static_file(name) -> File | None:
|
||||
|
||||
def admission(subevent_or_event):
|
||||
return (
|
||||
subevent_or_event.date_admission.astimezone(subevent_or_event.timezone)
|
||||
LocalizableDatetime(
|
||||
subevent_or_event.date_admission.astimezone(subevent_or_event.timezone),
|
||||
"SHORT_DATETIME_FORMAT",
|
||||
)
|
||||
if subevent_or_event.date_admission
|
||||
else None
|
||||
)
|
||||
|
||||
|
||||
def date_from(subevent_or_event):
|
||||
return subevent_or_event.get_date_from_display(
|
||||
subevent_or_event.timezone, short=True
|
||||
return LocalizableDatetime(
|
||||
subevent_or_event.date_from.astimezone(subevent_or_event.timezone),
|
||||
"SHORT_DATETIME_FORMAT",
|
||||
)
|
||||
|
||||
|
||||
@@ -395,62 +399,60 @@ def website(order_position, order):
|
||||
register_wallet_placeholders,
|
||||
dispatch_uid="plugin_wallet_register_wallet_placeholders",
|
||||
)
|
||||
def base_text_placeholders(sender, **kwargs):
|
||||
def base_text_placeholders(sender: Event, **kwargs):
|
||||
return [
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"event",
|
||||
LazyI18nString.from_gettext("Event"),
|
||||
GettextLocalizableString.gettext("Event"),
|
||||
{"event"},
|
||||
lambda event: event.name,
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"event_slug",
|
||||
LazyI18nString.from_gettext("Event Slug"),
|
||||
GettextLocalizableString.gettext("Event Slug"),
|
||||
{"event"},
|
||||
lambda event: event.slug,
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"admission",
|
||||
LazyI18nString.from_gettext("Admission"),
|
||||
GettextLocalizableString.gettext("Admission"),
|
||||
{"subevent_or_event"},
|
||||
admission,
|
||||
lambda subevent_or_event: subevent_or_event.get_date_from_display(
|
||||
subevent_or_event.timezone, short=True
|
||||
),
|
||||
date_from,
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"date_from",
|
||||
LazyI18nString.from_gettext("Begin"),
|
||||
GettextLocalizableString.gettext("Begin"),
|
||||
{"subevent_or_event"},
|
||||
date_from,
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"admission_or_date_from",
|
||||
LazyI18nString.from_gettext("Admission or Date From"),
|
||||
GettextLocalizableString.gettext("Admission or Date From"),
|
||||
{"subevent_or_event"},
|
||||
admission_or_date_from,
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"order",
|
||||
LazyI18nString.from_gettext("Order Code"),
|
||||
GettextLocalizableString.gettext("Order Code"),
|
||||
{"order"},
|
||||
lambda order: order.code,
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"total",
|
||||
LazyI18nString.from_gettext("Order Total"),
|
||||
GettextLocalizableString.gettext("Order Total"),
|
||||
{"event", "order"},
|
||||
lambda event, order: money_filter(order.total, event.currency),
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"order_email",
|
||||
LazyI18nString.from_gettext("Order Email"),
|
||||
GettextLocalizableString.gettext("Order Email"),
|
||||
{"order"},
|
||||
lambda order: order.email,
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"price",
|
||||
LazyI18nString.from_gettext("Item Price"),
|
||||
GettextLocalizableString.gettext("Item Price"),
|
||||
{"event", "order_position"},
|
||||
lambda event, order_position: money_filter(
|
||||
order_position.price, event.currency
|
||||
@@ -458,74 +460,78 @@ def base_text_placeholders(sender, **kwargs):
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"secret",
|
||||
LazyI18nString.from_gettext("Order Secret (QR-Code-Content)"),
|
||||
GettextLocalizableString.gettext("Order Secret (QR-Code-Content)"),
|
||||
{"order_position"},
|
||||
lambda order_position: order_position.secret,
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"item",
|
||||
LazyI18nString.from_gettext("Product"),
|
||||
GettextLocalizableString.gettext("Product"),
|
||||
{"item"},
|
||||
lambda item: item.name,
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"item_description",
|
||||
LazyI18nString.from_gettext("Product description"),
|
||||
GettextLocalizableString.gettext("Product description"),
|
||||
{"item"},
|
||||
lambda item: item.description,
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"item_with_variation",
|
||||
LazyI18nString.from_gettext("Product and variation"),
|
||||
GettextLocalizableString.gettext("Product and variation"),
|
||||
{"order_position"},
|
||||
lambda order_position: (
|
||||
f"{order_position.item.name} - {order_position.variation}"
|
||||
FormattingLocalizableString(
|
||||
"{name} - {variation}",
|
||||
name=order_position.item.name,
|
||||
variation=order_position.variation.name,
|
||||
)
|
||||
if order_position.variation
|
||||
else order_position.item.name
|
||||
),
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"seat",
|
||||
LazyI18nString.from_gettext("Seat: Full name"),
|
||||
{"event", "seat"},
|
||||
lambda event, seat: (
|
||||
seat
|
||||
if seat
|
||||
else (
|
||||
LazyI18nString.from_gettext("General admission")
|
||||
if event.seating_plan_id is not None
|
||||
else ""
|
||||
)
|
||||
GettextLocalizableString.gettext("Seat: Full name"),
|
||||
{"seat"},
|
||||
lambda seat: (
|
||||
FormattingLocalizableString("{seat}", seat=seat) if seat else None
|
||||
),
|
||||
LazyI18nString.from_gettext("Ground floor, Row 3, Seat 4"),
|
||||
GettextLocalizableString.gettext("Ground floor, Row 3, Seat 4")
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"seat_zone",
|
||||
LazyI18nString.from_gettext("Seat: zone"),
|
||||
{"event", "seat"},
|
||||
lambda event, seat: (
|
||||
seat.zone_name
|
||||
if seat
|
||||
else (
|
||||
LazyI18nString.from_gettext("General admission")
|
||||
if event.seating_plan_id is not None
|
||||
else ""
|
||||
)
|
||||
),
|
||||
LazyI18nString.from_gettext("Ground floor"),
|
||||
GettextLocalizableString.gettext("Seat: Zone"),
|
||||
{"seat"},
|
||||
lambda seat: seat.zone_name,
|
||||
GettextLocalizableString.gettext("Ground floor"),
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"seat_row",
|
||||
GettextLocalizableString.gettext("Seat: Row"),
|
||||
{"seat"},
|
||||
lambda seat: seat.row_label or seat.row_name,
|
||||
"C",
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"seat_number",
|
||||
GettextLocalizableString.gettext("Seat: Seat Number"),
|
||||
{"seat"},
|
||||
lambda seat: seat.seat_label or seat.seat_number,
|
||||
"5",
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"attendee_name",
|
||||
LazyI18nString.from_gettext("Attendee name"),
|
||||
GettextLocalizableString.gettext("Attendee name"),
|
||||
{"order_position"},
|
||||
lambda order_position: order_position.attendee_name,
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"program_start",
|
||||
LazyI18nString.from_gettext("Programm times: Start"),
|
||||
GettextLocalizableString.gettext("Programm times: Start"),
|
||||
{"item", "subevent_or_event"},
|
||||
lambda item, subevent_or_event: (
|
||||
date_format(
|
||||
LocalizableDatetime(
|
||||
min(pt.start for pt in item.program_times.all()).astimezone(
|
||||
subevent_or_event.timezone
|
||||
),
|
||||
@@ -537,10 +543,10 @@ def base_text_placeholders(sender, **kwargs):
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"program_end",
|
||||
LazyI18nString.from_gettext("Programm times: End"),
|
||||
GettextLocalizableString.gettext("Programm times: End"),
|
||||
{"item", "subevent_or_event"},
|
||||
lambda item, subevent_or_event: (
|
||||
date_format(
|
||||
LocalizableDatetime(
|
||||
max(pt.end for pt in item.program_times.all()).astimezone(
|
||||
subevent_or_event.timezone
|
||||
),
|
||||
@@ -552,56 +558,56 @@ def base_text_placeholders(sender, **kwargs):
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"organizer",
|
||||
LazyI18nString.from_gettext("Organizer"),
|
||||
GettextLocalizableString.gettext("Organizer"),
|
||||
{"organizer"},
|
||||
lambda organizer: organizer.name,
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"organizer_contact",
|
||||
LazyI18nString.from_gettext("Organizer Contact"),
|
||||
GettextLocalizableString.gettext("Organizer Contact"),
|
||||
{"event"},
|
||||
lambda event: event.settings.contact_mail,
|
||||
"contact@example.com",
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"order_code",
|
||||
LazyI18nString.from_gettext("Order Code"),
|
||||
GettextLocalizableString.gettext("Order Code"),
|
||||
{"order"},
|
||||
lambda order: order.code,
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"purchase_date",
|
||||
LazyI18nString.from_gettext("Purchase Date"),
|
||||
GettextLocalizableString.gettext("Purchase Date"),
|
||||
{"order", "subevent_or_event"},
|
||||
lambda order, subevent_or_event: date_format(
|
||||
lambda order, subevent_or_event: LocalizableDatetime(
|
||||
order.datetime.astimezone(subevent_or_event.timezone),
|
||||
"SHORT_DATETIME_FORMAT",
|
||||
),
|
||||
),
|
||||
FunctionalWalletTextPlaceholder(
|
||||
"website",
|
||||
LazyI18nString.from_gettext("Website"),
|
||||
GettextLocalizableString.gettext("Website"),
|
||||
{"order_position", "order"},
|
||||
website,
|
||||
),
|
||||
# IMAGES
|
||||
FunctionalWalletImagePlaceholder(
|
||||
"poweredby",
|
||||
LazyI18nString.from_gettext("Logo"),
|
||||
GettextLocalizableString.gettext("Logo"),
|
||||
set(),
|
||||
lambda: get_static_file("pretixplugins/wallet/logo.png"),
|
||||
static("pretixplugins/wallet/logo.png"),
|
||||
),
|
||||
FunctionalWalletImagePlaceholder(
|
||||
"poweredby_icon",
|
||||
LazyI18nString.from_gettext("Icon"),
|
||||
GettextLocalizableString.gettext("Icon"),
|
||||
set(),
|
||||
lambda: get_static_file("pretixplugins/wallet/icon.png"),
|
||||
static("pretixplugins/wallet/icon.png"),
|
||||
),
|
||||
FunctionalWalletImagePlaceholder(
|
||||
"example_no_preview",
|
||||
LazyI18nString.from_gettext("Image with no preview"),
|
||||
GettextLocalizableString.gettext("Image with no preview"),
|
||||
set(),
|
||||
lambda: get_static_file("pretix_passbook/icon.png"),
|
||||
),
|
||||
|
||||
+2
-2
@@ -28,8 +28,8 @@ const layoutStyle = computed(() => {
|
||||
v = `url(${URL.createObjectURL(val.file)})`; // TODO: encode
|
||||
} else if (settingDef.type == "image" && val && val.url) {
|
||||
v = `url(${val.url})`;
|
||||
} else if (settingDef.type == "image" && settingDef.attrs.default) {
|
||||
v = `url(${settingDef.attrs.default})`;
|
||||
} else if (settingDef.type == "image" && settingDef.default) {
|
||||
v = `url(${settingDef.default})`;
|
||||
} else {
|
||||
v = val;
|
||||
}
|
||||
|
||||
@@ -14,21 +14,17 @@ from django.conf import settings
|
||||
from django.contrib.staticfiles import finders
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.forms import ValidationError
|
||||
from django.templatetags.static import static
|
||||
from django.utils.encoding import force_bytes
|
||||
from django.utils.translation import gettext as _, override
|
||||
from i18nfield.strings import LazyI18nString
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from pretix.base.models import OrderPosition
|
||||
from pretix.plugins.wallet.i18n import (
|
||||
FormattingLocalizableString, GettextLocalizableString, Localizable,
|
||||
MaybeTranslatedString,
|
||||
)
|
||||
from pretix.plugins.wallet.styles.base import (
|
||||
ColorSettingsField,
|
||||
FieldGroup,
|
||||
FieldGroupDisplay,
|
||||
FloatSettingsField,
|
||||
ImageSettingsField,
|
||||
PassStyle,
|
||||
PlaceholderFieldEntry,
|
||||
TextFieldGroup,
|
||||
ColorSettingsField, FieldGroup, FieldGroupDisplay, FloatSettingsField,
|
||||
ImageSettingsField, PassStyle, PlaceholderFieldEntry, TextFieldGroup,
|
||||
WalletPlatform,
|
||||
)
|
||||
|
||||
@@ -40,32 +36,15 @@ class ApplePlatform(WalletPlatform):
|
||||
name = _("Apple")
|
||||
|
||||
|
||||
class FormattedLazyI18nString:
|
||||
def __init__(self, base_str: LazyI18nString, **format_args: str):
|
||||
self.base_str = base_str
|
||||
self.format_args = format_args
|
||||
|
||||
def localize(self, language):
|
||||
return self.base_str.localize(language).format(**self.format_args)
|
||||
|
||||
|
||||
def lazyi18nstring_from_gettext(text: str, locales: set[str]) -> LazyI18nString:
|
||||
data = {}
|
||||
for locale in locales:
|
||||
with override(locale):
|
||||
data[locale] = _(text)
|
||||
return LazyI18nString(data)
|
||||
|
||||
|
||||
class StringResource:
|
||||
entries: dict[str, LazyI18nString | FormattedLazyI18nString]
|
||||
entries: dict[str, MaybeTranslatedString]
|
||||
locales: set[str]
|
||||
|
||||
def __init__(self, locales):
|
||||
self.entries = {}
|
||||
self.locales = set(locales)
|
||||
|
||||
def add_entry(self, key: str, value: LazyI18nString | FormattedLazyI18nString):
|
||||
def add_entry(self, key: str, value: MaybeTranslatedString):
|
||||
if key in self.entries:
|
||||
raise ValueError(f"{key} already exists in this StringResource")
|
||||
self.entries[key] = value
|
||||
@@ -78,9 +57,10 @@ class StringResource:
|
||||
def generate_resource(self, language):
|
||||
output = ""
|
||||
for key, entry in self.entries.items():
|
||||
output += (
|
||||
f'"{self.escape(key)}" = "{self.escape(entry.localize(language))}";\n'
|
||||
)
|
||||
if isinstance(entry, str):
|
||||
output += f'"{self.escape(key)}" = "{self.escape(entry)}";\n'
|
||||
else:
|
||||
output += f'"{self.escape(key)}" = "{self.escape(entry.localize(language))}";\n'
|
||||
return output.strip()
|
||||
|
||||
def generate(self):
|
||||
@@ -208,12 +188,16 @@ class AppleWalletStyle(PassStyle):
|
||||
event = op.subevent or op.order.event
|
||||
tz = event.timezone
|
||||
|
||||
ticket = str(op.item.name)
|
||||
if op.variation:
|
||||
ticket += " - " + str(op.variation)
|
||||
ticket = (
|
||||
FormattingLocalizableString(
|
||||
"{name} - {variation}", name=op.item.name, variation=op.variation.name
|
||||
)
|
||||
if op.variation
|
||||
else op.item.name
|
||||
)
|
||||
|
||||
description = FormattedLazyI18nString(
|
||||
LazyI18nString.from_gettext("Ticket for {event} ({product})"),
|
||||
description = FormattingLocalizableString(
|
||||
GettextLocalizableString.gettext("Ticket for {event} ({product})"),
|
||||
event=self.event.name,
|
||||
product=ticket,
|
||||
)
|
||||
@@ -236,14 +220,13 @@ class AppleWalletStyle(PassStyle):
|
||||
**self.pass_content(fields, strings),
|
||||
}
|
||||
|
||||
# TODO: Apples specs says this should be "specified as a CSS-style RGB triple, such as rgb(100, 10, 110).", but hex codes work as well
|
||||
if bg_color := self.layout["settings"].get("bg_color"):
|
||||
pass_json["backgroundColor"] = (
|
||||
bg_color # TODO: specified as a CSS-style RGB triple, such as rgb(100, 10, 110).
|
||||
)
|
||||
pass_json["backgroundColor"] = bg_color
|
||||
if fg_color := self.layout["settings"].get("fg_color"):
|
||||
pass_json["foregroundColor"] = fg_color # TODO: specified as a CSS-style RGB triple, such as rgb(100, 10, 110).
|
||||
pass_json["foregroundColor"] = fg_color
|
||||
if label_color := self.layout["settings"].get("label_color"):
|
||||
pass_json["labelColor"] = label_color # TODO: specified as a CSS-style RGB triple, such as rgb(100, 10, 110).
|
||||
pass_json["labelColor"] = label_color
|
||||
|
||||
if "expirationDate" not in pass_json:
|
||||
if op.valid_until:
|
||||
@@ -289,7 +272,6 @@ class AppleWalletStyle(PassStyle):
|
||||
"longitude": float(event.get_lon),
|
||||
}
|
||||
|
||||
print(pass_json)
|
||||
return pass_json
|
||||
|
||||
def generate(self, op: OrderPosition):
|
||||
@@ -297,7 +279,6 @@ class AppleWalletStyle(PassStyle):
|
||||
filename = "{}-{}.pkpass".format(order.event.slug, order.code)
|
||||
|
||||
fields = self.get_pass_fields(op)
|
||||
|
||||
pkpass = SignedZipFile(
|
||||
self.event.settings.wallet_apple_ca_certificate.read(),
|
||||
self.event.settings.wallet_apple_certificate.read(),
|
||||
@@ -332,6 +313,7 @@ class AppleWalletStyle(PassStyle):
|
||||
pkpass.add_file("background.png", background.read())
|
||||
|
||||
for lang, content in strings.generate().items():
|
||||
print(lang, content)
|
||||
pkpass.add_file(f"{lang}.lproj/pass.strings", content)
|
||||
pkpass.add_file("pass.json", json.dumps(pass_json))
|
||||
result = pkpass.finish()
|
||||
@@ -360,8 +342,8 @@ class AppleWalletEventTicket(AppleWalletStyle):
|
||||
default_entries=[
|
||||
PlaceholderFieldEntry(
|
||||
content="admission_or_date_from",
|
||||
label=lazyi18nstring_from_gettext(
|
||||
"Admission", self.event.settings.locales
|
||||
label=GettextLocalizableString.gettext(
|
||||
"Admission"
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -388,8 +370,8 @@ class AppleWalletEventTicket(AppleWalletStyle):
|
||||
default_entries=[
|
||||
PlaceholderFieldEntry(
|
||||
content="item_with_variation",
|
||||
label=lazyi18nstring_from_gettext(
|
||||
"Product", self.event.settings.locales
|
||||
label=GettextLocalizableString.gettext(
|
||||
"Product"
|
||||
),
|
||||
)
|
||||
],
|
||||
@@ -404,8 +386,8 @@ class AppleWalletEventTicket(AppleWalletStyle):
|
||||
default_entries=[
|
||||
PlaceholderFieldEntry(
|
||||
content="seat",
|
||||
label=lazyi18nstring_from_gettext(
|
||||
"Seat", locales=self.event.settings.locales
|
||||
label=GettextLocalizableString.gettext(
|
||||
"Seat"
|
||||
),
|
||||
),
|
||||
PlaceholderFieldEntry(
|
||||
@@ -413,14 +395,14 @@ class AppleWalletEventTicket(AppleWalletStyle):
|
||||
),
|
||||
PlaceholderFieldEntry(
|
||||
content="program_start",
|
||||
label=lazyi18nstring_from_gettext(
|
||||
"From", locales=self.event.settings.locales
|
||||
label=GettextLocalizableString.gettext(
|
||||
"From"
|
||||
),
|
||||
),
|
||||
PlaceholderFieldEntry(
|
||||
content="program_end",
|
||||
label=lazyi18nstring_from_gettext(
|
||||
"To", locales=self.event.settings.locales
|
||||
label=GettextLocalizableString.gettext(
|
||||
"To"
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -450,8 +432,8 @@ class AppleWalletEventTicket(AppleWalletStyle):
|
||||
),
|
||||
PlaceholderFieldEntry(
|
||||
content="order_email",
|
||||
label=lazyi18nstring_from_gettext(
|
||||
"Ordered by", self.event.settings.locales
|
||||
label=GettextLocalizableString.gettext(
|
||||
"Ordered by"
|
||||
),
|
||||
),
|
||||
PlaceholderFieldEntry(
|
||||
@@ -511,16 +493,20 @@ class AppleWalletEventTicket(AppleWalletStyle):
|
||||
]
|
||||
|
||||
def convert_fields(self, strings, fields, prefix):
|
||||
# TODO: detect if a string-value/string-label is the same as a generated field name
|
||||
# eg if a placeholder returns "primary-0-label", this would not be shown to the user but instead replaces with the label
|
||||
converted = []
|
||||
for i, f in enumerate(fields):
|
||||
converted_field = {**f, "key": f"{prefix}-{i}"}
|
||||
if "label" in converted_field and isinstance(
|
||||
converted_field["label"], LazyI18nString
|
||||
converted_field["label"], Localizable
|
||||
):
|
||||
strings.add_entry(f"{prefix}-{i}-label", converted_field["label"])
|
||||
converted_field["label"] = f"{prefix}-{i}-label"
|
||||
|
||||
if isinstance(converted_field["value"], LazyI18nString):
|
||||
if isinstance(
|
||||
converted_field["value"], Localizable
|
||||
):
|
||||
strings.add_entry(f"{prefix}-{i}-value", converted_field["value"])
|
||||
converted_field["value"] = f"{prefix}-{i}-value"
|
||||
converted.append(converted_field)
|
||||
|
||||
@@ -4,20 +4,17 @@ from typing import Any, Literal, TypedDict
|
||||
import jsonschema
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.files import File
|
||||
from django.templatetags.static import static
|
||||
from i18nfield.strings import LazyI18nString
|
||||
|
||||
from pretix.api.helpers import handle_file_upload
|
||||
from pretix.base.models import OrderPosition
|
||||
|
||||
from ..i18n import localizable_to_dict
|
||||
from ..placeholders import (
|
||||
WalletPlaceholderRenderer,
|
||||
get_available_context,
|
||||
get_wallet_placeholder_renderer,
|
||||
get_wallet_placeholders,
|
||||
MaybeTranslatedString, WalletPlaceholderRenderer, get_available_context,
|
||||
get_wallet_placeholder_renderer, get_wallet_placeholders,
|
||||
)
|
||||
from django.templatetags.static import static
|
||||
from urllib.parse import urljoin
|
||||
from pretix.multidomain.urlreverse import eventreverse_absolute
|
||||
|
||||
|
||||
class WalletPlatform:
|
||||
@@ -61,7 +58,7 @@ class FieldGroup:
|
||||
) -> dict:
|
||||
raise NotImplementedError()
|
||||
|
||||
def asdict(self, context: LayoutContext):
|
||||
def asdict(self, locales, context: LayoutContext):
|
||||
return {
|
||||
"type": self.type.value,
|
||||
"identifier": self.identifier,
|
||||
@@ -83,44 +80,42 @@ class FieldEntryType(enum.Enum):
|
||||
|
||||
class FieldEntry[T]:
|
||||
type: FieldEntryType
|
||||
label: LazyI18nString | None
|
||||
label: MaybeTranslatedString | None
|
||||
content: T
|
||||
|
||||
def __init__(
|
||||
self, type: FieldEntryType, content: T, label: LazyI18nString | None = None
|
||||
self, type: FieldEntryType, content: T, label: MaybeTranslatedString | None = None
|
||||
):
|
||||
self.type = type
|
||||
self.label = label
|
||||
self.content = content
|
||||
|
||||
def asdict(self) -> dict:
|
||||
def asdict(self, locales) -> dict:
|
||||
return {
|
||||
"type": self.type.value,
|
||||
"content": self.content,
|
||||
"label": self.label.data if self.label is not None else None,
|
||||
"label": localizable_to_dict(self.label, locales) if self.label is not None else None,
|
||||
}
|
||||
|
||||
|
||||
class PlaceholderFieldEntry(FieldEntry[str]):
|
||||
type = FieldEntryType.PLACEHOLDER
|
||||
label: LazyI18nString | None
|
||||
content: str
|
||||
|
||||
def __init__(self, content: str, label: LazyI18nString | None = None):
|
||||
def __init__(self, content: str, label: MaybeTranslatedString | None = None):
|
||||
self.label = label
|
||||
self.content = content
|
||||
|
||||
|
||||
class CustomFieldEntry(FieldEntry[LazyI18nString]):
|
||||
class CustomFieldEntry(FieldEntry[MaybeTranslatedString]):
|
||||
type: FieldEntryType
|
||||
label: LazyI18nString | None
|
||||
content: LazyI18nString
|
||||
content: MaybeTranslatedString
|
||||
|
||||
def asdict(self) -> dict:
|
||||
def asdict(self, locales) -> dict:
|
||||
return {
|
||||
"type": self.type.value,
|
||||
"content": self.content.data,
|
||||
"label": self.label.data if self.label else None,
|
||||
"content": localizable_to_dict(self.content, locales),
|
||||
"label": localizable_to_dict(self.label, locales) if self.label else None,
|
||||
}
|
||||
|
||||
|
||||
@@ -170,11 +165,11 @@ class PlaceholderFieldGroup(FieldGroup):
|
||||
if self.required and (self.min_entries is None or self.min_entries < 1):
|
||||
self.min_entries = 1
|
||||
|
||||
def asdict(self, context: LayoutContext):
|
||||
def asdict(self, locales, context: LayoutContext):
|
||||
return {
|
||||
**super().asdict(context),
|
||||
**super().asdict(locales, context),
|
||||
"content_type": self.content_type.value,
|
||||
"default_entries": [x.asdict() for x in self.default_entries],
|
||||
"default_entries": [x.asdict(locales) for x in self.default_entries],
|
||||
"display": self.display.value,
|
||||
"min_entries": self.min_entries,
|
||||
"max_entries": self.max_entries,
|
||||
@@ -283,7 +278,7 @@ class SettingsField:
|
||||
required: bool
|
||||
default: str | None
|
||||
|
||||
def asdict(self):
|
||||
def asdict(self, locales):
|
||||
return {
|
||||
"identifier": self.identifier,
|
||||
"label": self.label,
|
||||
@@ -398,7 +393,7 @@ class FloatSettingsField(SettingsField):
|
||||
self.default = default
|
||||
|
||||
def layout_schema(self):
|
||||
schema = {"type": "number"}
|
||||
schema: dict[str, Any] = {"type": "number"}
|
||||
if self.min is not None:
|
||||
schema["minimum"] = self.min
|
||||
if self.max is not None:
|
||||
@@ -407,8 +402,8 @@ class FloatSettingsField(SettingsField):
|
||||
schema = {"oneOf": [schema, {"type": "null"}]}
|
||||
return schema
|
||||
|
||||
def asdict(self):
|
||||
return super().asdict() | {"min": self.min, "max": self.max}
|
||||
def asdict(self, locales):
|
||||
return super().asdict(locales) | {"min": self.min, "max": self.max}
|
||||
|
||||
|
||||
class PassStyle:
|
||||
@@ -466,9 +461,9 @@ class PassStyle:
|
||||
return {
|
||||
"identifier": self.identifier,
|
||||
"name": self.name,
|
||||
"fieldgroups": [x.asdict(context) for x in self.fieldgroups],
|
||||
"fieldgroups": [x.asdict(self.event.settings.locales, context) for x in self.fieldgroups],
|
||||
"preview_layout": self.preview_layout,
|
||||
"settings": [x.asdict() for x in self.settings],
|
||||
"settings": [x.asdict(self.event.settings.locales) for x in self.settings],
|
||||
}
|
||||
|
||||
def layout_schema(self):
|
||||
@@ -580,7 +575,7 @@ class PassStyle:
|
||||
for field in self.layout["fieldgroups"][group.identifier][
|
||||
"entries"
|
||||
]:
|
||||
field_entry = {}
|
||||
field_entry: dict[str, MaybeTranslatedString | None] = {}
|
||||
if group.display == FieldGroupDisplay.WITH_LABEL:
|
||||
field_entry["label"] = LazyI18nString(field["label"])
|
||||
if field["type"] == FieldEntryType.PLACEHOLDER.value:
|
||||
@@ -592,7 +587,7 @@ class PassStyle:
|
||||
and not str(field_entry["label"])
|
||||
and label
|
||||
):
|
||||
field_entry["label"] = LazyI18nString(label)
|
||||
field_entry["label"] = label
|
||||
|
||||
elif field["type"] == FieldEntryType.CUSTOM.value:
|
||||
field_entry["value"] = LazyI18nString(field["content"])
|
||||
|
||||
@@ -1,43 +1,29 @@
|
||||
import uuid
|
||||
from functools import cached_property
|
||||
from urllib.parse import urljoin
|
||||
import uuid
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from django.utils import translation
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.translation import gettext as _
|
||||
from walletobjects import ButtonJWT, EventTicketClass, EventTicketObject
|
||||
from walletobjects.comms import Comms
|
||||
from walletobjects.constants import (
|
||||
Barcode,
|
||||
ClassType,
|
||||
MultipleDevicesAndHoldersAllowedStatus,
|
||||
ObjectState,
|
||||
ObjectType,
|
||||
AnimationType, Barcode, ClassType, ConfirmationCode, DoorsOpen,
|
||||
MultipleDevicesAndHoldersAllowedStatus, ObjectState, ObjectType,
|
||||
ReviewStatus,
|
||||
AnimationType,
|
||||
DoorsOpen,
|
||||
ConfirmationCode
|
||||
)
|
||||
|
||||
|
||||
from pretix.base.models import Event, OrderPosition
|
||||
from pretix.base.settings import GlobalSettingsObject
|
||||
from pretix.multidomain.urlreverse import eventreverse_absolute
|
||||
from pretix.plugins.wallet.models import GoogleWalletInstance, GoogleWalletType
|
||||
from pretix.plugins.wallet.styles.base import (
|
||||
ColorSettingsField,
|
||||
FieldGroup,
|
||||
FieldGroupDisplay,
|
||||
FloatSettingsField,
|
||||
ImageFieldGroup,
|
||||
ImageSettingsField,
|
||||
PassStyle,
|
||||
PlaceholderFieldEntry,
|
||||
PredefinedFieldGroup,
|
||||
TextFieldGroup,
|
||||
WalletPlatform,
|
||||
ColorSettingsField, FieldGroupDisplay, FloatSettingsField, ImageFieldGroup,
|
||||
ImageSettingsField, PassStyle, PlaceholderFieldEntry, PredefinedFieldGroup,
|
||||
TextFieldGroup, WalletPlatform,
|
||||
)
|
||||
from django.db import transaction
|
||||
|
||||
SHIMMER = False
|
||||
|
||||
@@ -310,15 +296,31 @@ class GoogleWalletDefaultEventTicket(GoogleWalletEventTicketStyle):
|
||||
{
|
||||
"fieldgroup": "date",
|
||||
"sample": [
|
||||
{"content": "01/01/1970", "label": "Date"},
|
||||
{"content": "12:34", "label": "Time"},
|
||||
{
|
||||
"content": date_format(
|
||||
self.event.date_from.astimezone(
|
||||
self.event.timezone
|
||||
),
|
||||
"SHORT_DATE_FORMAT",
|
||||
),
|
||||
"label": "Date",
|
||||
},
|
||||
{
|
||||
"content": date_format(
|
||||
self.event.date_from.astimezone(
|
||||
self.event.timezone
|
||||
),
|
||||
"TIME_FORMAT",
|
||||
),
|
||||
"label": "Time",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"fieldgroup": "seating",
|
||||
"sample": [
|
||||
{"content": "5", "label": "Row"},
|
||||
{"content": "2", "label": "Seat"},
|
||||
{"content": _("Ground floor"), "label": _("Section")},
|
||||
{"content": "5 / 2", "label": _("Row") + " / " + _("Seat")},
|
||||
],
|
||||
},
|
||||
{"fieldgroup": "code"},
|
||||
@@ -339,6 +341,22 @@ class GoogleWalletDefaultEventTicket(GoogleWalletEventTicketStyle):
|
||||
{"content": self.venue()[1], "label": self.venue()[0]},
|
||||
],
|
||||
},
|
||||
{
|
||||
"fieldgroup": "date",
|
||||
"sample": [
|
||||
{
|
||||
"content": date_format(
|
||||
self.event.date_from.astimezone(
|
||||
self.event.timezone
|
||||
),
|
||||
"DATETIME_FORMAT",
|
||||
),
|
||||
"label": _("Event Start Time"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{"value": "ABCDE-1", "label": _("Ticket Number")},
|
||||
{"value": "ABCDE", "label": _("Order Number")},
|
||||
]
|
||||
},
|
||||
]
|
||||
@@ -364,8 +382,22 @@ class GoogleWalletDefaultEventTicket(GoogleWalletEventTicketStyle):
|
||||
output_class = super()._generate_class()
|
||||
if self.group_is_active("venue") and all(self.venue()):
|
||||
output_class.venue(*self.venue())
|
||||
|
||||
if self.group_is_active("date"):
|
||||
if self.event.date_from:
|
||||
output_class.start(self.event.date_from.isoformat())
|
||||
|
||||
if self.event.settings.show_date_to and self.event.date_to:
|
||||
output_class.end(self.event.date_to.isoformat())
|
||||
|
||||
if self.event.date_admission:
|
||||
output_class.doors_open(
|
||||
DoorsOpen.doorsOpen, self.event.date_admission.isoformat()
|
||||
)
|
||||
|
||||
if SHIMMER:
|
||||
output_class.securityAnimation(AnimationType.FOIL_SHIMMER)
|
||||
print(output_class)
|
||||
return output_class
|
||||
|
||||
def ticket_holder_name(self, op):
|
||||
@@ -382,14 +414,31 @@ class GoogleWalletDefaultEventTicket(GoogleWalletEventTicketStyle):
|
||||
output_object.reservation_info(op.order.code)
|
||||
output_object.ticket_number(op.code)
|
||||
|
||||
if self.group_is_active("venue") and self.ticket_holder_name(op):
|
||||
if self.group_is_active("ticket_holder") and self.ticket_holder_name(op):
|
||||
output_object.ticket_holder_name(self.ticket_holder_name(op))
|
||||
# output_object.ticket_type(
|
||||
# get_translated_dict(
|
||||
# str(op.item) + (" – " + str(op.variation.value) if op.variation else ""),
|
||||
# op.order.event.settings.get('locales')
|
||||
# )
|
||||
# )
|
||||
|
||||
if self.group_is_active("seating") and (seat := self.op.seat):
|
||||
if seat.zone_name:
|
||||
output_object.section(seat.zone_name)
|
||||
|
||||
if seat.row_label:
|
||||
output_object.row(seat.row_label)
|
||||
elif seat.row_name:
|
||||
output_object.row(seat.row_name)
|
||||
|
||||
if seat.seat_label:
|
||||
output_object.seat(seat.seat_label)
|
||||
elif seat.seat_number:
|
||||
output_object.seat(seat.seat_number)
|
||||
|
||||
output_object.valid_time_interval(op.valid_from, op.valid_until)
|
||||
output_object.ticket_type(
|
||||
get_translated_dict(
|
||||
str(op.item)
|
||||
+ (" – " + str(op.variation.value) if op.variation else ""),
|
||||
op.order.event.settings.get("locales"),
|
||||
)
|
||||
)
|
||||
|
||||
# places = django_settings.CURRENCY_PLACES.get(op.order.event.currency, 2)
|
||||
# output_object.face_value(int(op.price * 1000 ** places), op.order.event.currency)
|
||||
@@ -411,4 +460,5 @@ class GoogleWalletDefaultEventTicket(GoogleWalletEventTicketStyle):
|
||||
# )
|
||||
|
||||
# return self._comms().put_item(ObjectType.eventTicketObject, object_name, output_object)
|
||||
print(output_object)
|
||||
return output_object
|
||||
@@ -14,7 +14,6 @@ from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import (
|
||||
CreateView, DeleteView, DetailView, ListView, View,
|
||||
)
|
||||
from i18nfield.strings import LazyI18nString
|
||||
from pretix_vrpayment_wero.payment import HttpRequest
|
||||
|
||||
from pretix.base.i18n import language
|
||||
@@ -23,6 +22,7 @@ from pretix.base.services.tickets import get_preview_position
|
||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||
from pretix.helpers.database import rolledback_transaction
|
||||
from pretix.helpers.models import modelclone
|
||||
from pretix.plugins.wallet.i18n import localizable_to_dict
|
||||
|
||||
from .models import WalletLayout
|
||||
from .placeholders import (
|
||||
@@ -33,13 +33,6 @@ from .styles import (
|
||||
)
|
||||
|
||||
|
||||
def localize_lazyi18n_string(string: LazyI18nString, locales):
|
||||
data = {}
|
||||
for locale in locales:
|
||||
data[locale] = string.localize(locale)
|
||||
return data
|
||||
|
||||
|
||||
def get_editor_placeholders(event):
|
||||
with (
|
||||
rolledback_transaction(),
|
||||
@@ -50,11 +43,7 @@ def get_editor_placeholders(event):
|
||||
placeholders = {
|
||||
t: {
|
||||
pid: {
|
||||
"label": (
|
||||
localize_lazyi18n_string(p.label, event.settings.locales)
|
||||
if isinstance(p.label, LazyI18nString)
|
||||
else str(p.label)
|
||||
),
|
||||
"label": (localizable_to_dict(p.label, event.settings.locales)),
|
||||
"sample": str(context.render_sample(p)),
|
||||
"required_context": list(sorted(p.required_context)),
|
||||
}
|
||||
@@ -220,6 +209,8 @@ class LayoutPreviewView(EventPermissionRequiredMixin, View):
|
||||
layout.validate()
|
||||
|
||||
fname, mimet, data = layout.generate(p)
|
||||
if mimet == "text/uri-list":
|
||||
return redirect(data)
|
||||
resp = HttpResponse(data, content_type=mimet)
|
||||
ftype = fname.split(".")[-1]
|
||||
if not mimet.startswith("text/"):
|
||||
|
||||
Reference in new issue
Block a user