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 @@