=== Let's Code Cookie Banner ===
Contributors: letscodeadm
Tags: cookie, gdpr, cookie banner, privacy, consent
Requires at least: 5.9
Tested up to: 7.0
Requires PHP: 7.4
Stable tag: 1.2.8
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

GDPR-compliant cookie banner following Italian Data Protection Authority (Garante) guidelines, with consent log, cookie registry and scanner.

== Description ==

**Let's Code Cookie Banner** is a lightweight, fully compliant cookie consent solution designed for WordPress sites that must comply with GDPR and the Italian Data Protection Authority (Garante della Privacy) guidelines.

= Key features =

* **Banner with three equal buttons** — Accept all / Reject all / Review preferences. All three buttons share the same visual style to comply with the Garante's visual neutrality principle.
* **Four cookie categories** — Necessary, Statistics, Marketing, Preferences. Necessary cookies are always active.
* **Consent log** — Anonymous GDPR consent log stored in the WordPress database (no IP addresses or personal data stored). Exportable as CSV.
* **Cookie registry** — Manage the official list of cookies used by your site. Displayed in the preferences panel and via the `[lccb_cookie_policy]` shortcode.
* **Client-side cookie scanner** — Automatically detects cookies set by JavaScript on any page of your site.
* **Server-side cookie scanner (HttpOnly)** — The server makes an HTTP request to the target page and reads the `Set-Cookie` response headers, detecting HttpOnly cookies invisible to JavaScript.
* **Third-party domain scanner** — Detects third-party domains loaded by your site and scans them for cookies.
* **Global cookie database** — Automatically enrich unrecognised cookies detected on your site by looking them up in the Let's Code global cookie catalogue. The lookup is triggered manually by the administrator.
* **Fully customisable design** — Colours (banner, buttons, cookie policy table rows and headers), border radii, padding, font size and banner position are configurable with a live preview (requires the free [Let's Code Cookie Banner Pro](https://let-scode.it/cookie-banner-pro/) add-on).
* **Shortcodes** — `[lccb_manage_cookies]` adds a "Manage cookies" button that can be placed anywhere on your site (footer, widget, block); `[lccb_cookie_policy]` renders a full cookie policy table.
* **Floating "Manage Cookies" button** — Optional floating action button (FAB) always visible on the frontend, allowing visitors to reopen the preferences panel at any time. Can be enabled or disabled from the General tab.
* **YouTube embed blocking** — Automatically blocks YouTube iframes until the user grants marketing consent. Displays a CSS-only placeholder with a Play button; no request is sent to YouTube before consent.
* **Vimeo embed blocking** — Automatically blocks Vimeo iframes until the user grants marketing consent. Displays a CSS-only placeholder with a Play button; no request is sent to Vimeo before consent. Vimeo iframes that already carry `dnt=1` in their URL are not blocked, as they do not install tracking cookies.
* **Bilingual** — English and Italian frontend strings built in; fully translatable via `.po`/`.mo` files.

= External services =

This plugin communicates with the **Let's Code backoffice API** (`https://backoffice.let-scode.it`) for four purposes:

1. **Plugin registration** — On first activation, the plugin sends the site URL and the plugin slug to the API to obtain an anonymous API key. No personal data is transmitted.
2. **Cookie lookup** — When an administrator clicks "Update from global database", the names of unrecognised cookies are sent to the API, which returns metadata (category, vendor, description) from the Let's Code global cookie catalogue. No personal data is transmitted.
3. **Cookie contribution** — When an administrator clicks "Contribute" on a cookie, the site URL and the cookie's metadata are submitted to the global catalogue for manual review by the Let's Code team. This action is entirely opt-in and admin-initiated.
4. **Script contribution** — When an administrator clicks "Suggest to Community" in the Cookie Scanner tab, the normalized URLs of third-party scripts detected during the last client-side scan are submitted to the global catalogue via `/cookies/scripts/contribute`. Only the scheme, domain, and path are transmitted — query parameters, fragments, and all personal data are stripped before sending. This action is entirely opt-in and admin-initiated.

All four calls use `wp_remote_post()`, require administrator capability, and are triggered only by explicit admin actions — never automatically on page load.

On sites with the Pro licence active, this plugin also loads a **client-side autoblocking script** from the same backoffice service. This script is served from `https://backoffice.let-scode.it/autoblocking/{installation_id}` and is injected into every frontend page via `wp_enqueue_script()`. It intercepts third-party scripts and cookies before the visitor has given consent, and re-activates them once consent is granted. When a visitor loads a page, their browser fetches this script from the Let's Code backoffice server; this request transmits the visitor's IP address and browser user-agent to the backoffice server as part of the standard HTTP request. The installation ID embedded in the URL is an anonymous, randomly generated identifier (stored in `wp_options` during plugin registration) and contains no personal data. The script itself does not send any further data from the visitor's browser to the backoffice server.

* Service URL: `https://backoffice.let-scode.it`
* Privacy policy: `https://let-scode.it/privacy`

This plugin integrates the **Freemius SDK** to manage Pro licences, deliver plugin updates, and collect anonymous usage analytics (opt-in). Freemius communicates with its own servers when the plugin is activated and when licence-related actions are performed. Users who opt out during the activation screen transmit no data.

* Service URL: `https://api.freemius.com`
* Privacy policy: `https://freemius.com/privacy/`
* Terms of service: `https://freemius.com/terms/`

This plugin also includes a **server-side cookie scanner** that uses `wp_remote_get()` to fetch a URL entered by the administrator and reads the `Set-Cookie` response headers. This request targets a URL chosen by the admin (typically the site's own frontend) and is protected against SSRF: private IP ranges, loopback addresses and reserved IP blocks are blocked before the request is sent.

When a YouTube iframe is blocked by the plugin (because the visitor has not yet granted marketing consent), the plugin displays a placeholder image loaded from **YouTube's thumbnail service** (`img.youtube.com`). This request is made by the visitor's browser, is triggered only when a YouTube embed is present on the page, and transmits no personal data beyond the standard HTTP request (IP address, user-agent) handled by YouTube/Google.

* Service URL: `https://img.youtube.com`
* Privacy policy: `https://policies.google.com/privacy`
* Terms of service: `https://www.youtube.com/t/terms`

When a Vimeo iframe is blocked by the plugin (because the visitor has not yet granted marketing consent), the plugin displays a CSS-only placeholder — no request is sent to Vimeo before consent. When the visitor clicks Play (granting consent), the iframe is restored and the browser loads the video from **Vimeo** (`player.vimeo.com`). This request is handled by Vimeo and transmits the visitor's IP address and browser user-agent as part of the standard HTTP request. Vimeo iframes that already carry `dnt=1` in their URL are never blocked, as they do not install tracking cookies.

* Service URL: `https://player.vimeo.com`
* Privacy policy: `https://vimeo.com/privacy`
* Terms of service: `https://vimeo.com/terms`

== Installation ==

1. Upload the `lets-code-cookie-banner` folder to `/wp-content/plugins/`.
2. Activate the plugin through the **Plugins** menu in WordPress.
3. Go to **Let's Code Tools → Cookie Banner** to configure the plugin.
4. Enter your Cookie Policy URL in the **General** tab.
5. Add `[lccb_manage_cookies]` to your footer (or any widget/block) so visitors can reopen the preferences panel at any time.
6. Optionally add `[lccb_cookie_policy]` to your Cookie Policy page to display an auto-generated cookie table.

== Frequently Asked Questions ==

= Does the plugin store IP addresses? =

No. The consent log stores only the date, time, a random UUID and the consent choices (yes/no per category). No IP addresses or personal data are ever stored.

= Is the plugin compliant with the Italian Garante guidelines? =

The plugin is designed to follow the 2021/2022 guidelines of the Italian Data Protection Authority (Garante della Privacy), including the visual neutrality requirement (equal-weight buttons), the option to close the banner via an X (which applies default settings), and anonymous consent logging.

= What is the global cookie database? =

The Let's Code global cookie catalogue is a curated database of known cookies maintained by the Let's Code team. When you click "Update from global database" in the Cookie Management tab, the plugin sends the names of unrecognised cookies to the Let's Code API, which returns category, vendor and description metadata. No personal data is transmitted.

= Can I translate the plugin? =

Yes. The plugin is fully translatable. Generate the `.pot` file with:
`wp i18n make-pot . languages/lets-code-cookie-banner.pot`
Then create a `.po` file for your language and compile it to `.mo`.

= Where are consent records stored? =

In a custom `{prefix}_lccb_consents` table in your WordPress database. Records can be exported as CSV from the **Consent Log** tab. You can also delete records older than a configurable number of years directly from the admin panel.

== Screenshots ==

1. Cookie banner displayed to visitors — three equal buttons (Accept all, Reject all, Review preferences).
2. Cookie preferences panel — per-category toggles with expandable cookie list.
3. Admin panel — General settings tab.
4. Admin panel — Cookie Registry tab with cookies added from the global database.
5. Admin panel — Cookie Scanner tab with cookies detected on the site.

== Changelog ==

= 1.2.8 =
* Fix: "Check unknown cookies in registry" only reloaded the page (the only way the "Checked" status badge, rendered server-side, actually shows up) when every incomplete cookie was matched against the global database. On a partial match, cookies that were correctly recognized and saved server-side stayed shown as "Not checked" until a manual page refresh. The page now reloads whenever at least one cookie was updated, regardless of how many remain unmatched.

= 1.2.7 =
* Fix: in the Cookie Registry, the "Send unknown cookies for check" button only appeared when the global database lookup found zero matches at all. If even one cookie was recognized, the button stayed hidden — silently blocking the ability to contribute the remaining unidentified cookies. The button now appears whenever at least one cookie is still unidentified after the check, regardless of how many were successfully matched.

= 1.2.6 =
* Re-enable the "Account" submenu (`menu.account` in the Freemius bootstrap) so users can reach license/account management from the WP Admin sidebar instead of only via a transient redirect after activating a license.

= 1.2.5 =
* Fix: the Freemius SDK (`vendor/freemius/`) was never included in the WP.org distribution package because it's excluded from Git via `.gitignore`, and the deploy workflow didn't vendor it before packaging. This meant `lccb_fs()` silently returned `null` on every real installation — no opt-in screen, no Pro upsell, no usage analytics. The `deploy-to-wporg.yml` workflow now clones the Freemius SDK before deploying to SVN.

= 1.2.4 =
* Fix Freemius bootstrap: set `has_paid_plans` to `false`. This plugin's own Freemius product no longer sells any plan directly — Pro pricing is now exclusively on the separate Let's Code Cookie Banner Pro add-on. Prevents broken "Upgrade to Pro" links pointing to a plan that no longer exists on this product.

= 1.2.3 =
* Compliance (Guideline 5): removed the Design tab from the free plugin entirely. No "upgrade to Pro" placeholder is shown; design customisation is a Pro-only feature hosted separately and not included in this plugin. The CSS output continues to use hardcoded defaults; the `lccb_design_vars` filter remains as a standard WordPress extension point for add-ons.
* Compliance (Guideline 5): removed the design-specific branch from `sanitize_settings()`. A generic `lccb_sanitize_extra_settings` filter is fired after each settings save, allowing add-ons to handle their own fields.
* Distignore: excluded `vendor/freemius/patches/wp-pot+1.10.2.patch` from the WordPress.org distribution package.

= 1.2.2 =
* Compliance: removed design-field processing (colour pickers, spacing values) from the free plugin's settings sanitizer — all design saving is now exclusively handled by the Pro add-on via the `lccb_sanitize_design_settings` filter. The free plugin's CSS variable output now uses only hardcoded defaults; the Pro add-on injects custom values via the new `lccb_design_vars` filter.
* Compliance: "Powered by Let's Code" attribution is no longer displayed by default. Site administrators can opt in via a new checkbox in General Settings → "Powered by" Attribution.
* Distignore: excluded `vendor/freemius/.example.env` from the WordPress.org distribution package.

= 1.2.1 =
* Added Vimeo iframe blocking: Vimeo embeds are automatically blocked until the user grants marketing consent. A CSS-only placeholder with a Play button is shown; no request is sent to Vimeo before consent. Vimeo iframes already carrying `dnt=1` in their URL are left unblocked (they do not install tracking cookies).

= 1.2.0 =
* Architecture: moved Design Customisation, Full Website Scan, Full Third-Party Domains Scan (bulk), Add All to Cookie Registry, wp_script_attributes script blocking and CDN autoblocking to the separate `lets-code-cookie-banner-pro` add-on plugin. The free plugin now exposes WordPress action/filter hooks at each of these extension points so the Pro add-on can inject its features without modifying free code.
* Design tab: free users now see an upgrade notice; Pro users get the full colour/spacing/preview controls via the add-on.
* Scanner tab: free keeps Single Scan (client-side and server-side); Pro adds Full Website Scan table and Bulk Third-Party Domains Scan.
* Banner JS: removed activateBlockedScripts() — script re-activation on consent is now handled by the Pro add-on via the lccbConsentReady event.

= 1.1.3 =
* Compliance: all locally-executed features (Design Customisation, Full Website Scan, Full Third-Party Domains Scan, Add All to Cookie Registry, wp_script_attributes blocking) are now fully available to all users — no licence required (WP.org Guideline 5).
* Docs: documented the Pro CDN autoblocking script as an external service in the readme, including what data is transmitted and when (WP.org external-services requirement).

= 1.1.2 =
* Admin: Design tab is now a Pro-only feature with always-visible PRO badge; free users see an upgrade notice.
* Admin: Added "Powered by Let's Code" branded link at the bottom of the cookie banner.
* Admin: Added "Add all to Cookie Registry" Pro button in Cookie Scanner tab (hidden when list is empty).
* Admin: Shortcodes tab redesigned with titled cards for each shortcode; removed duplicate from General tab.
* Admin: Added GDPR retention recommendation notice in the Consent Log tab.
* Fix: Scan popup completion note now correctly references "Cookie Scanner" tab instead of old name.
* Fix: Added "please wait for completion" warning message inside the client-side scan overlay.

= 1.1.1 =
* Admin: Restructured admin panel into six dedicated tabs: General, Design, Cookie Scanner, Cookie Registry, Shortcodes, Consent Log.
* Admin: Added "Full Website Scan" Pro feature to scan all site pages in the Cookie Scanner tab.
* Fix: show_fab_button setting persisted correctly across saves.

= 1.1.0 =
* Added Freemius SDK integration for Pro licence management and anonymous usage analytics.
* Fixed i18n: ordered placeholders (%1$s/%2$s) in translatable strings.

= 1.0.0 =
* Initial release.
