diff --git a/doc/development/api/general.rst b/doc/development/api/general.rst
index a56ba2291a..3f96a4f338 100644
--- a/doc/development/api/general.rst
+++ b/doc/development/api/general.rst
@@ -47,7 +47,7 @@ Backend
-------
.. automodule:: pretix.control.signals
- :members: nav_event, html_head, quota_detail_html, nav_topbar, nav_global, organizer_edit_tabs
+ :members: nav_event, html_head, quota_detail_html, nav_topbar, nav_global, nav_organizer
.. automodule:: pretix.base.signals
diff --git a/src/pretix/base/signals.py b/src/pretix/base/signals.py
index 19b7c65076..0b2edda63e 100644
--- a/src/pretix/base/signals.py
+++ b/src/pretix/base/signals.py
@@ -1,3 +1,4 @@
+import warnings
from typing import Any, Callable, List, Tuple
import django.dispatch
@@ -52,6 +53,13 @@ class EventPluginSignal(django.dispatch.Signal):
return responses
+class DeprecatedSignal(django.dispatch.Signal):
+
+ def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
+ warnings.warn('This signal is deprecated and will soon be removed', stacklevel=3)
+ super().connect(receiver, sender=None, weak=True, dispatch_uid=None)
+
+
event_live_issues = EventPluginSignal(
providing_args=[]
)
diff --git a/src/pretix/control/signals.py b/src/pretix/control/signals.py
index 8776712b81..629b0dd0c2 100644
--- a/src/pretix/control/signals.py
+++ b/src/pretix/control/signals.py
@@ -1,6 +1,6 @@
from django.dispatch import Signal
-from pretix.base.signals import EventPluginSignal
+from pretix.base.signals import DeprecatedSignal, EventPluginSignal
restriction_formset = EventPluginSignal(
providing_args=["item"]
@@ -143,14 +143,29 @@ quota as argument in the ``quota`` keyword argument.
As with all plugin signals, the ``sender`` keyword argument will contain the event.
"""
-organizer_edit_tabs = Signal(
+organizer_edit_tabs = DeprecatedSignal(
providing_args=['organizer', 'request']
)
"""
-This signal is sent out to include tabs on the detail page of an organizer. Receivers
-should return a tuple with the first item being the tab title and the second item
-being the content as HTML. The receivers get the ``organizer`` and the ``request`` as
-keyword arguments.
-
-This is a regular django signal (no pretix event signal).
+Deprecated signal, no longer works. We just keep the definition so old plugins don't
+break the installation.
+"""
+
+
+nav_organizer = Signal(
+ providing_args=['organizer', 'request']
+)
+"""
+This signal is sent out to include tab links on the detail page of an organizer.
+Receivers are expected to return a list of dictionaries. The dictionaries
+should contain at least the keys ``label`` and ``url``. You should also return
+an ``active`` key with a boolean set to ``True``, when this item should be marked
+as active.
+
+If your linked view should stay in the tab-like context of this page, we recommend
+that you use ``pretix.control.views.organizer.OrganizerDetailViewMixin`` for your view
+and your tempalte inherits from ``pretixcontrol/organizers/base.html``.
+
+This is a regular django signal (no pretix event signal). Receivers will be passed
+the keyword arguments ``organizer`` and ``request``.
"""
diff --git a/src/pretix/control/templates/pretixcontrol/organizers/base.html b/src/pretix/control/templates/pretixcontrol/organizers/base.html
new file mode 100644
index 0000000000..3de3a4108d
--- /dev/null
+++ b/src/pretix/control/templates/pretixcontrol/organizers/base.html
@@ -0,0 +1,39 @@
+{% extends "pretixcontrol/base.html" %}
+{% load i18n %}
+{% load bootstrap3 %}
+{% block title %}{% trans "Organizer" %}{% endblock %}
+{% block content %}
+
+ {% blocktrans with name=organizer.name %}Organizer: {{ name }}{% endblocktrans %}
+
+
+ {% trans "Edit" %}
+
+
+
+
+ {% block inner %}
+ {% endblock %}
+
+{% endblock %}
diff --git a/src/pretix/control/templates/pretixcontrol/organizers/detail.html b/src/pretix/control/templates/pretixcontrol/organizers/detail.html
index 3a396dc6d2..17c2bf6a81 100644
--- a/src/pretix/control/templates/pretixcontrol/organizers/detail.html
+++ b/src/pretix/control/templates/pretixcontrol/organizers/detail.html
@@ -1,159 +1,34 @@
-{% extends "pretixcontrol/base.html" %}
+{% extends "pretixcontrol/organizers/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
-{% block title %}{% trans "Organizer" %}{% endblock %}
-{% block content %}
-
- {% blocktrans with name=organizer.name %}Organizer: {{ name }}{% endblocktrans %}
-
-
- {% trans "Edit" %}
-
-
-
-
-
-
-
- {% if events|length == 0 %}
-
- {% trans "You currently do not have access to any events." %}
-
- {% else %}
-
-
-
- | {% trans "Event name" %} |
- {% trans "Start date" %} |
-
-
-
- {% for e in events %}
-
- |
- {{ e.name }}
- |
- {{ e.get_date_from_display }} |
-
- {% endfor %}
-
-
- {% endif %}
-
-
- {% trans "Create a new event" %}
-
-
-
- {% if request.orgaperm.can_change_permissions %}
-
- {% endif %}
- {% for title, content in tabs %}
-
- {% endfor %}
-
+{% block inner %}
+ {% if events|length == 0 %}
+
+ {% trans "You currently do not have access to any events." %}
+
+ {% else %}
+
+
+
+ | {% trans "Event name" %} |
+ {% trans "Start date" %} |
+
+
+
+ {% for e in events %}
+
+ |
+ {{ e.name }}
+ |
+ {{ e.get_date_from_display }} |
+
+ {% endfor %}
+
+
+ {% endif %}
+
+
+ {% trans "Create a new event" %}
+
{% endblock %}
diff --git a/src/pretix/control/templates/pretixcontrol/organizers/teams.html b/src/pretix/control/templates/pretixcontrol/organizers/teams.html
new file mode 100644
index 0000000000..0638718efd
--- /dev/null
+++ b/src/pretix/control/templates/pretixcontrol/organizers/teams.html
@@ -0,0 +1,84 @@
+{% extends "pretixcontrol/organizers/base.html" %}
+{% load i18n %}
+{% load bootstrap3 %}
+{% block inner %}
+
+{% endblock %}
diff --git a/src/pretix/control/urls.py b/src/pretix/control/urls.py
index 9443585a56..0061cf7440 100644
--- a/src/pretix/control/urls.py
+++ b/src/pretix/control/urls.py
@@ -35,6 +35,7 @@ urlpatterns = [
url(r'^organizers/add$', organizer.OrganizerCreate.as_view(), name='organizers.add'),
url(r'^organizer/(?P[^/]+)/$', organizer.OrganizerDetail.as_view(), name='organizer'),
url(r'^organizer/(?P[^/]+)/edit$', organizer.OrganizerUpdate.as_view(), name='organizer.edit'),
+ url(r'^organizer/(?P[^/]+)/teams$', organizer.OrganizerTeamView.as_view(), name='organizer.teams'),
url(r'^events/$', main.EventList.as_view(), name='events'),
url(r'^events/add$', main.EventWizard.as_view(), name='events.add'),
url(r'^event/(?P[^/]+)/(?P[^/]+)/', include([
diff --git a/src/pretix/control/views/organizer.py b/src/pretix/control/views/organizer.py
index 76b1cdce8d..dd30f11709 100644
--- a/src/pretix/control/views/organizer.py
+++ b/src/pretix/control/views/organizer.py
@@ -14,7 +14,7 @@ from pretix.base.models import Organizer, OrganizerPermission, User
from pretix.base.services.mail import SendMailException, mail
from pretix.control.forms.organizer import OrganizerForm, OrganizerUpdateForm
from pretix.control.permissions import OrganizerPermissionRequiredMixin
-from pretix.control.signals import organizer_edit_tabs
+from pretix.control.signals import nav_organizer
from pretix.helpers.urls import build_absolute_uri
@@ -45,7 +45,23 @@ class OrganizerPermissionCreateForm(OrganizerPermissionForm):
user = forms.EmailField(required=False, label=_('User'))
-class OrganizerDetail(OrganizerPermissionRequiredMixin, DetailView):
+class OrganizerDetailViewMixin:
+
+ def get_context_data(self, **kwargs):
+ ctx = super().get_context_data(**kwargs)
+ ctx['nav_organizer'] = []
+ ctx['organizer'] = self.request.organizer
+
+ for recv, retv in nav_organizer.send(sender=self.request.organizer, request=self.request,
+ organizer=self.request.organizer):
+ ctx['nav_organizer'] += retv
+ return ctx
+
+ def get_object(self, queryset=None) -> Organizer:
+ return self.request.organizer
+
+
+class OrganizerDetail(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, DetailView):
model = Organizer
template_name = 'pretixcontrol/organizers/detail.html'
permission = None
@@ -54,6 +70,18 @@ class OrganizerDetail(OrganizerPermissionRequiredMixin, DetailView):
def get_object(self, queryset=None) -> Organizer:
return self.request.organizer
+ def get_context_data(self, **kwargs):
+ ctx = super().get_context_data(**kwargs)
+ ctx['events'] = self.request.organizer.events.all()
+ return ctx
+
+
+class OrganizerTeamView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, DetailView):
+ model = Organizer
+ template_name = 'pretixcontrol/organizers/teams.html'
+ permission = 'can_change_permissions'
+ context_object_name = 'organizer'
+
@cached_property
def formset(self):
fs = modelformset_factory(
@@ -86,13 +114,6 @@ class OrganizerDetail(OrganizerPermissionRequiredMixin, DetailView):
ctx = super().get_context_data(**kwargs)
ctx['formset'] = self.formset
ctx['add_form'] = self.add_form
- ctx['events'] = self.request.organizer.events.all()
- ctx['tabs'] = []
-
- for recv, retv in organizer_edit_tabs.send(sender=self.request.organizer, request=self.request,
- organizer=self.request.organizer):
- ctx['tabs'].append(retv)
-
return ctx
def _send_invite(self, instance):
@@ -116,12 +137,6 @@ class OrganizerDetail(OrganizerPermissionRequiredMixin, DetailView):
@transaction.atomic
def post(self, *args, **kwargs):
- if not self.request.orgaperm.can_change_permissions:
- raise PermissionDenied(_("You have no permission to do this."))
-
- if 'formset-TOTAL_FORMS' not in self.request.POST:
- return self.get(*args, **kwargs)
-
if self.formset.is_valid() and self.add_form.is_valid():
if self.add_form.has_changed():
logdata = {
@@ -186,7 +201,7 @@ class OrganizerDetail(OrganizerPermissionRequiredMixin, DetailView):
return self.get(*args, **kwargs)
def get_success_url(self) -> str:
- return reverse('control:organizer', kwargs={
+ return reverse('control:organizer.teams', kwargs={
'organizer': self.request.organizer.slug,
})