Files
att-consent/CLAUDE.md
Steve Hanlon 0a73181ea7 Refactor script injection to type="text/plain" inert pattern
Custom scripts are now rendered as inert <script type="text/plain"
data-att-cc-category="..."> tags in the page HTML. On consent,
consent-manager.js scans the DOM and activates matching elements.
This replaces the JSON-in-config approach and allows third-party
plugins (e.g. HFCM) to output consent-gated scripts using the
same data attribute convention.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 09:28:11 +00:00

85 lines
4.2 KiB
Markdown

# ATT Consent - Developer Context
## What This Is
A WordPress plugin implementing Google Consent Mode v2 with session attribution preservation. It replaces paid CMv2 plugins (e.g., CookieYes) with a self-contained, distributable alternative.
## Architecture
### Core Flow
1. **Inline `<head>` script (priority 1)** - Sets consent defaults and captures attribution to sessionStorage. Must run before any Google tags.
2. **Tracking script (priority 2)** - Loads gtag.js or GTM. In Advanced mode, loads immediately. In Basic mode, deferred until consent.
3. **Footer** - Banner HTML + enqueued `banner.js` and `banner.css`.
### Key Files
- `includes/class-frontend.php` - Orchestrates the output order. The `output_consent_defaults()` method generates the critical inline script.
- `public/js/consent-manager.js` - Core logic: `AttConsent.update()` replays attribution, sends consent signals, injects scripts, sets cookie. This is the public API.
- `public/js/banner.js` - UI only. Calls `AttConsent.update()` on button clicks.
- `includes/class-admin.php` - Admin settings with 5 tabs. Handles save via `admin_post`, scripts CRUD via AJAX.
- `includes/class-scripts-manager.php` - CRUD for custom JS snippets stored in `{prefix}att_cc_scripts` table.
### Settings
Single serialized option: `att_consent_settings`. Access via `ATT_Consent::get_settings()` which merges with defaults.
### Custom Scripts Table
```sql
{prefix}att_cc_scripts (id, name, snippet, category, placement, status, priority, created_at, updated_at)
```
Scripts are rendered in PHP as inert `<script type="text/plain" data-att-cc-category="{category}">` tags (head scripts at priority 99, footer scripts at priority 99). Non-script HTML (pixels, iframes) is wrapped in `<template data-att-cc-category="..." data-att-cc-type="html">`. On consent, `consent-manager.js` scans the DOM for matching elements and activates them by creating fresh executable copies.
Third-party plugins (e.g. HFCM) can output consent-gated scripts without any coupling to this plugin — they just need to use `type="text/plain"` and the `data-att-cc-category` attribute with a valid category (`functional`, `analytics`, `marketing`).
### Consent Cookie
Name: `att_cc_consent`. Value: JSON `{"functional":bool,"analytics":bool,"marketing":bool}`. First-party, SameSite=Lax.
### Attribution Storage
sessionStorage key `att_cc_attr` stores referrer, UTMs, GCLID/DCLID on first page of session. Key `att_cc_consent_given` tracks whether attribution has been replayed. Attribution is replayed via `gtag('set')` BEFORE `gtag('consent','update')` so GA4 session_start gets correct source.
## Category-to-Signal Mapping
| Category | Google Signals |
|----------|---------------|
| Necessary | `security_storage` (always granted) |
| Functional | `functionality_storage`, `personalization_storage` |
| Analytics | `analytics_storage` |
| Marketing | `ad_storage`, `ad_user_data`, `ad_personalization` |
## WordPress Hooks
**Filters:** `att_consent_settings_save`, `att_consent_banner_html`
**Actions:** `att_consent_before_banner`, `att_consent_after_banner`
**JS Event:** `att_consent_update` (CustomEvent with `detail: {functional, analytics, marketing}`)
## Coding Standards
- WordPress PHPCS (WordPress-Extra)
- All admin inputs sanitized: `sanitize_text_field`, `sanitize_hex_color`, `absint`, `wp_kses_post`
- Nonce verification on all forms and AJAX
- Capability check: `manage_options` for all admin operations
- Script snippets stored raw (admin-only input, intentionally executable)
- Frontend JS is vanilla - no jQuery dependency
- Text domain: `att-consent`
## Testing
Activate plugin, enter a GA4 Measurement ID in General settings, visit frontend. Verify:
- `window.dataLayer` contains consent default with `wait_for_update`
- Clicking Accept All fires `gtag('consent','update')` with all granted
- Cookie `att_cc_consent` is set
- Return visit: no banner shown, consent applied from cookie
- Attribution test: visit with `?utm_source=test`, navigate without consenting, consent on page 2, check GA4 DebugView for correct source
## Environment
- WordPress 6.8.3, Astra child theme
- Local Sites development environment
- WooCommerce + LearnPress site
- Previously used CookieYes (`cookie-law-info` plugin)