☰

Mein Blog

Kommentare und kleine Umgestaltung

Um das Kommentarsystem zu aktivieren, habe ich den Footer ein wenig umgestaltet. Während zuvor alles in einem details-Element ausgeblendet war, das man erst mit einem Klick öffnen musste, ist der Footer nun offen. Das sperrige Formular zum Kommentieren ist jetzt in einem details-Element. Die summary des details war zuvor eine Datumsangabe. Ich könnte mir vorstellen, dass das von unerfahrenen Internetnutzern auch als Permalink missinterpretiert werden konnte.

Die aktuelle Lösung widerspricht meinem eher minimalistischen Designgedanken doch sehr und ich muss mal schauen, ob mir das nicht irgendwann gegen den Strich geht und ich das wieder zurückbaue.

Das Kommentarsystem selbst liegt in einer Web Componente, die den Inhalt (Kommentare und Form) nachlädt. Ich schätze, das sollte vor den simpelsten SPAM-Bots schützen.

Name, E-Mail oder URL sind optionale Felder. Lediglich der Kommentar ist erforderlich. Ersttäter oder einen freigeschalteten Kommentar müssen vor mir geprüft werden. Identifiziert werden URLs und E‑Mail. Ich habe einfach keine Lust auf Klugscheißer, Idioten und spitzfindige AIs. Und das Internet ist voll davon…

verify-url

Neulich habe ich über die automatisierte Linkpflege geschrieben. Da der Check nun doch mehr als nur ein cURL-Request mit Auswertung des Statuscodes geworden ist, habe ich ein kleines Composer-Päckchen draus geschnürt.

Kein Symfony-Bundle, einfach nur ein paar Klassen:

use Kniebes\VerifyUrl\VerifyUrl;

$verifyUrl = new VerifyUrl();
$result = $verifyUrl->verify('https://example.com/artikel');

if ($result->isValid() && $result->getReplacement() === null) {
    // URL ist in Ordnung
}

if ($result->getReplacement() !== null) {
    // Original ist kaputt, Snapshot aus der Wayback Machine verwenden
    $alternateUrl = $result->getReplacement();
}

foreach ($result->getErrors() as $error) {
    // Gründe, warum die Original-URL verworfen wurde
}

Bei als defekt erkannten Links wird die Wayback Machine nach einer Alternative gefragt. Das Paket hält zwar zwei Sekunden Abstand zwischen den Anfragen, aber wenn man dem Service zu sehr auf den Zwickel geht, kommt trotzdem schnell ein 429. Das muss man außerhalb des Pakets abfangen.

Aktuell gibt es noch ein Problem mit Cloudflare. Wenn der Service vorgeschaltet ist und die Bot-Abfrage von Cloudflare ausgeliefert wird, werden die Seiten als kaputt erkannt und es wird eine Wayback-Machine-Alternative gesucht. Ich denke, da wird noch eine Analyse des Response-Headers notwendig sein, um den Link nicht generell als defekt, sondern als nicht überprüfbar zu markieren.

Immer wieder mal ein Thema: Linkpflege.

Wenn ein Blog schon eine Weile läuft, sammeln sich einige Links an. Und wie das halt so ist, bestehen davon so manche nicht den Test der Zeit. Um über geparkte Domains nicht auf schmuddlige Casino- oder sonstige Spam-Seiten zu verweisen, wäre es natürlich hübsch, die Links regelmäßig mal durchzuchecken.

Also filtere ich alle URLs aus jedem Post und sammle sie in einer Tabelle, die neben der Original-URL eine Ersatz-URL enthält. Das passiert automatisch beim Speichern eines Posts und initial mit einem kleinen Command, der das einmal für alle Posts macht. Ich hatte keine Lust, meine zigtausend Posts alle einmal zu speichern. Ein Cronjob geht nun her und knöpft sich alle halbe Stunde ein paar Hände voll URLs vor, prüft sie und sucht notfalls eine Ersatz-URL. In den meisten Fällen ist das ein Link auf die Wayback Machine.

Beim Generieren der Seiten werden die URLs aus den Posts bei Bedarf gegen ihre Ersatz-URLs getauscht.

