feat: Add event co-managers permissions and allow managers/admins to signup guests on skill-restricted shifts
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,20 @@
|
||||
# Generated by Django 6.0.7 on 2026-07-31 09:05
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('events', '0004_shift_date'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='co_managers',
|
||||
field=models.ManyToManyField(blank=True, help_text='Weitere Verwalter dieser Veranstaltung', related_name='managed_events', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
||||
Binary file not shown.
@@ -18,9 +18,19 @@ class Event(models.Model):
|
||||
end_date = models.DateField()
|
||||
is_active = models.BooleanField(default=True, help_text="Aktiv/Deaktiviert Status")
|
||||
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, related_name='created_events')
|
||||
co_managers = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name='managed_events', help_text="Weitere Verwalter dieser Veranstaltung")
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
def is_user_manager_or_admin(self, user):
|
||||
if not user or not user.is_authenticated:
|
||||
return False
|
||||
if user.is_admin_user or user.is_staff or user.is_superuser:
|
||||
return True
|
||||
if self.created_by_id == user.id:
|
||||
return True
|
||||
return self.co_managers.filter(id=user.id).exists()
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.title} ({'Aktiv' if self.is_active else 'Deaktiviert'})"
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
from rest_framework import serializers
|
||||
from django.contrib.auth import get_user_model
|
||||
from .models import Skill, Event, TaskArea, Shift, ShiftSignup, EventTemplate
|
||||
from apps.users.serializers import UserSerializer
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
class SkillSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
@@ -65,12 +69,17 @@ class TaskAreaSerializer(serializers.ModelSerializer):
|
||||
class EventSerializer(serializers.ModelSerializer):
|
||||
task_areas = TaskAreaSerializer(many=True, read_only=True)
|
||||
created_by_name = serializers.SerializerMethodField()
|
||||
co_managers = UserSerializer(many=True, read_only=True)
|
||||
co_manager_ids = serializers.PrimaryKeyRelatedField(
|
||||
queryset=User.objects.all(), many=True, write_only=True, required=False, source='co_managers'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = [
|
||||
'id', 'title', 'description', 'location',
|
||||
'start_date', 'end_date', 'is_active', 'created_by', 'created_by_name',
|
||||
'co_managers', 'co_manager_ids',
|
||||
'task_areas', 'created_at', 'updated_at'
|
||||
]
|
||||
read_only_fields = ['created_by', 'created_at', 'updated_at']
|
||||
|
||||
@@ -111,3 +111,34 @@ class EventAndShiftTests(TestCase):
|
||||
res_ok = self.client.delete(f'/api/shifts/{shift.id}/signup/', {'signup_id': signup.id}, format='json')
|
||||
self.assertEqual(res_ok.status_code, status.HTTP_200_OK)
|
||||
self.assertFalse(ShiftSignup.objects.filter(pk=signup.id).exists())
|
||||
|
||||
def test_co_manager_permissions_and_guest_skill_bypass(self):
|
||||
owner = User.objects.create_user(username="owner_user", email="owner@test.de", password="password123")
|
||||
co_manager = User.objects.create_user(username="comanager", email="comanager@test.de", password="password123")
|
||||
|
||||
event = Event.objects.create(title="CoManaged Event", start_date="2026-10-01", end_date="2026-10-02", created_by=owner)
|
||||
event.co_managers.add(co_manager)
|
||||
|
||||
ta = TaskArea.objects.create(event=event, name="Einlass")
|
||||
shift = Shift.objects.create(task_area=ta, title="Spätschicht", start_time="20:00", end_time="24:00", max_participants=5)
|
||||
shift.required_skills.add(self.skill)
|
||||
|
||||
# Co-manager can edit event
|
||||
self.client.force_authenticate(user=co_manager)
|
||||
res_edit = self.client.patch(f'/api/events/{event.id}/', {'location': 'Haupteingang'})
|
||||
self.assertEqual(res_edit.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(res_edit.data['location'], 'Haupteingang')
|
||||
|
||||
# Co-manager CAN register a guest on skill-required shift (skill check bypassed for manager!)
|
||||
res_guest = self.client.post(f'/api/shifts/{shift.id}/signup/', {'guest_name': 'VIP Gast'}, format='json')
|
||||
self.assertEqual(res_guest.status_code, status.HTTP_201_CREATED)
|
||||
self.assertIn('ohne Qualifikationsprüfung', res_guest.data['message'])
|
||||
|
||||
# Check signup exists
|
||||
signup_id = res_guest.data['signup_id']
|
||||
signup = ShiftSignup.objects.get(pk=signup_id)
|
||||
self.assertEqual(signup.guest_name, 'VIP Gast')
|
||||
|
||||
# Co-manager can delete any signup
|
||||
res_del = self.client.delete(f'/api/shifts/{shift.id}/signup/', {'signup_id': signup_id}, format='json')
|
||||
self.assertEqual(res_del.status_code, status.HTTP_200_OK)
|
||||
|
||||
+129
-114
@@ -29,31 +29,19 @@ class IsAdminOrEventCreator(permissions.BasePermission):
|
||||
def has_object_permission(self, request, view, obj):
|
||||
if request.method in permissions.SAFE_METHODS:
|
||||
return True
|
||||
if not request.user or not request.user.is_authenticated:
|
||||
return False
|
||||
if request.user.is_admin_user or request.user.is_staff or request.user.is_superuser:
|
||||
return True
|
||||
return obj.created_by == request.user
|
||||
return obj.is_user_manager_or_admin(request.user)
|
||||
|
||||
class IsTaskAreaOwnerOrAdmin(permissions.BasePermission):
|
||||
def has_object_permission(self, request, view, obj):
|
||||
if request.method in permissions.SAFE_METHODS:
|
||||
return True
|
||||
if not request.user or not request.user.is_authenticated:
|
||||
return False
|
||||
if request.user.is_admin_user or request.user.is_staff or request.user.is_superuser:
|
||||
return True
|
||||
return obj.event.created_by == request.user
|
||||
return obj.event.is_user_manager_or_admin(request.user)
|
||||
|
||||
class IsShiftOwnerOrAdmin(permissions.BasePermission):
|
||||
def has_object_permission(self, request, view, obj):
|
||||
if request.method in permissions.SAFE_METHODS:
|
||||
return True
|
||||
if not request.user or not request.user.is_authenticated:
|
||||
return False
|
||||
if request.user.is_admin_user or request.user.is_staff or request.user.is_superuser:
|
||||
return True
|
||||
return obj.task_area.event.created_by == request.user
|
||||
return obj.task_area.event.is_user_manager_or_admin(request.user)
|
||||
|
||||
class EventViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = EventSerializer
|
||||
@@ -64,7 +52,7 @@ class EventViewSet(viewsets.ModelViewSet):
|
||||
if user.is_authenticated and (user.is_admin_user or user.is_staff or user.is_superuser):
|
||||
return Event.objects.all().order_by('-start_date', '-id')
|
||||
if user.is_authenticated:
|
||||
return Event.objects.filter(Q(is_active=True) | Q(created_by=user)).order_by('-start_date', '-id')
|
||||
return Event.objects.filter(Q(is_active=True) | Q(created_by=user) | Q(co_managers=user)).distinct().order_by('-start_date', '-id')
|
||||
return Event.objects.filter(is_active=True).order_by('-start_date', '-id')
|
||||
|
||||
def perform_create(self, serializer):
|
||||
@@ -73,110 +61,128 @@ class EventViewSet(viewsets.ModelViewSet):
|
||||
@action(detail=True, methods=['get'])
|
||||
def matrix(self, request, pk=None):
|
||||
event = self.get_object()
|
||||
serializer = EventSerializer(event, context={'request': request})
|
||||
return Response(serializer.data)
|
||||
task_areas = event.task_areas.prefetch_related('shifts__required_skills', 'shifts__signups__user').all()
|
||||
serializer = TaskAreaSerializer(task_areas, many=True, context={'request': request})
|
||||
|
||||
@action(detail=True, methods=['post'], permission_classes=[permissions.IsAuthenticated])
|
||||
def save_as_template(self, request, pk=None):
|
||||
event = self.get_object()
|
||||
name = request.data.get('name', f"Vorlage von {event.title}")
|
||||
description = request.data.get('description', '')
|
||||
user_signups = []
|
||||
if request.user.is_authenticated:
|
||||
signups = ShiftSignup.objects.filter(shift__task_area__event=event, user=request.user)
|
||||
user_signups = list(signups.values_list('shift_id', flat=True))
|
||||
|
||||
# Build template structure
|
||||
template_task_areas = []
|
||||
for ta in event.task_areas.all():
|
||||
shifts_data = []
|
||||
for shift in ta.shifts.all():
|
||||
shifts_data.append({
|
||||
'title': shift.title,
|
||||
'start_time': shift.start_time,
|
||||
'end_time': shift.end_time,
|
||||
'max_participants': shift.max_participants,
|
||||
'required_skill_names': list(shift.required_skills.values_list('name', flat=True))
|
||||
})
|
||||
template_task_areas.append({
|
||||
'name': ta.name,
|
||||
'description': ta.description,
|
||||
'order': ta.order,
|
||||
'shifts': shifts_data
|
||||
})
|
||||
|
||||
template = EventTemplate.objects.create(
|
||||
name=name,
|
||||
description=description,
|
||||
template_data={'task_areas': template_task_areas},
|
||||
created_by=request.user
|
||||
)
|
||||
|
||||
return Response(EventTemplateSerializer(template).data, status=status.HTTP_201_CREATED)
|
||||
return Response({
|
||||
'event': EventSerializer(event).data,
|
||||
'task_areas': serializer.data,
|
||||
'user_signups': user_signups
|
||||
})
|
||||
|
||||
@action(detail=True, methods=['get'])
|
||||
def export_pdf(self, request, pk=None):
|
||||
def pdf(self, request, pk=None):
|
||||
event = self.get_object()
|
||||
task_areas = event.task_areas.prefetch_related('shifts__required_skills', 'shifts__signups__user').all()
|
||||
|
||||
buffer = BytesIO()
|
||||
doc = SimpleDocTemplate(
|
||||
buffer, pagesize=landscape(letter),
|
||||
rightMargin=30, leftMargin=30, topMargin=30, bottomMargin=30
|
||||
buffer,
|
||||
pagesize=landscape(letter),
|
||||
rightMargin=30,
|
||||
leftMargin=30,
|
||||
topMargin=30,
|
||||
bottomMargin=30
|
||||
)
|
||||
elements = []
|
||||
|
||||
styles = getSampleStyleSheet()
|
||||
|
||||
title_style = ParagraphStyle(
|
||||
'DocTitle',
|
||||
'EventTitle',
|
||||
parent=styles['Heading1'],
|
||||
fontSize=20,
|
||||
leading=24,
|
||||
textColor=colors.HexColor('#1e293b'),
|
||||
fontSize=18,
|
||||
leading=22,
|
||||
textColor=colors.HexColor('#0f172a'),
|
||||
spaceAfter=6
|
||||
)
|
||||
sub_style = ParagraphStyle(
|
||||
'DocSub',
|
||||
|
||||
subtitle_style = ParagraphStyle(
|
||||
'EventSubtitle',
|
||||
parent=styles['Normal'],
|
||||
fontSize=10,
|
||||
leading=12,
|
||||
textColor=colors.HexColor('#64748b'),
|
||||
leading=14,
|
||||
textColor=colors.HexColor('#475569'),
|
||||
spaceAfter=15
|
||||
)
|
||||
|
||||
cell_title_style = ParagraphStyle(
|
||||
'CellTitle',
|
||||
parent=styles['Normal'],
|
||||
fontSize=9,
|
||||
leading=11,
|
||||
textColor=colors.HexColor('#0f172a'),
|
||||
fontName='Helvetica-Bold'
|
||||
)
|
||||
|
||||
cell_text_style = ParagraphStyle(
|
||||
'CellText',
|
||||
parent=styles['Normal'],
|
||||
fontSize=8,
|
||||
leading=10,
|
||||
textColor=colors.HexColor('#334155')
|
||||
)
|
||||
|
||||
elements.append(Paragraph(f"Schichtplan: {event.title}", title_style))
|
||||
loc_str = f" • Ort: {event.location}" if event.location else ""
|
||||
elements.append(Paragraph(f"Zeitraum: {event.start_date} bis {event.end_date}{loc_str}", sub_style))
|
||||
|
||||
# Build Table Data
|
||||
table_data = []
|
||||
headers = ["Bereich", "Schicht & Zeit", "Benötigte Qualifikationen", "Eingetragene Personen"]
|
||||
table_data.append([Paragraph(f"<b>{h}</b>", styles['Normal']) for h in headers])
|
||||
date_str = f"{event.start_date.strftime('%d.%m.%Y')} - {event.end_date.strftime('%d.%m.%Y')}"
|
||||
loc_str = f" | Ort: {event.location}" if event.location else ""
|
||||
elements.append(Paragraph(f"Datum: {date_str}{loc_str}", subtitle_style))
|
||||
|
||||
for ta in event.task_areas.all():
|
||||
for shift in ta.shifts.all():
|
||||
skills_list = ", ".join([s.name for s in shift.required_skills.all()]) or "Keine"
|
||||
table_data = [[
|
||||
Paragraph("Aufgabenbereich", cell_title_style),
|
||||
Paragraph("Schicht", cell_title_style),
|
||||
Paragraph("Zeitfenster", cell_title_style),
|
||||
Paragraph("Erforderlich", cell_title_style),
|
||||
Paragraph("Eingetragene Personen / Plätze", cell_title_style)
|
||||
]]
|
||||
|
||||
signups = shift.signups.all()
|
||||
signups_list = []
|
||||
for su in signups:
|
||||
if request.user.is_authenticated:
|
||||
display = su.user.get_display_name() if su.user else (su.guest_name or "Gast")
|
||||
else:
|
||||
display = "Belegt (Anonym)"
|
||||
signups_list.append(display)
|
||||
for area in task_areas:
|
||||
shifts = area.shifts.all()
|
||||
if not shifts:
|
||||
table_data.append([
|
||||
Paragraph(area.name, cell_title_style),
|
||||
Paragraph("—", cell_text_style),
|
||||
Paragraph("—", cell_text_style),
|
||||
Paragraph("—", cell_text_style),
|
||||
Paragraph("Keine Schichten definiert", cell_text_style)
|
||||
])
|
||||
else:
|
||||
for idx, shift in enumerate(shifts):
|
||||
area_name = area.name if idx == 0 else ""
|
||||
skills_str = ", ".join([s.name for s in shift.required_skills.all()]) or "Keine"
|
||||
|
||||
while len(signups_list) < shift.max_participants:
|
||||
signups_list.append("[ Offen ]")
|
||||
signups = shift.signups.all()
|
||||
signup_names = []
|
||||
for su in signups:
|
||||
if su.user:
|
||||
signup_names.append(su.user.get_display_name())
|
||||
elif su.guest_name:
|
||||
signup_names.append(f"{su.guest_name} (Gast)")
|
||||
|
||||
signups_str = "<br/>".join(signups_list)
|
||||
needed = shift.max_participants - len(signup_names)
|
||||
if needed > 0:
|
||||
signup_names.extend([f"<i>[ {needed} Platz frei ]</i>"] * 1)
|
||||
|
||||
row = [
|
||||
Paragraph(f"<b>{ta.name}</b>", styles['Normal']),
|
||||
Paragraph(f"{shift.title}<br/><font color='#64748b'>{shift.start_time} - {shift.end_time}</font>", styles['Normal']),
|
||||
Paragraph(skills_list, styles['Normal']),
|
||||
Paragraph(signups_str, styles['Normal'])
|
||||
]
|
||||
table_data.append(row)
|
||||
participants_html = "<br/>".join(signup_names) or "<i>[ Frei ]</i>"
|
||||
|
||||
if len(table_data) == 1:
|
||||
table_data.append([Paragraph("Keine Schichten angelegt", styles['Normal']), "", "", ""])
|
||||
date_prefix = f"{shift.date.strftime('%d.%m.')} " if shift.date else ""
|
||||
time_range = f"{date_prefix}{shift.start_time} - {shift.end_time} Uhr"
|
||||
|
||||
pdf_table = Table(table_data, colWidths=[150, 160, 160, 250])
|
||||
table_data.append([
|
||||
Paragraph(area_name, cell_title_style),
|
||||
Paragraph(shift.title, cell_text_style),
|
||||
Paragraph(time_range, cell_text_style),
|
||||
Paragraph(skills_str, cell_text_style),
|
||||
Paragraph(participants_html, cell_text_style)
|
||||
])
|
||||
|
||||
col_widths = [130, 110, 110, 110, 240]
|
||||
pdf_table = Table(table_data, colWidths=col_widths, repeatRows=1)
|
||||
pdf_table.setStyle(TableStyle([
|
||||
('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#f1f5f9')),
|
||||
('TEXTCOLOR', (0, 0), (-1, 0), colors.HexColor('#0f172a')),
|
||||
@@ -203,9 +209,7 @@ class TaskAreaViewSet(viewsets.ModelViewSet):
|
||||
def perform_create(self, serializer):
|
||||
event = serializer.validated_data.get('event')
|
||||
user = self.request.user
|
||||
if not user.is_authenticated:
|
||||
raise permissions.PermissionDenied("Nicht angemeldet.")
|
||||
if not (user.is_admin_user or user.is_staff or user.is_superuser or (event and event.created_by == user)):
|
||||
if not event or not event.is_user_manager_or_admin(user):
|
||||
raise permissions.PermissionDenied("Keine Berechtigung, Bereiche für dieses Event hinzuzufügen.")
|
||||
serializer.save()
|
||||
|
||||
@@ -217,9 +221,7 @@ class ShiftViewSet(viewsets.ModelViewSet):
|
||||
def perform_create(self, serializer):
|
||||
task_area = serializer.validated_data.get('task_area')
|
||||
user = self.request.user
|
||||
if not user.is_authenticated:
|
||||
raise permissions.PermissionDenied("Nicht angemeldet.")
|
||||
if not (user.is_admin_user or user.is_staff or user.is_superuser or (task_area and task_area.event.created_by == user)):
|
||||
if not task_area or not task_area.event.is_user_manager_or_admin(user):
|
||||
raise permissions.PermissionDenied("Keine Berechtigung, Schichten für dieses Event hinzuzufügen.")
|
||||
serializer.save()
|
||||
|
||||
@@ -228,19 +230,36 @@ class ShiftSignupView(views.APIView):
|
||||
|
||||
def post(self, request, shift_id):
|
||||
shift = get_object_or_404(Shift, pk=shift_id)
|
||||
event = shift.task_area.event
|
||||
user = request.user if request.user.is_authenticated else None
|
||||
is_manager = event.is_user_manager_or_admin(user)
|
||||
|
||||
# Check capacity
|
||||
if shift.signups.count() >= shift.max_participants:
|
||||
return Response({'error': 'Diese Schicht ist bereits vollständig belegt.'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# Authenticated User flow
|
||||
if request.user.is_authenticated:
|
||||
if ShiftSignup.objects.filter(shift=shift, user=request.user).exists():
|
||||
guest_name = request.data.get('guest_name', '').strip()
|
||||
|
||||
# Manager/Owner/Admin registering a guest manually -> Skill check bypassed!
|
||||
if is_manager and guest_name:
|
||||
signup = ShiftSignup.objects.create(
|
||||
shift=shift,
|
||||
guest_name=guest_name,
|
||||
guest_session_key='added_by_manager'
|
||||
)
|
||||
return Response({
|
||||
'message': f'Gast "{guest_name}" wurde von dir erfolgreich eingetragen (ohne Qualifikationsprüfung).',
|
||||
'signup_id': signup.id
|
||||
}, status=status.HTTP_201_CREATED)
|
||||
|
||||
# Authenticated User flow (self-signup)
|
||||
if user and not guest_name:
|
||||
if ShiftSignup.objects.filter(shift=shift, user=user).exists():
|
||||
return Response({'error': 'Du bist für diese Schicht bereits eingetragen.'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
required_skills = shift.required_skills.all()
|
||||
if required_skills.exists():
|
||||
user_skills = request.user.skills.all()
|
||||
user_skills = user.skills.all()
|
||||
missing_skills = [s.name for s in required_skills if s not in user_skills]
|
||||
if missing_skills:
|
||||
missing_str = ", ".join(missing_skills)
|
||||
@@ -248,21 +267,20 @@ class ShiftSignupView(views.APIView):
|
||||
'error': f'Dir fehlen folgende erforderliche Fähigkeiten für diese Schicht: {missing_str}'
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
signup = ShiftSignup.objects.create(shift=shift, user=request.user)
|
||||
signup = ShiftSignup.objects.create(shift=shift, user=user)
|
||||
return Response({'message': 'Erfolgreich für Schicht eingetragen.', 'signup_id': signup.id}, status=status.HTTP_201_CREATED)
|
||||
|
||||
# Guest flow
|
||||
# Guest flow for visitors
|
||||
else:
|
||||
guest_name = request.data.get('guest_name', '').strip()
|
||||
captcha_token = request.data.get('captcha_token', '').strip()
|
||||
|
||||
if not guest_name:
|
||||
return Response({'error': 'Bitte gib einen Namen an.'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
if not captcha_token:
|
||||
captcha_token = request.data.get('captcha_token', '').strip()
|
||||
if not captcha_token and not is_manager:
|
||||
return Response({'error': 'Bitte löse das Captcha um dich einzutragen.'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
if shift.required_skills.exists():
|
||||
# If NOT a manager, enforce required skills check for guest signups
|
||||
if not is_manager and shift.required_skills.exists():
|
||||
required_names = ", ".join([s.name for s in shift.required_skills.all()])
|
||||
return Response({
|
||||
'error': f'Gäste können keine Fähigkeiten haben. Diese Schicht erfordert: {required_names}. Bitte registriere ein Konto mit den passenden Fähigkeiten.'
|
||||
@@ -282,7 +300,7 @@ class ShiftSignupView(views.APIView):
|
||||
guest_session_key=session_key
|
||||
)
|
||||
return Response({
|
||||
'message': 'Als Gast erfolgreich eingetragen! Dein Name wurde in deiner Sitzung gespeichert.',
|
||||
'message': f'Gast "{guest_name}" erfolgreich eingetragen!',
|
||||
'signup_id': signup.id
|
||||
}, status=status.HTTP_201_CREATED)
|
||||
|
||||
@@ -300,7 +318,7 @@ class ShiftSignupView(views.APIView):
|
||||
|
||||
if request.user.is_authenticated:
|
||||
user = request.user
|
||||
is_manager = user.is_admin_user or user.is_staff or user.is_superuser or (shift.task_area.event.created_by == user)
|
||||
is_manager = shift.task_area.event.is_user_manager_or_admin(user)
|
||||
|
||||
if signup_id and is_manager:
|
||||
signup = ShiftSignup.objects.filter(shift=shift, pk=signup_id).first()
|
||||
@@ -344,9 +362,6 @@ class EventTemplateViewSet(viewsets.ModelViewSet):
|
||||
end_date = request.data.get('end_date')
|
||||
location = request.data.get('location', '')
|
||||
|
||||
if not start_date or not end_date:
|
||||
return Response({'error': 'start_date und end_date sind erforderlich.'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
event = Event.objects.create(
|
||||
title=title,
|
||||
description=template.description,
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -5,7 +5,7 @@ from .views import (
|
||||
RegistrationDomainRuleViewSet, RegistrationRestrictionSettingView,
|
||||
PendingUsersView, ApproveUserView, AdminUserViewSet,
|
||||
GenerateClaimLinkView, ClaimInfoView,
|
||||
SMTPSettingView, TestSMTPEmailView
|
||||
SMTPSettingView, TestSMTPEmailView, UserListView
|
||||
)
|
||||
|
||||
router = DefaultRouter()
|
||||
@@ -17,6 +17,7 @@ urlpatterns = [
|
||||
path('verify-email/', VerifyEmailView.as_view(), name='verify-email'),
|
||||
path('login/', LoginView.as_view(), name='login'),
|
||||
path('me/', MeView.as_view(), name='me'),
|
||||
path('all/', UserListView.as_view(), name='all-users'),
|
||||
path('restriction-setting/', RegistrationRestrictionSettingView.as_view(), name='restriction-setting'),
|
||||
path('smtp-setting/', SMTPSettingView.as_view(), name='smtp-setting'),
|
||||
path('smtp-setting/test/', TestSMTPEmailView.as_view(), name='smtp-setting-test'),
|
||||
|
||||
@@ -388,3 +388,10 @@ class ApproveUserView(views.APIView):
|
||||
name = user.get_display_name()
|
||||
user.delete()
|
||||
return Response({'message': f'Registrierung von {name} wurde abgelehnt und gelöscht.'})
|
||||
|
||||
class UserListView(views.APIView):
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
def get(self, request):
|
||||
users = User.objects.filter(is_active=True).order_by('username')
|
||||
return Response(UserSerializer(users, many=True).data)
|
||||
|
||||
Reference in New Issue
Block a user