Allow to use a custom domain per event (#1617)

* Drop support for maindomain_urls/subdomain_urls in plugins

* Allow to use a custom domain per event

* Fix bug when manually saving domains

* Fix custom domains in debugging

* Fix middleware

* Fix middleware again, update docs
This commit is contained in:
Raphael Michel
2020-03-23 13:03:14 +01:00
committed by GitHub
parent ac2fc2de5c
commit 7e9c9beace
19 changed files with 386 additions and 89 deletions
+5 -12
View File
@@ -14,30 +14,23 @@ and with pretix, you can do this. On this page, you find out the necessary steps
With the pretix.eu hosted service
---------------------------------
Step 1: DNS Configuration
#########################
Go to "Organizers" in the backend and select your organizer account. Then, go to "Settings" and "Custom Domain".
This page will show you instructions on how to set up your own domain. Basically, it works like this:
Go to the website of the provider you registered your domain name with. Look for the "DNS" settings page in their
interface. Unfortunately, we can't tell you exactly how that is named and how it looks, since it is different for every
domain provider.
Use this interface to add a new subdomain record, e.g. ``tickets`` of the type ``CNAME`` (might also be called "alias").
The value of the record should be ``www.pretix.eu``.
Step 2: Wait for the DNS entry to propagate
###########################################
The value of the record should be the one shown on the "Custom Domain" page in pretix' backend.
Submit your changes and wait a bit, it can regularly take up to three hours for DNS changes to propagate to the caches
of all DNS servers. You can try checking by accessing your new subdomain, ``http://tickets.awesomepartycorp.com``.
If DNS was changed successfully, you should see a SSL certificate error. If you ignore the error and access the page
anyways, you should get a pretix-themed error page with the headline "Unknown domain".
Step 3: Tell us
###############
Write an email to support@pretix.eu, naming your new domain and your organizer account. We will then generate a SSL
certificate for you (for free!) and configure the domain.
Now, tell us about your domain on the "Custom Domain" page to get started.
With a custom pretix installation
---------------------------------
+7 -2
View File
@@ -15,7 +15,9 @@ from django.utils.translation.trans_real import (
)
from pretix.base.settings import GlobalSettingsObject
from pretix.multidomain.urlreverse import get_domain
from pretix.multidomain.urlreverse import (
get_event_domain, get_organizer_domain,
)
_supported = None
@@ -231,7 +233,10 @@ class SecurityMiddleware(MiddlewareMixin):
dynamicdomain += " " + settings.SITE_URL
if hasattr(request, 'organizer') and request.organizer:
domain = get_domain(request.organizer)
if hasattr(request, 'event') and request.event:
domain = get_event_domain(request.event, fallback=True)
else:
domain = get_organizer_domain(request.organizer)
if domain:
siteurlsplit = urlsplit(settings.SITE_URL)
if siteurlsplit.port and siteurlsplit.port not in (80, 443):
+52 -1
View File
@@ -1,4 +1,4 @@
from urllib.parse import urlencode
from urllib.parse import urlencode, urlparse
from django import forms
from django.conf import settings
@@ -32,6 +32,7 @@ from pretix.control.forms import (
SplitDateTimeField, SplitDateTimePickerWidget,
)
from pretix.control.forms.widgets import Select2
from pretix.multidomain.models import KnownDomain
from pretix.multidomain.urlreverse import build_absolute_uri
from pretix.plugins.banktransfer.payment import BankTransfer
from pretix.presale.style import get_fonts
@@ -291,6 +292,15 @@ class EventUpdateForm(I18nModelForm):
def __init__(self, *args, **kwargs):
self.change_slug = kwargs.pop('change_slug', False)
self.domain = kwargs.pop('domain', False)
kwargs.setdefault('initial', {})
self.instance = kwargs['instance']
if self.domain and self.instance:
initial_domain = self.instance.domains.first()
if initial_domain:
kwargs['initial'].setdefault('domain', initial_domain.domainname)
super().__init__(*args, **kwargs)
if not self.change_slug:
self.fields['slug'].widget.attrs['readonly'] = 'readonly'
@@ -298,6 +308,47 @@ class EventUpdateForm(I18nModelForm):
self.fields['location'].widget.attrs['placeholder'] = _(
'Sample Conference Center\nHeidelberg, Germany'
)
if self.domain:
self.fields['domain'] = forms.CharField(
max_length=255,
label=_('Custom domain'),
required=False,
help_text=_('You need to configure the custom domain in the webserver beforehand.')
)
def clean_domain(self):
d = self.cleaned_data['domain']
if d:
if d == urlparse(settings.SITE_URL).hostname:
raise ValidationError(
_('You cannot choose the base domain of this installation.')
)
if KnownDomain.objects.filter(domainname=d).exclude(event=self.instance.pk).exists():
raise ValidationError(
_('This domain is already in use for a different event or organizer.')
)
return d
def save(self, commit=True):
instance = super().save(commit)
if self.domain:
current_domain = instance.domains.first()
if self.cleaned_data['domain']:
if current_domain and current_domain.domainname != self.cleaned_data['domain']:
current_domain.delete()
KnownDomain.objects.create(
organizer=instance.organizer, event=instance, domainname=self.cleaned_data['domain']
)
elif not current_domain:
KnownDomain.objects.create(
organizer=instance.organizer, event=instance, domainname=self.cleaned_data['domain']
)
elif current_domain:
current_domain.delete()
instance.cache.clear()
return instance
def clean_slug(self):
if self.change_slug:
+3 -2
View File
@@ -95,9 +95,10 @@ class OrganizerUpdateForm(OrganizerForm):
raise ValidationError(
_('You cannot choose the base domain of this installation.')
)
if KnownDomain.objects.filter(domainname=d).exclude(organizer=self.instance.pk).exists():
if KnownDomain.objects.filter(domainname=d).exclude(organizer=self.instance.pk,
event__isnull=True).exists():
raise ValidationError(
_('This domain is already in use for a different organizer.')
_('This domain is already in use for a different event or organizer.')
)
return d
@@ -19,6 +19,9 @@
<legend>{% trans "Basics" %}</legend>
{% bootstrap_field form.name layout="control" %}
{% bootstrap_field form.slug layout="control" %}
{% if form.domain %}
{% bootstrap_field form.domain layout="control" %}
{% endif %}
{% bootstrap_field form.date_from layout="control" %}
{% bootstrap_field form.date_to layout="control" %}
<div class="geodata-section">
+7 -3
View File
@@ -48,7 +48,7 @@ from pretix.control.forms.event import (
from pretix.control.permissions import EventPermissionRequiredMixin
from pretix.control.views.user import RecentAuthenticationRequiredMixin
from pretix.helpers.database import rolledback_transaction
from pretix.multidomain.urlreverse import get_domain
from pretix.multidomain.urlreverse import get_event_domain
from pretix.plugins.stripe.payment import StripeSettingsHolder
from pretix.presale.style import regenerate_css
@@ -162,7 +162,10 @@ class EventUpdate(DecoupleMixin, EventSettingsViewMixin, EventPermissionRequired
change_css = True
if form.has_changed():
self.request.event.log_action('pretix.event.changed', user=self.request.user, data={
k: getattr(self.request.event, k) for k in form.changed_data
k: (form.cleaned_data.get(k).name
if isinstance(form.cleaned_data.get(k), File)
else form.cleaned_data.get(k))
for k in form.changed_data
})
if change_css:
@@ -184,6 +187,7 @@ class EventUpdate(DecoupleMixin, EventSettingsViewMixin, EventPermissionRequired
kwargs = super().get_form_kwargs()
if self.request.user.has_active_staff_session(self.request.session.session_key):
kwargs['change_slug'] = True
kwargs['domain'] = True
return kwargs
def post(self, request, *args, **kwargs):
@@ -1208,7 +1212,7 @@ class WidgetSettings(EventSettingsViewMixin, EventPermissionRequiredMixin, FormV
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['urlprefix'] = settings.SITE_URL
domain = get_domain(self.request.organizer)
domain = get_event_domain(self.request.event, fallback=True)
if domain:
siteurlsplit = urlsplit(settings.SITE_URL)
if siteurlsplit.port and siteurlsplit.port not in (80, 443):
@@ -0,0 +1,35 @@
import importlib.util
from django.apps import apps
from django.conf.urls import include, url
from pretix.multidomain.plugin_handler import plugin_event_urls
from pretix.presale.urls import event_patterns, locale_patterns
from pretix.urls import common_patterns
presale_patterns = [
url(r'', include((locale_patterns + [
url(r'', include(event_patterns)),
], 'presale')))
]
raw_plugin_patterns = []
for app in apps.get_app_configs():
if hasattr(app, 'PretixPluginMeta'):
if importlib.util.find_spec(app.name + '.urls'):
urlmod = importlib.import_module(app.name + '.urls')
if hasattr(urlmod, 'event_patterns'):
patterns = plugin_event_urls(urlmod.event_patterns, plugin=app.name)
raw_plugin_patterns.append(
url(r'', include((patterns, app.label)))
)
plugin_patterns = [
url(r'', include((raw_plugin_patterns, 'plugins')))
]
# The presale namespace comes last, because it contains a wildcard catch
urlpatterns = common_patterns + plugin_patterns + presale_patterns
handler404 = 'pretix.base.views.errors.page_not_found'
handler500 = 'pretix.base.views.errors.server_error'
@@ -1,5 +1,4 @@
import importlib.util
import warnings
from django.apps import apps
from django.conf.urls import include, url
@@ -38,14 +37,6 @@ for app in apps.get_app_configs():
raw_plugin_patterns.append(
url(r'', include((single_plugin_patterns, app.label)))
)
elif importlib.util.find_spec(app.name + '.maindomain_urls'): # noqa
warnings.warn('Please put your config in an \'urls\' module using the urlpatterns and event_patterns '
'attribute. Support for maindomain_urls in plugins will be dropped in the future.',
DeprecationWarning)
urlmod = importlib.import_module(app.name + '.maindomain_urls')
raw_plugin_patterns.append(
url(r'', include((urlmod, app.label)))
)
plugin_patterns = [
url(r'', include((raw_plugin_patterns, 'plugins')))
+25 -16
View File
@@ -14,7 +14,7 @@ from django.utils.cache import patch_vary_headers
from django.utils.deprecation import MiddlewareMixin
from django.utils.http import http_date
from pretix.base.models import Organizer
from pretix.base.models import Event, Organizer
from pretix.helpers.cookies import set_cookie_without_samesite
from pretix.multidomain.models import KnownDomain
@@ -37,33 +37,42 @@ class MultiDomainMiddleware(MiddlewareMixin):
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
request.port = int(port) if port else None
request.host = domain
if domain == default_domain:
request.urlconf = "pretix.multidomain.maindomain_urlconf"
elif domain:
cached = cache.get('pretix_multidomain_instance_{}'.format(domain))
orga = cache.get('pretix_multidomain_organizer_instance_{}'.format(domain))
if orga is None:
if cached is None:
try:
kd = KnownDomain.objects.select_related('organizer').get(domainname=domain) # noqa
kd = KnownDomain.objects.select_related('organizer', 'event').get(domainname=domain) # noqa
orga = kd.organizer
event = kd.event
except KnownDomain.DoesNotExist:
orga = False
cache.set('pretix_multidomain_organizer_instance_{}'.format(domain), orga, 3600)
event = False
cache.set('pretix_multidomain_instance_{}'.format(domain), (orga, event), 3600)
else:
orga, event = cached
if orga:
if event:
request.event_domain = True
request.organizer = orga if isinstance(orga, Organizer) else Organizer.objects.get(pk=orga)
request.event = event if isinstance(event, Event) else orga.events.get(pk=event)
request.urlconf = "pretix.multidomain.event_domain_urlconf"
elif orga:
request.organizer_domain = True
request.organizer = orga if isinstance(orga, Organizer) else Organizer.objects.get(pk=orga)
request.urlconf = "pretix.multidomain.subdomain_urlconf"
request.urlconf = "pretix.multidomain.organizer_domain_urlconf"
elif settings.DEBUG or domain in LOCAL_HOST_NAMES:
request.urlconf = "pretix.multidomain.maindomain_urlconf"
else:
if settings.DEBUG or domain in LOCAL_HOST_NAMES or domain == default_domain:
request.urlconf = "pretix.multidomain.maindomain_urlconf"
else:
raise DisallowedHost("Unknown host: %r" % host)
raise DisallowedHost("Unknown host: %r" % host)
else:
raise DisallowedHost("Invalid HTTP_HOST header: %r." % host)
# We need to manually set the urlconf for the whole thread. Normally, Django's basic request
# We need to manually set the urlconf for the whole thread. Normally, Django's basic request handling
# would do this for us, but we already need it in place for the other middlewares.
set_urlconf(request.urlconf)
@@ -0,0 +1,20 @@
# Generated by Django 2.2.11 on 2020-03-20 14:05
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0146_giftcardtransaction_text'),
('pretixmultidomain', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='knowndomain',
name='event',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='domains', to='pretixbase.Event'),
),
]
+19 -5
View File
@@ -1,13 +1,15 @@
from django.core.cache import cache
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django_scopes import scopes_disabled
from pretix.base.models import Organizer
from pretix.base.models import Event, Organizer
class KnownDomain(models.Model):
domainname = models.CharField(max_length=255, primary_key=True)
organizer = models.ForeignKey(Organizer, blank=True, null=True, related_name='domains', on_delete=models.CASCADE)
event = models.ForeignKey(Event, blank=True, null=True, related_name='domains', on_delete=models.PROTECT)
class Meta:
verbose_name = _("Known domain")
@@ -16,16 +18,28 @@ class KnownDomain(models.Model):
def __str__(self):
return self.domainname
@scopes_disabled()
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.organizer:
if self.event:
self.event.get_cache().clear()
elif self.organizer:
self.organizer.get_cache().clear()
for event in self.organizer.events.all():
event.get_cache().clear()
cache.delete('pretix_multidomain_organizer_{}'.format(self.domainname))
cache.delete('pretix_multidomain_organizer_instance_{}'.format(self.domainname))
cache.delete('pretix_multidomain_instance_{}'.format(self.domainname))
cache.delete('pretix_multidomain_event_{}'.format(self.domainname))
@scopes_disabled()
def delete(self, *args, **kwargs):
if self.organizer:
if self.event:
self.event.get_cache().clear()
elif self.organizer:
self.organizer.get_cache().clear()
for event in self.organizer.events.all():
event.get_cache().clear()
cache.delete('pretix_multidomain_organizer_{}'.format(self.domainname))
cache.delete('pretix_multidomain_organizer_instance_{}'.format(self.domainname))
cache.delete('pretix_multidomain_instance_{}'.format(self.domainname))
cache.delete('pretix_multidomain_event_{}'.format(self.domainname))
super().delete(*args, **kwargs)
@@ -1,5 +1,4 @@
import importlib.util
import warnings
from django.apps import apps
from django.conf.urls import include, url
@@ -33,15 +32,6 @@ for app in apps.get_app_configs():
url(r'', include((patterns, app.label)))
)
elif importlib.util.find_spec(app.name + '.subdomain_urls'): # noqa
warnings.warn('Please put your config in an \'urls\' module using the event_patterns '
'attribute. Support for subdomain_urls in plugins will be dropped in the future.',
DeprecationWarning)
urlmod = importlib.import_module(app.name + '.subdomain_urls')
raw_plugin_patterns.append(
url(r'', include((urlmod, app.label)))
)
plugin_patterns = [
url(r'', include((raw_plugin_patterns, 'plugins')))
]
+49 -7
View File
@@ -1,15 +1,48 @@
from urllib.parse import urljoin, urlsplit
from django.conf import settings
from django.db.models import Q
from django.urls import reverse
from pretix.base.models import Event, Organizer
from .models import KnownDomain
def get_domain(organizer):
def get_event_domain(event, fallback=False, return_info=False):
assert isinstance(event, Event)
suffix = ('_fallback' if fallback else '') + ('_info' if return_info else '')
domain = getattr(event, '_cached_domain' + suffix, None) or event.cache.get('domain' + suffix)
if domain is None:
domain = None, None
if fallback:
domains = KnownDomain.objects.filter(
Q(event=event) | Q(organizer_id=event.organizer_id, event__isnull=True)
)
domains_event = [d for d in domains if d.event_id == event.pk]
domains_org = [d for d in domains if not d.event_id]
if domains_event:
domain = domains_event[0].domainname, "event"
elif domains_org:
domain = domains_org[0].domainname, "organizer"
else:
domains = event.domains.all()
domain = domains[0].domainname if domains else None, "event"
event.cache.set('domain' + suffix, domain or 'none')
setattr(event, '_cached_domain' + suffix, domain or 'none')
elif domain == 'none':
setattr(event, '_cached_domain' + suffix, 'none')
domain = None, None
else:
setattr(event, '_cached_domain' + suffix, domain)
return domain if return_info or not isinstance(domain, tuple) else domain[0]
def get_organizer_domain(organizer):
assert isinstance(organizer, Organizer)
domain = getattr(organizer, '_cached_domain', None) or organizer.cache.get('domain')
if domain is None:
domains = organizer.domains.all()
domains = organizer.domains.filter(event__isnull=True)
domain = domains[0].domainname if domains else None
organizer.cache.set('domain', domain or 'none')
organizer._cached_domain = domain or 'none'
@@ -45,7 +78,7 @@ def mainreverse(name, kwargs=None):
def eventreverse(obj, 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.
organizers or events might have their own (sub)domain instead of a subpath.
Non-keyword arguments are not supported as we want do discourage using them for better
readability.
@@ -58,7 +91,7 @@ def eventreverse(obj, name, kwargs=None):
needed.
:returns: An absolute URL (including scheme and host) as a string
"""
from pretix.multidomain import subdomain_urlconf, maindomain_urlconf
from pretix.multidomain import event_domain_urlconf, organizer_domain_urlconf, maindomain_urlconf
c = None
if not kwargs:
@@ -69,18 +102,27 @@ def eventreverse(obj, name, kwargs=None):
kwargs = kwargs or {}
if isinstance(obj, Event):
kwargs['event'] = obj.slug
organizer = obj.organizer
event = obj
kwargs['event'] = obj.slug
elif isinstance(obj, Organizer):
organizer = obj
event = None
else:
raise TypeError('obj should be Event or Organizer')
domain = get_domain(organizer)
if event:
domain, domaintype = get_event_domain(obj, fallback=True, return_info=True)
else:
domain, domaintype = get_organizer_domain(organizer), "organizer"
if domain:
if domaintype == "event" and 'event' in kwargs:
del kwargs['event']
if 'organizer' in kwargs:
del kwargs['organizer']
path = reverse(name, kwargs=kwargs, urlconf=subdomain_urlconf)
path = reverse(name, kwargs=kwargs, urlconf=event_domain_urlconf if domaintype == "event" else organizer_domain_urlconf)
siteurlsplit = urlsplit(settings.SITE_URL)
if siteurlsplit.port and siteurlsplit.port not in (80, 443):
domain = '%s:%d' % (domain, siteurlsplit.port)
+2 -2
View File
@@ -6,14 +6,14 @@ from django.conf import settings
from pretix.base.services.tasks import EventTask
from pretix.celery_app import app
from pretix.multidomain.urlreverse import get_domain
from pretix.multidomain.urlreverse import get_event_domain
from pretix.plugins.stripe.models import RegisteredApplePayDomain
logger = logging.getLogger(__name__)
def get_domain_for_event(event):
domain = get_domain(event.organizer)
domain = get_event_domain(event, fallback=True)
if not domain:
siteurlsplit = urlsplit(settings.SITE_URL)
return siteurlsplit.hostname
+1 -1
View File
@@ -18,7 +18,7 @@ class EventMiddleware:
if url.namespace != 'presale':
return self.get_response(request)
if 'organizer' in url.kwargs or 'event' in url.kwargs:
if 'organizer' in url.kwargs or 'event' in url.kwargs or getattr(request, 'event_domain', False):
redirect = _detect_event(request, require_live=url.url_name != 'event.widget.productlist')
if redirect:
return redirect
+7 -2
View File
@@ -20,7 +20,9 @@ from pretix.base.services.tasks import (
TransactionAwareProfiledEventTask, TransactionAwareTask,
)
from pretix.celery_app import app
from pretix.multidomain.urlreverse import get_domain
from pretix.multidomain.urlreverse import (
get_event_domain, get_organizer_domain,
)
from pretix.presale.signals import sass_postamble, sass_preamble
logger = logging.getLogger('pretix.presale.style')
@@ -33,7 +35,10 @@ def compile_scss(object, file="main.scss", fonts=True):
def static(path):
sp = _static(path)
if not settings.MEDIA_URL.startswith("/") and sp.startswith("/"):
domain = get_domain(object.organizer if isinstance(object, Event) else object)
if isinstance(object, Event):
domain = get_event_domain(object, fallback=True)
else:
domain = get_organizer_domain(object)
if domain:
siteurlsplit = urlsplit(settings.SITE_URL)
if siteurlsplit.port and siteurlsplit.port not in (80, 443):
+28 -3
View File
@@ -14,7 +14,9 @@ from django_scopes import scope
from pretix.base.channels import WebshopSalesChannel
from pretix.base.middleware import LocaleMiddleware
from pretix.base.models import Event, Organizer
from pretix.multidomain.urlreverse import get_domain
from pretix.multidomain.urlreverse import (
get_event_domain, get_organizer_domain,
)
from pretix.presale.signals import process_request, process_response
SessionStore = import_module(settings.SESSION_ENGINE).SessionStore
@@ -31,7 +33,10 @@ def _detect_event(request, require_live=True, require_plugin=None):
url = resolve(request.path_info)
try:
if hasattr(request, 'organizer_domain'):
if hasattr(request, 'event_domain'):
# We are on an event's custom domain
pass
elif hasattr(request, 'organizer_domain'):
# We are on an organizer's custom domain
if 'organizer' in url.kwargs and url.kwargs['organizer']:
if url.kwargs['organizer'] != request.organizer.slug:
@@ -44,6 +49,16 @@ def _detect_event(request, require_live=True, require_plugin=None):
organizer=request.organizer,
)
request.organizer = request.organizer
# If this event has a custom domain, send the user there
domain = get_event_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]
r = redirect(urljoin('%s://%s' % (request.scheme, domain), path))
r['Access-Control-Allow-Origin'] = '*'
return r
else:
# We are on our main domain
if 'event' in url.kwargs and 'organizer' in url.kwargs:
@@ -55,6 +70,16 @@ def _detect_event(request, require_live=True, require_plugin=None):
organizer__slug=url.kwargs['organizer']
)
request.organizer = request.event.organizer
# If this event has a custom domain, send the user there
domain = get_event_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("/", 3)[-1]
r = redirect(urljoin('%s://%s' % (request.scheme, domain), path))
r['Access-Control-Allow-Origin'] = '*'
return r
elif 'organizer' in url.kwargs:
request.organizer = Organizer.objects.using(db).get(
slug=url.kwargs['organizer']
@@ -63,7 +88,7 @@ def _detect_event(request, require_live=True, require_plugin=None):
raise Http404()
# If this organizer has a custom domain, send the user there
domain = get_domain(request.organizer)
domain = get_organizer_domain(request.organizer)
if domain:
if request.port and request.port not in (80, 443):
domain = '%s:%d' % (domain, request.port)
+49 -9
View File
@@ -26,6 +26,11 @@ def test_control_only_on_main_domain(env, client):
assert r.status_code == 302
assert r['Location'] == 'http://example.com/control/login'
KnownDomain.objects.create(domainname='barfoo', organizer=env[0], event=env[1])
r = client.get('/control/login', HTTP_HOST='barfoo')
assert r.status_code == 302
assert r['Location'] == 'http://example.com/control/login'
@pytest.mark.django_db
def test_append_slash(env, client):
@@ -41,21 +46,30 @@ def test_unknown_domain(env, client):
@pytest.mark.django_db
def test_event_on_custom_domain(env, client):
def test_event_on_org_domain(env, client):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
r = client.get('/2015/', HTTP_HOST='foobar')
assert r.status_code == 200
assert b'<meta property="og:title" content="MRMCD2015" />' in r.content
@pytest.mark.django_db
def test_path_without_trailing_slash_on_custom_domain(env, client):
def test_event_on_custom_domain(env, client):
KnownDomain.objects.create(domainname='foobar', organizer=env[0], event=env[1])
r = client.get('/', HTTP_HOST='foobar')
assert r.status_code == 200
assert b'<meta property="og:title" content="MRMCD2015" />' in r.content
@pytest.mark.django_db
def test_path_without_trailing_slash_on_org_domain(env, client):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
r = client.get('/widget/product_list', HTTP_HOST='foobar')
assert r.status_code == 200
@pytest.mark.django_db
def test_event_with_custom_domain_on_main_domain(env, client):
def test_event_with_org_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
@@ -63,7 +77,24 @@ def test_event_with_custom_domain_on_main_domain(env, client):
@pytest.mark.django_db
def test_organizer_with_custom_domain_on_main_domain(env, client):
def test_event_with_custom_domain_on_main_domain(env, client):
KnownDomain.objects.create(domainname='foobar', organizer=env[0], event=env[1])
r = client.get('/mrmcd/2015/', HTTP_HOST='example.com')
assert r.status_code == 302
assert r['Location'] == 'http://foobar'
@pytest.mark.django_db
def test_event_with_custom_domain_on_org_domain(env, client):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
KnownDomain.objects.create(domainname='barfoo', organizer=env[0], event=env[1])
r = client.get('/2015/', HTTP_HOST='foobar')
assert r.status_code == 302
assert r['Location'] == 'http://barfoo'
@pytest.mark.django_db
def test_organizer_with_org_domain_on_main_domain(env, client):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
r = client.get('/mrmcd/', HTTP_HOST='example.com')
assert r.status_code == 302
@@ -71,7 +102,7 @@ def test_organizer_with_custom_domain_on_main_domain(env, client):
@pytest.mark.django_db
def test_event_on_custom_domain_only_with_wrong_organizer(env, client):
def test_event_on_org_domain_only_with_wrong_organizer(env, client):
organizer2 = Organizer.objects.create(name='Dummy', slug='dummy')
Event.objects.create(
organizer=organizer2, name='D1234', slug='1234',
@@ -83,7 +114,7 @@ def test_event_on_custom_domain_only_with_wrong_organizer(env, client):
@pytest.mark.django_db
def test_unknown_event_on_custom_domain(env, client):
def test_unknown_event_on_org_domain(env, client):
organizer2 = Organizer.objects.create(name='Dummy', slug='dummy')
Event.objects.create(
organizer=organizer2, name='D1234', slug='1234',
@@ -95,7 +126,7 @@ def test_unknown_event_on_custom_domain(env, client):
@pytest.mark.django_db
def test_cookie_domain_on_custom_domain(env, client):
def test_cookie_domain_on_org_domain(env, client):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
client.post('/2015/cart/add', HTTP_HOST='foobar')
r = client.get('/2015/', HTTP_HOST='foobar')
@@ -103,6 +134,16 @@ def test_cookie_domain_on_custom_domain(env, client):
assert r.client.cookies['pretix_session']['domain'] == ''
@pytest.mark.django_db
def test_cookie_domain_on_event_domain(env, client):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
KnownDomain.objects.create(domainname='barfoo', organizer=env[0], event=env[1])
client.post('/cart/add', HTTP_HOST='barfoo')
r = client.get('/', HTTP_HOST='barfoo')
assert r.client.cookies['pretix_csrftoken']['domain'] == ''
assert r.client.cookies['pretix_session']['domain'] == ''
@pytest.mark.django_db
def test_cookie_domain_on_main_domain(env, client):
with override_settings(SESSION_COOKIE_DOMAIN='example.com'):
@@ -113,12 +154,11 @@ def test_cookie_domain_on_main_domain(env, client):
@pytest.mark.django_db
@override_settings(USE_X_FORWARDED_HOST=True)
def test_with_forwarded_host(env, client):
settings.USE_X_FORWARDED_HOST = True
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
r = client.get('/2015/', HTTP_X_FORWARDED_HOST='foobar')
assert r.status_code == 200
settings.USE_X_FORWARDED_HOST = False
@pytest.mark.django_db
+74 -5
View File
@@ -30,6 +30,13 @@ def test_event_main_domain_front_page(env):
@pytest.mark.django_db
def test_event_custom_domain_kwargs(env):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
KnownDomain.objects.create(domainname='barfoo', organizer=env[0], event=env[1])
assert eventreverse(env[1], 'presale:event.checkout', {'step': 'payment'}) == 'http://barfoo/checkout/payment/'
@pytest.mark.django_db
def test_event_org_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/'
@@ -40,21 +47,36 @@ def test_event_main_domain_kwargs(env):
@pytest.mark.django_db
def test_event_custom_domain_front_page(env):
def test_event_org_domain_front_page(env):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
assert eventreverse(env[1], 'presale:event.index') == 'http://foobar/2015/'
assert eventreverse(env[0], 'presale:organizer.index') == 'http://foobar/'
@pytest.mark.django_db
def test_event_custom_domain_keep_port(env):
def test_event_custom_domain_front_page(env):
KnownDomain.objects.create(domainname='barfoo', organizer=env[0], event=env[1])
assert eventreverse(env[1], 'presale:event.index') == 'http://barfoo/'
assert eventreverse(env[0], 'presale:organizer.index') == '/mrmcd/'
@pytest.mark.django_db
def test_event_custom_and_org_domain_front_page(env):
KnownDomain.objects.create(domainname='barfoo', organizer=env[0], event=env[1])
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
assert eventreverse(env[1], 'presale:event.index') == 'http://barfoo/'
assert eventreverse(env[0], 'presale:organizer.index') == 'http://foobar/'
@pytest.mark.django_db
def test_event_org_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):
def test_event_org_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/'
@@ -82,7 +104,7 @@ def test_event_main_domain_cache(env):
'LOCATION': 'unique-snowflake',
}
})
def test_event_custom_domain_cache(env):
def test_event_org_domain_cache(env):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
env[0].get_cache().clear()
with assert_num_queries(1):
@@ -91,6 +113,45 @@ def test_event_custom_domain_cache(env):
eventreverse(env[1], 'presale:event.index')
@pytest.mark.django_db
@override_settings(CACHES={
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
})
def test_event_custom_domain_cache(env):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
KnownDomain.objects.create(domainname='barfoo', organizer=env[0], event=env[1])
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
@override_settings(CACHES={
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
})
@scopes_disabled()
def test_event_org_domain_cache_clear(env):
kd = KnownDomain.objects.create(domainname='foobar', organizer=env[0])
env[0].cache.clear()
with assert_num_queries(1):
eventreverse(env[1], 'presale:event.index')
kd.delete()
with assert_num_queries(2):
ev = Event.objects.get(pk=env[1].pk)
assert ev.pk == env[1].pk
assert ev.organizer == env[0]
with assert_num_queries(1):
eventreverse(ev, 'presale:event.index')
@pytest.mark.django_db
@override_settings(CACHES={
'default': {
@@ -100,7 +161,8 @@ def test_event_custom_domain_cache(env):
})
@scopes_disabled()
def test_event_custom_domain_cache_clear(env):
kd = KnownDomain.objects.create(domainname='foobar', organizer=env[0])
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
kd = KnownDomain.objects.create(domainname='barfoo', organizer=env[0], event=env[1])
env[0].cache.clear()
with assert_num_queries(1):
eventreverse(env[1], 'presale:event.index')
@@ -120,5 +182,12 @@ def test_event_main_domain_absolute(env):
@pytest.mark.django_db
def test_event_custom_domain_absolute(env):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
KnownDomain.objects.create(domainname='barfoo', organizer=env[0], event=env[1])
assert build_absolute_uri(env[1], 'presale:event.index') == 'http://barfoo/'
@pytest.mark.django_db
def test_event_org_domain_absolute(env):
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
assert build_absolute_uri(env[1], 'presale:event.index') == 'http://foobar/2015/'