Vorschaubild beim Teilen, robots.txt als Bot-Steuerung
Das Feld "Vorschaubild beim Teilen" gab es seit dem SEO-Paket, ausgegeben wurde es nie: keine mit KantSites gebaute Seite hatte ein og:image. Es wird jetzt aufgeloest - erst das gesetzte Vorschaubild, sonst das Beitragsbild - als absolute Adresse bis 1600 px, dazu og:site_name, og:locale, og:image:alt und twitter:card (klein ohne Bild, gross mit). Dasselbe Bild steht im strukturierten Datensatz eines Beitrags; Archive haben jetzt ebenfalls Open-Graph-Angaben. Eigene robots.txt-Zeilen: 200 statt 30, und Leerzeilen bleiben als Trenner erhalten - Regeln je Bot (welche KI-Crawler lesen duerfen) brauchen Bloecke, nicht einen Klumpen. Behoben: die Symbolerzeugung suchte feste Breiten statt der Varianten, die das Bild wirklich hat - ein 192 px grosses Zeichen, also der haeufigste Fall, scheiterte damit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f2ef2a4901
commit
35d8270afd
10 files changed
+137
-10
No files matched your search
+52
-1
@@ -877,6 +877,45 @@ final class CmsTest extends TestCase
|
||||
self::assertStringContainsString('Nächstes Projekt', $rand);
|
||||
}
|
||||
|
||||
public function testThePreviewPictureForSharingActuallyReachesTheHead(): void
|
||||
{
|
||||
// Das Feld „Vorschaubild beim Teilen" gab es seit dem SEO-Paket - ausgegeben wurde es
|
||||
// nie. Eine Einstellung, die speichert und nichts tut, ist schlimmer als keine.
|
||||
$media = new MediaLibrary($this->db, $this->path.'-media');
|
||||
$file = tempnam(sys_get_temp_dir(), 'kant-teilen-');
|
||||
$image = imagecreatetruecolor(1800, 1000);
|
||||
imagepng($image, $file);
|
||||
imagedestroy($image);
|
||||
$item = $media->upload(new UploadedFile($file, 'vorschau.png', test: true));
|
||||
@unlink($file);
|
||||
$media->update($item['id'], ['title' => 'Vorschau', 'alt' => 'Ein Blick auf die Website', 'archived' => false, 'version' => 1]);
|
||||
|
||||
$this->pages->publish('page-1', $this->pages->saveDraft('page-1', DemoContent::starter('Startseite'), 0), 1);
|
||||
|
||||
// Ohne Bild: die kleine Karte, kein Verweis auf ein Bild, das es nicht gibt.
|
||||
$ohne = $this->request('/')->getContent();
|
||||
self::assertStringNotContainsString('og:image', $ohne);
|
||||
self::assertStringContainsString('<meta name="twitter:card" content="summary">', $ohne);
|
||||
self::assertStringContainsString('<meta property="og:locale" content="de_DE">', $ohne);
|
||||
|
||||
$this->db->update('page', ['og_media_id' => $item['id']], ['id' => 'page-1']);
|
||||
$html = $this->request('/')->getContent();
|
||||
// Absolut, weil die Adresse in fremden Diensten landet, und höchstens 1600 px breit.
|
||||
self::assertStringContainsString('<meta property="og:image" content="https://cms.test/media/'.$item['id'].'/1600">', $html);
|
||||
self::assertStringContainsString('<meta property="og:image:alt" content="Ein Blick auf die Website">', $html);
|
||||
self::assertStringContainsString('<meta name="twitter:card" content="summary_large_image">', $html);
|
||||
|
||||
// Ein Beitrag ohne eigenes Vorschaubild nimmt sein Beitragsbild.
|
||||
$id = $this->publishedPost('mit-bild', 'Mit Bild', '2026-02-01', ['Projekte'], []);
|
||||
$this->posts->saveMetadata($id, ['featuredMediaId' => $item['id'], 'publishedAt' => '2026-02-01'], ['Projekte'], [], $this->pages->findForEditor($id)['version']);
|
||||
$page = $this->pages->findForEditor($id);
|
||||
$this->pages->publish($id, $page['draft_revision_id'], $page['version']);
|
||||
$beitrag = $this->request('/mit-bild')->getContent();
|
||||
self::assertStringContainsString('<meta property="og:image" content="https://cms.test/media/'.$item['id'].'/1600">', $beitrag);
|
||||
// Und dasselbe Bild steht im strukturierten Datensatz des Beitrags.
|
||||
self::assertStringContainsString('"image":"https://cms.test/media/'.$item['id'].'/1600"', $beitrag);
|
||||
}
|
||||
|
||||
public function testTheSiteIconIsMadeFromAPictureOfTheLibrary(): void
|
||||
{
|
||||
// Umut: „dann brauchen wir Favicon-Möglichkeit zum Festlegen“. Aus einem Bild der
|
||||
@@ -909,13 +948,25 @@ final class CmsTest extends TestCase
|
||||
self::assertSame([32, 32], [$size[0], $size[1]], 'Quadratisch, nicht gestaucht.');
|
||||
self::assertSame(200, $this->request('/apple-touch-icon.png')->getStatusCode());
|
||||
|
||||
// Ein winziges quadratisches Zeichen hat keine einzige der vier Regelbreiten - genau so
|
||||
// sieht ein echtes Favicon aus, und genau daran ist die erste Fassung gescheitert.
|
||||
$klein = tempnam(sys_get_temp_dir(), 'kant-zeichen-');
|
||||
$zeichen = imagecreatetruecolor(192, 192);
|
||||
imagepng($zeichen, $klein);
|
||||
imagedestroy($zeichen);
|
||||
$kleines = $media->upload(new UploadedFile($klein, 'zeichen.png', test: true));
|
||||
@unlink($klein);
|
||||
self::assertSame([192], array_column($kleines['variants'], 'width'));
|
||||
$this->site->save(['country' => 'DE', 'homePath' => 'home', 'faviconMediaId' => $kleines['id']]);
|
||||
self::assertSame(200, $this->request('/favicon.png')->getStatusCode());
|
||||
|
||||
// Ein Bild, das es nicht gibt, wird nicht gespeichert - sonst zeigte der Kopf ins Leere.
|
||||
try {
|
||||
$this->site->save(['country' => 'DE', 'homePath' => 'home', 'faviconMediaId' => str_repeat('a', 32)]);
|
||||
self::fail('Eine unbekannte Bildkennung muss abgelehnt werden.');
|
||||
} catch (InvalidPage) {
|
||||
}
|
||||
self::assertSame($item['id'], $this->site->load()['faviconMediaId']);
|
||||
self::assertSame($kleines['id'], $this->site->load()['faviconMediaId']);
|
||||
}
|
||||
|
||||
public function testUnderlinedLinksAreAChoiceAndOnlyApplyToRunningText(): void
|
||||
|
||||
Reference in new issue
Block a user