Compare commits

...

5 Commits

2 changed files with 118 additions and 97 deletions
+59 -62
View File
@@ -556,7 +556,7 @@ def _enforce_module_access():
flash(msg, 'info') flash(msg, 'info')
if name != 'inventory' and cfg.MODULES.is_enabled('inventory'): if name != 'inventory' and cfg.MODULES.is_enabled('inventory'):
return redirect(url_for('home')) return redirect(url_for('home_admin'))
elif name != 'library' and cfg.MODULES.is_enabled('library'): elif name != 'library' and cfg.MODULES.is_enabled('library'):
return redirect('/library') return redirect('/library')
@@ -630,7 +630,7 @@ def handle_build_error(e):
return redirect('/library') return redirect('/library')
if 'username' in session: if 'username' in session:
return redirect(url_for('home')) return redirect(url_for('home_admin'))
return redirect(url_for('login')) return redirect(url_for('login'))
@@ -708,10 +708,10 @@ def _permission_denied_fallback_endpoint(permissions, current_endpoint=None):
is_admin_user = bool(username and us.check_admin(username)) is_admin_user = bool(username and us.check_admin(username))
admin_home_allowed = _page_access_allowed(permissions, 'home_admin') and _action_access_allowed(permissions, 'can_manage_settings') admin_home_allowed = _page_access_allowed(permissions, 'home_admin') and _action_access_allowed(permissions, 'can_manage_settings')
for candidate in ('my_borrowed_items', 'tutorial_page', 'notifications_view', 'impressum', 'home'): for candidate in ('my_borrowed_items', 'tutorial_page', 'notifications_view', 'impressum', 'home_admin'):
if current_endpoint and candidate == current_endpoint: if current_endpoint and candidate == current_endpoint:
continue continue
if candidate == 'home' and is_admin_user and not admin_home_allowed: if candidate == 'home_admin' and is_admin_user and not admin_home_allowed:
continue continue
if _page_access_allowed(permissions, candidate): if _page_access_allowed(permissions, candidate):
return candidate return candidate
@@ -2178,7 +2178,7 @@ def _upload_excel_items(scope='inventory'):
if is_library_scope: if is_library_scope:
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')) return redirect(url_for('home_admin'))
excel_file = request.files.get(file_field) excel_file = request.files.get(file_field)
if not excel_file or not excel_file.filename: if not excel_file or not excel_file.filename:
@@ -2194,7 +2194,7 @@ def _upload_excel_items(scope='inventory'):
# Allow CSV imports for authenticated non-admin users only for inventory (non-library) # Allow CSV imports for authenticated non-admin users only for inventory (non-library)
if not (is_csv and not is_library_scope and 'username' in session): if not (is_csv and not is_library_scope and 'username' in session):
flash('Einfüge-Rechte erforderlich.', 'error') flash('Einfüge-Rechte erforderlich.', 'error')
return redirect(url_for('home')) return redirect(url_for('home_admin'))
filename_lower = excel_file.filename.lower() filename_lower = excel_file.filename.lower()
if not filename_lower.endswith(('.xlsx', '.csv')): if not filename_lower.endswith(('.xlsx', '.csv')):
@@ -2882,6 +2882,7 @@ def user_status():
##################################################### changes to be made to account for the new account permison managment system ############################## ##################################################### changes to be made to account for the new account permison managment system ##############################
@app.route('/') @app.route('/')
def home(): def home():
""" """
@@ -2895,33 +2896,7 @@ def home():
flash('Bitte mit registriertem Konto anmelden!', 'error') flash('Bitte mit registriertem Konto anmelden!', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
if not cfg.MODULES.is_enabled('inventory'): return redirect(url_for('home_admin'))
if cfg.MODULES.is_enabled('library'):
return redirect('/library')
else:
return "Weder Inventar- noch Bibliotheks-Modul sind aktiviert.", 403
elif not us.check_admin(session['username']):
return render_template(
'main.html',
username=session['username'],
library_module_enabled=cfg.MODULES.is_enabled('library'),
student_cards_module_enabled=cfg.MODULES.is_enabled('student_cards'),
mail_module_enabled=cfg.MODULES.is_enabled('mail'),
student_default_borrow_days=cfg.STUDENT_DEFAULT_BORROW_DAYS,
student_max_borrow_days=cfg.STUDENT_MAX_BORROW_DAYS,
open_item=request.args.get('open_item')
)
else:
permissions = _get_current_user_permissions() or us.build_default_permission_payload('standard_user')
if _page_access_allowed(permissions, 'home_admin') and _action_access_allowed(permissions, 'can_manage_settings'):
return redirect(url_for('home_admin'))
fallback_endpoint = _permission_denied_fallback_endpoint(permissions, current_endpoint='home')
if fallback_endpoint == 'logout':
flash('Für diesen Benutzer sind aktuell keine Seiten freigegeben.', 'error')
return redirect(url_for(fallback_endpoint))
@app.route('/home_admin') @app.route('/home_admin')
def home_admin(): def home_admin():
@@ -2942,9 +2917,12 @@ def home_admin():
else: else:
return "Weder Inventar- noch Bibliotheks-Modul sind aktiviert.", 403 return "Weder Inventar- noch Bibliotheks-Modul sind aktiviert.", 403
if not us.check_admin(session['username']): current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['pages'].get('home', 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'))
return render_template( return render_template(
'main_admin.html', 'main_admin.html',
username=session['username'], username=session['username'],
@@ -3052,7 +3030,7 @@ def library_view():
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')) return redirect(url_for('home_admin'))
return render_template( return render_template(
'library_table.html', 'library_table.html',
@@ -3080,7 +3058,7 @@ def library_loans_admin():
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')) return redirect(url_for('home_admin'))
_ensure_audit_indexes_once() _ensure_audit_indexes_once()
@@ -4478,7 +4456,7 @@ def login():
flask.Response: Rendered template or redirect flask.Response: Rendered template or redirect
""" """
if 'username' in session: if 'username' in session:
return redirect(url_for('home')) return redirect(url_for('home_admin'))
if request.method == 'POST': if request.method == 'POST':
username = request.form['username'] username = request.form['username']
password = request.form['password'] password = request.form['password']
@@ -4514,7 +4492,7 @@ def login():
fallback_endpoint = _permission_denied_fallback_endpoint(permissions, current_endpoint='login') fallback_endpoint = _permission_denied_fallback_endpoint(permissions, current_endpoint='login')
return redirect(url_for(fallback_endpoint)) return redirect(url_for(fallback_endpoint))
else: else:
return redirect(url_for('home')) return redirect(url_for('home_admin'))
else: else:
app.logger.warning(f"Login failed: username={encrypt_text(username)!r} tenant={current_tenant_id or 'default'} db={current_tenant_db} host={request.host} ip={encrypt_text(request.remote_addr)}") app.logger.warning(f"Login failed: username={encrypt_text(username)!r} tenant={current_tenant_id or 'default'} db={current_tenant_db} host={request.host} ip={encrypt_text(request.remote_addr)}")
flash('Ungültige Anmeldedaten', 'error') flash('Ungültige Anmeldedaten', 'error')
@@ -4584,7 +4562,7 @@ def change_password():
# Update the password # Update the password
if us.update_password(session['username'], new_password): if us.update_password(session['username'], new_password):
flash('Ihr Passwort wurde erfolgreich geändert.', 'success') flash('Ihr Passwort wurde erfolgreich geändert.', 'success')
return redirect(url_for('home')) return redirect(url_for('home_admin'))
else: else:
flash('Fehler beim Ändern des Passworts. Bitte versuchen Sie es später erneut.', 'error') flash('Fehler beim Ändern des Passworts. Bitte versuchen Sie es später erneut.', 'error')
@@ -5178,7 +5156,7 @@ def upload_item():
elif cfg.MODULES.is_enabled('library') and _page_access_allowed(permissions, 'home_library'): elif cfg.MODULES.is_enabled('library') and _page_access_allowed(permissions, 'home_library'):
success_redirect_endpoint = 'home_library' success_redirect_endpoint = 'home_library'
else: else:
success_redirect_endpoint = 'home' success_redirect_endpoint = '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', '')
@@ -6384,7 +6362,7 @@ def delete_item(id):
if not current_permissions['actions'].get('can_delete', False): if not current_permissions['actions'].get('can_delete', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion (Löschen) auszuführen.', 'error') flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion (Löschen) auszuführen.', 'error')
return redirect(url_for('home')) return redirect(url_for('home_admin'))
if not cfg.MODULES.is_enabled('inventory'): if not cfg.MODULES.is_enabled('inventory'):
flash('Bibliotheks-Modul ist deaktiviert.', 'error') flash('Bibliotheks-Modul ist deaktiviert.', 'error')
@@ -6448,11 +6426,11 @@ def delete_library_item(id):
if not current_permissions['actions'].get('can_delete', False): if not current_permissions['actions'].get('can_delete', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion (Löschen) auszuführen.', 'error') flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion (Löschen) auszuführen.', 'error')
return redirect(url_for('home')) return redirect(url_for('home_admin'))
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')) return redirect(url_for('home_admin'))
# Verify item exists and is a library item # Verify item exists and is a library item
try: try:
@@ -6522,7 +6500,7 @@ def bulk_delete_items():
if not current_permissions['actions'].get('can_delete', False): if not current_permissions['actions'].get('can_delete', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion (Löschen) auszuführen.', 'error') flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion (Löschen) auszuführen.', 'error')
return redirect(url_for('home')) return redirect(url_for('home_admin'))
if not cfg.MODULES.is_enabled('inventory'): if not cfg.MODULES.is_enabled('inventory'):
flash('Bibliotheks-Modul ist deaktiviert.', 'error') flash('Bibliotheks-Modul ist deaktiviert.', 'error')
@@ -6587,7 +6565,7 @@ def edit_item(id):
if not current_permissions['actions'].get('can_edit', False): if not current_permissions['actions'].get('can_edit', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion (Löschen) auszuführen.', 'error') flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion (Löschen) auszuführen.', 'error')
return redirect(url_for('home')) return redirect(url_for('home_admin'))
if not cfg.MODULES.is_enabled('inventory'): if not cfg.MODULES.is_enabled('inventory'):
flash('Bibliotheks-Modul ist deaktiviert.', 'error') flash('Bibliotheks-Modul ist deaktiviert.', 'error')
@@ -7005,7 +6983,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' # check for plausability redirect_target = 'home_admin'
item = it.get_item(id) item = it.get_item(id)
if not item: if not item:
@@ -7071,7 +7049,7 @@ def ausleihen(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:
flash('Keine Schülerin/kein Schüler mit dieser Ausweis-ID gefunden.', 'error') flash('Keine Schülerin/kein Schüler mit dieser Ausweis-ID gefunden.', 'error')
return redirect(url_for('home_admin')) return redirect(url_for('library'))
effective_borrower = student_user.get('Username') or student_user.get('username') or username effective_borrower = student_user.get('Username') or student_user.get('username') or username
if borrow_duration_days is None: if borrow_duration_days is None:
try: try:
@@ -7321,7 +7299,7 @@ def zurueckgeben(id):
item = it.get_item(id) item = it.get_item(id)
if not item: if not item:
flash('Element nicht gefunden', 'error') flash('Element nicht gefunden', 'error')
return redirect(url_for('home')) return redirect(url_for('home_admin'))
username = session['username'] username = session['username']
@@ -7903,20 +7881,20 @@ def terminplan():
if not cfg.MODULES.is_enabled('terminplan'): if not cfg.MODULES.is_enabled('terminplan'):
flash('Der Terminplaner ist deaktiviert.', 'info') flash('Der Terminplaner ist deaktiviert.', 'info')
return redirect(url_for('home')) return redirect(url_for('home_admin'))
# Make sure the template exists # Make sure the template exists
template_path = os.path.join(BASE_DIR, 'templates', 'terminplan.html') template_path = os.path.join(BASE_DIR, 'templates', 'terminplan.html')
if not os.path.exists(template_path): if not os.path.exists(template_path):
print(f"Template file not found: {template_path}") print(f"Template file not found: {template_path}")
flash('Vorlage nicht gefunden. Bitte kontaktieren Sie den Administrator.', 'error') flash('Vorlage nicht gefunden. Bitte kontaktieren Sie den Administrator.', 'error')
return redirect(url_for('home')) return redirect(url_for('home_admin'))
return render_template('terminplan.html', school_periods=SCHOOL_PERIODS) return render_template('terminplan.html', school_periods=SCHOOL_PERIODS)
except Exception as e: except Exception as e:
app.logger.error(f"Error rendering terminplan: {e}") app.logger.error(f"Error rendering terminplan: {e}")
flash('Ein Fehler ist beim Anzeigen des Kalenders aufgetreten.', 'error') flash('Ein Fehler ist beim Anzeigen des Kalenders aufgetreten.', 'error')
return redirect(url_for('home')) return redirect(url_for('home_admin'))
'''-------------------------------------------------------------------------------------------------------------ADMIN ROUTES------------------------------------------------------------------------------------------------------------------''' '''-------------------------------------------------------------------------------------------------------------ADMIN ROUTES------------------------------------------------------------------------------------------------------------------'''
@@ -7973,7 +7951,7 @@ def register():
action_permissions=action_permissions, action_permissions=action_permissions,
page_permissions=page_permissions, page_permissions=page_permissions,
) )
return redirect(url_for('home')) return redirect(url_for('home_admin'))
return render_template( return render_template(
'register.html', 'register.html',
library_module_enabled=cfg.MODULES.is_enabled('library'), library_module_enabled=cfg.MODULES.is_enabled('library'),
@@ -8620,17 +8598,36 @@ def admin_create_invoice(borrow_id):
flash('Für diese Ausleihe existiert bereits eine Rechnung. Bitte Korrekturbuchung verwenden.', 'warning') flash('Für diese Ausleihe existiert bereits eine Rechnung. Bitte Korrekturbuchung verwenden.', 'warning')
return redirect(url_for('admin_borrowings')) return redirect(url_for('admin_borrowings'))
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB]
student_cards = db['student_cards'] student_cards = db['student_cards']
card = student_cards.find_one({'_id': ObjectId(borrow_id)}) student_id = borrow_doc.get('AusweisId')
card = _decrypt_student_card_doc(card) borrower = "Unbekannt"
client.close()
borrower = card['SchülerName'] # 1. Prüfen: Gibt es eine AusweisId?
if student_id:
try:
card = student_cards.find_one({'_id': ObjectId(student_id)})
except Exception:
card = student_cards.find_one({'_id': student_id})
if not card:
flash('Der zugehörige Schülerausweis wurde in der Datenbank nicht gefunden.', 'error')
return redirect(url_for('admin_borrowings'))
card = _decrypt_student_card_doc(card)
borrower = card.get('SchülerName', 'Unbekannt')
# 2. Fallback: Wenn keine AusweisId da ist, schauen wir, ob ein Name unter 'User' gespeichert wurde
else:
fallback_user = borrow_doc.get('User')
if fallback_user:
borrower = str(fallback_user)
else:
flash('Dieser Ausleihe ist weder eine AusweisId noch ein Benutzername zugeordnet.', 'error')
return redirect(url_for('admin_borrowings'))
invoice_number = existing_invoice.get('invoice_number') or _build_invoice_number(borrow_doc['_id'], now) invoice_number = existing_invoice.get('invoice_number') or _build_invoice_number(borrow_doc['_id'], now)
item_name = item_doc.get('Name', '') item_name = item_doc.get('Name', '')
item_code = item_doc.get('Code_4', '') item_code = item_doc.get('Code_4', '')
invoice_data = { invoice_data = {
'invoice_number': invoice_number, 'invoice_number': invoice_number,
@@ -8639,7 +8636,7 @@ def admin_create_invoice(borrow_id):
'damage_reason': damage_reason, 'damage_reason': damage_reason,
'created_at': now, 'created_at': now,
'created_by': session.get('username', ''), 'created_by': session.get('username', ''),
'borrower': borrower, 'borrower': decrypt_text(borrower),
'item_id': str(item_doc['_id']), 'item_id': str(item_doc['_id']),
'item_name': item_name, 'item_name': item_name,
'item_code': item_code, 'item_code': item_code,
@@ -10419,7 +10416,7 @@ def notifications_view():
except Exception as exc: except Exception as exc:
app.logger.error(f"Error loading notifications: {exc}") app.logger.error(f"Error loading notifications: {exc}")
flash('Fehler beim Laden der Benachrichtigungen.', 'error') flash('Fehler beim Laden der Benachrichtigungen.', 'error')
return redirect(url_for('home')) return redirect(url_for('home_admin'))
finally: finally:
if client: if client:
client.close() client.close()
+59 -35
View File
@@ -2309,6 +2309,7 @@ document.addEventListener('DOMContentLoaded', ()=>{
</script> </script>
<div class="admin-content-container"> <div class="admin-content-container">
{% if current_permissions.actions.get('can_edit', False) %}
<!-- Admin conflict warning banner (populated by JS) --> <!-- Admin conflict warning banner (populated by JS) -->
<div id="conflict-banner" style="display:none; background:#fff3cd; border:1px solid #ffc107; border-left:4px solid #fd7e14; color:#856404; padding:12px 16px; border-radius:4px; margin-bottom:16px; position:relative;"> <div id="conflict-banner" style="display:none; background:#fff3cd; border:1px solid #ffc107; border-left:4px solid #fd7e14; color:#856404; padding:12px 16px; border-radius:4px; margin-bottom:16px; position:relative;">
<strong>⚠ Buchungskonflikte erkannt:</strong> <strong>⚠ Buchungskonflikte erkannt:</strong>
@@ -2335,6 +2336,8 @@ document.addEventListener('DOMContentLoaded', ()=>{
.catch(() => {}); .catch(() => {});
}); });
</script> </script>
{% endif %}
<div class="content"> <div class="content">
<h1 style="position:relative;">Inventar Objekte <h1 style="position:relative;">Inventar Objekte
<div class="view-switch-group"> <div class="view-switch-group">
@@ -2344,14 +2347,17 @@ document.addEventListener('DOMContentLoaded', ()=>{
<button id="toggle-favorites-view" class="view-toggle-btn" title="Nur Merkliste anzeigen"> <button id="toggle-favorites-view" class="view-toggle-btn" title="Nur Merkliste anzeigen">
<span id="favorites-view-icon">🔖</span> <span id="favorites-view-icon">🔖</span>
</button> </button>
{% if current_permissions.actions.get('can_delete', False) %}
<div class="bulk-delete-inline-wrap"> <div class="bulk-delete-inline-wrap">
<button type="button" id="bulk-delete-drawer-toggle" class="view-toggle-btn bulk-delete-drawer-toggle" aria-expanded="false" title="Massenlöschung" onclick="toggleBulkDeleteDrawer()"> <button type="button" id="bulk-delete-drawer-toggle" class="view-toggle-btn bulk-delete-drawer-toggle" aria-expanded="false" title="Massenlöschung" onclick="toggleBulkDeleteDrawer()">
<span class="drawer-icon"></span> <span class="drawer-icon"></span>
</button> </button>
</div> </div>
{% endif %}
</div> </div>
</h1> </h1>
<div id="items-indicator" class="items-indicator">Objekte im System: 0</div> <div id="items-indicator" class="items-indicator">Objekte im System: 0</div>
{% if current_permissions.actions.get('can_delete', False) %}
<div id="bulk-delete-drawer" class="bulk-delete-drawer" aria-hidden="true"> <div id="bulk-delete-drawer" class="bulk-delete-drawer" aria-hidden="true">
<div class="bulk-delete-content"> <div class="bulk-delete-content">
<strong>Massenlöschung nach Kriterien</strong> <strong>Massenlöschung nach Kriterien</strong>
@@ -2364,6 +2370,7 @@ document.addEventListener('DOMContentLoaded', ()=>{
<button type="button" id="bulk-delete-button" class="danger" onclick="deleteSelectedItems()" disabled>Auswahl löschen</button> <button type="button" id="bulk-delete-button" class="danger" onclick="deleteSelectedItems()" disabled>Auswahl löschen</button>
</div> </div>
</div> </div>
{% endif %}
<div class="filter-container"> <div class="filter-container">
<div class="filter-group"> <div class="filter-group">
<div class="filter-header"> <div class="filter-header">
@@ -2469,6 +2476,7 @@ document.addEventListener('DOMContentLoaded', ()=>{
</div> </div>
<!-- Add the edit modal form --> <!-- Add the edit modal form -->
{% if current_permissions.actions.get('can_edit', False) %}
<div id="edit-modal" class="item-modal"> <div id="edit-modal" class="item-modal">
<div class="modal-content"> <div class="modal-content">
<span class="close-modal" onclick="closeEditModal()">&times;</span> <span class="close-modal" onclick="closeEditModal()">&times;</span>
@@ -2646,6 +2654,7 @@ document.addEventListener('DOMContentLoaded', ()=>{
</form> </form>
</div> </div>
</div> </div>
{% endif %}
</div> </div>
<!-- Schedule Appointment Modal --> <!-- Schedule Appointment Modal -->
@@ -2724,7 +2733,6 @@ document.addEventListener('DOMContentLoaded', ()=>{
</div> </div>
</div> </div>
<!-- Initialize template variables before any JavaScript code -->
<script> <script>
// Create a global object to hold server-side template values // Create a global object to hold server-side template values
window.serverVars = {}; window.serverVars = {};
@@ -3239,7 +3247,7 @@ document.addEventListener('DOMContentLoaded', ()=>{
return; return;
} }
fetch('{{ url_for('bulk_delete_items') }}', { fetch("{{ url_for('bulk_delete_items') }}", {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@@ -3703,6 +3711,7 @@ document.addEventListener('DOMContentLoaded', ()=>{
Für Löschung markieren Für Löschung markieren
</label> </label>
</div> </div>
{% if current_permissions.actions.get('can_borrow', False) %}
${isAvailableForBorrow && !item.BlockedNow ? ${isAvailableForBorrow && !item.BlockedNow ?
`<form method="POST" action="{{ url_for('ausleihen', id='') }}${item._id}"> `<form method="POST" action="{{ url_for('ausleihen', id='') }}${item._id}">
${isGroupedItem ? ` ${isGroupedItem ? `
@@ -3724,12 +3733,21 @@ document.addEventListener('DOMContentLoaded', ()=>{
: :
`<button class="ausleihen disabled-button" disabled>${item.BlockedNow ? 'Reserviert' : 'Ausgeliehen'}</button>` `<button class="ausleihen disabled-button" disabled>${item.BlockedNow ? 'Reserviert' : 'Ausgeliehen'}</button>`
} }
{% endif %}
{% if current_permissions.actions.get('can_delete', False) %}
<form method="POST" action="{{ url_for('delete_item', id='') }}${item._id}" style="display:inline;" onsubmit="return confirm('Sind Sie sicher, dass Sie dieses Objekt löschen möchten?')"> <form method="POST" action="{{ url_for('delete_item', id='') }}${item._id}" style="display:inline;" onsubmit="return confirm('Sind Sie sicher, dass Sie dieses Objekt löschen möchten?')">
<button class="delete-button" type="submit">Löschen</button> <button class="delete-button" type="submit">Löschen</button>
</form> </form>
{% endif %}
{% if current_permissions.actions.get('can_edit', False) %}
<button class="edit-button" onclick="openEditModalForSelectedUnit('${item._id}', 'specific-item-card-${item._id}')">Bearbeiten</button> <button class="edit-button" onclick="openEditModalForSelectedUnit('${item._id}', 'specific-item-card-${item._id}')">Bearbeiten</button>
{% endif %}
{% if current_permissions.actions.get('can_insert', False) %}
<button class="duplicate-button" onclick="duplicateItem('${item._id}')">Duplizieren</button> <button class="duplicate-button" onclick="duplicateItem('${item._id}')">Duplizieren</button>
{% endif %}
{% if current_permissions.pages.get('admin_school_settings', False) %}
${canScheduleItem ? `<button class="schedule-button" onclick="openScheduleModal('${item._id}')">Reservieren</button>` : ''} ${canScheduleItem ? `<button class="schedule-button" onclick="openScheduleModal('${item._id}')">Reservieren</button>` : ''}
{% endif %}
</div> </div>
`; `;
itemsContainer.appendChild(card); itemsContainer.appendChild(card);
@@ -4506,10 +4524,10 @@ document.addEventListener('DOMContentLoaded', ()=>{
: '<div style="font-size:0.92rem;color:#64748b;">Keine Beschädigungs-Historie vorhanden.</div>'; : '<div style="font-size:0.92rem;color:#64748b;">Keine Beschädigungs-Historie vorhanden.</div>';
modalContent.innerHTML = ` modalContent.innerHTML = `
<h2>${item.Name}</h2> <h2>${escapeHtml(item.Name || '')}</h2>
${borrowerInfoHtml} ${borrowerInfoHtml}
${appointmentInfoHtml} ${appointmentInfoHtml}
<div class="modal-image-container"> <div class="modal-image-container">
${imagesHtml} ${imagesHtml}
${item.Images && item.Images.length > 1 ? ` ${item.Images && item.Images.length > 1 ? `
@@ -4518,66 +4536,67 @@ document.addEventListener('DOMContentLoaded', ()=>{
<div class="image-counter">1/${item.Images.length}</div> <div class="image-counter">1/${item.Images.length}</div>
` : ''} ` : ''}
</div> </div>
<div class="modal-details"> <div class="modal-details">
<div class="detail-group"> <div class="detail-group">
<div class="detail-label">Ort:</div> <div class="detail-label">Ort:</div>
<div class="detail-value">${item.Ort || '-'}</div> <div class="detail-value">${escapeHtml(item.Ort || '-')}</div>
</div> </div>
<div class="detail-group"> <div class="detail-group">
<div class="detail-label">Status:</div> <div class="detail-label">Status:</div>
<div class="detail-value ${isBorrowed ? 'status-borrowed' : ''}"> <div class="detail-value ${isBorrowed ? 'status-borrowed' : ''}">
${isBorrowed ? 'Ausgeliehen' : 'Verfügbar'} ${isBorrowed ? 'Ausgeliehen' : 'Verfügbar'}
</div> </div>
</div> </div>
<div class="detail-group"> <div class="detail-group">
<div class="detail-label">Unterrichtsfach:</div> <div class="detail-label">Unterrichtsfach:</div>
<div class="detail-value">${filter1Array.join(', ') || '-'}</div> <div class="detail-value">${escapeHtml(filter1Array.join(', ') || '-')}</div>
</div> </div>
<div class="detail-group"> <div class="detail-group">
<div class="detail-label">Jahrgangsstufe:</div> <div class="detail-label">Jahrgangsstufe:</div>
<div class="detail-value">${filter2Array.join(', ') || '-'}</div> <div class="detail-value">${escapeHtml(filter2Array.join(', ') || '-')}</div>
</div> </div>
<div class="detail-group"> <div class="detail-group">
<div class="detail-label">Schlagwort:</div> <div class="detail-label">Schlagwort:</div>
<div class="detail-value">${filter3Array.join(', ') || '-'}</div> <div class="detail-value">${escapeHtml(filter3Array.join(', ') || '-')}</div>
</div> </div>
<div class="detail-group"> <div class="detail-group">
<div class="detail-label">Code:</div> <div class="detail-label">Code:</div>
<div class="detail-value">${item.Code_4 || '-'}</div> <div class="detail-value">${escapeHtml(item.Code_4 || '-')}</div>
</div> </div>
<div class="detail-group"> <div class="detail-group">
<div class="detail-label">Anzahl:</div> <div class="detail-label">Anzahl:</div>
<div class="detail-value">${item.GroupedDisplayCount || 1}</div> <div class="detail-value">${escapeHtml(String(item.GroupedDisplayCount || 1))}</div>
</div> </div>
${isGroupedItem ? ` ${isGroupedItem ? `
<div class="detail-group"> <div class="detail-group">
<div class="detail-label">Verfügbar:</div> <div class="detail-label">Verfügbar:</div>
<div class="detail-value">${availableGroupedCount}</div> <div class="detail-value">${escapeHtml(String(availableGroupedCount))}</div>
</div>` : ''} </div>` : ''}
<div class="detail-group"> <div class="detail-group">
<div class="detail-label">Anschaffungsjahr:</div> <div class="detail-label">Anschaffungsjahr:</div>
<div class="detail-value">${item.Anschaffungsjahr || '-'}</div> <div class="detail-value">${escapeHtml(String(item.Anschaffungsjahr || '-'))}</div>
</div>
<div class="detail-group">
<div class="detail-label">Anschaffungskosten:</div>
<div class="detail-value">${item.Anschaffungskosten ? item.Anschaffungskosten + ' €' : '-'}</div>
</div>
<div class="detail-group full-width">
<div class="detail-label">Beschreibung:</div>
<div class="detail-value">${item.Beschreibung || '-'}</div>
</div> </div>
<div class="detail-group">
<div class="detail-label">Anschaffungskosten:</div>
<div class="detail-value">${item.Anschaffungskosten ? escapeHtml(String(item.Anschaffungskosten)) + ' €' : '-'}</div>
</div>
<div class="detail-group full-width">
<div class="detail-label">Beschreibung:</div>
<div class="detail-value">${escapeHtml(item.Beschreibung || '-')}</div>
</div>
{% if current_permissions.actions.get('can_view_logs', False) %}
<div class="detail-group full-width" style="margin-top:12px;"> <div class="detail-group full-width" style="margin-top:12px;">
<div class="detail-label" style="font-weight:600; color:#374151;">Beschädigungs-Historie</div> <div class="detail-label" style="font-weight:600; color:#374151;">Beschädigungs-Historie</div>
<div class="detail-value"> <div class="detail-value">
@@ -4590,7 +4609,8 @@ document.addEventListener('DOMContentLoaded', ()=>{
</div> </div>
</div> </div>
</div> </div>
{% endif %}
<div class="detail-group full-width" style="margin-top:12px; padding:10px; border:1px solid #e3e3e3; border-radius:8px;"> <div class="detail-group full-width" style="margin-top:12px; padding:10px; border:1px solid #e3e3e3; border-radius:8px;">
<div class="detail-label">Verfügbarkeit prüfen:</div> <div class="detail-label">Verfügbarkeit prüfen:</div>
<div class="detail-value"> <div class="detail-value">
@@ -4611,7 +4631,7 @@ document.addEventListener('DOMContentLoaded', ()=>{
</div> </div>
</div> </div>
</div> </div>
<div class="detail-group full-width" style="margin-top:15px;"> <div class="detail-group full-width" style="margin-top:15px;">
<div class="detail-label">Geplante Ausleihen:</div> <div class="detail-label">Geplante Ausleihen:</div>
<div class="detail-value"> <div class="detail-value">
@@ -4635,9 +4655,10 @@ document.addEventListener('DOMContentLoaded', ()=>{
</div> </div>
</div> </div>
</div> </div>
<div class="modal-actions"> <div class="modal-actions">
${!isBorrowed ? ${!isBorrowed ?
{% if current_permissions.actions.get('can_borrow', False) %}
`<form method="POST" action="/ausleihen/${item._id}"> `<form method="POST" action="/ausleihen/${item._id}">
${isGroupedItem ? ` ${isGroupedItem ? `
<div style="display:flex; gap:8px; flex-wrap:wrap; margin-bottom:8px; align-items:center;"> <div style="display:flex; gap:8px; flex-wrap:wrap; margin-bottom:8px; align-items:center;">
@@ -4651,6 +4672,9 @@ document.addEventListener('DOMContentLoaded', ()=>{
</div>` : ''} </div>` : ''}
<button class="ausleihen" type="submit">Ausleihen</button> <button class="ausleihen" type="submit">Ausleihen</button>
</form>` </form>`
{% else %}
`<button class="ausleihen disabled-button" disabled title="Keine Berechtigung">Ausleihen nicht möglich</button>`
{% endif %}
: (!isGroupedItem && item.User === currentUsername) ? : (!isGroupedItem && item.User === currentUsername) ?
`<form method="POST" action="/zurueckgeben/${item._id}"> `<form method="POST" action="/zurueckgeben/${item._id}">
<button class="ausleihen" type="submit">Zurückgeben</button> <button class="ausleihen" type="submit">Zurückgeben</button>
@@ -4659,7 +4683,7 @@ document.addEventListener('DOMContentLoaded', ()=>{
} }
<button class="edit-button" onclick="openEditModalForSelectedUnit('${item._id}', 'specific-item-modal-${item._id}')">Bearbeiten</button> <button class="edit-button" onclick="openEditModalForSelectedUnit('${item._id}', 'specific-item-modal-${item._id}')">Bearbeiten</button>
<button class="duplicate-button" onclick="duplicateItem('${item._id}')">Duplizieren</button> <button class="duplicate-button" onclick="duplicateItem('${item._id}')">Duplizieren</button>
${damageReports.length > 0 ? `<button class="damage-button" onclick="markDamageAsRepaired('${item._id}')">Repariert</button>` : `<button class="damage-button" onclick="registerDamage('${item._id}')">Schaden melden</button>`} ${damageReports && damageReports.length > 0 ? `<button class="damage-button" onclick="markDamageAsRepaired('${item._id}')">Repariert</button>` : `<button class="damage-button" onclick="registerDamage('${item._id}')">Schaden melden</button>`}
${canScheduleItem ? `<button class="schedule-button" onclick="openScheduleModal('${item._id}')">Reservieren</button>` : ''} ${canScheduleItem ? `<button class="schedule-button" onclick="openScheduleModal('${item._id}')">Reservieren</button>` : ''}
<form method="POST" action="/delete_item/${item._id}" style="display:inline;" onsubmit="return confirm('Sind Sie sicher?')"> <form method="POST" action="/delete_item/${item._id}" style="display:inline;" onsubmit="return confirm('Sind Sie sicher?')">
<button class="delete-button" type="submit">Löschen</button> <button class="delete-button" type="submit">Löschen</button>