Added multi-domain capabilities

This commit is contained in:
Raphael Michel
2015-10-18 14:01:28 +02:00
parent d7d6e74c04
commit 3fa0cfb077
38 changed files with 720 additions and 141 deletions
-9
View File
@@ -27,8 +27,6 @@ Example::
instance_name=pretix.de instance_name=pretix.de
url=http://localhost url=http://localhost
currency=EUR currency=EUR
cookiedomain=.pretix.de
securecookie=on
datadir=/data datadir=/data
plugins_default=pretix.plugins.sendmail,pretix.plugins.statistics plugins_default=pretix.plugins.sendmail,pretix.plugins.statistics
@@ -41,13 +39,6 @@ Example::
``currency`` ``currency``
The default currency as a three-letter code. Defaults to ``EUR``. The default currency as a three-letter code. Defaults to ``EUR``.
``cookiedomain``
The domain to be used for session cookies, csrf protection cookies and locale cookies.
Empty by default.
``securecookie``
Set the ``secure`` and ``httponly`` flags on session cookies. Off by default.
``datadir`` ``datadir``
The local path to a data directory that will be used for storing user uploads and similar The local path to a data directory that will be used for storing user uploads and similar
data. Defaults to thea value of the environment variable ``DATA_DIR`` or ``data``. data. Defaults to thea value of the environment variable ``DATA_DIR`` or ``data``.
+3 -8
View File
@@ -9,12 +9,11 @@ from pretix.base.models import (
CartPosition, Event, EventLock, Order, OrderPosition, Quota, CartPosition, Event, EventLock, Order, OrderPosition, Quota,
) )
from pretix.base.payment import BasePaymentProvider from pretix.base.payment import BasePaymentProvider
from pretix.base.services.cart import CartError
from pretix.base.services.mail import mail from pretix.base.services.mail import mail
from pretix.base.signals import ( from pretix.base.signals import (
order_paid, order_placed, register_payment_providers, order_paid, order_placed, register_payment_providers,
) )
from pretix.helpers.urls import build_absolute_uri from pretix.multidomain.urlreverse import build_absolute_uri
error_messages = { error_messages = {
'unavailable': _('Some of the products you selected were no longer available. ' 'unavailable': _('Some of the products you selected were no longer available. '
@@ -68,9 +67,7 @@ def mark_order_paid(order: Order, provider: str=None, info: str=None, date: date
{ {
'order': order, 'order': order,
'event': order.event, 'event': order.event,
'url': build_absolute_uri('presale:event.order', kwargs={ 'url': build_absolute_uri(order.event, 'presale:event.order', kwargs={
'event': order.event.slug,
'organizer': order.event.organizer.slug,
'order': order.code, 'order': order.code,
'secret': order.secret 'secret': order.secret
}), }),
@@ -189,9 +186,7 @@ def _perform_order(event: Event, payment_provider: BasePaymentProvider, position
{ {
'order': order, 'order': order,
'event': event, 'event': event,
'url': build_absolute_uri('presale:event.order', kwargs={ 'url': build_absolute_uri(event, 'presale:event.order', kwargs={
'event': event.slug,
'organizer': event.organizer.slug,
'order': order.code, 'order': order.code,
'secret': order.secret 'secret': order.secret
}), }),
+9 -3
View File
@@ -1,10 +1,10 @@
from urllib.parse import urlparse from urllib.parse import urljoin, urlparse
from django.conf import settings from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME from django.contrib.auth import REDIRECT_FIELD_NAME
from django.core.urlresolvers import get_script_prefix, resolve from django.core.urlresolvers import get_script_prefix, resolve
from django.http import Http404 from django.http import Http404
from django.shortcuts import resolve_url from django.shortcuts import redirect, resolve_url
from django.utils.encoding import force_str from django.utils.encoding import force_str
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
@@ -28,7 +28,13 @@ class PermissionMiddleware:
def process_request(self, request): def process_request(self, request):
url = resolve(request.path_info) url = resolve(request.path_info)
url_name = url.url_name url_name = url.url_name
if not request.path.startswith(get_script_prefix() + 'control') or url_name in self.EXCEPTIONS: if not request.path.startswith(get_script_prefix() + 'control'):
# This middleware should only touch the /control subpath
return
if hasattr(request, 'domain'):
# If the user is on a organizer's subdomain, he sould be redirected to pretix
return redirect(urljoin(settings.SITE_URL, request.get_full_path()))
if url_name in self.EXCEPTIONS:
return return
if not request.user.is_authenticated(): if not request.user.is_authenticated():
# Taken from django/contrib/auth/decorators.py # Taken from django/contrib/auth/decorators.py
+8
View File
@@ -0,0 +1,8 @@
from django.apps import AppConfig
class PretixMultidomainConfig(AppConfig):
name = 'pretix.multidomain'
label = 'pretixmultidomain'
default_app_config = 'pretix.multidomain.PretixMultidomainConfig'
+112
View File
@@ -0,0 +1,112 @@
import time
from urllib.parse import urlparse
from django.conf import settings
from django.contrib.sessions.middleware import \
SessionMiddleware as BaseSessionMiddleware
from django.core.exceptions import DisallowedHost
from django.http.request import split_domain_port
from django.middleware.csrf import CsrfViewMiddleware as BaseCsrfMiddleware
from django.utils.cache import patch_vary_headers
from django.utils.http import cookie_date
from pretix.multidomain.models import KnownDomain
class MultiDomainMiddleware:
def process_request(self, request):
# We try three options, in order of decreasing preference.
if settings.USE_X_FORWARDED_HOST and ('HTTP_X_FORWARDED_HOST' in request.META):
host = request.META['HTTP_X_FORWARDED_HOST']
elif 'HTTP_HOST' in request.META:
host = request.META['HTTP_HOST']
else:
# Reconstruct the host using the algorithm from PEP 333.
host = request.META['SERVER_NAME']
server_port = str(request.META['SERVER_PORT'])
if server_port != ('443' if request.is_secure() else '80'):
host = '%s:%s' % (host, server_port)
domain, port = split_domain_port(host)
default_domain, default_port = split_domain_port(urlparse(settings.SITE_URL).netloc)
if domain:
request.host = domain
request.port = int(port) if port else None
try:
kd = KnownDomain.objects.get(domainname=domain) # noqa
request.domain = kd
except:
if settings.DEBUG or domain in ('testserver', 'localhost') or domain == default_domain:
return # TODO: Select main page
raise DisallowedHost("Unknown host: %r" % host)
else:
request.organizer = kd.organizer
else:
raise DisallowedHost("Invalid HTTP_HOST header: %r." % host)
class SessionMiddleware(BaseSessionMiddleware):
def process_response(self, request, response):
try:
accessed = request.session.accessed
modified = request.session.modified
empty = request.session.is_empty()
except AttributeError:
pass
else:
# First check if we need to delete this cookie.
# The session should be deleted only if the session is entirely empty
if settings.SESSION_COOKIE_NAME in request.COOKIES and empty:
response.delete_cookie(settings.SESSION_COOKIE_NAME)
else:
if accessed:
patch_vary_headers(response, ('Cookie',))
if modified or settings.SESSION_SAVE_EVERY_REQUEST:
if request.session.get_expire_at_browser_close():
max_age = None
expires = None
else:
max_age = request.session.get_expiry_age()
expires_time = time.time() + max_age
expires = cookie_date(expires_time)
# Save the session data and refresh the client cookie.
# Skip session save for 500 responses, refs #3881.
if response.status_code != 500:
request.session.save()
response.set_cookie(settings.SESSION_COOKIE_NAME,
request.session.session_key, max_age=max_age,
expires=expires, domain=request.host,
path=settings.SESSION_COOKIE_PATH,
secure=request.scheme == 'https',
httponly=settings.SESSION_COOKIE_HTTPONLY or None)
return response
class CsrfViewMiddleware(BaseCsrfMiddleware):
def process_response(self, request, response):
if getattr(response, 'csrf_processing_done', False):
return response
# If CSRF_COOKIE is unset, then CsrfViewMiddleware.process_view was
# never called, probably because a request middleware returned a response
# (for example, contrib.auth redirecting to a login page).
if request.META.get("CSRF_COOKIE") is None:
return response
if not request.META.get("CSRF_COOKIE_USED", False):
return response
# Set the CSRF cookie even if it's already set, so we renew
# the expiry timer.
response.set_cookie(settings.CSRF_COOKIE_NAME,
request.META["CSRF_COOKIE"],
max_age=settings.CSRF_COOKIE_AGE,
domain=request.host,
path=settings.CSRF_COOKIE_PATH,
secure=request.scheme == 'https',
httponly=settings.CSRF_COOKIE_HTTPONLY
)
# Content varies with the CSRF cookie, so set the Vary header.
patch_vary_headers(response, ('Cookie',))
response.csrf_processing_done = True
return response
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import versions.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='KnownDomain',
fields=[
('domainname', models.CharField(serialize=False, max_length=255, primary_key=True)),
('organizer', versions.models.VersionedForeignKey(blank=True, to='pretixbase.Organizer', null=True)),
],
),
]
@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import versions.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixmultidomain', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='knowndomain',
options={'verbose_name': 'Known domain', 'verbose_name_plural': 'Known domains'},
),
migrations.AlterField(
model_name='knowndomain',
name='organizer',
field=versions.models.VersionedForeignKey(to='pretixbase.Organizer', null=True, related_name='domains', blank=True),
),
]
+27
View File
@@ -0,0 +1,27 @@
from django.db import models
from django.utils.translation import ugettext_lazy as _
from versions.models import VersionedForeignKey
from pretix.base.models import Organizer
class KnownDomain(models.Model):
domainname = models.CharField(max_length=255, primary_key=True)
organizer = VersionedForeignKey(Organizer, blank=True, null=True, related_name='domains')
class Meta:
verbose_name = _("Known domain")
verbose_name_plural = _("Known domains")
def __str__(self):
return self.domainname
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.organizer:
self.organizer.get_cache().clear()
def delete(self, *args, **kwargs):
if self.organizer:
self.organizer.get_cache().clear()
super().delete(*args, **kwargs)
@@ -0,0 +1,71 @@
from django import template
from django.core.urlresolvers import NoReverseMatch
from django.template import TemplateSyntaxError
from django.template.base import kwarg_re
from django.template.defaulttags import URLNode
from django.utils.encoding import smart_text
from django.utils.html import conditional_escape
register = template.Library()
class EventURLNode(URLNode):
def __init__(self, event, view_name, kwargs, asvar):
self.event = event
super().__init__(view_name, [], kwargs, asvar)
def render(self, context):
from pretix.multidomain.urlreverse import eventreverse
kwargs = {
smart_text(k, 'ascii'): v.resolve(context)
for k, v in self.kwargs.items()
}
view_name = self.view_name.resolve(context)
event = self.event.resolve(context)
url = ''
try:
url = eventreverse(event, view_name, kwargs=kwargs)
except NoReverseMatch:
if self.asvar is None:
raise
if self.asvar:
context[self.asvar] = url
return ''
else:
if context.autoescape:
url = conditional_escape(url)
return url
@register.tag
def eventurl(parser, token):
"""
Similar to {% url %} in the same way that eventreverse() is similar to reverse().
Takes an event object, an url name and optional keyword arguments
"""
bits = token.split_contents()
if len(bits) < 3:
raise TemplateSyntaxError("'%s' takes at least one argument, an event and the name of a url()." % bits[0])
viewname = parser.compile_filter(bits[2])
event = parser.compile_filter(bits[1])
kwargs = {}
asvar = None
bits = bits[3:]
if len(bits) >= 3 and bits[-2] == 'as':
asvar = bits[-1]
bits = bits[:-2]
if len(bits):
for bit in bits:
match = kwarg_re.match(bit)
if not match:
raise TemplateSyntaxError("Malformed arguments to eventurl tag")
name, value = match.groups()
if name:
kwargs[name] = parser.compile_filter(value)
else:
raise TemplateSyntaxError('Event urls only have keyword arguments.')
return EventURLNode(event, viewname, kwargs, asvar)
+44
View File
@@ -0,0 +1,44 @@
from urllib.parse import urljoin, urlsplit
from django.conf import settings
from django.core.urlresolvers import reverse
def get_domain(event):
c = event.organizer.get_cache()
domain = c.get('domain')
if domain is None:
domains = event.organizer.domains.all()
domain = domains[0].domainname if domains else None
c.set('domain', domain or 'none')
elif domain == 'none':
return None
return domain
def eventreverse(event, name, kwargs=None):
"""
Works similar to django.core.urlresolvers.reverse but takes into account that some
organizers might have their own (sub)domain instead of a subpath.
"""
kwargs = kwargs or {}
kwargs['event'] = event.slug
domain = get_domain(event)
if domain:
if 'organizer' in kwargs:
del kwargs['organizer']
path = reverse(name, kwargs=kwargs)
siteurlsplit = urlsplit(settings.SITE_URL)
if siteurlsplit.port and siteurlsplit.port not in (80, 443):
domain = '%s:%d' % (domain, siteurlsplit.port)
return urljoin('%s://%s' % (siteurlsplit.scheme, domain), path)
kwargs['organizer'] = event.organizer.slug
return reverse(name, kwargs=kwargs)
def build_absolute_uri(event, urlname, kwargs=None):
reversedurl = eventreverse(event, urlname, kwargs)
if '://' in reversedurl:
return reversedurl
return urljoin(settings.SITE_URL, reversedurl)
+3 -3
View File
@@ -11,7 +11,7 @@ from django.utils.translation import ugettext as __, ugettext_lazy as _
from pretix.base.models import Quota from pretix.base.models import Quota
from pretix.base.payment import BasePaymentProvider from pretix.base.payment import BasePaymentProvider
from pretix.base.services.orders import mark_order_paid from pretix.base.services.orders import mark_order_paid
from pretix.helpers.urls import build_absolute_uri from pretix.multidomain.urlreverse import build_absolute_uri
logger = logging.getLogger('pretix.plugins.paypal') logger = logging.getLogger('pretix.plugins.paypal')
@@ -87,8 +87,8 @@ class Paypal(BasePaymentProvider):
"payment_method": "paypal", "payment_method": "paypal",
}, },
"redirect_urls": { "redirect_urls": {
"return_url": build_absolute_uri('plugins:paypal:return'), "return_url": build_absolute_uri(request.event, 'plugins:paypal:return'),
"cancel_url": build_absolute_uri('plugins:paypal:abort'), "cancel_url": build_absolute_uri(request.event, 'plugins:paypal:abort'),
}, },
"transactions": [ "transactions": [
{ {
+1 -1
View File
@@ -3,7 +3,7 @@ from django.conf.urls import include, url
from .views import abort, retry, success from .views import abort, retry, success
urlpatterns = [ urlpatterns = [
url(r'^paypal/', include([ url(r'^(?:(?P<organizer>[^/]+)/)?(?P<event>[^/]+)/paypal/', include([
url(r'^abort/$', abort, name='abort'), url(r'^abort/$', abort, name='abort'),
url(r'^return/$', success, name='return'), url(r'^return/$', success, name='return'),
url(r'^retry/(?P<order>[^/]+)/', retry, name='retry') url(r'^retry/(?P<order>[^/]+)/', retry, name='retry')
+7 -13
View File
@@ -8,6 +8,7 @@ from django.utils.translation import ugettext as __, ugettext_lazy as _
from pretix.base.models import Event, Order from pretix.base.models import Event, Order
from pretix.helpers.urls import build_absolute_uri from pretix.helpers.urls import build_absolute_uri
from pretix.multidomain.urlreverse import eventreverse
from pretix.plugins.paypal.payment import Paypal from pretix.plugins.paypal.payment import Paypal
logger = logging.getLogger('pretix.plugins.paypal') logger = logging.getLogger('pretix.plugins.paypal')
@@ -23,10 +24,7 @@ def success(request):
request.session['payment_paypal_payer'] = payer request.session['payment_paypal_payer'] = payer
try: try:
event = Event.objects.current.get(identity=request.session['payment_paypal_event']) event = Event.objects.current.get(identity=request.session['payment_paypal_event'])
return redirect('presale:event.checkout', return redirect(eventreverse(event, 'presale:event.checkout', kwargs={'step': 'confirm'}))
event=event.slug,
organizer=event.organizer.slug,
step='confirm')
except Event.DoesNotExist: except Event.DoesNotExist:
pass # TODO: Handle this pass # TODO: Handle this
else: else:
@@ -38,10 +36,7 @@ def abort(request):
messages.error(request, _('It looks like you cancelled the PayPal payment')) messages.error(request, _('It looks like you cancelled the PayPal payment'))
try: try:
event = Event.objects.current.get(identity=request.session['payment_paypal_event']) event = Event.objects.current.get(identity=request.session['payment_paypal_event'])
return redirect('presale:event.checkout', return redirect(eventreverse(event, 'presale:event.checkout', kwargs={'step': 'payment'}))
event=event.slug,
organizer=event.organizer.slug,
step='payment')
except Event.DoesNotExist: except Event.DoesNotExist:
pass # TODO: Handle this pass # TODO: Handle this
@@ -103,8 +98,7 @@ def retry(request, order):
if resp: if resp:
return redirect(resp) return redirect(resp)
return redirect('presale:event.order', return redirect(eventreverse(order.event, 'presale:event.order', kwargs={
event=order.event.slug, 'order': order.code,
organizer=order.event.organizer.slug, 'secret': order.secret
order=order.code, }) + '?paid=yes')
secret=order.secret) + '?paid=yes'
+5 -20
View File
@@ -1,7 +1,6 @@
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from django.core.validators import EmailValidator from django.core.validators import EmailValidator
from django.db.models import Q, Sum from django.db.models import Q, Sum
from django.http import HttpResponseNotAllowed from django.http import HttpResponseNotAllowed
@@ -14,6 +13,7 @@ from django.views.generic.base import TemplateResponseMixin
from pretix.base.models import CartPosition, Order from pretix.base.models import CartPosition, Order
from pretix.base.services.orders import OrderError, perform_order from pretix.base.services.orders import OrderError, perform_order
from pretix.base.signals import register_payment_providers from pretix.base.signals import register_payment_providers
from pretix.multidomain.urlreverse import eventreverse
from pretix.presale.forms.checkout import ContactForm from pretix.presale.forms.checkout import ContactForm
from pretix.presale.signals import checkout_flow_steps from pretix.presale.signals import checkout_flow_steps
from pretix.presale.views import CartMixin from pretix.presale.views import CartMixin
@@ -59,32 +59,19 @@ class BaseCheckoutFlowStep:
return HttpResponseNotAllowed([]) return HttpResponseNotAllowed([])
def get_step_url(self): def get_step_url(self):
return reverse( return eventreverse(self.event, 'presale:event.checkout', kwargs={'step': self.identifier})
'presale:event.checkout',
kwargs={
'event': self.event.slug,
'organizer': self.event.organizer.slug,
'step': self.identifier
}
)
def get_prev_url(self, request): def get_prev_url(self, request):
prev = self.get_prev_applicable(request) prev = self.get_prev_applicable(request)
if not prev: if not prev:
return reverse('presale:event.index', kwargs={ return eventreverse(self.event, 'presale:event.index')
'event': self.request.event.slug,
'organizer': self.request.event.organizer.slug
})
else: else:
return prev.get_step_url() return prev.get_step_url()
def get_next_url(self, request): def get_next_url(self, request):
n = self.get_next_applicable(request) n = self.get_next_applicable(request)
if not n: if not n:
return reverse('presale:event.checkout.confirm', kwargs={ return eventreverse(self.event, 'presale:event.checkout.confirm')
'event': self.request.event.slug,
'organizer': self.request.event.organizer.slug
})
else: else:
return n.get_step_url() return n.get_step_url()
@@ -342,9 +329,7 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep):
return self.get_step_url() return self.get_step_url()
def get_order_url(self, order): def get_order_url(self, order):
return reverse('presale:event.order.pay.complete', kwargs={ return eventreverse(self.request.event, 'presale:event.order.pay.complete', kwargs={
'event': self.request.event.slug,
'organizer': self.request.event.organizer.slug,
'order': order.code, 'order': order.code,
'secret': order.secret 'secret': order.secret
}) })
+34 -4
View File
@@ -1,8 +1,12 @@
from urllib.parse import urljoin
from django.core.urlresolvers import resolve from django.core.urlresolvers import resolve
from django.http import Http404 from django.http import Http404
from django.shortcuts import redirect
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from pretix.base.models import Event from pretix.base.models import Event
from pretix.multidomain.urlreverse import get_domain
class EventMiddleware: class EventMiddleware:
@@ -15,10 +19,36 @@ class EventMiddleware:
if 'event.' in url_name and 'event' in url.kwargs: if 'event.' in url_name and 'event' in url.kwargs:
try: try:
request.event = Event.objects.current.filter( if hasattr(request, 'organizer'):
slug=url.kwargs['event'], # We are on an organizer's custom domain
organizer__slug=url.kwargs['organizer'], if 'organizer' in url.kwargs and url.kwargs['organizer']:
).select_related('organizer')[0] if url.kwargs['organizer'] != request.organizer.slug:
raise Http404(_('The selected event was not found.'))
path = "/" + request.get_full_path().split("/", 2)[-1]
return redirect(path)
request.event = Event.objects.current.filter(
slug=url.kwargs['event'],
organizer=request.organizer,
).select_related('organizer')[0]
else:
# We are on our main domain
if 'organizer' not in url.kwargs:
raise Http404(_('The selected event was not found.'))
request.event = Event.objects.current.filter(
slug=url.kwargs['event'],
organizer__slug=url.kwargs['organizer']
).select_related('organizer')[0]
# If this organizer has a custom domain, send the user there
domain = get_domain(request.event)
if domain:
if request.port and request.port not in (80, 443):
domain = '%s:%d' % (domain, request.port)
path = request.get_full_path().split("/", 2)[-1]
return redirect(urljoin('%s://%s' % (request.scheme, domain), path))
except IndexError: except IndexError:
raise Http404(_('The selected event was not found.')) raise Http404(_('The selected event was not found.'))
@@ -1,6 +1,7 @@
{% load compress %} {% load compress %}
{% load staticfiles %} {% load staticfiles %}
{% load i18n %} {% load i18n %}
{% load eventurl %}
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
@@ -22,7 +23,7 @@
<div class="container event"> <div class="container event">
<div class="page-header"> <div class="page-header">
<h1 class="pull-left"> <h1 class="pull-left">
<a href="{% url "presale:event.index" event=event.slug organizer=event.organizer.slug %}">{{ event.name }}</a> <a href="{% eventurl event "presale:event.index" %}">{{ event.name }}</a>
<small>{{ event.date_from|date:"DATE_FORMAT" }}{% if event.settings.show_date_to %} {{ event.date_to|date:"DATE_FORMAT" }}{% endif %}</small> <small>{{ event.date_from|date:"DATE_FORMAT" }}{% if event.settings.show_date_to %} {{ event.date_to|date:"DATE_FORMAT" }}{% endif %}</small>
</h1> </h1>
<div class="pull-right loginbox"> <div class="pull-right loginbox">
@@ -1,6 +1,7 @@
{% extends "pretixpresale/event/base.html" %} {% extends "pretixpresale/event/base.html" %}
{% load i18n %} {% load i18n %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load eventurl %}
{% block title %}{% trans "Confirm order" %}{% endblock %} {% block title %}{% trans "Confirm order" %}{% endblock %}
{% block content %} {% block content %}
<h2>{% trans "Confirm order" %}</h2> <h2>{% trans "Confirm order" %}</h2>
@@ -11,7 +12,7 @@
<div class="panel-heading"> <div class="panel-heading">
<div class="pull-right"> <div class="pull-right">
<a href=" <a href="
{% url "presale:event.index" organizer=request.event.organizer.slug event=request.event.slug %}"> {% eventurl request.event "presale:event.index" %}">
<span class="fa fa-edit"></span> <span class="fa fa-edit"></span>
{% trans "Modify" %} {% trans "Modify" %}
</a> </a>
@@ -41,7 +42,7 @@
<div class="panel-heading"> <div class="panel-heading">
{% if payment_provider.identifier != "free" %} {% if payment_provider.identifier != "free" %}
<div class="pull-right"> <div class="pull-right">
<a href="{% url "presale:event.checkout" step="payment" organizer=request.event.organizer.slug event=request.event.slug %}"> <a href="{% eventurl request.event "presale:event.checkout" step="payment" %}">
<span class="fa fa-edit"></span> <span class="fa fa-edit"></span>
{% trans "Modify" %} {% trans "Modify" %}
</a> </a>
@@ -59,7 +60,7 @@
<div class="panel panel-primary panel-contact"> <div class="panel panel-primary panel-contact">
<div class="panel-heading"> <div class="panel-heading">
<div class="pull-right"> <div class="pull-right">
<a href="{% url "presale:event.checkout" step="questions" organizer=request.event.organizer.slug event=request.event.slug %}"> <a href="{% eventurl request.event "presale:event.checkout" step="questions" %}">
<span class="fa fa-edit"></span> <span class="fa fa-edit"></span>
{% trans "Modify" %} {% trans "Modify" %}
</a> </a>
@@ -1,4 +1,5 @@
{% load i18n %} {% load i18n %}
{% load eventurl %}
{% for line in cart.positions %} {% for line in cart.positions %}
<div class="row-fluid cart-row"> <div class="row-fluid cart-row">
<div class="col-md-4 col-xs-6"> <div class="col-md-4 col-xs-6">
@@ -21,7 +22,7 @@
</div> </div>
<div class="col-md-2 col-xs-6 count"> <div class="col-md-2 col-xs-6 count">
{% if editable %} {% if editable %}
<form action="{% url "presale:event.cart.remove" event=event.slug organizer=event.organizer.slug %}" <form action="{% eventurl event "presale:event.cart.remove" %}"
method="post"> method="post">
{% csrf_token %} {% csrf_token %}
{% if line.variation %} {% if line.variation %}
@@ -36,7 +37,7 @@
{% endif %} {% endif %}
{{ line.count }} {{ line.count }}
{% if editable %} {% if editable %}
<form action="{% url "presale:event.cart.add" event=event.slug organizer=event.organizer.slug %}" <form action="{% eventurl event "presale:event.cart.add" %}"
method="post" data-asynctask> method="post" data-asynctask>
{% csrf_token %} {% csrf_token %}
{% if line.variation %} {% if line.variation %}
@@ -1,5 +1,6 @@
{% extends "pretixpresale/event/base.html" %} {% extends "pretixpresale/event/base.html" %}
{% load i18n %} {% load i18n %}
{% load eventurl %}
{% load thumbnail %} {% load thumbnail %}
{% block title %}{% trans "Presale" %}{% endblock %} {% block title %}{% trans "Presale" %}{% endblock %}
@@ -23,7 +24,7 @@
</div> </div>
<div class="col-md-4 col-md-offset-2 col-xs-12"> <div class="col-md-4 col-md-offset-2 col-xs-12">
<a class="btn btn-block btn-primary btn-lg" <a class="btn btn-block btn-primary btn-lg"
href="{% url "presale:event.checkout.start" organizer=request.event.organizer.slug event=request.event.slug %}"> href="{% eventurl request.event "presale:event.checkout.start" %}">
<i class="fa fa-shopping-cart"></i> {% trans "Proceed with checkout" %} <i class="fa fa-shopping-cart"></i> {% trans "Proceed with checkout" %}
</a> </a>
</div> </div>
@@ -51,7 +52,7 @@
{% endif %} {% endif %}
{% if event.presale_is_running or event.settings.show_items_outside_presale_period %} {% if event.presale_is_running or event.settings.show_items_outside_presale_period %}
<form method="post" data-asynctask <form method="post" data-asynctask
action="{% url "presale:event.cart.add" organizer=request.event.organizer.slug event=request.event.slug %}?next={{ request.path|urlencode }}"> action="{% eventurl request.event "presale:event.cart.add" %}?next={{ request.path|urlencode }}">
{% csrf_token %} {% csrf_token %}
{% for tup in items_by_category %} {% for tup in items_by_category %}
<section> <section>
@@ -1,6 +1,7 @@
{% extends "pretixpresale/event/base.html" %} {% extends "pretixpresale/event/base.html" %}
{% load i18n %} {% load i18n %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load eventurl %}
{% block title %}{% trans "Order details" %}{% endblock %} {% block title %}{% trans "Order details" %}{% endblock %}
{% block content %} {% block content %}
{% if "thanks" in request.GET or "paid" in request.GET %} {% if "thanks" in request.GET or "paid" in request.GET %}
@@ -32,7 +33,7 @@
</div> </div>
<div class="panel-body"> <div class="panel-body">
{% if can_retry %} {% if can_retry %}
<a href="{% url "presale:event.order.pay" organizer=request.event.organizer.slug event=request.event.slug secret=order.secret order=order.code %}" <a href="{% eventurl event "presale:event.order.pay" secret=order.secret order=order.code %}"
class="btn btn-primary pull-right"><i class="fa fa-money"></i> {% trans "Complete payment" %} class="btn btn-primary pull-right"><i class="fa fa-money"></i> {% trans "Complete payment" %}
</a> </a>
{% endif %} {% endif %}
@@ -59,7 +60,7 @@
{% endblocktrans %} {% endblocktrans %}
</p> </p>
{% for b in download_buttons %} {% for b in download_buttons %}
<a href="{% url "presale:event.order.download" organizer=request.event.organizer.slug event=request.event.slug secret=order.secret order=order.code output=b.identifier %}" <a href="{% eventurl event "presale:event.order.download" secret=order.secret order=order.code output=b.identifier %}"
class="btn btn-primary"> class="btn btn-primary">
<span class="fa {{ b.icon }}"></span> {{ b.text }} <span class="fa {{ b.icon }}"></span> {{ b.text }}
</a> </a>
@@ -76,7 +77,7 @@
<div class="panel-heading"> <div class="panel-heading">
{% if order.can_modify_answers %} {% if order.can_modify_answers %}
<div class="pull-right"> <div class="pull-right">
<a href="{% url "presale:event.order.modify" organizer=request.event.organizer.slug event=request.event.slug secret=order.secret order=order.code %}"> <a href="{% eventurl event "presale:event.order.modify" secret=order.secret order=order.code %}">
<span class="fa fa-edit"></span> <span class="fa fa-edit"></span>
{% trans "Change details" %} {% trans "Change details" %}
</a> </a>
@@ -94,7 +95,7 @@
<div class="row"> <div class="row">
<div class="col-md-12 text-right"> <div class="col-md-12 text-right">
<p> <p>
<a href="{% url 'presale:event.order.cancel' event=request.event.slug organizer=request.event.organizer.slug secret=order.secret order=order.code %}" <a href="{% eventurl event 'presale:event.order.cancel' secret=order.secret order=order.code %}"
class="btn btn-danger"> class="btn btn-danger">
<span class="fa fa-remove"></span> <span class="fa fa-remove"></span>
{% trans "Cancel order" %} {% trans "Cancel order" %}
@@ -1,5 +1,6 @@
{% extends "pretixpresale/event/base.html" %} {% extends "pretixpresale/event/base.html" %}
{% load i18n %} {% load i18n %}
{% load eventurl %}
{% block title %}{% trans "Cancel order" %}{% endblock %} {% block title %}{% trans "Cancel order" %}{% endblock %}
{% block content %} {% block content %}
<h2> <h2>
@@ -16,7 +17,7 @@
<div class="row checkout-button-row"> <div class="row checkout-button-row">
<div class="col-md-4"> <div class="col-md-4">
<a class="btn btn-block btn-default btn-lg" <a class="btn btn-block btn-default btn-lg"
href="{% url "presale:event.order" event=request.event.slug organizer=request.event.organizer.slug secret=order.secret order=order.code %}"> href="{% eventurl request.event "presale:event.order" secret=order.secret order=order.code %}">
{% trans "No, take me back" %} {% trans "No, take me back" %}
</a> </a>
</div> </div>
@@ -1,5 +1,6 @@
{% extends "pretixpresale/event/base.html" %} {% extends "pretixpresale/event/base.html" %}
{% load i18n %} {% load i18n %}
{% load eventurl %}
{% block title %}{% trans "Pay order" %}{% endblock %} {% block title %}{% trans "Pay order" %}{% endblock %}
{% block content %} {% block content %}
<h2> <h2>
@@ -16,7 +17,7 @@
<div class="row checkout-button-row"> <div class="row checkout-button-row">
<div class="col-md-4"> <div class="col-md-4">
<a class="btn btn-block btn-default btn-lg" <a class="btn btn-block btn-default btn-lg"
href="{% url "presale:event.order" event=request.event.slug organizer=request.event.organizer.slug secret=order.secret order=order.code %}"> href="{% eventurl request.event "presale:event.order" secret=order.secret order=order.code %}">
{% trans "Cancel" %} {% trans "Cancel" %}
</a> </a>
</div> </div>
@@ -1,5 +1,6 @@
{% extends "pretixpresale/event/base.html" %} {% extends "pretixpresale/event/base.html" %}
{% load i18n %} {% load i18n %}
{% load eventurl %}
{% block title %}{% trans "Pay order" %}{% endblock %} {% block title %}{% trans "Pay order" %}{% endblock %}
{% block content %} {% block content %}
<h2> <h2>
@@ -27,7 +28,7 @@
<div class="row checkout-button-row"> <div class="row checkout-button-row">
<div class="col-md-4"> <div class="col-md-4">
<a class="btn btn-block btn-default btn-lg" <a class="btn btn-block btn-default btn-lg"
href="{% url "presale:event.order" event=request.event.slug organizer=request.event.organizer.slug secret=order.secret order=order.code %}"> href="{% eventurl request.event "presale:event.order" secret=order.secret order=order.code %}">
{% trans "Cancel" %} {% trans "Cancel" %}
</a> </a>
</div> </div>
+30 -27
View File
@@ -6,33 +6,36 @@ import pretix.presale.views.event
import pretix.presale.views.locale import pretix.presale.views.locale
import pretix.presale.views.order import pretix.presale.views.order
eventurls = [
url(r'^cart/add$', pretix.presale.views.cart.CartAdd.as_view(), name='event.cart.add'),
url(r'^cart/remove$', pretix.presale.views.cart.CartRemove.as_view(), name='event.cart.remove'),
url(r'^checkout/start$', pretix.presale.views.checkout.CheckoutView.as_view(), name='event.checkout.start'),
url(r'^checkout/(?P<step>[^/]+)/$', pretix.presale.views.checkout.CheckoutView.as_view(),
name='event.checkout'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/$', pretix.presale.views.order.OrderDetails.as_view(),
name='event.order'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/cancel$',
pretix.presale.views.order.OrderCancel.as_view(),
name='event.order.cancel'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/modify$',
pretix.presale.views.order.OrderModify.as_view(),
name='event.order.modify'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/pay$', pretix.presale.views.order.OrderPay.as_view(),
name='event.order.pay'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/pay/confirm$',
pretix.presale.views.order.OrderPayDo.as_view(),
name='event.order.pay.confirm'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/pay/complete$',
pretix.presale.views.order.OrderPayComplete.as_view(),
name='event.order.pay.complete'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/download/(?P<output>[^/]+)$',
pretix.presale.views.order.OrderDownload.as_view(),
name='event.order.download'),
url(r'^$', pretix.presale.views.event.EventIndex.as_view(), name='event.index'),
]
urlpatterns = [ urlpatterns = [
url(r'^(?P<organizer>[^/]+)/(?P<event>[^/]+)/', include([
url(r'^$', pretix.presale.views.event.EventIndex.as_view(), name='event.index'),
url(r'^cart/add$', pretix.presale.views.cart.CartAdd.as_view(), name='event.cart.add'),
url(r'^cart/remove$', pretix.presale.views.cart.CartRemove.as_view(), name='event.cart.remove'),
url(r'^checkout/$', pretix.presale.views.checkout.CheckoutView.as_view(), name='event.checkout.start'),
url(r'^checkout/(?P<step>[^/]+)/$', pretix.presale.views.checkout.CheckoutView.as_view(),
name='event.checkout'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/$', pretix.presale.views.order.OrderDetails.as_view(),
name='event.order'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/cancel$',
pretix.presale.views.order.OrderCancel.as_view(),
name='event.order.cancel'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/modify$',
pretix.presale.views.order.OrderModify.as_view(),
name='event.order.modify'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/pay$', pretix.presale.views.order.OrderPay.as_view(),
name='event.order.pay'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/pay/confirm$',
pretix.presale.views.order.OrderPayDo.as_view(),
name='event.order.pay.confirm'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/pay/complete$',
pretix.presale.views.order.OrderPayComplete.as_view(),
name='event.order.pay.complete'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/download/(?P<output>[^/]+)$',
pretix.presale.views.order.OrderDownload.as_view(),
name='event.order.download'),
])),
url(r'^locale/set$', pretix.presale.views.locale.LocaleSet.as_view(), name='locale.set'), url(r'^locale/set$', pretix.presale.views.locale.LocaleSet.as_view(), name='locale.set'),
url(r'^(?P<event>[^/]+)/', include(eventurls)),
url(r'^(?P<organizer>[^/]+)/(?P<event>[^/]+)/', include(eventurls)),
] ]
+2 -7
View File
@@ -2,12 +2,12 @@ from datetime import timedelta
from itertools import groupby from itertools import groupby
from django.contrib.auth.views import redirect_to_login from django.contrib.auth.views import redirect_to_login
from django.core.urlresolvers import reverse
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.timezone import now from django.utils.timezone import now
from pretix.base.models import CartPosition from pretix.base.models import CartPosition
from pretix.base.signals import register_payment_providers from pretix.base.signals import register_payment_providers
from pretix.multidomain.urlreverse import eventreverse
def login_required(view_func): def login_required(view_func):
@@ -15,12 +15,7 @@ def login_required(view_func):
if request.user.is_authenticated(): if request.user.is_authenticated():
return view_func(request, *args, **kwargs) return view_func(request, *args, **kwargs)
path = request.path path = request.path
return redirect_to_login( return redirect_to_login(path, eventreverse(request.event, 'presale:event.checkout.login'), 'next')
path, reverse('presale:event.checkout.login', kwargs={
'organizer': request.event.organizer.slug,
'event': request.event.slug,
}), 'next'
)
return _wrapped_view return _wrapped_view
+2 -5
View File
@@ -1,5 +1,4 @@
from django.contrib import messages from django.contrib import messages
from django.core.urlresolvers import reverse
from django.http import JsonResponse from django.http import JsonResponse
from django.shortcuts import redirect from django.shortcuts import redirect
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
@@ -8,6 +7,7 @@ from django.views.generic import View
from pretix.base.services.cart import ( from pretix.base.services.cart import (
CartError, add_items_to_cart, remove_items_from_cart, CartError, add_items_to_cart, remove_items_from_cart,
) )
from pretix.multidomain.urlreverse import eventreverse
from pretix.presale.views import EventViewMixin from pretix.presale.views import EventViewMixin
from pretix.presale.views.async import AsyncAction from pretix.presale.views.async import AsyncAction
@@ -18,10 +18,7 @@ class CartActionMixin:
if "next" in self.request.GET and '://' not in self.request.GET: if "next" in self.request.GET and '://' not in self.request.GET:
return self.request.GET.get('next') return self.request.GET.get('next')
else: else:
return reverse('presale:event.index', kwargs={ return eventreverse(self.request.event, 'presale:event.index')
'event': self.request.event.slug,
'organizer': self.request.event.organizer.slug,
})
def get_success_url(self, value=None): def get_success_url(self, value=None):
return self.get_next_url() return self.get_next_url()
+2 -5
View File
@@ -1,10 +1,10 @@
from django.contrib import messages from django.contrib import messages
from django.core.urlresolvers import reverse
from django.http import Http404 from django.http import Http404
from django.shortcuts import redirect from django.shortcuts import redirect
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.views.generic import View from django.views.generic import View
from pretix.multidomain.urlreverse import eventreverse
from pretix.presale.checkoutflow import get_checkout_flow from pretix.presale.checkoutflow import get_checkout_flow
from pretix.presale.views import CartMixin from pretix.presale.views import CartMixin
@@ -14,10 +14,7 @@ class CheckoutView(CartMixin, View):
self.request = request self.request = request
if not self.positions and "async_id" not in request.GET: if not self.positions and "async_id" not in request.GET:
messages.error(request, _("Your cart is empty")) messages.error(request, _("Your cart is empty"))
return redirect(reverse('presale:event.index', kwargs={ return redirect(eventreverse(self.request.event, 'presale:event.index'))
'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug
}))
flow = get_checkout_flow(self.request.event) flow = get_checkout_flow(self.request.event)
for step in flow: for step in flow:
+5 -12
View File
@@ -14,6 +14,7 @@ from pretix.base.services.tickets import generate
from pretix.base.signals import ( from pretix.base.signals import (
register_payment_providers, register_ticket_outputs, register_payment_providers, register_ticket_outputs,
) )
from pretix.multidomain.urlreverse import eventreverse
from pretix.presale.views import CartMixin, EventViewMixin from pretix.presale.views import CartMixin, EventViewMixin
from pretix.presale.views.questions import QuestionsViewMixin from pretix.presale.views.questions import QuestionsViewMixin
@@ -36,9 +37,7 @@ class OrderDetailMixin:
return provider return provider
def get_order_url(self): def get_order_url(self):
return reverse('presale:event.order', kwargs={ return eventreverse(self.request.event, 'presale:event.order', kwargs={
'event': self.request.event.slug,
'organizer': self.request.event.organizer.slug,
'order': self.order.code, 'order': self.order.code,
'secret': self.order.secret 'secret': self.order.secret
}) })
@@ -132,9 +131,7 @@ class OrderPay(EventViewMixin, OrderDetailMixin, TemplateView):
return self.payment_provider.payment_form_render(self.request) return self.payment_provider.payment_form_render(self.request)
def get_confirm_url(self): def get_confirm_url(self):
return reverse('presale:event.order.pay.confirm', kwargs={ return eventreverse(self.request.event, 'presale:event.order.pay.confirm', kwargs={
'event': self.request.event.slug,
'organizer': self.request.event.organizer.slug,
'order': self.order.code, 'order': self.order.code,
'secret': self.order.secret 'secret': self.order.secret
}) })
@@ -169,9 +166,7 @@ class OrderPayDo(EventViewMixin, OrderDetailMixin, TemplateView):
return ctx return ctx
def get_payment_url(self): def get_payment_url(self):
return reverse('presale:event.order.pay', kwargs={ return eventreverse(self.request.event, 'presale:event.order.pay', kwargs={
'event': self.request.event.slug,
'organizer': self.request.event.organizer.slug,
'order': self.order.code, 'order': self.order.code,
'secret': self.order.secret 'secret': self.order.secret
}) })
@@ -194,9 +189,7 @@ class OrderPayComplete(EventViewMixin, OrderDetailMixin, View):
return redirect(resp or self.get_order_url() + '?paid=yes') return redirect(resp or self.get_order_url() + '?paid=yes')
def get_payment_url(self): def get_payment_url(self):
return reverse('presale:event.order.pay', kwargs={ return eventreverse(self.request.event, 'presale:event.order.pay', kwargs={
'event': self.request.event.slug,
'organizer': self.request.event.organizer.slug,
'order': self.order.code, 'order': self.order.code,
'secret': self.order.secret 'secret': self.order.secret
}) })
+9 -8
View File
@@ -65,7 +65,7 @@ PRETIX_PLUGINS_DEFAULT = config.get('pretix', 'plugins_default',
DEFAULT_CURRENCY = config.get('pretix', 'currency', fallback='EUR') DEFAULT_CURRENCY = config.get('pretix', 'currency', fallback='EUR')
ALLOWED_HOSTS = config.get('django', 'hosts', fallback='localhost').split(',') ALLOWED_HOSTS = ['*']
LANGUAGE_CODE = config.get('locale', 'default', fallback='en') LANGUAGE_CODE = config.get('locale', 'default', fallback='en')
TIME_ZONE = config.get('locale', 'timezone', fallback='UTC') TIME_ZONE = config.get('locale', 'timezone', fallback='UTC')
@@ -79,11 +79,6 @@ EMAIL_HOST_PASSWORD = config.get('mail', 'password', fallback='')
ADMINS = [('Admin', n) for n in config.get('mail', 'admins', fallback='').split(",") if n] ADMINS = [('Admin', n) for n in config.get('mail', 'admins', fallback='').split(",") if n]
SESSION_COOKIE_SECURE = SESSION_COOKIE_HTTPONLY = config.getboolean(
'pretix', 'securecookie', fallback=False)
LANGUAGE_COOKIE_DOMAIN = SESSION_COOKIE_DOMAIN = CSRF_COOKIE_DOMAIN = config.get(
'pretix', 'cookiedomain', fallback=None)
CACHES = { CACHES = {
'default': { 'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
@@ -98,6 +93,8 @@ if HAS_MEMCACHED:
'LOCATION': config.get('memcached', 'location'), 'LOCATION': config.get('memcached', 'location'),
} }
SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"
HAS_REDIS = config.has_option('redis', 'location') HAS_REDIS = config.has_option('redis', 'location')
if HAS_REDIS: if HAS_REDIS:
CACHES['redis'] = { CACHES['redis'] = {
@@ -126,6 +123,7 @@ STATIC_ROOT = '_static'
SESSION_COOKIE_NAME = 'pretix_session' SESSION_COOKIE_NAME = 'pretix_session'
LANGUAGE_COOKIE_NAME = 'pretix_language' LANGUAGE_COOKIE_NAME = 'pretix_language'
CSRF_COOKIE_NAME = 'pretix_csrftoken' CSRF_COOKIE_NAME = 'pretix_csrftoken'
SESSION_COOKIE_HTTPONLY = True
INSTALLED_APPS = ( INSTALLED_APPS = (
'django.contrib.auth', 'django.contrib.auth',
@@ -136,6 +134,7 @@ INSTALLED_APPS = (
'pretix.base', 'pretix.base',
'pretix.control', 'pretix.control',
'pretix.presale', 'pretix.presale',
'pretix.multidomain',
'compressor', 'compressor',
'bootstrap3', 'bootstrap3',
'debug_toolbar.apps.DebugToolbarConfig', 'debug_toolbar.apps.DebugToolbarConfig',
@@ -153,9 +152,10 @@ INSTALLED_APPS = (
) )
MIDDLEWARE_CLASSES = ( MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware', 'pretix.multidomain.middlewares.MultiDomainMiddleware',
'pretix.multidomain.middlewares.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'pretix.multidomain.middlewares.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'debug_toolbar.middleware.DebugToolbarMiddleware', 'debug_toolbar.middleware.DebugToolbarMiddleware',
@@ -272,6 +272,7 @@ MESSAGE_TAGS = {
messages.WARNING: 'alert-warning', messages.WARNING: 'alert-warning',
messages.SUCCESS: 'alert-success', messages.SUCCESS: 'alert-success',
} }
MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
THUMBNAIL_ALIASES = { THUMBNAIL_ALIASES = {
'': { '': {
+1
View File
@@ -31,6 +31,7 @@ for app in apps.get_app_configs():
pluginpatterns.append( pluginpatterns.append(
url(r'', include(urlmod, namespace=app.label)) url(r'', include(urlmod, namespace=app.label))
) )
urlpatterns.append( urlpatterns.append(
url(r'', include(pluginpatterns, namespace='plugins')) url(r'', include(pluginpatterns, namespace='plugins'))
) )
+35
View File
@@ -0,0 +1,35 @@
from django.db import DEFAULT_DB_ALIAS, connections
from django.test.utils import CaptureQueriesContext
# Inspired by /django/test/testcases.py
# but copied over to work without the unit test module
class _AssertNumQueriesContext(CaptureQueriesContext):
def __init__(self, num, connection):
self.num = num
super(_AssertNumQueriesContext, self).__init__(connection)
def __exit__(self, exc_type, exc_value, traceback):
super(_AssertNumQueriesContext, self).__exit__(exc_type, exc_value, traceback)
if exc_type is not None:
return
executed = len(self)
assert executed == self.num, "%d queries executed, %d expected\nCaptured queries were:\n%s" % (
executed, self.num,
'\n'.join(
query['sql'] for query in self.captured_queries
)
)
def assert_num_queries(num, func=None, *args, **kwargs):
using = kwargs.pop("using", DEFAULT_DB_ALIAS)
conn = connections[using]
context = _AssertNumQueriesContext(num, conn)
if func is None:
return context
with context:
func(*args, **kwargs)
View File
+88
View File
@@ -0,0 +1,88 @@
import pytest
from django.conf import settings
from django.template import Context, Template
from django.utils.timezone import now
from pretix.base.models import Event, Organizer
from pretix.multidomain.models import KnownDomain
@pytest.fixture
def env():
o = Organizer.objects.create(name='MRMCD', slug='mrmcd')
event = Event.objects.create(
organizer=o, name='MRMCD2015', slug='2015',
date_from=now()
)
settings.SITE_URL = 'http://example.com'
return o, event
@pytest.mark.django_db
def test_control_only_on_main_domain(env, client):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
r = client.get('/control/login', HTTP_HOST='foobar')
assert r.status_code == 302
assert r['Location'] == 'http://example.com/control/login'
@pytest.mark.django_db
def test_unknown_domain(env, client):
r = client.get('/control/login', HTTP_HOST='foobar')
assert r.status_code == 400
@pytest.mark.django_db
def test_event_on_custom_domain(env, client):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
r = client.get('/2015/', HTTP_HOST='foobar')
assert r.status_code == 200
@pytest.mark.django_db
def test_event_with_custom_domain_on_main_domain(env, client):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
r = client.get('/mrmcd/2015/', HTTP_HOST='example.com')
assert r.status_code == 302
assert r['Location'] == 'http://foobar/2015/'
@pytest.mark.django_db
def test_event_on_custom_domain_only_with_wrong_organizer(env, client):
organizer2 = Organizer.objects.create(name='Dummy', slug='dummy')
Event.objects.create(
organizer=organizer2, name='D1234', slug='1234',
date_from=now()
)
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
r = client.get('/dummy/1234/', HTTP_HOST='foobar')
assert r.status_code == 404
@pytest.mark.django_db
def test_unknown_event_on_custom_domain(env, client):
organizer2 = Organizer.objects.create(name='Dummy', slug='dummy')
Event.objects.create(
organizer=organizer2, name='D1234', slug='1234',
date_from=now()
)
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
r = client.get('/1234/', HTTP_HOST='foobar')
assert r.status_code == 404
@pytest.mark.django_db
def test_cookie_domain_on_custom_domain(env, client):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
r = client.get('/2015/', HTTP_HOST='foobar')
assert r.status_code == 200
assert r.client.cookies['pretix_csrftoken']['domain'] == 'foobar'
assert r.client.cookies['pretix_session']['domain'] == 'foobar'
@pytest.mark.django_db
def test_cookie_domain_on_main_domain(env, client):
r = client.get('/mrmcd/2015/', HTTP_HOST='example.com')
assert r.status_code == 200
assert r.client.cookies['pretix_csrftoken']['domain'] == 'example.com'
assert r.client.cookies['pretix_session']['domain'] == 'example.com'
+56
View File
@@ -0,0 +1,56 @@
import pytest
from django.conf import settings
from django.template import Context, Template
from django.utils.timezone import now
from pretix.base.models import Event, Organizer
from pretix.multidomain.models import KnownDomain
@pytest.fixture
def env():
o = Organizer.objects.create(name='MRMCD', slug='mrmcd')
event = Event.objects.create(
organizer=o, name='MRMCD2015', slug='2015',
date_from=now()
)
settings.SITE_URL = 'http://example.com'
return o, event
TEMPLATE_FRONT_PAGE = Template("{% load eventurl %} {% eventurl event 'presale:event.index' %}")
TEMPLATE_KWARGS = Template("{% load eventurl %} {% eventurl event 'presale:event.checkout' step='payment' %}")
@pytest.mark.django_db
def test_event_main_domain_front_page(env):
rendered = TEMPLATE_FRONT_PAGE.render(Context({
'event': env[1]
})).strip()
assert rendered == '/mrmcd/2015/'
@pytest.mark.django_db
def test_event_custom_domain_front_page(env):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
rendered = TEMPLATE_FRONT_PAGE.render(Context({
'event': env[1]
})).strip()
assert rendered == 'http://foobar/2015/'
@pytest.mark.django_db
def test_event_main_domain_kwargs(env):
rendered = TEMPLATE_KWARGS.render(Context({
'event': env[1]
})).strip()
assert rendered == '/mrmcd/2015/checkout/payment/'
@pytest.mark.django_db
def test_event_custom_domain_kwargs(env):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
rendered = TEMPLATE_KWARGS.render(Context({
'event': env[1]
})).strip()
assert rendered == 'http://foobar/2015/checkout/payment/'
+96
View File
@@ -0,0 +1,96 @@
import pytest
from django.conf import settings
from django.utils.timezone import now
from tests import assert_num_queries
from pretix.base.models import Event, Organizer
from pretix.multidomain.models import KnownDomain
from pretix.multidomain.urlreverse import build_absolute_uri, eventreverse
@pytest.fixture
def env():
o = Organizer.objects.create(name='MRMCD', slug='mrmcd')
event = Event.objects.create(
organizer=o, name='MRMCD2015', slug='2015',
date_from=now()
)
settings.SITE_URL = 'http://example.com'
return o, event
@pytest.mark.django_db
def test_event_main_domain_front_page(env):
assert eventreverse(env[1], 'presale:event.index') == '/mrmcd/2015/'
@pytest.mark.django_db
def test_event_custom_domain_kwargs(env):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
assert eventreverse(env[1], 'presale:event.checkout', {'step': 'payment'}) == 'http://foobar/2015/checkout/payment/'
@pytest.mark.django_db
def test_event_main_domain_kwargs(env):
assert eventreverse(env[1], 'presale:event.checkout', {'step': 'payment'}) == '/mrmcd/2015/checkout/payment/'
@pytest.mark.django_db
def test_event_custom_domain_front_page(env):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
assert eventreverse(env[1], 'presale:event.index') == 'http://foobar/2015/'
@pytest.mark.django_db
def test_event_custom_domain_keep_port(env):
settings.SITE_URL = 'http://example.com:8081'
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
assert eventreverse(env[1], 'presale:event.index') == 'http://foobar:8081/2015/'
@pytest.mark.django_db
def test_event_custom_domain_keep_scheme(env):
settings.SITE_URL = 'https://example.com'
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
assert eventreverse(env[1], 'presale:event.index') == 'https://foobar/2015/'
@pytest.mark.django_db
def test_event_main_domain_cache(env):
env[0].get_cache().clear()
with assert_num_queries(1):
eventreverse(env[1], 'presale:event.index')
with assert_num_queries(0):
eventreverse(env[1], 'presale:event.index')
@pytest.mark.django_db
def test_event_custom_domain_cache(env):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
env[0].get_cache().clear()
with assert_num_queries(1):
eventreverse(env[1], 'presale:event.index')
with assert_num_queries(0):
eventreverse(env[1], 'presale:event.index')
@pytest.mark.django_db
def test_event_custom_domain_cache_clear(env):
kd = KnownDomain.objects.create(domainname='foobar', organizer=env[0])
env[0].get_cache().clear()
with assert_num_queries(1):
eventreverse(env[1], 'presale:event.index')
kd.delete()
with assert_num_queries(1):
eventreverse(env[1], 'presale:event.index')
@pytest.mark.django_db
def test_event_main_domain_absolute(env):
assert build_absolute_uri(env[1], 'presale:event.index') == 'http://example.com/mrmcd/2015/'
@pytest.mark.django_db
def test_event_custom_domain_absolute(env):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
assert build_absolute_uri(env[1], 'presale:event.index') == 'http://foobar/2015/'
+1 -1
View File
@@ -34,7 +34,7 @@ class CheckoutTestCase(TestCase):
self._set_session('email', 'admin@localhost') self._set_session('email', 'admin@localhost')
def test_empty_cart(self): def test_empty_cart(self):
response = self.client.get('/%s/%s/checkout/' % (self.orga.slug, self.event.slug), follow=True) response = self.client.get('/%s/%s/checkout/start' % (self.orga.slug, self.event.slug), follow=True)
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug), self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
target_status_code=200) target_status_code=200)