Umut: "Beim Startseitenbild wird der Text & umgewandelt, aber in der Ansicht nach dem Speichern wird & daraus." RichText::sanitise() treated text without a single tag as plain text and only escaped it. What the editor sends is HTML, tag or no tag - so "Freudenstadt & Umgebung" gained one level of encoding on every save (&amp;, &amp;amp; ...) until the entity stood on the page for everyone to read. The draft of the kant-it.de home page had already reached three levels. The same branch carried a second fault of the same kind: nl2br() left the newline behind the <br>, and the next pass turned it into a second <br> via breakLines() - one paragraph became two. The branch now decodes first and replaces the line break instead of adding to it, so it ends up with the same representation as the DOM branch and both are stable under repeated saves. scripts/repair-doubled-entities.php cleans up what is already stored: the published and draft revision of every page and the areas, with a dry run, each change checked against the validator. Older revisions stay as history. DEEG: favicon is now its own square mark - the roof gable from the logo, white on the logo blue, rendered from logo-deeg.png - instead of the centre crop of the wide logo, which showed the "EE" of the wordmark. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
100 lines
4.2 KiB
PHP
100 lines
4.2 KiB
PHP
<?php
|
||
|
||
/**
|
||
* Baut das Favicon der DEEG-Installation aus dem Logo und setzt es.
|
||
*
|
||
* Bisher zeigte das Symbol auf das Logo selbst. Das Logo ist 209x100 – der Kern schneidet daraus
|
||
* mittig ein Quadrat, und übrig blieb das „EE“ aus dem Schriftzug. Ein Favicon wird bei 16 px
|
||
* gelesen, also braucht es ein eigenes, quadratisches Zeichen: ein Dachgiebel aus dem Logo, weiß
|
||
* auf Logo-Blau. Das Dach ist das Markenzeichen und bleibt auch klein eine Silhouette; der
|
||
* flächige blaue Grund trägt es auf hellen wie dunklen Browserleisten.
|
||
*
|
||
* Gerendert wird aus `logo-deeg.png`, damit das Symbol bei einem neuen Logo neu entsteht statt
|
||
* als Fremdkörper in der Mediathek zu liegen. Läuft nur gegen die DEEG-Datenbank.
|
||
*/
|
||
require dirname(__DIR__).'/config/bootstrap.php';
|
||
|
||
use App\Content\MediaLibrary;
|
||
use App\Content\SiteSettings;
|
||
use App\Infrastructure\ConnectionFactory;
|
||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||
|
||
$root = dirname(__DIR__);
|
||
$url = str_replace('%kernel.project_dir%', str_replace('\\', '/', $root), (string) $_SERVER['DATABASE_URL']);
|
||
if (!str_contains($url, 'immobilien-deeg')) {
|
||
fwrite(STDERR, "Sicherung: DATABASE_URL muss auf die DEEG-Datenbank zeigen.\n");
|
||
exit(1);
|
||
}
|
||
$quelle = $argv[1] ?? $root.'/var/immobilien-deeg-assets/logo-deeg.png';
|
||
if (!is_file($quelle)) {
|
||
fwrite(STDERR, "Logo nicht gefunden: $quelle\n");
|
||
exit(1);
|
||
}
|
||
|
||
// Der linke Giebel des Doppeldachs im Logo. Die Maße stammen aus dem Bild selbst: das Dach liegt
|
||
// über dem Schriftzug, x 3-203, y 0-40; die Hälfte davon ist ein Giebel.
|
||
$AUSSCHNITT = ['x1' => 3, 'y1' => 0, 'x2' => 103, 'y2' => 40];
|
||
$BLAU = [0, 129, 199]; // #0081c7, das Logo-Blau
|
||
$KANTE = 512; // Vorlage für alle Ableitungen des Kerns (16 bis 180)
|
||
$RAND = 0.06; // wenig Luft: klein gelesen zählt jede Fläche
|
||
|
||
$logo = imagecreatefrompng($quelle);
|
||
$w = $AUSSCHNITT['x2'] - $AUSSCHNITT['x1'] + 1;
|
||
$h = $AUSSCHNITT['y2'] - $AUSSCHNITT['y1'] + 1;
|
||
$zeichen = imagecreatetruecolor($w, $h);
|
||
imagealphablending($zeichen, false);
|
||
imagesavealpha($zeichen, true);
|
||
imagefilledrectangle($zeichen, 0, 0, $w, $h, imagecolorallocatealpha($zeichen, 0, 0, 0, 127));
|
||
for ($y = 0; $y < $h; ++$y) {
|
||
for ($x = 0; $x < $w; ++$x) {
|
||
// Das Logo ist ein Palettenbild; imagecolorsforindex beantwortet Palette und Truecolor.
|
||
$c = imagecolorsforindex($logo, imagecolorat($logo, $AUSSCHNITT['x1'] + $x, $AUSSCHNITT['y1'] + $y));
|
||
$hell = $c['red'] > 230 && $c['green'] > 230 && $c['blue'] > 230;
|
||
if ($c['alpha'] > 100 || $hell) {
|
||
continue;
|
||
}
|
||
imagesetpixel($zeichen, $x, $y, imagecolorallocate($zeichen, 255, 255, 255));
|
||
}
|
||
}
|
||
imagedestroy($logo);
|
||
|
||
$symbol = imagecreatetruecolor($KANTE, $KANTE);
|
||
imagealphablending($symbol, false);
|
||
imagesavealpha($symbol, true);
|
||
imagefilledrectangle($symbol, 0, 0, $KANTE, $KANTE, imagecolorallocate($symbol, $BLAU[0], $BLAU[1], $BLAU[2]));
|
||
imagealphablending($symbol, true);
|
||
$platz = (int) round($KANTE * (1 - 2 * $RAND));
|
||
$faktor = min($platz / $w, $platz / $h);
|
||
$zw = (int) round($w * $faktor);
|
||
$zh = (int) round($h * $faktor);
|
||
imagecopyresampled($symbol, $zeichen, (int) (($KANTE - $zw) / 2), (int) (($KANTE - $zh) / 2), 0, 0, $zw, $zh, $w, $h);
|
||
imagedestroy($zeichen);
|
||
|
||
$datei = sys_get_temp_dir().'/favicon-deeg.png';
|
||
imagepng($symbol, $datei, 9);
|
||
imagedestroy($symbol);
|
||
echo "Symbol gerendert: {$KANTE}x{$KANTE}\n";
|
||
|
||
$db = ConnectionFactory::create($url);
|
||
$media = new MediaLibrary($db, str_replace('%kernel.project_dir%', $root, (string) $_SERVER['MEDIA_DIRECTORY']));
|
||
$settings = new SiteSettings($db, $media);
|
||
|
||
$eintrag = $media->upload(new UploadedFile($datei, 'favicon-deeg.png', null, null, true));
|
||
$media->update($eintrag['id'], [
|
||
'title' => 'Favicon DEEG',
|
||
'alt' => 'Dachgiebel aus dem DEEG-Logo, weiß auf Blau',
|
||
'archived' => false,
|
||
'version' => $eintrag['version'],
|
||
]);
|
||
@unlink($datei);
|
||
echo "In der Mediathek: {$eintrag['id']}\n";
|
||
|
||
$aktuell = $settings->load();
|
||
$settings->save([
|
||
...$aktuell,
|
||
// Gespeichert als Liste, erwartet als Textfeld - wie es der Einstellungsbildschirm schickt.
|
||
'sameAs' => implode("\n", $aktuell['sameAs']),
|
||
'faviconMediaId' => $eintrag['id'],
|
||
]);
|
||
echo "Favicon gesetzt; der Kern hat 32 px (Browserleiste) und 180 px (Apple-Touch) daraus gerendert.\n";
|