Alles aus dem Nachbau von kant-it.de, den Umut durchgesehen hat. Eine Beitragsliste kann Datum, Autor, Auszug und Kategorie ausblenden und sieht damit aus wie ein Projektraster statt wie ein Blogarchiv. Beinahe lautlos danebengegangen: |default(true) macht in Twig aus einem gespeicherten false ein true - die Einstellung haette sich speichern lassen und nichts getan. Der Test prueft beide Richtungen. Ein Bild darf verlinken, damit ein Logo auf die Startseite zeigt; die Adresse geht durch dieselbe Pruefung wie jeder andere Link. Und der Kopfbereich kann beim Scrollen oben bleiben - als Einstellung der Website, weil nur das umschliessende Element kleben kann. Der groesste Fund steckt im Editor: die Liste bekannter Schemafassungen endete bei 12, waehrend Bausteine v13 bis v15 verlangen. In einem aktuellen Dokument liess sich damit kein Baustein mehr einfuegen, und Symbol, Fortschritt, Slider und externer Inhalt in gar keinem. Jetzt eine Zahl an einer Stelle, mit einem Test ueber jeden Typ und jede Fassung. Geprueft: 389 PHP-Tests unter 8.2 und 8.4, 18 JS-Tests, Editortest, Browserpruefung. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
157 lines
11 KiB
Twig
157 lines
11 KiB
Twig
{% extends 'admin/shell.html.twig' %}
|
||
{% set nav = 'settings' %}
|
||
{% block title %}Einstellungen{% endblock %}
|
||
{% block heading %}Unternehmen und Adressen{% endblock %}
|
||
{% block lead %}Aus diesen Angaben entstehen die strukturierten Daten der Startseite. Was hier steht, ist genau das, was Suchmaschinen lesen – es gibt keine zweite, unsichtbare Fassung.{% endblock %}
|
||
{% block content %}
|
||
|
||
{% if missingLegal is not empty %}
|
||
{# Not a hint, a warning: in Germany both pages are required, and they have to be reachable from
|
||
every page of the site. #}
|
||
<div class="card">
|
||
<div class="card-head"><h2>Pflichtseiten fehlen</h2><span class="muted">{{ missingLegal|join(' und ') }}</span></div>
|
||
<div class="card-body">
|
||
<p class="notice error" role="alert">Ohne {{ missingLegal|join(' und ') }} ist diese Website in Deutschland nicht rechtskonform – beide müssen von jeder Seite aus erreichbar sein. KantSites verlinkt sie automatisch im Fuß jeder Seite, sobald sie hier eingetragen sind.</p>
|
||
<p class="field-help">Der Knopf legt die fehlenden Seiten mit einem Gerüst an und verknüpft sie. Jede Pflichtangabe darin ist mit <code>NOCH AUSZUFÜLLEN</code> markiert – den Text musst du selbst schreiben oder von jemandem schreiben lassen, der dafür einstehen kann. Vorhandene Seiten werden nie überschrieben.</p>
|
||
<form action="{{ path('admin_legal_pages') }}" method="post">
|
||
<input type="hidden" name="_csrf_token" value="{{ csrf_token('settings') }}">
|
||
<button class="primary" type="submit">Fehlende Pflichtseiten anlegen</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
<section class="stack-section">
|
||
<div class="section-head"><h2>Unternehmensdaten</h2><span class="muted">Grundlage für Organization und LocalBusiness</span></div>
|
||
<form action="{{ path('admin_settings_save') }}" method="post" class="card card-body">
|
||
<input type="hidden" name="_csrf_token" value="{{ csrf_token('settings') }}">
|
||
<div class="fields">
|
||
<div>
|
||
<label for="legalName">Name des Unternehmens</label>
|
||
<input id="legalName" name="legalName" maxlength="200" value="{{ settings.legalName }}">
|
||
</div>
|
||
<div>
|
||
<label for="phone">Telefon</label>
|
||
<input id="phone" name="phone" maxlength="40" value="{{ settings.phone }}">
|
||
</div>
|
||
<div>
|
||
<label for="email">E-Mail</label>
|
||
<input id="email" name="email" type="email" maxlength="120" value="{{ settings.email }}">
|
||
</div>
|
||
<div>
|
||
<label for="street">Straße und Hausnummer</label>
|
||
<input id="street" name="street" maxlength="120" value="{{ settings.street }}">
|
||
</div>
|
||
<div>
|
||
<label for="postalCode">Postleitzahl</label>
|
||
<input id="postalCode" name="postalCode" maxlength="16" value="{{ settings.postalCode }}">
|
||
</div>
|
||
<div>
|
||
<label for="city">Ort</label>
|
||
<input id="city" name="city" maxlength="120" value="{{ settings.city }}">
|
||
</div>
|
||
<div>
|
||
<label for="country">Land</label>
|
||
<input id="country" name="country" maxlength="2" pattern="[A-Za-z]{2}" value="{{ settings.country }}">
|
||
<p class="field-help">Zweibuchstabiger Code, zum Beispiel DE.</p>
|
||
</div>
|
||
</div>
|
||
<label for="homePath">Startseite</label>
|
||
<select id="homePath" name="homePath">
|
||
{% for page in choosableHome %}<option value="{{ page.path }}"{{ settings.homePath == page.path ? ' selected' : '' }}>{{ page.title }} (/{{ page.path }})</option>{% endfor %}
|
||
</select>
|
||
<p class="field-help">Diese Seite antwortet unter <code>/</code>; ihre eigene Adresse leitet dauerhaft dorthin. Nur veröffentlichte Seiten sind wählbar, und die Startseite lässt sich nicht umbenennen oder in den Papierkorb legen.</p>
|
||
<label for="description">Kurzbeschreibung</label>
|
||
<input id="description" name="description" maxlength="300" value="{{ settings.description }}">
|
||
<label for="sameAs">Profile</label>
|
||
<textarea id="sameAs" name="sameAs" rows="3" placeholder="https://www.linkedin.com/company/…">{{ settings.sameAs|join('\n') }}</textarea>
|
||
<p class="field-help">Eine HTTPS-Adresse je Zeile, höchstens zwanzig.</p>
|
||
<label class="check-label" for="localBusiness"><input id="localBusiness" name="localBusiness" type="checkbox" value="1"{{ settings.localBusiness ? ' checked' : '' }}> Als lokales Unternehmen auszeichnen</label>
|
||
<p class="field-help">Braucht mindestens Straße und Ort. Sinnvoll bei einem Standort mit Kundenverkehr.</p>
|
||
<label class="check-label" for="llmsTxt"><input id="llmsTxt" name="llmsTxt" type="checkbox" value="1"{{ settings.llmsTxt ? ' checked' : '' }}> <code>llms.txt</code> ausliefern</label>
|
||
<p class="field-help">Eine maschinenlesbare Liste der veröffentlichten, indexierbaren Seiten. Optional. Kein Rankingfaktor und keine Voraussetzung für irgendeine Suchfunktion; ohne Aktivierung antwortet die Adresse mit 404.</p>
|
||
<label for="robotsExtra">Zusätzliche Zeilen für robots.txt</label>
|
||
<textarea id="robotsExtra" name="robotsExtra" rows="3">{{ settings.robotsExtra }}</textarea>
|
||
<p class="field-help">Nur Kommentarzeilen und einfache Anweisungen wie <code>Disallow: /pfad</code>. Höchstens dreißig Zeilen.</p>
|
||
<div class="section-head"><h2>Einwilligung</h2><span class="muted">Nur nötig, wenn die Website etwas von Dritten lädt</span></div>
|
||
<label class="check-label" for="stickyHeader"><input id="stickyHeader" name="stickyHeader" type="checkbox" value="1"{{ settings.stickyHeader ? ' checked' : '' }}> Kopfbereich beim Scrollen oben halten</label>
|
||
<label class="check-label" for="consentBanner"><input id="consentBanner" name="consentBanner" type="checkbox" value="1"{{ settings.consentBanner ? ' checked' : '' }}> Einwilligungshinweis anzeigen</label>
|
||
<p class="field-help">Ohne eingebettete Inhalte anderer Anbieter braucht diese Website keine Einwilligung – dann ist ein Banner nur im Weg und sollte aus bleiben. Eingebettete Inhalte laden ohnehin erst nach Zustimmung, unabhängig von diesem Schalter.</p>
|
||
<fieldset class="field-set">
|
||
<legend>Worüber gefragt wird</legend>
|
||
{% for value, label in consentGroups %}
|
||
<label class="check-label" for="group-{{ value }}"><input id="group-{{ value }}" name="consentGroups[]" type="checkbox" value="{{ value }}"{{ value in settings.consentGroups ? ' checked' : '' }}> {{ label }}</label>
|
||
<p class="field-help">{{ consentGroupHelp[value] }}</p>
|
||
{% endfor %}
|
||
</fieldset>
|
||
<label for="consentText">Text im Hinweis</label>
|
||
<textarea id="consentText" name="consentText" rows="3" maxlength="600">{{ settings.consentText }}</textarea>
|
||
<p class="field-help">Leer lassen für den Standardtext. Er soll sagen, was geladen wird und was ohne Zustimmung passiert – nicht zum Klicken drängen.</p>
|
||
<div class="fields">
|
||
<div>
|
||
<label for="privacyPath">Adresse der Datenschutzseite</label>
|
||
<input id="privacyPath" name="privacyPath" maxlength="400" value="{{ settings.privacyPath }}" placeholder="datenschutz">
|
||
<p class="field-help">Pflicht, sobald der Hinweis an ist, und die Seite muss existieren.</p>
|
||
</div>
|
||
<div>
|
||
<label for="imprintPath">Adresse des Impressums</label>
|
||
<input id="imprintPath" name="imprintPath" maxlength="400" value="{{ settings.imprintPath }}" placeholder="impressum">
|
||
</div>
|
||
</div>
|
||
<p class="field-help">Die Entscheidung liegt in einem Cookie dieser Website, ein halbes Jahr lang, ohne Kennung und ohne Übermittlung. Unter <code>/einwilligung</code> kann sie jederzeit geändert werden; auf jeder Seite steht dafür ein Link.</p>
|
||
|
||
<button class="primary" type="submit">Einstellungen speichern</button>
|
||
</form>
|
||
</section>
|
||
|
||
<section class="stack-section">
|
||
<div class="section-head"><h2>Weiterleitungen <span class="count">{{ redirects.total }}</span></h2><span class="muted">Werden vor dem 404 geprüft.</span></div>
|
||
<form action="{{ path('admin_redirect_save') }}" method="post" class="card card-body">
|
||
<input type="hidden" name="_csrf_token" value="{{ csrf_token('settings') }}">
|
||
<div class="fields">
|
||
<div>
|
||
<label for="from">Alte Adresse</label>
|
||
<input id="from" name="from" maxlength="400" required placeholder="alte-seite">
|
||
</div>
|
||
<div>
|
||
<label for="to">Neue Adresse</label>
|
||
<input id="to" name="to" maxlength="400" placeholder="leistungen/webdesign">
|
||
<p class="field-help">Bei 410 leer lassen.</p>
|
||
</div>
|
||
<div>
|
||
<label for="status">Art</label>
|
||
<select id="status" name="status">
|
||
<option value="301">301 – dauerhaft verschoben</option>
|
||
<option value="410">410 – entfernt</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<button class="primary" type="submit">Weiterleitung speichern</button>
|
||
</form>
|
||
|
||
{% if redirects.items is not empty %}
|
||
<table class="table redirect-table">
|
||
<caption class="visually-hidden">Gespeicherte Weiterleitungen</caption>
|
||
<thead><tr><th scope="col">Alte Adresse</th><th scope="col">Ziel</th><th scope="col">Art</th><th scope="col">Entfernen</th></tr></thead>
|
||
<tbody>
|
||
{% for entry in redirects.items %}
|
||
<tr>
|
||
<td><code>/{{ entry.from }}</code></td>
|
||
<td>{% if entry.to %}<code>/{{ entry.to }}</code>{% else %}<span class="muted">—</span>{% endif %}</td>
|
||
<td>{{ entry.status }}</td>
|
||
<td>
|
||
<form action="{{ path('admin_redirect_save') }}" method="post">
|
||
<input type="hidden" name="_csrf_token" value="{{ csrf_token('settings') }}">
|
||
<input type="hidden" name="remove" value="{{ entry.id }}">
|
||
<button class="text-button danger-text" type="submit" aria-label="Weiterleitung von /{{ entry.from }} entfernen">Entfernen</button>
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
{% include 'admin/pager.html.twig' with {result: redirects, route: 'admin_settings', label: 'Seiten der Weiterleitungen'} only %}
|
||
{% endif %}
|
||
</section>
|
||
<p class="footnote">KantSites · Arbeitsstand</p>
|
||
{% endblock %}
|