From db5f0aa02dd10f77d8179f9c9177ed9df77dd4f8 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Wed, 6 Sep 2017 09:31:33 +0200 Subject: [PATCH] Fix #156 -- Plug-in settings navigation hook --- doc/development/api/customview.rst | 80 ++++++++++++++++++- doc/development/api/general.rst | 2 +- src/pretix/control/signals.py | 19 +++++ .../templates/pretixcontrol/event/base.html | 2 +- .../pretixcontrol/event/settings_base.html | 7 ++ src/pretix/control/views/event.py | 37 ++++++--- 6 files changed, 132 insertions(+), 15 deletions(-) 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 %} +
+ {% csrf_token %} +
+ {% trans "Friends Tickets Settings" %} + {% bootstrap_form form layout="horizontal" %} +
+
+ +
+
+ {% 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'