Skip to content

Latest commit

 

History

History
521 lines (381 loc) · 21.5 KB

File metadata and controls

521 lines (381 loc) · 21.5 KB

Configuration

Table of contents

Extension alias: nowo_cookie_consent

# config/packages/nowo_cookie_consent.yaml
nowo_cookie_consent:
    doctrine:
        table_prefix: ''

    # Deprecated: use doctrine.table_prefix instead
    # table_prefix: ''

    # Categories shown in the modal (required is always shown)
    categories:
        - analytics
        - marketing
        - preferences

    # Persist user choices to CookieConsentLog entity
    use_logger: true

    # Load modal texts and display settings from Doctrine entities
    use_database_config: false

    # Show cookie definitions (name, category, duration, provider, purpose) in modal and legal pages
    use_cookie_inventory: false

    # Floating cookie icon button to reopen preferences (any corner)
    preferences_bubble_enabled: false
    preferences_bubble_position: bottom-right   # bottom-left | top-right | top-left
    # preferences_bubble_border_color: null     # hex, e.g. #30363c — bubble outline and SVG icon color
    # preferences_bubble_icon: null             # custom HTML/SVG; empty = default cookie SVG

    # Fetch modal settings from GET /cookie-consent/config in the browser
    fetch_config_via_api: false

    # HttpOnly flag on consent cookies
    http_only: true

    # Optional route name used as form action
    form_action: null

    # Keep enabled. Modal JS double-submits SameOrigin CSRF for XHR (no native submit).
    csrf_protection: true

    # Routes where the modal must not auto-open
    disabled_routes:
        - privacy
        - imprint

    # Route name patterns where the consent fragment is not rendered at all
    # (empty response; no Doctrine inventory/config work). Wildcards via fnmatch.
    # skip_render_routes:
    #     - staff_*
    #     - admin_*

    # When non-empty: render ONLY on these public route patterns (whitelist)
    # render_routes:
    #     - home
    #     - home_locale
    #     - guest_*
    #     - legal_*
    #     - nowo_auth_kit_*
    #     - health_*
    #     - nowo_pwa_*

    # Locale detection for the consent modal
    default_locale: en
    enabled_locales:
        - en
        - es
        - it
        - fr
        - de
        - pt
        - nl
        - pl
        - ca
    ui_theme: bootstrap   # bootstrap (default) or tailwind
    detect_locale_from_accept_language: true

    # Full-page overlay and scroll lock until the user chooses an option
    disable_page_interaction: false

Table prefix

When doctrine.table_prefix is set to e.g. app_, the log table becomes app_dashboard_cookie_log. Useful when multiple apps share a database or when your project uses a global table prefix convention (same pattern as DashboardMenuBundle).

Root table_prefix is deprecated but still supported as a fallback.

Locale detection

The bundle resolves the modal locale automatically in this order:

  1. locale query parameter
  2. _locale or locale request attribute
  3. Main request locale (for ESI/sub-requests)
  4. Current request locale
  5. Accept-Language header (when detect_locale_from_accept_language is true)
  6. default_locale

Configure supported languages with enabled_locales. Bundle translations ship for en, es, it, fr, de, pt, nl, pl, and ca.

UI theme

Choose the markup framework for the bundled consent modal:

nowo_cookie_consent:
    ui_theme: bootstrap   # default
    # ui_theme: tailwind
  • bootstrap — Bootstrap 5 modal markup (load Bootstrap CSS/JS in your layout, or rely on the CSS fallback in nowo-consent-modal.js).
  • tailwind — Tailwind utility classes; load Tailwind in your layout (CDN or build). No Bootstrap required.

Modal CSS and CSP (style-src nonces)

By default nowo-consent-modal.js injects cookie-consent.css as a <style> tag. Hosts with CSP style nonces should link the standalone file instead so styles are not dropped:

<link rel="stylesheet"
      href="{{ asset('nowo-cookie-consent.css', 'nowo_cookie_consent') }}"
      data-nowo-cookie-consent-css>

