diff --git a/server/pruef-neue-seiten.mjs b/server/pruef-neue-seiten.mjs index 4fc3c5e5..57cb1c81 100644 --- a/server/pruef-neue-seiten.mjs +++ b/server/pruef-neue-seiten.mjs @@ -533,6 +533,29 @@ melde("=== Stand das Neue auch wirklich da? ==="); ok(t.split("Ins Team holen").length - 1 === 1, `der Zugangsknopf ebenso (${t.split("Ins Team holen").length - 1})`); + /* ===== DER ROLLENNAME KOMMT ALS DATEN, NICHT AUS DEM SKRIPT ======== + + Seit dem 16.09.2026 steht der Name im Regeltext des Treffs nicht + mehr fest im Browser -- er kommt mit der Antwort des Servers. Der + Grund: Jede Datei unter workspace/ ist ohne Anmeldung abrufbar + (nachgemessen: `talente.js` liefert 200 und 25 KB Quelltext). + + DAS BRAUCHT EINE PRUEFUNG, und zwar genau hier. Im HTML steht als + Ersatzfassung „jemandem aus dem Team" -- ein Satz, der auch ohne + Skript stimmt. Kommt der Name NICHT an, bleibt diese Fassung + stehen, die Seite sieht vollkommen richtig aus, und niemand merkt + es. Ein Platzhalter „…" waere aufgefallen; eine richtige Ersatz- + fassung faellt nicht auf. Deshalb wird nachgesehen, ob wirklich + der Name dasteht. */ + const regeln = gesehen.regeln || ""; + ok(regeln.length > 400, `Regeln: die Seite ist gefüllt (${regeln.length} Zeichen)`); + ok(/sag es einem \S+/.test(regeln), + "Regeln: der Rollenname ist im Kontaktsatz angekommen"); + ok(!/jemandem aus dem Team/.test(regeln), + "die Ersatzfassung steht NICHT mehr da — sie wurde wirklich ersetzt"); + ok(/Pause von bis zu \d+ Tagen/.test(regeln), + "und die Frist steht als Zahl darin, nicht als Platzhalter"); + const hand = gesehen["entwicklung-hand"] || ""; const dogi = gesehen.entwicklung || ""; /* WENN ES SCHIEFGEHT, SOLL DIE MELDUNG SAGEN, WAS STATTDESSEN DA WAR. diff --git a/server/pruef-uebergang.mjs b/server/pruef-uebergang.mjs index 6bc17383..1264198d 100644 --- a/server/pruef-uebergang.mjs +++ b/server/pruef-uebergang.mjs @@ -410,6 +410,51 @@ melde("=== 8. Im Browser ==="); jemanden bestimmt, ohne ihn gefragt zu haben. */ ok(frage.vorbelegt === "", "und keiner ist vorausgewählt — hier muss jemand wirklich wählen"); + /* ===== DER ROLLENNAME STEHT NICHT MEHR IM SKRIPT (16.09.2026) ===== + + `talente.js` hatte `rolle: 'modi'` als festen Text -- und der Server + liefert jede Datei unter workspace/ an jeden aus, der die Adresse + kennt. Nachgemessen am 16.09. auf der echten Seite: ohne jede + Anmeldung 200 und 25 KB Quelltext. + + Jetzt fragt die Seite den Namen ab. Das ist eine Codeänderung an + der Stelle, an der aus einem Talent ein Mensch mit Zugang wird -- + der letzte Schritt des Trichters, und der einzige, den man nicht + zurücknehmen kann. Also wird er gemessen, nicht angenommen. */ + const wissen = await seite.evaluate(async () => { + const a = await fetch("/workspace/api/talente/lage", { credentials: "same-origin" }); + const l = await a.json(); + const quelle = await (await fetch("/workspace/assets/js/talente.js")).text(); + return { + rolle: l?.zugang?.rolle || "", + name: l?.zugang?.name || "", + imSkript: /rolle:\s*'modi'/.test(quelle), + }; + }); + ok(!!wissen.rolle && !!wissen.name, + `der Server sagt der Seite, welcher Zugang entsteht (${wissen.rolle} / ${wissen.name})`); + ok(wissen.imSkript === false, + "und im ausgelieferten Skript steht der Name nicht mehr"); + + /* UND DER KNOPF MUSS TROTZDEM DA SEIN. Der Wert wird jetzt erst beim + Zeichnen gerechnet -- mein erster Entwurf rechnete ihn zu früh, + und dann wäre er dauerhaft falsch gewesen und der Knopf nie + erschienen. Gefunden hat das die Reihenfolge im Quelltext, nicht + der Testlauf; deshalb steht die Frage ab jetzt hier. */ + await seite.goto(`http://127.0.0.1:${PORT}/workspace/talente.html`, { waitUntil: "networkidle" }); + await seite.waitForTimeout(900); + const knopf = await seite.evaluate(() => { + const k = [...document.querySelectorAll(".t-karte")] + .find((x) => x.dataset.stufe === "probe"); + return { + karte: !!k, + text: k?.querySelector(".t-schritt__knopf")?.textContent.trim() || "", + }; + }); + ok(knopf.karte, "eine Karte auf der Probe steht auf der Seite"); + ok(/Zugang anlegen/.test(knopf.text), + `und DogFather sieht den Knopf dafür („${knopf.text}“)`); + ok(konsole.length === 0, konsole.length ? "Fehler im Browser: " + konsole[0].slice(0, 90) : "keine Fehler im Browser"); await browser.close(); diff --git a/server/workspace-entwicklung.js b/server/workspace-entwicklung.js index af54d703..a89018ad 100644 --- a/server/workspace-entwicklung.js +++ b/server/workspace-entwicklung.js @@ -102,6 +102,27 @@ const KARTEN_ROLLEN = ["hand", "modi"]; * begleiten, die er selbst nicht macht. */ const BUDDY_ROLLEN = ["admin", "hand", "modi"]; +/** Welchen Zugang der letzte Schritt des Trichters anlegt. + * + * ER STEHT HIER UND NICHT IM BROWSER (16.09.2026). Die Talente-Seite + * hatte `rolle: 'modi'` als festen Text im Skript -- und jede Datei + * unter `workspace/` liefert der Server an JEDEN aus, der die Adresse + * kennt. Nachgemessen am 16.09.: `talente.js` kommt ohne jede + * Anmeldung mit 200 aus dem offenen Netz, 25 KB Quelltext, der + * Rollenname mittendrin. + * + * Damit stand der verborgene Zugang in einer Datei, die man nicht + * einmal geschenkt bekommen muss -- man kann sie abrufen. Die + * Rollentabelle nebenan sagt seit dem 11.09. genau das Richtige dazu: + * „Der Name steht hier und NICHT in einer Datei, die jeder + * herunterlaedt -- ausgeliefert wird er nur an den, der ihn selbst + * traegt, und an die, die ihn sehen duerfen." + * + * Die Seite fragt ihn deshalb ab, statt ihn zu kennen. Die Antwort + * geht ueber `nurLeitung` -- also nur an DogFather und die rechte + * Hand. */ +const ZUGANG_ROLLE = "modi"; + /* ===================================================================== DIE TABELLEN ===================================================================== */ @@ -898,6 +919,10 @@ entwicklungRouter.get("/workspace/api/talente/lage", (req, res) => { ORDER BY ${ROLLEN_SORTIERUNG}, name COLLATE NOCASE`).all(...BUDDY_ROLLEN), erster_tag: ERSTER_TAG, schicht_weit_tage: SCHICHT_WEIT_TAGE, + /* WELCHEN ZUGANG DER LETZTE SCHRITT ANLEGT -- Schluessel und + Wort. Beides kommt von hier, damit im Browser kein fester Text + dazu steht; siehe ZUGANG_ROLLE oben. */ + zugang: { rolle: ZUGANG_ROLLE, name: ROLLEN_NAME[ZUGANG_ROLLE] ?? ZUGANG_ROLLE }, kandidaten: karten, /* Wie viele auf jeder Stufe liegen -- der Trichter als Zahlen. Abgeleitet, damit er nicht danebenliegen kann. */ diff --git a/server/workspace-treff.js b/server/workspace-treff.js index 67981996..c5a0d495 100644 --- a/server/workspace-treff.js +++ b/server/workspace-treff.js @@ -439,7 +439,25 @@ treffRouter.get("/workspace/api/treff/regeln", (req, res) => { stamm_beitraege: STAMM_BEITRAEGE, anschlag_max: ANSCHLAG_MAX, grund_min: GRUND_MIN, - modi_frist_tage: MODI_FRIST_TAGE, + /* DER NAME KOMMT ALS DATEN MIT (16.09.2026). + + Filipe hat am 11.09. ausdruecklich gewaehlt, dass im Treff der + volle Rollenname neben einem Beitrag steht. Die Regel dazu steht + an ROLLEN_NAME in workspace.js: Der Name wird AUSGELIEFERT, aber + er steht nicht als fester Text in einer Datei, die jeder abrufen + kann -- und abrufen kann sie wirklich jeder, nachgemessen am + 16.09.: `talente.js` kommt ohne Anmeldung mit 200 aus dem Netz. + + Im Regeltext des Treffs stand er trotzdem dreimal fest im + Skript. Jetzt kommt er von hier. + + UND DER SCHLUESSEL HIESS SELBST SO: `modi_frist_tage`. Das + Wortleck-Muster ist `modi` und trifft das nicht, weil danach + ein Unterstrich folgt -- ein Leck durch eine Luecke im Muster, + nicht durch eine Entscheidung. Deshalb heisst das Feld jetzt + nach dem, was es bedeutet. */ + rolle_name: ROLLEN_NAME.modi, + pause_frist_tage: MODI_FRIST_TAGE, stufen: STUFEN.map((s) => ({ schluessel: s, name: STUFEN_NAME[s] })), /* Welche Bretter erst nach Freigabe erscheinen — auch das gehoert in den Regeltext, und auch das soll nicht abschreibbar sein. */ diff --git a/workspace/app.webmanifest b/workspace/app.webmanifest index 7d8274e8..3ffb87b1 100644 --- a/workspace/app.webmanifest +++ b/workspace/app.webmanifest @@ -13,25 +13,25 @@ "theme_color": "#06090f", "icons": [ { - "src": "/assets/img/app-symbole/workspace-192.png?v=202609160146", + "src": "/assets/img/app-symbole/workspace-192.png?v=202609161659", "sizes": "192x192", "type": "image/png", "purpose": "any" }, { - "src": "/assets/img/app-symbole/workspace-512.png?v=202609160146", + "src": "/assets/img/app-symbole/workspace-512.png?v=202609161659", "sizes": "512x512", "type": "image/png", "purpose": "any" }, { - "src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609160146", + "src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609161659", "sizes": "192x192", "type": "image/png", "purpose": "maskable" }, { - "src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609160146", + "src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609161659", "sizes": "512x512", "type": "image/png", "purpose": "maskable" diff --git a/workspace/assets/css/entwicklung.css b/workspace/assets/css/entwicklung.css index ddac18f6..ed293405 100644 --- a/workspace/assets/css/entwicklung.css +++ b/workspace/assets/css/entwicklung.css @@ -94,7 +94,7 @@ Bis heute hatte sie keine: Name, Rolle und der Weg zum Notizbrett standen direkt auf der Bühne — also auf einem Foto. Im Bildschirmfoto - lag „Frida · Modi · 2 von 28 angesehen" quer über dem Spicy-Media- + lag die Kopfzeile der Karte quer über dem Spicy-Media- Schriftzug, und der Knopf darunter über einer Chilischote. Das ist genau der Fall, für den die Kontrastmessung ihren dritten diff --git a/workspace/assets/css/gate.css b/workspace/assets/css/gate.css index b179f587..a8c460e4 100644 --- a/workspace/assets/css/gate.css +++ b/workspace/assets/css/gate.css @@ -831,7 +831,7 @@ body.oeffnet .buehne__bild { filter: saturate(1) brightness(1); transition: filt /* .72rem = 11,52 px. Vorher .68rem = 10,88 px und damit unter der Hausuntergrenze von 11,5 px -- gefunden von pruef-handy auf der Zugangswand von Team Dogi ("DogFather", "Team Dogi"). Das ist die - erste Seite, die ein neuer Modi ueberhaupt sieht, und sie wird mit + erste Seite, die jemand aus dem Team ueberhaupt sieht, und sie wird mit dem Daumen bedient. Dieselbe Hebung wie seinerzeit in heim.css. */ .marke { font-size: .72rem; margin-bottom: 3px; } diff --git a/workspace/assets/js/bereich.js b/workspace/assets/js/bereich.js index 5b59ca0d..9cd3eae8 100644 --- a/workspace/assets/js/bereich.js +++ b/workspace/assets/js/bereich.js @@ -255,7 +255,7 @@ WARUM DAS UEBERHAUPT DRANSTEHT: In einem Raum mit Zuschauern und Moderation muss man sehen, wer spricht. "Frida" und "Frida · - Modi" sind zwei verschiedene Saetze -- der zweite ist eine + jemand aus dem Team" sind zwei verschiedene Saetze -- der zweite ist eine Ansage, der erste eine Meinung. BEI MITGLIEDERN STEHT NICHTS DA. "Kessi · Community" waere eine diff --git a/workspace/assets/js/entwicklung.js b/workspace/assets/js/entwicklung.js index 0630ecb0..37f76d03 100644 --- a/workspace/assets/js/entwicklung.js +++ b/workspace/assets/js/entwicklung.js @@ -150,7 +150,7 @@ const ziel = $('ampel'); let a; try { a = await hole('/workspace/api/entwicklung/ampel'); } catch { return; } - if (!a.ok) return; // für einen Modi gibt es sie nicht + if (!a.ok) return; // nicht jede Rolle hat sie const r = await a.json(); ziel.textContent = ''; ziel.hidden = false; @@ -661,7 +661,7 @@ for (const p of lage.personen) ziel.append(personKarte(p, lage)); await ampel(); } else { - /* Für einen Modi ist DIESE Seite seine eigene Karte — das, + /* Für die Beurteilten ist DIESE Seite die eigene Karte — das, was die Leitung über ihn notiert hat, und was er selbst dazu sagt. Nicht „Entwicklung" als Überschrift: Das liest sich wie eine Beurteilung, und darum geht es hier nicht. diff --git a/workspace/assets/js/talente.js b/workspace/assets/js/talente.js index eee17288..07957b3a 100644 --- a/workspace/assets/js/talente.js +++ b/workspace/assets/js/talente.js @@ -55,7 +55,14 @@ }; let lage = null; - let darfAnlegen = false; // darf ich hier einen Modi-Zugang anlegen? + /* WAS ICH ANLEGEN DARF -- die rohe Liste vom Server. + Ausgewertet wird sie erst beim Zeichnen: Welcher Zugang hier + entsteht, sagt `lage`, und die wird NACH dieser Liste geholt. + Ein Wert, der zu frueh gerechnet wird, ist dauerhaft falsch -- + der Knopf waere nie erschienen. */ + let darfListe = null; + const darfAnlegen = () => Array.isArray(darfListe) + && !!lage?.zugang?.rolle && darfListe.includes(lage.zugang.rolle); const offen = new Set(); // welche Karten aufgeklappt sind /* ---- Der Trichter oben ---------------------------------------------- */ @@ -109,10 +116,10 @@ Aus einem Namen wird ein Mensch mit einem Zugang. Deshalb haengt genau daran das Anlegen selbst -- und nicht ein Satz, den man danach von Hand nachbaut. Bis heute stand hier "Zugang in - Personen & Zugaenge anlegen, Rolle Modi", und wer das vergass, + Personen & Zugaenge anlegen", und wer das vergass, hatte eine Karte auf "Im Team" und keinen Menschen darin. */ const zugangNoetig = k.naechste === 'imteam'; - if (k.knopf && k.naechste && (!zugangNoetig || darfAnlegen)) { + if (k.knopf && k.naechste && (!zugangNoetig || darfAnlegen())) { const b = el('button', 'schritt t-schritt__knopf', zugangNoetig ? 'Ins Team holen · Zugang anlegen' : k.knopf); b.type = 'button'; @@ -297,7 +304,7 @@ @returns {object|null} die angelegte Person, oder null bei Abbruch. */ async function zugangAnlegen(k) { - if (!darfAnlegen) { melde('Zugänge legt DogFather an.'); return null; } + if (!darfAnlegen()) { melde('Zugänge legt DogFather an.'); return null; } const vorschlag = String(k.titel || '').split(/[\s,(–-]+/)[0] || ''; const name = prompt( @@ -314,7 +321,11 @@ credentials: 'same-origin', method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ name: String(name).trim(), rolle: 'modi' }), + /* WELCHE ROLLE, SAGT DER SERVER (16.09.2026). Hier stand ein + fester Text -- und diese Datei liefert der Server an jeden + aus, der die Adresse kennt. Nachgemessen: ohne Anmeldung + 200, 25 KB Quelltext. */ + body: JSON.stringify({ name: String(name).trim(), rolle: lage.zugang.rolle }), }); } catch { melde('Keine Verbindung – der Zugang wurde nicht angelegt.'); return null; } @@ -336,7 +347,7 @@ einer Meldung, die von selbst verschwindet. Er ist das Einzige auf dieser Seite, das es nur ein einziges Mal gibt. */ function codeZeigen(neu) { - $('code-name').textContent = `${neu.name} · Modi`; + $('code-name').textContent = `${neu.name} · ${lage.zugang.name}`; $('code-wert').textContent = neu.code; $('code-kasten').hidden = false; $('code-kasten').scrollIntoView({ behavior: 'smooth', block: 'center' }); @@ -590,7 +601,7 @@ if (!a.ok) return; const ich = await a.json(); window.werZeigen?.(ich); - darfAnlegen = Array.isArray(ich?.darf_anlegen) && ich.darf_anlegen.includes('modi'); + darfListe = ich?.darf_anlegen; } catch { /* umgeleitet */ } } diff --git a/workspace/assets/js/treff-moderation.js b/workspace/assets/js/treff-moderation.js index 12bdf6a2..cbe8fdd6 100644 --- a/workspace/assets/js/treff-moderation.js +++ b/workspace/assets/js/treff-moderation.js @@ -2,7 +2,7 @@ Meldungen & Maßnahmen im Treff (11.09.2026) Drei Listen und ein Knopf. Die Regeln stehen alle auf dem Server — - wer eine Maßnahme verhängen darf, wie lange ein Modi sperren kann, + wer eine Maßnahme verhängen darf, wie lange das Team sperren kann, ob eine Meldung schon entschieden ist. Diese Datei zeichnet nur und fragt nach. @@ -293,8 +293,8 @@ if (!a.ok) return; const r = await a.json(); $('leiter').textContent = - `Hinweis, dann Pause, dann Ausschluss – in dieser Reihenfolge. Ein Modi kann ` - + `bis zu ${r.modi_frist_tage} Tage geben; alles darüber und jeder dauerhafte ` + `Hinweis, dann Pause, dann Ausschluss – in dieser Reihenfolge. Ein ${r.rolle_name} kann ` + + `bis zu ${r.pause_frist_tage} Tage geben; alles darüber und jeder dauerhafte ` + `Ausschluss ist DogFathers Entscheidung. Wer eine dauerhafte Maßnahme nicht ` + `verhängen darf, kann sie auch nicht aufheben.`; } catch { /* dann bleibt "…" stehen, und das sieht man */ } diff --git a/workspace/assets/js/treff-regeln.js b/workspace/assets/js/treff-regeln.js index 556a3881..819d57b6 100644 --- a/workspace/assets/js/treff-regeln.js +++ b/workspace/assets/js/treff-regeln.js @@ -3,7 +3,7 @@ Dieses Skript schreibt nichts dazu -- es setzt nur die Zahlen ein, die im Text stehen muessen: Fristen, Stufen, Mindestalter, wie lange - ein Modi eine Pause geben darf. + jemand aus dem Team eine Pause geben darf. WARUM UEBERHAUPT EIN SKRIPT FUER FUENF ZAHLEN @@ -71,9 +71,19 @@ + 'der Treff zu einem Stream für Erwachsene gehört.'; } + /* WEN MAN ANSPRICHT -- mit dem Namen aus den Daten. + + Im HTML steht „jemandem aus dem Team". Das ist kein Platzhalter, + sondern eine Fassung, die auch dann stimmt, wenn das Skript nicht + laeuft -- ein Satz mit einem sichtbaren Loch darin waere + schlechter als einer, der etwas allgemeiner ist. */ + if ($('kontakt-wem') && r.rolle_name) { + $('kontakt-wem').textContent = `einem ${r.rolle_name}`; + } + if ($('ausschluss')) { $('ausschluss').textContent = - `Ein Modi kann eine Pause von bis zu ${r.modi_frist_tage} Tagen geben. ` + `Ein ${r.rolle_name} kann eine Pause von bis zu ${r.pause_frist_tage} Tagen geben. ` + 'Alles, was länger gilt, und jeder endgültige Ausschluss ist DogFathers ' + 'Entscheidung. Jede dieser Entscheidungen wird mit Grund festgehalten – ' + 'auch, damit man sie zurücknehmen kann.'; diff --git a/workspace/aufgaben.html b/workspace/aufgaben.html index 43260a6f..a2d0059f 100644 --- a/workspace/aufgaben.html +++ b/workspace/aufgaben.html @@ -12,19 +12,19 @@ Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite eine Flaeche sind statt zweier. --> - - - - - - + + + + + + - +
@@ -282,10 +282,10 @@ - - - - - + + + + +