Download Tracker
Version 1.4.0
Added
Craft 4 support. The plugin now installs on Craft 4.0 and later as well as Craft 5, from the same release - there's no separate Craft 4 branch or version line to choose between. Nothing in the plugin needed changing to get there: every Craft API it uses exists in Craft 4 with an identical signature, and so does every control-panel Twig macro, block and filter its templates rely on, checked against 4.0.6 and 4.18.5 rather than assumed. PHP 8.2 stays the floor; the code parses under 8.0, but 8.0 and 8.1 are both end-of-life.
This matters most for the Link Vault import. Link Vault's schema is identical on its Craft 4 and Craft 5 releases, so the importer works as-is, and the people who most need it are the ones still on Craft 4: they can import and drop Link Vault first, rather than carrying it through a Craft 4 to 5 upgrade.
Version 1.3.0
Added
- A legend under the day-by-day chart, naming which colour is people and which is crawlers. The totals above the chart had always split the two, but nothing connected those words to the stacked bars, leaving the split to be guessed at. It only appears while crawlers are counted separately, since there's no split to explain otherwise.
Improved
- Every day in the chart now answers a hover with its date and numbers, not just the days tall enough to aim at. Each day gets a full-height, invisible hit area over its bar, and highlights while hovered, so a day reads the same whether it saw 400 downloads, one, or none - previously only days with a download carried a tooltip at all, and on a 365-day range a quiet day's bar was too small to reliably hit.
Version 1.2.1
Fixed
- Managed download links now work on Craft 5.9 and later. The signed link
from
craft.downloadTracker.url()carried its token in atokenquery parameter, which collides with Craft's own reserved route-token parameter. Craft 5.9 started rejecting any request whosetokendoesn't resolve to a real Craft token with a400 Bad Request, thrown before the plugin's controller ran - so every managed download returned a 400. The token now travels in adltparameter instead, sidestepping Craft entirely. The zero-touch click beacon was never affected; only managedurl()links were. If you embed managed links in statically-cached pages, clear those caches after updating so the new links are baked in.
Version 1.2.0
Added
- Import from Link Vault, for sites moving off Masuga's Link Vault plugin.
It folds Link Vault's row-per-download log into this plugin's per-file
counters, keeping the running totals, the day-by-day history and each file's
last-downloaded date. Run it from Download Tracker → Import (admins only,
and only shown while Link Vault is still installed) or with
php craft download-tracker/import/link-vault. - The Import page shows what will happen before you commit to anything: how many
downloads it will import, how many files they belong to, and what it will skip.
--dryRundoes the same on the command line. - The import runs as a queue job, so progress shows in the control panel's queue indicator and a large history won't time out. It's safe to re-run: it resumes from where it stopped rather than counting anything twice, so you can import, leave Link Vault running a while longer, and top up before uninstalling.
Fixed
- The per-file detail page's date range collapsed to a single day, so the chart,
the day-by-day table and the "in this range" totals only ever showed one day's
downloads - the day the range started - instead of the whole window. The same
bug made
craft.downloadTracker.daily()return one day rather than the range asked for. Both affect 1.1.0, and are most visible on a file with a long history, since a site with only recent downloads showed an empty range instead.
Notes
- Run the import before uninstalling Link Vault - it reads Link Vault's tables directly, and can't recover the history once they're gone.
- A count per file can't hold everything a row per download could. Imports keep the totals and daily history, and drop the per-user, per-IP and custom-field detail. Blocked leech attempts and downloads deleted in Link Vault are skipped. Link Vault never recorded a user agent, so imported downloads can't be split into people and crawlers, and all count as people.
Version 1.1.0
Added
- Crawler downloads setting, controlling what happens when a search engine,
AI crawler, link unfurler or monitoring tool downloads a file:
- Count separately (new default) - counted toward the total, and toward a crawler total of their own, so you can read your human numbers off the split.
- Don't count - the file is served, nothing is recorded.
- Block with a 403 - the served-download route refuses outright and streams nothing. Only the served route can block; the beacon has no file to withhold.
- Crawler detection covers the well-known search, AI, social and monitoring crawlers by name, with a catch-all for the long tail, and an Extra crawler user agents setting for anything else you see in your own logs.
- Per-file detail page: click a file in the Downloads list for its totals, a day-by-day chart and table over the last 30, 90 or 365 days, and a CSV export of that history.
- Twig API:
craft.downloadTracker.userTotal(),.crawlerTotal(), and.daily(file, days)for a file's day-by-day history. beforeTrackDownloadevents now carryisCrawler, so a handler can decide about crawler hits itself without touching the setting.
Changed
countis now the total: people and crawlers together. Under the new default, crawler downloads are included in it, and the human figure iscount - crawlerCount. Every existing list, report, widget and Twig call keeps working unchanged, but a site that previously discarded bot hits will see its totals rise once crawlers start counting. Installs that hadignorePrefetchAndBotson keep their existing behaviour automatically - see below - so this only affects new installs and anyone who opts in.- The served-download route now recognises prefetch and crawler requests, which it previously ignored entirely: it counted every request carrying a valid token, whatever sent it. A browser prefetch of a download link is therefore no longer counted twice (once when speculatively fetched, once when clicked).
- Browser prefetch and prerender requests are always served and never counted, whatever Crawler downloads is set to. A prefetch is a real browser getting ready for a real click - neither a crawler to turn away nor a download to count.
lastDownloadedtracks whencountlast moved, so a counted crawler hit updates it too.- The Downloads CSV export gained User downloads and Crawler downloads columns. They're appended after the existing ones, so anything reading that file by column position is unaffected.
Deprecated
ignorePrefetchAndBots, replaced bycrawlerMode. It's still honoured, from both project config andconfig/download-tracker.php:truemaps tocrawlerMode: 'ignore'andfalseto'separate', so upgrading doesn't change how an existing site counts. An explicitcrawlerModealways wins. Support for the old setting will be removed in 2.0.
Version 1.0.4
Fixed
- The served-download route now returns a plain 404 when a request arrives with
a missing or invalid token, instead of throwing a
BadRequestHttpException. The route is anonymous and its URL is easy to guess, so bots and stale links probe it constantly with no token at all. Each of those probes was raising an application exception, which filled up error trackers with noise that wasn't a fault in the site. Nothing about the token check has changed, and no file was ever served without a valid token. The rejection reason is still written to the Craft logs, so a genuinely broken signed link can still be traced.
Version 1.0.3
Changed
- Now we're in the plugin store, I thought it was time for a decent logo. Redrew the plugin icon as a proper coloured badge (a download arrow over rising count bars) so it stands on its own next to the other listings, and updated the control-panel mask icon to match.
Version 1.0.2
Fixed
- Settings page is now viewable (read-only) for admins in environments where
allowAdminChangesis off, instead of returning a 403. Saving is still blocked in those environments. - The Tracked extensions, Tracked path prefixes, and Excluded hosts textareas no longer render their values run together on a single line - each value now appears on its own line again.
Changed
- Redrew the control-panel icon with a more balanced arrow-and-tray glyph so it no longer looks squished in the sidebar.
Version 1.0.1
Changed
- Adopted the standard Coysh Digital commercial license.
- Refreshed the plugin icon and updated the README.
Version 1.0.0
Added
- Initial release.
- Atomic per-file download counter (one row per file) plus a bounded per-day rollup - no element-per-download or row-per-download bloat.
- Zero-touch JS beacon that auto-detects download links and counts them
without any template changes. Works with full-page static caches (e.g. Blitz)
because counting happens on a non-cached
/actions/...request. - Optional served-download route (
craft.downloadTracker.url(asset)) that streams or redirects to the file after counting - for gated, private, remote, or forced ("Save as…") downloads, with a signed, tamper-proof link. - Twig API:
craft.downloadTracker.url(),.total(),.record(),.top(). - Control panel: a sortable/searchable Downloads list with CSV export, saved reports, and a Top Downloads dashboard widget.
- Automatic garbage collection of the daily rollup beyond a configurable retention window.