Files
KantSites/templates/admin/editor.html.twig
T
kantarciandClaude Opus 5 ebd5883593 Beiträge mit Archiven, Beitragsliste und Feed (Arbeitspaket B)
Migration 20260912000600 ergänzt page.excerpt, author,
featured_media_id und published_at sowie die Tabellen term und
page_term. Beitragsmetadaten beschreiben die Seite, nicht ihren
Inhalt, und liegen deshalb als Spalten statt im Blockdokument: das
Archiv sortiert und paginiert damit in SQL, statt für jede
Auflistung alle Dokumente zu dekodieren.

Kategorien und Schlagwörter teilen sich eine Tabelle mit einem
kind-Feld. Beim Löschen einer Seite verschwinden ihre Zuordnungen,
die Begriffe bleiben als wiederverwendbare Etiketten; ein leer
gewordenes Archiv antwortet weiter mit 200 und leerer Liste.

Der Block postlist (Dokumentschema v5) speichert nur Anzahl und
Filter. Die Beiträge stellt der Controller beim Ausliefern zusammen,
damit ein gespeichertes Dokument keinen unveröffentlichten Beitrag
zeigen kann. Archive unter /beitraege, /kategorie/{slug} und
/schlagwort/{slug}, Feed unter /feed.xml; alle vier Segmente sind
für Wurzelseiten reserviert.

Geprüft: 91 PHP-Tests / 347 Assertions unter PHP 8.2.12 und 8.4.22,
12 JavaScript-Modelltests, Lint/Format/Container, Migration zuerst
gegen eine Datenbankkopie. Keine Browser- oder Lighthouse-Läufe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 11:25:09 +02:00

