Umut: "Beitragsseite gefaellt mir nicht, sollte wie bei kant-it.de sein oder besser. Falls noetig Plugin bauen." Die Originalseite hat Galerie, Vor/Zurueck zwischen Projekten und einen Abschlussaufruf. Galerie und Aufruf gab es laengst - sie waren nur nicht benutzt. Die Navigation gab es nicht. Kein Plugin: ein Plugin ist hier ein Bausteintyp, dessen Eigenschaften der Kern aufbewahrt, aber nicht rendert - es koennte also nichts ausgeben, solange es keine Laufzeit dafuer gibt. Und die Nachbarn eines Beitrags stehen in derselben Tabelle wie die Beitragsliste, die der Kern schon fuellt. Also ein Baustein: postnav. Gespeichert sind zwei Beschriftungen, mehr nicht - welcher Beitrag vorher und nachher kommt, entscheidet der Renderer. Gesucht wird innerhalb derselben Kategorie, und am Rand wird nicht umgebrochen. Ausserdem: die uebernommenen Projekttexte begannen mit dem wiederholten Titel (20 Beitraege), und schmale Abschnitte standen links statt mittig - max-width ohne margin:auto ist eine halbe Einstellung. Geprueft: 396 Tests unter PHP 8.2 und 8.4, 18 JS-Tests, Editortest, Browserpruefung, axe ohne Verstoss auf der Beitragsseite. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
79 lines
6.4 KiB
Twig
79 lines
6.4 KiB
Twig
<!doctype html>
|
||
<html lang="{{ page.locale }}">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>{{ page.document.title }}</title>
|
||
<meta name="description" content="{{ page.document.description }}">
|
||
<link rel="canonical" href="{{ canonical }}">
|
||
{% if page.robots|default('index') != 'index' %}<meta name="robots" content="noindex, follow">{% endif %}
|
||
<meta property="og:title" content="{{ page.document.title }}">
|
||
<meta property="og:description" content="{{ page.document.description }}">
|
||
<meta property="og:type" content="{{ page.type|default('page') == 'post' ? 'article' : 'website' }}">
|
||
<meta property="og:url" content="{{ canonical }}">
|
||
{% if socialImage|default(null) %}<meta property="og:image" content="{{ socialImage }}"><meta name="twitter:card" content="summary_large_image">{% endif %}
|
||
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||
<link rel="stylesheet" href="/assets/site.css">
|
||
{% if googleFonts|default(null) %}<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link rel="stylesheet" href="{{ googleFonts }}">{% endif %}
|
||
<link rel="alternate" type="application/rss+xml" title="Beiträge" href="/feed.xml">
|
||
{% if page.document.schemaVersion >= 2 %}<link rel="stylesheet" href="/assets/design.css">{% endif %}
|
||
{% if page.document.schemaVersion >= 3 %}<link rel="stylesheet" href="/assets/enhancements.css">{% endif %}
|
||
{% if needsTabs|default(false) %}<script src="/assets/tabs.js" defer></script>{% endif %}
|
||
{% if needsSlider|default(false) %}<script src="/assets/slider.js" defer></script>{% endif %}
|
||
{# Own scripts of this page, as a file. Never rendered in the editor preview: the preview must
|
||
show the layout, not run code that could change the page under the editor's hands. #}
|
||
{% if pageScript|default(null) and not editing|default(false) %}<script src="{{ pageScript }}" defer></script>{% endif %}
|
||
{# A data block, not a script: CSP script-src does not apply to application/ld+json. #}
|
||
{% for block in structuredData|default([]) %}<script type="application/ld+json">{{ block|json_encode(constant('JSON_UNESCAPED_SLASHES') b-or constant('JSON_UNESCAPED_UNICODE'))|raw }}</script>
|
||
{% endfor %}
|
||
{% if pageStyles|default('') %}
|
||
{# In the editor the preview inherits the administration's policy, which forbids inline
|
||
styles. The stylesheet therefore travels as inert template text and the editor applies
|
||
it as a constructed stylesheet – same result, no policy violation and a clean console. #}
|
||
{% if editing|default(false) %}<template id="kant-styles">{{ pageStyles|raw }}</template>
|
||
{% else %}<style>{{ pageStyles|raw }}</style>{% endif %}
|
||
{% endif %}
|
||
</head>
|
||
{% set consentOpen = not editing|default(false) and consentGate.needsBanner(app.request) %}
|
||
<body class="kant{{ consentOpen ? ' consent-open' : '' }}">
|
||
<a class="skip-link" href="#main">Zum Inhalt springen</a>
|
||
{# Header and footer are block documents like any page, so they are built from elements
|
||
instead of being written into this template. #}
|
||
{% if areas.header|default(null) %}
|
||
<header class="site-header{{ siteSettings.load().stickyHeader ? ' is-sticky' : '' }}">
|
||
{% for block in areas.header.blocks %}{% include 'blocks/block.html.twig' with {block: block, editing: editing|default(false), postLists: {}, forms: {}, breadcrumbs: [], navigation: navigation|default({}), sections: sections|default({}), richText: areas.header.schemaVersion|default(1) >= 12, richHeadings: areas.header.schemaVersion|default(1) >= 17, eagerImage: eagerImage|default(null)} only %}{% endfor %}
|
||
</header>
|
||
{% endif %}
|
||
<main id="main" tabindex="-1">
|
||
{% if postMeta is defined and postMeta is not null and siteSettings.load().postMetaHeader %}
|
||
<p class="post-header shell">
|
||
{% if page.published_at %}<time datetime="{{ page.published_at|date('Y-m-d') }}">{{ page.published_at|date('d.m.Y') }}</time>{% endif %}
|
||
{% if page.author %}<span>von {{ page.author }}</span>{% endif %}
|
||
{% for term in postMeta.category %}<a href="{{ term.href }}">{{ term.name }}</a>{% endfor %}
|
||
{% for term in postMeta.tag %}<a class="post-tag" href="{{ term.href }}">#{{ term.name }}</a>{% endfor %}
|
||
</p>
|
||
{% endif %}
|
||
{% for block in page.document.blocks %}{% include 'blocks/block.html.twig' with {block: block, editing: editing|default(false), postLists: postLists|default({}), forms: forms|default({}), breadcrumbs: breadcrumbs|default([]), navigation: navigation|default({}), sections: sections|default({}), richText: page.document.schemaVersion|default(1) >= 12, richHeadings: page.document.schemaVersion|default(1) >= 17, neighbours: neighbours|default({}), eagerImage: eagerImage|default(null)} only %}{% endfor %}
|
||
</main>
|
||
{% if areas.footer|default(null) %}
|
||
<footer class="site-footer">
|
||
{% for block in areas.footer.blocks %}{% include 'blocks/block.html.twig' with {block: block, editing: editing|default(false), postLists: {}, forms: {}, breadcrumbs: [], navigation: navigation|default({}), sections: sections|default({}), richText: areas.footer.schemaVersion|default(1) >= 12, richHeadings: areas.footer.schemaVersion|default(1) >= 17, eagerImage: null} only %}{% endfor %}
|
||
</footer>
|
||
{% endif %}
|
||
{# Privacy statement, imprint and the way back to the consent decision. All three have to be
|
||
reachable from every page, so they are part of the template and not of the footer area,
|
||
which someone could delete. One labelled landmark, so they are not loose text either. #}
|
||
{% set legal = consentGate.legalLinks() %}
|
||
{% set revocable = not editing|default(false) and consentGate.askedGroups is not empty and not consentGate.needsBanner(app.request) %}
|
||
{% if legal is not empty or revocable %}
|
||
<nav class="legal-bar" aria-label="Rechtliches">
|
||
{% for path, label in legal %}{% if not loop.first %} · {% endif %}<a href="/{{ path }}">{{ label }}</a>{% endfor %}
|
||
{% if revocable %}{% if legal is not empty %} · {% endif %}<a href="/einwilligung">Einwilligung ändern</a>{% endif %}
|
||
</nav>
|
||
{% endif %}
|
||
{% if not editing|default(false) and consentGate.needsBanner(app.request) %}
|
||
{% include 'consent-banner.html.twig' with {consent: consentGate.state(app.request), back: app.request.pathInfo} only %}
|
||
{% endif %}
|
||
</body>
|
||
</html>
|