=== COOKR – Cookie Consent & Script Blocking ===
Contributors: danjed
Tags: gdpr, cookie-banner, google-consent-mode, privacy, consent
Requires at least: 6.2
Tested up to: 7.0
Requires PHP: 7.4
Stable tag: 1.9.10
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

GDPR cookie consent with server-side script blocking. Prevent Google Analytics, GTM, Meta Pixel and third-party embeds from loading before consent.

== Description ==

GDPR cookie consent with real script blocking.

Block Google Analytics, Google Tag Manager, Meta Pixel, Hotjar, YouTube embeds, and other third-party services before they reach the browser.

Unlike JavaScript-based consent tools, blocked scripts never reach the browser at all.

Most cookie consent plugins display a banner and rely on JavaScript to stop tracking scripts. In many cases, those scripts can begin loading before the visitor has made a choice.

COOKR takes a different approach.

Scripts are blocked server-side before page delivery. Third-party services cannot execute until consent is explicitly granted. This helps website owners meet GDPR and TTDSG requirements more reliably.

Avoids the client-side race conditions common to JavaScript-only consent tools. No script guessing. No "hope it loads in time."

Consent enforcement instead of consent theatre.

✓ Server-side script blocking — blocked before the browser receives them
✓ Google Consent Mode v2 support
✓ No external consent cloud
✓ No proxy infrastructure
✓ No visitor data sent to third parties
✓ Works entirely on your WordPress installation

Built for site owners, agencies, and developers who want real GDPR cookie consent enforcement.

COOKR CORE includes:

* Consent banner & preferences UI
* Server-side script interception via PHP output buffer
* Auto-Blocker for third-party scripts and iframes
* Runtime Inspector
* CSP-aware restoration with nonce propagation
* Google Consent Mode v2 support
* Full JavaScript API (`window.cookrConsent`)
* Self-hosted operation — no external services required

COOKR RADR additionally includes:

* Privacy Radar — runtime detection and classification of third-party services
* Enforcement Verification Framework — structured protocol for proving enforcement claims
* Compatibility Matrix — tested stack database with VERIFIED/PARTIAL/UNKNOWN/FAILED states
* Automatic optimizer exclusions — LiteSpeed, WP Rocket, Autoptimize, FlyingPress
* Curated signature database with automatic weekly updates

= How It Works =

COOKR intercepts scripts in the PHP output buffer using `WP_HTML_Tag_Processor` before delivery to the browser. Matching script and iframe tags are neutralised server-side and restored only after the visitor grants consent.

There is no client-side race condition because blocking happens before the browser receives the page.

= Auto-Blocker =

Enable in Settings. Off by default.

When enabled, COOKR rewrites matching script tags and iframe tags server-side — setting `type="text/plain"` and preserving original attributes in `data-cookr-*` attributes for restoration after consent.

Test after enabling when using WP Rocket, LiteSpeed Cache, NitroPack, or Cloudflare Rocket Loader.

= Runtime Inspector =

The Runtime Inspector exposes third-party runtime activity directly in the browser — blocked scripts, restored services, iframe activity, detected domains.

Enable in Settings. Append `?cookr_debug=1` to any frontend URL while logged in as administrator.

= CSP-aware =

COOKR supports strict Content Security Policies without requiring `unsafe-inline`.

Restored scripts preserve CSP integrity via automatic nonce propagation. COOKR reads the nonce WordPress assigns to enqueued scripts at request time and passes it to restored scripts — no manual configuration required.

= Developer JS API =

`cookrConsent.has('analytics')`
`cookrConsent.require('marketing', callback)`
`cookrConsent.whenConsented('analytics').then(fn)`
`cookrConsent.on('consent' | 'change' | 'decline' | 'reset', handler)`
`cookrConsent.off(event, handler)`
`cookrConsent.getConsent()`
`cookrConsent.getExpiry()`
`cookrConsent.categories()`
`cookrConsent.reset()`

= Consent Categories =

* **Necessary** — Always active.
* **Analytics** — GA, GTM, Matomo, Hotjar, Clarity, etc.
* **Marketing** — Meta Pixel, Google Ads, TikTok, LinkedIn, etc.
* **External Media** — YouTube, Vimeo, Google Maps, etc.

= Does COOKR require an external cloud service? =

