"""Profil: Anzeigename, Profilbild und optionale freiwillige Angaben. Nichts außer dem (ohnehin vorhandenen) Anzeigenamen ist Pflicht. Die freiwilligen Felder sind über die Liste FREIWILLIGE_FELDER definiert und werden als JSON in user.freiwillige_angaben abgelegt – ein neues Feld ist damit eine Zeile mehr in der Liste, ohne DB-Migration. Läuft in beiden Shells (shell_base je Rolle). """ import json import os import uuid from flask import ( Blueprint, abort, current_app, flash, redirect, render_template, request, send_from_directory, url_for, ) from .. import db from ..auth import current_user, login_required from ..models import User from ..team_scope import team_query bp = Blueprint("profil", __name__, url_prefix="/profil") # Erlaubte Bildtypen fürs Profilbild (serverseitig geprüft). BILD_ENDUNGEN = {"png", "jpg", "jpeg", "gif", "webp"} # Optionale freiwillige Angaben. Neues Feld = ein Eintrag mehr; "mehrzeilig" für # Textarea. Werte landen als JSON-Objekt in user.freiwillige_angaben. FREIWILLIGE_FELDER = [ {"key": "pronomen", "label": "Pronomen", "placeholder": "z. B. sie/ihr, er/ihm"}, {"key": "ueber_mich", "label": "Über mich", "placeholder": "Ein, zwei Sätze – ganz freiwillig", "mehrzeilig": True}, ] _GUELTIGE_KEYS = {f["key"] for f in FREIWILLIGE_FELDER} def _endung(dateiname): if not dateiname or "." not in dateiname: return "" return dateiname.rsplit(".", 1)[1].lower() def _angaben_lesen(user): """freiwillige_angaben als Dict (robust gegen leer/kaputt).""" if not user.freiwillige_angaben: return {} try: daten = json.loads(user.freiwillige_angaben) return daten if isinstance(daten, dict) else {} except (ValueError, TypeError): return {} @bp.route("/", methods=("GET", "POST")) @login_required def index(): user = current_user() if request.method == "POST": anzeigename = (request.form.get("anzeigename") or "").strip() if not anzeigename: flash("Bitte einen Anzeigenamen angeben.") return redirect(url_for("profil.index")) user.anzeigename = anzeigename # Freiwillige Felder: nur bekannte Keys, leere Werte fallen raus. angaben = {} for feld in FREIWILLIGE_FELDER: wert = (request.form.get(feld["key"]) or "").strip() if wert: angaben[feld["key"]] = wert user.freiwillige_angaben = json.dumps(angaben, ensure_ascii=False) if angaben else None # Profilbild optional. Nur bei tatsächlichem Upload anfassen. datei = request.files.get("profilbild") if datei and datei.filename: endung = _endung(datei.filename) if endung not in BILD_ENDUNGEN: flash("Dieses Bildformat ist nicht erlaubt. Erlaubt: PNG, JPG, GIF, WebP.") return redirect(url_for("profil.index")) name = f"profil_{uuid.uuid4().hex}.{endung}" datei.save(os.path.join(current_app.config["UPLOAD_FOLDER"], name)) user.profilbild_pfad = name db.session.commit() flash("Profil gespeichert.") return redirect(url_for("profil.index")) return render_template( "profil.html", felder=FREIWILLIGE_FELDER, angaben=_angaben_lesen(user), ) @bp.route("/bild/") @login_required def bild(user_id): """Profilbild eines Team-Mitglieds ausliefern (team-gescoped).""" user = team_query(User).filter_by(id=user_id).first() if user is None or not user.profilbild_pfad: abort(404) return send_from_directory(current_app.config["UPLOAD_FOLDER"], user.profilbild_pfad)