mirror of
https://github.com/pretix/pretix.git
synced 2026-11-11 01:27:08 +00:00
WalletPlatform.{is_enabled,is_available,is_available_for_position}; cleanups; refactoring
This commit is contained in:
1 parent
97a448f1d0
commit
8ef6fccf21
18 files changed
+245
-132
No files matched your search
@@ -297,12 +297,20 @@ def get_download_buttons(event, show_disabled):
|
||||
return buttons
|
||||
|
||||
|
||||
def get_wallet_buttons(event):
|
||||
# TODO(wallet): implement this
|
||||
from pretix.plugins.wallet.platforms import AVAILABLE_PLATFORMS
|
||||
buttons = []
|
||||
for p in AVAILABLE_PLATFORMS:
|
||||
platform = p(event)
|
||||
attr, cont = platform.render_download_button()
|
||||
buttons.append({"identifier": platform.identifier, "attributes": attr, "content": cont})
|
||||
def get_wallet_buttons(event: Event, positions: list[OrderPosition], show_disabled: bool):
|
||||
from pretix.plugins.wallet.platforms import get_available_platforms
|
||||
platforms = get_available_platforms(event, show_disabled)
|
||||
|
||||
buttons = {"combined": []}
|
||||
for platform in platforms:
|
||||
if any(platform.is_available_for_position(pos) for pos in positions):
|
||||
attr, cont = platform.render_download_button()
|
||||
buttons["combined"].append({"identifier": platform.identifier, "attributes": attr, "content": cont})
|
||||
|
||||
for position in positions:
|
||||
buttons[position] = []
|
||||
for platform in platforms:
|
||||
if platform.is_available_for_position(position):
|
||||
attr, cont = platform.render_download_button()
|
||||
buttons[position].append({"identifier": platform.identifier, "attributes": attr, "content": cont})
|
||||
return buttons
|
||||
@@ -575,7 +575,7 @@
|
||||
</button>
|
||||
</form>
|
||||
{% endfor %}
|
||||
{% for b in wallet_buttons %}
|
||||
{% for b in wallet_buttons|getitem:line %}
|
||||
{% comment %} TODO(wallet): move view to core {% endcomment %}
|
||||
<form action="{% url "plugins:wallet:event.order.wallet.get" code=order.code event=request.event.slug organizer=request.event.organizer.slug position=line.pk platform=b.identifier %}"
|
||||
method="post" data-asynctask data-asynctask-download
|
||||
|
||||
@@ -544,7 +544,8 @@ class OrderDetail(OrderView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['items'] = self.get_items()
|
||||
items = self.get_items()
|
||||
ctx['items'] = items
|
||||
ctx['has_cancellation_fee'] = any(f.fee_type == OrderFee.FEE_TYPE_CANCELLATION for f in ctx['items']['fees'])
|
||||
ctx['event'] = self.request.event
|
||||
ctx['payments'] = self.order.payments.order_by('-created')
|
||||
@@ -566,8 +567,8 @@ class OrderDetail(OrderView):
|
||||
|
||||
pending_sum = self.order.pending_sum
|
||||
ctx['overpaid'] = pending_sum * -1
|
||||
ctx['download_buttons'] = self.download_buttons
|
||||
ctx['wallet_buttons'] = self.wallet_buttons
|
||||
ctx['download_buttons'] = get_download_buttons(self.request.event, show_disabled=True)
|
||||
ctx['wallet_buttons'] = get_wallet_buttons(self.request.event, items['positions'], show_disabled=True)
|
||||
ctx['payment_refund_sum'] = self.order.payment_refund_sum
|
||||
ctx['pending_sum'] = pending_sum
|
||||
ctx['uncancelled_invoice'] = self.order.invoices.exclude(
|
||||
@@ -576,14 +577,6 @@ class OrderDetail(OrderView):
|
||||
|
||||
return ctx
|
||||
|
||||
@cached_property
|
||||
def download_buttons(self):
|
||||
return get_download_buttons(self.request.event, show_disabled=True)
|
||||
|
||||
@cached_property
|
||||
def wallet_buttons(self):
|
||||
return get_wallet_buttons(self.request.event)
|
||||
|
||||
def get_items(self):
|
||||
queryset = self.object.all_positions
|
||||
|
||||
|
||||
@@ -8,13 +8,11 @@ from rest_framework import serializers, viewsets
|
||||
from pretix.api.serializers.i18n import I18nAwareModelSerializer
|
||||
|
||||
from .models import WalletLayout, WalletPlatformLayout
|
||||
from .platforms import AVAILABLE_PLATFORMS, AVAILABLE_STYLES_DICT
|
||||
from .platforms import get_available_platforms, AVAILABLE_STYLES_DICT
|
||||
|
||||
|
||||
class WalletPlatformLayoutSerializer(I18nAwareModelSerializer):
|
||||
platform = serializers.ChoiceField(
|
||||
choices=[p.identifier for p in AVAILABLE_PLATFORMS]
|
||||
)
|
||||
platform = serializers.ChoiceField(choices=[])
|
||||
style = serializers.CharField(allow_null=True, required=False)
|
||||
file_settings = serializers.JSONField(default=dict, required=False)
|
||||
|
||||
@@ -22,6 +20,13 @@ class WalletPlatformLayoutSerializer(I18nAwareModelSerializer):
|
||||
model = WalletPlatformLayout
|
||||
fields = ("platform", "style", "layout", "file_settings")
|
||||
|
||||
def get_fields(self):
|
||||
fields = super().get_fields()
|
||||
fields["platform"].choices = [
|
||||
p.identifier for p in get_available_platforms(self.context["event"], show_disabled=True)
|
||||
]
|
||||
return fields
|
||||
|
||||
def validate_layout(self, value):
|
||||
if not isinstance(value, dict):
|
||||
raise ValidationError(_("Layout must be a dict"))
|
||||
@@ -70,9 +75,6 @@ class WalletLayoutSerializer(I18nAwareModelSerializer):
|
||||
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"])
|
||||
|
||||
|
||||
@@ -54,6 +54,9 @@ class WalletLayout(LoggedModel):
|
||||
"name",
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class WalletPlatformLayout(LoggedModel):
|
||||
parent = models.ForeignKey(
|
||||
|
||||
@@ -6,7 +6,18 @@ from .google import (
|
||||
GoogleWalletDefaultEventTicket,
|
||||
)
|
||||
|
||||
AVAILABLE_PLATFORMS: list[type[WalletPlatform]] = [ApplePlatform, GooglePlatform]
|
||||
# TODO(wallet): replace with registry or similar
|
||||
PLATFORMS: list[type[WalletPlatform]] = [ApplePlatform, GooglePlatform]
|
||||
|
||||
|
||||
def get_available_platforms(event, show_disabled: bool = False):
|
||||
platforms = []
|
||||
for p in PLATFORMS:
|
||||
platform = p(event)
|
||||
if platform.is_available()[0] and (show_disabled or platform.is_enabled()):
|
||||
platforms.append(platform)
|
||||
return platforms
|
||||
|
||||
|
||||
AVAILABLE_STYLES: dict[str, list[type[PassStyle]]] = {
|
||||
"apple": [AppleWalletEventTicket],
|
||||
@@ -23,7 +34,7 @@ def get_style(platform: str, identifier: str) -> type[PassStyle] | None:
|
||||
|
||||
|
||||
__all__ = [
|
||||
"AVAILABLE_PLATFORMS",
|
||||
"get_available_platforms",
|
||||
"AVAILABLE_STYLES",
|
||||
"PassStyle",
|
||||
"WalletPlatform",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from collections import OrderedDict
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
@@ -24,7 +25,6 @@ from ...models import CombinedAppleWalletPass
|
||||
|
||||
from django.core.files.base import ContentFile
|
||||
from django import forms
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from pretix.base.forms import SecretKeySettingsField
|
||||
from pretix.control.forms import ClearableBasenameFileInput
|
||||
@@ -101,24 +101,43 @@ class ApplePlatform(WalletPlatform):
|
||||
|
||||
def generate(self, position, layout=None) -> str:
|
||||
if not layout:
|
||||
layout = self.get_layout(position).pass_layout
|
||||
pass_obj = layout.generate(position)
|
||||
op_layout = self.get_layout(position)
|
||||
if not op_layout:
|
||||
raise ValueError(
|
||||
_(
|
||||
"WalletPlatform.generate called without layout and without position layout"
|
||||
)
|
||||
)
|
||||
layout = op_layout.pass_layout
|
||||
|
||||
layout.generate(position)
|
||||
|
||||
return eventreverse_absolute(
|
||||
self.event,
|
||||
"plugins:wallet:apple_download",
|
||||
kwargs={"order": position.order.code, "secret": position.order.secret, "position": position.pk},
|
||||
kwargs={
|
||||
"order": position.order.code,
|
||||
"secret": position.order.secret,
|
||||
"position": position.pk,
|
||||
},
|
||||
)
|
||||
|
||||
def generate_order(self, order: Order):
|
||||
with tempfile.TemporaryFile() as d:
|
||||
with zipfile.ZipFile(d, "w") as zipf:
|
||||
for pos in order.positions_with_tickets:
|
||||
pass_obj = self.get_layout(pos).pass_layout.generate(pos)
|
||||
zipf.writestr(
|
||||
"{}-{}.pkpass".format(order.code, pos.positionid),
|
||||
pass_obj.content.read(),
|
||||
)
|
||||
if self.is_available_for_position(pos):
|
||||
layout = self.get_layout(pos)
|
||||
if not layout:
|
||||
raise ValueError(
|
||||
"Impossible: is_available_for_position==True but no layout"
|
||||
)
|
||||
|
||||
pass_obj = layout.pass_layout.generate(pos)
|
||||
zipf.writestr(
|
||||
"{}-{}.pkpass".format(order.code, pos.positionid),
|
||||
pass_obj.content.read(),
|
||||
)
|
||||
|
||||
d.seek(0)
|
||||
data = d.read()
|
||||
@@ -130,7 +149,9 @@ class ApplePlatform(WalletPlatform):
|
||||
order=order,
|
||||
file=None,
|
||||
)
|
||||
ct.file.save("{}-{}.pkpasses".format(order.code, self.identifier), ContentFile(data))
|
||||
ct.file.save(
|
||||
"{}-{}.pkpasses".format(order.code, self.identifier), ContentFile(data)
|
||||
)
|
||||
|
||||
return eventreverse_absolute(
|
||||
self.event,
|
||||
@@ -138,6 +159,19 @@ class ApplePlatform(WalletPlatform):
|
||||
kwargs={"order": order.code, "secret": order.secret},
|
||||
)
|
||||
|
||||
def is_available(self):
|
||||
for field in [
|
||||
"wallet_apple_team_id",
|
||||
"wallet_apple_pass_type_id",
|
||||
"wallet_apple_certificate",
|
||||
"wallet_apple_ca_certificate",
|
||||
"wallet_apple_key",
|
||||
]:
|
||||
if not self.event.settings.get(field):
|
||||
return (False, _("Missing field: {field}").format(field=field))
|
||||
return super().is_available()
|
||||
|
||||
|
||||
class StringResource:
|
||||
entries: dict[str, MaybeTranslatedString]
|
||||
locales: set[str]
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import enum
|
||||
from functools import cached_property
|
||||
from typing import Iterable, TypedDict
|
||||
from typing import TypedDict
|
||||
|
||||
import jsonschema
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.files import File
|
||||
from django.shortcuts import get_object_or_404
|
||||
from i18nfield.strings import LazyI18nString
|
||||
|
||||
from pretix.api.helpers import handle_file_upload
|
||||
from pretix.base.models import OrderPosition
|
||||
from pretix.base.models.orders import Order
|
||||
from pretix.base.settings import SettingsSandbox
|
||||
from pretix.plugins.wallet.models import WalletPlatformLayout
|
||||
|
||||
from ..i18n import MaybeTranslatedString, localizable_to_dict
|
||||
@@ -24,10 +24,27 @@ from .settings import SettingsField, SettingsFile
|
||||
from django.utils.translation import gettext as _
|
||||
from django.utils.html import format_html
|
||||
|
||||
|
||||
class WalletPlatform:
|
||||
identifier: str
|
||||
name: str
|
||||
|
||||
# TODO: register urls here?
|
||||
def __init__(self, event):
|
||||
self.event = event
|
||||
self.wallet_settings = SettingsSandbox('ticketoutput', 'wallet', event)
|
||||
|
||||
def is_enabled(self):
|
||||
if not self.wallet_settings.get('_enabled', as_type=bool):
|
||||
return False
|
||||
return True
|
||||
|
||||
def is_available(self) -> tuple[bool, str | None]:
|
||||
return (True, None)
|
||||
|
||||
def is_available_for_position(self, op: OrderPosition):
|
||||
return op.generate_ticket and bool(self.get_layout(op))
|
||||
|
||||
@classmethod
|
||||
def get_global_settings(cls, **kwargs):
|
||||
"""
|
||||
@@ -43,19 +60,19 @@ class WalletPlatform:
|
||||
attr: the html attributes that should be added to the button element as a dict[str, str] or None
|
||||
content: the content of the button
|
||||
"""
|
||||
return None, format_html('<span class="fa fa-download"></span> {}', _('Add to {wallet}').format(wallet=self.name))
|
||||
return None, format_html(
|
||||
'<span class="fa fa-download"></span> {}',
|
||||
_("Add to {wallet}").format(wallet=self.name),
|
||||
)
|
||||
|
||||
def get_layout(self, op):
|
||||
def get_layout(self, op) -> WalletPlatformLayout | None:
|
||||
if hasattr(op.item, "walletlayout"):
|
||||
wallet_layout = op.item.walletlayout.layout
|
||||
else:
|
||||
wallet_layout = op.event.wallet_layouts.get(default=True)
|
||||
platform_layout: WalletPlatformLayout = get_object_or_404(
|
||||
wallet_layout.platform_layouts, platform=self.identifier
|
||||
)
|
||||
return platform_layout
|
||||
return wallet_layout.platform_layouts.filter(platform=self.identifier).first()
|
||||
|
||||
def generate(self, position, layout=None) -> str:
|
||||
def generate(self, position, layout: "PassStyle | None"=None) -> str:
|
||||
"""
|
||||
This method should generate the wallet pass and return a url the user should be sent
|
||||
to to retrieve that pass (e.g. via download or by redirecting to the wallet provider)
|
||||
@@ -64,7 +81,10 @@ class WalletPlatform:
|
||||
if the pass content has changed.
|
||||
"""
|
||||
if not layout:
|
||||
layout = self.get_layout(position).pass_layout
|
||||
op_layout = self.get_layout(position)
|
||||
if not op_layout:
|
||||
raise ValueError(_("WalletPlatform.generate called without layout and without position layout"))
|
||||
layout = op_layout.pass_layout
|
||||
return layout.generate(position)
|
||||
|
||||
def generate_order(self, order: Order) -> str:
|
||||
@@ -78,11 +98,6 @@ class WalletPlatform:
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
# TODO: register urls here?
|
||||
def __init__(self, event):
|
||||
self.event = event
|
||||
|
||||
# TODO: is_available : check if all config for platform is set
|
||||
|
||||
class LayoutContext(TypedDict):
|
||||
placeholders: dict[str, dict]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import json
|
||||
import uuid
|
||||
from functools import cached_property
|
||||
from typing import Iterable, OrderedDict
|
||||
from typing import OrderedDict
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
@@ -202,7 +202,11 @@ class GooglePlatform(WalletPlatform):
|
||||
def generate(self, position, layout: PassStyle | None = None):
|
||||
# TODO?: early-exit caching (if object for position exists and layout is None)
|
||||
if not layout:
|
||||
layout = self.get_layout(position).pass_layout
|
||||
op_layout = self.get_layout(position)
|
||||
if not op_layout:
|
||||
raise ValueError(_("WalletPlatform.generate called without layout and without position layout"))
|
||||
layout = op_layout.pass_layout
|
||||
|
||||
class_object, ticket_object = layout.generate(position)
|
||||
self.put_item_cached(class_object)
|
||||
|
||||
@@ -212,9 +216,11 @@ class GooglePlatform(WalletPlatform):
|
||||
class_objects = []
|
||||
ticket_objects = []
|
||||
for position in order.positions_with_tickets:
|
||||
class_object, ticket_object = self.get_layout(
|
||||
position
|
||||
).pass_layout.generate(position)
|
||||
layout = self.get_layout(position)
|
||||
if not layout:
|
||||
raise ValueError("Impossible: is_available_for_position==True but no layout")
|
||||
|
||||
class_object, ticket_object = layout.generate(position)
|
||||
class_objects.append(class_object)
|
||||
ticket_objects.append(ticket_object)
|
||||
|
||||
@@ -222,3 +228,9 @@ class GooglePlatform(WalletPlatform):
|
||||
self.put_item_cached(co)
|
||||
|
||||
return self._get_jwt(ticket_objects)
|
||||
|
||||
def is_available(self):
|
||||
for field in ["wallet_google_issuer_id", "wallet_google_credentials"]:
|
||||
if not self.event.settings.get(field):
|
||||
return (False, _("Missing field: {field}").format(field=field))
|
||||
return super().is_available()
|
||||
@@ -34,7 +34,7 @@ from pretix.control.signals import item_forms
|
||||
from .forms import WalletLayoutItemForm
|
||||
from .models import WalletLayoutItem
|
||||
from .ticketoutput import WalletSettingsHolder
|
||||
from .platforms import AVAILABLE_PLATFORMS
|
||||
from .platforms import PLATFORMS
|
||||
from .tasks import send_update_apns
|
||||
from .models import AppleWalletPass, CombinedAppleWalletPass
|
||||
from .tasks import get_pass
|
||||
@@ -42,13 +42,12 @@ from .models import GoogleWalletInstance
|
||||
from .platforms.google.google_wallet import Resource
|
||||
|
||||
|
||||
|
||||
@receiver(register_ticket_outputs, dispatch_uid="output_wallet")
|
||||
def register_output(sender, **kwargs):
|
||||
return WalletSettingsHolder
|
||||
|
||||
|
||||
for platform in AVAILABLE_PLATFORMS:
|
||||
for platform in PLATFORMS:
|
||||
if hasattr(platform, "get_global_settings"):
|
||||
register_global_settings.connect(
|
||||
platform.get_global_settings,
|
||||
@@ -74,12 +73,12 @@ def control_item_forms(sender, request, item, **kwargs):
|
||||
return forms
|
||||
|
||||
|
||||
|
||||
@receiver(periodic_task, dispatch_uid="wallet_periodic")
|
||||
def periodic_tasks(sender, **kwargs):
|
||||
# TODO: move to platform?
|
||||
send_update_apns.apply_async()
|
||||
|
||||
|
||||
# TODO: move to platform?
|
||||
@receiver(invalidate_ticket_cache, dispatch_uid="invalidate_ticket_cache_wallet_apple")
|
||||
def invalidate_apple(
|
||||
@@ -106,6 +105,7 @@ def invalidate_apple(
|
||||
"model": "orderposition",
|
||||
"pk": pass_obj.order_position_id,
|
||||
"platform_identifier": "apple",
|
||||
"show_disabled": False
|
||||
}
|
||||
)
|
||||
|
||||
@@ -113,7 +113,6 @@ def invalidate_apple(
|
||||
passes_obj.delete()
|
||||
|
||||
|
||||
|
||||
@receiver(invalidate_ticket_cache, dispatch_uid="invalidate_ticket_cache_wallet_google")
|
||||
def invalidate_google(
|
||||
sender: Event, item: int | None, provider: str | None, order: int | None, **kwargs
|
||||
@@ -137,5 +136,6 @@ def invalidate_google(
|
||||
"model": "orderposition",
|
||||
"pk": inst.order_position_id,
|
||||
"platform_identifier": "google",
|
||||
"show_disabled": False
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -5,9 +5,9 @@ import ssl
|
||||
import httpx
|
||||
import tempfile
|
||||
from django.utils.timezone import now
|
||||
from django.db.models import OuterRef, Q, Exists
|
||||
from django.db.models import OuterRef, Exists
|
||||
|
||||
from pretix.plugins.wallet.platforms import AVAILABLE_PLATFORMS
|
||||
from pretix.plugins.wallet.platforms import get_available_platforms
|
||||
from pretix.plugins.wallet.platforms.base import WalletPlatform
|
||||
from .models import AppleWalletPass, AppleWalletDevice
|
||||
from django_scopes import scopes_disabled
|
||||
@@ -73,17 +73,17 @@ def send_update_apns():
|
||||
device.save()
|
||||
|
||||
|
||||
def get_platform(event, platform) -> WalletPlatform | None:
|
||||
for p in AVAILABLE_PLATFORMS:
|
||||
def get_platform(event, platform, show_disabled) -> WalletPlatform | None:
|
||||
for p in get_available_platforms(event, show_disabled):
|
||||
if p.identifier == platform:
|
||||
return p(event)
|
||||
return p
|
||||
|
||||
|
||||
@app.task(base=ProfiledTask)
|
||||
def get_pass(model: str, pk: int, platform_identifier: str):
|
||||
def get_pass(model: str, pk: int, platform_identifier: str, show_disabled: bool):
|
||||
if model == "order":
|
||||
order = Order.objects.select_related("event").get(id=pk)
|
||||
platform = get_platform(order.event, platform_identifier)
|
||||
platform = get_platform(order.event, platform_identifier, show_disabled)
|
||||
if not platform:
|
||||
return
|
||||
return platform.generate_order(order)
|
||||
@@ -91,7 +91,7 @@ def get_pass(model: str, pk: int, platform_identifier: str):
|
||||
position = OrderPosition.objects.select_related("order", "order__event").get(
|
||||
id=pk
|
||||
)
|
||||
platform = get_platform(position.order.event, platform_identifier)
|
||||
platform = get_platform(position.order.event, platform_identifier, show_disabled)
|
||||
if not platform:
|
||||
return
|
||||
return platform.generate(position)
|
||||
@@ -23,7 +23,6 @@ from django.urls import re_path
|
||||
|
||||
from pretix.api.urls import event_router
|
||||
from pretix.plugins.wallet.views.google import CallbackView
|
||||
import pretix
|
||||
|
||||
from .api import WalletLayoutViewSet
|
||||
from .views.apple import (
|
||||
@@ -111,7 +110,7 @@ event_patterns = [
|
||||
name="google_webhook",
|
||||
),
|
||||
re_path(
|
||||
r"^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/download/(?P<position>[0-9]+)/(?P<platform>[^/]+)$",
|
||||
r"^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/wallet/download/(?P<position>[0-9]+)/(?P<platform>[^/]+)$",
|
||||
PresaleWalletGetPass.as_view(),
|
||||
name="presale_event.order.wallet.get",
|
||||
),
|
||||
@@ -126,12 +125,12 @@ event_patterns = [
|
||||
name="apple_download_combined",
|
||||
),
|
||||
re_path(
|
||||
r"^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/download/(?P<platform>[^/]+)$",
|
||||
r"^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/wallet/download/(?P<platform>[^/]+)$",
|
||||
PresaleWalletGetPass.as_view(),
|
||||
name="presale_event.order.wallet.get.combined",
|
||||
),
|
||||
re_path(
|
||||
r"^ticket/(?P<order>[^/]+)/(?P<position>\d+)/(?P<secret>[A-Za-z0-9]+)/download/(?P<pid>[0-9]+)/(?P<platform>[^/]+)$",
|
||||
r"^ticket/(?P<order>[^/]+)/(?P<position>\d+)/(?P<secret>[A-Za-z0-9]+)/wallet/download/(?P<pid>[0-9]+)/(?P<platform>[^/]+)$",
|
||||
PresaleWalletGetPassPosition.as_view(),
|
||||
name="presale_event.order.position.wallet.get",
|
||||
),
|
||||
|
||||
@@ -38,7 +38,7 @@ from ..placeholders import (
|
||||
get_wallet_placeholders,
|
||||
)
|
||||
from ..platforms import (
|
||||
AVAILABLE_PLATFORMS,
|
||||
get_available_platforms,
|
||||
AVAILABLE_STYLES,
|
||||
AVAILABLE_STYLES_DICT,
|
||||
)
|
||||
@@ -97,7 +97,9 @@ class LayoutEditorView(LayoutDetailView):
|
||||
for style in AVAILABLE_STYLES.get(platform.identifier, [])
|
||||
},
|
||||
}
|
||||
for platform in AVAILABLE_PLATFORMS
|
||||
for platform in get_available_platforms(
|
||||
self.request.event, show_disabled=True
|
||||
)
|
||||
]
|
||||
context["placeholders"] = get_editor_placeholders(self.request.event)
|
||||
context["locales"] = {
|
||||
@@ -196,9 +198,10 @@ class LayoutPreviewView(EventPermissionRequiredMixin, View):
|
||||
base_file_settings = {}
|
||||
|
||||
platform = None
|
||||
for p in AVAILABLE_PLATFORMS:
|
||||
for p in get_available_platforms(event, show_disabled=True):
|
||||
if p.identifier == platform_id:
|
||||
platform = p(event)
|
||||
platform = p
|
||||
|
||||
if not platform:
|
||||
raise BadRequest("Unknown platform")
|
||||
if style_id not in AVAILABLE_STYLES_DICT[platform_id]:
|
||||
@@ -280,6 +283,7 @@ class LayoutDelete(WalletLayoutMixin, DeleteView):
|
||||
messages.success(self.request, _("The selected layout been deleted."))
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
|
||||
class OrderDownloadMixin:
|
||||
task = get_pass
|
||||
|
||||
@@ -291,9 +295,9 @@ class OrderDownloadMixin:
|
||||
|
||||
@cached_property
|
||||
def platform(self):
|
||||
for p in AVAILABLE_PLATFORMS:
|
||||
for p in get_available_platforms(self.request.event, show_disabled=True):
|
||||
if p.identifier == self.kwargs.get("platform"):
|
||||
return p(self.request.event)
|
||||
return p
|
||||
|
||||
|
||||
class WalletGetView(OrderDownloadMixin, AsyncAction, OrderView):
|
||||
@@ -318,10 +322,10 @@ class WalletGetView(OrderDownloadMixin, AsyncAction, OrderView):
|
||||
)
|
||||
|
||||
if "position" in kwargs:
|
||||
if not self.order_position.generate_ticket:
|
||||
if not self.platform.is_available_for_position(self.order_position):
|
||||
return self.error(_("Ticket download is not enabled for this product."))
|
||||
return self.do(
|
||||
"orderposition", self.order_position.pk, self.platform.identifier
|
||||
"orderposition", self.order_position.pk, self.platform.identifier, True
|
||||
)
|
||||
else:
|
||||
return self.do("order", self.order.pk, self.platform.identifier)
|
||||
return self.do("order", self.order.pk, self.platform.identifier, True)
|
||||
@@ -16,13 +16,26 @@ from pretix.presale.views import EventViewMixin
|
||||
from pretix.presale.views.order import (
|
||||
OrderDetailMixin, OrderPositionDetailMixin,
|
||||
)
|
||||
|
||||
from .control import OrderDownloadMixin
|
||||
from pretix.plugins.wallet.platforms import get_available_platforms
|
||||
from pretix.plugins.wallet.tasks import get_pass
|
||||
|
||||
|
||||
class PresaleOrderDownloadMixin(OrderDownloadMixin):
|
||||
class OrderDownloadMixin:
|
||||
task = get_pass
|
||||
known_errortypes = ['OrderError']
|
||||
|
||||
def get_success_message(self, value):
|
||||
return ""
|
||||
|
||||
def get_success_url(self, value):
|
||||
return value
|
||||
|
||||
@cached_property
|
||||
def platform(self):
|
||||
for p in get_available_platforms(self.request.event, show_disabled=False):
|
||||
if p.identifier == self.kwargs.get("platform"):
|
||||
return p
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
# TODO: check if available if is_available is implemented (or check in super.platform?)
|
||||
if not self.platform:
|
||||
@@ -43,17 +56,17 @@ class PresaleOrderDownloadMixin(OrderDownloadMixin):
|
||||
return self.error(OrderError(_('Please click the link we sent you via email to download your tickets.')))
|
||||
|
||||
if "position" in kwargs:
|
||||
if not self.order_position.generate_ticket:
|
||||
return self.error(_("Ticket download is not enabled for this product."))
|
||||
if not self.platform.is_available_for_position(self.order_position):
|
||||
return self.error(OrderError(_("Ticket download is not enabled for this product.")))
|
||||
return self.do(
|
||||
"orderposition", self.order_position.pk, self.platform.identifier
|
||||
"orderposition", self.order_position.pk, self.platform.identifier, False
|
||||
)
|
||||
else:
|
||||
return self.do("order", self.order.pk, self.platform.identifier)
|
||||
return self.do("order", self.order.pk, self.platform.identifier, False)
|
||||
|
||||
|
||||
@method_decorator(xframe_options_exempt, 'dispatch')
|
||||
class PresaleWalletGetPass(PresaleOrderDownloadMixin, EventViewMixin, OrderDetailMixin, AsyncAction, View):
|
||||
class PresaleWalletGetPass(OrderDownloadMixin, EventViewMixin, OrderDetailMixin, AsyncAction, View):
|
||||
def get_error_url(self):
|
||||
return self.get_order_url()
|
||||
|
||||
@@ -72,7 +85,7 @@ class PresaleWalletGetPass(PresaleOrderDownloadMixin, EventViewMixin, OrderDetai
|
||||
|
||||
|
||||
@method_decorator(xframe_options_exempt, 'dispatch')
|
||||
class PresaleWalletGetPassPosition(PresaleOrderDownloadMixin, EventViewMixin, OrderPositionDetailMixin, AsyncAction, View):
|
||||
class PresaleWalletGetPassPosition(OrderDownloadMixin, EventViewMixin, OrderPositionDetailMixin, AsyncAction, View):
|
||||
def get_error_url(self):
|
||||
return self.get_position_url()
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
{% load safelink %}
|
||||
{% load rich_text %}
|
||||
{% load money %}
|
||||
{% load getitem %}
|
||||
|
||||
{% blocktrans asvar s_taxes %}taxes{% endblocktrans %}
|
||||
<div role="table" aria-label="{% trans "Your cart" %}" aria-describedby="{% if cart.is_ordered %}cart-description{% else%}cart-deadline{% endif %}" aria-rowcount="{{ cart.positions|length|add:2 }}">
|
||||
<div class="sr-only" role="rowgroup">
|
||||
@@ -220,36 +222,7 @@
|
||||
{% if download %}
|
||||
<div role="cell" class="download-desktop">
|
||||
{% if line in tickets_with_download %}
|
||||
{% for b in download_buttons %}
|
||||
<form action="{% if position_page and line.addon_to %}{% eventurl event "presale:event.order.position.download" secret=line.addon_to.web_secret order=order.code output=b.identifier pid=line.pk position=line.addon_to.positionid %}{% elif position_page %}{% eventurl event "presale:event.order.position.download" secret=line.web_secret order=order.code output=b.identifier pid=line.pk position=line.positionid %}{% else %}{% eventurl event "presale:event.order.download" secret=order.secret order=order.code output=b.identifier position=line.pk %}{% endif %}"
|
||||
method="post" data-asynctask data-asynctask-download class="download-btn-form{% if b.javascript_required or b.confirmation_text %} requirejs{% endif %}">
|
||||
{% csrf_token %}
|
||||
<button type="submit"
|
||||
class="btn btn-sm {% if b.identifier == "pdf" %}btn-primary{% else %}btn-default{% endif %}"
|
||||
{% if b.confirmation_text %}data-confirmation-text="{{ b.confirmation_text|rich_text|force_escape }}"{% endif %}>
|
||||
<span class="fa {{ b.icon }}" aria-hidden="true"></span> {{ b.text }}
|
||||
</button>
|
||||
</form>
|
||||
{% endfor %}
|
||||
{% for b in wallet_buttons %}
|
||||
{% comment %} TODO(wallet): move view to core {% endcomment %}
|
||||
<form
|
||||
action="{% if position_page and line.addon_to %}
|
||||
{% eventurl event "plugins:wallet:presale_event.order.position.wallet.get" secret=line.addon_to.web_secret order=order.code platform=b.identifier pid=line.pk position=line.addon_to.positionid %}
|
||||
{% elif position_page %}
|
||||
{% eventurl event "plugins:wallet:presale_event.order.position.wallet.get" secret=line.web_secret order=order.code platform=b.identifier pid=line.pk position=line.positionid %}
|
||||
{% else %}
|
||||
{% eventurl event "plugins:wallet:presale_event.order.wallet.get" secret=order.secret order=order.code platform=b.identifier position=line.pk %}
|
||||
{% endif %}"
|
||||
method="post" data-asynctask data-asynctask-download class="download-btn-form">
|
||||
{% csrf_token %}
|
||||
<button type="submit"
|
||||
class="btn btn-sm btn-default {{ b.attributes.class }}"
|
||||
{% include "pretixbase/fragments/attributes.html" with attributes=b.attributes %}>
|
||||
{{ b.content }}
|
||||
</button>
|
||||
</form>
|
||||
{% endfor %}
|
||||
{% include "pretixpresale/event/fragment_item_downloads.html" with line=line %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% elif line.addon_to %}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
{% load i18n %}
|
||||
{% load eventurl %}
|
||||
{% load rich_text %}
|
||||
{% load getitem %}
|
||||
|
||||
{% if download_email_required %}
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
@@ -55,7 +57,7 @@
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% for b in wallet_buttons %}
|
||||
{% for b in wallet_buttons.combined %}
|
||||
{# TODO(wallet): move view to core #}
|
||||
<form action="{% eventurl event "plugins:wallet:presale_event.order.wallet.get.combined" secret=order.secret order=order.code platform=b.identifier %}"
|
||||
method="post" data-asynctask data-asynctask-download class="download-btn-form">
|
||||
@@ -75,7 +77,7 @@
|
||||
</p>
|
||||
{% elif tickets_with_download|length == 1 %}
|
||||
<div>
|
||||
{% for b in download_buttons %}
|
||||
{% comment %} {% for b in download_buttons %}
|
||||
<form action="{% if position_page and tickets_with_download.0.addon_to %}{% eventurl event "presale:event.order.position.download" secret=tickets_with_download.0.addon_to.web_secret order=order.code output=b.identifier pid=tickets_with_download.0.pk position=tickets_with_download.0.addon_to.positionid %}{% elif position_page %}{% eventurl event "presale:event.order.position.download" secret=tickets_with_download.0.web_secret order=order.code output=b.identifier pid=tickets_with_download.0.pk position=tickets_with_download.0.positionid %}{% else %}{% eventurl event "presale:event.order.download" secret=order.secret order=order.code output=b.identifier position=tickets_with_download.0.pk %}{% endif %}"
|
||||
method="post" data-asynctask data-asynctask-download
|
||||
class="download-btn-form {% if b.javascript_required or b.confirmation_text %}requirejs{% endif %}">
|
||||
@@ -87,8 +89,10 @@
|
||||
</button>
|
||||
</form>
|
||||
{% endfor %}
|
||||
{% for b in wallet_buttons %}
|
||||
{% comment %} TODO(wallet): move view to core {% endcomment %}
|
||||
{% for b in wallet_buttons|getitem:tickets_with_download.0 %}
|
||||
TODO(wallet): move view to core {% endcomment %}
|
||||
{% include "pretixpresale/event/fragment_item_downloads.html" with line=tickets_with_download.0 button_size="lg" download_text="long_text" %}
|
||||
{% comment %}
|
||||
<form
|
||||
action="{% if position_page and tickets_with_download.0.addon_to %}
|
||||
{% eventurl event "plugins:wallet:presale_event.order.position.wallet.get" secret=tickets_with_download.0.addon_to.web_secret order=order.code platform=b.identifier pid=tickets_with_download.0.pk position=tickets_with_download.0.addon_to.positionid %}
|
||||
@@ -105,7 +109,7 @@
|
||||
{{ b.content }}
|
||||
</button>
|
||||
</form>
|
||||
{% endfor %}
|
||||
{% endfor %} {% endcomment %}
|
||||
</div>
|
||||
<p class="help-block">
|
||||
{% blocktrans trimmed %}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
{% load i18n %}
|
||||
{% load icon %}
|
||||
{% load eventurl %}
|
||||
{% load daterange %}
|
||||
{% load safelink %}
|
||||
{% load rich_text %}
|
||||
{% load money %}
|
||||
{% load getitem %}
|
||||
|
||||
{% for b in download_buttons %}
|
||||
<form action="{% if position_page and line.addon_to %}{% eventurl event "presale:event.order.position.download" secret=line.addon_to.web_secret order=order.code output=b.identifier pid=line.pk position=line.addon_to.positionid %}{% elif position_page %}{% eventurl event "presale:event.order.position.download" secret=line.web_secret order=order.code output=b.identifier pid=line.pk position=line.positionid %}{% else %}{% eventurl event "presale:event.order.download" secret=order.secret order=order.code output=b.identifier position=line.pk %}{% endif %}"
|
||||
method="post" data-asynctask data-asynctask-download class="download-btn-form{% if b.javascript_required or b.confirmation_text %} requirejs{% endif %}">
|
||||
{% csrf_token %}
|
||||
<button type="submit"
|
||||
class="btn btn-{{ button_size|default:"sm" }} {% if b.identifier == "pdf" %}btn-primary{% else %}btn-default{% endif %}"
|
||||
{% if b.confirmation_text %}data-confirmation-text="{{ b.confirmation_text|rich_text|force_escape }}"{% endif %}>
|
||||
{% with download_text=download_text|default:"text" %}
|
||||
<span class="fa {{ b.icon }}" aria-hidden="true"></span> {{ b|getitem:download_text }}
|
||||
{% endwith %}
|
||||
</button>
|
||||
</form>
|
||||
{% endfor %}
|
||||
{% for b in wallet_buttons|getitem:line %}
|
||||
{% comment %} TODO(wallet): move views to core {% endcomment %}
|
||||
<form
|
||||
action="{% spaceless %}{% if position_page and line.addon_to %}
|
||||
{% eventurl event "plugins:wallet:presale_event.order.position.wallet.get" secret=line.addon_to.web_secret order=order.code platform=b.identifier pid=line.pk position=line.addon_to.positionid %}
|
||||
{% elif position_page %}
|
||||
{% eventurl event "plugins:wallet:presale_event.order.position.wallet.get" secret=line.web_secret order=order.code platform=b.identifier pid=line.pk position=line.positionid %}
|
||||
{% else %}
|
||||
{% eventurl event "plugins:wallet:presale_event.order.wallet.get" secret=order.secret order=order.code platform=b.identifier position=line.pk %}
|
||||
{% endif %}{% endspaceless %}"
|
||||
method="post" data-asynctask data-asynctask-download class="download-btn-form">
|
||||
{% csrf_token %}
|
||||
<button type="submit"
|
||||
class="btn btn-{{ button_size|default:"sm" }} btn-default {{ b.attributes.class }}"
|
||||
{% include "pretixbase/fragments/attributes.html" with attributes=b.attributes %}>
|
||||
{{ b.content }}
|
||||
</button>
|
||||
</form>
|
||||
{% endfor %}
|
||||
@@ -259,7 +259,7 @@ class TicketPageMixin:
|
||||
order=self.order
|
||||
)
|
||||
|
||||
ctx['tickets_with_download'] = [p for p in ctx['cart']['positions'] if p in positions_with_tickets]
|
||||
ctx['tickets_with_download'] = [p for p in ctx['cart']['positions'] if p in positions_with_tickets] # TODO(wallet): can this just be simplyfied to 'positions_with_tickets'
|
||||
|
||||
ctx['download_buttons'] = self.download_buttons
|
||||
ctx['wallet_buttons'] = self.wallet_buttons
|
||||
@@ -274,7 +274,8 @@ class TicketPageMixin:
|
||||
|
||||
@cached_property
|
||||
def wallet_buttons(self):
|
||||
return get_wallet_buttons(self.request.event)
|
||||
return get_wallet_buttons(self.request.event, self.order.positions_with_tickets, show_disabled=False)
|
||||
|
||||
|
||||
@method_decorator(xframe_options_exempt, 'dispatch')
|
||||
class OrderDetails(EventViewMixin, OrderDetailMixin, CartMixin, TicketPageMixin, TemplateView):
|
||||
|
||||
Reference in new issue
Block a user