No. COOKR runs entirely on your WordPress installation.

= Does visitor consent data leave the server? =

No. Consent data is stored locally on your site.

= Is the Auto-Blocker enabled by default? =

No. Enable and test it after installation, particularly when using caching or JavaScript optimization plugins.

= Which services can be blocked? =

Any third-party script or iframe matching configured domains. Examples: Google Tag Manager, Meta Pixel, YouTube embeds, TikTok Analytics.

= Does COOKR support Google Consent Mode v2? =

Yes. Enable in settings when using GTM or GA4.

= How do I inspect runtime activity? =

Enable the Runtime Inspector in settings and append `?cookr_debug=1` to any frontend URL while logged in as administrator.

= Does COOKR store personal data? =

The consent log stores a hashed IP (not the raw IP address), consent choices, and a timestamp. The raw IP address is never stored.

= Is COOKR compatible with strict CSP? =

Yes. COOKR automatically reads the nonce WordPress assigns to enqueued scripts and passes it to restored scripts, preserving compatibility with `strict-dynamic` CSP policies. No manual configuration is required.

= Will COOKR work with caching plugins such as LiteSpeed Cache, WP Rocket, or Cloudflare? =

Yes, but always test after enabling script optimization features such as JavaScript combine, defer, delay, or Rocket Loader. COOKR performs script blocking server-side, but aggressive optimization plugins may alter script delivery and should be verified on your site.

RADR v1.9.8 adds automatic exclusion filters for LiteSpeed Cache, WP Rocket, Autoptimize, and FlyingPress — COOKR registers itself as excluded from JS combination pipelines automatically.

= Does COOKR require HTTPS? =

Yes. COOKR requires HTTPS for consent state to persist correctly across page loads. Modern browsers restrict cookie behaviour on HTTP origins — on HTTP, the consent cookie may not persist, causing the banner to reappear on every page load. HTTPS is also a legal recommendation under GDPR for any site collecting consent.

= What WordPress version is required? =

WordPress 6.2 or higher. COOKR uses `WP_HTML_Tag_Processor` for safe, attribute-aware script rewriting, introduced in WP 6.2.

== External Services ==

This plugin does not connect to any external service by default.

The auto-blocker contains a built-in list of known third-party domains (such as googletagmanager.com, connect.facebook.net, maps.googleapis.com, etc.) that is used purely as a local reference to identify and block scripts before consent. No data is sent to these domains by this plugin — the list is pattern-matching data stored locally in the plugin code.

== Changelog ==

= 1.9.10 =
* Fixed COOKR CRAZE built-in preset: the minimised (reopen) button was unstyled and the preferences-panel toggle switches showed the site's global accent colour (could be any colour, including red) instead of the preset's green — the preset never set `--cookr-accent`, so `cookr.css`'s defaults for `#cookr-reopen` and `.cookr-switch input:checked + .cookr-slider` (both driven by `var(--cookr-accent)`) fell through to the global accent colour setting
* Added `#cookr-banner, #cookr-reopen { --cookr-accent: #00ff88; }` and `#cookr-reopen svg rect { fill: #000000; stroke: #00ff88; }` to the preset — toggle switches and the reopen pill/dot now render in the preset's black/green theme regardless of the global accent colour setting
* Fixed admin preview panel loading spinner: `@keyframes cookr-spin` only animates `transform: rotate()`, but the spinner's centering also used `transform: translate(-50%, -50%)` — animating `transform` between an implicit `translate(...)` start and a `rotate(360deg)` end produced a visible jump/skip instead of a clean spin. Centering now uses `top/left + negative margin`, leaving `transform` free for rotation only
* Made the admin preview banner fully static — removed the cosmetic preferences-panel click handler, and the preview banner / reopen button no longer respond to hover or click (`pointer-events: none`). The preview is a visual reference only, not an interactive demo
* Fixed a critical non-idempotency bug in the custom CSS sanitiser (`cookr_scope_css`): every settings save re-sanitises the *currently saved* value (the textarea is repopulated with the previously-scoped output), but the selector-mirroring logic was not idempotent — an already-mirrored selector like `#cookr-banner .cookr-inner` would itself pass the #cookr-/.cookr- prefix check and get re-added on each save, and the `.cookr-title svg` icon-colour helper would re-fire for both a selector and its mirror, with both copies persisting forward. On real-world installs this had compounded over repeated saves into 150+ duplicate copies of some rules and a single selector list repeated 18 times, ballooning the saved CSS to 220KB. Selectors within each rule and the full set of emitted rules are now deduplicated (exact-string, order-preserving), making the sanitiser idempotent — re-saving now reproduces the same output instead of growing it. Existing bloated saved CSS will collapse back to its correct minimal form on the next save
* Fixed critical bug in custom CSS sanitiser (`cookr_scope_css`): a CSS comment placed directly above a rule (e.g. `/* Buttons */` followed by `.cookr-btn { ... }`) caused the comment text to be parsed as part of the selector, failing the #cookr-/.cookr-/:root prefix check and silently dropping the entire rule with no error shown to the admin
* CSS comments are now stripped before selector parsing — fixes silent loss of `:root` accent overrides and any commented/sectioned custom CSS
* Added font detection for classic themes that load fonts via enqueued stylesheets (e.g. Kadence default installs) — previously only customizer-saved typography and block-theme theme.json fonts were detected
* New detection path parses Google Fonts and Bunny Fonts family names from registered stylesheets on the frontend, cached in a 6-hour transient, invalidated on theme switch

