=== Plugnect Analytics for WooCommerce ===
Contributors: pluginect
Tags: analytics, woocommerce, statistics, utm, gdpr
Requires at least: 6.2
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 4.31.9
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Audience and sales analytics hosted on your own server. No third-party service, no cookie by default.

== Description ==

An online shop asks three questions, always the same ones: where do people come from, what do they do once they are here, and how much does it bring in. The usual tools answer by sending your data to a third party, dropping cookies, and forcing a consent banner that drives part of your visitors away, thus measuring less accurately what they claim to measure.

This plugin answers those questions while keeping the measurement data inside your WordPress database. No measurement data is ever sent to a third-party service. No cookie is dropped with the default settings.

= What it measures =

* **Audience**: visits, visitors, page views, duration, bounce rate, devices, browsers, operating systems, languages, screen resolutions.
* **Acquisition**: sources, mediums, referring sites, search engines, with the logo of each channel.
* **Behaviour**: page views, entry and exit pages, typical paths, internal searches, drop-off points.
* **E-commerce**: revenue, orders, average order value, conversion rate, complete funnel, read directly from WooCommerce.
* **Products**: views, add-to-cart, add-to-cart rate, sales per product.
* **Geography**: countries, with conversion and revenue per country, on an interactive globe.
* **Real time**: traffic, active pages and the live activity feed over the last thirty minutes.
* **Campaigns**: creation of UTM links and short links.
* **Explanations and leads**: every figure explains itself, and cautious rules point at what deserves a look once there are enough visits.

= Separate Pro extension =

A paid module is distributed separately, from our own site. Its code is not part of this package. It adds screens that do not exist here at all: individual journeys unrolled page by page, first and last touch attribution, multi-year consolidated history, a reports screen and CSV exports, and campaign performance figures.

