=== Snorklee Analytics ===
Contributors: moovswell
Tags: analytics, statistics, privacy, gdpr, no-cookies
Requires at least: 5.0
Tested up to: 7.0
Requires PHP: 7.4
Stable tag: 2.3.4
License: MIT
License URI: https://opensource.org/licenses/MIT

Privacy-first, EU-sovereign analytics. No cookies, no consent banner. Built-in self-host mode that bypasses ad-blockers — no DNS or Nginx config.

== Description ==

Snorklee Analytics is the official WordPress plugin for Snorklee — a privacy-first, 100% EU-sovereign web analytics service. The plugin auto-injects a single `<script>` tag on every public page of your site, with two modes :

= Standard mode (3rd-party) =

The classic snippet : `<script defer src="https://snorklee.com/w.js" data-site="yoursite.com"></script>`. Simple, lightweight, no server-side load. Drawback : Chrome 121+ in incognito mode and ad-blockers (uBlock Origin, Brave Shields, AdGuard) block requests to third-party "analytics" domains by default. Depending on your audience, **5 to 20 % of sessions silently disappear**.

= Self-host mode (1st-party — built-in proxy) =

**The killer feature.** With one toggle, the plugin :

1. Serves the tracker JS from your own domain via a WordPress route (`/js/flow.js`)
2. Proxies all events (`/api/event`, `/api/ping`, `/api/zone`, `/api/click`) through WordPress to snorklee.com
3. Caches the tracker JS for 1 hour using the WordPress Transients API (no extra DB load)
4. Forwards `X-Forwarded-For` so snorklee.com sees the visitor's real IP for geolocation

The visitor's browser sees **only 1st-party requests** to your own domain. uBlock, Brave, AdGuard and Chrome's Tracking Protection have nothing to filter. No DNS configuration. No Nginx tweak. No cPanel access required. **Works on every WordPress hosting**, including OVH/IONOS/Infomaniak shared plans.

= Privacy by construction =

* No cookie set on the visitor's browser
* No persistent identifier (visitor hash rotates daily, salt rotates monthly)
* No cross-site tracking (each site has an isolated identifier space)
* No advertising classification (no UTM parsing, no paid/organic split)
* Honors `Do Not Track`, `Sec-GPC`, and a localStorage opt-out flag
* Designed so that the ePrivacy consent requirement (article 5(3)) is **not triggered** : nothing is stored on or read from the visitor's device — documented in the Snorklee compliance dossier

= What the plugin does NOT do =

* No phone-home from PHP for visitor tracking in standard mode (everything happens client-side) — the only server-side call is the optional AI-crawler beacon described under "External services", disabled with one toggle
* No tracking pixel injected by the plugin itself
* No data collection beyond what the standard Snorklee JS tracker does
* No third-party scripts loaded
* No SQL table created (the plugin only stores 3 options : domain, dashboard URL, self-host toggle)
* No personal data stored on your WordPress server (in self-host mode, events transit through but are not logged)

= Why use the plugin instead of pasting the snippet manually =

* Survives theme updates (the snippet sits in plugin code, not in `header.php`)
* Settings page with built-in installation test (HTTP probe on home page)
* Self-host mode in one click (impossible without writing PHP yourself)
* Multisite-compatible
* Clean uninstall : no leftover options, no SQL tables, no transient
* MIT-licensed source code under 600 lines, fully auditable

== External services ==