Das ist nicht mein erster Versuch, defekten Links Herr zu werden. Aber es ist der erste, der vollkommen automatisch läuft. Und ich kann durch sukzessive Optimierung des Linkchecks die Ergebnisse verbessern, da ich nach Entfernen der Ersatz-URL immer wieder mit der Original-URL testen kann.

return preg_replace_callback(  
    pattern: '~(<a\s[^>]*?href=")(https?://[^"]+)(")~i',  
    callback: fn(array $matches) => isset($this->replacements[$matches[2]])  
        ? $matches[1]  
            .htmlspecialchars(  
                string: $this->replacements[$matches[2]],   
                double_encode: false  
            )  
            .$matches[3]  
        : $matches[0],  
    subject: $html  
) ?? $html;

Alles in allem dreht sich die Ersetzung um einen Aufruf von preg_replace_callback(). $html ist offensichtlich der HTML‑Code und $this->replacements ist ein Array, das die Original-URLs als Schlüssel und den Ersatz als Wert hat. Beim ersten Aufruf wird $this->replacements einmal initialisiert, sodass der Renderer beliebig viele Posts oder Seiten ohne weiteren Query verarbeiten kann.

Blogpost Syndication

In den Details unter dem Blogpost ist der Punkt „Syndication“ hinzugekommen. Es werden Links zu dem Post auf Flickr, standard-reader.app, bsky.app, bubbles.town und ruhr.social (Mastodon) aufgeführt, wenn es denn eine passende Syndikation gibt.

Bis auf bubbles.town lagen alle URLs bereits vor, da die Verteilung auf die Dienste aktiv erfolgt. Bubbles.town ist da etwas anders. Wenn ein Blog dort gelistet ist, wird ein Post automatisch aufgenommen. Die URL zum Post lässt sich recht einfach über den API-Call GET https://bubbles.town/api/vote-count?url=<url-encoded-permalink> ermitteln. Da lediglich die ID benötigt wird, um die URL zu generieren: $url = 'https://bubbles.town/entry/'.$response->id

Bis neulich konnte ich mich bei Standard.site noch mit meiner etwas faulen, numerischen TID noch so durchschlängeln. Ich hatte es mir einfach gemacht und die aufsteigende ID des Blogposts benutzt.

Aber nun nimmt man das AT Protokoll wohl etwas ernster und meine letzten Posts wurden abgelehnt. Ja, mit Recht! Ich weiß. Also habe ich io-atmosphere etwas angepasst und erzeuge jetzt eine passende. Allerdings mache ich die selbst und frage nicht den Server. Das war mir für einen Samstagabend zu viel Theater!

Ich habe den Dark Mode etwas aufgehellt. Nicht so extrem, es ist noch immer ein Dark Mode aber eben kein Amoled Black Dark Mode. :)

Woche 35 · 2026

Für KW34 gab es kein Update. Schade. Ich würde es nachholen, aber ich habe alles aus der Woche vergessen. Gab wohl nichts bemerkenswrtes.


Eine Woche Urlaub ist einfach so verflogen. Ich hätte zwei nehmen sollen. Und nach zwei Wochen hätte ich mir gewünscht, dass ich drei genommen hätte. Ich nehme um meinen Geburtstag herum immer Urlaub. Manchmal mache ich einen kleinen Kurztrip, manchmal auch nicht. Dieses Jahr bin ich zu Hause geblieben. Die Woche war trotzdem recht bunt. Ich war etwas vor der Tür, um mal die neuen Schuhe auszuprobieren: Die Sohle hat ein klasse Profil mit gutem Griff. Die sind insgesamt etwas solider als die Saguaro, was auf längerer Strecke von Vorteil ist. Kein Drücken, kein Nerven. Sind perfekt.


