profil hübscher
This commit is contained in:
parent
4956fdf6ef
commit
226d3bf69c
@ -490,10 +490,9 @@ def profil():
|
|||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"profil.html",
|
"profil.html",
|
||||||
page_title="Profil",
|
|
||||||
active_page="profil",
|
|
||||||
profile=profile,
|
profile=profile,
|
||||||
gruppen=gruppen,
|
groups=gruppen,
|
||||||
|
mandant_level_label=format_level(profile["mandant_level"]),
|
||||||
**get_current_user()
|
**get_current_user()
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1930,10 +1929,12 @@ def useradmin_user_upload():
|
|||||||
if not uploaded_file or uploaded_file.filename == "":
|
if not uploaded_file or uploaded_file.filename == "":
|
||||||
form_error = "Bitte eine CSV-Datei auswählen."
|
form_error = "Bitte eine CSV-Datei auswählen."
|
||||||
else:
|
else:
|
||||||
|
file_bytes = uploaded_file.read()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
content = uploaded_file.read().decode("utf-8-sig")
|
content = file_bytes.decode("utf-8-sig")
|
||||||
except Exception:
|
except UnicodeDecodeError:
|
||||||
form_error = "Die Datei konnte nicht gelesen werden. Bitte UTF-8 CSV verwenden."
|
form_error = "Die Datei ist nicht im UTF-8 Format. Bitte als UTF-8 CSV speichern."
|
||||||
|
|
||||||
if not form_error:
|
if not form_error:
|
||||||
reader = csv.reader(io.StringIO(content), delimiter=";")
|
reader = csv.reader(io.StringIO(content), delimiter=";")
|
||||||
@ -1942,13 +1943,22 @@ def useradmin_user_upload():
|
|||||||
if not rows:
|
if not rows:
|
||||||
form_error = "Die Datei ist leer."
|
form_error = "Die Datei ist leer."
|
||||||
|
|
||||||
|
# Nur echte Datenzeilen zählen (keine leeren)
|
||||||
|
data_rows = [
|
||||||
|
row for row in rows
|
||||||
|
if row and any(str(col).strip() for col in row)
|
||||||
|
]
|
||||||
|
|
||||||
|
if len(data_rows) > 50:
|
||||||
|
form_error = f"Maximal 50 Benutzer erlaubt. Datei enthält {len(data_rows)} Zeilen."
|
||||||
|
|
||||||
parsed_users = []
|
parsed_users = []
|
||||||
|
|
||||||
if not form_error:
|
if not form_error:
|
||||||
email_pattern = r"^[^@\s]+@[^@\s]+\.[^@\s]+$"
|
email_pattern = r"^[^@\s]+@[^@\s]+\.[^@\s]+$"
|
||||||
seen_emails_in_file = set()
|
seen_emails_in_file = set()
|
||||||
|
|
||||||
for line_no, row in enumerate(rows, start=1):
|
for line_no, row in enumerate(data_rows, start=1):
|
||||||
if not row or all(not str(col).strip() for col in row):
|
if not row or all(not str(col).strip() for col in row):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -2060,3 +2070,60 @@ def useradmin_user_delete(user_id):
|
|||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
return redirect(url_for("useradmin_mandant"))
|
return redirect(url_for("useradmin_mandant"))
|
||||||
|
|
||||||
|
@app.route("/pwdchange", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
|
def pwdchange():
|
||||||
|
user_id = session.get("user_id")
|
||||||
|
|
||||||
|
form_error = None
|
||||||
|
success_message = None
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
old_password = request.form.get("old_password", "")
|
||||||
|
new_password = request.form.get("new_password", "")
|
||||||
|
new_password2 = request.form.get("new_password2", "")
|
||||||
|
|
||||||
|
if not old_password or not new_password or not new_password2:
|
||||||
|
form_error = "Alle Felder sind erforderlich."
|
||||||
|
elif new_password != new_password2:
|
||||||
|
form_error = "Neue Passwörter stimmen nicht überein."
|
||||||
|
elif len(new_password) < 8:
|
||||||
|
form_error = "Das Passwort muss mindestens 8 Zeichen lang sein."
|
||||||
|
else:
|
||||||
|
conn = get_connection()
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
cur.execute("""
|
||||||
|
SELECT password_hash
|
||||||
|
FROM app_user
|
||||||
|
WHERE id = %s
|
||||||
|
""", (user_id,))
|
||||||
|
row = cur.fetchone()
|
||||||
|
|
||||||
|
if not row:
|
||||||
|
form_error = "User nicht gefunden."
|
||||||
|
elif not check_password_hash(row[0], old_password):
|
||||||
|
form_error = "Altes Passwort ist falsch."
|
||||||
|
else:
|
||||||
|
new_hash = generate_password_hash(new_password)
|
||||||
|
|
||||||
|
cur.execute("""
|
||||||
|
UPDATE app_user
|
||||||
|
SET password_hash = %s
|
||||||
|
WHERE id = %s
|
||||||
|
""", (new_hash, user_id))
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
success_message = "Passwort erfolgreich geändert."
|
||||||
|
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
"pwdchange.html",
|
||||||
|
page_title="Passwort ändern",
|
||||||
|
form_error=form_error,
|
||||||
|
success_message=success_message,
|
||||||
|
**get_current_user()
|
||||||
|
)
|
||||||
@ -2,35 +2,47 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<h1>Profil</h1>
|
<div class="page-header">
|
||||||
|
<h1>Profil</h1>
|
||||||
<table class="admin-table">
|
|
||||||
<tr><th>ID</th><td>{{ profile.id }}</td></tr>
|
|
||||||
<tr><th>Name</th><td>{{ profile.name }}</td></tr>
|
|
||||||
<tr><th>E-Mail</th><td>{{ profile.email }}</td></tr>
|
|
||||||
<tr><th>Mandant</th><td>{{ profile.mandant_name }} ({{ profile.mandant_kuerzel }})</td></tr>
|
|
||||||
<tr><th>Mandant E-Mail</th><td>{{ profile.mandant_email or '-' }}</td></tr>
|
|
||||||
<tr><th>Mandant Level</th><td>{{ profile.mandant_level_label }}</td></tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<th>Gruppen im Mandanten</th>
|
|
||||||
<td>
|
|
||||||
{% if gruppen %}
|
|
||||||
<div class="group-badges">
|
|
||||||
{% for gruppe in gruppen %}
|
|
||||||
<span class="group-badge">{{ gruppe }}</span>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
-
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<div class="admin-actions">
|
|
||||||
<a href="/pwdchange" class="btn-primary">Passwort ändern</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<section class="admin-section">
|
||||||
|
<div class="admin-panel profile-panel">
|
||||||
|
|
||||||
|
<div class="profile-grid">
|
||||||
|
|
||||||
|
<div class="profile-label">ID</div>
|
||||||
|
<div class="profile-value">{{ profile.id }}</div>
|
||||||
|
|
||||||
|
<div class="profile-label">Name</div>
|
||||||
|
<div class="profile-value">{{ profile.name }}</div>
|
||||||
|
|
||||||
|
<div class="profile-label">E-Mail</div>
|
||||||
|
<div class="profile-value">{{ profile.email }}</div>
|
||||||
|
|
||||||
|
<div class="profile-label">Mandant</div>
|
||||||
|
<div class="profile-value">{{ profile.mandant_name }}</div>
|
||||||
|
|
||||||
|
<div class="profile-label">Mandant E-Mail</div>
|
||||||
|
<div class="profile-value">{{ profile.mandant_email }}</div>
|
||||||
|
|
||||||
|
<div class="profile-label">Mandant Level</div>
|
||||||
|
<div class="profile-value">{{ mandant_level_label }}</div>
|
||||||
|
|
||||||
|
<div class="profile-label">Gruppen</div>
|
||||||
|
<div class="profile-value">
|
||||||
|
{% for g in groups %}
|
||||||
|
<span class="badge">{{ g }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="profile-actions">
|
||||||
|
<a href="/pwdchange" class="btn-primary">Passwort ändern</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -1215,3 +1215,58 @@ button {
|
|||||||
margin: 6px 0 0;
|
margin: 6px 0 0;
|
||||||
color: #526172;
|
color: #526172;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===============================
|
||||||
|
Profil Layout
|
||||||
|
=============================== */
|
||||||
|
|
||||||
|
.profile-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 220px 1fr;
|
||||||
|
row-gap: 14px;
|
||||||
|
column-gap: 20px;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Labels linksbündig */
|
||||||
|
.profile-label {
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1e3a5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Werte */
|
||||||
|
.profile-value {
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gruppen-Badges */
|
||||||
|
.badge {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 6px 14px;
|
||||||
|
margin-right: 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: #e7edf5;
|
||||||
|
color: #1e3a5f;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
.profile-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-label {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-label {
|
||||||
|
color: #64748b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-value {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
3
app/flask-postgres/tests/userliste2.csv
Normal file
3
app/flask-postgres/tests/userliste2.csv
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Max Mustermann;mm@kolb.cc;1;Geheimnis
|
||||||
|
Susi Wunderschön;sw@kolb.cc;1;Geheimnis
|
||||||
|
Petra Sauberfrau;ps@kolb.cc;1;Geheimnis
|
||||||
|
Loading…
Reference in New Issue
Block a user