=== LandexHub Consent – Cookie Banner, GDPR & Google Consent Mode v2 ===
Contributors: landexhub
Tags: cookie-consent, cookie-banner, gdpr, consent-mode, cookies
Requires at least: 6.3
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 0.9.2
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Cookie banner for GDPR with Google Consent Mode v2, script gating, site diagnostics and consent logging. No cloud service required.

== Description ==

LandexHub Consent is a lightweight, configurable cookie consent banner (cookie bar) for WordPress. It sets the Google Consent Mode v2 default **before** the Google tags it loads, lets you gate third-party scripts behind consent, checks your front page for sources that load before the visitor decides, and records a privacy-minimal proof of consent — all stored in your own WordPress. No visitor IP address is stored in the consent log, and the plugin sends no telemetry and no data of its own anywhere. Google tags you choose to configure talk to Google, as described under External services.

**Features**

* Consent banner and preferences modal, responsive and accessible, with a live preview in the admin.
* Four categories — necessary, analytics, marketing, personalization — mapped to Google Consent Mode v2 signals.
* Google Consent Mode v2, **advanced by default**: measurement runs cookieless before consent and fully after. The `denied` default is printed early in `<head>`, before the GTM container or gtag.js set in this plugin. Switch to **basic** if the gtag.js measurement ID set in this plugin should not load at all until the visitor agrees. A GTM container always loads; what each tag inside it does before consent is set in GTM.
* Optional GTM / gtag.js loading — fill in your container or measurement ID (G-, AW- or DC-) and the plugin loads it right after its consent default.
* Script gating — mark any script (inline or external) with `type="text/plain"` and `data-consent-category` (an external one keeps its address in `data-src`), or gate an enqueued script with the `LandexHub_Consent::block_handle()` PHP helper. The plugin runs it once the visitor consents to that category.
* Site check — reads the HTML of your front page and lists the third-party scripts, embedded frames and known tracking pixels it recognises there, an estimate of who adds each one and whether consent governs it. Requests that scripts make later, other pages and the tags inside a GTM container are not checked. Each source that loads before consent comes with step-by-step instructions to gate it by hand.
* Google tag order check — warns when a tag is configured before the consent default (it would measure without consent) or when the same container is loaded twice.
* Services list — keep a list of the services your site uses with their consent category, and show it to visitors on your cookie policy page with the `[landexhub_consent_services]` shortcode or the "Services & cookies overview" block. A bundled catalog of 47 known services adds names, cookies and privacy policy links.
* Consent integrations — publishes the visitor's decision through the WP Consent API standard for plugins that read it, and sends Google Consent Mode v2 signals that Google tags added by other plugins follow. Tested with Site Kit by Google (its own Consent Mode on or off) and MonsterInsights.
* Proof-of-consent log in your own database (no IP address in the records), with filters, CSV/JSON export and 30-day statistics. Each record carries a fingerprint of the bar texts its page was shown with; when that fingerprint is not known, the texts current at the time are recorded and the record is marked so. You choose how long records are kept (3 months to 5 years, or until you delete them).
* WordPress privacy tools integration (export/erase) and automatic cleanup of old records.
* Three-step setup on first run, settings import/export, consent reset and a diagnostic report.
* Admin translated into Czech, Slovak, Polish and German.
* Multisite supported.

**Privacy**

The plugin reports nothing about your site or your visitors to anyone. The consent log is written to a table in your own site, without IP addresses. Scripts you configure yourself (GTM, gtag.js) are loaded from Google.

