111 lines
3.7 KiB
Python
111 lines
3.7 KiB
Python
"""Start-Blueprint.
|
||
|
||
Hält die Startseite und – vorerst – schlanke Platzhalter für die übrigen
|
||
Navigationsziele (Termine, Team, Profil, Benachrichtigungen). Diese Platzhalter
|
||
werden in späteren Sessions durch die echten Ansichten/Blueprints ersetzt
|
||
(planung, dienste, dokumente, chat, profil). Interne App: alles login_required.
|
||
"""
|
||
|
||
from flask import Blueprint, current_app, redirect, render_template, request, url_for
|
||
|
||
from .. import db
|
||
from ..auth import current_user, login_required
|
||
from ..models import Benachrichtigung
|
||
from ..team_scope import team_query
|
||
|
||
main_bp = Blueprint("main", __name__)
|
||
|
||
# Theme-Modi (serverseitig, ohne JS): "system" folgt der Systemeinstellung,
|
||
# "hell"/"dunkel" überschreiben sie fest. Gespeichert in einem Cookie.
|
||
THEME_MODI = ("system", "hell", "dunkel")
|
||
|
||
|
||
@main_bp.app_context_processor
|
||
def inject_theme():
|
||
"""Aktiven Theme-Modus aus dem Cookie in allen Templates verfügbar machen."""
|
||
modus = request.cookies.get("theme", "system")
|
||
return {"current_theme": modus if modus in THEME_MODI else "system"}
|
||
|
||
|
||
@main_bp.app_context_processor
|
||
def inject_shell_base():
|
||
"""Wählt die Layout-Shell je Rolle: Admins bekommen die Sidebar-Shell, alle
|
||
anderen die mobile Bottom-Nav-Shell. Templates: {% extends shell_base %}."""
|
||
user = current_user()
|
||
basis = "base_admin.html" if (user and user.ist_admin) else "base_user.html"
|
||
return {"shell_base": basis}
|
||
|
||
|
||
@main_bp.route("/")
|
||
@login_required
|
||
def index():
|
||
return render_template("index.html", user=current_user())
|
||
|
||
|
||
@main_bp.route("/theme", methods=("POST",))
|
||
def theme_setzen():
|
||
"""Theme-Modus setzen (Cookie) und zur Ausgangsseite zurück. Kein Login nötig,
|
||
damit die Einstellung überall greift; CSRF-geschützt wie alle POST-Formulare."""
|
||
modus = request.form.get("modus", "system")
|
||
if modus not in THEME_MODI:
|
||
modus = "system"
|
||
|
||
# Nur lokale Pfade als Rücksprungziel zulassen (kein Open-Redirect).
|
||
ziel = request.form.get("next", "")
|
||
if not ziel.startswith("/"):
|
||
ziel = url_for("main.index")
|
||
|
||
antwort = redirect(ziel)
|
||
if modus == "system":
|
||
antwort.delete_cookie("theme")
|
||
else:
|
||
antwort.set_cookie(
|
||
"theme", modus, max_age=60 * 60 * 24 * 365,
|
||
samesite="Lax", httponly=True,
|
||
secure=current_app.config.get("SESSION_COOKIE_SECURE", False),
|
||
)
|
||
return antwort
|
||
|
||
|
||
# --- Platzhalter, damit die Bottom-Nav schon vollständig funktioniert ---------
|
||
# „Termine" zeigt auf planung.verfuegbarkeit (Session 6).
|
||
@main_bp.route("/team")
|
||
@login_required
|
||
def team():
|
||
return render_template("team.html")
|
||
|
||
|
||
@main_bp.app_context_processor
|
||
def inject_benachrichtigungen_zahl():
|
||
"""Anzahl ungelesener Benachrichtigungen für die Glocke (0, wenn nicht eingeloggt)."""
|
||
user = current_user()
|
||
if user is None:
|
||
return {"benachrichtigungen_ungelesen": 0}
|
||
anzahl = (
|
||
team_query(Benachrichtigung)
|
||
.filter_by(user_id=user.id, gelesen=False)
|
||
.count()
|
||
)
|
||
return {"benachrichtigungen_ungelesen": anzahl}
|
||
|
||
|
||
@main_bp.route("/benachrichtigungen")
|
||
@login_required
|
||
def benachrichtigungen():
|
||
eintraege = (
|
||
team_query(Benachrichtigung)
|
||
.filter_by(user_id=current_user().id)
|
||
.order_by(Benachrichtigung.erstellt_am.desc())
|
||
.all()
|
||
)
|
||
return render_template("benachrichtigungen.html", eintraege=eintraege)
|
||
|
||
|
||
@main_bp.route("/benachrichtigungen/gelesen", methods=("POST",))
|
||
@login_required
|
||
def benachrichtigungen_gelesen():
|
||
team_query(Benachrichtigung).filter_by(
|
||
user_id=current_user().id, gelesen=False
|
||
).update({"gelesen": True}, synchronize_session=False)
|
||
db.session.commit()
|
||
return redirect(url_for("main.benachrichtigungen"))
|