feat(mass-mail): make announcements opt-in, and stoppable at any time - #261
Merged
Conversation
Mass mail reached every active, verified member, and there was no opt-out anywhere for it to honour. It now reaches only the members who asked for it, and every message carries a way out. - users.mass_mail_opt_in_at records when consent was given. The audience count, every batch a campaign claims and every message it sends all require it, so a member who stops the announcements drops out of a campaign that is already half sent. - Members ask on the registration form or on their notification preferences — both unticked — and can withdraw from either. - Every message carries a signed unsubscribe link on a new mass-mail token scope, so stopping the announcements needs no sign-in. The organiser guide's mass mail and registration sections say all of this, and the MyBB parity page records that allownotices is an opt-out an import cannot honestly carry over as consent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ojmw2ioA5aJq89WzMWd6V
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Mass mail reached every active, verified member, and there was no opt-out anywhere for it to honour — the admin screen said so in as many words. Under the GDPR that is neither consent nor a way to withdraw it. This makes the audience opt-in, and gives every message a way out.
What it does
Consent is a record, not a default. A new
users.mass_mail_opt_in_atcolumn (migration0058, partial index) stores when consent was given; null means the member never asked. Three places require it, not one:/admin/users/mail,So a member who stops the announcements drops out of a campaign that is already half sent, not merely the next one.
Two ways in. The registration form carries an unticked E-mail me the board's announcements box, and the same switch sits on Notifications → Preferences. Both start off. Registering enrols nobody.
Three ways out. That preferences switch, and the unsubscribe link now in every message's footer — signed on a new
mass-mailscope of the token that already carriedthread,forumandemail, so it needs no sign-in and still goes through the existing POST-confirm page rather than a GET a link scanner could trip.Admin copy stopped lying. The bullet reading "There is no unsubscribe link and no per-member opt-out" is gone; the audience label reads "Every member who has asked for announcements (N)".
Notes for the reviewer
allownoticesacross. It is an opt-out defaulted on, so importing it as consent would record an answer nobody gave. There is a parity entry saying so.List-Unsubscribeheader:OutgoingMailhas no headers field, so that means changing the port and every driver. The in-body link satisfies withdrawal; the header is a deliverability nicety worth its own change.Verification
pnpm verifygreen; typecheck and 1,927 unit tests green after the last edits.mass-mailtoken scope, the unsubscribe action, the preferences action, and the registration action.activation-no-js), opt-in and withdrawal plus registration enrolment (notifications-no-js, two new tests), the announcements notice on/unsubscribe(session-no-js), and the admin mass mail screen including an audience-of-0 assertion (admin-tabs-no-js, 19/19).getByLabel('Message')on the same form. Reworded, re-run green.🤖 Generated with Claude Code
https://claude.ai/code/session_015ojmw2ioA5aJq89WzMWd6V
Generated by Claude Code