<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title># notACMS</title>
        <link>https://notacms.holas.pl/de/</link>
        <description><![CDATA[AI-friendly static site generator. Zero database. Pure Markdown.]]></description>
        <language>de</language>
        <atom:link href="https://notacms.holas.pl/de/feed/" rel="self" type="application/rss+xml"/>
                        <lastBuildDate>Sat, 13 Jun 2026 00:00:00 +0000</lastBuildDate>
                        <item>
            <title><![CDATA[notACMS 1.2.1 — Charset-Fix, präzisere Build-Warnungen, neue Dokumentationsseiten]]></title>
            <link>https://notacms.holas.pl/de/beitraege/release-1-2-1/</link>
            <guid isPermaLink="true">https://notacms.holas.pl/de/beitraege/release-1-2-1/</guid>
                        <pubDate>Sat, 13 Jun 2026 00:00:00 +0000</pubDate>
                        <description><![CDATA[Was ist neu in 1.2.1 Ein kleines Follow-up zu 1.2.0 mit zwei Bugfixes und erweiterter Dokumentation. nginx Charset Plaintext-Antworten — /llms.txt, /robots.txt — wurden ohne Charset-Deklaration ausgeliefert. Browser stellten Nicht-ASCII-Zeichen als Kauderwelsch dar. charset utf-8; ist jetzt auf Server-Block-Ebene in docker/nginx.conf.template gesetzt, sodass jeder Antworttyp automatisch die korrek…]]></description>
            <content:encoded><![CDATA[<h2>Was ist neu in 1.2.1<a id="was-ist-neu-in-121" href="#was-ist-neu-in-121" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Ein kleines Follow-up zu 1.2.0 mit zwei Bugfixes und erweiterter Dokumentation.</p>
<h2>nginx Charset<a id="nginx-charset" href="#nginx-charset" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Plaintext-Antworten — <code>/llms.txt</code>, <code>/robots.txt</code> — wurden ohne Charset-Deklaration ausgeliefert. Browser stellten Nicht-ASCII-Zeichen als Kauderwelsch dar. <code>charset utf-8;</code> ist jetzt auf Server-Block-Ebene in <code>docker/nginx.conf.template</code> gesetzt, sodass jeder Antworttyp automatisch die korrekte Deklaration erhält.</p>
<h2>Mehrdeutige Directory-Key-Warnung<a id="mehrdeutige-directory-key-warnung" href="#mehrdeutige-directory-key-warnung" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Die Warnung &quot;Ambiguous directory key&quot; wurde bisher zur Zeit des Baum-Aufbaus ausgelöst — in dem Moment, wenn ein zweites Content-Element mit demselben Basename registriert wurde — unabhängig davon, ob dieser kurze Key jemals in einem Template tatsächlich verwendet wurde.</p>
<p>Die Warnung wird jetzt auf den tatsächlichen Lookup verschoben: Sie erscheint einmalig und nur dann, wenn <code>content_item()</code> oder <code>content_url()</code> mit einem mehrdeutigen Kurz-Key aufgerufen wird. Sites mit Namenskollisionen in verschiedenen Content-Abschnitten sehen keine überflüssigen Warnungen mehr für Keys, die sie nie referenzieren.</p>
<h2>Zwei neue Dokumentationsseiten<a id="zwei-neue-dokumentationsseiten" href="#zwei-neue-dokumentationsseiten" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Das Demo-Theme hat jetzt Seiten für <strong>Theme-Entwicklung</strong> und <strong>Sicherheit</strong>, verfügbar in allen vier Sprachversionen.</p>
<p><strong><a href="/de/theme-entwicklung/">Theme-Entwicklung</a></strong> behandelt das vollständige Template-Schichten-Modell — wie <code>local/templates/</code>, registrierte Theme-Pfade und das Bare-Core nach Priorität aufgelöst werden — sowie eine Referenz für Template-Kontext-Verträge, Twig-Globals, Funktionen und Filter, die ContentItem-API, erforderliche Übersetzungsschlüssel und die sechs Portabilitätsregeln, die jedes Theme befolgen sollte.</p>
<p><strong><a href="/de/sicherheit/">Sicherheit</a></strong> erklärt das Bedrohungsmodell, das die Sicherheitsentscheidungen von notACMS prägt: keine Datenbank, kein dynamisches Rendering, eine Build-Pipeline, die niemals Content ausführt. Die Seite behandelt die eingebauten Schutzmaßnahmen (Turnstile-Hostname-Validierung, Path-Traversal-Schutz, strikte Typen), das bewusste CSRF-Design beim Kontaktformular und die Deployment-Best-Practices für eine Produktionsinstanz.</p>
<p>Beide Seiten erscheinen im Dokumentations-Dropdown der Navigation und in der Seitenleiste.</p>
<h2>Vollständiger Changelog<a id="vollständiger-changelog" href="#vollständiger-changelog" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p><a rel="nofollow noopener noreferrer" target="_blank" href="https://github.com/holas1337/notACMS/blob/main/CHANGELOG.md#121---2026-06-13">CHANGELOG.md</a></p>
]]></content:encoded>
                                    <category><![CDATA[releases]]></category>
                                    <category><![CDATA[release]]></category>
                        <category><![CDATA[announcement]]></category>
                    </item>
                <item>
            <title><![CDATA[notACMS 1.2.0 — das Audit-Release]]></title>
            <link>https://notacms.holas.pl/de/beitraege/release-1-2-0/</link>
            <guid isPermaLink="true">https://notacms.holas.pl/de/beitraege/release-1-2-0/</guid>
                        <pubDate>Fri, 12 Jun 2026 00:00:00 +0000</pubDate>
                        <description><![CDATA[Das Audit-Release 1.2.0 ist das Ergebnis eines vollständigen Audits von notACMS — Architektur, Sicherheit, Codequalität und beide Template-Bäume. Rund 170 Findings wurden behoben. Die Highlights: Robustheit Eine Markdown-Datei mit kaputtem Frontmatter legt nicht mehr eine ganze Sprache oder den Build lahm — sie wird übersprungen und mit Pfad und Fehler gemeldet. Ein fehlendes slug: kann die Starts…]]></description>
            <content:encoded><![CDATA[<h2>Das Audit-Release<a id="das-audit-release" href="#das-audit-release" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>1.2.0 ist das Ergebnis eines vollständigen Audits von notACMS — Architektur, Sicherheit, Codequalität und beide Template-Bäume. Rund 170 Findings wurden behoben. Die Highlights:</p>
<h3>Robustheit<a id="robustheit" href="#robustheit" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<ul>
<li>Eine Markdown-Datei mit kaputtem Frontmatter legt nicht mehr eine ganze Sprache oder den Build lahm — sie wird übersprungen und mit Pfad und Fehler gemeldet.</li>
<li>Ein fehlendes <code>slug:</code> kann die Startseite nicht mehr still übernehmen; <code>posts_per_page: 0</code> crasht den Blog nicht mehr; doppelte URLs, mehrdeutige Verzeichnisschlüssel und ungültige Tag-Werte erzeugen klare Build-Warnungen.</li>
<li>Entwürfe und geplante <strong>Seiten</strong> werden jetzt wirklich aus Builds, Menüs und <code>/llms.txt</code> ausgeschlossen — bisher wurden nur Beiträge gefiltert.</li>
<li><code>app:build -o &lt;dir&gt;</code> weigert sich ohne <code>--force</code>, ein anderes Verzeichnis als das konfigurierte Static-Verzeichnis zu leeren.</li>
<li>Der Aufruf von <code>/pl/</code> beim ersten Besuch leitet nicht mehr auf die englische Version um — die URL gilt als Sprachauswahl und ein Cookie wird gesetzt. Das Locale-Cookie ist jetzt auch <code>Secure</code>-bedingt, sodass der Mechanismus in HTTP-Entwicklungsumgebungen funktioniert.</li>
<li>Suchergebnis-Ausschnitte zeigen jetzt hervorgehobene Begriffe als echte <code>&lt;mark&gt;</code>-Markierungen statt als wörtlichen <code>&amp;lt;mark&amp;gt;</code>-Text.</li>
<li>Das bare Starter-Theme enthält jetzt Platzhalter-<code>contact_form</code>-Werte in <code>_site.yaml</code> — kein Build-Fehler mehr bei einer Neuinstallation.</li>
</ul>
<h3>Sicherheit<a id="sicherheit" href="#sicherheit" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<ul>
<li>ImageMagick läuft über <code>Symfony\Process</code> mit Argument-Arrays — <code>exec()</code> ist Geschichte.</li>
<li>Turnstile prüft jetzt den Antwort-Hostname gegen die <code>base_url</code> und loggt einen Fehler, wenn die mitgelieferten Test-Keys in Produktion aktiv sind. Ein Boot-Check warnt bei Platzhalter-<code>APP_SECRET</code>.</li>
<li>JSON-LD-Output ist hex-escaped (<code>&lt;/script&gt;</code> in einem Titel bricht nicht mehr aus), Übersetzungskataloge enthalten kein HTML mehr, und die nginx-Security-Header gelten jetzt auch für <code>/assets/</code>- und <code>/media/</code>-Antworten.</li>
<li>Die Kontakt-API mit Sprachpräfix war im Docker-Runtime-Deployment wegen eines nginx-Regex-Bugs unerreichbar — behoben.</li>
</ul>
<h3>Für Theme-Entwickler<a id="für-theme-entwickler" href="#für-theme-entwickler" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<ul>
<li>Neu: <a rel="nofollow noopener noreferrer" target="_blank" href="https://github.com/holas1337/notACMS/blob/main/docs/THEME_BUILDING.md">THEME_BUILDING.md</a> — der vollständige Theme-API-Vertrag: Template-Kontexte pro Route, Twig-Funktionen und -Globals, die ContentItem-API und die Übersetzungsschlüssel, die jedes Theme definieren muss.</li>
<li><code>#[LocalizedRoute]</code> funktioniert jetzt in <code>local/src/Controller/</code>, der Sprachwechsel funktioniert korrekt auf Seiten mit 3+ Sprachen, und die Static-Build-Pipeline ist in wiederverwendbare Services zerlegt.</li>
</ul>
<h3>Neu<a id="neu" href="#neu" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<ul>
<li>Der Static Build erzeugt jetzt eine <code>/llms.txt</code>-Datei pro Sprache — die neuesten Beiträge in einem maschinenlesbaren Format für LLM-Kontextfenster. Konfigurierbar über <code>llms_limit</code> in <code>_site.yaml</code> (Standard: 5). Das Template ist per-Theme über den <code>@base</code>-Twig-Namespace überschreibbar.</li>
</ul>
<h3>Breaking Changes<a id="breaking-changes" href="#breaking-changes" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<p>Einige — jede mit einzeiliger Migration. <code>directoryKey()</code> liefert volle Content-Pfade (Basename-Lookups funktionieren weiter), <code>getTree()</code> ist nach <code>ContentTreeProviderInterface</code> umgezogen, <code>blogPosting()</code> nimmt eine benannte Map, der Kontext-Schlüssel <code>lang_switch_url</code> ist weg, vier tote Übersetzungsschlüssel wurden entfernt und das <code>old-template</code>-Paket aus 1.0 wurde eingestellt. Der vollständige Guide: <a rel="nofollow noopener noreferrer" target="_blank" href="https://github.com/holas1337/notACMS/blob/main/UPGRADE-1.2.md">UPGRADE-1.2.md</a>.</p>
]]></content:encoded>
                                    <category><![CDATA[releases]]></category>
                                    <category><![CDATA[release]]></category>
                        <category><![CDATA[security]]></category>
                        <category><![CDATA[refactoring]]></category>
                    </item>
                <item>
            <title><![CDATA[notACMS 1.1.4 — Sicherheitsupdate Symfony 7.4.13 und Twig 3.27.0]]></title>
            <link>https://notacms.holas.pl/de/beitraege/release-1-1-4/</link>
            <guid isPermaLink="true">https://notacms.holas.pl/de/beitraege/release-1-1-4/</guid>
                        <pubDate>Thu, 28 May 2026 00:00:00 +0000</pubDate>
                        <description><![CDATA[Sicherheitsupdate: Symfony 7.4.13 und Twig 3.27.0 Sofort aktualisieren. Führe composer update im Projekt aus und rebuild anschließend. Symfony 7.4.13 (6 CVEs) CVE-2026-48489 — Firewall-Umgehung: der failure_forward-Handler wertete einen vom Angreifer kontrollierten _failure_path-Parameter im internen Subrequest aus und ermöglichte so das Umgehen der Firewall. CVE-2026-48736 — SSRF-Umgehung in NoPr…]]></description>
            <content:encoded><![CDATA[<h2>Sicherheitsupdate: Symfony 7.4.13 und Twig 3.27.0<a id="sicherheitsupdate-symfony-7413-und-twig-3270" href="#sicherheitsupdate-symfony-7413-und-twig-3270" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p><strong>Sofort aktualisieren.</strong> Führe <code>composer update</code> im Projekt aus und rebuild anschließend.</p>
<h3>Symfony 7.4.13 (6 CVEs)<a id="symfony-7413-6-cves" href="#symfony-7413-6-cves" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<ul>
<li><strong>CVE-2026-48489</strong> — Firewall-Umgehung: der <code>failure_forward</code>-Handler wertete einen vom Angreifer kontrollierten <code>_failure_path</code>-Parameter im internen Subrequest aus und ermöglichte so das Umgehen der Firewall.</li>
<li><strong>CVE-2026-48736</strong> — SSRF-Umgehung in <code>NoPrivateNetworkHttpClient</code> und <code>IpUtils::PRIVATE_SUBNETS</code> über IPv6-Übergangsadressformen (IPv4-mapped, IPv4-compatible, 6to4, Teredo).</li>
<li><strong>CVE-2026-48761</strong> — <code>HtmlSanitizer</code> bereinigt URL-Attribute auf <code>&lt;object&gt;</code>, <code>&lt;applet&gt;</code>, <code>&lt;iframe&gt;</code>, <code>&lt;img&gt;</code> und die URL im <code>&lt;meta http-equiv=&quot;refresh&quot;&gt;</code>-Inhalt nicht.</li>
<li><strong>CVE-2026-48760</strong> — <code>HtmlSanitizer</code> akzeptierte prozentkodierte BiDi-Zeichen und Unicode-Whitespace in URLs und ermöglichte so eine Sanitizer-Umgehung durch visuelles Spoofing.</li>
<li><strong>CVE-2026-48784</strong> — <code>UrlGenerator</code> kodierte verkettete <code>../</code>- und <code>./</code>-Segmente falsch und erzeugte URLs, die aus dem beabsichtigten Pfad herausführen konnten.</li>
<li><strong>CVE-2026-48747</strong> — Mailer: Der Webhook-Signaturalgorithmus von Mailomat war nicht auf SHA-256 festgelegt, was Algorithm-Substitution-Angriffe ermöglichte.</li>
</ul>
<p>Vollständige Liste: <a rel="nofollow noopener noreferrer" target="_blank" href="https://symfony.com/blog/symfony-7-4-13-released">symfony.com/blog/symfony-7-4-13-released</a>.</p>
<h3>Twig 3.27.0 (5 CVEs)<a id="twig-3270-5-cves" href="#twig-3270-5-cves" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<p>Alle fünf sind Sandbox-Umgehungen. Wenn dein Theme benutzergesteuerte Templates in einer Sandbox rendert, sind diese kritisch:</p>
<ul>
<li><strong>CVE-2026-46636</strong> — Umgehung der Allowlist für Filter, Tags und Funktionen, wenn sich der Sandbox-Status zwischen Renders in langlebigen Prozessen ändert (z. B. FrankenPHP, RoadRunner).</li>
<li><strong>CVE-2026-48808</strong> — <code>column</code>-Filter umgeht die Property-Allowlist unter <code>SourcePolicyInterface</code>.</li>
<li><strong>CVE-2026-48806</strong> — <code>__toString()</code>-Policy-Umgehung über dynamische Mapping-Keys: <code>{% set arr = {(obj): &quot;value&quot;} %}</code>.</li>
<li><strong>CVE-2026-48807</strong> — <code>__toString()</code>-Policy-Umgehung über <code>Traversable</code>-Objekte in den Filtern <code>join</code>/<code>replace</code> und den Operatoren <code>in</code>/<code>not in</code>.</li>
<li><strong>CVE-2026-48805</strong> — Sandbox-Statusregression in veralteten internen Wrappern (<code>twig_check_arrow_in_sandbox()</code>, <code>twig_array_some()</code>, <code>twig_array_every()</code>).</li>
</ul>
<h2>Weitere Änderungen in 1.1.4<a id="weitere-änderungen-in-114" href="#weitere-änderungen-in-114" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<ul>
<li><code>symfony/polyfill-*</code> von v1.37.0 auf v1.38.1 aktualisiert.</li>
<li>Dev-Abhängigkeiten aktualisiert: <code>phpstan/phpstan</code> 2.1.55 → 2.2.1, <code>phpunit/phpunit</code> 13.1.10 → 13.1.13, <code>rector/rector</code> 2.4.4 → 2.4.5.</li>
</ul>
<h2>So aktualisierst du<a id="so-aktualisierst-du" href="#so-aktualisierst-du" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<pre><code class="language-bash">composer update
ddev build   # oder das entsprechende Build-Kommando in deiner Umgebung
</code></pre>
<p>Es sind keine Konfigurationsänderungen oder Migrationsschritte erforderlich.</p>
<h2>Vollständiges Changelog<a id="vollständiges-changelog" href="#vollständiges-changelog" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p><a rel="nofollow noopener noreferrer" target="_blank" href="https://github.com/holas1337/notACMS/blob/main/CHANGELOG.md#114---2026-05-28">CHANGELOG.md</a></p>
]]></content:encoded>
                                    <category><![CDATA[releases]]></category>
                                    <category><![CDATA[release]]></category>
                        <category><![CDATA[security]]></category>
                        <category><![CDATA[symfony]]></category>
                    </item>
                <item>
            <title><![CDATA[notACMS 1.1.3 — JSON-Schema-Dateien und Sicherheitsupdate Symfony 7.4.12]]></title>
            <link>https://notacms.holas.pl/de/beitraege/release-1-1-3/</link>
            <guid isPermaLink="true">https://notacms.holas.pl/de/beitraege/release-1-1-3/</guid>
                        <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
                        <description><![CDATA[JSON-Schema-Dateien für Konfiguration und Frontmatter Sechs JSON-Schema-Draft-07-Dateien liegen nun unter config/schema/: Schema-Datei Beschreibt site.schema.json _site.yaml — Site-Einstellungen, Locales, Social-Links, Kontaktformular routes.schema.json _routes.yaml — locale URL-Pfad-Überschreibungen tags.schema.json _tags.yaml — Tag-Übersetzungstabelle post.frontmatter.schema.json Frontmatter für…]]></description>
            <content:encoded><![CDATA[<h2>JSON-Schema-Dateien für Konfiguration und Frontmatter<a id="json-schema-dateien-für-konfiguration-und-frontmatter" href="#json-schema-dateien-für-konfiguration-und-frontmatter" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Sechs JSON-Schema-Draft-07-Dateien liegen nun unter <code>config/schema/</code>:</p>
<table>
<thead>
<tr>
<th>Schema-Datei</th>
<th>Beschreibt</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>site.schema.json</code></td>
<td><code>_site.yaml</code> — Site-Einstellungen, Locales, Social-Links, Kontaktformular</td>
</tr>
<tr>
<td><code>routes.schema.json</code></td>
<td><code>_routes.yaml</code> — locale URL-Pfad-Überschreibungen</td>
</tr>
<tr>
<td><code>tags.schema.json</code></td>
<td><code>_tags.yaml</code> — Tag-Übersetzungstabelle</td>
</tr>
<tr>
<td><code>post.frontmatter.schema.json</code></td>
<td>Frontmatter für Blog-Posts</td>
</tr>
<tr>
<td><code>page.frontmatter.schema.json</code></td>
<td>Frontmatter für statische Seiten</td>
</tr>
<tr>
<td><code>category.frontmatter.schema.json</code></td>
<td>Frontmatter für Kategorie-Index-Dateien</td>
</tr>
</tbody>
</table>
<p>Alle Template-YAML-Dateien (<code>_site.yaml</code>, <code>_routes.yaml</code>, <code>_tags.yaml</code>) in <code>docs/bare/</code>, <code>docs/demo/</code> und <code>docs/customization/old-template/</code> enthalten jetzt einen <code># yaml-language-server: $schema=</code>-Kommentar mit dem URL auf den main-Branch. VS Code (und jeder Editor mit YAML Language Server) erkennt diese automatisch — Validierung und Autovervollständigung funktionieren ohne zusätzliche Projektkonfiguration.</p>
<p>Die Schemas sind für KI-gestütztes Authoring optimiert: Beschreibungen enthalten Standardwerte, Constraints und Verhaltensdetails, sodass ein KI-Agent in einem anderen Projekt ein Schema abrufen und genau wissen kann, was jedes Feld bewirkt.</p>
<p>Ein Schema direkt vom main-Branch abrufen:</p>
<pre><code>https://raw.githubusercontent.com/holas1337/notACMS/main/config/schema/&lt;name&gt;.schema.json
</code></pre>
<h2>Sicherheitsupdate: Symfony 7.4.12 und Twig 3.26.0<a id="sicherheitsupdate-symfony-7412-und-twig-3260" href="#sicherheitsupdate-symfony-7412-und-twig-3260" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p><strong>Sofort aktualisieren.</strong> Führe <code>composer update</code> im Projekt aus und rebuild anschließend.</p>
<h3>Symfony 7.4.12 (21 CVEs)<a id="symfony-7412-21-cves" href="#symfony-7412-21-cves" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<p>Die relevantesten für CMS-Deployments:</p>
<ul>
<li><strong>CVE-2026-45073</strong> — SQL-Injection in <code>Cache</code> durch nicht validierten <code>$prefix</code> in <code>PdoAdapter::doClear()</code>.</li>
<li><strong>CVE-2026-45071</strong> — XXE / lokale Dateioffenlegung in <code>DomCrawler::addXmlContent()</code> bei aktiviertem <code>validateOnParse</code>.</li>
<li><strong>CVE-2026-45075</strong> — HEAD-Anfragen umgehen den <code>methods</code>-Filter in den Attributen <code>#[IsGranted]</code>, <code>#[IsCsrfTokenValid]</code> und <code>#[IsSignatureValid]</code>.</li>
<li><strong>CVE-2026-45072</strong> — XSS in <code>CodeExtension::fileExcerpt()</code> in <code>TwigBridge</code>.</li>
<li><strong>CVE-2026-45068</strong> — Header-Injection in <code>SendmailTransport</code>; Adressen, die mit einem Bindestrich beginnen, werden nun abgelehnt.</li>
<li><strong>CVE-2026-45067</strong> — E-Mail-Adressen mit Zeilenumbrüchen wurden von <code>Mime\Address</code> akzeptiert — werden nun abgelehnt.</li>
<li><strong>CVE-2026-45305 / 45304 / 45133</strong> — Katastrophales Backtracking und unbegrenzte Rekursion im YAML-Parser.</li>
<li><strong>CVE-2026-45066 / 45064 / 45753</strong> — Drei <code>HtmlSanitizer</code>-Umgehungen: BiDi-Override-Zeichen, URL-Parser-Differenzen sowie nicht bereinigte <code>action</code>/<code>formaction</code>/<code>poster</code>/<code>cite</code>-Attribute.</li>
</ul>
<p>Vollständige Liste: <a rel="nofollow noopener noreferrer" target="_blank" href="https://symfony.com/blog/symfony-7-4-12-released">symfony.com/blog/symfony-7-4-12-released</a>.</p>
<h3>Twig 3.26.0 (4 CVEs)<a id="twig-3260-4-cves" href="#twig-3260-4-cves" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<p>Alle vier sind Sandbox-Umgehungen. Wenn dein Theme Twigs Sandbox für benutzerbereitgestellte Templates verwendet, sind diese kritisch:</p>
<ul>
<li><strong>CVE-2026-46635</strong> — Umgehung der Property-Allowlist über den <code>column</code>-Filter (<code>array_column</code> auf Objekten).</li>
<li><strong>CVE-2026-46638</strong> — <code>{% sandbox %}{% include %}</code> überspringt <code>checkSecurity()</code> bei gecachten Templates; unvollständige Behebung von CVE-2024-45411.</li>
<li><strong>CVE-2026-24425</strong> — Sandbox-Umgehung bei Verwendung einer Source-Policy.</li>
<li><strong>CVE-2026-47732</strong> — Mehrfache <code>__toString()</code>-Policy-Umgehungen über ungesicherte String-Koerzionspunkte.</li>
</ul>
<h2>Weitere Änderungen in 1.1.3<a id="weitere-änderungen-in-113" href="#weitere-änderungen-in-113" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<ul>
<li><strong>Veraltete Pagefind-Fragmente behoben</strong> — <code>scripts/rebuild-content.sh</code> löscht jetzt <code>public/pagefind/</code> vor der Neuindizierung. Zuvor hinterließen entfernte oder umbenannte Inhalte verwaiste Fragment-Dateien, die Pagefind neben aktuellen Ergebnissen auslieferte.</li>
<li><strong>DESIGN.md-Verbesserungen</strong> — Hartcodierte Hex-Werte in den <code>DESIGN.md</code>-Dateien von bare und demo durch Token-Referenzen ersetzt; <code>primary</code>-Farbalias hinzugefügt; <code>card-hover</code>-Komponenten-Token zum Demo-Theme hinzugefügt.</li>
</ul>
<h2>So aktualisierst du<a id="so-aktualisierst-du" href="#so-aktualisierst-du" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<pre><code class="language-bash">composer update
ddev build   # oder das entsprechende Build-Kommando in deiner Umgebung
</code></pre>
<p>Es sind keine Konfigurationsänderungen oder Migrationsschritte erforderlich.</p>
<h2>Vollständiges Changelog<a id="vollständiges-changelog" href="#vollständiges-changelog" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p><a rel="nofollow noopener noreferrer" target="_blank" href="https://github.com/holas1337/notACMS/blob/main/CHANGELOG.md#113---2026-05-20">CHANGELOG.md</a></p>
]]></content:encoded>
                                    <category><![CDATA[releases]]></category>
                                    <category><![CDATA[release]]></category>
                        <category><![CDATA[security]]></category>
                        <category><![CDATA[symfony]]></category>
                    </item>
                <item>
            <title><![CDATA[notACMS 1.1.2 — Kanonische URLs und ein Builder für strukturierte Daten]]></title>
            <link>https://notacms.holas.pl/de/beitraege/release-1-1-2/</link>
            <guid isPermaLink="true">https://notacms.holas.pl/de/beitraege/release-1-1-2/</guid>
                        <pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate>
                        <description><![CDATA[Eine kanonische URL pro Seite Wenn die Standardlokalisierung deiner Site en ist, waren sowohl /en/blog/ als auch /blog/ erreichbar und lieferten denselben Inhalt aus. Das sind zwei indexierbare URLs für eine Seite — und Suchmaschinen wählen die, die ihnen gefällt, was selten die ist, die du dir wünschst. 1.1.2 ergänzt den DefaultLocaleRedirectListener. Jede Anfrage an /&lt;standardlokalisierung&gt…]]></description>
            <content:encoded><![CDATA[<h2>Eine kanonische URL pro Seite<a id="eine-kanonische-url-pro-seite" href="#eine-kanonische-url-pro-seite" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Wenn die Standardlokalisierung deiner Site <code>en</code> ist, waren sowohl <code>/en/blog/</code> als auch <code>/blog/</code> erreichbar und lieferten denselben Inhalt aus. Das sind zwei indexierbare URLs für eine Seite — und Suchmaschinen wählen die, die ihnen gefällt, was selten die ist, die du dir wünschst.</p>
<p>1.1.2 ergänzt den <code>DefaultLocaleRedirectListener</code>. Jede Anfrage an <code>/&lt;standardlokalisierung&gt;/...</code> liefert eine <code>301</code>-Weiterleitung zur URL ohne Präfix:</p>
<pre><code>GET /en/blog/         →  301 Location: /blog/
GET /en/about/?ref=x  →  301 Location: /about/?ref=x
GET /pl/blog/         →  200 (nicht-Standardlokalisierung, unangetastet)
</code></pre>
<p>Der Listener läuft vor dem Symfony-Router, sodass die Weiterleitung erfolgt, bevor das Framework überhaupt versucht, eine Route zu treffen — keine verschwendete Arbeit, kein 404-Rauschen in den Logs. Nicht-Standardlokalisierungen (<code>pl</code>, <code>de</code>, <code>fr</code> auf dieser Site) bleiben unangetastet.</p>
<p>Außerdem schließt das eine kleine SEO-Lücke: Backlinks, die das Lokalisierungspräfix versehentlich enthalten, konsolidieren sich nun zur kanonischen URL, statt die Domain-Autorität aufzuteilen.</p>
<h2>Schema.org JSON-LD als flüssiger Builder<a id="schemaorg-json-ld-als-flüssiger-builder" href="#schemaorg-json-ld-als-flüssiger-builder" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Bis 1.1.1 baute jedes Template, das strukturierte Daten benötigte, ein PHP-Array inline ein, leitete es durch <code>|json_encode|raw</code> und packte das Ergebnis in einen <code>&lt;script&gt;</code>-Tag. Sechs Templates, sechs nahezu identische Blöcke — und jeder eine neue Gelegenheit, <code>JSON_UNESCAPED_SLASHES</code> zu vergessen oder <code>&lt;script&gt;false&lt;/script&gt;</code> auszuliefern, falls irgendein Frontmatter-Feld ein ungültiges Byte enthielt.</p>
<p>1.1.2 ersetzt das alles durch einen Service und zwei Twig-Funktionen:</p>
<pre><code class="language-twig">{% block structured_data %}{{ json_ld(structured_data().blogPosting(
    content.title(),
    site_base_url ~ content.url(),
    content.date(),
    content.htmlContent,
    site_author.name,
    content.image() ? site_base_url ~ content.image() : null
)) }}{% endblock %}
</code></pre>
<p><code>structured_data()</code> liefert den Builder; verkette eine typisierte Methode (<code>webSite</code>, <code>webPage</code>, <code>blogPosting</code>, <code>collectionPage</code>, <code>contactPage</code>, <code>person</code>, <code>breadcrumbList</code>, <code>organization</code>, <code>imageObject</code>) und das Ergebnis ist ein einfaches Array. <code>json_ld(array)</code> kodiert es und packt es in einen <code>&lt;script type=&quot;application/ld+json&quot;&gt;</code>-Tag. Leere und null-Werte werden rekursiv entfernt, sodass optionale Eingaben (eine nicht gesetzte Autor-E-Mail, ein fehlendes Bild) schlicht nicht in der Ausgabe erscheinen.</p>
<p>Die Kodierung verwendet jetzt <code>JSON_THROW_ON_ERROR</code> — fehlerhaftes UTF-8 im Frontmatter zeigt sich als echte Exception während des Renderings, statt stillschweigend einen kaputten <code>&lt;script&gt;</code>-Tag auszuliefern.</p>
<p>Die schlichten Kern-Templates für die Seiten <code>contact</code>, <code>default</code> und <code>projects</code> geben jetzt ebenfalls Schema.org-Markup aus. Bisher hatten schlichte Bereitstellungen schlechteres SEO als jedes Anpassungsbeispiel, das wir mitliefern; diese Lücke ist geschlossen.</p>
<p>Die vollständige Builder-API und wie du den <code>structured_data</code>-Block in deinem eigenen Theme überschreibst, findest du im <a rel="nofollow noopener noreferrer" target="_blank" href="https://github.com/holas1337/notACMS/blob/main/docs/CUSTOMIZATION.md#structured-data-json-ld">Anpassungsleitfaden → Strukturierte Daten</a>.</p>
<h2>Sicherheitsfixes<a id="sicherheitsfixes" href="#sicherheitsfixes" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Zwei Bugs sind während des 1.1.2-Reviews aufgetaucht und vor der Veröffentlichung behoben worden:</p>
<ul>
<li><strong>Open-Redirect über die Pfad-Normalisierung.</strong> Die Methode <code>Request::getPathInfo()</code> von Symfony fasst wiederholte Schrägstriche nicht zusammen, sodass eine Anfrage an <code>/&lt;standardlokalisierung&gt;//evil.com</code> andernfalls <code>Location: //evil.com</code> erzeugt hätte — eine protokollrelative Weiterleitung, der Browser cross-origin folgen. Der neue Redirect-Listener verweigert nun Weiterleitungen auf alles, was kein lokaler Pfad mit einem einzelnen führenden Schrägstrich ist.</li>
<li><strong>XSS in Suchergebnissen.</strong> <code>assets/search.js</code> band das <code>excerpt</code>-Feld von Pagefind ohne Escaping in innerHTML ein. Der Demo-Build war bereits korrekt; der Kern war auseinandergedriftet. Beide sind nun synchron. Die <code>&lt;mark&gt;</code>-Hervorhebungstags von Pagefind werden in den Suchergebnissen des Kerns nicht mehr gerendert — ein bewusster Kompromiss zugunsten der sichereren Standardausgabe.</li>
</ul>
<h2>Ebenfalls neu in 1.1.2<a id="ebenfalls-neu-in-112" href="#ebenfalls-neu-in-112" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<ul>
<li><strong>Interner Twig-Refactor.</strong> Die Logik für Sidebar, Breadcrumbs, Beitragslabels, og-Bilder und Blog-Filter wurde aus den Templates in dedizierte Twig-Erweiterungen ausgelagert. Templates konzentrieren sich auf das Layout; Tests können jede Helper-Funktion direkt anvisieren. Neue Anpassungsfläche: siehe <a rel="nofollow noopener noreferrer" target="_blank" href="https://github.com/holas1337/notACMS/blob/main/docs/CUSTOMIZATION.md#layout-helpers">Layout-Helfer</a> im Anpassungsleitfaden.</li>
<li><strong><code>O(1)</code>-Lookups nach Verzeichnisschlüssel</strong> in <code>ContentTree</code> — eine kleine Performance-Verbesserung auf Sites mit Hunderten von Seiten.</li>
<li><strong><code>docs/customization/old-template/</code> ist nun veraltet</strong> und wird in 1.2.0 entfernt. Es war eine einmalige Übergangshilfe für 1.0→1.1; neue Anpassungsarbeiten sollten von <code>custom-footer/</code> oder <code>self-hosted-fonts/</code> ausgehen.</li>
<li><strong>Abhängigkeitsupdates:</strong> Symfony 7.4.8 → 7.4.9 im gesamten Bundle, PHPStan 2.1.51 → 2.1.54, PHPUnit 13.1.7 → 13.1.8.</li>
</ul>
<h2>Vollständige Liste der Änderungen<a id="vollständige-liste-der-änderungen" href="#vollständige-liste-der-änderungen" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Alle Änderungen mit Kategorien: <a rel="nofollow noopener noreferrer" target="_blank" href="https://github.com/holas1337/notACMS/blob/main/CHANGELOG.md#112---2026-05-04">CHANGELOG.md</a>.</p>
]]></content:encoded>
                                    <category><![CDATA[releases]]></category>
                                    <category><![CDATA[release]]></category>
                        <category><![CDATA[announcement]]></category>
                    </item>
                <item>
            <title><![CDATA[notACMS 1.1.1 — Navigationslabels aus Frontmatter und deploy ohne Überschreiben]]></title>
            <link>https://notacms.holas.pl/de/beitraege/release-1-1-1/</link>
            <guid isPermaLink="true">https://notacms.holas.pl/de/beitraege/release-1-1-1/</guid>
                        <pubDate>Sun, 26 Apr 2026 00:00:00 +0000</pubDate>
                        <description><![CDATA[Navigationslabels aus Frontmatter Navigationslabels für Content-Seiten kommen jetzt aus dem Frontmatter — nicht aus Übersetzungskeys. Setze menu.label im Frontmatter einer Seite und es wird zum Label in der Navigation; lass es weg und der Seiten-title wird als Fallback genutzt. --- title: &quot;Architektur-Leitfaden&quot; slug: &quot;architecture-guide&quot; menu: label: &quot;Architektur&quot; we…]]></description>
            <content:encoded><![CDATA[<h2>Navigationslabels aus Frontmatter<a id="navigationslabels-aus-frontmatter" href="#navigationslabels-aus-frontmatter" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Navigationslabels für Content-Seiten kommen jetzt aus dem Frontmatter — nicht aus Übersetzungskeys. Setze <code>menu.label</code> im Frontmatter einer Seite und es wird zum Label in der Navigation; lass es weg und der Seiten-<code>title</code> wird als Fallback genutzt.</p>
<pre><code class="language-yaml">---
title: &quot;Architektur-Leitfaden&quot;
slug: &quot;architecture-guide&quot;
menu:
  label: &quot;Architektur&quot;
  weight: 30
---
</code></pre>
<p>Die neue Twig-Funktion <code>content_item()</code> löst jede Content-Seite über den Directory-Key auf:</p>
<pre><code class="language-twig">{{ content_item('architecture-guide', 'de').menuLabel() }}
{# → &quot;Architektur&quot; (oder der Seitentitel, wenn menu.label fehlt) #}
</code></pre>
<p><strong>Warum das wichtig ist:</strong> Bisher musste jede <code>messages.*.yaml</code> pro Locale Übersetzungen für <code>nav.home</code>, <code>nav.blog</code>, <code>nav.about</code>, <code>nav.contact</code>, <code>nav.privacy_policy</code> (Core) und <code>site.releases</code>, <code>site.about</code>, <code>site.manual</code>, <code>site.architecture</code>, <code>site.customization</code>, <code>site.locales</code>, <code>site.design_reference</code>, <code>site.contact</code> (Demo) enthalten. Eine neue Seite bedeutete N Übersetzungsdateien zu aktualisieren. Jetzt reicht ein Frontmatter-Feld. Custom Templates, die auf die entfernten Keys verweisen, sollten auf <code>content_item('key', locale).menuLabel()</code> umsteigen.</p>
<h2>Deploy respektiert bestehenden Inhalt<a id="deploy-respektiert-bestehenden-inhalt" href="#deploy-respektiert-bestehenden-inhalt" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<h3>Der Bug<a id="der-bug" href="#der-bug" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<p><code>./notACMS deploy --prod</code> würde bei <strong>jedem</strong> Deploy das gesamte <code>local/</code>-Verzeichnis sichern und durch <code>docs/demo/</code> ersetzen — selbst wenn es bereits eigenen Inhalt, Templates und Anpassungen enthielt. Die Seed-Logik unterschied nicht zwischen „Nutzer möchte explizit ein Theme neu seeden&quot; und „Nutzer möchte nur mit bestehendem Inhalt redeployen.&quot;</p>
<h3>Der Fix<a id="der-fix" href="#der-fix" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<p>Deploy verhält sich jetzt genauso wie <code>ddev build</code>:</p>
<ul>
<li><strong>Kein <code>--bare</code> / <code>--demo</code> Flag</strong> → seedet <code>local/</code> nur wenn es fehlt oder leer ist; überspringt, wenn Inhalt vorhanden ist.</li>
<li><strong><code>--bare</code> oder <code>--demo</code> explizit übergeben</strong> → sichert bestehendes <code>local/</code> und seedet das gewählte Theme.</li>
</ul>
<pre><code class="language-bash">./notACMS deploy --prod          # bestehendes local/ bleibt erhalten
./notACMS deploy --prod --demo   # erzwingt Neu-Seed aus docs/demo/
./notACMS deploy --prod --bare   # erzwingt Neu-Seed aus docs/bare/
</code></pre>
<p><code>--prod</code> steuert jetzt nur Composer-Flags (<code>--no-dev</code>) und die Laufzeitumgebung (<code>APP_ENV=prod</code>). Es berührt <code>local/</code> nie.</p>
<h2>Ebenfalls in 1.1.1<a id="ebenfalls-in-111" href="#ebenfalls-in-111" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<ul>
<li><strong>Dependency-Update:</strong> <code>symfony/polyfill-*</code> Pakete aktualisiert von v1.36.0 auf v1.37.0.</li>
<li><strong>Polnische, deutsche und französische Demo-Inhalte</strong> überprüft und verbessert auf allen Seiten und Blog-Posts.</li>
<li><strong>Übersetzungsstil-Guides</strong> zum AI-Agent-Skill-System für Polnisch, Deutsch und Französisch hinzugefügt, um konsistente Qualität bei zukünftigen Übersetzungen sicherzustellen.</li>
</ul>
<h2>Vollständige Liste der Änderungen<a id="vollständige-liste-der-änderungen" href="#vollständige-liste-der-änderungen" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Alle Änderungen mit Kategorien: <a rel="nofollow noopener noreferrer" target="_blank" href="https://github.com/holas1337/notACMS/blob/main/CHANGELOG.md#111---2026-04-26">CHANGELOG.md</a>.</p>
]]></content:encoded>
                                    <category><![CDATA[releases]]></category>
                                    <category><![CDATA[release]]></category>
                        <category><![CDATA[announcement]]></category>
                    </item>
                <item>
            <title><![CDATA[notACMS 1.1.0 — Schlanker Kern, Demo-Theme, freie Wahl]]></title>
            <link>https://notacms.holas.pl/de/beitraege/release-1-1-0/</link>
            <guid isPermaLink="true">https://notacms.holas.pl/de/beitraege/release-1-1-0/</guid>
                        <pubDate>Fri, 24 Apr 2026 00:00:00 +0000</pubDate>
                        <description><![CDATA[Zwei Themes, eine Entscheidung beim ersten Build Die wichtigste Änderung in 1.1.0: notACMS liefert jetzt zwei Themes ab Werk, und du wählst beim allerersten Build eines davon aus: ./notACMS deploy --demo # Standard — das Amber-Phosphor-Design, das du hier siehst ./notACMS deploy --bare # ein minimales Wireframe: Systemschriften, Light Mode, ~200 Zeilen CSS ddev build unterstützt dieselben Flags. E…]]></description>
            <content:encoded><![CDATA[<h2>Zwei Themes, eine Entscheidung beim ersten Build<a id="zwei-themes-eine-entscheidung-beim-ersten-build" href="#zwei-themes-eine-entscheidung-beim-ersten-build" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Die wichtigste Änderung in 1.1.0: notACMS liefert jetzt zwei Themes ab Werk, und du wählst beim allerersten Build eines davon aus:</p>
<pre><code class="language-bash">./notACMS deploy --demo    # Standard — das Amber-Phosphor-Design, das du hier siehst
./notACMS deploy --bare    # ein minimales Wireframe: Systemschriften, Light Mode, ~200 Zeilen CSS
</code></pre>
<p><code>ddev build</code> unterstützt dieselben Flags. Egal, welches du wählst — alle Funktionen laufen: mehrsprachige Inhalte, Blog, RSS, Sitemap, Suche, Kontaktformular, Bilder mit responsiven Varianten, Lesezeit, Lesefortschritt. Der Unterschied liegt nur in der Optik — und vor allem darin, wie viel du erbst, bevor du mit dem Anpassen beginnst.</p>
<h3>Schlanker Kern<a id="schlanker-kern" href="#schlanker-kern" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<p>Der Kern in <code>templates/</code>, <code>assets/</code>, <code>translations/</code> ist jetzt ein Wireframe. Er ist bewusst minimal gehalten, damit das Überschreiben eines einzelnen Blocks nicht gleich eine visuelle Sprache mitschleift, gegen die du ankämpfen musst. Wenn du ein maßgeschneidertes Design baust, starte mit dem Bare-Theme und du besitzt das gesamte Erscheinungsbild ab Tag eins.</p>
<h3>Demo-Theme<a id="demo-theme" href="#demo-theme" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<p>Das Demo befindet sich unter <code>docs/demo/</code> und wird nach <code>local/</code> kopiert, wenn du <code>--demo</code> wählst. Es ist das vollständige Amber-Phosphor-Design, das du gerade liest — Dark Mode, Such-Overlay, Docs-Sidebar, Theme-Umschalter, alles dabei. Starte hier, wenn du heute ein poliertes Design willst und planst anzupassen, statt neu zu bauen.</p>
<h2>Das <code>local/</code>-Override-System<a id="das-local-override-system" href="#das-local-override-system" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Beide Themes nutzen denselben Mechanismus: Jede Datei in <code>local/</code> hat Vorrang vor dem entsprechenden Pfad im Kern.</p>
<table>
<thead>
<tr>
<th>Schicht</th>
<th>Override-Ort</th>
<th>Resolver</th>
</tr>
</thead>
<tbody>
<tr>
<td>Twig-Templates</td>
<td><code>local/templates/*.html.twig</code></td>
<td>Symfony-Kernel</td>
</tr>
<tr>
<td>SCSS-Entrypoint</td>
<td><code>local/assets/styles/app_local.scss</code></td>
<td>Sass-Bundle</td>
</tr>
<tr>
<td>JS-Entrypoint</td>
<td><code>local/assets/app.js</code></td>
<td>AssetMapper-Importmap</td>
</tr>
<tr>
<td>Übersetzungen</td>
<td><code>local/translations/messages.*.yaml</code></td>
<td>Symfony-Translator</td>
</tr>
<tr>
<td>Inhalte</td>
<td><code>local/content/**</code></td>
<td>Parameter <code>notacms_content</code></td>
</tr>
<tr>
<td>Nginx-Snippets</td>
<td><code>local/docker/nginx/*.conf</code></td>
<td>Container-Entrypoint</td>
</tr>
</tbody>
</table>
<p>Der Kern wird nie bearbeitet. <code>git pull</code> bleibt sauber. Anpassungen befinden sich im Repo deiner Site, nicht in einem Fork dieses Repos.</p>
<h2>Upgrade von 1.0.0<a id="upgrade-von-100" href="#upgrade-von-100" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Wenn du 1.0.0 benutzt hast und deine Site exakt so aussehen soll wie vorher, ist das Kompatibilitätspaket ein Einzeiler:</p>
<pre><code class="language-bash">cp -r docs/customization/old-template/. local/
ddev build
</code></pre>
<p>Das legt das komplette Theme vor 1.1.0 — Templates, SCSS, Schriften, Bilder, Übersetzungen — in <code>local/</code> ab. Deine Site rendert exakt wie zuvor. Anschließend kannst du Dateien aus <code>local/</code> selektiv entfernen, sobald du neue Design-Elemente übernimmst.</p>
<p>Die echten Breaking Changes, auf die du stößt, wenn du den alten Kern angepasst hast:</p>
<ul>
<li><strong>SCSS-Entrypoint umbenannt</strong>: <code>local/assets/styles/local.scss</code> → <code>local/assets/styles/app_local.scss</code>. Benenne die Datei um und passe den Import in <code>local/assets/app.js</code> an.</li>
<li><strong>SCSS-Farbvariablen im Kern durch CSS Custom Properties ersetzt</strong>: <code>$color-body</code> → <code>var(--text)</code>, <code>$color-link</code> → <code>var(--accent)</code> usw. Vollständige Zuordnung in <a rel="nofollow noopener noreferrer" target="_blank" href="https://github.com/holas1337/notACMS/blob/main/UPGRADE-1.1.md">UPGRADE-1.1.md</a>.</li>
<li><strong>Übersetzungsschlüssel entfernt</strong> aus dem Kern (im Demo-Theme weiterhin vorhanden): <code>header.tagline</code>, <code>nav.projects</code>, <code>nav.search</code>, <code>blog.published_on</code>, <code>blog.comments_disabled</code>. Wenn deine Templates <code>'...'|trans</code> auf diese Schlüssel aufrufen, passe das an.</li>
<li><strong>Verzeichnis <code>docs/examples/</code> entfernt</strong>: Die nützlichen Teile sind nach <code>docs/customization/old-template/</code> umgezogen; die Scratch-Override-Beispiele wurden durch das Bare/Demo-Modell abgelöst.</li>
</ul>
<p>Siehe <a rel="nofollow noopener noreferrer" target="_blank" href="https://github.com/holas1337/notACMS/blob/main/UPGRADE-1.1.md">UPGRADE-1.1.md</a> für die vollständige Liste mit Vorher/Nachher-Snippets.</p>
<h2>Ebenfalls neu in 1.1.0<a id="ebenfalls-neu-in-110" href="#ebenfalls-neu-in-110" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<ul>
<li><strong>Lesezeit</strong> und <strong>Lesefortschritt</strong> auf Posts, Docs und Releases.</li>
<li><strong>Sprachumschalter</strong> als Twig-Extension (<code>lang_switch_urls</code>) mit korrekten Fallback-Ketten für Archive, paginierte Listen und Startseiten.</li>
<li><strong>Post-Excerpts entfernen Überschrift-Anker</strong> — keine verirrten <code>#</code>-Zeichen mehr in den Listenkarten.</li>
<li><strong>Test-Suite-Gerüst</strong> unter <code>tests/Unit/</code>, <code>tests/Integration/</code>, <code>tests/Fixtures/</code> mit initialer Abdeckung und einem vom Host ausführbaren <code>test</code>-Befehl.</li>
<li><strong>KI-Agent-Skills</strong> unter <code>.claude/skills/</code> für die Arbeit am Repo: Locales hinzufügen, Doc-Alignment-Checks, Site-Sweeps, Übersetzungen und Erstellen von Upgrade-Guides.</li>
</ul>
<h2>Vollständige Liste der Änderungen<a id="vollständige-liste-der-änderungen" href="#vollständige-liste-der-änderungen" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Jede Änderung mit Kategorie: <a rel="nofollow noopener noreferrer" target="_blank" href="https://github.com/holas1337/notACMS/blob/main/CHANGELOG.md#110---2026-04-24">CHANGELOG.md</a>.</p>
]]></content:encoded>
                                    <category><![CDATA[releases]]></category>
                                    <category><![CDATA[release]]></category>
                        <category><![CDATA[announcement]]></category>
                    </item>
                <item>
            <title><![CDATA[notACMS v1.0.0]]></title>
            <link>https://notacms.holas.pl/de/beitraege/release-1-0-0/</link>
            <guid isPermaLink="true">https://notacms.holas.pl/de/beitraege/release-1-0-0/</guid>
                        <pubDate>Thu, 09 Apr 2026 00:00:00 +0000</pubDate>
                        <description><![CDATA[Was in v1.0.0 steckt Nach mehreren Monaten internem Einsatz in persönlichen Projekten markiere ich das erste stabile Release. Der Kern-Funktionsumfang ist solide genug, um damit echte Seiten zu bauen. Inhalts-Pipeline Die Inhalts-Pipeline ist das Herzstück von notACMS. Sie liest das local/content/-Verzeichnis, parst Frontmatter und generiert mit einem einzigen Befehl eine vollständige statische Se…]]></description>
            <content:encoded><![CDATA[<h2>Was in v1.0.0 steckt<a id="was-in-v100-steckt" href="#was-in-v100-steckt" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Nach mehreren Monaten internem Einsatz in persönlichen Projekten markiere ich das erste stabile Release. Der Kern-Funktionsumfang ist solide genug, um damit echte Seiten zu bauen.</p>
<h3>Inhalts-Pipeline<a id="inhalts-pipeline" href="#inhalts-pipeline" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<p>Die Inhalts-Pipeline ist das Herzstück von notACMS. Sie liest das <code>local/content/</code>-Verzeichnis, parst Frontmatter und generiert mit einem einzigen Befehl eine vollständige statische Seite.</p>
<ul>
<li>Markdown-Inhalte mit YAML-Frontmatter</li>
<li>CommonMark-Rendering mit Überschriften-Permalinks</li>
<li>Excerpt-Generierung aus Fließtext</li>
<li>Berechnung der Lesezeit</li>
</ul>
<h3>Mehrsprachiges Routing<a id="mehrsprachiges-routing" href="#mehrsprachiges-routing" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<p>Locales werden in <code>_site.yaml</code> definiert. Jede Locale erhält ihren eigenen URL-Raum, mit optionalen Pfadüberschreibungen in <code>_routes.yaml</code>. <code>hreflang</code>-Tags werden automatisch generiert.</p>
<pre><code class="language-yaml">locales:
  en:
    label: &quot;English&quot;
    date_format: &quot;M d, Y&quot;
  pl:
    label: &quot;Polski&quot;
    date_format: &quot;d.m.Y&quot;
</code></pre>
<h3>Pagefind-Suche<a id="pagefind-suche" href="#pagefind-suche" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<p>Volltextsuche ist über <a rel="nofollow noopener noreferrer" target="_blank" href="https://pagefind.app/">Pagefind</a> in die statische Ausgabe eingebaut. Der Build-Befehl generiert den Suchindex automatisch. Keine externe API, keine serverseitige Suche — nur ein statischer Index, der auch offline funktioniert.</p>
<h3>Bildverarbeitung<a id="bildverarbeitung" href="#bildverarbeitung" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<p>Bilder, die sich neben den Inhalten befinden, werden beim Build verarbeitet. notACMS generiert WebP-Varianten in mehreren Breiten, aktualisiert <code>src</code>-Attribute automatisch mit responsivem <code>srcset</code> und übernimmt die Pfadzuordnung zwischen Inhalts- und Ausgabeverzeichnissen.</p>
<h3>DDEV lokale Entwicklung<a id="ddev-lokale-entwicklung" href="#ddev-lokale-entwicklung" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<p>Die Entwicklungsumgebung ist mit DDEV vollständig containerisiert. <code>ddev start</code> stellt PHP 8.5, Nginx und alle Build-Werkzeuge bereit. <code>ddev build</code> erzeugt die statische Ausgabe. <code>ddev code-check</code> führt PHPStan und PHP CS Fixer aus.</p>
<h2>Breaking Changes<a id="breaking-changes" href="#breaking-changes" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Dies ist das erste stabile Release. Wer eine Pre-1.0-Version verwendet hat: Das <code>_site.yaml</code>-Schema prüfen — der <code>social</code>-Schlüssel wurde von einer Liste in eine Map geändert.</p>
<h2>Upgrade<a id="upgrade" href="#upgrade" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<pre><code class="language-bash">git pull
ddev composer install
ddev build
</code></pre>
<h2>Was als nächstes kommt<a id="was-als-nächstes-kommt" href="#was-als-nächstes-kommt" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>v1.1.0 wird sich auf das Design-System und die Dokumentation konzentrieren. Diese Seite — gebaut mit notACMS — wird zur offiziellen Dokumentation und Design-Referenz.</p>
]]></content:encoded>
                                    <category><![CDATA[releases]]></category>
                                    <category><![CDATA[release]]></category>
                        <category><![CDATA[announcement]]></category>
                    </item>
                <item>
            <title><![CDATA[Warum ich notACMS gebaut habe]]></title>
            <link>https://notacms.holas.pl/de/beitraege/the-idea/</link>
            <guid isPermaLink="true">https://notacms.holas.pl/de/beitraege/the-idea/</guid>
                        <pubDate>Sun, 01 Feb 2026 00:00:00 +0000</pubDate>
                        <description><![CDATA[Ausführlichere Hintergrundgeschichte in meinem persönlichen Blog: I left WordPress. Das Problem, auf das ich immer wieder stieß Jedes PHP-Projekt, an dem ich arbeitete, brauchte irgendwann eine Website. Dokumentation, eine Landingpage, ein Blog — das Übliche. Und jedes Mal, wenn ich nach einem Tool griff, landete ich am selben Ort: einem Tool, das mich dazu brachte, wie es zu denken — nicht wie ei…]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>Ausführlichere Hintergrundgeschichte in meinem persönlichen Blog: <a rel="nofollow noopener noreferrer" target="_blank" href="https://holas.pl/blog/why-i-left-wordpress/">I left WordPress</a>.</p>
</blockquote>
<h2>Das Problem, auf das ich immer wieder stieß<a id="das-problem-auf-das-ich-immer-wieder-stieß" href="#das-problem-auf-das-ich-immer-wieder-stieß" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Jedes PHP-Projekt, an dem ich arbeitete, brauchte irgendwann eine Website. Dokumentation, eine Landingpage, ein Blog — das Übliche. Und jedes Mal, wenn ich nach einem Tool griff, landete ich am selben Ort: einem Tool, das mich dazu brachte, wie es zu denken — nicht wie ein PHP-Entwickler.</p>
<p>Hugo ist schnell, aber seine Template-Sprache ist fremd. Jekyll setzt Ruby voraus. WordPress hat die falsche Form für statische Inhalte. Next.js ist mächtig, verwandelt aber ein Markdown-Publishing-Problem in ein JavaScript-Bundler-Problem.</p>
<p>Ich dachte immer wieder: Ich kenne Symfony bereits. Ich kenne Twig bereits. Ich habe Composer bereits. Warum muss ich ein neues Ökosystem lernen, nur um Text zu veröffentlichen?</p>
<h2>Die Entscheidung gegen eine Datenbank<a id="die-entscheidung-gegen-eine-datenbank" href="#die-entscheidung-gegen-eine-datenbank" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Das Erste, was ich entschied: keine Datenbank. Nicht „Datenbank optional&quot; — gar keine Datenbank. Inhalte befinden sich in Dateien. Der Build-Prozess liest Dateien. Die Ausgabe sind Dateien.</p>
<p>Das erzwingt eine gewisse Disziplin. Die Inhaltsstruktur muss explizit und vorhersehbar sein. Es gibt keine Datenbankabfrage, auf die man zurückgreifen kann, wenn man verwandte Beiträge finden oder eine Sitemap generieren möchte. Alles muss aus dem Dateibaum ableitbar sein.</p>
<p>Diese Einschränkung stellte sich als die richtige heraus. Sie macht das System leicht verständlich, einfach zu sichern und trivial für KI-Tools nutzbar.</p>
<h2>KI-freundlich vom Konzept her<a id="ki-freundlich-vom-konzept-her" href="#ki-freundlich-vom-konzept-her" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Ich begann darüber ernsthaft nachzudenken, als ich LLMs in meiner täglichen Arbeit einzusetzen begann. Eine KI um Hilfe beim Generieren von Inhalten, Übersetzen von Seiten oder Validieren von YAML-Schemas zu bitten, ist unkompliziert, wenn das Format Plain Text ist.</p>
<p>Bei einem datenbankgestützten CMS müsste man das Schema erklären, SQL schreiben und Migrationen verwalten. Mit notACMS übergibt man der KI ein Verzeichnis mit Markdown-Dateien, und sie kann Inhalte direkt lesen, generieren und bearbeiten — weil das Format einfach Text ist.</p>
<p>Das ist keine nachträglich hinzugefügte Funktion. Es ist der Grund, warum das System so gestaltet ist, wie es ist.</p>
<h2>Das Symfony-Fundament<a id="das-symfony-fundament" href="#das-symfony-fundament" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Ich habe notACMS auf Symfony 7 aufgebaut, aus demselben Grund, warum ich Symfony für alles andere nutze: Es ist explizit, typisiert und gut dokumentiert. Der DI-Container, Konsolenbefehle, Twig — all das ist Standard-Symfony. An der Infrastruktur ist nichts Neuartiges.</p>
<p>Das Inhaltsmodell ist der interessante Teil. Das Routing-System liest <code>_routes.yaml</code> und generiert Symfony-Routen aus dem Inhaltsbaum. Der Build-Befehl rendert jede Route und schreibt statisches HTML. Die Service-Schicht ist schlank und austauschbar.</p>
<h2>Was als nächstes kommt<a id="was-als-nächstes-kommt" href="#was-als-nächstes-kommt" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Dieses erste Release ist ein funktionierendes Fundament. Was ich noch bauen möchte:</p>
<ul>
<li>Pagefind-Suchintegration (kommt in v1.0.0)</li>
<li>Bessere Bild-Pipeline mit WebP-Generierung</li>
<li>Vollständigere mehrsprachige Unterstützung</li>
<li>Eine vollwertige Design-Referenz-Seite (dazu entwickelt sich diese Seite gerade)</li>
</ul>
<p>Das Repository ist öffentlich. Wer mitverfolgen oder beitragen möchte: der Link befindet sich in der Fußzeile.</p>
]]></content:encoded>
                                    <category><![CDATA[releases]]></category>
                                    <category><![CDATA[announcement]]></category>
                        <category><![CDATA[open-source]]></category>
                    </item>
            </channel>
</rss>
