# Changelog

All notable changes to ClearConsent will be documented here.
Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
Versioning: [Semantic Versioning](https://semver.org/)

## [Unreleased]

### Added
- Banner logo — optional ClearConsent mark on the consent notice (uses the agency logo when white-label is set; hidden when white-label hides branding and no custom logo)
- Banner layout option — choose buttons-only or category toggles on the consent banner (Banner choices)
- Styled category toggles on the consent banner — required categories shown locked/on
- Agency: Import settings from a CSV backup — restores each site by blog_id, skips deleted sites and corrupt rows (reports updated/missing/invalid), leaves sites not in the file untouched; gated behind a confirmation
- Agency: Export all sites — download a CSV backup (blog_id, site_url, settings_json) of every network site, for rollback before pushing
- Agency: My Sites tab for network administrators (Agency tier) — resolved at the network level so it appears on every subsite; pushing from a subsite uses that subsite as the source
- Gauges (Fathom Analytics) cookie signature — _gauges_* cookies are recognized as one Gauges tracker and resolve when a single gauges service is configured
- Geo-targeting: show the consent banner by region. Site owners can enable specific countries and US
  states and set per-region behavior (show opt-in, show opt-out, or hide), with an "everywhere else"
  default. Region is detected cache-safely via edge/CDN country headers and fails open to showing the
  banner. Honors the Global Privacy Control signal. No legal mapping is shipped.
- Debugging section on the Logs tab — exports a JSON support bundle (settings, configured scripts, and flagged trackers with their managed status) to copy, download, or send to support; never includes consent-log data, and script bodies are opt-in via a checkbox
- Plugin detection (is_plugin_active) for WooCommerce-specific cookie attribution
- WooCommerce Analytics scan result links to the setting to disable it
- Essential cookie classification — functional cookies shown as Covered without requiring consent gating
- WooCommerce cookie signatures — essential cart/payment cookies and analytics cookies recognised separately
- Export configuration to JSON file (settings, style, scripts, white label — selectable)
- Import configuration from export file with per-section selection
- Script Manager — paste tracking scripts directly into ClearConsent for automatic consent gating (Pro)
- Preset templates for GA4, Meta Pixel, GTM, Hotjar, Clarity, and Intercom
- "Add to Script Manager" button on scan results for known services
- Enable/disable toggle on Script Manager cards (no page reload)
- File location finder — scan results show which theme or plugin file contains each detected script
- "Found in X files" expandable list on scan results with file path and line number
- Note: consent log CSV export available from Export / Import section and the Logs tab
- PHP cookie consent check pattern in How to use tab
- Scan results show blocking capability per tracker (auto-blocked, needs tagging, or server-set cookie)
- "How?" link on needs-tagging trackers pointing to How to use tab
- Clean-room consent banner and script blocking (replacement for 
  Klaro-based predecessor, renamed to ClearConsent)
- Output buffer rewriter — automatically blocks hardcoded tracker 
  scripts without manual tagging (Pro)
- Layer 1 script_loader_tag interception for wp_enqueue_script trackers
- Iframe placeholder blocking with click-to-load UI
- Visual services editor with drag-and-drop reordering
- Tabbed Visual/JSON editor for services and translations
- Automatic scanner — browser scan (client-side iframe) and server 
  scan (Set-Cookie header parsing + DB option scan)
- Known tracker signature map (signatures.json) with 10+ services
- Google Consent Mode v2 — default deny-all before GA loads, 
  update on consent change
- Consent logging with anonymised IP (last octet zeroed), CSV export,
  configurable retention
- Agency Dashboard Phase 1 — multisite push settings to subsites
- White label branding — custom plugin name, logo, support URL, 
  hide credit line (Agency tier)
- Style tab with 6 pre-built templates (Default, Dark, Minimal, Bold, 
  Warm, Professional)
- "Required only" button on consent banner
- Privacy Policy link in notice footer
- Free plan: up to 3 manually configured services, full banner and 
  blocking
- Freemius licensing integration (Free / Pro / Agency tiers)
- Configurable "Required only" button label

### Removed
- Separate "Show a title on the consent banner" checkbox from Behavior 
  (now inline in Consent notice text section)

### Changed
- Google Consent Mode labeling corrected to describe the actual consent-gated behavior (Google tags wait until consent; all v2 signals denied by default) instead of implying advanced cookieless-modeling mode
- Settings reorganized: the Behavior section is gone — "Require a choice before browsing" moved to General, and Geo-targeting + Honor GPC now have their own Geo-targeting section (Load manually only stays under Developer options; Group by category stays under Services)
- Removed the global "Services start turned on" option — each service's own "Default (pre-enabled)" is now the sole source of initial state; optional services start off unless individually pre-enabled
- Removed the "Hide banner for administrators" option — the banner and script gating now apply to everyone, including logged-in admins (test in a private window)
- Agency: pushing settings now requires confirmation that names the source site and target count before overwriting
- Coverage tooltip now includes a tracker signature note where defined (e.g. Gauges host/platform context); the WooCommerce Analytics Disable-in-settings hint is now scoped to WooCommerce Analytics only
- Geo-targeting settings now report detection from the live visitor path and no longer imply geo is broken when wp-admin lacks edge headers
- Scan results: moved the blocking-status badge out of the Location column into the Coverage column, which now shows the specific status (Auto-blocked / Managed by ClearConsent / Manually tagged) as a coloured pill
- Moved "Load manually only" into Settings → Advanced → Developer options (it's a custom-code feature)
- Moved "Group services by category" into the Services section, directly above the services editor
- Scan results table now separates "Configured" (whether a tracker is set up as a consent service) from "Coverage" (whether the script is gated/blocked), as two distinct columns
- Renamed the scan-result "Add service" action to "Configure" and moved it into the Configured column
- Scan results Service configured column replaced with Coverage status (checkmark / warning / no-entry)
- PHP cookie scan results now show the plugin/theme file location
- Consent log export removed from Export / Import (already available in Logs tab)
- Export / Import section is now collapsible
- Script Manager modal field order — Name first, quick-add preset below with "None" default
- "Service" renamed to "Consent service" with clearer description
- Quick-add preset fills Code only (not Name or Consent service)
- Browser and server scans merged into single "Scan site" button
- Scan results Location column shows human-readable source (Database, Plugin, Theme, Cookie header, etc.)
- Scan results Source column removed
- Notice title and description now have inline show/hide toggles in 
  the Consent notice text section
- Privacy policy URL moved to Consent notice text section
- Service card Groups row hidden when grouping is disabled
- {purposes} placeholder hint moved to tooltip on Notice description field
- Notice description now allows basic HTML (links, bold, italic, line breaks)
- Visual separators added between settings sections
- Settings tab reorganised — button show/hide options moved inline with 
  their label fields
- Google Consent Mode, Translations, Logging, and Credit line moved 
  into Advanced collapsible section
- "Show as centered popup" moved to Style tab
- Credit line enabled by default
- Google Consent Mode v2 dependent fields now hidden until the feature 
  is enabled
- Consent logging retention field now hidden until logging is enabled

### Fixed
- Banner logo toggle (show_logo) now persists when unchecked on the Settings tab — its save was in the Style-save block, so unchecking on Settings never saved; moved to the Settings-save block next to show_credit
- PHP 7.4 compatibility: replaced str_starts_with() (PHP 8.0+ / WP 5.9 polyfill) with strpos() in the Script Manager file scan, so it runs on PHP 7.4 regardless of WP version
- Coverage: trackers with a signature note (e.g. Gauges) now show an honest host/source state from the signature (label + tooltip) instead of claiming they will be gated — even when a data-cc-name tag is present; Gauges reads Host-managed
- Geo-targeting: opt-out regions now pre-select optional services and opt-in regions clear them, overriding the per-service default (previously the region rule had no effect on the consent defaults)
- Multisite (subdirectory): geo-targeting region cache is now scoped per subsite, so one site's region rule no longer affects another
- Purge page cache (best-effort) when blocked-service configuration changes
- Reject cookie names starting with "wp"/"wordpress", which disable page caching on managed hosts
- Front-end pages are no longer marked no-cache, so full-page caching (Batcache, Edge Cache, and others) works again; consent is applied client-side so a single cached page is correct for every visitor and no choice leaks between users. The scan render stays uncached.
- Multisite: the network My Sites push screen no longer appears as a tab on individual sites settings (it remains under Network Admin)
- Scanner: detect and warn when a server-level page cache served a cached copy, so stale results are obvious
- Scanner: updated WooCommerce Analytics detection for current WooCommerce script filenames
- Multisite (subdirectory): consent choices are now scoped per subsite instead of shared across the network
- Multisite: consent now applies network-wide after pushing settings to subsites (protection was staying dormant on pushed sites)
- Repeat "Scan site" runs no longer double scan results — each server scan clears the previous result set before collecting
- Managed-duplicate warnings now name the true script origin (e.g. WP Header & Footer plugin) instead of the rendered "Hardcoded script: inline" form
- Managed Script Manager rows no longer show "✅ Managed" when a foreign duplicate still loads before consent — now shows "⚠️ Duplicate" with the foreign location
- Server-set HttpOnly cookie rows no longer show "Identify & configure" (consent-gating is impossible for PHP-set cookies)
- Combined scan shows Scan complete with server results when the browser scan times out, instead of Scan failed
- Tagged-slug cache TTL reduced to 5 minutes so removing a data-cc-name tag reflects on the next scan
- WooCommerce Analytics shows a Disable-in-settings status and link instead of a Script Manager button
- Tagged trackers (e.g. GA4) persist in scan results after a rescan instead of disappearing
- Coverage detects tagged scripts by data-cc-name in the page, regardless of whether the service is configured
- GA4 Add to Script Manager now combines both script tags (loader + init block)
- Add to Script Manager now prefills the actual captured script code (DB-option scripts such as Meta Pixel were not capturing raw code)
- File location scanner false positives from overly broad pattern matching
- Edit script modal reflects actual active state instead of always showing as active
- "Add to Script Manager" now prefills with the actual captured script code, not a generic preset template
- Configured services showing as unclassified in scan results when detected via cookie name match
- Notice description HTML rendered as literal text instead of parsed markup
- Required only button text invisible on hover in dark theme
- Fatal error on Style tab caused by missing notice_as_modal field renderer
- Translations section partially visible when Additional languages 
  is disabled
- Output buffer blocker was inerting its own config/GCM inline script
- Checkbox settings (embedded mode, group by purpose etc.) could not 
  be unchecked due to sanitize() defaults overwrite
- White label save was resetting all other settings (separate form 
  not detected as its own save type)
- Fixed: browser scan reporter script was never injected due to 
  wp_head hook registered too late (inside wp_enqueue_scripts)
- Fixed: server scan adds cache-busting query string to bypass 
  FastCGI and page caches on all hosting environments
- Duplicate scan results from multiple scan triggers
- Scan results merging correctly across browser and server scans

### Security
- Consent log stores anonymised IPs only (IPv4 last octet zeroed, 
  IPv6 last 80 bits zeroed)
- Browser scan URL is nonce-gated (10-minute expiry)
- Consent log AJAX rate-limited to 5 events/IP/hour
