diff --git a/src/pretix/base/models/event.py b/src/pretix/base/models/event.py
index caacf81922..3de4f68bc5 100644
--- a/src/pretix/base/models/event.py
+++ b/src/pretix/base/models/event.py
@@ -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(
diff --git a/src/pretix/control/forms/event.py b/src/pretix/control/forms/event.py
index d640e026e3..3be1ea7054 100644
--- a/src/pretix/control/forms/event.py
+++ b/src/pretix/control/forms/event.py
@@ -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
diff --git a/src/pretix/control/forms/organizer.py b/src/pretix/control/forms/organizer.py
index fb5f44ef10..8a4082e86a 100644
--- a/src/pretix/control/forms/organizer.py
+++ b/src/pretix/control/forms/organizer.py
@@ -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):
diff --git a/src/pretix/control/templates/pretixcontrol/event/settings.html b/src/pretix/control/templates/pretixcontrol/event/settings.html
index f858f3eab6..f701872c05 100644
--- a/src/pretix/control/templates/pretixcontrol/event/settings.html
+++ b/src/pretix/control/templates/pretixcontrol/event/settings.html
@@ -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" %}
diff --git a/src/pretix/control/templates/pretixcontrol/organizers/edit.html b/src/pretix/control/templates/pretixcontrol/organizers/edit.html
index ce39171d1c..f9d000a1f2 100644
--- a/src/pretix/control/templates/pretixcontrol/organizers/edit.html
+++ b/src/pretix/control/templates/pretixcontrol/organizers/edit.html
@@ -294,6 +294,71 @@
{% bootstrap_field sform.invoice_regenerate_allowed layout="control" %}
+ {% if domain_formset %}
+