Skip to content

Bug 1883428 - Improve the needinfo email situation - #2698

Open
Xzzz wants to merge 25 commits into
mozilla:masterfrom
Xzzz:bug-1883428
Open

Xzzz wants to merge 25 commits into
mozilla:masterfrom
Xzzz:bug-1883428

Conversation

@Xzzz

@Xzzz Xzzz commented Aug 12, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Needinfo and other flag notifications (request/grant/deny/clear) were previously sent as a separate email via Flag::notify(). This decouples that: flag events are now rendered as a section inside the normal bugmail, sent through Bugzilla/BugMail.pm, instead of a standalone notify() email.

  • Bugzilla/BugMail.pm: _get_flag_mail_events() gathers flag activity (requested/answered, including needinfo auto-clear on reply - status X, which the old notify()-based flow handled but was initially missed here) and resolves the related attachment so templates can check can_review / external_redirect
  • Core template/en/default/email/bugmail.{txt,html}.tmpl and the BMO override extensions/BMO/template/en/default/email/bugmail.{txt,html}.tmpl render a flag_events section, with a Hook.process('flag_event', ...) extension point
  • extensions/BMO fully overrides the core bugmail templates on this instance, so the flag-event content is ported into both
  • Needinfo and Splinter hook fragments (reporter-aware wording + user-guide link; review-tool link) are re-added under the new flag_event hook, replacing the ones orphaned by the earlier removal of request/email.txt.tmpl. Splinter's review link now gates on attachment.can_review instead of ispatch, so it also covers GitHub PR/Phabricator attachments, matching other call sites in the codebase
  • BMO's own external-redirect attachment link is inlined directly in the BMO templates rather than hooked, since TT's TRIM strips each hook fragment's edges independently and concatenating BMO's + Splinter's fragments with no separator produced garbled text when both fire on the same event

Test plan

  • Verified end-to-end on a local instance: needinfo request, needinfo auto-clear (reply), needinfo requested-from-reporter wording, plain review flag (Splinter link), review flag on a GitHub PR attachment (both BMO and Splinter links render, correctly separated), both text and HTML bugmail formats
  • perl -c on modified Perl files
  • Grepped the repo for orphaned references to the removed Flag::notify/hook fragments

References

Xzzz added 3 commits August 12, 2026 15:48
…necessary emails)

Remove Flag::notify()'s separate text-only flag emails and instead add flag requestee/requester
and per-flagtype cc_list addresses to the recipient list built by BugMail.pm, so
flag grant/deny/needinfo notices ride along with the normal bug-change email instead of
arriving as a second, plaintext-only message (also fixes bug 1410772).

- Add REL_FLAG_REQUESTEE/REL_FLAG_REQUESTER/REL_FLAG_TYPE_CC relationships; requestee/requester
  respect the existing EVT_FLAG_REQUESTED/EVT_REQUESTED_FLAG opt-in, type_cc is unconditional
  (matches notify()'s prior behavior)
- Add a flag-events section to bugmail.txt.tmpl/bugmail.html.tmpl, with more descriptive wording
  for the needinfo requestee
- Preserve cc_list addresses with no Bugzilla account via a small side path (bugmail-flagtype-cc.txt.tmpl)
  sent directly through MessageToMTA
…templates

The previous commit added a flag-event section to the core bugmail templates, but
extensions/BMO/template/.../email/bugmail.{txt,html}.tmpl fully overrides those
core templates, so the new section was never executed. Also fixes a real gap the
core-only version missed: needinfo's normal resolution (auto-clear to flag status 'X'
when the requestee replies) wasn't handled (only +/- were) so the single most common
needinfo outcome silently produced no "your request was answered" notice. notify()
covered this case; now BugMail.pm does too, with matching "cleared" wording alongside
"granted" and "denied".

- Port the flag-event section into the BMO override templates (txt + html), with
  a Hook.process('flag_event', ...) extension point mirroring the old request/email.txt.tmpl
  hook mechanism
- Add Needinfo and Splinter hook fragments preserving their previous content
  (reporter-aware wording + wiki guide link; Splinter review-tool link, now gated
  on attachment.can_review so it also covers GitHub PR/Phabricator attachments,
  not just ispatch)
- Inline BMO's own external-redirect (GitHub PR) attachment link directly in the
  BMO templates rather than hooking it: hooking it caused two fragments (BMO + Splinter)
  to concatenate with no separator when both fire for the same event, since TT's TRIM
  strips whitespace at each independently compiled hook fragment's own edges.
  The original design avoided this by having the parent template render that piece
  directly instead of through the shared print-hook slot
- Resolve the attachment object in _get_flag_mail_events() so hooks can check
  can_review/external_redirect
- Remove the three hook fragments orphaned by the previous commit's deletion of
  request/email.txt.tmpl
Comment thread Bugzilla/BugMail.pm Outdated
Comment thread Bugzilla/BugMail.pm
Comment thread Bugzilla/BugMail.pm
Comment thread template/en/default/email/bugmail-flagtype-cc.txt.tmpl
Comment thread Bugzilla/BugMail.pm Outdated
Comment thread template/en/default/global/reason-descs.none.tmpl
Xzzz added 6 commits August 19, 2026 17:12
…g X status wording and missing body-headers

