Skip to content

Latest commit

 

History

History
171 lines (122 loc) · 6 KB

File metadata and controls

171 lines (122 loc) · 6 KB

Quickstart

Install

composer require symfinity/privacy-settings-bundle

With Flex, the recipe registers the bundle, copies default category config, and wires the consent submit route.

Embed ConsentBanner (production)

Add the banner to your existing layout — one component line; CSS is injected automatically:

{# templates/base.html.twig #}
<body>
    <twig:ConsentBanner privacyPolicyUrl="https://example.com/privacy" />
    {% block content %}{% endblock %}
</body>

Equivalent: {{ component('ConsentBanner', { privacyPolicyUrl: '…' }) }} — tag and function syntax invoke the same Twig component.

Omit subjectKey for anonymous visitors. See Usage for props and kernel theming.

Configure categories

The Flex recipe copies a four-category default (required, analytics, marketing, media). Adjust labels and descriptions for your policy; keep ids stable so stored consent and Twig gates stay aligned.

# config/packages/symfinity_privacy_settings.yaml
symfinity_privacy_settings:
    categories:
        - id: required
          label: Required
          default_state: required
          description: Essential cookies and storage for security, remembering your consent choices, and core site functionality. Always active.
        - id: analytics
          label: Analytics
          default_state: disabled
          description: Optional measurement of how the site is used — page views, performance, and errors — to help improve the experience.
        - id: marketing
          label: Marketing
          default_state: disabled
          description: Optional cookies for ad campaign measurement and showing related content on other websites.
        - id: media
          label: Media
          default_state: disabled
          description: Optional third-party embeds such as video and audio players, maps, widgets, and social media content.

See Configuration for storage, enforcement, and default_state semantics.

Symfony integration

Flex copies config/routes/symfinity_privacy_settings.yaml, which imports the consent submit route (and optional dev demo loader). Manual installs: copy that file from the package into config/routes/ or import the bundle file:

# config/routes/symfinity_privacy_settings.yaml
symfinity_privacy_settings:
    resource: '@PrivacySettingsBundle/config/routes.yaml'

ConsentSubmitController reads the posted privacy[…] fields, calls PreferenceCaptureService, and redirects back to the referer.

The banner records choices. v0.2 adds enforcement helpers — see Enforcement for privacy_consent() and PrivacyMediaEmbed.

React to consent changes

Listen for ConsentDecisionEvent when a category toggles (banner save or programmatic capture):

<?php

declare(strict_types=1);

namespace App\EventListener;

use Symfinity\PrivacySettingsBundle\Event\ConsentDecisionEvent;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;

#[AsEventListener]
final class AnalyticsConsentListener
{
    public function __invoke(ConsentDecisionEvent $event): void
    {
        if ('analytics' !== $event->categoryId || !$event->newState) {
            return;
        }

        // Enable analytics scripts, tag managers, etc.
    }
}

Read choices in a controller

Inject PreferenceRestoreService when you need the effective state for a visitor key:

use Symfinity\PrivacySettingsBundle\Consent\PreferenceRestoreService;
use Symfinity\PrivacySettingsBundle\Symfony\CategoryModelNormalizer;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

public function __construct(
    private readonly PreferenceRestoreService $restoreService,
    private readonly CategoryModelNormalizer $normalizer,
    #[Autowire(param: 'symfinity.privacy_settings.categories')]
    private readonly array $rawCategories,
) {}

public function dashboard(string $subjectKey): Response
{
    $categories = $this->normalizer->normalize($this->rawCategories);
    $choices = $this->restoreService->effectiveChoices($subjectKey, $categories);

    // $choices['analytics'] === true|false
}

Gate integrations (v0.2)

See Enforcement for all Twig blocking methods (comparison table + examples).

{# 1. Server-side if — scripts, pixels, sections, manual iframes #}
{% if privacy_consent('analytics') %}
    <script src="/assets/analytics.js" defer></script>
{% endif %}

{# 2. Media iframe — uses media category; facade + Load when denied #}
<twig:PrivacyMediaEmbed provider="youtube" videoId="dQw4w9WgXcQ" title="Demo video" />

{# 3. Marketing pixel (same pattern as analytics) #}
{% if privacy_consent('marketing') %}
    <img src="https://example.com/pixel.gif" alt="" width="1" height="1" hidden>
{% endif %}

{# 4. Declarative script (requires enforcement.client_scripts: true) #}
<script type="text/plain" data-privacy-category="analytics" src="/assets/analytics.js"></script>

ConsentBanner loads privacy-settings-bundle/styles/privacy-settings-consent.css automatically via AssetMapper — no importmap.php or app.js import required.

Mark blocked assets

Declare scripts or iframes that belong to a category. Prefer privacy_consent() for Twig snippets, or enable the opt-in unblocker — see Enforcement.

<script type="text/plain" data-privacy-category="analytics" src="/assets/analytics.js"></script>

See strict-attribute-contract for forbidden aliases.

Headless use

Skip rendering ConsentBanner and call capture/restore services directly — see Usage.

Dev smoke route (not production)

GET /_privacy/demo is off in prod (default). It is enabled automatically in dev and test for maintainer smoke — see Verification.

Do not enable in production unless you explicitly want a public showcase page.

Maintainer tests (monorepo)

cd src/symfinity
./bin/php vendor/bin/phpunit packages/privacy-settings-bundle/tests/