Add Vorlagen editor, upload portal, onboarding, and participant import command
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy (push) Has been cancelled
Code Quality / quality (push) Has been cancelled

- Dokument-Vorlagen-Editor: create/edit/reset document templates (admin)
- Upload-Portal: public portal for Nachweis uploads via token
- Onboarding: invite Destinatäre via email with multi-step wizard
- Bestätigungsschreiben: preview and send confirmation letters
- Email settings: SMTP configuration UI
- Management command: import_veranstaltung_teilnehmer for bulk participant import

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
SysAdmin Agent
2026-03-21 09:25:18 +00:00
parent fdf078fa10
commit aed540fe4b
51 changed files with 5335 additions and 33 deletions

View File

@@ -1878,7 +1878,50 @@ def email_settings(request):
},
)
imap_settings = AppConfiguration.objects.filter(category="email", is_active=True).order_by("order")
# Ensure SMTP settings exist in DB (auto-init)
smtp_defaults = [
("smtp_host", "SMTP Server", "Hostname des SMTP-Servers (z.B. smtp.ionos.de)", "smtp.ionos.de", "text", 10),
("smtp_port", "SMTP Port", "Port des SMTP-Servers (465 für SSL, 587 für STARTTLS)", "465", "number", 11),
("smtp_user", "SMTP Benutzername", "Benutzername / E-Mail-Adresse für die SMTP-Anmeldung", "", "text", 12),
("smtp_password", "SMTP Passwort", "Passwort für die SMTP-Anmeldung", "", "password", 13),
("smtp_use_ssl", "SSL/TLS verwenden (SMTP)", "Sichere Verbindung zum SMTP-Server (empfohlen für Port 465)", "True", "boolean", 14),
("smtp_from_email", "Absenderadresse", "Absenderadresse für ausgehende E-Mails", "buero@vhtv-stiftung.de", "text", 15),
]
for key, name, desc, default, stype, order in smtp_defaults:
AppConfiguration.objects.get_or_create(
key=key,
defaults={
"display_name": name,
"description": desc,
"value": default,
"default_value": default,
"setting_type": stype,
"category": "email",
"order": order,
},
)
# Ensure notification settings exist in DB (auto-init)
notification_defaults = [
("notification_email", "Benachrichtigungs-E-Mail", "Empfänger für interne Benachrichtigungen (z.B. neue Onboardings). Wenn leer, wird die Absenderadresse verwendet.", "", "text", 20),
]
for key, name, desc, default, stype, order in notification_defaults:
AppConfiguration.objects.get_or_create(
key=key,
defaults={
"display_name": name,
"description": desc,
"value": default,
"default_value": default,
"setting_type": stype,
"category": "email",
"order": order,
},
)
imap_settings = AppConfiguration.objects.filter(category="email", key__startswith="imap_", is_active=True).order_by("order")
smtp_settings = AppConfiguration.objects.filter(category="email", key__startswith="smtp_", is_active=True).order_by("order")
notification_settings = AppConfiguration.objects.filter(category="email", key="notification_email", is_active=True).order_by("order")
test_result = None
@@ -1887,7 +1930,8 @@ def email_settings(request):
if action == "save":
updated = 0
for setting in imap_settings:
all_email_settings = AppConfiguration.objects.filter(category="email", is_active=True)
for setting in all_email_settings:
field_name = f"setting_{setting.key}"
if setting.setting_type == "boolean":
new_val = "True" if field_name in request.POST else "False"
@@ -1954,13 +1998,124 @@ def email_settings(request):
"message": f"Verbindungsfehler: {e}",
}
elif action == "test_smtp":
import smtplib
import ssl as ssl_module
host = get_config("smtp_host")
port = int(get_config("smtp_port", 465))
user = get_config("smtp_user")
password = get_config("smtp_password")
use_ssl = get_config("smtp_use_ssl", True)
if not all([host, user, password]):
test_result = {
"success": False,
"message": "SMTP-Server, Benutzername und Passwort müssen konfiguriert sein.",
"section": "smtp",
}
else:
try:
if use_ssl:
context = ssl_module.create_default_context()
conn = smtplib.SMTP_SSL(host, port, context=context, timeout=15)
else:
conn = smtplib.SMTP(host, port, timeout=15)
conn.starttls()
conn.login(user, password)
conn.quit()
test_result = {
"success": True,
"message": f"SMTP-Verbindung erfolgreich! Angemeldet als {user}.",
"section": "smtp",
}
except smtplib.SMTPAuthenticationError as e:
test_result = {
"success": False,
"message": f"SMTP-Authentifizierungsfehler: {e}",
"section": "smtp",
}
except Exception as e:
test_result = {
"success": False,
"message": f"SMTP-Verbindungsfehler: {e}",
"section": "smtp",
}
elif action == "test_smtp_send":
from django.core.mail import EmailMessage, get_connection
from django.utils import timezone
test_email = request.POST.get("test_email", "").strip()
if not test_email:
test_result = {
"success": False,
"message": "Bitte geben Sie eine Empfänger-E-Mail-Adresse ein.",
"section": "smtp",
}
else:
host = get_config("smtp_host")
port = int(get_config("smtp_port", 465))
user = get_config("smtp_user")
password = get_config("smtp_password")
use_ssl = get_config("smtp_use_ssl", True)
from_email = get_config("smtp_from_email", "buero@vhtv-stiftung.de")
if not all([host, user, password]):
test_result = {
"success": False,
"message": "SMTP-Server, Benutzername und Passwort müssen konfiguriert sein.",
"section": "smtp",
}
else:
try:
connection = get_connection(
backend="django.core.mail.backends.smtp.EmailBackend",
host=host,
port=port,
username=user,
password=password,
use_ssl=bool(use_ssl),
use_tls=False,
fail_silently=False,
)
now = timezone.now().strftime("%d.%m.%Y %H:%M")
msg = EmailMessage(
subject=f"[vHTV-Stiftung] SMTP-Test ({now})",
body=(
f"Dies ist eine Test-E-Mail der Stiftungsverwaltung.\n\n"
f"Zeitpunkt: {now}\n"
f"SMTP-Server: {host}:{port}\n"
f"Absender: {from_email}\n\n"
f"Wenn Sie diese E-Mail erhalten, funktioniert der E-Mail-Versand korrekt."
),
from_email=from_email,
to=[test_email],
connection=connection,
)
msg.send()
test_result = {
"success": True,
"message": f"Test-E-Mail wurde an {test_email} gesendet! Bitte prüfen Sie den Posteingang (und Spam-Ordner).",
"section": "smtp",
}
except Exception as e:
test_result = {
"success": False,
"message": f"E-Mail-Versand fehlgeschlagen: {e}",
"section": "smtp",
}
# Refresh after save
imap_settings = AppConfiguration.objects.filter(category="email", is_active=True).order_by("order")
imap_settings = AppConfiguration.objects.filter(category="email", key__startswith="imap_", is_active=True).order_by("order")
smtp_settings = AppConfiguration.objects.filter(category="email", key__startswith="smtp_", is_active=True).order_by("order")
notification_settings = AppConfiguration.objects.filter(category="email", key="notification_email", is_active=True).order_by("order")
context = {
"imap_settings": imap_settings,
"smtp_settings": smtp_settings,
"notification_settings": notification_settings,
"test_result": test_result,
"title": "E-Mail / IMAP Konfiguration",
"title": "E-Mail-Konfiguration (IMAP & SMTP)",
}
return render(request, "stiftung/email_settings.html", context)