Repository navigation
Releases: oliweb-ch/statamic-privacy-analytics
Release list
v4.11.0 — CP interface fully translated
What's new
All remaining hard-coded strings in the Control Panel are now routed through statamic-analytics::messages, making every label locale-aware.
Translated strings
- Dashboard widget (
analytics-overview.blade.php): title, "View all" link, "Last 7 days" label, "Today" / "Page Views" / "Unique Visitors" stat captions, and the widget load error message. - MaxMind credentials warning (
Dashboard.vue): the entire notice (title, body, sign-up link label, disable hint). - Heatmap day labels (
HeatmapWidget.vue):Mon…Sun→day_mon…day_sun. - Real-time badge (
RealTimeWidget.vue):LIVE→ locale key.
New keys added to both en and fr with identical key sets. French values match the previously hard-coded strings exactly — French control panels look the same.
Upgrade
No migrations, no config changes. Drop-in upgrade.
composer require oliweb/statamic-privacy-analytics:^4.11For maintainers forking this addon: run npm run build after pulling, then commit resources/dist/ before tagging.
Full changelog
See UPGRADE.md for details.
Thanks @trampish for the contribution.
v4.10.0
What's changed
Tracker JS — source unique via pipeline Vite
Le script de tracking injecté par {{ statamic_analytics:tracker }} est désormais compilé depuis une seule source (resources/js/tracker.js) via Vite/Rollup. Le heredoc PHP qui dupliquait la logique tracker dans ConsentBanner a été supprimé.
Pour les utilisateurs de l'addon (Composer) : rien à faire. Le bundle compilé (resources/dist/tracker.js) est livré dans le package. Comportement identique à v4.9.
Architecture :
resources/js/tracker.js ← source unique, spec Vitest
↓
resources/js/tracker-bundle.js ← point d'entrée IIFE, lit window.__anl
↓
Vite/Rollup build
↓
resources/dist/tracker.js ← bundle IIFE livré dans le package
↓
ConsentBanner::tracker() ← file_get_contents + injection window.__anl
↓
<script> inline dans le HTML
Tests
ConsentBannerTrackerTest— réécriture des tests de pattern-matching en tests comportementaux réels surtracker()ConsentBannerDisplayTest(nouveau) — premiers tests sur le rendu de la bannière de consentement : position (bottom/top/center), textes configurables, structure Alpinetracker-bundle.test.js(nouveau) — smoke test Vitest qui exécute le bundle IIFE compilé dans jsdom et vérifie le comportement end-to-end
Résultats : 146 tests PHPUnit · 46 tests Vitest
No breaking changes
Mise à jour compatible depuis v4.9. Voir UPGRADE.md pour le détail.
v4.9.0
Privacy fixes
Server-side consent enforcement (full static mode)
Previously, when tracking.consent.enabled was true, consent was enforced only by the JS tracker. A direct call to GET /statamic-analytics/track bypassed the check entirely.
The beacon endpoint now verifies session('analytics_consent') server-side. A beacon is silently dropped (HTTP 204) if the visitor has not consented.
When consent.enabled is active, the beacon participates in the site's existing Laravel session — no additional cookie is set.
Granular geolocation opt-out now enforced server-side
The geolocation toggle in the consent banner was stored in the session but never read by the server. IP geolocation was performed regardless of the visitor's choice.
From v4.9, declining geolocation skips GeolocationService::lookup() entirely — country_code, country_name, city are stored as NULL. The flag is serialised in the job payload so it survives the async queue path.
Analytics metric corrections
Bounce rate
The previous bounce rate was incorrect (ratio of new page URLs per session, not single-page sessions).
Corrected formula: sessions with exactly 1 page view / total sessions
Your dashboard will show different values from v4.9 onward — the new figures are analytically meaningful; the previous ones were not.
Entry pages
Entry pages now uses MIN(id) per session to identify the single first page view. Concurrent beacons in the same second are resolved deterministically by insertion order.
Your entry pages data will show different (more accurate) values from v4.9 onward.
Other fixes
- JS tracker date consistency — Inline tracker used
toISOString()(UTC date) withgetHours()(local time), causing incorrect new-day detection around midnight in non-UTC timezones. Both now use local time. analytics:healthcache check — Now performs a full read/write/delete cycle and fails if the read returns an unexpected value.
Upgrade
No migrations, no config changes required. See UPGRADE.md for details.
composer require oliweb/statamic-privacy-analytics:^4.9v4.8.0 — PostgreSQL support & configurable DB connection
Fixed
- PostgreSQL compatibility: boolean columns (
is_new_visitor,is_new_page_visit) were compared with= 1/= 0in raw SQL — MySQL/MariaDB/SQLite accept this, Postgres does not. Fixed with portableCASE WHEN column THEN 1 ELSE 0 ENDsyntax. - PostgreSQL: division by zero on
bounce_rateingetTopPages()when no row hasis_new_page_visit = true. Protected withNULLIF. - Heatmap: missing
pgsqlbranch was falling through to MySQL-onlyHOUR()/DAYOFWEEK()functions. Fixed withEXTRACT()for Postgres.
Added
- Configurable database connection via
statamic-analytics.database_connection(env:ANALYTICS_DB_CONNECTION). Defaults tonull(no behavior change for existing installs). Useful when analytics data must live on a specific connection regardless of the app default. - CI: added
test-postgresjob (PostgreSQL 16) mirroring the existing MySQL/MariaDB jobs.
v4.7.4
Tests
PHP (PHPUnit)
- TrackControllerTest — 13 tests HTTP du beacon
GET /statamic-analytics/track: validation des paramètres, filtrage IP, dérivationpage_urldepuis le header Referer, détection bot - HealthCheckTest — 13 tests de la commande
analytics:health: exit codes, queue, géolocalisation, compatibilité static cache - TrackPageVisitFilteringTest — test supplémentaire : le middleware se désactive bien en
strategy=fullsans déclencher de tracking
JS (Vitest)
resources/js/tracker.js— extraction lisible du script inline deConsentBanner::tracker(), mêmes clés de stockage (_anl_vid,_anl_sid…), toutes les fonctions exportéesresources/js/tracker.test.js— 42 tests : UUID, flags de visite, consentement, construction des params beacon,init()avec fake timers et mock fetchvitest.config.js— environnement jsdom, nettoyage automatique entre tests
CI
- Job
test-jsajouté dans le workflow GitHub Actions (Node 22,npm ci,vitest run)
Aucun changement de comportement en production — release exclusivement dédiée à la couverture de tests.
v4.7.3
Bug fix
Double-tracking en strategy=full (static caching)
En static caching full, PHP est appelé sur les cache miss (première requête par URL). Le middleware TrackPageVisit trackait la visite, puis le beacon JS la trackait également — résultat : chaque URL était comptée deux fois à sa première mise en cache.
Changements
TrackPageVisit: early return immédiat sistrategy=full— le middleware ne track plus rien, le beacon JS reste le seul chemin de trackinganalytics:health: le check static cache passe de ⚠ à ✓ pourstrategy=full(comportement désormais attendu et correct)- README : corrections des descriptions concernant le comportement sur cache miss
v4.7.2 — Fix check beacon endpoint dans analytics:health
Correctif
analytics:health signalait toujours ✗ Beacon endpoint à tort.
La route GET /statamic-analytics/track n'avait pas de nom explicite — Statamic lui assignait un nom auto-généré (generated::...). Route::has('statamic-analytics.track') retournait donc toujours false.
Correction : ajout de ->name('statamic-analytics.track') sur la route. Aucun impact fonctionnel — le tracker JS et le contrôleur utilisent l'URL en dur, pas le nom de route.
v4.7.1 — Fix fatal error Command::fail() / alert()
Correctif
PHP Fatal error au chargement de HealthCheck (introduit en v4.7.0)
Illuminate\Console\Command expose public function fail() et public function alert() dans Laravel récent. Les méthodes privées homonymes de HealthCheck réduisaient la visibilité, ce qui est interdit par PHP et provoquait un fatal error à l'autoload — faisant échouer tous les tests (exit code 2).
Renommage : pass/alert/fail → printOk/printWarn/printFail.
Aucun autre changement. La commande analytics:health fonctionne identiquement.
v4.7.0 — Commande analytics:health
Nouvelle commande : analytics:health
php artisan analytics:healthDiagnostic complet de l'addon en une passe. 11 vérifications, sortie colorée ✓/⚠/✗ avec suggestion d'action pour chaque problème détecté.
Checks
| Check | Ce qui est vérifié |
|---|---|
| Configuration | Config chargée |
| Database | Connexion + tables présentes + comptage événements |
| Cache | Cycle écriture / lecture / suppression |
| Encryption | APP_KEY définie (requis pour les jobs chiffrés) |
| Queue | Connexion configurée et présente dans queue.connections ; synchrone si null |
| Geolocation | Provider configuré ; disabled → avertissement |
| MaxMind | Credentials présents, fichier .mmdb existant et lisible, âge ≤ 30 jours |
| Storage permissions | Répertoires cache, logs et geoip accessibles en écriture |
| Scheduler | analytics-scheduler.log modifié dans les 25 dernières heures |
| Failed jobs | Nombre d'entrées TrackPageViewJob dans failed_jobs |
| Static cache | Stratégie full détectée → rappel d'ajouter {{ statamic_analytics:tracker }} |
| Beacon endpoint | Route statamic-analytics.track enregistrée |
Le code de retour est 1 si au moins un check échoue — utilisable dans un script de déploiement ou un pipeline CI.
Aucun breaking change
v4.7.0 est entièrement rétrocompatible.
v4.6.2 — Hardening beacon trust model + corrections docs privacy
Corrections suite à audit de sécurité/privacy
P2 — Endpoint beacon : dérivation de page_url depuis le header Referer
Le contrôleur de tracking ne fait plus confiance à la valeur page_url fournie par le query string du beacon. Il dérive désormais la page trackée depuis le header HTTP Referer du beacon, que le navigateur positionne automatiquement sur la page ayant déclenché la requête — et qui ne peut pas être falsifié par simple manipulation de l'URL.
Fallback sur la valeur client uniquement si le header Referer est absent (navigateurs avec Referrer-Policy stricte ou extensions bloquant ce header).
referrer_url (page précédente, lue via document.referrer côté JS) reste fournie par le client — le serveur ne peut pas la dériver autrement. Elle est sanitisée (scheme + host + path uniquement).
Documentation privacy
README — ajout d'une note explicite sur _anl_vid :
- identifiant
localStoragepersistant, sans expiry, spécifique au modestrategy=full - distinction avec le mode standard (session Laravel, non-persistant)
- recommandation de mention en politique de confidentialité si le contexte légal l'exige
docs/data-processing-overview.md + .fr.md — deux corrections :
- Referrer : « domaine uniquement » → « schéma + domaine + chemin » (alignement avec le code réel)
- Transfert de données : la formule « aucun transfert hors Suisse » est maintenant conditionnelle à la configuration MaxMind par défaut — la note précise qu'avec
ip-apiactivé, les IP sont transmises à un service externe
La section Cookies est renommée Cookies et stockage navigateur et documente le localStorage persistant en mode full static caching.