Markers that skip injection: link[data-nowo-cookie-consent-css], html[data-nowo-cookie-consent-external-css="true"], or #cookieconsent[data-nowo-external-css="true"].

Recommended DB profile layout

When use_database_config: true, modal layout / position / equal-weight buttons live on CookieConsentConfig (admin Profile settings), not YAML. Recommended defaults (new entity rows from 1.9.0):

Option Recommended
Consent modal box + wide + bottom + left + equal-weight buttons
Preferences modal same corner + equal-weight buttons

Override --nowo-cc-* tokens in host CSS to match brand colors.

Override templates per theme:

Theme Modal Form theme
bootstrap templates/bundles/NowoCookieConsentBundle/cookie_consent.html.twig form/cookie_consent_theme.html.twig
tailwind templates/bundles/NowoCookieConsentBundle/cookie_consent.tailwind.html.twig form/cookie_consent_theme.tailwind.html.twig

Twig helpers:

  • nowo_cookie_consent_enabled_locales()
  • nowo_cookie_consent_locale()

Translations

Translation domain: NowoCookieConsentBundle (CamelCase, matching the bundle name).

The bundle ships YAML files for en, es, it, fr, de, pt, nl, pl, and ca under src/Resources/translations/. Symfony loads the app translations first; missing keys fall back to the bundle.

How to override (application)

  1. Use the same domain: NowoCookieConsentBundle.
  2. Create a file in your application:
    • translations/NowoCookieConsentBundle.<locale>.yaml (or .xlf if your project uses XLF).
  3. Override only the keys you need. Keys not defined in the app file use the bundle default.

Example — override for the Spanish locale file (English sample values; replace with your locale copy):

# translations/NowoCookieConsentBundle.es.yaml
nowo_cookie_consent:
    consent_modal:
        title: 'Cookie settings'
        description: 'We use cookies to improve your experience.'
    category:
        analytics: 'Analytics'
  1. Clear the Symfony cache in dev if translations do not appear: php bin/console cache:clear.

Set nowo_cookie_consent.privacy_route to a Symfony route name to link the privacy policy from the modal.

When use_database_config: true, per-locale copy can also be stored in CookieConsentConfigTranslation entities (admin CRUD); YAML overrides still apply to keys not overridden in the database layer.

Database configuration

When use_database_config is true, the bundle loads copy and display settings from Doctrine entities:

  • CookieConsentConfig — behavior and modal layout (one default enabled record is typical)
  • CookieConsentConfigTranslation — per-locale texts mapped to bundle translation keys

Tables: dashboard_cookie_config and dashboard_cookie_config_translation (with optional doctrine.table_prefix).

The Symfony 8 demo enables this option and provides an admin CRUD at /demo/admin/cookie-consent-config.

Page overlay (per profile)

Enable the full-page overlay and scroll lock per consent profile:

  1. Set use_database_config: true in config/packages/nowo_cookie_consent.yaml.
  2. Open profile settings in the admin UI:
    • Bundle admin (import bundle routes): /cookie-consent-config/{id}/settings redirects to /settings/profile — routes nowo_cookie_consent_config_settings_edit (redirect) and nowo_cookie_consent_config_settings_section (section form)
    • Section slugs: profile, behavior, appearance, consent-modal, preferences-modal, route-targeting
    • Symfony 8 demo: Admin → Cookie consent config → profile → Settings
  3. Open the Appearance tab, enable Disable page interaction (disablePageInteraction).
  4. Adjust overlay intensity with Color theme on the same screen (--nowo-cc-overlay in CSS).

The bundle ships one FormType per settings tab under Nowo\CookieConsentBundle\Form\Settings\ (resolved via CookieConsentConfigSettingsSection::formType()), CookieConsentConfigSettingsAdminController, area/section tab partials, and Bootstrap templates under @NowoCookieConsentBundle/admin/config/. Wire routes via @NowoCookieConsentBundle/Resources/config/routing.yaml (same as cookie inventory admin).

