This commit is contained in:
Kara Engelhardt
2026-07-28 11:30:50 +02:00
parent 802a7b3a24
commit edd5f29524
11 changed files with 431 additions and 123 deletions
+202 -56
View File
@@ -1,40 +1,71 @@
from pretix.base.models import Event, OrderPosition
from .base import FieldGroupDisplay, PassLayout, PassStyle, PredefinedFieldGroup, TextFieldGroup, WalletPlatform
from .base import (
FieldGroupDisplay,
ImageFieldGroup,
PassLayout,
PassStyle,
PlaceholderFieldEntry,
PredefinedFieldGroup,
TextFieldGroup,
WalletPlatform,
)
from django.utils.translation import gettext_lazy as _, gettext
import json
from walletobjects import ButtonJWT, EventTicketClass, EventTicketObject
from walletobjects.comms import Comms
from walletobjects.constants import (
Barcode, ClassType, ConfirmationCode, DoorsOpen,
MultipleDevicesAndHoldersAllowedStatus, ObjectState, ObjectType,
ReviewStatus, Seat,
Barcode,
ClassType,
ConfirmationCode,
DoorsOpen,
MultipleDevicesAndHoldersAllowedStatus,
ObjectState,
ObjectType,
ReviewStatus,
Seat,
)
from pretix.base.settings import GlobalSettingsObject
import uuid
from pretix.multidomain.urlreverse import eventreverse_absolute
from django.utils import translation
from django.conf import settings
def _get_instance_uuid():
gs = GlobalSettingsObject()
if not gs.settings.wallet_google_instance_uuid:
gs.settings.wallet_google_instance_uuid = str(uuid.uuid4())
return gs.settings.get("wallet_google_instance_uuid")
return gs.settings.wallet_google_instance_uuid
def get_class_id(event: Event):
# TODO: add layout id somewhere
instance_uuid = _get_instance_uuid()
issuer_id = event.settings.get('wallet_google_issuer_id')
return "%s.pretix-%s-%s-%s" % (issuer_id, instance_uuid, event.organizer.slug, event.slug)
issuer_id = event.settings.get("wallet_google_issuer_id")
return "%s.pretix-%s-%s-%s" % (
issuer_id,
instance_uuid,
event.organizer.slug,
event.slug,
)
def get_object_id(op: OrderPosition):
instance_uuid = _get_instance_uuid()
issuer_id = op.order.event.settings.get('wallet_google_issuer_id')
issuer_id = op.order.event.settings.get("wallet_google_issuer_id")
return "%s.pretix-%s-%s-%s-%s-%s" % (
issuer_id,
instance_uuid,
op.order.event.organizer.slug,
op.order.event.slug,
op.order.code,
op.positionid,
)
return "%s.pretix-%s-%s-%s-%s-%s" % (issuer_id, instance_uuid,
op.order.event.organizer.slug, op.order.event.slug,
op.order.code, op.positionid)
def get_translated_dict(string, locales):
translated = {}
@@ -54,35 +85,69 @@ def get_translated_string(string, locale):
return translated
class GooglePlatform(WalletPlatform):
identifier = "google"
name = _("Google")
@classmethod
def generate(cls, layout: PassLayout, op: OrderPosition):
class_object = cls._generate_class(op.order.event)
from ..views import get_layout_variables
# ticket_object = cls._get_object(op)
order = op.order
event = order.event
# if not ticket_object:
# return False
context = {
"placeholders": get_layout_variables(event), # TODO: move to higher class
"evaluation_context": [op, order, event],
"credentials": event.settings.get("wallet_google_credentials").read(),
"locale": event.settings.locale,
"locales": event.settings.locales,
"issuerName": event.organizer.name,
"eventName": event.name,
"classId": get_class_id(event),
"objectId": get_object_id(op),
"homepageUrl": eventreverse_absolute(event, "presale:event.index"),
# TODO: add webhook view & register in pass
"webhookUrl": "", # eventreverse_absolute(event.organizer,"plugins:wallet:google_webhook",))
}
# generated_jwt = self._comms().sign_jwt(
# ButtonJWT(
# origins=[django_settings.SITE_URL],
# issuer=self._comms().client_email,
# event_ticket_objects=[ticket_object],
# skinny=True
# )
# )
data = layout.generate(context)
return "pass", "text/plain", data
# if generated_jwt:
# return 'googlepaypass', 'text/uri-list', 'https://pay.google.com/gp/v/save/%s' % generated_jwt
# else:
# return False
data = {"class": class_object}
return 'pass.json', 'application/json', json.dumps(data)
class GoogleWalletStyle(PassStyle):
platform = GooglePlatform
@classmethod
def _comms(cls, event: Event):
return Comms()
def _generate_class(self, layout, context, fields):
raise NotImplementedError()
def _generate_object(self, layout, context, fields):
raise NotImplementedError()
def generate(self, layout, context):
comms = Comms(context["credentials"])
fields = self.get_pass_fields(layout, context)
class_object = self._generate_class(layout, context, fields)
ticket_object = self._generate_object(layout, context, fields)
generated_jwt = comms.sign_jwt(
ButtonJWT(
origins=[settings.SITE_URL],
issuer=comms.client_email,
event_ticket_classes=[class_object],
event_ticket_objects=[ticket_object],
skinny=False,
)
)
return "https://pay.google.com/gp/v/save/%s" % generated_jwt
# from ..views import get_layout_variables
# order = op.order
@@ -120,27 +185,84 @@ class GooglePlatform(WalletPlatform):
# data = layout.generate(context)
# return filename, "application/vnd.apple.pkpass", data
@classmethod
def _generate_class(cls, event: Event):
class_name = get_class_id(event)
class GoogleWalletEventTicket(GoogleWalletStyle):
identifier = "event"
name = "Event Ticket"
fieldgroups = [
ImageFieldGroup(
identifier="logo",
name=_("Logo"),
min_entries=0,
max_entries=1,
default_entries=[
PlaceholderFieldEntry(
content="poweredby",
)
],
),
PredefinedFieldGroup(identifier="seating", name=_("Seating")),
TextFieldGroup(
identifier="code",
name=_("QR-Code"),
max_entries=1,
display=FieldGroupDisplay.CODE,
default_entries=[
PlaceholderFieldEntry(
content="secret",
)
],
),
]
preview_layout = [
[
{
"children": [
{"fieldgroup": "logo", "relSize": 1},
{
"value": "issuerName",
"relSize": 3,
"display": ["large", "centered"],
},
]
},
{
"children": [
{"value": "venueName", "display": "small"},
{"value": "eventName", "display": "large"},
],
"direction": "column",
},
{
"children": [
{"value": "01/01/1970", "label": "Date"},
{"value": "12:34", "label": "Time"},
]
},
{"fieldgroup": "seating"},
{"fieldgroup": "code"},
]
]
def _generate_class(self, layout: PassLayout, context, fields):
output_class = EventTicketClass(
event.organizer.name,
class_name,
context["issuerName"],
context["classId"],
MultipleDevicesAndHoldersAllowedStatus.multipleHolders, # TODO: Make configurable
event.name,
ReviewStatus.draft, # TODO: ReviewStatus.underReview,
event.settings.locale
context["eventName"],
ReviewStatus.underReview,
context["locale"],
)
output_class.homepage_uri(
eventreverse_absolute(event, 'presale:event.index'),
get_translated_string('Website', event.settings.locale),
get_translated_dict('Website', event.settings.locales)
context["homepageUrl"],
get_translated_string("Website", context["locale"]),
get_translated_dict("Website", context["locales"]),
)
# TODO: add webhook view & register in pass
# output_class.callback_url(context['webhookUrl'])
# output_class.callback_url(eventreverse_absolute(event.organizer, 'plugins:pretix_googlepaypasses:webhook'))
# TODO: move to pass settings or set defaults
# if (event.settings.get('ticketoutput_googlepaypasses_latitude')
# and event.settings.get('ticketoutput_googlepaypasses_longitude')):
# output_class.locations(
@@ -153,9 +275,7 @@ class GooglePlatform(WalletPlatform):
# event.geo_lon
# )
output_class.country_code(event.settings.locale)
output_class.hide_barcode(False)
# output_class.country_code(event.settings.locale)
# if event.settings.get('ticketoutput_googlepaypasses_hero'):
# output_class.hero_image(
@@ -205,17 +325,43 @@ class GooglePlatform(WalletPlatform):
# return self._comms().put_item(ClassType.eventTicketClass, class_name, output_class)
return output_class._eventTicketClass
def _generate_object(self, layout: PassLayout, context, fields):
class_id = context["classId"]
object_id = context["objectId"]
output_object = EventTicketObject(
object_id, class_id, ObjectState.active, context["locale"]
)
class GoogleWalletStyle(PassStyle):
platform = GooglePlatform
# output_object.barcode(Barcode.qrCode, op.secret, op.secret)
# output_object.reservation_info("%s-%s" % (op.order.event.slug, op.order.code))
# output_object.ticket_holder_name(op.attendee_name or (op.addon_to.attendee_name if op.addon_to else ''))
# output_object.ticket_number(op.secret)
# output_object.ticket_type(
# get_translated_dict(
# str(op.item) + (" " + str(op.variation.value) if op.variation else ""),
# op.order.event.settings.get('locales')
# )
# )
class GoogleWalletEventTicket(PassStyle):
identifier = "event"
name = "Event Ticket"
platform = GooglePlatform
fieldgroups = [
PredefinedFieldGroup(identifier="seating", name=_("Seating")),
TextFieldGroup(identifier="qrcode", name=_("QR-Code"), display=FieldGroupDisplay.PLAIN),
]
preview_layout = None
# places = django_settings.CURRENCY_PLACES.get(op.order.event.currency, 2)
# output_object.face_value(int(op.price * 1000 ** places), op.order.event.currency)
# if op.order.event.seating_plan_id is not None:
# if op.seat:
# output_object.seat(
# get_translated_dict(
# _(str(op.seat)),
# op.order.event.settings.get('locales')
# )
# )
# else:
# output_object.seat(
# get_translated_dict(
# _('General admission'),
# op.order.event.settings.get('locales')
# )
# )
# return self._comms().put_item(ObjectType.eventTicketObject, object_name, output_object)
return output_object._eventTicketObject