user edit
This commit is contained in:
parent
9a54373835
commit
a0057fca32
@ -16,7 +16,7 @@ from flask import (
|
|||||||
)
|
)
|
||||||
from werkzeug.security import check_password_hash, generate_password_hash
|
from werkzeug.security import check_password_hash, generate_password_hash
|
||||||
|
|
||||||
from config import Config
|
from config import Config, COUNTRY_VAT_LABELS
|
||||||
from db import get_connection, fetchone_dict, fetchall_dict
|
from db import get_connection, fetchone_dict, fetchall_dict
|
||||||
from auth import login_required
|
from auth import login_required
|
||||||
from permissions import is_video_allowed_for_level
|
from permissions import is_video_allowed_for_level
|
||||||
@ -28,6 +28,7 @@ from security import (
|
|||||||
)
|
)
|
||||||
from logging_config import setup_logging
|
from logging_config import setup_logging
|
||||||
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config.from_object(Config)
|
app.config.from_object(Config)
|
||||||
app.secret_key = app.config["SECRET_KEY"]
|
app.secret_key = app.config["SECRET_KEY"]
|
||||||
@ -243,6 +244,7 @@ def preise():
|
|||||||
"preise2.html",
|
"preise2.html",
|
||||||
page_title="Preise",
|
page_title="Preise",
|
||||||
active_page="preise",
|
active_page="preise",
|
||||||
|
vat_label=COUNTRY_VAT_LABELS.get(session.get("country", "DE")),
|
||||||
**get_current_user()
|
**get_current_user()
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -770,3 +772,173 @@ def set_country():
|
|||||||
|
|
||||||
next_url = request.args.get("next") or request.referrer or url_for("home")
|
next_url = request.args.get("next") or request.referrer or url_for("home")
|
||||||
return redirect(next_url)
|
return redirect(next_url)
|
||||||
|
|
||||||
|
@app.route("/useradmin/mandant/user/<int:user_id>", methods=["GET", "POST"])
|
||||||
|
@user_admin_required
|
||||||
|
def useradmin_user_edit(user_id):
|
||||||
|
import re
|
||||||
|
|
||||||
|
current_mandant_id = session.get("mandant_id")
|
||||||
|
|
||||||
|
conn = get_connection()
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
cur.execute("""
|
||||||
|
SELECT id, email, name, mandant_id, status
|
||||||
|
FROM app_user
|
||||||
|
WHERE id = %s
|
||||||
|
AND mandant_id = %s
|
||||||
|
""", (user_id, current_mandant_id))
|
||||||
|
user = fetchone_dict(cur)
|
||||||
|
|
||||||
|
if not user:
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
cur.execute("""
|
||||||
|
SELECT id, group_name
|
||||||
|
FROM app_group
|
||||||
|
WHERE mandant_id = %s
|
||||||
|
ORDER BY group_name
|
||||||
|
""", (current_mandant_id,))
|
||||||
|
gruppen = fetchall_dict(cur)
|
||||||
|
|
||||||
|
cur.execute("""
|
||||||
|
SELECT group_id
|
||||||
|
FROM user_group
|
||||||
|
WHERE user_id = %s
|
||||||
|
AND mandant_id = %s
|
||||||
|
""", (user_id, current_mandant_id))
|
||||||
|
assigned_rows = cur.fetchall()
|
||||||
|
assigned_group_ids = [str(row[0]) for row in assigned_rows]
|
||||||
|
|
||||||
|
form_error = None
|
||||||
|
success_message = None
|
||||||
|
|
||||||
|
form_values = {
|
||||||
|
"email": user["email"],
|
||||||
|
"name": user["name"],
|
||||||
|
"status": str(user["status"]),
|
||||||
|
"selected_groups": assigned_group_ids,
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
email = request.form.get("email", "").strip().lower()
|
||||||
|
name = request.form.get("name", "").strip()
|
||||||
|
status = request.form.get("status", "1").strip()
|
||||||
|
password = request.form.get("password", "")
|
||||||
|
password2 = request.form.get("password2", "")
|
||||||
|
selected_groups = request.form.getlist("group_ids")
|
||||||
|
|
||||||
|
form_values = {
|
||||||
|
"email": email,
|
||||||
|
"name": name,
|
||||||
|
"status": status,
|
||||||
|
"selected_groups": selected_groups,
|
||||||
|
}
|
||||||
|
|
||||||
|
email_pattern = r"^[^@\s]+@[^@\s]+\.[^@\s]+$"
|
||||||
|
|
||||||
|
if not email:
|
||||||
|
form_error = "E-Mail ist ein Pflichtfeld."
|
||||||
|
elif not re.match(email_pattern, email):
|
||||||
|
form_error = "Bitte eine gültige E-Mail-Adresse eingeben."
|
||||||
|
elif not name:
|
||||||
|
form_error = "Name ist ein Pflichtfeld."
|
||||||
|
else:
|
||||||
|
cur.execute("""
|
||||||
|
SELECT id
|
||||||
|
FROM app_user
|
||||||
|
WHERE lower(email) = %s
|
||||||
|
AND id <> %s
|
||||||
|
""", (email, user_id))
|
||||||
|
existing_user = cur.fetchone()
|
||||||
|
|
||||||
|
if existing_user:
|
||||||
|
form_error = "Ein anderer Benutzer mit dieser E-Mail existiert bereits."
|
||||||
|
|
||||||
|
if not form_error and (password or password2):
|
||||||
|
if not password:
|
||||||
|
form_error = "Bitte neues Passwort eingeben."
|
||||||
|
elif not password2:
|
||||||
|
form_error = "Bitte neues Passwort bestätigen."
|
||||||
|
elif password != password2:
|
||||||
|
form_error = "Die beiden Passwörter stimmen nicht überein."
|
||||||
|
elif len(password) < 8:
|
||||||
|
form_error = "Das Passwort muss mindestens 8 Zeichen lang sein."
|
||||||
|
|
||||||
|
if not form_error:
|
||||||
|
cur.execute("""
|
||||||
|
UPDATE app_user
|
||||||
|
SET email = %s,
|
||||||
|
name = %s,
|
||||||
|
status = %s
|
||||||
|
WHERE id = %s
|
||||||
|
AND mandant_id = %s
|
||||||
|
""", (email, name, int(status or 1), user_id, current_mandant_id))
|
||||||
|
|
||||||
|
if password:
|
||||||
|
password_hash = generate_password_hash(password)
|
||||||
|
cur.execute("""
|
||||||
|
UPDATE app_user
|
||||||
|
SET password_hash = %s
|
||||||
|
WHERE id = %s
|
||||||
|
AND mandant_id = %s
|
||||||
|
""", (password_hash, user_id, current_mandant_id))
|
||||||
|
|
||||||
|
cur.execute("""
|
||||||
|
DELETE FROM user_group
|
||||||
|
WHERE user_id = %s
|
||||||
|
AND mandant_id = %s
|
||||||
|
""", (user_id, current_mandant_id))
|
||||||
|
|
||||||
|
selected_group_ids = []
|
||||||
|
for gid in selected_groups:
|
||||||
|
try:
|
||||||
|
selected_group_ids.append(int(gid))
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if selected_group_ids:
|
||||||
|
cur.execute("""
|
||||||
|
SELECT id
|
||||||
|
FROM app_group
|
||||||
|
WHERE mandant_id = %s
|
||||||
|
AND id = ANY(%s)
|
||||||
|
""", (current_mandant_id, selected_group_ids))
|
||||||
|
valid_groups = cur.fetchall()
|
||||||
|
|
||||||
|
for row in valid_groups:
|
||||||
|
group_id = row[0]
|
||||||
|
cur.execute("""
|
||||||
|
INSERT INTO user_group (user_id, group_id, mandant_id)
|
||||||
|
VALUES (%s, %s, %s)
|
||||||
|
""", (user_id, group_id, current_mandant_id))
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
success_message = "Benutzer wurde erfolgreich aktualisiert."
|
||||||
|
|
||||||
|
cur.execute("""
|
||||||
|
SELECT group_id
|
||||||
|
FROM user_group
|
||||||
|
WHERE user_id = %s
|
||||||
|
AND mandant_id = %s
|
||||||
|
""", (user_id, current_mandant_id))
|
||||||
|
assigned_rows = cur.fetchall()
|
||||||
|
form_values["selected_groups"] = [str(row[0]) for row in assigned_rows]
|
||||||
|
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
"useradmin_user_edit.html",
|
||||||
|
page_title="User bearbeiten",
|
||||||
|
active_page="useradmin",
|
||||||
|
edit_user=user,
|
||||||
|
gruppen=gruppen,
|
||||||
|
form_values=form_values,
|
||||||
|
form_error=form_error,
|
||||||
|
success_message=success_message,
|
||||||
|
**get_current_user()
|
||||||
|
)
|
||||||
@ -11,3 +11,9 @@ class Config:
|
|||||||
DB_PORT = os.getenv("DB_PORT", "5432")
|
DB_PORT = os.getenv("DB_PORT", "5432")
|
||||||
|
|
||||||
LOG_DIR = os.getenv("LOG_DIR", "./logs")
|
LOG_DIR = os.getenv("LOG_DIR", "./logs")
|
||||||
|
|
||||||
|
COUNTRY_VAT_LABELS = {
|
||||||
|
"DE": "inkl. 19% USt",
|
||||||
|
"AT": "inkl. 20% USt",
|
||||||
|
"CH": "exkl. MwSt",
|
||||||
|
}
|
||||||
@ -17,13 +17,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<p class="pricing-note-strong">
|
<p class="pricing-note-strong">
|
||||||
Alle Preise verstehen sich
|
Alle Preise verstehen sich
|
||||||
{% if country == "DE" %}
|
<span class="price-note">{{ vat_label }}</span>.
|
||||||
inkl. 19% USt
|
|
||||||
{% elif country == "AT" %}
|
|
||||||
inkl. 20% USt
|
|
||||||
{% elif country == "CH" %}
|
|
||||||
exkl. MwSt
|
|
||||||
{% endif %}.
|
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,10 @@
|
|||||||
<h2>Benutzerübersicht</h2>
|
<h2>Benutzerübersicht</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="admin-actions">
|
||||||
|
<a href="/useradmin/mandant/new" class="btn-primary">Neuer User</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="table-wrap">
|
<div class="table-wrap">
|
||||||
<table class="mandanten-table">
|
<table class="mandanten-table">
|
||||||
<thead>
|
<thead>
|
||||||
@ -24,6 +28,7 @@
|
|||||||
<th>Letzter Login</th>
|
<th>Letzter Login</th>
|
||||||
<th>Mandant</th>
|
<th>Mandant</th>
|
||||||
<th>Level</th>
|
<th>Level</th>
|
||||||
|
<th>Aktionen</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -36,6 +41,11 @@
|
|||||||
<td>{{ user.last_login or "-" }}</td>
|
<td>{{ user.last_login or "-" }}</td>
|
||||||
<td>{{ user.mandant_name }}</td>
|
<td>{{ user.mandant_name }}</td>
|
||||||
<td>{{ user.mandant_level }}</td>
|
<td>{{ user.mandant_level }}</td>
|
||||||
|
<td class="col-actions">
|
||||||
|
<div class="table-actions">
|
||||||
|
<a href="/useradmin/mandant/user/{{ user.id }}" class="btn-primary btn-small">Bearbeiten</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
157
app/flask-postgres/app/templates/useradmin_user_edit.html
Normal file
157
app/flask-postgres/app/templates/useradmin_user_edit.html
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="page-header">
|
||||||
|
<h1>User bearbeiten</h1>
|
||||||
|
<p class="intro-text">Benutzer im aktuellen Mandanten ändern, Gruppen zuweisen und Passwort zurücksetzen.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="admin-section">
|
||||||
|
<div class="admin-panel">
|
||||||
|
|
||||||
|
{% if form_error %}
|
||||||
|
<div class="error-box">{{ form_error }}</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if success_message %}
|
||||||
|
<div class="success-box">{{ success_message }}</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form method="post" id="user-edit-form" novalidate class="admin-grid-form">
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label>ID</label>
|
||||||
|
<input type="text" value="{{ edit_user.id }}" readonly>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="email">E-Mail</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
value="{{ form_values.email }}"
|
||||||
|
required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="name">Name</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="name"
|
||||||
|
name="name"
|
||||||
|
value="{{ form_values.name }}"
|
||||||
|
required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="status">Status</label>
|
||||||
|
<select id="status" name="status">
|
||||||
|
<option value="0" {% if form_values.status == '0' %}selected{% endif %}>0 - nicht aktiviert</option>
|
||||||
|
<option value="1" {% if form_values.status == '1' %}selected{% endif %}>1 - OK</option>
|
||||||
|
<option value="2" {% if form_values.status == '2' %}selected{% endif %}>2 - locked</option>
|
||||||
|
<option value="3" {% if form_values.status == '3' %}selected{% endif %}>3 - disabled</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="password">Neues Passwort</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
placeholder="leer lassen = unverändert">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="password2">Passwort bestätigen</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="password2"
|
||||||
|
name="password2"
|
||||||
|
placeholder="leer lassen = unverändert">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row form-row-full">
|
||||||
|
<label>Gruppen des Mandanten</label>
|
||||||
|
<div class="checkbox-group">
|
||||||
|
{% for gruppe in gruppen %}
|
||||||
|
<label class="checkbox-item">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="group_ids"
|
||||||
|
value="{{ gruppe.id }}"
|
||||||
|
{% if gruppe.id|string in form_values.selected_groups %}checked{% endif %}>
|
||||||
|
<span>{{ gruppe.group_name }}</span>
|
||||||
|
</label>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row form-row-full">
|
||||||
|
<div id="user-edit-error" class="error-box" style="display:none;"></div>
|
||||||
|
<div class="admin-actions">
|
||||||
|
<button type="submit" class="btn-primary">Speichern</button>
|
||||||
|
<a href="/useradmin/mandant" class="btn-secondary">Zurück</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
const form = document.getElementById("user-edit-form");
|
||||||
|
const errorBox = document.getElementById("user-edit-error");
|
||||||
|
|
||||||
|
if (!form) return;
|
||||||
|
|
||||||
|
form.addEventListener("submit", function (event) {
|
||||||
|
const email = document.getElementById("email").value.trim();
|
||||||
|
const name = document.getElementById("name").value.trim();
|
||||||
|
const password = document.getElementById("password").value;
|
||||||
|
const password2 = document.getElementById("password2").value;
|
||||||
|
|
||||||
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
|
let errors = [];
|
||||||
|
|
||||||
|
if (!email) {
|
||||||
|
errors.push("E-Mail ist ein Pflichtfeld.");
|
||||||
|
} else if (!emailRegex.test(email)) {
|
||||||
|
errors.push("Bitte eine gültige E-Mail-Adresse eingeben.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!name) {
|
||||||
|
errors.push("Name ist ein Pflichtfeld.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password || password2) {
|
||||||
|
if (!password) {
|
||||||
|
errors.push("Bitte neues Passwort eingeben.");
|
||||||
|
}
|
||||||
|
if (!password2) {
|
||||||
|
errors.push("Bitte neues Passwort bestätigen.");
|
||||||
|
}
|
||||||
|
if (password && password2 && password !== password2) {
|
||||||
|
errors.push("Die beiden Passwörter stimmen nicht überein.");
|
||||||
|
}
|
||||||
|
if (password && password.length < 8) {
|
||||||
|
errors.push("Das Passwort muss mindestens 8 Zeichen lang sein.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors.length > 0) {
|
||||||
|
event.preventDefault();
|
||||||
|
errorBox.innerHTML = errors.join("<br>");
|
||||||
|
errorBox.style.display = "block";
|
||||||
|
} else {
|
||||||
|
errorBox.innerHTML = "";
|
||||||
|
errorBox.style.display = "none";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
Loading…
Reference in New Issue
Block a user