The database value overrides the global YAML default when both are set. Twig helper: nowo_cookie_consent_disable_page_interaction().

Cookie inventory

When use_cookie_inventory is true, the bundle exposes structured cookie definitions in the preferences modal (per category) and via nowo_cookie_consent_cookie_inventory().

Each entry includes:

Field Description
name Cookie name (e.g. _ga, PHPSESSID)
category Consent block / category (required, analytics, marketing, …)
duration Retention period (free text, e.g. Session, 2 years)
type first_party or third_party
provider Vendor or domain (translatable)
purpose GDPR purpose text (translatable)
allowed_by_default Pre-check in granular mode before consent is saved (YAML: allowed_by_default; DB column)

Database entities (recommended for admin CRUD)

When use_database_config is enabled, store definitions as Doctrine entities linked to each profile:

  • CookieDefinitionname, duration, category, type, sort_order, allowed_by_default
  • CookieDefinitionTranslation — per-locale provider and purpose

Tables (with optional prefix):

  • {prefix}dashboard_cookie_definition
  • {prefix}dashboard_cookie_definition_translation

Run doctrine:schema:update or add a migration after enabling the bundle entities. The demo seeds sample rows with app:seed-cookie-definitions.

Static YAML inventory

When the active profile has no database rows, you can declare cookies in configuration:

nowo_cookie_consent:
    use_cookie_inventory: true
    cookie_inventory:
        - name: PHPSESSID
          duration: Session
          category: required
          type: first_party
          sort_order: 0
          translations:
              en:
                  provider: This website
                  purpose: Keeps your session active.
              es:
                  provider: Este sitio
                  purpose: Keeps your session active.
        - name: _ga
          duration: 2 years
          category: analytics
          type: third_party
          sort_order: 10
          allowed_by_default: false
          translations:
              en:
                  provider: Google Analytics
                  purpose: Distinguishes users for statistics.

Database definitions take precedence over YAML when both exist for the active profile.

Twig helper: nowo_cookie_consent_cookie_inventory().

Admin CRUD (bundle)

The bundle ships CookieDefinitionAdminController and Bootstrap admin templates under @NowoCookieConsentBundle/admin/cookie_definition/. Import the controller route in your app (same pattern as your config admin). Forms use CookieDefinitionType with embedded translations.

Route name prefix: nowo_cookie_consent_cookie_definitions_*.

Granular cookie selection

When enabled, optional categories show a cookie inventory table with an Allow column. Visitors can toggle individual cookies; category switches sync with per-cookie choices.

Enable globally (YAML default) or per profile when use_database_config: true:

nowo_cookie_consent:
    use_cookie_inventory: true
    granular_cookie_selection: true   # bundle default; overridden by CookieConsentConfig when DB config is active

Required cookies are always on and never appear in the granular toggle list.

Twig helper: nowo_cookie_consent_granular_cookie_selection().

Per-cookie consent is stored in the consent cookie JSON map. Check programmatically:

$cookieChecker->isCookieAllowedByUser('_ga', 'analytics');

Page interaction overlay

When disable_page_interaction is true, the modal adds a full-page overlay and blocks scrolling until the visitor accepts, rejects, or saves preferences.

Enable globally in YAML (works without Doctrine):

nowo_cookie_consent:
    disable_page_interaction: true

With use_database_config: true, the active CookieConsentConfig profile overrides the YAML default via disablePageInteraction.

Overlay opacity follows the active color_theme (--nowo-cc-overlay CSS variable). Twig helper: nowo_cookie_consent_disable_page_interaction().

Preferences bubble

When preferences_bubble_enabled is true, the bundle renders a fixed circular button with a cookie icon. It uses the same data-nowo-open-consent handler as manual “Cookie settings” links and opens the preferences step of the modal.

Keep the modal in the DOM after consent is saved:

