Forms are an editor now: one card per field with add and remove, the technical name derived from the label, options only for a choice, and a new "Aussehen" section with a button label and four colours (field border and background, button and button text) that travel to the page as one rule per form inside the hashed style block. A live preview draws the form with the site's default button and names the text contrast. Statistics open with six figures (views against the same span before, average per day, busiest day, pages, referred views, missing addresses), a chart with grid lines and a title per bar, a weekday chart and ranked lists with share bars. The design screen shows the palette as colour cards with seven ready-made palettes, picks the base colours from the palette, previews every font in itself and the typography as a sample. The sidebar ends in an account card with role and a logout icon. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
104 lines
3.9 KiB
PHP
104 lines
3.9 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
|
|
{
|
|
$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_EDITOR');
|
|
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_EDITOR');
|
|
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');
|
|
}
|
|
}
|