Nicht nur mein Geburtstag ist in diese Woche gefallen, sondern auch das 25. Jubiläum meines Blogs. Und wie ich so über das Blog und seine technische Basis nachdachte, kam mir wieder die Idee, das endlich mal alles ordentlich zu machen. Es blieb nicht bei dem Gedanken und ich habe angefangen, ein Symfony-Projekt aufzusetzen und loszulegen. Ich hätte es gern nur ein wenig ordentlicher und strukturierter. Wobei mir Symfony helfen wird. Getaggte Dienste, Events und Messenger und so was sind schon ungemein praktisch und es hat dem Code des aktuellen Blogs nicht gutgetan, das entweder gar nicht zu haben oder als halbgare, selbstgehäkelte Version.

Es werden wieder zwei getrennte Projekte. Ein Admin-Projekt zur Pflege der Daten und ein Frontend, das letztlich das sichtbare Blog darstellt. Konzeptionell will ich nicht viel ändern. Das Frontend holt sich die Daten für die aufgerufene Seite über eine API vom Admin-Projekt ab und daddelt das Ganze dann mit Templates in einer HTML-Seite zusammen, die im Dateisystem abgelegt wird. Ein SSG im weitesten Sinne, der allerdings auch rein dynamisch arbeiten kann, wenn das Schreiben der HTML‑Seite nicht aktiv ist.

Das Admin-Projekt liegt auf GitHub. Und das Frontend gibt es bisher nicht. Der Spaß befindet sich noch in einem sehr frühen Stadium. Es gibt nicht mal fertige Migrationen. Momentan erstelle ich die Tabellen noch via doctrine:schema:update. Viel mehr als einloggen, Beiträge erstellen und Auflisten geht bislang nicht. Und offensichtlich hatte ich auch noch keinen Grund, eine README‑Datei zu schreiben.

Mein Blog hat heute 25‑jähriges Bestehen.

Der Post Alles neu hat am 24. August 2001 den Grundstein für dieses Blog gelegt. Der Post war eine Zäsur. Ich hatte mich entschlossen, nicht mehr auf Englisch, sondern auf Deutsch zu bloggen. Und es war mein erstes reines CSS-Layout ohne den Gebrauch von Tabellen als Layoutelemente. Das habe ich nicht ganz so konsequent durchgehalten. Soweit ich mich erinnern kann, gab es mal ein paar Table-Layouts. Im Archiv kann man sich durch die Jahre klicken.

Inhaltlich, technisch und gestalterisch hat sich in den 25 Jahren einiges getan. Im Geburtstagspost von 2025 gab es bereits eine kleine Übersicht von aus der Wayback Maschine recoverten Designs. Und die Technik dahinter. Zeitweilig waren die klassischen Blogsysteme wie pMachine, Textpattern und WordPress der Unterbau. Aber wirklich glücklich war ich nur mit meinen eigenen Lösungen.

Links und kleine Anekdoten waren von jeher die Basiszutaten für das Journal. Früher mit starkem Bezug zu Linux. Nach 2005 wurde es etwas Mac-lastiger und mit dem Wechsel von analoger Fotografie in die Welt der digitalen Fotografie kamen mit zunehmendem Ausmaß auch Fotografien hinzu.

Themen für einzelne Posts stammten schon immer von mir. Ich hatte noch nie Lust, mich mit Themen zu befassen, die in irgendwelchen Netzwerken und der Blogosphäre en vogue sind. Ein vereinzeltes Stöckchen oder die freitäglichen Fünf waren hier die Ausnahme.

Es gab schon immer nur ein oder zwei Hände voll Blogs. Und von den Blogs sind in den Jahren leider zu viele auf der Strecke geblieben. Ich freue mich immer wieder, wenn ich sehe, dass Andreas, Dominik, Jan, Marc oder Simon, um nur ein paar zu nennen, etwas Neues posten.

In den vergangenen 25 Jahren gab es einige Z(w)eitblogs, die ich aus einer Laune heraus eröffnet habe und die später aus einer anderen Laune heraus wieder eingestampft wurden. Momentan laufen da der Photostream/das Photoblog und ein Blog, in den der mürrische alte Mann sich auskotzen kann, wenn es denn mal notwendig sein sollte.

Ein Neugiertool

