Compare commits
No commits in common. "87c3af5dc93875d530355886444d9947ed9df714" and "5c1f78c26d63e0064917b9037b50457bb4dc9154" have entirely different histories.
87c3af5dc9
...
5c1f78c26d
@ -15,18 +15,16 @@ from flask import (
|
|||||||
abort,
|
abort,
|
||||||
)
|
)
|
||||||
from werkzeug.security import check_password_hash, generate_password_hash
|
from werkzeug.security import check_password_hash, generate_password_hash
|
||||||
from werkzeug.utils import secure_filename
|
|
||||||
|
|
||||||
from config import Config, COUNTRY_VAT_LABELS
|
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, is_course_allowed_for_level, get_allowed_checklist_levels_for_mandant_level
|
from permissions import is_video_allowed_for_level, is_course_allowed_for_level
|
||||||
from security import (
|
from security import (
|
||||||
admin_required,
|
admin_required,
|
||||||
get_current_user,
|
get_current_user,
|
||||||
get_current_user_mandant_level,
|
get_current_user_mandant_level,
|
||||||
user_admin_required,
|
user_admin_required,
|
||||||
contentmanager_required
|
|
||||||
)
|
)
|
||||||
from logging_config import setup_logging
|
from logging_config import setup_logging
|
||||||
|
|
||||||
@ -1073,198 +1071,3 @@ def course_page(course_id, page_number):
|
|||||||
page_number=page_number,
|
page_number=page_number,
|
||||||
**get_current_user()
|
**get_current_user()
|
||||||
)
|
)
|
||||||
|
|
||||||
@app.route("/dokumente")
|
|
||||||
@contentmanager_required
|
|
||||||
def dokumente():
|
|
||||||
mandant_id = session.get("mandant_id")
|
|
||||||
mandant_level = session.get("mandant_level")
|
|
||||||
|
|
||||||
allowed_levels = get_allowed_checklist_levels_for_mandant_level(mandant_level)
|
|
||||||
|
|
||||||
conn = get_connection()
|
|
||||||
cur = conn.cursor()
|
|
||||||
|
|
||||||
cur.execute("""
|
|
||||||
SELECT
|
|
||||||
c.id,
|
|
||||||
c.level,
|
|
||||||
c.title,
|
|
||||||
c.short_description,
|
|
||||||
c.default_filename,
|
|
||||||
u.uploaded_at,
|
|
||||||
u.uploaded_by_user_id,
|
|
||||||
u.filesize,
|
|
||||||
u.stored_filename,
|
|
||||||
u.original_filename,
|
|
||||||
au.name AS uploaded_by_name
|
|
||||||
FROM certification_checklist c
|
|
||||||
LEFT JOIN mandant_checklist_upload u
|
|
||||||
ON u.checklist_item_id = c.id
|
|
||||||
AND u.mandant_id = %s
|
|
||||||
LEFT JOIN app_user au
|
|
||||||
ON au.id = u.uploaded_by_user_id
|
|
||||||
WHERE c.level = ANY(%s)
|
|
||||||
ORDER BY c.level, c.id
|
|
||||||
""", (mandant_id, allowed_levels))
|
|
||||||
|
|
||||||
items = fetchall_dict(cur)
|
|
||||||
|
|
||||||
cur.close()
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
return render_template(
|
|
||||||
"dokumente.html",
|
|
||||||
page_title="Dokumente",
|
|
||||||
active_page="dokumente",
|
|
||||||
items=items,
|
|
||||||
**get_current_user()
|
|
||||||
)
|
|
||||||
|
|
||||||
@app.route("/dokumente/upload/<int:item_id>", methods=["POST"])
|
|
||||||
@contentmanager_required
|
|
||||||
def dokument_upload(item_id):
|
|
||||||
mandant_id = session.get("mandant_id")
|
|
||||||
user_id = session.get("user_id")
|
|
||||||
|
|
||||||
uploaded_file = request.files.get("file")
|
|
||||||
if not uploaded_file or uploaded_file.filename == "":
|
|
||||||
return redirect(url_for("dokumente"))
|
|
||||||
|
|
||||||
conn = get_connection()
|
|
||||||
cur = conn.cursor()
|
|
||||||
|
|
||||||
cur.execute("""
|
|
||||||
SELECT id, default_filename
|
|
||||||
FROM certification_checklist
|
|
||||||
WHERE id = %s
|
|
||||||
""", (item_id,))
|
|
||||||
item = fetchone_dict(cur)
|
|
||||||
|
|
||||||
if not item:
|
|
||||||
cur.close()
|
|
||||||
conn.close()
|
|
||||||
abort(404)
|
|
||||||
|
|
||||||
original_filename = secure_filename(uploaded_file.filename)
|
|
||||||
final_name_part = item["default_filename"] or original_filename
|
|
||||||
stored_filename = f"{item_id}-{secure_filename(final_name_part)}"
|
|
||||||
|
|
||||||
mandant_dir = os.path.join("/files", str(mandant_id))
|
|
||||||
os.makedirs(mandant_dir, exist_ok=True)
|
|
||||||
|
|
||||||
full_path = os.path.join(mandant_dir, stored_filename)
|
|
||||||
uploaded_file.save(full_path)
|
|
||||||
|
|
||||||
filesize = os.path.getsize(full_path)
|
|
||||||
|
|
||||||
cur.execute("""
|
|
||||||
SELECT stored_filename
|
|
||||||
FROM mandant_checklist_upload
|
|
||||||
WHERE mandant_id = %s
|
|
||||||
AND checklist_item_id = %s
|
|
||||||
""", (mandant_id, item_id))
|
|
||||||
existing = cur.fetchone()
|
|
||||||
|
|
||||||
if existing:
|
|
||||||
old_filename = existing[0]
|
|
||||||
old_path = os.path.join(mandant_dir, old_filename)
|
|
||||||
if os.path.exists(old_path) and old_filename != stored_filename:
|
|
||||||
os.remove(old_path)
|
|
||||||
|
|
||||||
cur.execute("""
|
|
||||||
UPDATE mandant_checklist_upload
|
|
||||||
SET uploaded_at = CURRENT_TIMESTAMP,
|
|
||||||
uploaded_by_user_id = %s,
|
|
||||||
filesize = %s,
|
|
||||||
stored_filename = %s,
|
|
||||||
original_filename = %s
|
|
||||||
WHERE mandant_id = %s
|
|
||||||
AND checklist_item_id = %s
|
|
||||||
""", (user_id, filesize, stored_filename, original_filename, mandant_id, item_id))
|
|
||||||
else:
|
|
||||||
cur.execute("""
|
|
||||||
INSERT INTO mandant_checklist_upload (
|
|
||||||
mandant_id,
|
|
||||||
checklist_item_id,
|
|
||||||
uploaded_by_user_id,
|
|
||||||
filesize,
|
|
||||||
stored_filename,
|
|
||||||
original_filename
|
|
||||||
)
|
|
||||||
VALUES (%s, %s, %s, %s, %s, %s)
|
|
||||||
""", (mandant_id, item_id, user_id, filesize, stored_filename, original_filename))
|
|
||||||
|
|
||||||
conn.commit()
|
|
||||||
cur.close()
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
return redirect(url_for("dokumente"))
|
|
||||||
|
|
||||||
@app.route("/dokumente/delete/<int:item_id>", methods=["POST"])
|
|
||||||
@contentmanager_required
|
|
||||||
def dokument_delete(item_id):
|
|
||||||
mandant_id = session.get("mandant_id")
|
|
||||||
mandant_dir = os.path.join("/files", str(mandant_id))
|
|
||||||
|
|
||||||
conn = get_connection()
|
|
||||||
cur = conn.cursor()
|
|
||||||
|
|
||||||
cur.execute("""
|
|
||||||
SELECT stored_filename
|
|
||||||
FROM mandant_checklist_upload
|
|
||||||
WHERE mandant_id = %s
|
|
||||||
AND checklist_item_id = %s
|
|
||||||
""", (mandant_id, item_id))
|
|
||||||
row = cur.fetchone()
|
|
||||||
|
|
||||||
if row:
|
|
||||||
stored_filename = row[0]
|
|
||||||
full_path = os.path.join(mandant_dir, stored_filename)
|
|
||||||
|
|
||||||
if os.path.exists(full_path):
|
|
||||||
os.remove(full_path)
|
|
||||||
|
|
||||||
cur.execute("""
|
|
||||||
DELETE FROM mandant_checklist_upload
|
|
||||||
WHERE mandant_id = %s
|
|
||||||
AND checklist_item_id = %s
|
|
||||||
""", (mandant_id, item_id))
|
|
||||||
conn.commit()
|
|
||||||
|
|
||||||
cur.close()
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
return redirect(url_for("dokumente"))
|
|
||||||
|
|
||||||
@app.route("/dokumente/file/<int:item_id>")
|
|
||||||
@contentmanager_required
|
|
||||||
def dokument_file(item_id):
|
|
||||||
mandant_id = session.get("mandant_id")
|
|
||||||
|
|
||||||
conn = get_connection()
|
|
||||||
cur = conn.cursor()
|
|
||||||
|
|
||||||
cur.execute("""
|
|
||||||
SELECT stored_filename
|
|
||||||
FROM mandant_checklist_upload
|
|
||||||
WHERE mandant_id = %s
|
|
||||||
AND checklist_item_id = %s
|
|
||||||
""", (mandant_id, item_id))
|
|
||||||
row = cur.fetchone()
|
|
||||||
|
|
||||||
cur.close()
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
if not row:
|
|
||||||
abort(404)
|
|
||||||
|
|
||||||
stored_filename = row[0]
|
|
||||||
mandant_dir = os.path.join("/files", str(mandant_id))
|
|
||||||
return send_from_directory(mandant_dir, stored_filename)
|
|
||||||
|
|
||||||
@app.template_filter("datetime")
|
|
||||||
def format_datetime(value):
|
|
||||||
if not value:
|
|
||||||
return "-"
|
|
||||||
return value.strftime("%d.%m.%Y %H:%M")
|
|
||||||
@ -52,24 +52,3 @@ def is_course_allowed_for_level(code: str, mandant_level: int | None) -> bool:
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_allowed_checklist_levels_for_mandant_level(mandant_level: int | None) -> list[int]:
|
|
||||||
if mandant_level is None:
|
|
||||||
return []
|
|
||||||
|
|
||||||
# 0 = Admin -> alles
|
|
||||||
if mandant_level == 0:
|
|
||||||
return [1, 2, 3]
|
|
||||||
|
|
||||||
# 1 = Gold -> Bronze + Silber + Gold
|
|
||||||
if mandant_level == 1:
|
|
||||||
return [1, 2, 3]
|
|
||||||
|
|
||||||
# 2 = Silber -> Bronze + Silber
|
|
||||||
if mandant_level == 2:
|
|
||||||
return [1, 2]
|
|
||||||
|
|
||||||
# 3 = Bronze -> nur Bronze
|
|
||||||
if mandant_level == 3:
|
|
||||||
return [1]
|
|
||||||
|
|
||||||
return []
|
|
||||||
@ -69,7 +69,6 @@ def get_current_user():
|
|||||||
"is_logged_in": bool(session.get("user_id")),
|
"is_logged_in": bool(session.get("user_id")),
|
||||||
"is_admin": user_is_admin() if session.get("user_id") else False,
|
"is_admin": user_is_admin() if session.get("user_id") else False,
|
||||||
"is_user_admin": user_is_user_admin() if session.get("user_id") else False,
|
"is_user_admin": user_is_user_admin() if session.get("user_id") else False,
|
||||||
"is_contentmanager": user_is_contentmanager() if session.get("user_id") else False,
|
|
||||||
"country": country,
|
"country": country,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,40 +121,3 @@ def user_admin_required(view_func):
|
|||||||
return view_func(*args, **kwargs)
|
return view_func(*args, **kwargs)
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
def user_is_contentmanager():
|
|
||||||
user_id = session.get("user_id")
|
|
||||||
current_mandant_id = session.get("mandant_id")
|
|
||||||
|
|
||||||
if not user_id or not current_mandant_id:
|
|
||||||
return False
|
|
||||||
|
|
||||||
conn = get_connection()
|
|
||||||
cur = conn.cursor()
|
|
||||||
|
|
||||||
cur.execute("""
|
|
||||||
SELECT 1
|
|
||||||
FROM user_group ug
|
|
||||||
JOIN app_group g ON g.id = ug.group_id
|
|
||||||
WHERE ug.user_id = %s
|
|
||||||
AND ug.mandant_id = %s
|
|
||||||
AND g.mandant_id = %s
|
|
||||||
AND g.group_name = 'Contentmanager'
|
|
||||||
LIMIT 1
|
|
||||||
""", (user_id, current_mandant_id, current_mandant_id))
|
|
||||||
|
|
||||||
result = cur.fetchone()
|
|
||||||
|
|
||||||
cur.close()
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
return result is not None
|
|
||||||
|
|
||||||
def contentmanager_required(view_func):
|
|
||||||
@wraps(view_func)
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
if not session.get("user_id"):
|
|
||||||
return redirect(url_for("login", next=request.path))
|
|
||||||
if not user_is_contentmanager():
|
|
||||||
abort(403)
|
|
||||||
return view_func(*args, **kwargs)
|
|
||||||
return wrapper
|
|
||||||
@ -40,9 +40,6 @@
|
|||||||
{% if is_user_admin %}
|
{% if is_user_admin %}
|
||||||
<a href="/useradmin/mandant">Useradministration</a>
|
<a href="/useradmin/mandant">Useradministration</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if is_contentmanager %}
|
|
||||||
<a href="/dokumente">Dokumente</a>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<a href="/logout">Logout</a>
|
<a href="/logout">Logout</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,103 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<div class="page-header">
|
|
||||||
<h1>Dokumente</h1>
|
|
||||||
<p class="intro-text">Checkliste der hochzuladenden Zertifizierungsdokumente.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<section class="admin-section">
|
|
||||||
<div class="admin-panel">
|
|
||||||
<div class="table-wrap">
|
|
||||||
<table class="mandanten-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>ID</th>
|
|
||||||
<th>Titel</th>
|
|
||||||
<th>Kurzbeschreibung</th>
|
|
||||||
<th>Datei</th>
|
|
||||||
<th>Status</th>
|
|
||||||
<th>Datum</th>
|
|
||||||
<th>User</th>
|
|
||||||
<th>Größe</th>
|
|
||||||
<th>Aktionen</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for item in items %}
|
|
||||||
<tr>
|
|
||||||
<td class="col-id">{{ item.id }}</td>
|
|
||||||
<td>{{ item.title }}</td>
|
|
||||||
<td>{{ item.short_description or "-" }}</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
{% if item.stored_filename %}
|
|
||||||
<a href="/dokumente/file/{{ item.id }}">
|
|
||||||
{{ item.original_filename or item.stored_filename }}
|
|
||||||
</a>
|
|
||||||
{% else %}
|
|
||||||
-
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
{% if item.stored_filename %}
|
|
||||||
<span class="status-ok">✔ OK</span>
|
|
||||||
{% else %}
|
|
||||||
<span class="status-missing">Fehlt</span>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<!-- 📅 Datum -->
|
|
||||||
<td>
|
|
||||||
{% if item.uploaded_at %}
|
|
||||||
{{ item.uploaded_at | datetime }}
|
|
||||||
{% else %}
|
|
||||||
-
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<!-- 👤 User -->
|
|
||||||
<td>
|
|
||||||
{{ item.uploaded_by_name or "-" }}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<!-- 📦 Filesize -->
|
|
||||||
<td>
|
|
||||||
{% if item.filesize %}
|
|
||||||
{% if item.filesize < 1024 %}
|
|
||||||
{{ item.filesize }} B
|
|
||||||
{% elif item.filesize < 1024*1024 %}
|
|
||||||
{{ (item.filesize / 1024)|round(1) }} KB
|
|
||||||
{% else %}
|
|
||||||
{{ (item.filesize / (1024*1024))|round(2) }} MB
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
-
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<div class="table-actions">
|
|
||||||
{% if not item.stored_filename %}
|
|
||||||
<form method="post" action="/dokumente/upload/{{ item.id }}" enctype="multipart/form-data">
|
|
||||||
<input type="file" name="file" required>
|
|
||||||
<button type="submit" class="btn-primary btn-small">Upload</button>
|
|
||||||
</form>
|
|
||||||
{% else %}
|
|
||||||
<form method="post" action="/dokumente/delete/{{ item.id }}">
|
|
||||||
<button type="submit" class="btn-danger btn-small">Delete</button>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
@ -719,24 +719,3 @@ button {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* =========================
|
|
||||||
Dokumente
|
|
||||||
========================= */
|
|
||||||
.status-ok {
|
|
||||||
color: #178b35;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-missing {
|
|
||||||
color: #b62323;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
.col-id {
|
|
||||||
width: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mandanten-table td,
|
|
||||||
.mandanten-table th {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user