Files
KantSites/src/Controller/ThemeController.php
T
kantarciandClaude Fable 5.1 7eb462ac7b Redirect the other www spelling to SITE_ORIGIN, hold roles to the role help and fix the audit findings
SITE_ORIGIN alone decides whether the site is www or not: CanonicalHostSubscriber
sends the other spelling there with 301, cms:site:origin writes the setting, the
system screen shows it. The roles now match the role help (editor: drafts,
publisher: live content, admin: design, settings, users, system). The installer no
longer reopens on an unreachable database, the session cookie follows ADMIN_PATH,
percent-encoded credentials no longer break the container, consent only accepts
same-site decisions, JSON-LD is HTML-safe, own CSS stays in its block, plus the
editor, updater, download and public-output findings from the full review
(ADR 099, ADR 100).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 20:15:20 +02:00

107 lines
4.1 KiB
PHP

<?php
namespace App\Controller;
use App\Content\ButtonDesign;
use App\Content\FontLibrary;
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 ThemeController extends AbstractController
{
public function __construct(private Theme $theme, private FontLibrary $fonts)
{
}
#[Route('/%kant.admin_path%/theme', name: 'admin_theme', methods: ['GET'])]
public function index(): Response
{
// Das Design gilt sofort auf jeder Seite - das ist Administratorensache, wie die
// Rollenhilfe es verspricht.
$this->denyAccessUnlessGranted('ROLE_ADMIN');
$theme = $this->theme->load();
return $this->render('admin/theme.html.twig', [
'theme' => $theme,
'fallbacks' => Theme::FALLBACKS,
'weights' => Theme::WEIGHTS,
'buttonOptions' => ButtonDesign::OPTIONS,
'buttonPresets' => ButtonDesign::PRESETS,
'fontsLocal' => $this->fonts->hasLocal($theme),
'fontsMissing' => $this->fonts->missing($theme),
// Die Vorschau der Schriften nutzt die Dateien, die schon hier liegen.
'fontsStylesheet' => $this->fonts->hasStylesheet(),
'palettes' => Theme::PALETTES,
'colorNames' => Theme::COLOR_NAMES,
]);
}
/**
* Holt die gewaehlten Google-Schriften auf den eigenen Server.
*
* Auf Zuruf, nicht bei einem Seitenaufruf: das dauert Sekunden und braucht eine Verbindung
* nach aussen, die ein Besucher nie abwarten soll.
*/
#[Route('/%kant.admin_path%/theme/fonts', name: 'admin_theme_fonts', methods: ['POST'])]
public function fonts(Request $request): Response
{
$this->denyAccessUnlessGranted('ROLE_ADMIN');
if (!$this->isCsrfTokenValid('theme', $request->request->getString('_csrf_token'))) {
$this->addFlash('error', 'Deine Sitzung ist abgelaufen. Bitte melde dich erneut an.');
return $this->redirectToRoute('admin_theme');
}
try {
$result = $this->fonts->fetch($this->theme->load());
$this->addFlash('success', sprintf(
'%d Schriftdatei(en) geladen, %s insgesamt: %s. Die Website lädt jetzt nichts mehr von Google.',
$result['files'],
$this->bytes($result['bytes']),
implode(', ', $result['families']),
));
} catch (\RuntimeException $error) {
$this->addFlash('error', $error->getMessage());
}
return $this->redirectToRoute('admin_theme');
}
private function bytes(int $bytes): string
{
return $bytes < 1024 * 1024 ? round($bytes / 1024).' KB' : round($bytes / 1024 / 1024, 1).' MB';
}
#[Route('/%kant.admin_path%/theme/save', name: 'admin_theme_save', methods: ['POST'])]
public function save(Request $request): Response
{
$this->denyAccessUnlessGranted('ROLE_ADMIN');
if (!$this->isCsrfTokenValid('theme', $request->request->getString('_csrf_token'))) {
$this->addFlash('error', 'Deine Sitzung ist abgelaufen. Bitte melde dich erneut an.');
return $this->redirectToRoute('admin_theme');
}
$fonts = [];
foreach ($request->request->all('fonts') as $row) {
if (!is_array($row)) {
continue;
}
$row['weights'] = array_map('intval', (array) ($row['weights'] ?? []));
$fonts[] = $row;
}
try {
$this->theme->save($request->request->all('colors'), $fonts, $request->request->all('base'), $request->request->has('buttons') ? $request->request->all('buttons') : null);
} catch (InvalidPage $e) {
$this->addFlash('error', $e->getMessage());
return $this->redirectToRoute('admin_theme');
}
$this->addFlash('success', 'Das Design wurde gespeichert. Es gilt sofort auf allen Seiten.');
return $this->redirectToRoute('admin_theme');
}
}