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>
107 lines
4.1 KiB
PHP
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');
|
|
}
|
|
}
|