diff --git a/src/pretix/api/serializers/event.py b/src/pretix/api/serializers/event.py
index c6fef3d69..4276b756b 100644
--- a/src/pretix/api/serializers/event.py
+++ b/src/pretix/api/serializers/event.py
@@ -747,6 +747,7 @@ class EventSettingsSerializer(SettingsSerializer):
'max_items_per_order',
'reservation_time',
'contact_mail',
+ 'contact_url',
'show_variations_expanded',
'hide_sold_out',
'meta_noindex',
diff --git a/src/pretix/api/serializers/organizer.py b/src/pretix/api/serializers/organizer.py
index 9df859701..c62b91a3d 100644
--- a/src/pretix/api/serializers/organizer.py
+++ b/src/pretix/api/serializers/organizer.py
@@ -576,6 +576,7 @@ class OrganizerSettingsSerializer(SettingsSerializer):
'customer_accounts_require_login_for_order_access',
'invoice_regenerate_allowed',
'contact_mail',
+ 'contact_url',
'imprint_url',
'organizer_info_text',
'event_list_type',
diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py
index 53fbbfda3..ae679d186 100644
--- a/src/pretix/base/settings.py
+++ b/src/pretix/base/settings.py
@@ -2284,6 +2284,17 @@ DEFAULTS = {
help_text=_("We'll show this publicly to allow attendees to contact you.")
)
},
+ 'contact_url': {
+ 'default': None,
+ 'type': str,
+ 'serializer_class': serializers.URLField,
+ 'form_class': forms.URLField,
+ 'form_kwargs': dict(
+ label=_("Contact URL"),
+ help_text=_("If you set this, the footer contact link will point here instead of using the email address above. "
+ "Please note that you still need to add a contact email address that will be shared with all emails you send.")
+ )
+ },
'imprint_url': {
'default': None,
'type': str,
diff --git a/src/pretix/control/forms/event.py b/src/pretix/control/forms/event.py
index 588ee56a8..5d86c0f94 100644
--- a/src/pretix/control/forms/event.py
+++ b/src/pretix/control/forms/event.py
@@ -625,6 +625,7 @@ class EventSettingsForm(EventSettingsValidationMixin, FormPlaceholderMixin, Sett
'max_items_per_order',
'reservation_time',
'contact_mail',
+ 'contact_url',
'show_variations_expanded',
'hide_sold_out',
'meta_noindex',
@@ -1904,6 +1905,12 @@ class QuickSetupForm(I18nForm):
required=False,
help_text=_("We'll show this publicly to allow attendees to contact you.")
)
+ contact_url = forms.URLField(
+ label=_("Contact URL"),
+ required=False,
+ help_text=_("If you set this, the footer contact link will point here instead of using the email address above. "
+ "Please note that you still need to add a contact email address that will be shared with all emails you send.")
+ )
total_quota = forms.IntegerField(
label=_("Total capacity"),
min_value=0,
diff --git a/src/pretix/control/forms/organizer.py b/src/pretix/control/forms/organizer.py
index 007856250..2bafd2d42 100644
--- a/src/pretix/control/forms/organizer.py
+++ b/src/pretix/control/forms/organizer.py
@@ -604,6 +604,7 @@ class OrganizerSettingsForm(SettingsForm):
'customer_accounts_require_login_for_order_access',
'invoice_regenerate_allowed',
'contact_mail',
+ 'contact_url',
'imprint_url',
'organizer_info_text',
'event_list_type',
diff --git a/src/pretix/control/templates/pretixcontrol/event/quick_setup.html b/src/pretix/control/templates/pretixcontrol/event/quick_setup.html
index 571c1c5e3..733ebd08a 100644
--- a/src/pretix/control/templates/pretixcontrol/event/quick_setup.html
+++ b/src/pretix/control/templates/pretixcontrol/event/quick_setup.html
@@ -193,6 +193,7 @@
{% endblocktrans %}
{% bootstrap_field form.contact_mail layout="control" %}
+ {% bootstrap_field form.contact_url layout="control" %}
{% bootstrap_field form.imprint_url layout="control" %}
diff --git a/src/pretix/control/templates/pretixcontrol/event/settings.html b/src/pretix/control/templates/pretixcontrol/event/settings.html
index 8d9a46f73..8077e5718 100644
--- a/src/pretix/control/templates/pretixcontrol/event/settings.html
+++ b/src/pretix/control/templates/pretixcontrol/event/settings.html
@@ -30,6 +30,7 @@
{% bootstrap_field form.date_admission layout="control" %}
{% bootstrap_field form.currency layout="control" %}
{% bootstrap_field sform.contact_mail layout="control" %}
+ {% bootstrap_field sform.contact_url layout="control" %}
{% bootstrap_field sform.imprint_url layout="control" %}
{% bootstrap_field form.is_public layout="control" %}
{% bootstrap_field form.all_sales_channels layout="control" %}
diff --git a/src/pretix/control/templates/pretixcontrol/organizers/edit.html b/src/pretix/control/templates/pretixcontrol/organizers/edit.html
index 960c6fb4f..0c2090d38 100644
--- a/src/pretix/control/templates/pretixcontrol/organizers/edit.html
+++ b/src/pretix/control/templates/pretixcontrol/organizers/edit.html
@@ -34,6 +34,7 @@
{% endif %}
{% bootstrap_field sform.imprint_url layout="control" %}
{% bootstrap_field sform.contact_mail layout="control" %}
+ {% bootstrap_field sform.contact_url layout="control" %}
{% bootstrap_field sform.organizer_info_text layout="control" %}
{% bootstrap_field sform.event_team_provisioning layout="control" %}
{% if sform.allowed_restricted_plugins %}
diff --git a/src/pretix/presale/templates/pretixpresale/event/base.html b/src/pretix/presale/templates/pretixpresale/event/base.html
index b3f566c83..660ce7386 100644
--- a/src/pretix/presale/templates/pretixpresale/event/base.html
+++ b/src/pretix/presale/templates/pretixpresale/event/base.html
@@ -219,8 +219,8 @@
{% endif %}
{% endblock %}
{% block footernav %}
- {% if request.event.settings.contact_mail %}
- {% trans "Contact" %}
+ {% if request.event.settings.contact_url or request.event.settings.contact_mail %}
+ {% trans "Contact" %}
{% endif %}
{% if request.event.settings.privacy_url %}
{% trans "Privacy policy" %}
diff --git a/src/pretix/presale/templates/pretixpresale/organizers/base.html b/src/pretix/presale/templates/pretixpresale/organizers/base.html
index da77df7cd..2f9affeae 100644
--- a/src/pretix/presale/templates/pretixpresale/organizers/base.html
+++ b/src/pretix/presale/templates/pretixpresale/organizers/base.html
@@ -97,8 +97,10 @@
{% endblock %}
{% endblock %}
{% block footernav %}
- {% if not request.event and request.organizer.settings.contact_mail %}
- {% trans "Contact" %}
+ {% if not request.event %}
+ {% if request.organizer.settings.contact_url or request.organizer.settings.contact_mail %}
+ {% trans "Contact" %}
+ {% endif %}
{% endif %}
{% if not request.event and request.organizer.settings.privacy_url %}
{% trans "Privacy policy" %}
diff --git a/src/tests/api/test_events.py b/src/tests/api/test_events.py
index fd1929185..3787216fc 100644
--- a/src/tests/api/test_events.py
+++ b/src/tests/api/test_events.py
@@ -1413,11 +1413,13 @@ def test_event_create_with_seating_maps(token_client, organizer, event, meta_pro
@pytest.mark.django_db
def test_get_event_settings(token_client, organizer, event):
event.settings.imprint_url = "https://example.org"
+ event.settings.contact_url = "https://example.org/contact"
resp = token_client.get(
'/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug),
)
assert resp.status_code == 200
assert resp.data['imprint_url'] == "https://example.org"
+ assert resp.data['contact_url'] == "https://example.org/contact"
assert resp.data['seating_allow_blocked_seats_for_channel'] == []
resp = token_client.get(
@@ -1443,9 +1445,11 @@ def test_patch_event_settings(token_client, organizer, event, team):
team.save()
organizer.settings.imprint_url = 'https://example.org'
+ organizer.settings.contact_url = 'https://example.org/contact'
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug),
{
+ 'contact_url': 'https://example.com/contact',
'imprint_url': 'https://example.com',
'confirm_texts': [
{
@@ -1458,10 +1462,12 @@ def test_patch_event_settings(token_client, organizer, event, team):
format='json'
)
assert resp.status_code == 200
+ assert resp.data['contact_url'] == "https://example.com/contact"
assert resp.data['imprint_url'] == "https://example.com"
assert resp.data['seating_allow_blocked_seats_for_channel'] == ['web']
assert not resp.data['reusable_media_active']
event.settings.flush()
+ assert event.settings.contact_url == 'https://example.com/contact'
assert event.settings.imprint_url == 'https://example.com'
assert event.settings.seating_allow_blocked_seats_for_channel == ['web']
assert not event.settings.reusable_media_active
@@ -1471,6 +1477,7 @@ def test_patch_event_settings(token_client, organizer, event, team):
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug),
{
+ 'contact_url': 'https://example.com/contact',
'imprint_url': 'https://example.com',
'confirm_texts': [
{
@@ -1513,13 +1520,16 @@ def test_patch_event_settings(token_client, organizer, event, team):
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug),
{
+ 'contact_url': None,
'imprint_url': None,
},
format='json'
)
assert resp.status_code == 200
+ assert resp.data['contact_url'] == "https://example.org/contact"
assert resp.data['imprint_url'] == "https://example.org"
event.settings.flush()
+ assert event.settings.contact_url == 'https://example.org/contact'
assert event.settings.imprint_url == 'https://example.org'
resp = token_client.put(
@@ -1583,6 +1593,18 @@ def test_patch_event_settings(token_client, organizer, event, team):
@pytest.mark.django_db
def test_patch_event_settings_validation(token_client, organizer, event):
+ resp = token_client.patch(
+ '/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug),
+ {
+ 'contact_url': 'invalid',
+ },
+ format='json'
+ )
+ assert resp.status_code == 400
+ assert resp.data == {
+ 'contact_url': ['Enter a valid URL.']
+ }
+
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug),
{
diff --git a/src/tests/api/test_organizers.py b/src/tests/api/test_organizers.py
index 2109e8de1..1ec00a5ad 100644
--- a/src/tests/api/test_organizers.py
+++ b/src/tests/api/test_organizers.py
@@ -148,6 +148,27 @@ def test_patch_settings(token_client, organizer):
)
assert resp.status_code == 400
+ resp = token_client.patch(
+ '/api/v1/organizers/{}/settings/'.format(organizer.slug),
+ {
+ 'contact_url': 'invalid'
+ },
+ format='json'
+ )
+ assert resp.status_code == 400
+
+ resp = token_client.patch(
+ '/api/v1/organizers/{}/settings/'.format(organizer.slug),
+ {
+ 'contact_url': 'https://example.org/contact',
+ },
+ format='json'
+ )
+ assert resp.status_code == 200
+ assert resp.data['contact_url'] == 'https://example.org/contact'
+ organizer.settings.flush()
+ assert organizer.settings.contact_url == 'https://example.org/contact'
+
resp = token_client.patch(
'/api/v1/organizers/{}/settings/'.format(organizer.slug),
{
diff --git a/src/tests/presale/test_event.py b/src/tests/presale/test_event.py
index 5ffd28db3..70dade2be 100644
--- a/src/tests/presale/test_event.py
+++ b/src/tests/presale/test_event.py
@@ -81,6 +81,16 @@ class EventMiddlewareTest(EventTestMixin, SoupTest):
doc = self.get_doc('/%s/%s/' % (self.orga.slug, self.event.slug))
self.assertIn(str(self.event.name), doc.find("h1").text)
+ def test_footer_contact_url_overrides_mailto(self):
+ self.event.settings.contact_mail = 'orga@example.org'
+ self.event.settings.contact_url = 'https://example.org/contact'
+
+ doc = self.get_doc('/%s/%s/' % (self.orga.slug, self.event.slug))
+ contact_link = doc.find('a', string='Contact')
+
+ self.assertIsNotNone(contact_link)
+ self.assertTrue(contact_link['href'].startswith('/redirect/?url=https%3A//example.org/contact'))
+
def test_no_session_cookie_set_on_event_index_view(self):
resp = self.client.get('/%s/%s/' % (self.orga.slug, self.event.slug))
self.assertEqual(resp.status_code, 200)
diff --git a/src/tests/presale/test_organizer_page.py b/src/tests/presale/test_organizer_page.py
index ead641455..263d97f51 100644
--- a/src/tests/presale/test_organizer_page.py
+++ b/src/tests/presale/test_organizer_page.py
@@ -46,6 +46,17 @@ def test_organizer_page_shown(env, client):
assert 'MRMCD e.V.' in r.rendered_content
+@pytest.mark.django_db
+def test_organizer_footer_contact_url_overrides_mailto(env, client):
+ env[0].settings.contact_mail = 'orga@example.org'
+ env[0].settings.contact_url = 'https://example.org/contact'
+
+ r = client.get('/mrmcd/')
+
+ assert '/redirect/?url=https%3A//example.org/contact' in r.rendered_content
+ assert 'mailto:orga@example.org' not in r.rendered_content
+
+
@pytest.mark.django_db
def test_public_event_on_page(env, client):
env[1].is_public = True