Move site content between installations as a package

System now exports a content package and imports one, by upload or from
var/pakete. Pages with their revisions, areas, menus, media, forms,
redirects, terms, translations, design and settings are replaced;
submissions, statistics, tasks, plugin state and service keys stay, and
accounts only come along when that is chosen explicitly. A backup comes
first, the site is in maintenance meanwhile, and every file and document
in the package is checked before anything is written.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
kantarciandClaude Opus 5.5 committed 2026-10-12 16:31:06 +02:00
1 parent a57b829a15
commit 32e42f973b
13 files changed
+973 -2

No files matched your search

+14
View File
@@ -2,6 +2,20 @@
Stand: 12.10.2026. Arbeitstitel KantSites; Produktname offen.
## ADR 118 – Inhaltspakete: Export und Import ohne die Konten (12.10.2026)
Anlass: Umut: „Können wir sowas wie einen Importer einbauen, weil sich nach jeder Anpassung sonst das Passwort ändert? Wo man auswählen muss, ob Passwort/Benutzer überschrieben werden sollen. Gerne auch als Plugin.“ Bisher wurde eine lokal gebaute Website als ganze SQLite-Datei hochgeladen – samt Konten (neues Passwort nach jedem Neuaufbau) und ohne das, was nur live entstanden war (Anfragen, Statistik).
**Entscheidung: im Kern, nicht als Plugin.** Plugins führen bewusst keinen eigenen Servercode aus (ADR zum Pluginsystem; serverseitiger Code nur mit Isolation). Ein Import schreibt Datenbank und Medienordner – das gehört in den Kern, unter System, nur für Administratoren.
**Paket.** Zip mit `paket.json` (Art, Format 1, Fassung, Website, Konten ja/nein, Zahlen, SHA-256 von `inhalt.json`), `inhalt.json` (Zeilen je Tabelle) und `medien/` (Bildvarianten, PDFs, erzeugte Seitensymbole). JSON statt SQLite-Datei: Ein Paket geht von SQLite nach MySQL und zurück, und jede Zeile wird vor dem Schreiben geprüft. Inhalt: `page`, `revision`, `partial`, `menu`, `media`, `document`, `form`, `redirect`, `term`, `page_term`, `page_translation` und die Website (Name, Sprache, Gestaltung, Einstellungen). **Nicht** darin: Anfragen, Statistik, Aufgaben, Plugin-Stand, Migrationsliste und die Zugangsdaten zu Diensten (`turnstile`, `turnstileSiteKey`, `turnstileSecret`). Konten nur mit „Konten mitnehmen“ beim Export.
**Import (`App\Ops\SitePackage::import`).** Prüfen vor Schreiben: nur bekannte Einträge im Zip (keine Pfade nach oben, keine anderen Dateitypen, Größengrenzen gegen Zip-Bomben), Prüfsumme von `inhalt.json`, nur bekannte Tabellen und Spaltennamen, jedes Seiten- und Bereichsdokument durch `DocumentValidator`, Paket nicht aus einer neueren Fassung. Dann Sicherung (`vor Import`), Wartungszustand, Inhalte in einer Transaktion ersetzen (nur Spalten, die es hier gibt – ein älteres Paket passt so in ein neueres Schema), Website-Einstellungen übernehmen außer den Dienst-Zugangsdaten, Anfragen über die Formularadresse dem neuen Formular zuordnen, Medienordner ersetzen. **Konten nur mit eigenem Häkchen** („Konten und Passwörter aus dem Paket übernehmen“); ohne bleiben Benutzer und Passwörter der Website. Ein Häkchen bei einem Paket ohne Konten bricht ab, ohne etwas zu ändern.
**Oberfläche.** System → „Inhalte übertragen“: Export als Download (Datei wird nach dem Senden gelöscht), Import per Upload oder aus `var/pakete/` (für Pakete über der Upload-Grenze des Hostings, die angezeigt wird), Bestätigungshäkchen, Ergebnisseite mit den Schritten und einem Hinweis, falls Schriften der neuen Gestaltung auf dem Server fehlen. Konsole: `cms:paket:export [--mit-benutzern] [--ziel=…]`, `cms:paket:import <datei> [--mit-benutzern] [--ja]`.
**Folgen.** Das Zurückspielen einer Sicherung bleibt Konsolensache; der Import ist der Weg für Inhalte aus einer anderen Installation, mit eigener Sicherung davor. Reset-Skripte können ein Paket schreiben (zuerst `reset-huefner.sh` → `var/huefner-paket.zip`). Keine Migration.
## ADR 117 – Menü schließt beim Klick daneben, Netzwerk-Symbole, schlankere Symbole (12.10.2026)
Anlass: Umut: „Bei Mobilmenü, wenn es aufgeklappt ist, schließt es nicht, wenn man irgendwo anders hinklickt.“ Und für Hüfner: Facebook, Instagram und „Angebot anfragen“ als Menü-Buttons statt eigener Knöpfe im Kopf.
+8
View File
@@ -1,5 +1,13 @@
# Projektstatus
## Fassung 1.32.0: Inhaltspakete – Export und Import ohne die Konten (12.10.2026)
**Anlass:** Umut: Nach jedem Hochladen einer neu aufgebauten Datenbank galt live ein anderes Passwort; gewünscht ist ein Importer mit der Wahl, ob Benutzer/Passwort überschrieben werden („gerne auch als Plugin“).
**Neu im Kern (ADR 118):** System → „Inhalte übertragen“: Paket exportieren (Download, Konten nur mit Häkchen) und importieren (Upload oder aus `var/pakete/`, Upload-Grenze des Hostings angezeigt). Ersetzt werden Seiten mit Fassungen, Bereiche, Menüs, Medien/PDFs, Formulare, Weiterleitungen, Kategorien, Übersetzungen, Gestaltung und Einstellungen; Anfragen (neu dem Formular zugeordnet), Statistik, Aufgaben, Plugin-Stand und Turnstile-Zugangsdaten bleiben; Konten nur mit eigenem Häkchen. Vorher Sicherung, währenddessen Wartung, jede Datei und jedes Dokument geprüft. Konsole: `cms:paket:export`, `cms:paket:import`. Im Kern statt als Plugin, weil Plugins keinen Servercode ausführen. `reset-huefner.sh` schreibt zuletzt `var/huefner-paket.zip`.
**Geprüft:** PHPUnit vollständig 665 grün (neu `SitePackageTest`: Import ersetzt Inhalte und behält Konten, Anfragen, Dienst-Zugangsdaten; Konten nur mit beiden Häkchen; neuere Fassung abgelehnt; fünf manipulierte Pakete ändern nichts – fremde Datei mit `../`, Skript als Medium, veränderter Inhalt, fremde Tabelle, ungültiges Dokument). `npm test` 41, `lint:twig`, php-cs-fixer. Durchlauf auf der Konsole: Hüfner exportiert und in eine veränderte Kopie importiert (Passwort, Anfrage, Turnstile blieben, Alpakaseite kam zurück, Medienordner ersetzt). Oberfläche in der lokalen Hüfner-Verwaltung: zu großer Upload mit Hinweis abgelehnt, „Konten übernehmen“ bei Paket ohne Konten abgebrochen, Import aus `var/pakete/` abgeschlossen, danach weiter angemeldet. `test:editor` grün inklusive axe der Systemseite – ein erster Lauf parallel zu PHPUnit scheiterte, der Wiederholungslauf allein bestand.
## Fassung 1.31.0: Menü schließt beim Klick daneben, Netzwerk-Symbole; Hüfner-Kopf (12.10.2026)
**Anlass:** Umut: aufgeklapptes Mobilmenü schließt nicht beim Klick daneben; bei Hüfner wirkt der Kopf auf dem iPad verloren (Menü-Knopf mittig), und Facebook, Instagram und „Angebot anfragen“ sollen ins Menü.