diff --git a/src/pretix/base/models/organizer.py b/src/pretix/base/models/organizer.py index ddac919f41..20ddb9062f 100644 --- a/src/pretix/base/models/organizer.py +++ b/src/pretix/base/models/organizer.py @@ -403,13 +403,13 @@ class Team(LoggedModel): ) result = set() - for permission in get_all_event_permissions(): - if self.all_event_permissions or self.limit_event_permissions.get(permission.name): - result.add(permission.name) + for permission in get_all_event_permissions().keys(): + if self.all_event_permissions or self.limit_event_permissions.get(permission): + result.add(permission) - for permission in get_all_organizer_permissions(): - if self.all_organizer_permissions or self.limit_organizer_permissions.get(permission.name): - result.add(permission.name) + for permission in get_all_organizer_permissions().keys(): + if self.all_organizer_permissions or self.limit_organizer_permissions.get(permission): + result.add(permission) if include_legacy: # Add legacy permissions as well for plugin compatibility @@ -447,6 +447,19 @@ class Team(LoggedModel): def active_tokens(self): return self.tokens.filter(active=True) + def save(self, **kwargs): + if not isinstance(self.limit_event_permissions, dict): + raise TypeError("Permissions must be a dictionary") + if not isinstance(self.limit_organizer_permissions, dict): + raise TypeError("Permissions must be a dictionary") + for k in self.limit_event_permissions.values(): + if k is not True: + raise TypeError("Permissions must only contain True values") + for k in self.limit_organizer_permissions.values(): + if k is not True: + raise TypeError("Permissions must only contain True values") + return super().save(**kwargs) + class Meta: verbose_name = _("Team") verbose_name_plural = _("Teams") diff --git a/src/pretix/base/permissions.py b/src/pretix/base/permissions.py index 46d6fbfb8f..8ce11dcaf0 100644 --- a/src/pretix/base/permissions.py +++ b/src/pretix/base/permissions.py @@ -75,7 +75,8 @@ def get_all_organizer_permissions(): @receiver(register_event_permissions, dispatch_uid="base_register_default_event_permissions") def register_default_event_permissions(sender, **kwargs): return [ - Permission("event.settings.general:write", _("Change general settings"), None, None), + Permission("event.settings.general:write", _("Change general settings"), None, + _("This includes access to all settings not listed explicitly below, including plugin settings.")), Permission("event.settings.payment:write", _("Change payment settings"), None, None), Permission("event.settings.plugins:write", _("Change plugin settings"), None, None), Permission("event.settings.email.sender:write", _("Change email sending settings"), None, None), @@ -86,9 +87,9 @@ def register_default_event_permissions(sender, **kwargs): Permission("event.orders:read", _("View orders"), None, None), Permission("event.orders:write", _("Change orders"), None, _("This includes the ability to cancel and refund individual orders.")), Permission("event.orders:checkin", _("Check-in orders"), None, None), - Permission("event:cancel", pgettext_lazy("subevent", "Cancel the entire event or date"), None, None), Permission("event.vouchers:read", _("View vouchers"), None, None), Permission("event.vouchers:write", _("Change vouchers"), None, None), + Permission("event:cancel", pgettext_lazy("subevent", " entire event or date"), None, None), ] @@ -96,7 +97,8 @@ def register_default_event_permissions(sender, **kwargs): def register_default_organizer_permissions(sender, **kwargs): return [ Permission("organizer.events:create", _("Create events"), None, None), - Permission("organizer.settings.general:write", _("Change settings"), None, None), + Permission("organizer.settings.general:write", _("Change settings"), None, + _("This includes access to all organizer-level functionality not listed explicitly below, including plugin settings.")), Permission("organizer.teams:write", _("Change teams"), None, _("This includes the ability to give someone (including oneself) additional permissions.")), Permission("organizer.giftcards:read", _("View gift cards"), None, None), @@ -107,5 +109,5 @@ def register_default_organizer_permissions(sender, **kwargs): Permission("organizer.reusablemedia:write", _("Change reusable media"), None, None), Permission("organizer.devices:read", _("View devices"), None, None), Permission("organizer.devices:write", _("Change devices"), None, - _("This inclues the ability to give access to events and date oneself does not have access to.")), + _("This includes the ability to give access to events and data oneself does not have access to.")), ] diff --git a/src/pretix/control/forms/organizer.py b/src/pretix/control/forms/organizer.py index ed53bcf434..ca42af0475 100644 --- a/src/pretix/control/forms/organizer.py +++ b/src/pretix/control/forms/organizer.py @@ -76,6 +76,9 @@ from pretix.base.models import ( ) from pretix.base.models.customers import CustomerSSOClient, CustomerSSOProvider from pretix.base.models.organizer import OrganizerFooterLink, TeamQuerySet +from pretix.base.permissions import ( + get_all_event_permissions, get_all_organizer_permissions, +) from pretix.base.settings import ( PERSON_NAME_SCHEMES, PERSON_NAME_TITLE_GROUPS, validate_organizer_settings, ) @@ -297,6 +300,18 @@ class MembershipTypeForm(I18nModelForm): fields = ['name', 'transferable', 'allow_parallel_usage', 'max_usages'] +class PermissionMultipleChoiceField(forms.MultipleChoiceField): + def to_python(self, value): + return { + k: True for k in super().to_python(value) if k + } + + def prepare_value(self, value): + if isinstance(value, dict): + return [k for k, v in value.items() if v is True] + return super().prepare_value(value) + + class TeamForm(forms.ModelForm): def __init__(self, *args, **kwargs): @@ -305,6 +320,44 @@ class TeamForm(forms.ModelForm): self.fields['limit_events'].queryset = organizer.events.all().order_by( '-has_subevents', '-date_from' ) + self.fields['limit_event_permissions'] = PermissionMultipleChoiceField( + label=self.fields['limit_event_permissions'].label, + choices=[ + ( + p.name, + format_html( + '{} ({})', + p.label, p.help_text, p.name + ) if p.help_text + else format_html('{} ({})', p.label, p.name) + ) + for p in get_all_event_permissions().values() + ], + widget=forms.CheckboxSelectMultiple(attrs={ + 'data-inverse-dependency': '#id_all_event_permissions', + 'class': 'scrolling-multiple-choice scrolling-multiple-choice-large', + }), + required=False, + ) + self.fields['limit_organizer_permissions'] = PermissionMultipleChoiceField( + label=self.fields['limit_organizer_permissions'].label, + choices=[ + ( + p.name, + format_html( + '{} ({})', + p.label, p.help_text, p.name + ) if p.help_text + else format_html('{} ({})', p.label, p.name) + ) + for p in get_all_organizer_permissions().values() + ], + widget=forms.CheckboxSelectMultiple(attrs={ + 'data-inverse-dependency': '#id_all_organizer_permissions', + 'class': 'scrolling-multiple-choice scrolling-multiple-choice-large', + }), + required=False, + ) class Meta: model = Team @@ -323,7 +376,7 @@ class TeamForm(forms.ModelForm): def clean(self): data = super().clean() - if self.instance.pk and not data['can_change_teams']: + if self.instance.pk and not data['all_organizer_permissions'] and not data.get('limit_organizer_permissions', {}).get('organizer.teams:write'): if not self.instance.organizer.teams.exclude(pk=self.instance.pk).filter( TeamQuerySet.organizer_permission_q("organizer.teams:write"), members__isnull=False diff --git a/src/pretix/control/templates/pretixcontrol/organizers/team_edit.html b/src/pretix/control/templates/pretixcontrol/organizers/team_edit.html index a5bcb8bd2b..3802a9b0aa 100644 --- a/src/pretix/control/templates/pretixcontrol/organizers/team_edit.html +++ b/src/pretix/control/templates/pretixcontrol/organizers/team_edit.html @@ -22,25 +22,16 @@
{% trans "Organizer permissions" %} - {% bootstrap_field form.can_create_events layout="control" %} - {% bootstrap_field form.can_manage_gift_cards layout="control" %} - {% bootstrap_field form.can_manage_customers layout="control" %} - {% bootstrap_field form.can_manage_reusable_media layout="control" %} - {% bootstrap_field form.can_change_teams layout="control" %} - {% bootstrap_field form.can_change_organizer_settings layout="control" %} + {% bootstrap_field form.all_organizer_permissions layout="control" %} + {% bootstrap_field form.limit_organizer_permissions layout="control" %}
{% trans "Event permissions" %} {% bootstrap_field form.all_events layout="control" %} {% bootstrap_field form.limit_events layout="control" %} - {% bootstrap_field form.can_change_event_settings layout="control" %} - {% bootstrap_field form.can_change_items layout="control" %} - {% bootstrap_field form.can_view_orders layout="control" %} - {% bootstrap_field form.can_change_orders layout="control" %} - {% bootstrap_field form.can_checkin_orders layout="control" %} - {% bootstrap_field form.can_view_vouchers layout="control" %} - {% bootstrap_field form.can_change_vouchers layout="control" %} + {% bootstrap_field form.all_event_permissions layout="control" %} + {% bootstrap_field form.limit_event_permissions layout="control" %}