- terms was never defined: added `[% PROCESS global/variables.none.tmpl %]` to fix it
- status 'X' (cleared) rendered as "denied": now render as "cleared" (like the other templates)
- added missing `@@body-headers@@` placeholder: without it, BMO's `_replace_placeholder_in_part` got nothing to substitute and these mails lose the body headers the old request/email.txt.tmpl carried
…achment visibility at dequeue

- enqueue(): flatten flag_events before it hits the job queue
- dequeue(): inflate flag_events and re-check attachment visibility at send time
Comment thread Bugzilla/BugMail.pm
Comment thread Bugzilla/BugMail.pm
Comment thread Bugzilla/BugMail.pm
Comment thread template/en/default/request/email.txt.tmpl
Comment thread Bugzilla/BugMail.pm Outdated
Comment thread Bugzilla/BugMail.pm
Comment thread Bugzilla/BugMail.pm
Xzzz added 2 commits August 21, 2026 18:12
- don't add the requester as recipient when they cleared their own request
- give flag-type cc_list account holders the actual event content
- don't drop flag-only bugmail when there are no diffs/comments
- restore X-Bugzilla-Flag-Requestee header, carrying requestee/requester through the mailer-queue flatten/inflate cycle
- break same-second ties on id when resolving a flag's previous status
- don't credit a watcher with a role that was never actually inherited
@Xzzz
Xzzz requested a review from dklawren August 21, 2026 16:16
- Added `package main;` at top of file
- Added `## no critic (Variables::ProtectPrivateVars)` before the private-sub reference
Comment thread Bugzilla/BugMail.pm Outdated
? Bugzilla::Attachment->new({id => $_->{attachment_id}, cache => 1})
: undef,
}
} @{$vars->{flag_events}}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unguarded deref of $vars->{flag_events}. any bug_mail job already sitting in TheSchwartz when this deploys was enqueued without a flag_events key, so dequeue dies with "Can't use an undefined value as an ARRAY reference" and those mails are lost across retries. use @{$vars->{flag_events} || []}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: guarded with @{$vars->{flag_events} || []}

Comment thread Bugzilla/BugMail.pm
Comment thread Bugzilla/BugMail.pm
Comment thread Bugzilla/BugMail.pm
Comment thread Bugzilla/Constants.pm
Comment thread template/en/default/email/bugmail-flagtype-cc.txt.tmpl
Comment thread Bugzilla/BugMail.pm
@Xzzz
Xzzz requested a review from dklawren September 8, 2026 16:42
Comment thread Bugzilla/Flag.pm
Comment thread Bugzilla/BugMail.pm Outdated
# that's now been granted/denied, becomes a recipient even without any
# other role on the bug. Flag-type cc_list addresses (admin-configured,
# bypasses per-user opt-in) are added the same way notify() used to.
my @flag_events

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the guard you fixed in sendMail and in dequeue is still missing from the third copy at line 147, which runs before this block:

return {'sent' => []} unless scalar(@diffs) || scalar(@$comments);

flag changes normally write a flagtypes.name bugs_activity row, so @diffs is non-empty and i can't hand you a repro through the ui for this one on its own. still worth || @flag_events for consistency with the other two, and because this return also skips _send_flag_type_cc_raw_mail at line 464. would need @flag_events hoisted above the return, $start/$end are already in scope there


[% FOREACH event = flag_events %]
[% IF event.action == 'requested' %]
[% IF event.type.name == 'needinfo' %]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

branching on event.type.name == 'needinfo' puts a bmo-only flag type into an upstream core template.

the flag_event hook this pr adds is the right place for it, and extensions/Needinfo/template/en/default/hook/email/bugmail-flag_event.txt.tmpl already fires on exactly this condition, it just contributes the follow-up sentences rather than the headline.

suggest moving the headline into the needinfo hook and letting the generic ELSE wording stand in core. same applies to bugmail.html.tmpl and to both bmo overrides, where the special case is duplicated again

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the needinfo special case from the core text/HTML templates and from both BMO overrides, so every flag request uses the generic "has requested from you" line. The "needs more information from you to work on this bug" sentence now opens the Needinfo flag_event hook, followed by the existing reporter-aware wording and the user-guide link.

=> Fixed in "Bug 1883428 - Move the needinfo headline into the Needinfo flag_event hook"

@Xzzz
Xzzz requested a review from dklawren September 24, 2026 15:53
Comment thread Bugzilla/BugMail.pm
$user_cache->{$row->{setter_id}}
||= Bugzilla::User->new({id => $row->{setter_id}, cache => 1});

if ($row->{status} eq '?' && $row->{requestee_id}) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flag type cc_list no longer gets mail for ? with no requestee or a direct +/- (e.g. approval-* requests), which notify() used to send, so release lists lose these notices