This plugin connects to the Snorklee analytics service (https://snorklee.com), which is what it exists for. Three kinds of requests are involved :

1. **Visitor tracking (standard mode)** : the visitor's browser loads the tracker from `snorklee.com/w.js` and sends pageview events to `snorklee.com/api/event`. Data sent : page path (query string stripped), referrer, viewport size, user agent. The visitor's IP is used transiently for country/region geolocation and is never stored. No cookies, no persistent identifier.
2. **Visitor tracking (self-host mode)** : same data, but the requests transit through your own WordPress site, which forwards them server-side to `snorklee.com`.
3. **AI-crawler beacon (server-side, optional, on by default)** : when a known AI crawler (GPTBot, ClaudeBot, PerplexityBot…) requests a page, the plugin reports that crawl server-to-server to `snorklee.com/api/event` (crawler user agent + page path, query string stripped). This concerns bot traffic only, never your human visitors. One toggle on the settings page disables it.

Service provider : Snorklee (France). Terms : https://snorklee.com/docs/en/gdpr · Privacy : https://snorklee.com/en/privacy-compliance · Data collected : https://snorklee.com/docs/en/data-collected

== Installation ==

= Method 1 : upload the ZIP (recommended) =

1. Download `snorklee.zip` from your Snorklee dashboard (Integration tab → WordPress plugin card).
2. In wp-admin → Plugins → Add New → Upload Plugin, select the ZIP.
3. Activate the plugin.
4. Go to Settings → Snorklee.
5. Enter your **site domain** (e.g. `yoursite.com`) — the plugin suggests it automatically based on `home_url()`.
6. Save. Tracking is active immediately on every public page.

= Method 2 : optionally enable self-host mode =

After step 6 above :

7. On the same settings page, toggle **"Enable 1st-party proxy (recommended)"**.
8. Save.
9. Click "Test installation" to confirm everything works.

= Verifying =

Visit any public page of your site. Check the Snorklee dashboard's "Verify installation" card. Within a few seconds, your pageview should appear. The integration tab also shows whether self-host mode is detected (1st-party proxy).

== Frequently Asked Questions ==

= Where is my site domain ? =

It's the domain under which your site is publicly accessible (e.g. `mysite.com`, without `www.` and without `https://`). The plugin auto-suggests it based on `home_url()` — usually a single click to fill it in.

= Does the plugin track admin pages ? =

No. The snippet is only injected on public pages (frontend). The wp-admin area is excluded.

= Do I need a cookie banner ? =

**No.** Snorklee sets no cookie and stores nothing on the visitor's device, so the ePrivacy consent requirement (article 5(3)) is not triggered. Compliance is documented in the Snorklee compliance dossier (Compliance tab of the dashboard).

= How does self-host mode work ? =

The plugin registers WordPress rewrite rules that map `/js/flow.js` and `/api/{event,ping,zone,click}` to internal handlers. These handlers fetch the upstream resource from `snorklee.com`, cache the tracker JS for 1 hour (Transients API), forward events with the visitor's real IP via `X-Forwarded-For`, and return the response. From the visitor's browser perspective, all traffic is 1st-party — ad-blockers cannot tell it's analytics.

= Will self-host mode slow down my site ? =

Marginally. The tracker JS itself is cached for 1 hour, so most visitors hit a near-instant cache. Each event proxy adds a few milliseconds of server time (an outbound HTTP request to snorklee.com), but the visitor's browser uses `sendBeacon()` which is non-blocking — invisible to the user even if the proxy adds 50 ms of latency.

= What happens if snorklee.com is down ? =

In self-host mode, if the upstream is unreachable when serving the tracker JS, the plugin returns a no-op stub script (`/* tracker upstream unavailable */`) so your page does not break. Events sent during the outage are lost (no offline queue, by GDPR minimization doctrine). The plugin re-fetches the tracker on the next request once upstream recovers.

= Can I switch back to standard mode ? =

Yes, anytime. Turn off the self-host toggle and save. The plugin flushes the rewrite rules and reverts to injecting the third-party snippet immediately.

= Does the plugin work with caching plugins (WP Rocket, W3 Total Cache, etc.) ? =

Yes. The tracker is enqueued through the standard WordPress script API (`wp_enqueue_script`) and printed in the `<head>`, so caching plugins capture it like any other script. The proxied tracker JS uses standard `Cache-Control: public, max-age=3600`, which CDNs and WP cache plugins handle correctly.

= How do I uninstall completely ? =

Deactivate and delete the plugin from wp-admin → Plugins. The plugin removes its 3 options + tracker cache automatically (multisite-safe). No SQL table to drop. No leftovers.

= Where does the tracker source code live ? =

The plugin proxies the official snorklee tracker, served from `snorklee.com/w.js` (or your custom dashboard URL). Source code of this plugin : in your `wp-content/plugins/snorklee/` folder, under 600 lines of plain PHP, no obfuscation.

== Screenshots ==

1. Settings page : domain field + self-host toggle + status preview + installation test
2. Self-host mode active : visible status pill + 1st-party snippet preview
3. Installation test result : "Tracker correctly installed in self-host mode (1st-party detected)"

== Changelog ==

= 2.3.4 =
* Install test now correctly reports self-host mode : it detects the 1st-party `/js/flow.js` route (WordPress rewrites the enqueued path to an absolute same-origin URL, which the previous scheme-based check misread as 3rd-party). Tracking itself was unaffected — only the test verdict.

= 2.3.3 =
* Plugin URI and Author URI are now distinct (Plugin URI points to the plugin docs page, Author URI to the company site)

= 2.3.2 =
* No inline `<script>` left : the admin-menu "open in new tab" behavior moved to an enqueued `assets/admin-menu.js` (data passed via `wp_localize_script`)
* The settings-page snippet preview is now built without a literal script-tag string in the source (it was display-only text, never injected)
* Removed an outdated source-code URL from the readme

= 2.3.1 =
* Plugin Check (PCP) pass for the wordpress.org submission : zero error, zero warning
* The tracker `<script>` tag is now enqueued via `wp_enqueue_script` + `script_loader_tag` (same rendered tag, standard API)
* All `$_SERVER` reads go through `wp_unslash()` + `sanitize_text_field()` / `esc_url_raw()`
* `wp_parse_url()` replaces `parse_url()` in the AI-crawler beacon
* Multisite uninstall uses `get_sites()` instead of a direct SQL query
* Removed a vestigial `load_plugin_textdomain()` call (the plugin ships its own translation files)

= 2.3.0 =
* Top-level "Snorklee" menu in the admin sidebar, with a direct "Open dashboard" link to your Snorklee dashboard (new tab)
* The plugins-list action link now reads "Settings" (localized), following the WordPress convention
* Settings page unchanged, now reachable via the Snorklee menu

= 2.2.0 =
* UI now available in 6 languages : French, English, German, Italian, Spanish, Dutch (was FR + EN)
* Locale variants fall back to their language file (de_AT → de_DE, es_MX → es_ES, en_GB → en_US…)
* Documentation links now point to the localized docs (6 languages)
* Wording aligned with the current Snorklee compliance framing (ePrivacy article 5(3) not triggered)

= 2.1.1 =
* Fix : uninstall now also removes the `snorklee_crawler_beacon` and `snorklee_rewrite_version` options (clean uninstall)
* Fix : AI-crawler beacon strips the query string from the reported path (consistent with the JS tracker, never stores campaign parameters)

= 2.1.0 =
* Fix : self-host proxy now also maps `/api/click` (click-heatmap beacons) — was missing, so proxied sites silently lost click-heatmap data
* Rewrite rules now re-flush automatically on plugin update (no manual reactivation needed)

= 2.0.0 =
* Self-host mode (1st-party proxy) built into the plugin via WordPress REST + rewrite rules
* Installation test button (HTTP probe with i18n result messages)
* UI in 6 languages : French, English, German, Italian, Spanish, Dutch (extensible : drop a `languages/<locale>.php` file)
* Settings page redesign with the snorklee brand palette (dark mode)
* Fix : default dashboard URL was incorrect in v1.0.0 (`app.snorklee.com` → `snorklee.com`)
* Fix : Plugin URI / Author URI now point to the correct `.com` domain
* CNIL referential update : July 2025 (was 2020-091 in v1.0.0)
* Multisite uninstall improved (cleans up new option keys + transient cache)

= 1.0.0 =
* Initial release. Auto-inject snippet on `wp_head`, settings page, multisite-safe uninstall.

== Upgrade Notice ==

= 2.0.0 =
Major upgrade : self-host mode (anti ad-blocker, anti Chrome Tracking Protection) and several v1 bugs fixed (default URL was broken). Re-save your settings after upgrade — the option key changed from `wml_site_id` to `snorklee_site_domain` for clarity.