= 1.9.9 =
* Fixed banner close behaviour — added state-aware close button always visible in banner
* Fixed Escape key — now follows same state-aware dismiss logic as close button
* No consent overwrite when closing with stored consent and no unsaved changes
* Prompts before discarding unsaved preference changes (native confirm dialog)

= 1.9.8 =
* Fixed LiteSpeed Cache JS Combine compatibility — cookrConfig inline block now isolated to a dedicated script handle, preventing third-party inline errors from aborting COOKR initialisation
* Added automatic JS exclusion filters for LiteSpeed Cache, WP Rocket, Autoptimize, and FlyingPress — COOKR registers itself as excluded from JS combination pipelines automatically
* Updated compatibility matrix schema — added protocol, protocol_version, test_payload, verification_method, special_checks fields across all entries
* Recorded first VERIFIED enforcement baseline: WordPress Core, Protocol v1.0, all six test cases passed
* Recorded LiteSpeed Cache JS Combine finding: FAIL without exclusion, PASS with automatic exclusion

= 1.9.7 =
* Fixed PHP notice in auto-blocker iframe rewrite handling
* Fixed blocked-status reporting for detected domains
* Fixed consent log rate-limit bypass edge case
* Fixed missing policy_version storage in consent records
* Fixed remote signature update setting persistence
* Fixed language switcher save-state detection
* Added full-consent buffer shortcut optimization

= 1.9.6 =
* Compliance improvements for WP.org review
* Removed generic CDN entries from auto-blocker allowlist
* Export uses explicit allowlist for safe settings only
* Build tooling improvements

= 1.9.5 =
* WordPress.org review and compliance-related improvements
* Internal maintenance and review-related updates

= 1.9.2 =
* Initial public CORE release
* 3×3 visual position picker replaces dropdown
* Accent colour now applies to banner icon and buttons in preview
* Auto-Blocker wording updated — recommended framing, test-after-enable guidance
* Runtime Inspector enabled by default
* Preserve data on uninstall enabled by default
* Consent log default retention reduced to 100 entries
* COOKR CRAZE built-in CSS preset

= 1.8.2 =
* Added: Runtime Inspector — detects unknown third-party script and iframe domains at runtime
* Added: Persistent findings stored per domain (first seen, last seen, page count)
* Added: Runtime Inspector toggle with configurable auto-disable duration

= 1.2.0 =
* Added: Google Consent Mode v2
* Added: debug inspector
* Added: browser chrome preview in admin dashboard

= 1.1.0 =
* Plugin renamed from GDPR Cookie Consent to COOKR
* Added: WP_HTML_Tag_Processor for safe, attribute-aware script rewriting
* Added: CSP nonce propagation

= 1.0.0 =
* Initial release

== Upgrade Notice ==

= 1.9.8 =
Patch release. Fixes LiteSpeed Cache JS Combine compatibility issue that could prevent the consent banner from rendering. Adds automatic optimizer exclusions for LiteSpeed, WP Rocket, Autoptimize, and FlyingPress.
