mirror of
https://github.com/pretix/pretix.git
synced 2026-05-12 16:24:00 +00:00
Refactor to permission groups
This commit is contained in:
@@ -46,7 +46,7 @@ from pretix.base.models import (
|
||||
)
|
||||
from pretix.base.models.seating import SeatingPlanLayoutValidator
|
||||
from pretix.base.permissions import (
|
||||
get_all_event_permissions, get_all_organizer_permissions,
|
||||
get_all_event_permission_groups, get_all_organizer_permission_groups,
|
||||
)
|
||||
from pretix.base.plugins import (
|
||||
PLUGIN_LEVEL_EVENT, PLUGIN_LEVEL_EVENT_ORGANIZER_HYBRID,
|
||||
@@ -355,8 +355,18 @@ class TeamSerializer(serializers.ModelSerializer):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['limit_event_permissions'].choices = [(p.name, p.name) for p in get_all_event_permissions().values()]
|
||||
self.fields['limit_organizer_permissions'].choices = [(p.name, p.name) for p in get_all_organizer_permissions().values()]
|
||||
|
||||
event_perms_flattened = []
|
||||
organizer_perms_flattened = []
|
||||
for pg in get_all_event_permission_groups().values():
|
||||
for action in pg.actions:
|
||||
event_perms_flattened.append(f"{pg.name}:{action}")
|
||||
for pg in get_all_organizer_permission_groups().values():
|
||||
for action in pg.actions:
|
||||
organizer_perms_flattened.append(f"{pg.name}:{action}")
|
||||
|
||||
self.fields['limit_event_permissions'].choices = [(p, p) for p in event_perms_flattened]
|
||||
self.fields['limit_organizer_permissions'].choices = [(p, p) for p in organizer_perms_flattened]
|
||||
|
||||
def to_representation(self, instance):
|
||||
r = super().to_representation(instance)
|
||||
@@ -406,6 +416,25 @@ class TeamSerializer(serializers.ModelSerializer):
|
||||
|
||||
if full_data.get('limit_events') and full_data.get('all_events'):
|
||||
raise ValidationError('Do not set both limit_events and all_events.')
|
||||
|
||||
full_data.update(data)
|
||||
for pg in get_all_event_permission_groups().values():
|
||||
requested = ",".join(sorted(
|
||||
a for a in pg.actions if self.instance and full_data["limit_event_permissions"].get(f"{pg.name}:{a}")
|
||||
))
|
||||
if requested not in (",".join(sorted(opt.actions)) for opt in pg.options):
|
||||
raise ValidationError(f"For permission group {pg.name}, the valid combinations of actions are "
|
||||
f"'{'\' or \''.join(','.join(opt.actions) for opt in pg.options)}' but you tried to "
|
||||
f"set '{requested}'.")
|
||||
for pg in get_all_organizer_permission_groups().values():
|
||||
requested = ",".join(sorted(
|
||||
a for a in pg.actions if self.instance and full_data["limit_organizer_permissions"].get(f"{pg.name}:{a}")
|
||||
))
|
||||
if requested not in (",".join(sorted(opt.actions)) for opt in pg.options):
|
||||
raise ValidationError(f"For permission group {pg.name}, the valid combinations of actions are "
|
||||
f"'{'\' or \''.join(','.join(opt.actions) for opt in pg.options)}' but you tried to "
|
||||
f"set '{requested}'.")
|
||||
|
||||
return data
|
||||
|
||||
|
||||
|
||||
@@ -398,12 +398,13 @@ class Team(LoggedModel):
|
||||
}
|
||||
|
||||
def event_permission_set(self, include_legacy=True) -> set:
|
||||
from ..permissions import get_all_event_permissions
|
||||
from ..permissions import get_all_event_permission_groups
|
||||
|
||||
result = set()
|
||||
for permission in get_all_event_permissions().keys():
|
||||
if self.all_event_permissions or self.limit_event_permissions.get(permission):
|
||||
result.add(permission)
|
||||
for pg in get_all_event_permission_groups().values():
|
||||
for action in pg.actions:
|
||||
if self.all_event_permissions or self.limit_event_permissions.get(f"{pg.name}:{action}"):
|
||||
result.add(f"{pg.name}:{action}")
|
||||
|
||||
if include_legacy:
|
||||
# Add legacy permissions as well for plugin compatibility
|
||||
@@ -414,12 +415,13 @@ class Team(LoggedModel):
|
||||
return result
|
||||
|
||||
def organizer_permission_set(self, include_legacy=True) -> set:
|
||||
from ..permissions import get_all_organizer_permissions
|
||||
from ..permissions import get_all_organizer_permission_groups
|
||||
|
||||
result = set()
|
||||
for permission in get_all_organizer_permissions().keys():
|
||||
if self.all_organizer_permissions or self.limit_organizer_permissions.get(permission):
|
||||
result.add(permission)
|
||||
for pg in get_all_organizer_permission_groups().values():
|
||||
for action in pg.actions:
|
||||
if self.all_organizer_permissions or self.limit_organizer_permissions.get(f"{pg.name}:{action}"):
|
||||
result.add(f"{pg.name}:{action}")
|
||||
|
||||
if include_legacy:
|
||||
# Add legacy permissions as well for plugin compatibility
|
||||
|
||||
@@ -21,13 +21,15 @@
|
||||
#
|
||||
|
||||
import logging
|
||||
from collections import OrderedDict, namedtuple
|
||||
from collections import OrderedDict
|
||||
from typing import Dict, List, NamedTuple, Tuple
|
||||
|
||||
from django.dispatch import receiver
|
||||
from django.utils.functional import Promise
|
||||
from django.utils.translation import gettext_lazy as _, pgettext_lazy
|
||||
|
||||
from pretix.base.signals import (
|
||||
register_event_permissions, register_organizer_permissions,
|
||||
register_event_permission_groups, register_organizer_permission_groups,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -35,17 +37,28 @@ _ALL_EVENT_PERMISSIONS = None
|
||||
_ALL_ORGANIZER_PERMISSIONS = None
|
||||
|
||||
|
||||
Permission = namedtuple('Permission', ('name', 'label', 'plugin_name', 'help_text'))
|
||||
class PermissionOption(NamedTuple):
|
||||
actions: Tuple[str, ...]
|
||||
label: str | Promise
|
||||
help_text: str | Promise = None
|
||||
|
||||
|
||||
def get_all_event_permissions():
|
||||
class PermissionGroup(NamedTuple):
|
||||
name: str
|
||||
label: str | Promise
|
||||
actions: List[str]
|
||||
options: List[PermissionOption]
|
||||
help_text: str | Promise = None
|
||||
|
||||
|
||||
def get_all_event_permission_groups() -> Dict[str, PermissionGroup]:
|
||||
global _ALL_EVENT_PERMISSIONS
|
||||
|
||||
if _ALL_EVENT_PERMISSIONS:
|
||||
return _ALL_EVENT_PERMISSIONS
|
||||
|
||||
types = OrderedDict()
|
||||
for recv, ret in register_event_permissions.send(None):
|
||||
for recv, ret in register_event_permission_groups.send(None):
|
||||
if isinstance(ret, (list, tuple)):
|
||||
for r in ret:
|
||||
types[r.name] = r
|
||||
@@ -55,14 +68,14 @@ def get_all_event_permissions():
|
||||
return types
|
||||
|
||||
|
||||
def get_all_organizer_permissions():
|
||||
def get_all_organizer_permission_groups() -> Dict[str, PermissionGroup]:
|
||||
global _ALL_ORGANIZER_PERMISSIONS
|
||||
|
||||
if _ALL_ORGANIZER_PERMISSIONS:
|
||||
return _ALL_ORGANIZER_PERMISSIONS
|
||||
|
||||
types = OrderedDict()
|
||||
for recv, ret in register_organizer_permissions.send(None):
|
||||
for recv, ret in register_organizer_permission_groups.send(None):
|
||||
if isinstance(ret, (list, tuple)):
|
||||
for r in ret:
|
||||
types[r.name] = r
|
||||
@@ -72,46 +85,167 @@ def get_all_organizer_permissions():
|
||||
return types
|
||||
|
||||
|
||||
@receiver(register_event_permissions, dispatch_uid="base_register_default_event_permissions")
|
||||
OPTS_ALL_READ = [
|
||||
PermissionOption(actions=tuple(), label=pgettext_lazy("permission_level", "View")),
|
||||
PermissionOption(actions=("write",), label=pgettext_lazy("permission_level", "View and change")),
|
||||
]
|
||||
OPTS_ALL_READ_SETTINGS_API = [
|
||||
PermissionOption(actions=tuple(), label=pgettext_lazy("permission_level", "View"),
|
||||
help_text=_("API only")),
|
||||
PermissionOption(actions=("write",), label=pgettext_lazy("permission_level", "View and change")),
|
||||
]
|
||||
OPTS_ALL_READ_SETTINGS_PARENT = [
|
||||
PermissionOption(actions=tuple(), label=pgettext_lazy("permission_level", "View"),
|
||||
help_text=_("Menu item will only show up if the user has permission for general settings.")),
|
||||
PermissionOption(actions=("write",), label=pgettext_lazy("permission_level", "View and change")),
|
||||
]
|
||||
OPTS_READ_WRITE = [
|
||||
PermissionOption(actions=tuple(), label=pgettext_lazy("permission_level", "No access")),
|
||||
PermissionOption(actions=("read",), label=pgettext_lazy("permission_level", "View")),
|
||||
PermissionOption(actions=("read", "write"), label=pgettext_lazy("permission_level", "View and change")),
|
||||
]
|
||||
|
||||
|
||||
@receiver(register_event_permission_groups, dispatch_uid="base_register_default_event_permissions")
|
||||
def register_default_event_permissions(sender, **kwargs):
|
||||
return [
|
||||
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.tax:write", _("Change tax rules"), None, None),
|
||||
Permission("event.settings.invoicing:write", _("Change invoicing settings"), None, None),
|
||||
Permission("event.subevents:write", pgettext_lazy("subevent", "Change event series dates"), None,
|
||||
_("Read access is granted to all teams with access to the event.")),
|
||||
Permission("event.items:write", _("Change products, quotas, and questions"), None,
|
||||
_("Also includes related objects like categories or discounts. Read access is granted to all teams with access to the event.")),
|
||||
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.vouchers:read", _("View vouchers"), None, None),
|
||||
Permission("event.vouchers:write", _("Change vouchers"), None, None),
|
||||
Permission("event:cancel", pgettext_lazy("subevent", "Cancel entire event or date"), None, None),
|
||||
PermissionGroup(
|
||||
name="event.settings.general",
|
||||
label=_("General settings"),
|
||||
actions=["write"],
|
||||
options=OPTS_ALL_READ_SETTINGS_API,
|
||||
help_text=_(
|
||||
"This includes access to all settings not listed explicitly below, including plugin settings."
|
||||
),
|
||||
),
|
||||
PermissionGroup(
|
||||
name="event.settings.payment",
|
||||
label=_("Payment settings"),
|
||||
actions=["write"],
|
||||
options=OPTS_ALL_READ_SETTINGS_PARENT,
|
||||
),
|
||||
PermissionGroup(
|
||||
name="event.settings.tax",
|
||||
label=_("Tax settings"),
|
||||
actions=["write"],
|
||||
options=OPTS_ALL_READ_SETTINGS_PARENT,
|
||||
),
|
||||
PermissionGroup(
|
||||
name="event.settings.invoicing",
|
||||
label=_("Invoicing settings"),
|
||||
actions=["write"],
|
||||
options=OPTS_ALL_READ_SETTINGS_PARENT,
|
||||
),
|
||||
PermissionGroup(
|
||||
name="event.subevents",
|
||||
label=_("Event series dates"),
|
||||
actions=["write"],
|
||||
options=OPTS_ALL_READ,
|
||||
),
|
||||
PermissionGroup(
|
||||
name="event.items",
|
||||
label=_("Products, quotas and questions"),
|
||||
actions=["write"],
|
||||
options=OPTS_ALL_READ,
|
||||
help_text=_("Also includes related objects like categories or discounts."),
|
||||
),
|
||||
PermissionGroup(
|
||||
name="event.orders",
|
||||
label=_("Orders"),
|
||||
actions=["read", "write", "checkin"],
|
||||
options=[
|
||||
PermissionOption(actions=tuple(), label=pgettext_lazy("permission_level", "No access")),
|
||||
PermissionOption(actions=("checkin",), label=pgettext_lazy("permission_level", "Only check-in")),
|
||||
PermissionOption(actions=("read",), label=pgettext_lazy("permission_level", "View all")),
|
||||
PermissionOption(actions=("read", "checkin"), label=pgettext_lazy("permission_level", "View all and check-in")),
|
||||
PermissionOption(actions=("read", "write"), label=pgettext_lazy("permission_level", "View all and change"),
|
||||
help_text=_("Includes the ability to cancel and refund individual orders.")),
|
||||
],
|
||||
help_text=_("Also includes related objects like the waiting list."),
|
||||
),
|
||||
PermissionGroup(
|
||||
name="event.vouchers",
|
||||
label=_("Vouchers"),
|
||||
actions=["read", "write"],
|
||||
options=OPTS_READ_WRITE,
|
||||
),
|
||||
PermissionGroup(
|
||||
name="event",
|
||||
label=_("Full event or date cancellation"),
|
||||
actions=["cancel"],
|
||||
options=[
|
||||
# If we ever add more actions, we need a new UI idea here
|
||||
PermissionOption(actions=tuple(), label=pgettext_lazy("permission_level", "Not allowed")),
|
||||
PermissionOption(actions=("cancel",), label=pgettext_lazy("permission_level", "Allowed")),
|
||||
],
|
||||
help_text="",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@receiver(register_organizer_permissions, dispatch_uid="base_register_default_organizer_permissions")
|
||||
@receiver(register_organizer_permission_groups, dispatch_uid="base_register_default_organizer_permissions")
|
||||
def register_default_organizer_permissions(sender, **kwargs):
|
||||
return [
|
||||
Permission("organizer.events:create", _("Create events"), 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. Read access "
|
||||
"is implicitly granted to the same team.")),
|
||||
Permission("organizer.giftcards:read", _("View gift cards"), None, None),
|
||||
Permission("organizer.giftcards:write", _("Change gift cards"), None, None),
|
||||
Permission("organizer.customers:read", _("View customer accounts"), None, None),
|
||||
Permission("organizer.customers:write", _("Change customer accounts"), None, None),
|
||||
Permission("organizer.reusablemedia:read", _("View reusable media"), None,
|
||||
_("This includes access to data of tickets connected to reusable media.")),
|
||||
Permission("organizer.reusablemedia:write", _("Change reusable media"), None, None),
|
||||
Permission("organizer.devices:read", _("View devices and gates"), None, None),
|
||||
Permission("organizer.devices:write", _("Change devices and gates"), None,
|
||||
_("This includes the ability to give access to events and data oneself does not have access to.")),
|
||||
Permission("organizer.seatingplans:write", _("Change seating plans"), None,
|
||||
_("Read access is implicitly given to all teams.")),
|
||||
PermissionGroup(
|
||||
name="organizer.events",
|
||||
label=_("Events"),
|
||||
actions=["create"],
|
||||
options=[
|
||||
PermissionOption(actions=tuple(), label=pgettext_lazy("permission_level", "Only existing events")),
|
||||
PermissionOption(actions=("create",), label=pgettext_lazy("permission_level", "Create new events")),
|
||||
],
|
||||
help_text="",
|
||||
),
|
||||
PermissionGroup(
|
||||
name="organizer.settings.general",
|
||||
label=_("Settings"),
|
||||
actions=["write"],
|
||||
options=OPTS_ALL_READ_SETTINGS_API,
|
||||
help_text=_("This includes access to all organizer-level functionality not listed explicitly below, including plugin settings."),
|
||||
),
|
||||
PermissionGroup(
|
||||
name="organizer.teams",
|
||||
label=_("Teams"),
|
||||
actions=["write"],
|
||||
options=[
|
||||
PermissionOption(actions=tuple(), label=pgettext_lazy("permission_level", "No access")),
|
||||
PermissionOption(actions=("write",), label=pgettext_lazy("permission_level", "View and change"),
|
||||
help_text=_("Includes the ability to give someone (including oneself) additional permissions.")),
|
||||
],
|
||||
),
|
||||
PermissionGroup(
|
||||
name="organizer.giftcards",
|
||||
label=_("Gift cards"),
|
||||
actions=["read", "write"],
|
||||
options=OPTS_READ_WRITE,
|
||||
),
|
||||
PermissionGroup(
|
||||
name="organizer.customers",
|
||||
label=_("Customers"),
|
||||
actions=["read", "write"],
|
||||
options=OPTS_READ_WRITE,
|
||||
),
|
||||
PermissionGroup(
|
||||
name="organizer.reusablemedia",
|
||||
label=_("Reusable media"),
|
||||
actions=["read", "write"],
|
||||
options=OPTS_READ_WRITE,
|
||||
),
|
||||
PermissionGroup(
|
||||
name="organizer.devices",
|
||||
label=_("Devices"),
|
||||
actions=["read", "write"],
|
||||
options=[
|
||||
PermissionOption(actions=tuple(), label=pgettext_lazy("permission_level", "No access")),
|
||||
PermissionOption(actions=("read",), label=pgettext_lazy("permission_level", "View")),
|
||||
PermissionOption(actions=("read", "write"), label=pgettext_lazy("permission_level", "View and change"),
|
||||
help_text=_("Includes the ability to give access to events and data oneself does not have access to.")),
|
||||
],
|
||||
),
|
||||
PermissionGroup(
|
||||
name="organizer.seatingplans",
|
||||
label=_("Seating plans"),
|
||||
actions=["write"],
|
||||
options=OPTS_ALL_READ,
|
||||
),
|
||||
]
|
||||
|
||||
@@ -561,16 +561,16 @@ however for this signal, the ``sender`` **may also be None** to allow creating t
|
||||
notification settings!
|
||||
"""
|
||||
|
||||
register_event_permissions = GlobalSignal()
|
||||
register_event_permission_groups = GlobalSignal()
|
||||
"""
|
||||
This signal is sent out to get all known permissions. Receivers should return an
|
||||
instance of pretix.base.permissions.Permission or a list of such instances.
|
||||
instance of pretix.base.permissions.PermissionGroup or a list of such instances.
|
||||
"""
|
||||
|
||||
register_organizer_permissions = GlobalSignal()
|
||||
register_organizer_permission_groups = GlobalSignal()
|
||||
"""
|
||||
This signal is sent out to get all known permissions. Receivers should return an
|
||||
instance of pretix.base.permissions.Permission or a list of such instances.
|
||||
instance of pretix.base.permissions.PermissionGroup or a list of such instances.
|
||||
"""
|
||||
|
||||
notification = EventPluginSignal()
|
||||
|
||||
@@ -77,7 +77,7 @@ 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,
|
||||
get_all_event_permission_groups, get_all_organizer_permission_groups,
|
||||
)
|
||||
from pretix.base.settings import (
|
||||
PERSON_NAME_SCHEMES, PERSON_NAME_TITLE_GROUPS, validate_organizer_settings,
|
||||
@@ -335,34 +335,62 @@ 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, self._make_label(p)) 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, self._make_label(p)) 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,
|
||||
)
|
||||
self.event_field_names = []
|
||||
for pg in get_all_event_permission_groups().values():
|
||||
initial = ",".join(sorted(
|
||||
a for a in pg.actions if self.instance and self.instance.limit_event_permissions.get(f"{pg.name}:{a}")
|
||||
)) or "EMPTY"
|
||||
self.fields[f'event_{pg.name}'] = forms.ChoiceField(
|
||||
choices=[
|
||||
(
|
||||
",".join(sorted(opt.actions)) or "EMPTY",
|
||||
format_html(
|
||||
'{label} '
|
||||
'<span class="fa fa-question-circle fa-fw text-muted" data-toggle="tooltip"'
|
||||
' data-placement="right" title="{help_text}"></span>',
|
||||
label=opt.label,
|
||||
help_text=opt.help_text,
|
||||
) if opt.help_text else opt.label,
|
||||
)
|
||||
for opt in pg.options
|
||||
],
|
||||
label=pg.label,
|
||||
help_text=pg.help_text,
|
||||
initial=initial,
|
||||
widget=forms.RadioSelect,
|
||||
)
|
||||
self.event_field_names.append(f'event_{pg.name}')
|
||||
self.organizer_field_names = []
|
||||
for pg in get_all_organizer_permission_groups().values():
|
||||
initial = ",".join(sorted(
|
||||
a for a in pg.actions if self.instance and self.instance.limit_organizer_permissions.get(f"{pg.name}:{a}")
|
||||
)) or "EMPTY"
|
||||
self.fields[f'organizer_{pg.name}'] = forms.ChoiceField(
|
||||
choices=[
|
||||
(
|
||||
",".join(sorted(opt.actions)) or "EMPTY",
|
||||
format_html(
|
||||
'{label} '
|
||||
'<span class="fa fa-question-circle fa-fw text-muted" data-toggle="tooltip"'
|
||||
' data-placement="right" title="{help_text}"></span>',
|
||||
label=opt.label,
|
||||
help_text=opt.help_text,
|
||||
) if opt.help_text else opt.label,
|
||||
)
|
||||
for opt in pg.options
|
||||
],
|
||||
label=pg.label,
|
||||
help_text=pg.help_text,
|
||||
initial=initial,
|
||||
widget=forms.RadioSelect,
|
||||
)
|
||||
self.organizer_field_names.append(f'organizer_{pg.name}')
|
||||
|
||||
class Meta:
|
||||
model = Team
|
||||
fields = ['name', 'require_2fa', 'all_events', 'limit_events',
|
||||
'all_event_permissions', 'limit_event_permissions',
|
||||
'all_organizer_permissions', 'limit_organizer_permissions']
|
||||
'all_event_permissions',
|
||||
'all_organizer_permissions',]
|
||||
widgets = {
|
||||
'limit_events': forms.CheckboxSelectMultiple(attrs={
|
||||
'data-inverse-dependency': '#id_all_events',
|
||||
@@ -375,6 +403,33 @@ class TeamForm(forms.ModelForm):
|
||||
|
||||
def clean(self):
|
||||
data = super().clean()
|
||||
|
||||
data['limit_event_permissions'] = {}
|
||||
if not data['all_event_permissions']:
|
||||
for pg in get_all_event_permission_groups().values():
|
||||
selected = data.get(f'event_{pg.name}', 'EMPTY')
|
||||
if selected == "EMPTY":
|
||||
selected_actions = []
|
||||
else:
|
||||
selected_actions = selected.split(',')
|
||||
for action in pg.actions:
|
||||
if action in selected_actions:
|
||||
data['limit_event_permissions'][f"{pg.name}:{action}"] = True
|
||||
self.instance.limit_event_permissions = data['limit_event_permissions']
|
||||
|
||||
data['limit_organizer_permissions'] = {}
|
||||
if not data['all_organizer_permissions']:
|
||||
for pg in get_all_organizer_permission_groups().values():
|
||||
selected = data.get(f'organizer_{pg.name}', 'EMPTY')
|
||||
if selected == "EMPTY":
|
||||
selected_actions = []
|
||||
else:
|
||||
selected_actions = selected.split(',')
|
||||
for action in pg.actions:
|
||||
if action in selected_actions:
|
||||
data['limit_organizer_permissions'][f"{pg.name}:{action}"] = True
|
||||
self.instance.limit_organizer_permissions = data['limit_organizer_permissions']
|
||||
|
||||
if self.instance.pk and not data['all_organizer_permissions'] and 'organizer.teams:write' not in data.get('limit_organizer_permissions', []):
|
||||
if not self.instance.organizer.teams.exclude(pk=self.instance.pk).filter(
|
||||
TeamQuerySet.organizer_permission_q("organizer.teams:write"),
|
||||
@@ -385,6 +440,20 @@ class TeamForm(forms.ModelForm):
|
||||
|
||||
return data
|
||||
|
||||
@property
|
||||
def changed_data_for_log(self):
|
||||
r = {}
|
||||
for k in self.changed_data:
|
||||
if k == "limit_events":
|
||||
r[k] = [e.id for e in getattr(self.instance, k).all()]
|
||||
elif k.startswith("event_"):
|
||||
r["limit_event_permissions"] = self.instance.limit_event_permissions
|
||||
elif k.startswith("organizer_"):
|
||||
r["limit_organizer_permissions"] = self.instance.limit_organizer_permissions
|
||||
else:
|
||||
r[k] = getattr(self.instance, k)
|
||||
return r
|
||||
|
||||
|
||||
class GateForm(forms.ModelForm):
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{% extends "pretixcontrol/organizers/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap3 %}
|
||||
{% load getitem %}
|
||||
{% block inner %}
|
||||
{% if team %}
|
||||
<h1>{% trans "Team:" %} {{ team.name }}</h1>
|
||||
@@ -23,7 +24,11 @@
|
||||
<fieldset>
|
||||
<legend>{% trans "Organizer permissions" %}</legend>
|
||||
{% bootstrap_field form.all_organizer_permissions layout="control" %}
|
||||
{% bootstrap_field form.limit_organizer_permissions layout="control" %}
|
||||
<div class="team-permission-groups col-md-9 col-md-offset-3" data-display-dependency="#id_all_organizer_permissions" data-inverse>
|
||||
{% for f in form.organizer_field_names %}
|
||||
{% bootstrap_field form|getitem:f layout="control" %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{% trans "Event permissions" %}</legend>
|
||||
@@ -31,7 +36,11 @@
|
||||
{% bootstrap_field form.all_events layout="control" %}
|
||||
{% bootstrap_field form.limit_events layout="control" %}
|
||||
{% bootstrap_field form.all_event_permissions layout="control" %}
|
||||
{% bootstrap_field form.limit_event_permissions layout="control" %}
|
||||
<div class="team-permission-groups col-md-9 col-md-offset-3" data-display-dependency="#id_all_event_permissions" data-inverse>
|
||||
{% for f in form.event_field_names %}
|
||||
{% bootstrap_field form|getitem:f layout="control" %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
|
||||
@@ -903,10 +903,7 @@ class TeamCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
|
||||
form.instance.organizer = self.request.organizer
|
||||
ret = super().form_valid(form)
|
||||
form.instance.members.add(self.request.user)
|
||||
form.instance.log_action('pretix.team.created', user=self.request.user, data={
|
||||
k: getattr(self.object, k) if k != 'limit_events' else [e.id for e in getattr(self.object, k).all()]
|
||||
for k in form.changed_data
|
||||
})
|
||||
form.instance.log_action('pretix.team.created', user=self.request.user, data=form.changed_data_for_log)
|
||||
return ret
|
||||
|
||||
def form_invalid(self, form):
|
||||
@@ -938,10 +935,7 @@ class TeamUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
if form.has_changed():
|
||||
self.object.log_action('pretix.team.changed', user=self.request.user, data={
|
||||
k: getattr(self.object, k) if k != 'limit_events' else [e.id for e in getattr(self.object, k).all()]
|
||||
for k in form.changed_data
|
||||
})
|
||||
self.object.log_action('pretix.team.changed', user=self.request.user, data=form.changed_data_for_log)
|
||||
messages.success(self.request, _('Your changes have been saved.'))
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
@@ -417,6 +417,30 @@ div.scrolling-multiple-choice, div.scrolling-choice {
|
||||
}
|
||||
}
|
||||
}
|
||||
.team-permission-groups {
|
||||
border: 1px solid $input-border;
|
||||
border-radius: $input-border-radius;
|
||||
padding: 10px 15px;
|
||||
|
||||
.radio {
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
&:not(:has(.fa-fw)) {
|
||||
// Visual adjustment between options with and without help text
|
||||
&:after {
|
||||
display: inline-block;
|
||||
content: " ";
|
||||
padding-right: 1.28571em;
|
||||
}
|
||||
}
|
||||
}
|
||||
.control-label {
|
||||
text-align: left;
|
||||
}
|
||||
.form-group:last-child, .help-block {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
table td > .checkbox {
|
||||
margin: 0;
|
||||
position: static;
|
||||
|
||||
Reference in New Issue
Block a user