Der Erklaerkasten auf jeder Seite ist weg
Filipe, mit dem Kasten im Bild: "das muss sofort weg. ueberall das ist scheisse." Entfernt, nicht ausgeblendet: - workspace/assets/js/erklaerung.js - server/workspace-erklaerung.js samt Router-Einbindung in index.js - server/pruef-erklaerung.mjs - der Stilblock in start.css - die Skript-Zeile auf allen 25 Seiten AUSGEBLENDET WAERE KEIN ENTFERNEN. Der Weg haette weiter geantwortet, das Skript waere weiter geladen worden, und beim naechsten Umbau waere der Kasten irgendwo wieder aufgetaucht. Was weg soll, wird weggenommen. NACHGEMESSEN STATT ANGENOMMEN: - kein Verweis auf die geloeschten Dateien mehr im Repo, - der Server startet ohne Fehler (frische Datenbank, Protokoll leer), - keine Reste (erkl__, erklaerung.js) in Seiten oder Stilvorlagen. Vier andere Pruefungen nennen ebenfalls "Erklaerung" -- sie meinen ANDERE: den Satz im Neu-Fenster des Chats, die Stufen-Seite, die Rollenkarten. Die bleiben unberuehrt; nachgesehen, nicht vermutet. pruef-workspace-seiten, pruef-css-klassen und pruef-lesbarkeit laufen gruen. Die Zahl der Pruefungen sinkt um die der geloeschten Datei -- das ist hier richtig: Sie prueften etwas, das es nicht mehr gibt. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -208,7 +208,7 @@ export function freigabenSetzen(dateiId, ids, person) {
|
||||
const SPALTEN = `
|
||||
d.id, d.name_original, d.groesse, d.typ, d.status, d.notiz,
|
||||
d.creator_id, d.hochgeladen_von, d.erstellt, d.geaendert,
|
||||
d.creator_extern,
|
||||
d.creator_extern, d.ersetzt_id,
|
||||
${externSql("pc.name", "d.creator_extern")} AS creator_name,
|
||||
ph.name AS hochgeladen_name`;
|
||||
|
||||
@@ -217,6 +217,48 @@ const VERBUND = `
|
||||
LEFT JOIN personen pc ON pc.id = d.creator_id
|
||||
LEFT JOIN personen ph ON ph.id = d.hochgeladen_von`;
|
||||
|
||||
/* =====================================================================
|
||||
WELCHE FASSUNG IST DAS? (15.09.2026)
|
||||
|
||||
Aus „ersetzt_id“ wird eine Kette. Jede Datei bekommt zwei Angaben:
|
||||
|
||||
fassung die wievielte sie ist (1 = die erste)
|
||||
ersetzt_am ob es eine neuere gibt -- dann ist SIE nicht mehr aktuell
|
||||
|
||||
GERECHNET, NICHT GESPEICHERT. Eine Spalte "version" muesste beim
|
||||
Loeschen einer mittleren Fassung nachgezogen werden, und genau das
|
||||
vergisst man. Die Kette steht in den Verweisen; die Nummer folgt
|
||||
daraus.
|
||||
|
||||
NUR AUS DER UEBERGEBENEN LISTE: Ist eine frueherere Fassung fuer
|
||||
diese Person gar nicht sichtbar, zaehlt sie hier auch nicht mit --
|
||||
eine Zahl, die eine unsichtbare Datei mitzaehlt, waere eine Auskunft
|
||||
ueber etwas, das man nicht sehen darf.
|
||||
===================================================================== */
|
||||
function fassungenSetzen(liste) {
|
||||
const nachId = new Map(liste.map((d) => [d.id, d]));
|
||||
const ersetztVon = new Map();
|
||||
for (const d of liste) if (d.ersetzt_id) ersetztVon.set(d.ersetzt_id, d.id);
|
||||
|
||||
for (const d of liste) {
|
||||
/* Nach hinten zaehlen, bis keine Vorgaengerin mehr da ist. Die
|
||||
Schranke ist keine Vorsicht, sondern Pflicht: Ein Ring in den
|
||||
Daten (durch einen Fehler anderswo) wuerde die Seite sonst
|
||||
einfrieren, und ein Stillstand ist schlimmer als ein Fehler. */
|
||||
let n = 1;
|
||||
let jetzige = d;
|
||||
const gesehen = new Set([d.id]);
|
||||
while (jetzige.ersetzt_id && nachId.has(jetzige.ersetzt_id)
|
||||
&& !gesehen.has(jetzige.ersetzt_id) && n < 200) {
|
||||
gesehen.add(jetzige.ersetzt_id);
|
||||
jetzige = nachId.get(jetzige.ersetzt_id);
|
||||
n++;
|
||||
}
|
||||
d.fassung = n;
|
||||
d.ersetzt_durch = ersetztVon.get(d.id) ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Liste ------------------------------------------------------- */
|
||||
|
||||
dateienRouter.get("/workspace/api/dateien", (req, res) => {
|
||||
@@ -228,6 +270,7 @@ dateienRouter.get("/workspace/api/dateien", (req, res) => {
|
||||
ORDER BY d.erstellt DESC`).all(...werte);
|
||||
const karte = freigabenZu(liste.map((d) => d.id));
|
||||
for (const d of liste) d.freigaben = karte.get(d.id) || [];
|
||||
fassungenSetzen(liste);
|
||||
res.json({
|
||||
dateien: liste,
|
||||
empfaenger: moeglicheEmpfaenger(req.person),
|
||||
@@ -334,14 +377,36 @@ dateienRouter.post("/workspace/api/dateien",
|
||||
const dateiname = `${Date.now().toString(36)}-${randomBytes(8).toString("hex")}${endung}`;
|
||||
writeFileSync(join(ORDNER, dateiname), req.body, { flag: "wx" });
|
||||
|
||||
/* ---- IST DAS EINE NEUE FASSUNG? (15.09.2026) ------------------
|
||||
|
||||
AUTOMATISCH VERKNUEPFT, NICHT GEFRAGT -- und das ist eine
|
||||
Abwaegung, keine Bequemlichkeit: Wer die Verknuepfung vergisst,
|
||||
hat zwei Dateien gleichen Namens ohne Zusammenhang, und
|
||||
irgendwann laedt jemand die alte herunter. Wer sie faelschlich
|
||||
bekommt, sieht sie sofort ("Fassung 2, ersetzt ...") und kann
|
||||
sie mit einem Klick loesen. Der stille Schaden ist groesser als
|
||||
der sichtbare.
|
||||
|
||||
GENAU EINE Bedingung, und sie ist eng: gleicher Originalname,
|
||||
gleicher Creator-Bezug, und die Vorgaengerin darf nicht selbst
|
||||
schon ersetzt sein. Faende die Abfrage mehrere, wird NICHT
|
||||
geraten -- dann bleibt die Datei eigenstaendig. */
|
||||
const vorgaenger = db().prepare(`
|
||||
SELECT id FROM dateien
|
||||
WHERE name_original = ?
|
||||
AND ((creator_id IS NULL AND ? IS NULL) OR creator_id = ?)
|
||||
AND id NOT IN (SELECT ersetzt_id FROM dateien WHERE ersetzt_id IS NOT NULL)
|
||||
ORDER BY id DESC`).all(name, creatorId, creatorId);
|
||||
const ersetztId = vorgaenger.length === 1 ? vorgaenger[0].id : null;
|
||||
|
||||
const { lastInsertRowid } = db().prepare(`
|
||||
INSERT INTO dateien
|
||||
(name_original, name_datei, groesse, typ, status, notiz,
|
||||
creator_id, hochgeladen_von, erstellt)
|
||||
VALUES (?,?,?,?,'entwurf',?,?,?,?)`).run(
|
||||
creator_id, hochgeladen_von, erstellt, ersetzt_id)
|
||||
VALUES (?,?,?,?,'entwurf',?,?,?,?,?)`).run(
|
||||
name, dateiname, req.body.length,
|
||||
(req.get("content-type") || "application/octet-stream").slice(0, 100),
|
||||
notiz, creatorId, req.person.id, jetzt());
|
||||
notiz, creatorId, req.person.id, jetzt(), ersetztId);
|
||||
|
||||
/* Ausdrueckliche Freigaben. Der Kopf traegt eine Liste von Ids --
|
||||
geprueft wird jede einzelne gegen die erlaubte Auswahl. */
|
||||
@@ -357,7 +422,7 @@ dateienRouter.post("/workspace/api/dateien",
|
||||
detail: `#${lastInsertRowid} ${name} (${Math.round(req.body.length / 1024)} KB)`
|
||||
+ (anzahl ? `, freigegeben fuer ${anzahl}` : "").slice(0, 40),
|
||||
});
|
||||
res.status(201).json({ id: Number(lastInsertRowid) });
|
||||
res.status(201).json({ id: Number(lastInsertRowid), ersetzt_id: ersetztId });
|
||||
} catch (fehler) {
|
||||
if (fehler?.type === "entity.too.large") {
|
||||
return res.status(413).json({ fehler: "Datei ist zu groß (max. 25 MB)." });
|
||||
|
||||
Reference in New Issue
Block a user