Merkmal-Optionen vorausgefüllt und bei aktivem Merkmal änderbar, Kiosk-Felder

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-26 13:52:15 +01:00
1 parent 99d02d5c29
commit 3255900f3f
7 files changed
+82 -90

No files matched your search

+26 -4
View File
@@ -7,6 +7,10 @@
# Passwort-Felder - "type" steuert den HTML-Input-Typ und die
# Parsing-Logik in parse_optionen_form().
#
# Seit 26.10.2026 (Kiosk) gibt es auch "textarea", "url" und "number", und
# alle Felder außer Passwörtern sind mit dem aktuellen Wert vorausgefüllt
# (templates/_merkmal_optionen.html), auch bei aktivem Merkmal änderbar.
#
# Passwort-Felder werden nie mit ihrem aktuellen Wert vorausgefüllt (siehe
# Templates) und nur übernommen, wenn tatsächlich etwas eingegeben wurde -
# ein leer gelassenes Passwort-Feld beim Bearbeiten überschreibt also
@@ -47,19 +51,37 @@ OPTIONEN_FELDER = {
"veyon": [
{"key": "veyon_lehrer", "label": "Lehrergerät (Veyon Master mit Schlüssel und Raumliste)", "type": "checkbox"},
],
# 26.10.2026: Kiosk (platform-docs roadmap/kiosk.md, Rolle kiosk).
"kiosk": [
{"key": "kiosk_startseite", "label": "Startseite", "type": "url", "placeholder": "https://…"},
{"key": "kiosk_whitelist", "label": "Whitelist: erlaubte Domains, eine je Zeile, optional mit Pfad. Subdomains sind eingeschlossen, die Domain der Startseite ist immer erlaubt.",
"type": "textarea", "placeholder": "opac.beispiel.de\nschule.de/vertretungsplan"},
{"key": "kiosk_frei_surfen", "label": "Frei surfen (Whitelist aus)", "type": "checkbox"},
{"key": "kiosk_anzeigename", "label": "Name am Anmeldebildschirm (leer = „Kiosk“)", "type": "text"},
{"key": "kiosk_leerlauf_minuten", "label": "Nach so vielen Minuten ohne Eingabe mit der Startseite neu beginnen (leer = 10, 0 = nie)", "type": "number"},
{"key": "kiosk_ohne_leiste", "label": "Bedienleiste (Zurück, Startseite, Abmelden) ausblenden, z. B. für Anzeigetafeln", "type": "checkbox"},
{"key": "kiosk_bildschirm_an", "label": "Bildschirm schaltet nie ab", "type": "checkbox"},
{"key": "kiosk_autostart", "label": "Autostart: Kiosk startet ohne Anmeldung, der Anmeldebildschirm erscheint nicht mehr", "type": "checkbox"},
],
}
def parse_optionen_form(form, merkmal_key: str) -> dict:
"""Liest die zu einem Merkmal gehörenden optionen-Felder aus einem
FastAPI-Formular gemäß OPTIONEN_FELDER - Checkboxen werden zu bool,
Text-/Passwort-Felder nur übernommen, wenn tatsächlich etwas eingegeben
wurde (leer = "nicht ändern", siehe Modul-Kommentar oben)."""
Passwort-Felder nur übernommen, wenn tatsächlich etwas eingegeben
wurde (leer = "nicht ändern", siehe Modul-Kommentar oben), Text-Felder
immer (sie sind vorausgefüllt)."""
ergebnis = {}
for feld in OPTIONEN_FELDER.get(merkmal_key, []):
roh = form.get(f"optionen.{feld['key']}")
if feld["type"] == "checkbox":
ergebnis[feld["key"]] = roh == "on"
elif roh:
ergebnis[feld["key"]] = roh
elif feld["type"] == "password":
if roh:
ergebnis[feld["key"]] = roh
elif roh is not None:
# Seit 26.10.2026 vorausgefüllt (_merkmal_optionen.html): leer
# heißt hier "entfernen", nicht "beibehalten".
ergebnis[feld["key"]] = roh.strip()
return ergebnis
+46
View File
@@ -0,0 +1,46 @@
{# Merkmal-Optionen (ADR-0010) an OE und Gruppe, Feld-Definitionen in
optionen_felder.py. Seit 26.10.2026 (Kiosk) vorausgefüllt und bei aktivem
Merkmal unter "Einstellungen" änderbar; Passwörter bleiben leer
(leer = beibehalten). #}
{% macro optionen_felder_html(felder, werte) %}
<div class="optionen">
{% for feld in felder %}
<label>
{% if feld.type == "checkbox" %}
<input type="checkbox" name="optionen.{{ feld.key }}" {% if werte.get(feld.key) %}checked{% endif %}>
{{ feld.label }}
{% elif feld.type == "textarea" %}
{{ feld.label }}
<textarea name="optionen.{{ feld.key }}" rows="{{ feld.rows or 4 }}" autocomplete="off"
{% if feld.placeholder %}placeholder="{{ feld.placeholder }}"{% endif %}>{{ werte.get(feld.key) or "" }}</textarea>
{% elif feld.type == "password" %}
{{ feld.label }}
<input type="password" name="optionen.{{ feld.key }}" autocomplete="off"
{% if werte.get(feld.key) %}placeholder="(bereits gesetzt - leer lassen zum Beibehalten)"{% endif %}>
{% else %}
{{ feld.label }}
<input type="{{ feld.type }}" name="optionen.{{ feld.key }}" autocomplete="off"
value="{{ werte.get(feld.key) if werte.get(feld.key) is not none else '' }}"
{% if feld.placeholder %}placeholder="{{ feld.placeholder }}"{% endif %}>
{% endif %}
</label>
{% endfor %}
</div>
{% endmacro %}
{% macro merkmal_aktivieren(felder, werte, aktiv, select_url, knopf_text) %}
{% if aktiv != true %}
<form method="post" action="{{ select_url }}">
{% if felder %}{{ optionen_felder_html(felder, werte or {}) }}{% endif %}
<button type="submit">{{ knopf_text }}</button>
</form>
{% elif felder %}
<details class="optionen-bearbeiten">
<summary>Einstellungen</summary>
<form method="post" action="{{ select_url }}">
{{ optionen_felder_html(felder, werte or {}) }}
<button type="submit">Einstellungen speichern</button>
</form>
</details>
{% endif %}
{% endmacro %}
+2 -2
View File
@@ -172,13 +172,13 @@ oder sie unter <a href="/struktur">Organisation › Standorte/Zwecke</a> direkt
<h4>🧭 Standardbrowser</h4>
<div class="ws-box">
<form method="post" action="{{ ws_basis }}/{{ workspace_id }}/standardbrowser">
<label>Webapps ohne Kiosk-Modus und Links aus anderen Programmen öffnen sich in
<label>Webapps ohne eigenes Fenster und Links aus anderen Programmen öffnen sich in
<select name="browser">
<option value="" {% if not workspace.standardbrowser %}selected{% endif %}>keine Vorgabe (Standard der Distro)</option>
{% for b in workspace.standardbrowser_auswahl %}<option value="{{ b.key }}" {% if b.key == workspace.standardbrowser %}selected{% endif %}>{{ b.name }}</option>{% endfor %}
</select>
</label>
<small>Zur Wahl stehen Firefox und die Browser, die im gespeicherten Entwurf enthalten sind. Webapps mit Kiosk-Modus starten weiterhin als eigenes Chrome- bzw. Chromium-Fenster.</small>
<small>Zur Wahl stehen Firefox und die Browser, die im gespeicherten Entwurf enthalten sind. Webapps mit eigenem Fenster starten weiterhin als eigenes Chrome- bzw. Chromium-Fenster.</small>
<button type="submit" class="outline">Speichern</button>
</form>
</div>
+2 -21
View File
@@ -1,4 +1,5 @@
{% extends "admin/layout.html" %}
{% from "_merkmal_optionen.html" import merkmal_aktivieren with context %}
{% block title %}Gruppe {{ gruppe.name }} — Tuxflotte Admin{% endblock %}
{% block admin_content %}
<p><a href="/admin/kunden/{{ organization_id }}/gruppen">← Gruppen</a></p>
@@ -30,27 +31,7 @@
{% endif %}
</td>
<td>
{% if eintrag.aktiv != true %}
<form method="post" action="/admin/kunden/{{ organization_id }}/gruppen/{{ gruppe.id }}/merkmale/{{ eintrag.merkmal }}/select">
{% if optionen_felder.get(eintrag.merkmal) %}
<div class="optionen">
{% for feld in optionen_felder[eintrag.merkmal] %}
<label>
{% if feld.type == "checkbox" %}
<input type="checkbox" name="optionen.{{ feld.key }}">
{{ feld.label }}
{% else %}
{{ feld.label }}
<input type="{{ feld.type }}" name="optionen.{{ feld.key }}" autocomplete="off"
{% if eintrag.optionen and eintrag.optionen.get(feld.key) %}placeholder="(bereits gesetzt - leer lassen zum Beibehalten)"{% endif %}>
{% endif %}
</label>
{% endfor %}
</div>
{% endif %}
<button type="submit">Aktiv</button>
</form>
{% endif %}
{{ merkmal_aktivieren(optionen_felder.get(eintrag.merkmal), eintrag.optionen, eintrag.aktiv, "/admin/kunden/" ~ organization_id ~ "/gruppen/" ~ gruppe.id ~ "/merkmale/" ~ eintrag.merkmal ~ "/select", "Aktiv") }}
{% if eintrag.aktiv != false %}
<form method="post" action="/admin/kunden/{{ organization_id }}/gruppen/{{ gruppe.id }}/merkmale/{{ eintrag.merkmal }}/deselect" style="display:inline">
<button type="submit" class="secondary">Inaktiv</button>
+2 -21
View File
@@ -1,4 +1,5 @@
{% extends "admin/layout.html" %}
{% from "_merkmal_optionen.html" import merkmal_aktivieren with context %}
{% block title %}OE-Merkmale — Tuxflotte Admin{% endblock %}
{% block admin_content %}
<p><a href="/admin/kunden/{{ organization_id }}">← Organisation</a></p>
@@ -31,27 +32,7 @@
{% endif %}
</td>
<td>
{% if eintrag.aktiv != true %}
<form method="post" action="/admin/kunden/{{ organization_id }}/organisationseinheiten/{{ oe_id }}/merkmale/{{ eintrag.merkmal }}/select">
{% if optionen_felder.get(eintrag.merkmal) %}
<div class="optionen">
{% for feld in optionen_felder[eintrag.merkmal] %}
<label>
{% if feld.type == "checkbox" %}
<input type="checkbox" name="optionen.{{ feld.key }}">
{{ feld.label }}
{% else %}
{{ feld.label }}
<input type="{{ feld.type }}" name="optionen.{{ feld.key }}" autocomplete="off"
{% if eintrag.optionen and eintrag.optionen.get(feld.key) %}placeholder="(bereits gesetzt - leer lassen zum Beibehalten)"{% endif %}>
{% endif %}
</label>
{% endfor %}
</div>
{% endif %}
<button type="submit">Aktiv</button>
</form>
{% endif %}
{{ merkmal_aktivieren(optionen_felder.get(eintrag.merkmal), eintrag.optionen, eintrag.aktiv, "/admin/kunden/" ~ organization_id ~ "/organisationseinheiten/" ~ oe_id ~ "/merkmale/" ~ eintrag.merkmal ~ "/select", "Aktiv") }}
{% if eintrag.aktiv != false %}
<form method="post" action="/admin/kunden/{{ organization_id }}/organisationseinheiten/{{ oe_id }}/merkmale/{{ eintrag.merkmal }}/deselect" style="display:inline">
<button type="submit" class="secondary">Inaktiv</button>
+2 -21
View File
@@ -1,4 +1,5 @@
{% extends "base.html" %}
{% from "_merkmal_optionen.html" import merkmal_aktivieren with context %}
{% block title %}Gruppe {{ gruppe.name }} — Tuxflotte{% endblock %}
{% block content %}
<p><a href="/gruppen">← Gruppen</a></p>
@@ -33,27 +34,7 @@
{% endif %}
</td>
<td>
{% if eintrag.aktiv != true %}
<form method="post" action="/gruppen/{{ gruppe.id }}/merkmale/{{ eintrag.merkmal }}/select">
{% if optionen_felder.get(eintrag.merkmal) %}
<div class="optionen">
{% for feld in optionen_felder[eintrag.merkmal] %}
<label>
{% if feld.type == "checkbox" %}
<input type="checkbox" name="optionen.{{ feld.key }}">
{{ feld.label }}
{% else %}
{{ feld.label }}
<input type="{{ feld.type }}" name="optionen.{{ feld.key }}" autocomplete="off"
{% if eintrag.optionen.get(feld.key) %}placeholder="(bereits gesetzt - leer lassen zum Beibehalten)"{% endif %}>
{% endif %}
</label>
{% endfor %}
</div>
{% endif %}
<button type="submit">Aktiv setzen</button>
</form>
{% endif %}
{{ merkmal_aktivieren(optionen_felder.get(eintrag.merkmal), eintrag.optionen, eintrag.aktiv, "/gruppen/" ~ gruppe.id ~ "/merkmale/" ~ eintrag.merkmal ~ "/select", "Aktiv setzen") }}
{% if eintrag.aktiv != false %}
<form method="post" action="/gruppen/{{ gruppe.id }}/merkmale/{{ eintrag.merkmal }}/deselect">
<button type="submit" class="secondary">Inaktiv setzen</button>
+2 -21
View File
@@ -1,4 +1,5 @@
{% extends "base.html" %}
{% from "_merkmal_optionen.html" import merkmal_aktivieren with context %}
{% block title %}Merkmale {{ oe.name }} — Tuxflotte{% endblock %}
{% block content %}
<p><a href="/struktur">← Standorte/Zwecke</a></p>
@@ -71,27 +72,7 @@
{% endif %}
</td>
<td>
{% if eintrag.aktiv != true %}
<form method="post" action="/struktur/{{ oe.id }}/merkmale/{{ eintrag.merkmal }}/select">
{% if optionen_felder.get(eintrag.merkmal) %}
<div class="optionen">
{% for feld in optionen_felder[eintrag.merkmal] %}
<label>
{% if feld.type == "checkbox" %}
<input type="checkbox" name="optionen.{{ feld.key }}">
{{ feld.label }}
{% else %}
{{ feld.label }}
<input type="{{ feld.type }}" name="optionen.{{ feld.key }}" autocomplete="off"
{% if eintrag.optionen.get(feld.key) %}placeholder="(bereits gesetzt - leer lassen zum Beibehalten)"{% endif %}>
{% endif %}
</label>
{% endfor %}
</div>
{% endif %}
<button type="submit">Aktiv setzen</button>
</form>
{% endif %}
{{ merkmal_aktivieren(optionen_felder.get(eintrag.merkmal), eintrag.optionen, eintrag.aktiv, "/struktur/" ~ oe.id ~ "/merkmale/" ~ eintrag.merkmal ~ "/select", "Aktiv setzen") }}
{% if eintrag.aktiv != false %}
<form method="post" action="/struktur/{{ oe.id }}/merkmale/{{ eintrag.merkmal }}/deselect">
<button type="submit" class="secondary">Inaktiv setzen</button>