Das Ideen-Board -- und fuenf Kacheln, die ins Leere fuehrten
KAPITEL 5.6: "Sammelstelle fuer Content-, Live- und Community-Ideen mit
Priorisierung." Fast nichts davon musste neu gebaut werden: `eintraege`
hat Titel, Text, Status und `dringlichkeit` (hoch/mittel/niedrig) -- und
das IST die Priorisierung. Eine eigene Tabelle daneben waere eine VIERTE
Sichtbarkeitsregel gewesen; genau deren Vervielfaeltigung hat heute
schon ein Leck verursacht.
Der Bereich gehoert niemandem einzeln (`ohneCreatorBezug`) -- eine Idee
gehoert der Runde. KEIN `fuerAlle`: Das waere der naheliegende Griff
gewesen und der falsche, denn es heisst woertlich JEDER. Wer die
Sammlung sieht, entscheidet dieselbe Regel wie ueberall.
DER RISKANTE TEIL WAR DIE DATENBANK. Die erlaubten Bereiche stehen als
CHECK-Regel, und SQLite kann die nicht aendern -- die Tabelle muss neu
gebaut werden. Die alte Umstellung fuer 'agentur' schreibt dafuer den
ganzen Bauplan von Hand ab; im Kommentar dort steht, dass dabei schon
einmal drei Spalten vergessen wurden. Statt einer vierten Abschrift ist
die Fassung von heute Morgen jetzt tabellenunabhaengig
(checkListeErweitern): Spaltenliste aus der Tabelle, Sicherung vorher,
Zaehlung innerhalb der Transaktion.
UND DABEI WAERE EIN STILLER TOTALAUSFALL PASSIERT. Das Muster fuer den
Tabellenkopf stand in einem Template-Literal -- dort verschluckt
JavaScript den Backslash, aus `\s` wird `s`, das Muster hiess
"CREATE TABLEs+..." und traf nie etwas. Die Umstellung haette
SCHWEIGEND nichts getan: kein Fehler, kein Hinweis, nur ein Bereich, den
es nie gegeben haette. Im Quelltext war das nicht zu sehen; gefunden hat
es eine Messung. Jetzt steht dort gar kein Muster mehr -- alles vor der
ersten Klammer IST der Tabellenkopf, und das kann man nicht falsch
maskieren.
Die Pruefung baut deshalb eine ECHTE ALTE Tabelle und laesst die
Umstellung darauf laufen. Eine frische Datenbank bringt den Bereich
schon mit -- die Umstellung liefe gar nicht erst an, und alles waere
gruen, ohne das Riskante angesehen zu haben.
=== DER GROESSERE FUND ===
FUENF VON ZEHN MODI-KACHELN FUEHRTEN INS LEERE. Der Server hat eine
Liste, welche Rolle welche Seite oeffnen darf; bereich.html und
profil.html schlossen 'modi' aus. Vier Bereichs-Kacheln und das eigene
Profil leiteten wortlos auf die Startseite zurueck.
Der Rollen-Rundgang meldete sie trotzdem als "ok", und zu Recht: Eine
Umleitung ist kein Fehler. Die Seite laedt, keine rote Konsole, keine
4xx-Antwort. Sie ist nur eine ANDERE. Das ist eine eigene Fehlerklasse
-- nicht "kaputt", sondern "fuehrt woandershin" -- und sie faellt nur
dem auf, der die Anwendung benutzt und merkt, dass ein Knopf nichts tut.
Beinahe waere meine eigene Pruefung darauf hereingefallen: Sie fand auf
der zurueckgeleiteten Startseite das Wort "Ideen-Board" -- den Text der
KACHEL -- und hielt sie fuer das Board. Jetzt steht die Adresse in der
Bedingung.
DIE SPERRE DAGEGEN GILT AB SOFORT FUER ALLE: pruef-rollen klickt fuer
JEDE Rolle jede Kachel durch, die sie angeboten bekommt, und verlangt,
dass sie dort ankommt. Geprueft wird die Zusage der Startseite, nicht
eine Liste daneben -- eine Liste koennte selbst veralten. 113 -> 243
Pruefungen; der Zuwachs ist genau das.
Er hat im ersten Anlauf zwei weitere Loecher gefunden:
* "Mein Profil" war die falsche Seite. profil.html ist der
Creator-Entwicklungsplan und antwortet mit 404, wenn die Person kein
Creator ist. Die eigene Seite heisst steckbrief.html.
* content.html stand auf `null` ("jede angemeldete Rolle") -- mit der
neuen Rolle also auch sie. Die Seite laedt, ihre Schnittstelle gibt
404. Das ist die Kehrseite von "geschuetzt ist die Regel, nicht die
Ausnahme": Eine NEUE ROLLE erbt jedes `null` automatisch.
Und ein Modi kommt nur in SEINE Bereiche -- sonst waere er ueber die
Adresszeile in der Agentur-Ablage gelandet. Welche erlaubt sind, wird
aus seinen Kacheln abgeleitet statt danebengeschrieben.
=== KLEINERES, ABER SICHTBARES ===
Ueber dem Board stand "Betreuung" -- die Beschriftung fuer Akten, die
UEBER jemanden gefuehrt werden. Eine Sammelstelle ist das Gegenteil.
Aufgefallen auf dem Bildschirmfoto, wie so oft heute.
Der Farbton der Kachel ist nicht nach Gefuehl gewaehlt: Alle 22
vorhandenen waren belegt, also wurde der Abstand zu jedem ausgerechnet
und der genommen, der sich am deutlichsten unterscheidet, ohne grau zu
wirken (#5f8a9f, Abstand 127).
BEINAHE HAETTE ICH EIN LOCH REPARIERT, DAS ES NICHT GIBT: Meine Pruefung
meldete, die Suche verrate die Ideen an Spicy Media. Tatsaechlich fand
sie null Treffer -- die Pruefung hatte ihren EIGENEN Suchbegriff
wiedergefunden, weil die Antwort ihn im Feld `frage` zurueckspiegelt.
Gezaehlt wird jetzt, was gefunden wurde.
pruef-spicy erwartete den alten Wortlaut der Umstellungsmeldung. Sie
nennt jetzt Tabelle und Spalte statt "Rolle"; geprueft wird der Sinn,
nicht der Satz.
GEPRUEFT: pruef-modi-ideen (30, neu), pruef-rollen (243 statt 113),
pruef-modi-verborgen (75), pruef-modi-katalog (29), pruef-spicy (60),
pruef-css-klassen, pruef-start-ansicht.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+108
-26
@@ -281,6 +281,10 @@ const MODI_BEREICHE = [
|
||||
name: "Dateien", unter: "Ablage und Freigaben", zeichen: "dateien",
|
||||
ton: 19, ziel: "dateien.html", szene: "arena" },
|
||||
|
||||
{ gruppe: "Täglich", gruppeUnter: "Was du sowieso jeden Tag aufmachst",
|
||||
name: "Ideen-Board", unter: "Gesammelt und nach Dringlichkeit sortiert",
|
||||
zeichen: "content", ton: 23, ziel: "bereich.html?b=ideen", szene: "portal" },
|
||||
|
||||
{ gruppe: "Rund ums Live", gruppeUnter: "Vor, während und nach der Sendung",
|
||||
name: "Live-Ablauf", unter: "Checkliste für vorher, mittendrin und danach",
|
||||
zeichen: "live", ton: 3, ziel: "bereich.html?b=live", szene: "portal" },
|
||||
@@ -292,8 +296,8 @@ const MODI_BEREICHE = [
|
||||
ton: 14, ziel: "bereich.html?b=technik", szene: "garage" },
|
||||
|
||||
{ gruppe: "Für dich", gruppeUnter: "Deine Seite im Team",
|
||||
name: "Mein Profil", unter: "Deine Angaben und dein Bild", zeichen: "profil",
|
||||
ton: 20, ziel: "profil.html", szene: "halle" },
|
||||
name: "Mein Steckbrief", unter: "Dein Bild und deine Kanäle", zeichen: "steckbrief",
|
||||
ton: 10, ziel: "steckbrief.html", szene: "halle" },
|
||||
{ gruppe: "Für dich", gruppeUnter: "Deine Seite im Team",
|
||||
name: "Wissen", unter: "Nachschlagen statt nachfragen", zeichen: "wissen",
|
||||
ton: 9, ziel: "wissen.html", szene: "arena" },
|
||||
@@ -312,6 +316,23 @@ export function bereicheFuer(person) {
|
||||
return person?.rolle === "modi" ? MODI_BEREICHE : null;
|
||||
}
|
||||
|
||||
/* WELCHE BEREICHSSEITEN EIN MODI OEFFNEN DARF -- abgeleitet aus seinen
|
||||
eigenen Kacheln und nicht danebengeschrieben.
|
||||
|
||||
Eine zweite Liste waere die Stelle, an der es auseinanderlaeuft: Wer
|
||||
eine Kachel hinzufuegt und den Eintrag vergisst, baut einen Knopf,
|
||||
der auf die Startseite zurueckwirft. Wer eine Kachel entfernt und den
|
||||
Eintrag vergisst, laesst eine Tuer offen. So kann beides nicht
|
||||
passieren.
|
||||
|
||||
Gebraucht wird sie, weil `bereich.html` seit heute fuer ihn offen ist
|
||||
-- ohne diese Einschraenkung kaeme er ueber die Adresszeile auch in
|
||||
die Agentur-Ablage, die ihn nichts angeht. */
|
||||
export const MODI_BEREICHE_ERLAUBT = new Set(
|
||||
MODI_BEREICHE
|
||||
.map((k) => (/bereich\.html\?b=([a-z]+)/.exec(k.ziel) || [])[1])
|
||||
.filter(Boolean));
|
||||
|
||||
/* Der Satz unter der Begruessung. Die fuenf bekannten Rollen haben ihn
|
||||
in assets/js/start.js stehen ("Creator · dein eigener Bereich"); fuer
|
||||
einen Modi darf er dort nicht stehen und kommt deshalb von hier.
|
||||
@@ -493,10 +514,19 @@ let _dbFehler = null;
|
||||
@param marker Die Rolle, an der erkannt wird, ob schon umgestellt ist.
|
||||
@param rollen Die vollstaendige neue Liste erlaubter Rollen.
|
||||
===================================================================== */
|
||||
function rollenRegelUmstellen(d, marker, rollen, jetztStempel) {
|
||||
function checkListeErweitern(d, tabelle, spalte, marker, werte, jetztStempel) {
|
||||
const rollenPlan = d.prepare(
|
||||
"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'personen'").get()?.sql || "";
|
||||
const regel = rollenPlan.match(/rolle\s+IN\s*\(([^)]*)\)/i)?.[1] || "";
|
||||
"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?").get(tabelle)?.sql || "";
|
||||
/* DOPPELTE BACKSLASHES, und das ist kein Schoenheitsfehler:
|
||||
In einem Template-Literal verschluckt JavaScript den einfachen
|
||||
Backslash -- aus `\s` wird schlicht `s`, das Muster hiesse dann
|
||||
"bereichs+INs*(...)" und traefe nie etwas. Die Umstellung haette
|
||||
daraufhin STILLSCHWEIGEND nichts getan: keine Regel gefunden,
|
||||
also nichts umzustellen, kein Fehler, kein Hinweis. Gemessen
|
||||
statt ueberlegt -- der Unterschied war im Quelltext nicht zu
|
||||
sehen. */
|
||||
const muster = new RegExp(`${spalte}\\s+IN\\s*\\(([^)]*)\\)`, "i");
|
||||
const regel = rollenPlan.match(muster)?.[1] || "";
|
||||
/* Keine Regel gefunden heisst: Es gibt nichts umzustellen. Nicht
|
||||
etwa "dann bauen wir sie neu" -- eine Tabelle ohne CHECK ist ein
|
||||
Zustand, den jemand ansehen sollte, kein Fall fuer Automatik. */
|
||||
@@ -522,19 +552,32 @@ function rollenRegelUmstellen(d, marker, rollen, jetztStempel) {
|
||||
unveraendert, die Schutzabfrage darunter schlug an -- und die
|
||||
Umstellung waere auf dem echten Server NIE gelaufen. Sie haette es
|
||||
gesagt (das ist der Wert der Abfrage), aber sie waere nie gelaufen. */
|
||||
const neuerPlan = rollenPlan
|
||||
.replace(/CREATE TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?["'`]?personen["'`]?/i,
|
||||
"CREATE TABLE personen_neu")
|
||||
.replace(/rolle\s+IN\s*\([^)]*\)/i,
|
||||
`rolle IN (${rollen.map((r) => `'${r}'`).join(",")})`);
|
||||
if (!neuerPlan.includes(`'${marker}'`) || !neuerPlan.includes("personen_neu")) {
|
||||
/* KEIN MUSTER FUER DEN TABELLENKOPF -- alles vor der ersten Klammer
|
||||
IST der Kopf ("CREATE TABLE IF NOT EXISTS eintraege "). Hier stand
|
||||
eine Regel mit Anfuehrungszeichen, Backticks und Backslashes in
|
||||
einem Template-Literal, und genau daran ist sie gescheitert:
|
||||
JavaScript verschluckt darin den einfachen Backslash, aus dem
|
||||
Muster wurde "CREATE TABLEs+..." und es traf nie etwas. Die
|
||||
Umstellung haette daraufhin STILLSCHWEIGEND nichts getan.
|
||||
|
||||
Ein Schnitt an der ersten Klammer braucht keine Maskierung, ist in
|
||||
einer Zeile zu lesen und kann nicht falsch escaped sein. */
|
||||
const klammer = rollenPlan.indexOf('(');
|
||||
if (klammer < 0) {
|
||||
console.error('[workspace] Umstellung abgebrochen: Bauplan ohne Klammer.');
|
||||
return;
|
||||
}
|
||||
const neuerPlan = (`CREATE TABLE ${tabelle}_neu ` + rollenPlan.slice(klammer))
|
||||
.replace(new RegExp(`${spalte}\\s+IN\\s*\\([^)]*\\)`, "i"),
|
||||
`${spalte} IN (${werte.map((r) => `'${r}'`).join(",")})`);
|
||||
if (!neuerPlan.includes(`'${marker}'`) || !neuerPlan.includes(`${tabelle}_neu`)) {
|
||||
console.error("[workspace] Umstellung abgebrochen: Der Bauplan liess sich nicht "
|
||||
+ "umschreiben. Steht die CHECK-Regel noch so da wie erwartet?");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Die Spaltenliste kommt aus der Tabelle, nicht aus dem Gedaechtnis. */
|
||||
const spalten = d.prepare("PRAGMA table_info(personen)").all().map((z) => z.name);
|
||||
const spalten = d.prepare(`PRAGMA table_info(${tabelle})`).all().map((z) => z.name);
|
||||
if (!spalten.length) {
|
||||
console.error("[workspace] Umstellung abgebrochen: keine Spalten gefunden.");
|
||||
return;
|
||||
@@ -546,28 +589,28 @@ function rollenRegelUmstellen(d, marker, rollen, jetztStempel) {
|
||||
Transaktion umschalten. */
|
||||
d.exec("PRAGMA foreign_keys = OFF");
|
||||
try {
|
||||
const vorher = d.prepare("SELECT COUNT(*) AS n FROM personen").get().n;
|
||||
const vorher = d.prepare(`SELECT COUNT(*) AS n FROM ${tabelle}`).get().n;
|
||||
d.exec("BEGIN");
|
||||
d.exec(neuerPlan);
|
||||
d.exec(`INSERT INTO personen_neu (${liste}) SELECT ${liste} FROM personen;`);
|
||||
const nachher = d.prepare("SELECT COUNT(*) AS n FROM personen_neu").get().n;
|
||||
d.exec(`INSERT INTO ${tabelle}_neu (${liste}) SELECT ${liste} FROM ${tabelle};`);
|
||||
const nachher = d.prepare(`SELECT COUNT(*) AS n FROM ${tabelle}_neu`).get().n;
|
||||
/* Die Zaehlung steht INNERHALB der Transaktion -- stimmt sie nicht,
|
||||
wird zurueckgerollt und die alte Tabelle bleibt unberuehrt. */
|
||||
if (nachher !== vorher) {
|
||||
d.exec("ROLLBACK");
|
||||
console.error(`[workspace] Umstellung abgebrochen: ${vorher} Personen vorher, `
|
||||
console.error(`[workspace] Umstellung abgebrochen: ${vorher} Zeilen vorher, `
|
||||
+ `${nachher} nachher. Sicherung: ${sicherung}`);
|
||||
} else {
|
||||
d.exec("DROP TABLE personen;");
|
||||
d.exec("ALTER TABLE personen_neu RENAME TO personen;");
|
||||
d.exec(`DROP TABLE ${tabelle};`);
|
||||
d.exec(`ALTER TABLE ${tabelle}_neu RENAME TO ${tabelle};`);
|
||||
d.exec("COMMIT");
|
||||
const kaputt = d.prepare("PRAGMA foreign_key_check").all();
|
||||
if (kaputt.length) {
|
||||
console.error("[workspace] ACHTUNG: nach der Umstellung", kaputt.length,
|
||||
"verwaiste Verweise. Sicherung liegt unter", sicherung);
|
||||
} else {
|
||||
console.log(`[workspace] Rolle '${marker}' freigeschaltet, ${nachher} Personen, `
|
||||
+ `${spalten.length} Spalten uebernommen, Verweise geprueft.`);
|
||||
console.log(`[workspace] '${marker}' in ${tabelle}.${spalte} freigeschaltet, `
|
||||
+ `${nachher} Zeilen, ${spalten.length} Spalten uebernommen, Verweise geprueft.`);
|
||||
}
|
||||
}
|
||||
} catch (fehler) {
|
||||
@@ -1417,10 +1460,20 @@ function umstellungen(d) {
|
||||
rollenRegelUmstellen() -- einmal, nicht je Rolle abgeschrieben.
|
||||
Beide Aufrufe stehen hier: Auf einer Datenbank, die 'spicy' schon
|
||||
kennt, tut der erste nichts und nur der zweite laeuft. */
|
||||
rollenRegelUmstellen(d, "spicy",
|
||||
checkListeErweitern(d, "personen", "rolle", "spicy",
|
||||
["spicy", "admin", "manager", "scout", "creator"], jetztStempel);
|
||||
rollenRegelUmstellen(d, "modi",
|
||||
checkListeErweitern(d, "personen", "rolle", "modi",
|
||||
["spicy", "admin", "manager", "scout", "creator", "modi"], jetztStempel);
|
||||
|
||||
/* DER BEREICH FUER DAS IDEEN-BOARD (10.09.2026, Kapitel 5.6).
|
||||
|
||||
Dieselbe Umstellung, andere Tabelle -- und genau deshalb steht sie
|
||||
ab jetzt EINMAL da. Der alte agentur-Block weiter unten schreibt
|
||||
den ganzen Bauplan von Hand ab; im Kommentar dort steht, dass dabei
|
||||
schon einmal drei Spalten vergessen wurden. Eine vierte Abschrift
|
||||
waere die vierte Gelegenheit dazu. */
|
||||
checkListeErweitern(d, "eintraege", "bereich", "ideen",
|
||||
["live", "content", "technik", "community", "schutz", "agentur", "ideen"], jetztStempel);
|
||||
}
|
||||
|
||||
export function db() {
|
||||
@@ -1892,7 +1945,7 @@ export function db() {
|
||||
CREATE TABLE IF NOT EXISTS eintraege (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
bereich TEXT NOT NULL
|
||||
CHECK (bereich IN ('live','content','technik','community','schutz','agentur')),
|
||||
CHECK (bereich IN ('live','content','technik','community','schutz','agentur','ideen')),
|
||||
art TEXT NOT NULL,
|
||||
titel TEXT NOT NULL,
|
||||
text TEXT,
|
||||
@@ -2334,7 +2387,18 @@ export const workspaceRouter = express.Router();
|
||||
const GESCHUETZT = {
|
||||
"/workspace/start.html": null,
|
||||
"/workspace/uebersicht.html": null,
|
||||
"/workspace/content.html": null,
|
||||
/* AUSDRUECKLICH OHNE 'modi' (10.09.2026). Vorher stand hier `null`
|
||||
("jede angemeldete Rolle") -- und mit der neuen Rolle hiess das
|
||||
auch: sie. Die Seite laedt dann zwar, ihre Schnittstelle antwortet
|
||||
ihm aber mit 404, weil `content` nicht zu seinen Bereichen gehoert.
|
||||
Eine Seite, die man oeffnen kann und die dann leer bleibt, sieht
|
||||
aus wie ein Fehler -- und ist einer.
|
||||
|
||||
Das ist die Kehrseite von "geschuetzt ist die Regel, nicht die
|
||||
Ausnahme": Eine NEUE ROLLE erbt jedes `null` automatisch. Wer eine
|
||||
Rolle hinzufuegt, muss diese Liste durchgehen -- der Kachel-Durchgang
|
||||
in pruef-rollen findet die Faelle, die dabei auffallen. */
|
||||
"/workspace/content.html": ["spicy", "admin", "manager", "scout", "creator"],
|
||||
"/workspace/aufgaben.html": null,
|
||||
/* Chat (06.09.2026): jede Rolle. WEN man erreicht, entscheidet
|
||||
schreibbareIds() -- nicht der Zugang zur Seite. Ein Creator, der
|
||||
@@ -2388,10 +2452,28 @@ const GESCHUETZT = {
|
||||
/* Der eigene Steckbrief -- jede Rolle hat einen. Ein Creator wird
|
||||
dort nicht hingeschickt (er sieht seinen auf profil.html), darf die
|
||||
Seite aber aufrufen: Sie zeigt ihm dasselbe, nur ohne die Akte. */
|
||||
"/workspace/steckbrief.html": ["spicy", "admin", "manager", "scout", "creator"],
|
||||
/* 'modi' kam am 10.09.2026 dazu. Der Steckbrief ist die EIGENE Seite
|
||||
("Dein Bild und deine Kanäle"); profil.html daneben ist der
|
||||
Entwicklungsplan eines Creators und fuer ihn sinnlos. Der erste
|
||||
Anlauf hatte genau das verwechselt -- die Kachel hiess "Mein
|
||||
Profil" und fuehrte auf eine Seite, deren Schnittstelle mit 404
|
||||
antwortet, weil sie ausdruecklich nur Creator kennt. Gefunden vom
|
||||
neuen Kachel-Durchgang in pruef-rollen. */
|
||||
"/workspace/steckbrief.html": ["spicy", "admin", "manager", "scout", "creator", "modi"],
|
||||
"/workspace/kalender.html": null,
|
||||
"/workspace/dateien.html": null,
|
||||
"/workspace/bereich.html": ["spicy", "admin", "manager", "scout", "creator"],
|
||||
/* 'modi' kam am 10.09.2026 dazu -- und es war ein echter Fund: Ohne
|
||||
diese Zeile leiteten VIER seiner Kacheln (Live-Ablauf, Community,
|
||||
Technik, Ideen-Board) wortlos auf die Startseite zurueck. Der
|
||||
Rollen-Rundgang meldete sie trotzdem als "ok": Eine Umleitung ist
|
||||
kein Fehler, die Seite laedt ja -- nur eben eine andere. Seither
|
||||
prueft pruef-rollen fuer JEDE Rolle, dass jede Kachel, die sie
|
||||
bekommt, auch wirklich dort ankommt.
|
||||
|
||||
WELCHE Bereiche ein Modi oeffnen darf, entscheidet die Liste
|
||||
seiner Kacheln (MODI_BEREICHE_ERLAUBT) -- sonst kaeme er ueber die
|
||||
Adresszeile auch in die Agentur-Ablage. */
|
||||
"/workspace/bereich.html": ["spicy", "admin", "manager", "scout", "creator", "modi"],
|
||||
"/workspace/report.html": ["spicy", "admin", "manager", "scout", "creator"],
|
||||
"/workspace/scouting.html": ["spicy", "admin", "manager", "scout"],
|
||||
/* DIE ARBEITSLAGE DES TEAMS (09.09.2026).
|
||||
|
||||
Reference in New Issue
Block a user