All options live under the root key nowo_blog_kit.
- Full YAML tree
- Top-level options
- security
- web_ui
- listing
- comments
- html
- doctrine
- Twig globals
- FormKit profiles
- Translation overrides
- Examples
nowo_blog_kit:
user_class: null
default_locale: es
locales: [es, en]
security:
access_roles: [ROLE_ADMIN]
manage_roles: [ROLE_EDITOR]
moderate_roles: [ROLE_MODERATOR]
configure_roles: [ROLE_ADMIN]
access_checker: null
allow_unauthenticated: false
object_access:
strategy: none
service: null
web_ui:
layout_template: '@NowoBlogKitBundle/admin/layout.html.twig'
public_layout_template: '@NowoBlogKitBundle/public/layout.html.twig'
css_framework: bootstrap5
icon_set: bootstrap-icons
row_actions_display: icon
page_size: 20
privacy_url: '#'
listing:
mode: paginated
masonry:
strategy: masonry
columns_mobile: 1
columns_tablet: 2
columns_desktop: 2
comments:
rate_limit:
strategy: fixed_window
limit: 5
interval_seconds: 60
service: null
captcha:
strategy: honeypot
site_key: ''
secret_key: ''
min_score: 0.5
honeypot_field: website
service: null
html:
sanitize:
strategy: none
service: null
doctrine:
table_prefix: ''
connection: default| Key | Type | Default | Description |
|---|---|---|---|
user_class |
string|null | null |
FQCN of the host user entity implementing BlogUserInterface. Required for Doctrine resolve_target_entities. |
default_locale |
string | es |
Default locale used for translation fallback and BlogLocales. |
locales |
list | [es, en] |
Locales exposed to admin translation forms and public fallback. |
security |
map | see YAML | Access control for manage / moderate / configure routes. |
web_ui |
map | see YAML | Admin and public shells, CSS framework, pagination, privacy URL. |
doctrine |
map | see YAML | Bundle table prefixing and connection name. |
This bundle uses a single configuration tree. It does not expose default_profile / profiles of its own. FormKit profiles used by admin filters are documented below.
| Key | Default | Description |
|---|---|---|
access_roles |
[ROLE_ADMIN] |
Canonical REQ-UI-002 key. Any matching role grants all admin capabilities (manage, moderate, configure). Empty list adds no extra grant. |
manage_roles |
[ROLE_EDITOR] |
Article and tag admin access (admin_blog except comments/settings). |
moderate_roles |
[ROLE_MODERATOR] |
Comment moderation (admin_blog_comments*). Editors with manage_roles also moderate. |
configure_roles |
[ROLE_ADMIN] |
Settings screen (admin_blog_settings). |
access_checker |
null |
Optional service id implementing BlogKitAccessCheckerInterface. |
allow_unauthenticated |
false |
When true, the bundle uses an allow-all checker. Intended only for trusted demos. |
object_access.strategy |
none |
After roles: none (every granted editor sees all rows), owner (publications scoped to createdBy; configure roles see all), or service. |
object_access.service |
null |
Service id implementing BlogKitResourceAccessCheckerInterface when strategy: service. |
The bundle enforces roles on route names beginning with admin_blog (BlogKitAdminAccessSubscriber). Object checks run in admin controllers via BlogKitAccessDenied (edit/delete/inline, and listing filter for owner / custom services). Twig helpers nowo_blog_kit_can_manage_article(), nowo_blog_kit_can_manage_tag(), and nowo_blog_kit_can_moderate_comment() hide row actions; they are not a substitute for the service.
| Key | Default | Description |
|---|---|---|
layout_template |
@NowoBlogKitBundle/admin/layout.html.twig |
Base layout used by admin screens. Point this to your own layout to embed the admin UI in host chrome. |
public_layout_template |
@NowoBlogKitBundle/public/layout.html.twig |
Base layout used by public index and article pages. |
css_framework |
bootstrap5 |
Host CSS stack. The bundle keeps semantic blog-* markup and remaps buttons via UiKit. Allowed: bootstrap, bootstrap4, bootstrap5, tabler, tailwind, foundation, custom, none. Load the matching framework CSS in the host layout; switching this key MUST NOT require forking page Twig files. Default matches the FrankenPHP demo. |
icon_set |
bootstrap-icons |
How action glyphs are drawn. Allowed: bootstrap-icons, tabler-icons, ux_icon, svg_inline, none. Prepended to UiKit when the host has not set nowo_ui_kit.icon_set. |
row_actions_display |
icon |
Admin table row actions: icon, text, or icon_text. Switching this MUST NOT require forking list Twig. Prepended to UiKit when the host has not set nowo_ui_kit.row_actions_display. |
page_size |
20 |
Admin list page size (1–200). |
privacy_url |
# |
URL used in the public comment privacy checkbox label. |
Public /blog index. Admin settings can override with inherit (use YAML). Column fields use 0 to keep YAML.
| Key | Default | Description |
|---|---|---|
mode |
paginated |
paginated (numbered pages) or infinite (IntersectionObserver loads ?partial=1 card fragments). |
masonry.strategy |
masonry |
masonry (packed columns), grid (uniform rows), or list (single column). |
masonry.columns_mobile |
1 |
Columns below 640px (1–2). Ignored when strategy is list. |
masonry.columns_tablet |
2 |
Columns from 640px (1–2). Ignored when strategy is list. |
masonry.columns_desktop |
2 |
Columns from 960px (1–3). Ignored when strategy is list. |
BlogSettings.per_page (admin) is the page size and the infinite-scroll batch size.
Public POST /blog/{slug}/comments protection. Admin settings (/admin/blog/settings) can override the strategies with inherit meaning “use YAML”. CAPTCHA site_key / secret_key are YAML-only.
| Key | Default | Description |
|---|---|---|
strategy |
fixed_window |
none, fixed_window (per IP), per_ip_article, sliding_window, or service. |
limit |
5 |
Max posts per interval. 0 disables the limiter. |
interval_seconds |
60 |
Window length. |
service |
null |
Service id implementing BlogCommentRateLimiterInterface when strategy: service. |
Requires Symfony cache.app. Without a cache pool, limiting is skipped.
| Key | Default | Description |
|---|---|---|
strategy |
honeypot |
none, honeypot, recaptcha_v2, recaptcha_v3, hcaptcha, turnstile, or service. |
site_key |
'' |
Public widget key for remote providers. |
secret_key |
'' |
Server-side verification key. |
min_score |
0.5 |
Minimum reCAPTCHA v3 score. |
honeypot_field |
website |
Hidden field name for the honeypot strategy. |
service |
null |
Service id implementing BlogCommentCaptchaStrategyInterface when strategy: service. |
Remote providers need site_key and secret_key. Missing keys fail closed (form invalid).
| Key | Default | Description |
|---|---|---|
sanitize.strategy |
none |
none (trusted-editor |raw), strip, allowlist (safe CMS tags + YouTube/Vimeo iframes), or service. |
sanitize.service |
null |
Service id implementing BlogHtmlSanitizerInterface when strategy: service. |
Sanitizing runs on article translation persist and again on public render (before hashtag links / body enhancer).
| Key | Default | Description |
|---|---|---|
table_prefix |
'' |
Prefix applied to all bundle entity tables through TablePrefixListener. |
connection |
default |
Connection name recorded in configuration for host alignment. |
Example:
nowo_blog_kit:
doctrine:
table_prefix: 'tenant_a_'The bundle Twig extension publishes:
| Global | Meaning |
|---|---|
nowo_blog_kit_layout |
Active admin layout template |
nowo_blog_kit_public_layout |
Active public layout template |
nowo_blog_kit_css_framework |
Selected CSS framework hint |
nowo_blog_kit_default_locale |
Configured default locale |
nowo_blog_kit_locales |
Configured locales |
nowo_blog_kit_privacy_url |
Privacy policy URL for the comment form |
nowo_blog_kit_icon_set |
Selected icon set |
nowo_blog_kit_row_actions_display |
Selected row-action display mode |
nowo_blog_kit_can_manage |
Whether the current user may manage articles and tags |
nowo_blog_kit_can_moderate |
Whether the current user may moderate comments |
nowo_blog_kit_can_configure |
Whether the current user may edit blog settings |
Twig function:
| Function | Meaning |
|---|---|
nowo_blog_kit_container_class() |
Width wrapper (blog-container) plus container (Bootstrap/Tabler) or grid-container (Foundation). Tailwind / custom / none stay on blog-container only. |
nowo_blog_kit_captcha() |
Active comment CAPTCHA context (strategy, site_key, script_url, widget_class). |
nowo_blog_kit_can_manage_article(article) |
Object-level publication access (after roles). |
nowo_blog_kit_can_manage_tag(tag) |
Object-level tag access. |
nowo_blog_kit_can_moderate_comment(comment) |
Object-level comment moderation access. |
The extension prepends FormKit profiles when the host has not already defined them:
blog_kit— defaults for blog forms (translation_domain: NowoBlogKitBundle)filter— GET list filters (auto_placeholder, no labels)
The extension also prepends FormKit type_map.entity → Symfony\Bridge\Doctrine\Form\Type\EntityType so article tag fields resolve. Override nowo_form_kit.type_map in the host if needed.
Override those keys in nowo_form_kit.profiles if you need project-specific form chrome. For Bootstrap 5 hosts, set nowo_form_kit.css_framework: bootstrap (FormKit has no bootstrap5 value), add field_types for checkbox / choice, and register twig.form_themes with @NowoFormKitBundle/form/static_blocks.html.twig then bootstrap_5_layout.html.twig. The FrankenPHP demo does this in demo/symfony8/config/packages/.
Catalogues ship as NowoBlogKitBundle.{locale}.yaml under src/Resources/translations/ (en, es, it, fr, pt, de, nl).
To override strings in the host application, place the same domain in translations/NowoBlogKitBundle.{locale}.yaml (or XLIFF). Symfony loader order gives the application catalogue precedence for the same domain and locale.
See also USAGE.md and SECURITY.md.
Role-based editor access:
nowo_blog_kit:
security:
access_roles: [ROLE_ADMIN]
manage_roles: [ROLE_EDITOR, ROLE_ADMIN]
moderate_roles: [ROLE_MODERATOR, ROLE_ADMIN]
configure_roles: [ROLE_ADMIN]Custom access checker service:
nowo_blog_kit:
security:
access_checker: App\Security\BlogEditorAccessCheckerEditors only manage their own publications:
nowo_blog_kit:
security:
manage_roles: [ROLE_EDITOR]
configure_roles: [ROLE_ADMIN]
object_access:
strategy: ownerCustom object-level rules (teams, workflow, …):
nowo_blog_kit:
security:
object_access:
strategy: service
service: App\Security\BlogPublicationAccessCheckerHost layout + CSS framework (do not fork page templates):
nowo_blog_kit:
web_ui:
layout_template: 'admin/layout.html.twig' # project chrome (demo FrankenPHP uses this)
public_layout_template: 'base.html.twig'
css_framework: bootstrap5 # or: tailwind | foundation | custom
icon_set: bootstrap-icons
row_actions_display: icon # or: text | icon_text
privacy_url: '/privacy'Keep stylesheets / javascripts blocks in the host layout. Public pages stack nowo-ui.css + blog.css. Remap look-and-feel with --nowo-blog-* / --color-* (or --nowo-ui-*) instead of copying public/index.html.twig.
If the project content block is not admin / nowo_ui_content / body, use a one-file bridge:
{# templates/admin/nowo_blog_kit_bridge.html.twig #}
{% extends 'admin/layout.html.twig' %}
{% block body %}
{% block nowo_ui_content %}{% endblock %}
{% block admin %}{% endblock %}
{% endblock %}nowo_blog_kit:
web_ui:
layout_template: 'admin/nowo_blog_kit_bridge.html.twig'# Tailwind host (load Tailwind in base.html.twig)
nowo_blog_kit:
web_ui:
public_layout_template: 'base.html.twig'
css_framework: tailwind
icon_set: none
row_actions_display: text
# Foundation host
nowo_blog_kit:
web_ui:
public_layout_template: 'base.html.twig'
css_framework: foundation
icon_set: svg_inline
row_actions_display: icon_text
# Own CSS: semantic blog-* + nowo-ui-* only
nowo_blog_kit:
web_ui:
public_layout_template: 'base.html.twig'
css_framework: custom
icon_set: svg_inline
row_actions_display: textPublic listing (paginator or infinite scroll):
nowo_blog_kit:
listing:
mode: infinite # paginated | infinite
masonry:
strategy: masonry # masonry | grid | list
columns_mobile: 1
columns_tablet: 2
columns_desktop: 2Comment rate limit + CAPTCHA + HTML sanitizer:
nowo_blog_kit:
comments:
rate_limit:
strategy: per_ip_article
limit: 3
interval_seconds: 120
captcha:
strategy: recaptcha_v3
site_key: '%env(BLOG_CAPTCHA_SITE_KEY)%'
secret_key: '%env(BLOG_CAPTCHA_SECRET_KEY)%'
min_score: 0.5
html:
sanitize:
strategy: allowlistAdmin pages extend @NowoBlogKitBundle/admin/base.html.twig and public pages extend @NowoBlogKitBundle/public/base.html.twig. Those templates call {{ parent() }} in stylesheets / javascripts and then load nowo_ui_kit + nowo_blog_kit assets inside nested nowo_ui_styles / nowo_ui_scripts (REQ-UI-001). Keep matching stylesheets and javascripts blocks in the host layout so stacking works. Override only those nested blocks if you need extra CSS/JS. Do not fork every CRUD page to inject CSS/JS.
Row actions compose UiKit _row_actions. Switch web_ui.row_actions_display (icon / text / icon_text) without copying list templates. Deletes open a native <dialog> confirm (_delete_confirm.html.twig + UiKit _confirm) with POST + CSRF in the footer. The inline CMS editor is the same native-dialog contract (_modal_form.html.twig). See UiKit ADOPTION and STIMULUS.