Bisher hatte ich nie ein Logging von Referern. Aber irgendwo bin ich dann noch neugierig. Und so sendet das Blog nun einen kleinen Beacon und teilt einem API‑Endpoint Referer und URL mit. Gespeichert werden nur Referer und die lokale URL. Also kein Profiling oder Fingerprinting. Naja, wie auch mit den paar Daten. Ich habe mich für eine so kleine Lösung entschieden, weil ich die Daten nicht an einen externen Anbieter senden wollte und auch keine Lust hatte, ein Risikotool lokal zu installieren, das letztendlich viel mehr leistet, als ich eigentlich möchte.

Wenn das einem der Langzeitbesucher negativ aufstößt, werde ich das auch gern wieder rauswerfen. Eine kurze Nachricht genügt. Zumal der Nutzen ohnehin nur begrenzt ist, da seit strict-origin-when-cross-origin nur noch die Domain mit Protokoll und ohne Pfad ankommt. Ja, und das ist auch eigentlich gut so.

neue Statistik

Langweilige Interna: Auf der Statistikseite gibt es eine neue Statistik (Top Cast) in Form einer Cloud. Ich habe mich dabei gegen ein SVG entschieden und eine einfache Liste benutzt. Dargestellt wird der über den Movie Tracker gepflegte Cast nach Anzahl seiner Mitwirkung in Filmen. Wenn das mal ausgereift ist, werde ich das auch für die Tag Cloud dieser Seite nutzen können. Momentan befindet sich das Feature noch in einem Branch, da ich noch die Filtermechanik hinzufügen möchte.

Es gibt drei neue Statistiken: Last.fm-Scrobbles pro Jahr, Anzahl Filme vs. Serien und Anzahl Filme vs. Episoden.

Zahlen & Fakten

Ich wollte schon immer eine Statistik-Seite haben und nun ist sie da. Erst mal mit drei grundlegenden Grafiken, um verschiedene Diagrammformen auszuprobieren.

  • Posts je Jahr als horizontales Diagramm.
  • Posts je Typ als vertikales Diagramm.
  • Posts je Monat wären weder horizontal noch vertikal praktikabel gewesen. Also habe ich mich für eine Heatmap entschieden.

Bei der Gelegenheit musste ich mich ein wenig mit SVG auseinandersetzen und habe die Diagramme dann auch vollständig als SVG generiert. Keine externen Pakete oder irgendein fancy stuff. Nur ein bisschen rechnen. Sogar mit einer hübschen Farbumkehr im Darkmode.


Update: Es sind noch zwei Charts hinzugekommen. Posts je Stunde und Posts je Wochentag, und es hat sich herausgestellt, dass ich gar nicht mal so ein extremer Wochenendblogger bin, wie ich vermutet hatte.

Tagebuch

Vor einer Weile habe ich angefangen, ein digitales Offline-Tagebuch zu schreiben. Also schon in digitaler Form auf einem Rechner, aber nicht im Web veröffentlicht. Das hat zur Folge, dass wesentlich weniger Hirnfürze hier im Blog landen. Und das ist gut.

Inhaltlich geht es im Großen und Ganzen um Fakten zum Tag und um Gespräche oder Ereignisse, an die ich mich später mal erinnern möchte oder muss. Nichts, das zu privat ist.

Die Texte schreibe ich in Markdown mit einer Art Frontmatter. Jeden Tag erstelle ich eine Datei, e. g. 2026-07-18.md, und schreibe dort mehr oder weniger ungeordnet rein. Zettlr hat sich als das ultimative Schreibwerkzeug erwiesen und hat sogar eine LanguageTool-API-Integration.

Plaintext, was Markdown ja letztendlich ist, erscheint mir als nachhaltiges Format, um Texte längerfristig zu speichern. Wer schon das eine oder andere Jahrzehnt bloggt, hat sich vielleicht auch schon mal Gedanken darüber gemacht, ob es nachhaltig ist, ein Blog ausschließlich in einer Datenbank zu halten.

Damit ich von jedem Gerät aus schreiben kann, lagern die Texte in einem privaten Git-Repository. Um den Spaß bequem lesen zu können, gibt es noch ein kleines Git-PHP-Projekt zur Ausgabe des Tagebuchs im Browser.