Skip to content

Add opt-in email digest for newly approved plugins - #480

Merged
simonhamp merged 2 commits into
NativePHP:mainfrom
lessevv:plugin-approval-email-digest
Nov 2, 2026
Merged

simonhamp merged 2 commits into
NativePHP:mainfrom
lessevv:plugin-approval-email-digest

Conversation

@lessevv

@lessevv lessevv commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

What

Adds an admin-triggered way to email subscribers about newly approved plugins as a single combined digest, instead of one email per plugin.

  • New new_plugin_notified_at column on plugins tracks which approved plugins haven't been included in an email digest yet (Plugin::pendingNewPluginNotification() scope).
  • New NewPluginsAvailable notification (mail + database) that covers one or many plugins in a single message.
  • New SendPendingPluginEmailDigest job: batches everything pending into one email per subscriber, excluding each recipient's own plugins from their own digest.
  • New "Email Subscribers (N)" header action on the admin Plugins list, visible only when there's something pending, that dispatches the job.
  • plugins:resend-new-plugin-notifications now sends the same batched notification, so resending for named plugins is one combined email instead of one per plugin — signature, validation, and --dry-run behaviour are unchanged.

Why

#476 moved automatic new-plugin announcements to in-app only, so nobody gets emailed on every single approval anymore — which fixed the spam problem, but also removed any way to email people about new plugins at all. That PR's own description flagged this as a follow-up:

If we want to keep a manual "email people about a batch of new plugins" escape hatch, it needs its own notification class; it can't share this one any more.

This PR is that escape hatch: an admin can review what's piled up (the action label shows the count) and send one email covering everything at once, rather than either spamming per-approval or having no email option at all.

What this does not change

  • Plugin::approve() — untouched. It still dispatches SendNewPluginNotifications for the automatic in-app notification, same as after Stop emailing everyone on every plugin approval #476.
  • NewPluginAvailable / SendNewPluginNotifications — untouched. They remain the automatic, in-app-only, per-approval path.
  • PluginApproved (the author's own "your plugin was approved" email) — untouched.

Tests

  • tests/Feature/Jobs/SendPendingPluginEmailDigestTest.php — digest content, opt-out/unverified exclusion, own-plugin exclusion per recipient, marks plugins notified, idempotent on already-notified plugins, no-op when nothing pending.
  • tests/Feature/Notifications/NewPluginsAvailableTest.php — via(), mail subject/content for one vs many plugins, database payload, unsubscribe link.
  • tests/Feature/Filament/SendPendingNewPluginNotificationsActionTest.php — action visibility and job dispatch.
  • tests/Feature/ResendNewPluginNotificationsTest.php — updated for the new notification class; added coverage for the combined-digest behaviour and for marking plugins as notified.

🤖 Generated with Claude Code

lessevv and others added 2 commits August 21, 2026 17:37
Plugin approvals now announce in-app only (NativePHP#476), so subscribers no
longer get flooded with mail on every single approval. This adds back
an explicit, admin-triggered way to email people about new plugins
without returning to one-email-per-approval.

A `new_plugin_notified_at` column on plugins tracks which approved
plugins haven't been included in an email digest yet. A new
`NewPluginsAvailable` notification (mail + database) covers one or
many plugins in a single message. `SendPendingPluginEmailDigest`
batches everything pending into one email per subscriber, excluding
each recipient's own plugins from their own digest.

An "Email Subscribers (N)" action on the admin Plugins list dispatches
the job manually. `plugins:resend-new-plugin-notifications` is
switched to the same batched notification so resending for named
plugins also sends one combined email instead of one per plugin,
keeping its existing signature, validation, and dry-run behaviour.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@simonhamp

Copy link
Copy Markdown
Member

thanks @lessevv!

@simonhamp
simonhamp merged commit e0afd2f into NativePHP:main Nov 2, 2026
1 check passed
simonhamp added a commit that referenced this pull request Nov 3, 2026
A blank line in Plugin.php picked up two spaces in #480, which has failed
the lint check on main and every PR since.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
simonhamp added a commit that referenced this pull request Nov 3, 2026
* Mint a fresh GitHub App token for each Satis build

Installation tokens last an hour and we cache them for 55 minutes, so a
build could be sent a token with only a few minutes left. Satis keeps the
token in its queue until a worker clones with it, so a short backlog is
enough for the clone to fail with "Invalid username or token".

Builds, and release syncs that are going to trigger one, now mint a new
installation token. Everything else keeps using the cache.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Remove stray whitespace that fails Pint on main

A blank line in Plugin.php picked up two spaces in #480, which has failed
the lint check on main and every PR since.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants