Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e46f8b0c66 | |||
| 06486f039f | |||
| feac00f0df | |||
| 0b0169ef96 | |||
| a6db3a001f | |||
| 8fa495a23c | |||
| 9df7a73db1 | |||
| dcfd23b412 | |||
| d523dd0a68 | |||
| d7d96d5567 | |||
| a0018eebd5 |
+73
-69
@@ -18,7 +18,6 @@ Features:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from flask import Flask, render_template, request, redirect, url_for, session, flash, send_from_directory, get_flashed_messages, jsonify, Response, make_response, send_file, abort
|
from flask import Flask, render_template, request, redirect, url_for, session, flash, send_from_directory, get_flashed_messages, jsonify, Response, make_response, send_file, abort
|
||||||
from flask_wtf.csrf import CSRFProtect
|
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||||
from werkzeug.exceptions import HTTPException
|
from werkzeug.exceptions import HTTPException
|
||||||
@@ -116,7 +115,6 @@ app.config['PREFERRED_URL_SCHEME'] = 'https' if app.config['SESSION_COOKIE_SECUR
|
|||||||
# app.config['QR_CODE_FOLDER'] = cfg.QR_CODE_FOLDER # QR Code storage deactivated
|
# app.config['QR_CODE_FOLDER'] = cfg.QR_CODE_FOLDER # QR Code storage deactivated
|
||||||
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1)
|
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1)
|
||||||
app.register_blueprint(terminplaner_bp, url_prefix='/terminplaner')
|
app.register_blueprint(terminplaner_bp, url_prefix='/terminplaner')
|
||||||
csrf = CSRFProtect(app)
|
|
||||||
|
|
||||||
"""--------------------------------------------------------------Path Init-------------------------------------------------------"""
|
"""--------------------------------------------------------------Path Init-------------------------------------------------------"""
|
||||||
|
|
||||||
@@ -3607,23 +3605,26 @@ def api_item_detail(item_id):
|
|||||||
borrows_html = ''
|
borrows_html = ''
|
||||||
if borrow_records:
|
if borrow_records:
|
||||||
rows = []
|
rows = []
|
||||||
for rec in borrow_records:
|
count = 1
|
||||||
user_raw = rec.get('User')
|
while count < 3:
|
||||||
try:
|
for rec in borrow_records:
|
||||||
user = decrypt_text(user_raw) if user_raw is not None else ''
|
user_raw = rec.get('User')
|
||||||
except Exception:
|
try:
|
||||||
user = user_raw
|
user = decrypt_text(user_raw) if user_raw is not None else ''
|
||||||
status = rec.get('Status', '')
|
except Exception:
|
||||||
start = fmt_dt(rec.get('Start'))
|
user = user_raw
|
||||||
end = fmt_dt(rec.get('End'))
|
status = rec.get('Status', '')
|
||||||
notes = html.escape(str(rec.get('Notes') or ''))
|
start = fmt_dt(rec.get('Start'))
|
||||||
|
end = fmt_dt(rec.get('End'))
|
||||||
rows.append(
|
notes = html.escape(str(rec.get('Notes') or ''))
|
||||||
f"<li><strong>{html.escape(str(user or '-'))}</strong> — "
|
|
||||||
f"{html.escape(str(status))} — "
|
rows.append(
|
||||||
f"{html.escape(str(start))} → {html.escape(str(end))}"
|
f"<li><strong>{html.escape(str(user or '-'))}</strong> — "
|
||||||
f"{(' — ' + notes) if notes else ''}</li>"
|
f"{html.escape(str(status))} — "
|
||||||
)
|
f"{html.escape(str(start))} → {html.escape(str(end))}"
|
||||||
|
f"{(' — ' + notes) if notes else ''}</li>"
|
||||||
|
)
|
||||||
|
count += 1
|
||||||
borrows_html = f"<h3>Ausleihhistorie</h3><ul>{''.join(rows)}</ul>"
|
borrows_html = f"<h3>Ausleihhistorie</h3><ul>{''.join(rows)}</ul>"
|
||||||
|
|
||||||
detail_html = f"""
|
detail_html = f"""
|
||||||
@@ -7905,66 +7906,69 @@ def terminplan():
|
|||||||
@app.route('/register', methods=['GET', 'POST'])
|
@app.route('/register', methods=['GET', 'POST'])
|
||||||
def register():
|
def register():
|
||||||
"""
|
"""
|
||||||
User registration route.false
|
User registration route.
|
||||||
Returns:
|
Returns:
|
||||||
flask.Response: Rendered template or redirect
|
flask.Response: Rendered template or redirect
|
||||||
"""
|
"""
|
||||||
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 Adresse zu nutzen, versuchen Sie es erneut, nachdem Sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
if 'username' in session:
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
password = request.form['password']
|
password = request.form['password']
|
||||||
name = (request.form.get('name') or '').strip()
|
name = (request.form.get('name') or '').strip()
|
||||||
last_name = (request.form.get('last-name') or '').strip()
|
last_name = (request.form.get('last-name') or '').strip()
|
||||||
|
|
||||||
# Generate a username from the first 3 letters of first and last name.
|
username = us.build_unique_username_from_name(name, last_name)
|
||||||
username = us.build_unique_username_from_name(name, last_name)
|
|
||||||
|
permission_preset = (request.form.get('permission_preset') or 'standard_user').strip()
|
||||||
|
use_custom_permissions = request.form.get('use_custom_permissions') == 'on'
|
||||||
|
|
||||||
|
if not username or not password or not name or not last_name:
|
||||||
|
flash('Bitte füllen Sie alle Felder aus', 'error')
|
||||||
|
return redirect(url_for('register'))
|
||||||
|
|
||||||
permission_preset = (request.form.get('permission_preset') or 'standard_user').strip()
|
if not us.check_password_strength(password):
|
||||||
use_custom_permissions = request.form.get('use_custom_permissions') == 'on'
|
flash('Passwort ist zu schwach oder entspricht nicht den Richtlinien', 'error')
|
||||||
|
return redirect(url_for('register'))
|
||||||
|
|
||||||
|
action_permissions = None
|
||||||
|
page_permissions = None
|
||||||
|
if use_custom_permissions:
|
||||||
|
action_permissions = {}
|
||||||
|
for action_key, _ in PERMISSION_ACTION_OPTIONS:
|
||||||
|
action_permissions[action_key] = request.form.get(f'action_{action_key}') == 'on'
|
||||||
|
|
||||||
|
page_permissions = {}
|
||||||
|
for endpoint_name, _ in PERMISSION_PAGE_OPTIONS:
|
||||||
|
page_permissions[endpoint_name] = request.form.get(f'page_{endpoint_name}') == 'on'
|
||||||
|
|
||||||
if not username or not password or not name or not last_name:
|
us.add_user(
|
||||||
flash('Bitte füllen Sie alle Felder aus', 'error')
|
username,
|
||||||
return redirect(url_for('register'))
|
password,
|
||||||
if not us.check_password_strength(password):
|
name,
|
||||||
flash('Passwort ist zu schwach', 'error')
|
last_name,
|
||||||
return redirect(url_for('register'))
|
is_student=False,
|
||||||
|
student_card_id=None,
|
||||||
action_permissions = None
|
max_borrow_days=None,
|
||||||
page_permissions = None
|
permission_preset=permission_preset,
|
||||||
if use_custom_permissions:
|
action_permissions=action_permissions,
|
||||||
action_permissions = {}
|
page_permissions=page_permissions,
|
||||||
for action_key, _ in PERMISSION_ACTION_OPTIONS:
|
|
||||||
action_permissions[action_key] = request.form.get(f'action_{action_key}') == 'on'
|
|
||||||
|
|
||||||
page_permissions = {}
|
|
||||||
for endpoint_name, _ in PERMISSION_PAGE_OPTIONS:
|
|
||||||
page_permissions[endpoint_name] = request.form.get(f'page_{endpoint_name}') == 'on'
|
|
||||||
|
|
||||||
us.add_user(
|
|
||||||
username,
|
|
||||||
password,
|
|
||||||
name,
|
|
||||||
last_name,
|
|
||||||
is_student=False,
|
|
||||||
student_card_id=None,
|
|
||||||
max_borrow_days=None,
|
|
||||||
permission_preset=permission_preset,
|
|
||||||
action_permissions=action_permissions,
|
|
||||||
page_permissions=page_permissions,
|
|
||||||
)
|
|
||||||
return redirect(url_for('home_admin'))
|
|
||||||
return render_template(
|
|
||||||
'register.html',
|
|
||||||
library_module_enabled=cfg.MODULES.is_enabled('library'),
|
|
||||||
student_cards_module_enabled=cfg.MODULES.is_enabled('student_cards'),
|
|
||||||
student_default_borrow_days=cfg.STUDENT_DEFAULT_BORROW_DAYS,
|
|
||||||
student_max_borrow_days=cfg.STUDENT_MAX_BORROW_DAYS
|
|
||||||
)
|
)
|
||||||
flash('Sie sind nicht berechtigt, diese Seite anzuzeigen', 'error')
|
|
||||||
return redirect(url_for('login'))
|
flash(f'Benutzer "{username}" wurde erfolgreich registriert!', 'success')
|
||||||
|
return redirect(url_for('home_admin'))
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
'register.html',
|
||||||
|
library_module_enabled=cfg.MODULES.is_enabled('library'),
|
||||||
|
student_cards_module_enabled=cfg.MODULES.is_enabled('student_cards'),
|
||||||
|
student_default_borrow_days=cfg.STUDENT_DEFAULT_BORROW_DAYS,
|
||||||
|
student_max_borrow_days=cfg.STUDENT_MAX_BORROW_DAYS,
|
||||||
|
permission_presets=getattr(us, 'PERMISSION_PRESETS', {}),
|
||||||
|
permission_action_options=PERMISSION_ACTION_OPTIONS,
|
||||||
|
permission_page_options=PERMISSION_PAGE_OPTIONS
|
||||||
|
)
|
||||||
|
|
||||||
@app.route('/user_del', methods=['GET'])
|
@app.route('/user_del', methods=['GET'])
|
||||||
def user_del():
|
def user_del():
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
flask
|
flask
|
||||||
|
flask-wtf
|
||||||
werkzeug
|
werkzeug
|
||||||
gunicorn
|
gunicorn
|
||||||
pymongo
|
pymongo
|
||||||
|
|||||||
+24
-15
@@ -1375,8 +1375,10 @@
|
|||||||
<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 %}
|
||||||
|
{% if current_permissions.actions.get('can_manage_users', False) %}
|
||||||
<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 %}
|
||||||
|
{% endif %}
|
||||||
{% if current_permissions.pages.get('admin_school_settings', False) %}
|
{% 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 %}
|
{% endif %}
|
||||||
@@ -1619,11 +1621,10 @@
|
|||||||
<div id="cookie-banner" role="dialog" aria-live="polite" aria-label="Cookie-Hinweis">
|
<div id="cookie-banner" role="dialog" aria-live="polite" aria-label="Cookie-Hinweis">
|
||||||
<div class="cb-inner">
|
<div class="cb-inner">
|
||||||
<div class="cb-text">
|
<div class="cb-text">
|
||||||
Wir verwenden technisch notwendige Cookies, um Ihre Sitzung zu verwalten und die Anwendung bereitzustellen. Bitte akzeptieren Sie Cookies, um fortzufahren.
|
Wir verwenden ausschließlich technisch notwendige Cookies, um Ihre Sitzung zu verwalten und die Anwendung bereitzustellen. Bitte bestätigen Sie dies, um fortzufahren.
|
||||||
</div>
|
</div>
|
||||||
<div class="cb-actions">
|
<div class="cb-actions">
|
||||||
<button class="btn-decline" id="cookie-decline">Ablehnen</button>
|
<button class="btn-accept" id="cookie-accept">Notwendige Cookies akzeptieren</button>
|
||||||
<button class="btn-accept" id="cookie-accept">Akzeptieren</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1728,31 +1729,39 @@
|
|||||||
(function(){
|
(function(){
|
||||||
function getCookie(name){
|
function getCookie(name){
|
||||||
const v = document.cookie.split(';').map(s=>s.trim());
|
const v = document.cookie.split(';').map(s=>s.trim());
|
||||||
for(const c of v){ if(c.startsWith(name+'=')) return decodeURIComponent(c.split('=')[1]); }
|
for(const c of v){
|
||||||
|
if(c.startsWith(name+'=')) return decodeURIComponent(c.split('=')[1]);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCookie(name, value, days){
|
function setCookie(name, value, days){
|
||||||
const d = new Date(); d.setTime(d.getTime() + (days*24*60*60*1000));
|
const d = new Date();
|
||||||
|
d.setTime(d.getTime() + (days*24*60*60*1000));
|
||||||
document.cookie = name + '=' + encodeURIComponent(value) + ';expires=' + d.toUTCString() + ';path=/;SameSite=Lax';
|
document.cookie = name + '=' + encodeURIComponent(value) + ';expires=' + d.toUTCString() + ';path=/;SameSite=Lax';
|
||||||
}
|
}
|
||||||
function showBanner(){ var el = document.getElementById('cookie-banner'); if(el) el.style.display = 'block'; }
|
|
||||||
function hideBanner(){ var el = document.getElementById('cookie-banner'); if(el) el.style.display = 'none'; }
|
|
||||||
|
|
||||||
// If not decided yet, show banner and block app until decision
|
function showBanner(){
|
||||||
|
var el = document.getElementById('cookie-banner');
|
||||||
|
if(el) el.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideBanner(){
|
||||||
|
var el = document.getElementById('cookie-banner');
|
||||||
|
if(el) el.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfen, ob der Nutzer bereits zugestimmt hat
|
||||||
const consent = getCookie('cookie_consent');
|
const consent = getCookie('cookie_consent');
|
||||||
if(!consent){
|
if(!consent){
|
||||||
showBanner();
|
showBanner();
|
||||||
// Optionally blur content until consent
|
|
||||||
document.body.style.filter = 'none';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nur noch der Akzeptieren-Button für vitale Cookies ist vorhanden
|
||||||
document.getElementById('cookie-accept')?.addEventListener('click', function(){
|
document.getElementById('cookie-accept')?.addEventListener('click', function(){
|
||||||
setCookie('cookie_consent','accepted',365);
|
setCookie('cookie_consent', 'vital_accepted', 365);
|
||||||
hideBanner();
|
hideBanner();
|
||||||
});
|
});
|
||||||
document.getElementById('cookie-decline')?.addEventListener('click', function(){
|
|
||||||
setCookie('cookie_consent','declined',365);
|
|
||||||
window.location.href = 'https://www.ecosia.org/';
|
|
||||||
});
|
|
||||||
|
|
||||||
const username = {{ (session['username'] if 'username' in session else '')|tojson }};
|
const username = {{ (session['username'] if 'username' in session else '')|tojson }};
|
||||||
const isTutorialPage = window.location.pathname === {{ url_for('tutorial_page')|tojson }};
|
const isTutorialPage = window.location.pathname === {{ url_for('tutorial_page')|tojson }};
|
||||||
|
|||||||
@@ -25,9 +25,6 @@
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="form-card">
|
<div class="form-card">
|
||||||
<form method="POST" action="{{ url_for('register') }}">
|
<form method="POST" action="{{ url_for('register') }}">
|
||||||
|
|
||||||
<!-- CSRF-Schutz (Zwingend erforderlich für POST) -->
|
|
||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="name">Vorname</label>
|
<label for="name">Vorname</label>
|
||||||
@@ -65,17 +62,16 @@
|
|||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
<span class="input-icon">🔒</span>
|
<span class="input-icon">🔒</span>
|
||||||
<!-- HTML5 Pattern blockiert unsichere Passwörter vor dem Absenden -->
|
<!-- HTML5 Pattern blockiert unsichere Passwörter vor dem Absenden -->
|
||||||
<input type="password"
|
<input
|
||||||
id="password"
|
id="password"
|
||||||
name="password"
|
name="password"
|
||||||
placeholder="Geben Sie ein sicheres Passwort ein"
|
placeholder="Geben Sie ein sicheres Passwort ein"
|
||||||
required
|
required
|
||||||
pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{12,}">
|
pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{12,}">
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn-secondary" id="toggle-pw-btn" onclick="togglePasswordVisibility()" title="Passwort anzeigen/verbergen">👁️</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="pw-actions">
|
<div class="pw-actions">
|
||||||
<button type="button" class="btn-secondary" onclick="generateSecurePassword()">🎲 Automatisches Passwort generieren</button>
|
<button type="button" class="btn-secondary" onclick="generateSecurePassword()">Passwort generieren</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
flask
|
flask
|
||||||
|
flask-wtf
|
||||||
werkzeug
|
werkzeug
|
||||||
gunicorn
|
gunicorn
|
||||||
pymongo==4.6.3
|
pymongo==4.6.3
|
||||||
|
|||||||
Reference in New Issue
Block a user