Files
KantSites/src/Controller/SettingsController.php
T
kantarciandClaude Opus 5 d6d476177a
CMS quality / php (8.2) (push) Canceled after 0s
CMS quality / php (8.4) (push) Canceled after 0s
CMS quality / browser (push) Canceled after 0s
CMS quality / mysql (push) Canceled after 0s
Fassung 1.4.3: Update ohne Git, Verwaltung auf dem Telefon, eigene Symbole
Vier Wuensche und ein Fund beim Nachmessen.

- Farbwaehler im Designbereich. Das Textfeld bleibt massgeblich, damit
  "@akzent" ein Verweis auf die Palette bleibt und nicht beim ersten
  Klick zu einem festen Hexwert erstarrt (ADR 066).
- Die Verwaltung auf dem Telefon: das Menue klappt unter 760 px zu und
  nennt den Bereich, in dem man steht; Ziele 44 px, kein Skript.
- Eigene SVG-Symbole statt Unicode-Zeichen - ein Telefon ersetzt die
  sonst durch Emoji in fremdem Stil. Zwei Eintraege trugen dabei
  denselben Pfad; ein Test findet beides (ADR 067).
- Update aus dem Releasearchiv: der Weg fuer Installationen ohne Git.
  Zwei Schritte, weil ein Prozess, der sich selbst ausgetauscht hat,
  mit dem alten Code weiterarbeitet. var/, .env, hochgeladene
  Schriften und das erzeugte Buendel bleiben unberuehrt; public/ wird
  dazu Datei fuer Datei zusammengefuehrt. Geprueft ohne Netz gegen
  eine Releaseseite im Speicher (ADR 068).

Beim Nachmessen der echten Startseite statt einer Testseite: das
groesste Element ueber der Falz ist dort kein <img>, sondern der
Hintergrund des ersten Abschnitts - und der steht nur im Stylesheet.
Drei Vorabverweise mit denselben Geraetegrenzen wie der Compiler holen
genau die Variante, die die Regel auch nimmt. LCP 1,3 s -> 1,1 s.
Dabei gefunden: dasselbe Bild wurde je Geraeteregel neu abgefragt -
Startseite 19 -> 15 Abfragen (ADR 069).

Geprueft: 428 Tests auf PHP 8.2 und 8.4, Editor-, Browser- und
Linkpruefung, Lighthouse auf drei echten Seiten (alles ausser der
Leistung der Startseite 100).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 23:01:32 +02:00

