diff --git a/server/pruef-css-klassen.mjs b/server/pruef-css-klassen.mjs index 173b8304..ae7c5fa3 100644 --- a/server/pruef-css-klassen.mjs +++ b/server/pruef-css-klassen.mjs @@ -426,6 +426,65 @@ console.log("\n=== Gegenprobe ==="); "aus classList.add(bedingung ? 'a' : 'b') werden beide Klassen gelesen, aber nicht die Variable"); } +/* ======================================================================= + KEIN SKRIPT BAUT SICH SEINE EIGENE ROLLENLISTE + + Am 07.09.2026 hat Filipe gemeldet, dass „Creator-Profile" bei Spicy + Media abbricht. Die Ursache war nicht diese Seite: In NEUN Skripten + stand `const LEITUNG = new Set(['admin', 'manager'])`, und in keinem + davon `spicy`. profil.js nahm deshalb den Creator-Zweig und lud das + eigene Profil -- ein Spicy-Zugang ist kein Creator, also 404. + + Acht der neun sind nicht kaputtgegangen. Sie haben sich still falsch + verhalten, und das ist der Grund für diese Prüfung: Die Liste steht + jetzt EINMAL in bereiche.js, und wer sich wieder eine eigene baut, + bekommt hier einen Fehler statt in vier Wochen eine Fehlermeldung + bei einer Rolle, an die niemand gedacht hat. + ======================================================================= */ +console.log("\n=== Rollenlisten: eine Quelle, nicht neun"); +{ + const jsOrdner = join(WS, "assets/js"); + const dateien = readdirSync(jsOrdner).filter((n) => n.endsWith(".js")); + ok(dateien.length >= 15, `${dateien.length} Workspace-Skripte durchgesehen`); + + /* Gesucht wird eine Rollenliste, die 'admin' enthält und in einer + Zuweisung steht -- also `new Set([...])` oder `= [...]`. Der + Kommentar in bereiche.js, der den alten Fehler ZITIERT, darf nicht + mitzählen: Sonst verdeckt ausgerechnet die Erklärung den Befund. */ + const eigenbau = []; + for (const n of dateien) { + const roh = readFileSync(join(jsOrdner, n), "utf8"); + const text = roh.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/\/\/[^\n]*/g, " "); + for (const m of text.matchAll(/(?:new Set\(\s*)?\[\s*((?:'[a-z]+'\s*,\s*)*'[a-z]+')\s*\]/g)) { + const rollen = m[1].split(",").map((x) => x.trim().replace(/'/g, "")); + if (!rollen.includes("admin") || rollen.length < 2) continue; + /* bereiche.js DARF: dort steht die eine Quelle. Und die Listen an + den Kacheln selbst sind etwas anderes -- sie sagen, wer eine + KACHEL sieht, nicht wer zur Leitung gehört. Erkennbar daran, + dass sie hinter `rollen:` stehen. */ + if (n === "bereiche.js") continue; + const davor = text.slice(Math.max(0, m.index - 40), m.index); + if (/rollen\s*:\s*$/.test(davor)) continue; + eigenbau.push(`${n}: [${rollen.join(", ")}]`); + } + } + ok(eigenbau.length === 0, eigenbau.length + ? `eigene Rollenliste gefunden: ${eigenbau.slice(0, 4).join(" · ")}` + : "kein Skript baut sich eine eigene Rollenliste"); + + /* GEGENPROBE: Der Sucher muss so eine Liste auch WIRKLICH finden -- + sonst wäre die Zeile darüber grün, weil das Muster nie greift. */ + const probe = " const LEITUNG = new Set(['admin', 'manager']);"; + const trifft = [...probe.matchAll(/(?:new Set\(\s*)?\[\s*((?:'[a-z]+'\s*,\s*)*'[a-z]+')\s*\]/g)] + .some((m) => m[1].includes("admin")); + ok(trifft, "und der Sucher erkennt so eine Liste auch wirklich"); + + /* Und die eine Quelle muss die Rolle enthalten, um die es ging. */ + const quelle = readFileSync(join(jsOrdner, "bereiche.js"), "utf8"); + ok(/LEITUNG\s*=\s*new Set\(\[[^\]]*'spicy'[^\]]*'admin'[^\]]*'manager'[^\]]*\]\)/.test(quelle), + "bereiche.js führt Spicy Media, DogFather und Manager als Leitung"); +} + /* ======================================================================= DIE SIEBEN KOPIEN DER MODULLISTE MÜSSEN GLEICH SEIN diff --git a/server/pruef-scout-zuteilung.mjs b/server/pruef-scout-zuteilung.mjs index 0c236d35..9ff3d203 100644 --- a/server/pruef-scout-zuteilung.mjs +++ b/server/pruef-scout-zuteilung.mjs @@ -368,20 +368,32 @@ await bDogi.kontext.close(); aufgeloest: "die manager sollen diese kategorien garnicht sehen." Seitdem fuehrt personen.html fuer ihn zurueck zur Startseite. - Die Pruefung wird deshalb nicht geloescht, sondern umgedreht -- eine - geloeschte Pruefung hinterlaesst keine Spur davon, dass hier einmal - etwas anderes galt. */ + AM 07.09.2026 EIN DRITTES MAL GEDREHT, und der Weg dorthin gehoert + in die Akte: Manager sollen "creator und auch wirklich nur creator + hinzufuegen koennen" -- dafuer muessen sie die Seite wieder oeffnen + duerfen. Die Seite geht seither auf; die VERWALTUNG dahinter bleibt + zu. + + Genau das ist jetzt die Aussage, und sie ist schaerfer als beide + vorherigen: Nicht "er kommt rein" und nicht "er kommt nicht rein", + sondern "er kommt rein UND sieht dort trotzdem keine einzige + Personenzeile". Die beiden Zeilen darunter standen schon vorher da + und waren die ganze Zeit die wichtigeren -- sie messen, dass er sich + keine Sichtbarkeit zuteilen kann. + + Die Pruefung wird bei jeder Drehung umgeschrieben, nie geloescht: + Eine geloeschte Pruefung hinterlaesst keine Spur davon, dass hier + einmal etwas anderes galt -- und der neue Weg waere ab da ungeprueft. */ const bMara = await seiteAls("manager", "CODE-MANA-0001"); const rM = await bMara.seite.evaluate(() => ({ wahlen: document.querySelectorAll('select[id^="manager-"]').length, personen: document.querySelectorAll(".person").length, wo: location.pathname, })); -ok(rM.wo.endsWith("/start.html"), - `ein Manager landet auf der Startseite statt in der Personenverwaltung ` - + `(${rM.wo.split("/").pop()})`); +ok(rM.wo.endsWith("/personen.html"), + `ein Manager kommt auf die Personenseite (${rM.wo.split("/").pop()})`); ok(rM.personen === 0, - `und sieht dort keine einzige Personenzeile (${rM.personen})`); + `sieht dort aber keine einzige Personenzeile (${rM.personen})`); ok(rM.wahlen === 0, `erst recht keine Zuteilungs-Auswahl (${rM.wahlen}) – er koennte sich sonst ` + "seine eigene Sichtbarkeit vergeben"); diff --git a/server/pruef-sicht.mjs b/server/pruef-sicht.mjs index 0c41047d..672b6daf 100644 --- a/server/pruef-sicht.mjs +++ b/server/pruef-sicht.mjs @@ -252,9 +252,10 @@ for (const [pfad, name, feld] of [ Verabredungen sieht er nicht mehr in seinem eigenen Kalender; dafuer gibt es den Umschalter. Genau dieser Wechsel wird hier gemessen: ohne Umschalter keins der beiden, mit Umschalter genau das eine. - Und ein Termin ohne Teilnehmer geht weiterhin alle an -- sonst - koennte die Regel auch einfach alles ausblenden und waere trotzdem - gruen. */ + Dass die Regel nicht einfach ALLES ausblendet, zeigt der Umschalter: + Als Patrick kommen beide seiner Eintraege an. Ohne diesen Nachweis + waere ein Kalender, der gar nichts mehr liefert, die sauberste + Pruefung von allen. */ { const d2 = new DatabaseSync(process.env.WORKSPACE_DB); const rein = d2.prepare( @@ -262,7 +263,12 @@ for (const [pfad, name, feld] of [ VALUES (?,?,?,?,?,?,?)`); rein.run("PATRICK-TERMIN", "call", heute + "T10:00", 30, idTili, jetzt, idPat); rein.run("BEN-TERMIN", "call", heute + "T12:00", 30, idLuna, jetzt, idBen); - rein.run("TEAM-TERMIN", "termin", heute + "T14:00", 30, null, jetzt, idPat); + /* Am 07.09.2026 nachgezogen: Ein Termin OHNE Teilnehmer ist kein + Team-Termin mehr, sondern der private Eintrag dessen, der ihn + angelegt hat -- Filipe hat es an seinem "Manager Meeting" in + Cigdems Kalender gesehen. Der hier gehoert also Patrick, und genau + das wird unten geprueft. */ + rein.run("PATRICKS NOTIZ", "termin", heute + "T14:00", 30, null, jetzt, idPat); d2.close(); const j = async (k, s) => { @@ -273,10 +279,11 @@ for (const [pfad, name, feld] of [ const patT = await j(kDogi, idPat); ok(!alleT.includes("PATRICK-TERMIN") && !alleT.includes("BEN-TERMIN"), `DogFather sieht fremde Verabredungen nicht mehr (${alleT.join(", ") || "nichts"})`); - ok(alleT.includes("TEAM-TERMIN"), - "den Termin ohne Teilnehmer aber schon -- der geht alle an"); - ok(patT.includes("PATRICK-TERMIN") && !patT.includes("BEN-TERMIN"), - `mit dem Umschalter auf Patrick genau dessen (${patT.join(", ")})`); + ok(!alleT.includes("PATRICKS NOTIZ"), + "auch einen Termin OHNE Teilnehmer nicht -- der gehoert dem, der ihn anlegt"); + ok(patT.includes("PATRICK-TERMIN") && patT.includes("PATRICKS NOTIZ") + && !patT.includes("BEN-TERMIN"), + `mit dem Umschalter auf Patrick genau dessen, beide (${patT.join(", ")})`); } /* ================================================================ diff --git a/server/pruef-spicy.mjs b/server/pruef-spicy.mjs index be1740a8..09b6053c 100644 --- a/server/pruef-spicy.mjs +++ b/server/pruef-spicy.mjs @@ -375,13 +375,13 @@ const ruf = async (art, weg, keks, koerper) => { /* Der Kalender folgt seit dem 07.09.2026 einer ANDEREN Regel als Bereiche und Aufgaben, und das ist Absicht (Begruendung steht bei termineSichtbar in workspace.js): Dort entscheidet die Rolle, hier - entscheidet allein die BETEILIGUNG. Wer niemanden eintraegt, meint - alle -- also sieht Spicy Media DogFathers teilnehmerlosen Termin - sehr wohl, Max' Verabredung mit Luna dagegen nicht. Genau - umgekehrt zu den beiden Bloecken darueber. Die zwei Zeilen stehen - hier zusammen, weil erst beide zeigen, dass wirklich die - Beteiligung zaehlt und nicht die Rolle -- eine allein waere auch - erfuellt, wenn schlicht alles oder gar nichts ankaeme. */ + entscheidet allein die BETEILIGUNG -- und zwar fuer jeden gleich. + Spicy Media sieht deshalb WEDER DogFathers teilnehmerlosen Termin + NOCH Max' Verabredung mit Luna. + + Weil hier zweimal "nicht" steht, braucht es die Gegenprobe + darunter zwingend: Sonst waere derselbe gruene Haken auch bei + einem Kalender zu haben, der gar nichts mehr liefert. */ await ruf("POST", "/workspace/api/termine", keksDogi, { titel: "DogFathers Team-Termin", art: "termin", beginn: heute + "T20:00", dauer_min: 30 }); await ruf("POST", "/workspace/api/termine", keksMax, @@ -389,10 +389,28 @@ const ruf = async (art, weg, keks, koerper) => { creator_id: idLuna }); const term = await ruf("GET", `/workspace/api/termine?von=${heute}&bis=${heute}`, keksSpicy); const tTitel = (term.daten?.termine || []).map((t) => t.titel); - ok(tTitel.includes("DogFathers Team-Termin"), - `der Termin ohne Teilnehmer geht alle an -- auch sie (${tTitel.join(", ") || "nichts"})`); + ok(!tTitel.includes("DogFathers Team-Termin"), + `auch seinen teilnehmerlosen Termin nicht (${tTitel.join(", ") || "nichts"})`); ok(!tTitel.includes("Max sein Termin"), - "Max' Verabredung mit Luna dagegen nicht -- die geht nur die beiden an"); + "und Max' Verabredung mit Luna auch nicht -- beide gehen sie nichts an"); + + /* GEGENPROBE ZUM KALENDER: Ein Termin, bei dem sie eingetragen IST, + kommt sehr wohl an. Erst diese Zeile macht die beiden "nicht" + darueber zu einer Aussage. */ + /* Ihre Nummer wird hier erfragt und nicht aus Block 2 mitgenommen -- + die steht dort in einem eigenen Gueltigkeitsbereich, und eine + Variable ueber drei Bloecke zu schleifen, nur um eine Zahl zu + haben, ist genau die Art Verbindung, die spaeter jemand loest. */ + const ichSpicy = await ruf("GET", "/workspace/api/ich", keksSpicy); + const nrSpicy = ichSpicy.daten?.id; + ok(Number.isInteger(nrSpicy) && nrSpicy > 0, `ihre Nummer ist lesbar (${nrSpicy})`); + const mitIhr = await ruf("POST", "/workspace/api/termine", keksDogi, + { titel: "Abstimmung mit Spicy", art: "termin", beginn: heute + "T22:00", + dauer_min: 30, teilnehmer: [nrSpicy] }); + ok(mitIhr.status === 201, `ein Termin MIT ihr angelegt (${mitIhr.status})`); + const term2 = await ruf("GET", `/workspace/api/termine?von=${heute}&bis=${heute}`, keksSpicy); + ok((term2.daten?.termine || []).some((t) => t.titel === "Abstimmung mit Spicy"), + "den sieht sie -- markiert werden ist der Weg, jemanden zu beteiligen"); /* GEGENPROBE: DogFather selbst sieht weiterhin BEIDE. Ohne sie bewiesen die Zeilen oben nur, dass irgendetwas gefiltert wird. */ diff --git a/server/pruef-teilnehmer.mjs b/server/pruef-teilnehmer.mjs index 7b69ef16..312ef6a4 100644 --- a/server/pruef-teilnehmer.mjs +++ b/server/pruef-teilnehmer.mjs @@ -303,14 +303,19 @@ console.log("\n=== Gegenprobe ==="); ok(await siehtTermin(K.luna, geheim), "jetzt sieht sie ihn — die Messung erkennt auch 'sichtbar'"); - /* Und der neue Fall selbst, damit er nicht ungeprüft bleibt: OHNE - Teilnehmer sieht ihn jeder. Ohne diese Zeile bliebe die Regel, die - den Umbau ausgelöst hat, ausgerechnet hier ungemessen. */ + /* UND DER FALL, DER SICH AM SELBEN TAG NOCH EINMAL GEDREHT HAT: + Ein Termin OHNE Teilnehmer ist nicht „für alle", sondern privat. + Hier stand zwischenzeitlich das Gegenteil -- Filipe hat an seinem + „Manager Meeting" in Cigdems Kalender gezeigt, warum das falsch + war. Die Teilnehmerliste ist jetzt die einzige Art, jemanden zu + beteiligen; deshalb gehört genau dieser Fall hierher. */ const c = await ruf("POST", "/workspace/api/termine", { - titel: "Geht alle an", art: "termin", beginn: heute + "T21:30", + titel: "Nur fuer mich", art: "termin", beginn: heute + "T21:30", }, K.dogi); - ok(await siehtTermin(K.luna, c.daten?.id), - "einen Termin ganz ohne Teilnehmer sieht dagegen jeder"); + ok(!(await siehtTermin(K.luna, c.daten?.id)), + "einen Termin ganz ohne Teilnehmer sieht nur, wer ihn angelegt hat"); + ok(await siehtTermin(K.dogi, c.daten?.id), + " – der nämlich schon, sonst wäre die Zeile darüber wertlos"); } console.log(`\n${fehler === 0 ? "ALLES IN ORDNUNG" : `${fehler} FEHLER`}`); diff --git a/server/workspace-aufgaben.js b/server/workspace-aufgaben.js index ac23d26b..e9fb151e 100644 --- a/server/workspace-aufgaben.js +++ b/server/workspace-aufgaben.js @@ -547,13 +547,20 @@ aufgabenRouter.patch("/workspace/api/aufgaben/:id", gleicheHerkunft, (req, res) unterscheiden. */ /** Wer darf abbrechen? DogFather, Manager, Scouts -- Creator nicht. */ -const DARF_ABBRECHEN = new Set(["admin", "manager", "scout"]); +/* Spicy Media steht hier seit dem 07.09.2026 mit drin: Die Rolle hat + "die gleichen rechte wie dogfather ausser Automationen, Personen nur + anlegen, keine privaten Daten". Abbrechen gehoert nicht zu den + Ausnahmen -- sie fehlte hier nur, weil diese Liste beim Anlegen der + Rolle uebersehen wurde. Gefunden hat es keine Ueberlegung, sondern + eine Pruefung, die nach eigenen Rollenlisten sucht + (pruef-css-klassen.mjs). */ +const DARF_ABBRECHEN = new Set(["spicy", "admin", "manager", "scout"]); const ABBRUCH_GRUND_MAX = 500; aufgabenRouter.post("/workspace/api/aufgaben/:id/abbrechen", gleicheHerkunft, (req, res) => { try { if (!DARF_ABBRECHEN.has(req.person.rolle)) { - return res.status(403).json({ fehler: "Nur DogFather, Manager und Scouts können Aufgaben abbrechen." }); + return res.status(403).json({ fehler: "Nur DogFather, Spicy Media, Manager und Scouts können Aufgaben abbrechen." }); } const id = Number(req.params.id); if (!Number.isInteger(id)) return res.status(400).json({ fehler: "ungueltig" }); diff --git a/server/workspace.js b/server/workspace.js index 1a6ecb57..3a5ab057 100644 --- a/server/workspace.js +++ b/server/workspace.js @@ -1683,7 +1683,13 @@ const GESCHUETZT = { Lesezeichen hat, waere weiterhin hineingekommen. Beides gehoert zusammen: bereiche.js zeigt sie nur noch DogFather, diese Zeile laesst nur ihn hinein. */ - "/workspace/leistung.html": ["spicy", "admin"], + /* NUR DogFather -- auch nicht Spicy Media (07.09.2026, zweite + Praezisierung). Beim Anlegen der Rolle stand hier ["spicy","admin"], + weil Spicy Media "dieselben Rechte ausser Automationen" bekommen + sollte. Filipe hat es an der laufenden Seite gesehen und + widersprochen: "bei der spicy rolle, die zahlen diese kategorie + nicht sehen." */ + "/workspace/leistung.html": ["admin"], /* NUR DogFather (02.09.2026). Die Rollenauswahl verspricht das seit jeher ("Manager -- dieselben Rechte, ausser der Personenverwaltung"), die Schranke hielt sich nur nicht daran. @@ -2370,11 +2376,14 @@ export function termineSichtbar(person, praefix = "t") { const p = praefix; /* ===================================================================== - JEDER SIEHT NUR SEINE EIGENEN TERMINE -- ODER DIE, DIE ALLE ANGEHEN - (07.09.2026) + JEDER SIEHT NUR SEINE EIGENEN TERMINE (07.09.2026) - Wunsch Filipe: "calls oder termine soll jeder nur sehen die er - selber macht oder jeden betrifft." + Wunsch Filipe, zuerst: "calls oder termine soll jeder nur sehen + die er selber macht oder jeden betrifft." Wenige Stunden spaeter, + nachdem er das Ergebnis gesehen hat, praeziser: "jeder soll und + darf im kalender immer nur seine eigenen eintraege nur sehen." + Die zweite Fassung gilt -- Begruendung unten beim entfallenen + Fall "geht alle an". DAS GILT AUCH FUER DOGFATHER, und das ist die eigentliche Aenderung. Bis hierher bekam er `1=1` -- er sah jeden Termin von @@ -2383,12 +2392,11 @@ export function termineSichtbar(person, praefix = "t") { eigenen untergehen. Ein Kalender, in dem alles steht, ist kein Kalender mehr. - "JEDEN BETRIFFT" braucht eine Definition, sonst ist es Auslegung. - Sie steht hier: ein Termin OHNE Gegenueber und OHNE Teilnehmerliste. - Wer einen Termin anlegt und niemanden eintraegt, meint alle -- das - ist die Team-Besprechung, die Schulung, der Feiertag. Sobald auch - nur EINE Person eingetragen ist, ist es eine Verabredung und geht - nur die Beteiligten etwas an. + "EIGEN" IST HIER GENAU DEFINIERT, sonst wird es Auslegung: Ich habe + den Termin angelegt (erstellt_von), er ist mir zugeordnet + (creator_id), ich bin das Gegenueber (teilnehmer_id) -- oder ich + stehe in der Teilnehmerliste. Sonst nichts. Kein Rollenrecht, keine + Betreuung, keine Ausnahme fuer teilnehmerlose Termine. Diese Regel ersetzt die Rollenfrage vollstaendig: Es gibt hier nichts mehr, was DogFather sieht und ein Creator nicht. Was ein @@ -2420,14 +2428,33 @@ export function termineSichtbar(person, praefix = "t") { + ` OR ${dabei})`; const werte = [person.id, person.id, person.id, person.id]; - /* "Geht alle an": kein Gegenueber, kein Teilnehmer. `NOT EXISTS` und - nicht `COUNT(*) = 0` -- der Server hoert beim ersten Treffer auf zu - suchen, statt jedes Mal die ganze Nebentabelle zu zaehlen. */ - const fuerAlle = `(${p}.creator_id IS NULL AND ${p}.teilnehmer_id IS NULL` - + ` AND NOT EXISTS (SELECT 1 FROM ${nebenTabelle.tabelle} ta` - + ` WHERE ta.${nebenTabelle.spalte} = ${p}.id))`; + /* HIER STAND EIN ZWEITER FALL, UND ER IST AM 07.09.2026 ENTFALLEN. - return { wo: `(${eigen} OR ${fuerAlle})`, werte }; + Er hiess "geht alle an": ein Termin ohne Gegenueber und ohne + Teilnehmer war fuer jeden sichtbar, nach der Ueberlegung "wer + niemanden eintraegt, meint alle". + + Die Ueberlegung war falsch, und Filipe hat es an einem echten Fall + gezeigt: In Cigdems Kalender stand sein "Manager Meeting" -- SEIN + Termin, den er fuer sich eingetragen hatte. Wer keinen Teilnehmer + eintraegt, meint eben meistens nicht "alle", sondern "mich". Und + die Auslegung lag nicht bei dem, der den Termin anlegt, sondern + hier im Code -- das ist die eigentliche Schwaeche gewesen. + + Filipe: "jeder soll und darf im kalender immer nur seine eigenen + eintraege nur sehen." + + Was alle angeht, geht sie jetzt an, weil jemand sie EINTRAEGT: + "bei calls und protocoll will ich dass jeder seine termine von + seinem kalender sieht und die wenn sie markiert werden im termin." + Die Teilnehmerliste ist damit die einzige Antwort auf die Frage, + wen ein Termin etwas angeht -- eine sichtbare Entscheidung im + Formular statt einer unsichtbaren Regel im Server. + + DIESELBE REGEL GILT FUER CALLS & PROTOKOLLE: Die Call-Liste ruft + genau diese Funktion auf (workspace-calls.js -> sichtbar). Beide + Seiten koennen deshalb gar nicht auseinanderlaufen. */ + return { wo: eigen, werte }; } /* ===================================================================== diff --git a/workspace/assets/js/aufgaben.js b/workspace/assets/js/aufgaben.js index f3e45da2..db1e9167 100644 --- a/workspace/assets/js/aufgaben.js +++ b/workspace/assets/js/aufgaben.js @@ -10,7 +10,10 @@ /* DogFather und Manager haben dieselben Rechte -- die Unterscheidung macht nur die Personenverwaltung, und die prueft der Server. */ - const LEITUNG = new Set(['admin', 'manager']); + /* Die Leitungsliste steht in bereiche.js -- eine Antwort statt neun. + Sie stand hier frueher selbst, ohne 'spicy', und genau das war der + Fehler, den Filipe am 07.09.2026 gefunden hat. */ + const LEITUNG = window.Bereiche.LEITUNG; /* `leer` ist der Satz, der in einer leeren Spalte steht. Vorher stand dort in allen vier Spalten ein Gedankenstrich -- vier graue Striche @@ -37,7 +40,10 @@ Dieselbe Menge steht im Server (DARF_ABBRECHEN in workspace-aufgaben.js) und DORT sitzt die Sperre. Hier geht es nur darum, niemandem einen Knopf hinzustellen, der dann 403 sagt. */ - const DARF_ABBRECHEN = new Set(['admin', 'manager', 'scout']); + /* Leitung plus Scouts. Aus der einen Rollenquelle abgeleitet statt + abgeschrieben -- die abgeschriebene Fassung hatte 'spicy' + vergessen, und ein fehlender Knopf faellt niemandem auf. */ + const DARF_ABBRECHEN = new Set([...window.Bereiche.LEITUNG, 'scout']); const STATUS_NAME = { offen: 'Offen', arbeit: 'In Arbeit', review: 'Review', erledigt: 'Erledigt', }; diff --git a/workspace/assets/js/bereich.js b/workspace/assets/js/bereich.js index 9ce99802..76abe447 100644 --- a/workspace/assets/js/bereich.js +++ b/workspace/assets/js/bereich.js @@ -11,7 +11,10 @@ /* DogFather und Manager haben dieselben Rechte -- die Unterscheidung macht nur die Personenverwaltung, und die prueft der Server. */ - const LEITUNG = new Set(['admin', 'manager']); + /* Die Leitungsliste steht in bereiche.js -- eine Antwort statt neun. + Sie stand hier frueher selbst, ohne 'spicy', und genau das war der + Fehler, den Filipe am 07.09.2026 gefunden hat. */ + const LEITUNG = window.Bereiche.LEITUNG; /* Wer hier eintraegt: DogFather, Manager und Scout. Der Creator liest nur mit -- diese Bereiche sind die Betreuungsakte, nicht sein diff --git a/workspace/assets/js/bereiche.js b/workspace/assets/js/bereiche.js index 62bf53fd..4092036b 100644 --- a/workspace/assets/js/bereiche.js +++ b/workspace/assets/js/bereiche.js @@ -389,7 +389,7 @@ wegzunehmen, ohne die Seite zu schliessen, waere nur ein versteckter Weg und keine Regel. */ { name: 'Zahlen', unter: 'Diamanten, LIVE-Tage, Verweildauer', zeichen: 'zahlen', ton: 2, - rollen: ['spicy', 'admin'], ziel: 'leistung.html', szene: 'arena' }, + rollen: ['admin'], ziel: 'leistung.html', szene: 'arena' }, { name: 'Start-Check', unter: 'Erstanalyse, 16 Punkte', zeichen: 'startcheck', ton: 16, rollen: ['spicy', 'admin', 'manager', 'creator', 'scout'], ziel: 'startcheck.html', szene: 'wald' }, { name: 'LIVE-Analyse', unter: 'Vorher, während, danach', zeichen: 'live', ton: 3, @@ -759,5 +759,38 @@ return s; } - window.Bereiche = { ZEICHEN, GRUPPEN, zuSeite, zeichenBauen, merkzeichenBauen }; + /* =================================================================== + WER GEHÖRT ZUR LEITUNG? EINE ANTWORT, NICHT NEUN (07.09.2026) + + Filipe hat gemeldet, dass die Seite „Creator-Profile" bei Spicy + Media mit „Profil konnte nicht geladen werden" abbricht. Die + Ursache war nicht diese eine Seite: In NEUN Skripten stand + dieselbe Zeile + + const LEITUNG = new Set(['admin', 'manager']); // <- ohne spicy + + und in keinem davon stand `spicy`. profil.js hat deshalb den + Zweig für Creator genommen und das EIGENE Profil geladen -- und + ein Spicy-Media-Zugang ist kein Creator, also 404. + + Das ist die Bauart, die von selbst auseinanderläuft: Wer eine + Rolle hinzufügt, müsste an neun Stellen daran denken, und die + acht, an die er nicht gedacht hat, gehen nicht kaputt -- sie + verhalten sich nur still falsch. Deshalb steht die Antwort jetzt + hier, an EINER Stelle, in einer Datei, die jede dieser Seiten vor + ihrem eigenen Skript lädt. + + `pruef-css-klassen.mjs` verlangt, dass kein Workspace-Skript sich + wieder eine eigene Leitungsliste baut. */ + const LEITUNG = new Set(['spicy', 'admin', 'manager']); + + /* Die Reihenfolge, in der Rollen überall erscheinen -- gleiche + Begründung, dieselbe Falle: kalender.js hatte seine eigene, ohne + Spicy Media, und sortierte sie damit ans Ende. */ + const ROLLENFOLGE = ['spicy', 'admin', 'manager', 'scout', 'creator']; + + window.Bereiche = { + ZEICHEN, GRUPPEN, zuSeite, zeichenBauen, merkzeichenBauen, + LEITUNG, ROLLENFOLGE, + }; })(); diff --git a/workspace/assets/js/calls.js b/workspace/assets/js/calls.js index 85885871..f0cefa48 100644 --- a/workspace/assets/js/calls.js +++ b/workspace/assets/js/calls.js @@ -14,7 +14,10 @@ überall sonst: DogFather und Manager entscheiden, alle anderen benutzen. Der Server prüft es noch einmal -- das hier ist nur die Anzeige. */ - const LEITUNG = new Set(['admin', 'manager']); + /* Die Leitungsliste steht in bereiche.js -- eine Antwort statt neun. + Sie stand hier frueher selbst, ohne 'spicy', und genau das war der + Fehler, den Filipe am 07.09.2026 gefunden hat. */ + const LEITUNG = window.Bereiche.LEITUNG; let ich = null; let daten = null; diff --git a/workspace/assets/js/chat.js b/workspace/assets/js/chat.js index a2285fef..11f18d94 100644 --- a/workspace/assets/js/chat.js +++ b/workspace/assets/js/chat.js @@ -495,7 +495,11 @@ /* Nach Rollen gruppiert, wie bei der Teilnehmerwahl im Kalender. Die Rolle steht einmal über der Gruppe statt an jedem Namen. */ - for (const rolle of ['admin', 'manager', 'scout', 'creator']) { + /* Die Rollenfolge kommt aus bereiche.js. Stand sie hier selbst, + fehlte 'spicy' -- und wer nicht in der Schleife vorkommt, taucht + in der Gesprächsliste gar nicht auf. Kein Fehler, nur eine + Person, die es fuer die Anderen nicht gibt. */ + for (const rolle of window.Bereiche.ROLLENFOLGE) { const leute = personen.filter((p) => p.rolle === rolle); if (!leute.length) continue; diff --git a/workspace/assets/js/content.js b/workspace/assets/js/content.js index f3937548..37563535 100644 --- a/workspace/assets/js/content.js +++ b/workspace/assets/js/content.js @@ -20,7 +20,10 @@ (() => { 'use strict'; - const LEITUNG = new Set(['admin', 'manager']); + /* Die Leitungsliste steht in bereiche.js -- eine Antwort statt neun. + Sie stand hier frueher selbst, ohne 'spicy', und genau das war der + Fehler, den Filipe am 07.09.2026 gefunden hat. */ + const LEITUNG = window.Bereiche.LEITUNG; const darfEintragen = () => ich && ich.rolle !== 'creator'; const $ = (id) => document.getElementById(id); diff --git a/workspace/assets/js/dateien.js b/workspace/assets/js/dateien.js index 558f38c5..2f176274 100644 --- a/workspace/assets/js/dateien.js +++ b/workspace/assets/js/dateien.js @@ -10,7 +10,10 @@ /* DogFather und Manager haben dieselben Rechte -- die Unterscheidung macht nur die Personenverwaltung, und die prueft der Server. */ - const LEITUNG = new Set(['admin', 'manager']); + /* Die Leitungsliste steht in bereiche.js -- eine Antwort statt neun. + Sie stand hier frueher selbst, ohne 'spicy', und genau das war der + Fehler, den Filipe am 07.09.2026 gefunden hat. */ + const LEITUNG = window.Bereiche.LEITUNG; const $ = (id) => document.getElementById(id); const STATUSNAME = { entwurf: 'Entwurf', review: 'Review', freigegeben: 'Freigegeben' }; diff --git a/workspace/assets/js/kalender.js b/workspace/assets/js/kalender.js index 9342bf90..4f4e0920 100644 --- a/workspace/assets/js/kalender.js +++ b/workspace/assets/js/kalender.js @@ -40,7 +40,10 @@ /* DogFather und Manager haben dieselben Rechte -- die Unterscheidung macht nur die Personenverwaltung, und die prüft der Server. */ - const LEITUNG = new Set(['admin', 'manager']); + /* Die Leitungsliste steht in bereiche.js -- eine Antwort statt neun. + Sie stand hier frueher selbst, ohne 'spicy', und genau das war der + Fehler, den Filipe am 07.09.2026 gefunden hat. */ + const LEITUNG = window.Bereiche.LEITUNG; const $ = (id) => document.getElementById(id); const el = (tag, klasse, text) => { @@ -88,7 +91,12 @@ const ROLLENGRUPPE = { admin: 'DogFather', manager: 'Manager', scout: 'Scouts', creator: 'Creator', }; - const ROLLENFOLGE = ['admin', 'manager', 'scout', 'creator']; + /* Auch diese Liste steht in bereiche.js -- sie hatte denselben + Mangel wie die Leitungsliste: kein 'spicy'. Eine unbekannte Rolle + landet bei indexOf() auf -1 und damit VOR allen anderen; Spicy + Media stand dadurch in der Teilnehmerwahl ganz oben statt an + ihrem Platz. Kein Absturz, nur still falsch. */ + const ROLLENFOLGE = window.Bereiche.ROLLENFOLGE; /** Ein bis zwei Buchstaben, die als Zeichen im Kreis stehen. * "Filipe Fernandes Ribeiro" -> "FR", "Luna" -> "L". diff --git a/workspace/assets/js/kopf.js b/workspace/assets/js/kopf.js index 0f01ada2..287d05a9 100644 --- a/workspace/assets/js/kopf.js +++ b/workspace/assets/js/kopf.js @@ -84,7 +84,9 @@ Und wo auch das nicht geht (alte Browser, kein sicherer Kontext), erscheint der Knopf gar nicht erst -- ein dritter Ausgang statt einer Schaltfläche, die nichts tut. */ - const DARF_TEILEN = new Set(['admin', 'manager', 'scout']); + /* Gleiche Menge, gleiche Begruendung wie beim Abbrechen: Leitung + plus Scouts, abgeleitet statt abgeschrieben. */ + const DARF_TEILEN = new Set([...window.Bereiche.LEITUNG, 'scout']); function teilenKnopf(ich) { const leiste = document.querySelector('.kopfleiste__rechts'); diff --git a/workspace/assets/js/leistung.js b/workspace/assets/js/leistung.js index f8c8b1e4..7138239b 100644 --- a/workspace/assets/js/leistung.js +++ b/workspace/assets/js/leistung.js @@ -23,7 +23,10 @@ return k; }; - const LEITUNG = new Set(['admin', 'manager']); + /* Die Leitungsliste steht in bereiche.js -- eine Antwort statt neun. + Sie stand hier frueher selbst, ohne 'spicy', und genau das war der + Fehler, den Filipe am 07.09.2026 gefunden hat. */ + const LEITUNG = window.Bereiche.LEITUNG; let ich = null; let creator = []; let offenerCreator = 0; diff --git a/workspace/assets/js/personen.js b/workspace/assets/js/personen.js index 55ae8b89..82092295 100644 --- a/workspace/assets/js/personen.js +++ b/workspace/assets/js/personen.js @@ -15,10 +15,17 @@ Zwei Listen fuer dieselbe Frage, eine im Server und eine hier -- und beim Hinzufuegen einer Rolle wurde nur die erste gepflegt. */ - const LEITUNG = new Set(['spicy', 'admin', 'manager']); + const LEITUNG = window.Bereiche.LEITUNG; const $ = (id) => document.getElementById(id); - const ROLLENNAME = { admin: 'DogFather', manager: 'Manager', scout: 'Scout', creator: 'Creator' }; + /* 'spicy' fehlte hier bis zum 07.09.2026. Vier Stellen lesen daraus + (Codeanzeige, Personenliste, Scout-Zeile, Manager-Zeile), und alle + vier haben den Fehlgriff mit `|| p.rolle` aufgefangen -- in der + Liste stand dann schlicht „spicy" statt „Spicy Media". Ein + Auffangwert, der halbwegs plausibel aussieht, ist der beste Weg, + eine Luecke jahrelang zu behalten. */ + const ROLLENNAME = { spicy: 'Spicy Media', admin: 'DogFather', + manager: 'Manager', scout: 'Scout', creator: 'Creator' }; /* Reihenfolge und Erklaerung an EINER Stelle. Sie gilt fuer die Rollenwahl im Formular und fuer alles, was sonst Rollen auflistet. */ @@ -54,7 +61,7 @@ Aus derselben Liste wie die Beschreibungen, damit die beiden nicht auseinanderlaufen. */ - const darf = ich?.rolle === 'admin' ? ['spicy', 'admin', 'manager', 'scout', 'creator'] + const darf = ich?.rolle === 'admin' ? [...window.Bereiche.ROLLENFOLGE] : ich?.rolle === 'spicy' ? ['manager', 'creator'] : ['creator']; for (const r of ROLLEN.filter((x) => darf.includes(x.wert))) { diff --git a/workspace/assets/js/profil.js b/workspace/assets/js/profil.js index 20ee57ea..1d3dad2f 100644 --- a/workspace/assets/js/profil.js +++ b/workspace/assets/js/profil.js @@ -10,7 +10,10 @@ /* DogFather und Manager haben dieselben Rechte -- die Unterscheidung macht nur die Personenverwaltung, und die prueft der Server. */ - const LEITUNG = new Set(['admin', 'manager']); + /* Die Leitungsliste steht in bereiche.js -- eine Antwort statt neun. + Sie stand hier frueher selbst, ohne 'spicy', und genau das war der + Fehler, den Filipe am 07.09.2026 gefunden hat. */ + const LEITUNG = window.Bereiche.LEITUNG; const OFFEN = ['handles', 'nische', 'live_zeiten', 'technik', 'ziel_live', 'ziel_content', 'ziel_community', 'ziel_technik', diff --git a/workspace/assets/js/report.js b/workspace/assets/js/report.js index 12f2a78a..fd4803fd 100644 --- a/workspace/assets/js/report.js +++ b/workspace/assets/js/report.js @@ -10,7 +10,10 @@ /* DogFather und Manager haben dieselben Rechte -- die Unterscheidung macht nur die Personenverwaltung, und die prueft der Server. */ - const LEITUNG = new Set(['admin', 'manager']); + /* Die Leitungsliste steht in bereiche.js -- eine Antwort statt neun. + Sie stand hier frueher selbst, ohne 'spicy', und genau das war der + Fehler, den Filipe am 07.09.2026 gefunden hat. */ + const LEITUNG = window.Bereiche.LEITUNG; const $ = (id) => document.getElementById(id); const BEREICHSNAME = { diff --git a/workspace/assets/js/scouting.js b/workspace/assets/js/scouting.js index 49bed198..475d082e 100644 --- a/workspace/assets/js/scouting.js +++ b/workspace/assets/js/scouting.js @@ -14,7 +14,10 @@ /* DogFather und Manager haben dieselben Rechte -- die Unterscheidung macht nur die Personenverwaltung, und die prueft der Server. */ - const LEITUNG = new Set(['admin', 'manager']); + /* Die Leitungsliste steht in bereiche.js -- eine Antwort statt neun. + Sie stand hier frueher selbst, ohne 'spicy', und genau das war der + Fehler, den Filipe am 07.09.2026 gefunden hat. */ + const LEITUNG = window.Bereiche.LEITUNG; const $ = (id) => document.getElementById(id); const WEITER = { diff --git a/workspace/assets/js/uebersicht.js b/workspace/assets/js/uebersicht.js index 8a7154a2..3eba1a3f 100644 --- a/workspace/assets/js/uebersicht.js +++ b/workspace/assets/js/uebersicht.js @@ -311,7 +311,12 @@ const knopf = document.getElementById('creator-neu'); const dialog = document.getElementById('creator-dialog'); if (!knopf || !dialog || !ich) return; - if (!['admin', 'manager'].includes(ich.rolle)) return; + /* Leitung -- aus der einen Rollenquelle, nicht abgeschrieben. + Abgeschrieben stand hier ['admin','manager'], also OHNE Spicy + Media: Der Server haette sie anlegen lassen (nurLeitung schliesst + sie ein), aber den Knopf dazu hat sie nie gesehen. Ein fehlender + Knopf sieht nicht nach Fehler aus, sondern nach Absicht. */ + if (!window.Bereiche.LEITUNG.has(ich.rolle)) return; knopf.hidden = false; const f = (id) => document.getElementById(id); diff --git a/workspace/aufgaben.html b/workspace/aufgaben.html index a78a6efd..e4dd2bfd 100644 --- a/workspace/aufgaben.html +++ b/workspace/aufgaben.html @@ -9,10 +9,10 @@ - - - - + + + + @@ -212,10 +212,10 @@ - - - - - + + + + + diff --git a/workspace/automation.html b/workspace/automation.html index 76061787..b26b2011 100644 --- a/workspace/automation.html +++ b/workspace/automation.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -135,10 +135,10 @@ - - - - - + + + + + diff --git a/workspace/bereich.html b/workspace/bereich.html index 86aa9a6b..ed082ed6 100644 --- a/workspace/bereich.html +++ b/workspace/bereich.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -144,14 +144,14 @@ - - - - - - - - - + + + + + + + + + diff --git a/workspace/calls.html b/workspace/calls.html index 5da22414..13585f18 100644 --- a/workspace/calls.html +++ b/workspace/calls.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -77,11 +77,11 @@ - - - - - - + + + + + + diff --git a/workspace/chat.html b/workspace/chat.html index 079f2f0a..2cc8f97c 100644 --- a/workspace/chat.html +++ b/workspace/chat.html @@ -9,17 +9,17 @@ - - + + - - - + + + @@ -155,9 +155,9 @@ - - - - + + + + diff --git a/workspace/content.html b/workspace/content.html index 70cc68bb..381c645d 100644 --- a/workspace/content.html +++ b/workspace/content.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -167,12 +167,12 @@ - - - - - - - + + + + + + + diff --git a/workspace/dateien.html b/workspace/dateien.html index b63ec745..baef23ae 100644 --- a/workspace/dateien.html +++ b/workspace/dateien.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -96,10 +96,10 @@ - - - - - + + + + + diff --git a/workspace/index.html b/workspace/index.html index aa8ee0ca..776654d7 100644 --- a/workspace/index.html +++ b/workspace/index.html @@ -27,7 +27,7 @@ - + @@ -202,6 +202,6 @@ - + diff --git a/workspace/kalender.html b/workspace/kalender.html index 7e08fbdd..bca2ee8a 100644 --- a/workspace/kalender.html +++ b/workspace/kalender.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -258,11 +258,11 @@ - - - - - - + + + + + + diff --git a/workspace/leistung.html b/workspace/leistung.html index ad201889..4b0836e4 100644 --- a/workspace/leistung.html +++ b/workspace/leistung.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -212,10 +212,10 @@ - - - - - + + + + + diff --git a/workspace/personen.html b/workspace/personen.html index 5ff3f4e4..4e719c04 100644 --- a/workspace/personen.html +++ b/workspace/personen.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -137,10 +137,10 @@ - - - - - + + + + + diff --git a/workspace/profil.html b/workspace/profil.html index 6011fb3e..88c42bd0 100644 --- a/workspace/profil.html +++ b/workspace/profil.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -202,11 +202,11 @@ - - - - - - + + + + + + diff --git a/workspace/report.html b/workspace/report.html index 3fd99d92..1d2f687c 100644 --- a/workspace/report.html +++ b/workspace/report.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -94,11 +94,11 @@ - - - - - - + + + + + + diff --git a/workspace/scouting.html b/workspace/scouting.html index 03ff08f1..fe90e634 100644 --- a/workspace/scouting.html +++ b/workspace/scouting.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -127,10 +127,10 @@ - - - - - + + + + + diff --git a/workspace/start.html b/workspace/start.html index 979c6676..d6f51667 100644 --- a/workspace/start.html +++ b/workspace/start.html @@ -9,12 +9,12 @@ - - + + - - + + @@ -264,10 +264,10 @@

- - - - - + + + + + diff --git a/workspace/startcheck.html b/workspace/startcheck.html index 3776ab59..3f875af1 100644 --- a/workspace/startcheck.html +++ b/workspace/startcheck.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -73,11 +73,11 @@ - - - - - - + + + + + + diff --git a/workspace/steckbrief.html b/workspace/steckbrief.html index c2ab3206..30c81f44 100644 --- a/workspace/steckbrief.html +++ b/workspace/steckbrief.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -114,10 +114,10 @@ - - - - - + + + + + diff --git a/workspace/uebersicht.html b/workspace/uebersicht.html index d24f3d95..7b3c0434 100644 --- a/workspace/uebersicht.html +++ b/workspace/uebersicht.html @@ -9,13 +9,13 @@ - - + + - - - + + + @@ -137,10 +137,10 @@ - + - - - + + + diff --git a/workspace/wissen.html b/workspace/wissen.html index 9737d916..76aa01be 100644 --- a/workspace/wissen.html +++ b/workspace/wissen.html @@ -9,11 +9,11 @@ - - - - - + + + + + @@ -178,10 +178,10 @@ - - - - - + + + + +