mirror of
https://github.com/pretix/pretix.git
synced 2026-05-06 15:24:02 +00:00
* New models * CRUD UI * UI for adding/removing team members * Log display for teams * Fix invitations, move frontend * Drop old models (incomplete) * Drop more old stuff * Drop even more old stuff * Fix tests * Fix permission test * flake8 fix * Add tests fore the new code * Rebase migrations
This commit is contained in:
@@ -2,6 +2,7 @@ from django import forms
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import RegexValidator
|
||||
from django.db.models import Q
|
||||
from django.utils.timezone import get_current_timezone_name
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from i18nfield.forms import I18nFormField, I18nTextarea
|
||||
@@ -26,7 +27,7 @@ class EventWizardFoundationForm(forms.Form):
|
||||
self.fields['organizer'] = forms.ModelChoiceField(
|
||||
label=_("Organizer"),
|
||||
queryset=Organizer.objects.filter(
|
||||
id__in=self.user.organizer_perms.filter(can_create_events=True).values_list('organizer', flat=True)
|
||||
id__in=self.user.teams.filter(can_create_events=True).values_list('organizer', flat=True)
|
||||
),
|
||||
widget=forms.RadioSelect,
|
||||
empty_label=None,
|
||||
@@ -111,6 +112,16 @@ class EventWizardBasicsForm(I18nModelForm):
|
||||
|
||||
class EventWizardCopyForm(forms.Form):
|
||||
|
||||
@staticmethod
|
||||
def copy_from_queryset(user):
|
||||
return Event.objects.filter(
|
||||
Q(organizer_id__in=user.teams.filter(
|
||||
all_events=True, can_change_event_settings=True, can_change_items=True
|
||||
).values_list('organizer', flat=True)) | Q(id__in=user.teams.filter(
|
||||
can_change_event_settings=True, can_change_items=True
|
||||
).values_list('limit_events__id', flat=True))
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs.pop('organizer')
|
||||
kwargs.pop('locales')
|
||||
@@ -118,11 +129,7 @@ class EventWizardCopyForm(forms.Form):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['copy_from_event'] = forms.ModelChoiceField(
|
||||
label=_("Copy configuration from"),
|
||||
queryset=Event.objects.filter(
|
||||
id__in=self.user.event_perms.filter(
|
||||
can_change_items=True, can_change_settings=True
|
||||
).values_list('event', flat=True)
|
||||
),
|
||||
queryset=EventWizardCopyForm.copy_from_queryset(self.user),
|
||||
widget=forms.RadioSelect,
|
||||
empty_label=_('Do not copy'),
|
||||
required=False
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from django import forms
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from pretix.base.forms import I18nModelForm
|
||||
from pretix.base.models import Organizer
|
||||
from pretix.base.models import Organizer, Team
|
||||
from pretix.multidomain.models import KnownDomain
|
||||
|
||||
|
||||
@@ -65,3 +66,35 @@ class OrganizerUpdateForm(OrganizerForm):
|
||||
instance.get_cache().clear()
|
||||
|
||||
return instance
|
||||
|
||||
|
||||
class TeamForm(forms.ModelForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
organizer = kwargs.pop('organizer')
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['limit_events'].queryset = organizer.events.all()
|
||||
|
||||
class Meta:
|
||||
model = Team
|
||||
fields = ['name', 'all_events', 'limit_events', 'can_create_events',
|
||||
'can_change_teams', 'can_change_organizer_settings',
|
||||
'can_change_event_settings', 'can_change_items',
|
||||
'can_view_orders', 'can_change_orders',
|
||||
'can_view_vouchers', 'can_change_vouchers']
|
||||
widgets = {
|
||||
'limit_events': forms.CheckboxSelectMultiple(attrs={
|
||||
'data-inverse-dependency': '#id_all_events'
|
||||
}),
|
||||
}
|
||||
|
||||
def clean(self):
|
||||
data = super().clean()
|
||||
if self.instance.pk and not data['can_change_teams']:
|
||||
if not self.instance.organizer.teams.exclude(pk=self.instance.pk).filter(
|
||||
can_change_teams=True, 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
|
||||
|
||||
@@ -121,7 +121,10 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
|
||||
'pretix.event.permissions.invited': _('A user has been invited to the event team.'),
|
||||
'pretix.event.permissions.changed': _('A user\'s permissions have been changed.'),
|
||||
'pretix.event.permissions.deleted': _('A user has been removed from the event team.'),
|
||||
'pretix.waitinglist.voucher': _('A voucher has been sent to a person on the waiting list.')
|
||||
'pretix.waitinglist.voucher': _('A voucher has been sent to a person on the waiting list.'),
|
||||
'pretix.team.created': _('The team has been created.'),
|
||||
'pretix.team.changed': _('The team settings have been modified.'),
|
||||
'pretix.team.deleted': _('The team settings has been deleted.'),
|
||||
}
|
||||
|
||||
data = json.loads(logentry.data)
|
||||
@@ -149,6 +152,23 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
|
||||
if logentry.action_type.startswith('pretix.event.tickets.provider.'):
|
||||
return _('The settings of a ticket output provider have been changed.')
|
||||
|
||||
if logentry.action_type == 'pretix.team.member.added':
|
||||
return _('{user} has been added to the team.').format(user=data.get('email'))
|
||||
|
||||
if logentry.action_type == 'pretix.team.member.removed':
|
||||
return _('{user} has been removed from the team.').format(user=data.get('email'))
|
||||
|
||||
if logentry.action_type == 'pretix.team.member.joined':
|
||||
return _('{user} has joined the team using the invite sent to {email}.').format(
|
||||
user=data.get('email'), email=data.get('invite_email')
|
||||
)
|
||||
|
||||
if logentry.action_type == 'pretix.team.invite.created':
|
||||
return _('{user} has been invited to the team.').format(user=data.get('email'))
|
||||
|
||||
if logentry.action_type == 'pretix.team.invite.deleted':
|
||||
return _('The invite for {user} has been revoked.').format(user=data.get('email'))
|
||||
|
||||
if logentry.action_type == 'pretix.user.settings.changed':
|
||||
text = str(_('Your account settings have been changed.'))
|
||||
if 'email' in data:
|
||||
|
||||
@@ -9,9 +9,7 @@ from django.utils.deprecation import MiddlewareMixin
|
||||
from django.utils.encoding import force_str
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from pretix.base.models import (
|
||||
Event, EventPermission, Organizer, OrganizerPermission,
|
||||
)
|
||||
from pretix.base.models import Event, Organizer
|
||||
|
||||
|
||||
class PermissionMiddleware(MiddlewareMixin):
|
||||
@@ -61,53 +59,23 @@ class PermissionMiddleware(MiddlewareMixin):
|
||||
return redirect_to_login(
|
||||
path, resolved_login_url, REDIRECT_FIELD_NAME)
|
||||
|
||||
events = Event.objects.all() if request.user.is_superuser else request.user.events
|
||||
request.user.events_cache = events.order_by(
|
||||
"organizer", "date_from").prefetch_related("organizer")
|
||||
events = request.user.get_events_with_any_permission()
|
||||
request.user.events_cache = events.order_by("organizer", "date_from").prefetch_related("organizer")
|
||||
if 'event' in url.kwargs and 'organizer' in url.kwargs:
|
||||
try:
|
||||
if request.user.is_superuser:
|
||||
request.event = Event.objects.filter(
|
||||
slug=url.kwargs['event'],
|
||||
organizer__slug=url.kwargs['organizer'],
|
||||
).select_related('organizer')[0]
|
||||
request.eventperm = EventPermission(
|
||||
event=request.event,
|
||||
user=request.user
|
||||
)
|
||||
else:
|
||||
request.event = Event.objects.filter(
|
||||
slug=url.kwargs['event'],
|
||||
permitted__id__exact=request.user.id,
|
||||
organizer__slug=url.kwargs['organizer'],
|
||||
).select_related('organizer')[0]
|
||||
request.eventperm = EventPermission.objects.get(
|
||||
event=request.event,
|
||||
user=request.user
|
||||
)
|
||||
request.organizer = request.event.organizer
|
||||
except IndexError:
|
||||
request.event = Event.objects.filter(
|
||||
slug=url.kwargs['event'],
|
||||
organizer__slug=url.kwargs['organizer'],
|
||||
).select_related('organizer').first()
|
||||
if not request.event or not request.user.has_event_permisson(request.event.organizer, request.event):
|
||||
raise Http404(_("The selected event was not found or you "
|
||||
"have no permission to administrate it."))
|
||||
request.organizer = request.event.organizer
|
||||
request.eventpermset = request.user.get_event_permission_set(request.organizer, request.event)
|
||||
elif 'organizer' in url.kwargs:
|
||||
try:
|
||||
if request.user.is_superuser:
|
||||
request.organizer = Organizer.objects.filter(
|
||||
slug=url.kwargs['organizer'],
|
||||
)[0]
|
||||
request.orgaperm = OrganizerPermission(
|
||||
organizer=request.organizer,
|
||||
user=request.user
|
||||
)
|
||||
else:
|
||||
request.organizer = Organizer.objects.filter(
|
||||
slug=url.kwargs['organizer'],
|
||||
permitted__id__exact=request.user.id,
|
||||
)[0]
|
||||
request.orgaperm = OrganizerPermission.objects.get(
|
||||
organizer=request.organizer,
|
||||
user=request.user
|
||||
)
|
||||
except IndexError:
|
||||
request.organizer = Organizer.objects.filter(
|
||||
slug=url.kwargs['organizer'],
|
||||
).first()
|
||||
if not request.organizer or not request.user.has_organizer_permisson(request.organizer):
|
||||
raise Http404(_("The selected organizer was not found or you "
|
||||
"have no permission to administrate it."))
|
||||
request.orgapermset = request.user.get_organizer_permission_set(request.organizer)
|
||||
|
||||
@@ -1,37 +1,29 @@
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from pretix.base.models import EventPermission, OrganizerPermission
|
||||
|
||||
|
||||
def event_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':
|
||||
# Legacy support
|
||||
permission = 'can_change_event_settings'
|
||||
|
||||
def decorator(function):
|
||||
def wrapper(request, *args, **kw):
|
||||
if not request.user.is_authenticated: # NOQA
|
||||
# just a double check, should not ever happen
|
||||
raise PermissionDenied()
|
||||
if request.user.is_superuser:
|
||||
|
||||
allowed = (
|
||||
request.user.is_superuser
|
||||
or request.user.has_event_permisson(request.organizer, request.event, permission)
|
||||
)
|
||||
if allowed:
|
||||
return function(request, *args, **kw)
|
||||
try:
|
||||
perm = EventPermission.objects.get(
|
||||
event=request.event,
|
||||
user=request.user
|
||||
)
|
||||
except EventPermission.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
allowed = not permission
|
||||
try:
|
||||
if permission:
|
||||
allowed = getattr(perm, permission)
|
||||
except AttributeError:
|
||||
pass
|
||||
if allowed or request.user.is_superuser:
|
||||
return function(request, *args, **kw)
|
||||
|
||||
raise PermissionDenied(_('You do not have permission to view this content.'))
|
||||
return wrapper
|
||||
return decorator
|
||||
@@ -55,29 +47,23 @@ 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':
|
||||
# Legacy support
|
||||
permission = 'can_change_organizer_settings'
|
||||
|
||||
def decorator(function):
|
||||
def wrapper(request, *args, **kw):
|
||||
if not request.user.is_authenticated: # NOQA
|
||||
# just a double check, should not ever happen
|
||||
raise PermissionDenied()
|
||||
if request.user.is_superuser:
|
||||
|
||||
allowed = (
|
||||
request.user.is_superuser
|
||||
or request.user.has_organizer_permisson(request.organizer, permission)
|
||||
)
|
||||
if allowed:
|
||||
return function(request, *args, **kw)
|
||||
try:
|
||||
perm = OrganizerPermission.objects.get(
|
||||
organizer=request.organizer,
|
||||
user=request.user
|
||||
)
|
||||
except OrganizerPermission.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
allowed = not permission
|
||||
try:
|
||||
if permission:
|
||||
allowed = getattr(perm, permission)
|
||||
except AttributeError:
|
||||
pass
|
||||
if allowed or request.user.is_superuser:
|
||||
return function(request, *args, **kw)
|
||||
|
||||
raise PermissionDenied(_('You do not have permission to view this content.'))
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{% load i18n %}{% blocktrans with url=url|safe %}Hello,
|
||||
|
||||
you have been invited to the team of an event that uses pretix for their
|
||||
you have been invited to a team on pretix, a platform to perform event
|
||||
ticket sales.
|
||||
|
||||
Event: {{ event }}
|
||||
Organizer: {{ organizer }}
|
||||
Team: {{ team }}
|
||||
|
||||
If you want to join that team, just click on the following link:
|
||||
{{ url }}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
{% load i18n %}{% blocktrans with url=url|safe %}Hello,
|
||||
|
||||
you have been invited to the team of an event organizer that uses pretix
|
||||
for their ticket sales.
|
||||
|
||||
Organizer: {{ organizer }}
|
||||
|
||||
If you want to join that team, just click on the following link:
|
||||
{{ url }}
|
||||
|
||||
If you do not want to join, you can safely ignore or delete this email.
|
||||
|
||||
Best regards,
|
||||
|
||||
Your pretix team
|
||||
{% endblocktrans %}
|
||||
@@ -10,7 +10,7 @@
|
||||
{% trans "Dashboard" %}
|
||||
</a>
|
||||
</li>
|
||||
{% if request.eventperm.can_change_settings or request.eventperm.can_change_permissions %}
|
||||
{% if 'can_change_event_settings' in request.eventpermset or 'can_change_permissions' in request.eventpermset %}
|
||||
<li>
|
||||
<a href="{% url 'control:event.settings' organizer=request.event.organizer.slug event=request.event.slug %}">
|
||||
<i class="fa fa-wrench fa-fw"></i>
|
||||
@@ -18,7 +18,7 @@
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if request.eventperm.can_change_items %}
|
||||
{% if 'can_change_items' in request.eventpermset %}
|
||||
<li>
|
||||
<a href="{% url 'control:event.items' organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="has-children">
|
||||
@@ -55,7 +55,7 @@
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if request.eventperm.can_view_orders %}
|
||||
{% if 'can_view_orders' in request.eventpermset %}
|
||||
<li>
|
||||
<a href="{% url 'control:event.orders' organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
class="has-children">
|
||||
@@ -93,7 +93,7 @@
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if request.eventperm.can_view_vouchers %}
|
||||
{% if 'can_view_vouchers' in request.eventpermset %}
|
||||
<li>
|
||||
<a href="{% url 'control:event.vouchers' organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
{% if url_name == "event.vouchers" %}class="active"{% endif %}>
|
||||
|
||||
@@ -15,9 +15,11 @@
|
||||
{% trans "Customer actions" %}
|
||||
</option>
|
||||
{% for up in userlist %}
|
||||
<option value="{{ up.user_id }}" {% if request.GET.user == up.user_id %}selected="selected"{% endif %}>
|
||||
{{ up.user }}
|
||||
</option>
|
||||
{% if up.user__id %}
|
||||
<option value="{{ up.user__id }}" {% if request.GET.user == up.user__id %}selected="selected"{% endif %}>
|
||||
{{ up.user__email }}
|
||||
</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
<button class="btn btn-primary" type="submit">{% trans "Filter" %}</button>
|
||||
|
||||
@@ -1,88 +1,19 @@
|
||||
{% extends "pretixcontrol/event/settings_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load bootstrap3 %}
|
||||
{% block inside %}
|
||||
<form action="" method="post" class="form-horizontal form-permissions">
|
||||
{% csrf_token %}
|
||||
<fieldset>
|
||||
<legend>{% trans "Permissions" %}</legend>
|
||||
{% bootstrap_formset_errors formset %}
|
||||
{{ formset.management_form }}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "User" %}</th>
|
||||
<th>{% trans "Change settings" %}</th>
|
||||
<th>{% trans "Change products" %}</th>
|
||||
<th>{% trans "View orders" %}</th>
|
||||
<th>{% trans "Change orders" %}</th>
|
||||
<th>{% trans "Change permissions" %}</th>
|
||||
<th>{% trans "View vouchers" %}</th>
|
||||
<th>{% trans "Change vouchers" %}</th>
|
||||
<th>{% trans "Delete" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for form in formset %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ form.id }}
|
||||
{% if form.instance.user %}
|
||||
{{ form.instance.user }}
|
||||
{% else %}
|
||||
{{ form.instance.invite_email }}
|
||||
<span class="fa fa-envelope-o" data-toggle="tooltip"
|
||||
title="{% trans "invited, pending response" %}"></span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ form.can_change_settings }}</td>
|
||||
<td>{{ form.can_change_items }}</td>
|
||||
<td>{{ form.can_view_orders }}</td>
|
||||
<td>{{ form.can_change_orders }}</td>
|
||||
<td>{{ form.can_change_permissions }}</td>
|
||||
<td>{{ form.can_view_vouchers }}</td>
|
||||
<td>{{ form.can_change_vouchers }}</td>
|
||||
<td>{{ form.DELETE }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="9">
|
||||
<strong>{% trans "Adding a new user" %}</strong><br>
|
||||
{% blocktrans trimmed %}
|
||||
To add a new user, you can enter their email address here. If they already have a
|
||||
pretix account, they will immediately be added to the event. Otherwise, they will
|
||||
be sent an email with an invitation.
|
||||
{% endblocktrans %}
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="row-fluid">
|
||||
<div class="col-sm-12">
|
||||
{% bootstrap_field add_form.user layout='inline' %}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ add_form.can_change_settings }}</td>
|
||||
<td>{{ add_form.can_change_items }}</td>
|
||||
<td>{{ add_form.can_view_orders }}</td>
|
||||
<td>{{ add_form.can_change_orders }}</td>
|
||||
<td>{{ add_form.can_change_permissions }}</td>
|
||||
<td>{{ add_form.can_view_vouchers }}</td>
|
||||
<td>{{ add_form.can_change_vouchers }}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
{% trans "Save" %}
|
||||
</button>
|
||||
<div class="section-moved">
|
||||
<img src="{% static "pretixcontrol/img/moved.svg" %}" class="img-moved">
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Permission settings have moved and are now configured as part of an organizer account instead
|
||||
of every event on its own.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<a href="{% url "control:organizer.teams" organizer=request.event.organizer.slug %}"
|
||||
class="btn btn-link btn-lg">{% trans "Go to the organizer team settings" %}</a>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
{% block content %}
|
||||
<h1>{% trans "Settings" %}</h1>
|
||||
<ul class="nav nav-pills">
|
||||
{% if request.eventperm.can_change_settings %}
|
||||
{% if 'can_change_event_settings' in request.eventpermset %}
|
||||
<li {% if "event.settings" == url_name %}class="active"{% endif %}>
|
||||
<a href="{% url 'control:event.settings' organizer=request.event.organizer.slug event=request.event.slug %}">
|
||||
{% trans "General" %}
|
||||
@@ -41,8 +41,6 @@
|
||||
{% trans "Invoicing" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if request.eventperm.can_change_permissions %}
|
||||
<li {% if "event.settings.permissions" == url_name %}class="active"{% endif %}>
|
||||
<a href="{% url 'control:event.settings.permissions' organizer=request.event.organizer.slug event=request.event.slug %}">
|
||||
{% trans "Permissions" %}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
{% block inside %}
|
||||
<h1>
|
||||
{% blocktrans with name=quota.name %}Quota: {{ name }}{% endblocktrans %}
|
||||
{% if request.eventperm.can_change_items %}
|
||||
{% if 'can_change_items' 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>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
{% endblocktrans %}
|
||||
{% include "pretixcontrol/orders/fragment_order_status.html" with order=order class="pull-right" %}
|
||||
</h1>
|
||||
{% if request.eventperm.can_change_orders %}
|
||||
{% if 'can_change_orders' in request.eventpermset %}
|
||||
{% if order.status == 'n' or order.status == 'p' or order.status == 'e' %}
|
||||
<form action="{% url "control:event.order.transition" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}"
|
||||
method="post">
|
||||
@@ -151,7 +151,7 @@
|
||||
<div class="panel panel-default items">
|
||||
<div class="panel-heading">
|
||||
<div class="pull-right">
|
||||
{% if order.changable and request.eventperm.can_change_orders %}
|
||||
{% if order.changable and 'can_change_orders' in request.eventpermset %}
|
||||
<a href="{% url "control:event.order.change" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}">
|
||||
<span class="fa fa-edit"></span>
|
||||
{% trans "Change products" %}
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
{% block content %}
|
||||
<h1>
|
||||
{% blocktrans with name=organizer.name %}Organizer: {{ name }}{% endblocktrans %}
|
||||
<a href="{% url "control:organizer.edit" organizer=organizer.slug %}"
|
||||
class="btn btn-default">
|
||||
<span class="fa fa-edit"></span>
|
||||
{% trans "Edit" %}
|
||||
</a>
|
||||
{% if 'can_change_organizer_settings' in request.orgapermset %}
|
||||
<a href="{% url "control:organizer.edit" organizer=organizer.slug %}"
|
||||
class="btn btn-default">
|
||||
<span class="fa fa-edit"></span>
|
||||
{% trans "Edit" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</h1>
|
||||
<ul class="nav nav-pills">
|
||||
<li {% if "organizer" == url_name %}class="active"{% endif %}>
|
||||
@@ -17,10 +19,10 @@
|
||||
{% trans "Events" %}
|
||||
</a>
|
||||
</li>
|
||||
{% if request.orgaperm.can_change_permissions %}
|
||||
<li {% if "organizer.teams" == url_name %}class="active"{% endif %}>
|
||||
{% if 'can_change_teams' in request.orgapermset %}
|
||||
<li {% if "organizer.team" in url_name %}class="active"{% endif %}>
|
||||
<a href="{% url "control:organizer.teams" organizer=organizer.slug %}">
|
||||
{% trans "Permissions" %}
|
||||
{% trans "Teams" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
{% extends "pretixcontrol/organizers/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap3 %}
|
||||
{% block inner %}
|
||||
<h2>{% trans "Delete team:" %} {{ team.name }}</h2>
|
||||
{% if not possible %}
|
||||
<p>{% blocktrans %}You cannot delete the team because there would be noone left who could change team permissions afterwards.{% endblocktrans %}</p>
|
||||
<div class="form-group submit-group">
|
||||
<a href="{% url "control:organizer.teams" organizer=request.organizer.slug %}" class="btn btn-default btn-cancel">
|
||||
{% trans "Cancel" %}
|
||||
</a>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
{% else %}
|
||||
<form action="" method="post" class="form-horizontal">
|
||||
{% csrf_token %}
|
||||
<p>{% blocktrans %}Are you sure you want to delete the team?{% endblocktrans %}
|
||||
</p>
|
||||
<div class="form-group submit-group">
|
||||
<a href="{% url "control:organizer.teams" organizer=request.organizer.slug%}" class="btn btn-default btn-cancel">
|
||||
{% trans "Cancel" %}
|
||||
</a>
|
||||
<button type="submit" class="btn btn-danger btn-save">
|
||||
{% trans "Delete" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,47 @@
|
||||
{% extends "pretixcontrol/organizers/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap3 %}
|
||||
{% block inner %}
|
||||
{% if team %}
|
||||
<h2>{% trans "Team:" %} {{ team.name }}</h2>
|
||||
{% else %}
|
||||
<h2>{% trans "Create a new team" %}</h2>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
You will be able to add team members in the next step.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% endif %}
|
||||
<form class="form-horizontal" action="" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form_errors form %}
|
||||
<fieldset>
|
||||
<legend>{% trans "General information" %}</legend>
|
||||
{% bootstrap_field form.name layout="horizontal" %}
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{% trans "Organizer permissions" %}</legend>
|
||||
{% bootstrap_field form.can_create_events layout="horizontal" %}
|
||||
{% bootstrap_field form.can_change_teams layout="horizontal" %}
|
||||
{% bootstrap_field form.can_change_organizer_settings layout="horizontal" %}
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{% trans "Event permissions" %}</legend>
|
||||
|
||||
{% bootstrap_field form.all_events layout="horizontal" %}
|
||||
{% bootstrap_field form.limit_events layout="horizontal" %}
|
||||
{% bootstrap_field form.can_change_event_settings layout="horizontal" %}
|
||||
{% bootstrap_field form.can_change_items layout="horizontal" %}
|
||||
{% bootstrap_field form.can_view_orders layout="horizontal" %}
|
||||
{% bootstrap_field form.can_change_orders layout="horizontal" %}
|
||||
{% bootstrap_field form.can_view_vouchers layout="horizontal" %}
|
||||
{% bootstrap_field form.can_change_vouchers layout="horizontal" %}
|
||||
</fieldset>
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
{% trans "Save" %}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,82 @@
|
||||
{% extends "pretixcontrol/organizers/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap3 %}
|
||||
{% block inner %}
|
||||
<h2>
|
||||
{% trans "Team:" %} {{ team.name }}
|
||||
<a href="{% url "control:organizer.team.edit" organizer=organizer.slug team=team.pk %}"
|
||||
class="btn btn-default">
|
||||
<span class="fa fa-edit"></span>
|
||||
{% trans "Edit" %}
|
||||
</a>
|
||||
</h2>
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
<!-- Trick browsers into taking this as a default -->
|
||||
<button type="submit" class="btn btn-primary btn-sm btn-block nearly-gone"></button>
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Member" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for u in team.members.all %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ u.email }}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<button type="submit" name="remove-member" value="{{ u.id }}"
|
||||
class="btn btn-danger btn-sm btn-block">
|
||||
<i class="fa fa-times"></i> {% trans "Remove" %}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% for i in team.invites.all %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ i.email }}
|
||||
<span class="fa fa-envelope-o" data-toggle="tooltip"
|
||||
title="{% trans "invited, pending response" %}"></span>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<button type="submit" name="remove-invite" value="{{ i.id }}"
|
||||
class="btn btn-danger btn-sm btn-block">
|
||||
<i class="fa fa-times"></i> {% trans "Remove" %}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>
|
||||
{% bootstrap_field add_form.user layout='inline' %}<br>
|
||||
{% blocktrans trimmed %}
|
||||
To add a new user, you can enter their email address here. If they already have a
|
||||
pretix account, they will immediately be added to the event. Otherwise, they will
|
||||
be sent an email with an invitation.
|
||||
{% endblocktrans %}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<button type="submit" class="btn btn-primary btn-sm btn-block">
|
||||
<i class="fa fa-plus"></i> {% trans "Add" %}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</form>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
{% trans "Team history" %}
|
||||
</h3>
|
||||
</div>
|
||||
{% include "pretixcontrol/includes/logs.html" with obj=team %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -2,83 +2,57 @@
|
||||
{% load i18n %}
|
||||
{% load bootstrap3 %}
|
||||
{% block inner %}
|
||||
<form action="" method="post" class="form-horizontal form-permissions">
|
||||
{% csrf_token %}
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
You can use the following list to control who can create new events in the name of
|
||||
this organizer and who can add more people to this list. This does <strong>not</strong>
|
||||
control who has access to a particular event. You can control the access to an
|
||||
event in the "Permissions" section of the event's settings. A user does not need to
|
||||
be on the list here to get access to an event.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<p>
|
||||
{% trans "Everyone on this list can control the organizer settings on this page." %}
|
||||
</p>
|
||||
|
||||
{% bootstrap_formset_errors formset %}
|
||||
{{ formset.management_form }}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "User" %}</th>
|
||||
<th>{% trans "Create events" %}</th>
|
||||
<th>{% trans "Change permissions" %}</th>
|
||||
<th>{% trans "Delete" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for form in formset %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ form.id }}
|
||||
{% if form.instance.user %}
|
||||
{{ form.instance.user }}
|
||||
{% else %}
|
||||
{{ form.instance.invite_email }}
|
||||
<span class="fa fa-envelope-o" data-toggle="tooltip"
|
||||
title="{% trans "invited, pending response" %}"></span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ form.can_create_events }}</td>
|
||||
<td>{{ form.can_change_permissions }}</td>
|
||||
<td>{{ form.DELETE }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="9">
|
||||
<strong>{% trans "Adding a new user" %}</strong><br>
|
||||
{% blocktrans trimmed %}
|
||||
To add a new user, you can enter their email address here. If they
|
||||
already have a pretix account, they will immediately be added to the team.
|
||||
Otherwise, they will be sent an email with an invitation.
|
||||
<p>
|
||||
{% trans "The list below shows all teams that exist within this organizer." %}
|
||||
</p>
|
||||
{% if request.user.is_superuser %}
|
||||
<a href="{% url "control:organizer.team.add" organizer=request.organizer.slug %}" class="btn btn-default">
|
||||
<span class="fa fa-plus"></span>
|
||||
{% trans "Create a new team" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Team name" %}</th>
|
||||
<th>{% trans "Members" %}</th>
|
||||
<th>{% trans "Events" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for t in teams %}
|
||||
<tr>
|
||||
<td><strong>
|
||||
<a href="{% url "control:organizer.team" organizer=request.organizer.slug team=t.id %}">
|
||||
{{ t.name }}
|
||||
</a>
|
||||
</strong></td>
|
||||
<td>
|
||||
{{ t.memcount }}
|
||||
{% if t.invcount %}
|
||||
{% blocktrans trimmed with count=t.invcount %}
|
||||
+ {{ count }} invited
|
||||
{% endblocktrans %}
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="row-fluid">
|
||||
<div class="col-sm-12">
|
||||
{% bootstrap_field add_form.user layout='inline' %}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ add_form.can_create_events }}</td>
|
||||
<td>{{ add_form.can_change_permissions }}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
{% trans "Save" %}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if t.all_events %}
|
||||
{% trans "All" %}
|
||||
{% else %}
|
||||
{{ t.eventcount }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<a href="{% url "control:organizer.team" organizer=request.organizer.slug team=t.id %}"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-list"></i></a>
|
||||
<a href="{% url "control:organizer.team.edit" organizer=request.organizer.slug team=t.id %}"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:organizer.team.delete" organizer=request.organizer.slug team=t.id %}"
|
||||
class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="row">
|
||||
{% if request.eventperm.can_change_orders %}
|
||||
{% if 'can_change_orders' 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>
|
||||
@@ -48,7 +48,7 @@
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
<div class="{% if request.eventperm.can_change_orders %}col-md-6{% else %}col-md-12{% endif %}">
|
||||
<div class="{% if 'can_change_orders' in request.eventpermset %}col-md-6{% else %}col-md-12{% endif %}">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{% trans "Sales estimate" %}
|
||||
|
||||
@@ -35,7 +35,14 @@ urlpatterns = [
|
||||
url(r'^organizers/add$', organizer.OrganizerCreate.as_view(), name='organizers.add'),
|
||||
url(r'^organizer/(?P<organizer>[^/]+)/$', organizer.OrganizerDetail.as_view(), name='organizer'),
|
||||
url(r'^organizer/(?P<organizer>[^/]+)/edit$', organizer.OrganizerUpdate.as_view(), name='organizer.edit'),
|
||||
url(r'^organizer/(?P<organizer>[^/]+)/teams$', organizer.OrganizerTeamView.as_view(), name='organizer.teams'),
|
||||
url(r'^organizer/(?P<organizer>[^/]+)/teams$', organizer.TeamListView.as_view(), name='organizer.teams'),
|
||||
url(r'^organizer/(?P<organizer>[^/]+)/team/add$', organizer.TeamCreateView.as_view(), name='organizer.team.add'),
|
||||
url(r'^organizer/(?P<organizer>[^/]+)/team/(?P<team>[^/]+)/$', organizer.TeamMemberView.as_view(),
|
||||
name='organizer.team'),
|
||||
url(r'^organizer/(?P<organizer>[^/]+)/team/(?P<team>[^/]+)/edit$', organizer.TeamUpdateView.as_view(),
|
||||
name='organizer.team.edit'),
|
||||
url(r'^organizer/(?P<organizer>[^/]+)/team/(?P<team>[^/]+)/delete$', organizer.TeamDeleteView.as_view(),
|
||||
name='organizer.team.delete'),
|
||||
url(r'^events/$', main.EventList.as_view(), name='events'),
|
||||
url(r'^events/add$', main.EventWizard.as_view(), name='events.add'),
|
||||
url(r'^event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/', include([
|
||||
|
||||
@@ -9,6 +9,7 @@ from django.contrib.auth import (
|
||||
)
|
||||
from django.contrib.auth.tokens import default_token_generator
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.db import transaction
|
||||
from django.shortcuts import redirect, render
|
||||
from django.urls import reverse
|
||||
from django.utils.functional import cached_property
|
||||
@@ -23,9 +24,7 @@ from u2flib_server.utils import rand_bytes
|
||||
from pretix.base.forms.auth import (
|
||||
LoginForm, PasswordForgotForm, PasswordRecoverForm, RegistrationForm,
|
||||
)
|
||||
from pretix.base.models import (
|
||||
EventPermission, OrganizerPermission, U2FDevice, User,
|
||||
)
|
||||
from pretix.base.models import TeamInvite, U2FDevice, User
|
||||
from pretix.base.services.mail import SendMailException, mail
|
||||
from pretix.helpers.urls import build_absolute_uri
|
||||
|
||||
@@ -108,35 +107,30 @@ def invite(request, token):
|
||||
ctx = {}
|
||||
|
||||
try:
|
||||
perm = EventPermission.objects.get(invite_token=token)
|
||||
desc = perm.event.name
|
||||
except EventPermission.DoesNotExist:
|
||||
try:
|
||||
perm = OrganizerPermission.objects.get(invite_token=token)
|
||||
desc = perm.organizer.name
|
||||
except OrganizerPermission.DoesNotExist:
|
||||
messages.error(request, _('You used an invalid link. Please copy the link from your email to the address bar '
|
||||
'and make sure it is correct and that the link has not been used before.'))
|
||||
return redirect('control:auth.login')
|
||||
inv = TeamInvite.objects.get(token=token)
|
||||
except TeamInvite.DoesNotExist:
|
||||
messages.error(request, _('You used an invalid link. Please copy the link from your email to the address bar '
|
||||
'and make sure it is correct and that the link has not been used before.'))
|
||||
return redirect('control:auth.login')
|
||||
|
||||
if request.user.is_authenticated:
|
||||
try:
|
||||
if isinstance(perm, EventPermission):
|
||||
EventPermission.objects.get(event=perm.event, user=request.user)
|
||||
else:
|
||||
OrganizerPermission.objects.get(organizer=perm.organizer, user=request.user)
|
||||
if inv.team.members.filter(pk=request.user.pk).exists():
|
||||
messages.error(request, _('You cannot accept the invitation for "{}" as you already are part of '
|
||||
'this team.').format(desc))
|
||||
'this team.').format(inv.team.name))
|
||||
return redirect('control:index')
|
||||
else:
|
||||
with transaction.atomic():
|
||||
inv.team.members.add(request.user)
|
||||
inv.team.log_action(
|
||||
'pretix.team.member.joined', data={
|
||||
'email': request.user.email,
|
||||
'invite_email': inv.email,
|
||||
'user': request.user.pk
|
||||
}
|
||||
)
|
||||
inv.delete()
|
||||
messages.success(request, _('You are now part of the team "{}".').format(inv.team.name))
|
||||
return redirect('control:index')
|
||||
except (EventPermission.DoesNotExist, OrganizerPermission.DoesNotExist):
|
||||
pass
|
||||
|
||||
perm.invite_token = None
|
||||
perm.invite_email = None
|
||||
perm.user = request.user
|
||||
perm.save()
|
||||
messages.success(request, _('You have now access to "{}".').format(desc))
|
||||
return redirect('control:index')
|
||||
|
||||
if request.method == 'POST':
|
||||
form = RegistrationForm(data=request.POST)
|
||||
@@ -151,14 +145,20 @@ def invite(request, token):
|
||||
auth_login(request, user)
|
||||
request.session['pretix_auth_login_time'] = int(time.time())
|
||||
|
||||
perm.invite_token = None
|
||||
perm.invite_email = None
|
||||
perm.user = user
|
||||
perm.save()
|
||||
messages.success(request, _('Welcome to pretix! You have now access to "{}".').format(desc))
|
||||
with transaction.atomic():
|
||||
inv.team.members.add(request.user)
|
||||
inv.team.log_action(
|
||||
'pretix.team.member.joined', data={
|
||||
'email': user.email,
|
||||
'invite_email': inv.email,
|
||||
'user': user.pk
|
||||
}
|
||||
)
|
||||
inv.delete()
|
||||
messages.success(request, _('Welcome to pretix! You are now part of the team "{}".').format(inv.team.name))
|
||||
return redirect('control:index')
|
||||
else:
|
||||
form = RegistrationForm(initial={'email': perm.invite_email})
|
||||
form = RegistrationForm(initial={'email': inv.email})
|
||||
ctx['form'] = form
|
||||
return render(request, 'pretixcontrol/auth/invite.html', ctx)
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ from django.utils.formats import date_format
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from pretix.base.models import (
|
||||
Event, Item, Order, OrderPosition, Voucher, WaitingListEntry,
|
||||
Item, Order, OrderPosition, Voucher, WaitingListEntry,
|
||||
)
|
||||
from pretix.control.signals import (
|
||||
event_dashboard_widgets, user_dashboard_widgets,
|
||||
@@ -207,11 +207,12 @@ def event_index(request, organizer, event):
|
||||
for r, result in event_dashboard_widgets.send(sender=request.event):
|
||||
widgets.extend(result)
|
||||
|
||||
can_change_orders = request.user.has_event_permisson(request.organizer, request.event, 'can_change_orders')
|
||||
qs = request.event.logentry_set.all().select_related('user', 'content_type').order_by('-datetime')
|
||||
qs = qs.exclude(action_type__in=OVERVIEW_BLACKLIST)
|
||||
if not request.eventperm.can_view_orders:
|
||||
if not request.user.has_event_permisson(request.organizer, request.event, 'can_view_orders'):
|
||||
qs = qs.exclude(content_type=ContentType.objects.get_for_model(Order))
|
||||
if not request.eventperm.can_view_vouchers:
|
||||
if not request.user.has_event_permisson(request.organizer, request.event, 'can_view_vouchers'):
|
||||
qs = qs.exclude(content_type=ContentType.objects.get_for_model(Voucher))
|
||||
|
||||
a_qs = request.event.requiredaction_set.filter(done=False)
|
||||
@@ -221,7 +222,7 @@ def event_index(request, organizer, event):
|
||||
ctx = {
|
||||
'widgets': rearrange(widgets),
|
||||
'logs': qs[:5],
|
||||
'actions': a_qs[:5] if request.eventperm.can_change_orders else [],
|
||||
'actions': a_qs[:5] if can_change_orders else [],
|
||||
'has_domain': has_domain
|
||||
}
|
||||
|
||||
@@ -242,7 +243,8 @@ def event_index(request, organizer, event):
|
||||
def user_event_widgets(**kwargs):
|
||||
user = kwargs.pop('user')
|
||||
widgets = []
|
||||
events = Event.objects.filter(permitted__id__exact=user.pk).select_related("organizer").order_by('-date_from')
|
||||
|
||||
events = user.get_events_with_any_permission().select_related('organizer')
|
||||
for event in events:
|
||||
widgets.append({
|
||||
'content': '<div class="event">{event}<span class="from">{df}</span><span class="to">{dt}</span></div>'.format(
|
||||
|
||||
@@ -2,14 +2,12 @@ import re
|
||||
from collections import OrderedDict
|
||||
from datetime import timedelta
|
||||
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.files import File
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import transaction
|
||||
from django.forms import modelformset_factory
|
||||
from django.http import HttpResponse, HttpResponseBadRequest, JsonResponse
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.utils import translation
|
||||
@@ -22,14 +20,12 @@ from django.views.generic.base import TemplateView, View
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from pytz import timezone
|
||||
|
||||
from pretix.base.forms import I18nModelForm
|
||||
from pretix.base.models import (
|
||||
CachedTicket, Event, EventPermission, Item, ItemVariation, LogEntry, Order,
|
||||
RequiredAction, User, Voucher,
|
||||
CachedTicket, Event, Item, ItemVariation, LogEntry, Order, RequiredAction,
|
||||
Voucher,
|
||||
)
|
||||
from pretix.base.services import tickets
|
||||
from pretix.base.services.invoices import build_preview_invoice_pdf
|
||||
from pretix.base.services.mail import SendMailException, mail
|
||||
from pretix.base.signals import (
|
||||
event_live_issues, register_payment_providers, register_ticket_outputs,
|
||||
)
|
||||
@@ -50,7 +46,7 @@ class EventUpdate(EventPermissionRequiredMixin, UpdateView):
|
||||
model = Event
|
||||
form_class = EventUpdateForm
|
||||
template_name = 'pretixcontrol/event/settings.html'
|
||||
permission = 'can_change_settings'
|
||||
permission = 'can_change_event_settings'
|
||||
|
||||
@cached_property
|
||||
def object(self) -> Event:
|
||||
@@ -115,7 +111,7 @@ class EventUpdate(EventPermissionRequiredMixin, UpdateView):
|
||||
class EventPlugins(EventPermissionRequiredMixin, TemplateView, SingleObjectMixin):
|
||||
model = Event
|
||||
context_object_name = 'event'
|
||||
permission = 'can_change_settings'
|
||||
permission = 'can_change_event_settings'
|
||||
template_name = 'pretixcontrol/event/plugins.html'
|
||||
|
||||
def get_object(self, queryset=None) -> Event:
|
||||
@@ -178,7 +174,7 @@ class EventPlugins(EventPermissionRequiredMixin, TemplateView, SingleObjectMixin
|
||||
class PaymentSettings(EventPermissionRequiredMixin, TemplateView, SingleObjectMixin):
|
||||
model = Event
|
||||
context_object_name = 'event'
|
||||
permission = 'can_change_settings'
|
||||
permission = 'can_change_event_settings'
|
||||
template_name = 'pretixcontrol/event/payment.html'
|
||||
|
||||
def get_object(self, queryset=None) -> Event:
|
||||
@@ -264,7 +260,7 @@ class PaymentSettings(EventPermissionRequiredMixin, TemplateView, SingleObjectMi
|
||||
|
||||
class EventSettingsFormView(EventPermissionRequiredMixin, FormView):
|
||||
model = Event
|
||||
permission = 'can_change_settings'
|
||||
permission = 'can_change_event_settings'
|
||||
|
||||
def get_context_data(self, *args, **kwargs) -> dict:
|
||||
context = super().get_context_data(*args, **kwargs)
|
||||
@@ -300,7 +296,7 @@ class InvoiceSettings(EventSettingsFormView):
|
||||
model = Event
|
||||
form_class = InvoiceSettingsForm
|
||||
template_name = 'pretixcontrol/event/invoicing.html'
|
||||
permission = 'can_change_settings'
|
||||
permission = 'can_change_event_settings'
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
if 'preview' in self.request.POST:
|
||||
@@ -315,7 +311,7 @@ class InvoiceSettings(EventSettingsFormView):
|
||||
|
||||
|
||||
class InvoicePreview(EventPermissionRequiredMixin, View):
|
||||
permission = 'can_change_settings'
|
||||
permission = 'can_change_event_settings'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
pdf = build_preview_invoice_pdf(request.event)
|
||||
@@ -328,7 +324,7 @@ class DisplaySettings(EventSettingsFormView):
|
||||
model = Event
|
||||
form_class = DisplaySettingsForm
|
||||
template_name = 'pretixcontrol/event/display.html'
|
||||
permission = 'can_change_settings'
|
||||
permission = 'can_change_event_settings'
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
return reverse('control:event.settings.display', kwargs={
|
||||
@@ -364,7 +360,7 @@ class MailSettings(EventSettingsFormView):
|
||||
model = Event
|
||||
form_class = MailSettingsForm
|
||||
template_name = 'pretixcontrol/event/mail.html'
|
||||
permission = 'can_change_settings'
|
||||
permission = 'can_change_event_settings'
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
return reverse('control:event.settings.mail', kwargs={
|
||||
@@ -407,7 +403,7 @@ class MailSettings(EventSettingsFormView):
|
||||
|
||||
|
||||
class MailSettingsPreview(EventPermissionRequiredMixin, View):
|
||||
permission = 'can_change_settings'
|
||||
permission = 'can_change_event_settings'
|
||||
|
||||
# return the origin text if key is missing in dict
|
||||
class SafeDict(dict):
|
||||
@@ -513,7 +509,7 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
|
||||
|
||||
|
||||
class TicketSettingsPreview(EventPermissionRequiredMixin, View):
|
||||
permission = 'can_change_settings'
|
||||
permission = 'can_change_event_settings'
|
||||
|
||||
@cached_property
|
||||
def output(self):
|
||||
@@ -545,7 +541,7 @@ class TicketSettings(EventPermissionRequiredMixin, FormView):
|
||||
model = Event
|
||||
form_class = TicketSettingsForm
|
||||
template_name = 'pretixcontrol/event/tickets.html'
|
||||
permission = 'can_change_settings'
|
||||
permission = 'can_change_event_settings'
|
||||
|
||||
def get_context_data(self, *args, **kwargs) -> dict:
|
||||
context = super().get_context_data(*args, **kwargs)
|
||||
@@ -637,140 +633,12 @@ class TicketSettings(EventPermissionRequiredMixin, FormView):
|
||||
return providers
|
||||
|
||||
|
||||
class EventPermissionForm(I18nModelForm):
|
||||
class Meta:
|
||||
model = EventPermission
|
||||
fields = (
|
||||
'can_change_settings', 'can_change_items', 'can_change_permissions', 'can_view_orders',
|
||||
'can_change_orders', 'can_view_vouchers', 'can_change_vouchers'
|
||||
)
|
||||
|
||||
|
||||
class EventPermissionCreateForm(EventPermissionForm):
|
||||
user = forms.EmailField(required=False, label=_('User'))
|
||||
|
||||
|
||||
class EventPermissions(EventPermissionRequiredMixin, TemplateView):
|
||||
model = Event
|
||||
form_class = TicketSettingsForm
|
||||
template_name = 'pretixcontrol/event/permissions.html'
|
||||
permission = 'can_change_permissions'
|
||||
|
||||
@cached_property
|
||||
def formset(self):
|
||||
fs = modelformset_factory(
|
||||
EventPermission,
|
||||
form=EventPermissionForm,
|
||||
can_delete=True, can_order=False, extra=0
|
||||
)
|
||||
return fs(data=self.request.POST if self.request.method == "POST" else None,
|
||||
prefix="formset",
|
||||
queryset=EventPermission.objects.filter(event=self.request.event))
|
||||
|
||||
@cached_property
|
||||
def add_form(self):
|
||||
return EventPermissionCreateForm(data=self.request.POST if self.request.method == "POST" else None,
|
||||
prefix="add")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['formset'] = self.formset
|
||||
ctx['add_form'] = self.add_form
|
||||
return ctx
|
||||
|
||||
def _send_invite(self, instance):
|
||||
try:
|
||||
mail(
|
||||
instance.invite_email,
|
||||
_('pretix account invitation'),
|
||||
'pretixcontrol/email/invitation.txt',
|
||||
{
|
||||
'user': self,
|
||||
'event': self.request.event.name,
|
||||
'url': build_absolute_uri('control:auth.invite', kwargs={
|
||||
'token': instance.invite_token
|
||||
})
|
||||
},
|
||||
event=None,
|
||||
locale=self.request.LANGUAGE_CODE
|
||||
)
|
||||
except SendMailException:
|
||||
pass # Already logged
|
||||
|
||||
@transaction.atomic
|
||||
def post(self, *args, **kwargs):
|
||||
if self.formset.is_valid() and self.add_form.is_valid():
|
||||
if self.add_form.has_changed():
|
||||
logdata = {
|
||||
k: v for k, v in self.add_form.cleaned_data.items()
|
||||
}
|
||||
|
||||
try:
|
||||
self.add_form.instance.event = self.request.event
|
||||
self.add_form.instance.event_id = self.request.event.id
|
||||
self.add_form.instance.user = User.objects.get(email=self.add_form.cleaned_data['user'])
|
||||
self.add_form.instance.user_id = self.add_form.instance.user.id
|
||||
except User.DoesNotExist:
|
||||
self.add_form.instance.invite_email = self.add_form.cleaned_data['user']
|
||||
if EventPermission.objects.filter(invite_email=self.add_form.instance.invite_email,
|
||||
event=self.request.event).exists():
|
||||
messages.error(self.request, _('This user already has been invited for this event.'))
|
||||
return self.get(*args, **kwargs)
|
||||
|
||||
self.add_form.save()
|
||||
self._send_invite(self.add_form.instance)
|
||||
|
||||
self.request.event.log_action(
|
||||
'pretix.event.permissions.invited', user=self.request.user, data=logdata
|
||||
)
|
||||
else:
|
||||
if EventPermission.objects.filter(user=self.add_form.instance.user,
|
||||
event=self.request.event).exists():
|
||||
messages.error(self.request, _('This user already has permissions for this event.'))
|
||||
return self.get(*args, **kwargs)
|
||||
self.add_form.save()
|
||||
logdata['user'] = self.add_form.instance.user_id
|
||||
self.request.event.log_action(
|
||||
'pretix.event.permissions.added', user=self.request.user, data=logdata
|
||||
)
|
||||
for form in self.formset.forms:
|
||||
if form.has_changed():
|
||||
changedata = {
|
||||
k: form.cleaned_data.get(k) for k in form.changed_data
|
||||
}
|
||||
changedata['user'] = form.instance.user_id
|
||||
self.request.event.log_action(
|
||||
'pretix.event.permissions.changed', user=self.request.user, data=changedata
|
||||
)
|
||||
if form.instance.user_id == self.request.user.pk:
|
||||
if not form.cleaned_data['can_change_permissions'] or form in self.formset.deleted_forms:
|
||||
messages.error(self.request, _('You cannot remove your own permission to view this page.'))
|
||||
return self.get(*args, **kwargs)
|
||||
|
||||
for form in self.formset.deleted_forms:
|
||||
logdata = {
|
||||
k: v for k, v in form.cleaned_data.items()
|
||||
}
|
||||
self.request.event.log_action(
|
||||
'pretix.event.permissions.deleted', user=self.request.user, data=logdata
|
||||
)
|
||||
|
||||
self.formset.save()
|
||||
messages.success(self.request, _('Your changes have been saved.'))
|
||||
return redirect(self.get_success_url())
|
||||
else:
|
||||
messages.error(self.request, _('Your changes could not be saved.'))
|
||||
return self.get(*args, **kwargs)
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
return reverse('control:event.settings.permissions', kwargs={
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
'event': self.request.event.slug
|
||||
})
|
||||
|
||||
|
||||
class EventLive(EventPermissionRequiredMixin, TemplateView):
|
||||
permission = 'can_change_settings'
|
||||
permission = 'can_change_event_settings'
|
||||
template_name = 'pretixcontrol/event/live.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
@@ -840,9 +708,9 @@ class EventLog(EventPermissionRequiredMixin, ListView):
|
||||
def get_queryset(self):
|
||||
qs = self.request.event.logentry_set.all().select_related('user', 'content_type').order_by('-datetime')
|
||||
qs = qs.exclude(action_type__in=OVERVIEW_BLACKLIST)
|
||||
if not self.request.eventperm.can_view_orders:
|
||||
if not self.request.user.has_event_permisson(self.request.organizer, self.request.event, 'can_view_orders'):
|
||||
qs = qs.exclude(content_type=ContentType.objects.get_for_model(Order))
|
||||
if not self.request.eventperm.can_view_vouchers:
|
||||
if not self.request.user.has_event_permisson(self.request.organizer, self.request.event, 'can_view_vouchers'):
|
||||
qs = qs.exclude(content_type=ContentType.objects.get_for_model(Voucher))
|
||||
|
||||
if self.request.GET.get('user') == 'yes':
|
||||
@@ -856,7 +724,7 @@ class EventLog(EventPermissionRequiredMixin, ListView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data()
|
||||
ctx['userlist'] = self.request.event.user_perms.select_related('user')
|
||||
ctx['userlist'] = self.request.event.logentry_set.order_by().distinct().values('user__id', 'user__email')
|
||||
return ctx
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.generic import ListView
|
||||
from formtools.wizard.views import SessionWizardView
|
||||
|
||||
from pretix.base.models import Event, EventPermission, OrganizerPermission
|
||||
from pretix.base.models import Event, Team
|
||||
from pretix.control.forms.event import (
|
||||
EventWizardBasicsForm, EventWizardCopyForm, EventWizardFoundationForm,
|
||||
)
|
||||
@@ -20,22 +20,13 @@ class EventList(ListView):
|
||||
template_name = 'pretixcontrol/events/index.html'
|
||||
|
||||
def get_queryset(self):
|
||||
if self.request.user.is_superuser:
|
||||
return Event.objects.all().select_related("organizer").prefetch_related(
|
||||
"_settings_objects", "organizer___settings_objects"
|
||||
)
|
||||
else:
|
||||
return Event.objects.filter(
|
||||
permitted__id__exact=self.request.user.pk
|
||||
).select_related("organizer").prefetch_related(
|
||||
"_settings_objects", "organizer___settings_objects"
|
||||
)
|
||||
return self.request.user.get_events_with_any_permission().select_related('organizer').prefetch_related(
|
||||
'_settings_objects', 'organizer___settings_objects'
|
||||
)
|
||||
|
||||
|
||||
def condition_copy(wizard):
|
||||
return EventPermission.objects.filter(
|
||||
user=wizard.request.user, can_change_settings=True, can_change_items=True
|
||||
).exists()
|
||||
return EventWizardCopyForm.copy_from_queryset(wizard.request.user).exists()
|
||||
|
||||
|
||||
class EventWizard(SessionWizardView):
|
||||
@@ -55,8 +46,7 @@ class EventWizard(SessionWizardView):
|
||||
|
||||
def get_context_data(self, form, **kwargs):
|
||||
ctx = super().get_context_data(form, **kwargs)
|
||||
ctx['has_organizer'] = OrganizerPermission.objects.filter(user=self.request.user,
|
||||
can_create_events=True).exists()
|
||||
ctx['has_organizer'] = self.request.user.teams.filter(can_create_events=True).exists()
|
||||
return ctx
|
||||
|
||||
def get_form_kwargs(self, step=None):
|
||||
@@ -81,7 +71,20 @@ class EventWizard(SessionWizardView):
|
||||
event.organizer = foundation_data['organizer']
|
||||
event.plugins = settings.PRETIX_PLUGINS_DEFAULT
|
||||
form_dict['basics'].save()
|
||||
EventPermission.objects.create(event=event, user=self.request.user)
|
||||
|
||||
has_control_rights = self.request.user.teams.filter(
|
||||
organizer=event.organizer, all_events=True, can_change_event_settings=True, can_change_items=True,
|
||||
can_change_orders=True, can_change_vouchers=True
|
||||
).exists()
|
||||
if not has_control_rights:
|
||||
t = Team.objects.create(
|
||||
organizer=event.organizer, name=_('Team {event}').format(event=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
|
||||
)
|
||||
t.members.add(self.request.user)
|
||||
t.limit_events.add(event)
|
||||
|
||||
logdata = {}
|
||||
for f in form_list:
|
||||
|
||||
@@ -3,16 +3,19 @@ from django.contrib import messages
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import transaction
|
||||
from django.forms import modelformset_factory
|
||||
from django.shortcuts import redirect
|
||||
from django.db.models import Count
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.generic import CreateView, DetailView, ListView, UpdateView
|
||||
from django.views.generic import (
|
||||
CreateView, DeleteView, DetailView, ListView, UpdateView,
|
||||
)
|
||||
|
||||
from pretix.base.forms import I18nModelForm
|
||||
from pretix.base.models import Organizer, OrganizerPermission, User
|
||||
from pretix.base.models import Organizer, Team, TeamInvite, User
|
||||
from pretix.base.services.mail import SendMailException, mail
|
||||
from pretix.control.forms.organizer import OrganizerForm, OrganizerUpdateForm
|
||||
from pretix.control.forms.organizer import (
|
||||
OrganizerForm, OrganizerUpdateForm, TeamForm,
|
||||
)
|
||||
from pretix.control.permissions import OrganizerPermissionRequiredMixin
|
||||
from pretix.control.signals import nav_organizer
|
||||
from pretix.helpers.urls import build_absolute_uri
|
||||
@@ -28,25 +31,14 @@ class OrganizerList(ListView):
|
||||
if self.request.user.is_superuser:
|
||||
return Organizer.objects.all()
|
||||
else:
|
||||
return Organizer.objects.filter(
|
||||
permitted__id__exact=self.request.user.pk
|
||||
)
|
||||
return Organizer.objects.filter(pk__in=self.request.user.teams.values_list('organizer', flat=True))
|
||||
|
||||
|
||||
class OrganizerPermissionForm(I18nModelForm):
|
||||
class Meta:
|
||||
model = OrganizerPermission
|
||||
fields = (
|
||||
'can_create_events', 'can_change_permissions'
|
||||
)
|
||||
|
||||
|
||||
class OrganizerPermissionCreateForm(OrganizerPermissionForm):
|
||||
class InviteForm(forms.Form):
|
||||
user = forms.EmailField(required=False, label=_('User'))
|
||||
|
||||
|
||||
class OrganizerDetailViewMixin:
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['nav_organizer'] = []
|
||||
@@ -82,135 +74,12 @@ class OrganizerTeamView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMix
|
||||
permission = 'can_change_permissions'
|
||||
context_object_name = 'organizer'
|
||||
|
||||
@cached_property
|
||||
def formset(self):
|
||||
fs = modelformset_factory(
|
||||
OrganizerPermission,
|
||||
form=OrganizerPermissionForm,
|
||||
can_delete=True, can_order=False, extra=0
|
||||
)
|
||||
return fs(
|
||||
data=(
|
||||
self.request.POST
|
||||
if self.request.method == "POST" and 'formset-TOTAL_FORMS' in self.request.POST
|
||||
else None
|
||||
),
|
||||
prefix="formset",
|
||||
queryset=OrganizerPermission.objects.filter(organizer=self.request.organizer)
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def add_form(self):
|
||||
return OrganizerPermissionCreateForm(
|
||||
data=(
|
||||
self.request.POST
|
||||
if self.request.method == "POST" and 'formset-TOTAL_FORMS' in self.request.POST
|
||||
else None
|
||||
),
|
||||
prefix="add"
|
||||
)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['formset'] = self.formset
|
||||
ctx['add_form'] = self.add_form
|
||||
return ctx
|
||||
|
||||
def _send_invite(self, instance):
|
||||
try:
|
||||
mail(
|
||||
instance.invite_email,
|
||||
_('pretix account invitation'),
|
||||
'pretixcontrol/email/invitation_organizer.txt',
|
||||
{
|
||||
'user': self,
|
||||
'organizer': self.request.organizer.name,
|
||||
'url': build_absolute_uri('control:auth.invite', kwargs={
|
||||
'token': instance.invite_token
|
||||
})
|
||||
},
|
||||
event=None,
|
||||
locale=self.request.LANGUAGE_CODE
|
||||
)
|
||||
except SendMailException:
|
||||
pass # Already logged
|
||||
|
||||
@transaction.atomic
|
||||
def post(self, *args, **kwargs):
|
||||
if self.formset.is_valid() and self.add_form.is_valid():
|
||||
if self.add_form.has_changed():
|
||||
logdata = {
|
||||
k: v for k, v in self.add_form.cleaned_data.items()
|
||||
}
|
||||
|
||||
try:
|
||||
self.add_form.instance.organizer = self.request.organizer
|
||||
self.add_form.instance.organizer_id = self.request.organizer.id
|
||||
self.add_form.instance.user = User.objects.get(email=self.add_form.cleaned_data['user'])
|
||||
self.add_form.instance.user_id = self.add_form.instance.user.id
|
||||
except User.DoesNotExist:
|
||||
self.add_form.instance.invite_email = self.add_form.cleaned_data['user']
|
||||
if OrganizerPermission.objects.filter(invite_email=self.add_form.instance.invite_email,
|
||||
organizer=self.request.organizer).exists():
|
||||
messages.error(self.request, _('This user already has been invited for this team.'))
|
||||
return self.get(*args, **kwargs)
|
||||
|
||||
self.add_form.save()
|
||||
self._send_invite(self.add_form.instance)
|
||||
|
||||
self.request.organizer.log_action(
|
||||
'pretix.organizer.permissions.invited', user=self.request.user, data=logdata
|
||||
)
|
||||
else:
|
||||
if OrganizerPermission.objects.filter(user=self.add_form.instance.user,
|
||||
organizer=self.request.organizer).exists():
|
||||
messages.error(self.request, _('This user already has permissions for this team.'))
|
||||
return self.get(*args, **kwargs)
|
||||
self.add_form.save()
|
||||
logdata['user'] = self.add_form.instance.user_id
|
||||
self.request.organizer.log_action(
|
||||
'pretix.organizer.permissions.added', user=self.request.user, data=logdata
|
||||
)
|
||||
for form in self.formset.forms:
|
||||
if form.has_changed():
|
||||
changedata = {
|
||||
k: form.cleaned_data.get(k) for k in form.changed_data
|
||||
}
|
||||
changedata['user'] = form.instance.user_id
|
||||
self.request.organizer.log_action(
|
||||
'pretix.organizer.permissions.changed', user=self.request.user, data=changedata
|
||||
)
|
||||
if form.instance.user_id == self.request.user.pk:
|
||||
if not form.cleaned_data['can_change_permissions'] or form in self.formset.deleted_forms:
|
||||
messages.error(self.request, _('You cannot remove your own permission to view this page.'))
|
||||
return self.get(*args, **kwargs)
|
||||
|
||||
for form in self.formset.deleted_forms:
|
||||
logdata = {
|
||||
k: v for k, v in form.cleaned_data.items()
|
||||
}
|
||||
self.request.organizer.log_action(
|
||||
'pretix.organizer.permissions.deleted', user=self.request.user, data=logdata
|
||||
)
|
||||
|
||||
self.formset.save()
|
||||
messages.success(self.request, _('Your changes have been saved.'))
|
||||
return redirect(self.get_success_url())
|
||||
else:
|
||||
messages.error(self.request, _('Your changes could not be saved.'))
|
||||
return self.get(*args, **kwargs)
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
return reverse('control:organizer.teams', kwargs={
|
||||
'organizer': self.request.organizer.slug,
|
||||
})
|
||||
|
||||
|
||||
class OrganizerUpdate(OrganizerPermissionRequiredMixin, UpdateView):
|
||||
model = Organizer
|
||||
form_class = OrganizerUpdateForm
|
||||
template_name = 'pretixcontrol/organizers/edit.html'
|
||||
permission = None
|
||||
permission = 'can_change_organizer_settings'
|
||||
context_object_name = 'organizer'
|
||||
|
||||
def get_object(self, queryset=None) -> Organizer:
|
||||
@@ -243,14 +112,268 @@ class OrganizerCreate(CreateView):
|
||||
raise PermissionDenied() # TODO
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
messages.success(self.request, _('The new organizer has been created.'))
|
||||
ret = super().form_valid(form)
|
||||
OrganizerPermission.objects.create(
|
||||
organizer=form.instance, user=self.request.user,
|
||||
can_create_events=True
|
||||
t = Team.objects.create(
|
||||
organizer=form.instance, name=_('Administrators'),
|
||||
all_events=True, can_create_events=True, can_change_teams=True,
|
||||
can_change_organizer_settings=True, 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
|
||||
)
|
||||
t.members.add(self.request.user)
|
||||
return ret
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
return reverse('control:organizers')
|
||||
|
||||
|
||||
class TeamListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, ListView):
|
||||
model = Team
|
||||
template_name = 'pretixcontrol/organizers/teams.html'
|
||||
permission = 'can_change_teams'
|
||||
context_object_name = 'teams'
|
||||
|
||||
def get_queryset(self):
|
||||
return self.request.organizer.teams.annotate(
|
||||
memcount=Count('members', distinct=True),
|
||||
eventcount=Count('limit_events', distinct=True),
|
||||
invcount=Count('invites', distinct=True)
|
||||
).all()
|
||||
|
||||
|
||||
class TeamCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, CreateView):
|
||||
model = Team
|
||||
template_name = 'pretixcontrol/organizers/team_edit.html'
|
||||
permission = 'can_change_teams'
|
||||
form_class = TeamForm
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs['organizer'] = self.request.organizer
|
||||
return kwargs
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
return get_object_or_404(Team, organizer=self.request.organizer, pk=self.kwargs.get('team'))
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('control:organizer.team', kwargs={
|
||||
'organizer': self.request.organizer.slug,
|
||||
'team': self.object.pk
|
||||
})
|
||||
|
||||
def form_valid(self, form):
|
||||
messages.success(self.request, _('The team has been created. You can now add members to the team.'))
|
||||
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
|
||||
})
|
||||
return ret
|
||||
|
||||
def form_invalid(self, form):
|
||||
messages.error(self.request, _('Your changes could not be saved.'))
|
||||
return super().form_invalid(form)
|
||||
|
||||
|
||||
class TeamUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, UpdateView):
|
||||
model = Team
|
||||
template_name = 'pretixcontrol/organizers/team_edit.html'
|
||||
permission = 'can_change_teams'
|
||||
context_object_name = 'team'
|
||||
form_class = TeamForm
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs['organizer'] = self.request.organizer
|
||||
return kwargs
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
return get_object_or_404(Team, organizer=self.request.organizer, pk=self.kwargs.get('team'))
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('control:organizer.team', kwargs={
|
||||
'organizer': self.request.organizer.slug,
|
||||
'team': self.object.pk
|
||||
})
|
||||
|
||||
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
|
||||
})
|
||||
messages.success(self.request, _('Your changes have been saved.'))
|
||||
return super().form_valid(form)
|
||||
|
||||
def form_invalid(self, form):
|
||||
messages.error(self.request, _('Your changes could not be saved.'))
|
||||
return super().form_invalid(form)
|
||||
|
||||
|
||||
class TeamDeleteView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, DeleteView):
|
||||
model = Team
|
||||
template_name = 'pretixcontrol/organizers/team_delete.html'
|
||||
permission = 'can_change_teams'
|
||||
context_object_name = 'team'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
return get_object_or_404(Team, organizer=self.request.organizer, pk=self.kwargs.get('team'))
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('control:organizer.teams', kwargs={
|
||||
'organizer': self.request.organizer.slug,
|
||||
})
|
||||
|
||||
def get_context_data(self, *args, **kwargs) -> dict:
|
||||
context = super().get_context_data(*args, **kwargs)
|
||||
context['possible'] = self.is_allowed()
|
||||
return context
|
||||
|
||||
def is_allowed(self) -> bool:
|
||||
return self.request.organizer.teams.exclude(pk=self.kwargs.get('team')).filter(
|
||||
can_change_teams=True, members__isnull=False
|
||||
).exists()
|
||||
|
||||
@transaction.atomic
|
||||
def delete(self, request, *args, **kwargs):
|
||||
success_url = self.get_success_url()
|
||||
self.object = self.get_object()
|
||||
if self.is_allowed():
|
||||
self.object.log_action('pretix.team.deleted', user=self.request.user)
|
||||
self.object.delete()
|
||||
messages.success(request, _('The selected team has been deleted.'))
|
||||
return redirect(success_url)
|
||||
else:
|
||||
messages.error(request, _('The selected team cannot be deleted.'))
|
||||
return redirect(success_url)
|
||||
|
||||
|
||||
class TeamMemberView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, DetailView):
|
||||
template_name = 'pretixcontrol/organizers/team_members.html'
|
||||
context_object_name = 'team'
|
||||
permission = 'can_change_teams'
|
||||
model = Team
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
return get_object_or_404(Team, organizer=self.request.organizer, pk=self.kwargs.get('team'))
|
||||
|
||||
@cached_property
|
||||
def add_form(self):
|
||||
return InviteForm(data=self.request.POST if self.request.method == "POST" else None)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['add_form'] = self.add_form
|
||||
return ctx
|
||||
|
||||
def _send_invite(self, instance):
|
||||
try:
|
||||
mail(
|
||||
instance.email,
|
||||
_('pretix account invitation'),
|
||||
'pretixcontrol/email/invitation.txt',
|
||||
{
|
||||
'user': self,
|
||||
'organizer': self.request.organizer.name,
|
||||
'team': instance.team.name,
|
||||
'url': build_absolute_uri('control:auth.invite', kwargs={
|
||||
'token': instance.token
|
||||
})
|
||||
},
|
||||
event=None,
|
||||
locale=self.request.LANGUAGE_CODE
|
||||
)
|
||||
except SendMailException:
|
||||
pass # Already logged
|
||||
|
||||
@transaction.atomic
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
|
||||
if 'remove-member' in request.POST:
|
||||
try:
|
||||
user = User.objects.get(pk=request.POST.get('remove-member'))
|
||||
except User.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
other_admin_teams = self.request.organizer.teams.exclude(pk=self.object.pk).filter(
|
||||
can_change_teams=True, members__isnull=False
|
||||
).exists()
|
||||
if not other_admin_teams and self.object.can_change_teams and self.object.members.count() == 1:
|
||||
messages.error(self.request, _('You cannot remove the last member from this team as noone would '
|
||||
'be left with the permission to change teams.'))
|
||||
return redirect(self.get_success_url())
|
||||
else:
|
||||
self.object.members.remove(user)
|
||||
self.object.log_action(
|
||||
'pretix.team.member.removed', user=self.request.user, data={
|
||||
'email': user.email,
|
||||
'user': user.pk
|
||||
}
|
||||
)
|
||||
messages.success(self.request, _('The member has been removed from the team.'))
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
elif 'remove-invite' in request.POST:
|
||||
try:
|
||||
invite = self.object.invites.get(pk=request.POST.get('remove-invite'))
|
||||
except TeamInvite.DoesNotExist:
|
||||
messages.error(self.request, _('Invalid invite selected.'))
|
||||
return redirect(self.get_success_url())
|
||||
else:
|
||||
invite.delete()
|
||||
self.object.log_action(
|
||||
'pretix.team.invite.deleted', user=self.request.user, data={
|
||||
'email': invite.email
|
||||
}
|
||||
)
|
||||
messages.success(self.request, _('The invite has been revoked.'))
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
elif self.add_form.is_valid() and self.add_form.has_changed():
|
||||
|
||||
try:
|
||||
user = User.objects.get(email=self.add_form.cleaned_data['user'])
|
||||
except User.DoesNotExist:
|
||||
if self.object.invites.filter(email=self.add_form.cleaned_data['user']).exists():
|
||||
messages.error(self.request, _('This user already has been invited for this team.'))
|
||||
return self.get(request, *args, **kwargs)
|
||||
|
||||
invite = self.object.invites.create(email=self.add_form.cleaned_data['user'])
|
||||
self._send_invite(invite)
|
||||
self.object.log_action(
|
||||
'pretix.team.invite.created', user=self.request.user, data={
|
||||
'email': self.add_form.cleaned_data['user']
|
||||
}
|
||||
)
|
||||
messages.success(self.request, _('The new member has been invited to the team.'))
|
||||
return redirect(self.get_success_url())
|
||||
else:
|
||||
if self.object.members.filter(pk=user.pk).exists():
|
||||
messages.error(self.request, _('This user already has permissions for this team.'))
|
||||
return self.get(request, *args, **kwargs)
|
||||
|
||||
self.object.members.add(user)
|
||||
self.object.log_action(
|
||||
'pretix.team.member.added', user=self.request.user,
|
||||
data={
|
||||
'email': user.email,
|
||||
'user': user.pk,
|
||||
}
|
||||
)
|
||||
messages.success(self.request, _('The new member has been added to the team.'))
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
else:
|
||||
messages.error(self.request, _('Your changes could not be saved.'))
|
||||
return self.get(request, *args, **kwargs)
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
return reverse('control:organizer.team', kwargs={
|
||||
'organizer': self.request.organizer.slug,
|
||||
'team': self.object.pk
|
||||
})
|
||||
|
||||
@@ -44,7 +44,7 @@ class WaitingListView(EventPermissionRequiredMixin, ListView):
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
if 'assign' in request.POST:
|
||||
if not request.eventperm.can_change_orders:
|
||||
if not request.user.has_event_permisson(request.organizer, request.event, 'can_change_orders'):
|
||||
messages.error(request, _('You do not have permission to do this'))
|
||||
return redirect(reverse('control:event.orders.waitinglist', kwargs={
|
||||
'event': request.event.slug,
|
||||
|
||||
Reference in New Issue
Block a user