153 lines
6.4 KiB
PHP
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.
<?php
namespace App\Controller;
use App\Content\Consent;
use App\Content\DemoContent;
use App\Content\MediaLibrary;
use App\Content\PageRepository;
use App\Content\RedirectRepository;
use App\Content\SiteSettings;
use App\Content\Theme;
use App\Domain\InvalidPage;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
final class SettingsController extends AbstractController
{
public function __construct(private SiteSettings $settings, private RedirectRepository $redirects, private PageRepository $pages, private MediaLibrary $media, private Theme $theme)
{
}
#[Route('/%kant.admin_path%/settings', name: 'admin_settings', methods: ['GET'])]
public function index(Request $request): Response
{
$choosable = [];
foreach ($this->pages->listForEditor() as $page) {
// Only a published page can serve as the root; a draft would answer every visit with 404.
if ('page' === $page['type'] && null !== $page['published_revision_id']) {
$document = json_decode($page['document'] ?? '{}', true, 512, JSON_THROW_ON_ERROR);
$choosable[] = ['path' => $page['path'], 'title' => $document['title'] ?? $page['path']];
}
}
return $this->render('admin/settings.html.twig', [
'settings' => $this->settings->load(),
'redirects' => $this->redirects->all($request->query->getInt('seite', 1)),
'choosableHome' => $choosable,
'consentGroups' => Consent::GROUPS,
'missingLegal' => $this->settings->missingLegalPages(),
'consentGroupHelp' => Consent::GROUP_HELP,
// Für das Symbol der Website: die Mediathek zur Auswahl, die neuesten zuerst.
'images' => $this->media->page(1, false, 96)['items'],
// Fuer die Farbfelder: dieselbe Palette, die auch der Editor anbietet.
'palette' => $this->theme->load()['colors'],
]);
}
#[Route('/%kant.admin_path%/settings/company', name: 'admin_settings_save', methods: ['POST'])]
public function save(Request $request): Response
{
$this->denyAccessUnlessGranted('ROLE_EDITOR');
if (!$this->isCsrfTokenValid('settings', $request->request->getString('_csrf_token'))) {
return $this->back('Deine Sitzung ist abgelaufen. Bitte melde dich erneut an.');
}
try {
$this->settings->save($request->request->all());
} catch (InvalidPage $e) {
return $this->back($e->getMessage());
}
return $this->back('Die Unternehmensdaten wurden gespeichert.', 'success');
}
#[Route('/%kant.admin_path%/settings/redirects', name: 'admin_redirect_save', methods: ['POST'])]
public function saveRedirect(Request $request): Response
{
$this->denyAccessUnlessGranted('ROLE_EDITOR');
if (!$this->isCsrfTokenValid('settings', $request->request->getString('_csrf_token'))) {
return $this->back('Deine Sitzung ist abgelaufen. Bitte melde dich erneut an.');
}
$remove = $request->request->getString('remove');
if ('' !== $remove) {
$this->redirects->delete($remove);
return $this->back('Die Weiterleitung wurde entfernt.', 'success');
}
try {
$this->redirects->save(
$request->request->getString('from'),
$request->request->getString('to'),
$request->request->getInt('status', 301),
);
} catch (InvalidPage $e) {
return $this->back($e->getMessage());
}
return $this->back('Die Weiterleitung wurde gespeichert.', 'success');
}
/**
* Creates the privacy statement and the imprint, with a skeleton whose gaps are marked.
*
* The pages are mandatory, the text is not something a CMS can write: what gets created is the
* structure with every required item named and marked as still to be filled in.
*/
#[Route('/%kant.admin_path%/settings/rechtliches', name: 'admin_legal_pages', methods: ['POST'])]
public function legalPages(Request $request): Response
{
$this->denyAccessUnlessGranted('ROLE_EDITOR');
if (!$this->isCsrfTokenValid('settings', $request->request->getString('_csrf_token'))) {
return $this->back('Deine Sitzung ist abgelaufen. Bitte melde dich erneut an.');
}
$settings = $this->settings->load();
$created = [];
try {
foreach (DemoContent::LEGAL as $slug => $label) {
$key = 'datenschutz' === $slug ? 'privacyPath' : 'imprintPath';
// An existing page is never overwritten - it may already carry the real text.
$existing = null !== $this->pages->findPublished($slug) || $this->pageExists($slug);
if (!$existing) {
$id = $this->pages->createPage($slug, null, 'page', DemoContent::legalPage($slug));
$page = $this->pages->findForEditor($id);
$this->pages->publish($id, $page['draft_revision_id'], $page['version']);
$created[] = $label;
}
$settings[$key] = $slug;
}
$this->settings->save([...$settings, 'consentGroups' => $settings['consentGroups'], 'sameAs' => implode('
', $settings['sameAs'])]);
} catch (InvalidPage $e) {
return $this->back($e->getMessage());
}
return $this->back(
[] === $created
? 'Die Seiten waren schon vorhanden und sind jetzt verknüpft. Der Inhalt bleibt unverändert.'
: sprintf('%s angelegt und verknüpft. Beide Seiten sind veröffentlicht und müssen noch ausgefüllt werden – die Pflichtangaben sind darin markiert.', implode(' und ', $created)),
'success',
);
}
/** Also finds a page that exists but is not published yet. */
private function pageExists(string $path): bool
{
foreach ($this->pages->listForEditor() as $page) {
if ($path === $page['path']) {
return true;
}
}
return false;
}
private function back(string $message, string $type = 'error'): Response
{
$this->addFlash($type, $message);
return $this->redirectToRoute('admin_settings');
}
}