Do you remember what we discussed about how to handle the arbitrary plain text accounts in cc_list since they may or may not be real bmo accounts?

We can either manually add them to the cc of the emails as-is or look up each one to see if a real account exists and then add it normally.

I have an (old) bug that is suppose to make the text field a mapping table of real bmo accounts but it was never done.

@Xzzz Xzzz Sep 28, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in "Bug 1883428 - Notify flag type cc_list of every flag change as notify() did"

Changes

  • Every flag change now produces an event: a ? with no requestee, a direct +/-, or a clear not preceded by ? becomes a generic set event. It adds no requestee or requester as a recipient, but still reaches the flag type's cc_list, like notify() did
  • cc_list users get neutral wording. If a cc_list user isn't the person asked or the person who asked, they get the set wording. Before, they got "from you" or "your request" wording or the needinfo text.
  • Templates: each of the 5 templates has a set line for it: "<setter> set <flag><status> [for attachment N]". A clear shows "(cleared)" instead.

On addresses that aren't real accounts: we went with the option to add them as-is. Each cc_list entry is looked up. If it's a real account, it's added as a normal recipient. If not, it gets a separate plain-text notice sent straight to that address (_send_flag_type_cc_raw_mail / email/bugmail-flagtype-cc.txt.tmpl), because the normal mail pipeline only handles account ids.
=> Nothing is dropped, except that on group-restricted bugs or private attachments these addresses are skipped, same as notify() did.

Comment thread Bugzilla/FlagType.pm Outdated

# Bugzilla::BugMail uses this module, so load it lazily.
require Bugzilla::BugMail;
Bugzilla::BugMail::Send($_, {changer => Bugzilla->user})

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this runs a full Send() for every affected bug inside the admin web request, so an inclusion/exclusion edit that touches many bugs can hang or time out, could this be queued or batched instead

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be queued in jobqueue anyway and claude messed up. Can you verify?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked: it was only partially queued, so the concern was valid ;)

With use_mailer_queue on, only the final render and delivery went to the job queue (sendMail() inserts one bug_mail job per recipient). Everything before that in Send() ran inline in the admin request for every affected bug: bug load, activity/comment/flag queries, recipient and watcher resolution, per-user permission and preference checks, and the bugmail_recipients hook.
The previous commit made it heavier on this path: every flag cleared by the edit now produces a set event, so Send() no longer returns early for those bugs.

Changes

  • FlagType::update: after the commit, it inserts a single bug_mail_send job with the affected bug ids and the admin as changer, instead of calling Send() per bug. When use_mailer_queue is off, it still sends inline as before, since there's no queue to use.
  • New Bugzilla::Job::BugMailSend: runs Send() for each bug in the worker, as the admin. Bugs deleted since the job was queued are skipped. It reuses Bugzilla::Job::Mailer's retry settings. A retry doesn't re-mail bugs already done, because Send() advances lastdiffed.
  • JobQueue.pm: bug_mail_send added to JOB_MAP. Workers register every mapped job, so nothing else is needed.

If someone changes one of these bugs before the job runs, that change's own Send() mails the pending flag events, and the job then finds nothing left to send for that bug.

=> Fixed in "Bug 1883428 - Queue bugmail for bugs affected by a flag type edit"

Comment thread t/bugmail-flag-events.t
package main;

## no critic (Variables::ProtectPrivateVars)
my $visible = \&Bugzilla::BugMail::_flag_event_visible_to;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only _flag_event_visible_to() is tested, please add coverage for _get_flag_mail_events() (prev ? check, X auto-clear) and recipient selection

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added DB-backed coverage in a new t/bmo/bugmail-flag-events.t. It needs real flag_activity rows, so it runs in test_bmo against MySQL, not in test_sanity like t/bugmail-flag-events.t (whose header now points to the new file). Passes locally

Each scenario uses a fresh bug with lastdiffed pinned, and inserts flag_activity rows at fixed times before, inside and after the mail window, so the window is deterministic. A dedicated flag type has a cc_list with one real account and one address with no account. Mail delivery is set to None, so Send() builds every recipient's mail and returns who it went to without sending anything.

Changes

  • _get_flag_mail_events(): one event per in-window row, classified as requested/answered/set, rows outside the window ignored. It covers:
    • an answer finding its requester from a ? before the window (prev ? check)
    • an X auto-clear in the same second as its ? counting as an answer (tie broken on id)
    • clearing an already-granted flag becoming a set with no requester
  • Recipient selection in Send():
    • request: the requestee (no role on the bug) and the cc_list account are mailed; the requester and an unrelated user aren't
    • answer: the requester is mailed, the person who answered isn't
    • requester cancelling their own request: they aren't mailed, cc_list still is
    • ? with no requestee: only cc_list is mailed, and _get_flag_type_cc() puts the address with no account on the raw mail path

=> Fixed in "Bug 1883428 - Add DB-backed tests for flag mail events and recipients"

@Xzzz
Xzzz requested a review from dklawren September 28, 2026 13:12

This branch has not been deployed

No deployments
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