mirror of
https://github.com/pretix/pretix.git
synced 2026-10-01 19:14:43 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
164ad660e8 | ||
|
|
7bc9ca261b | ||
|
|
ac8ac20f8d |
@@ -553,13 +553,23 @@ class TeamInviteSerializer(serializers.ModelSerializer):
|
||||
|
||||
class TeamAPITokenSerializer(serializers.ModelSerializer):
|
||||
active = serializers.BooleanField(default=True, read_only=True)
|
||||
sales_channel = serializers.SlugRelatedField(
|
||||
slug_field="identifier",
|
||||
queryset=SalesChannel.objects.none(),
|
||||
required=False,
|
||||
allow_empty=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = TeamAPIToken
|
||||
fields = (
|
||||
'id', 'name', 'active'
|
||||
'id', 'name', 'active', 'sales_channel'
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['sales_channel'].queryset = self.context['organizer'].organizer.sales_channels.all() if 'organizer' in self.context else SalesChannel.objects.none()
|
||||
|
||||
|
||||
class TeamMemberSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
|
||||
@@ -28,6 +28,7 @@ from django.contrib.auth.hashers import make_password
|
||||
from django.db import transaction
|
||||
from django.db.models import OuterRef, Q, Subquery, Sum
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.http.response import Http404
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.timezone import now
|
||||
@@ -467,7 +468,7 @@ class TeamInviteViewSet(CreateModelMixin, DestroyModelMixin, viewsets.ReadOnlyMo
|
||||
serializer.save(team=self.team)
|
||||
|
||||
|
||||
class TeamAPITokenViewSet(CreateModelMixin, DestroyModelMixin, viewsets.ReadOnlyModelViewSet):
|
||||
class TeamAPITokenViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = TeamAPITokenSerializer
|
||||
queryset = TeamAPIToken.objects.none()
|
||||
permission = 'organizer.teams:write'
|
||||
@@ -506,6 +507,20 @@ class TeamAPITokenViewSet(CreateModelMixin, DestroyModelMixin, viewsets.ReadOnly
|
||||
self.team.log_action(
|
||||
'pretix.team.token.created', auth=self.request.auth, user=self.request.user, data={
|
||||
'name': instance.name,
|
||||
'sales_channel': instance.sales_channel,
|
||||
'id': instance.pk
|
||||
}
|
||||
)
|
||||
|
||||
@transaction.atomic()
|
||||
def perform_update(self, serializer):
|
||||
if not self.team.active:
|
||||
raise Http404()
|
||||
instance = serializer.save(team=self.team)
|
||||
self.team.log_action(
|
||||
'pretix.team.token.changed', auth=self.request.auth, user=self.request.user, data={
|
||||
'name': instance.name,
|
||||
'sales_channel': instance.sales_channel,
|
||||
'id': instance.pk
|
||||
}
|
||||
)
|
||||
|
||||
@@ -178,6 +178,7 @@ class Device(LoggedModel):
|
||||
info = models.JSONField(
|
||||
null=True, blank=True,
|
||||
)
|
||||
sales_channel = models.ForeignKey("SalesChannel", on_delete=models.SET_NULL, null=True, blank=True)
|
||||
|
||||
objects = ScopedManager(organizer='organizer')
|
||||
|
||||
|
||||
@@ -535,11 +535,14 @@ class TeamAPIToken(models.Model):
|
||||
:type active: bool
|
||||
:param token: The secret required to submit to the API
|
||||
:type token: str
|
||||
:param sales_channel: Default value for sales channel on API calls with this token
|
||||
:type sales_channel: SalesChannel
|
||||
"""
|
||||
team = models.ForeignKey(Team, related_name="tokens", on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=190)
|
||||
active = models.BooleanField(default=True)
|
||||
token = models.CharField(default=generate_api_token, max_length=64)
|
||||
sales_channel = models.ForeignKey("SalesChannel", on_delete=models.CASCADE, null=True, blank=True)
|
||||
|
||||
def get_event_permission_set(self, organizer, event) -> set:
|
||||
"""
|
||||
|
||||
@@ -489,6 +489,7 @@ class DeviceForm(forms.ModelForm):
|
||||
help_text=self.fields['security_profile'].help_text,
|
||||
choices=[(k, v.verbose_name) for k, v in get_all_security_profiles().items()],
|
||||
)
|
||||
self.fields["sales_channel"].queryset = organizer.sales_channels.all()
|
||||
|
||||
def clean(self):
|
||||
d = super().clean()
|
||||
@@ -499,7 +500,7 @@ class DeviceForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Device
|
||||
fields = ['name', 'all_events', 'limit_events', 'security_profile', 'gate']
|
||||
fields = ['name', 'all_events', 'limit_events', 'security_profile', 'gate', 'sales_channel']
|
||||
widgets = {
|
||||
'limit_events': forms.CheckboxSelectMultiple(attrs={
|
||||
'data-inverse-dependency': '#id_all_events',
|
||||
@@ -507,7 +508,8 @@ class DeviceForm(forms.ModelForm):
|
||||
}),
|
||||
}
|
||||
field_classes = {
|
||||
'limit_events': SafeEventMultipleChoiceField
|
||||
'limit_events': SafeEventMultipleChoiceField,
|
||||
"sales_channel": SafeModelChoiceField,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
<legend>{% trans "Advanced settings" %}</legend>
|
||||
{% bootstrap_field form.security_profile layout="control" %}
|
||||
{% bootstrap_field form.gate layout="control" %}
|
||||
{% bootstrap_field form.sales_channel layout="control" %}
|
||||
</fieldset>
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Sales channel" %}</th>
|
||||
<th width="150"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -113,6 +114,13 @@
|
||||
</del>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-muted">
|
||||
{% if t.sales_channel %}
|
||||
{{ t.sales_channel }}
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
{% if t.active %}
|
||||
<button type="submit" name="remove-token" value="{{ t.id }}"
|
||||
@@ -129,6 +137,9 @@
|
||||
<td>
|
||||
{% bootstrap_field add_token_form.name layout='inline' %}<br>
|
||||
</td>
|
||||
<td>
|
||||
{% bootstrap_field add_token_form.sales_channel layout='inline' %}<br>
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
<button type="submit" class="btn btn-primary btn-sm btn-block">
|
||||
<i class="fa fa-plus"></i> {% trans "Add" %}
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
<p>
|
||||
{% trans "The list below shows all teams that exist within this organizer." %}
|
||||
</p>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<details class="panel panel-default" open data-stateful="organizers_teams_filter">
|
||||
<summary class="panel-heading">
|
||||
<h3 class="panel-title">{% trans "Filter" %}</h3>
|
||||
</div>
|
||||
</summary>
|
||||
<form class="panel-body filter-form" action="" method="get">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
@@ -24,7 +24,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</details>
|
||||
<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" %}
|
||||
@@ -65,11 +65,11 @@
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
<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>
|
||||
class="btn btn-default btn-sm" title="{% trans "Team members" %}"><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>
|
||||
class="btn btn-default btn-sm" title="{% trans "Edit" %}"><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>
|
||||
class="btn btn-danger btn-sm" title="{% trans "Delete" %}"><i class="fa fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@@ -40,6 +40,8 @@ from collections import Counter
|
||||
from datetime import time, timedelta
|
||||
from decimal import Decimal
|
||||
from hashlib import sha1
|
||||
|
||||
from django_scopes.forms import SafeModelChoiceField
|
||||
from itertools import groupby
|
||||
from json import JSONDecodeError
|
||||
|
||||
@@ -180,6 +182,11 @@ class InviteForm(forms.Form):
|
||||
|
||||
class TokenForm(forms.Form):
|
||||
name = forms.CharField(required=False, label=_('Token name'))
|
||||
sales_channel = SafeModelChoiceField(required=False, label=_('Sales channel'), queryset=SalesChannel.objects.none())
|
||||
|
||||
def __init__(self, organizer, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["sales_channel"].queryset = organizer.sales_channels.all()
|
||||
|
||||
|
||||
class OrganizerDetailViewMixin:
|
||||
@@ -1019,7 +1026,7 @@ class TeamMemberView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
|
||||
@cached_property
|
||||
def add_token_form(self):
|
||||
return TokenForm(data=(self.request.POST
|
||||
if self.request.method == "POST" and "name" in self.request.POST else None))
|
||||
if self.request.method == "POST" and "name" in self.request.POST else None), organizer=self.request.organizer)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
@@ -1164,10 +1171,14 @@ class TeamMemberView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
elif "name" in self.request.POST and self.add_token_form.is_valid() and self.add_token_form.has_changed():
|
||||
token = self.object.tokens.create(name=self.add_token_form.cleaned_data['name'])
|
||||
token = self.object.tokens.create(
|
||||
name=self.add_token_form.cleaned_data['name'],
|
||||
sales_channel=self.add_token_form.cleaned_data['sales_channel']
|
||||
)
|
||||
self.object.log_action(
|
||||
'pretix.team.token.created', user=self.request.user, data={
|
||||
'name': self.add_token_form.cleaned_data['name'],
|
||||
'sales_channel': self.add_token_form.cleaned_data['sales_channel'],
|
||||
'id': token.pk
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user