=== Statable Analytics ===
Contributors: statable
Tags: analytics, privacy, cookieless, statistics, gdpr
Requires at least: 6.3
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 0.34.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Privacy-first, cookie-free web analytics by Statable. Add the tracking script in seconds and keep admins out of your stats.

== Description ==

Statable is privacy-first, cookie-free web analytics. This plugin adds the Statable tracking script to your site and excludes logged-in admins (and any roles you choose) from your stats.

A tour of Statable itself — the service this plugin connects to. What it shows is what the plugin draws inside wp-admin, on the Statistics tab.

https://www.youtube.com/watch?v=o6tKAjgK5xg

* Cookie-free, with no persistent identifier: the visitor hash is keyed with a server-side secret, unique to your website, and changes every day — and neither the IP address nor the user-agent is stored.
* Your statistics in wp-admin — visitors, pageviews, a daily chart, your top pages, sources, countries and devices, and your own custom events, goals, properties and funnels, drawn natively rather than framed from elsewhere.
* Lightweight (~2 KB compressed), loaded deferred — no impact on page speed.
* Excludes administrators from tracking out of the box.
* Optional public widget — a visitor map, a 3D globe, top countries or a live counter — placed with the Statable block or a shortcode.
* Set up in two steps, without leaving your WordPress dashboard.

Setup happens entirely in your dashboard: enter your email, type the code we send you, and the plugin creates your site and starts tracking. Nothing to copy between tabs. If you already have a Statable account, use the same email and this site is added to it.

= What it costs =

Statable is a paid service, and this plugin is the WordPress client for it. Every new account starts with a 30-day trial that needs no card; after it, plans run from $9 a month for 10,000 monthly pageviews across all your sites. Sites on a .edu, .github.io or .gitlab.io domain are free for as long as they display one of the public widgets. Current prices are always at https://statable.com/pricing — the plugin itself is free and GPL, and stays useful for as long as the account behind it is active.

== External services ==

This plugin connects to Statable, a third-party analytics service operated by Key Arg B.V. (Netherlands), to collect and display website analytics.

The plugin contacts Statable in five distinct ways.

**1. Visitor tracking (every front-end page view)**

