forked from CGM_Public/pretix_original
add missing packages
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-2021 rami.io GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PretixStorefrontApiConfig(AppConfig):
|
||||
name = "pretix.storefrontapi"
|
||||
label = "pretixstorefrontapi"
|
||||
|
||||
def ready(self):
|
||||
from . import signals # noqa
|
||||
@@ -0,0 +1,400 @@
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework import serializers, viewsets
|
||||
from rest_framework.generics import get_object_or_404
|
||||
from rest_framework.response import Response
|
||||
|
||||
from pretix.base.models import (
|
||||
Event, Item, ItemCategory, ItemVariation, Quota, SubEvent,
|
||||
)
|
||||
from pretix.base.storelogic.products import (
|
||||
get_items_for_product_list, item_group_by_category,
|
||||
)
|
||||
from pretix.base.templatetags.rich_text import rich_text
|
||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||
from pretix.storefrontapi.permission import StorefrontEventPermission
|
||||
from pretix.storefrontapi.serializers import I18nFlattenedModelSerializer
|
||||
|
||||
|
||||
def opt_str(o):
|
||||
if o is None:
|
||||
return None
|
||||
return str(o)
|
||||
|
||||
|
||||
class RichtTextField(serializers.Field):
|
||||
def to_representation(self, value):
|
||||
return rich_text(value)
|
||||
|
||||
|
||||
class DynamicAttrField(serializers.Field):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.attr = kwargs.pop("attr")
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def to_representation(self, value):
|
||||
return getattr(value, self.attr)
|
||||
|
||||
|
||||
class EventURLField(serializers.Field):
|
||||
def to_representation(self, ev):
|
||||
if isinstance(ev, SubEvent):
|
||||
return build_absolute_uri(
|
||||
ev.event, "presale:event.index", kwargs={"subevent": ev.pk}
|
||||
)
|
||||
return build_absolute_uri(ev, "presale:event.index")
|
||||
|
||||
|
||||
class EventSettingsField(serializers.Field):
|
||||
def to_representation(self, ev):
|
||||
event = ev.event if isinstance(ev, SubEvent) else ev
|
||||
return {
|
||||
"display_net_prices": event.settings.display_net_prices,
|
||||
"show_variations_expanded": event.settings.show_variations_expanded,
|
||||
"show_times": event.settings.show_times,
|
||||
"show_dates_on_frontpage": event.settings.show_dates_on_frontpage,
|
||||
"voucher_explanation_text": str(
|
||||
rich_text(event.settings.voucher_explanation_text, safelinks=False)
|
||||
),
|
||||
"frontpage_text": str(
|
||||
rich_text(
|
||||
(
|
||||
ev.frontpage_text
|
||||
if isinstance(ev, SubEvent)
|
||||
else event.settings.frontpage_text
|
||||
),
|
||||
safelinks=False,
|
||||
)
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class CategorySerializer(I18nFlattenedModelSerializer):
|
||||
description = RichtTextField()
|
||||
|
||||
class Meta:
|
||||
model = ItemCategory
|
||||
fields = [
|
||||
"id",
|
||||
"name",
|
||||
"description",
|
||||
]
|
||||
|
||||
|
||||
class PricingField(serializers.Field):
|
||||
def to_representation(self, item_or_var):
|
||||
if isinstance(item_or_var, Item) and item_or_var.has_variations:
|
||||
return None
|
||||
|
||||
item = item_or_var if isinstance(item_or_var, Item) else item_or_var.item
|
||||
|
||||
return {
|
||||
"display_price": {
|
||||
"net": opt_str(item_or_var.display_price.net),
|
||||
"gross": opt_str(item_or_var.display_price.gross),
|
||||
"tax_rate": opt_str(
|
||||
item_or_var.display_price.rate
|
||||
if not item.includes_mixed_tax_rate
|
||||
else None
|
||||
),
|
||||
"tax_name": opt_str(
|
||||
item_or_var.display_price.name
|
||||
if not item.includes_mixed_tax_rate
|
||||
else None
|
||||
),
|
||||
},
|
||||
"original_price": (
|
||||
{
|
||||
"net": opt_str(item_or_var.original_price.net),
|
||||
"gross": opt_str(item_or_var.original_price.gross),
|
||||
"tax_rate": opt_str(
|
||||
item_or_var.original_price.rate
|
||||
if not item.includes_mixed_tax_rate
|
||||
else None
|
||||
),
|
||||
"tax_name": opt_str(
|
||||
item_or_var.original_price.name
|
||||
if not item.includes_mixed_tax_rate
|
||||
else None
|
||||
),
|
||||
}
|
||||
if item_or_var.original_price
|
||||
else None
|
||||
),
|
||||
"free_price": item.free_price,
|
||||
"suggested_price": {
|
||||
"net": opt_str(item_or_var.suggested_price.net),
|
||||
"gross": opt_str(item_or_var.suggested_price.gross),
|
||||
"tax_rate": opt_str(
|
||||
item_or_var.suggested_price.rate
|
||||
if not item.includes_mixed_tax_rate
|
||||
else None
|
||||
),
|
||||
"tax_name": opt_str(
|
||||
item_or_var.suggested_price.name
|
||||
if not item.includes_mixed_tax_rate
|
||||
else None
|
||||
),
|
||||
},
|
||||
"mandatory_priced_addons": item.mandatory_priced_addons,
|
||||
"includes_mixed_tax_rate": item.includes_mixed_tax_rate,
|
||||
}
|
||||
|
||||
|
||||
class AvailabilityField(serializers.Field):
|
||||
def to_representation(self, item_or_var):
|
||||
if isinstance(item_or_var, Item) and item_or_var.has_variations:
|
||||
return None
|
||||
|
||||
item = item_or_var if isinstance(item_or_var, Item) else item_or_var.item
|
||||
|
||||
if (
|
||||
item_or_var.current_unavailability_reason == "require_voucher"
|
||||
or item.current_unavailability_reason == "require_voucher"
|
||||
):
|
||||
return {
|
||||
"available": False,
|
||||
"code": "require_voucher",
|
||||
"message": _("Enter a voucher code below to buy this product."),
|
||||
"waiting_list": False,
|
||||
"max_selection": 0,
|
||||
"quota_left": None,
|
||||
}
|
||||
elif (
|
||||
item_or_var.current_unavailability_reason == "available_from"
|
||||
or item.current_unavailability_reason == "available_from"
|
||||
):
|
||||
return {
|
||||
"available": False,
|
||||
"code": "available_from",
|
||||
"message": _("Not available yet."),
|
||||
"waiting_list": False,
|
||||
"max_selection": 0,
|
||||
"quota_left": None,
|
||||
}
|
||||
elif (
|
||||
item_or_var.current_unavailability_reason == "available_until"
|
||||
or item.current_unavailability_reason == "available_until"
|
||||
):
|
||||
return {
|
||||
"available": False,
|
||||
"code": "available_until",
|
||||
"message": _("Not available any more."),
|
||||
"waiting_list": False,
|
||||
"max_selection": 0,
|
||||
"quota_left": None,
|
||||
}
|
||||
elif item_or_var.cached_availability[0] <= Quota.AVAILABILITY_ORDERED:
|
||||
return {
|
||||
"available": False,
|
||||
"code": "sold_out",
|
||||
"message": _("SOLD OUT"),
|
||||
"waiting_list": self.context["allow_waitinglist"]
|
||||
and item.allow_waitinglist,
|
||||
"max_selection": 0,
|
||||
"quota_left": 0,
|
||||
}
|
||||
elif item_or_var.cached_availability[0] < Quota.AVAILABILITY_OK:
|
||||
return {
|
||||
"available": False,
|
||||
"code": "reserved",
|
||||
"message": _(
|
||||
"All remaining products are reserved but might become available again."
|
||||
),
|
||||
"waiting_list": self.context["allow_waitinglist"]
|
||||
and item.allow_waitinglist,
|
||||
"max_selection": 0,
|
||||
"quota_left": 0,
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"available": True,
|
||||
"code": "ok",
|
||||
"message": None,
|
||||
"waiting_list": False,
|
||||
"max_selection": item_or_var.order_max,
|
||||
"quota_left": (
|
||||
item_or_var.cached_availability[1]
|
||||
if item.show_quota_left
|
||||
and item_or_var.cached_availability[1] is not None
|
||||
else None
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class VariationSerializer(I18nFlattenedModelSerializer):
|
||||
description = RichtTextField()
|
||||
pricing = PricingField(source="*")
|
||||
availability = AvailabilityField(source="*")
|
||||
|
||||
class Meta:
|
||||
model = ItemVariation
|
||||
fields = [
|
||||
"id",
|
||||
"value",
|
||||
"description",
|
||||
"pricing",
|
||||
"availability",
|
||||
]
|
||||
|
||||
|
||||
class ItemSerializer(I18nFlattenedModelSerializer):
|
||||
description = RichtTextField()
|
||||
available_variations = VariationSerializer(many=True, read_only=True)
|
||||
pricing = PricingField(source="*")
|
||||
availability = AvailabilityField(source="*")
|
||||
has_variations = serializers.BooleanField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Item
|
||||
fields = [
|
||||
"id",
|
||||
"name",
|
||||
"has_variations",
|
||||
"description",
|
||||
"picture",
|
||||
"min_per_order",
|
||||
"free_price",
|
||||
"available_variations",
|
||||
"pricing",
|
||||
"availability",
|
||||
]
|
||||
|
||||
|
||||
class ProductGroupField(serializers.Field):
|
||||
def to_representation(self, ev):
|
||||
event = ev.event if isinstance(ev, SubEvent) else ev
|
||||
|
||||
items, display_add_to_cart = get_items_for_product_list(
|
||||
event,
|
||||
subevent=ev if isinstance(ev, SubEvent) else None,
|
||||
require_seat=False,
|
||||
channel=self.context["sales_channel"],
|
||||
memberships=(
|
||||
self.context["customer"].usable_memberships(
|
||||
for_event=ev, testmode=event.testmode
|
||||
)
|
||||
if self.context.get("customer")
|
||||
else None
|
||||
),
|
||||
)
|
||||
return [
|
||||
{
|
||||
"category": (
|
||||
CategorySerializer(cat, context=self.context).data if cat else None
|
||||
),
|
||||
"items": ItemSerializer(items, many=True, context=self.context).data,
|
||||
}
|
||||
for cat, items in item_group_by_category(items)
|
||||
]
|
||||
|
||||
|
||||
class BaseEventDetailSerializer(I18nFlattenedModelSerializer):
|
||||
public_url = EventURLField(source="*", read_only=True)
|
||||
settings = EventSettingsField(source="*", read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = [
|
||||
"name",
|
||||
"has_subevents",
|
||||
"public_url",
|
||||
"currency",
|
||||
"settings",
|
||||
]
|
||||
|
||||
def to_representation(self, ev):
|
||||
r = super().to_representation(ev)
|
||||
event = ev.event if isinstance(ev, SubEvent) else ev
|
||||
|
||||
if not event.settings.presale_start_show_date or event.presale_is_running:
|
||||
r["effective_presale_start"] = None
|
||||
if not event.settings.show_date_to:
|
||||
r["date_to"] = None
|
||||
|
||||
return r
|
||||
|
||||
|
||||
class SubEventDetailSerializer(BaseEventDetailSerializer):
|
||||
testmode = serializers.BooleanField(source="event.testmode")
|
||||
has_subevents = serializers.BooleanField(source="event.has_subevents")
|
||||
product_list = ProductGroupField(source="*")
|
||||
|
||||
# todo: vouchers_exist
|
||||
# todo: date range
|
||||
# todo: waiting list info
|
||||
# todo: has seating
|
||||
|
||||
class Meta:
|
||||
model = SubEvent
|
||||
fields = [
|
||||
"name",
|
||||
"testmode",
|
||||
"has_subevents",
|
||||
"public_url",
|
||||
"currency",
|
||||
"settings",
|
||||
"location",
|
||||
"date_from",
|
||||
"date_to",
|
||||
"date_admission",
|
||||
"presale_is_running",
|
||||
"effective_presale_start",
|
||||
"product_list",
|
||||
]
|
||||
|
||||
|
||||
class EventDetailSerializer(BaseEventDetailSerializer):
|
||||
# todo: vouchers_exist
|
||||
# todo: date range
|
||||
# todo: waiting list info
|
||||
# todo: has seating
|
||||
product_list = ProductGroupField(source="*")
|
||||
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = [
|
||||
"name",
|
||||
"testmode",
|
||||
"has_subevents",
|
||||
"public_url",
|
||||
"currency",
|
||||
"settings",
|
||||
"location",
|
||||
"date_from",
|
||||
"date_to",
|
||||
"date_admission",
|
||||
"presale_is_running",
|
||||
"effective_presale_start",
|
||||
"product_list",
|
||||
]
|
||||
|
||||
|
||||
class EventViewSet(viewsets.ViewSet):
|
||||
queryset = Event.objects.none()
|
||||
lookup_url_kwarg = "event"
|
||||
lookup_field = "slug"
|
||||
permission_classes = [
|
||||
StorefrontEventPermission,
|
||||
]
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
event = request.event # Lookup is already done
|
||||
|
||||
ctx = {
|
||||
"sales_channel": request.sales_channel,
|
||||
"customer": None,
|
||||
"event": event,
|
||||
"allow_waitinglist": True,
|
||||
}
|
||||
if event.has_subevents:
|
||||
if "subevent" in request.GET:
|
||||
ctx["event"] = request.event
|
||||
subevent = get_object_or_404(
|
||||
request.event.subevents, pk=request.GET.get("subevent"), active=True
|
||||
)
|
||||
serializer = SubEventDetailSerializer(subevent, context=ctx)
|
||||
else:
|
||||
serializer = BaseEventDetailSerializer(event, context=ctx)
|
||||
else:
|
||||
serializer = EventDetailSerializer(event, context=ctx)
|
||||
return Response(serializer.data)
|
||||
@@ -0,0 +1,134 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-2021 rami.io GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import logging
|
||||
|
||||
from dateutil.parser import parse
|
||||
from django.http import HttpRequest
|
||||
from django.urls import resolve
|
||||
from django.utils.timezone import now
|
||||
from django_scopes import scope
|
||||
from rest_framework.response import Response
|
||||
|
||||
from pretix.base.middleware import LocaleMiddleware
|
||||
from pretix.base.models import Event, Organizer
|
||||
from pretix.base.timemachine import timemachine_now_var
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ApiMiddleware:
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request: HttpRequest):
|
||||
if not request.path.startswith("/storefrontapi/"):
|
||||
return self.get_response(request)
|
||||
|
||||
url = resolve(request.path_info)
|
||||
try:
|
||||
request.organizer = Organizer.objects.filter(
|
||||
slug=url.kwargs["organizer"],
|
||||
).first()
|
||||
except Organizer.DoesNotExist:
|
||||
return Response(
|
||||
{"detail": "Organizer not found."},
|
||||
status=404,
|
||||
)
|
||||
|
||||
with scope(organizer=getattr(request, "organizer", None)):
|
||||
# todo: Authorization
|
||||
is_authorized_public = False # noqa
|
||||
is_authorized_private = True
|
||||
sales_channel_id = "web" # todo: get form authorization
|
||||
|
||||
if "event" in url.kwargs:
|
||||
try:
|
||||
request.event = request.organizer.events.get(
|
||||
slug=url.kwargs["event"],
|
||||
organizer=request.organizer,
|
||||
)
|
||||
|
||||
if not request.event.live and not is_authorized_private:
|
||||
return Response(
|
||||
{"detail": "Event not live."},
|
||||
status=403,
|
||||
)
|
||||
|
||||
except Event.DoesNotExist:
|
||||
return Response(
|
||||
{"detail": "Event not found."},
|
||||
status=404,
|
||||
)
|
||||
|
||||
try:
|
||||
request.sales_channel = request.organizer.sales_channels.get(
|
||||
identifier=sales_channel_id
|
||||
)
|
||||
|
||||
if (
|
||||
"X-Storefront-Time-Machine-Date" in request.headers
|
||||
and "event" in url.kwargs
|
||||
):
|
||||
if not request.event.testmode:
|
||||
return Response(
|
||||
{
|
||||
"detail": "Time machine can only be used for events in test mode."
|
||||
},
|
||||
status=400,
|
||||
)
|
||||
try:
|
||||
time_machine_date = parse(
|
||||
request.headers["X-Storefront-Time-Machine-Date"]
|
||||
)
|
||||
except ValueError:
|
||||
return Response(
|
||||
{"detail": "Invalid time machine header"},
|
||||
status=400,
|
||||
)
|
||||
else:
|
||||
request.now_dt = time_machine_date
|
||||
request.now_dt_is_fake = True
|
||||
timemachine_now_var.set(
|
||||
request.now_dt if request.now_dt_is_fake else None
|
||||
)
|
||||
else:
|
||||
request.now_dt = now()
|
||||
request.now_dt_is_fake = False
|
||||
|
||||
if (
|
||||
not request.event.all_sales_channels
|
||||
and request.sales_channel.identifier
|
||||
not in (
|
||||
s.identifier for s in request.event.limit_sales_channels.all()
|
||||
)
|
||||
):
|
||||
return Response(
|
||||
{"detail": "Event not available on this sales channel."},
|
||||
status=403,
|
||||
)
|
||||
|
||||
LocaleMiddleware(NotImplementedError).process_request(request)
|
||||
r = self.get_response(request)
|
||||
r["Access-Control-Allow-Origin"] = "*" # todo: allow whitelist?
|
||||
return r
|
||||
finally:
|
||||
timemachine_now_var.set(None)
|
||||
@@ -0,0 +1,30 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-2021 rami.io GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
from rest_framework.permissions import BasePermission
|
||||
|
||||
|
||||
class StorefrontEventPermission(BasePermission):
|
||||
|
||||
def has_permission(self, request, view):
|
||||
# TODO: Check middleware results
|
||||
return True
|
||||
@@ -0,0 +1,30 @@
|
||||
from i18nfield.fields import I18nCharField, I18nTextField
|
||||
from rest_framework.fields import Field
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
|
||||
|
||||
class I18nFlattenedField(Field):
|
||||
def __init__(self, **kwargs):
|
||||
self.allow_blank = kwargs.pop("allow_blank", False)
|
||||
self.trim_whitespace = kwargs.pop("trim_whitespace", True)
|
||||
self.max_length = kwargs.pop("max_length", None)
|
||||
self.min_length = kwargs.pop("min_length", None)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def to_representation(self, value):
|
||||
return str(value)
|
||||
|
||||
def to_internal_value(self, data):
|
||||
raise TypeError("Input not supported.")
|
||||
|
||||
|
||||
class I18nFlattenedModelSerializer(ModelSerializer):
|
||||
pass
|
||||
|
||||
|
||||
I18nFlattenedModelSerializer.serializer_field_mapping[I18nCharField] = (
|
||||
I18nFlattenedField
|
||||
)
|
||||
I18nFlattenedModelSerializer.serializer_field_mapping[I18nTextField] = (
|
||||
I18nFlattenedField
|
||||
)
|
||||
@@ -0,0 +1,47 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-2021 rami.io GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import importlib
|
||||
|
||||
from django.apps import apps
|
||||
from django.urls import include, re_path
|
||||
from rest_framework import routers
|
||||
|
||||
from .endpoints import event
|
||||
|
||||
storefront_orga_router = routers.DefaultRouter()
|
||||
storefront_orga_router.register(r"events", event.EventViewSet)
|
||||
|
||||
storefront_event_router = routers.DefaultRouter()
|
||||
|
||||
# Force import of all plugins to give them a chance to register URLs with the router
|
||||
for app in apps.get_app_configs():
|
||||
if hasattr(app, "PretixPluginMeta"):
|
||||
if importlib.util.find_spec(app.name + ".urls"):
|
||||
importlib.import_module(app.name + ".urls")
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r"^organizers/(?P<organizer>[^/]+)/", include(storefront_orga_router.urls)),
|
||||
re_path(
|
||||
r"^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/",
|
||||
include(storefront_event_router.urls),
|
||||
),
|
||||
]
|
||||
Reference in New Issue
Block a user