From 93502777b53b647ad01585198cf32168db81df45 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Sun, 29 May 2016 18:49:04 +0200 Subject: [PATCH] Documented pretix' URL layer --- doc/development/api/general.rst | 6 +- doc/development/index.rst | 1 + doc/development/urlconfig.rst | 88 ++++++++++++++++++++++++++++ src/pretix/multidomain/urlreverse.py | 12 +++- src/pretix/urls.py | 2 - 5 files changed, 102 insertions(+), 7 deletions(-) create mode 100644 doc/development/urlconfig.rst diff --git a/doc/development/api/general.rst b/doc/development/api/general.rst index 637f0d4443..e3d3ad9919 100644 --- a/doc/development/api/general.rst +++ b/doc/development/api/general.rst @@ -29,7 +29,7 @@ on the type of navigation. You should also return an ``active`` key with a boole set to ``True``, when this item should be marked as active. The ``request`` object will have an attribute ``event``. -``pretix.control.signals.nav_event``: +``pretix.control.signals.nav_event`` The sidebar navigation when the admin has selected an event. Order events @@ -37,11 +37,11 @@ Order events There are multiple signals that will be sent out in the ordering cycle: -``pretix.base.signals.order_placed``: +``pretix.base.signals.order_placed`` Sent out every time an order has been created. Provides the ``order`` as the only keyword argument. -``pretix.base.signals.order_paid``: +``pretix.base.signals.order_paid`` Sent out every time an order has been paid. Provides the ``order`` as the only keyword argument. diff --git a/doc/development/index.rst b/doc/development/index.rst index 100dc56db2..9129fe4e7d 100644 --- a/doc/development/index.rst +++ b/doc/development/index.rst @@ -12,6 +12,7 @@ Contents: contribution/index models api/index + urlconfig .. TODO:: Document settings objects, ItemVariation objects, form fields. \ No newline at end of file diff --git a/doc/development/urlconfig.rst b/doc/development/urlconfig.rst new file mode 100644 index 0000000000..fd86531f67 --- /dev/null +++ b/doc/development/urlconfig.rst @@ -0,0 +1,88 @@ +Working with URLs +================= + +As soon as you write a plugin that provides a new view to the user (or if you want to +contribute to pretix itself), you need to understand how URLs work in pretix as it slightly +differs from the standard Django system. + +The reason for the complicated URL handling is that pretix supports custom subdomains for +single organizers. In this example we will use an event organizer with the slug ``bigorg`` +that manages an awesome conference with the slug ``awesomecon``. If pretix is installed +on pretix.eu, this event is available by default at ``https://pretix.eu/bigorg/awesomecon/`` +and the admin panel is available at ``https://pretix.eu/control/event/bigorg/awesomecon/``. + +If the organizer now configures a custom domain like ``tickets.bigorg.com``, his event will +from now on be available on ``https://tickets.bigorg.com/awesomecon/``. The former URL at +``pretix.eu`` will redirect there. However, the admin panel will still only be available +on ``pretix.eu`` for convenience and security reasons. + +URL routing +----------- + +The hard part about implementing this URL routing in Django is that +``https://pretix.eu/bigorg/awesomecon/`` contains two parameters of nearly arbitrary content +and ``https://tickets.bigorg.com/awesomecon/`` contains only one. The only robust way to do +this is by having *seperate* URL configuration for those two cases. In pretix, we call the +former our ``maindomain`` config and the latter our ``subdomain`` config. For pretix' core +modules we do some magic to avoid duplicate configuration, but for a fairly simple plugin with +only a handful of routes, we recommend just configuring the two URL sets seperately. + +The file ``maindomain_urls.py`` inside your plugin package will be loaded and scanned for +URL configuration automatically and should be provided by any plugin that provides any view. + +A very basic example that provides one view in the admin panel and one view in the frontend +could look like this:: + + from django.conf.urls import url + + from . import views + + urlpatterns = [ + url(r'^control/event/(?P[^/]+)/(?P[^/]+)/mypluginname/', + views.AdminView.as_view(), name='backend'), + url(r'^(?P[^/]+)/(?P[^/]+)/mypluginname/', + views.FrontendView.as_view(), name='frontend'), + ] + +A matching configuration for custom domains will be expected in the ``subdomain_urls.py`` file +of your package and would look like this:: + + from django.conf.urls import url + + from . import views + + urlpatterns = [ + url(r'^(?P[^/]+)/mypluginname/', + views.FrontendView.as_view(), name='frontend'), + ] + +If you only provide URLs in the admin area, you do not need to provide a ``subdomain_urls`` module. + +URL reversal +------------ + +pretix uses Django's URL namespacing feature. The URLs of pretix' core are available in the ``control`` +and ``presale`` namespaces, there are only very few URLs in the root namespace. Your plugin's URLs will +be available in the ``plugins:`` namespace, e.g. the form of the email sending plugin is +available as ``plugins:sendmail:send``. + +Generating an URL for the frontend is a complicated task, because you need to know whether the event's +organizer uses a custom URL or not and then generate the URL with a different domain and different +arguments based on this information. pretix provides some helpers to make this easier. The first helper +is a python method that emulates a behaviour similar to ``reverse``: + +.. autofunction:: pretix.multidomain.urlreverse.eventreverse + +In addition, there is a template tag that works similar to ``url`` but takes an event or organizer object +as its first argument and can be used like this:: + + {% load eventurl %} + Pay + + +Implementation details +---------------------- + +There are some other caveats when using a design like this, e.g. you have to care about cookie domains +and referer verification yourself. If you want to see how we built this, look into the ``pretix/multidomain/`` +sub-tree. \ No newline at end of file diff --git a/src/pretix/multidomain/urlreverse.py b/src/pretix/multidomain/urlreverse.py index 2a3667ba78..2b7f8ebee2 100644 --- a/src/pretix/multidomain/urlreverse.py +++ b/src/pretix/multidomain/urlreverse.py @@ -20,10 +20,18 @@ def get_domain(organizer): def eventreverse(obj, name, kwargs=None): """ - Works similar to django.core.urlresolvers.reverse but takes into account that some + Works similar to ``django.core.urlresolvers.reverse`` but takes into account that some organizers might have their own (sub)domain instead of a subpath. - :param obj: An event or organizer + Non-keyword arguments are not supported as we want do discourage using them for better + readability. + + :param obj: An ``Event`` or ``Organizer`` object + :param name: The name of the URL route + :param kwargs: A dictionary of additional keyword arguments that should be used. You do not + need to provide the organizer or event slug here, it will be added automatically as + needed. + :returns: An absolute URL (including scheme and host) as a string """ from pretix.multidomain import subdomain_urlconf, maindomain_urlconf diff --git a/src/pretix/urls.py b/src/pretix/urls.py index 532b74e1fd..4956418b3b 100644 --- a/src/pretix/urls.py +++ b/src/pretix/urls.py @@ -1,5 +1,3 @@ -import uuid - from django.conf import settings from django.conf.urls import include, url from django.utils import timezone