* **What it does:** loads the Statable tracking script (`https://statable.com/js/...`) on your front end, which sends pageview and engagement events to `https://statable.com/api/event`.
* **What data is sent:** the visited URL, the referrer, and the visitor's IP address and user-agent. No cookies are set. The IP address and user-agent are used in transit only: they are combined with your website's id and the current date into a hash, keyed with a secret held on our servers, that identifies a visitor for that one day on that one website, and neither is written to the analytics database — what is stored is the hash plus what was derived from them, such as country, browser and device. Because the same person can still be recognised within a day, this is pseudonymous data rather than anonymous data — see the [Privacy Policy](https://statable.com/privacy).
* **When:** on each front-end page view by a visitor who is not excluded.

**2. Public widgets (only on pages where you placed one)**

* **What it does:** if you add the Statable block or the `[statable_widget]` shortcode to a page, that page loads the widget's script from `https://statable.com/js/...` and the widget then reads the figures it displays — countries, cities, the live visitor count — from `https://statable.com/api/widget/...`. Those are your site's own aggregated statistics, the same ones a public share link shows; no visitor is identified by them.
* **What data is sent:** the request for the script and for the aggregated figures carries the visitor's IP address and user-agent, as any web request does; nothing else. The widget script also contains the tracker, which sends the request described in (1) to `https://statable.com/api/event` from every page showing a widget — for every visitor, including excluded roles, and also when tracking is switched off in the plugin. The plugin gives that copy of the tracker no site identifier, so Statable does not record it as a pageview.
* **When:** only on pages where you placed a widget. To resolve a widget's script address, the plugin's admin screens ask `https://statable.com/api/v1/sites/{id}/snippet` once and cache the answer; a visitor's page never makes that request.

**3. Setting up your account (only when you connect, from wp-admin)**

* **What it does:** creates or signs you in to a Statable account and creates a site for this install, so you never have to copy identifiers between tabs.
* **What is sent, and when:**
    * When you submit your email: that address, to `https://statable.com/api/v1/auth/send-otp`, so a one-time code can be mailed to you.
    * When you submit the code: the address, the code, and your acceptance of the Terms, to `https://statable.com/api/v1/auth/verify-otp`. Statable records the acceptance with its date and the IP the request came from. In return the plugin receives an API key, which it stores in your site's database.
    * Immediately after: your site URL and timezone, to `https://statable.com/api/v1/sites`, to create the site.
* **Nothing in this group happens unless an administrator starts the connect flow.** You can disconnect at any time on the plugin's settings screen, which deletes the stored key from your database.

**4. Reading your statistics (only while an administrator is looking at them)**

* **What it does:** reads the numbers shown on the plugin's Statistics tab from `https://statable.com/api/v1/query`, `https://statable.com/api/v1/current-visitors`, `https://statable.com/api/v1/props`, `https://statable.com/api/v1/funnels`, `https://statable.com/api/v1/sites` and `https://statable.com/api/v1/subscription`, using the stored API key. The Settings tab additionally reads your site's tracking-script and hostname settings from `https://statable.com/api/v1/sites/{id}/settings/tracking` and `.../settings/hostnames`.
* **What is sent:** your site's Statable id, the chosen period, and the requested metrics. No visitor data and no content from your WordPress site leaves your server in these requests.
* **When:** when an administrator opens the plugin's screens. Answers are cached briefly, so a refresh does not repeat the request. Nothing is requested while a visitor loads your site.

**5. Changing three of your Statable settings (only when an administrator asks)**

* **What it does:** writes the setting you just changed back to Statable — the tracking script's feature list to `https://statable.com/api/v1/sites/{id}/settings/tracking`, your site's timezone to `https://statable.com/api/v1/sites/{id}`, and your hostname to `https://statable.com/api/v1/sites/{id}/settings/hostnames`.
* **What is sent:** only the value being changed, with the stored API key. No visitor data.
* **When:** never on its own — only when an administrator presses the corresponding button on the plugin's Settings tab. Every other Statable setting is a link out to your dashboard, not something this plugin writes.

Statable Terms of Use: https://statable.com/terms
Statable Privacy Policy: https://statable.com/privacy

== Installation ==

1. Install and activate the plugin.
2. Go to **Statable** in the admin menu.
3. Enter your email, accept the Terms, and click **Email me a code**.
4. Type the code. Your account and site are created automatically, and tracking starts for non-excluded visitors.

== Frequently Asked Questions ==

= Do I need a Statable account? =
Yes, and the plugin creates one for you during setup — enter your email and the code we send you.

= Where do I see my statistics? =
On the **Statistics** tab of the plugin, in wp-admin: your visitors, pageviews, visits, bounce rate and visit duration against the previous period, a daily chart, your top pages, sources, countries and devices, and — for whatever your site measures on top of that — your custom events, goals, properties and funnels. It is drawn by the plugin from Statable's API, not an embedded dashboard, so an ad blocker cannot blank it. The full dashboard, with everything this screen leaves out, is one click away on statable.com.

= Are admins tracked? =
No. Administrators are excluded by default; you can exclude additional roles in settings.

= Can visitors opt out? =
Yes. Place the `[statable_opt_out]` shortcode anywhere (e.g. your privacy page). It shows a toggle that excludes the visitor on that device. Statable also honors Do Not Track and Global Privacy Control automatically.

= Does it work with caching/optimization plugins? =
Yes. The tracker tag carries skip-markers for WP Rocket, SiteGround Optimizer, Autoptimize, Cloudflare Rocket Loader and WP Meteor so it isn't combined, minified, or re-deferred into breakage.

== Screenshots ==

1. Your statistics in wp-admin: visitors, pageviews, bounce rate and visit duration against the previous period, over a daily chart — with a filter applied, one of twenty-two fields you can narrow by.
2. Where the traffic goes and where it came from — top pages, sources, countries and devices, each with its own breakdown tabs.
3. What your site measures on top of that: custom events, goals with conversion rates, properties and funnels.
4. Setup in two steps — your email, then the code we mail you. The account and the site are created for you.
5. Settings: which roles stay out of your stats, what the tracking script measures, and the state of the connection.
6. The visitor map on a page of your own, placed with the Statable block. Hovering a country names it, with the visits and the share it accounts for.
7. The same block set to the 3D globe, turning beside the text and answering the same hover: one block, four widgets, and the layout is yours.

== Changelog ==

The full history — every release back to 0.1.0 — is in changelog.txt, which ships with the plugin. The directory truncates a changelog past 5,000 characters, so only the recent entries are repeated here.

= 0.34.0 =
* The two scripts the plugin still printed into the page are now enqueued files like everything else it loads. The opt-out toggle's behaviour moved to assets/js/opt-out.js, and a widget renders from an empty placeholder that an enqueued script turns into the bundle's own tag — in the same place, with the same options, so nothing about the page changes. The placeholder carries nothing but an id: the script address and the options reach the page through the enqueued script, never through the post content, so nothing that can be written into a post can name a script to load. Browsers can cache both files, and the plugin prints no script tag of its own anywhere.
* Both files also cope with two things a page can do to them: content rendered after the footer scripts have gone out (a cookie banner carrying the opt-out shortcode, say) still gets its script, and an optimizer that pulls the file into the head no longer runs it before there is a page to look at. They carry the same "leave me alone" markers for caching plugins that the tracker has carried all along.
* A shortcode or block can no longer hand the counter inside a widget bundle a different endpoint or a callback: `tracking-api` and `before-send` join the attributes a widget refuses to pass through.
* The External services section of this readme now covers the public widgets — what a page with one loads from Statable and what that request carries — which it had left out. The Widgets tab and the readme now also say precisely what the counter inside a widget bundle does: it sends the request, and Statable records nothing from it.

= 0.33.0 =
* The listing now opens with a tour of Statable, the service behind the plugin, said plainly as that — the directory turns a video URL in the readme into a player, and a still screenshot cannot show a globe turning or a map answering a hover.
* Housekeeping for the WordPress.org directory: the readme now says plainly what Statable costs and what the free tier asks in return, before anyone installs rather than after. It also lists the seven screens the directory shows — including both widgets the block can draw — and records that the plugin was tested against WordPress 7.1.
* The plugin no longer calls load_plugin_textdomain(). It ships no translations of its own — the ones it has arrive from translate.wordpress.org, which WordPress has loaded unaided since 4.6 — so the call and the Domain Path header that went with it named a directory that was never there.
* Four places where a posted value is checked further down now say so where the check would be looked for. No behaviour changes: the site id was already cast, the feature list already went through sanitize_key(), and the two JSON payloads were already parsed field by field.

= 0.32.0 =
* The site's address on Statable now comes back with the site itself, so the plugin no longer asks for a widget snippet just to read it out of the URL. That detour, its retry timer and the code behind it are gone; installs connected before the change pick the value up on their next look.
* "Tracking is switched off" now admits the exception it had. A widget bundle contains the counter, so a page showing a Statable widget keeps being counted with the switch off — when that is the case, the screen says so instead of promising silence. The Widgets tab says it too, before you add one.

= 0.31.0 =
* The privacy wording now says the visitor hash is keyed with a server-side secret, which it is on both ingest nodes. Without that key the hash would be guessable from an IP and a user-agent, so it is the part of the sentence that carries the weight — and it is only claimed now that it has been checked rather than assumed.

= 0.30.0 =
* Two sentences that were simply untrue are gone. An expiring key was said to stop tracking: it does not — the tag on your pages carries no key, so visits keep being counted and only the statistics screens here go dark. And disconnecting was said to remove the script on the next page load, which is not true behind a page cache: cached pages keep serving the old markup until the cache is purged, and the screen now says so.
* "You are excluded from tracking" is no longer stated to people who are not. The exclusion is by role and the list is editable, so the screen that explains why no data has arrived now checks first.
* The trial countdown matches statable.com. The plugin rounded up and the dashboard rounds down, so the same trial read 61 days here and 60 there for its whole length.
* What the plugin says about visitor data is exact now: the IP address and the user-agent are combined with your website's id and the current date into a hash, and neither of them is written to the analytics database — only the hash and what was derived from them.
* On the "no pageviews yet" screen, the explanation of the test sits under the buttons it describes rather than beside them.

= 0.29.0 =
* The plugin now notices when something else on your site is already counting into Statable. Two trackers on one page is not twice the data: only the first one the browser reaches runs, the other quietly counts nothing, and a site that was tracked by hand before this plugin arrived can split its history in two without a single visible symptom.
* It looks at your home page the way a visitor gets it, so it finds the tag wherever it was put — a theme template, a header-scripts field, a snippet plugin, a template part. A tag inserted by a tag manager is not in the page yet at that point, so a clean result means "nothing found", not "nothing there".
* When the other tracker belongs to a site in your account, the notice names it and offers to count into that one instead — which is the fix when the older tag is the one with the history behind it. Otherwise remove the older tag and keep this one. Widgets are never mistaken for trackers.
