Compare commits

...

13 Commits

7 changed files with 552 additions and 231 deletions
+25 -13
View File
@@ -118,14 +118,14 @@ jobs:
echo "tag=$TAG" >> "$GITHUB_OUTPUT" echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "image=$IMAGE" >> "$GITHUB_OUTPUT" echo "image=$IMAGE" >> "$GITHUB_OUTPUT"
echo "lower_repo=$LOWER_REPO" >> "$GITHUB_OUTPUT" echo "lower_repo=$LOWER_REPO" >> "$GITHUB_OUTPUT"
- name: Ensure Docker CLI is available and up to date - name: Ensure Docker CLI is available and up to date
run: | run: |
install_docker=true install_docker=true
# Prüfen, ob Docker existiert und ob die Version ausreicht # Prüfen, ob Docker existiert und ob die Version ausreicht
if command -v docker >/dev/null 2>&1; then if command -v docker >/dev/null 2>&1; then
# Extrahiere die Major-Version (z. B. "20" aus "20.10.24" oder "26" aus "26.1.0") # Extrahiere die Major-Version
DOCKER_MAJOR=$(docker --version | grep -oE '[0-9]+' | head -n1) DOCKER_MAJOR=$(docker --version | grep -oE '[0-9]+' | head -n1)
# API 1.44 erfordert mindestens Docker v25 # API 1.44 erfordert mindestens Docker v25
@@ -135,19 +135,31 @@ jobs:
fi fi
if [ "$install_docker" = true ]; then if [ "$install_docker" = true ]; then
echo "Veraltete oder fehlende Docker-Installation erkannt. Führe Update durch..." echo "Veraltete oder fehlende Docker-Installation erkannt. Lade statische Docker CLI herunter..."
if command -v apt-get >/dev/null 2>&1; then
apt-get update DOCKER_VERSION="26.1.4"
DEBIAN_FRONTEND=noninteractive apt-get install -y curl
# Nutzt das offizielle Docker-Skript (installiert docker-ce statt das alte docker.io) # Download der statischen Binaries via curl oder wget (umgeht apt-get komplett)
curl -fsSL https://get.docker.com | sh if command -v curl >/dev/null 2>&1; then
elif command -v apk >/dev/null 2>&1; then curl -fsSLO "https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz"
apk update
apk add --no-cache docker-cli
else else
echo "Error: no supported package manager found to install docker" wget -q "https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz"
exit 1
fi fi
tar -xzf docker-${DOCKER_VERSION}.tgz
# Installation in lokalen Benutzer-Pfad, um sudo/root-Rechte-Probleme zu vermeiden
mkdir -p "$HOME/.local/bin"
cp docker/docker "$HOME/.local/bin/"
# Pfad für nachfolgende GitHub Actions Schritte verfügbar machen
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
# Pfad für diesen spezifischen Shell-Run exportieren
export PATH="$HOME/.local/bin:$PATH"
rm -rf docker docker-${DOCKER_VERSION}.tgz
echo "Docker CLI wurde erfolgreich aktualisiert."
else else
echo "Docker CLI ist bereits auf einem aktuellen Stand." echo "Docker CLI ist bereits auf einem aktuellen Stand."
fi fi
+343 -143
View File
@@ -1258,7 +1258,9 @@ def inject_version():
try: try:
client = MongoClient(MONGODB_HOST, MONGODB_PORT) client = MongoClient(MONGODB_HOST, MONGODB_PORT)
db = client[MONGODB_DB] db = client[MONGODB_DB]
unread_notification_count = _get_unread_notification_count(db, session['username'], is_admin=is_admin) # Pass the computed permission instead of the strict is_admin boolean
can_manage = current_permissions.get('actions', {}).get('can_manage_settings', False)
unread_notification_count = _get_unread_notification_count(db, session['username'], is_admin=(is_admin or can_manage))
except Exception: except Exception:
unread_notification_count = 0 unread_notification_count = 0
finally: finally:
@@ -1978,9 +1980,11 @@ def _upload_student_cards_excel():
flash('Nicht angemeldet.', 'error') flash('Nicht angemeldet.', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
if not us.check_admin(session['username']): current_permissions = us.get_effective_permissions(session['username'])
flash('Administratorrechte erforderlich.', 'error')
return redirect(url_for('home_admin')) if not current_permissions['actions'].get('can_manage_user', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('library'))
if not cfg.MODULES.is_enabled('student_cards'): if not cfg.MODULES.is_enabled('student_cards'):
flash('Schülerausweis-Modul ist deaktiviert.', 'error') flash('Schülerausweis-Modul ist deaktiviert.', 'error')
@@ -2850,20 +2854,18 @@ def test_connection():
Returns: Returns:
dict: Status information including version and status code dict: Status information including version and status code
""" """
if 'username' not in session or not us.check_admin(session['username']):
return {'status': 'forbidden'}, 403
return {'status': 'success', 'message': 'Connection successful', 'status_code': 200} return {'status': 'success', 'message': 'Connection successful', 'status_code': 200}
""" if sucess in deployment the funktion can be removed
@app.route('/user_status') @app.route('/user_status')
def user_status(): def user_status():
"""
API endpoint to get the current user's status (username, admin status). API endpoint to get the current user's status (username, admin status).
Used by JavaScript in templates to personalize the UI. Used by JavaScript in templates to personalize the UI.
Returns: Returns:
JSON: User status information or error if not authenticated JSON: User status information or error if not authenticated
"""
if 'username' in session: if 'username' in session:
is_admin = us.check_admin(session['username']) is_admin = us.check_admin(session['username'])
return jsonify({ return jsonify({
@@ -2876,8 +2878,10 @@ def user_status():
'authenticated': False, 'authenticated': False,
'error': 'Not logged in' 'error': 'Not logged in'
}), 401 }), 401
"""
##################################################### changes to be made to account for the new account permison managment system ##############################
@app.route('/') @app.route('/')
def home(): def home():
""" """
@@ -2979,7 +2983,6 @@ def library_export_excel(scope='all'):
return redirect(url_for('login')) return redirect(url_for('login'))
username = session['username'] username = session['username']
is_admin_user = us.check_admin(username)
# Base query # Base query
query = { query = {
@@ -2994,8 +2997,10 @@ def library_export_excel(scope='all'):
filename = f"Bibliothek_Ausgeliehen_{username}.xlsx" filename = f"Bibliothek_Ausgeliehen_{username}.xlsx"
elif scope == 'all_borrowed': elif scope == 'all_borrowed':
if not is_admin_user: current_permissions = us.get_effective_permissions(session['username'])
# Consider adding a flash message here before redirecting
if not current_permissions['actions'].get('can_view_logs', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect('/library') return redirect('/library')
query['Verfuegbar'] = False query['Verfuegbar'] = False
filename = "Bibliothek_Alle_Ausleihen.xlsx" filename = "Bibliothek_Alle_Ausleihen.xlsx"
@@ -3053,7 +3058,7 @@ def library_view():
'library_table.html', 'library_table.html',
username=session['username'], username=session['username'],
library_module_enabled=cfg.MODULES.is_enabled('library'), library_module_enabled=cfg.MODULES.is_enabled('library'),
is_admin=us.check_admin(session['username']), is_admin=us.check_admin(session['username']), # consider if this is needet
student_cards_module_enabled=cfg.MODULES.is_enabled('student_cards'), student_cards_module_enabled=cfg.MODULES.is_enabled('student_cards'),
student_default_borrow_days=cfg.STUDENT_DEFAULT_BORROW_DAYS, student_default_borrow_days=cfg.STUDENT_DEFAULT_BORROW_DAYS,
student_max_borrow_days=cfg.STUDENT_MAX_BORROW_DAYS student_max_borrow_days=cfg.STUDENT_MAX_BORROW_DAYS
@@ -3066,15 +3071,22 @@ def library_loans_admin():
if 'username' not in session: if 'username' not in session:
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error') flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
if not us.check_admin(session['username']):
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['pages'].get('library_loans_admin', False):
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error') flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) return redirect(url_for('library_view'))
if not cfg.MODULES.is_enabled('library'): if not cfg.MODULES.is_enabled('library'):
flash('Bibliotheks-Modul ist deaktiviert.', 'error') flash('Bibliotheks-Modul ist deaktiviert.', 'error')
return redirect(url_for('home_admin')) return redirect(url_for('home'))
_ensure_audit_indexes_once() _ensure_audit_indexes_once()
# IMPORT HINZUGEFÜGT: Entschlüsselungs-Tool importieren
from modules.inventarsystem.data_protection import decrypt_text
def fmt_dt(dt): def fmt_dt(dt):
try: try:
return dt.strftime('%d.%m.%Y %H:%M') if dt else '' return dt.strftime('%d.%m.%Y %H:%M') if dt else ''
@@ -3121,6 +3133,9 @@ def library_loans_admin():
item_has_damage = bool(item_doc.get('HasDamage')) or condition_value == 'destroyed' or bool(item_doc.get('DamageReports')) item_has_damage = bool(item_doc.get('HasDamage')) or condition_value == 'destroyed' or bool(item_doc.get('DamageReports'))
damage_reports = item_doc.get('DamageReports', []) or [] damage_reports = item_doc.get('DamageReports', []) or []
raw_user = record.get('User', '')
decrypted_user = decrypt_text(raw_user) if raw_user else ''
loan_entries.append({ loan_entries.append({
'id': str(record.get('_id')), 'id': str(record.get('_id')),
'item_id': item_id, 'item_id': item_id,
@@ -3128,7 +3143,7 @@ def library_loans_admin():
'item_code': item_doc.get('Code_4', ''), 'item_code': item_doc.get('Code_4', ''),
'item_author': item_doc.get('Author', ''), 'item_author': item_doc.get('Author', ''),
'item_isbn': item_doc.get('ISBN', ''), 'item_isbn': item_doc.get('ISBN', ''),
'user': record.get('User', ''), 'user': decrypted_user,
'status': record.get('Status', ''), 'status': record.get('Status', ''),
'start': fmt_dt(record.get('Start')), 'start': fmt_dt(record.get('Start')),
'end': fmt_dt(record.get('End')), 'end': fmt_dt(record.get('End')),
@@ -3181,7 +3196,6 @@ def library_loans_admin():
if client: if client:
client.close() client.close()
@app.route('/api/library_items') @app.route('/api/library_items')
def api_library_items(): def api_library_items():
""" """
@@ -3482,7 +3496,9 @@ def api_library_scan_action():
# Toggle back: item is currently borrowed -> return # Toggle back: item is currently borrowed -> return
current_borrower = str(item_doc.get('User') or '').strip() current_borrower = str(item_doc.get('User') or '').strip()
if current_borrower and current_borrower != borrower_name and not us.check_admin(session['username']): current_permissions = us.get_effective_permissions(session['username'])
if current_borrower and current_borrower != borrower_name and not current_permissions['actions'].get('can_edit', False):
return jsonify({ return jsonify({
'ok': False, 'ok': False,
'message': f"Medium ist aktuell an '{current_borrower}' ausgeliehen und kann mit diesem Ausweis nicht zurückgegeben werden." 'message': f"Medium ist aktuell an '{current_borrower}' ausgeliehen und kann mit diesem Ausweis nicht zurückgegeben werden."
@@ -3655,8 +3671,11 @@ def api_library_item_update(item_id):
"""Admin-only API to edit library item core fields from the library table view.""" """Admin-only API to edit library item core fields from the library table view."""
if 'username' not in session: if 'username' not in session:
return jsonify({'ok': False, 'message': 'Nicht angemeldet.'}), 401 return jsonify({'ok': False, 'message': 'Nicht angemeldet.'}), 401
if not us.check_admin(session['username']): current_permissions = us.get_effective_permissions(session['username'])
return jsonify({'ok': False, 'message': 'Administratorrechte erforderlich.'}), 403
if not current_permissions['actions'].get('can_edit', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('library'))
if not cfg.MODULES.is_enabled('library'): if not cfg.MODULES.is_enabled('library'):
return jsonify({'ok': False, 'message': 'Bibliotheks-Modul ist deaktiviert.'}), 403 return jsonify({'ok': False, 'message': 'Bibliotheks-Modul ist deaktiviert.'}), 403
@@ -3885,9 +3904,11 @@ def student_cards_admin():
if 'username' not in session: if 'username' not in session:
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error') flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
if not us.check_admin(session['username']): current_permissions = us.get_effective_permissions(session['username'])
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) if not current_permissions['actions'].get('can_manage_users', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('library'))
if not cfg.MODULES.is_enabled('student_cards'): if not cfg.MODULES.is_enabled('student_cards'):
flash('Schülerausweis-Modul ist deaktiviert.', 'error') flash('Schülerausweis-Modul ist deaktiviert.', 'error')
return redirect(url_for('home_admin')) return redirect(url_for('home_admin'))
@@ -4028,9 +4049,11 @@ def student_cards_print():
if 'username' not in session: if 'username' not in session:
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error') flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
if not us.check_admin(session['username']): current_permissions = us.get_effective_permissions(session['username'])
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) if not current_permissions['actions'].get('can_manage_users', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('library'))
if not cfg.MODULES.is_enabled('student_cards'): if not cfg.MODULES.is_enabled('student_cards'):
flash('Schülerausweis-Modul ist deaktiviert.', 'error') flash('Schülerausweis-Modul ist deaktiviert.', 'error')
return redirect(url_for('home_admin')) return redirect(url_for('home_admin'))
@@ -4060,9 +4083,11 @@ def student_card_barcode_print():
if 'username' not in session: if 'username' not in session:
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error') flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
if not us.check_admin(session['username']): current_permissions = us.get_effective_permissions(session['username'])
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) if not current_permissions['actions'].get('can_manage_users', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('library'))
if not cfg.MODULES.is_enabled('student_cards'): if not cfg.MODULES.is_enabled('student_cards'):
flash('Schülerausweis-Modul ist deaktiviert.', 'error') flash('Schülerausweis-Modul ist deaktiviert.', 'error')
return redirect(url_for('home_admin')) return redirect(url_for('home_admin'))
@@ -4091,9 +4116,11 @@ def student_card_barcode_download():
""" """
if 'username' not in session: if 'username' not in session:
return redirect(url_for('login')) return redirect(url_for('login'))
if not us.check_admin(session['username']): current_permissions = us.get_effective_permissions(session['username'])
flash('Zugriff verweigert.', 'error')
return redirect(url_for('login')) if not current_permissions['actions'].get('can_manage_users', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('library'))
if not cfg.MODULES.is_enabled('student_cards'): if not cfg.MODULES.is_enabled('student_cards'):
flash('Schülerausweis-Modul ist deaktiviert.', 'error') flash('Schülerausweis-Modul ist deaktiviert.', 'error')
return redirect(url_for('home_admin')) return redirect(url_for('home_admin'))
@@ -4273,9 +4300,11 @@ def student_card_single_barcode_download(card_id):
""" """
if 'username' not in session: if 'username' not in session:
return redirect(url_for('login')) return redirect(url_for('login'))
if not us.check_admin(session['username']): current_permissions = us.get_effective_permissions(session['username'])
flash('Zugriff verweigert.', 'error')
return redirect(url_for('login')) if not current_permissions['actions'].get('can_manage_users', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('library'))
if not cfg.MODULES.is_enabled('student_cards'): if not cfg.MODULES.is_enabled('student_cards'):
flash('Schülerausweis-Modul ist deaktiviert.', 'error') flash('Schülerausweis-Modul ist deaktiviert.', 'error')
return redirect(url_for('home_admin')) return redirect(url_for('home_admin'))
@@ -4970,13 +4999,14 @@ def api_booking_conflicts():
if 'username' not in session: if 'username' not in session:
return jsonify({'error': 'Not authenticated'}), 401 return jsonify({'error': 'Not authenticated'}), 401
try: try:
is_admin = us.check_admin(session['username'])
client = MongoClient(MONGODB_HOST, MONGODB_PORT) client = MongoClient(MONGODB_HOST, MONGODB_PORT)
db = client[MONGODB_DB] db = client[MONGODB_DB]
ausleihungen = db['ausleihungen'] ausleihungen = db['ausleihungen']
query = {'ConflictDetected': True, 'Status': 'active'} query = {'ConflictDetected': True, 'Status': 'active'}
if not is_admin: current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['actions'].get('can_view_logs', False):
query['User'] = session['username'] query['User'] = session['username']
conflicts = list(ausleihungen.find(query)) conflicts = list(ausleihungen.find(query))
@@ -6133,8 +6163,11 @@ def duplicate_item():
# Check if user is admin # Check if user is admin
username = session['username'] username = session['username']
if not us.check_admin(username): current_permissions = us.get_effective_permissions(session['username'])
return jsonify({'success': False, 'message': 'Keine Administratorrechte'}), 403
if not current_permissions['actions'].get('can_edit', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
# Detect if request is from mobile device # Detect if request is from mobile device
is_mobile = 'Mobile' in request.headers.get('User-Agent', '') is_mobile = 'Mobile' in request.headers.get('User-Agent', '')
@@ -6344,11 +6377,18 @@ def delete_item(id):
flask.Response: Redirect to admin homepage flask.Response: Redirect to admin homepage
""" """
if 'username' not in session: if 'username' not in session:
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error') flash('Nicht angemeldet.', 'error')
return redirect(url_for('login'))
if not us.check_admin(session['username']):
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['actions'].get('can_delete', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion (Löschen) auszuführen.', 'error')
return redirect(url_for('home'))
if not cfg.MODULES.is_enabled('inventory'):
flash('Bibliotheks-Modul ist deaktiviert.', 'error')
return redirect(url_for('library'))
# Resolve all related item ids (grouped variants) and load their data # Resolve all related item ids (grouped variants) and load their data
group_item_ids = it.get_group_item_ids(id) group_item_ids = it.get_group_item_ids(id)
@@ -6403,12 +6443,16 @@ def delete_library_item(id):
if 'username' not in session: if 'username' not in session:
flash('Nicht angemeldet.', 'error') flash('Nicht angemeldet.', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
if not us.check_admin(session['username']):
flash('Administratorrechte erforderlich.', 'error') current_permissions = us.get_effective_permissions(session['username'])
return redirect(url_for('login'))
if not current_permissions['actions'].get('can_delete', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion (Löschen) auszuführen.', 'error')
return redirect(url_for('home'))
if not cfg.MODULES.is_enabled('library'): if not cfg.MODULES.is_enabled('library'):
flash('Bibliotheks-Modul ist deaktiviert.', 'error') flash('Bibliotheks-Modul ist deaktiviert.', 'error')
return redirect(url_for('home_admin')) return redirect(url_for('home'))
# Verify item exists and is a library item # Verify item exists and is a library item
try: try:
@@ -6471,9 +6515,18 @@ def delete_library_item(id):
def bulk_delete_items(): def bulk_delete_items():
"""Soft-delete multiple selected item groups in one request.""" """Soft-delete multiple selected item groups in one request."""
if 'username' not in session: if 'username' not in session:
return jsonify({'success': False, 'message': 'Nicht angemeldet'}), 401 flash('Nicht angemeldet.', 'error')
if not us.check_admin(session['username']): return redirect(url_for('login'))
return jsonify({'success': False, 'message': 'Administratorrechte erforderlich'}), 403
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['actions'].get('can_delete', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion (Löschen) auszuführen.', 'error')
return redirect(url_for('home'))
if not cfg.MODULES.is_enabled('inventory'):
flash('Bibliotheks-Modul ist deaktiviert.', 'error')
return redirect(url_for('library'))
payload = request.get_json(silent=True) or {} payload = request.get_json(silent=True) or {}
item_ids = payload.get('item_ids') or request.form.getlist('item_ids') item_ids = payload.get('item_ids') or request.form.getlist('item_ids')
@@ -6527,11 +6580,18 @@ def edit_item(id):
flask.Response: Redirect to admin homepage with status message flask.Response: Redirect to admin homepage with status message
""" """
if 'username' not in session: if 'username' not in session:
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error') flash('Nicht angemeldet.', 'error')
return redirect(url_for('login'))
if not us.check_admin(session['username']):
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['actions'].get('can_edit', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion (Löschen) auszuführen.', 'error')
return redirect(url_for('home'))
if not cfg.MODULES.is_enabled('inventory'):
flash('Bibliotheks-Modul ist deaktiviert.', 'error')
return redirect(url_for('library'))
# Strip whitespace from all text fields # Strip whitespace from all text fields
name = sanitize_form_value(request.form.get('name')) name = sanitize_form_value(request.form.get('name'))
@@ -6652,6 +6712,17 @@ def edit_item(id):
@app.route('/update_group', methods=['POST']) @app.route('/update_group', methods=['POST'])
def update_group(): def update_group():
if 'username' not in session:
flash('Nicht angemeldet.', 'error')
return redirect(url_for('login'))
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['actions'].get('can_edit', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion (Löschen) auszuführen.', 'error')
return redirect(url_for('library'))
data = request.get_json() data = request.get_json()
series_group_id = data.get('series_group_id') series_group_id = data.get('series_group_id')
@@ -6712,8 +6783,6 @@ def report_damage(id):
"""Register a damage report entry for an item (admin only).""" """Register a damage report entry for an item (admin only)."""
if 'username' not in session: if 'username' not in session:
return jsonify({'success': False, 'message': 'Nicht angemeldet'}), 401 return jsonify({'success': False, 'message': 'Nicht angemeldet'}), 401
if not us.check_admin(session['username']):
return jsonify({'success': False, 'message': 'Administratorrechte erforderlich'}), 403
payload = request.get_json(silent=True) or {} payload = request.get_json(silent=True) or {}
description = str(payload.get('description', '')).strip() or 'Schaden gemeldet' description = str(payload.get('description', '')).strip() or 'Schaden gemeldet'
@@ -6812,8 +6881,11 @@ def mark_damage_repaired(id):
"""Mark all currently open damage reports of an item as repaired (admin only).""" """Mark all currently open damage reports of an item as repaired (admin only)."""
if 'username' not in session: if 'username' not in session:
return jsonify({'success': False, 'message': 'Nicht angemeldet'}), 401 return jsonify({'success': False, 'message': 'Nicht angemeldet'}), 401
if not us.check_admin(session['username']): current_permissions = us.get_effective_permissions(session['username'])
return jsonify({'success': False, 'message': 'Administratorrechte erforderlich'}), 403
if not current_permissions['actions'].get('can_edit', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion (Löschen) auszuführen.', 'error')
return redirect(url_for('home_admin'))
client = None client = None
try: try:
@@ -6933,7 +7005,7 @@ def ausleihen(id):
if requested_return_to == 'library' and cfg.MODULES.is_enabled('library'): if requested_return_to == 'library' and cfg.MODULES.is_enabled('library'):
redirect_target = 'library_view' redirect_target = 'library_view'
else: else:
redirect_target = 'home_admin' if us.check_admin(username) else 'home' redirect_target = 'home_admin' if us.check_admin(username) else 'home' # check for plausability
item = it.get_item(id) item = it.get_item(id)
if not item: if not item:
@@ -6992,7 +7064,9 @@ def ausleihen(id):
return redirect(url_for(redirect_target)) return redirect(url_for(redirect_target))
# Admins can borrow on behalf of students via student card id. # Admins can borrow on behalf of students via student card id.
if us.check_admin(username) and not is_library_item: current_permissions = us.get_effective_permissions(session['username'])
if current_permissions['actions'].get('can_borrow') and not is_library_item:
if student_card_id: if student_card_id:
student_user = us.get_user_by_student_card(student_card_id) student_user = us.get_user_by_student_card(student_card_id)
if not student_user: if not student_user:
@@ -7250,8 +7324,10 @@ def zurueckgeben(id):
return redirect(url_for('home')) return redirect(url_for('home'))
username = session['username'] username = session['username']
if not item.get('Verfuegbar', True) and (us.check_admin(session['username']) or item.get('User') == username): current_permissions = us.get_effective_permissions(session['username'])
if not item.get('Verfuegbar', True) and (current_permissions['actions'].get('can_manage_users', False)) or item.get('User') == username):
try: try:
# Get ALL active borrowing records for this item and complete them # Get ALL active borrowing records for this item and complete them
client = MongoClient(MONGODB_HOST, MONGODB_PORT) client = MongoClient(MONGODB_HOST, MONGODB_PORT)
@@ -7317,8 +7393,6 @@ def zurueckgeben(id):
if source_page == 'my_borrowed_items' or '/my_borrowed_items' in referrer: if source_page == 'my_borrowed_items' or '/my_borrowed_items' in referrer:
return redirect(url_for('my_borrowed_items')) return redirect(url_for('my_borrowed_items'))
if 'username' in session and not us.check_admin(session['username']):
return redirect(url_for('home'))
return redirect(url_for('home_admin')) return redirect(url_for('home_admin'))
@app.route('/get_filter', methods=['GET']) @app.route('/get_filter', methods=['GET'])
@@ -7360,22 +7434,20 @@ def get_ausleihung_by_item_route(id):
user=session.get('username', None) user=session.get('username', None)
) )
ausleihung['VerifiedStatus'] = current_status ausleihung['VerifiedStatus'] = current_status
# Admin users can see all borrowing details current_permissions = us.get_effective_permissions(session['username'])
# Regular users can only see their own borrowings
if ausleihung and (us.check_admin(session['username']) or ausleihung.get('User') == session['username']): if ausleihung and (current_permissions['actions'].get('can_manage_users')) or ausleihung.get('User') == session['username']:
return {'ausleihung': ausleihung, 'status': 'success'} return {'ausleihung': ausleihung, 'status': 'success'}
# Get item name for better error message
item = it.get_item(id) item = it.get_item(id)
item_name = item.get('Name', 'Unknown') if item else 'Unknown' item_name = item.get('Name', 'Unknown') if item else 'Unknown'
# Return a more informative error
return { return {
'error': 'No active borrowing record found for this item', 'error': 'No active borrowing record found for this item',
'item_name': item_name, 'item_name': item_name,
'status': 'not_found' 'status': 'not_found'
}, 200 # Return 200 instead of 404 to allow processing of the error message }, 200
@app.route('/get_planned_bookings/<item_id>') @app.route('/get_planned_bookings/<item_id>')
@@ -7383,8 +7455,11 @@ def get_planned_bookings(item_id):
""" """
Return all planned bookings for a given item (admin only). Return all planned bookings for a given item (admin only).
""" """
if 'username' not in session or not us.check_admin(session['username']): current_permissions = us.get_effective_permissions(session['username'])
return jsonify({'ok': False, 'error': 'unauthorized'}), 403
if not current_permissions['pages'].get('admin_borrowings', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
try: try:
client = MongoClient(MONGODB_HOST, MONGODB_PORT) client = MongoClient(MONGODB_HOST, MONGODB_PORT)
@@ -7802,8 +7877,10 @@ def cancel_booking(id):
if not booking: if not booking:
return {"success": False, "error": "Booking not found"}, 404 return {"success": False, "error": "Booking not found"}, 404
current_permissions = us.get_effective_permissions(session['username'])
# Check if user owns this booking # Check if user owns this booking
if booking.get('User') != session['username'] and not us.check_admin(session['username']): if booking.get('User') != session['username'] and not current_permissions['actions'].get('can_manage_users', False):
return {"success": False, "error": "Not authorized to cancel this booking"}, 403 return {"success": False, "error": "Not authorized to cancel this booking"}, 403
# Cancel the booking # Cancel the booking
@@ -7883,7 +7960,7 @@ def register():
page_permissions = {} page_permissions = {}
for endpoint_name, _ in PERMISSION_PAGE_OPTIONS: for endpoint_name, _ in PERMISSION_PAGE_OPTIONS:
page_permissions[endpoint_name] = request.form.get(f'page_{endpoint_name}') == 'on' page_permissions[endpoint_name] = request.form.get(f'page_{endpoint_name}') == 'on'
us.add_user( us.add_user(
username, username,
password, password,
@@ -8042,12 +8119,19 @@ def admin_borrowings():
""" """
Admin view: list all active and planned borrowings with ability to reset. Admin view: list all active and planned borrowings with ability to reset.
""" """
if 'username' not in session or not us.check_admin(session['username']): if 'username' not in session:
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error') flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['pages'].get('admin_borrowings', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
_ensure_audit_indexes_once() _ensure_audit_indexes_once()
from modules.inventarsystem.data_protection import decrypt_text
client = MongoClient(MONGODB_HOST, MONGODB_PORT) client = MongoClient(MONGODB_HOST, MONGODB_PORT)
db = client[MONGODB_DB] db = client[MONGODB_DB]
ausleihungen = db['ausleihungen'] ausleihungen = db['ausleihungen']
@@ -8081,6 +8165,10 @@ def admin_borrowings():
item_name = None item_name = None
item_cost = None item_cost = None
has_damage = False has_damage = False
raw_user = r.get('User', '')
decrypted_user = decrypt_text(raw_user) if raw_user else ''
entries.append({ entries.append({
'id': str(r.get('_id')), 'id': str(r.get('_id')),
'item_id': str(item_doc.get('_id')) if item_doc and item_doc.get('_id') else str(it_id or ''), 'item_id': str(item_doc.get('_id')) if item_doc and item_doc.get('_id') else str(it_id or ''),
@@ -8088,7 +8176,7 @@ def admin_borrowings():
'item_name': str(item_name or ''), 'item_name': str(item_name or ''),
'item_cost': fmt_money(item_cost), 'item_cost': fmt_money(item_cost),
'item_cost_raw': item_cost if item_cost is not None else '', 'item_cost_raw': item_cost if item_cost is not None else '',
'user': r.get('User', ''), 'user': decrypted_user,
'status': r.get('Status', ''), 'status': r.get('Status', ''),
'start': fmt_dt(r.get('Start')), 'start': fmt_dt(r.get('Start')),
'end': fmt_dt(r.get('End')), 'end': fmt_dt(r.get('End')),
@@ -8119,9 +8207,15 @@ def admin_borrowings():
@app.route('/admin/audit/verify', methods=['GET']) @app.route('/admin/audit/verify', methods=['GET'])
def admin_verify_audit_chain(): def admin_verify_audit_chain():
"""Admin endpoint to verify audit chain integrity.""" """Admin endpoint to verify audit chain integrity."""
if 'username' not in session or not us.check_admin(session['username']): if 'username' not in session:
return jsonify({'ok': False, 'error': 'forbidden'}), 403 return jsonify({'ok': False, 'error': 'forbidden'}), 403
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['pages'].get('admin_audit_dashboard', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
client = None client = None
try: try:
client = MongoClient(MONGODB_HOST, MONGODB_PORT) client = MongoClient(MONGODB_HOST, MONGODB_PORT)
@@ -8139,10 +8233,16 @@ def admin_verify_audit_chain():
@app.route('/admin/audit', methods=['GET']) @app.route('/admin/audit', methods=['GET'])
def admin_audit_dashboard(): def admin_audit_dashboard():
"""Admin dashboard for audit chain status and recent events.""" """Admin dashboard for audit chain status and recent events."""
if 'username' not in session or not us.check_admin(session['username']): if 'username' not in session:
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error') flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['pages'].get('admin_audit_dashboard', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
client = None client = None
try: try:
client = MongoClient(MONGODB_HOST, MONGODB_PORT) client = MongoClient(MONGODB_HOST, MONGODB_PORT)
@@ -8194,9 +8294,15 @@ def admin_audit_dashboard():
@app.route('/admin/audit/export/pdf/official', methods=['GET']) @app.route('/admin/audit/export/pdf/official', methods=['GET'])
def admin_audit_export_pdf_official(): def admin_audit_export_pdf_official():
"""Export audit report as professional PDF (Official Report - full DIN 5008 compliant).""" """Export audit report as professional PDF (Official Report - full DIN 5008 compliant)."""
if 'username' not in session or not us.check_admin(session['username']): if 'username' not in session:
return jsonify({'ok': False, 'error': 'forbidden'}), 403 return jsonify({'ok': False, 'error': 'forbidden'}), 403
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['pages'].get('admin_audit_dashboard', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
try: try:
limit = int((request.args.get('limit') or '1000').strip()) limit = int((request.args.get('limit') or '1000').strip())
except Exception: except Exception:
@@ -8383,10 +8489,16 @@ def admin_reset_borrowing(borrow_id):
- If active: complete it and free the item - If active: complete it and free the item
- If planned: cancel it - If planned: cancel it
""" """
if 'username' not in session or not us.check_admin(session['username']): if 'username' not in session:
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error') flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['actions'].get('can_manage_users', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
try: try:
client = MongoClient(MONGODB_HOST, MONGODB_PORT) client = MongoClient(MONGODB_HOST, MONGODB_PORT)
db = client[MONGODB_DB] db = client[MONGODB_DB]
@@ -8450,10 +8562,16 @@ def admin_reset_borrowing(borrow_id):
@app.route('/admin/borrowings/<borrow_id>/invoice', methods=['POST']) @app.route('/admin/borrowings/<borrow_id>/invoice', methods=['POST'])
def admin_create_invoice(borrow_id): def admin_create_invoice(borrow_id):
"""Create a PDF invoice for a destroyed borrowed item.""" """Create a PDF invoice for a destroyed borrowed item."""
if 'username' not in session or not us.check_admin(session['username']): if 'username' not in session:
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error') flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['pages'].get('admin_borrowings', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
client = None client = None
try: try:
client = MongoClient(MONGODB_HOST, MONGODB_PORT) client = MongoClient(MONGODB_HOST, MONGODB_PORT)
@@ -8613,10 +8731,16 @@ def admin_create_invoice(borrow_id):
@app.route('/admin/borrowings/<borrow_id>/invoice/mark-paid', methods=['POST']) @app.route('/admin/borrowings/<borrow_id>/invoice/mark-paid', methods=['POST'])
def admin_mark_invoice_paid(borrow_id): def admin_mark_invoice_paid(borrow_id):
"""Mark an existing invoice as paid.""" """Mark an existing invoice as paid."""
if 'username' not in session or not us.check_admin(session['username']): if 'username' not in session:
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error') flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['pages'].get('admin_borrowings', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
client = None client = None
try: try:
client = MongoClient(MONGODB_HOST, MONGODB_PORT) client = MongoClient(MONGODB_HOST, MONGODB_PORT)
@@ -8691,10 +8815,16 @@ def admin_mark_invoice_paid(borrow_id):
@app.route('/admin/borrowings/<borrow_id>/invoice/finalize', methods=['POST']) @app.route('/admin/borrowings/<borrow_id>/invoice/finalize', methods=['POST'])
def admin_finalize_invoice_and_repair(borrow_id): def admin_finalize_invoice_and_repair(borrow_id):
"""Mark invoice as paid and item as repaired in one action.""" """Mark invoice as paid and item as repaired in one action."""
if 'username' not in session or not us.check_admin(session['username']): if 'username' not in session:
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error') flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['pages'].get('admin_borrowings', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
client = None client = None
try: try:
client = MongoClient(MONGODB_HOST, MONGODB_PORT) client = MongoClient(MONGODB_HOST, MONGODB_PORT)
@@ -8811,10 +8941,16 @@ def admin_finalize_invoice_and_repair(borrow_id):
@app.route('/admin/borrowings/<borrow_id>/invoice/pdf', methods=['GET']) @app.route('/admin/borrowings/<borrow_id>/invoice/pdf', methods=['GET'])
def admin_view_invoice_pdf(borrow_id): def admin_view_invoice_pdf(borrow_id):
"""View a previously created invoice PDF for a borrowing.""" """View a previously created invoice PDF for a borrowing."""
if 'username' not in session or not us.check_admin(session['username']): if 'username' not in session:
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error') flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['pages'].get('admin_borrowings', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
client = None client = None
try: try:
client = MongoClient(MONGODB_HOST, MONGODB_PORT) client = MongoClient(MONGODB_HOST, MONGODB_PORT)
@@ -8861,10 +8997,16 @@ def admin_view_invoice_pdf(borrow_id):
@app.route('/admin/borrowings/<borrow_id>/invoice/correction', methods=['POST']) @app.route('/admin/borrowings/<borrow_id>/invoice/correction', methods=['POST'])
def admin_add_invoice_correction(borrow_id): def admin_add_invoice_correction(borrow_id):
"""Append an invoice correction entry without mutating the original invoice body.""" """Append an invoice correction entry without mutating the original invoice body."""
if 'username' not in session or not us.check_admin(session['username']): if 'username' not in session:
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error') flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['pages'].get('admin_borrowings', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
client = None client = None
try: try:
client = MongoClient(MONGODB_HOST, MONGODB_PORT) client = MongoClient(MONGODB_HOST, MONGODB_PORT)
@@ -8937,12 +9079,17 @@ def admin_add_invoice_correction(borrow_id):
@app.route('/admin/library/items/<item_id>/invoices', methods=['GET']) @app.route('/admin/library/items/<item_id>/invoices', methods=['GET'])
def library_item_invoices(item_id): def library_item_invoices(item_id):
"""Show all stored invoices for one specific library item.""" """Show all stored invoices for one specific library item."""
if 'username' not in session or not us.check_admin(session['username']): if 'username' not in session:
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error') flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
if not cfg.MODULES.is_enabled('library'): if not cfg.MODULES.is_enabled('library'):
flash('Bibliotheks-Modul ist deaktiviert.', 'error') flash('Bibliotheks-Modul ist deaktiviert.', 'error')
return redirect(url_for('home_admin')) return redirect(url_for('home_admin'))
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['pages'].get('library_loans_admin', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('library'))
client = None client = None
try: try:
@@ -9072,10 +9219,15 @@ def admin_update_user_name():
Returns: Returns:
flask.Response: Redirect to user management page flask.Response: Redirect to user management page
""" """
if 'username' not in session or not us.check_admin(session['username']): if 'username' not in session:
flash('Nicht autorisierter Zugriff', 'error') flash('Nicht autorisierter Zugriff', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['actions'].get('can_manage_users', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
username = request.form.get('username') username = request.form.get('username')
name = html.escape(request.form.get('name')) name = html.escape(request.form.get('name'))
last_name = html.escape(request.form.get('last_name')) last_name = html.escape(request.form.get('last_name'))
@@ -9204,9 +9356,11 @@ def logs():
if 'username' not in session: if 'username' not in session:
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error') flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
if not us.check_admin(session['username']): current_permissions = us.get_effective_permissions(session['username'])
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) if not current_permissions['pages'].get('logs', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
# Get ausleihungen # Get ausleihungen
all_ausleihungen = au.get_ausleihungen() all_ausleihungen = au.get_ausleihungen()
@@ -9270,6 +9424,10 @@ def logs():
logs_collection = db['system_logs'] logs_collection = db['system_logs']
extra_logs = list(logs_collection.find({'type': {'$in': ['damage_report', 'damage_repair']}})) extra_logs = list(logs_collection.find({'type': {'$in': ['damage_report', 'damage_repair']}}))
from modules.inventarsystem.data_protection import decrypt_text
from bson.objectid import ObjectId
for log_item in extra_logs: for log_item in extra_logs:
log_type = log_item.get('type', '') log_type = log_item.get('type', '')
item_id = log_item.get('item_id') item_id = log_item.get('item_id')
@@ -9289,7 +9447,7 @@ def logs():
note = log_item.get('note', '') note = log_item.get('note', '')
formatted_items.append({ formatted_items.append({
'Item': item_name, 'Item': item_name,
'User': log_item.get('user', 'Unknown User'), 'User': decrypt_text(log_item.get('user', 'Unknown User')),
'Start': ts_display, 'Start': ts_display,
'End': '-', 'End': '-',
'Duration': '-', 'Duration': '-',
@@ -9303,7 +9461,7 @@ def logs():
resolved_count = log_item.get('resolved_count', 0) resolved_count = log_item.get('resolved_count', 0)
formatted_items.append({ formatted_items.append({
'Item': item_name, 'Item': item_name,
'User': log_item.get('user', 'Unknown User'), 'User': decrypt_text(log_item.get('user', 'Unknown User')),
'Start': ts_display, 'Start': ts_display,
'End': '-', 'End': '-',
'Duration': '-', 'Duration': '-',
@@ -9368,16 +9526,10 @@ def get_usernames():
if 'username' not in session: if 'username' not in session:
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error') flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
if not us.check_admin(session['username']): current_permissions = us.get_effective_permissions(session['username'])
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('logout')) if current_permissions['actions'].get('can_manage_users', False):
elif 'username' in session and us.check_admin(session['username']): return jsonify(us.get_all_users())
return jsonify(us.get_all_users()) # Fixed to use get_all_users
else:
flash('Bitte melden Sie sich an, um auf diese Funktion zuzugreifen', 'error')
return redirect(url_for('login')) # Added proper return
# New routes for filter management
@app.route('/manage_filters') @app.route('/manage_filters')
def manage_filters(): def manage_filters():
@@ -9391,9 +9543,11 @@ def manage_filters():
if 'username' not in session: if 'username' not in session:
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error') flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
if not us.check_admin(session['username']): current_permissions = us.get_effective_permissions(session['username'])
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) if not current_permissions['pages'].get('manage_filters', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
# Get predefined filter values # Get predefined filter values
filter1_values = it.get_predefined_filter_values(1) filter1_values = it.get_predefined_filter_values(1)
@@ -9424,8 +9578,13 @@ def add_filter_value(filter_num):
Returns: Returns:
flask.Response: Redirect to filter management page flask.Response: Redirect to filter management page
""" """
if 'username' not in session or not us.check_admin(session['username']): if 'username' not in session:
return jsonify({'success': False, 'error': 'Not authorized'}), 403 return jsonify({'success': False, 'error': 'Not authorized'}), 403
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['actions'].get('can_edit', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
value = sanitize_form_value(request.form.get('value')) value = sanitize_form_value(request.form.get('value'))
@@ -9459,8 +9618,14 @@ def remove_filter_value(filter_num, value):
Returns: Returns:
flask.Response: Redirect to filter management page flask.Response: Redirect to filter management page
""" """
if 'username' not in session or not us.check_admin(session['username']): if 'username' not in session:
return jsonify({'success': False, 'error': 'Not authorized'}), 403 return jsonify({'success': False, 'error': 'Not authorized'}), 403
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['actions'].get('can_edit', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
# Remove the value from the filter # Remove the value from the filter
success = it.remove_predefined_filter_value(filter_num, value) success = it.remove_predefined_filter_value(filter_num, value)
@@ -9484,8 +9649,11 @@ def edit_filter_value(filter_num, old_value):
Returns: Returns:
flask.Response: Redirect to filter management page flask.Response: Redirect to filter management page
""" """
if 'username' not in session or not us.check_admin(session['username']): current_permissions = us.get_effective_permissions(session['username'])
return jsonify({'success': False, 'error': 'Not authorized'}), 403
if not current_permissions['actions'].get('can_edit', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
new_value = sanitize_form_value(request.form.get('new_value')) new_value = sanitize_form_value(request.form.get('new_value'))
@@ -9822,8 +9990,13 @@ def fetch_book_info(isbn):
Optimized for global literature AND German educational books. Optimized for global literature AND German educational books.
""" """
# Authorization Checks # Authorization Checks
if 'username' not in session or not us.check_admin(session['username']): if 'username' not in session:
return jsonify({"error": "Not authorized"}), 403 return jsonify({"error": "Not authorized"}), 403
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['actions'].get('can_insert', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('library'))
if not cfg.MODULES.is_enabled('library'): if not cfg.MODULES.is_enabled('library'):
return jsonify({"error": "Bibliotheks-Modul ist deaktiviert."}), 403 return jsonify({"error": "Bibliotheks-Modul ist deaktiviert."}), 403
@@ -9870,8 +10043,11 @@ def download_book_cover():
""" """
if 'username' not in session: if 'username' not in session:
return jsonify({"error": "Not authorized"}), 403 return jsonify({"error": "Not authorized"}), 403
if not us.check_admin(session['username']): current_permissions = us.get_effective_permissions(session['username'])
return jsonify({"error": "Admin privileges required"}), 403
if not current_permissions['actions'].get('can_insert', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('library'))
if not cfg.MODULES.is_enabled('library'): if not cfg.MODULES.is_enabled('library'):
return jsonify({"error": "Bibliotheks-Modul ist deaktiviert."}), 403 return jsonify({"error": "Bibliotheks-Modul ist deaktiviert."}), 403
@@ -10200,9 +10376,11 @@ def notifications_view():
username = session['username'] username = session['username']
is_admin_user = False is_admin_user = False
try: current_permissions = us.get_effective_permissions(session['username'])
is_admin_user = us.check_admin(username)
except Exception: if not current_permissions['actions'].get('can_manage_settings', False):
is_admin_user = True
else:
is_admin_user = False is_admin_user = False
client = None client = None
@@ -10284,9 +10462,12 @@ def mark_all_notifications_read():
username = session['username'] username = session['username']
is_admin_user = False is_admin_user = False
try:
is_admin_user = us.check_admin(username) current_permissions = us.get_effective_permissions(session['username'])
except Exception:
if not current_permissions['actions'].get('can_manage_settings', False):
is_admin_user = True
else:
is_admin_user = False is_admin_user = False
query = { query = {
@@ -10327,9 +10508,12 @@ def notifications_unread_status():
username = session['username'] username = session['username']
is_admin_user = False is_admin_user = False
try:
is_admin_user = us.check_admin(username) current_permissions = us.get_effective_permissions(session['username'])
except Exception:
if not current_permissions['actions'].get('can_manage_settings', False):
is_admin_user = True
else:
is_admin_user = False is_admin_user = False
cached_payload, version_tag = _get_cached_unread_status(username, is_admin=is_admin_user) cached_payload, version_tag = _get_cached_unread_status(username, is_admin=is_admin_user)
@@ -10398,7 +10582,7 @@ def notifications_unread_status():
@app.route('/admin/damaged_items') @app.route('/admin/damaged_items')
def damaged_items(): def admin_damaged_items():
"""Admin-Übersicht aller aktiven und vergangenen Ausleihen.""" """Admin-Übersicht aller aktiven und vergangenen Ausleihen."""
if 'username' not in session: if 'username' not in session:
flash('Administratorrechte erforderlich.', 'error') flash('Administratorrechte erforderlich.', 'error')
@@ -10478,9 +10662,15 @@ def add_location_value():
Returns: Returns:
flask.Response: Redirect to location management page flask.Response: Redirect to location management page
""" """
if 'username' not in session or not us.check_admin(session['username']): if 'username' not in session:
return jsonify({'success': False, 'error': 'Not authorized'}), 403 return jsonify({'success': False, 'error': 'Not authorized'}), 403
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['actions'].get('can_insert', False) or not current_permissions['actions'].get('can_edit', True):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
value = sanitize_form_value(request.form.get('value')) value = sanitize_form_value(request.form.get('value'))
if not value: if not value:
@@ -10508,9 +10698,15 @@ def remove_location_value(value):
Returns: Returns:
flask.Response: Redirect to location management page flask.Response: Redirect to location management page
""" """
if 'username' not in session or not us.check_admin(session['username']): if 'username' not in session:
return jsonify({'success': False, 'error': 'Not authorized'}), 403 return jsonify({'success': False, 'error': 'Not authorized'}), 403
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['actions'].get('can_delete', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
# Remove the value from locations # Remove the value from locations
success = it.remove_predefined_location(value) success = it.remove_predefined_location(value)
@@ -10532,9 +10728,11 @@ def manage_locations():
if 'username' not in session: if 'username' not in session:
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error') flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
if not us.check_admin(session['username']): current_permissions = us.get_effective_permissions(session['username'])
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('login')) if not current_permissions['actions'].get('can_edit', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
# Get predefined location values # Get predefined location values
location_values = it.get_predefined_locations() location_values = it.get_predefined_locations()
@@ -10553,13 +10751,10 @@ def admin_school_settings():
if 'username' not in session: if 'username' not in session:
flash('Bitte melden Sie sich mit einem administrativen Konto an.', 'error') flash('Bitte melden Sie sich mit einem administrativen Konto an.', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
if not us.check_admin(session['username']): current_permissions = us.get_effective_permissions(session['username'])
flash('Für diese Seite sind Administratorrechte erforderlich.', 'error')
return redirect(url_for('login')) if not current_permissions['actions'].get('can_manage_settings', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
permissions = _get_current_user_permissions()
if not _action_access_allowed(permissions, 'can_manage_settings'):
flash('Sie haben keine Berechtigung, die Schulstammdaten zu ändern.', 'error')
return redirect(url_for('home_admin')) return redirect(url_for('home_admin'))
current_school = _get_school_info_for_export() current_school = _get_school_info_for_export()
@@ -10967,10 +11162,12 @@ def cancel_ausleihung_route(id):
# Log ausleihung details for debugging # Log ausleihung details for debugging
ausleihung_status = ausleihung.get('Status', 'unknown') ausleihung_status = ausleihung.get('Status', 'unknown')
ausleihung_user = ausleihung.get('User', 'unknown') ausleihung_user = ausleihung.get('User', 'unknown')
print(f"Found ausleihung: ID={id}, User={ausleihung_user}, Status={ausleihung_status}") print(f"Found ausleihung: ID={id}, Status={ausleihung_status}")
current_permissions = us.get_effective_permissions(session['username'])
# Check if the ausleihung belongs to the current user # Check if the ausleihung belongs to the current user
if ausleihung_user != username and not us.check_admin(username): if ausleihung_user != username and not (current_permissions['actions'].get('can_manage_users', False)):
print(f"Authorization failure: {username} attempted to cancel ausleihung belonging to {ausleihung_user}") print(f"Authorization failure: {username} attempted to cancel ausleihung belonging to {ausleihung_user}")
flash('Sie sind nicht berechtigt, diese Ausleihung zu stornieren', 'error') flash('Sie sind nicht berechtigt, diese Ausleihung zu stornieren', 'error')
return redirect(url_for('my_borrowed_items')) return redirect(url_for('my_borrowed_items'))
@@ -11035,8 +11232,11 @@ def reset_item(id):
if 'username' not in session: if 'username' not in session:
return jsonify({'success': False, 'error': 'Not authenticated'}), 401 return jsonify({'success': False, 'error': 'Not authenticated'}), 401
if not us.check_admin(session['username']): current_permissions = us.get_effective_permissions(session['username'])
return jsonify({'success': False, 'error': 'Not authorized'}), 403
if not current_permissions['actions'].get('can_manage_users', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('home_admin'))
try: try:
# Import the ausleihung module # Import the ausleihung module
+8 -2
View File
@@ -323,7 +323,8 @@ def get_effective_permissions(username):
if bool(user.get('Admin', False)): if bool(user.get('Admin', False)):
return build_default_permission_payload('full_access') return build_default_permission_payload('full_access')
preset_key = user.get('PermissionPreset') or 'standard_user' preset_key = user.get('PermissionPreset')
print(preset_key)
payload = build_default_permission_payload(preset_key) payload = build_default_permission_payload(preset_key)
payload['actions'] = _normalize_bool_map(user.get('ActionPermissions', {}), payload['actions']) payload['actions'] = _normalize_bool_map(user.get('ActionPermissions', {}), payload['actions'])
payload['pages'] = _normalize_bool_map(user.get('PagePermissions', {}), payload['pages']) payload['pages'] = _normalize_bool_map(user.get('PagePermissions', {}), payload['pages'])
@@ -552,10 +553,15 @@ def add_user(
for key, value in page_permissions.items(): for key, value in page_permissions.items():
permission_defaults['pages'][str(key)] = bool(value) permission_defaults['pages'][str(key)] = bool(value)
if permission_preset == "full_access":
can_admin_preset_based = True
else:
can_admin_preset_based = False
user_doc = { user_doc = {
'Username': username, 'Username': username,
'Password': hashing(password), 'Password': hashing(password),
'Admin': False, 'Admin': can_admin_preset_based,
'active_ausleihung': None, 'active_ausleihung': None,
'name': name.strip() if name else '', 'name': name.strip() if name else '',
'last_name': last_name.strip() if last_name else '', 'last_name': last_name.strip() if last_name else '',
+173 -70
View File
@@ -1130,7 +1130,7 @@
<li class="nav-item" data-nav-fixed="true"> <li class="nav-item" data-nav-fixed="true">
<a class="nav-link {% if current_path == url_for('terminplan') %}nav-active{% endif %}" href="{{ url_for('terminplan') }}" data-tutorial-tip="Hier sehen Sie den Kalender mit den bestehenden Terminen.">Kalender</a> <a class="nav-link {% if current_path == url_for('terminplan') %}nav-active{% endif %}" href="{{ url_for('terminplan') }}" data-tutorial-tip="Hier sehen Sie den Kalender mit den bestehenden Terminen.">Kalender</a>
</li> </li>
{% if current_permissions.pages.get('terminplan', True) and current_permissions.actions.get('can_insert', True) %} {% if current_permissions.pages.get('terminplan', False) and current_permissions.actions.get('can_insert', False) %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link quick-link-pill {% if current_path == url_for('terminplaner.configure') %}nav-active{% endif %}" href="{{ url_for('terminplaner.configure') }}" data-tutorial-tip="Neuen Terminplan erstellen und an Ihr Team versenden.">Neue Planung</a> <a class="nav-link quick-link-pill {% if current_path == url_for('terminplaner.configure') %}nav-active{% endif %}" href="{{ url_for('terminplaner.configure') }}" data-tutorial-tip="Neuen Terminplan erstellen und an Ihr Team versenden.">Neue Planung</a>
</li> </li>
@@ -1145,20 +1145,22 @@
<a class="nav-link dropdown-toggle" href="#" id="termMoreDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false" title="Weitere Optionen">Mehr Optionen</a> <a class="nav-link dropdown-toggle" href="#" id="termMoreDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false" title="Weitere Optionen">Mehr Optionen</a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="termMoreDropdown"> <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="termMoreDropdown">
{% if 'username' in session %} {% if 'username' in session %}
{% if current_permissions.pages.get('tutorial_page', True) %} {% if current_permissions.pages.get('tutorial_page', False) %}
<li><a class="dropdown-item" href="{{ url_for('tutorial_page') }}">Tutorial</a></li> <li><a class="dropdown-item" href="{{ url_for('tutorial_page') }}">Tutorial</a></li>
{% endif %} {% endif %}
{% if current_permissions.pages.get('admin_school_settings', False) %}
<li><a class="dropdown-item" href="{{ url_for('admin_school_settings') }}">Schulstammdaten</a></li> <li><a class="dropdown-item" href="{{ url_for('admin_school_settings') }}">Schulstammdaten</a></li>
{% if current_permissions.actions.get('can_view_logs', True) and current_permissions.pages.get('admin_audit_dashboard', True) %} {% endif %}
{% if current_permissions.actions.get('can_view_logs', False) or current_permissions.pages.get('admin_audit_dashboard', False) %}
<li><a class="dropdown-item" href="{{ url_for('admin_audit_dashboard') }}">Audit Dashboard</a></li> <li><a class="dropdown-item" href="{{ url_for('admin_audit_dashboard') }}">Audit Dashboard</a></li>
{% endif %} {% endif %}
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
{% endif %} {% endif %}
{% if current_permissions.pages.get('home', True) %} {% if current_permissions.pages.get('home', False) %}
<li><a class="dropdown-item" href="{{ url_for('home') }}">Inventarsystem</a></li> <li><a class="dropdown-item" href="{{ url_for('home') }}">Inventarsystem</a></li>
{% endif %} {% endif %}
{% if current_permissions.pages.get('library_view', True) and library_module_enabled %} {% if current_permissions.pages.get('library_view', False) and library_module_enabled %}
<li><a class="dropdown-item" href="{{ url_for('library_view') }}">Bibliothek</a></li> <li><a class="dropdown-item" href="{{ url_for('library_view') }}">Bibliothek</a></li>
{% endif %} {% endif %}
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
@@ -1191,7 +1193,7 @@
<span class="user-notification-dot {% if unread_notification_count and unread_notification_count > 0 %}visible{% endif %}" aria-hidden="true"></span> <span class="user-notification-dot {% if unread_notification_count and unread_notification_count > 0 %}visible{% endif %}" aria-hidden="true"></span>
</button> </button>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="invUserMenuDropdown"> <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="invUserMenuDropdown">
{% if current_permissions.pages.get('notifications_view', True) %} {% if current_permissions.pages.get('notifications_view', False) %}
<li><a class="dropdown-item" href="{{ url_for('notifications_view') }}">Benachrichtigungen</a></li> <li><a class="dropdown-item" href="{{ url_for('notifications_view') }}">Benachrichtigungen</a></li>
{% endif %} {% endif %}
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
@@ -1214,13 +1216,13 @@
</button> </button>
<div class="collapse navbar-collapse" id="inventoryNavContent"> <div class="collapse navbar-collapse" id="inventoryNavContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0" id="inventoryNavList"> <ul class="navbar-nav me-auto mb-2 mb-lg-0" id="inventoryNavList">
{% if current_permissions.pages.get('home', True) %} {% if current_permissions.pages.get('home', False) %}
<li class="nav-item" data-nav-fixed="true"> <li class="nav-item" data-nav-fixed="true">
<a class="nav-link {% if current_path == url_for('home') %}nav-active{% endif %}" href="{{ url_for('home') }}" data-tutorial-tip="Starten Sie hier mit dem Materialbestand und suchen Sie nach Artikeln.">Artikel</a> <a class="nav-link {% if current_path == url_for('home') %}nav-active{% endif %}" href="{{ url_for('home') }}" data-tutorial-tip="Starten Sie hier mit dem Materialbestand und suchen Sie nach Artikeln.">Artikel</a>
</li> </li>
{% endif %} {% endif %}
{% if 'username' in session %} {% if 'username' in session %}
{% if current_permissions.pages.get('my_borrowed_items', True) %} {% if current_permissions.pages.get('my_borrowed_items', False) %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link quick-link-pill {% if current_path == url_for('my_borrowed_items') %}nav-active{% endif %}" href="{{ url_for('my_borrowed_items') }}" data-tutorial-tip="Ihre aktuellen Ausleihen und Rückgaben finden Sie hier.">Meine Ausleihen</a> <a class="nav-link quick-link-pill {% if current_path == url_for('my_borrowed_items') %}nav-active{% endif %}" href="{{ url_for('my_borrowed_items') }}" data-tutorial-tip="Ihre aktuellen Ausleihen und Rückgaben finden Sie hier.">Meine Ausleihen</a>
</li> </li>
@@ -1239,42 +1241,44 @@
</a> </a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="invMoreDropdown"> <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="invMoreDropdown">
{% if 'username' in session %} {% if 'username' in session %}
{% if current_permissions.pages.get('tutorial_page', True) %} {% if current_permissions.pages.get('tutorial_page', False) %}
<li><a class="dropdown-item" href="{{ url_for('tutorial_page') }}">Tutorial</a></li> <li><a class="dropdown-item" href="{{ url_for('tutorial_page') }}">Tutorial</a></li>
{% endif %} {% endif %}
{% if current_permissions.pages.get('upload_admin', True) and current_permissions.actions.get('can_insert', True) %} {% if current_permissions.pages.get('upload_admin', False) and current_permissions.actions.get('can_insert', False) %}
<li><a class="dropdown-item" href="{{ url_for('upload_admin') }}"> Hochladen</a></li> <li><a class="dropdown-item" href="{{ url_for('upload_admin') }}"> Hochladen</a></li>
{% endif %} {% endif %}
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
{% endif %} {% endif %}
{% if 'username' in session and (session.get('admin', False) or is_admin) and current_permissions.actions.get('can_manage_settings', True) %} {% if 'username' in session and current_permissions.actions.get('can_manage_settings', False) %}
<li><h6 class="dropdown-header">Verwaltung</h6></li> <li><h6 class="dropdown-header">Verwaltung</h6></li>
{% if current_permissions.pages.get('manage_filters', True) %} {% if current_permissions.pages.get('manage_filters', False) %}
<li><a class="dropdown-item" href="{{ url_for('manage_filters') }}">Filter verwalten</a></li> <li><a class="dropdown-item" href="{{ url_for('manage_filters') }}">Filter verwalten</a></li>
{% endif %} {% endif %}
{% if current_permissions.pages.get('manage_locations', True) %} {% if current_permissions.pages.get('manage_locations', False) %}
<li><a class="dropdown-item" href="{{ url_for('manage_locations') }}">Orte verwalten</a></li> <li><a class="dropdown-item" href="{{ url_for('manage_locations') }}">Orte verwalten</a></li>
{% endif %} {% endif %}
{% if current_permissions.pages.get('admin_school_settings', False) %}
<li><a class="dropdown-item" href="{{ url_for('admin_school_settings') }}">Schulstammdaten</a></li> <li><a class="dropdown-item" href="{{ url_for('admin_school_settings') }}">Schulstammdaten</a></li>
{% if current_permissions.pages.get('admin_borrowings', True) %} {% endif %}
{% if current_permissions.pages.get('admin_borrowings', False) %}
<li><a class="dropdown-item" href="{{ url_for('admin_borrowings') }}">Ausleihen</a></li> <li><a class="dropdown-item" href="{{ url_for('admin_borrowings') }}">Ausleihen</a></li>
{% endif %} {% endif %}
{% if current_permissions.pages.get('admin_damaged_items', True) %} {% if current_permissions.pages.get('admin_damaged_items', False) %}
<li><a class="dropdown-item" href="{{ url_for('admin_damaged_items') }}">Defekte Items</a></li> <li><a class="dropdown-item" href="{{ url_for('admin_damaged_items') }}">Defekte Items</a></li>
{% endif %} {% endif %}
{% if current_permissions.actions.get('can_view_logs', True) and current_permissions.pages.get('admin_audit_dashboard', True) %} {% if current_permissions.actions.get('can_view_logs', False) and current_permissions.pages.get('admin_audit_dashboard', False) %}
<li><a class="dropdown-item" href="{{ url_for('admin_audit_dashboard') }}">Audit Dashboard</a></li> <li><a class="dropdown-item" href="{{ url_for('admin_audit_dashboard') }}">Audit Dashboard</a></li>
{% endif %} {% endif %}
{% if current_permissions.actions.get('can_view_logs', True) and current_permissions.pages.get('logs', True) %} {% if current_permissions.actions.get('can_view_logs', False) and current_permissions.pages.get('logs', False) %}
<li><a class="dropdown-item" href="{{ url_for('logs') }}">Logs</a></li> <li><a class="dropdown-item" href="{{ url_for('logs') }}">Logs</a></li>
{% endif %} {% endif %}
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
{% if current_permissions.actions.get('can_manage_users', True) %} {% if current_permissions.actions.get('can_manage_users', False) %}
<li><h6 class="dropdown-header">System</h6></li> <li><h6 class="dropdown-header">System</h6></li>
{% if current_permissions.pages.get('user_del', True) %} {% if current_permissions.pages.get('user_del', False) %}
<li><a class="dropdown-item" href="{{ url_for('user_del') }}">Benutzer verwalten</a></li> <li><a class="dropdown-item" href="{{ url_for('user_del') }}">Benutzer verwalten</a></li>
{% endif %} {% endif %}
{% if current_permissions.pages.get('register', True) %} {% if current_permissions.pages.get('register', False) %}
<li><a class="dropdown-item" href="{{ url_for('register') }}">Neuer Benutzer</a></li> <li><a class="dropdown-item" href="{{ url_for('register') }}">Neuer Benutzer</a></li>
{% endif %} {% endif %}
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
@@ -1311,7 +1315,7 @@
<span class="user-notification-dot {% if unread_notification_count and unread_notification_count > 0 %}visible{% endif %}" aria-hidden="true"></span> <span class="user-notification-dot {% if unread_notification_count and unread_notification_count > 0 %}visible{% endif %}" aria-hidden="true"></span>
</button> </button>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="invUserMenuDropdown"> <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="invUserMenuDropdown">
{% if current_permissions.pages.get('notifications_view', True) %} {% if current_permissions.pages.get('notifications_view', False) %}
<li><a class="dropdown-item" href="{{ url_for('notifications_view') }}">Benachrichtigungen</a></li> <li><a class="dropdown-item" href="{{ url_for('notifications_view') }}">Benachrichtigungen</a></li>
{% endif %} {% endif %}
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
@@ -1336,19 +1340,19 @@
</button> </button>
<div class="collapse navbar-collapse" id="libraryNavContent"> <div class="collapse navbar-collapse" id="libraryNavContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0" id="libraryNavList"> <ul class="navbar-nav me-auto mb-2 mb-lg-0" id="libraryNavList">
{% if current_permissions.pages.get('library_view', True) %} {% if current_permissions.pages.get('library_view', False) %}
<li class="nav-item" data-nav-fixed="true"> <li class="nav-item" data-nav-fixed="true">
<a class="nav-link {% if current_path == url_for('library_view') %}nav-active{% endif %}" href="{{ url_for('library_view') }}" data-tutorial-tip="Die Bibliothek zeigt Ihnen alle Medien und verfügbaren Bücher.">Medien</a> <a class="nav-link {% if current_path == url_for('library_view') %}nav-active{% endif %}" href="{{ url_for('library_view') }}" data-tutorial-tip="Die Bibliothek zeigt Ihnen alle Medien und verfügbaren Bücher.">Medien</a>
</li> </li>
{% endif %} {% endif %}
{% if 'username' in session %} {% if 'username' in session %}
{% if current_permissions.pages.get('tutorial_page', True) %} {% if current_permissions.pages.get('tutorial_page', False) %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link quick-link-pill {% if current_path == url_for('tutorial_page') %}nav-active{% endif %}" href="{{ url_for('tutorial_page') }}" data-tutorial-tip="Nutzen Sie das Tutorial, um die Bibliotheksfunktionen kennenzulernen.">Tutorial</a> <a class="nav-link quick-link-pill {% if current_path == url_for('tutorial_page') %}nav-active{% endif %}" href="{{ url_for('tutorial_page') }}" data-tutorial-tip="Nutzen Sie das Tutorial, um die Bibliotheksfunktionen kennenzulernen.">Tutorial</a>
</li> </li>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if 'username' in session and current_permissions.actions.get('can_insert', True) and current_permissions.pages.get('library_admin', True) %} {% if 'username' in session and current_permissions.actions.get('can_insert', False) and current_permissions.pages.get('library_admin', False) %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link nav-priority-link {% if current_path == url_for('library_admin') %}nav-active{% endif %}" href="{{ url_for('library_admin') }}" data-tutorial-tip="Neue Bibliotheksmedien können hier aufgenommen werden.">📖 Hochladen</a> <a class="nav-link nav-priority-link {% if current_path == url_for('library_admin') %}nav-active{% endif %}" href="{{ url_for('library_admin') }}" data-tutorial-tip="Neue Bibliotheksmedien können hier aufgenommen werden.">📖 Hochladen</a>
</li> </li>
@@ -1365,22 +1369,24 @@
Mehr Optionen Mehr Optionen
</a> </a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="libMoreDropdown"> <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="libMoreDropdown">
{% if 'username' in session and (session.get('admin', False) or is_admin) and current_permissions.actions.get('can_manage_settings', True) %} {% if 'username' in session and current_permissions.actions.get('can_manage_settings', False) %}
<li><h6 class="dropdown-header">Bibliotheks-Verwaltung</h6></li> <li><h6 class="dropdown-header">Bibliotheks-Verwaltung</h6></li>
{% if current_permissions.pages.get('library_loans_admin', True) %} {% if current_permissions.pages.get('library_loans_admin', False) %}
<li><a class="dropdown-item" href="{{ url_for('library_loans_admin') }}">Ausleihen / Defekte Items</a></li> <li><a class="dropdown-item" href="{{ url_for('library_loans_admin') }}">Ausleihen / Defekte Items</a></li>
{% endif %} {% endif %}
{% if student_cards_module_enabled %} {% if student_cards_module_enabled %}
<li><a class="dropdown-item" href="{{ url_for('student_cards_admin') }}">Bibliotheksausweis</a></li> <li><a class="dropdown-item" href="{{ url_for('student_cards_admin') }}">Bibliotheksausweis</a></li>
{% endif %} {% endif %}
{% if current_permissions.pages.get('admin_school_settings', False) %}
<li><a class="dropdown-item" href="{{ url_for('admin_school_settings') }}">Schulstammdaten</a></li> <li><a class="dropdown-item" href="{{ url_for('admin_school_settings') }}">Schulstammdaten</a></li>
{% endif %}
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
{% if current_permissions.actions.get('can_manage_users', True) %} {% if current_permissions.actions.get('can_manage_users', False) %}
<li><h6 class="dropdown-header">System</h6></li> <li><h6 class="dropdown-header">System</h6></li>
{% if current_permissions.pages.get('user_del', True) %} {% if current_permissions.pages.get('user_del', False) %}
<li><a class="dropdown-item" href="{{ url_for('user_del') }}">Benutzer verwalten</a></li> <li><a class="dropdown-item" href="{{ url_for('user_del') }}">Benutzer verwalten</a></li>
{% endif %} {% endif %}
{% if current_permissions.pages.get('register', True) %} {% if current_permissions.pages.get('register', False) %}
<li><a class="dropdown-item" href="{{ url_for('register') }}">Neuer Benutzer</a></li> <li><a class="dropdown-item" href="{{ url_for('register') }}">Neuer Benutzer</a></li>
{% endif %} {% endif %}
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
@@ -1414,7 +1420,7 @@
<span class="user-notification-dot {% if unread_notification_count and unread_notification_count > 0 %}visible{% endif %}" aria-hidden="true"></span> <span class="user-notification-dot {% if unread_notification_count and unread_notification_count > 0 %}visible{% endif %}" aria-hidden="true"></span>
</button> </button>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="libUserMenuDropdown"> <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="libUserMenuDropdown">
{% if current_permissions.pages.get('notifications_view', True) %} {% if current_permissions.pages.get('notifications_view', False) %}
<li><a class="dropdown-item" href="{{ url_for('notifications_view') }}">Benachrichtigungen</a></li> <li><a class="dropdown-item" href="{{ url_for('notifications_view') }}">Benachrichtigungen</a></li>
{% endif %} {% endif %}
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
@@ -1642,30 +1648,79 @@
</div> </div>
<datalist id="function-search-options"> <datalist id="function-search-options">
{% if current_permissions.pages.get('home', False) %}
<option value="Artikel"></option> <option value="Artikel"></option>
<option value="Meine Ausleihen"></option> {% endif %}
<option value="Benachrichtigungen"></option>
<option value="Tutorial"></option> {% if 'username' in session %}
{% if current_permissions.pages.get('my_borrowed_items', False) %}
<option value="Meine Ausleihen"></option>
{% endif %}
{% if current_permissions.pages.get('notifications_view', False) %}
<option value="Benachrichtigungen"></option>
{% endif %}
{% if current_permissions.pages.get('tutorial_page', False) %}
<option value="Tutorial"></option>
{% endif %}
{% endif %}
<option value="Impressum"></option> <option value="Impressum"></option>
<option value="Lizenz"></option> <option value="Lizenz"></option>
{% if library_module_enabled %} {% if library_module_enabled %}
<option value="Bibliothek"></option> {% if current_permissions.pages.get('library_view', False) %}
<option value="Meine Medien"></option> <option value="Bibliothek"></option>
{% endif %} {% endif %}
{% if 'username' in session and (session.get('admin', False) or is_admin) %} {% if 'username' in session and current_permissions.pages.get('my_borrowed_items', False) %}
<option value="Hochladen"></option> <option value="Meine Medien"></option>
<option value="Ausleihen Verwaltung"></option> {% endif %}
<option value="Defekte Items"></option>
<option value="Filter verwalten"></option>
<option value="Orte verwalten"></option>
<option value="Schulstammdaten"></option>
<option value="Audit Dashboard"></option>
<option value="Logs"></option>
<option value="Benutzer verwalten"></option>
<option value="Neuer Benutzer"></option>
{% if student_cards_module_enabled %}
<option value="Bibliotheksausweis"></option>
{% endif %} {% endif %}
{% if 'username' in session %}
{% if current_permissions.pages.get('upload_admin', False) and current_permissions.actions.get('can_insert', False) %}
<option value="Hochladen"></option>
{% endif %}
{% if current_permissions.pages.get('admin_borrowings', False) or current_permissions.pages.get('library_loans_admin', False) %}
<option value="Ausleihen Verwaltung"></option>
{% endif %}
{% if current_permissions.pages.get('admin_damaged_items', False) or current_permissions.pages.get('library_loans_admin', False) %}
<option value="Defekte Items"></option>
{% endif %}
{% if current_permissions.pages.get('manage_filters', False) %}
<option value="Filter verwalten"></option>
{% endif %}
{% if current_permissions.pages.get('manage_locations', False) %}
<option value="Orte verwalten"></option>
{% endif %}
{% if current_permissions.pages.get('admin_school_settings', False) %}
<option value="Schulstammdaten"></option>
{% endif %}
{% if current_permissions.actions.get('can_view_logs', False) and current_permissions.pages.get('admin_audit_dashboard', False) %}
<option value="Audit Dashboard"></option>
{% endif %}
{% if current_permissions.actions.get('can_view_logs', False) and current_permissions.pages.get('logs', False) %}
<option value="Logs"></option>
{% endif %}
{% if current_permissions.actions.get('can_manage_users', False) %}
{% if current_permissions.pages.get('user_del', False) %}
<option value="Benutzer verwalten"></option>
{% endif %}
{% if current_permissions.pages.get('register', False) %}
<option value="Neuer Benutzer"></option>
{% endif %}
{% endif %}
{% if student_cards_module_enabled and current_permissions.pages.get('student_cards_admin', False) %}
<option value="Bibliotheksausweis"></option>
{% endif %}
{% endif %} {% endif %}
</datalist> </datalist>
@@ -1713,32 +1768,80 @@
const loginHintKey = username ? ('inventarsystem_notification_login_hint_v1_' + username) : null; const loginHintKey = username ? ('inventarsystem_notification_login_hint_v1_' + username) : null;
const functionSearchEntries = [ const functionSearchEntries = [
{% if current_permissions.pages.get('home', False) %}
{ label: 'Artikel', keywords: ['artikel', 'inventar', 'home'], url: {{ url_for('home')|tojson }} }, { label: 'Artikel', keywords: ['artikel', 'inventar', 'home'], url: {{ url_for('home')|tojson }} },
{ label: 'Meine Ausleihen', keywords: ['meine ausleihen', 'ausleihen', 'borrowed'], url: {{ url_for('my_borrowed_items')|tojson }} }, {% endif %}
{ label: 'Benachrichtigungen', keywords: ['benachrichtigungen', 'nachrichten', 'notifications'], url: {{ url_for('notifications_view')|tojson }} },
{ label: 'Tutorial', keywords: ['tutorial', 'hilfe', 'anleitung'], url: {{ url_for('tutorial_page')|tojson }} }, {% if 'username' in session %}
{% if current_permissions.pages.get('my_borrowed_items', False) %}
{ label: 'Meine Ausleihen', keywords: ['meine ausleihen', 'ausleihen', 'borrowed'], url: {{ url_for('my_borrowed_items')|tojson }} },
{% endif %}
{% if current_permissions.pages.get('notifications_view', False) %}
{ label: 'Benachrichtigungen', keywords: ['benachrichtigungen', 'nachrichten', 'notifications'], url: {{ url_for('notifications_view')|tojson }} },
{% endif %}
{% if current_permissions.pages.get('tutorial_page', False) %}
{ label: 'Tutorial', keywords: ['tutorial', 'hilfe', 'anleitung'], url: {{ url_for('tutorial_page')|tojson }} },
{% endif %}
{% endif %}
{ label: 'Impressum', keywords: ['impressum'], url: {{ url_for('impressum')|tojson }} }, { label: 'Impressum', keywords: ['impressum'], url: {{ url_for('impressum')|tojson }} },
{ label: 'Lizenz', keywords: ['lizenz', 'license'], url: {{ url_for('license')|tojson }} }, { label: 'Lizenz', keywords: ['lizenz', 'license'], url: {{ url_for('license')|tojson }} },
{% if library_module_enabled %} {% if library_module_enabled %}
{ label: 'Bibliothek', keywords: ['bibliothek', 'medien'], url: {{ url_for('library_view')|tojson }} }, {% if current_permissions.pages.get('library_view', False) %}
{% endif %} { label: 'Bibliothek', keywords: ['bibliothek', 'medien'], url: {{ url_for('library_view')|tojson }} },
{% if 'username' in session and (session.get('admin', False) or is_admin) %} {% endif %}
{ label: 'Hochladen', keywords: ['hochladen', 'upload'], url: {{ url_for('upload_admin')|tojson }} },
{ label: 'Ausleihen Verwaltung', keywords: ['ausleihen verwaltung', 'admin borrowings'], url: {{ url_for('admin_borrowings')|tojson }} },
{ label: 'Defekte Items', keywords: ['defekte items', 'defekt', 'schaden'], url: {{ url_for('admin_damaged_items')|tojson }} },
{ label: 'Filter verwalten', keywords: ['filter verwalten', 'filter'], url: {{ url_for('manage_filters')|tojson }} },
{ label: 'Orte verwalten', keywords: ['orte verwalten', 'orte', 'location'], url: {{ url_for('manage_locations')|tojson }} },
{ label: 'Schulstammdaten', keywords: ['schule', 'settings', 'stammdaten', 'school settings'], url: {{ url_for('admin_school_settings')|tojson }} },
{ label: 'Audit Dashboard', keywords: ['audit', 'audit dashboard'], url: {{ url_for('admin_audit_dashboard')|tojson }} },
{ label: 'Logs', keywords: ['logs', 'protokoll'], url: {{ url_for('logs')|tojson }} },
{ label: 'Benutzer verwalten', keywords: ['benutzer verwalten', 'user'], url: {{ url_for('user_del')|tojson }} },
{ label: 'Neuer Benutzer', keywords: ['neuer benutzer', 'register'], url: {{ url_for('register')|tojson }} },
{% if library_module_enabled %}
{ label: 'Bibliotheks-Ausleihen', keywords: ['bibliotheks ausleihen', 'library loans'], url: {{ url_for('library_loans_admin')|tojson }} },
{% endif %}
{% if student_cards_module_enabled %}
{ label: 'Bibliotheksausweis', keywords: ['bibliotheksausweis', 'student card'], url: {{ url_for('student_cards_admin')|tojson }} },
{% endif %} {% endif %}
{% if 'username' in session %}
{% if current_permissions.pages.get('upload_admin', False) and current_permissions.actions.get('can_insert', False) %}
{ label: 'Hochladen', keywords: ['hochladen', 'upload'], url: {{ url_for('upload_admin')|tojson }} },
{% endif %}
{% if current_permissions.pages.get('admin_borrowings', False) %}
{ label: 'Ausleihen Verwaltung', keywords: ['ausleihen verwaltung', 'admin borrowings'], url: {{ url_for('admin_borrowings')|tojson }} },
{% endif %}
{% if current_permissions.pages.get('admin_damaged_items', False) %}
{ label: 'Defekte Items', keywords: ['defekte items', 'defekt', 'schaden'], url: {{ url_for('admin_damaged_items')|tojson }} },
{% endif %}
{% if current_permissions.pages.get('manage_filters', False) %}
{ label: 'Filter verwalten', keywords: ['filter verwalten', 'filter'], url: {{ url_for('manage_filters')|tojson }} },
{% endif %}
{% if current_permissions.pages.get('manage_locations', False) %}
{ label: 'Orte verwalten', keywords: ['orte verwalten', 'orte', 'location'], url: {{ url_for('manage_locations')|tojson }} },
{% endif %}
{% if current_permissions.pages.get('admin_school_settings', False) %}
{ label: 'Schulstammdaten', keywords: ['schule', 'settings', 'stammdaten', 'school settings'], url: {{ url_for('admin_school_settings')|tojson }} },
{% endif %}
{% if current_permissions.actions.get('can_view_logs', False) and current_permissions.pages.get('admin_audit_dashboard', False) %}
{ label: 'Audit Dashboard', keywords: ['audit', 'audit dashboard'], url: {{ url_for('admin_audit_dashboard')|tojson }} },
{% endif %}
{% if current_permissions.actions.get('can_view_logs', False) and current_permissions.pages.get('logs', False) %}
{ label: 'Logs', keywords: ['logs', 'protokoll'], url: {{ url_for('logs')|tojson }} },
{% endif %}
{% if current_permissions.actions.get('can_manage_users', False) %}
{% if current_permissions.pages.get('user_del', False) %}
{ label: 'Benutzer verwalten', keywords: ['benutzer verwalten', 'user'], url: {{ url_for('user_del')|tojson }} },
{% endif %}
{% if current_permissions.pages.get('register', False) %}
{ label: 'Neuer Benutzer', keywords: ['neuer benutzer', 'register'], url: {{ url_for('register')|tojson }} },
{% endif %}
{% endif %}
{% if library_module_enabled and current_permissions.pages.get('library_loans_admin', False) %}
{ label: 'Bibliotheks-Ausleihen', keywords: ['bibliotheks ausleihen', 'library loans'], url: {{ url_for('library_loans_admin')|tojson }} },
{% endif %}
{% if student_cards_module_enabled and current_permissions.pages.get('student_cards_admin', False) %}
{ label: 'Bibliotheksausweis', keywords: ['bibliotheksausweis', 'student card'], url: {{ url_for('student_cards_admin')|tojson }} },
{% endif %}
{% endif %} {% endif %}
]; ];
+1 -1
View File
@@ -22,7 +22,7 @@
<p><strong>Name:</strong> Invario UG</p> <p><strong>Name:</strong> Invario UG</p>
<p><strong>Adresse:</strong> Am Sportplatz 10<br>83052 Bruckmühl<br>Deutschland</p> <p><strong>Adresse:</strong> Am Sportplatz 10<br>83052 Bruckmühl<br>Deutschland</p>
</address> </address>
<p><strong>E-Mail:</strong> <a href="mailto:info@invario.eu">info@invario.eu</a></p> <p><strong>E-Mail:</strong> <a href="mailto:info@invario-software.de">info@invario-software.de</a></p>
</div> </div>
<div class="impressum-section mb-4"> <div class="impressum-section mb-4">
+1 -1
View File
@@ -469,7 +469,7 @@
} }
</style> </style>
<div class="library-table-container" id="libraryTableContainer" data-can-edit="{{ 1 if is_admin else 0 }}"> <div class="library-table-container" id="libraryTableContainer" data-can-edit="{{ 1 if current_permissions.actions.get('can_edit', False) else 0 }}">
<!-- Header --> <!-- Header -->
<div class="library-header"> <div class="library-header">
<h1>📚 Bibliothek</h1> <h1>📚 Bibliothek</h1>
+1 -1
View File
@@ -122,7 +122,7 @@
<h3>Meldung von Sicherheitslücken</h3> <h3>Meldung von Sicherheitslücken</h3>
<div class="license-exception-notice"> <div class="license-exception-notice">
<h3>⚠️ Responsible Disclosure</h3> <h3>⚠️ Responsible Disclosure</h3>
<p>Falls Sie eine Sicherheitslücke entdecken, wenden sie sich umgehend an die Email: info@invario.eu . <p>Falls Sie eine Sicherheitslücke entdecken, wenden sie sich umgehend an die Email: info@invario-software.de .
</div> </div>
</div> </div>
</div><!-- /#pane-security --> </div><!-- /#pane-security -->