Files
KantSites/templates/admin/content-results.html.twig
T
kantarciandClaude Opus 5 a976c146de Choose menu anchors, link targets in plugin settings and existing post terms
A menu entry picks its anchor from the linked page, link settings of plugins and own menu
links get page and PDF choice next to the address, and the post settings offer existing
categories and tags to click.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 09:56:07 +02:00

220 lines
17 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.
{% set plural = contentType == 'post' ? 'Beiträge' : 'Seiten' %}
<div class="section-head"><h2>{{ status == 'trash' ? 'Papierkorb' : plural }} <span class="count">{{ result.total }}</span></h2><span class="muted">{{ result.total ? (result.page - 1) * result.limit + 1 : 0 }}–{{ min(result.page * result.limit, result.total) }} von {{ result.total }}</span></div>
{% include 'admin/pager.html.twig' with {route: listRoute, params: {q: query, status: status, limit: result.limit}, previousLabel: 'Zurück', nextLabel: 'Weiter', label: 'Ergebnisseiten oben'} %}
<div class="rows">
{% for page in pages %}
{# Newest first, not as a tree: indenting subpages here would suggest a parent that is not
the row above. The address in the row shows where a page sits. #}
<article class="row row-stack">
<div class="row-line">
<span class="row-icon" aria-hidden="true">{{ page.type == 'post' ? '✎' : '▤' }}</span>
<div class="row-main">
<h3><a href="{{ path('admin_editor', {id: page.id}) }}">{{ page.title }}</a></h3>
<p>/{{ page.path == 'home' ? '' : page.path }} · {{ page.type == 'post' ? 'Beitrag' : 'Seite' }} · {{ page.locale|upper }}{% if page.publish_at %} · geplant für {{ page.publish_at|date('d.m.Y H:i') }} UTC{% endif %}</p>
</div>
{% if page.published_revision_id is null %}
<span class="pill pill-off">Nicht veröffentlicht</span>
{% elseif page.published_revision_id == page.draft_revision_id %}
<span class="pill pill-live">Veröffentlicht</span>
{% else %}
<span class="pill pill-draft">Entwurf geändert</span>
{% endif %}
<div class="row-actions">
<a class="secondary" href="{{ path('admin_editor', {id: page.id}) }}" aria-label="{{ page.title }} bearbeiten">Bearbeiten <span aria-hidden="true">→</span></a>
<a class="text-button" href="{{ path('admin_revisions', {id: page.id}) }}" aria-label="Fassungen von {{ page.title }}">Fassungen</a>
</div>
</div>
{% if is_granted('ROLE_EDITOR') or is_granted('ROLE_PUBLISHER') %}
{# One disclosure per row instead of six popovers next to each other: everything that is
not the daily „bearbeiten“ lives here and opens in place. #}
<details class="sheet">
<summary aria-label="Einstellungen von {{ page.title }}">Einstellungen</summary>
<div class="sheet-body">
{% if is_granted('ROLE_EDITOR') %}
<div class="sheet-part">
<h4>{{ page.type == 'post' ? 'Beitragsdaten' : 'Seitendaten' }}</h4>
<form action="{{ path('admin_post_meta', {id: page.id}) }}" method="post">
<input type="hidden" name="_list_type" value="{{ contentType }}"><input type="hidden" name="_list_query" value="{{ query }}"><input type="hidden" name="_list_status" value="{{ status }}"><input type="hidden" name="_list_page" value="{{ result.page }}"><input type="hidden" name="_list_limit" value="{{ result.limit }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token('pages') }}">
<input type="hidden" name="expectedVersion" value="{{ page.version }}">
<div class="fields">
<div>
<label for="seotitle-{{ page.id }}">Titel für Suchergebnisse</label>
<input id="seotitle-{{ page.id }}" name="seoTitle" maxlength="160" value="{{ page.seo_title }}" placeholder="{{ page.document.title|default(page.path) }}">
<p class="field-help">Leer lassen, wenn der Titel der Seite auch in der Suche stehen soll. Google zeigt etwa 60 Zeichen und schneidet danach ab; was dahinter steht, liest niemand.</p>
</div>
<div>
<label for="robots-{{ page.id }}">Suchmaschinen</label>
<select id="robots-{{ page.id }}" name="robots">
<option value="index"{{ page.robots != 'noindex' ? ' selected' : '' }}>Darf indexiert werden</option>
<option value="noindex"{{ page.robots == 'noindex' ? ' selected' : '' }}>Nicht indexieren</option>
</select>
<p class="field-help">Nicht indexierte Seiten erscheinen auch nicht in der Sitemap.</p>
</div>
<div>
{% include 'admin/media-field.html.twig' with {id: 'og-' ~ page.id, name: 'ogMediaId', label: 'Vorschaubild beim Teilen', value: page.og_media_id|default(''), help: 'Erscheint, wenn jemand die Seite in sozialen Netzen oder Messengern teilt.'} only %}
</div>
{% if page.type == 'post' %}
<div>
<label for="date-{{ page.id }}">Veröffentlicht am</label>
<input id="date-{{ page.id }}" name="publishedAt" type="date" value="{{ page.published_at ? page.published_at|date('Y-m-d') : '' }}">
</div>
<div>
<label for="author-{{ page.id }}">Autor</label>
<input id="author-{{ page.id }}" name="author" maxlength="120" value="{{ page.author }}">
</div>
<div>
<label for="categories-{{ page.id }}">Kategorien</label>
<input id="categories-{{ page.id }}" name="categories" value="{{ page.terms.category }}" placeholder="Projekte, Referenzen" data-term-input="{{ termNames.category|default([])|json_encode|e('html_attr') }}">
</div>
<div>
<label for="tags-{{ page.id }}">Schlagwörter</label>
<input id="tags-{{ page.id }}" name="tags" value="{{ page.terms.tag }}" placeholder="WordPress, Migration" data-term-input="{{ termNames.tag|default([])|json_encode|e('html_attr') }}">
<p class="field-help">Mehrere durch Komma trennen. Unbekannte Namen werden angelegt.</p>
</div>
<div>
{% include 'admin/media-field.html.twig' with {id: 'media-' ~ page.id, name: 'featuredMediaId', label: 'Beitragsbild', value: page.featured_media_id|default(''), help: 'Erscheint in Beitragslisten und im Feed.'} only %}
</div>
<div>
<label for="excerpt-{{ page.id }}">Auszug</label>
<textarea id="excerpt-{{ page.id }}" name="excerpt" maxlength="500" rows="3">{{ page.excerpt }}</textarea>
<p class="field-help">Erscheint in der Übersicht und im Feed. Leer lassen, um die Seitenbeschreibung zu verwenden.</p>
</div>
{% endif %}
</div>
<button class="primary" type="submit">Speichern</button>
</form>
</div>
{% endif %}
{% if is_granted('ROLE_EDITOR') and page.path != 'home' %}
<div class="sheet-part">
<h4>Adresse</h4>
<form action="{{ path('admin_move', {id: page.id}) }}" method="post">
<input type="hidden" name="_list_type" value="{{ contentType }}"><input type="hidden" name="_list_query" value="{{ query }}"><input type="hidden" name="_list_status" value="{{ status }}"><input type="hidden" name="_list_page" value="{{ result.page }}"><input type="hidden" name="_list_limit" value="{{ result.limit }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token('pages') }}">
<input type="hidden" name="expectedVersion" value="{{ page.version }}">
<div class="fields">
<div>
<label for="slug-{{ page.id }}">Adresssegment</label>
<input id="slug-{{ page.id }}" name="slug" maxlength="80" required value="{{ page.slug }}" pattern="[a-z0-9]+(-[a-z0-9]+)*">
</div>
<div>
{% include 'admin/content-picker.html.twig' with {id: 'parent-' ~ page.id, name: 'parent_id', label: 'Übergeordnete Seite (optional)', parents: true, selected: parentChoices[page.parent_id]|default(null)} %}
<p class="field-help">Unterseiten ziehen mit. Von jeder alten Adresse wird dauerhaft weitergeleitet.</p>
</div>
</div>
<button class="secondary" type="submit">Adresse ändern</button>
</form>
</div>
{% endif %}
{% if is_granted('ROLE_EDITOR') %}
<div class="sheet-part">
<h4>Kopf- und Fußbereich</h4>
<p>Standard ist der gemeinsame Bereich. „Kein Bereich“ liefert die Seite ohne Kopf beziehungsweise Fuß aus – für eine Landingpage ohne Navigation. Weitere Bereiche legst du unter Bereiche an.</p>
<form action="{{ path('admin_page_areas', {id: page.id}) }}" method="post">
<input type="hidden" name="_list_type" value="{{ contentType }}"><input type="hidden" name="_list_query" value="{{ query }}"><input type="hidden" name="_list_status" value="{{ status }}"><input type="hidden" name="_list_page" value="{{ result.page }}"><input type="hidden" name="_list_limit" value="{{ result.limit }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token('pages') }}">
<input type="hidden" name="expectedVersion" value="{{ page.version }}">
<div class="fields">
{% for kind, label in {header: 'Kopfbereich', footer: 'Fußbereich'} %}
<div>
<label for="{{ kind }}-{{ page.id }}">{{ label }}</label>
<select id="{{ kind }}-{{ page.id }}" name="{{ kind }}Area">
<option value=""{{ page[kind ~ '_area'] is null ? ' selected' : '' }}>Standard</option>
{% for slug, name in areaChoices[kind] %}{% if slug != 'default' %}
<option value="{{ slug }}"{{ page[kind ~ '_area'] == slug ? ' selected' : '' }}>{{ name }}</option>
{% endif %}{% endfor %}
<option value="none"{{ page[kind ~ '_area'] == 'none' ? ' selected' : '' }}>Kein Bereich</option>
</select>
</div>
{% endfor %}
</div>
<button class="secondary" type="submit">Bereiche speichern</button>
</form>
</div>
{% endif %}
{% if is_granted('ROLE_PUBLISHER') %}
<div class="sheet-part">
<h4>Veröffentlichung planen</h4>
<form action="{{ path('admin_schedule', {id: page.id}) }}" method="post">
<input type="hidden" name="_list_type" value="{{ contentType }}"><input type="hidden" name="_list_query" value="{{ query }}"><input type="hidden" name="_list_status" value="{{ status }}"><input type="hidden" name="_list_page" value="{{ result.page }}"><input type="hidden" name="_list_limit" value="{{ result.limit }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token('pages') }}">
<input type="hidden" name="expectedVersion" value="{{ page.version }}">
<div class="field">
<label for="publish-{{ page.id }}">Veröffentlichen am</label>
<input id="publish-{{ page.id }}" name="publishAt" type="datetime-local" value="{{ page.publish_at ? page.publish_at|date('Y-m-d\\TH:i') : '' }}">
<p class="field-help">Zeiten in UTC. Leer lassen, um die Planung zu entfernen. Veröffentlicht wird beim ersten Aufruf der Website nach diesem Zeitpunkt; wie oft nachgesehen wird, steht unter <a href="{{ path('admin_tasks') }}">Aufgaben</a>.</p>
</div>
<button class="secondary" type="submit">Zeitpunkt speichern</button>
</form>
</div>
{% endif %}
{% if (is_granted('ROLE_EDITOR') and page.path != 'home') or is_granted('ROLE_ADMIN') %}
<div class="sheet-part">
<h4>Entfernen</h4>
<div class="sheet-actions">
{% if is_granted('ROLE_EDITOR') and page.path != 'home' %}
<form action="{{ path('admin_trash', {id: page.id}) }}" method="post">
<input type="hidden" name="_list_type" value="{{ contentType }}"><input type="hidden" name="_list_query" value="{{ query }}"><input type="hidden" name="_list_status" value="{{ status }}"><input type="hidden" name="_list_page" value="{{ result.page }}"><input type="hidden" name="_list_limit" value="{{ result.limit }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token('pages') }}">
<input type="hidden" name="expectedVersion" value="{{ page.version }}">
<button class="secondary" type="submit">In den Papierkorb</button>
</form>
{% endif %}
{% if is_granted('ROLE_ADMIN') %}
{# The class is what the security test looks for: an editor must not even be
offered this. #}
<div class="delete-panel">
<form action="{{ path('admin_delete', {id: page.id}) }}" method="post">
<input type="hidden" name="_list_type" value="{{ contentType }}"><input type="hidden" name="_list_query" value="{{ query }}"><input type="hidden" name="_list_status" value="{{ status }}"><input type="hidden" name="_list_page" value="{{ result.page }}"><input type="hidden" name="_list_limit" value="{{ result.limit }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token('pages') }}">
<input type="hidden" name="expectedVersion" value="{{ page.version }}">
<button class="subtle danger-text" type="submit">Endgültig löschen</button>
</form>
</div>
{% endif %}
</div>
<p>Der Papierkorb ist umkehrbar: die Seite samt Unterseiten ist öffentlich nicht mehr erreichbar, alle Fassungen bleiben.{% if is_granted('ROLE_ADMIN') %} Endgültig löschen entfernt „{{ page.title }}“ mit allen Fassungen, ohne Papierkorb, und lässt sich nicht rückgängig machen.{% endif %}</p>
</div>
{% endif %}
</div>
</details>
{% endif %}
</article>
{% else %}
{% if status != 'trash' %}<div class="empty"><h3>{{ result.total == 0 ? 'Keine passenden Einträge.' : '' }}</h3>{% if result.total == 0 %}<p>{{ query or status != 'all' ? 'Ändere die Suche oder den Statusfilter.' : 'Lege oben deinen ersten Inhalt an.' }}</p>{% endif %}</div>{% endif %}
{% endfor %}
</div>
{% if trash is not empty %}
<div class="rows">
{% for entry in trash %}
<article class="row">
<span class="row-icon" aria-hidden="true">🗑</span>
<div class="row-main">
<h3>/{{ entry.path }}</h3>
<p>Im Papierkorb seit <time datetime="{{ entry.deletedAt|date('c') }}">{{ entry.deletedAt|date('d.m.Y H:i') }}</time> UTC</p>
</div>
{% if is_granted('ROLE_EDITOR') %}
<div class="row-actions">
<form action="{{ path('admin_restore_page', {id: entry.id}) }}" method="post">
<input type="hidden" name="_list_type" value="{{ contentType }}"><input type="hidden" name="_list_query" value="{{ query }}"><input type="hidden" name="_list_status" value="{{ status }}"><input type="hidden" name="_list_page" value="{{ result.page }}"><input type="hidden" name="_list_limit" value="{{ result.limit }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token('pages') }}">
<button class="secondary" type="submit">Wiederherstellen</button>
</form>
</div>
{% endif %}
</article>
{% endfor %}
</div>
{% endif %}
{% if status == 'trash' and trash is empty %}<div class="empty"><h3>Der Papierkorb ist leer.</h3></div>{% endif %}
{% include 'admin/pager.html.twig' with {route: listRoute, params: {q: query, status: status, limit: result.limit}, previousLabel: 'Zurück', nextLabel: 'Weiter', label: 'Ergebnisseiten unten'} %}