{% if nowo_cookie_consent_should_embed_modal() %}
    {{ render(path('nowo_cookie_consent.show_if_not_set')) }}
{% endif %}

Position with preferences_bubble_position: bottom-right (default), bottom-left, top-right, or top-left.

Set preferences_bubble_border_color (hex, e.g. #60fed2) for the transparent bubble outline and cookie icon. With use_database_config: true, configure per profile via CookieConsentConfig::preferencesBubbleBorderColor in the admin settings form.

Set preferences_bubble_icon to custom SVG or HTML markup (e.g. an emoji wrapped in a <span>). With use_database_config: true, configure per profile via CookieConsentConfig::preferencesBubbleIcon in the admin settings form. Leave empty to use the default cookie SVG.

Twig helpers:

  • nowo_cookie_consent_preferences_bubble_enabled()
  • nowo_cookie_consent_preferences_bubble_position()
  • nowo_cookie_consent_should_embed_modal()

Override markup: templates/bundles/NowoCookieConsentBundle/cookie_consent_preferences_bubble.html.twig.

Two-step modal

When two_step_modal is enabled on the profile, the preferences step includes a close control (data-nowo-hide-preferences) that returns to the compact consent banner without closing the modal.

Config API (GET)

When fetch_config_via_api is true, the bundle exposes JSON endpoints for client-side modal configuration:

Route Path
nowo_cookie_consent.config /cookie-consent/config?locale=en
nowo_cookie_consent.config_localized /{_locale}/cookie-consent/config

Response shape:

{
  "code": 200,
  "data": {
    "autoShow": true,
    "guiOptions": { "consentModal": { "layout": "box", "position": "bottom center" } },
    "language": {
      "default": "en",
      "translations": {
        "en": {
          "consentModal": {
            "title": "Cookie settings",
            "description": "We use cookies..."
          }
        }
      }
    }
  }
}

The frontend script reads data-nowo-config-url on the modal, performs a GET, and applies the payload before opening the modal.

Admin Web UI

Admin pages extend @NowoCookieConsentBundle/admin/base.html.twig, which extends web_ui.layout_template (Twig global nowo_cookie_consent_layout_template) and stacks stylesheets / javascripts with {{ parent() }} (REQ-UI-001). Prefer pointing layout_template at your project layout (or a thin bridge that maps nowo_ui_content into your body block) instead of copying list/form templates. The default admin/layout.html.twig is a full HTML document (no parent()).

ui_theme vs web_ui.css_framework

These are independent settings:

Setting Scope Default Controls
ui_theme Public modal bootstrap Markup variant for the GDPR consent modal rendered on the front-end (bootstrap or tailwind). Does not affect admin pages.
web_ui.css_framework Admin UI bootstrap5 CSS stack loaded in the bundle's admin layout. When set to bootstrap, bootstrap4, bootstrap5, or tabler the Bootstrap 5 CDN link is included automatically. For any other value (custom, tailwind, none, foundation) the layout injects UiKit nowo-ui.css via package nowo_ui_kit.

Change web_ui.css_framework without affecting the public modal, and vice-versa.

Option Default Description
web_ui.enabled true Registers admin access enforcement when security is configured
web_ui.path_prefix /cookie-consent-config Documented URL prefix for host access_control
web_ui.layout_template @NowoCookieConsentBundle/admin/layout.html.twig Root Twig layout extended by admin/base.html.twig
web_ui.css_framework bootstrap5 Host CSS stack hint (bootstrap / bootstrap4 / bootstrap5 / tabler → Bootstrap CDN; custom / tailwind / none / foundation → UiKit nowo-ui.css via nowo_ui_kit)
web_ui.icon_set bootstrap-icons Icon hint
web_ui.list_page_size 20 Page size for cookie definition admin lists
nowo_cookie_consent:
    web_ui:
        layout_template: 'base.html.twig'
        css_framework: bootstrap5   # change to custom/tailwind/none to drop Bootstrap CDN
        list_page_size: 20

Using a custom CSS framework (no Bootstrap CDN)

Set web_ui.css_framework: custom (or tailwind / none) to remove the Bootstrap CDN dependency from the admin layout. The bundle then automatically links UiKit admin stylesheet (nowo-ui.css) from the nowo_ui_kit asset package:

nowo_cookie_consent:
    web_ui:
        css_framework: custom

UiKit nowo-ui.css ships semantic styles plus --nowo-ui-* CSS custom properties for easy token overrides. Override tokens in your host CSS:

/* Override design tokens scoped to the admin root */
.nowo-ui-root {
    --nowo-ui-primary:       #6366f1;
    --nowo-ui-primary-hover: #4f46e5;
    --nowo-ui-bg:            #f9fafb;
    --nowo-ui-border-radius: 0.5rem;
}

If you supply your own host layout via web_ui.layout_template, load your CSS there and set css_framework: custom so the bundle does not inject its fallback stylesheet.

When using the project layout, load host CSS/JS in that layout; admin/base.html.twig stacks them with {{ parent() }} on both stylesheets and javascripts. Semantic hooks use nowo-ui-* classes. Legacy templates under admin/cookie_definition/layout.html.twig and admin/config/layout.html.twig remain as BC aliases that extend admin/base.html.twig.

Admin security

nowo_cookie_consent:
    security:
        access_roles: [ROLE_ADMIN]
        # access_checker: App\Security\CookieConsentAccessChecker
        allow_unauthenticated: false

Also lock the path in the host firewall:

# config/packages/security.yaml
security:
    access_control:
        - { path: ^/cookie-consent-config, roles: ROLE_ADMIN }

allow_unauthenticated: true is for demos/CI only. Production must keep it false and require symfony/security-bundle. The default CookieConsentAccessCheckerInterface implementation grants access when any configured role is granted; set a custom security.access_checker service id to replace it.

Twig overrides

REQ-TWIG-001. Namespace: NowoCookieConsentBundle. Full procedure, freeze rule, and the overridable <subpath> table: USAGE.md — Overriding templates. Theme-specific modal/form paths: UI theme. Prefer web_ui.layout_template over copying admin pages — Admin Web UI.

Twig helpers

  • nowo_cookie_consent_is_saved()
  • nowo_cookie_consent_is_category_allowed('analytics')
  • nowo_cookie_consent_is_open_by_default(route, disabled_routes)
  • nowo_cookie_consent_enabled_locales()
  • nowo_cookie_consent_locale()
  • nowo_cookie_consent_cookie_inventory()
  • nowo_cookie_consent_granular_cookie_selection()
  • nowo_cookie_consent_disable_page_interaction()
  • nowo_cookie_consent_should_embed_modal()
  • nowo_cookie_consent_should_render()
  • nowo_cookie_consent_preferences_bubble_enabled()
  • nowo_cookie_consent_preferences_bubble_position()
  • nowo_cookie_consent_preferences_bubble_border_color()
  • nowo_cookie_consent_preferences_bubble_icon()
  • nowo_cookie_consent_two_step_modal()

Cold start / Site Backup

When the application database schema is not ready (for example during Site Backup Bundle cold start), set one of these main-request attributes to false:

Attribute Constant
_nowo_site_backup_schema_exists ColdStartRequestAttributes::SITE_BACKUP_SCHEMA_EXISTS (shared with Site Backup Bundle)
_nowo_cookie_consent_schema_ready ColdStartRequestAttributes::COOKIE_CONSENT_SCHEMA_READY (local override)

Effects on the main request:

  • CookieConsentConfigTranslationSubscriber skips config resolution.
  • CookieConsentFormSubscriber skips consent form handling.
  • nowo_cookie_consent_should_render() returns false.

If Doctrine is still unreachable, subscribers log at debug and soft-skip on Doctrine\DBAL\Exception / Doctrine\ORM\Exception\ORMException only.

No bundle configuration keys are required when Site Backup already publishes _nowo_site_backup_schema_exists.