Nothing in this plugin is limited, capped or conditional. There is no licence key, no trial, no quota and no feature that waits for an upgrade. Where the separate module would add a screen, the interface simply says so in one line and links to [its presentation page](https://pluginect.com/pluginect-analytics/), with no pop-up, no blurred content and no nagging notice.

= What it does not do =

The pseudonymous identifier that links two pages of the same visit changes every night: a visitor who comes back the next day is someone else to the system. This is a deliberate limitation.

== Privacy ==

= No cookie =

Default setting: no cookie, no local storage, no session storage. The plugin writes nothing in the browser. The legal qualification nevertheless depends on your configuration, on your other plugins and on your jurisdiction; document the measurement in your privacy policy.

A single optional cookie exists: the attribution memory, disabled by default, which links a purchase to a campaign click that happened several days earlier. It only contains the origin of the visit, never an identifier. The Settings screen always states, plainly, whether a cookie is dropped or not.

= The IP address is never stored =

It is used for a fraction of a second to compute an identifier, then forgotten. It is **truncated before that computation**: only the network prefix is kept, three octets in IPv4, three groups in IPv6. The result is a daily pseudonymous identifier based on that prefix and the browser signature. It reduces precision compared with a full address, but must not be presented as guaranteed anonymisation in every context.

= The point to be aware of =

A visit that ends in an order is linked to that order; otherwise there would be neither revenue per campaign nor return on investment. For those visits (and only those) a link exists to a customer identifiable through the order. This is personal data under the GDPR, and it is what justifies the automatic purge of the detailed data.

We would rather write it down plainly than display "no personal data" without nuance.

= Two retention periods =

* **The detail**: visits, pages, events, is purged automatically. The default is 24 months, configurable from 1 to 120 months.
* **The consolidated history**: a daily summary without any individual visit, is kept for up to ten years by default, with a configurable duration.

Consolidation always happens **before** the purge: a day deleted without having been summarised would be lost twice.

= What remains your responsibility =

Describe the measurement, the data, the purposes and the retention periods in your privacy policy. Determine with your counsel the legal basis and the obligations that apply to your configuration and your jurisdiction.

== Security ==

= The collection endpoint =

It is public, and it has to be: an anonymous browser calls it. It is therefore treated as an attack surface.

* Bots are filtered by signature, around fifty families, configurable.
* A visit stops being recorded beyond five hundred pages: no real visit reaches that number.
* Throughput is capped per network and per hour, which prevents inflating the tables by changing the browser signature on every call.
* Received parameters are bounded in number and length before any processing.

= The administration =

* Every write action checks a capability **and** an anti-CSRF token.
* Maintenance tasks only run for an authorised account, never from an arbitrary AJAX call.
* Every parameterised query goes through `$wpdb->prepare()`. The only interpolated values are table names built by the plugin.
* Every output is escaped when displayed.

= CSV exports =

A spreadsheet executes any cell starting with `=`, `+`, `-` or `@`. Our exports contain values coming from the outside, a campaign name, a page path, a search term. A visitor arriving with `?utm_campaign==1+1` would have been enough to trigger a formula on the computer of whoever opens the export.

Those cells are neutralised with a leading apostrophe. Negative amounts, on the other hand, are recognised as numbers and left untouched: a safe but uncomputable export would have been useless.

= Uninstallation =

Deactivation touches no data. Deletion also keeps it by default. To request a complete wipe, first enable the corresponding option in **Pluginect Analytics → Settings → Data**, then delete the plugin.

== Installation ==

1. Install and activate WooCommerce.
2. Install and activate this plugin.
3. Open **Pluginect Analytics** in the admin menu and review **Settings**: retention periods, excluded roles, excluded paths.

Measurement starts immediately. The consolidated history, on the other hand, builds up night after night.

== Frequently Asked Questions ==

= Is a consent banner required? =

The default setting drops no cookie, but that characteristic alone does not allow a universal legal conclusion. Check your complete configuration and your jurisdiction, and mention the measurement in your privacy policy.

= Why do the figures differ from Google Analytics? =

The scopes differ: the tag is served from your own domain, excluded bots and roles are configurable, the identifier changes every day, and the definitions of sessions, bounces and conversions are not necessarily those of another tool.

= Is a visitor who comes back tomorrow recognised? =

No, and that is intentional. The fingerprint changes every night. "Returning" here means: came back during the same day.

= WordPress scheduled tasks are disabled on my server =

The purge and the consolidation will not run on their own. Consolidation catches up every time the administration is opened; the purge does not. Schedule a system task on `wp-cron.php`.

== For developers ==

Available filters:

* `cbaz_hits_per_hour`, cap on calls to the collection endpoint, per network and per hour. Default: 600.
* `cbaz_insights_min_sessions`, measured visits required over the period before any lead is shown. Default: 200.
* `cbaz_help_entries`: add or reword glossary entries. `cbaz_insights` and `cbaz_insights_facts`: add your own leads.
* `cbaz_hide_admin_notices`, return `true` to deliberately hide other plugins' notices on our screens (not recommended).

Tables created, all prefixed with `{$wpdb->prefix}cbaz_`: `sessions`, `views`, `events`, `campaigns`, `daily`, `daily_dim`.

== Screenshots ==

1. Overview, visits, revenue, orders and conversion rate for the period, compared with the previous one, and the leads worth checking.
2. Every figure carries a small “i” explaining, in two sentences, what it measures and how to read it.
3. Acquisition, where visitors come from, and which sources actually convert.
4. E-commerce, the funnel from visitor to order, and where conversions are lost.
5. Products, views, add-to-cart rate and sales for each product in the catalogue.
6. Campaigns, build the UTM link and the short link of each operation, and copy them in one click.
7. Geography, conversion and revenue per country, on an interactive globe.
8. Real time, traffic and active pages over the last thirty minutes.
9. Journeys, each visit unrolled page by page, from entry to exit.
10. Settings, tracking, privacy and retention, with no account to create.

== Upgrade Notice ==

= 4.31.9 =
Requires WordPress 6.2 or later. No database change.

= 4.31.8 =
Every feature in this package is fully functional, with no cap or condition. No database change.

= 4.31.7 =
Every feature is now fully functional, with no cap or condition of any kind. No database change.

== Changelog ==

= 4.31.9 =
* Every database query now passes its table and column names to `$wpdb->prepare()` through the `%i` identifier placeholder, and every value as an argument, instead of being built into the SQL string. This is why the plugin now needs WordPress 6.2, which introduced that placeholder.
* The table that maps a referring host name to a brand moved from the code into `data/referrers.json`. It is reference data, read locally; the plugin makes no network request of any kind.

= 4.31.8 =
* The journeys screen and the queries behind it are no longer part of this package. They moved to the separate module, where they belong: this plugin does not contain that code at all any more, rather than containing it in a reduced form.

= 4.31.7 =
* Every feature in this plugin is now fully functional, with no limit of any kind. Journeys are no longer capped, their filters, search, pagination and statistics are available, and the live activity feed is shown in full. The code that limited them has been removed, not merely disabled.
* The plugin no longer checks for a companion module before running anything. Where a separate add-on provides extra screens, the plugin simply checks whether that code is present, and links to it rather than showing a button that would do nothing.

= 4.31.6 =
* Security: campaign short links (`/go/slug`) now refuse any destination that is not a plain http or https address, and go through WordPress' own safe redirect with the campaign host explicitly allowed, instead of bypassing it.
* Security: the campaign form no longer hands the whole submitted request to the save function. Each field is named, read and cleaned one by one, so nothing travels that the form did not ask for.
* Hardening: every piece of markup produced by the interface (icons, badges, charts, bars, tooltips) is now filtered at the point where it is printed, with a narrow list of allowed tags and attributes, instead of being printed as is.
* Hardening: the display parameters read from the address (period, filters, tab, pagination) all go through a single checked entry point, cleaned according to the type expected, rather than being read in some thirty places.
* Hardening: database queries no longer sit under a blanket exemption covering a whole file. Each query carries its own, with the reason written next to it.
* Fix: the settings screen of the Pro add-on no longer writes a style block into the page; the rule moved to a stylesheet.

Older entries are listed on the plugin page at pluginect.com.
