feat: Nutzerdetails (Telefon/Adresse) + Formular-Fix bei Passwortfehler in /admin/users
This commit is contained in:
parent
13616c9006
commit
ce90a2bd32
107
app.py
107
app.py
|
|
@ -790,7 +790,11 @@ def _role_pill(role):
|
||||||
return '<span class="pill %s">%s</span>' % (html.escape(role), html.escape(role))
|
return '<span class="pill %s">%s</span>' % (html.escape(role), html.escape(role))
|
||||||
|
|
||||||
|
|
||||||
def _users_body(tenant_id, notice=""):
|
def _users_body(tenant_id, notice="", form_vals=None):
|
||||||
|
# form_vals: bei einem fehlgeschlagenen POST die eingegebenen Werte (ausser
|
||||||
|
# Passwoerter), damit das Formular nicht komplett geleert wird (Nutzer-
|
||||||
|
# Vorgabe 2026-07-13, analog zum gleichen Fix bei /setup/new).
|
||||||
|
fv = form_vals or {}
|
||||||
users = db.list_users(tenant_id)
|
users = db.list_users(tenant_id)
|
||||||
rows = []
|
rows = []
|
||||||
for u in users:
|
for u in users:
|
||||||
|
|
@ -802,13 +806,17 @@ def _users_body(tenant_id, notice=""):
|
||||||
'<option value="%s" %s>%s</option>' % (r, "selected" if r == u["role"] else "", r)
|
'<option value="%s" %s>%s</option>' % (r, "selected" if r == u["role"] else "", r)
|
||||||
for r in ("admin", "agent")
|
for r in ("admin", "agent")
|
||||||
)
|
)
|
||||||
|
name = " ".join(x for x in (u["vorname"], u["nachname"]) if x) or "--"
|
||||||
rows.append("""<tr>
|
rows.append("""<tr>
|
||||||
<td>%s</td>
|
<td>%s</td>
|
||||||
|
<td class="sz">%s</td>
|
||||||
<td>%s</td>
|
<td>%s</td>
|
||||||
<td>%s</td>
|
<td>%s</td>
|
||||||
<td class="sz">%s</td>
|
<td class="sz">%s</td>
|
||||||
<td class="sz">%s</td>
|
<td class="sz">%s</td>
|
||||||
|
<td class="sz">%s</td>
|
||||||
<td>
|
<td>
|
||||||
|
<a class="btn ghost" href="/admin/users/%s/edit" style="padding:4px 10px;font-size:12px">Details</a>
|
||||||
<form method="post" action="/admin/users/%s/role" style="display:inline">
|
<form method="post" action="/admin/users/%s/role" style="display:inline">
|
||||||
<select name="role" onchange="this.form.submit()">%s</select>
|
<select name="role" onchange="this.form.submit()">%s</select>
|
||||||
</form>
|
</form>
|
||||||
|
|
@ -817,29 +825,46 @@ def _users_body(tenant_id, notice=""):
|
||||||
</form>
|
</form>
|
||||||
</td>
|
</td>
|
||||||
</tr>""" % (
|
</tr>""" % (
|
||||||
html.escape(u["email"]), _role_pill(u["role"]), status_pill,
|
html.escape(u["email"]), html.escape(name), _role_pill(u["role"]), status_pill,
|
||||||
html.escape(u["auth_source"]),
|
html.escape(u["telefon"] or ""), html.escape(u["auth_source"]),
|
||||||
u["last_login_at"].strftime("%Y-%m-%d %H:%M") if u["last_login_at"] else "nie",
|
u["last_login_at"].strftime("%Y-%m-%d %H:%M") if u["last_login_at"] else "nie",
|
||||||
u["id"], role_opts, u["id"], toggle_label,
|
u["id"], u["id"], role_opts, u["id"], toggle_label,
|
||||||
))
|
))
|
||||||
table = """<table class="tickets"><thead><tr>
|
table = """<table class="tickets"><thead><tr>
|
||||||
<th>E-Mail</th><th>Rolle</th><th>Status</th><th>Quelle</th><th>Letzte Anmeldung</th><th>Aktionen</th>
|
<th>E-Mail</th><th>Name</th><th>Rolle</th><th>Status</th><th>Telefon</th><th>Quelle</th><th>Letzte Anmeldung</th><th>Aktionen</th>
|
||||||
</tr></thead><tbody>%s</tbody></table>""" % ("".join(rows) or "<tr><td colspan='6' class='sz'>Keine Benutzer.</td></tr>")
|
</tr></thead><tbody>%s</tbody></table>""" % ("".join(rows) or "<tr><td colspan='8' class='sz'>Keine Benutzer.</td></tr>")
|
||||||
|
|
||||||
add_form = """<div class="panel"><h3 style="margin-top:0">Neuen Benutzer anlegen</h3>
|
add_form = """<div class="panel"><h3 style="margin-top:0">Neuen Benutzer anlegen</h3>
|
||||||
<div class="sz" style="margin-bottom:10px">Manuelle Verwaltung. Eine AD/LDAP-Anbindung ist als naechste Ausbaustufe vorgesehen (Spalte "Quelle" zeigt dann "ldap" statt "local").</div>
|
<div class="sz" style="margin-bottom:10px">Manuelle Verwaltung. Eine AD/LDAP-Anbindung ist als naechste Ausbaustufe vorgesehen (Spalte "Quelle" zeigt dann "ldap" statt "local").</div>
|
||||||
<form method="post" action="/admin/users">
|
<form method="post" action="/admin/users">
|
||||||
<div class="grid-2">
|
<div class="grid-2">
|
||||||
<div class="formrow"><label>E-Mail</label><input type="email" name="email" required></div>
|
<div class="formrow"><label>E-Mail</label><input type="email" name="email" value="%s" required></div>
|
||||||
<div class="formrow"><label>Rolle</label><select name="role"><option value="agent">agent</option><option value="admin">admin</option></select></div>
|
<div class="formrow"><label>Rolle</label><select name="role"><option value="agent" %s>agent</option><option value="admin" %s>admin</option></select></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-2">
|
<div class="grid-2">
|
||||||
<div class="formrow"><label>Passwort</label><input type="password" name="password" required></div>
|
<div class="formrow"><label>Passwort</label><input type="password" name="password" required></div>
|
||||||
<div class="formrow"><label>Passwort wiederholen</label><input type="password" name="password2" required></div>
|
<div class="formrow"><label>Passwort wiederholen</label><input type="password" name="password2" required></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="hint">Mindestens 10 Zeichen. Der Benutzer sollte das Passwort nach der ersten Anmeldung selbst aendern.</div>
|
<div class="hint">Mindestens 10 Zeichen. Der Benutzer sollte das Passwort nach der ersten Anmeldung selbst aendern.</div>
|
||||||
|
<h2 class="section-title">Details (optional)</h2>
|
||||||
|
<div class="grid-2">
|
||||||
|
<div class="formrow"><label>Vorname</label><input name="vorname" value="%s"></div>
|
||||||
|
<div class="formrow"><label>Nachname</label><input name="nachname" value="%s"></div>
|
||||||
|
</div>
|
||||||
|
<div class="grid-2">
|
||||||
|
<div class="formrow"><label>Telefon</label><input name="telefon" value="%s"></div>
|
||||||
|
<div class="formrow"><label>Abteilung</label><input name="abteilung" value="%s"></div>
|
||||||
|
</div>
|
||||||
|
<div class="formrow"><label>Adresse</label><input name="adresse" value="%s"></div>
|
||||||
<button class="btn" type="submit">Anlegen</button>
|
<button class="btn" type="submit">Anlegen</button>
|
||||||
</form></div>"""
|
</form></div>""" % (
|
||||||
|
html.escape(fv.get("email", "")),
|
||||||
|
"selected" if fv.get("role") == "agent" or not fv.get("role") else "",
|
||||||
|
"selected" if fv.get("role") == "admin" else "",
|
||||||
|
html.escape(fv.get("vorname", "")), html.escape(fv.get("nachname", "")),
|
||||||
|
html.escape(fv.get("telefon", "")), html.escape(fv.get("abteilung", "")),
|
||||||
|
html.escape(fv.get("adresse", "")),
|
||||||
|
)
|
||||||
|
|
||||||
return '<h1 class="page-title">Benutzerverwaltung</h1>' + notice + table + add_form
|
return '<h1 class="page-title">Benutzerverwaltung</h1>' + notice + table + add_form
|
||||||
|
|
||||||
|
|
@ -851,6 +876,7 @@ def admin_users_route():
|
||||||
return r
|
return r
|
||||||
tenant_id = session["tenant_id"]
|
tenant_id = session["tenant_id"]
|
||||||
notice = ""
|
notice = ""
|
||||||
|
form_vals = {}
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
email = request.form.get("email", "").strip().lower()
|
email = request.form.get("email", "").strip().lower()
|
||||||
role = request.form.get("role", "agent").strip()
|
role = request.form.get("role", "agent").strip()
|
||||||
|
|
@ -858,6 +884,13 @@ def admin_users_route():
|
||||||
role = "agent"
|
role = "agent"
|
||||||
pw = request.form.get("password", "")
|
pw = request.form.get("password", "")
|
||||||
pw2 = request.form.get("password2", "")
|
pw2 = request.form.get("password2", "")
|
||||||
|
vorname = request.form.get("vorname", "").strip()
|
||||||
|
nachname = request.form.get("nachname", "").strip()
|
||||||
|
telefon = request.form.get("telefon", "").strip()
|
||||||
|
abteilung = request.form.get("abteilung", "").strip()
|
||||||
|
adresse = request.form.get("adresse", "").strip()
|
||||||
|
form_vals = {"email": email, "role": role, "vorname": vorname, "nachname": nachname,
|
||||||
|
"telefon": telefon, "abteilung": abteilung, "adresse": adresse}
|
||||||
if not email or "@" not in email:
|
if not email or "@" not in email:
|
||||||
notice = "<p class='err'>Bitte eine gueltige E-Mail-Adresse angeben.</p>"
|
notice = "<p class='err'>Bitte eine gueltige E-Mail-Adresse angeben.</p>"
|
||||||
elif len(pw) < 10:
|
elif len(pw) < 10:
|
||||||
|
|
@ -867,11 +900,63 @@ def admin_users_route():
|
||||||
elif db.email_exists(email):
|
elif db.email_exists(email):
|
||||||
notice = "<p class='err'>Diese E-Mail-Adresse ist bereits registriert.</p>"
|
notice = "<p class='err'>Diese E-Mail-Adresse ist bereits registriert.</p>"
|
||||||
else:
|
else:
|
||||||
uid = db.create_user(tenant_id, email, generate_password_hash(pw), role=role)
|
uid = db.create_user(tenant_id, email, generate_password_hash(pw), role=role,
|
||||||
|
vorname=vorname or None, nachname=nachname or None,
|
||||||
|
telefon=telefon or None, abteilung=abteilung or None,
|
||||||
|
adresse=adresse or None)
|
||||||
db.log_audit(tenant_id, session["user_id"], "user_created", "user", str(uid),
|
db.log_audit(tenant_id, session["user_id"], "user_created", "user", str(uid),
|
||||||
{"email": email, "role": role, "auth_source": "local"}, _client_ip())
|
{"email": email, "role": role, "auth_source": "local"}, _client_ip())
|
||||||
notice = "<p class='sz' style='color:var(--ok)'>Benutzer angelegt.</p>"
|
notice = "<p class='sz' style='color:var(--ok)'>Benutzer angelegt.</p>"
|
||||||
return _shell("/admin/users", "Benutzerverwaltung", _users_body(tenant_id, notice))
|
form_vals = {}
|
||||||
|
return _shell("/admin/users", "Benutzerverwaltung", _users_body(tenant_id, notice, form_vals))
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/admin/users/<int:user_id>/edit", methods=["GET", "POST"])
|
||||||
|
def admin_users_edit_route(user_id):
|
||||||
|
r = _require_admin()
|
||||||
|
if r:
|
||||||
|
return r
|
||||||
|
tenant_id = session["tenant_id"]
|
||||||
|
u = db.get_user(tenant_id, user_id)
|
||||||
|
if not u:
|
||||||
|
abort(404)
|
||||||
|
error = None
|
||||||
|
if request.method == "POST":
|
||||||
|
vorname = request.form.get("vorname", "").strip()
|
||||||
|
nachname = request.form.get("nachname", "").strip()
|
||||||
|
telefon = request.form.get("telefon", "").strip()
|
||||||
|
abteilung = request.form.get("abteilung", "").strip()
|
||||||
|
adresse = request.form.get("adresse", "").strip()
|
||||||
|
db.update_user_profile(tenant_id, user_id, vorname or None, nachname or None,
|
||||||
|
telefon or None, abteilung or None, adresse or None)
|
||||||
|
db.log_audit(tenant_id, session["user_id"], "user_profile_updated", "user", str(user_id),
|
||||||
|
None, _client_ip())
|
||||||
|
u = dict(u)
|
||||||
|
u.update({"vorname": vorname, "nachname": nachname, "telefon": telefon,
|
||||||
|
"abteilung": abteilung, "adresse": adresse})
|
||||||
|
error = "Gespeichert."
|
||||||
|
err_html = ("<p class='sz' style='color:var(--ok)'>%s</p>" % html.escape(error)) if error else ""
|
||||||
|
body = """<h1 class="page-title">Benutzerdetails</h1>
|
||||||
|
<div class="sz" style="margin-bottom:14px">%s</div>
|
||||||
|
<div class="panel">%s<form method="post">
|
||||||
|
<div class="grid-2">
|
||||||
|
<div class="formrow"><label>Vorname</label><input name="vorname" value="%s"></div>
|
||||||
|
<div class="formrow"><label>Nachname</label><input name="nachname" value="%s"></div>
|
||||||
|
</div>
|
||||||
|
<div class="grid-2">
|
||||||
|
<div class="formrow"><label>Telefon</label><input name="telefon" value="%s"></div>
|
||||||
|
<div class="formrow"><label>Abteilung</label><input name="abteilung" value="%s"></div>
|
||||||
|
</div>
|
||||||
|
<div class="formrow"><label>Adresse</label><input name="adresse" value="%s"></div>
|
||||||
|
<button class="btn" type="submit">Speichern</button>
|
||||||
|
</form></div>
|
||||||
|
<a class="btn ghost" href="/admin/users">Zurueck zur Benutzerverwaltung</a>""" % (
|
||||||
|
html.escape(u["email"]), err_html,
|
||||||
|
html.escape(u["vorname"] or ""), html.escape(u["nachname"] or ""),
|
||||||
|
html.escape(u["telefon"] or ""), html.escape(u["abteilung"] or ""),
|
||||||
|
html.escape(u["adresse"] or ""),
|
||||||
|
)
|
||||||
|
return _shell("/admin/users", "Benutzerdetails", body)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/admin/users/<int:user_id>/role", methods=["POST"])
|
@app.route("/admin/users/<int:user_id>/role", methods=["POST"])
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue