diff --git a/doc/development/api/customview.rst b/doc/development/api/customview.rst
index 8a1b8a5768..3e64555075 100644
--- a/doc/development/api/customview.rst
+++ b/doc/development/api/customview.rst
@@ -60,7 +60,85 @@ your views::
def admin_view(request, organizer, event):
...
-Similarly, there is ``organizer_permission_required`` and ``OrganizerPermissionRequiredMixin``.
+Similarly, there is ``organizer_permission_required`` and ``OrganizerPermissionRequiredMixin``. In case of
+event-related views, there is also a signal that allows you to add the view to the event navigation like this::
+
+
+ from django.core.urlresolvers import resolve, reverse
+ from django.dispatch import receiver
+ from django.utils.translation import ugettext_lazy as _
+ from pretix.control.signals import nav_event
+
+
+ @receiver(nav_event, dispatch_uid='friends_tickets_nav')
+ def navbar_info(sender, request, **kwargs):
+ url = resolve(request.path_info)
+ if not request.user.has_event_permission(request.organizer, request.event, 'can_change_vouchers'):
+ return []
+ return [{
+ 'label': _('My plugin view'),
+ 'icon': 'heart',
+ 'url': reverse('plugins:myplugin:index', kwargs={
+ 'event': request.event.slug,
+ 'organizer': request.organizer.slug,
+ }),
+ 'active': url.namespace == 'plugins:myplugin' and url.url_name == 'review',
+ }]
+
+
+Event settings view
+-------------------
+
+A special case of a control panel view is a view hooked into the event settings page. For this case, there is a
+special navigation signal::
+
+ @receiver(nav_event_settings, dispatch_uid='friends_tickets_nav_settings')
+ def navbar_settings(sender, request, **kwargs):
+ url = resolve(request.path_info)
+ return [{
+ 'label': _('My settings'),
+ 'url': reverse('plugins:myplugin:settings', kwargs={
+ 'event': request.event.slug,
+ 'organizer': request.organizer.slug,
+ }),
+ 'active': url.namespace == 'plugins:myplugin' and url.url_name == 'settings',
+ }]
+
+Also, your view should inherit from ``EventSettingsViewMixin`` and your template from ``pretixcontrol/event/settings_base.html``
+for good integration. If you just want to display a form, you could do it like the following::
+
+ class MySettingsView(EventSettingsViewMixin, EventSettingsFormView):
+ model = Event
+ permission = 'can_change_settings'
+ form_class = MySettingsForm
+ template_name = 'my_plugin/settings.html'
+
+ def get_success_url(self, **kwargs):
+ return reverse('plugins:myplugin:settings', kwargs={
+ 'organizer': self.request.event.organizer.slug,
+ 'event': self.request.event.slug,
+ })
+
+With this template::
+
+ {% extends "pretixcontrol/event/settings_base.html" %}
+ {% load i18n %}
+ {% load bootstrap3 %}
+ {% block title %} {% trans "Friends Tickets Settings" %} {% endblock %}
+ {% block inside %}
+
+ {% endblock %}
Frontend views
--------------
diff --git a/doc/development/api/general.rst b/doc/development/api/general.rst
index aa6dfae8a5..3023e78e71 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, nav_organizer
+ :members: nav_event, html_head, quota_detail_html, nav_topbar, nav_global, nav_organizer, nav_event_settings
.. automodule:: pretix.base.signals
diff --git a/src/pretix/control/signals.py b/src/pretix/control/signals.py
index 86734c0eb9..a995ecae07 100644
--- a/src/pretix/control/signals.py
+++ b/src/pretix/control/signals.py
@@ -181,3 +181,22 @@ 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``.
"""
+
+
+nav_event_settings = Signal(
+ providing_args=['request']
+)
+"""
+This signal is sent out to include tab links on the settings page of an event.
+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.event.EventSettingsViewMixin`` for your view
+and your tempalte inherits from ``pretixcontrol/event/settings_base.html``.
+
+As with all plugin signals, the ``sender`` keyword argument will contain the event.
+A second keyword argument ``request`` will contain the request object.
+"""
diff --git a/src/pretix/control/templates/pretixcontrol/event/base.html b/src/pretix/control/templates/pretixcontrol/event/base.html
index 75b14a81bb..a1ddb4d6fa 100644
--- a/src/pretix/control/templates/pretixcontrol/event/base.html
+++ b/src/pretix/control/templates/pretixcontrol/event/base.html
@@ -14,7 +14,7 @@
{% if 'can_change_event_settings' in request.eventpermset %}
+ {% if is_event_settings or "event.settings" == url_name or "event.settings." in url_name %}class="active"{% endif %}>
{% trans "Settings" %}
diff --git a/src/pretix/control/templates/pretixcontrol/event/settings_base.html b/src/pretix/control/templates/pretixcontrol/event/settings_base.html
index 72c4094bcb..7995853c92 100644
--- a/src/pretix/control/templates/pretixcontrol/event/settings_base.html
+++ b/src/pretix/control/templates/pretixcontrol/event/settings_base.html
@@ -52,6 +52,13 @@
{% endif %}
+ {% for nav in nav_event_settings %}
+
+
+ {{ nav.label }}
+
+
+ {% endfor %}
{% block inside %}
{% endblock %}
diff --git a/src/pretix/control/views/event.py b/src/pretix/control/views/event.py
index 038184260a..9efbb2f2f9 100644
--- a/src/pretix/control/views/event.py
+++ b/src/pretix/control/views/event.py
@@ -39,6 +39,7 @@ from pretix.control.forms.event import (
PaymentSettingsForm, ProviderForm, TaxRuleForm, TicketSettingsForm,
)
from pretix.control.permissions import EventPermissionRequiredMixin
+from pretix.control.signals import nav_event_settings
from pretix.helpers.urls import build_absolute_uri
from pretix.presale.style import regenerate_css
@@ -46,6 +47,18 @@ from . import CreateView, UpdateView
from ..logdisplay import OVERVIEW_BLACKLIST
+class EventSettingsViewMixin:
+ def get_context_data(self, **kwargs):
+ ctx = super().get_context_data(**kwargs)
+ ctx['nav_event_settings'] = []
+ ctx['is_event_settings'] = True
+
+ for recv, retv in nav_event_settings.send(sender=self.request.event, request=self.request):
+ ctx['nav_event_settings'] += retv
+ ctx['nav_event_settings'].sort(key=lambda n: n['label'])
+ return ctx
+
+
class MetaDataEditorMixin:
meta_form = EventMetaValueForm
meta_model = EventMetaValue
@@ -81,7 +94,7 @@ class MetaDataEditorMixin:
f.delete()
-class EventUpdate(EventPermissionRequiredMixin, MetaDataEditorMixin, UpdateView):
+class EventUpdate(EventSettingsViewMixin, EventPermissionRequiredMixin, MetaDataEditorMixin, UpdateView):
model = Event
form_class = EventUpdateForm
template_name = 'pretixcontrol/event/settings.html'
@@ -150,7 +163,7 @@ class EventUpdate(EventPermissionRequiredMixin, MetaDataEditorMixin, UpdateView)
return tz.localize(dt.replace(tzinfo=None)) if dt is not None else None
-class EventPlugins(EventPermissionRequiredMixin, TemplateView, SingleObjectMixin):
+class EventPlugins(EventSettingsViewMixin, EventPermissionRequiredMixin, TemplateView, SingleObjectMixin):
model = Event
context_object_name = 'event'
permission = 'can_change_event_settings'
@@ -213,7 +226,7 @@ class EventPlugins(EventPermissionRequiredMixin, TemplateView, SingleObjectMixin
})
-class PaymentSettings(EventPermissionRequiredMixin, TemplateView, SingleObjectMixin):
+class PaymentSettings(EventSettingsViewMixin, EventPermissionRequiredMixin, TemplateView, SingleObjectMixin):
model = Event
context_object_name = 'event'
permission = 'can_change_event_settings'
@@ -332,7 +345,7 @@ class EventSettingsFormView(EventPermissionRequiredMixin, FormView):
return self.get(request)
-class InvoiceSettings(EventSettingsFormView):
+class InvoiceSettings(EventSettingsViewMixin, EventSettingsFormView):
model = Event
form_class = InvoiceSettingsForm
template_name = 'pretixcontrol/event/invoicing.html'
@@ -360,7 +373,7 @@ class InvoicePreview(EventPermissionRequiredMixin, View):
return resp
-class DisplaySettings(EventSettingsFormView):
+class DisplaySettings(EventSettingsViewMixin, EventSettingsFormView):
model = Event
form_class = DisplaySettingsForm
template_name = 'pretixcontrol/event/display.html'
@@ -396,7 +409,7 @@ class DisplaySettings(EventSettingsFormView):
return self.get(request)
-class MailSettings(EventSettingsFormView):
+class MailSettings(EventSettingsViewMixin, EventSettingsFormView):
model = Event
form_class = MailSettingsForm
template_name = 'pretixcontrol/event/mail.html'
@@ -580,7 +593,7 @@ class TicketSettingsPreview(EventPermissionRequiredMixin, View):
})
-class TicketSettings(EventPermissionRequiredMixin, FormView):
+class TicketSettings(EventSettingsViewMixin, EventPermissionRequiredMixin, FormView):
model = Event
form_class = TicketSettingsForm
template_name = 'pretixcontrol/event/tickets.html'
@@ -689,7 +702,7 @@ class TicketSettings(EventPermissionRequiredMixin, FormView):
return providers
-class EventPermissions(EventPermissionRequiredMixin, TemplateView):
+class EventPermissions(EventSettingsViewMixin, EventPermissionRequiredMixin, TemplateView):
template_name = 'pretixcontrol/event/permissions.html'
@@ -844,7 +857,7 @@ class EventComment(EventPermissionRequiredMixin, View):
})
-class TaxList(EventPermissionRequiredMixin, ListView):
+class TaxList(EventSettingsViewMixin, EventPermissionRequiredMixin, ListView):
model = TaxRule
context_object_name = 'taxrules'
paginate_by = 30
@@ -855,7 +868,7 @@ class TaxList(EventPermissionRequiredMixin, ListView):
return self.request.event.tax_rules.all()
-class TaxCreate(EventPermissionRequiredMixin, CreateView):
+class TaxCreate(EventSettingsViewMixin, EventPermissionRequiredMixin, CreateView):
model = TaxRule
form_class = TaxRuleForm
template_name = 'pretixcontrol/event/tax_edit.html'
@@ -886,7 +899,7 @@ class TaxCreate(EventPermissionRequiredMixin, CreateView):
return super().form_invalid(form)
-class TaxUpdate(EventPermissionRequiredMixin, UpdateView):
+class TaxUpdate(EventSettingsViewMixin, EventPermissionRequiredMixin, UpdateView):
model = TaxRule
form_class = TaxRuleForm
template_name = 'pretixcontrol/event/tax_edit.html'
@@ -923,7 +936,7 @@ class TaxUpdate(EventPermissionRequiredMixin, UpdateView):
return super().form_invalid(form)
-class TaxDelete(EventPermissionRequiredMixin, DeleteView):
+class TaxDelete(EventSettingsViewMixin, EventPermissionRequiredMixin, DeleteView):
model = TaxRule
template_name = 'pretixcontrol/event/tax_delete.html'
permission = 'can_change_event_settings'