mirror of
https://github.com/pretix/pretix.git
synced 2026-05-06 15:24:02 +00:00
Pluggable permissions (#5728)
* Data model draft * Refactor query and assignment usages of old permissions * Backend UI * API serializer * Big string replace * Docs, tests and fixes for teams api * Update docs for device auth * Eliminate old names * Make tests pass * Use new permissions, remove inconsistencies * Add test for translations * Show plugin permissions * Add permission for seating plans * Fix plugin activation * Fix failing test * Refactor to permission groups * Update doc/api/resources/devices.rst Co-authored-by: luelista <weller@rami.io> * Update doc/api/resources/events.rst Co-authored-by: luelista <weller@rami.io> * Update src/pretix/api/serializers/organizer.py Co-authored-by: luelista <weller@rami.io> * Fix typo * Fix python version compat * Replacement after rebase * Add proper permission handling for exports * Docs for exporters * Runtime linting of permission names * Fix typos * Show export page even without orders permission * More legacy compat * Do not strongly validate before plugins are loaded * Rebase migration * Add permission for outgoing mails * Review notes * Update doc/api/resources/teams.rst Co-authored-by: Richard Schreiber <schreiber@pretix.eu> * Clean up logic around exporters * Review and failures * Fix migration leading to forbidden combination * Handle permissions on event copying * Remove print-statements * Make test clearer * Review feedback * Add AnyPermissionOf * migration safety --------- Co-authored-by: luelista <weller@rami.io> Co-authored-by: Richard Schreiber <schreiber@pretix.eu>
This commit is contained in:
@@ -102,7 +102,7 @@ def _default_context(request):
|
||||
complain_testmode_orders = request.event.orders.filter(testmode=True).exists()
|
||||
request.event.cache.set('complain_testmode_orders', complain_testmode_orders, 30)
|
||||
ctx['complain_testmode_orders'] = complain_testmode_orders and request.user.has_event_permission(
|
||||
request.organizer, request.event, 'can_view_orders', request=request
|
||||
request.organizer, request.event, 'event.orders:read', request=request
|
||||
)
|
||||
else:
|
||||
ctx['complain_testmode_orders'] = False
|
||||
|
||||
@@ -62,6 +62,7 @@ from pretix.base.forms import (
|
||||
)
|
||||
from pretix.base.models import Event, Organizer, TaxRule, Team
|
||||
from pretix.base.models.event import EventFooterLink, EventMetaValue, SubEvent
|
||||
from pretix.base.models.organizer import TeamQuerySet
|
||||
from pretix.base.models.tax import TAX_CODE_LISTS
|
||||
from pretix.base.reldate import RelativeDateField, RelativeDateTimeField
|
||||
from pretix.base.services.placeholders import FormPlaceholderMixin
|
||||
@@ -100,11 +101,12 @@ class EventWizardFoundationForm(forms.Form):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.user = kwargs.pop('user')
|
||||
self.session = kwargs.pop('session')
|
||||
self.clone_from = kwargs.pop('clone_from')
|
||||
super().__init__(*args, **kwargs)
|
||||
qs = Organizer.objects.all()
|
||||
if not self.user.has_active_staff_session(self.session.session_key):
|
||||
qs = qs.filter(
|
||||
id__in=self.user.teams.filter(can_create_events=True).values_list('organizer', flat=True)
|
||||
id__in=self.user.teams.filter(TeamQuerySet.organizer_permission_q("organizer.events:create")).values_list('organizer', flat=True)
|
||||
)
|
||||
self.fields['organizer'] = forms.ModelChoiceField(
|
||||
label=_("Organizer"),
|
||||
@@ -125,6 +127,16 @@ class EventWizardFoundationForm(forms.Form):
|
||||
self.fields['organizer'].initial = organizer
|
||||
self.fields['locales'].initial = organizer.settings.locales
|
||||
|
||||
def clean(self):
|
||||
d = super().clean()
|
||||
if d.get('organizer') and self.clone_from and not self.user.has_active_staff_session(self.session.session_key):
|
||||
if not self.clone_from.allow_copy_data(d['organizer'], self.user):
|
||||
raise ValidationError({
|
||||
"organizer": _("You do not have a sufficient level of access on the event you selected "
|
||||
"to copy it to the desired organizer.")
|
||||
})
|
||||
return d
|
||||
|
||||
|
||||
class EventWizardBasicsForm(I18nModelForm):
|
||||
error_messages = {
|
||||
@@ -198,6 +210,7 @@ class EventWizardBasicsForm(I18nModelForm):
|
||||
self.has_subevents = kwargs.pop('has_subevents')
|
||||
self.user = kwargs.pop('user')
|
||||
self.session = kwargs.pop('session')
|
||||
self.clone_from = kwargs.pop('clone_from')
|
||||
super().__init__(*args, **kwargs)
|
||||
if 'timezone' not in self.initial:
|
||||
self.initial['timezone'] = get_current_timezone_name()
|
||||
@@ -238,6 +251,16 @@ class EventWizardBasicsForm(I18nModelForm):
|
||||
'check "{field}" above.').format(field=self.fields["no_taxes"].label)
|
||||
})
|
||||
|
||||
if self.clone_from and not self.user.has_active_staff_session(self.session.session_key):
|
||||
if data.get("team"):
|
||||
source_event_perms = self.user.get_event_permission_set(self.organizer, self.clone_from)
|
||||
team_perms = data["team"].event_permission_set(include_legacy=False)
|
||||
if any(t not in source_event_perms for t in team_perms):
|
||||
raise ValidationError({
|
||||
"team": _("You cannot choose a team that would give you more access than you have on "
|
||||
"the event you are copying.")
|
||||
})
|
||||
|
||||
# change timezone
|
||||
zone = ZoneInfo(data.get('timezone'))
|
||||
data['date_from'] = self.reset_timezone(zone, data.get('date_from'))
|
||||
@@ -261,9 +284,12 @@ class EventWizardBasicsForm(I18nModelForm):
|
||||
|
||||
@staticmethod
|
||||
def has_control_rights(user, organizer, session):
|
||||
# It's mostly pointless to let a user create an event where they can't event change the name or create products,
|
||||
# so we detect if the user has sufficient access for that on a new event.
|
||||
return user.teams.filter(
|
||||
organizer=organizer, all_events=True, can_change_event_settings=True, can_change_items=True,
|
||||
can_change_orders=True, can_change_vouchers=True
|
||||
TeamQuerySet.event_permission_q("event.settings.general:write"),
|
||||
organizer=organizer,
|
||||
all_events=True,
|
||||
).exists() or user.has_active_staff_session(session.session_key)
|
||||
|
||||
|
||||
@@ -293,18 +319,24 @@ class EventWizardCopyForm(forms.Form):
|
||||
if user.has_active_staff_session(session.session_key):
|
||||
return Event.objects.all()
|
||||
return Event.objects.filter(
|
||||
# It is generally pointless to let users copy events when they would not even be able to change the
|
||||
# date of the event they have just created. Therefore, even if it looks wrong, we're checking a write
|
||||
# permission for read access.
|
||||
Q(organizer_id__in=user.teams.filter(
|
||||
all_events=True, can_change_event_settings=True, can_change_items=True
|
||||
TeamQuerySet.event_permission_q("event.settings.general:write"),
|
||||
all_events=True,
|
||||
).values_list('organizer', flat=True)) | Q(id__in=user.teams.filter(
|
||||
can_change_event_settings=True, can_change_items=True
|
||||
TeamQuerySet.event_permission_q("event.settings.general:write"),
|
||||
).values_list('limit_events__id', flat=True))
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs.pop('organizer')
|
||||
self.organizer = kwargs.pop('organizer')
|
||||
kwargs.pop('locales')
|
||||
self.session = kwargs.pop('session')
|
||||
self.team = kwargs.pop('team')
|
||||
kwargs.pop('has_subevents')
|
||||
kwargs.pop('clone_from')
|
||||
self.user = kwargs.pop('user')
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -323,6 +355,24 @@ class EventWizardCopyForm(forms.Form):
|
||||
)
|
||||
self.fields['copy_from_event'].widget.choices = self.fields['copy_from_event'].choices
|
||||
|
||||
def clean(self):
|
||||
d = super().clean()
|
||||
if d.get('copy_from_event') and not self.user.has_active_staff_session(self.session.session_key):
|
||||
if not d['copy_from_event'].allow_copy_data(self.organizer, self.user):
|
||||
raise ValidationError({
|
||||
"copy_from_event": _("You do not have a sufficient level of access on the event you selected "
|
||||
"to copy it to the desired organizer.")
|
||||
})
|
||||
if self.team:
|
||||
source_event_perms = self.user.get_event_permission_set(self.organizer, d['copy_from_event'])
|
||||
team_perms = self.team.event_permission_set(include_legacy=False)
|
||||
if any(t not in source_event_perms for t in team_perms):
|
||||
raise ValidationError({
|
||||
"copy_from_event": _("You cannot choose an event on which you have less access than the "
|
||||
"team you selected in the previous step.")
|
||||
})
|
||||
return d
|
||||
|
||||
|
||||
class EventMetaValueForm(forms.ModelForm):
|
||||
|
||||
|
||||
@@ -1111,7 +1111,7 @@ class OrderPaymentSearchFilterForm(forms.Form):
|
||||
self.fields['organizer'].queryset = Organizer.objects.filter(
|
||||
pk__in=self.request.user.teams.values_list('organizer', flat=True)
|
||||
)
|
||||
self.fields['event'].queryset = self.request.user.get_events_with_permission('can_view_orders')
|
||||
self.fields['event'].queryset = self.request.user.get_events_with_permission('event.orders:read')
|
||||
|
||||
self.fields['provider'].choices += get_all_payment_providers()
|
||||
|
||||
|
||||
@@ -75,7 +75,10 @@ from pretix.base.models import (
|
||||
ReusableMedium, SalesChannel, Team,
|
||||
)
|
||||
from pretix.base.models.customers import CustomerSSOClient, CustomerSSOProvider
|
||||
from pretix.base.models.organizer import OrganizerFooterLink
|
||||
from pretix.base.models.organizer import OrganizerFooterLink, TeamQuerySet
|
||||
from pretix.base.permissions import (
|
||||
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,
|
||||
)
|
||||
@@ -297,7 +300,34 @@ 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 _make_label(self, p):
|
||||
source = '{}'
|
||||
params = [p.label]
|
||||
|
||||
if p.plugin_name:
|
||||
source = '<span class="fa fa-puzzle-piece text-muted" data-toggle="tooltip" title="{}"></span> ' + source
|
||||
params.insert(0, _("Provided by a plugin"))
|
||||
|
||||
if p.help_text:
|
||||
source += ' <span class="fa fa-info-circle text-muted" data-toggle="tooltip" title="{}"></span>'
|
||||
params.append(p.help_text)
|
||||
|
||||
source += ' (<code>{}</code>)'
|
||||
params.append(p.name)
|
||||
return format_html(source, *params)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
organizer = kwargs.pop('organizer')
|
||||
@@ -305,16 +335,62 @@ class TeamForm(forms.ModelForm):
|
||||
self.fields['limit_events'].queryset = organizer.events.all().order_by(
|
||||
'-has_subevents', '-date_from'
|
||||
)
|
||||
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', 'can_create_events',
|
||||
'can_change_teams', 'can_change_organizer_settings',
|
||||
'can_manage_gift_cards', 'can_manage_customers',
|
||||
'can_manage_reusable_media',
|
||||
'can_change_event_settings', 'can_change_items',
|
||||
'can_view_orders', 'can_change_orders', 'can_checkin_orders',
|
||||
'can_view_vouchers', 'can_change_vouchers']
|
||||
fields = ['name', 'require_2fa', 'all_events', 'limit_events',
|
||||
'all_event_permissions',
|
||||
'all_organizer_permissions',]
|
||||
widgets = {
|
||||
'limit_events': forms.CheckboxSelectMultiple(attrs={
|
||||
'data-inverse-dependency': '#id_all_events',
|
||||
@@ -327,15 +403,57 @@ class TeamForm(forms.ModelForm):
|
||||
|
||||
def clean(self):
|
||||
data = super().clean()
|
||||
if self.instance.pk and not data['can_change_teams']:
|
||||
|
||||
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(
|
||||
can_change_teams=True, members__isnull=False
|
||||
TeamQuerySet.organizer_permission_q("organizer.teams:write"),
|
||||
members__isnull=False
|
||||
).exists():
|
||||
raise ValidationError(_('The changes could not be saved because there would be no remaining team with '
|
||||
'the permission to change teams and permissions.'))
|
||||
|
||||
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):
|
||||
|
||||
|
||||
@@ -45,7 +45,9 @@ from django.utils.translation import gettext as _
|
||||
from django_scopes import scope
|
||||
|
||||
from pretix.base.models import Event, Organizer
|
||||
from pretix.base.models.auth import SuperuserPermissionSet, User
|
||||
from pretix.base.models.auth import (
|
||||
EventPermissionSet, OrganizerPermissionSet, SuperuserPermissionSet, User,
|
||||
)
|
||||
from pretix.helpers.http import redirect_to_url
|
||||
from pretix.helpers.security import (
|
||||
Session2FASetupRequired, SessionInvalid, SessionPasswordChangeRequired,
|
||||
@@ -170,7 +172,7 @@ class PermissionMiddleware:
|
||||
if request.user.has_active_staff_session(request.session.session_key):
|
||||
request.eventpermset = SuperuserPermissionSet()
|
||||
else:
|
||||
request.eventpermset = request.user.get_event_permission_set(request.organizer, request.event)
|
||||
request.eventpermset = EventPermissionSet(request.user.get_event_permission_set(request.organizer, request.event))
|
||||
elif 'organizer' in url.kwargs:
|
||||
if url.kwargs['organizer'] == '-':
|
||||
# This is a hack that just takes the user to ANY organizer. It's useful to link to features in support
|
||||
@@ -192,7 +194,7 @@ class PermissionMiddleware:
|
||||
if request.user.has_active_staff_session(request.session.session_key):
|
||||
request.orgapermset = SuperuserPermissionSet()
|
||||
else:
|
||||
request.orgapermset = request.user.get_organizer_permission_set(request.organizer)
|
||||
request.orgapermset = OrganizerPermissionSet(request.user.get_organizer_permission_set(request.organizer))
|
||||
|
||||
with scope(organizer=getattr(request, 'organizer', None)):
|
||||
r = self.get_response(request)
|
||||
|
||||
@@ -43,24 +43,29 @@ def get_event_navigation(request: HttpRequest):
|
||||
'icon': 'dashboard',
|
||||
}
|
||||
]
|
||||
if 'can_change_event_settings' in request.eventpermset:
|
||||
event_settings = [
|
||||
{
|
||||
'label': _('General'),
|
||||
'url': reverse('control:event.settings', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': url.url_name == 'event.settings',
|
||||
},
|
||||
{
|
||||
'label': _('Payment'),
|
||||
'url': reverse('control:event.settings.payment', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': url.url_name in ('event.settings.payment', 'event.settings.payment.provider'),
|
||||
},
|
||||
event_settings = []
|
||||
if "event.settings.general:write" in request.eventpermset:
|
||||
event_settings.append({
|
||||
'label': _('General'),
|
||||
'url': reverse('control:event.settings', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': url.url_name == 'event.settings',
|
||||
})
|
||||
|
||||
if "event.settings.payment:write" in request.eventpermset or "event.settings.general:write" in request.eventpermset:
|
||||
event_settings.append({
|
||||
'label': _('Payment'),
|
||||
'url': reverse('control:event.settings.payment', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': url.url_name in ('event.settings.payment', 'event.settings.payment.provider'),
|
||||
})
|
||||
|
||||
if "event.settings.general:write" in request.eventpermset:
|
||||
event_settings += [
|
||||
{
|
||||
'label': _('Plugins'),
|
||||
'url': reverse('control:event.settings.plugins', kwargs={
|
||||
@@ -84,23 +89,31 @@ def get_event_navigation(request: HttpRequest):
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': url.url_name == 'event.settings.mail',
|
||||
},
|
||||
{
|
||||
'label': _('Taxes'),
|
||||
'url': reverse('control:event.settings.tax', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': url.url_name.startswith('event.settings.tax'),
|
||||
},
|
||||
{
|
||||
'label': _('Invoicing'),
|
||||
'url': reverse('control:event.settings.invoice', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': url.url_name == 'event.settings.invoice',
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
if "event.settings.tax:write" in request.eventpermset or "event.settings.general:write" in request.eventpermset:
|
||||
event_settings.append({
|
||||
'label': _('Taxes'),
|
||||
'url': reverse('control:event.settings.tax', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': url.url_name.startswith('event.settings.tax'),
|
||||
})
|
||||
|
||||
if "event.settings.invoicing:write" in request.eventpermset or "event.settings.general:write" in request.eventpermset:
|
||||
event_settings.append({
|
||||
'label': _('Invoicing'),
|
||||
'url': reverse('control:event.settings.invoice', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': url.url_name == 'event.settings.invoice',
|
||||
})
|
||||
|
||||
if "event.settings.general:write" in request.eventpermset:
|
||||
event_settings += [
|
||||
{
|
||||
'label': pgettext_lazy('action', 'Cancellation'),
|
||||
'url': reverse('control:event.settings.cancel', kwargs={
|
||||
@@ -118,88 +131,87 @@ def get_event_navigation(request: HttpRequest):
|
||||
'active': url.url_name == 'event.settings.widget',
|
||||
},
|
||||
]
|
||||
|
||||
# It would be better to allow plugins to handle the permission themselves, but for backwards compatibility
|
||||
# we need to have it in the "if" statement
|
||||
event_settings += sorted(
|
||||
sum((list(a[1]) for a in nav_event_settings.send(request.event, request=request)), []),
|
||||
key=lambda r: r['label']
|
||||
)
|
||||
if event_settings:
|
||||
nav.append({
|
||||
'label': _('Settings'),
|
||||
'url': reverse('control:event.settings', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'url': event_settings[0]["url"],
|
||||
'active': False,
|
||||
'icon': 'wrench',
|
||||
'children': event_settings
|
||||
})
|
||||
|
||||
if 'can_change_items' in request.eventpermset:
|
||||
nav.append({
|
||||
'label': _('Products'),
|
||||
'url': reverse('control:event.items', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': False,
|
||||
'icon': 'ticket',
|
||||
'children': [
|
||||
{
|
||||
'label': _('Products'),
|
||||
'url': reverse('control:event.items', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': url.url_name in (
|
||||
'event.item', 'event.items.add', 'event.items') or "event.item." in url.url_name,
|
||||
},
|
||||
{
|
||||
'label': _('Quotas'),
|
||||
'url': reverse('control:event.items.quotas', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': 'event.items.quota' in url.url_name,
|
||||
},
|
||||
{
|
||||
'label': _('Categories'),
|
||||
'url': reverse('control:event.items.categories', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': 'event.items.categories' in url.url_name,
|
||||
},
|
||||
{
|
||||
'label': _('Questions'),
|
||||
'url': reverse('control:event.items.questions', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': 'event.items.questions' in url.url_name,
|
||||
},
|
||||
{
|
||||
'label': _('Discounts'),
|
||||
'url': reverse('control:event.items.discounts', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': 'event.items.discounts' in url.url_name,
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
if 'can_change_event_settings' in request.eventpermset:
|
||||
if request.event.has_subevents:
|
||||
nav.append({
|
||||
'label': pgettext_lazy('subevent', 'Dates'),
|
||||
'url': reverse('control:event.subevents', kwargs={
|
||||
nav.append({
|
||||
'label': _('Products'),
|
||||
'url': reverse('control:event.items', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': False,
|
||||
'icon': 'ticket',
|
||||
'children': [
|
||||
{
|
||||
'label': _('Products'),
|
||||
'url': reverse('control:event.items', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': ('event.subevent' in url.url_name),
|
||||
'icon': 'calendar',
|
||||
})
|
||||
'active': url.url_name in (
|
||||
'event.item', 'event.items.add', 'event.items') or "event.item." in url.url_name,
|
||||
},
|
||||
{
|
||||
'label': _('Quotas'),
|
||||
'url': reverse('control:event.items.quotas', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': 'event.items.quota' in url.url_name,
|
||||
},
|
||||
{
|
||||
'label': _('Categories'),
|
||||
'url': reverse('control:event.items.categories', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': 'event.items.categories' in url.url_name,
|
||||
},
|
||||
{
|
||||
'label': _('Questions'),
|
||||
'url': reverse('control:event.items.questions', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': 'event.items.questions' in url.url_name,
|
||||
},
|
||||
{
|
||||
'label': _('Discounts'),
|
||||
'url': reverse('control:event.items.discounts', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': 'event.items.discounts' in url.url_name,
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
if 'can_view_orders' in request.eventpermset:
|
||||
if request.event.has_subevents:
|
||||
nav.append({
|
||||
'label': pgettext_lazy('subevent', 'Dates'),
|
||||
'url': reverse('control:event.subevents', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': ('event.subevent' in url.url_name),
|
||||
'icon': 'calendar',
|
||||
})
|
||||
|
||||
if 'event.orders:read' in request.eventpermset:
|
||||
children = [
|
||||
{
|
||||
'label': _('All orders'),
|
||||
@@ -242,7 +254,7 @@ def get_event_navigation(request: HttpRequest):
|
||||
'active': 'event.orders.waitinglist' in url.url_name,
|
||||
},
|
||||
]
|
||||
if 'can_change_orders' in request.eventpermset:
|
||||
if 'event.orders:write' in request.eventpermset:
|
||||
children.append({
|
||||
'label': _('Import'),
|
||||
'url': reverse('control:event.orders.import', kwargs={
|
||||
@@ -261,8 +273,18 @@ def get_event_navigation(request: HttpRequest):
|
||||
'icon': 'shopping-cart',
|
||||
'children': children
|
||||
})
|
||||
else:
|
||||
nav.append({
|
||||
'label': _('Export'),
|
||||
'url': reverse('control:event.orders.export', kwargs={
|
||||
'event': request.event.slug,
|
||||
'organizer': request.event.organizer.slug,
|
||||
}),
|
||||
'active': 'event.orders.export' in url.url_name,
|
||||
'icon': 'download',
|
||||
})
|
||||
|
||||
if 'can_view_vouchers' in request.eventpermset:
|
||||
if 'event.vouchers:read' in request.eventpermset:
|
||||
nav.append({
|
||||
'label': _('Vouchers'),
|
||||
'url': reverse('control:event.vouchers', kwargs={
|
||||
@@ -291,7 +313,7 @@ def get_event_navigation(request: HttpRequest):
|
||||
]
|
||||
})
|
||||
|
||||
if 'can_view_orders' in request.eventpermset:
|
||||
if 'event.orders:read' in request.eventpermset or 'event.settings.general:write' in request.eventpermset:
|
||||
nav.append({
|
||||
'label': pgettext_lazy('navigation', 'Check-in'),
|
||||
'url': reverse('control:event.orders.checkinlists', kwargs={
|
||||
@@ -485,7 +507,7 @@ def get_organizer_navigation(request):
|
||||
'icon': 'calendar',
|
||||
},
|
||||
]
|
||||
if 'can_change_organizer_settings' in request.orgapermset:
|
||||
if 'organizer.settings.general:write' in request.orgapermset:
|
||||
nav.append({
|
||||
'label': _('Settings'),
|
||||
'url': reverse('control:organizer.edit', kwargs={
|
||||
@@ -539,7 +561,7 @@ def get_organizer_navigation(request):
|
||||
]
|
||||
})
|
||||
|
||||
if 'can_change_teams' in request.orgapermset:
|
||||
if 'organizer.teams:write' in request.orgapermset:
|
||||
nav.append({
|
||||
'label': _('Teams'),
|
||||
'url': reverse('control:organizer.teams', kwargs={
|
||||
@@ -549,7 +571,7 @@ def get_organizer_navigation(request):
|
||||
'icon': 'group',
|
||||
})
|
||||
|
||||
if 'can_manage_gift_cards' in request.orgapermset:
|
||||
if 'organizer.giftcards:read' in request.orgapermset or 'organizer.giftcards:write' in request.orgapermset:
|
||||
children = []
|
||||
children.append({
|
||||
'label': _('Gift cards'),
|
||||
@@ -559,7 +581,7 @@ def get_organizer_navigation(request):
|
||||
'active': 'organizer.giftcard' in url.url_name and 'acceptance' not in url.url_name,
|
||||
'children': children,
|
||||
})
|
||||
if 'can_change_organizer_settings' in request.orgapermset:
|
||||
if 'organizer.settings.general:write' in request.orgapermset:
|
||||
children.append(
|
||||
{
|
||||
'label': _('Acceptance'),
|
||||
@@ -580,7 +602,7 @@ def get_organizer_navigation(request):
|
||||
|
||||
if request.organizer.settings.customer_accounts:
|
||||
children = []
|
||||
if 'can_manage_customers' in request.orgapermset:
|
||||
if 'organizer.customers:read' in request.orgapermset or 'organizer.customers:write' in request.orgapermset:
|
||||
children.append(
|
||||
{
|
||||
'label': _('Customers'),
|
||||
@@ -590,7 +612,7 @@ def get_organizer_navigation(request):
|
||||
'active': 'organizer.customer' in url.url_name,
|
||||
}
|
||||
)
|
||||
if 'can_change_organizer_settings' in request.orgapermset:
|
||||
if 'organizer.settings.general:write' in request.orgapermset:
|
||||
children.append(
|
||||
{
|
||||
'label': _('Membership types'),
|
||||
@@ -629,16 +651,17 @@ def get_organizer_navigation(request):
|
||||
})
|
||||
|
||||
if request.organizer.settings.reusable_media_active:
|
||||
nav.append({
|
||||
'label': _('Reusable media'),
|
||||
'url': reverse('control:organizer.reusable_media', kwargs={
|
||||
'organizer': request.organizer.slug
|
||||
}),
|
||||
'icon': 'key',
|
||||
'active': 'organizer.reusable_medi' in url.url_name,
|
||||
})
|
||||
if 'organizer.reusablemedia:read' in request.orgapermset or 'organizer.reusablemedia:write' in request.orgapermset:
|
||||
nav.append({
|
||||
'label': _('Reusable media'),
|
||||
'url': reverse('control:organizer.reusable_media', kwargs={
|
||||
'organizer': request.organizer.slug
|
||||
}),
|
||||
'icon': 'key',
|
||||
'active': 'organizer.reusable_medi' in url.url_name,
|
||||
})
|
||||
|
||||
if 'can_change_organizer_settings' in request.orgapermset:
|
||||
if 'organizer.devices:read' in request.orgapermset or 'organizer.devices:write' in request.orgapermset:
|
||||
nav.append({
|
||||
'label': _('Devices'),
|
||||
'url': reverse('control:organizer.devices', kwargs={
|
||||
@@ -672,7 +695,7 @@ def get_organizer_navigation(request):
|
||||
'icon': 'download',
|
||||
})
|
||||
|
||||
if 'can_change_organizer_settings' in request.orgapermset:
|
||||
if 'organizer.settings.general:write' in request.orgapermset:
|
||||
merge_in(nav, [{
|
||||
'parent': reverse('control:organizer.export', kwargs={
|
||||
'organizer': request.organizer.slug,
|
||||
@@ -684,6 +707,7 @@ def get_organizer_navigation(request):
|
||||
'active': (url.url_name == 'organizer.datasync.failedjobs'),
|
||||
}])
|
||||
|
||||
if 'organizer.outgoingmails:read' in request.orgapermset:
|
||||
nav.append({
|
||||
'label': _('Outgoing emails'),
|
||||
'url': reverse('control:organizer.outgoingmails', kwargs={
|
||||
|
||||
@@ -38,6 +38,9 @@ from django.core.exceptions import PermissionDenied
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from pretix.base.permissions import (
|
||||
assert_valid_event_permission, assert_valid_organizer_permission,
|
||||
)
|
||||
from pretix.helpers.http import redirect_to_url
|
||||
|
||||
|
||||
@@ -55,7 +58,9 @@ def event_permission_required(permission):
|
||||
"""
|
||||
if permission == 'can_change_settings':
|
||||
# Legacy support
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
|
||||
assert_valid_event_permission(permission)
|
||||
|
||||
def decorator(function):
|
||||
def wrapper(request, *args, **kw):
|
||||
@@ -79,7 +84,7 @@ class EventPermissionRequiredMixin:
|
||||
This mixin is equivalent to the event_permission_required view decorator but
|
||||
is in a form suitable for class-based views.
|
||||
"""
|
||||
permission = ''
|
||||
permission = None # None means "any permission"
|
||||
|
||||
@classmethod
|
||||
def as_view(cls, **initkwargs):
|
||||
@@ -92,9 +97,11 @@ def organizer_permission_required(permission):
|
||||
This view decorator rejects all requests with a 403 response which are not from
|
||||
users having the given permission for the event the request is associated with.
|
||||
"""
|
||||
if permission == 'can_change_settings':
|
||||
if permission in ('event.settings.general:write', 'can_change_settings', 'can_change_event_settings'):
|
||||
# Legacy support
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
|
||||
assert_valid_organizer_permission(permission)
|
||||
|
||||
def decorator(function):
|
||||
def wrapper(request, *args, **kw):
|
||||
@@ -116,7 +123,7 @@ class OrganizerPermissionRequiredMixin:
|
||||
This mixin is equivalent to the organizer_permission_required view decorator but
|
||||
is in a form suitable for class-based views.
|
||||
"""
|
||||
permission = ''
|
||||
permission = None # None means "any permission"
|
||||
|
||||
@classmethod
|
||||
def as_view(cls, **initkwargs):
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
{% block content %}
|
||||
<h1>
|
||||
{% blocktrans with name=checkinlist.name %}Check-in list: {{ name }}{% endblocktrans %}
|
||||
{% if 'can_change_event_settings' in request.eventpermset %}
|
||||
{% if 'event.settings.general:write' in request.eventpermset %}
|
||||
<a href="{% url "control:event.orders.checkinlists.edit" event=request.event.slug organizer=request.event.organizer.slug list=checkinlist.pk %}"
|
||||
class="btn btn-default">
|
||||
<span class="fa fa-wrench"></span>
|
||||
@@ -87,7 +87,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
{% if "can_change_orders" in request.eventpermset or "can_checkin_orders" in request.eventpermset %}
|
||||
{% if "event.orders:write" in request.eventpermset or "event.orders:checkin" in request.eventpermset %}
|
||||
<label aria-label="{% trans "select all rows for batch-operation" %}"
|
||||
class="batch-select-label"><input type="checkbox" data-toggle-table/></label>
|
||||
{% endif %}
|
||||
@@ -132,7 +132,7 @@
|
||||
{% for e in entries %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if "can_change_orders" in request.eventpermset or "can_checkin_orders" in request.eventpermset %}
|
||||
{% if "event.orders:write" in request.eventpermset or "event.orders:checkin" in request.eventpermset %}
|
||||
<input type="checkbox" name="checkin" id="id_checkin" class="" value="{{ e.pk }}"/>
|
||||
{% endif %}
|
||||
</td>
|
||||
@@ -207,7 +207,7 @@
|
||||
</table>
|
||||
</div>
|
||||
<div class="batch-select-actions">
|
||||
{% if "can_change_orders" in request.eventpermset or "can_checkin_orders" in request.eventpermset %}
|
||||
{% if "event.orders:write" in request.eventpermset or "event.orders:checkin" in request.eventpermset %}
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
<span class="fa fa-sign-in" aria-hidden="true"></span>
|
||||
{% trans "Check-In selected attendees" %}
|
||||
@@ -217,7 +217,7 @@
|
||||
{% trans "Check-Out selected attendees" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if "can_change_orders" in request.eventpermset %}
|
||||
{% if "event.orders:write" in request.eventpermset %}
|
||||
<button type="submit" class="btn btn-danger btn-save" name="revert"
|
||||
formaction="{% url "control:event.orders.checkinlists.bulk_revert" event=request.event.slug organizer=request.event.organizer.slug list=checkinlist.pk %}"
|
||||
data-no-asynctask
|
||||
|
||||
@@ -63,27 +63,27 @@
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
{% if "can_change_event_settings" in request.eventpermset %}
|
||||
{% if "event.settings.general:write" in request.eventpermset %}
|
||||
<a href="{% url "control:event.orders.checkinlists.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new check-in list" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if can_change_organizer_settings %}
|
||||
{% if link_device_settings %}
|
||||
<a href="{% url "control:organizer.devices" organizer=request.organizer.slug %}"
|
||||
class="btn btn-default btn-lg"><i class="fa fa-tablet"></i> {% trans "Connected devices" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p>
|
||||
{% if "can_change_event_settings" in request.eventpermset %}
|
||||
{% if "event.settings.general:write" in request.eventpermset %}
|
||||
<a href="{% url "control:event.orders.checkinlists.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new check-in list" %}</a>
|
||||
{% endif %}
|
||||
{% if can_change_organizer_settings %}
|
||||
{% if link_device_settings %}
|
||||
<a href="{% url "control:organizer.devices" organizer=request.organizer.slug %}"
|
||||
class="btn btn-default"><i class="fa fa-tablet"></i> {% trans "Connected devices" %}</a>
|
||||
{% endif %}
|
||||
{% if "can_change_orders" in request.eventpermset %}
|
||||
{% if "event.settings.general:write" in request.eventpermset and "event.orders:write" in request.eventpermset %}
|
||||
<a href="{% url "control:event.orders.checkinlists.reset" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-default">
|
||||
<span class="fa fa-repeat"></span>
|
||||
@@ -100,7 +100,9 @@
|
||||
<a href="?{% url_replace request 'ordering' '-name' %}"><i class="fa fa-caret-down"></i></a>
|
||||
<a href="?{% url_replace request 'ordering' 'name' %}"><i class="fa fa-caret-up"></i></a>
|
||||
</th>
|
||||
<th>{% trans "Checked in" %}</th>
|
||||
{% if "event.orders:read" in request.eventpermset %}
|
||||
<th>{% trans "Checked in" %}</th>
|
||||
{% endif %}
|
||||
{% if request.event.has_subevents %}
|
||||
<th>
|
||||
{% trans "Date" context "subevent" %}
|
||||
@@ -119,18 +121,20 @@
|
||||
<strong><a
|
||||
href="{% url "control:event.orders.checkinlists.show" organizer=request.event.organizer.slug event=request.event.slug list=cl.id %}">{{ cl.name }}</a></strong>
|
||||
</td>
|
||||
<td>
|
||||
<div class="quotabox availability">
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-success progress-bar-{{ cl.percent }}">
|
||||
{% if "event.orders:read" in request.eventpermset %}
|
||||
<td>
|
||||
<div class="quotabox availability">
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-success progress-bar-{{ cl.percent }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="numbers">
|
||||
{{ cl.checkin_count|default_if_none:"0" }} /
|
||||
{{ cl.position_count|default_if_none:"0" }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="numbers">
|
||||
{{ cl.checkin_count|default_if_none:"0" }} /
|
||||
{{ cl.position_count|default_if_none:"0" }}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</td>
|
||||
{% endif %}
|
||||
{% if request.event.has_subevents %}
|
||||
{% if cl.subevent %}
|
||||
<td>
|
||||
@@ -156,16 +160,18 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
<a href="{% url "control:event.orders.checkinlists.show" organizer=request.event.organizer.slug event=request.event.slug list=cl.id %}"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-eye"></i></a>
|
||||
{% if "can_change_event_settings" in request.eventpermset %}
|
||||
{% if "event.orders:read" in request.eventpermset %}
|
||||
<a href="{% url "control:event.orders.checkinlists.show" organizer=request.event.organizer.slug event=request.event.slug list=cl.id %}"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-eye"></i></a>
|
||||
<a href="{% url "control:event.orders.checkinlists.simulator" organizer=request.event.organizer.slug event=request.event.slug list=cl.id %}"
|
||||
title="{% trans "Check-in simulator" %}" data-toggle="tooltip"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-flask"></i></a>
|
||||
{% endif %}
|
||||
{% if "event.settings.general:write" in request.eventpermset %}
|
||||
<a href="{% url "control:event.orders.checkinlists.add" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ cl.id }}"
|
||||
class="btn btn-sm btn-default" title="{% trans "Clone" %}" data-toggle="tooltip">
|
||||
<span class="fa fa-copy"></span>
|
||||
</a>
|
||||
<a href="{% url "control:event.orders.checkinlists.simulator" organizer=request.event.organizer.slug event=request.event.slug list=cl.id %}"
|
||||
title="{% trans "Check-in simulator" %}" data-toggle="tooltip"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-flask"></i></a>
|
||||
<a href="{% url "control:event.orders.checkinlists.edit" organizer=request.event.organizer.slug event=request.event.slug list=cl.id %}"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-wrench"></i></a>
|
||||
<a href="{% url "control:event.orders.checkinlists.delete" organizer=request.event.organizer.slug event=request.event.slug list=cl.id %}"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
{% block inside %}
|
||||
<h1>
|
||||
{% blocktrans with name=checkinlist.name %}Check-in list: {{ name }}{% endblocktrans %}
|
||||
{% if 'can_change_event_settings' in request.eventpermset %}
|
||||
{% if 'event.settings.general:write' in request.eventpermset %}
|
||||
<a href="{% url "control:event.orders.checkinlists.edit" event=request.event.slug organizer=request.event.organizer.slug list=checkinlist.pk %}"
|
||||
class="btn btn-default">
|
||||
<span class="fa fa-wrench"></span>
|
||||
|
||||
@@ -11,18 +11,20 @@
|
||||
<ul class="list-group">
|
||||
{% for identifier, display_name, pending, objects in providers %}
|
||||
<li class="list-group-item">
|
||||
<form action="{% url "control:event.order.sync_job" organizer=event.organizer.slug event=event.slug code=order.code provider=identifier %}" method="post" class="form-inline pull-right">
|
||||
{% csrf_token %}
|
||||
{% if pending %}
|
||||
{% if pending.not_before > now or pending.need_manual_retry %}
|
||||
<button type="submit" name="run_job_now" value="{{ pending.pk }}" class="btn btn-default"><i class="fa fa-refresh"></i> {% trans "Retry now" %}</button>
|
||||
{% if "event.orders:write" in request.eventpermset %}
|
||||
<form action="{% url "control:event.order.sync_job" organizer=event.organizer.slug event=event.slug code=order.code provider=identifier %}" method="post" class="form-inline pull-right">
|
||||
{% csrf_token %}
|
||||
{% if pending %}
|
||||
{% if pending.not_before > now or pending.need_manual_retry %}
|
||||
<button type="submit" name="run_job_now" value="{{ pending.pk }}" class="btn btn-default"><i class="fa fa-refresh"></i> {% trans "Retry now" %}</button>
|
||||
{% endif %}
|
||||
<button type="submit" name="cancel_job" value="{{ pending.pk }}" class="btn btn-danger"><i class="fa fa-times"></i> {% trans "Cancel" %}</button>
|
||||
{% else %}
|
||||
<button type="submit" class="btn btn-default"><i class="fa fa-refresh"></i> {% trans "Sync now" %}</button>
|
||||
<input type="hidden" name="queue_sync" value="true">
|
||||
{% endif %}
|
||||
<button type="submit" name="cancel_job" value="{{ pending.pk }}" class="btn btn-danger"><i class="fa fa-times"></i> {% trans "Cancel" %}</button>
|
||||
{% else %}
|
||||
<button type="submit" class="btn btn-default"><i class="fa fa-refresh"></i> {% trans "Sync now" %}</button>
|
||||
<input type="hidden" name="queue_sync" value="true">
|
||||
{% endif %}
|
||||
</form>
|
||||
</form>
|
||||
{% endif %}
|
||||
<p><b>{{ display_name }}</b></p>
|
||||
{% if pending %}
|
||||
<p>
|
||||
|
||||
@@ -40,12 +40,16 @@
|
||||
this option.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<a href="{% url "control:event.cancel" organizer=request.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-danger btn-block btn-lg">
|
||||
<span class="fa fa-ban"></span>
|
||||
{% trans "Cancel event" %}
|
||||
</a>
|
||||
<div class="col-sm-12 col-md-3 text-center">
|
||||
{% if "event:cancel" in request.eventpermset %}
|
||||
<a href="{% url "control:event.cancel" organizer=request.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-danger btn-block btn-lg">
|
||||
<span class="fa fa-ban"></span>
|
||||
{% trans "Cancel event" %}
|
||||
</a>
|
||||
{% else %}
|
||||
{% trans "No permission" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<span class="{% if e.time < nearly_now %}text-muted{% endif %}">
|
||||
{{ e.entry.description }}
|
||||
</span>
|
||||
{% if e.entry.edit_url %}
|
||||
{% if e.entry.edit_url and e.entry.edit_permission in request.eventpermset %}
|
||||
|
||||
<a href="{{ e.entry.edit_url }}" class="text-muted">
|
||||
<span class="fa fa-edit"></span>
|
||||
|
||||
@@ -155,22 +155,24 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
{% trans "Event logs" %}
|
||||
</h3>
|
||||
</div>
|
||||
<ul class="list-group" id="logs_target">
|
||||
<div class="logs-lazy-loading">
|
||||
<span class="fa fa-cog fa-4x"></span>
|
||||
{% if "event.orders:read" in request.eventpermset or "event.orders:write" in request.eventpermset or "event.settings.general:write" in request.eventpermset or "event.items:write" in request.eventpermset %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
{% trans "Event logs" %}
|
||||
</h3>
|
||||
</div>
|
||||
<ul class="list-group" id="logs_target">
|
||||
<div class="logs-lazy-loading">
|
||||
<span class="fa fa-cog fa-4x"></span>
|
||||
</div>
|
||||
</ul>
|
||||
<div class="panel-footer">
|
||||
<a href="{% url "control:event.log" event=request.event.slug organizer=request.event.organizer.slug %}">
|
||||
{% trans "Show more logs" %}
|
||||
</a>
|
||||
</div>
|
||||
</ul>
|
||||
<div class="panel-footer">
|
||||
<a href="{% url "control:event.log" event=request.event.slug organizer=request.event.organizer.slug %}">
|
||||
{% trans "Show more logs" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -165,13 +165,15 @@
|
||||
</p>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-default btn-lg" name="preview" value="preview" formtarget="_blank">
|
||||
{% trans "Save and show preview" %}
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
{% trans "Save" %}
|
||||
</button>
|
||||
</div>
|
||||
{% if "event.settings.invoicing:write" in request.eventpermset %}
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-default btn-lg" name="preview" value="preview" formtarget="_blank">
|
||||
{% trans "Save and show preview" %}
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
{% trans "Save" %}
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
@@ -41,14 +41,17 @@
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
<a href="{% url 'control:event.settings.payment.provider' event=request.event.slug organizer=request.organizer.slug provider=provider.identifier %}"
|
||||
class="btn btn-default">
|
||||
<span class="fa fa-cog"></span>
|
||||
{% trans "Settings" %}
|
||||
</a>
|
||||
{% if "event.settings.payment:write" in request.eventpermset %}
|
||||
<a href="{% url 'control:event.settings.payment.provider' event=request.event.slug organizer=request.organizer.slug provider=provider.identifier %}"
|
||||
class="btn btn-default">
|
||||
<span class="fa fa-cog"></span>
|
||||
{% trans "Settings" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% if "event.settings.general:write" in request.eventpermset %}
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<br>
|
||||
@@ -58,6 +61,7 @@
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -83,10 +87,12 @@
|
||||
{% bootstrap_field form.payment_explanation layout="control" %}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
{% trans "Save" %}
|
||||
</button>
|
||||
</div>
|
||||
{% if "event.settings.payment:write" in request.eventpermset %}
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
{% trans "Save" %}
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
@@ -23,8 +23,10 @@
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<a href="{% url "control:event.settings.tax.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new tax rule" %}</a>
|
||||
{% if "event.settings.tax:write" in request.eventpermset %}
|
||||
<a href="{% url "control:event.settings.tax.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new tax rule" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="table-responsive">
|
||||
@@ -42,10 +44,14 @@
|
||||
{% for tr in taxrules %}
|
||||
<tr>
|
||||
<td>
|
||||
<strong><a
|
||||
href="{% url "control:event.settings.tax.edit" organizer=request.event.organizer.slug event=request.event.slug rule=tr.id %}">
|
||||
{{ tr.internal_name|default:tr.name }}
|
||||
</a></strong>
|
||||
{% if "event.settings.tax:write" in request.eventpermset %}
|
||||
<strong><a
|
||||
href="{% url "control:event.settings.tax.edit" organizer=request.event.organizer.slug event=request.event.slug rule=tr.id %}">
|
||||
{{ tr.internal_name|default:tr.name }}
|
||||
</a></strong>
|
||||
{% else %}
|
||||
<strong>{{ tr.internal_name|default:tr.name }}</strong>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if tr.default %}
|
||||
@@ -53,7 +59,7 @@
|
||||
<span class="fa fa-check"></span>
|
||||
{% trans "Default" %}
|
||||
</span>
|
||||
{% else %}
|
||||
{% elif "event.settings.tax:write" in request.eventpermset %}
|
||||
<form class="form-inline" method="post"
|
||||
action="{% url "control:event.settings.tax.default" organizer=request.event.organizer.slug event=request.event.slug rule=tr.id %}">
|
||||
{% csrf_token %}
|
||||
@@ -83,10 +89,12 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
<a href="{% url "control:event.settings.tax.edit" organizer=request.event.organizer.slug event=request.event.slug rule=tr.id %}"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.settings.tax.delete" organizer=request.event.organizer.slug event=request.event.slug rule=tr.id %}"
|
||||
class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
{% if "event.settings.tax:write" in request.eventpermset %}
|
||||
<a href="{% url "control:event.settings.tax.edit" organizer=request.event.organizer.slug event=request.event.slug rule=tr.id %}"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.settings.tax.delete" organizer=request.event.organizer.slug event=request.event.slug rule=tr.id %}"
|
||||
class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@@ -94,9 +102,11 @@
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="5">
|
||||
<a href="{% url "control:event.settings.tax.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new tax rule" %}
|
||||
</a>
|
||||
{% if "event.settings.tax:write" in request.eventpermset %}
|
||||
<a href="{% url "control:event.settings.tax.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new tax rule" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
@@ -111,10 +121,12 @@
|
||||
{% bootstrap_field form.tax_rounding layout="control" %}
|
||||
{% bootstrap_field form.display_net_prices layout="control" %}
|
||||
</fieldset>
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
{% trans "Save" %}
|
||||
</button>
|
||||
</div>
|
||||
{% if "event.settings.tax:write" in request.eventpermset %}
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
{% trans "Save" %}
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<a href="{% url "control:event.items.categories.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new category" %}</a>
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<a href="{% url "control:event.items.categories.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new category" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p>
|
||||
<a href="{% url "control:event.items.categories.add" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new category" %}
|
||||
</a>
|
||||
</p>
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<p>
|
||||
<a href="{% url "control:event.items.categories.add" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new category" %}
|
||||
</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="table-responsive">
|
||||
@@ -39,7 +43,11 @@
|
||||
{% for c in categories %}
|
||||
<tr data-dnd-id="{{ c.id }}">
|
||||
<td>
|
||||
<strong><a href="{% url "control:event.items.categories.edit" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}">{{ c.internal_name|default:c.name }}</a></strong>
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<strong><a href="{% url "control:event.items.categories.edit" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}">{{ c.internal_name|default:c.name }}</a></strong>
|
||||
{% else %}
|
||||
<strong>{{ c.internal_name|default:c.name }}</strong>
|
||||
{% endif %}
|
||||
<br>
|
||||
<small class="text-muted">
|
||||
#{{ c.pk }}
|
||||
@@ -49,15 +57,17 @@
|
||||
{{ c.get_category_type_display }}
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
<button title="{% trans "Move up" %}" formaction="{% url "control:event.items.categories.up" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-default btn-sm sortable-up"{% if forloop.counter0 == 0 and not page_obj.has_previous %} disabled{% endif %}><i class="fa fa-arrow-up"></i></button>
|
||||
<button title="{% trans "Move down" %}" formaction="{% url "control:event.items.categories.down" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-default btn-sm sortable-down"{% if forloop.revcounter0 == 0 and not page_obj.has_next %} disabled{% endif %}><i class="fa fa-arrow-down"></i></button>
|
||||
<span class="dnd-container" title="{% trans "Click and drag this button to reorder. Double click to show buttons for reordering." %}"></span>
|
||||
<a title="{% trans "Edit" %}" href="{% url "control:event.items.categories.edit" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.items.categories.add" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ c.id }}"
|
||||
class="btn btn-sm btn-default" title="{% trans "Clone" %}" data-toggle="tooltip">
|
||||
<span class="fa fa-copy"></span>
|
||||
</a>
|
||||
<a title="{% trans "Delete" %}" href="{% url "control:event.items.categories.delete" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<button title="{% trans "Move up" %}" formaction="{% url "control:event.items.categories.up" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-default btn-sm sortable-up"{% if forloop.counter0 == 0 and not page_obj.has_previous %} disabled{% endif %}><i class="fa fa-arrow-up"></i></button>
|
||||
<button title="{% trans "Move down" %}" formaction="{% url "control:event.items.categories.down" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-default btn-sm sortable-down"{% if forloop.revcounter0 == 0 and not page_obj.has_next %} disabled{% endif %}><i class="fa fa-arrow-down"></i></button>
|
||||
<span class="dnd-container" title="{% trans "Click and drag this button to reorder. Double click to show buttons for reordering." %}"></span>
|
||||
<a title="{% trans "Edit" %}" href="{% url "control:event.items.categories.edit" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.items.categories.add" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ c.id }}"
|
||||
class="btn btn-sm btn-default" title="{% trans "Clone" %}" data-toggle="tooltip">
|
||||
<span class="fa fa-copy"></span>
|
||||
</a>
|
||||
<a title="{% trans "Delete" %}" href="{% url "control:event.items.categories.delete" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@@ -39,15 +39,19 @@
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<a href="{% url "control:event.items.discounts.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new discount" %}</a>
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<a href="{% url "control:event.items.discounts.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new discount" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p>
|
||||
<a href="{% url "control:event.items.discounts.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new discount" %}
|
||||
</a>
|
||||
</p>
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<p>
|
||||
<a href="{% url "control:event.items.discounts.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new discount" %}
|
||||
</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="table-responsive">
|
||||
@@ -70,8 +74,12 @@
|
||||
{% else %}
|
||||
<del>
|
||||
{% endif %}
|
||||
<a href="{% url "control:event.items.discounts.edit" organizer=request.event.organizer.slug event=request.event.slug discount=d.id %}">
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<a href="{% url "control:event.items.discounts.edit" organizer=request.event.organizer.slug event=request.event.slug discount=d.id %}">
|
||||
{{ d.internal_name }}</a>
|
||||
{% else %}
|
||||
{{ d.internal_name }}
|
||||
{% endif %}
|
||||
{% if d.active %}
|
||||
</strong>
|
||||
{% else %}
|
||||
@@ -134,23 +142,25 @@
|
||||
</td>
|
||||
{% endif %}
|
||||
<td class="text-right flip">
|
||||
<button formaction="{% url "control:event.items.discounts.up" organizer=request.event.organizer.slug event=request.event.slug discount=d.id %}"
|
||||
class="btn btn-default btn-sm sortable-up" title="{% trans "Move up" %}"
|
||||
{% if forloop.counter0 == 0 and not page_obj.has_previous %}
|
||||
disabled{% endif %}><i class="fa fa-arrow-up"></i></button>
|
||||
<button formaction="{% url "control:event.items.discounts.down" organizer=request.event.organizer.slug event=request.event.slug discount=d.id %}"
|
||||
class="btn btn-default btn-sm sortable-down" title="{% trans "Move down" %}"
|
||||
{% if forloop.revcounter0 == 0 and not page_obj.has_next %} disabled{% endif %}>
|
||||
<i class="fa fa-arrow-down"></i></button>
|
||||
<span class="dnd-container" title="{% trans "Click and drag this button to reorder. Double click to show buttons for reordering." %}"></span>
|
||||
<a href="{% url "control:event.items.discounts.edit" organizer=request.event.organizer.slug event=request.event.slug discount=d.id %}"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.items.discounts.add" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ d.id }}"
|
||||
class="btn btn-sm btn-default" title="{% trans "Clone" %}" data-toggle="tooltip">
|
||||
<span class="fa fa-copy"></span>
|
||||
</a>
|
||||
<a href="{% url "control:event.items.discounts.delete" organizer=request.event.organizer.slug event=request.event.slug discount=d.id %}"
|
||||
class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<button formaction="{% url "control:event.items.discounts.up" organizer=request.event.organizer.slug event=request.event.slug discount=d.id %}"
|
||||
class="btn btn-default btn-sm sortable-up" title="{% trans "Move up" %}"
|
||||
{% if forloop.counter0 == 0 and not page_obj.has_previous %}
|
||||
disabled{% endif %}><i class="fa fa-arrow-up"></i></button>
|
||||
<button formaction="{% url "control:event.items.discounts.down" organizer=request.event.organizer.slug event=request.event.slug discount=d.id %}"
|
||||
class="btn btn-default btn-sm sortable-down" title="{% trans "Move down" %}"
|
||||
{% if forloop.revcounter0 == 0 and not page_obj.has_next %} disabled{% endif %}>
|
||||
<i class="fa fa-arrow-down"></i></button>
|
||||
<span class="dnd-container" title="{% trans "Click and drag this button to reorder. Double click to show buttons for reordering." %}"></span>
|
||||
<a href="{% url "control:event.items.discounts.edit" organizer=request.event.organizer.slug event=request.event.slug discount=d.id %}"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.items.discounts.add" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ d.id }}"
|
||||
class="btn btn-sm btn-default" title="{% trans "Clone" %}" data-toggle="tooltip">
|
||||
<span class="fa fa-copy"></span>
|
||||
</a>
|
||||
<a href="{% url "control:event.items.discounts.delete" organizer=request.event.organizer.slug event=request.event.slug discount=d.id %}"
|
||||
class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@@ -21,14 +21,18 @@
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<a href="{% url "control:event.items.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new product" %}</a>
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<a href="{% url "control:event.items.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new product" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p>
|
||||
<a href="{% url "control:event.items.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new product" %}</a>
|
||||
</p>
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<p>
|
||||
<a href="{% url "control:event.items.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new product" %}</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="table-responsive">
|
||||
@@ -51,7 +55,9 @@
|
||||
<tbody>
|
||||
<tr class="sortable-disabled"><th colspan="9" scope="colgroup" class="text-muted">
|
||||
{{ c.internal_name|default:c.name }}{% if c.category_type != "normal" %} <span class="font-normal">({{ c.get_category_type_display }})</span>{% endif %}
|
||||
<a href="{% url "control:event.items.categories.edit" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" title="{% trans "Edit" %}"><span class="fa fa-edit fa-fw"></span></a>
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<a href="{% url "control:event.items.categories.edit" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" title="{% trans "Edit" %}"><span class="fa fa-edit fa-fw"></span></a>
|
||||
{% endif %}
|
||||
</th></tr>
|
||||
</tbody>
|
||||
{% endif %}
|
||||
@@ -62,7 +68,11 @@
|
||||
<tr data-dnd-id="{{ i.id }}" {% if not i.active %}class="row-muted"{% endif %}>
|
||||
<td><strong>
|
||||
{% if not i.active %}<strike>{% endif %}
|
||||
<a href="{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}">{{ i }}</a>
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<a href="{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}">{{ i }}</a>
|
||||
{% else %}
|
||||
{{ i }}
|
||||
{% endif %}
|
||||
{% if not i.active %}</strike>{% endif %}
|
||||
</strong>
|
||||
<br>
|
||||
@@ -158,12 +168,14 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right flip col-actions">
|
||||
<button title="{% trans "Move up" %}" formaction="{% url "control:event.items.up" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-default btn-sm sortable-up"{% if forloop.counter0 == 0 %} disabled{% endif %}><i class="fa fa-arrow-up"></i></button>
|
||||
<button title="{% trans "Move down" %}" formaction="{% url "control:event.items.down" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-default btn-sm sortable-down"{% if forloop.revcounter0 == 0 %} disabled{% endif %}><i class="fa fa-arrow-down"></i></button>
|
||||
<span class="dnd-container" title="{% trans "Click and drag this button to reorder. Double click to show buttons for reordering." %}"></span>
|
||||
<a href="{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-default btn-sm" title="{% trans "Edit" %}"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.items.add" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ i.id }}" class="btn btn-default btn-sm" title="{% trans "Clone" %}" data-toggle="tooltip"><i class="fa fa-copy"></i></a>
|
||||
<a href="{% url "control:event.items.delete" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-danger btn-sm" title="{% trans "Delete" %}"><i class="fa fa-trash"></i></a>
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<button title="{% trans "Move up" %}" formaction="{% url "control:event.items.up" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-default btn-sm sortable-up"{% if forloop.counter0 == 0 %} disabled{% endif %}><i class="fa fa-arrow-up"></i></button>
|
||||
<button title="{% trans "Move down" %}" formaction="{% url "control:event.items.down" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-default btn-sm sortable-down"{% if forloop.revcounter0 == 0 %} disabled{% endif %}><i class="fa fa-arrow-down"></i></button>
|
||||
<span class="dnd-container" title="{% trans "Click and drag this button to reorder. Double click to show buttons for reordering." %}"></span>
|
||||
<a href="{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-default btn-sm" title="{% trans "Edit" %}"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.items.add" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ i.id }}" class="btn btn-default btn-sm" title="{% trans "Clone" %}" data-toggle="tooltip"><i class="fa fa-copy"></i></a>
|
||||
<a href="{% url "control:event.items.delete" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-danger btn-sm" title="{% trans "Delete" %}"><i class="fa fa-trash"></i></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@@ -7,45 +7,57 @@
|
||||
{% block inside %}
|
||||
<h1>
|
||||
{% blocktrans with name=question.question %}Question: {{ name }}{% endblocktrans %}
|
||||
<a href="{% url "control:event.items.questions.edit" event=request.event.slug organizer=request.event.organizer.slug question=question.pk %}"
|
||||
class="btn btn-default">
|
||||
<span class="fa fa-edit"></span>
|
||||
{% trans "Edit question" %}
|
||||
</a>
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<a href="{% url "control:event.items.questions.edit" event=request.event.slug organizer=request.event.organizer.slug question=question.pk %}"
|
||||
class="btn btn-default">
|
||||
<span class="fa fa-edit"></span>
|
||||
{% trans "Edit question" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</h1>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{% trans "Filter" %}</h3>
|
||||
{% if 'event.orders:read' in request.eventpermset %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{% trans "Filter" %}</h3>
|
||||
</div>
|
||||
<form class="panel-body filter-form" action="" method="get">
|
||||
<div class="row">
|
||||
<div class="col-md-2 col-xs-6">
|
||||
{% bootstrap_field form.status %}
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6">
|
||||
{% bootstrap_field form.item %}
|
||||
</div>
|
||||
{% if has_subevents %}
|
||||
<div class="col-md-3 col-xs-6">
|
||||
{% bootstrap_field form.subevent %}
|
||||
</div>
|
||||
<div class="col-md-4 col-xs-6">
|
||||
{% bootstrap_field form.date_range %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<button class="btn btn-primary btn-lg" type="submit">
|
||||
<span class="fa fa-filter"></span>
|
||||
{% trans "Filter" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<form class="panel-body filter-form" action="" method="get">
|
||||
<div class="row">
|
||||
<div class="col-md-2 col-xs-6">
|
||||
{% bootstrap_field form.status %}
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6">
|
||||
{% bootstrap_field form.item %}
|
||||
</div>
|
||||
{% if has_subevents %}
|
||||
<div class="col-md-3 col-xs-6">
|
||||
{% bootstrap_field form.subevent %}
|
||||
</div>
|
||||
<div class="col-md-4 col-xs-6">
|
||||
{% bootstrap_field form.date_range %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<button class="btn btn-primary btn-lg" type="submit">
|
||||
<span class="fa fa-filter"></span>
|
||||
{% trans "Filter" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="row">
|
||||
{% if not stats %}
|
||||
{% if 'event.orders:read' not in request.eventpermset %}
|
||||
<div class="empty-collection col-md-10 col-xs-12">
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
No permission to view answers.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
{% elif not stats %}
|
||||
<div class="empty-collection col-md-10 col-xs-12">
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
|
||||
@@ -10,10 +10,12 @@
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% csrf_token %}
|
||||
<p>
|
||||
<a href="{% url "control:event.items.questions.add" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new question" %}
|
||||
</a>
|
||||
</p>
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<p>
|
||||
<a href="{% url "control:event.items.questions.add" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new question" %}
|
||||
</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-quotas">
|
||||
<thead>
|
||||
@@ -24,7 +26,9 @@
|
||||
<th class="iconcol"></th>
|
||||
<th class="iconcol"></th>
|
||||
<th>{% trans "Products" %}</th>
|
||||
<th class="action-col-2"></th>
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<th class="action-col-2"></th>
|
||||
{% endif %}
|
||||
<th class="action-col-2"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -79,16 +83,22 @@
|
||||
<small>{% trans "All personalized products" %}</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="dnd-container">
|
||||
</td>
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<td class="dnd-container">
|
||||
</td>
|
||||
{% endif %}
|
||||
<td class="text-right flip">
|
||||
{% if q.pk %}
|
||||
<a href="{% url "control:event.items.questions.show" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-default btn-sm"><i class="fa fa-bar-chart"></i></a>
|
||||
<a href="{% url "control:event.items.questions.edit" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.items.questions.delete" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<a href="{% url "control:event.items.questions.edit" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.items.questions.delete" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<a href="{% url "control:event.settings" organizer=request.event.organizer.slug event=request.event.slug %}#tab-0-2-open"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-wrench"></i></a>
|
||||
{% if 'event.settings.general:write' in request.eventpermset %}
|
||||
<a href="{% url "control:event.settings" organizer=request.event.organizer.slug event=request.event.slug %}#tab-0-2-open"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-wrench"></i></a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
{% block inside %}
|
||||
<h1>
|
||||
{% blocktrans with name=quota.name %}Quota: {{ name }}{% endblocktrans %}
|
||||
{% if 'can_change_items' in request.eventpermset %}
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<a href="{% url "control:event.items.quotas.edit" event=request.event.slug organizer=request.event.organizer.slug quota=quota.pk %}"
|
||||
class="btn btn-default">
|
||||
<span class="fa fa-edit"></span>
|
||||
|
||||
@@ -30,14 +30,18 @@
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
<a href="{% url "control:event.items.quotas.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new quota" %}</a>
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<a href="{% url "control:event.items.quotas.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new quota" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p>
|
||||
<a href="{% url "control:event.items.quotas.add" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new quota" %}
|
||||
</a>
|
||||
</p>
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<p>
|
||||
<a href="{% url "control:event.items.quotas.add" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new quota" %}
|
||||
</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-quotas">
|
||||
<thead>
|
||||
@@ -91,12 +95,14 @@
|
||||
<td>{% if q.size == None %}Unlimited{% else %}{{ q.size }}{% endif %}</td>
|
||||
<td>{% include "pretixcontrol/items/fragment_quota_availability.html" with availability=q.cached_avail closed=q.closed %}</td>
|
||||
<td class="text-right flip">
|
||||
<a href="{% url "control:event.items.quotas.edit" organizer=request.event.organizer.slug event=request.event.slug quota=q.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.items.quotas.add" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ q.id }}"
|
||||
class="btn btn-sm btn-default" title="{% trans "Clone" %}" data-toggle="tooltip">
|
||||
<span class="fa fa-copy"></span>
|
||||
</a>
|
||||
<a href="{% url "control:event.items.quotas.delete" organizer=request.event.organizer.slug event=request.event.slug quota=q.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
{% if 'event.items:write' in request.eventpermset %}
|
||||
<a href="{% url "control:event.items.quotas.edit" organizer=request.event.organizer.slug event=request.event.slug quota=q.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.items.quotas.add" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ q.id }}"
|
||||
class="btn btn-sm btn-default" title="{% trans "Clone" %}" data-toggle="tooltip">
|
||||
<span class="fa fa-copy"></span>
|
||||
</a>
|
||||
<a href="{% url "control:event.items.quotas.delete" organizer=request.event.organizer.slug event=request.event.slug quota=q.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
{% endif %}
|
||||
{% include "pretixcontrol/orders/fragment_order_status.html" with order=order class="pull-right flip" %}
|
||||
</h1>
|
||||
{% if 'can_change_orders' in request.eventpermset %}
|
||||
{% if 'event.orders:write' in request.eventpermset %}
|
||||
<form action="{% url "control:event.order.transition" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}"
|
||||
method="post">
|
||||
{% csrf_token %}
|
||||
@@ -193,7 +193,7 @@
|
||||
<dt>{% trans "Order locale" %}</dt>
|
||||
<dd>
|
||||
{{ display_locale }}
|
||||
{% if "can_change_orders" in request.eventpermset %}
|
||||
{% if "event.orders:write" in request.eventpermset %}
|
||||
<a href="{% url "control:event.order.locale" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}" class="btn btn-default btn-xs">
|
||||
<span class="fa fa-edit"></span>
|
||||
</a>
|
||||
@@ -220,7 +220,7 @@
|
||||
{{ order.customer.identifier }} – {{ order.customer.email }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if "can_change_orders" in request.eventpermset %}
|
||||
{% if "event.orders:write" in request.eventpermset %}
|
||||
<a href="{% url "control:event.order.contact" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}" class="btn btn-default btn-xs">
|
||||
<span class="fa fa-edit"></span>
|
||||
</a>
|
||||
@@ -233,7 +233,7 @@
|
||||
{% if order.email and order.email_known_to_work %}
|
||||
<span class="fa fa-check-circle text-success" data-toggle="tooltip" title="{% trans "We know that this email address works because the user clicked a link we sent them." %}"></span>
|
||||
{% endif %}
|
||||
{% if "can_change_orders" in request.eventpermset %}
|
||||
{% if "event.orders:write" in request.eventpermset %}
|
||||
<a href="{% url "control:event.order.contact" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}" class="btn btn-default btn-xs">
|
||||
<span class="fa fa-edit"></span>
|
||||
</a>
|
||||
@@ -257,7 +257,7 @@
|
||||
<dt>{% trans "Phone number" %}</dt>
|
||||
<dd>
|
||||
{{ order.phone|default_if_none:""|phone_format }}
|
||||
{% if "can_change_orders" in request.eventpermset %}
|
||||
{% if "event.orders:write" in request.eventpermset %}
|
||||
<a href="{% url "control:event.order.contact" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}" class="btn btn-default btn-xs">
|
||||
<span class="fa fa-edit"></span>
|
||||
</a>
|
||||
@@ -319,7 +319,7 @@
|
||||
<span class="fa fa-check text-success fa-stack-1x fa-stack-shifted"></span>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if i.transmission_status != "inflight" %}
|
||||
{% if i.transmission_status != "inflight" and "event.orders:write" in request.eventpermset %}
|
||||
<form class="form-inline helper-display-inline" method="post"
|
||||
action="{% url "control:event.order.retransmitinvoice" event=request.event.slug organizer=request.event.organizer.slug code=order.code id=i.pk %}">
|
||||
{% csrf_token %}
|
||||
@@ -334,7 +334,7 @@
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if not i.canceled %}
|
||||
{% if i.regenerate_allowed %}
|
||||
{% if i.regenerate_allowed and "event.orders:write" in request.eventpermset %}
|
||||
<form class="form-inline helper-display-inline" method="post"
|
||||
action="{% url "control:event.order.regeninvoice" event=request.event.slug organizer=request.event.organizer.slug code=order.code id=i.pk %}">
|
||||
{% csrf_token %}
|
||||
@@ -344,7 +344,7 @@
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if not i.is_cancellation %}
|
||||
{% if not i.is_cancellation and "event.orders:write" in request.eventpermset %}
|
||||
<form class="form-inline helper-display-inline" method="post"
|
||||
action="{% url "control:event.order.reissueinvoice" event=request.event.slug organizer=request.event.organizer.slug code=order.code id=i.pk %}">
|
||||
{% csrf_token %}
|
||||
@@ -371,7 +371,7 @@
|
||||
<br/>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if can_generate_invoice and 'can_change_orders' in request.eventpermset %}
|
||||
{% if can_generate_invoice and 'event.orders:write' in request.eventpermset %}
|
||||
<br/>
|
||||
<form class="form-inline helper-display-inline" method="post"
|
||||
action="{% url "control:event.order.geninvoice" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}">
|
||||
@@ -382,7 +382,7 @@
|
||||
</form>
|
||||
{% endif %}
|
||||
</dd>
|
||||
{% elif can_generate_invoice and 'can_change_orders' in request.eventpermset %}
|
||||
{% elif can_generate_invoice and 'event.orders:write' in request.eventpermset %}
|
||||
<dt>{% trans "Invoices" %}</dt>
|
||||
<dd>
|
||||
<form class="form-inline helper-display-inline" method="post"
|
||||
@@ -400,7 +400,7 @@
|
||||
<div class="panel panel-default items">
|
||||
<div class="panel-heading">
|
||||
<div class="pull-right flip">
|
||||
{% if 'can_change_orders' in request.eventpermset %}
|
||||
{% if 'event.orders:write' in request.eventpermset %}
|
||||
<a href="{% url "control:event.order.info" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}">
|
||||
<span class="fa fa-edit"></span>
|
||||
{% trans "Change answers" %}
|
||||
@@ -893,7 +893,7 @@
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if order.payment_refund_sum > 0 and "can_change_orders" in request.eventpermset %}
|
||||
{% if order.payment_refund_sum > 0 and "event.orders:write" in request.eventpermset %}
|
||||
<a href="{% url "control:event.order.refunds.start" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}" class="btn btn-default">
|
||||
{% trans "Create a refund" %}
|
||||
</a>
|
||||
@@ -1012,7 +1012,7 @@
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<div class="pull-right flip">
|
||||
{% if 'can_change_orders' in request.eventpermset %}
|
||||
{% if 'event.orders:write' in request.eventpermset %}
|
||||
<a href="{% url "control:event.order.info" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}">
|
||||
<span class="fa fa-edit"></span>
|
||||
{% trans "Change" %}
|
||||
@@ -1088,7 +1088,7 @@
|
||||
{% bootstrap_field comment_form.custom_followup_at %}
|
||||
{% bootstrap_field comment_form.checkin_attention show_help=True show_label=False %}
|
||||
{% bootstrap_field comment_form.checkin_text show_help=True show_label=False %}
|
||||
{% if "can_change_orders" in request.eventpermset %}
|
||||
{% if "event.orders:write" in request.eventpermset %}
|
||||
<button class="btn btn-default">
|
||||
{% trans "Update comment" %}
|
||||
</button>
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
{% if s.export_verbose_name == "?" %}
|
||||
<strong class="text-danger">
|
||||
<span class="fa fa-warning fa-fw"></span>
|
||||
{% trans "Exporter not found" %}
|
||||
{% trans "Exporter not found or no permission" %}
|
||||
</strong>
|
||||
{% elif s.error_counter >= 5 %}
|
||||
<strong class="text-danger">
|
||||
@@ -115,5 +115,9 @@
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% empty %}
|
||||
<p class="empty-collection">
|
||||
{% trans "There are no exporters available for you." %}
|
||||
</p>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -39,16 +39,18 @@
|
||||
</fieldset>
|
||||
{% if schedule_form %}
|
||||
{% include "pretixcontrol/orders/fragment_export_schedule_form.html" %}
|
||||
<div class="form-group submit-group">
|
||||
<button formaction="{{ request.get_full_path }}" name="schedule" value="save" type="submit"
|
||||
class="btn btn-primary btn-save" data-no-asynctask>
|
||||
{% if scheduled_copy_from %}
|
||||
{% trans "Save copy" %}
|
||||
{% else %}
|
||||
{% trans "Save" %}
|
||||
{% endif %}
|
||||
</button>
|
||||
</div>
|
||||
{% if not no_save %}
|
||||
<div class="form-group submit-group">
|
||||
<button formaction="{{ request.get_full_path }}" name="schedule" value="save" type="submit"
|
||||
class="btn btn-primary btn-save" data-no-asynctask>
|
||||
{% if scheduled_copy_from %}
|
||||
{% trans "Save copy" %}
|
||||
{% else %}
|
||||
{% trans "Save" %}
|
||||
{% endif %}
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
<table class="table table-condensed table-hover table-orders">
|
||||
<thead>
|
||||
<tr>
|
||||
{% if "can_change_orders" in request.eventpermset %}
|
||||
{% if "event.orders:write" in request.eventpermset %}
|
||||
<th>
|
||||
<label aria-label="{% trans "select all rows for batch-operation" %}"
|
||||
class="batch-select-label"><input type="checkbox" data-toggle-table/></label>
|
||||
@@ -154,7 +154,7 @@
|
||||
<a href="?{% url_replace request 'ordering' 'status' %}"><i class="fa fa-caret-up"></i></a>
|
||||
</th>
|
||||
</tr>
|
||||
{% if page_obj.paginator.num_pages > 1 and "can_change_orders" in request.eventpermset %}
|
||||
{% if page_obj.paginator.num_pages > 1 and "event.orders:write" in request.eventpermset %}
|
||||
<tr class="table-select-all warning hidden">
|
||||
<td>
|
||||
<input type="checkbox" name="__ALL" id="__all"
|
||||
@@ -171,7 +171,7 @@
|
||||
<tbody>
|
||||
{% for o in orders %}
|
||||
<tr>
|
||||
{% if "can_change_orders" in request.eventpermset %}
|
||||
{% if "event.orders:write" in request.eventpermset %}
|
||||
<td>
|
||||
<label aria-label="{% trans "select row for batch-operation" %}"
|
||||
class="batch-select-label"><input type="checkbox" name="order"
|
||||
@@ -281,7 +281,7 @@
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
{% if "can_change_orders" in request.eventpermset %}
|
||||
{% if "event.orders:write" in request.eventpermset %}
|
||||
<div class="batch-select-actions">
|
||||
<div class="btn-group dropup">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"
|
||||
|
||||
@@ -100,28 +100,30 @@
|
||||
{{ r.amount|money:request.event.currency }}
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
{% if r.state == "transit" or r.state == "created" %}
|
||||
<a href="{% url "control:event.order.refunds.cancel" event=request.event.slug organizer=request.event.organizer.slug code=r.order.code refund=r.pk %}?next={{ request.get_full_path|urlencode }}"
|
||||
class="btn btn-danger btn-xs" data-toggle="tooltip">
|
||||
<span class="fa fa-times"></span>
|
||||
{% trans "Cancel" %}
|
||||
</a>
|
||||
<a href="{% url "control:event.order.refunds.done" event=request.event.slug organizer=request.event.organizer.slug code=r.order.code refund=r.pk %}?next={{ request.get_full_path|urlencode }}"
|
||||
class="btn btn-primary btn-xs" data-toggle="tooltip">
|
||||
<span class="fa fa-check"></span>
|
||||
{% trans "Confirm as done" %}
|
||||
</a>
|
||||
{% if "event.orders:write" in request.eventpermset %}
|
||||
{% if r.state == "transit" or r.state == "created" %}
|
||||
<a href="{% url "control:event.order.refunds.cancel" event=request.event.slug organizer=request.event.organizer.slug code=r.order.code refund=r.pk %}?next={{ request.get_full_path|urlencode }}"
|
||||
class="btn btn-danger btn-xs" data-toggle="tooltip">
|
||||
<span class="fa fa-times"></span>
|
||||
{% trans "Cancel" %}
|
||||
</a>
|
||||
<a href="{% url "control:event.order.refunds.done" event=request.event.slug organizer=request.event.organizer.slug code=r.order.code refund=r.pk %}?next={{ request.get_full_path|urlencode }}"
|
||||
class="btn btn-primary btn-xs" data-toggle="tooltip">
|
||||
<span class="fa fa-check"></span>
|
||||
{% trans "Confirm as done" %}
|
||||
</a>
|
||||
{% elif r.state == "external" %}
|
||||
<a href="{% url "control:event.order.refunds.cancel" event=request.event.slug organizer=request.event.organizer.slug code=r.order.code refund=r.pk %}?next={{ request.get_full_path|urlencode }}"
|
||||
class="btn btn-default btn-xs" data-toggle="tooltip">
|
||||
<span class="fa fa-times"></span>
|
||||
{% trans "Ignore" %}
|
||||
</a>
|
||||
<a href="{% url "control:event.order.refunds.process" event=request.event.slug organizer=request.event.organizer.slug code=r.order.code refund=r.pk %}?next={{ request.get_full_path|urlencode }}"
|
||||
class="btn btn-primary btn-xs" data-toggle="tooltip">
|
||||
<span class="fa fa-check"></span>
|
||||
{% trans "Process refund" %}
|
||||
</a>
|
||||
<a href="{% url "control:event.order.refunds.cancel" event=request.event.slug organizer=request.event.organizer.slug code=r.order.code refund=r.pk %}?next={{ request.get_full_path|urlencode }}"
|
||||
class="btn btn-default btn-xs" data-toggle="tooltip">
|
||||
<span class="fa fa-times"></span>
|
||||
{% trans "Ignore" %}
|
||||
</a>
|
||||
<a href="{% url "control:event.order.refunds.process" event=request.event.slug organizer=request.event.organizer.slug code=r.order.code refund=r.pk %}?next={{ request.get_full_path|urlencode }}"
|
||||
class="btn btn-primary btn-xs" data-toggle="tooltip">
|
||||
<span class="fa fa-check"></span>
|
||||
{% trans "Process refund" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -93,16 +93,18 @@
|
||||
{% endif %}
|
||||
</dl>
|
||||
</form>
|
||||
<div class="text-right">
|
||||
<a href="{% url "control:organizer.customer.edit" organizer=request.organizer.slug customer=customer.identifier %}"
|
||||
class="btn btn-default">
|
||||
<i class="fa fa-edit"></i> {% trans "Edit" %}
|
||||
</a>
|
||||
<a href="{% url "control:organizer.customer.anonymize" organizer=request.organizer.slug customer=customer.identifier %}"
|
||||
class="btn btn-danger">
|
||||
<i class="fa fa-trash"></i> {% trans "Anonymize" %}
|
||||
</a>
|
||||
</div>
|
||||
{% if "organizer.customers:write" in request.orgapermset %}
|
||||
<div class="text-right">
|
||||
<a href="{% url "control:organizer.customer.edit" organizer=request.organizer.slug customer=customer.identifier %}"
|
||||
class="btn btn-default">
|
||||
<i class="fa fa-edit"></i> {% trans "Edit" %}
|
||||
</a>
|
||||
<a href="{% url "control:organizer.customer.anonymize" organizer=request.organizer.slug customer=customer.identifier %}"
|
||||
class="btn btn-danger">
|
||||
<i class="fa fa-trash"></i> {% trans "Anonymize" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default items">
|
||||
@@ -162,35 +164,39 @@
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
<a href="{% url "control:organizer.customer.membership.edit" organizer=request.organizer.slug customer=customer.identifier id=m.pk %}"
|
||||
data-toggle="tooltip"
|
||||
title="{% trans "Edit" %}"
|
||||
class="btn btn-default">
|
||||
<i class="fa fa-edit"></i>
|
||||
</a>
|
||||
{% if m.testmode %}
|
||||
<a href="{% url "control:organizer.customer.membership.delete" organizer=request.organizer.slug customer=customer.identifier id=m.pk %}"
|
||||
{% if "organizer.customers:write" in request.orgapermset %}
|
||||
<a href="{% url "control:organizer.customer.membership.edit" organizer=request.organizer.slug customer=customer.identifier id=m.pk %}"
|
||||
data-toggle="tooltip"
|
||||
title="{% trans "Delete" %}"
|
||||
class="btn btn-danger">
|
||||
<i class="fa fa-trash"></i>
|
||||
title="{% trans "Edit" %}"
|
||||
class="btn btn-default">
|
||||
<i class="fa fa-edit"></i>
|
||||
</a>
|
||||
{% if m.testmode %}
|
||||
<a href="{% url "control:organizer.customer.membership.delete" organizer=request.organizer.slug customer=customer.identifier id=m.pk %}"
|
||||
data-toggle="tooltip"
|
||||
title="{% trans "Delete" %}"
|
||||
class="btn btn-danger">
|
||||
<i class="fa fa-trash"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="7">
|
||||
<a href="{% url "control:organizer.customer.membership.add" organizer=request.organizer.slug customer=customer.identifier %}"
|
||||
class="btn btn-default">
|
||||
<i class="fa fa-plus"></i>
|
||||
{% trans "Add membership" %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
{% if "organizer.customers:write" in request.orgapermset %}
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="7">
|
||||
<a href="{% url "control:organizer.customer.membership.add" organizer=request.organizer.slug customer=customer.identifier %}"
|
||||
class="btn btn-default">
|
||||
<i class="fa fa-plus"></i>
|
||||
{% trans "Add membership" %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
<div class="panel panel-default items">
|
||||
@@ -300,14 +306,18 @@
|
||||
{% for gc in gift_cards %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{% url "control:organizer.giftcard" organizer=organizer.slug giftcard=gc.id %}">
|
||||
<strong>{{ gc.secret }}</strong></a>
|
||||
{% if gc.testmode %}
|
||||
<span class="label label-warning">{% trans "TEST MODE" %}</span>
|
||||
{% endif %}
|
||||
{% if gc.expired %}
|
||||
<span class="label label-danger">{% trans "Expired" %}</span>
|
||||
{% endif %}
|
||||
{% if "organizer.giftcards:read" in request.orgapermset %}
|
||||
<a href="{% url "control:organizer.giftcard" organizer=organizer.slug giftcard=gc.id %}">
|
||||
<strong>{{ gc.secret }}</strong></a>
|
||||
{% else %}
|
||||
<strong>{{ gc.secret|slice:":3" }}…</strong>
|
||||
{% endif %}
|
||||
{% if gc.testmode %}
|
||||
<span class="label label-warning">{% trans "TEST MODE" %}</span>
|
||||
{% endif %}
|
||||
{% if gc.expired %}
|
||||
<span class="label label-danger">{% trans "Expired" %}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ gc.issuance|date:"SHORT_DATETIME_FORMAT" }}</td>
|
||||
<td>{% if gc.expires %}{{ gc.expires|date:"SHORT_DATETIME_FORMAT" }}{% endif %}</td>
|
||||
@@ -316,10 +326,12 @@
|
||||
<p class="text-right">{{ gc.value|money:gc.currency }}</p>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<a href="{% url "control:organizer.giftcard" organizer=organizer.slug giftcard=gc.id %}"
|
||||
class="btn btn-default btn-sm" data-toggle="tooltip" title="{% trans "Details" %}">
|
||||
<i class="fa fa-eye"></i>
|
||||
</a>
|
||||
{% if "organizer.giftcards:read" in request.orgapermset %}
|
||||
<a href="{% url "control:organizer.giftcard" organizer=organizer.slug giftcard=gc.id %}"
|
||||
class="btn btn-default btn-sm" data-toggle="tooltip" title="{% trans "Details" %}">
|
||||
<i class="fa fa-eye"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
No customer accounts have been created yet.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<a href="{% url "control:organizer.customer.create" organizer=request.organizer.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new customer" %}</a>
|
||||
{% if "organizer.customers:write" in request.orgapermset %}
|
||||
<a href="{% url "control:organizer.customer.create" organizer=request.organizer.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new customer" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="panel panel-default">
|
||||
@@ -43,10 +45,12 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<p>
|
||||
<a href="{% url "control:organizer.customer.create" organizer=request.organizer.slug %}"
|
||||
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new customer" %}</a>
|
||||
</p>
|
||||
{% if "organizer.customers:write" in request.orgapermset %}
|
||||
<p>
|
||||
<a href="{% url "control:organizer.customer.create" organizer=request.organizer.slug %}"
|
||||
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new customer" %}</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
{% blocktrans with name=request.organizer.name %}Organizer: {{ name }}{% endblocktrans %}
|
||||
</h1>
|
||||
{% if events|length == 0 and not filter_form.filtered %}
|
||||
{% if "can_create_events" in request.orgapermset %}
|
||||
{% if "organizer.events:create" in request.orgapermset %}
|
||||
<p>
|
||||
<a href="{% url "control:events.add" %}?organizer={{ request.organizer.slug }}" class="btn btn-primary">
|
||||
<span class="fa fa-plus"></span>
|
||||
@@ -51,7 +51,7 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% if "can_create_events" in request.orgapermset %}
|
||||
{% if "organizer.events:create" in request.orgapermset %}
|
||||
<p>
|
||||
<a href="{% url "control:events.add" %}?organizer={{ request.organizer.slug }}" class="btn btn-primary">
|
||||
<span class="fa fa-plus"></span>
|
||||
@@ -147,7 +147,7 @@
|
||||
data-toggle="tooltip">
|
||||
<span class="fa fa-eye"></span>
|
||||
</a>
|
||||
{% if "can_create_events" in request.orgapermset %}
|
||||
{% if "organizer.events:create" in request.orgapermset %}
|
||||
<a href="{% url "control:events.add" %}?clone={{ e.pk }}" class="btn btn-sm btn-default"
|
||||
title="{% trans "Clone event" %}" data-toggle="tooltip">
|
||||
<span class="fa fa-copy"></span>
|
||||
|
||||
@@ -51,10 +51,12 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<p>
|
||||
<a href="{% url "control:organizer.device.add" organizer=request.organizer.slug %}"
|
||||
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Connect a device" %}</a>
|
||||
</p>
|
||||
{% if "organizer.devices:write" in request.orgapermset %}
|
||||
<p>
|
||||
<a href="{% url "control:organizer.device.add" organizer=request.organizer.slug %}"
|
||||
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Connect a device" %}</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
<form action="{% url "control:organizer.device.bulk_edit" organizer=request.organizer.slug %}" method="post">
|
||||
{% csrf_token %}
|
||||
{% for field in filter_form %}
|
||||
@@ -64,10 +66,12 @@
|
||||
<table class="table table-condensed table-hover table-quotas">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<label aria-label="{% trans "select all rows for batch-operation" %}"
|
||||
class="batch-select-label"><input type="checkbox" data-toggle-table/></label>
|
||||
</th>
|
||||
{% if "organizer.devices:write" in request.orgapermset %}
|
||||
<th>
|
||||
<label aria-label="{% trans "select all rows for batch-operation" %}"
|
||||
class="batch-select-label"><input type="checkbox" data-toggle-table/></label>
|
||||
</th>
|
||||
{% endif %}
|
||||
<th>{% trans "Device ID" %}
|
||||
<a href="?{% url_replace request 'ordering' '-device_id' %}"><i
|
||||
class="fa fa-caret-down"></i></a>
|
||||
@@ -105,12 +109,14 @@
|
||||
<tbody>
|
||||
{% for d in devices %}
|
||||
<tr {% if d.revoked %}class="text-muted"{% endif %}>
|
||||
<td>
|
||||
<label aria-label="{% trans "select row for batch-operation" %}"
|
||||
class="batch-select-label"><input type="checkbox" name="device"
|
||||
class="batch-select-checkbox"
|
||||
value="{{ d.pk }}"/></label>
|
||||
</td>
|
||||
{% if "organizer.devices:write" in request.orgapermset %}
|
||||
<td>
|
||||
<label aria-label="{% trans "select row for batch-operation" %}"
|
||||
class="batch-select-label"><input type="checkbox" name="device"
|
||||
class="batch-select-checkbox"
|
||||
value="{{ d.pk }}"/></label>
|
||||
</td>
|
||||
{% endif %}
|
||||
<td>
|
||||
{{ d.device_id }}
|
||||
</td>
|
||||
@@ -158,15 +164,17 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
{% if not d.initialized %}
|
||||
<a href="{% url "control:organizer.device.connect" organizer=request.organizer.slug device=d.id %}"
|
||||
class="btn btn-primary btn-sm"><i class="fa fa-link"></i>
|
||||
{% trans "Connect" %}</a>
|
||||
{% endif %}
|
||||
{% if not d.initialized or d.api_token %}
|
||||
<a href="{% url "control:organizer.device.revoke" organizer=request.organizer.slug device=d.id %}"
|
||||
class="btn btn-default btn-sm">
|
||||
{% trans "Revoke access" %}</a>
|
||||
{% if "organizer.devices:write" in request.orgapermset %}
|
||||
{% if not d.initialized %}
|
||||
<a href="{% url "control:organizer.device.connect" organizer=request.organizer.slug device=d.id %}"
|
||||
class="btn btn-primary btn-sm"><i class="fa fa-link"></i>
|
||||
{% trans "Connect" %}</a>
|
||||
{% endif %}
|
||||
{% if not d.initialized or d.api_token %}
|
||||
<a href="{% url "control:organizer.device.revoke" organizer=request.organizer.slug device=d.id %}"
|
||||
class="btn btn-default btn-sm">
|
||||
{% trans "Revoke access" %}</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if d.initialized %}
|
||||
<a href="{% url "control:organizer.device.logs" organizer=request.organizer.slug device=d.id %}"
|
||||
@@ -175,19 +183,23 @@
|
||||
{% trans "Logs" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
<a href="{% url "control:organizer.device.edit" organizer=request.organizer.slug device=d.id %}"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
{% if "organizer.devices:write" in request.orgapermset %}
|
||||
<a href="{% url "control:organizer.device.edit" organizer=request.organizer.slug device=d.id %}"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="batch-select-actions">
|
||||
<button type="submit" class="btn btn-primary btn-save" name="action" value="edit">
|
||||
<i class="fa fa-edit"></i>{% trans "Edit selected" %}
|
||||
</button>
|
||||
</div>
|
||||
{% if "organizer.devices:write" in request.orgapermset %}
|
||||
<div class="batch-select-actions">
|
||||
<button type="submit" class="btn btn-primary btn-save" name="action" value="edit">
|
||||
<i class="fa fa-edit"></i>{% trans "Edit selected" %}
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% include "pretixcontrol/pagination.html" %}
|
||||
{% endif %}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
{% if s.export_verbose_name == "?" %}
|
||||
<strong class="text-danger">
|
||||
<span class="fa fa-warning fa-fw"></span>
|
||||
{% trans "Exporter not found" %}
|
||||
{% trans "Exporter not found or no permission" %}
|
||||
</strong>
|
||||
{% elif s.error_counter >= 5 %}
|
||||
<strong class="text-danger">
|
||||
@@ -115,5 +115,9 @@
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% empty %}
|
||||
<p class="empty-collection">
|
||||
{% trans "There are no exporters available for you." %}
|
||||
</p>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -40,16 +40,18 @@
|
||||
</fieldset>
|
||||
{% if schedule_form %}
|
||||
{% include "pretixcontrol/orders/fragment_export_schedule_form.html" %}
|
||||
<div class="form-group submit-group">
|
||||
<button formaction="{{ request.get_full_path }}" name="schedule" value="save" type="submit"
|
||||
class="btn btn-primary btn-save" data-no-asynctask>
|
||||
{% if scheduled_copy_from %}
|
||||
{% trans "Save copy" %}
|
||||
{% else %}
|
||||
{% trans "Save" %}
|
||||
{% endif %}
|
||||
</button>
|
||||
</div>
|
||||
{% if not no_save %}
|
||||
<div class="form-group submit-group">
|
||||
<button formaction="{{ request.get_full_path }}" name="schedule" value="save" type="submit"
|
||||
class="btn btn-primary btn-save" data-no-asynctask>
|
||||
{% if scheduled_copy_from %}
|
||||
{% trans "Save copy" %}
|
||||
{% else %}
|
||||
{% trans "Save" %}
|
||||
{% endif %}
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
|
||||
@@ -6,10 +6,12 @@
|
||||
<p>
|
||||
{% trans "The list below shows gates that you can use to group check-in devices." %}
|
||||
</p>
|
||||
<a href="{% url "control:organizer.gate.add" organizer=request.organizer.slug %}" class="btn btn-default">
|
||||
<span class="fa fa-plus"></span>
|
||||
{% trans "Create a new gate" %}
|
||||
</a>
|
||||
{% if "organizer.devices:write" in request.orgapermset %}
|
||||
<a href="{% url "control:organizer.gate.add" organizer=request.organizer.slug %}" class="btn btn-default">
|
||||
<span class="fa fa-plus"></span>
|
||||
{% trans "Create a new gate" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -21,15 +23,21 @@
|
||||
{% for g in gates %}
|
||||
<tr>
|
||||
<td><strong>
|
||||
<a href="{% url "control:organizer.gate.edit" organizer=request.organizer.slug gate=g.id %}">
|
||||
{% if "organizer.devices:write" in request.orgapermset %}
|
||||
<a href="{% url "control:organizer.gate.edit" organizer=request.organizer.slug gate=g.id %}">
|
||||
{{ g.name }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ g.name }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</strong></td>
|
||||
<td class="text-right flip">
|
||||
<a href="{% url "control:organizer.gate.edit" organizer=request.organizer.slug gate=g.id %}"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:organizer.gate.delete" organizer=request.organizer.slug gate=g.id %}"
|
||||
class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
{% if "organizer.devices:write" in request.orgapermset %}
|
||||
<a href="{% url "control:organizer.gate.edit" organizer=request.organizer.slug gate=g.id %}"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:organizer.gate.delete" organizer=request.organizer.slug gate=g.id %}"
|
||||
class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@@ -10,10 +10,12 @@
|
||||
{% if card.testmode %}
|
||||
<span class="label label-warning">{% trans "TEST MODE" %}</span>
|
||||
{% endif %}
|
||||
<a href="{% url "control:organizer.giftcard.edit" organizer=request.organizer.slug giftcard=card.id %}"
|
||||
class="btn btn-default">
|
||||
<i class="fa fa-edit"></i> {% trans "Edit" %}
|
||||
</a>
|
||||
{% if "organizer.giftcards:write" in request.orgapermset %}
|
||||
<a href="{% url "control:organizer.giftcard.edit" organizer=request.organizer.slug giftcard=card.id %}"
|
||||
class="btn btn-default">
|
||||
<i class="fa fa-edit"></i> {% trans "Edit" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-10 col-xs-12">
|
||||
@@ -112,22 +114,24 @@
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<input type="text" class="form-control helper-display-block" placeholder="{% trans "Text" %}"
|
||||
name="text">
|
||||
</td>
|
||||
<td class="text-right form-inline">
|
||||
<input type="text" class="form-control input-sm" placeholder="{% trans "Value" %}" name="value">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<span class="fa fa-plus"></span>
|
||||
</button>
|
||||
</td>
|
||||
{% if "organizer.giftcards:write" in request.orgapermset %}
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<input type="text" class="form-control helper-display-block" placeholder="{% trans "Text" %}"
|
||||
name="text">
|
||||
</td>
|
||||
<td class="text-right form-inline">
|
||||
<input type="text" class="form-control input-sm" placeholder="{% trans "Value" %}" name="value">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<span class="fa fa-plus"></span>
|
||||
</button>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
</tfoot>
|
||||
</tr>
|
||||
</tfoot>
|
||||
{% endif %}
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -15,10 +15,11 @@
|
||||
or you can manually issue gift cards.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<a href="{% url "control:organizer.giftcard.add" organizer=request.organizer.slug %}"
|
||||
class="btn btn-default btn-lg"><i class="fa fa-plus"></i> {% trans "Manually issue a gift card" %}
|
||||
</a>
|
||||
{% if "organizer.giftcards:write" in request.orgapermset %}
|
||||
<a href="{% url "control:organizer.giftcard.add" organizer=request.organizer.slug %}"
|
||||
class="btn btn-default btn-lg"><i class="fa fa-plus"></i> {% trans "Manually issue a gift card" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="panel panel-default">
|
||||
@@ -45,10 +46,12 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<p>
|
||||
<a href="{% url "control:organizer.giftcard.add" organizer=request.organizer.slug %}"
|
||||
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Manually issue a gift card" %}</a>
|
||||
</p>
|
||||
{% if "organizer.giftcards:write" in request.orgapermset %}
|
||||
<p>
|
||||
<a href="{% url "control:organizer.giftcard.add" organizer=request.organizer.slug %}"
|
||||
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Manually issue a gift card" %}</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
No media have been created yet.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<a href="{% url "control:organizer.reusable_medium.create" organizer=request.organizer.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new medium" %}</a>
|
||||
{% if "organizer.reusablemedia:write" in request.orgapermset %}
|
||||
<a href="{% url "control:organizer.reusable_medium.create" organizer=request.organizer.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new medium" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="panel panel-default">
|
||||
@@ -40,10 +42,12 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<p>
|
||||
<a href="{% url "control:organizer.reusable_medium.create" organizer=request.organizer.slug %}"
|
||||
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new medium" %}</a>
|
||||
</p>
|
||||
{% if "organizer.reusablemedia:write" in request.orgapermset %}
|
||||
<p>
|
||||
<a href="{% url "control:organizer.reusable_medium.create" organizer=request.organizer.slug %}"
|
||||
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new medium" %}</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
@@ -77,9 +81,13 @@
|
||||
{% if m.customer %}
|
||||
<span class="helper-display-block">
|
||||
<span class="fa fa-user fa-fw"></span>
|
||||
<a href="{% url "control:organizer.customer" organizer=request.organizer.slug customer=m.customer.identifier %}">
|
||||
{% if "organizer.customers:read" in request.orgapermset %}
|
||||
<a href="{% url "control:organizer.customer" organizer=request.organizer.slug customer=m.customer.identifier %}">
|
||||
{{ m.customer }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ m.customer }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if m.linked_orderposition %}
|
||||
@@ -92,8 +100,12 @@
|
||||
{% if m.linked_giftcard %}
|
||||
<span class="helper-display-block">
|
||||
<span class="fa fa-credit-card fa-fw"></span>
|
||||
<a href="{% url "control:organizer.giftcard" organizer=request.organizer.slug giftcard=m.linked_giftcard.id %}">
|
||||
{{ m.linked_giftcard.secret }}</a>
|
||||
{% if "organizer.giftcards:read" in request.orgapermset %}
|
||||
<a href="{% url "control:organizer.giftcard" organizer=request.organizer.slug giftcard=m.linked_giftcard.id %}">
|
||||
{{ m.linked_giftcard.secret }}</a>
|
||||
{% else %}
|
||||
{{ m.linked_giftcard.secret|slice:":3" }}…
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
@@ -22,60 +22,68 @@
|
||||
</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{% trans "Media type" context "reusable_media" %}</dt>
|
||||
<dd>{{ medium.get_type_display }}</dd>
|
||||
<dt>{% trans "Identifier" context "reusable_media" %}</dt>
|
||||
<dd><code>{{ medium.identifier }}</code></dd>
|
||||
<dt>{% trans "Status" %}</dt>
|
||||
<dd>
|
||||
{% if not medium.active %}
|
||||
{% trans "disabled" %}
|
||||
{% elif medium.is_expired %}
|
||||
{% trans "expired" %}
|
||||
{% else %}
|
||||
{% trans "active" %}
|
||||
{% endif %}
|
||||
</dd>
|
||||
<dt>{% trans "Connections" context "reusable_media" %}</dt>
|
||||
<dd>
|
||||
{% if medium.customer %}
|
||||
<span class="helper-display-block">
|
||||
<span class="fa fa-user fa-fw"></span>
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{% trans "Media type" context "reusable_media" %}</dt>
|
||||
<dd>{{ medium.get_type_display }}</dd>
|
||||
<dt>{% trans "Identifier" context "reusable_media" %}</dt>
|
||||
<dd><code>{{ medium.identifier }}</code></dd>
|
||||
<dt>{% trans "Status" %}</dt>
|
||||
<dd>
|
||||
{% if not medium.active %}
|
||||
{% trans "disabled" %}
|
||||
{% elif medium.is_expired %}
|
||||
{% trans "expired" %}
|
||||
{% else %}
|
||||
{% trans "active" %}
|
||||
{% endif %}
|
||||
</dd>
|
||||
<dt>{% trans "Connections" context "reusable_media" %}</dt>
|
||||
<dd>
|
||||
{% if medium.customer %}
|
||||
<span class="helper-display-block">
|
||||
<span class="fa fa-user fa-fw"></span>
|
||||
{% if "organizer.customers:read" in request.orgapermset %}
|
||||
<a href="{% url "control:organizer.customer" organizer=request.organizer.slug customer=medium.customer.identifier %}">
|
||||
{{ medium.customer }}
|
||||
</a>
|
||||
</span>
|
||||
{% else %}
|
||||
{{ medium.customer }}
|
||||
{% endif %}
|
||||
{% if medium.linked_orderposition %}
|
||||
<span class="helper-display-block">
|
||||
<span class="fa fa-ticket fa-fw"></span>
|
||||
<a href="{% url "control:event.order" event=medium.linked_orderposition.order.event.slug organizer=request.organizer.slug code=medium.linked_orderposition.order.code %}">
|
||||
{{ medium.linked_orderposition.order.code }}</a>-{{ medium.linked_orderposition.positionid }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if medium.linked_giftcard %}
|
||||
<span class="helper-display-block">
|
||||
<span class="fa fa-credit-card fa-fw"></span>
|
||||
<a href="{% url "control:organizer.giftcard" organizer=request.organizer.slug giftcard=medium.linked_giftcard.id %}">
|
||||
{{ medium.linked_giftcard.secret }}</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
</dd>
|
||||
{% if medium.notes %}
|
||||
<dt>{% trans "Notes" %}</dt>
|
||||
<dd>{{ medium.notes }}</dd>
|
||||
</span>
|
||||
{% endif %}
|
||||
</dl>
|
||||
</form>
|
||||
<div class="text-right">
|
||||
<a href="{% url "control:organizer.reusable_medium.edit" organizer=request.organizer.slug pk=medium.pk %}"
|
||||
class="btn btn-default">
|
||||
<i class="fa fa-edit"></i> {% trans "Edit" %}
|
||||
</a>
|
||||
</div>
|
||||
{% if medium.linked_orderposition %}
|
||||
<span class="helper-display-block">
|
||||
<span class="fa fa-ticket fa-fw"></span>
|
||||
<a href="{% url "control:event.order" event=medium.linked_orderposition.order.event.slug organizer=request.organizer.slug code=medium.linked_orderposition.order.code %}">
|
||||
{{ medium.linked_orderposition.order.code }}</a>-{{ medium.linked_orderposition.positionid }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if medium.linked_giftcard %}
|
||||
<span class="helper-display-block">
|
||||
<span class="fa fa-credit-card fa-fw"></span>
|
||||
{% if "organizer.giftcards:read" in request.orgapermset %}
|
||||
<a href="{% url "control:organizer.giftcard" organizer=request.organizer.slug giftcard=medium.linked_giftcard.id %}">
|
||||
{{ medium.linked_giftcard.secret }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ medium.linked_giftcard.secret|slice:":3" }}…
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</dd>
|
||||
{% if medium.notes %}
|
||||
<dt>{% trans "Notes" %}</dt>
|
||||
<dd>{{ medium.notes }}</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
{% if "organizer.reusablemedia:write" in request.orgapermset %}
|
||||
<div class="text-right">
|
||||
<a href="{% url "control:organizer.reusable_medium.edit" organizer=request.organizer.slug pk=medium.pk %}"
|
||||
class="btn btn-default">
|
||||
<i class="fa fa-edit"></i> {% trans "Edit" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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>
|
||||
@@ -22,25 +23,24 @@
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{% trans "Organizer permissions" %}</legend>
|
||||
{% 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" %}
|
||||
<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>
|
||||
|
||||
{% 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" %}
|
||||
<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">
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<a href="{% url "control:event.subevents.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i>
|
||||
{% trans "Create a new date" context "subevent" %}</a>
|
||||
<a href="{% url "control:event.subevents.bulk" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i>
|
||||
{% trans "Create many new dates" context "subevent" %}</a>
|
||||
{% if "event.subevents:write" in request.eventpermset %}
|
||||
<a href="{% url "control:event.subevents.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i>
|
||||
{% trans "Create a new date" context "subevent" %}</a>
|
||||
<a href="{% url "control:event.subevents.bulk" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i>
|
||||
{% trans "Create many new dates" context "subevent" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="panel panel-default">
|
||||
@@ -65,7 +67,7 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% if "can_change_event_settings" in request.eventpermset %}
|
||||
{% if "event.subevents:write" in request.eventpermset %}
|
||||
<p>
|
||||
<a href="{% url "control:event.subevents.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-default"><i class="fa fa-plus"></i>
|
||||
@@ -84,11 +86,13 @@
|
||||
<table class="table table-hover table-quotas">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
{% if "can_change_event_settings" in request.eventpermset %}
|
||||
<label aria-label="{% trans "select all rows for batch-operation" %}" class="batch-select-label"><input type="checkbox" data-toggle-table/></label>
|
||||
{% endif %}
|
||||
</th>
|
||||
{% if "event.subevents:write" in request.eventpermset %}
|
||||
<th>
|
||||
{% if "event.subevents:write" in request.eventpermset %}
|
||||
<label aria-label="{% trans "select all rows for batch-operation" %}" class="batch-select-label"><input type="checkbox" data-toggle-table/></label>
|
||||
{% endif %}
|
||||
</th>
|
||||
{% endif %}
|
||||
<th>
|
||||
{% trans "Name" %}
|
||||
</th>
|
||||
@@ -107,7 +111,7 @@
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
{% if "can_change_event_settings" in request.eventpermset and page_obj.paginator.num_pages > 1 %}
|
||||
{% if "event.subevents:write" in request.eventpermset and page_obj.paginator.num_pages > 1 %}
|
||||
<tr class="table-select-all warning hidden">
|
||||
<td>
|
||||
<input type="checkbox" name="__ALL" id="__all" data-results-total="{{ page_obj.paginator.count }}">
|
||||
@@ -123,11 +127,11 @@
|
||||
<tbody>
|
||||
{% for s in subevents %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if "can_change_event_settings" in request.eventpermset %}
|
||||
{% if "event.subevents:write" in request.eventpermset %}
|
||||
<td>
|
||||
<label aria-label="{% trans "select row for batch-operation" %}" class="batch-select-label"><input type="checkbox" name="subevent" class="batch-select-checkbox" value="{{ s.pk }}"/></label>
|
||||
{% endif %}
|
||||
</td>
|
||||
</td>
|
||||
{% endif %}
|
||||
<td>
|
||||
<strong><a href="{% url "control:event.subevent" organizer=request.event.organizer.slug event=request.event.slug subevent=s.id %}?returnto={{ request.GET.urlencode|urlencode }}">
|
||||
{{ s.name }}</a></strong><br>
|
||||
@@ -173,35 +177,39 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
<a href="{% url "control:event.orders" organizer=request.event.organizer.slug event=request.event.slug %}?subevent={{ s.id }}" class="btn btn-default btn-sm" title="{% trans "Show orders" %}"><i class="fa fa-shopping-cart" aria-hidden="true"></i></a>
|
||||
{% if "event.orders:read" in request.eventpermset %}
|
||||
<a href="{% url "control:event.orders" organizer=request.event.organizer.slug event=request.event.slug %}?subevent={{ s.id }}" class="btn btn-default btn-sm" title="{% trans "Show orders" %}"><i class="fa fa-shopping-cart" aria-hidden="true"></i></a>
|
||||
{% endif %}
|
||||
|
||||
<a href="{% url "control:event.subevent" organizer=request.event.organizer.slug event=request.event.slug subevent=s.id %}?returnto={{ request.GET.urlencode|urlencode }}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<div class="btn-group {% if forloop.revcounter0 < 2 %}dropup{% endif %}">
|
||||
<button type="button" class="btn btn-default btn-sm dropdown-toggle"
|
||||
data-toggle="dropdown">
|
||||
<span class="fa fa-copy"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
<li>
|
||||
<a href="{% url "control:event.subevents.add" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ s.id }}">
|
||||
{% trans "Use as a template for a new date" context "subevent" %}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url "control:event.subevents.bulk" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ s.id }}">
|
||||
{% trans "Use as a template for many new dates" context "subevent" %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<a href="{% url "control:event.subevent.delete" organizer=request.event.organizer.slug event=request.event.slug subevent=s.id %}?returnto={{ request.GET.urlencode|urlencode }}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
{% if "event.subevents:write" in request.eventpermset %}
|
||||
<a href="{% url "control:event.subevent" organizer=request.event.organizer.slug event=request.event.slug subevent=s.id %}?returnto={{ request.GET.urlencode|urlencode }}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<div class="btn-group {% if forloop.revcounter0 < 2 %}dropup{% endif %}">
|
||||
<button type="button" class="btn btn-default btn-sm dropdown-toggle"
|
||||
data-toggle="dropdown">
|
||||
<span class="fa fa-copy"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
<li>
|
||||
<a href="{% url "control:event.subevents.add" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ s.id }}">
|
||||
{% trans "Use as a template for a new date" context "subevent" %}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url "control:event.subevents.bulk" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ s.id }}">
|
||||
{% trans "Use as a template for many new dates" context "subevent" %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<a href="{% url "control:event.subevent.delete" organizer=request.event.organizer.slug event=request.event.slug subevent=s.id %}?returnto={{ request.GET.urlencode|urlencode }}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% if "can_change_event_settings" in request.eventpermset %}
|
||||
{% if "event.subevents:write" in request.eventpermset %}
|
||||
<div class="batch-select-actions">
|
||||
<button type="submit" class="btn btn-danger btn-save" name="action" value="delete">
|
||||
<i class="fa fa-trash"></i>{% trans "Delete selected" %}
|
||||
|
||||
@@ -120,7 +120,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if "can_change_vouchers" in request.eventpermset %}
|
||||
{% if "event.vouchers:write" in request.eventpermset %}
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
{% trans "Save" %}
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
{% if "can_change_vouchers" in request.eventpermset %}
|
||||
{% if "event.vouchers:write" in request.eventpermset %}
|
||||
<a href="{% url "control:event.vouchers.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new voucher" %}</a>
|
||||
<a href="{% url "control:event.vouchers.bulk" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
@@ -83,7 +83,7 @@
|
||||
</div>
|
||||
{% else %}
|
||||
<p>
|
||||
{% if "can_change_vouchers" in request.eventpermset %}
|
||||
{% if "event.vouchers:write" in request.eventpermset %}
|
||||
<a href="{% url "control:event.vouchers.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new voucher" %}</a>
|
||||
<a href="{% url "control:event.vouchers.bulk" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
@@ -103,7 +103,7 @@
|
||||
<table class="table table-hover table-quotas">
|
||||
<thead>
|
||||
<tr>
|
||||
{% if "can_change_vouchers" in request.eventpermset %}
|
||||
{% if "event.vouchers:write" in request.eventpermset %}
|
||||
<th>
|
||||
<label aria-label="{% trans "select all rows for batch-operation" %}" class="batch-select-label">
|
||||
<input type="checkbox" data-toggle-table />
|
||||
@@ -148,7 +148,7 @@
|
||||
<tbody>
|
||||
{% for v in vouchers %}
|
||||
<tr>
|
||||
{% if "can_change_vouchers" in request.eventpermset %}
|
||||
{% if "event.vouchers:write" in request.eventpermset %}
|
||||
<td>
|
||||
<label aria-label="{% trans "select row for batch-operation" %}" class="batch-select-label">
|
||||
<input type="checkbox" name="voucher" class="batch-select-checkbox" value="{{ v.pk }}"/>
|
||||
@@ -192,7 +192,7 @@
|
||||
</td>
|
||||
{% endif %}
|
||||
<td class="text-right flip">
|
||||
{% if "can_change_vouchers" in request.eventpermset %}
|
||||
{% if "event.vouchers:write" in request.eventpermset %}
|
||||
<a href="{% url "control:event.vouchers.bulk" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ v.id }}"
|
||||
class="btn btn-sm btn-default" title="{% trans "Use as a template for new vouchers" %}" data-toggle="tooltip">
|
||||
<span class="fa fa-copy"></span>
|
||||
@@ -205,7 +205,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% if "can_change_vouchers" in request.eventpermset %}
|
||||
{% if "event.vouchers:write" in request.eventpermset %}
|
||||
<div class="batch-select-actions">
|
||||
<button type="submit" class="btn btn-danger" name="action" value="delete">
|
||||
<i class="fa fa-trash" aria-hidden="true"></i>
|
||||
|
||||
@@ -7,10 +7,12 @@
|
||||
{% block content %}
|
||||
<h1>
|
||||
{% trans "Waiting list" %}
|
||||
<a href="{% url "control:event.settings" event=request.event.slug organizer=request.organizer.slug %}#waiting-list-open" class="btn btn-default">
|
||||
<span class="fa fa-cog"></span>
|
||||
{% trans "Settings" %}
|
||||
</a>
|
||||
{% if "event.settings.general:write" in request.eventpermset %}
|
||||
<a href="{% url "control:event.settings" event=request.event.slug organizer=request.organizer.slug %}#waiting-list-open" class="btn btn-default">
|
||||
<span class="fa fa-cog"></span>
|
||||
{% trans "Settings" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</h1>
|
||||
{% if not request.event.settings.waiting_list_enabled %}
|
||||
<div class="alert alert-danger">
|
||||
@@ -27,7 +29,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="row">
|
||||
{% if 'can_change_orders' in request.eventpermset %}
|
||||
{% if 'event.orders:write' in request.eventpermset %}
|
||||
<form method="post" class="col-md-6"
|
||||
action="{% url "control:event.orders.waitinglist.auto" event=request.event.slug organizer=request.organizer.slug %}"
|
||||
data-asynctask>
|
||||
@@ -80,7 +82,7 @@
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
<div class="{% if 'can_change_orders' in request.eventpermset %}col-md-6{% else %}col-md-12{% endif %}">
|
||||
<div class="{% if 'event.orders:write' in request.eventpermset %}col-md-6{% else %}col-md-12{% endif %}">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{% trans "Sales estimate" %}
|
||||
@@ -152,7 +154,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
{% if "can_change_orders" in request.eventpermset %}
|
||||
{% if "event.orders:write" in request.eventpermset %}
|
||||
<label aria-label="{% trans "select all rows for batch-operation" %}" class="batch-select-label"><input type="checkbox" data-toggle-table/></label>
|
||||
{% endif %}
|
||||
</th>
|
||||
@@ -172,7 +174,7 @@
|
||||
<th>{% trans "Voucher" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
{% if "can_change_orders" in request.eventpermset and page_obj.paginator.num_pages > 1 %}
|
||||
{% if "event.orders:write" in request.eventpermset and page_obj.paginator.num_pages > 1 %}
|
||||
<tr class="table-select-all warning hidden">
|
||||
<td>
|
||||
<input type="checkbox" name="__ALL" id="__all" data-results-total="{{ page_obj.paginator.count }}">
|
||||
@@ -189,7 +191,7 @@
|
||||
{% for e in entries %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if "can_change_orders" in request.eventpermset %}
|
||||
{% if "event.orders:write" in request.eventpermset %}
|
||||
<label aria-label="{% trans "select row for batch-operation" %}" class="batch-select-label"><input type="checkbox" name="entry" class="batch-select-checkbox" value="{{ e.pk }}"/></label>
|
||||
{% endif %}
|
||||
</td>
|
||||
@@ -259,31 +261,34 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
{% if not e.voucher %}
|
||||
<button name="move_top" value="{{ e.pk }}" class="btn btn-default btn-sm"
|
||||
data-toggle="tooltip" title="{% trans "Move to the top of the list" %}">
|
||||
<span class="fa fa-thumbs-up"></span>
|
||||
</button>
|
||||
<button name="move_end" value="{{ e.pk }}" class="btn btn-default btn-sm"
|
||||
data-toggle="tooltip" title="{% trans "Move to the end of the list" %}">
|
||||
<span class="fa fa-thumbs-down"></span>
|
||||
</button>
|
||||
{% if 'event.orders:write' in request.eventpermset %}
|
||||
{% if not e.voucher %}
|
||||
<button name="move_top" value="{{ e.pk }}" class="btn btn-default btn-sm"
|
||||
data-toggle="tooltip" title="{% trans "Move to the top of the list" %}">
|
||||
<span class="fa fa-thumbs-up"></span>
|
||||
</button>
|
||||
<button name="move_end" value="{{ e.pk }}" class="btn btn-default btn-sm"
|
||||
data-toggle="tooltip" title="{% trans "Move to the end of the list" %}">
|
||||
<span class="fa fa-thumbs-down"></span>
|
||||
</button>
|
||||
|
||||
<a href="{% url "control:event.orders.waitinglist.edit" organizer=request.event.organizer.slug event=request.event.slug entry=e.id %}"
|
||||
class="btn btn-default btn-sm" title="{% trans "Edit entry" %}"
|
||||
data-toggle="tooltip">
|
||||
<i class="fa fa-edit" aria-hidden="true"></i>
|
||||
</a>
|
||||
<a href="{% url "control:event.orders.waitinglist.edit" organizer=request.event.organizer.slug event=request.event.slug entry=e.id %}"
|
||||
class="btn btn-default btn-sm" title="{% trans "Edit entry" %}"
|
||||
data-toggle="tooltip">
|
||||
<i class="fa fa-edit" aria-hidden="true"></i>
|
||||
</a>
|
||||
|
||||
<a href="{% url "control:event.orders.waitinglist.delete" organizer=request.event.organizer.slug event=request.event.slug entry=e.id %}?next={{ request.get_full_path|urlencode }}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
{% else %}
|
||||
<button class="btn btn-default btn-sm disabled">
|
||||
<span class="fa fa-thumbs-up"></span>
|
||||
</button>
|
||||
<button class="btn btn-default btn-sm disabled">
|
||||
<span class="fa fa-thumbs-down"></span>
|
||||
</button>
|
||||
<span class="btn btn-danger btn-sm disabled"><i class="fa fa-trash"></i></span>
|
||||
<a href="{% url "control:event.orders.waitinglist.delete" organizer=request.event.organizer.slug event=request.event.slug entry=e.id %}?next={{ request.get_full_path|urlencode }}"
|
||||
class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
{% else %}
|
||||
<button class="btn btn-default btn-sm disabled">
|
||||
<span class="fa fa-thumbs-up"></span>
|
||||
</button>
|
||||
<button class="btn btn-default btn-sm disabled">
|
||||
<span class="fa fa-thumbs-down"></span>
|
||||
</button>
|
||||
<span class="btn btn-danger btn-sm disabled"><i class="fa fa-trash"></i></span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -291,7 +296,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% if "can_change_orders" in request.eventpermset %}
|
||||
{% if "event.orders:write" in request.eventpermset %}
|
||||
<div class="batch-select-actions">
|
||||
<button type="submit" class="btn btn-danger btn-save" name="action" value="delete">
|
||||
<i class="fa fa-trash"></i>
|
||||
|
||||
@@ -53,6 +53,7 @@ from pretix.base.media import MEDIA_TYPES
|
||||
from pretix.base.models import Checkin, LogEntry, Order, OrderPosition
|
||||
from pretix.base.models.checkin import CheckinList
|
||||
from pretix.base.models.orders import PrintLog
|
||||
from pretix.base.permissions import AnyPermissionOf
|
||||
from pretix.base.services.checkin import (
|
||||
LazyRuleVars, _logic_annotate_for_graphic_explain,
|
||||
)
|
||||
@@ -150,7 +151,7 @@ class CheckInListShow(EventPermissionRequiredMixin, PaginationMixin, CheckInList
|
||||
model = Checkin
|
||||
context_object_name = 'entries'
|
||||
template_name = 'pretixcontrol/checkin/index.html'
|
||||
permission = 'can_view_orders'
|
||||
permission = 'event.orders:read'
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.list = get_object_or_404(self.request.event.checkin_lists.all(), pk=kwargs.get("list"))
|
||||
@@ -211,7 +212,7 @@ class CheckInListBulkRevertConfirmView(CheckInListQueryMixin, EventPermissionReq
|
||||
|
||||
|
||||
class CheckInListBulkActionView(CheckInListQueryMixin, EventPermissionRequiredMixin, AsyncPostView):
|
||||
permission = ('can_change_orders', 'can_checkin_orders')
|
||||
permission = AnyPermissionOf('event.orders:write', 'event.orders:checkin')
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.list = get_object_or_404(self.request.event.checkin_lists.all(), pk=kwargs.get("list"))
|
||||
@@ -228,7 +229,7 @@ class CheckInListBulkActionView(CheckInListQueryMixin, EventPermissionRequiredMi
|
||||
self.list = get_object_or_404(request.event.checkin_lists.all(), pk=kwargs.get("list"))
|
||||
positions = self.get_queryset()
|
||||
if request.POST.get('revert') == 'true':
|
||||
if not request.user.has_event_permission(request.organizer, request.event, 'can_change_orders', request=request):
|
||||
if not request.user.has_event_permission(request.organizer, request.event, 'event.orders:write', request=request):
|
||||
raise PermissionDenied()
|
||||
for op in positions:
|
||||
if op.order.status == Order.STATUS_PAID or (
|
||||
@@ -295,7 +296,7 @@ class CheckInListBulkActionView(CheckInListQueryMixin, EventPermissionRequiredMi
|
||||
class CheckinListList(EventPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
model = CheckinList
|
||||
context_object_name = 'checkinlists'
|
||||
permission = 'can_view_orders'
|
||||
permission = AnyPermissionOf('event.orders:read', 'event.settings.general:write')
|
||||
template_name = 'pretixcontrol/checkin/lists.html'
|
||||
ordering = ('subevent__date_from', 'name', 'pk')
|
||||
|
||||
@@ -317,9 +318,9 @@ class CheckinListList(EventPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
cl.subevent.event = self.request.event # re-use same event object to make sure settings are cached
|
||||
ctx['checkinlists'] = clists
|
||||
|
||||
ctx['can_change_organizer_settings'] = self.request.user.has_organizer_permission(
|
||||
ctx['link_device_settings'] = self.request.user.has_organizer_permission(
|
||||
self.request.organizer,
|
||||
'can_change_organizer_settings',
|
||||
'organizer.devices:read',
|
||||
self.request
|
||||
)
|
||||
ctx['filter_form'] = self.filter_form
|
||||
@@ -335,7 +336,7 @@ class CheckinListCreate(EventPermissionRequiredMixin, CreateView):
|
||||
model = CheckinList
|
||||
form_class = CheckinListForm
|
||||
template_name = 'pretixcontrol/checkin/list_edit.html'
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
context_object_name = 'checkinlist'
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
@@ -386,7 +387,7 @@ class CheckinListUpdate(EventPermissionRequiredMixin, UpdateView):
|
||||
model = CheckinList
|
||||
form_class = CheckinListForm
|
||||
template_name = 'pretixcontrol/checkin/list_edit.html'
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
context_object_name = 'checkinlist'
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
@@ -445,7 +446,7 @@ class CheckinListUpdate(EventPermissionRequiredMixin, UpdateView):
|
||||
class CheckinListDelete(EventPermissionRequiredMixin, CompatDeleteView):
|
||||
model = CheckinList
|
||||
template_name = 'pretixcontrol/checkin/list_delete.html'
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
context_object_name = 'checkinlist'
|
||||
|
||||
def get_object(self, queryset=None) -> CheckinList:
|
||||
@@ -476,7 +477,7 @@ class CheckinListDelete(EventPermissionRequiredMixin, CompatDeleteView):
|
||||
class CheckinListView(EventPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
model = Checkin
|
||||
context_object_name = 'checkins'
|
||||
permission = 'can_view_orders'
|
||||
permission = 'event.orders:read'
|
||||
template_name = 'pretixcontrol/checkin/checkins.html'
|
||||
ordering = ('-datetime', '-pk')
|
||||
|
||||
@@ -505,7 +506,7 @@ class CheckinListView(EventPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
|
||||
class CheckInListSimulator(EventPermissionRequiredMixin, FormView):
|
||||
template_name = 'pretixcontrol/checkin/simulator.html'
|
||||
permission = 'can_view_orders'
|
||||
permission = 'event.orders:read'
|
||||
form_class = CheckinListSimulatorForm
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
@@ -575,9 +576,15 @@ class CheckInListSimulator(EventPermissionRequiredMixin, FormView):
|
||||
|
||||
class CheckInResetView(CheckInListQueryMixin, EventPermissionRequiredMixin, AsyncFormView):
|
||||
form_class = CheckinResetForm
|
||||
permission = "can_change_orders"
|
||||
permission = "event.orders:write"
|
||||
template_name = "pretixcontrol/checkin/reset.html"
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
# Special case, we want two permissions to be set
|
||||
if not request.user.has_event_permission(request.organizer, request.event, "event.settings.general:write", request=request):
|
||||
raise PermissionDenied()
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_error_url(self, *args):
|
||||
return reverse(
|
||||
"control:event.orders.checkinlists",
|
||||
|
||||
@@ -66,6 +66,7 @@ from pretix.control.signals import (
|
||||
from pretix.helpers.daterange import daterange
|
||||
|
||||
from ...base.models.orders import CancellationRequest
|
||||
from ...base.models.organizer import TeamQuerySet
|
||||
from ...base.templatetags.money import money_filter
|
||||
from ..logdisplay import OVERVIEW_BANLIST
|
||||
|
||||
@@ -350,10 +351,10 @@ def event_index(request, organizer, event):
|
||||
except SubEvent.DoesNotExist:
|
||||
pass
|
||||
|
||||
can_view_orders = request.user.has_event_permission(request.organizer, request.event, 'can_view_orders',
|
||||
can_view_orders = request.user.has_event_permission(request.organizer, request.event, 'event.orders:read',
|
||||
request=request)
|
||||
can_change_event_settings = request.user.has_event_permission(request.organizer, request.event,
|
||||
'can_change_event_settings', request=request)
|
||||
'event.settings.general:write', request=request)
|
||||
|
||||
widgets = []
|
||||
if can_view_orders:
|
||||
@@ -425,11 +426,11 @@ def event_index_log_lazy(request, organizer, event):
|
||||
'device').order_by('-datetime')
|
||||
qs = qs.exclude(action_type__in=OVERVIEW_BANLIST)
|
||||
|
||||
can_view_orders = request.user.has_event_permission(request.organizer, request.event, 'can_view_orders',
|
||||
can_view_orders = request.user.has_event_permission(request.organizer, request.event, 'event.orders:read',
|
||||
request=request)
|
||||
can_change_event_settings = request.user.has_event_permission(request.organizer, request.event,
|
||||
'can_change_event_settings', request=request)
|
||||
can_view_vouchers = request.user.has_event_permission(request.organizer, request.event, 'can_view_vouchers',
|
||||
'event.settings.general:write', request=request)
|
||||
can_view_vouchers = request.user.has_event_permission(request.organizer, request.event, 'event.vouchers:read',
|
||||
request=request)
|
||||
|
||||
if not can_view_orders:
|
||||
@@ -441,7 +442,7 @@ def event_index_log_lazy(request, organizer, event):
|
||||
ContentType.objects.get_for_model(Voucher),
|
||||
ContentType.objects.get_for_model(Order)
|
||||
]
|
||||
if request.user.has_event_permission(request.organizer, request.event, 'can_change_items', request=request):
|
||||
if request.user.has_event_permission(request.organizer, request.event, 'event.items:write', request=request):
|
||||
allowed_types += [
|
||||
ContentType.objects.get_for_model(Item),
|
||||
ContentType.objects.get_for_model(ItemCategory),
|
||||
@@ -491,8 +492,13 @@ def widgets_for_event_qs(request, qs, user, nmax, lazy=False):
|
||||
# Get set of events where we have the permission to show the # of orders
|
||||
if not lazy:
|
||||
events_with_orders = set(qs.filter(
|
||||
Q(organizer_id__in=user.teams.filter(all_events=True, can_view_orders=True).values_list('organizer', flat=True))
|
||||
| Q(id__in=user.teams.filter(can_view_orders=True).values_list('limit_events__id', flat=True))
|
||||
Q(organizer_id__in=user.teams.filter(
|
||||
TeamQuerySet.event_permission_q("event.orders:read"),
|
||||
all_events=True,
|
||||
).values_list('organizer', flat=True))
|
||||
| Q(id__in=user.teams.filter(
|
||||
TeamQuerySet.event_permission_q("event.orders:read"),
|
||||
).values_list('limit_events__id', flat=True))
|
||||
).values_list('id', flat=True))
|
||||
|
||||
tpl = """
|
||||
@@ -635,7 +641,7 @@ def user_index(request):
|
||||
|
||||
ctx = {
|
||||
'widgets': rearrange(widgets),
|
||||
'can_create_event': request.user.teams.filter(can_create_events=True).exists(),
|
||||
'can_create_event': request.user.teams.with_organizer_permission("organizer.events:create").exists(),
|
||||
'upcoming': widgets_for_event_qs(
|
||||
request,
|
||||
annotated_event_query(request, lazy=True).filter(
|
||||
|
||||
@@ -72,7 +72,7 @@ def on_control_order_info(sender: Event, request, order: Order, **kwargs):
|
||||
|
||||
|
||||
class ControlSyncJob(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
def post(self, request, provider, *args, **kwargs):
|
||||
prov, meta = datasync_providers.get(active_in=self.request.event, identifier=provider)
|
||||
@@ -154,7 +154,7 @@ class GlobalFailedSyncJobsView(AdministratorPermissionRequiredMixin, FailedSyncJ
|
||||
|
||||
|
||||
class OrganizerFailedSyncJobsView(OrganizerPermissionRequiredMixin, FailedSyncJobsView):
|
||||
permission = "can_change_organizer_settings"
|
||||
permission = "organizer.settings.general:write"
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(
|
||||
@@ -163,7 +163,7 @@ class OrganizerFailedSyncJobsView(OrganizerPermissionRequiredMixin, FailedSyncJo
|
||||
|
||||
|
||||
class EventFailedSyncJobsView(EventPermissionRequiredMixin, FailedSyncJobsView):
|
||||
permission = "can_change_event_settings"
|
||||
permission = "event.settings.general:write"
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(
|
||||
|
||||
@@ -50,7 +50,7 @@ from . import CreateView, PaginationMixin, UpdateView
|
||||
class DiscountDelete(EventPermissionRequiredMixin, CompatDeleteView):
|
||||
model = Discount
|
||||
template_name = 'pretixcontrol/items/discount_delete.html'
|
||||
permission = 'can_change_items'
|
||||
permission = 'event.items:write'
|
||||
context_object_name = 'discount'
|
||||
|
||||
def get_context_data(self, *args, **kwargs) -> dict:
|
||||
@@ -96,7 +96,7 @@ class DiscountUpdate(EventPermissionRequiredMixin, UpdateView):
|
||||
model = Discount
|
||||
form_class = DiscountForm
|
||||
template_name = 'pretixcontrol/items/discount.html'
|
||||
permission = 'can_change_items'
|
||||
permission = 'event.items:write'
|
||||
context_object_name = 'discount'
|
||||
|
||||
def get_object(self, queryset=None) -> Discount:
|
||||
@@ -139,7 +139,7 @@ class DiscountCreate(EventPermissionRequiredMixin, CreateView):
|
||||
model = Discount
|
||||
form_class = DiscountForm
|
||||
template_name = 'pretixcontrol/items/discount.html'
|
||||
permission = 'can_change_items'
|
||||
permission = 'event.items:write'
|
||||
context_object_name = 'discount'
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
@@ -227,7 +227,7 @@ def discount_move(request, discount, up=True):
|
||||
messages.success(request, _('The order of discounts has been updated.'))
|
||||
|
||||
|
||||
@event_permission_required("can_change_items")
|
||||
@event_permission_required("event.items:write")
|
||||
@require_http_methods(["POST"])
|
||||
def discount_move_up(request, organizer, event, discount):
|
||||
discount_move(request, discount, up=True)
|
||||
@@ -236,7 +236,7 @@ def discount_move_up(request, organizer, event, discount):
|
||||
event=request.event.slug)
|
||||
|
||||
|
||||
@event_permission_required("can_change_items")
|
||||
@event_permission_required("event.items:write")
|
||||
@require_http_methods(["POST"])
|
||||
def discount_move_down(request, organizer, event, discount):
|
||||
discount_move(request, discount, up=False)
|
||||
@@ -246,7 +246,7 @@ def discount_move_down(request, organizer, event, discount):
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
@event_permission_required("can_change_items")
|
||||
@event_permission_required("event.items:write")
|
||||
@require_http_methods(["POST"])
|
||||
def reorder_discounts(request, organizer, event):
|
||||
try:
|
||||
|
||||
@@ -106,6 +106,7 @@ from ...base.i18n import language
|
||||
from ...base.models.items import (
|
||||
Item, ItemCategory, ItemMetaProperty, Question, Quota,
|
||||
)
|
||||
from ...base.permissions import AnyPermissionOf
|
||||
from ...base.plugins import (
|
||||
PLUGIN_LEVEL_EVENT, PLUGIN_LEVEL_EVENT_ORGANIZER_HYBRID,
|
||||
PLUGIN_LEVEL_ORGANIZER,
|
||||
@@ -155,7 +156,7 @@ class MetaDataEditorMixin:
|
||||
property=p,
|
||||
disabled=(
|
||||
p.protected and
|
||||
not self.request.user.has_organizer_permission(self.request.organizer, 'can_change_organizer_settings', request=self.request)
|
||||
not self.request.user.has_organizer_permission(self.request.organizer, 'organizer.settings.general:write', request=self.request)
|
||||
),
|
||||
instance=val_instances.get(p.pk, self.meta_model(property=p, event=self.object)),
|
||||
data=(self.request.POST if self.request.method == "POST" else None)
|
||||
@@ -187,7 +188,7 @@ class EventUpdate(DecoupleMixin, EventSettingsViewMixin, EventPermissionRequired
|
||||
model = Event
|
||||
form_class = EventUpdateForm
|
||||
template_name = 'pretixcontrol/event/settings.html'
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
|
||||
@cached_property
|
||||
def object(self) -> Event:
|
||||
@@ -346,7 +347,7 @@ class EventUpdate(DecoupleMixin, EventSettingsViewMixin, EventPermissionRequired
|
||||
class EventPlugins(EventSettingsViewMixin, EventPermissionRequiredMixin, TemplateView, SingleObjectMixin):
|
||||
model = Event
|
||||
context_object_name = 'event'
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
template_name = 'pretixcontrol/event/plugins.html'
|
||||
|
||||
def get_object(self, queryset=None) -> Event:
|
||||
@@ -447,15 +448,14 @@ class EventPlugins(EventSettingsViewMixin, EventPermissionRequiredMixin, Templat
|
||||
continue
|
||||
|
||||
if getattr(pluginmeta, 'level', PLUGIN_LEVEL_EVENT) == PLUGIN_LEVEL_EVENT_ORGANIZER_HYBRID:
|
||||
if not request.user.has_organizer_permission(request.organizer, "can_change_organizer_settings", request):
|
||||
messages.error(
|
||||
request,
|
||||
_("You do not have sufficient permission to enable plugins that need to be enabled "
|
||||
"for the entire organizer account.")
|
||||
)
|
||||
continue
|
||||
|
||||
if module not in self.object.organizer.get_plugins():
|
||||
if not request.user.has_organizer_permission(request.organizer, "organizer.settings.general:write", request):
|
||||
messages.error(
|
||||
request,
|
||||
_("You do not have sufficient permission to enable plugins that need to be enabled "
|
||||
"for the entire organizer account.")
|
||||
)
|
||||
continue
|
||||
self.object.organizer.log_action('pretix.organizer.plugins.enabled', user=self.request.user,
|
||||
data={'plugin': module})
|
||||
self.object.organizer.enable_plugin(module, allow_restricted=request.event.settings.allowed_restricted_plugins)
|
||||
@@ -502,7 +502,7 @@ class EventPlugins(EventSettingsViewMixin, EventPermissionRequiredMixin, Templat
|
||||
class PaymentProviderSettings(EventSettingsViewMixin, EventPermissionRequiredMixin, TemplateView, SingleObjectMixin):
|
||||
model = Event
|
||||
context_object_name = 'event'
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.payment:write'
|
||||
template_name = 'pretixcontrol/event/payment_provider.html'
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
@@ -581,7 +581,7 @@ class PaymentProviderSettings(EventSettingsViewMixin, EventPermissionRequiredMix
|
||||
|
||||
class EventSettingsFormView(EventPermissionRequiredMixin, DecoupleMixin, FormView):
|
||||
model = Event
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
|
||||
def get_context_data(self, *args, **kwargs) -> dict:
|
||||
context = super().get_context_data(*args, **kwargs)
|
||||
@@ -618,10 +618,28 @@ class EventSettingsFormView(EventPermissionRequiredMixin, DecoupleMixin, FormVie
|
||||
return self.render_to_response(self.get_context_data(form=form))
|
||||
|
||||
|
||||
class PaymentSettings(EventSettingsViewMixin, EventSettingsFormView):
|
||||
class WritePermissionMixin:
|
||||
def post(self, request, *args, **kwargs):
|
||||
# Special case, we want to allow different access for read and write
|
||||
if not request.user.has_event_permission(request.organizer, request.event, self.write_permission,
|
||||
request=request):
|
||||
raise PermissionDenied()
|
||||
return super().post(request, *args, **kwargs)
|
||||
|
||||
def get_form(self, *args, **kwargs):
|
||||
form = super().get_form(*args, **kwargs)
|
||||
if not self.request.user.has_event_permission(
|
||||
self.request.organizer, self.request.event, self.write_permission, request=self.request):
|
||||
for f in form.fields.values():
|
||||
f.disabled = True
|
||||
return form
|
||||
|
||||
|
||||
class PaymentSettings(WritePermissionMixin, EventSettingsViewMixin, EventSettingsFormView):
|
||||
template_name = 'pretixcontrol/event/payment.html'
|
||||
form_class = PaymentSettingsForm
|
||||
permission = 'can_change_event_settings'
|
||||
permission = AnyPermissionOf('event.settings.payment:write', 'event.settings.general:write')
|
||||
write_permission = 'event.settings.payment:write'
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
return reverse('control:event.settings.payment', kwargs={
|
||||
@@ -647,10 +665,11 @@ class PaymentSettings(EventSettingsViewMixin, EventSettingsFormView):
|
||||
return context
|
||||
|
||||
|
||||
class TaxSettings(EventSettingsViewMixin, EventSettingsFormView):
|
||||
class TaxSettings(WritePermissionMixin, EventSettingsViewMixin, EventSettingsFormView):
|
||||
template_name = 'pretixcontrol/event/tax.html'
|
||||
form_class = TaxSettingsForm
|
||||
permission = 'can_change_event_settings'
|
||||
permission = AnyPermissionOf('event.settings.tax:write', 'event.settings.general:write')
|
||||
write_permission = 'event.settings.tax:write'
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
return reverse('control:event.settings.tax', kwargs={
|
||||
@@ -666,11 +685,12 @@ class TaxSettings(EventSettingsViewMixin, EventSettingsFormView):
|
||||
return context
|
||||
|
||||
|
||||
class InvoiceSettings(EventSettingsViewMixin, EventSettingsFormView):
|
||||
class InvoiceSettings(WritePermissionMixin, EventSettingsViewMixin, EventSettingsFormView):
|
||||
model = Event
|
||||
form_class = InvoiceSettingsForm
|
||||
template_name = 'pretixcontrol/event/invoicing.html'
|
||||
permission = 'can_change_event_settings'
|
||||
permission = AnyPermissionOf('event.settings.invoicing:write', 'event.settings.general:write')
|
||||
write_permission = 'event.settings.invoicing:write'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
types = get_transmission_types()
|
||||
@@ -704,7 +724,7 @@ class CancelSettings(EventSettingsViewMixin, EventSettingsFormView):
|
||||
model = Event
|
||||
form_class = CancelSettingsForm
|
||||
template_name = 'pretixcontrol/event/cancel.html'
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
return reverse('control:event.settings.cancel', kwargs={
|
||||
@@ -738,7 +758,7 @@ class CancelSettings(EventSettingsViewMixin, EventSettingsFormView):
|
||||
|
||||
|
||||
class InvoicePreview(EventPermissionRequiredMixin, View):
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.invoicing:write'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
fname, ftype, fcontent = build_preview_invoice_pdf(request.event)
|
||||
@@ -753,7 +773,7 @@ class InvoicePreview(EventPermissionRequiredMixin, View):
|
||||
|
||||
|
||||
class DangerZone(EventPermissionRequiredMixin, TemplateView):
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
template_name = 'pretixcontrol/event/dangerzone.html'
|
||||
|
||||
|
||||
@@ -769,7 +789,7 @@ class MailSettings(EventSettingsViewMixin, EventSettingsFormView):
|
||||
model = Event
|
||||
form_class = MailSettingsForm
|
||||
template_name = 'pretixcontrol/event/mail.html'
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
return reverse('control:event.settings.mail', kwargs={
|
||||
@@ -801,7 +821,7 @@ class MailSettings(EventSettingsViewMixin, EventSettingsFormView):
|
||||
|
||||
|
||||
class MailSettingsSetup(EventPermissionRequiredMixin, MailSettingsSetupView):
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
basetpl = 'pretixcontrol/event/base.html'
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
@@ -817,7 +837,7 @@ class MailSettingsSetup(EventPermissionRequiredMixin, MailSettingsSetupView):
|
||||
|
||||
|
||||
class MailSettingsPreview(EventPermissionRequiredMixin, View):
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
|
||||
# create index-language mapping
|
||||
@cached_property
|
||||
@@ -887,7 +907,7 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
|
||||
|
||||
|
||||
class MailSettingsRendererPreview(MailSettingsPreview):
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
return HttpResponse(status=405)
|
||||
@@ -935,7 +955,7 @@ class MailSettingsRendererPreview(MailSettingsPreview):
|
||||
|
||||
|
||||
class TicketSettingsPreview(EventPermissionRequiredMixin, View):
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
|
||||
@cached_property
|
||||
def output(self):
|
||||
@@ -967,7 +987,7 @@ class TicketSettings(EventSettingsViewMixin, EventPermissionRequiredMixin, FormV
|
||||
model = Event
|
||||
form_class = TicketSettingsForm
|
||||
template_name = 'pretixcontrol/event/tickets.html'
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
|
||||
def get_context_data(self, *args, **kwargs) -> dict:
|
||||
context = super().get_context_data(*args, **kwargs)
|
||||
@@ -1078,7 +1098,7 @@ class EventPermissions(EventSettingsViewMixin, EventPermissionRequiredMixin, Tem
|
||||
|
||||
|
||||
class EventLive(EventPermissionRequiredMixin, TemplateView):
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
template_name = 'pretixcontrol/event/live.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
@@ -1145,12 +1165,12 @@ class EventLive(EventPermissionRequiredMixin, TemplateView):
|
||||
|
||||
|
||||
class EventTransferSession(EventPermissionRequiredMixin, TemplateView):
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
template_name = 'pretixcontrol/event/transfer_session.html'
|
||||
|
||||
|
||||
class EventDelete(RecentAuthenticationRequiredMixin, EventPermissionRequiredMixin, FormView):
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
template_name = 'pretixcontrol/event/delete.html'
|
||||
form_class = EventDeleteForm
|
||||
|
||||
@@ -1218,20 +1238,20 @@ class EventLog(EventPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
'user', 'content_type', 'api_token', 'oauth_application', 'device'
|
||||
).order_by('-datetime', '-pk')
|
||||
qs = qs.exclude(action_type__in=OVERVIEW_BANLIST)
|
||||
if not self.request.user.has_event_permission(self.request.organizer, self.request.event, 'can_view_orders',
|
||||
if not self.request.user.has_event_permission(self.request.organizer, self.request.event, 'event.orders:read',
|
||||
request=self.request):
|
||||
qs = qs.exclude(content_type=ContentType.objects.get_for_model(Order))
|
||||
if not self.request.user.has_event_permission(self.request.organizer, self.request.event, 'can_view_vouchers',
|
||||
if not self.request.user.has_event_permission(self.request.organizer, self.request.event, 'event.vouchers:read',
|
||||
request=self.request):
|
||||
qs = qs.exclude(content_type=ContentType.objects.get_for_model(Voucher))
|
||||
if not self.request.user.has_event_permission(self.request.organizer, self.request.event,
|
||||
'can_change_event_settings', request=self.request):
|
||||
'event.settings.general:write', request=self.request):
|
||||
allowed_types = [
|
||||
ContentType.objects.get_for_model(Voucher),
|
||||
ContentType.objects.get_for_model(Order)
|
||||
]
|
||||
if self.request.user.has_event_permission(self.request.organizer, self.request.event,
|
||||
'can_change_items', request=self.request):
|
||||
'event.items:write', request=self.request):
|
||||
allowed_types += [
|
||||
ContentType.objects.get_for_model(Item),
|
||||
ContentType.objects.get_for_model(ItemCategory),
|
||||
@@ -1268,7 +1288,7 @@ class EventLog(EventPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
|
||||
|
||||
class EventComment(EventPermissionRequiredMixin, View):
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
form = CommentForm(self.request.POST)
|
||||
@@ -1297,7 +1317,7 @@ class TaxCreate(EventSettingsViewMixin, EventPermissionRequiredMixin, CreateView
|
||||
model = TaxRule
|
||||
form_class = TaxRuleForm
|
||||
template_name = 'pretixcontrol/event/tax_edit.html'
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.tax:write'
|
||||
context_object_name = 'taxrule'
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
@@ -1358,7 +1378,7 @@ class TaxUpdate(EventSettingsViewMixin, EventPermissionRequiredMixin, UpdateView
|
||||
model = TaxRule
|
||||
form_class = TaxRuleForm
|
||||
template_name = 'pretixcontrol/event/tax_edit.html'
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.tax:write'
|
||||
context_object_name = 'rule'
|
||||
|
||||
def get_object(self, queryset=None) -> TaxRule:
|
||||
@@ -1422,7 +1442,7 @@ class TaxUpdate(EventSettingsViewMixin, EventPermissionRequiredMixin, UpdateView
|
||||
|
||||
class TaxDefault(EventSettingsViewMixin, EventPermissionRequiredMixin, DetailView):
|
||||
model = TaxRule
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.tax:write'
|
||||
|
||||
def get_object(self, queryset=None) -> TaxRule:
|
||||
try:
|
||||
@@ -1467,7 +1487,7 @@ class TaxDefault(EventSettingsViewMixin, EventPermissionRequiredMixin, DetailVie
|
||||
class TaxDelete(EventSettingsViewMixin, EventPermissionRequiredMixin, CompatDeleteView):
|
||||
model = TaxRule
|
||||
template_name = 'pretixcontrol/event/tax_delete.html'
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.tax:write'
|
||||
context_object_name = 'taxrule'
|
||||
|
||||
def get_object(self, queryset=None) -> TaxRule:
|
||||
@@ -1504,7 +1524,7 @@ class TaxDelete(EventSettingsViewMixin, EventPermissionRequiredMixin, CompatDele
|
||||
|
||||
class WidgetSettings(EventSettingsViewMixin, EventPermissionRequiredMixin, FormView):
|
||||
template_name = 'pretixcontrol/event/widget.html'
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
form_class = WidgetCodeForm
|
||||
|
||||
def get_form_kwargs(self):
|
||||
@@ -1533,7 +1553,7 @@ class WidgetSettings(EventSettingsViewMixin, EventPermissionRequiredMixin, FormV
|
||||
|
||||
class QuickSetupView(FormView):
|
||||
template_name = 'pretixcontrol/event/quick_setup.html'
|
||||
permission = 'can_change_event_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
form_class = QuickSetupForm
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
|
||||
@@ -159,7 +159,7 @@ def item_move(request, item, up=True):
|
||||
messages.success(request, _('The order of items has been updated.'))
|
||||
|
||||
|
||||
@event_permission_required("can_change_items")
|
||||
@event_permission_required("event.items:write")
|
||||
@require_http_methods(["POST"])
|
||||
def item_move_up(request, organizer, event, item):
|
||||
item_move(request, item, up=True)
|
||||
@@ -168,7 +168,7 @@ def item_move_up(request, organizer, event, item):
|
||||
event=request.event.slug)
|
||||
|
||||
|
||||
@event_permission_required("can_change_items")
|
||||
@event_permission_required("event.items:write")
|
||||
@require_http_methods(["POST"])
|
||||
def item_move_down(request, organizer, event, item):
|
||||
item_move(request, item, up=False)
|
||||
@@ -178,7 +178,7 @@ def item_move_down(request, organizer, event, item):
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
@event_permission_required("can_change_items")
|
||||
@event_permission_required("event.items:write")
|
||||
@require_http_methods(["POST"])
|
||||
def reorder_items(request, organizer, event, category):
|
||||
try:
|
||||
@@ -215,7 +215,7 @@ def reorder_items(request, organizer, event, category):
|
||||
class CategoryDelete(EventPermissionRequiredMixin, CompatDeleteView):
|
||||
model = ItemCategory
|
||||
template_name = 'pretixcontrol/items/category_delete.html'
|
||||
permission = 'can_change_items'
|
||||
permission = 'event.items:write'
|
||||
context_object_name = 'category'
|
||||
|
||||
def get_object(self, queryset=None) -> ItemCategory:
|
||||
@@ -249,7 +249,7 @@ class CategoryUpdate(EventPermissionRequiredMixin, UpdateView):
|
||||
model = ItemCategory
|
||||
form_class = CategoryForm
|
||||
template_name = 'pretixcontrol/items/category.html'
|
||||
permission = 'can_change_items'
|
||||
permission = 'event.items:write'
|
||||
context_object_name = 'category'
|
||||
|
||||
def get_object(self, queryset=None) -> ItemCategory:
|
||||
@@ -287,7 +287,7 @@ class CategoryCreate(EventPermissionRequiredMixin, CreateView):
|
||||
model = ItemCategory
|
||||
form_class = CategoryForm
|
||||
template_name = 'pretixcontrol/items/category.html'
|
||||
permission = 'can_change_items'
|
||||
permission = 'event.items:write'
|
||||
context_object_name = 'category'
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
@@ -371,7 +371,7 @@ def category_move(request, category, up=True):
|
||||
messages.success(request, _('The order of categories has been updated.'))
|
||||
|
||||
|
||||
@event_permission_required("can_change_items")
|
||||
@event_permission_required("event.items:write")
|
||||
@require_http_methods(["POST"])
|
||||
def category_move_up(request, organizer, event, category):
|
||||
category_move(request, category, up=True)
|
||||
@@ -380,7 +380,7 @@ def category_move_up(request, organizer, event, category):
|
||||
event=request.event.slug)
|
||||
|
||||
|
||||
@event_permission_required("can_change_items")
|
||||
@event_permission_required("event.items:write")
|
||||
@require_http_methods(["POST"])
|
||||
def category_move_down(request, organizer, event, category):
|
||||
category_move(request, category, up=False)
|
||||
@@ -390,7 +390,7 @@ def category_move_down(request, organizer, event, category):
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
@event_permission_required("can_change_items")
|
||||
@event_permission_required("event.items:write")
|
||||
@require_http_methods(["POST"])
|
||||
def reorder_categories(request, organizer, event):
|
||||
try:
|
||||
@@ -522,7 +522,7 @@ class QuestionList(ListView):
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
@event_permission_required("can_change_items")
|
||||
@event_permission_required("event.items:write")
|
||||
@require_http_methods(["POST"])
|
||||
def reorder_questions(request, organizer, event):
|
||||
try:
|
||||
@@ -570,7 +570,7 @@ def reorder_questions(request, organizer, event):
|
||||
class QuestionDelete(EventPermissionRequiredMixin, CompatDeleteView):
|
||||
model = Question
|
||||
template_name = 'pretixcontrol/items/question_delete.html'
|
||||
permission = 'can_change_items'
|
||||
permission = 'event.items:write'
|
||||
context_object_name = 'question'
|
||||
|
||||
def get_object(self, queryset=None) -> Question:
|
||||
@@ -664,7 +664,7 @@ class QuestionMixin:
|
||||
class QuestionView(EventPermissionRequiredMixin, ChartContainingView, DetailView):
|
||||
model = Question
|
||||
template_name = 'pretixcontrol/items/question.html'
|
||||
permission = 'can_change_items'
|
||||
permission = None
|
||||
template_name_field = 'question'
|
||||
|
||||
@cached_property
|
||||
@@ -753,7 +753,7 @@ class QuestionUpdate(EventPermissionRequiredMixin, QuestionMixin, UpdateView):
|
||||
model = Question
|
||||
form_class = QuestionForm
|
||||
template_name = 'pretixcontrol/items/question_edit.html'
|
||||
permission = 'can_change_items'
|
||||
permission = 'event.items:write'
|
||||
context_object_name = 'question'
|
||||
|
||||
def get_object(self, queryset=None) -> Question:
|
||||
@@ -794,7 +794,7 @@ class QuestionCreate(EventPermissionRequiredMixin, QuestionMixin, CreateView):
|
||||
model = Question
|
||||
form_class = QuestionForm
|
||||
template_name = 'pretixcontrol/items/question_edit.html'
|
||||
permission = 'can_change_items'
|
||||
permission = 'event.items:write'
|
||||
context_object_name = 'question'
|
||||
|
||||
def get_form_kwargs(self):
|
||||
@@ -888,7 +888,7 @@ class QuotaCreate(EventPermissionRequiredMixin, CreateView):
|
||||
model = Quota
|
||||
form_class = QuotaForm
|
||||
template_name = 'pretixcontrol/items/quota_edit.html'
|
||||
permission = 'can_change_items'
|
||||
permission = 'event.items:write'
|
||||
context_object_name = 'quota'
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
@@ -1055,7 +1055,7 @@ class QuotaView(ChartContainingView, DetailView):
|
||||
raise Http404(_("The requested quota does not exist."))
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
if not request.user.has_event_permission(request.organizer, request.event, 'can_change_items', request):
|
||||
if not request.user.has_event_permission(request.organizer, request.event, 'event.items:write', request):
|
||||
raise PermissionDenied()
|
||||
quota = self.get_object()
|
||||
if 'reopen' in request.POST:
|
||||
@@ -1085,7 +1085,7 @@ class QuotaUpdate(EventPermissionRequiredMixin, UpdateView):
|
||||
model = Quota
|
||||
form_class = QuotaForm
|
||||
template_name = 'pretixcontrol/items/quota_edit.html'
|
||||
permission = 'can_change_items'
|
||||
permission = 'event.items:write'
|
||||
context_object_name = 'quota'
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
@@ -1143,7 +1143,7 @@ class QuotaUpdate(EventPermissionRequiredMixin, UpdateView):
|
||||
class QuotaDelete(EventPermissionRequiredMixin, CompatDeleteView):
|
||||
model = Quota
|
||||
template_name = 'pretixcontrol/items/quota_delete.html'
|
||||
permission = 'can_change_items'
|
||||
permission = 'event.items:write'
|
||||
context_object_name = 'quota'
|
||||
|
||||
def get_object(self, queryset=None) -> Quota:
|
||||
@@ -1246,7 +1246,7 @@ class MetaDataEditorMixin:
|
||||
class ItemCreate(EventPermissionRequiredMixin, MetaDataEditorMixin, CreateView):
|
||||
form_class = ItemCreateForm
|
||||
template_name = 'pretixcontrol/item/create.html'
|
||||
permission = 'can_change_items'
|
||||
permission = 'event.items:write'
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
return reverse('control:event.item', kwargs={
|
||||
@@ -1322,7 +1322,7 @@ class ItemCreate(EventPermissionRequiredMixin, MetaDataEditorMixin, CreateView):
|
||||
class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, MetaDataEditorMixin, UpdateView):
|
||||
form_class = ItemUpdateForm
|
||||
template_name = 'pretixcontrol/item/index.html'
|
||||
permission = 'can_change_items'
|
||||
permission = 'event.items:write'
|
||||
|
||||
@cached_property
|
||||
def plugin_forms(self):
|
||||
@@ -1584,7 +1584,7 @@ class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, MetaDataE
|
||||
class ItemDelete(EventPermissionRequiredMixin, CompatDeleteView):
|
||||
model = Item
|
||||
template_name = 'pretixcontrol/item/delete.html'
|
||||
permission = 'can_change_items'
|
||||
permission = 'event.items:write'
|
||||
context_object_name = 'item'
|
||||
|
||||
def get_context_data(self, *args, **kwargs) -> dict:
|
||||
|
||||
@@ -86,7 +86,7 @@ class OutgoingMailListView(OutgoingMailQueryMixin, OrganizerDetailViewMixin, Org
|
||||
template_name = 'pretixcontrol/organizers/outgoing_mails.html'
|
||||
# Assume "the highest" permission level for now because emails could belog to any event, order, or customer.
|
||||
# We plan to add a special permissoin in the future
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.outgoingmails:read'
|
||||
context_object_name = 'mails'
|
||||
paginate_by = 100
|
||||
|
||||
@@ -100,7 +100,7 @@ class OutgoingMailListView(OutgoingMailQueryMixin, OrganizerDetailViewMixin, Org
|
||||
class OutgoingMailDetailView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, DetailView):
|
||||
model = OutgoingMail
|
||||
template_name = 'pretixcontrol/organizers/outgoing_mail.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.outgoingmails:read'
|
||||
context_object_name = 'mail'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
@@ -136,7 +136,7 @@ class OutgoingMailDetailView(OrganizerDetailViewMixin, OrganizerPermissionRequir
|
||||
|
||||
|
||||
class OutgoingMailBulkAction(OutgoingMailQueryMixin, OrganizerPermissionRequiredMixin, OrganizerDetailViewMixin, View):
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.outgoingmails:read'
|
||||
|
||||
@transaction.atomic
|
||||
def post(self, request, *args, **kwargs):
|
||||
|
||||
@@ -51,6 +51,7 @@ from i18nfield.strings import LazyI18nString
|
||||
from pretix.base.forms import SafeSessionWizardView
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import Event, EventMetaValue, Organizer, Quota, Team
|
||||
from pretix.base.models.organizer import TeamQuerySet
|
||||
from pretix.base.services.quotas import QuotaAvailability
|
||||
from pretix.control.forms.event import (
|
||||
EventWizardBasicsForm, EventWizardCopyForm, EventWizardFoundationForm,
|
||||
@@ -195,7 +196,9 @@ class EventWizard(SafeSessionWizardView):
|
||||
qs = Organizer.objects.all()
|
||||
if not self.request.user.has_active_staff_session(self.request.session.session_key):
|
||||
qs = qs.filter(
|
||||
id__in=self.request.user.teams.filter(can_create_events=True).values_list('organizer', flat=True)
|
||||
id__in=self.request.user.teams.filter(
|
||||
TeamQuerySet.organizer_permission_q("organizer.events:create"),
|
||||
).values_list('organizer', flat=True)
|
||||
)
|
||||
organizer = qs.get(slug=self.request.GET.get('organizer'))
|
||||
initial['organizer'] = organizer
|
||||
@@ -213,12 +216,7 @@ class EventWizard(SafeSessionWizardView):
|
||||
except Event.DoesNotExist:
|
||||
allow = False
|
||||
else:
|
||||
allow = (
|
||||
request.user.has_event_permission(clone_from.organizer, clone_from,
|
||||
'can_change_event_settings', request)
|
||||
and request.user.has_event_permission(clone_from.organizer, clone_from,
|
||||
'can_change_items', request)
|
||||
)
|
||||
allow = request.user.has_event_permission(clone_from.organizer, clone_from, None, request)
|
||||
if not allow:
|
||||
messages.error(self.request, _('You do not have permission to clone this event.'))
|
||||
else:
|
||||
@@ -227,7 +225,7 @@ class EventWizard(SafeSessionWizardView):
|
||||
|
||||
def get_context_data(self, form, **kwargs):
|
||||
ctx = super().get_context_data(form, **kwargs)
|
||||
ctx['has_organizer'] = self.request.user.teams.filter(can_create_events=True).exists()
|
||||
ctx['has_organizer'] = self.request.user.teams.filter(TeamQuerySet.organizer_permission_q("organizer.events:create")).exists()
|
||||
if self.steps.current == 'basics':
|
||||
ctx['organizer'] = self.get_cleaned_data_for_step('foundation').get('organizer')
|
||||
return ctx
|
||||
@@ -244,6 +242,7 @@ class EventWizard(SafeSessionWizardView):
|
||||
kwargs = {
|
||||
'user': self.request.user,
|
||||
'session': self.request.session,
|
||||
'clone_from': self.clone_from,
|
||||
}
|
||||
if step != 'foundation':
|
||||
fdata = self.get_cleaned_data_for_step('foundation')
|
||||
@@ -255,6 +254,13 @@ class EventWizard(SafeSessionWizardView):
|
||||
}
|
||||
# The show must go on, we catch this error in render()
|
||||
kwargs.update(fdata)
|
||||
if step == 'copy':
|
||||
bdata = self.get_cleaned_data_for_step('basics')
|
||||
if bdata:
|
||||
bdata = {
|
||||
'team': bdata.get('team'),
|
||||
}
|
||||
kwargs.update(bdata)
|
||||
return kwargs
|
||||
|
||||
def get_template_names(self):
|
||||
@@ -279,31 +285,50 @@ class EventWizard(SafeSessionWizardView):
|
||||
user=self.request.user,
|
||||
)
|
||||
|
||||
if not EventWizardBasicsForm.has_control_rights(self.request.user, event.organizer, self.request.session):
|
||||
if copy_data and copy_data['copy_from_event']:
|
||||
copy_from_event = copy_data['copy_from_event']
|
||||
elif self.clone_from:
|
||||
copy_from_event = self.clone_from
|
||||
else:
|
||||
copy_from_event = None
|
||||
|
||||
if not EventWizardBasicsForm.has_control_rights(
|
||||
self.request.user, event.organizer, self.request.session
|
||||
):
|
||||
if basics_data["team"] is not None:
|
||||
t = basics_data["team"]
|
||||
t.limit_events.add(event)
|
||||
elif event.organizer.settings.event_team_provisioning:
|
||||
# Create a new team for new events with full access, but for copied events with the same access
|
||||
# as the source
|
||||
limit_event_permissions = {}
|
||||
if copy_from_event and copy_from_event.organizer == event.organizer:
|
||||
source_teams = self.request.user._get_teams_for_event(copy_from_event.organizer, copy_from_event)
|
||||
all_event_permissions = any(t.all_event_permissions for t in source_teams)
|
||||
if not all_event_permissions:
|
||||
for t in source_teams:
|
||||
limit_event_permissions.update(t.limit_event_permissions)
|
||||
else:
|
||||
# The cross-organizer case is protected through allow_copy_data
|
||||
all_event_permissions = True
|
||||
|
||||
t = Team.objects.create(
|
||||
organizer=event.organizer,
|
||||
name=_('Team {event}').format(
|
||||
event=str(event.name)[:100] + "…" if len(str(event.name)) > 100 else str(event.name)
|
||||
),
|
||||
can_change_event_settings=True, can_change_items=True,
|
||||
can_view_orders=True, can_change_orders=True, can_view_vouchers=True,
|
||||
can_change_vouchers=True
|
||||
all_organizer_permissions=False,
|
||||
all_event_permissions=all_event_permissions,
|
||||
limit_event_permissions=limit_event_permissions,
|
||||
)
|
||||
t.members.add(self.request.user)
|
||||
t.limit_events.add(event)
|
||||
t.log_action('pretix.team.created', user=self.request.user, data={
|
||||
'_created_by_event_wizard': True,
|
||||
'name': t.name,
|
||||
'can_change_event_settings': True,
|
||||
'can_change_items': True,
|
||||
'can_view_orders': True,
|
||||
'can_change_orders': True,
|
||||
'can_view_vouchers': True,
|
||||
'can_change_vouchers': True,
|
||||
'all_organizer_permissions': False,
|
||||
'all_event_permissions': all_event_permissions,
|
||||
'limit_event_permissions': limit_event_permissions,
|
||||
'limit_events': [event.pk],
|
||||
})
|
||||
|
||||
@@ -314,11 +339,8 @@ class EventWizard(SafeSessionWizardView):
|
||||
})
|
||||
event.log_action('pretix.event.settings', user=self.request.user, data=logdata)
|
||||
|
||||
if copy_data and copy_data['copy_from_event']:
|
||||
from_event = copy_data['copy_from_event']
|
||||
event.copy_data_from(from_event)
|
||||
elif self.clone_from:
|
||||
event.copy_data_from(self.clone_from)
|
||||
if copy_from_event:
|
||||
event.copy_data_from(copy_from_event)
|
||||
else:
|
||||
event.set_active_plugins(settings.PRETIX_PLUGINS_DEFAULT.split(","),
|
||||
allow_restricted=settings.PRETIX_PLUGINS_DEFAULT.split(","))
|
||||
@@ -331,10 +353,8 @@ class EventWizard(SafeSessionWizardView):
|
||||
event.set_defaults()
|
||||
|
||||
if basics_data['tax_rate'] is not None:
|
||||
if self.clone_from:
|
||||
default_tax_rule = self.clone_from.cached_default_tax_rule
|
||||
elif copy_data and copy_data['copy_from_event']:
|
||||
default_tax_rule = from_event.cached_default_tax_rule
|
||||
if copy_from_event:
|
||||
default_tax_rule = copy_from_event.cached_default_tax_rule
|
||||
else:
|
||||
default_tax_rule = None
|
||||
if not default_tax_rule or default_tax_rule.rate != basics_data['tax_rate']:
|
||||
@@ -348,7 +368,7 @@ class EventWizard(SafeSessionWizardView):
|
||||
event.settings.set('locale', basics_data['locale'])
|
||||
event.settings.set('locales', foundation_data['locales'])
|
||||
|
||||
if (copy_data and copy_data['copy_from_event']) or self.clone_from or event.has_subevents:
|
||||
if copy_from_event or event.has_subevents:
|
||||
return redirect(reverse('control:event.settings', kwargs={
|
||||
'organizer': event.organizer.slug,
|
||||
'event': event.slug,
|
||||
|
||||
@@ -214,7 +214,7 @@ class BaseProcessView(AsyncAction, FormView):
|
||||
|
||||
class OrderImportView(EventPermissionRequiredMixin, BaseImportView):
|
||||
template_name = 'pretixcontrol/orders/import_start.html'
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
def get_process_url(self, request, cf, charset):
|
||||
return reverse('control:event.orders.import.process', kwargs={
|
||||
@@ -225,7 +225,7 @@ class OrderImportView(EventPermissionRequiredMixin, BaseImportView):
|
||||
|
||||
|
||||
class OrderProcessView(EventPermissionRequiredMixin, BaseProcessView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
template_name = 'pretixcontrol/orders/import_process.html'
|
||||
form_class = OrdersProcessForm
|
||||
task = import_orders
|
||||
@@ -257,7 +257,7 @@ class OrderProcessView(EventPermissionRequiredMixin, BaseProcessView):
|
||||
|
||||
class VoucherImportView(EventPermissionRequiredMixin, BaseImportView):
|
||||
template_name = 'pretixcontrol/vouchers/import_start.html'
|
||||
permission = 'can_change_vouchers'
|
||||
permission = 'event.vouchers:write'
|
||||
|
||||
def get_process_url(self, request, cf, charset):
|
||||
return reverse('control:event.vouchers.import.process', kwargs={
|
||||
@@ -268,7 +268,7 @@ class VoucherImportView(EventPermissionRequiredMixin, BaseImportView):
|
||||
|
||||
|
||||
class VoucherProcessView(EventPermissionRequiredMixin, BaseProcessView):
|
||||
permission = 'can_change_vouchers'
|
||||
permission = 'event.vouchers:write'
|
||||
template_name = 'pretixcontrol/vouchers/import_process.html'
|
||||
form_class = VouchersProcessForm
|
||||
task = import_vouchers
|
||||
|
||||
@@ -92,7 +92,9 @@ from pretix.base.payment import PaymentException
|
||||
from pretix.base.secrets import assign_ticket_secret
|
||||
from pretix.base.services import tickets
|
||||
from pretix.base.services.cancelevent import cancel_event
|
||||
from pretix.base.services.export import export, scheduled_event_export
|
||||
from pretix.base.services.export import (
|
||||
export, init_event_exporters, scheduled_event_export,
|
||||
)
|
||||
from pretix.base.services.invoices import (
|
||||
generate_cancellation, generate_invoice, invoice_pdf, invoice_pdf_task,
|
||||
invoice_qualified, regenerate_invoice, transmit_invoice,
|
||||
@@ -109,9 +111,7 @@ from pretix.base.services.tax import (
|
||||
VATIDFinalError, VATIDTemporaryError, validate_vat_id,
|
||||
)
|
||||
from pretix.base.services.tickets import generate
|
||||
from pretix.base.signals import (
|
||||
order_modified, register_data_exporters, register_ticket_outputs,
|
||||
)
|
||||
from pretix.base.signals import order_modified, register_ticket_outputs
|
||||
from pretix.base.templatetags.money import money_filter
|
||||
from pretix.base.templatetags.rich_text import markdown_compile_email
|
||||
from pretix.base.views.mixins import OrderQuestionsViewMixin
|
||||
@@ -169,7 +169,7 @@ class OrderSearchMixin:
|
||||
|
||||
class OrderSearch(OrderSearchMixin, EventPermissionRequiredMixin, TemplateView):
|
||||
template_name = 'pretixcontrol/orders/search.html'
|
||||
permission = 'can_view_orders'
|
||||
permission = 'event.orders:read'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
@@ -199,7 +199,7 @@ class OrderSearch(OrderSearchMixin, EventPermissionRequiredMixin, TemplateView):
|
||||
|
||||
class BaseOrderBulkActionView(OrderSearchMixin, EventPermissionRequiredMixin, AsyncFormView):
|
||||
template_name = 'pretixcontrol/orders/bulk_action.html'
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
form_class = forms.Form
|
||||
|
||||
def get_queryset(self):
|
||||
@@ -402,7 +402,7 @@ class OrderList(OrderSearchMixin, EventPermissionRequiredMixin, PaginationMixin,
|
||||
model = Order
|
||||
context_object_name = 'orders'
|
||||
template_name = 'pretixcontrol/orders/index.html'
|
||||
permission = 'can_view_orders'
|
||||
permission = 'event.orders:read'
|
||||
|
||||
def get_queryset(self):
|
||||
qs = Order.objects.filter(
|
||||
@@ -526,7 +526,7 @@ class OrderView(EventPermissionRequiredMixin, DetailView):
|
||||
|
||||
class OrderDetail(OrderView):
|
||||
template_name = 'pretixcontrol/order/index.html'
|
||||
permission = 'can_view_orders'
|
||||
permission = 'event.orders:read'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
@@ -626,7 +626,7 @@ class OrderDetail(OrderView):
|
||||
|
||||
class OrderTransactions(OrderView):
|
||||
template_name = 'pretixcontrol/order/transactions.html'
|
||||
permission = 'can_view_orders'
|
||||
permission = 'event.orders:read'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
@@ -645,7 +645,7 @@ class OrderTransactions(OrderView):
|
||||
|
||||
class OrderDownload(AsyncAction, OrderView):
|
||||
task = generate
|
||||
permission = 'can_view_orders'
|
||||
permission = 'event.orders:read'
|
||||
|
||||
def get_success_url(self, value):
|
||||
return self.get_self_url()
|
||||
@@ -744,7 +744,7 @@ class OrderDownload(AsyncAction, OrderView):
|
||||
|
||||
|
||||
class OrderComment(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
form = CommentForm(self.request.POST)
|
||||
@@ -784,7 +784,7 @@ class OrderComment(OrderView):
|
||||
|
||||
|
||||
class OrderApprove(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
if self.order.require_approval:
|
||||
@@ -803,7 +803,7 @@ class OrderApprove(OrderView):
|
||||
|
||||
|
||||
class OrderDelete(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
if self.order.testmode:
|
||||
@@ -833,7 +833,7 @@ class OrderDelete(OrderView):
|
||||
|
||||
|
||||
class OrderDeny(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
if self.order.require_approval:
|
||||
@@ -859,7 +859,7 @@ class OrderDeny(OrderView):
|
||||
|
||||
|
||||
class OrderPaymentCancel(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
@cached_property
|
||||
def payment(self):
|
||||
@@ -898,7 +898,7 @@ class OrderPaymentCancel(OrderView):
|
||||
|
||||
|
||||
class OrderRefundCancel(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
@cached_property
|
||||
def refund(self):
|
||||
@@ -928,7 +928,7 @@ class OrderRefundCancel(OrderView):
|
||||
|
||||
|
||||
class OrderRefundProcess(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
@cached_property
|
||||
def refund(self):
|
||||
@@ -967,7 +967,7 @@ class OrderRefundProcess(OrderView):
|
||||
|
||||
|
||||
class OrderRefundDone(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
@cached_property
|
||||
def refund(self):
|
||||
@@ -990,7 +990,7 @@ class OrderRefundDone(OrderView):
|
||||
|
||||
|
||||
class OrderCancellationRequestDelete(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
@cached_property
|
||||
def req(self):
|
||||
@@ -1024,7 +1024,7 @@ class OrderCancellationRequestDelete(OrderView):
|
||||
|
||||
|
||||
class OrderPaymentConfirm(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
@cached_property
|
||||
def payment(self):
|
||||
@@ -1078,7 +1078,7 @@ class OrderPaymentConfirm(OrderView):
|
||||
|
||||
|
||||
class OrderRefundView(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
@cached_property
|
||||
def start_form(self):
|
||||
@@ -1427,7 +1427,7 @@ class OrderRefundView(OrderView):
|
||||
|
||||
|
||||
class OrderTransition(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
@cached_property
|
||||
def req(self):
|
||||
@@ -1592,7 +1592,7 @@ class OrderTransition(OrderView):
|
||||
|
||||
|
||||
class OrderInvoiceCreate(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
with transaction.atomic():
|
||||
@@ -1618,7 +1618,7 @@ class OrderInvoiceCreate(OrderView):
|
||||
|
||||
|
||||
class OrderCheckVATID(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
try:
|
||||
@@ -1666,7 +1666,7 @@ class OrderCheckVATID(OrderView):
|
||||
|
||||
|
||||
class OrderInvoiceRegenerate(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
try:
|
||||
@@ -1699,7 +1699,7 @@ class OrderInvoiceRegenerate(OrderView):
|
||||
|
||||
|
||||
class OrderInvoiceRetransmit(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
with transaction.atomic(durable=True):
|
||||
@@ -1730,7 +1730,7 @@ class OrderInvoiceRetransmit(OrderView):
|
||||
|
||||
|
||||
class OrderInvoiceReissue(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
with transaction.atomic():
|
||||
@@ -1781,7 +1781,7 @@ class OrderInvoiceInspect(AdministratorPermissionRequiredMixin, OrderView):
|
||||
|
||||
|
||||
class OrderResendLink(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
if 'position' in kwargs:
|
||||
@@ -1798,7 +1798,7 @@ class OrderResendLink(OrderView):
|
||||
|
||||
|
||||
class InvoiceDownload(EventPermissionRequiredMixin, View):
|
||||
permission = 'can_view_orders'
|
||||
permission = 'event.orders:read'
|
||||
|
||||
def get_order_url(self):
|
||||
return reverse('control:event.order', kwargs={
|
||||
@@ -1842,7 +1842,7 @@ class InvoiceDownload(EventPermissionRequiredMixin, View):
|
||||
|
||||
|
||||
class OrderExtend(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
if self.form.is_valid():
|
||||
@@ -1890,7 +1890,7 @@ class OrderExtend(OrderView):
|
||||
|
||||
|
||||
class OrderReactivate(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
@cached_property
|
||||
def reactivate_form(self):
|
||||
@@ -1940,7 +1940,7 @@ class OrderReactivate(OrderView):
|
||||
|
||||
|
||||
class OrderChange(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
template_name = 'pretixcontrol/order/change.html'
|
||||
|
||||
@cached_property
|
||||
@@ -2197,7 +2197,7 @@ class OrderChange(OrderView):
|
||||
|
||||
|
||||
class OrderModifyInformation(OrderQuestionsViewMixin, OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
template_name = 'pretixcontrol/order/change_questions.html'
|
||||
only_user_visible = False
|
||||
all_optional = True
|
||||
@@ -2250,7 +2250,7 @@ class OrderModifyInformation(OrderQuestionsViewMixin, OrderView):
|
||||
|
||||
|
||||
class OrderContactChange(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
template_name = 'pretixcontrol/order/change_contact.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
@@ -2265,7 +2265,7 @@ class OrderContactChange(OrderView):
|
||||
data=self.request.POST if self.request.method == "POST" else None,
|
||||
customers=self.request.organizer.settings.customer_accounts and (
|
||||
self.request.user.has_organizer_permission(
|
||||
self.request.organizer, 'can_manage_customers', request=self.request
|
||||
self.request.organizer, 'organizer.customers:write', request=self.request
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -2334,7 +2334,7 @@ class OrderContactChange(OrderView):
|
||||
|
||||
|
||||
class OrderLocaleChange(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
template_name = 'pretixcontrol/order/change_locale.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
@@ -2390,7 +2390,7 @@ class OrderViewMixin:
|
||||
|
||||
class OrderSendMail(EventPermissionRequiredMixin, OrderViewMixin, FormView):
|
||||
template_name = 'pretixcontrol/order/sendmail.html'
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
form_class = OrderMailForm
|
||||
|
||||
def get_form_kwargs(self):
|
||||
@@ -2514,7 +2514,7 @@ class OrderPositionSendMail(OrderSendMail):
|
||||
|
||||
class OrderEmailHistory(EventPermissionRequiredMixin, OrderViewMixin, ListView):
|
||||
template_name = 'pretixcontrol/order/mail_history.html'
|
||||
permission = 'can_view_orders'
|
||||
permission = 'event.orders:read'
|
||||
model = LogEntry
|
||||
context_object_name = 'logs'
|
||||
paginate_by = 10
|
||||
@@ -2551,7 +2551,7 @@ class OrderEmailHistory(EventPermissionRequiredMixin, OrderViewMixin, ListView):
|
||||
|
||||
|
||||
class AnswerDownload(EventPermissionRequiredMixin, OrderViewMixin, ListView):
|
||||
permission = 'can_view_orders'
|
||||
permission = 'event.orders:read'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
answid = kwargs.get('answer')
|
||||
@@ -2575,7 +2575,7 @@ class AnswerDownload(EventPermissionRequiredMixin, OrderViewMixin, ListView):
|
||||
|
||||
class OverView(EventPermissionRequiredMixin, TemplateView):
|
||||
template_name = 'pretixcontrol/orders/overview.html'
|
||||
permission = 'can_view_orders'
|
||||
permission = 'event.orders:read'
|
||||
|
||||
@cached_property
|
||||
def filter_form(self):
|
||||
@@ -2614,7 +2614,7 @@ class OverView(EventPermissionRequiredMixin, TemplateView):
|
||||
|
||||
|
||||
class OrderGo(EventPermissionRequiredMixin, View):
|
||||
permission = 'can_view_orders'
|
||||
permission = 'event.orders:read'
|
||||
|
||||
def get_order(self, code):
|
||||
try:
|
||||
@@ -2649,12 +2649,7 @@ class OrderGo(EventPermissionRequiredMixin, View):
|
||||
class ExportMixin:
|
||||
@cached_property
|
||||
def exporters(self):
|
||||
responses = register_data_exporters.send(self.request.event)
|
||||
raw_exporters = [response(self.request.event, self.request.organizer) for r, response in responses if response]
|
||||
raw_exporters = [
|
||||
ex for ex in raw_exporters
|
||||
if ex.available_for_user(self.request.user if self.request.user and self.request.user.is_authenticated else None)
|
||||
]
|
||||
raw_exporters = list(init_event_exporters(self.request.event, user=self.request.user, request=self.request))
|
||||
return sorted(
|
||||
raw_exporters,
|
||||
key=lambda ex: (0 if ex.category else 1, ex.category or "", 0 if ex.featured else 1, str(ex.verbose_name).lower())
|
||||
@@ -2699,7 +2694,7 @@ class ExportMixin:
|
||||
return ex
|
||||
|
||||
def get_scheduled_queryset(self):
|
||||
if not self.request.user.has_event_permission(self.request.organizer, self.request.event, 'can_change_event_settings',
|
||||
if not self.request.user.has_event_permission(self.request.organizer, self.request.event, 'event.settings.general:write',
|
||||
request=self.request):
|
||||
qs = self.request.event.scheduled_exports.filter(owner=self.request.user)
|
||||
else:
|
||||
@@ -2726,7 +2721,7 @@ class ExportMixin:
|
||||
|
||||
|
||||
class ExportDoView(EventPermissionRequiredMixin, ExportMixin, AsyncAction, TemplateView):
|
||||
permission = 'can_view_orders'
|
||||
permission = None
|
||||
known_errortypes = ['ExportError', 'ExportEmptyError']
|
||||
task = export
|
||||
template_name = 'pretixcontrol/orders/export_form.html'
|
||||
@@ -2771,11 +2766,20 @@ class ExportDoView(EventPermissionRequiredMixin, ExportMixin, AsyncAction, Templ
|
||||
cf.date = now()
|
||||
cf.expires = now() + timedelta(hours=24)
|
||||
cf.save()
|
||||
return self.do(self.request.event.id, str(cf.id), self.exporter.identifier, data)
|
||||
return self.do(
|
||||
self.request.event.id,
|
||||
user=self.request.user.id,
|
||||
fileid=str(cf.id),
|
||||
provider=self.exporter.identifier,
|
||||
device=None,
|
||||
token=None,
|
||||
form_data=data,
|
||||
staff_session=self.request.user.has_active_staff_session(self.request.session.session_key)
|
||||
)
|
||||
|
||||
|
||||
class ExportView(EventPermissionRequiredMixin, ExportMixin, ListView):
|
||||
permission = 'can_view_orders'
|
||||
permission = None
|
||||
paginate_by = 25
|
||||
context_object_name = 'scheduled'
|
||||
|
||||
@@ -2787,7 +2791,16 @@ class ExportView(EventPermissionRequiredMixin, ExportMixin, ListView):
|
||||
@transaction.atomic()
|
||||
def post(self, request, *args, **kwargs):
|
||||
if request.POST.get("schedule") == "save":
|
||||
if not self.has_permission():
|
||||
if self.scheduled and self.scheduled.pk and not self.has_permission_to_edit_scheduled():
|
||||
messages.error(
|
||||
self.request,
|
||||
_(
|
||||
"Your user account does not have sufficient permission to run this report, therefore "
|
||||
"you cannot change it."
|
||||
)
|
||||
)
|
||||
return super().get(request, *args, **kwargs)
|
||||
elif (not self.scheduled or not self.scheduled.pk) and not self.has_permission_to_create_scheduled():
|
||||
messages.error(
|
||||
self.request,
|
||||
_(
|
||||
@@ -2875,8 +2888,32 @@ class ExportView(EventPermissionRequiredMixin, ExportMixin, ListView):
|
||||
def get_queryset(self):
|
||||
return self.get_scheduled_queryset()
|
||||
|
||||
def has_permission(self):
|
||||
return self.request.user.has_event_permission(self.request.organizer, self.request.event, "can_view_orders")
|
||||
def has_permission_to_edit_scheduled(self):
|
||||
# Exports can be edited by
|
||||
# - their owner
|
||||
# - any staff session user
|
||||
# - any user with permission for organizer settings *and* the permissions required to run the rport
|
||||
# This is to prevent a possible privilege escalation where user A creates a scheduled export and
|
||||
# user B has settings permission (= they can see the export configuration), but not enough permission
|
||||
# to run the export themselves. Without this check, user B could modify the export and add themselves
|
||||
# as a recipient. Thereby, user B would gain access to data they can't have.
|
||||
if not self.exporter:
|
||||
return False
|
||||
if self.scheduled.owner == self.request.user:
|
||||
return True
|
||||
if self.request.user.has_active_staff_session(self.request.session.session_key):
|
||||
return True
|
||||
if not self.exporter.available_for_user(self.request.user):
|
||||
return False
|
||||
if self.request.user.has_event_permission(self.request.organizer, self.request.event,
|
||||
"event.settings.general:write", request=self.request):
|
||||
return self.request.user.has_event_permission(self.request.organizer, self.request.event,
|
||||
self.exporter.get_required_event_permission())
|
||||
|
||||
def has_permission_to_create_scheduled(self):
|
||||
# Exports can only be created if the user has the correct permissions. We *ignore* staff sessions, because
|
||||
# the export is not *run* during a staff session and then would fail at the scheduled time.
|
||||
return self.request.user.has_event_permission(self.request.organizer, self.request.event, self.exporter.get_required_event_permission())
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
@@ -2885,6 +2922,15 @@ class ExportView(EventPermissionRequiredMixin, ExportMixin, ListView):
|
||||
ctx['schedule_form'] = self.schedule_form
|
||||
ctx['rrule_form'] = self.rrule_form
|
||||
ctx['scheduled_copy_from'] = self.scheduled_copy_from
|
||||
|
||||
if self.scheduled and self.scheduled.pk and not self.has_permission_to_edit_scheduled() and self.exporter:
|
||||
ctx['no_save'] = True
|
||||
for f in self.exporter.form.fields.values():
|
||||
f.disabled = True
|
||||
for f in self.rrule_form.fields.values():
|
||||
f.disabled = True
|
||||
for f in self.schedule_form.fields.values():
|
||||
f.disabled = True
|
||||
elif not self.exporter:
|
||||
for s in ctx['scheduled']:
|
||||
try:
|
||||
@@ -2895,7 +2941,7 @@ class ExportView(EventPermissionRequiredMixin, ExportMixin, ListView):
|
||||
|
||||
|
||||
class DeleteScheduledExportView(EventPermissionRequiredMixin, ExportMixin, CompatDeleteView):
|
||||
permission = 'can_view_orders'
|
||||
permission = None
|
||||
template_name = 'pretixcontrol/orders/export_delete.html'
|
||||
context_object_name = 'export'
|
||||
|
||||
@@ -2944,7 +2990,7 @@ class RefundList(EventPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
model = OrderRefund
|
||||
context_object_name = 'refunds'
|
||||
template_name = 'pretixcontrol/orders/refunds.html'
|
||||
permission = 'can_view_orders'
|
||||
permission = 'event.orders:read'
|
||||
|
||||
def get_queryset(self):
|
||||
qs = OrderRefund.objects.filter(
|
||||
@@ -2969,7 +3015,7 @@ class RefundList(EventPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
|
||||
class EventCancel(EventPermissionRequiredMixin, AsyncAction, FormView):
|
||||
template_name = 'pretixcontrol/orders/cancel.html'
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event:cancel'
|
||||
form_class = EventCancelForm
|
||||
task = cancel_event
|
||||
known_errortypes = ['OrderError']
|
||||
@@ -3054,7 +3100,7 @@ class EventCancel(EventPermissionRequiredMixin, AsyncAction, FormView):
|
||||
|
||||
class EventCancelConfirm(EventPermissionRequiredMixin, AsyncAction, FormView):
|
||||
template_name = 'pretixcontrol/orders/cancel_confirm.html'
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
form_class = EventCancelConfirmForm
|
||||
task = cancel_event
|
||||
known_errortypes = ['OrderError']
|
||||
|
||||
@@ -96,15 +96,18 @@ from pretix.base.models.giftcards import (
|
||||
GiftCardAcceptance, GiftCardTransaction, gen_giftcard_secret,
|
||||
)
|
||||
from pretix.base.models.orders import CancellationRequest
|
||||
from pretix.base.models.organizer import SalesChannel, TeamAPIToken
|
||||
from pretix.base.models.organizer import (
|
||||
SalesChannel, TeamAPIToken, TeamQuerySet,
|
||||
)
|
||||
from pretix.base.payment import PaymentException
|
||||
from pretix.base.plugins import (
|
||||
PLUGIN_LEVEL_EVENT, PLUGIN_LEVEL_EVENT_ORGANIZER_HYBRID,
|
||||
PLUGIN_LEVEL_ORGANIZER,
|
||||
)
|
||||
from pretix.base.services.export import multiexport, scheduled_organizer_export
|
||||
from pretix.base.services.export import (
|
||||
init_organizer_exporters, multiexport, scheduled_organizer_export,
|
||||
)
|
||||
from pretix.base.services.mail import mail, prefix_subject
|
||||
from pretix.base.signals import register_multievent_data_exporters
|
||||
from pretix.base.templatetags.rich_text import markdown_compile_email
|
||||
from pretix.base.views.tasks import AsyncAction
|
||||
from pretix.control.forms.exports import ScheduledOrganizerExportForm
|
||||
@@ -245,13 +248,13 @@ class OrganizerDetail(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin
|
||||
class OrganizerTeamView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, DetailView):
|
||||
model = Organizer
|
||||
template_name = 'pretixcontrol/organizers/teams.html'
|
||||
permission = 'can_change_permissions'
|
||||
permission = 'organizer.teams:write'
|
||||
context_object_name = 'organizer'
|
||||
|
||||
|
||||
class OrganizerSettingsFormView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, FormView):
|
||||
model = Organizer
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
@@ -282,7 +285,7 @@ class OrganizerSettingsFormView(OrganizerDetailViewMixin, OrganizerPermissionReq
|
||||
class OrganizerMailSettings(OrganizerSettingsFormView):
|
||||
form_class = MailSettingsForm
|
||||
template_name = 'pretixcontrol/organizers/mail.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('control:organizer.settings.mail', kwargs={
|
||||
@@ -308,7 +311,7 @@ class OrganizerMailSettings(OrganizerSettingsFormView):
|
||||
|
||||
|
||||
class MailSettingsSetup(OrganizerPermissionRequiredMixin, MailSettingsSetupView):
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
basetpl = 'pretixcontrol/base.html'
|
||||
|
||||
def get_success_url(self):
|
||||
@@ -323,7 +326,7 @@ class MailSettingsSetup(OrganizerPermissionRequiredMixin, MailSettingsSetupView)
|
||||
|
||||
|
||||
class MailSettingsPreview(OrganizerPermissionRequiredMixin, View):
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
|
||||
# return the origin text if key is missing in dict
|
||||
class SafeDict(dict):
|
||||
@@ -455,7 +458,7 @@ class OrganizerUpdate(OrganizerPermissionRequiredMixin, UpdateView):
|
||||
model = Organizer
|
||||
form_class = OrganizerUpdateForm
|
||||
template_name = 'pretixcontrol/organizers/edit.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
context_object_name = 'organizer'
|
||||
|
||||
@cached_property
|
||||
@@ -583,10 +586,7 @@ class OrganizerCreate(CreateView):
|
||||
ret = super().form_valid(form)
|
||||
t = Team.objects.create(
|
||||
organizer=form.instance, name=_('Administrators'),
|
||||
all_events=True, can_create_events=True, can_change_teams=True, can_manage_gift_cards=True,
|
||||
can_change_organizer_settings=True, can_change_event_settings=True, can_change_items=True,
|
||||
can_manage_customers=True, can_manage_reusable_media=True,
|
||||
can_view_orders=True, can_change_orders=True, can_view_vouchers=True, can_change_vouchers=True
|
||||
all_events=True, all_event_permissions=True, all_organizer_permissions=True,
|
||||
)
|
||||
t.members.add(self.request.user)
|
||||
return ret
|
||||
@@ -600,7 +600,7 @@ class OrganizerCreate(CreateView):
|
||||
class OrganizerPlugins(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, TemplateView, SingleObjectMixin):
|
||||
model = Organizer
|
||||
context_object_name = 'organizer'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
template_name = 'pretixcontrol/organizers/plugins.html'
|
||||
|
||||
def get_object(self, queryset=None) -> Organizer:
|
||||
@@ -772,14 +772,14 @@ class OrganizerPlugins(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixi
|
||||
class OrganizerPluginEvents(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, FormView):
|
||||
model = Organizer
|
||||
context_object_name = 'organizer'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
template_name = 'pretixcontrol/organizers/plugin_events.html'
|
||||
form_class = OrganizerPluginEventsForm
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs["events"] = self.request.user.get_events_with_permission(
|
||||
"can_change_event_settings", request=self.request
|
||||
"event.settings.general:write", request=self.request
|
||||
).filter(organizer=self.request.organizer)
|
||||
kwargs["initial"] = {
|
||||
"events": self.request.organizer.events.filter(plugins__regex='(^|,)' + self.plugin.module + '(,|$)')
|
||||
@@ -857,7 +857,7 @@ class OrganizerPluginEvents(OrganizerDetailViewMixin, OrganizerPermissionRequire
|
||||
class TeamListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
model = Team
|
||||
template_name = 'pretixcontrol/organizers/teams.html'
|
||||
permission = 'can_change_teams'
|
||||
permission = 'organizer.teams:write'
|
||||
context_object_name = 'teams'
|
||||
|
||||
def get_queryset(self):
|
||||
@@ -883,7 +883,7 @@ class TeamListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, P
|
||||
class TeamCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CreateView):
|
||||
model = Team
|
||||
template_name = 'pretixcontrol/organizers/team_edit.html'
|
||||
permission = 'can_change_teams'
|
||||
permission = 'organizer.teams:write'
|
||||
form_class = TeamForm
|
||||
|
||||
def get_form_kwargs(self):
|
||||
@@ -906,10 +906,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):
|
||||
@@ -920,7 +917,7 @@ class TeamCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
|
||||
class TeamUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, UpdateView):
|
||||
model = Team
|
||||
template_name = 'pretixcontrol/organizers/team_edit.html'
|
||||
permission = 'can_change_teams'
|
||||
permission = 'organizer.teams:write'
|
||||
context_object_name = 'team'
|
||||
form_class = TeamForm
|
||||
|
||||
@@ -941,10 +938,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)
|
||||
|
||||
@@ -956,7 +950,7 @@ class TeamUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
|
||||
class TeamDeleteView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CompatDeleteView):
|
||||
model = Team
|
||||
template_name = 'pretixcontrol/organizers/team_delete.html'
|
||||
permission = 'can_change_teams'
|
||||
permission = 'organizer.teams:write'
|
||||
context_object_name = 'team'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
@@ -974,7 +968,8 @@ class TeamDeleteView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
|
||||
|
||||
def is_allowed(self) -> bool:
|
||||
return self.request.organizer.teams.exclude(pk=self.kwargs.get('team')).filter(
|
||||
can_change_teams=True, members__isnull=False
|
||||
TeamQuerySet.organizer_permission_q("organizer.teams:write"),
|
||||
members__isnull=False
|
||||
).exists() or self.request.user.has_active_staff_session(self.request.session.session_key)
|
||||
|
||||
@transaction.atomic
|
||||
@@ -1015,7 +1010,7 @@ class TeamDeleteView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
|
||||
class TeamMemberView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, DetailView):
|
||||
template_name = 'pretixcontrol/organizers/team_members.html'
|
||||
context_object_name = 'team'
|
||||
permission = 'can_change_teams'
|
||||
permission = 'organizer.teams:write'
|
||||
model = Team
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
@@ -1067,9 +1062,10 @@ class TeamMemberView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
|
||||
pass
|
||||
else:
|
||||
other_admin_teams = self.request.organizer.teams.exclude(pk=self.object.pk).filter(
|
||||
can_change_teams=True, members__isnull=False
|
||||
TeamQuerySet.organizer_permission_q("organizer.teams:write"),
|
||||
members__isnull=False
|
||||
).exists() or self.request.user.has_active_staff_session(self.request.session.session_key)
|
||||
if not other_admin_teams and self.object.can_change_teams and self.object.members.count() == 1:
|
||||
if not other_admin_teams and self.object.has_organizer_permission("organizer.teams:write") and self.object.members.count() == 1:
|
||||
messages.error(self.request, _('You cannot remove the last member from this team as no one would '
|
||||
'be left with the permission to change teams.'))
|
||||
return redirect(self.get_success_url())
|
||||
@@ -1233,7 +1229,7 @@ class DeviceQueryMixin:
|
||||
class DeviceListView(DeviceQueryMixin, OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, ListView):
|
||||
model = Device
|
||||
template_name = 'pretixcontrol/organizers/devices.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.devices:read'
|
||||
context_object_name = 'devices'
|
||||
paginate_by = 100
|
||||
|
||||
@@ -1246,7 +1242,7 @@ class DeviceListView(DeviceQueryMixin, OrganizerDetailViewMixin, OrganizerPermis
|
||||
class DeviceCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CreateView):
|
||||
model = Device
|
||||
template_name = 'pretixcontrol/organizers/device_edit.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.devices:write'
|
||||
form_class = DeviceForm
|
||||
|
||||
def get_form_kwargs(self):
|
||||
@@ -1277,7 +1273,7 @@ class DeviceCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixi
|
||||
|
||||
class DeviceLogView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, ListView):
|
||||
template_name = 'pretixcontrol/organizers/device_logs.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.devices:read'
|
||||
model = LogEntry
|
||||
context_object_name = 'logs'
|
||||
paginate_by = 20
|
||||
@@ -1305,7 +1301,7 @@ class DeviceLogView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
|
||||
class DeviceUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, UpdateView):
|
||||
model = Device
|
||||
template_name = 'pretixcontrol/organizers/device_edit.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.devices:write'
|
||||
context_object_name = 'device'
|
||||
form_class = DeviceForm
|
||||
|
||||
@@ -1348,7 +1344,7 @@ class DeviceUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixi
|
||||
|
||||
class DeviceBulkUpdateView(DeviceQueryMixin, OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, FormView):
|
||||
template_name = 'pretixcontrol/organizers/device_bulk_edit.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.devices:write'
|
||||
context_object_name = 'device'
|
||||
form_class = DeviceBulkEditForm
|
||||
|
||||
@@ -1462,7 +1458,7 @@ class DeviceBulkUpdateView(DeviceQueryMixin, OrganizerDetailViewMixin, Organizer
|
||||
class DeviceConnectView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, DetailView):
|
||||
model = Device
|
||||
template_name = 'pretixcontrol/organizers/device_connect.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.devices:write'
|
||||
context_object_name = 'device'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
@@ -1494,7 +1490,7 @@ class DeviceConnectView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMix
|
||||
class DeviceRevokeView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, DetailView):
|
||||
model = Device
|
||||
template_name = 'pretixcontrol/organizers/device_revoke.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.devices:write'
|
||||
context_object_name = 'device'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
@@ -1524,7 +1520,7 @@ class DeviceRevokeView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixi
|
||||
class WebHookListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, ListView):
|
||||
model = WebHook
|
||||
template_name = 'pretixcontrol/organizers/webhooks.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
context_object_name = 'webhooks'
|
||||
|
||||
def get_queryset(self):
|
||||
@@ -1534,7 +1530,7 @@ class WebHookListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin
|
||||
class WebHookCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CreateView):
|
||||
model = WebHook
|
||||
template_name = 'pretixcontrol/organizers/webhook_edit.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
form_class = WebHookForm
|
||||
|
||||
def get_form_kwargs(self):
|
||||
@@ -1568,7 +1564,7 @@ class WebHookCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMix
|
||||
class WebHookUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, UpdateView):
|
||||
model = WebHook
|
||||
template_name = 'pretixcontrol/organizers/webhook_edit.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
context_object_name = 'webhook'
|
||||
form_class = WebHookForm
|
||||
|
||||
@@ -1611,7 +1607,7 @@ class WebHookUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMix
|
||||
class WebHookLogsView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, ListView):
|
||||
model = WebHook
|
||||
template_name = 'pretixcontrol/organizers/webhook_logs.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
context_object_name = 'calls'
|
||||
paginate_by = 50
|
||||
|
||||
@@ -1653,7 +1649,7 @@ class WebHookLogsView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin
|
||||
class GiftCardAcceptanceInviteView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, FormView):
|
||||
model = GiftCardAcceptance
|
||||
template_name = 'pretixcontrol/organizers/giftcard_acceptance_invite.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
form_class = GiftCardAcceptanceInviteForm
|
||||
|
||||
def get_form_kwargs(self):
|
||||
@@ -1686,7 +1682,7 @@ class GiftCardAcceptanceInviteView(OrganizerDetailViewMixin, OrganizerPermission
|
||||
class GiftCardAcceptanceListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, ListView):
|
||||
model = GiftCardAcceptance
|
||||
template_name = 'pretixcontrol/organizers/giftcard_acceptance_list.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
context_object_name = 'acceptor_acceptance'
|
||||
paginate_by = 50
|
||||
|
||||
@@ -1755,7 +1751,7 @@ class GiftCardAcceptanceListView(OrganizerDetailViewMixin, OrganizerPermissionRe
|
||||
class GiftCardListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, ListView):
|
||||
model = GiftCard
|
||||
template_name = 'pretixcontrol/organizers/giftcards.html'
|
||||
permission = 'can_manage_gift_cards'
|
||||
permission = 'organizer.giftcards:read'
|
||||
context_object_name = 'giftcards'
|
||||
paginate_by = 50
|
||||
|
||||
@@ -1778,7 +1774,7 @@ class GiftCardListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixi
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['filter_form'] = self.filter_form
|
||||
ctx['other_organizers'] = self.request.user.get_organizers_with_permission(
|
||||
'can_manage_gift_cards', self.request
|
||||
'organizer.giftcards:write', self.request
|
||||
).exclude(pk=self.request.organizer.pk)
|
||||
return ctx
|
||||
|
||||
@@ -1789,7 +1785,7 @@ class GiftCardListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixi
|
||||
|
||||
class GiftCardDetailView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, DetailView):
|
||||
template_name = 'pretixcontrol/organizers/giftcard.html'
|
||||
permission = 'can_manage_gift_cards'
|
||||
permission = 'organizer.giftcards:read'
|
||||
context_object_name = 'card'
|
||||
|
||||
def get_object(self, queryset=None) -> Organizer:
|
||||
@@ -1800,6 +1796,8 @@ class GiftCardDetailView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMi
|
||||
|
||||
@transaction.atomic()
|
||||
def post(self, request, *args, **kwargs):
|
||||
if not request.user.has_organizer_permission(request.organizer, "organizer.giftcards:write", request=request):
|
||||
raise PermissionDenied()
|
||||
self.object = GiftCard.objects.select_for_update(of=OF_SELF).get(pk=self.get_object().pk)
|
||||
if 'revert' in request.POST:
|
||||
t = get_object_or_404(self.object.transactions.all(), pk=request.POST.get('revert'), order__isnull=False)
|
||||
@@ -1881,7 +1879,7 @@ class GiftCardDetailView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMi
|
||||
|
||||
class GiftCardCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CreateView):
|
||||
template_name = 'pretixcontrol/organizers/giftcard_create.html'
|
||||
permission = 'can_manage_gift_cards'
|
||||
permission = 'organizer.giftcards:write'
|
||||
form_class = GiftCardCreateForm
|
||||
success_url = 'invalid'
|
||||
|
||||
@@ -1932,7 +1930,7 @@ class GiftCardCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMi
|
||||
|
||||
class GiftCardUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, UpdateView):
|
||||
template_name = 'pretixcontrol/organizers/giftcard_edit.html'
|
||||
permission = 'can_manage_gift_cards'
|
||||
permission = 'organizer.giftcards:write'
|
||||
form_class = GiftCardUpdateForm
|
||||
success_url = 'invalid'
|
||||
context_object_name = 'card'
|
||||
@@ -2012,7 +2010,7 @@ class ExportMixin:
|
||||
)),
|
||||
('events',
|
||||
forms.ModelMultipleChoiceField(
|
||||
queryset=self.events,
|
||||
queryset=ex.events,
|
||||
widget=forms.CheckboxSelectMultiple(
|
||||
attrs={
|
||||
'class': 'scrolling-multiple-choice',
|
||||
@@ -2025,29 +2023,9 @@ class ExportMixin:
|
||||
])
|
||||
return ex
|
||||
|
||||
@cached_property
|
||||
def events(self):
|
||||
return self.request.user.get_events_with_permission('can_view_orders', request=self.request).filter(
|
||||
organizer=self.request.organizer
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def exporters(self):
|
||||
responses = register_multievent_data_exporters.send(self.request.organizer)
|
||||
raw_exporters = [
|
||||
response(Event.objects.none() if issubclass(response, OrganizerLevelExportMixin) else self.events,
|
||||
self.request.organizer)
|
||||
for r, response in responses
|
||||
if response
|
||||
]
|
||||
raw_exporters = [
|
||||
ex for ex in raw_exporters
|
||||
if (
|
||||
not isinstance(ex, OrganizerLevelExportMixin) or
|
||||
self.request.user.has_organizer_permission(self.request.organizer, ex.organizer_required_permission,
|
||||
self.request)
|
||||
) and ex.available_for_user(self.request.user if self.request.user and self.request.user.is_authenticated else None)
|
||||
]
|
||||
raw_exporters = list(init_organizer_exporters(self.request.organizer, user=self.request.user, request=self.request))
|
||||
return sorted(
|
||||
raw_exporters,
|
||||
key=lambda ex: (
|
||||
@@ -2061,7 +2039,7 @@ class ExportMixin:
|
||||
return ctx
|
||||
|
||||
def get_scheduled_queryset(self):
|
||||
if not self.request.user.has_organizer_permission(self.request.organizer, 'can_change_organizer_settings',
|
||||
if not self.request.user.has_organizer_permission(self.request.organizer, 'organizer.settings.general:write',
|
||||
request=self.request):
|
||||
qs = self.request.organizer.scheduled_exports.filter(owner=self.request.user)
|
||||
else:
|
||||
@@ -2146,7 +2124,16 @@ class ExportView(OrganizerPermissionRequiredMixin, ExportMixin, ListView):
|
||||
@transaction.atomic()
|
||||
def post(self, request, *args, **kwargs):
|
||||
if request.POST.get("schedule") == "save":
|
||||
if not self.has_permission():
|
||||
if self.scheduled and self.scheduled.pk and not self.has_permission_to_edit_scheduled():
|
||||
messages.error(
|
||||
self.request,
|
||||
_(
|
||||
"Your user account does not have sufficient permission to run this report, therefore "
|
||||
"you cannot change it."
|
||||
)
|
||||
)
|
||||
return super().get(request, *args, **kwargs)
|
||||
elif (not self.scheduled or not self.scheduled.pk) and not self.has_permission_to_create_scheduled():
|
||||
messages.error(
|
||||
self.request,
|
||||
_(
|
||||
@@ -2237,12 +2224,58 @@ class ExportView(OrganizerPermissionRequiredMixin, ExportMixin, ListView):
|
||||
def get_queryset(self):
|
||||
return self.get_scheduled_queryset()
|
||||
|
||||
def has_permission(self):
|
||||
if isinstance(self.exporter, OrganizerLevelExportMixin):
|
||||
if not self.request.user.has_organizer_permission(self.request.organizer, self.exporter.organizer_required_permission):
|
||||
return False
|
||||
if self.exporter and not self.exporter.available_for_user(self.request.user):
|
||||
def has_permission_to_edit_scheduled(self):
|
||||
# Exports can be edited by
|
||||
# - their owner
|
||||
# - any staff session user
|
||||
# - any user with permission for organizer settings *and* the permissions required to run the report
|
||||
# This is to prevent a possible privilege escalation where user A creates a scheduled export and
|
||||
# user B has settings permission (= they can see the export configuration), but not enough permission
|
||||
# to run the export themselves. Without this check, user B could modify the export and add themselves
|
||||
# as a recipient. Thereby, user B would gain access to data they can't have.
|
||||
if not self.exporter:
|
||||
# Triggered in scenario 5 in test_organizer_edit_restrictions
|
||||
return False
|
||||
if self.scheduled.owner == self.request.user:
|
||||
return True
|
||||
if self.request.user.has_active_staff_session(self.request.session.session_key):
|
||||
return True
|
||||
if not self.exporter.available_for_user(self.request.user):
|
||||
return False
|
||||
if self.request.user.has_organizer_permission(self.request.organizer, "organizer.settings.general:write", request=self.request):
|
||||
if isinstance(self.exporter, OrganizerLevelExportMixin):
|
||||
# Test scenario 5/6 in test_organizer_edit_restrictions
|
||||
return self.request.user.has_organizer_permission(
|
||||
self.request.organizer, self.exporter.get_required_organizer_permission(), request=self.request
|
||||
)
|
||||
else:
|
||||
if self.scheduled.export_form_data.get("all_events", False):
|
||||
# Test scenario 1/2 in test_organizer_edit_restrictions
|
||||
return self.request.user.teams.filter(
|
||||
TeamQuerySet.event_permission_q(self.exporter.get_required_event_permission()),
|
||||
all_events=True,
|
||||
).exists()
|
||||
else:
|
||||
# Test scenario 3/4 in test_organizer_edit_restrictions
|
||||
events_selected = self.scheduled.export_form_data.get("events", [])
|
||||
events_permission = set(self.request.user.get_events_with_permission(
|
||||
self.exporter.get_required_event_permission(), request=self.request,
|
||||
).values_list("pk", flat=True))
|
||||
return all(e in events_permission for e in events_selected)
|
||||
|
||||
def has_permission_to_create_scheduled(self):
|
||||
# Exports can only be created if the user has the correct permissions. We *ignore* staff sessions, because
|
||||
# the export is not *run* during a staff session and then would fail at the scheduled time.
|
||||
if self.exporter:
|
||||
if isinstance(self.exporter, OrganizerLevelExportMixin):
|
||||
if not self.request.user.has_organizer_permission(self.request.organizer, self.exporter.get_required_organizer_permission()):
|
||||
return False
|
||||
else:
|
||||
permission_name = self.exporter.get_required_event_permission()
|
||||
if not any(t.has_event_permission(permission_name) for t in self.request.user.teams.filter(organizer=self.request.organizer)):
|
||||
return False
|
||||
if not self.exporter.available_for_user(self.request.user):
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
@@ -2251,6 +2284,17 @@ class ExportView(OrganizerPermissionRequiredMixin, ExportMixin, ListView):
|
||||
ctx['schedule_form'] = self.schedule_form
|
||||
ctx['rrule_form'] = self.rrule_form
|
||||
ctx['scheduled_copy_from'] = self.scheduled_copy_from
|
||||
|
||||
if self.scheduled and self.scheduled.pk and not self.has_permission_to_edit_scheduled() and self.exporter:
|
||||
ctx['no_save'] = True
|
||||
for f in self.exporter.form.fields.values():
|
||||
f.disabled = True
|
||||
f.widget.attrs.pop("data-inverse-dependency", None)
|
||||
for f in self.rrule_form.fields.values():
|
||||
f.disabled = True
|
||||
for f in self.schedule_form.fields.values():
|
||||
f.disabled = True
|
||||
|
||||
elif not self.exporter:
|
||||
for s in ctx['scheduled']:
|
||||
try:
|
||||
@@ -2307,7 +2351,7 @@ class RunScheduledExportView(OrganizerPermissionRequiredMixin, ExportMixin, View
|
||||
class GateListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, ListView):
|
||||
model = Gate
|
||||
template_name = 'pretixcontrol/organizers/gates.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.devices:read'
|
||||
context_object_name = 'gates'
|
||||
|
||||
def get_queryset(self):
|
||||
@@ -2317,7 +2361,7 @@ class GateListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, L
|
||||
class GateCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CreateView):
|
||||
model = Gate
|
||||
template_name = 'pretixcontrol/organizers/gate_edit.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.devices:write'
|
||||
form_class = GateForm
|
||||
|
||||
def get_form_kwargs(self):
|
||||
@@ -2351,7 +2395,7 @@ class GateCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
|
||||
class GateUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, UpdateView):
|
||||
model = Gate
|
||||
template_name = 'pretixcontrol/organizers/gate_edit.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.devices:write'
|
||||
context_object_name = 'gate'
|
||||
form_class = GateForm
|
||||
|
||||
@@ -2386,7 +2430,7 @@ class GateUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
|
||||
class GateDeleteView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CompatDeleteView):
|
||||
model = Gate
|
||||
template_name = 'pretixcontrol/organizers/gate_delete.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.devices:write'
|
||||
context_object_name = 'gate'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
@@ -2410,7 +2454,7 @@ class GateDeleteView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
|
||||
class EventMetaPropertyListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, ListView):
|
||||
model = EventMetaProperty
|
||||
template_name = 'pretixcontrol/organizers/properties.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
context_object_name = 'properties'
|
||||
|
||||
def get_queryset(self):
|
||||
@@ -2461,7 +2505,7 @@ class EventMetaPropertyEditorMixin:
|
||||
|
||||
class EventMetaPropertyCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, EventMetaPropertyEditorMixin, CreateView):
|
||||
model = EventMetaProperty
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
return EventMetaProperty()
|
||||
@@ -2491,7 +2535,7 @@ class EventMetaPropertyCreateView(OrganizerDetailViewMixin, OrganizerPermissionR
|
||||
|
||||
class EventMetaPropertyUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, EventMetaPropertyEditorMixin, UpdateView):
|
||||
model = EventMetaProperty
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
context_object_name = 'property'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
@@ -2523,7 +2567,7 @@ class EventMetaPropertyUpdateView(OrganizerDetailViewMixin, OrganizerPermissionR
|
||||
class EventMetaPropertyDeleteView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CompatDeleteView):
|
||||
model = EventMetaProperty
|
||||
template_name = 'pretixcontrol/organizers/property_delete.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
context_object_name = 'property'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
@@ -2567,7 +2611,7 @@ def meta_property_move(request, property, up=True):
|
||||
messages.success(request, _('The order of properties has been updated.'))
|
||||
|
||||
|
||||
@organizer_permission_required("can_change_organizer_settings")
|
||||
@organizer_permission_required("organizer.settings.general:write")
|
||||
@require_http_methods(["POST"])
|
||||
def meta_property_move_up(request, organizer, property):
|
||||
meta_property_move(request, property, up=True)
|
||||
@@ -2575,7 +2619,7 @@ def meta_property_move_up(request, organizer, property):
|
||||
organizer=request.organizer.slug)
|
||||
|
||||
|
||||
@organizer_permission_required("can_change_organizer_settings")
|
||||
@organizer_permission_required("organizer.settings.general:write")
|
||||
@require_http_methods(["POST"])
|
||||
def meta_property_move_down(request, organizer, property):
|
||||
meta_property_move(request, property, up=False)
|
||||
@@ -2584,7 +2628,7 @@ def meta_property_move_down(request, organizer, property):
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
@organizer_permission_required("can_change_organizer_settings")
|
||||
@organizer_permission_required("organizer.settings.general:write")
|
||||
@require_http_methods(["POST"])
|
||||
def reorder_meta_properties(request, organizer):
|
||||
try:
|
||||
@@ -2616,7 +2660,7 @@ def reorder_meta_properties(request, organizer):
|
||||
|
||||
class LogView(OrganizerPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
template_name = 'pretixcontrol/organizers/logs.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
model = LogEntry
|
||||
context_object_name = 'logs'
|
||||
|
||||
@@ -2641,7 +2685,7 @@ class LogView(OrganizerPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
class MembershipTypeListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, ListView):
|
||||
model = MembershipType
|
||||
template_name = 'pretixcontrol/organizers/membershiptypes.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
context_object_name = 'types'
|
||||
|
||||
def get_queryset(self):
|
||||
@@ -2651,7 +2695,7 @@ class MembershipTypeListView(OrganizerDetailViewMixin, OrganizerPermissionRequir
|
||||
class MembershipTypeCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CreateView):
|
||||
model = MembershipType
|
||||
template_name = 'pretixcontrol/organizers/membershiptype_edit.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
form_class = MembershipTypeForm
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
@@ -2685,7 +2729,7 @@ class MembershipTypeCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequ
|
||||
class MembershipTypeUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, UpdateView):
|
||||
model = MembershipType
|
||||
template_name = 'pretixcontrol/organizers/membershiptype_edit.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
context_object_name = 'type'
|
||||
form_class = MembershipTypeForm
|
||||
|
||||
@@ -2720,7 +2764,7 @@ class MembershipTypeUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequ
|
||||
class MembershipTypeDeleteView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CompatDeleteView):
|
||||
model = MembershipType
|
||||
template_name = 'pretixcontrol/organizers/membershiptype_delete.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
context_object_name = 'type'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
@@ -2750,7 +2794,7 @@ class MembershipTypeDeleteView(OrganizerDetailViewMixin, OrganizerPermissionRequ
|
||||
class SSOProviderListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, ListView):
|
||||
model = CustomerSSOProvider
|
||||
template_name = 'pretixcontrol/organizers/ssoproviders.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
context_object_name = 'providers'
|
||||
|
||||
def get_queryset(self):
|
||||
@@ -2760,7 +2804,7 @@ class SSOProviderListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredM
|
||||
class SSOProviderCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CreateView):
|
||||
model = CustomerSSOProvider
|
||||
template_name = 'pretixcontrol/organizers/ssoprovider_edit.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
form_class = SSOProviderForm
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
@@ -2794,7 +2838,7 @@ class SSOProviderCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequire
|
||||
class SSOProviderUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, UpdateView):
|
||||
model = CustomerSSOProvider
|
||||
template_name = 'pretixcontrol/organizers/ssoprovider_edit.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
context_object_name = 'provider'
|
||||
form_class = SSOProviderForm
|
||||
|
||||
@@ -2836,7 +2880,7 @@ class SSOProviderUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequire
|
||||
class SSOProviderDeleteView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CompatDeleteView):
|
||||
model = CustomerSSOProvider
|
||||
template_name = 'pretixcontrol/organizers/ssoprovider_delete.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
context_object_name = 'provider'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
@@ -2866,7 +2910,7 @@ class SSOProviderDeleteView(OrganizerDetailViewMixin, OrganizerPermissionRequire
|
||||
class SSOClientListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, ListView):
|
||||
model = CustomerSSOClient
|
||||
template_name = 'pretixcontrol/organizers/ssoclients.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
context_object_name = 'clients'
|
||||
|
||||
def get_queryset(self):
|
||||
@@ -2876,7 +2920,7 @@ class SSOClientListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMix
|
||||
class SSOClientCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CreateView):
|
||||
model = CustomerSSOClient
|
||||
template_name = 'pretixcontrol/organizers/ssoclient_edit.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
form_class = SSOClientForm
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
@@ -2916,7 +2960,7 @@ class SSOClientCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredM
|
||||
class SSOClientUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, UpdateView):
|
||||
model = CustomerSSOClient
|
||||
template_name = 'pretixcontrol/organizers/ssoclient_edit.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
context_object_name = 'client'
|
||||
form_class = SSOClientForm
|
||||
|
||||
@@ -2966,7 +3010,7 @@ class SSOClientUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredM
|
||||
class SSOClientDeleteView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CompatDeleteView):
|
||||
model = CustomerSSOClient
|
||||
template_name = 'pretixcontrol/organizers/ssoclient_delete.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
context_object_name = 'client'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
@@ -2996,7 +3040,7 @@ class SSOClientDeleteView(OrganizerDetailViewMixin, OrganizerPermissionRequiredM
|
||||
class CustomerListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
model = Customer
|
||||
template_name = 'pretixcontrol/organizers/customers.html'
|
||||
permission = 'can_manage_customers'
|
||||
permission = 'organizer.customers:read'
|
||||
context_object_name = 'customers'
|
||||
|
||||
def get_queryset(self):
|
||||
@@ -3017,7 +3061,7 @@ class CustomerListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixi
|
||||
|
||||
class CustomerDetailView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
template_name = 'pretixcontrol/organizers/customer.html'
|
||||
permission = 'can_manage_customers'
|
||||
permission = 'organizer.customers:read'
|
||||
context_object_name = 'orders'
|
||||
|
||||
def get_queryset(self):
|
||||
@@ -3133,7 +3177,7 @@ class CustomerDetailView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMi
|
||||
|
||||
class CustomerCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CreateView):
|
||||
template_name = 'pretixcontrol/organizers/customer_edit.html'
|
||||
permission = 'can_manage_customers'
|
||||
permission = 'organizer.customers:write'
|
||||
context_object_name = 'customer'
|
||||
form_class = CustomerCreateForm
|
||||
|
||||
@@ -3163,7 +3207,7 @@ class CustomerCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMi
|
||||
|
||||
class CustomerUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, UpdateView):
|
||||
template_name = 'pretixcontrol/organizers/customer_edit.html'
|
||||
permission = 'can_manage_customers'
|
||||
permission = 'organizer.customers:write'
|
||||
context_object_name = 'customer'
|
||||
form_class = CustomerUpdateForm
|
||||
|
||||
@@ -3192,7 +3236,7 @@ class CustomerUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMi
|
||||
|
||||
class MembershipUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, UpdateView):
|
||||
template_name = 'pretixcontrol/organizers/customer_membership.html'
|
||||
permission = 'can_manage_customers'
|
||||
permission = 'organizer.customers:write'
|
||||
context_object_name = 'membership'
|
||||
form_class = MembershipUpdateForm
|
||||
|
||||
@@ -3232,7 +3276,7 @@ class MembershipUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequired
|
||||
|
||||
class MembershipDeleteView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CompatDeleteView):
|
||||
template_name = 'pretixcontrol/organizers/customer_membership_delete.html'
|
||||
permission = 'can_manage_customers'
|
||||
permission = 'organizer.customers:write'
|
||||
context_object_name = 'membership'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
@@ -3270,7 +3314,7 @@ class MembershipDeleteView(OrganizerDetailViewMixin, OrganizerPermissionRequired
|
||||
|
||||
class MembershipCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CreateView):
|
||||
template_name = 'pretixcontrol/organizers/customer_membership.html'
|
||||
permission = 'can_manage_customers'
|
||||
permission = 'organizer.customers:write'
|
||||
context_object_name = 'membership'
|
||||
form_class = MembershipUpdateForm
|
||||
|
||||
@@ -3309,7 +3353,7 @@ class MembershipCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequired
|
||||
|
||||
class CustomerAnonymizeView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, DetailView):
|
||||
template_name = 'pretixcontrol/organizers/customer_anonymize.html'
|
||||
permission = 'can_manage_customers'
|
||||
permission = 'organizer.customers:write'
|
||||
context_object_name = 'customer'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
@@ -3336,7 +3380,7 @@ class CustomerAnonymizeView(OrganizerDetailViewMixin, OrganizerPermissionRequire
|
||||
class ReusableMediaListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
model = ReusableMedium
|
||||
template_name = 'pretixcontrol/organizers/reusable_media.html'
|
||||
permission = 'can_manage_reusable_media'
|
||||
permission = 'organizer.reusablemedia:read'
|
||||
context_object_name = 'media'
|
||||
|
||||
def get_queryset(self):
|
||||
@@ -3360,7 +3404,7 @@ class ReusableMediaListView(OrganizerDetailViewMixin, OrganizerPermissionRequire
|
||||
|
||||
class ReusableMediumDetailView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, TemplateView):
|
||||
template_name = 'pretixcontrol/organizers/reusable_medium.html'
|
||||
permission = 'can_manage_reusable_media'
|
||||
permission = 'organizer.reusablemedia:read'
|
||||
|
||||
@cached_property
|
||||
def medium(self):
|
||||
@@ -3377,7 +3421,7 @@ class ReusableMediumDetailView(OrganizerDetailViewMixin, OrganizerPermissionRequ
|
||||
|
||||
class ReusableMediumCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CreateView):
|
||||
template_name = 'pretixcontrol/organizers/reusable_medium_edit.html'
|
||||
permission = 'can_manage_reusable_media'
|
||||
permission = 'organizer.reusablemedia:write'
|
||||
context_object_name = 'medium'
|
||||
form_class = ReusableMediumCreateForm
|
||||
|
||||
@@ -3406,7 +3450,7 @@ class ReusableMediumCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequ
|
||||
|
||||
class ReusableMediumUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, UpdateView):
|
||||
template_name = 'pretixcontrol/organizers/reusable_medium_edit.html'
|
||||
permission = 'can_manage_reusable_media'
|
||||
permission = 'organizer.reusablemedia:write'
|
||||
context_object_name = 'medium'
|
||||
form_class = ReusableMediumUpdateForm
|
||||
|
||||
@@ -3436,7 +3480,7 @@ class ReusableMediumUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequ
|
||||
class ChannelListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, ListView):
|
||||
model = SalesChannel
|
||||
template_name = 'pretixcontrol/organizers/channels.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
context_object_name = 'channels'
|
||||
|
||||
def get_queryset(self):
|
||||
@@ -3455,7 +3499,7 @@ class ChannelEditorMixin:
|
||||
|
||||
class ChannelCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, ChannelEditorMixin, CreateView):
|
||||
model = SalesChannel
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
template_name = 'pretixcontrol/organizers/channel_add.html'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
@@ -3527,7 +3571,7 @@ class ChannelCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMix
|
||||
|
||||
class ChannelUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, ChannelEditorMixin, UpdateView):
|
||||
model = SalesChannel
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
context_object_name = 'channel'
|
||||
template_name = 'pretixcontrol/organizers/channel_edit.html'
|
||||
|
||||
@@ -3572,7 +3616,7 @@ class ChannelUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMix
|
||||
class ChannelDeleteView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CompatDeleteView):
|
||||
model = SalesChannel
|
||||
template_name = 'pretixcontrol/organizers/channel_delete.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
permission = 'organizer.settings.general:write'
|
||||
context_object_name = 'channel'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
@@ -3628,7 +3672,7 @@ def channel_move(request, channel, up=True):
|
||||
messages.success(request, _('The order of sales channels has been updated.'))
|
||||
|
||||
|
||||
@organizer_permission_required("can_change_organizer_settings")
|
||||
@organizer_permission_required("organizer.settings.general:write")
|
||||
@require_http_methods(["POST"])
|
||||
def channel_move_up(request, organizer, channel):
|
||||
channel_move(request, channel, up=True)
|
||||
@@ -3636,7 +3680,7 @@ def channel_move_up(request, organizer, channel):
|
||||
organizer=request.organizer.slug)
|
||||
|
||||
|
||||
@organizer_permission_required("can_change_organizer_settings")
|
||||
@organizer_permission_required("organizer.settings.general:write")
|
||||
@require_http_methods(["POST"])
|
||||
def channel_move_down(request, organizer, channel):
|
||||
channel_move(request, channel, up=False)
|
||||
@@ -3645,7 +3689,7 @@ def channel_move_down(request, organizer, channel):
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
@organizer_permission_required("can_change_organizer_settings")
|
||||
@organizer_permission_required("organizer.settings.general:write")
|
||||
@require_http_methods(["POST"])
|
||||
def reorder_channels(request, organizer):
|
||||
try:
|
||||
|
||||
@@ -58,7 +58,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
|
||||
template_name = 'pretixcontrol/pdf/index.html'
|
||||
permission = 'can_change_settings'
|
||||
permission = 'event.settings.general:write'
|
||||
accepted_formats = (
|
||||
'application/pdf',
|
||||
)
|
||||
|
||||
@@ -85,7 +85,7 @@ class OrderSearch(PaginationMixin, ListView):
|
||||
|
||||
if not self.request.user.has_active_staff_session(self.request.session.session_key):
|
||||
qs = qs.filter(
|
||||
Q(event_id__in=self.request.user.get_events_with_permission('can_view_orders').values_list('id', flat=True))
|
||||
Q(event_id__in=self.request.user.get_events_with_permission('event.orders:read').values_list('id', flat=True))
|
||||
)
|
||||
|
||||
if self.filter_form.is_valid():
|
||||
@@ -159,7 +159,7 @@ class PaymentSearch(PaginationMixin, ListView):
|
||||
|
||||
if not self.request.user.has_active_staff_session(self.request.session.session_key):
|
||||
qs = qs.filter(
|
||||
Q(order__event_id__in=self.request.user.get_events_with_permission('can_view_orders').values_list('id', flat=True))
|
||||
Q(order__event_id__in=self.request.user.get_events_with_permission('event.orders:read').values_list('id', flat=True))
|
||||
)
|
||||
|
||||
if self.filter_form.is_valid():
|
||||
|
||||
@@ -76,7 +76,7 @@ class ShredderMixin:
|
||||
|
||||
|
||||
class StartShredView(RecentAuthenticationRequiredMixin, EventPermissionRequiredMixin, ShredderMixin, TemplateView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
template_name = 'pretixcontrol/shredder/index.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
@@ -87,7 +87,7 @@ class StartShredView(RecentAuthenticationRequiredMixin, EventPermissionRequiredM
|
||||
|
||||
|
||||
class ShredDownloadView(RecentAuthenticationRequiredMixin, EventPermissionRequiredMixin, ShredderMixin, TemplateView):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
template_name = 'pretixcontrol/shredder/download.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
@@ -119,7 +119,7 @@ class ShredDownloadView(RecentAuthenticationRequiredMixin, EventPermissionRequir
|
||||
|
||||
|
||||
class ShredExportView(RecentAuthenticationRequiredMixin, EventPermissionRequiredMixin, ShredderMixin, AsyncAction, View):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
task = export
|
||||
known_errortypes = ['ShredError']
|
||||
|
||||
@@ -148,7 +148,7 @@ class ShredExportView(RecentAuthenticationRequiredMixin, EventPermissionRequired
|
||||
|
||||
|
||||
class ShredDoView(RecentAuthenticationRequiredMixin, EventPermissionRequiredMixin, ShredderMixin, AsyncAction, View):
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
task = shred
|
||||
known_errortypes = ['ShredError']
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ class SubEventList(EventPermissionRequiredMixin, PaginationMixin, SubEventQueryM
|
||||
model = SubEvent
|
||||
context_object_name = 'subevents'
|
||||
template_name = 'pretixcontrol/subevents/index.html'
|
||||
permission = 'can_change_settings'
|
||||
permission = None
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset(True).prefetch_related(
|
||||
@@ -156,7 +156,7 @@ class SubEventList(EventPermissionRequiredMixin, PaginationMixin, SubEventQueryM
|
||||
class SubEventDelete(EventPermissionRequiredMixin, CompatDeleteView):
|
||||
model = SubEvent
|
||||
template_name = 'pretixcontrol/subevents/delete.html'
|
||||
permission = 'can_change_settings'
|
||||
permission = 'event.subevents:write'
|
||||
context_object_name = 'subevents'
|
||||
|
||||
def get_object(self, queryset=None) -> SubEvent:
|
||||
@@ -241,7 +241,7 @@ class SubEventEditorMixin(MetaDataEditorMixin):
|
||||
property=p,
|
||||
disabled=(
|
||||
p.protected and
|
||||
not self.request.user.has_organizer_permission(self.request.organizer, 'can_change_organizer_settings', request=self.request)
|
||||
not self.request.user.has_organizer_permission(self.request.organizer, 'organizer.settings.general:write', request=self.request)
|
||||
),
|
||||
default=self._default_meta.get(p.name, ''),
|
||||
instance=val_instances.get(p.pk, self.meta_model(property=p, subevent=self.object)),
|
||||
@@ -508,7 +508,7 @@ class SubEventEditorMixin(MetaDataEditorMixin):
|
||||
class SubEventUpdate(EventPermissionRequiredMixin, SubEventEditorMixin, UpdateView):
|
||||
model = SubEvent
|
||||
template_name = 'pretixcontrol/subevents/detail.html'
|
||||
permission = 'can_change_settings'
|
||||
permission = 'event.subevents:write'
|
||||
context_object_name = 'subevent'
|
||||
form_class = SubEventForm
|
||||
|
||||
@@ -575,7 +575,7 @@ class SubEventUpdate(EventPermissionRequiredMixin, SubEventEditorMixin, UpdateVi
|
||||
class SubEventCreate(SubEventEditorMixin, EventPermissionRequiredMixin, CreateView):
|
||||
model = SubEvent
|
||||
template_name = 'pretixcontrol/subevents/detail.html'
|
||||
permission = 'can_change_settings'
|
||||
permission = 'event.subevents:write'
|
||||
context_object_name = 'subevent'
|
||||
form_class = SubEventForm
|
||||
|
||||
@@ -669,7 +669,7 @@ class SubEventCreate(SubEventEditorMixin, EventPermissionRequiredMixin, CreateVi
|
||||
|
||||
|
||||
class SubEventBulkAction(SubEventQueryMixin, EventPermissionRequiredMixin, View):
|
||||
permission = 'can_change_settings'
|
||||
permission = 'event.subevents:write'
|
||||
|
||||
@transaction.atomic
|
||||
def post(self, request, *args, **kwargs):
|
||||
@@ -740,7 +740,7 @@ class SubEventBulkAction(SubEventQueryMixin, EventPermissionRequiredMixin, View)
|
||||
class SubEventBulkCreate(SubEventEditorMixin, EventPermissionRequiredMixin, AsyncFormView):
|
||||
model = SubEvent
|
||||
template_name = 'pretixcontrol/subevents/bulk.html'
|
||||
permission = 'can_change_settings'
|
||||
permission = 'event.subevents:write'
|
||||
context_object_name = 'subevent'
|
||||
form_class = SubEventBulkForm
|
||||
itemformclass = BulkSubEventItemForm
|
||||
@@ -1065,7 +1065,7 @@ class SubEventBulkCreate(SubEventEditorMixin, EventPermissionRequiredMixin, Asyn
|
||||
|
||||
|
||||
class SubEventBulkEdit(SubEventQueryMixin, EventPermissionRequiredMixin, FormView):
|
||||
permission = 'can_change_settings'
|
||||
permission = 'event.subevents:write'
|
||||
form_class = SubEventBulkEditForm
|
||||
template_name = 'pretixcontrol/subevents/bulk_edit.html'
|
||||
context_object_name = 'subevent'
|
||||
@@ -1170,7 +1170,10 @@ class SubEventBulkEdit(SubEventQueryMixin, EventPermissionRequiredMixin, FormVie
|
||||
kwargs = {}
|
||||
|
||||
if self.sampled_quotas is not None:
|
||||
kwargs['instance'] = self.get_queryset()[0]
|
||||
try:
|
||||
kwargs['instance'] = self.get_queryset()[0]
|
||||
except IndexError:
|
||||
raise Http404("No matching dates")
|
||||
|
||||
formsetclass = inlineformset_factory(
|
||||
SubEvent, Quota,
|
||||
|
||||
@@ -51,6 +51,7 @@ from pretix.base.models import (
|
||||
ItemVariation, ItemVariationMetaValue, Order, OrderPosition, Organizer,
|
||||
SubEventMetaValue, User, Voucher,
|
||||
)
|
||||
from pretix.base.models.organizer import TeamQuerySet
|
||||
from pretix.control.forms.event import EventWizardCopyForm
|
||||
from pretix.control.permissions import (
|
||||
event_permission_required, organizer_permission_required,
|
||||
@@ -172,7 +173,7 @@ def event_list(request):
|
||||
return JsonResponse(doc)
|
||||
|
||||
|
||||
@organizer_permission_required(("can_manage_gift_cards", "can_manage_reusable_media"))
|
||||
@organizer_permission_required(("organizer.giftcards:read", "organizer.reusablemedia:write"))
|
||||
def giftcard_select2(request, **kwargs):
|
||||
query = request.GET.get('query', '')
|
||||
try:
|
||||
@@ -180,7 +181,7 @@ def giftcard_select2(request, **kwargs):
|
||||
except ValueError:
|
||||
page = 1
|
||||
|
||||
if request.user.has_organizer_permission(request.organizer, 'can_manage_gift_cards', request):
|
||||
if request.user.has_organizer_permission(request.organizer, 'organizer.giftcards:write', request):
|
||||
qs = request.organizer.issued_gift_cards.filter(
|
||||
Q(secret__icontains=query)
|
||||
).order_by('secret')
|
||||
@@ -210,7 +211,7 @@ def giftcard_select2(request, **kwargs):
|
||||
return JsonResponse(doc)
|
||||
|
||||
|
||||
@organizer_permission_required(("can_manage_reusable_media", "can_manage_gift_cards"))
|
||||
@organizer_permission_required(("organizer.reusablemedia:write", "organizer.giftcards:write"))
|
||||
def ticket_select2(request, **kwargs):
|
||||
query = request.GET.get('query', '')
|
||||
try:
|
||||
@@ -240,8 +241,13 @@ def ticket_select2(request, **kwargs):
|
||||
qs_orders = qs_orders.filter(
|
||||
exact_match | (
|
||||
soft_match & (
|
||||
Q(order__event__organizer_id__in=request.user.teams.filter(all_events=True, can_view_orders=True).values_list('organizer', flat=True))
|
||||
| Q(order__event_id__in=request.user.teams.filter(can_view_orders=True).values_list('limit_events__id', flat=True))
|
||||
Q(order__event__organizer_id__in=request.user.teams.filter(
|
||||
TeamQuerySet.event_permission_q("event.orders:read"),
|
||||
all_events=True,
|
||||
).values_list('organizer', flat=True))
|
||||
| Q(order__event_id__in=request.user.teams.filter(
|
||||
TeamQuerySet.event_permission_q("event.orders:read")
|
||||
).values_list('limit_events__id', flat=True))
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -270,7 +276,7 @@ def ticket_select2(request, **kwargs):
|
||||
return JsonResponse(doc)
|
||||
|
||||
|
||||
@organizer_permission_required("can_manage_customers")
|
||||
@organizer_permission_required("organizer.customers:write")
|
||||
def customer_select2(request, **kwargs):
|
||||
query = request.GET.get('query', '')
|
||||
try:
|
||||
@@ -337,9 +343,9 @@ def nav_context_list(request):
|
||||
if not request.user.has_active_staff_session(request.session.session_key):
|
||||
qs_orders = qs_orders.filter(
|
||||
Q(event__organizer_id__in=request.user.teams.filter(
|
||||
all_events=True, can_view_orders=True).values_list('organizer', flat=True))
|
||||
TeamQuerySet.event_permission_q("event.orders:read"), all_events=True).values_list('organizer', flat=True))
|
||||
| Q(event_id__in=request.user.teams.filter(
|
||||
can_view_orders=True).values_list('limit_events__id', flat=True))
|
||||
TeamQuerySet.event_permission_q("event.orders:read")).values_list('limit_events__id', flat=True))
|
||||
)
|
||||
|
||||
qs_vouchers = Voucher.objects.filter(
|
||||
@@ -348,9 +354,9 @@ def nav_context_list(request):
|
||||
if not request.user.has_active_staff_session(request.session.session_key):
|
||||
qs_vouchers = qs_vouchers.filter(
|
||||
Q(event__organizer_id__in=request.user.teams.filter(
|
||||
all_events=True, can_view_vouchers=True).values_list('organizer', flat=True))
|
||||
TeamQuerySet.event_permission_q("event.vouchers:read"), all_events=True).values_list('organizer', flat=True))
|
||||
| Q(event_id__in=request.user.teams.filter(
|
||||
can_view_vouchers=True).values_list('limit_events__id', flat=True))
|
||||
TeamQuerySet.event_permission_q("event.vouchers:read")).values_list('limit_events__id', flat=True))
|
||||
)
|
||||
else:
|
||||
qs_vouchers = Voucher.objects.none()
|
||||
@@ -813,7 +819,7 @@ def organizer_select2(request):
|
||||
qs = qs.filter(Q(name__icontains=term) | Q(slug__icontains=term))
|
||||
if not request.user.has_active_staff_session(request.session.session_key):
|
||||
if 'can_create' in request.GET:
|
||||
qs = qs.filter(pk__in=request.user.teams.filter(can_create_events=True).values_list('organizer', flat=True))
|
||||
qs = qs.filter(pk__in=request.user.teams.filter(TeamQuerySet.organizer_permission_q("organizer.events:create")).values_list('organizer', flat=True))
|
||||
else:
|
||||
qs = qs.filter(pk__in=request.user.teams.values_list('organizer', flat=True))
|
||||
|
||||
@@ -976,21 +982,21 @@ def item_meta_values(request, organizer, event):
|
||||
var_matches = var_matches.filter(variation__item__event__organizer_id=organizer.pk)
|
||||
all_access = (
|
||||
request.user.has_active_staff_session(request.session.session_key)
|
||||
or request.user.teams.filter(all_events=True, organizer=organizer, can_change_items=True).exists()
|
||||
or request.user.teams.filter(TeamQuerySet.event_permission_q("event.items:write"), all_events=True, organizer=organizer).exists()
|
||||
)
|
||||
if not all_access:
|
||||
defaults = defaults.filter(
|
||||
event__id__in=request.user.teams.filter(can_change_items=True).values_list(
|
||||
event__id__in=request.user.teams.filter(TeamQuerySet.event_permission_q("event.items:write")).values_list(
|
||||
'limit_events__id', flat=True
|
||||
)
|
||||
)
|
||||
matches = matches.filter(
|
||||
item__event__id__in=request.user.teams.filter(can_change_items=True).values_list(
|
||||
item__event__id__in=request.user.teams.filter(TeamQuerySet.event_permission_q("event.items:write")).values_list(
|
||||
'limit_events__id', flat=True
|
||||
)
|
||||
)
|
||||
var_matches = var_matches.filter(
|
||||
variation__item__event__id__in=request.user.teams.filter(can_change_items=True).values_list(
|
||||
variation__item__event__id__in=request.user.teams.filter(TeamQuerySet.event_permission_q("event.items:write")).values_list(
|
||||
'limit_events__id', flat=True
|
||||
)
|
||||
)
|
||||
@@ -1007,10 +1013,16 @@ def item_meta_values(request, organizer, event):
|
||||
})
|
||||
|
||||
|
||||
@organizer_permission_required(("can_view_orders", "can_change_organizer_settings"))
|
||||
# This decorator is a bit of a hack since this is not technically an organizer permission, but it does the job here --
|
||||
# anyone who can see orders for any event can see the check-in log view where this is used as a filter
|
||||
def devices_select2(request, **kwargs):
|
||||
allowed = (
|
||||
# This check is a bit of a hack since this is not technically an organizer permission, but it does the job here --
|
||||
# anyone who can see orders for any event can see the check-in log view where this is used as a filter
|
||||
request.user.has_organizer_permission(request.organizer, "organizer.devices:read", request=request) or
|
||||
request.user.get_events_with_permission("event.orders:read").filter(organizer=request.organizer).exists()
|
||||
)
|
||||
if not allowed:
|
||||
raise PermissionDenied()
|
||||
|
||||
query = request.GET.get('query', '')
|
||||
try:
|
||||
page = int(request.GET.get('page', '1'))
|
||||
@@ -1045,10 +1057,16 @@ def devices_select2(request, **kwargs):
|
||||
return JsonResponse(doc)
|
||||
|
||||
|
||||
@organizer_permission_required(("can_view_orders", "can_change_event_settings", "can_change_organizer_settings"))
|
||||
# This decorator is a bit of a hack since this is not technically an organizer permission, but it does the job here --
|
||||
# anyone who can see orders for any event can see the check-in log view where this is used as a filter
|
||||
def gate_select2(request, **kwargs):
|
||||
allowed = (
|
||||
# This check is a bit of a hack since this is not technically an organizer permission, but it does the job here --
|
||||
# anyone who can see orders for any event can see the check-in log view where this is used as a filter
|
||||
request.user.has_organizer_permission(request.organizer, "organizer.devices:read", request=request) or
|
||||
request.user.get_events_with_permission("event.orders:read").filter(organizer=request.organizer).exists()
|
||||
)
|
||||
if not allowed:
|
||||
raise PermissionDenied()
|
||||
|
||||
query = request.GET.get('query', '')
|
||||
try:
|
||||
page = int(request.GET.get('page', '1'))
|
||||
|
||||
@@ -63,6 +63,7 @@ from pretix.base.models import (
|
||||
CartPosition, LogEntry, Voucher, WaitingListEntry,
|
||||
)
|
||||
from pretix.base.models.vouchers import generate_codes
|
||||
from pretix.base.permissions import AnyPermissionOf
|
||||
from pretix.base.services.mail import prefix_subject
|
||||
from pretix.base.services.placeholders import get_sample_context
|
||||
from pretix.base.services.vouchers import vouchers_send
|
||||
@@ -83,7 +84,7 @@ class VoucherList(PaginationMixin, EventPermissionRequiredMixin, ListView):
|
||||
model = Voucher
|
||||
context_object_name = 'vouchers'
|
||||
template_name = 'pretixcontrol/vouchers/index.html'
|
||||
permission = 'can_view_vouchers'
|
||||
permission = 'event.vouchers:read'
|
||||
|
||||
@scopes_disabled() # we have an event check here, and we can save some performance on subqueries
|
||||
def get_queryset(self):
|
||||
@@ -155,7 +156,7 @@ class VoucherList(PaginationMixin, EventPermissionRequiredMixin, ListView):
|
||||
|
||||
class VoucherTags(EventPermissionRequiredMixin, TemplateView):
|
||||
template_name = 'pretixcontrol/vouchers/tags.html'
|
||||
permission = 'can_view_vouchers'
|
||||
permission = 'event.vouchers:read'
|
||||
|
||||
def get_queryset(self):
|
||||
qs = self.request.event.vouchers.order_by('tag').filter(
|
||||
@@ -196,7 +197,7 @@ class VoucherTags(EventPermissionRequiredMixin, TemplateView):
|
||||
class VoucherDeleteCarts(EventPermissionRequiredMixin, CompatDeleteView):
|
||||
model = Voucher
|
||||
template_name = 'pretixcontrol/vouchers/delete_carts.html'
|
||||
permission = 'can_change_vouchers'
|
||||
permission = 'event.vouchers:write'
|
||||
context_object_name = 'voucher'
|
||||
|
||||
def get_object(self, queryset=None) -> Voucher:
|
||||
@@ -228,7 +229,7 @@ class VoucherDeleteCarts(EventPermissionRequiredMixin, CompatDeleteView):
|
||||
class VoucherDelete(EventPermissionRequiredMixin, CompatDeleteView):
|
||||
model = Voucher
|
||||
template_name = 'pretixcontrol/vouchers/delete.html'
|
||||
permission = 'can_change_vouchers'
|
||||
permission = 'event.vouchers:write'
|
||||
context_object_name = 'voucher'
|
||||
|
||||
def get_object(self, queryset=None) -> Voucher:
|
||||
@@ -270,7 +271,7 @@ class VoucherDelete(EventPermissionRequiredMixin, CompatDeleteView):
|
||||
class VoucherUpdate(EventPermissionRequiredMixin, UpdateView):
|
||||
model = Voucher
|
||||
template_name = 'pretixcontrol/vouchers/detail.html'
|
||||
permission = ('can_change_vouchers', 'can_view_vouchers')
|
||||
permission = AnyPermissionOf('event.vouchers:write', 'event.vouchers:read')
|
||||
context_object_name = 'voucher'
|
||||
|
||||
def form_invalid(self, form):
|
||||
@@ -286,7 +287,7 @@ class VoucherUpdate(EventPermissionRequiredMixin, UpdateView):
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
form = super().get_form(form_class)
|
||||
if not self.request.user.has_event_permission(self.request.organizer, self.request.event, 'can_change_vouchers',
|
||||
if not self.request.user.has_event_permission(self.request.organizer, self.request.event, 'event.vouchers:write',
|
||||
request=self.request):
|
||||
for f in form.fields.values():
|
||||
f.disabled = True
|
||||
@@ -313,7 +314,7 @@ class VoucherUpdate(EventPermissionRequiredMixin, UpdateView):
|
||||
|
||||
@transaction.atomic
|
||||
def post(self, request, *args, **kwargs):
|
||||
if not request.user.has_event_permission(request.organizer, request.event, 'can_change_vouchers',
|
||||
if not request.user.has_event_permission(request.organizer, request.event, 'event.vouchers:write',
|
||||
request=request):
|
||||
raise PermissionDenied()
|
||||
return super().post(request, *args, **kwargs)
|
||||
@@ -344,7 +345,7 @@ class VoucherUpdate(EventPermissionRequiredMixin, UpdateView):
|
||||
class VoucherCreate(EventPermissionRequiredMixin, CreateView):
|
||||
model = Voucher
|
||||
template_name = 'pretixcontrol/vouchers/detail.html'
|
||||
permission = 'can_change_vouchers'
|
||||
permission = 'event.vouchers:write'
|
||||
context_object_name = 'voucher'
|
||||
|
||||
def form_invalid(self, form):
|
||||
@@ -389,7 +390,7 @@ class VoucherCreate(EventPermissionRequiredMixin, CreateView):
|
||||
|
||||
|
||||
class VoucherGo(EventPermissionRequiredMixin, View):
|
||||
permission = 'can_view_vouchers'
|
||||
permission = 'event.vouchers:read'
|
||||
|
||||
def get_voucher(self, code):
|
||||
return Voucher.objects.get(code__iexact=code, event=self.request.event)
|
||||
@@ -408,7 +409,7 @@ class VoucherGo(EventPermissionRequiredMixin, View):
|
||||
class VoucherBulkCreate(EventPermissionRequiredMixin, AsyncFormView):
|
||||
model = Voucher
|
||||
template_name = 'pretixcontrol/vouchers/bulk.html'
|
||||
permission = 'can_change_vouchers'
|
||||
permission = 'event.vouchers:write'
|
||||
context_object_name = 'voucher'
|
||||
atomic_execute = True
|
||||
|
||||
@@ -540,7 +541,7 @@ class VoucherBulkCreate(EventPermissionRequiredMixin, AsyncFormView):
|
||||
|
||||
|
||||
class VoucherBulkMailPreview(EventPermissionRequiredMixin, View):
|
||||
permission = 'can_change_vouchers'
|
||||
permission = 'event.vouchers:write'
|
||||
|
||||
# return the origin text if key is missing in dict
|
||||
class SafeDict(dict):
|
||||
@@ -579,7 +580,7 @@ class VoucherBulkMailPreview(EventPermissionRequiredMixin, View):
|
||||
|
||||
|
||||
class VoucherRNG(EventPermissionRequiredMixin, View):
|
||||
permission = 'can_change_vouchers'
|
||||
permission = 'event.vouchers:write'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
try:
|
||||
@@ -603,7 +604,7 @@ class VoucherRNG(EventPermissionRequiredMixin, View):
|
||||
|
||||
|
||||
class VoucherBulkAction(EventPermissionRequiredMixin, View):
|
||||
permission = 'can_change_vouchers'
|
||||
permission = 'event.vouchers:write'
|
||||
|
||||
@cached_property
|
||||
def objects(self):
|
||||
|
||||
@@ -64,7 +64,7 @@ from . import UpdateView
|
||||
class AutoAssign(EventPermissionRequiredMixin, AsyncAction, View):
|
||||
task = assign_automatically
|
||||
known_errortypes = ['WaitingListError']
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
def get_success_message(self, value):
|
||||
return _('{num} vouchers have been created and sent out via email.').format(num=value)
|
||||
@@ -154,7 +154,7 @@ class WaitingListQuerySetMixin:
|
||||
|
||||
class WaitingListActionView(EventPermissionRequiredMixin, WaitingListQuerySetMixin, View):
|
||||
model = WaitingListEntry
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
|
||||
def _redirect_back(self):
|
||||
if "next" in self.request.GET and url_has_allowed_host_and_scheme(self.request.GET.get("next"), allowed_hosts=None):
|
||||
@@ -244,7 +244,7 @@ class WaitingListView(EventPermissionRequiredMixin, WaitingListQuerySetMixin, Pa
|
||||
model = WaitingListEntry
|
||||
context_object_name = 'entries'
|
||||
template_name = 'pretixcontrol/waitinglist/index.html'
|
||||
permission = 'can_view_orders'
|
||||
permission = 'event.orders:read'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
@@ -372,7 +372,7 @@ class WaitingListView(EventPermissionRequiredMixin, WaitingListQuerySetMixin, Pa
|
||||
class EntryDelete(EventPermissionRequiredMixin, CompatDeleteView):
|
||||
model = WaitingListEntry
|
||||
template_name = 'pretixcontrol/waitinglist/delete.html'
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
context_object_name = 'entry'
|
||||
|
||||
def get_object(self, queryset=None) -> WaitingListEntry:
|
||||
@@ -405,7 +405,7 @@ class EntryDelete(EventPermissionRequiredMixin, CompatDeleteView):
|
||||
class EntryEdit(EventPermissionRequiredMixin, UpdateView):
|
||||
model = WaitingListEntry
|
||||
template_name = 'pretixcontrol/waitinglist/edit.html'
|
||||
permission = 'can_change_orders'
|
||||
permission = 'event.orders:write'
|
||||
form_class = WaitingListEntryEditForm
|
||||
context_object_name = 'entry'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user