110 lines
3.6 KiB
Python
110 lines
3.6 KiB
Python
"""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/<int:user_id>")
|
||
@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)
|