Baseline für Vision 2026: Veranstaltungsmodul + ausstehende Änderungen
Alle bestehenden, nicht commiteten Änderungen als Ausgangsbasis für den vision-2026 Branch übernommen (Veranstaltungsmodul, Serienbrief, etc.). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@ from django.db.models import (Avg, Count, DecimalField, F, IntegerField, Q,
|
||||
from django.db.models.functions import Cast, Coalesce, NullIf, Replace
|
||||
from django.http import HttpResponse, JsonResponse
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django_otp.decorators import otp_required
|
||||
@@ -6430,13 +6431,20 @@ def user_login(request):
|
||||
|
||||
log_login(request, user)
|
||||
|
||||
messages.success(request, f"Willkommen zurück, {user.username}!")
|
||||
|
||||
# Redirect to safe next URL path or home
|
||||
# Determine redirect target
|
||||
next_param = request.GET.get("next") or request.POST.get("next")
|
||||
if next_param and next_param.startswith("/"):
|
||||
return redirect(next_param)
|
||||
return redirect("stiftung:home")
|
||||
if not next_param or not next_param.startswith("/"):
|
||||
next_param = reverse("stiftung:home")
|
||||
|
||||
# Check if user has 2FA enabled - redirect to verification first
|
||||
has_2fa = TOTPDevice.objects.filter(user=user, confirmed=True).exists()
|
||||
if has_2fa:
|
||||
from urllib.parse import urlencode
|
||||
verify_url = reverse("stiftung:two_factor_verify") + "?" + urlencode({"next": next_param})
|
||||
return redirect(verify_url)
|
||||
|
||||
messages.success(request, f"Willkommen zurück, {user.username}!")
|
||||
return redirect(next_param)
|
||||
else:
|
||||
messages.error(request, "Ungültige Anmeldedaten.")
|
||||
else:
|
||||
@@ -8685,3 +8693,153 @@ def veranstaltung_serienbrief_pdf(request, pk):
|
||||
response = HttpResponse(pdf, content_type="application/pdf")
|
||||
response["Content-Disposition"] = f'attachment; filename="{filename}"'
|
||||
return response
|
||||
|
||||
|
||||
@login_required
|
||||
def veranstaltung_serienbrief_vorschau(request, pk):
|
||||
"""HTML-Vorschau des Serienbriefs im Browser (kein PDF-Download)"""
|
||||
veranstaltung = get_object_or_404(Veranstaltung, pk=pk)
|
||||
teilnehmer = veranstaltung.teilnehmer.all().order_by("nachname", "vorname")
|
||||
return render(
|
||||
request,
|
||||
"stiftung/veranstaltung/serienbrief_vorschau.html",
|
||||
{
|
||||
"veranstaltung": veranstaltung,
|
||||
"teilnehmer": teilnehmer,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
def veranstaltung_create(request):
|
||||
"""Neue Veranstaltung erstellen"""
|
||||
from .forms import VeranstaltungForm
|
||||
|
||||
if request.method == "POST":
|
||||
form = VeranstaltungForm(request.POST)
|
||||
if form.is_valid():
|
||||
veranstaltung = form.save()
|
||||
messages.success(request, f'Veranstaltung "{veranstaltung.titel}" wurde erstellt.')
|
||||
return redirect("stiftung:veranstaltung_detail", pk=veranstaltung.pk)
|
||||
else:
|
||||
messages.error(request, "Bitte korrigieren Sie die Fehler im Formular.")
|
||||
else:
|
||||
form = VeranstaltungForm()
|
||||
|
||||
return render(request, "stiftung/veranstaltung/form.html", {
|
||||
"form": form,
|
||||
"title": "Neue Veranstaltung erstellen",
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
def veranstaltung_update(request, pk):
|
||||
"""Veranstaltung bearbeiten (inkl. Serienbrief-Felder)"""
|
||||
from .forms import VeranstaltungForm
|
||||
|
||||
veranstaltung = get_object_or_404(Veranstaltung, pk=pk)
|
||||
|
||||
if request.method == "POST":
|
||||
form = VeranstaltungForm(request.POST, instance=veranstaltung)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
messages.success(request, f'Veranstaltung "{veranstaltung.titel}" wurde aktualisiert.')
|
||||
return redirect("stiftung:veranstaltung_detail", pk=veranstaltung.pk)
|
||||
else:
|
||||
messages.error(request, "Bitte korrigieren Sie die Fehler im Formular.")
|
||||
else:
|
||||
form = VeranstaltungForm(instance=veranstaltung)
|
||||
|
||||
return render(request, "stiftung/veranstaltung/form.html", {
|
||||
"form": form,
|
||||
"veranstaltung": veranstaltung,
|
||||
"title": f"Veranstaltung bearbeiten: {veranstaltung.titel}",
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
def veranstaltung_delete(request, pk):
|
||||
"""Veranstaltung löschen"""
|
||||
veranstaltung = get_object_or_404(Veranstaltung, pk=pk)
|
||||
|
||||
if request.method == "POST":
|
||||
titel = veranstaltung.titel
|
||||
veranstaltung.delete()
|
||||
messages.success(request, f'Veranstaltung "{titel}" wurde gelöscht.')
|
||||
return redirect("stiftung:veranstaltung_list")
|
||||
|
||||
return render(request, "stiftung/veranstaltung/delete.html", {
|
||||
"veranstaltung": veranstaltung,
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
def teilnehmer_create(request, veranstaltung_pk):
|
||||
"""Teilnehmer zu einer Veranstaltung hinzufügen"""
|
||||
from .forms import VeranstaltungsteilnehmerForm
|
||||
|
||||
veranstaltung = get_object_or_404(Veranstaltung, pk=veranstaltung_pk)
|
||||
|
||||
if request.method == "POST":
|
||||
form = VeranstaltungsteilnehmerForm(request.POST)
|
||||
if form.is_valid():
|
||||
teilnehmer = form.save(commit=False)
|
||||
teilnehmer.veranstaltung = veranstaltung
|
||||
teilnehmer.save()
|
||||
messages.success(request, f"{teilnehmer.vorname} {teilnehmer.nachname} wurde hinzugefügt.")
|
||||
return redirect("stiftung:veranstaltung_detail", pk=veranstaltung.pk)
|
||||
else:
|
||||
messages.error(request, "Bitte korrigieren Sie die Fehler im Formular.")
|
||||
else:
|
||||
form = VeranstaltungsteilnehmerForm()
|
||||
|
||||
return render(request, "stiftung/veranstaltung/teilnehmer_form.html", {
|
||||
"form": form,
|
||||
"veranstaltung": veranstaltung,
|
||||
"title": "Teilnehmer hinzufügen",
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
def teilnehmer_update(request, veranstaltung_pk, pk):
|
||||
"""Teilnehmer bearbeiten"""
|
||||
from .forms import VeranstaltungsteilnehmerForm
|
||||
|
||||
veranstaltung = get_object_or_404(Veranstaltung, pk=veranstaltung_pk)
|
||||
teilnehmer = get_object_or_404(Veranstaltungsteilnehmer, pk=pk, veranstaltung=veranstaltung)
|
||||
|
||||
if request.method == "POST":
|
||||
form = VeranstaltungsteilnehmerForm(request.POST, instance=teilnehmer)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
messages.success(request, f"{teilnehmer.vorname} {teilnehmer.nachname} wurde aktualisiert.")
|
||||
return redirect("stiftung:veranstaltung_detail", pk=veranstaltung.pk)
|
||||
else:
|
||||
messages.error(request, "Bitte korrigieren Sie die Fehler im Formular.")
|
||||
else:
|
||||
form = VeranstaltungsteilnehmerForm(instance=teilnehmer)
|
||||
|
||||
return render(request, "stiftung/veranstaltung/teilnehmer_form.html", {
|
||||
"form": form,
|
||||
"veranstaltung": veranstaltung,
|
||||
"teilnehmer": teilnehmer,
|
||||
"title": f"Teilnehmer bearbeiten: {teilnehmer.vorname} {teilnehmer.nachname}",
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
def teilnehmer_delete(request, veranstaltung_pk, pk):
|
||||
"""Teilnehmer aus Veranstaltung entfernen"""
|
||||
veranstaltung = get_object_or_404(Veranstaltung, pk=veranstaltung_pk)
|
||||
teilnehmer = get_object_or_404(Veranstaltungsteilnehmer, pk=pk, veranstaltung=veranstaltung)
|
||||
|
||||
if request.method == "POST":
|
||||
name = f"{teilnehmer.vorname} {teilnehmer.nachname}"
|
||||
teilnehmer.delete()
|
||||
messages.success(request, f"{name} wurde aus der Teilnehmerliste entfernt.")
|
||||
return redirect("stiftung:veranstaltung_detail", pk=veranstaltung.pk)
|
||||
|
||||
return render(request, "stiftung/veranstaltung/teilnehmer_delete.html", {
|
||||
"veranstaltung": veranstaltung,
|
||||
"teilnehmer": teilnehmer,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user