Dostosowywanie
Nadpisz szablony, style, serwisy PHP i konfigurację serwera — wszystko z katalogu local/.
System Nadpisywania local/
Każde dostosowanie żyje wewnątrz local/. Ten katalog to twoja warstwa site-specific. Core templates/, assets/ i src/ to framework. Twoje nadpisywania w local/ mają pierwszeństwo.
local/
├── assets/ # Nadpisywania SCSS + custom JS
│ ├── images/ # nadpisanie og-default.jpg
│ └── app.js # ładowany jako drugi entrypoint
├── content/ # cała twoja treść (strony, blog, config)
├── docker/ # custom konfiguracja Nginx
│ └── nginx/ # snippet .conf (redirects, strony błędów)
├── docs/ # site-specific EDITOR_GUIDE.md + STYLEGUIDE.md
├── src/ # custom serwisy PHP (namespace NotACms\Local\)
├── templates/ # nadpisywania szablonów
└── translations/ # nadpisywania stringów UI (messages.*.yaml)
Nadpisywanie Szablonów
Aby nadpisać szablon core, utwórz plik w tej samej ścieżce pod local/templates/:
# Nadpisz domyślny szablon strony
cp templates/page/default.html.twig local/templates/page/default.html.twig
# Teraz edytuj local/templates/page/default.html.twig
Loader Twig sprawdza najpierw local/templates/, potem fallback do templates/. Możesz nadpisać dowolny szablon bez dotykania plików core.
Nadpisywanie pojedynczego bloku
Użyj namespace @base, aby rozszerzyć oryginalny szablon i nadpisać tylko konkretne bloki:
{# local/templates/blog/post.html.twig #}
{% extends '@base/blog/post.html.twig' %}
{% block sidebar_bottom %}
<div class="my-widget">...</div>
{% endblock %}
Namespace @base zawsze wskazuje na oryginalny katalog templates/, więc możesz rozszerzyć prawdziwy plik bez tworzenia odwołania cyklicznego.
Pełne zastąpienie szablonu
Utwórz plik w tej samej ścieżce bez extends — zastępuje on oryginał w całości:
{# local/templates/components/navigation.html.twig #}
<nav class="my-nav">
<a href="/">Home</a>
</nav>
Częste nadpisywania:
| Szablon Core | Cel |
|---|---|
templates/base.html.twig |
Globalny layout, header, footer |
templates/components/navigation.html.twig |
Linki nawigacyjne |
templates/page/default.html.twig |
Domyślny layout strony |
templates/page/doc.html.twig |
Layout dokumentacji |
templates/blog/post.html.twig |
Layout posta bloga |
Nadpisywanie SCSS
local/assets/ jest ładowane jako drugi entrypoint importmap (app-local). Utwórz local/assets/app.js, który importuje twój SCSS:
// local/assets/app.js
import './styles/app_local.scss';
// local/assets/styles/app_local.scss
// OSTRZEŻENIE: plik główny musi nazywać się app_local.scss — nie app.scss ani inaczej.
// sass-bundle wymaga unikalnych nazw bazowych dla wszystkich głównych plików SCSS.
// Importuj zmienne SCSS core (kolory, odstępy, typografia) do użycia w nadpisaniach:
@import '../../../../assets/styles/variables';
// Nadpisz CSS custom properties dla swojej marki:
:root {
--accent: #2563EB; // przełącz na niebieski
--accent-bg: #EFF6FF;
}
// Dodaj custom style komponentów poniżej
.my-custom-hero {
background: var(--accent);
color: #fff;
}
Ważne: Przy użyciu
app-localmusisz także nadpisaćbase.html.twig, aby załadować oba entrypointy. Utwórzlocal/templates/base.html.twig:{# local/templates/base.html.twig #} {% extends '@base/base.html.twig' %} {% block stylesheets %} {{ importmap('app') }} {{ importmap('app-local') }} {% endblock %}Przekazanie obu entrypointów do
importmap()gwarantuje poprawną kolejność ładowania CSS:app.css(oryginał) najpierw, potem twoje nadpisywania.
Wskazówka: Zawsze nadpisuj CSS custom properties (
--accent,--bg, etc.) zamiast surowych wartości hex. Dzięki temu tryb jasny i ciemny będą działać poprawnie z kolorami Twojej marki. Pełną listę tokenów znajdziesz w Design Reference.
Custom Serwisy PHP
Utwórz interfejs serwisu w src/Service/, zaimplementuj go, a Symfony autowired go automatycznie. Dla rozszerzeń site-specific umieść implementacje w local/src/ z namespace NotACms\Local\:
// local/src/Service/MyCustomService.php
namespace NotACms\Local\Service;
use NotACms\Service\Content\ContentServiceInterface;
final class MyCustomService
{
public function __construct(
private readonly ContentServiceInterface $content,
) {}
/**
* @return array<\NotACms\Content\ContentItem>
*/
public function recentPosts(string $locale): array
{
return $this->content->getRecentPosts($locale, 5);
}
}
Inject do kontrolera przez constructor injection — kontener DI Symfony załatwia resztę.
Zaawansowane: Event listenery, filtry Twig, dekoratory serwisów
Oprócz podstawowych serwisów, możesz też tworzyć event listenery, filtry Twig i dekoratory serwisów w local/src/:
// local/src/EventListener/MyListener.php
namespace NotACms\Local\EventListener;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
#[AsEventListener]
final class MyListener
{
public function __invoke(MyEvent $event): void { /* ... */ }
}
// local/src/Twig/MyExtension.php
namespace NotACms\Local\Twig;
use Twig\Attribute\AsTwigFilter;
final class MyExtension
{
#[AsTwigFilter('my_filter')]
public function myFilter(string $value): string { /* ... */ }
}
// local/src/Service/MySiteConfigDecorator.php
namespace NotACms\Local\Service;
use NotACms\Service\SiteConfigServiceInterface;
use Symfony\Component\DependencyInjection\Attribute\AsDecorator;
#[AsDecorator(decorates: SiteConfigServiceInterface::class)]
final class MySiteConfigDecorator implements SiteConfigServiceInterface
{
public function __construct(private readonly SiteConfigServiceInterface $inner) {}
// nadpisz tylko metody, których potrzebujesz
}
Custom Konfiguracja Nginx
Dla deploymentów produkcyjnych umieść snippet konfiguracji Nginx w local/docker/nginx/. Każdy plik .conf w tym katalogu jest automatycznie dołączany wewnątrz bloku server {}.
Przy pierwszym deployu bootstrap seeduje trzy pliki z docs/demo/docker/nginx/:
redirects.conf— przekierowania SEO (www → non-www, stare URLe)error-pages.conf— strony błędów uwzględniające localecsp.conf— rozszerzona Content-Security-Policy dla zewnętrznych originów motywu demo (Phosphor Icons, Google Fonts)
# local/docker/nginx/redirects.conf — przykład
location = /old-page/ { return 301 /new-page/; }
location ~ "^\d{4}/\d{2}/\d{2}/([a-z0-9-]+)/$" { return 301 /blog/$1/; }
Konfigurację można podzielić na wiele plików — wszystkie *.conf są dołączane. Użyj prefiksów z numerami, jeśli kolejność ma znaczenie (np. 10-redirects.conf, 20-cache.conf).
Nadpisanie Content-Security-Policy
Szablon core emituje CSP bezpieczne dla motywu bare poprzez zmienną $csp ustawianą przed includem local/docker/nginx/*.conf, więc dowolny plik nadpisania może ją przedefiniować. Aby rozszerzyć policy dla własnych zewnętrznych assetów, utwórz plik typu local/docker/nginx/csp.conf z pojedynczą dyrektywą set:
# local/docker/nginx/csp.conf — rozszerza CSP o zewnętrzne originy
set $csp "default-src 'self'; script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval' data: cdn.example.com; style-src 'self' 'unsafe-inline' fonts.googleapis.com; font-src 'self' fonts.gstatic.com; img-src 'self' data:; frame-src 'self'; connect-src 'self';";
Nie dodawaj drugiego add_header Content-Security-Policy — przeglądarki biorą część wspólną wielu nagłówków CSP (bardziej restrykcyjne, nie szersze).
Nadpisywanie Tłumaczeń
Utwórz local/translations/messages.en.yaml i/lub messages.pl.yaml, messages.de.yaml z tylko kluczami, które chcesz zmienić. Są one merge'owane na wierzch plików core tłumaczeń, więc pominięte klucze zachowują swoją oryginalną wartość.
# local/translations/messages.pl.yaml
nav:
blog: "Artykuły"
footer:
copyright: "Wszelkie prawa zastrzeżone — Moja strona"
Możesz nadpisać dowolny klucz z translations/messages.*.yaml. Zagnieżdżone klucze używają tej samej wciętej struktury YAML co pliki oryginalne.
Domyślny obraz OG
Obraz zastępczy og:image wyświetlany, gdy strona nie ma obrazka wyróżniającego, domyślnie używa markowego placeholdera. Aby go zastąpić, umieść swój obraz w local/assets/images/og-default.jpg. Szablon automatycznie odczytuje z tej ścieżki.
Przy pierwszym ./notACMS deploy lub ddev build bootstrap seeduje ten plik z domyślnego core, jeśli jeszcze nie istnieje. Zastąp geseedowany plik własnym.
Dla zupełnie innego podejścia (inny format, wymiary lub logika), nadpisz blok {% block og_default_image %} w local/templates/base.html.twig:
{# local/templates/base.html.twig #}
{% extends '@base/base.html.twig' %}
{% block og_default_image %}
<meta property="og:image" content="{{ site_base_url ~ asset('images/og-default.jpg') }}">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:type" content="image/jpeg">
{% endblock %}
Lokalna Dokumentacja
local/docs/ zawiera dwa pliki referencyjne site-specific, oba gitignored i seedowane przy pierwszym deployu:
local/docs/EDITOR_GUIDE.md — przewodnik po pisaniu dla twojej strony: kategorie, zatwierdzone tagi, głos i style generowania obrazów. Seeded z docs/demo/docs/EDITOR_GUIDE.md. Mechaniki treści systemowej (pola frontmatter, struktura URL, serie, szkice) pozostają w docs/EDITOR_GUIDE.md.
local/docs/STYLEGUIDE.md — referencja designu dla twojej strony: tokeny projektowe, paleta kolorów, typografia i lista komponentów. Seeded z docs/demo/docs/STYLEGUIDE.md. Mechaniki styleguide (strona dev /styleguide/, konwencje SCSS) pozostają w docs/STYLEGUIDE.md.
Oba pliki są swobodnie edytowalne po seedingu — bootstrap nigdy ich nie nadpisuje.
Rozszerzanie Builda
Dodaj custom kroki builda tworząc komendę Symfony Console w local/src/Command/:
// local/src/Command/GenerateSitemapCommand.php
namespace NotACms\Local\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
#[AsCommand(name: 'local:sitemap')]
class GenerateSitemapCommand extends Command
{
// ...
}
Potem dodaj do swojego aliasu ddev build lub wywołaj explicit z ddev exec bin/console local:sitemap.
Gotowe Przykłady
Katalog docs/customization/ zawiera kompletne, gotowe do skopiowania przykłady. Każdy zawiera wszystkie niezbędne pliki i README z dokładnymi komendami cp.
| Przykład | Wzorzec | Co demonstruje |
|---|---|---|
custom-footer |
Pełna zamiana bazy | Własny footer — pokazuje jak dostosować prosty footer core |
custom-post-card |
Zamiana komponentu + SCSS | Pozioma karta posta — pokazuje @base extend, importmap app-local i nadpisanie komponentu |
self-hosted-fonts |
Pełna zamiana bazy + SCSS | Własne czcionki — pokazuje preload, @font-face i zastępowanie systemowych czcionek |
php-service-decorator |
PHP #[AsDecorator] |
Pomijanie walidacji Turnstile — pokazuje namespace NotACms\Local\ i wzorzec dekoratora |
twig-filter |
PHP #[AsTwigFilter] |
Dodanie filtra ` |
Dostosowywanie Produkcyjne
Zmienne Środowiskowe
Utwórz .env.local w głównym katalogu projektu (gitignored):
# Wymagane
APP_SECRET=changeme-generate-new
URL=example.com
# Sieć
NGINX_PORT=8123
CERTRESOLVER=le # lub 'dummy' do testowania
# Opcjonalne: Formularz kontaktowy
RUNTIME_PHP_ENABLED=true
MAILER_DSN=smtp://user:pass@smtp.example.com:587
TURNSTILE_SITE_KEY=1x00000000000000000000AA
TURNSTILE_SECRET_KEY=1x0000000000000000000000000000000AA
Nadpisywanie Docker Compose
Utwórz docker-compose.override.yaml dla lokalnych dostosowań:
services:
nginx:
ports:
- "8080:80" # Nadpisz NGINX_PORT dla tej maszyny
Nagłówki Bezpieczeństwa
Dodaj do local/docker/nginx/redirects.conf:
# Nagłówki bezpieczeństwa
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Cache statycznych assetów
location ~* \.(css|js|webp|ico|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
SSL / HTTPS z Traefik
Dołączony Docker Compose ma etykiety Traefik dla automatycznego SSL.
Wymagania:
- Sieć zewnętrzna:
docker network create web - Rekord DNS A wskazujący na IP serwera
- Porty 80/443 otwarte
.env.local:
URL=yourdomain.com
CERTRESOLVER=le # Let's Encrypt
#CERTRESOLVER=dummy # Self-signed do testowania
Dla custom konfiguracji Traefik, dodaj etykiety do docker-compose.override.yaml.
Custom Kroki Builda
Dodaj komendy post-build edytując scripts/rebuild-content.sh lub tworząc wrapper:
#!/bin/bash
# custom-deploy.sh
./notACMS deploy --prod
# Custom: Sync do S3, purge CDN, etc.
aws s3 sync public/static/ s3://my-bucket/ --delete
Uprawnienia Plików
Upewnij się co do poprawnego własności dla produkcji:
# Napraw uprawnienia przed deploy
sudo chown -R $USER:$USER .
chmod 755 local/docker/nginx/