Installer: sechs Bildschirme, und eine Datei davor
/install fuehrt von einer leeren Installation zu einer Website: Systempruefung, Datenbank (SQLite oder MySQL, Verbindung sofort geprueft), Migration im Prozess ohne Konsole, Website mit Startseite, Bereichen, Menue sowie Impressum und Datenschutzgeruest, erstes Konto, Abschlussliste. Jeder Schritt schreibt sein Ergebnis sofort, damit ein Neuladen nichts verliert. Gesperrt wird ueber den Zustand der Datenbank - gibt es ein Konto, antwortet /install mit 404. Eine Datei koennte jemand loeschen. Solange nichts installiert ist, fuehrt jede Adresse zum Installer statt zu einem 500 aus einer fehlenden Tabelle. Und weil ohne APP_SECRET nicht einmal die Seite bootet, die das erklaeren wuerde, erzeugt der erste Start das Geheimnis selbst. scripts/install.php ist die einzelne Datei davor: Adresse und SHA-256 stehen darin (vom Buildskript eingetragen), ohne passende Pruefsumme wird nicht entpackt, jeder Archiveintrag wird gegen das Zielverzeichnis geprueft, und danach loescht sie sich selbst. Geprueft: 379 Tests unter PHP 8.2 und 8.4, darunter der ganze Ablauf durch die HTTP-Schicht, neun ZIP-Slip-Varianten, axe ohne Verstoss auf allen Installationsbildschirmen, und das entpackte Releasearchiv startet in den Installer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c3bff7d936
commit
e34b4dca67
25 files changed
+1937
-7
No files matched your search
@@ -129,6 +129,41 @@ final class DemoContent
|
||||
*
|
||||
* @return array<string, array{name: string, document: array}>
|
||||
*/
|
||||
/**
|
||||
* The start page of a fresh installation - not demo content.
|
||||
*
|
||||
* Short on purpose. Somebody who has just installed should find a page they can edit, not a
|
||||
* brochure about the CMS they have to delete first. Exactly one H1, as every page requires.
|
||||
*/
|
||||
public static function starterPage(string $siteName): array
|
||||
{
|
||||
return [
|
||||
'schemaVersion' => 16,
|
||||
'title' => $siteName,
|
||||
'description' => 'NOCH AUSZUFÜLLEN: ein Satz darüber, was es hier gibt. Er erscheint in Suchergebnissen.',
|
||||
'blocks' => [
|
||||
self::section('start-hero', [
|
||||
self::heading('start-heading', $siteName, 1),
|
||||
self::text('start-intro', 'Diese Seite ist der Anfang. Melde dich an, öffne sie im Editor und ersetze diesen Text durch deinen eigenen.'),
|
||||
], 'mint'),
|
||||
self::section('start-body', [
|
||||
self::heading('start-next-heading', 'Die nächsten Schritte', 2),
|
||||
self::text('start-next', "Impressum und Datenschutzerklärung sind bereits angelegt und müssen noch ausgefüllt werden – die Pflichtangaben sind darin markiert.\n\nUnter „Design“ legst du Farben, Schriften und die Breite fest, unter „Menüs“ die Navigation, unter „Einstellungen“ den Namen und die Angaben zum Unternehmen."),
|
||||
], 'paper', 'narrow'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/** Header and footer of a fresh installation, with the site name instead of a wordmark. */
|
||||
public static function starterAreas(string $siteName): array
|
||||
{
|
||||
$areas = self::areas();
|
||||
$areas['header']['document']['blocks'][0]['children'][0]['children'][0]['children'][0]['props']['text'] = $siteName;
|
||||
$areas['footer']['document']['blocks'][0]['children'][0]['children'][0]['children'][0]['props']['text'] = '© '.date('Y').' '.$siteName;
|
||||
|
||||
return $areas;
|
||||
}
|
||||
|
||||
public static function areas(): array
|
||||
{
|
||||
$row = static fn (string $id, array $children): array => [
|
||||
|
||||
Reference in new issue
Block a user