mirror of
https://github.com/pretix/pretix.git
synced 2026-05-03 14:54:04 +00:00
Allow to use custom domains for some but not all events (Z#23153875) (#4627)
* Allow to use custom domains for some but not all events * Update src/pretix/multidomain/urlreverse.py * Apply suggestions from code review Co-authored-by: Mira <weller@rami.io> * Logging for domain config changes --------- Co-authored-by: Mira <weller@rami.io>
This commit is contained in:
@@ -823,6 +823,9 @@ class Event(EventMixin, LoggedModel):
|
||||
self.save()
|
||||
self.log_action('pretix.object.cloned', data={'source': other.slug, 'source_id': other.pk})
|
||||
|
||||
if hasattr(other, 'alternative_domain_assignment'):
|
||||
other.alternative_domain_assignment.domain.event_assignments.create(event=self)
|
||||
|
||||
if not self.all_sales_channels:
|
||||
self.limit_sales_channels.set(
|
||||
self.organizer.sales_channels.filter(
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
# License for the specific language governing permissions and limitations under the License.
|
||||
|
||||
from decimal import Decimal
|
||||
from urllib.parse import urlencode, urlparse
|
||||
from urllib.parse import urlencode
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
import pycountry
|
||||
@@ -76,8 +76,10 @@ from pretix.control.forms import (
|
||||
)
|
||||
from pretix.control.forms.widgets import Select2
|
||||
from pretix.helpers.countries import CachedCountries
|
||||
from pretix.multidomain.models import KnownDomain
|
||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||
from pretix.multidomain.models import AlternativeDomainAssignment, KnownDomain
|
||||
from pretix.multidomain.urlreverse import (
|
||||
build_absolute_uri, get_organizer_domain,
|
||||
)
|
||||
from pretix.plugins.banktransfer.payment import BankTransfer
|
||||
from pretix.presale.style import get_fonts
|
||||
|
||||
@@ -363,14 +365,9 @@ 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:
|
||||
@@ -379,48 +376,54 @@ class EventUpdateForm(I18nModelForm):
|
||||
self.fields['location'].widget.attrs['placeholder'] = _(
|
||||
'Sample Conference Center\nHeidelberg, Germany'
|
||||
)
|
||||
if self.domain:
|
||||
|
||||
try:
|
||||
self.fields['domain'] = forms.CharField(
|
||||
max_length=255,
|
||||
label=_('Custom domain'),
|
||||
label=_('Domain'),
|
||||
initial=self.instance.domain.domainname,
|
||||
required=False,
|
||||
disabled=True,
|
||||
help_text=_('You can configure this in your organizer settings.')
|
||||
)
|
||||
except KnownDomain.DoesNotExist:
|
||||
domain = get_organizer_domain(self.instance.organizer)
|
||||
try:
|
||||
current_domain_assignment = self.instance.alternative_domain_assignment
|
||||
except AlternativeDomainAssignment.DoesNotExist:
|
||||
current_domain_assignment = None
|
||||
self.fields['domain'] = forms.ChoiceField(
|
||||
label=_('Domain'),
|
||||
help_text=_('You can add more domains in your organizer account.'),
|
||||
choices=[('', _('Same as organizer account') + (f" ({domain})" if domain else ""))] + [
|
||||
(d.domainname, d.domainname) for d in self.instance.organizer.domains.filter(mode=KnownDomain.MODE_ORG_ALT_DOMAIN)
|
||||
],
|
||||
initial=current_domain_assignment.domain_id if current_domain_assignment else "",
|
||||
required=False,
|
||||
help_text=_('You need to configure the custom domain in the webserver beforehand.')
|
||||
)
|
||||
self.fields['limit_sales_channels'].queryset = self.event.organizer.sales_channels.all()
|
||||
self.fields['limit_sales_channels'].widget = SalesChannelCheckboxSelectMultiple(self.event, attrs={
|
||||
'data-inverse-dependency': '<[name$=all_sales_channels]',
|
||||
}, choices=self.fields['limit_sales_channels'].widget.choices)
|
||||
|
||||
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()
|
||||
try:
|
||||
current_domain_assignment = instance.alternative_domain_assignment
|
||||
except AlternativeDomainAssignment.DoesNotExist:
|
||||
current_domain_assignment = None
|
||||
if self.cleaned_data['domain'] and not hasattr(instance, 'domain'):
|
||||
domain = self.instance.organizer.domains.get(mode=KnownDomain.MODE_ORG_ALT_DOMAIN, domainname=self.cleaned_data["domain"])
|
||||
AlternativeDomainAssignment.objects.update_or_create(
|
||||
event=instance,
|
||||
defaults={
|
||||
"domain": domain,
|
||||
}
|
||||
)
|
||||
instance.cache.clear()
|
||||
elif current_domain_assignment:
|
||||
current_domain_assignment.delete()
|
||||
instance.cache.clear()
|
||||
|
||||
return instance
|
||||
|
||||
@@ -133,63 +133,108 @@ class OrganizerDeleteForm(forms.Form):
|
||||
class OrganizerUpdateForm(OrganizerForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.domain = kwargs.pop('domain', False)
|
||||
self.change_slug = kwargs.pop('change_slug', False)
|
||||
kwargs.setdefault('initial', {})
|
||||
self.instance = kwargs['instance']
|
||||
if self.domain and self.instance:
|
||||
initial_domain = self.instance.domains.filter(event__isnull=True).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'
|
||||
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(organizer=self.instance.pk,
|
||||
event__isnull=True).exists():
|
||||
raise ValidationError(
|
||||
_('This domain is already in use for a different event or organizer.')
|
||||
)
|
||||
return d
|
||||
|
||||
def clean_slug(self):
|
||||
if self.change_slug:
|
||||
return self.cleaned_data['slug']
|
||||
return self.instance.slug
|
||||
|
||||
def save(self, commit=True):
|
||||
instance = super().save(commit)
|
||||
|
||||
if self.domain:
|
||||
current_domain = instance.domains.filter(event__isnull=True).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, domainname=self.cleaned_data['domain'])
|
||||
elif not current_domain:
|
||||
KnownDomain.objects.create(organizer=instance, domainname=self.cleaned_data['domain'])
|
||||
elif current_domain:
|
||||
current_domain.delete()
|
||||
instance.cache.clear()
|
||||
for ev in instance.events.all():
|
||||
ev.cache.clear()
|
||||
class KnownDomainForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = KnownDomain
|
||||
fields = ["domainname", "mode", "event"]
|
||||
field_classes = {
|
||||
"event": SafeModelChoiceField,
|
||||
}
|
||||
|
||||
return instance
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.organizer = kwargs.pop('organizer')
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["event"].queryset = self.organizer.events.all()
|
||||
if self.instance and self.instance.pk:
|
||||
self.fields["domainname"].widget.attrs['readonly'] = 'readonly'
|
||||
|
||||
def clean_domainname(self):
|
||||
if self.instance and self.instance.pk:
|
||||
return self.instance.domainname
|
||||
d = self.cleaned_data['domainname']
|
||||
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(organizer=self.instance.organizer).exists():
|
||||
raise ValidationError(
|
||||
_('This domain is already in use for a different event or organizer.')
|
||||
)
|
||||
return d
|
||||
|
||||
def clean(self):
|
||||
d = super().clean()
|
||||
|
||||
if d["mode"] == KnownDomain.MODE_ORG_DOMAIN and d["event"]:
|
||||
raise ValidationError(
|
||||
_("Do not choose an event for this mode.")
|
||||
)
|
||||
|
||||
if d["mode"] == KnownDomain.MODE_ORG_ALT_DOMAIN and d["event"]:
|
||||
raise ValidationError(
|
||||
_("Do not choose an event for this mode. You can assign events to this domain in event settings.")
|
||||
)
|
||||
|
||||
if d["mode"] == KnownDomain.MODE_EVENT_DOMAIN and not d["event"]:
|
||||
raise ValidationError(
|
||||
_("You need to choose an event.")
|
||||
)
|
||||
|
||||
return d
|
||||
|
||||
|
||||
class BaseKnownDomainFormSet(forms.BaseInlineFormSet):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.organizer = kwargs.pop('organizer')
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def _construct_form(self, i, **kwargs):
|
||||
kwargs['organizer'] = self.organizer
|
||||
return super()._construct_form(i, **kwargs)
|
||||
|
||||
@property
|
||||
def empty_form(self):
|
||||
form = self.form(
|
||||
auto_id=self.auto_id,
|
||||
prefix=self.add_prefix('__prefix__'),
|
||||
empty_permitted=True,
|
||||
use_required_attribute=False,
|
||||
organizer=self.organizer,
|
||||
)
|
||||
self.add_fields(form, None)
|
||||
return form
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
data = [f.cleaned_data for f in self.forms]
|
||||
|
||||
if len([d for d in data if d.get("mode") == KnownDomain.MODE_ORG_DOMAIN and not d.get("DELETE")]) > 1:
|
||||
raise ValidationError(_("You may set only one organizer domain."))
|
||||
|
||||
return data
|
||||
|
||||
|
||||
KnownDomainFormset = inlineformset_factory(
|
||||
Organizer, KnownDomain,
|
||||
KnownDomainForm,
|
||||
formset=BaseKnownDomainFormSet,
|
||||
can_order=False, can_delete=True, extra=0
|
||||
)
|
||||
|
||||
|
||||
class SafeOrderPositionChoiceField(forms.ModelChoiceField):
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
{% bootstrap_field form.name layout="control" %}
|
||||
{% bootstrap_field form.slug layout="control" %}
|
||||
{% if form.domain %}
|
||||
{% bootstrap_field form.domain layout="control" %}
|
||||
{% bootstrap_field form.domain layout="horizontal" %}
|
||||
{% endif %}
|
||||
{% bootstrap_field form.date_from layout="control" %}
|
||||
{% bootstrap_field form.date_to layout="control" %}
|
||||
|
||||
@@ -294,6 +294,71 @@
|
||||
<legend>{% trans "Invoices" %}</legend>
|
||||
{% bootstrap_field sform.invoice_regenerate_allowed layout="control" %}
|
||||
</fieldset>
|
||||
{% if domain_formset %}
|
||||
<fieldset>
|
||||
<legend>{% trans "Domains" %}</legend>
|
||||
<div class="alert alert-warning">
|
||||
{% trans "This dialog is intended for advanced users." %}
|
||||
{% trans "The domain needs to be configured on your webserver before it can be used here." %}
|
||||
</div>
|
||||
<div class="formset" data-formset data-formset-prefix="{{ domain_formset.prefix }}">
|
||||
{{ domain_formset.management_form }}
|
||||
{% bootstrap_formset_errors domain_formset %}
|
||||
<div data-formset-body>
|
||||
{% for form in domain_formset %}
|
||||
<div class="row formset-row" data-formset-form>
|
||||
<div class="sr-only">
|
||||
{{ form.id }}
|
||||
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
{% bootstrap_field form.domainname layout='' form_group_class="" %}
|
||||
{% bootstrap_form_errors form %}
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
{% bootstrap_field form.mode layout='' form_group_class="" %}
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
{% bootstrap_field form.event layout='' form_group_class="" %}
|
||||
</div>
|
||||
<div class="col-md-2 text-right flip">
|
||||
<label aria-hidden="true"> </label><br>
|
||||
<button type="button" class="btn btn-danger" data-formset-delete-button>
|
||||
<i class="fa fa-trash"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<script type="form-template" data-formset-empty-form>
|
||||
{% escapescript %}
|
||||
<div class="row formset-row" data-formset-form>
|
||||
<div class="sr-only">
|
||||
{{ domain_formset.empty_form.id }}
|
||||
{% bootstrap_field domain_formset.empty_form.DELETE form_group_class="" layout="inline" %}
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
{% bootstrap_field domain_formset.empty_form.domainname layout='' form_group_class="" %}
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
{% bootstrap_field domain_formset.empty_form.mode layout='' form_group_class="" %}
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
{% bootstrap_field domain_formset.empty_form.event layout='' form_group_class="" %}
|
||||
</div>
|
||||
<div class="col-md-2 text-right flip">
|
||||
<label aria-hidden="true"> </label><br>
|
||||
<button type="button" class="btn btn-danger" data-formset-delete-button>
|
||||
<i class="fa fa-trash"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
{% endescapescript %}
|
||||
</script>
|
||||
<p>
|
||||
<button type="button" class="btn btn-default" data-formset-add>
|
||||
<i class="fa fa-plus"></i> {% trans "Add domain" %}</button>
|
||||
</p>
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
|
||||
@@ -239,7 +239,6 @@ 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):
|
||||
|
||||
@@ -104,11 +104,11 @@ from pretix.control.forms.organizer import (
|
||||
CustomerCreateForm, CustomerUpdateForm, DeviceBulkEditForm, DeviceForm,
|
||||
EventMetaPropertyAllowedValueFormSet, EventMetaPropertyForm, GateForm,
|
||||
GiftCardAcceptanceInviteForm, GiftCardCreateForm, GiftCardUpdateForm,
|
||||
MailSettingsForm, MembershipTypeForm, MembershipUpdateForm,
|
||||
OrganizerDeleteForm, OrganizerFooterLinkFormset, OrganizerForm,
|
||||
OrganizerSettingsForm, OrganizerUpdateForm, ReusableMediumCreateForm,
|
||||
ReusableMediumUpdateForm, SalesChannelForm, SSOClientForm, SSOProviderForm,
|
||||
TeamForm, WebHookForm,
|
||||
KnownDomainFormset, MailSettingsForm, MembershipTypeForm,
|
||||
MembershipUpdateForm, OrganizerDeleteForm, OrganizerFooterLinkFormset,
|
||||
OrganizerForm, OrganizerSettingsForm, OrganizerUpdateForm,
|
||||
ReusableMediumCreateForm, ReusableMediumUpdateForm, SalesChannelForm,
|
||||
SSOClientForm, SSOProviderForm, TeamForm, WebHookForm,
|
||||
)
|
||||
from pretix.control.forms.rrule import RRuleForm
|
||||
from pretix.control.logdisplay import OVERVIEW_BANLIST
|
||||
@@ -447,6 +447,10 @@ class OrganizerUpdate(OrganizerPermissionRequiredMixin, UpdateView):
|
||||
def get_object(self, queryset=None) -> Organizer:
|
||||
return self.object
|
||||
|
||||
@cached_property
|
||||
def domain_config(self):
|
||||
return self.request.user.has_active_staff_session(self.request.session.session_key)
|
||||
|
||||
@cached_property
|
||||
def sform(self):
|
||||
return OrganizerSettingsForm(
|
||||
@@ -461,6 +465,8 @@ class OrganizerUpdate(OrganizerPermissionRequiredMixin, UpdateView):
|
||||
context = super().get_context_data(*args, **kwargs)
|
||||
context['sform'] = self.sform
|
||||
context['footer_links_formset'] = self.footer_links_formset
|
||||
if self.domain_config:
|
||||
context['domain_formset'] = self.domain_formset
|
||||
return context
|
||||
|
||||
@transaction.atomic
|
||||
@@ -483,6 +489,8 @@ class OrganizerUpdate(OrganizerPermissionRequiredMixin, UpdateView):
|
||||
self.request.organizer.log_action('pretix.organizer.footerlinks.changed', user=self.request.user, data={
|
||||
'data': self.footer_links_formset.cleaned_data
|
||||
})
|
||||
if self.domain_config and self.domain_formset.has_changed():
|
||||
self._save_domain_config()
|
||||
if form.has_changed():
|
||||
self.request.organizer.log_action(
|
||||
'pretix.organizer.changed',
|
||||
@@ -493,10 +501,22 @@ class OrganizerUpdate(OrganizerPermissionRequiredMixin, UpdateView):
|
||||
messages.success(self.request, _('Your changes have been saved.'))
|
||||
return super().form_valid(form)
|
||||
|
||||
def _save_domain_config(self):
|
||||
for form in self.domain_formset.initial_forms:
|
||||
if form.instance.pk and form.has_changed():
|
||||
self.object.domains.get(pk=form.instance.pk).log_delete(self.request.user)
|
||||
self.domain_formset.save()
|
||||
for new_obj in self.domain_formset.new_objects:
|
||||
new_obj.log_create(self.request.user)
|
||||
for ch_obj, form in self.domain_formset.changed_objects:
|
||||
ch_obj.log_create(self.request.user)
|
||||
self.request.organizer.cache.clear()
|
||||
for ev in self.request.organizer.events.all():
|
||||
ev.cache.clear()
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
if self.request.user.has_active_staff_session(self.request.session.session_key):
|
||||
kwargs['domain'] = True
|
||||
kwargs['change_slug'] = True
|
||||
return kwargs
|
||||
|
||||
@@ -508,7 +528,7 @@ class OrganizerUpdate(OrganizerPermissionRequiredMixin, UpdateView):
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
form = self.get_form()
|
||||
if form.is_valid() and self.sform.is_valid() and self.footer_links_formset.is_valid():
|
||||
if form.is_valid() and self.sform.is_valid() and self.footer_links_formset.is_valid() and (not self.domain_config or self.domain_formset.is_valid()):
|
||||
return self.form_valid(form)
|
||||
else:
|
||||
return self.form_invalid(form)
|
||||
@@ -519,6 +539,11 @@ class OrganizerUpdate(OrganizerPermissionRequiredMixin, UpdateView):
|
||||
organizer=self.object,
|
||||
prefix="footer-links", instance=self.object)
|
||||
|
||||
@cached_property
|
||||
def domain_formset(self):
|
||||
return KnownDomainFormset(self.request.POST if self.request.method == "POST" else None, prefix="domains",
|
||||
instance=self.object, organizer=self.object)
|
||||
|
||||
def save_footer_links_formset(self, obj):
|
||||
self.footer_links_formset.save()
|
||||
|
||||
|
||||
@@ -80,28 +80,32 @@ class MultiDomainMiddleware(MiddlewareMixin):
|
||||
request.port = int(port) if port else None
|
||||
request.host = domain
|
||||
if domain == default_domain:
|
||||
request.domain_mode = "system"
|
||||
request.urlconf = "pretix.multidomain.maindomain_urlconf"
|
||||
elif domain:
|
||||
cached = cache.get('pretix_multidomain_instance_{}'.format(domain))
|
||||
cached = cache.get('pretix_multidomain_instances_{}'.format(domain))
|
||||
|
||||
if cached is None:
|
||||
try:
|
||||
kd = KnownDomain.objects.select_related('organizer', 'event').get(domainname=domain) # noqa
|
||||
orga = kd.organizer
|
||||
event = kd.event
|
||||
mode = kd.mode
|
||||
except KnownDomain.DoesNotExist:
|
||||
orga = False
|
||||
event = False
|
||||
mode = "system"
|
||||
cache.set(
|
||||
'pretix_multidomain_instance_{}'.format(domain),
|
||||
(orga.pk if orga else None, event.pk if event else None),
|
||||
'pretix_multidomain_instances_{}'.format(domain),
|
||||
(orga.pk if orga else None, event.pk if event else None, mode),
|
||||
3600
|
||||
)
|
||||
else:
|
||||
orga, event = cached
|
||||
orga, event, mode = cached
|
||||
|
||||
if event:
|
||||
if mode == KnownDomain.MODE_EVENT_DOMAIN:
|
||||
request.event_domain = True
|
||||
request.domain_mode = KnownDomain.MODE_EVENT_DOMAIN
|
||||
if isinstance(event, Event):
|
||||
request.organizer = orga
|
||||
request.event = event
|
||||
@@ -110,11 +114,18 @@ class MultiDomainMiddleware(MiddlewareMixin):
|
||||
request.event = Event.objects.select_related('organizer').get(pk=event)
|
||||
request.organizer = request.event.organizer
|
||||
request.urlconf = "pretix.multidomain.event_domain_urlconf"
|
||||
elif orga:
|
||||
elif mode == KnownDomain.MODE_ORG_ALT_DOMAIN:
|
||||
request.organizer_domain = True
|
||||
request.domain_mode = KnownDomain.MODE_ORG_ALT_DOMAIN
|
||||
request.organizer = orga if isinstance(orga, Organizer) else Organizer.objects.get(pk=orga)
|
||||
request.urlconf = "pretix.multidomain.organizer_alternative_domain_urlconf"
|
||||
elif mode == KnownDomain.MODE_ORG_DOMAIN:
|
||||
request.organizer_domain = True
|
||||
request.domain_mode = KnownDomain.MODE_ORG_DOMAIN
|
||||
request.organizer = orga if isinstance(orga, Organizer) else Organizer.objects.get(pk=orga)
|
||||
request.urlconf = "pretix.multidomain.organizer_domain_urlconf"
|
||||
elif settings.DEBUG or domain in LOCAL_HOST_NAMES:
|
||||
request.domain_mode = "system"
|
||||
request.urlconf = "pretix.multidomain.maindomain_urlconf"
|
||||
else:
|
||||
with scopes_disabled():
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
# Generated by Django 4.2.16 on 2024-11-12 10:46
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("pretixbase", "0273_remove_checkinlist_auto_checkin_sales_channels"),
|
||||
("pretixmultidomain", "0002_knowndomain_event"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="AlternativeDomainAssignment",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True, primary_key=True, serialize=False
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="knowndomain",
|
||||
name="mode",
|
||||
field=models.CharField(default="organizer", max_length=255),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="knowndomain",
|
||||
name="event",
|
||||
field=models.OneToOneField(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="domain",
|
||||
to="pretixbase.event",
|
||||
),
|
||||
),
|
||||
migrations.RunSQL(
|
||||
sql="UPDATE pretixmultidomain_knowndomain SET mode = 'event' WHERE event_id IS NOT NULL",
|
||||
reverse_sql=migrations.RunSQL.noop,
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name="knowndomain",
|
||||
constraint=models.UniqueConstraint(
|
||||
condition=models.Q(("mode", "organizer")),
|
||||
fields=("organizer",),
|
||||
name="unique_organizer_domain",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="alternativedomainassignment",
|
||||
name="domain",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="event_assignments",
|
||||
to="pretixmultidomain.knowndomain",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="alternativedomainassignment",
|
||||
name="event",
|
||||
field=models.OneToOneField(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="alternative_domain_assignment",
|
||||
to="pretixbase.event",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -21,6 +21,7 @@
|
||||
#
|
||||
from django.core.cache import cache
|
||||
from django.db import models
|
||||
from django.db.models import Q
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_scopes import scopes_disabled
|
||||
|
||||
@@ -28,39 +29,134 @@ 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)
|
||||
MODE_ORG_DOMAIN = "organizer"
|
||||
MODE_ORG_ALT_DOMAIN = "organizer_alternative"
|
||||
MODE_EVENT_DOMAIN = "event"
|
||||
MODES = (
|
||||
(MODE_ORG_DOMAIN, _("Organizer domain")),
|
||||
(MODE_ORG_ALT_DOMAIN, _("Alternative organizer domain for a set of events")),
|
||||
(MODE_EVENT_DOMAIN, _("Event domain")),
|
||||
)
|
||||
|
||||
domainname = models.CharField(
|
||||
max_length=255,
|
||||
primary_key=True,
|
||||
verbose_name=_("Domain name"),
|
||||
)
|
||||
mode = models.CharField(
|
||||
max_length=255,
|
||||
choices=MODES,
|
||||
default=MODE_ORG_DOMAIN,
|
||||
verbose_name=_("Mode"),
|
||||
)
|
||||
organizer = models.ForeignKey(
|
||||
Organizer,
|
||||
blank=True,
|
||||
null=True,
|
||||
related_name='domains',
|
||||
on_delete=models.CASCADE
|
||||
)
|
||||
event = models.OneToOneField(
|
||||
Event,
|
||||
blank=True,
|
||||
null=True,
|
||||
related_name='domain',
|
||||
on_delete=models.PROTECT,
|
||||
verbose_name=_("Event"),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Known domain")
|
||||
verbose_name_plural = _("Known domains")
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=("organizer",),
|
||||
name="unique_organizer_domain",
|
||||
condition=Q(mode="organizer"),
|
||||
),
|
||||
]
|
||||
ordering = ("-mode", "domainname")
|
||||
|
||||
def __str__(self):
|
||||
return self.domainname
|
||||
|
||||
@scopes_disabled()
|
||||
def save(self, *args, **kwargs):
|
||||
if self.event:
|
||||
self.mode = KnownDomain.MODE_EVENT_DOMAIN
|
||||
elif self.mode == KnownDomain.MODE_EVENT_DOMAIN:
|
||||
raise ValueError("Event domain needs event")
|
||||
super().save(*args, **kwargs)
|
||||
if self.event:
|
||||
self.event.get_cache().clear()
|
||||
try:
|
||||
self.event.alternative_domain_assignment.delete()
|
||||
except AlternativeDomainAssignment.DoesNotExist:
|
||||
pass
|
||||
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_instance_{}'.format(self.domainname))
|
||||
cache.delete('pretix_multidomain_instances_{}'.format(self.domainname))
|
||||
cache.delete('pretix_multidomain_event_{}'.format(self.domainname))
|
||||
|
||||
@scopes_disabled()
|
||||
def delete(self, *args, **kwargs):
|
||||
if self.event:
|
||||
self.event.get_cache().clear()
|
||||
self.event.cache.clear()
|
||||
elif self.organizer:
|
||||
self.organizer.get_cache().clear()
|
||||
self.organizer.cache.clear()
|
||||
for event in self.organizer.events.all():
|
||||
event.get_cache().clear()
|
||||
event.cache.clear()
|
||||
cache.delete('pretix_multidomain_organizer_{}'.format(self.domainname))
|
||||
cache.delete('pretix_multidomain_instance_{}'.format(self.domainname))
|
||||
cache.delete('pretix_multidomain_instances_{}'.format(self.domainname))
|
||||
cache.delete('pretix_multidomain_event_{}'.format(self.domainname))
|
||||
super().delete(*args, **kwargs)
|
||||
|
||||
def _log_domain_action(self, user, data):
|
||||
if self.event:
|
||||
self.event.log_action(
|
||||
'pretix.event.settings',
|
||||
user=user,
|
||||
data=data
|
||||
)
|
||||
else:
|
||||
self.organizer.log_action(
|
||||
'pretix.organizer.settings',
|
||||
user=user,
|
||||
data=data
|
||||
)
|
||||
|
||||
def log_create(self, user):
|
||||
self._log_domain_action(user, {'add_alt_domain': self.domainname} if self.mode == KnownDomain.MODE_ORG_ALT_DOMAIN else {'domain': self.domainname})
|
||||
|
||||
def log_delete(self, user):
|
||||
self._log_domain_action(user, {'remove_alt_domain': self.domainname} if self.mode == KnownDomain.MODE_ORG_ALT_DOMAIN else {'domain': None})
|
||||
|
||||
|
||||
class AlternativeDomainAssignment(models.Model):
|
||||
domain = models.ForeignKey(
|
||||
KnownDomain,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="event_assignments",
|
||||
)
|
||||
event = models.OneToOneField(
|
||||
Event,
|
||||
related_name="alternative_domain_assignment",
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
@scopes_disabled()
|
||||
def save(self, *args, **kwargs):
|
||||
super().save(*args, **kwargs)
|
||||
self.event.cache.clear()
|
||||
cache.delete('pretix_multidomain_instances_{}'.format(self.domain_id))
|
||||
cache.delete('pretix_multidomain_event_{}'.format(self.domain_id))
|
||||
|
||||
@scopes_disabled()
|
||||
def delete(self, *args, **kwargs):
|
||||
self.event.cache.clear()
|
||||
cache.delete('pretix_multidomain_instances_{}'.format(self.domain_id))
|
||||
cache.delete('pretix_multidomain_event_{}'.format(self.domain_id))
|
||||
super().delete(*args, **kwargs)
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-2021 rami.io GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import importlib.util
|
||||
|
||||
from django.apps import apps
|
||||
from django.urls import include, re_path
|
||||
|
||||
from pretix.multidomain.plugin_handler import plugin_event_urls
|
||||
from pretix.presale.urls import event_patterns, locale_patterns
|
||||
from pretix.presale.views import organizer
|
||||
from pretix.urls import common_patterns
|
||||
|
||||
presale_patterns = [
|
||||
re_path(r'', include((locale_patterns + [
|
||||
re_path(r'^$', organizer.RedirectToOrganizerIndex.as_view(), name='organizer.alt.index'),
|
||||
re_path(r'^(?P<event>[^/]+)/', 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(
|
||||
re_path(r'^(?P<event>[^/]+)/', include((patterns, app.label)))
|
||||
)
|
||||
if hasattr(urlmod, 'organizer_patterns'):
|
||||
patterns = plugin_event_urls(urlmod.organizer_patterns, plugin=app.name)
|
||||
raw_plugin_patterns.append(
|
||||
re_path(r'', include((patterns, app.label)))
|
||||
)
|
||||
|
||||
plugin_patterns = [
|
||||
re_path(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'
|
||||
@@ -43,28 +43,33 @@ from pretix.base.models import Event, Organizer
|
||||
from .models import KnownDomain
|
||||
|
||||
|
||||
def get_event_domain(event, fallback=False, return_info=False):
|
||||
def get_event_domain(event, fallback=False, return_mode=False):
|
||||
assert isinstance(event, Event)
|
||||
if not event.pk:
|
||||
# Can happen on the "event deleted" response
|
||||
return (None, None) if return_info else None
|
||||
suffix = ('_fallback' if fallback else '') + ('_info' if return_info else '')
|
||||
return (None, None) if return_mode else None
|
||||
suffix = ('_fallback' if fallback else '') + ('_mode' if return_mode else '')
|
||||
domain = getattr(event, '_cached_domain' + suffix, None) or event.cache.get('domain' + suffix)
|
||||
if domain is None:
|
||||
domain = None, None
|
||||
if fallback:
|
||||
if hasattr(event, 'alternative_domain_assignment'):
|
||||
domain = event.alternative_domain_assignment.domain_id, KnownDomain.MODE_ORG_ALT_DOMAIN
|
||||
elif fallback:
|
||||
domains = KnownDomain.objects.filter(
|
||||
Q(event=event) | Q(organizer_id=event.organizer_id, event__isnull=True)
|
||||
Q(event=event, mode=KnownDomain.MODE_EVENT_DOMAIN) |
|
||||
Q(organizer_id=event.organizer_id, event__isnull=True, mode=KnownDomain.MODE_ORG_DOMAIN)
|
||||
)
|
||||
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"
|
||||
domain = domains_event[0].domainname, KnownDomain.MODE_EVENT_DOMAIN
|
||||
elif domains_org:
|
||||
domain = domains_org[0].domainname, "organizer"
|
||||
domain = domains_org[0].domainname, KnownDomain.MODE_ORG_DOMAIN
|
||||
else:
|
||||
domains = event.domains.all()
|
||||
domain = domains[0].domainname if domains else None, "event"
|
||||
try:
|
||||
domain = event.domain.domainname, KnownDomain.MODE_EVENT_DOMAIN
|
||||
except KnownDomain.DoesNotExist:
|
||||
domain = None, None
|
||||
event.cache.set('domain' + suffix, domain or 'none')
|
||||
setattr(event, '_cached_domain' + suffix, domain or 'none')
|
||||
elif domain == 'none':
|
||||
@@ -72,7 +77,7 @@ def get_event_domain(event, fallback=False, return_info=False):
|
||||
domain = None, None
|
||||
else:
|
||||
setattr(event, '_cached_domain' + suffix, domain)
|
||||
return domain if return_info or not isinstance(domain, tuple) else domain[0]
|
||||
return domain if return_mode else domain[0]
|
||||
|
||||
|
||||
def get_organizer_domain(organizer):
|
||||
@@ -81,7 +86,7 @@ def get_organizer_domain(organizer):
|
||||
return None
|
||||
domain = getattr(organizer, '_cached_domain', None) or organizer.cache.get('domain')
|
||||
if domain is None:
|
||||
domains = organizer.domains.filter(event__isnull=True)
|
||||
domains = organizer.domains.filter(event__isnull=True, mode=KnownDomain.MODE_ORG_DOMAIN)
|
||||
domain = domains[0].domainname if domains else None
|
||||
organizer.cache.set('domain', domain or 'none')
|
||||
organizer._cached_domain = domain or 'none'
|
||||
@@ -131,7 +136,8 @@ def eventreverse(obj, name, kwargs=None):
|
||||
:returns: An absolute or relative URL as a string
|
||||
"""
|
||||
from pretix.multidomain import (
|
||||
event_domain_urlconf, maindomain_urlconf, organizer_domain_urlconf,
|
||||
event_domain_urlconf, maindomain_urlconf,
|
||||
organizer_alternative_domain_urlconf, organizer_domain_urlconf,
|
||||
)
|
||||
|
||||
c = None
|
||||
@@ -153,17 +159,24 @@ def eventreverse(obj, name, kwargs=None):
|
||||
raise TypeError('obj should be Event or Organizer')
|
||||
|
||||
if event:
|
||||
domain, domaintype = get_event_domain(obj, fallback=True, return_info=True)
|
||||
domain, domaintype = get_event_domain(obj, fallback=True, return_mode=True)
|
||||
else:
|
||||
domain, domaintype = get_organizer_domain(organizer), "organizer"
|
||||
domain, domaintype = get_organizer_domain(organizer), KnownDomain.MODE_ORG_DOMAIN
|
||||
|
||||
if domain:
|
||||
if domaintype == "event" and 'event' in kwargs:
|
||||
if domaintype == KnownDomain.MODE_EVENT_DOMAIN and 'event' in kwargs:
|
||||
del kwargs['event']
|
||||
if 'organizer' in kwargs:
|
||||
del kwargs['organizer']
|
||||
|
||||
path = reverse(name, kwargs=kwargs, urlconf=event_domain_urlconf if domaintype == "event" else organizer_domain_urlconf)
|
||||
if domaintype == KnownDomain.MODE_EVENT_DOMAIN:
|
||||
urlconf = event_domain_urlconf
|
||||
elif domaintype == KnownDomain.MODE_ORG_ALT_DOMAIN:
|
||||
urlconf = organizer_alternative_domain_urlconf
|
||||
else:
|
||||
urlconf = organizer_domain_urlconf
|
||||
|
||||
path = reverse(name, kwargs=kwargs, urlconf=urlconf)
|
||||
siteurlsplit = urlsplit(settings.SITE_URL)
|
||||
if siteurlsplit.port and siteurlsplit.port not in (80, 443):
|
||||
domain = '%s:%d' % (domain, siteurlsplit.port)
|
||||
|
||||
@@ -57,8 +57,9 @@ from pretix.base.middleware import LocaleMiddleware
|
||||
from pretix.base.models import Customer, Event, Organizer
|
||||
from pretix.base.timemachine import time_machine_now_assigned_from_request
|
||||
from pretix.helpers.http import redirect_to_url
|
||||
from pretix.multidomain.models import KnownDomain
|
||||
from pretix.multidomain.urlreverse import (
|
||||
get_event_domain, get_organizer_domain,
|
||||
build_absolute_uri, get_event_domain, get_organizer_domain,
|
||||
)
|
||||
from pretix.presale.signals import process_request, process_response
|
||||
|
||||
@@ -134,7 +135,7 @@ def update_customer_session_auth_hash(request, customer):
|
||||
|
||||
|
||||
def add_customer_to_request(request):
|
||||
if 'cross_domain_customer_auth' in request.GET and request.event_domain:
|
||||
if 'cross_domain_customer_auth' in request.GET and request.domain_mode in (KnownDomain.MODE_EVENT_DOMAIN, KnownDomain.MODE_ORG_ALT_DOMAIN):
|
||||
# The user is logged in on the main domain and now wants to take their session
|
||||
# to a event-specific domain. We validate the one time token received via a
|
||||
# query parameter and make sure we invalidate it right away. Then, we look up
|
||||
@@ -258,11 +259,13 @@ def _detect_event(request, require_live=True, require_plugin=None):
|
||||
|
||||
url = resolve(request.path_info)
|
||||
|
||||
request_domain_mode = getattr(request, 'domain_mode', 'system')
|
||||
print("Mode", request_domain_mode)
|
||||
try:
|
||||
if hasattr(request, 'event_domain'):
|
||||
if request_domain_mode == KnownDomain.MODE_EVENT_DOMAIN:
|
||||
# We are on an event's custom domain
|
||||
pass
|
||||
elif hasattr(request, 'organizer_domain'):
|
||||
elif request_domain_mode in (KnownDomain.MODE_ORG_DOMAIN, KnownDomain.MODE_ORG_ALT_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:
|
||||
@@ -277,12 +280,20 @@ def _detect_event(request, require_live=True, require_plugin=None):
|
||||
organizer=request.organizer,
|
||||
)
|
||||
|
||||
# If this event has a custom domain, send the user there
|
||||
domain = get_event_domain(request.event)
|
||||
if domain:
|
||||
# If this event has a custom domain or is not available on this alt domain, send the user there
|
||||
domain, domainmode = get_event_domain(request.event, fallback=False, return_mode=True)
|
||||
if not domain and request_domain_mode == KnownDomain.MODE_ORG_ALT_DOMAIN:
|
||||
path = request.get_full_path().split("/", 2)[-1]
|
||||
r = redirect_to_url(build_absolute_uri(request.event, "presale:event.index") + path)
|
||||
r['Access-Control-Allow-Origin'] = '*'
|
||||
return r
|
||||
elif domain and domain != request.host:
|
||||
if request.port and request.port not in (80, 443):
|
||||
domain = '%s:%d' % (domain, request.port)
|
||||
path = request.get_full_path().split("/", 2)[-1]
|
||||
if domainmode == KnownDomain.MODE_EVENT_DOMAIN:
|
||||
path = request.get_full_path().split("/", 2)[-1]
|
||||
else:
|
||||
path = request.get_full_path()
|
||||
r = redirect_to_url(urljoin('%s://%s' % (request.scheme, domain), path))
|
||||
r['Access-Control-Allow-Origin'] = '*'
|
||||
return r
|
||||
@@ -299,11 +310,14 @@ def _detect_event(request, require_live=True, require_plugin=None):
|
||||
request.organizer = request.event.organizer
|
||||
|
||||
# If this event has a custom domain, send the user there
|
||||
domain = get_event_domain(request.event)
|
||||
domain, domainmode = get_event_domain(request.event, fallback=False, return_mode=True)
|
||||
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]
|
||||
if domainmode == KnownDomain.MODE_EVENT_DOMAIN:
|
||||
path = request.get_full_path().split("/", 3)[-1]
|
||||
else:
|
||||
path = request.get_full_path().split("/", 2)[-1]
|
||||
r = redirect_to_url(urljoin('%s://%s' % (request.scheme, domain), path))
|
||||
r['Access-Control-Allow-Origin'] = '*'
|
||||
return r
|
||||
@@ -377,6 +391,7 @@ def _detect_event(request, require_live=True, require_plugin=None):
|
||||
except Event.DoesNotExist:
|
||||
try:
|
||||
if hasattr(request, 'organizer_domain'):
|
||||
# Redirect for case-insensitive event slug
|
||||
event = request.organizer.events.get(
|
||||
slug__iexact=url.kwargs['event'],
|
||||
organizer=request.organizer,
|
||||
@@ -388,6 +403,7 @@ def _detect_event(request, require_live=True, require_plugin=None):
|
||||
return r
|
||||
else:
|
||||
if 'event' in url.kwargs and 'organizer' in url.kwargs:
|
||||
# Redirect for case-insensitive event or organizer slug
|
||||
event = Event.objects.select_related('organizer').get(
|
||||
slug__iexact=url.kwargs['event'],
|
||||
organizer__slug__iexact=url.kwargs['organizer']
|
||||
@@ -403,6 +419,7 @@ def _detect_event(request, require_live=True, require_plugin=None):
|
||||
raise Http404(_('The selected event was not found.'))
|
||||
except Organizer.DoesNotExist:
|
||||
if 'organizer' in url.kwargs:
|
||||
# Redirect for case-insensitive organizer slug
|
||||
try:
|
||||
organizer = Organizer.objects.get(
|
||||
slug__iexact=url.kwargs['organizer']
|
||||
|
||||
@@ -77,7 +77,7 @@ class RedirectBackMixin:
|
||||
self.redirect_field_name,
|
||||
self.request.GET.get(self.redirect_field_name, '')
|
||||
)
|
||||
hosts = list(KnownDomain.objects.filter(event__organizer=self.request.organizer).values_list('domainname', flat=True))
|
||||
hosts = list(KnownDomain.objects.filter(organizer=self.request.organizer).values_list('domainname', flat=True))
|
||||
siteurlsplit = urlsplit(settings.SITE_URL)
|
||||
if siteurlsplit.port and siteurlsplit.port not in (80, 443):
|
||||
hosts = ['%s:%d' % (h, siteurlsplit.port) for h in hosts]
|
||||
@@ -168,7 +168,7 @@ class LogoutView(View):
|
||||
return HttpResponseRedirect(next_page)
|
||||
|
||||
def get_next_page(self):
|
||||
if getattr(self.request, 'event_domain', False):
|
||||
if getattr(self.request, 'domain_mode', 'system') in (KnownDomain.MODE_ORG_ALT_DOMAIN, KnownDomain.MODE_EVENT_DOMAIN):
|
||||
# After we cleared the cookies on this domain, redirect to the parent domain to clear cookies as well
|
||||
next_page = eventreverse(self.request.organizer, 'presale:organizer.customer.logout', kwargs={})
|
||||
if self.redirect_field_name in self.request.POST or self.redirect_field_name in self.request.GET:
|
||||
@@ -188,7 +188,7 @@ class LogoutView(View):
|
||||
self.redirect_field_name,
|
||||
self.request.GET.get(self.redirect_field_name)
|
||||
)
|
||||
hosts = list(KnownDomain.objects.filter(event__organizer=self.request.organizer).values_list('domainname', flat=True))
|
||||
hosts = list(KnownDomain.objects.filter(organizer=self.request.organizer).values_list('domainname', flat=True))
|
||||
siteurlsplit = urlsplit(settings.SITE_URL)
|
||||
if siteurlsplit.port and siteurlsplit.port not in (80, 443):
|
||||
hosts = ['%s:%d' % (h, siteurlsplit.port) for h in hosts]
|
||||
|
||||
@@ -71,7 +71,7 @@ from pretix.helpers.formats.en.formats import (
|
||||
)
|
||||
from pretix.helpers.http import redirect_to_url
|
||||
from pretix.helpers.thumb import get_thumbnail
|
||||
from pretix.multidomain.urlreverse import eventreverse
|
||||
from pretix.multidomain.urlreverse import build_absolute_uri, eventreverse
|
||||
from pretix.presale.forms.organizer import EventListFilterForm
|
||||
from pretix.presale.ical import get_public_ical
|
||||
from pretix.presale.views import OrganizerViewMixin
|
||||
@@ -1305,3 +1305,8 @@ class OrganizerFavicon(View):
|
||||
return redirect_to_url(get_thumbnail(icon_file, '32x32^', formats=settings.PILLOW_FORMATS_QUESTIONS_FAVICON).thumb.url)
|
||||
else:
|
||||
return redirect_to_url(static("pretixbase/img/favicon.ico"))
|
||||
|
||||
|
||||
class RedirectToOrganizerIndex(View):
|
||||
def get(self, *args, **kwargs):
|
||||
return redirect_to_url(build_absolute_uri(self.request.organizer, "presale:organizer.index"))
|
||||
|
||||
@@ -1967,7 +1967,7 @@ def test_pdf_data(token_client, organizer, event, order, django_assert_max_num_q
|
||||
assert not resp.data['positions'][0].get('pdf_data')
|
||||
|
||||
# order list
|
||||
with django_assert_max_num_queries(31):
|
||||
with django_assert_max_num_queries(32):
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?pdf_data=true'.format(
|
||||
organizer.slug, event.slug
|
||||
))
|
||||
@@ -1982,7 +1982,7 @@ def test_pdf_data(token_client, organizer, event, order, django_assert_max_num_q
|
||||
assert not resp.data['results'][0]['positions'][0].get('pdf_data')
|
||||
|
||||
# position list
|
||||
with django_assert_max_num_queries(34):
|
||||
with django_assert_max_num_queries(35):
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/orderpositions/?pdf_data=true'.format(
|
||||
organizer.slug, event.slug
|
||||
))
|
||||
|
||||
@@ -40,16 +40,21 @@ def env():
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_control_only_on_main_domain(env, client):
|
||||
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
|
||||
KnownDomain.objects.create(domainname='foobar', organizer=env[0], mode=KnownDomain.MODE_ORG_DOMAIN)
|
||||
r = client.get('/control/login', HTTP_HOST='foobar')
|
||||
assert r.status_code == 302
|
||||
assert r['Location'] == 'http://example.com/control/login'
|
||||
|
||||
KnownDomain.objects.create(domainname='barfoo', organizer=env[0], event=env[1])
|
||||
KnownDomain.objects.create(domainname='barfoo', organizer=env[0], event=env[1], mode=KnownDomain.MODE_EVENT_DOMAIN)
|
||||
r = client.get('/control/login', HTTP_HOST='barfoo')
|
||||
assert r.status_code == 302
|
||||
assert r['Location'] == 'http://example.com/control/login'
|
||||
|
||||
KnownDomain.objects.create(domainname='altfoo', organizer=env[0], mode=KnownDomain.MODE_ORG_ALT_DOMAIN)
|
||||
r = client.get('/control/login', HTTP_HOST='altfoo')
|
||||
assert r.status_code == 302
|
||||
assert r['Location'] == 'http://example.com/control/login'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_append_slash(env, client):
|
||||
@@ -80,6 +85,15 @@ def test_event_on_custom_domain(env, client):
|
||||
assert b'<meta property="og:title" content="MRMCD2015" />' in r.content
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_on_org_alt_domain(env, client):
|
||||
d = KnownDomain.objects.create(domainname='foobar', organizer=env[0], mode=KnownDomain.MODE_ORG_ALT_DOMAIN)
|
||||
d.event_assignments.create(event=env[1])
|
||||
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_org_domain(env, client):
|
||||
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
|
||||
@@ -95,6 +109,15 @@ def test_event_with_org_domain_on_main_domain(env, client):
|
||||
assert r['Location'] == 'http://foobar/2015/'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_with_org_alt_domain_on_main_domain(env, client):
|
||||
d = KnownDomain.objects.create(domainname='foobar', organizer=env[0])
|
||||
d.event_assignments.create(event=env[1])
|
||||
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_with_custom_domain_on_main_domain(env, client):
|
||||
KnownDomain.objects.create(domainname='foobar', organizer=env[0], event=env[1])
|
||||
@@ -112,6 +135,40 @@ def test_event_with_custom_domain_on_org_domain(env, client):
|
||||
assert r['Location'] == 'http://barfoo'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_with_custom_domain_on_org_alt_domain(env, client):
|
||||
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
|
||||
KnownDomain.objects.create(domainname='altfoo', organizer=env[0], mode=KnownDomain.MODE_ORG_ALT_DOMAIN)
|
||||
KnownDomain.objects.create(domainname='barfoo', organizer=env[0], event=env[1])
|
||||
r = client.get('/2015/', HTTP_HOST='altfoo')
|
||||
assert r.status_code == 302
|
||||
assert r['Location'] == 'http://barfoo'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_with_org_alt_domain_on_org_domain(env, client):
|
||||
KnownDomain.objects.create(domainname='foobar', organizer=env[0], mode=KnownDomain.MODE_ORG_DOMAIN)
|
||||
d = KnownDomain.objects.create(domainname='altfoo', organizer=env[0], mode=KnownDomain.MODE_ORG_ALT_DOMAIN)
|
||||
d.event_assignments.create(event=env[1])
|
||||
|
||||
r = client.get('/2015/', HTTP_HOST='foobar')
|
||||
assert r.status_code == 302
|
||||
assert r['Location'] == 'http://altfoo/2015/'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_without_org_alt_domain_on_org_alt_domain(env, client):
|
||||
KnownDomain.objects.create(domainname='foobar', organizer=env[0], mode=KnownDomain.MODE_ORG_ALT_DOMAIN)
|
||||
r = client.get('/', HTTP_HOST='foobar')
|
||||
assert r.status_code == 302
|
||||
assert r['Location'] == 'http://example.com/mrmcd/'
|
||||
|
||||
KnownDomain.objects.create(domainname='foobaz', organizer=env[0], mode=KnownDomain.MODE_ORG_DOMAIN)
|
||||
r = client.get('/', HTTP_HOST='foobar')
|
||||
assert r.status_code == 302
|
||||
assert r['Location'] == 'http://foobaz/'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_organizer_with_org_domain_on_main_domain(env, client):
|
||||
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
|
||||
@@ -143,6 +200,10 @@ def test_unknown_event_on_org_domain(env, client):
|
||||
r = client.get('/1234/', HTTP_HOST='foobar')
|
||||
assert r.status_code == 404
|
||||
|
||||
KnownDomain.objects.create(domainname='altfoo', organizer=env[0], mode=KnownDomain.MODE_ORG_ALT_DOMAIN)
|
||||
r = client.get('/1234/', HTTP_HOST='altfoo')
|
||||
assert r.status_code == 404
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_cookie_domain_on_org_domain(env, client):
|
||||
@@ -153,6 +214,16 @@ def test_cookie_domain_on_org_domain(env, client):
|
||||
assert r.client.cookies['pretix_session']['domain'] == ''
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_cookie_domain_on_org_alt_domain(env, client):
|
||||
d = KnownDomain.objects.create(domainname='foobar', organizer=env[0], mode=KnownDomain.MODE_ORG_ALT_DOMAIN)
|
||||
d.event_assignments.create(event=env[1])
|
||||
client.post('/2015/cart/add', HTTP_HOST='foobar')
|
||||
r = client.get('/2015/', HTTP_HOST='foobar')
|
||||
assert r.client.cookies['pretix_csrftoken']['domain'] == ''
|
||||
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])
|
||||
|
||||
@@ -62,6 +62,36 @@ def test_event_custom_domain_front_page(env):
|
||||
assert rendered == 'http://foobar/2015/'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_custom_event_domain_front_page(env):
|
||||
KnownDomain.objects.create(domainname='foobar', organizer=env[0], event=env[1], mode=KnownDomain.MODE_EVENT_DOMAIN)
|
||||
rendered = TEMPLATE_FRONT_PAGE.render(Context({
|
||||
'event': env[1]
|
||||
})).strip()
|
||||
assert rendered == 'http://foobar/'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_custom_org_alt_domain_front_page(env):
|
||||
KnownDomain.objects.create(domainname='foobar', organizer=env[0], mode=KnownDomain.MODE_ORG_DOMAIN)
|
||||
d = KnownDomain.objects.create(domainname='altfoo', organizer=env[0], mode=KnownDomain.MODE_ORG_ALT_DOMAIN)
|
||||
d.event_assignments.create(event=env[1])
|
||||
rendered = TEMPLATE_FRONT_PAGE.render(Context({
|
||||
'event': env[1]
|
||||
})).strip()
|
||||
assert rendered == 'http://altfoo/2015/'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_custom_org_alt_domain_unassigned_front_page(env):
|
||||
KnownDomain.objects.create(domainname='foobar', organizer=env[0], mode=KnownDomain.MODE_ORG_DOMAIN)
|
||||
KnownDomain.objects.create(domainname='altfoo', organizer=env[0], mode=KnownDomain.MODE_ORG_ALT_DOMAIN)
|
||||
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({
|
||||
|
||||
@@ -37,7 +37,7 @@ def env():
|
||||
organizer=o, name='MRMCD2015', slug='2015',
|
||||
date_from=now()
|
||||
)
|
||||
event.get_cache().clear()
|
||||
event.cache.clear()
|
||||
return o, event
|
||||
|
||||
|
||||
@@ -60,6 +60,16 @@ def test_event_org_domain_kwargs(env):
|
||||
assert eventreverse(env[1], 'presale:event.checkout', {'step': 'payment'}) == 'http://foobar/2015/checkout/payment/'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_org_alt_domain_kwargs(env):
|
||||
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
|
||||
d = KnownDomain.objects.create(domainname='altfoo', organizer=env[0], mode=KnownDomain.MODE_ORG_ALT_DOMAIN)
|
||||
assert eventreverse(env[1], 'presale:event.checkout', {'step': 'payment'}) == 'http://foobar/2015/checkout/payment/'
|
||||
d.event_assignments.create(event=env[1])
|
||||
with scopes_disabled():
|
||||
assert eventreverse(Event.objects.get(pk=env[1].pk), 'presale:event.checkout', {'step': 'payment'}) == 'http://altfoo/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/'
|
||||
@@ -72,6 +82,15 @@ def test_event_org_domain_front_page(env):
|
||||
assert eventreverse(env[0], 'presale:organizer.index') == 'http://foobar/'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_org_alt_domain_front_page(env):
|
||||
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
|
||||
d = KnownDomain.objects.create(domainname='altfoo', organizer=env[0], mode=KnownDomain.MODE_ORG_ALT_DOMAIN)
|
||||
d.event_assignments.create(event=env[1])
|
||||
assert eventreverse(env[1], 'presale:event.index') == 'http://altfoo/2015/'
|
||||
assert eventreverse(env[0], 'presale:organizer.index') == 'http://foobar/'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_custom_domain_front_page(env):
|
||||
KnownDomain.objects.create(domainname='barfoo', organizer=env[0], event=env[1])
|
||||
@@ -109,8 +128,8 @@ def test_event_org_domain_keep_scheme(env):
|
||||
}
|
||||
})
|
||||
def test_event_main_domain_cache(env):
|
||||
env[0].get_cache().clear()
|
||||
with assert_num_queries(1):
|
||||
env[0].cache.clear()
|
||||
with assert_num_queries(2):
|
||||
eventreverse(env[1], 'presale:event.index')
|
||||
with assert_num_queries(0):
|
||||
eventreverse(env[1], 'presale:event.index')
|
||||
@@ -125,8 +144,8 @@ def test_event_main_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):
|
||||
env[0].cache.clear()
|
||||
with assert_num_queries(2):
|
||||
eventreverse(env[1], 'presale:event.index')
|
||||
with assert_num_queries(0):
|
||||
eventreverse(env[1], 'presale:event.index')
|
||||
@@ -142,13 +161,36 @@ def test_event_org_domain_cache(env):
|
||||
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()
|
||||
env[0].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_alt_domain_cache_clear(env):
|
||||
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
|
||||
kd_alt = KnownDomain.objects.create(domainname='altfoo', organizer=env[0], mode=KnownDomain.MODE_ORG_ALT_DOMAIN)
|
||||
env[0].cache.clear()
|
||||
with assert_num_queries(2):
|
||||
eventreverse(env[1], 'presale:event.index')
|
||||
kd_alt.event_assignments.create(event=env[1])
|
||||
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': {
|
||||
@@ -160,14 +202,14 @@ def test_event_custom_domain_cache(env):
|
||||
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):
|
||||
with assert_num_queries(2):
|
||||
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):
|
||||
with assert_num_queries(2):
|
||||
eventreverse(ev, 'presale:event.index')
|
||||
|
||||
|
||||
@@ -190,7 +232,7 @@ def test_event_custom_domain_cache_clear(env):
|
||||
ev = Event.objects.get(pk=env[1].pk)
|
||||
assert ev.pk == env[1].pk
|
||||
assert ev.organizer == env[0]
|
||||
with assert_num_queries(1):
|
||||
with assert_num_queries(2):
|
||||
eventreverse(ev, 'presale:event.index')
|
||||
|
||||
|
||||
@@ -210,3 +252,11 @@ def test_event_custom_domain_absolute(env):
|
||||
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/'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_org_alt_domain_absolute(env):
|
||||
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
|
||||
d = KnownDomain.objects.create(domainname='altfoo', organizer=env[0], mode=KnownDomain.MODE_ORG_ALT_DOMAIN)
|
||||
d.event_assignments.create(event=env[1])
|
||||
assert build_absolute_uri(env[1], 'presale:event.index') == 'http://altfoo/2015/'
|
||||
|
||||
@@ -378,6 +378,13 @@ def test_org_sso_login_new_customer_popup(env, client, provider):
|
||||
_sso_login(client, provider, popup_origin="https://popuporigin")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_org_sso_login_new_customer_popup_org_alt_domain(env, client, provider):
|
||||
d = KnownDomain.objects.create(organizer=env[0], domainname="popuporigin", mode=KnownDomain.MODE_ORG_ALT_DOMAIN)
|
||||
d.event_assignments.create(event=env[1])
|
||||
_sso_login(client, provider, popup_origin="https://popuporigin")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_org_sso_login_new_customer_popup_invalid_origin(env, client, provider):
|
||||
KnownDomain.objects.create(organizer=env[0], event=env[1], domainname="popuporigin")
|
||||
@@ -696,16 +703,21 @@ def client2():
|
||||
return Client()
|
||||
|
||||
|
||||
def _cross_domain_login(env, client, client2):
|
||||
def _cross_domain_login(env, client, client2, org_alt=False):
|
||||
with scopes_disabled():
|
||||
customer = env[0].customers.create(email='john@example.org', is_verified=True)
|
||||
customer.set_password('foo')
|
||||
customer.save()
|
||||
KnownDomain.objects.create(domainname='org.test', organizer=env[0])
|
||||
KnownDomain.objects.create(domainname='event.test', organizer=env[0], event=env[1])
|
||||
if org_alt:
|
||||
d = KnownDomain.objects.create(domainname='event.test', organizer=env[0], mode=KnownDomain.MODE_ORG_ALT_DOMAIN)
|
||||
d.event_assignments.create(event=env[1])
|
||||
else:
|
||||
KnownDomain.objects.create(domainname='event.test', organizer=env[0], event=env[1])
|
||||
|
||||
# Log in on org domain
|
||||
r = client.post('/account/login?next=https://event.test/redeem&request_cross_domain_customer_auth=true', {
|
||||
path = '/conf/' if org_alt else '/'
|
||||
r = client.post(f'/account/login?next=https://event.test{path}redeem&request_cross_domain_customer_auth=true', {
|
||||
'email': 'john@example.org',
|
||||
'password': 'foo',
|
||||
}, HTTP_HOST='org.test')
|
||||
@@ -713,12 +725,12 @@ def _cross_domain_login(env, client, client2):
|
||||
|
||||
u = urlparse(r.headers['Location'])
|
||||
assert u.netloc == 'event.test'
|
||||
assert u.path == '/redeem'
|
||||
assert u.path == path + 'redeem'
|
||||
q = parse_qs(u.query)
|
||||
assert 'cross_domain_customer_auth' in q
|
||||
|
||||
# Take session over to event domain
|
||||
r = client2.get(f'/?{u.query}', HTTP_HOST='event.test')
|
||||
r = client2.get(f'{path}?{u.query}', HTTP_HOST='event.test')
|
||||
assert r.status_code == 200
|
||||
assert b'john@example.org' in r.content
|
||||
|
||||
@@ -727,12 +739,27 @@ def _cross_domain_login(env, client, client2):
|
||||
def test_cross_domain_login(env, client, client2):
|
||||
_cross_domain_login(env, client, client2)
|
||||
|
||||
# Logged in on org domain
|
||||
# Logged in on evnet domain
|
||||
r = client.get('/', HTTP_HOST='event.test')
|
||||
assert r.status_code == 200
|
||||
assert b'john@example.org' in r.content
|
||||
|
||||
# Logged in on event domain
|
||||
# Logged in on org domain
|
||||
r = client2.get('/', HTTP_HOST='org.test')
|
||||
assert r.status_code == 200
|
||||
assert b'john@example.org' in r.content
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_cross_domain_login_org_alt(env, client, client2):
|
||||
_cross_domain_login(env, client, client2, org_alt=True)
|
||||
|
||||
# Logged in on org alt domain
|
||||
r = client.get('/conf/', HTTP_HOST='event.test')
|
||||
assert r.status_code == 200
|
||||
assert b'john@example.org' in r.content
|
||||
|
||||
# Logged in on org domain
|
||||
r = client2.get('/', HTTP_HOST='org.test')
|
||||
assert r.status_code == 200
|
||||
assert b'john@example.org' in r.content
|
||||
|
||||
Reference in New Issue
Block a user