18-Jun-2026 — campaign recipient address, per-list post-send actions, Pub/Sub guidance, and scoped cl... #278
build 702 — commit 4b2fd87
Campaign "Sent to" footer showed the sender's address for every recipient
The branded email layout footer renders Sent to {{params.memberMergeFields.EMAIL}}. For campaigns these params tokens must be rewritten to {{contact.EMAIL}} so Brevo fills in each recipient's own address, and that rewrite only runs when renderBrandedTemplate is called with campaign = true.
- Originally fixed in #279 (
fc4d8894), then regressed in #273 (10c1d92e, version-controlled email templates retiring Brevo as a template store): the rewrittenemailRequest.body/localTemplateHtmlbranches inperformTemplateSubstitutiondropped thecampaignargument, so it defaulted tofalse. - Campaign sends then stopped converting params → contact, and the footer's EMAIL was substituted at build time with the campaign's single
paramsvalue — the logged-in sender — baked identically into every recipient's copy. - Forwarding
campaignto bothrenderBrandedTemplatecalls restores per-recipient{{contact.EMAIL}}in the footer.
Whole-list sends now run their post-send actions
The disable / bulk-delete post-send actions previously only ran on the transactional selected-members path; a whole-list campaign send never applied them.
- After a campaign send, every group member subscribed to the list has the configured post-send actions applied (disable group member and/or cascade delete).
- A shared
applyPostSendActionsToMemberscontroller now backs both the batch and campaign paths. - New authenticated
POST /member/apply-post-send-actionsroute andmemberService.applyPostSendActions. - The pre-send warning now counts the whole list (it was showing 0 in list mode, only counting individually selected members), and the campaign success message reports how many members were removed or disabled.
- The email composer auto-fills the recipient selection when a narrow list is active (
autoFillnow bound tostate.narrowListId !== nullrather than always false).
Inbox push (Pub/Sub) setup is clearer about its topic
Enabling real-time WATCH delivery on a mailbox needs a Pub/Sub topic that already exists and lets Gmail publish to it. The admin page gave no hint of which topic, and a missing topic failed with a raw "topic not found".
- The push-config endpoint now returns the configured topic name (
InboxPushConfigResponse.configuredTopicName), read fromgoogleInbox.pubsubTopicName. - The mailbox-connections page shows whether a topic is set up and points back to step 1 ("Run Google Cloud setup") when it is not, and pre-fills a mailbox's topic from the configured one when switching it to WATCH.
- WATCH-mode failures now explain that the topic must exist and grant gmail-api-push@system.gserviceaccount.com the Publisher role, with the steps to put it right.
"Clear all forwards" now deletes the live Cloudflare rules, scoped and confirmed
On the committee email settings, "Clear all forwards" previously only set every role's forwardEmailTarget to CATCHALL in NGX's own config; it issued no Cloudflare deletes. The live per-role forwarding rules stayed active, and because Cloudflare matches literal rules before the catch-all, they kept diverting role mail away from the catch-all inbox — so the button did not actually achieve "everything via the catch-all".
- Clicking the button now opens an inline confirmation that states how many live Cloudflare forwarding rules will be deleted before anything happens, names the catch-all destination they will route to instead, and offers Cancel.
- Deletion is scoped to the current site's own domain/subdomain:
domainForwardRules()selects only rules whose literaltomatcher address ends in@<baseDomain>. Sibling subdomains that share a Cloudflare zone, and the catch-all rule itself, are left untouched. - On confirm it deletes each matched rule via the same
deleteRulepath the per-role "Delete Forward" button uses, sets the non-vacant roles to CATCHALL, reloads the live rules, saves the committee config, and reports the count deleted. Worker-backed rules removed this way leave their script behind, which remains visible under the existing Orphaned Cloudflare Workers section for separate cleanup.