Einstieg Schritt 7: Einladung zur Personalisierung ohne Seitenwechsel
Geräteseite: Formular immer da; fehlt "Personalisierung möglich", kommt das Gerät beim Senden in die Gruppe "Personalisierte Geräte" (eine je Organisation, bei Bedarf angelegt, Merkmal aktiv). Gruppen- und Standortseite: mit aktivem Merkmal eine Liste der Geräte mit E-Mail-Feld, nach dem Senden zurück auf dieselbe Seite. Gefunden im Erstkontakt-Probelauf. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e44b1b64b8
commit
6e79baedf6
9 files changed
+248
-56
No files matched your search
@@ -0,0 +1,80 @@
|
||||
"""
|
||||
Einladungen zur Personalisierung dort, wo der Kunde gerade ist (Einstieg
|
||||
vereinfachen, Schritt 7, 25.10.2026): auf der Geräteseite ohne vorher das
|
||||
Merkmal setzen zu müssen, und auf der Seite einer Gruppe bzw. eines
|
||||
Standorts/Zwecks mit aktivem Merkmal für alle ihre Geräte.
|
||||
"""
|
||||
|
||||
from anode_client import anode_request
|
||||
|
||||
PERSONALISIERUNG_MERKMAL = "personalisierung"
|
||||
# Eine gemeinsame Gruppe je Organisation statt einer Ein-Geräte-Gruppe pro
|
||||
# Gerät; Gruppen können ein Merkmal nur einschalten, nie abschalten.
|
||||
PERSONALISIERT_GRUPPE = "Personalisierte Geräte"
|
||||
|
||||
PERSONALISIERUNG_STATUS = {
|
||||
"eingeladen": "Einladung verschickt, noch nicht eingelöst",
|
||||
"code_offen": "Code erzeugt, noch nicht eingelöst",
|
||||
"eingereicht": "eingelöst, wartet auf Bestätigung per Mail",
|
||||
"bestaetigt": "Konto gewählt, wird beim nächsten Start eingerichtet",
|
||||
"angelegt": "eingerichtet",
|
||||
"zurueckgezogen": "zurückgezogen",
|
||||
"verfallen": "verfallen",
|
||||
"fehlgeschlagen": "am Gerät fehlgeschlagen",
|
||||
"beendet": "beendet",
|
||||
}
|
||||
|
||||
|
||||
def personalisierung_moeglich(katalog: list[dict]) -> bool:
|
||||
return any(e.get("merkmal") == PERSONALISIERUNG_MERKMAL and e.get("state") == "present" for e in katalog)
|
||||
|
||||
|
||||
def personalisierung_einschalten(organization_id: str, device_id: str) -> str | None:
|
||||
"""Gerät in die Gruppe "Personalisierte Geräte" (bei Bedarf angelegt, Merkmal
|
||||
aktiv). Rückgabe: Fehlermeldung oder None."""
|
||||
|
||||
gruppen = anode_request("GET", f"/api/v1/organizations/{organization_id}/gruppen")
|
||||
gruppe = next(
|
||||
(g for g in gruppen.get("gruppen", []) if g["name"] == PERSONALISIERT_GRUPPE),
|
||||
None,
|
||||
) if gruppen.get("success") else None
|
||||
|
||||
if gruppe is None:
|
||||
result = anode_request(
|
||||
"POST", f"/api/v1/organizations/{organization_id}/gruppen", json={"name": PERSONALISIERT_GRUPPE}
|
||||
)
|
||||
if not result.get("success"):
|
||||
return result.get("message") or "Die Gruppe „Personalisierte Geräte“ ließ sich nicht anlegen."
|
||||
gruppe = result["gruppe"]
|
||||
|
||||
result = anode_request(
|
||||
"POST", f"/api/v1/gruppen/{gruppe['id']}/merkmale/{PERSONALISIERUNG_MERKMAL}/select", json={"optionen": {}}
|
||||
)
|
||||
if not result.get("success"):
|
||||
return result.get("message") or "„Personalisierung möglich“ ließ sich nicht setzen."
|
||||
|
||||
result = anode_request("POST", f"/api/v1/devices/{device_id}/gruppen/{gruppe['id']}")
|
||||
if not result.get("success"):
|
||||
return result.get("message") or "Das Gerät ließ sich der Gruppe nicht zuordnen."
|
||||
return None
|
||||
|
||||
|
||||
def email_domaenen(organization_id: str) -> list[str]:
|
||||
result = anode_request("GET", f"/api/v1/organizations/{organization_id}")
|
||||
return result["organization"].get("email_domaenen", []) if result.get("success") else []
|
||||
|
||||
|
||||
def einladungs_zeilen(devices: list[dict]) -> list[dict]:
|
||||
"""Je Gerät der Stand der Personalisierung für die Liste auf Gruppen- und
|
||||
Standortseiten."""
|
||||
|
||||
zeilen = []
|
||||
for device in devices:
|
||||
result = anode_request("GET", f"/api/v1/devices/{device['id']}/personalisierung")
|
||||
pers = result.get("personalisierungen", []) if result.get("success") else []
|
||||
zeilen.append({
|
||||
"device": device,
|
||||
"konten": [p for p in pers if p["status"] == "angelegt"],
|
||||
"laufend": next((p for p in pers if p["status"] in ("eingeladen", "bestaetigt")), None),
|
||||
})
|
||||
return zeilen
|
||||
+35
-22
@@ -5,6 +5,7 @@ from fastapi.responses import RedirectResponse
|
||||
|
||||
from anode_client import anode_request
|
||||
from auth import get_current_user
|
||||
from einladungen import PERSONALISIERUNG_STATUS, personalisierung_einschalten, personalisierung_moeglich
|
||||
from routers.gruppen import fetch_gruppen
|
||||
from routers.personalisierung import _passwort_hash, einladung_senden
|
||||
from templating import templates
|
||||
@@ -150,19 +151,6 @@ async def gruppen_aktualisieren(request: Request, device_id: str, user: dict = D
|
||||
return RedirectResponse("/geraete", status_code=303)
|
||||
|
||||
|
||||
PERSONALISIERUNG_STATUS = {
|
||||
"eingeladen": "Einladung verschickt, noch nicht eingelöst",
|
||||
"code_offen": "Code erzeugt, noch nicht eingelöst",
|
||||
"eingereicht": "eingelöst, wartet auf Bestätigung per Mail",
|
||||
"bestaetigt": "Konto gewählt, wird beim nächsten Start eingerichtet",
|
||||
"angelegt": "eingerichtet",
|
||||
"zurueckgezogen": "zurückgezogen",
|
||||
"verfallen": "verfallen",
|
||||
"fehlgeschlagen": "am Gerät fehlgeschlagen",
|
||||
"beendet": "beendet",
|
||||
}
|
||||
|
||||
|
||||
def email_sichtbar(email: str | None, domaenen: list[str]) -> bool:
|
||||
"""
|
||||
ADR-0041 (15.10.2026): Adressen aus der Domänenliste der Organisation
|
||||
@@ -219,11 +207,11 @@ def geraete_info(
|
||||
(p for p in personalisierungen if p["status"] in ("eingeladen", "bestaetigt")), None
|
||||
),
|
||||
"personalisierung_status": PERSONALISIERUNG_STATUS,
|
||||
# Die Adresse lässt sich nur bei Geräten mit dem Merkmal eintragen;
|
||||
# anode prüft das noch einmal.
|
||||
"personalisierung_moeglich": any(
|
||||
e.get("merkmal") == "personalisierung" and e.get("state") == "present" for e in katalog
|
||||
),
|
||||
# Ohne das Merkmal setzt "Einladung senden" es über die Gruppe
|
||||
# "Personalisierte Geräte" (Schritt 7, 25.10.2026). Fehlt es im
|
||||
# Katalog ganz (etwa Ubuntu), gibt es keine Einladung.
|
||||
"personalisierung_moeglich": personalisierung_moeglich(katalog),
|
||||
"personalisierung_verfuegbar": any(e.get("merkmal") == "personalisierung" for e in katalog),
|
||||
"email_domaenen": org_result["organization"].get("email_domaenen", []) if org_result.get("success") else [],
|
||||
"einladung": einladung,
|
||||
"neues_passwort": neues_passwort,
|
||||
@@ -240,17 +228,34 @@ def personalisierung_einladen(
|
||||
email_lokal: str = Form(""),
|
||||
email_domain: str = Form(""),
|
||||
bestehenden_behalten: str = Form(""),
|
||||
zurueck: str = Form(""),
|
||||
user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
ADR-0041, vereinfachter Ablauf (24.10.2026): Adresse des Benutzers
|
||||
eintragen, die Einladung mit dem Link geht sofort raus. "Erneut senden"
|
||||
schickt dieselbe Adresse noch einmal; der alte Link wird ungültig.
|
||||
|
||||
Schritt 7 (25.10.2026): Fehlt dem Gerät "Personalisierung möglich", kommt
|
||||
es vorher in die Gruppe "Personalisierte Geräte". Mit "zurueck" (Seite
|
||||
einer Gruppe oder eines Standorts/Zwecks) geht es danach dorthin zurück.
|
||||
"""
|
||||
|
||||
if find_device_in_organization(device_id, user.organization_id) is None:
|
||||
raise HTTPException(status_code=404, detail="Gerät wurde nicht gefunden.")
|
||||
|
||||
if not (zurueck.startswith("/gruppen/") or zurueck.startswith("/struktur/")):
|
||||
zurueck = ""
|
||||
|
||||
def antwort(einladung: dict, status_code: int = 200):
|
||||
if not zurueck:
|
||||
return geraete_info(request, device_id, user, einladung, status_code=status_code)
|
||||
if einladung.get("fehler"):
|
||||
query = {"einladung_fehler": einladung["fehler"]}
|
||||
else:
|
||||
query = {"einladung_ok": einladung["gesendet_an"]}
|
||||
return RedirectResponse(f"{zurueck}?{urlencode(query)}#einladungen", status_code=303)
|
||||
|
||||
# Mit Domänenliste nur der Teil vor dem @ plus gewählte Domäne.
|
||||
if email_lokal.strip():
|
||||
lokal = email_lokal.strip()
|
||||
@@ -258,7 +263,15 @@ def personalisierung_einladen(
|
||||
else:
|
||||
adresse = email.strip()
|
||||
if not adresse:
|
||||
return geraete_info(request, device_id, user, {"fehler": "Bitte eine E-Mail-Adresse angeben."}, status_code=400)
|
||||
return antwort({"fehler": "Bitte eine E-Mail-Adresse angeben."}, status_code=400)
|
||||
|
||||
katalog = anode_request("GET", f"/api/v1/devices/{device_id}/auftragskatalog").get("auftragskatalog", [])
|
||||
if not personalisierung_moeglich(katalog):
|
||||
if not any(e.get("merkmal") == "personalisierung" for e in katalog):
|
||||
return antwort({"fehler": "Für dieses Gerät gibt es keine Personalisierung."}, status_code=400)
|
||||
fehler = personalisierung_einschalten(user.organization_id, device_id)
|
||||
if fehler:
|
||||
return antwort({"fehler": fehler}, status_code=400)
|
||||
|
||||
result = anode_request(
|
||||
"POST",
|
||||
@@ -267,15 +280,15 @@ def personalisierung_einladen(
|
||||
)
|
||||
if not result.get("success"):
|
||||
fehler = result.get("message") or "Die Einladung wurde abgelehnt."
|
||||
return geraete_info(request, device_id, user, {"fehler": fehler}, status_code=400)
|
||||
return antwort({"fehler": fehler}, status_code=400)
|
||||
|
||||
try:
|
||||
einladung_senden(result)
|
||||
except ValueError:
|
||||
anode_request("POST", f"/api/v1/devices/{device_id}/personalisierung/zurueckziehen")
|
||||
return geraete_info(request, device_id, user, {"fehler": "Die E-Mail-Adresse ist ungültig."}, status_code=400)
|
||||
return antwort({"fehler": "Die E-Mail-Adresse ist ungültig."}, status_code=400)
|
||||
|
||||
return geraete_info(request, device_id, user, {"gesendet_an": adresse})
|
||||
return antwort({"gesendet_an": adresse})
|
||||
|
||||
|
||||
@router.post("/geraete/{device_id}/personalisierung/{personalisierung_id}/sperren")
|
||||
|
||||
@@ -4,6 +4,7 @@ from fastapi.responses import RedirectResponse
|
||||
|
||||
from anode_client import anode_request
|
||||
from auth import get_current_user
|
||||
from einladungen import PERSONALISIERUNG_MERKMAL, PERSONALISIERUNG_STATUS, einladungs_zeilen, email_domaenen
|
||||
from optionen_felder import OPTIONEN_FELDER, parse_optionen_form
|
||||
from templating import templates
|
||||
|
||||
@@ -114,6 +115,12 @@ def gruppe_detail(request: Request, gruppe_id: str, user=Depends(get_current_use
|
||||
if any(g["id"] == gruppe_id for g in device_gruppen):
|
||||
mitglied_ids.add(device["id"])
|
||||
|
||||
# Schritt 7 (25.10.2026): mit aktivem "Personalisierung möglich" gleich
|
||||
# hier einladen, ohne Wechsel auf die einzelnen Geräteseiten.
|
||||
einladungen = None
|
||||
if any(e["merkmal"] == PERSONALISIERUNG_MERKMAL and e.get("aktiv") for e in merkmale):
|
||||
einladungen = einladungs_zeilen([d for d in devices if d["id"] in mitglied_ids])
|
||||
|
||||
return templates.TemplateResponse(
|
||||
request,
|
||||
"gruppe_detail.html",
|
||||
@@ -124,6 +131,10 @@ def gruppe_detail(request: Request, gruppe_id: str, user=Depends(get_current_use
|
||||
"devices": devices,
|
||||
"mitglied_ids": mitglied_ids,
|
||||
"optionen_felder": OPTIONEN_FELDER,
|
||||
"einladungen": einladungen,
|
||||
"email_domaenen": email_domaenen(user.organization_id) if einladungen is not None else [],
|
||||
"personalisierung_status": PERSONALISIERUNG_STATUS,
|
||||
"einladung_zurueck": f"/gruppen/{gruppe_id}",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
+26
-1
@@ -4,6 +4,7 @@ from urllib.parse import urlencode
|
||||
|
||||
from anode_client import anode_request
|
||||
from auth import get_current_user
|
||||
from einladungen import PERSONALISIERUNG_MERKMAL, PERSONALISIERUNG_STATUS, einladungs_zeilen, email_domaenen
|
||||
from optionen_felder import OPTIONEN_FELDER, parse_optionen_form
|
||||
from routers.workspace import creator_frei, zuweisbare_workspaces
|
||||
from templating import templates
|
||||
@@ -174,10 +175,34 @@ def oe_merkmale_ansicht(request: Request, oe_id: str, user=Depends(get_current_u
|
||||
kategorie_name = eintrag["kategorie"]["name"] if eintrag["kategorie"] else "Ohne Kategorie"
|
||||
kategorien.setdefault(kategorie_name, []).append(eintrag)
|
||||
|
||||
# Schritt 7 (25.10.2026): wie bei Gruppen, für die Geräte hier und
|
||||
# in den Standorten/Zwecken darunter.
|
||||
einladungen = None
|
||||
if any(e["merkmal"] == PERSONALISIERUNG_MERKMAL and e.get("aktiv") for e in merkmale):
|
||||
oes = fetch_organisationseinheiten(user.organization_id)
|
||||
unten = {oe_id}
|
||||
while True:
|
||||
neu = {o["id"] for o in oes if o.get("parent_id") in unten} - unten
|
||||
if not neu:
|
||||
break
|
||||
unten |= neu
|
||||
devices_result = anode_request("GET", f"/api/v1/organizations/{user.organization_id}/devices")
|
||||
devices = devices_result.get("devices", []) if devices_result.get("success") else []
|
||||
einladungen = einladungs_zeilen([d for d in devices if d.get("oe_id") in unten])
|
||||
|
||||
return templates.TemplateResponse(
|
||||
request,
|
||||
"struktur_merkmale.html",
|
||||
{"user": user, "oe": oe, "kategorien": kategorien, "optionen_felder": OPTIONEN_FELDER},
|
||||
{
|
||||
"user": user,
|
||||
"oe": oe,
|
||||
"kategorien": kategorien,
|
||||
"optionen_felder": OPTIONEN_FELDER,
|
||||
"einladungen": einladungen,
|
||||
"email_domaenen": email_domaenen(user.organization_id) if einladungen is not None else [],
|
||||
"personalisierung_status": PERSONALISIERUNG_STATUS,
|
||||
"einladung_zurueck": f"/struktur/{oe_id}/merkmale",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
{# Formular „Einladung senden“ für ein Gerät. Erwartet: einladung_device_id,
|
||||
email_domaenen, einladung_konten (bool), einladung_laufend (bool),
|
||||
optional einladung_zurueck (Seite, auf die es danach zurückgeht). #}
|
||||
<form method="post" action="/geraete/{{ einladung_device_id }}/personalisierung/einladung">
|
||||
{% if einladung_zurueck %}<input type="hidden" name="zurueck" value="{{ einladung_zurueck }}">{% endif %}
|
||||
{% if email_domaenen %}
|
||||
<fieldset role="group">
|
||||
<input type="text" name="email_lokal" required autocomplete="off" placeholder="vorname.nachname" aria-label="E-Mail-Adresse">
|
||||
{% if email_domaenen | length == 1 %}
|
||||
<input type="hidden" name="email_domain" value="{{ email_domaenen[0] }}">
|
||||
<input type="text" value="@{{ email_domaenen[0] }}" readonly tabindex="-1" aria-label="Domäne">
|
||||
{% else %}
|
||||
<select name="email_domain" aria-label="Domäne">
|
||||
{% for d in email_domaenen %}<option value="{{ d }}">@{{ d }}</option>{% endfor %}
|
||||
</select>
|
||||
{% endif %}
|
||||
{% if einladung_zurueck %}<button type="submit" class="outline">Einladung senden</button>{% endif %}
|
||||
</fieldset>
|
||||
{% elif einladung_zurueck %}
|
||||
<fieldset role="group">
|
||||
<input type="email" name="email" required autocomplete="off" placeholder="E-Mail-Adresse" aria-label="E-Mail-Adresse">
|
||||
<button type="submit" class="outline">Einladung senden</button>
|
||||
</fieldset>
|
||||
{% else %}
|
||||
<input type="email" name="email" required autocomplete="off" aria-label="E-Mail-Adresse">
|
||||
{% endif %}
|
||||
{% if einladung_konten and not einladung_zurueck %}
|
||||
<label>
|
||||
<input type="checkbox" name="bestehenden_behalten" value="1">
|
||||
Bestehenden Zugang behalten
|
||||
</label>
|
||||
<small>Ohne Haken wird das bisherige Konto sofort gesperrt und samt seinen Dateien entfernt, sobald das neue eingerichtet ist.</small>
|
||||
{% endif %}
|
||||
{% if not einladung_zurueck %}
|
||||
<button type="submit" class="outline">
|
||||
{% if einladung_laufend %}Neue Einladung senden (ersetzt die laufende){% elif einladung_konten %}Erneut personalisieren{% else %}Einladung senden{% endif %}
|
||||
</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
@@ -0,0 +1,39 @@
|
||||
{# Einladungen für alle Geräte einer Gruppe bzw. eines Standorts/Zwecks mit
|
||||
aktivem „Personalisierung möglich“. Erwartet: einladungen, email_domaenen,
|
||||
einladung_zurueck. #}
|
||||
<section id="einladungen">
|
||||
<h3>Personalisierung: Einladungen</h3>
|
||||
{% if request.query_params.get("einladung_ok") %}<p><ins>Einladung an {{ request.query_params.get("einladung_ok") }} verschickt. Der Link gilt 7 Tage.</ins></p>{% endif %}
|
||||
{% if request.query_params.get("einladung_fehler") %}<p><mark>{{ request.query_params.get("einladung_fehler") }}</mark></p>{% endif %}
|
||||
<p><small>Trage bei jedem Gerät die E-Mail-Adresse der Person ein, die es bekommt. Sie erhält einen Link, wählt Anmeldenamen und Passwort und startet das Gerät neu. Bestehende Konten ersetzen, sperren oder zurücksetzen geht auf der Seite des Geräts.</small></p>
|
||||
{% if not einladungen %}
|
||||
<p>Hier sind noch keine Geräte.</p>
|
||||
{% else %}
|
||||
<figure>
|
||||
<table>
|
||||
<thead><tr><th>Gerät</th><th>Stand</th><th>Einladen</th></tr></thead>
|
||||
<tbody>
|
||||
{% for z in einladungen %}
|
||||
<tr>
|
||||
<td><a href="/geraete/{{ z.device.id }}#personalisierung">{{ z.device.hostname or z.device.device_fingerprint }}</a></td>
|
||||
<td>
|
||||
{% if z.konten %}{% for k in z.konten %}<strong>{{ k.anmeldename }}</strong> ({{ k.email }}){% if not loop.last %}, {% endif %}{% endfor %}
|
||||
{% elif z.laufend %}<small>{{ personalisierung_status[z.laufend.status] }} – {{ z.laufend.email }}</small>
|
||||
{% else %}<small>noch niemand</small>{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if z.konten or z.laufend %}
|
||||
<a href="/geraete/{{ z.device.id }}#personalisierung"><small>auf der Geräteseite</small></a>
|
||||
{% else %}
|
||||
{% with einladung_device_id=z.device.id, einladung_konten=false, einladung_laufend=false %}
|
||||
{% include "_einladung_formular.html" %}
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
{% endif %}
|
||||
</section>
|
||||
@@ -53,8 +53,7 @@
|
||||
<p><small>
|
||||
Trage die E-Mail-Adresse der Person ein, die dieses Gerät bekommt. Sie
|
||||
erhält einen Link, wählt darüber Anmeldenamen und Passwort und startet
|
||||
das Gerät danach neu; nach bis zu 2 Minuten kann sie sich anmelden. Das
|
||||
Gerät braucht dafür das Merkmal „Personalisierung möglich“.
|
||||
das Gerät danach neu; nach bis zu 2 Minuten kann sie sich anmelden.
|
||||
</small></p>
|
||||
|
||||
{% if einladung and einladung.gesendet_an %}
|
||||
@@ -130,38 +129,16 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if personalisierung_moeglich %}
|
||||
<form method="post" action="/geraete/{{ device.id }}/personalisierung/einladung">
|
||||
{% if email_domaenen %}
|
||||
<label for="email_lokal">E-Mail-Adresse</label>
|
||||
<fieldset role="group">
|
||||
<input type="text" id="email_lokal" name="email_lokal" required autocomplete="off" placeholder="vorname.nachname">
|
||||
{% if email_domaenen | length == 1 %}
|
||||
<input type="hidden" name="email_domain" value="{{ email_domaenen[0] }}">
|
||||
<input type="text" value="@{{ email_domaenen[0] }}" readonly tabindex="-1" aria-label="Domäne">
|
||||
{% else %}
|
||||
<select name="email_domain" aria-label="Domäne">
|
||||
{% for d in email_domaenen %}<option value="{{ d }}">@{{ d }}</option>{% endfor %}
|
||||
</select>
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
{% else %}
|
||||
<label for="email">E-Mail-Adresse</label>
|
||||
<input type="email" id="email" name="email" required autocomplete="off">
|
||||
{% endif %}
|
||||
{% if personalisierung_konten %}
|
||||
<label>
|
||||
<input type="checkbox" name="bestehenden_behalten" value="1">
|
||||
Bestehenden Zugang behalten
|
||||
</label>
|
||||
<small>Ohne Haken wird das bisherige Konto sofort gesperrt und samt seinen Dateien entfernt, sobald das neue eingerichtet ist.</small>
|
||||
{% endif %}
|
||||
<button type="submit" class="outline">
|
||||
{% if personalisierung_laufend %}Neue Einladung senden (ersetzt die laufende){% elif personalisierung_konten %}Erneut personalisieren{% else %}Einladung senden{% endif %}
|
||||
</button>
|
||||
</form>
|
||||
{% if not personalisierung_verfuegbar %}
|
||||
<p><small>Für dieses Gerät gibt es keine Personalisierung (etwa bei Ubuntu-Workspaces).</small></p>
|
||||
{% else %}
|
||||
<p><small>Für dieses Gerät ist „Personalisierung möglich“ nicht gesetzt (Workspace, Standort/Zweck oder Gruppe).</small></p>
|
||||
{% if not personalisierung_moeglich %}
|
||||
<p><small>Mit dem Senden kommt das Gerät in die Gruppe „Personalisierte Geräte“; damit ist „Personalisierung möglich“ für das Gerät gesetzt.</small></p>
|
||||
{% endif %}
|
||||
<label>E-Mail-Adresse</label>
|
||||
{% with einladung_device_id=device.id, einladung_konten=personalisierung_konten | length > 0, einladung_laufend=personalisierung_laufend is not none, einladung_zurueck=none %}
|
||||
{% include "_einladung_formular.html" %}
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
|
||||
{% if personalisierungen %}
|
||||
|
||||
@@ -75,6 +75,10 @@
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
{% if einladungen is not none %}
|
||||
{% include "_einladungen_liste.html" %}
|
||||
{% endif %}
|
||||
|
||||
<section>
|
||||
<h3>Geräte in dieser Gruppe</h3>
|
||||
{% if not devices %}
|
||||
|
||||
@@ -71,4 +71,8 @@
|
||||
</details>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if einladungen is not none %}
|
||||
{% include "_einladungen_liste.html" %}
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
Reference in new issue
Block a user