43 lines
7.1 KiB
Twig
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends 'admin/base.html.twig' %}
{% block title %}{{ page.document.title }} bearbeiten{% endblock %}
{% block body_class %}editor-body{% endblock %}
{% block scripts %}<script type="module" src="/assets/editor.js"></script>{% endblock %}
{% block body %}
<div id="editor" data-page-id="{{ page.id }}" data-can-publish="{{ is_granted('ROLE_PUBLISHER') ? 'true' : 'false' }}">
<header class="editor-header">
<a class="back-link" href="{{ path('admin_pages') }}" aria-label="Zur Seitenübersicht">← <span>Seiten</span></a>
<div class="editor-page-name"><h1>{{ page.document.title }}</h1><span>/{{ page.slug == 'home' ? '' : page.slug }}</span></div>
<div class="history-actions"><button id="undo" class="icon-button" title="Rückgängig (Strg+Z)" aria-label="Rückgängig" disabled>↶</button><button id="redo" class="icon-button" title="Wiederholen (Strg+Umschalt+Z)" aria-label="Wiederholen" disabled>↷</button></div>
<div class="save-actions"><span id="save-status" role="status" aria-live="polite">Wird geladen …</span><button id="save" class="secondary" disabled>Entwurf speichern</button>{% if is_granted('ROLE_PUBLISHER') %}<button id="publish" class="primary" disabled>Veröffentlichen</button>{% endif %}</div>
</header>
<div id="message" class="editor-message" role="alert" hidden></div>
<div id="recovery" class="recovery-bar" hidden><button id="download" class="secondary">Änderungen als JSON sichern</button><button id="reload" class="secondary">Serverstand laden</button><a href="{{ path('admin_login') }}" target="_blank" rel="noopener">Erneut anmelden ↗</a></div>
<main id="admin-main" class="editor-layout" tabindex="-1">
<aside class="left-panel" aria-label="Elemente und Seitenstruktur">
<section class="panel-section"><div class="panel-title"><h2>Werkzeugkasten</h2><button id="toggle-library" class="text-button" aria-expanded="true" aria-controls="toolbox">− <span class="visually-hidden">Werkzeugkasten einklappen</span></button></div><div id="toolbox"><h3 class="preset-heading">Element hinzufügen</h3>
<p class="panel-hint">In die gewählte Sektion oder hinter das gewählte Element.</p>
<div id="library" class="library">
{% for type, label in {section:'Sektion', row:'Zeile', column:'Spalte', heading:'Überschrift', text:'Text', image:'Bild', button:'Button', gallery:'Galerie', postlist:'Beitragsliste'} %}<button type="button" data-add="{{ type }}" disabled><span aria-hidden="true">{{ {section:'▣',row:'☰',column:'▥',heading:'H',text:'T',image:'▧',button:'↗',gallery:'▦',postlist:'☷'}[type] }}</span>{{ label }}</button>{% endfor %}
</div>
<button id="open-media-library" class="secondary full-width media-launch" disabled>Medienverwaltung öffnen</button>
<h3 class="preset-heading">Fertige Abschnitte</h3><p class="panel-hint">Wird nach dem aktuellen Seitenabschnitt eingefügt.</p>
<div id="presets" class="preset-library"><button data-preset="intro" disabled><span aria-hidden="true">▤</span> Intro mit Button</button><button data-preset="columns" disabled><span aria-hidden="true">▥</span> Zwei Leistungen</button><button data-preset="cta" disabled><span aria-hidden="true">↗</span> Kontaktaufruf</button></div>
</div></section>
<section class="panel-section structure-section"><div class="panel-title"><h2>Struktur</h2><button id="select-page" class="text-button">Seite</button></div><label class="search-label" for="tree-search">Element suchen</label><input id="tree-search" type="search" placeholder="Text oder Elementtyp …"><div class="tree-tools"><button id="expand-all" class="text-button">Alle öffnen</button><button id="collapse-all" class="text-button">Einklappen</button></div><p class="panel-hint">Ziehen oder mit den Schaltflächen verschieben.</p><nav id="structure" aria-label="Blöcke auswählen"></nav>
<div class="block-actions"><button id="move-up" title="Nach oben verschieben" aria-label="Nach oben verschieben">↑</button><button id="move-down" title="Nach unten verschieben" aria-label="Nach unten verschieben">↓</button><button id="nest" title="In vorherigen Container verschachteln" aria-label="In vorherigen Container verschachteln">⇥</button><button id="outdent" title="Eine Ebene herausheben" aria-label="Eine Ebene herausheben">⇤</button><button id="duplicate">Duplizieren</button><button id="delete" class="danger-text">Löschen</button></div>
</section>
</aside>
<section class="canvas-panel" aria-label="Live-Vorschau">
<div class="canvas-toolbar"><span class="canvas-label">LIVE-VORSCHAU</span><div class="viewport-controls" role="group" aria-label="Vorschaugröße"><button data-viewport="desktop" aria-pressed="true">Desktop</button><button data-viewport="tablet" aria-pressed="false">Tablet</button><button data-viewport="mobile" aria-pressed="false">Mobil</button></div><a href="{{ page.slug == 'home' ? '/' : '/' ~ page.slug }}" target="_blank" rel="noopener" aria-label="Öffentliche Seite öffnen">↗</a></div>
<p id="preview-status" class="preview-status" role="status">Vorschau wird geladen …</p>
<nav id="breadcrumbs" class="breadcrumbs" aria-label="Ausgewähltes Element"></nav>
<div class="canvas-scroll" tabindex="0" role="region" aria-label="Vorschau scrollen"><div id="preview-stage"><iframe id="preview" title="Vorschau der bearbeiteten Seite" sandbox="allow-same-origin" class="viewport-desktop"></iframe></div></div>
</section>
<aside class="right-panel" aria-label="Eigenschaften"><div class="panel-section"><p class="eyebrow">FEINSCHLIFF</p><h2 id="properties-title">Seiteneinstellungen</h2><div class="property-tabs" role="group" aria-label="Eigenschaftenbereich"><button data-panel="content" aria-pressed="true">Inhalt</button><button data-panel="design" aria-pressed="false">Design</button></div><div id="properties"><p class="muted">Inhalte werden geladen …</p></div></div><div class="panel-note">Die Vorschau zeigt deinen Entwurf. Besucher sehen weiterhin den veröffentlichten Stand.</div></aside>
</main>
<noscript><p class="notice error">Zum Bearbeiten muss JavaScript aktiviert sein. Die öffentliche Website funktioniert ohne JavaScript.</p></noscript>
<dialog id="publish-dialog" aria-labelledby="publish-title"><h2 id="publish-title">Seite veröffentlichen?</h2><p>Der gespeicherte Entwurf ersetzt den bisher sichtbaren Inhalt dieser Seite.</p><div class="dialog-actions"><button id="cancel-publish" class="secondary">Abbrechen</button><button id="confirm-publish" class="primary">Jetzt veröffentlichen</button></div></dialog>
<dialog id="reload-dialog" aria-labelledby="reload-title"><h2 id="reload-title">Serverstand laden?</h2><p>Lokale Änderungen und dein Undo-Verlauf werden ersetzt. Sichere deine Änderungen vorher als JSON.</p><div class="dialog-actions"><button id="cancel-reload" class="secondary">Abbrechen</button><button id="confirm-reload" class="primary">Serverstand übernehmen</button></div></dialog>
</div>
{% endblock %}