Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Statamic Activity Laravel Package

goldnead/statamic-activity

Goldnead/Statamic Activity adds an activity log to Statamic, letting you record and review user actions and content changes across your site. A lightweight way to track what happens in the control panel and keep an audit trail of edits, publishes, and other events.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Upgrade with composer update — v1.6.0 ships no migration, so there is nothing to publish or run. But this release changes runtime behaviour on upgrade, so the update is not "done" until you have checked retention.
  • First place to look is config/activity.php, specifically retention.days. On an installation that has never published the config, this value now defaults to 365 instead of "keep everything".
  • Before deploying, run the command in dry-run mode against each environment (local, staging, production) and read the output:
php artisan activity:prune --dry-run
  • Second place to look is the Control Panel: Settings → (your addon) → Einstellungen. The per-brand "Retention days" field has been removed; the page now only tells you that the value lives in config/activity.php. Expect support questions about the missing field.
  • First use case: an existing install that has been accumulating activity rows for years. Decide explicitly between "prune at 365 days" (do nothing, accept the new default), "keep everything" ('days' => null), or "prune at N days" (ACTIVITY_RETENTION_DAYS=N / 'days' => N) — then verify with a dry run before the scheduler fires.
// config/activity.php (published)
'retention' => [
    'days' => env('ACTIVITY_RETENTION_DAYS', 365), // null = keep forever
    // 'anonymize_after_days' still exists and is still per brand — see Gotchas
],

Implementation Patterns

Pin the policy in config, not in env alone. The env var is convenient per environment, but the default belongs in the published config file so the behaviour is visible in code review. env('ACTIVITY_RETENTION_DAYS', 365) in the repo, ACTIVITY_RETENTION_DAYS= per environment.

Fresh installs. If you want the historical "never delete" behaviour, set 'days' => null in your published config before the scheduler first runs activity:prune. Doing it after the first run means the data is already gone.

Upgrading an install with a previously published config. Your file still reads env('ACTIVITY_RETENTION_DAYS') with no default, so it keeps everything — you are not affected by the new 365-day default, but you are also silently out of step with the documented default. Either edit the line to add the default, or re-publish and diff. Pick one deliberately and note it in the upgrade runbook.

Installation-wide, not per brand. Retention is one number for the whole store. activity:prune already deleted across every brand using a single value; the old per-brand field on the Settings page was never read. If you had built internal docs, dashboards, or support macros around "brand X retains 30 days, brand Y retains 400", those are now wrong — collapse them to one global value.

Ad-hoc pruning. Use --days=N on the command for one-off reductions (e.g. trimming a noisy import) rather than temporarily changing config and forgetting to revert it. Any window below 1 is now refused, so there is no accidental --days=0 wipe.

Scheduling and alerting. Schedule the command and make failures visible. Because an invalid window now makes the command exit with an error and delete nothing, a bad env var (empty string, 0, "365 ", a typo) turns pruning quietly into "never runs". Wire the exit code into your monitoring rather than assuming the cron line is enough.

// routes/console.php or the scheduler
$schedule->command('activity:prune')
    ->daily()
    ->onFailure(fn () => /* notify: retention window invalid or prune failed */);

Deployment order. Set ACTIVITY_RETENTION_DAYS (or the config default) before the new code goes live, so the first scheduled run under v1.6.0 uses the window you chose rather than the package default.

Treat the config key as the extension point. If you need custom policies (per-tenant retention, legal holds), implement them in your own command or job that reads the same retention.days value — do not reintroduce per-brand storage and expect the package to honour it.

Gotchas and Tips

  • The default flip is the upgrade risk, and it is silent. The first activity:prune after updating will delete everything older than 365 days on installs with no config file and no env var. There is no migration, no notice, no log line you will see unless you look. Run --dry-run first, every time, on every environment.
  • Two installs, two behaviours. "Published config with env() and no default" keeps data forever; "no config / re-published config" prunes at 365. Two sites on the same package version will behave differently. When someone reports "we lost history" or "pruning never runs", check whether config/activity.php exists and what the days line says before touching anything else.
  • 0 and negatives are refused, not clamped. The global window, --days, and any per-event-type window below 1 or non-numeric cause the command to error out and delete nothing. That is the right safety behaviour, but it means a dynamically computed window (e.g. --days=${RETENTION}) can halt pruning entirely instead of pruning conservatively. Validate the value in your deploy pipeline.
  • Empty env vars are the classic trap. ACTIVITY_RETENTION_DAYS= in a .env is not null and not 365 — it is an empty string that must not be treated as a valid window. Prefer leaving the variable out of .env entirely when you want the default.
  • 'days' => null means "keep forever", but only if it survives. Casting it through (int) anywhere in your own code turns it into 0, which the command now refuses. Keep it null end-to-end.
  • Anonymisation is still inconsistent, and this release did not fix it. retention.anonymize_after_days is still stored per brand, while activity:anonymize runs across the whole store. Do not present the anonymise setting to users as per-brand scoping — it is not. Track this as a known gap rather than building on it.
  • Stale brand_settings rows are harmless but real. Values previously stored for retention.days per brand are no longer read. There is no migration; delete them by hand if you want a clean table, but do not write code that depends on their absence.
  • Debugging checklist when nothing is being pruned: (1) does config/activity.php exist and is it cached (php artisan config:clear)? (2) is retention.days present, and is it null? (3) does ACTIVITY_RETENTION_DAYS resolve to a number ≥ 1? (4) does activity:prune --dry-run exit non-zero? (5) is the scheduled command actually registered and running (check schedule:list)? (6) are per-event-type windows overriding the global one?
  • Debugging checklist when too much was pruned: confirm the config file is the published one and not a stale cached copy; a php artisan config:cache from before the upgrade will keep serving the old value.
  • Verify visually and numerically. Since there is no migration, the fastest smoke test after upgrading is: run the dry run, then load Settings and confirm the per-brand retention field is gone and the page points at config/activity.php. Clear caches with php artisan optimize:clear if the old field still renders.
  • Tests: assert on the command's exit code and on the dry-run output, not on log strings or translated copy. Cover the null case and the < 1 rejection case explicitly — those are the two behaviours most likely to regress.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
takashato/laravel-vietqr
aitor24/laralang
kraenzle-ritter/entity-relations
dindigital/din-image
pomirleanu/image-colors
sawmainek/laravel-api-generator
delatbabel/jsonresponses
mouhamedfd/permissions-generator
eduplus/qr
laravel2580/idehelpers
delatbabel/viewpages
anavel/gettext
muhamadrezaar/highcharts
delatbabel/keylists
frankkessler/salesforce-laravel-oauth2-rest
anavel/foundation
wdmtech/enviricons
eii/laravel-captcha
muka/shape-reader
tdt/json