Plugin author: LandexHub — [terms](https://landexhub.com/terms), [privacy policy](https://landexhub.com/privacy). LandexHub also makes a separate, optional add-on; this plugin does not need it.

**Source code**

Nothing in this plugin is minified, compressed or obfuscated. The JavaScript in `assets/js/` is compiled from TypeScript, and those TypeScript sources ship with the plugin in the `src/` folder together with the compiler configuration and build instructions (`src/README.md`). One command with the TypeScript compiler reproduces the shipped files byte for byte.

== External services ==

This plugin does not call home and sends no telemetry. Depending on what **you** configure, it talks to the following services:

**Google Tag Manager / Google tag (gtag.js)** — optional, disabled by default. If you enter your own GTM container ID or gtag.js measurement ID in the plugin settings, the plugin embeds the standard Google snippet, and every visitor's browser then loads the tag from `www.googletagmanager.com` (requests `gtm.js` or `gtag/js`). This is required for the measurement you configured to work. The plugin prints the Google Consent Mode v2 `denied` default before these tags, unless a developer turns that default off with the `landexhub_consent_emit_consent_default` filter. No `<noscript>` fallback is printed, so visitors without JavaScript load nothing from Google. What Google receives with those requests (IP address, user agent, page URL, consent state signals) is described in Google's documentation: [Google Terms of Service](https://policies.google.com/terms), [Google Privacy Policy](https://policies.google.com/privacy). If you leave the setting empty, nothing is loaded from Google by this plugin.

**Your own site (loopback)** — the site check fetches your own homepage over HTTP (a loopback request to your own server) to scan the HTML for third-party scripts and the Google tag order. No external service is involved.

There are no other remote requests: no telemetry, no fonts, no CDNs. Links to the LandexHub website open in a new tab and send nothing by themselves.

== Installation ==

1. Upload the plugin to `/wp-content/plugins/landexhub-consent`, or install it from Plugins → Add New.
2. Activate the plugin.
3. The three-step setup (appearance → measurement → site check) opens on first run; everything then lives under four tabs — Appearance, Services, Diagnostics, Advanced.
4. Gate third-party scripts with `type="text/plain"` and `data-consent-category` — the Services page shows what was found and how to gate each source (guide: https://docs.landexhub.com/consent/blocking-scripts).

== Frequently Asked Questions ==

= Does it store visitors' IP addresses? =

Not in the consent log. It is privacy-minimal: it stores a random consent identifier, the chosen categories, a timestamp, the page path and a fingerprint of the bar texts. To limit abuse of the recording, a request counter is kept under a salted hash of the visitor's IP address; it expires after 10 minutes.

= How do I handle a privacy request from an anonymous visitor? =

A record of an anonymous visitor has no IP address, e-mail or other identifier besides its random consent ID, so the log alone cannot tie it to a person (GDPR art. 11). A visitor can identify their own records with their consent ID, shown at the bottom of the consent settings dialog. Search the Consent log for that ID to show or export the records; an erase button deletes them. Logged-in users are covered by the standard WordPress Export/Erase Personal Data tools.

= Does it send data to third parties? =

The plugin itself sends nothing: no telemetry, and consent records stay in your WordPress. If you configure a GTM container or a Google measurement ID, your visitors' browsers load those Google tags and they communicate with Google — see External services. Scripts you gate run only after consent and then talk to their own providers.

= Does it support Google Consent Mode v2? =

Yes, in advanced mode (the default) or basic mode. In advanced mode Google tags load before consent and send cookieless signals until the visitor agrees; in basic mode the gtag.js ID set in this plugin loads only after consent. The `denied` default is printed before the GTM container or gtag.js set in this plugin. If you set your own consent default elsewhere, turn the plugin's default off with the `landexhub_consent_emit_consent_default` filter; it is not detected automatically.

= Can I block Google Analytics / Meta Pixel until consent? =

Mark the script tag with `type="text/plain" data-consent-category="marketing"`; for an external script also move its address from `src` to `data-src`. Or gate an enqueued script with the `LandexHub_Consent::block_handle( $handle, 'marketing' )` PHP helper. Google tags loaded through GTM or gtag.js follow Google Consent Mode, so they are usually not blocked; make sure the tags in your GTM container use consent settings — Custom HTML tags and non-Google tags in a container do not follow Consent Mode by themselves. Guide: [Blocking scripts](https://docs.landexhub.com/consent/blocking-scripts).

= Can visitors change or withdraw their consent later? =

Yes, always. By default a "Cookie settings" link is printed in the site footer; you can switch to a floating icon instead, place the `[landexhub_consent_settings]` shortcode or the "Cookie settings link" block anywhere, or call `LandexHubConsent.openSettings()` from your own link. A change updates Google Consent Mode and the WP Consent API immediately. One technical honesty note: a third-party script that already ran on the current page cannot be un-run — after a withdrawal it stays inactive from the next page load on.

= What does the site check do? =

It fetches your own front page (a request to your own server) and lists the third-party scripts, embedded frames and known tracking pixels it recognises in the HTML, who adds each of them and whether consent governs it. For every source that loads before consent it shows how to gate it by hand. It only sees the front page — a source that loads only on a subpage does not show up there.

= Does it pass consent to other plugins? =

Through two channels. When the free WP Consent API plugin is installed, the plugin publishes the visitor's decision on that shared standard, both in the browser and for `wp_has_consent()` in PHP, so plugins that read it can follow it. Google tags added by other plugins follow the Google Consent Mode v2 signals the plugin sends, as long as they load after its default.

Tested end to end (first visit, accept, reload, withdrawal, consent reset): Site Kit by Google with its own Consent Mode on (it then reads the WP Consent API) and off (its Google tag follows Consent Mode), and MonsterInsights with a manually entered GA4 ID (its tag follows Consent Mode; it does not read the WP Consent API). Other plugins were not tested; check them on your site.

= Can I show visitors a list of the services this site uses? =

Yes — put the `[landexhub_consent_services]` shortcode (or the "Services & cookies overview" block) on your cookie policy page. It renders a table of the services found on your front page and the ones in your services list, with consent category, known cookies and privacy policy links. It is generated technical data, not legal advice.

= Does the plugin make my site GDPR compliant? =

No plugin can do that on its own. LandexHub Consent provides the technical tools — consent banner, script gating, proof of consent, data export and erasure. Whether your site complies depends on how it is configured and on what it does; that responsibility stays with the site owner.

= Does it support IAB TCF? =

No. The plugin does not implement the IAB Transparency & Consent Framework (no TC string, no registered CMP ID). If your site serves personalised ads through Google AdSense or Ad Manager to visitors in the EEA/UK, Google requires a TCF-certified CMP — this plugin is not the right tool for that setup. For sites without ad networks (analytics, marketing pixels, embeds), Consent Mode v2 and script gating cover the consent flow.

== Screenshots ==

1. The consent banner with Accept / Reject and a preferences link.
2. The preferences modal with per-category toggles.
3. Appearance editor with live preview of the bar.
4. Services: everything the front page loads, who adds it and whether consent governs it.
5. Step-by-step guide to gate a source by hand.
6. Diagnostics: a verdict built from real checks, each with its evidence.
7. Consent log with filters, export and wording snapshots.

== Changelog ==

= 0.9.2 =
* The consent log retention is now a setting: keep records for 3 months up to 5 years, or until you delete them (default 12 months).
* The "Designed by LandexHub" credit link in the banner is off by default and has its own setting.
* Services list: save any source from the site check to your list with a consent category; the list feeds the shortcode and block for your cookie policy page.
* Settings are validated against a strict schema on every save and import.
* The consent log records the user ID of logged-in visitors, so the WordPress privacy tools can export and erase their records.
* The site check reads the front page; the admin was simplified to four tabs.
* A category you turn off is no longer granted by a visitor's older consent cookie.
* The site check judges every occurrence of a source against what the consent bar can actually start later, reports partly gated services, and states that it reads the front page HTML only.
* The public services overview never starts a site check; it shows the result of the last check made in the administration.
* The consent log writes nothing while it is turned off, reports a failed write as an error, records the wording fingerprint of the page the visitor saw, and adds a new record when the user or the wording changes.
* `LandexHub_Consent::block_handle()` also holds back the inline code attached to the script and runs it in its original order after consent.
* Settings import checks the whole file and the resulting services list before anything is written. If a database write fails, the previous services list is restored, and the notice says when an import was only partly applied.
* Gated scripts start one after another and the consent is checked again before each one, so a withdrawal stops the scripts that have not started yet.
* The site check reports a gated source whose category is turned off: it does not load before consent, but it never runs either.
* Multisite: every site of a network has its own consent cookie, so a choice made on one site is not taken as consent on another; deleting a site also deletes its consent log table.
* WP Consent API: an "allow" that outlived the visitor's choice (expired cookie, consent reset, another site of the network) is no longer read as consent, neither by `wp_has_consent()` in PHP nor in the browser. Each consent reset and each site of a network uses its own WP Consent API cookies.
* No `<noscript>` GTM iframe any more: visitors without JavaScript, who cannot give consent, load nothing from Google.
* A Google ID set in the plugin shows up as its service in the admin and in the public services overview: G- as Google Analytics, AW- as Google Ads, DC- as Floodlight, GTM- as Google Tag Manager. UA- IDs are no longer accepted; a stored one is kept, not loaded, and the admin says so.
* The public services overview keeps the result of the last successful site check with its date, and says when the site has not been checked yet.
* Texts in the admin and in this readme describe what the plugin does more precisely (Consent Mode modes, site check scope, consent log, integrations tested with Site Kit and MonsterInsights).
* The services list limit (200 entries) is a fixed safeguard for the size of the stored setting; the filter that changed it was removed.
* An integration the plugin cannot pass consent to shows no switch; integrations not tested with this plugin are marked as such.
* The link that removes a service from the list only works for that service.

= 0.9.1 =
Initial release.

== Upgrade Notice ==

= 0.9.2 =
Configurable consent log retention, opt-in credit link and stricter settings validation.
