diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py index ffa5e4630..c52838041 100644 --- a/src/pretix/base/settings.py +++ b/src/pretix/base/settings.py @@ -305,7 +305,7 @@ Your {event} team""")) class SettingsProxy: """ - This objects allows convenient access to settings stored in the + This object allows convenient access to settings stored in the EventSettings/OrganizerSettings database model. It exposes all settings as properties and it will do all the nasty inheritance and defaults stuff for you. @@ -442,7 +442,7 @@ class SettingsProxy: def set(self, key: str, value: Any) -> None: """ - Stores a setting to the database of this object. + Stores a setting to the database of its object. """ if key in self._cache(): s = self._cache()[key] diff --git a/src/pretix/base/signals.py b/src/pretix/base/signals.py index a8103e48e..324b05797 100644 --- a/src/pretix/base/signals.py +++ b/src/pretix/base/signals.py @@ -122,3 +122,9 @@ be everything between a minute and a day. The actions you perform should be idempotent, i.e. it should not make a difference if this is sent out more often than expected. """ + +register_global_settings = django.dispatch.Signal() +""" +All plugins that are installed may send fields for the global settings form, as +an OrderedDict of (setting name, form field). +""" diff --git a/src/pretix/control/forms/global_settings.py b/src/pretix/control/forms/global_settings.py new file mode 100644 index 000000000..7c073b43b --- /dev/null +++ b/src/pretix/control/forms/global_settings.py @@ -0,0 +1,42 @@ +from collections import OrderedDict + +from django.utils.translation import ugettext_lazy as _ + +from pretix.base.forms import SettingsForm +from pretix.base.i18n import I18nFormField, I18nTextInput +from pretix.base.models.settings import GlobalSetting +from pretix.base.settings import SettingsProxy +from pretix.base.signals import register_global_settings + + +class GlobalSettingsObject: + def __init__(self): + self.settings = SettingsProxy(self, type=GlobalSetting) + self.setting_objects = GlobalSetting.objects + self.slug = 'GLOBALSETTINGS' + + +class GlobalSettingsForm(SettingsForm): + def __init__(self, *args, **kwargs): + self.obj = GlobalSettingsObject() + super().__init__(*args, obj=self.obj, **kwargs) + + self.fields = OrderedDict([ + ('footer_text', I18nFormField( + widget=I18nTextInput, + required=False, + label=_("Additional footer text"), + help_text=_("Will be included as additional text in the footer, site-wide.") + )), + ('footer_link', I18nFormField( + widget=I18nTextInput, + required=False, + label=_("Additional footer link"), + help_text=_("Will be included as the link in the additional footer text.") + )) + ]) + responses = register_global_settings.send(self) + for r, response in responses: + for key, value in response.items(): + # We need to be this explicit, since OrderedDict.update does not retain ordering + self.fields[key] = value diff --git a/src/pretix/control/permissions.py b/src/pretix/control/permissions.py index ef5708063..8b24f810a 100644 --- a/src/pretix/control/permissions.py +++ b/src/pretix/control/permissions.py @@ -81,7 +81,7 @@ def organizer_permission_required(permission): class OrganizerPermissionRequiredMixin: """ - This mixin is equivalent to the event_permission_required view decorator but + This mixin is equivalent to the organizer_permission_required view decorator but is in a form suitable for class-based views. """ permission = '' @@ -90,3 +90,33 @@ class OrganizerPermissionRequiredMixin: def as_view(cls, **initkwargs): view = super(OrganizerPermissionRequiredMixin, cls).as_view(**initkwargs) return organizer_permission_required(cls.permission)(view) + + +def administrator_permission_required(permission): + """ + This view decorator rejects all requests with a 403 response which are not from + users with the is_superuser flag. + """ + def decorator(function): + def wrapper(request, *args, **kw): + if not request.user.is_authenticated: # NOQA + # just a double check, should not ever happen + raise PermissionDenied() + if not request.user.is_superuser: + raise PermissionDenied(_('You do not have permission to view this content.')) + return function(request, *args, **kw) + return wrapper + return decorator + + +class AdministratorPermissionRequiredMixin: + """ + This mixin is equivalent to the administrator_permission_required view decorator but + is in a form suitable for class-based views. + """ + permission = '' + + @classmethod + def as_view(cls, **initkwargs): + view = super(AdministratorPermissionRequiredMixin, cls).as_view(**initkwargs) + return administrator_permission_required(cls.permission)(view) diff --git a/src/pretix/control/templates/pretixcontrol/base.html b/src/pretix/control/templates/pretixcontrol/base.html index ef9190faa..ae9f7ab4f 100644 --- a/src/pretix/control/templates/pretixcontrol/base.html +++ b/src/pretix/control/templates/pretixcontrol/base.html @@ -101,6 +101,14 @@ {% trans "Dashboard" %} + {% if request.user.is_superuser %} +