--- phase: 01-rodo-cookie-consent plan: 01 type: execute wave: 1 depends_on: [] files_modified: - wp-content/themes/body-relax/functions.php autonomous: false delegation: off --- ## Goal Dokończyć integrację RODO: zablokować PixelYourSite (Facebook Pixel) gdy brak zgody oraz naprawić scenariusz powracającego użytkownika w Consent Mode v2. ## Purpose Audyt kodu wykazał: - `cookies.php` (Divi `header.php` linia 4, przed `wp_head()`) już ustawia Consent Mode v2 defaults (all denied) — AC-4 jest spełnione ✅ - `googleConsentModeHandler()` w JS aktualizuje consent po `window.load` — działa dla nowych użytkowników ✅ - **Problem 1:** dla powracających użytkowników (mają cookie `cnp_consent=true`) GTM czeka maks. 500ms (`wait_for_update`), a consent update przychodzi z JS po załadowaniu strony — ryzyko race condition na wolnych połączeniach ⚠️ - **Problem 2:** PixelYourSite ładuje Facebook Pixel server-side (PHP), niezależnie od cookies zgody — nie honoruje `cnp_consent` ❌ Cookies ustawiane przez Cookie Notice Pro (Flerosoft jQuery plugin w `Divi/libs/CookieNoticePro/`): - `cnp_consent` = `"true"` (akceptacja) lub `"false"` (odmowa) - `cnp_prefs` = JSON URL-encoded, np. `["necessary","marketing","analytics"]` ## Output `wp-content/themes/body-relax/functions.php` — dodane dwie funkcje: 1. Early consent update (wp_head priority 1) — PHP odczytuje `cnp_consent`/`cnp_prefs`, wywołuje `gtag('consent', 'update', ...)` przed snippetem GTM — eliminuje race condition 2. PixelYourSite GDPR filter — granularnie blokuje pixele na podstawie kategorii zgody ## Project Context @.paul/PROJECT.md ## Source Files @wp-content/themes/body-relax/functions.php @wp-content/themes/Divi/libs/CookieNoticePro/cookies.php @wp-content/themes/Divi/libs/CookieNoticePro/cookienoticepro.script.js @wp-content/themes/Divi/header.php @wp-content/plugins/pixelyoursite/includes/class-events-manager.php @wp-content/plugins/pixelyoursite/includes/functions-gdpr.php ## AC-1: Powracający użytkownik — brak race condition w Consent Mode ```gherkin Given użytkownik ma cookie cnp_consent="true" i cnp_prefs zawierający "marketing","analytics" When strona się ładuje Then w PRZED snippetem GTM pojawia się gtag('consent', 'update', {all: 'granted'}) And GTM tagi śledzące mogą się uruchomić od razu (nie czekają na JS po window.load) ``` ## AC-2: Granularna blokada PixelYourSite ```gherkin Given brak cookie cnp_consent lub cnp_consent="false" When PHP renderuje stronę Then PixelYourSite nie wstrzykuje kodu Facebook Pixel (fbq) na stronie ``` ## AC-3: Kategorie zgody honorowane przez PixelYourSite ```gherkin Given cnp_consent="true" ale cnp_prefs NIE zawiera "marketing" When PHP renderuje stronę Then Facebook Pixel jest zablokowany (brak kategorii marketing) And jeśli cnp_prefs zawiera "analytics" — pixele analityczne działają ``` ## AC-4: Consent Mode v2 defaults — już działa, nie psuć ```gherkin Given cookies.php jest includowany w header.php linia 4 (przed wp_head) When strona się ładuje Then gtag('consent', 'default', {all: 'denied'}) jest ustawione przed GTM — NIE ZMIENIAJ tego mechanizmu ``` Task 1: Early consent update dla powracających użytkowników wp-content/themes/body-relax/functions.php Dodaj na końcu pliku funkcję hookowaną do `wp_head` z priorytetem 1. Cel: gdy użytkownik ma już cookie `cnp_consent=true`, PHP odczytuje je server-side i wstrzykuje `gtag('consent', 'update', ...)` PRZED snippetem GTM (GTM enqueuje się przez wp_head, który odpala hooki w kolejności priorytetów — nasz priority 1 jest pierwszy). Dla nowych użytkowników (brak cookie) — funkcja nic nie robi, defaults z cookies.php wystarczą. ```php add_action( 'wp_head', 'szkolenia_consent_early_update', 1 ); function szkolenia_consent_early_update() { $consent = isset( $_COOKIE['cnp_consent'] ) ? $_COOKIE['cnp_consent'] : ''; if ( $consent !== 'true' ) { return; // Nowy użytkownik lub odmowa — defaults z cookies.php wystarczą } $prefs = []; if ( isset( $_COOKIE['cnp_prefs'] ) ) { $decoded = json_decode( urldecode( $_COOKIE['cnp_prefs'] ), true ); if ( is_array( $decoded ) ) { $prefs = $decoded; } } $analytics = in_array( 'analytics', $prefs ) ? 'granted' : 'denied'; $marketing = in_array( 'marketing', $prefs ) ? 'granted' : 'denied'; ?> 1. `php -l wp-content/themes/body-relax/functions.php` — brak błędów 2. W przeglądarce z cookie `cnp_consent=true` (ustaw ręcznie w DevTools): DevTools → Elements → `` — szukaj `gtag('consent', 'update'` — powinno być PRZED skryptem GTM 3. W przeglądarce bez cookie: brak `gtag('consent', 'update'` w źródle — tylko `gtag('consent', 'default'` z cookies.php AC-1 spełnione: early consent update dla powracających użytkowników Task 2: PixelYourSite GDPR filter z granularnymi kategoriami wp-content/themes/body-relax/functions.php Dodaj po Task 1 granularne filtry WordPress dla PixelYourSite. PixelYourSite używa filtrów: - `pys_disable_by_gdpr` — blokuje WSZYSTKIE pixele (zwróć true = zablokuj) - `pys_disable_facebook_by_gdpr` — tylko Facebook Pixel - `pys_disable_analytics_by_gdpr` — tylko Google Analytics - `pys_disable_bing_by_gdpr` — tylko Microsoft/Bing Ads Mapowanie kategorii Cookie Notice Pro → typy pikseli: - "marketing" → Facebook Pixel, Bing Ads - "analytics" → Google Analytics ```php add_filter( 'pys_disable_facebook_by_gdpr', 'szkolenia_pys_marketing_consent' ); add_filter( 'pys_disable_bing_by_gdpr', 'szkolenia_pys_marketing_consent' ); function szkolenia_pys_marketing_consent( $disabled ) { if ( $disabled ) return true; return ! szkolenia_cnp_has_preference( 'marketing' ); } add_filter( 'pys_disable_analytics_by_gdpr', 'szkolenia_pys_analytics_consent' ); add_filter( 'pys_disable_google_ads_by_gdpr', 'szkolenia_pys_marketing_consent' ); function szkolenia_pys_analytics_consent( $disabled ) { if ( $disabled ) return true; return ! szkolenia_cnp_has_preference( 'analytics' ); } function szkolenia_cnp_has_preference( $type ) { if ( ! isset( $_COOKIE['cnp_consent'] ) || $_COOKIE['cnp_consent'] !== 'true' ) { return false; } if ( ! isset( $_COOKIE['cnp_prefs'] ) ) { return false; } $prefs = json_decode( urldecode( $_COOKIE['cnp_prefs'] ), true ); return is_array( $prefs ) && in_array( $type, $prefs, true ); } ``` UWAGA: Filtry `pys_disable_*_by_gdpr` zwracają `true` = zablokuj, `false` = pozwól. UWAGA: `szkolenia_cnp_has_preference` jest funkcją pomocniczą — zdefiniuj ją tylko raz. UWAGA: Nie dodawaj `pys_disable_by_gdpr` (globalnego) — granularne filtry są bardziej precyzyjne i nie zablokują Pinterest jeśli będzie potrzebny. 1. `php -l wp-content/themes/body-relax/functions.php` — brak błędów 2. Tryb incognito (brak cookies): DevTools → Elements → Ctrl+F szukaj `fbq(` — NIE powinno być w źródle 3. Z cookie `cnp_consent=true` i `cnp_prefs=["necessary","marketing","analytics"]` (URL-encoded, ustaw w DevTools): `fbq(` powinno być widoczne w źródle 4. Z cookie `cnp_consent=true` i `cnp_prefs=["necessary","analytics"]` (bez marketing): `fbq(` NIE powinno być (marketing zablokowany) AC-2 i AC-3 spełnione: PixelYourSite honoruje kategorię "marketing" i "analytics" Dwie funkcje w `wp-content/themes/body-relax/functions.php`: 1. `szkolenia_consent_early_update()` — PHP early gtag update dla powracających użytkowników 2. `szkolenia_pys_*_consent` filtry — granularna blokada PixelYourSite **Test A — Nowy użytkownik (brak zgody):** 1. Otwórz stronę w trybie incognito 2. DevTools → Network: BRAK requestów do `facebook.com/tr` 3. DevTools → Elements → ``: widoczny `gtag('consent', 'default', {...denied...})` z cookies.php, brak `gtag('consent', 'update', ...)` 4. DevTools → Elements: brak `fbq(` w kodzie strony **Test B — Powracający użytkownik (pełna zgoda):** 1. DevTools → Application → Cookies → dodaj: - `cnp_consent` = `true` - `cnp_prefs` = `%5B%22necessary%22%2C%22preferences%22%2C%22marketing%22%2C%22analytics%22%5D` 2. Odśwież stronę (NIE incognito) 3. DevTools → Elements → ``: widoczny `gtag('consent', 'update', {all: 'granted'})` PRZED snippetem GTM 4. DevTools → Elements: widoczny `fbq(` w kodzie strony **Test C — Użytkownik zaakceptował tylko analitykę (bez marketingu):** 1. Ustaw cookies: `cnp_consent=true`, `cnp_prefs=%5B%22necessary%22%2C%22analytics%22%5D` 2. Odśwież 3. DevTools → Elements: BRAK `fbq(` (Facebook zablokowany bo brak "marketing") 4. `gtag('consent', 'update', {...})`: `analytics_storage: 'granted'`, `ad_storage: 'denied'` **Test D — Kliknięcie "Akceptuj" w banerze:** 1. Wyczyść cookies, odśwież stronę incognito — baner powinien się pojawić 2. Kliknij "Akceptuj" 3. Odśwież stronę — cookie `cnp_consent=true` powinno być ustawione 4. Network tab: widoczne requesty Facebook Pixel Wpisz "approved" jeśli wszystkie testy przeszły, lub opisz co nie działa ## DO NOT CHANGE - wp-content/themes/Divi/* (w tym `libs/CookieNoticePro/cookies.php`, `header.php`) — Consent Mode v2 defaults już działają - wp-content/plugins/* (żadne pliki wtyczek) - wp-content/themes/body-relax/style.css - wp-content/themes/body-relax/divi-children-engine/* ## SCOPE LIMITS - Ten plan NIE konfiguruje GTM kontenera (tagi/triggery consent-aware) — osobna praca w GTM UI - Ten plan NIE zmienia konfiguracji Cookie Notice Pro (config w cookienoticepro.script.js) - Nie obsługuj Pinterest (`pys_disable_pinterest_by_gdpr`) — brak przesłanek że jest używany - Nie dodawaj error handling dla scenariuszy niemożliwych Przed oznaczeniem planu jako ukończony: - [ ] `php -l wp-content/themes/body-relax/functions.php` — brak błędów - [ ] Tryb incognito: brak `fbq(` i brak consent update w HTML - [ ] Cookie cnp_consent=true + prefs z marketing: `fbq(` w HTML i gtag update granted - [ ] Cookie cnp_consent=true + prefs BEZ marketing: brak `fbq(` - [ ] Checkpoint human-verify zaakceptowany - Wszystkie 3 AC spełnione (AC-4 był już OK przed planem) - Weryfikacja manualna przeszła (checkpoint approved) - Brak błędów PHP - Strona renderuje się normalnie, baner cookies działa Po ukończeniu utwórz `.paul/phases/01-rodo-cookie-consent/01-01-SUMMARY.md`