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 %} - - - - - - - - - {% for e in events %} - - - - - {% endfor %} - -
{% trans "Event name" %}{% trans "Start date" %}
- {{ e.name }} - {{ e.get_date_from_display }}
- {% endif %} - - - {% trans "Create a new event" %} - -
-
- {% if request.orgaperm.can_change_permissions %} -
-
-
- {% csrf_token %} -

- {% blocktrans trimmed %} - You can use the following list to control who can create new events in the name of - this organizer and who can add more people to this list. This does not - control who has access to a particular event. You can control the access to an - event in the "Permissions" section of the event's settings. A user does not need to - be on the list here to get access to an event. - {% endblocktrans %} -

-

- {% trans "Everyone on this list can control the organizer settings on this page." %} -

- - {% bootstrap_formset_errors formset %} - {{ formset.management_form }} -
- - - - - - - - - - - {% for form in formset %} - - - - - - - {% endfor %} - - - - - - - - - - - - -
{% trans "User" %}{% trans "Create events" %}{% trans "Change permissions" %}{% trans "Delete" %}
- {{ form.id }} - {% if form.instance.user %} - {{ form.instance.user }} - {% else %} - {{ form.instance.invite_email }} - - {% endif %} - {{ form.can_create_events }}{{ form.can_change_permissions }}{{ form.DELETE }}
- {% trans "Adding a new user" %}
- {% blocktrans trimmed %} - To add a new user, you can enter their email address here. If they - already have a pretix account, they will immediately be added to the team. - Otherwise, they will be sent an email with an invitation. - {% endblocktrans %} -
-
-
- {% bootstrap_field add_form.user layout='inline' %} -
-
-
{{ add_form.can_create_events }}{{ add_form.can_change_permissions }}
-
-
- -
- -
-
-
- {% endif %} - {% for title, content in tabs %} -
-
- {{ content }} -
-
- {% endfor %} -
+{% block inner %} + {% if events|length == 0 %} +

+ {% trans "You currently do not have access to any events." %} +

+ {% else %} + + + + + + + + + {% for e in events %} + + + + + {% endfor %} + +
{% trans "Event name" %}{% trans "Start date" %}
+ {{ e.name }} + {{ e.get_date_from_display }}
+ {% 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 %} +
+ {% csrf_token %} +

+ {% blocktrans trimmed %} + You can use the following list to control who can create new events in the name of + this organizer and who can add more people to this list. This does not + control who has access to a particular event. You can control the access to an + event in the "Permissions" section of the event's settings. A user does not need to + be on the list here to get access to an event. + {% endblocktrans %} +

+

+ {% trans "Everyone on this list can control the organizer settings on this page." %} +

+ + {% bootstrap_formset_errors formset %} + {{ formset.management_form }} +
+ + + + + + + + + + + {% for form in formset %} + + + + + + + {% endfor %} + + + + + + + + + + + + +
{% trans "User" %}{% trans "Create events" %}{% trans "Change permissions" %}{% trans "Delete" %}
+ {{ form.id }} + {% if form.instance.user %} + {{ form.instance.user }} + {% else %} + {{ form.instance.invite_email }} + + {% endif %} + {{ form.can_create_events }}{{ form.can_change_permissions }}{{ form.DELETE }}
+ {% trans "Adding a new user" %}
+ {% blocktrans trimmed %} + To add a new user, you can enter their email address here. If they + already have a pretix account, they will immediately be added to the team. + Otherwise, they will be sent an email with an invitation. + {% endblocktrans %} +
+
+
+ {% bootstrap_field add_form.user layout='inline' %} +
+
+
{{ add_form.can_create_events }}{{ add_form.can_change_permissions }}
+
+
+ +
+ +
+{% 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, })