close
Skip to content

Notes: Email users mentioned in a note - #79606

Merged
adamsilverstein merged 202 commits into
trunkfrom
add/notes-mention-notifications
Aug 4, 2026
Merged

Notes: Email users mentioned in a note#79606
adamsilverstein merged 202 commits into
trunkfrom
add/notes-mention-notifications

Conversation

@adamsilverstein

@adamsilverstein adamsilverstein commented Jun 27, 2026

Copy link
Copy Markdown
Member

What

Emails users who are @mentioned in a note. The mention autocomplete UI landed in #79604; this is the notification half of that feature.

Fixes #80719

Related: #73415 - that issue covered both halves, and the autocompleter shipped in #79604.

Note

This PR has been trimmed to the minimal version. Earlier revisions carried a per-thread followers model, which is now tracked in #80279, and a deep link that opened the editor focused on the linked thread, which is a separate enhancement and can follow in a later release.

How

On rest_insert_comment for note comments - the same hook core uses for its post-author notification - gutenberg_notify_note_mentions() parses the mentions out of the saved content and emails each mentioned user.

Mentions are stored as chips carrying the mentioned user's ID in a class token: <span class="wp-note-mention user-N">@Name</span>, the markup #79604 and #80528 landed. Only elements carrying both classes are treated as mentions. The email is composed in the recipient's locale via switch_to_user_locale() and links to the post editor with get_edit_post_link(), the same way core's own note notification does.

Notification audience

  • Post author is deliberately left out; core already notifies them of every note via wp_new_comment_via_rest_notify_postauthor(), so excluding them here avoids a duplicate email.
  • The note's own author is never notified about their own note.
  • Recipients are limited to users who can edit_comment the note, the same bar as the REST API's read permission for notes, so emails can't leak note content to users who can't see the note in the editor.
  • Notifications fire only when a note is created; editing a note doesn't re-notify.
  • The whole feature honors the existing wp_notes_notify preference.

Extensibility

No new filters. Core's comment_notification_recipients already runs on every note through wp_notify_postauthor(), so extra recipients can be added there; pre_wp_mail and wp_mail can suppress or reroute delivery to another channel; and gutenberg_send_note_notification() is a plain function, so a plugin can notify anyone it likes from its own rest_insert_comment callback.

Testing

Test in WordPress Playground

  1. As an admin, create a second user with an editor role and an email address you can inspect (or install an email logger).
  2. Add a note to a block and @mention the second user.
  3. The mentioned user receives a "[Site] You were mentioned in a note on ..." email linking to the post editor.
  4. Mention yourself: no email. Mention the post author: they get core's regular "new note" email only, no duplicate.

phpunit/notes-mentions-test.php covers mention parsing (non-mention spans and plain anchors ignored, IDs deduped), the mentioned-user email, email content and the editor link, author self-exclusion, post-author exclusion, capability gating, nonexistent-user mentions, the wp_notes_notify off switch, and - through real REST dispatches - that creating a note via POST /wp/v2/comments triggers the email while updating one doesn't, which guards the rest_insert_comment wiring itself.

npm run test:unit:php:base -- --filter Tests_Notes_Mentions

12 tests pass locally. Worth noting the parser previously looked for <a class="wp-note-mention ..."> anchors, which was the markup before #80528 switched mentions to span chips - against current trunk it matched nothing and no email was ever sent. Reverting just the tag name in the parser turns 5 of these tests red, so the coverage does hold the fix down.

Follow-up

talldan and others added 30 commits May 14, 2026 07:38
…ers/className

The trunk `useRichText` now consumes `allowedFormats`,
`withoutInteractiveFormatting`, and a format-type handler context directly,
returning `formatTypes` alongside the editor state. Lean on that and drop the
duplicated `useFormatTypes` / editor-only-format wrappers.

Add `autocompleters` (forwarded to `useBlockEditorAutocompleteProps`) and
`className` props so callers like the Notes inline form can wire `@`-mention
completers and customize the contenteditable styling. Route the stylesheet
through the block-editor entry instead of importing the SCSS from JS.
Add `@wordpress/block-editor` to the fields package's dependencies — the new
`RichTextControl` is imported via private APIs and the dependency was missing,
tripping `import/no-extraneous-dependencies`.

In `fields/rich-text/edit.tsx`, forward the new `className` and
`autocompleters` config to `RichTextControl` and make `config` itself
optional (consumers like the title field do not pass one). Drop the commented
placeholder `ConfiguredRichTextEdit` in the title field.
Add focused unit tests covering:

- `RichTextControl`: labeled-textbox markup, `hideLabelFromVision`,
  `disableLineBreaks`/`aria-multiline` toggling, and consumer-supplied
  `className` merging.
- `fields/rich-text/edit`: that the wrapper forwards field label/value/id to
  the underlying control, that change events flow through `field.setValue`
  back to the consumer's `onChange`, that optional config props
  (`clientId`, `placeholder`, `allowedFormats`, etc.) are passed through,
  and that a missing config object does not crash.
A `<label for>` only contributes an accessible name to native form
controls, not to a `<div role="textbox">`. Mirroring `label` onto
`aria-label` gives the contenteditable a stable accessible name so
assistive tech and Playwright `getByRole('textbox', { name })` lookups
resolve consistently regardless of `hideLabelFromVision`.
The wrapper's prop type was `Pick<DataFormControlProps, ...> & { config: RichTextFieldConfig }`,
which is not contravariantly assignable to `ComponentType<DataFormControlProps<Item>>`
because the rich-text `config` shape diverges from the generic one. Accepting the
standard `DataFormControlProps` and narrowing `config` at the call site keeps the
wrapper usable as a `Field.Edit` and drops the now-unneeded `@ts-expect-error`.
`@wordpress/block-editor` ships no `.d.ts` files, so the type-declaration
build (`tsgo --build`) fails to resolve the import even though the package
is declared as a runtime dependency. Use `@ts-ignore` (rather than the
`@ts-expect-error` that was dropped earlier) so the directive does not
flip to "unused" under per-package `tsc` checks that happen to resolve
the source successfully.
`FormatEdit` populates `keyboardShortcuts` and `inputEvents` Sets via
context, but `RichTextControl` never attached a `keydown` or `input`
listener to the contenteditable, so registered shortcuts (Cmd+B, Cmd+I,
Cmd+K, etc.) and native InputEvents (formatBold/formatItalic) never
fired. Cmd+K also bubbled past the control to open the WordPress
command palette because no shortcut consumed it.

Attach the existing `shortcuts` and `input-events` listeners while the
control is focused, mirroring the in-canvas `RichText` wiring. The
link format's `RichTextShortcut` now calls `preventDefault()` on
Cmd+K, which causes the command palette's global handler (which bails
on `defaultPrevented`) to skip opening.

Add unit tests covering shortcut dispatch on focus, no dispatch when
unfocused, and listener teardown on blur.
`RichTextControl` now invokes each format type's `__unstableInputRule`
on `input`/`compositionend` events, so e.g. typing `` `code` `` in a
notes field auto-applies `core/code`'s inline-code format the same way
it does in the canvas.

The block-editor's existing `input-rules.js` listener handles three
distinct concerns (block prefix transforms, block input transforms,
format input rules), and pulls in `@wordpress/blocks`, block-editor
store actions, and `onReplace`/`selectionChange` callbacks. None of
those apply to a standalone field control — so wire a focused handler
that only runs the format-rule reduce, mirroring the same branch
inline.

Also set `suppressContentEditableWarning` on the contenteditable so
React doesn't warn when `useRichText` writes value into the DOM
directly (matching in-canvas `RichText`).
`isVisible` controls whether a format's toolbar button is shown — it's
how a format hides its toolbar surface in contexts that don't have a
toolbar (e.g., the standalone `RichTextControl` used in DataForm
fields). It should not gate the link popover itself: the popover is
triggered by `Cmd+K` (or the toolbar button) and represents the link
editing UI, not a toolbar element.

Before this change, `RichTextControl` consumers that hid format
toolbars (`isVisible={false}`) could press `Cmd+K` on a selection and
nothing visible would happen — the shortcut fired, `addingLink`
flipped true, but `InlineLinkUI` was suppressed by the same flag that
hid the toolbar button.
When a format type opens a popover from inside `RichTextControl` (the
inline link UI on Cmd+K, or any similar format-spawned UI), focus
moves out of the contenteditable to the popover's first focusable
element. That fired the textbox's onBlur, which flipped `isSelected`
to false, unmounted `FormatEdit`, and tore the popover down before it
ever rendered.

Defer the `isSelected = false` flip via a 0ms `setTimeout` so the new
focus target has a chance to land. If the active element is inside
`.components-popover`, leave the control selected — it's a format
popover keeping the user in this control's interaction scope.

Verified with a new unit test covering the popover-focus case in
addition to the existing focus/blur shortcut tests.
RichTextControl deliberately drops the block-editor selection
coupling that focuses the in-canvas RichText, so a standalone
consumer (e.g. a note form) has no way to place the caret in the
field when it opens — regressing focus-on-open behavior the old
RichText got for free.

Add an opt-in focusOnMount prop that focuses the contenteditable on
mount via useRefEffect (mirroring the existing eventListenersRef
pattern in this file). Off by default so DataForms and other
consumers are unaffected. Named focusOnMount rather than autoFocus
to match @wordpress/compose's useFocusOnMount and to avoid the
jsx-a11y/no-autofocus rule, since this is not the browser autofocus
attribute. Covered by unit tests for both the default-off and
opt-in cases.
…l package

The new control is intended for standalone form fields and does not touch
the block tree or selection, so it does not belong in @wordpress/block-editor.
Hosting it there forced @wordpress/fields to take a dependency on the entire
block-editor module graph just to render a single form input.

Move RichTextControl into a new lower-level package that depends only on
@wordpress/rich-text plus @wordpress/components/compose/element. The new
package vendors the small helpers it owns (getAllowedFormats, the keyboard
and input-event contexts, the two event-listener modules, and a
BlockContext-free FormatEdit) so block-editor's canvas RichText keeps its
own copies untouched. The autocompleters prop is dropped from this initial
release; it depended on block-editor's autocomplete component and no current
consumer wires it.

@wordpress/fields now imports the control via the new package's private API
and the @wordpress/block-editor dependency is removed.
Format types (e.g. the inline link UI) open portaled popovers. Blur
handling previously kept the field selected whenever focus moved into any
`.components-popover`, which can match popovers this control did not open.

Host the format UI in a private `SlotFillProvider` paired with the
control's own `Popover.Slot`, wrapped in a `data-rich-text-control-popover-slot`
marker and portaled to the field's document body (so popovers escape any
scroll/overflow container). Blur handling then matches that marker
precisely, plus `[data-wp-compat-overlay-slot]` for popovers migrated to
`@wordpress/ui`. This also avoids leaking format fills into an ambient
slot registry and removes the `SlotFillProvider` warning that a bare
`Popover.Slot` emits without a provider.

Update tests: focus into the dedicated slot or the compat overlay keeps
the field selected; focus into an unrelated popover now deselects.
Register a story so the control can be tested in isolation as popovers and
other UI migrate to `@wordpress/ui`. The story renders a controlled field
(Default and WithInitialValue), wraps it in a `SlotFillProvider`, and imports
`@wordpress/format-library` so the formatting shortcuts (Cmd+B/I) and the
inline link popover can be exercised without the editor.
Standard format types (bold, link, …) render `RichTextShortcut` and
`RichTextInputEvent`, which read `keyboardShortcutContext` /
`inputEventContext`. Those contexts and components lived in
`@wordpress/block-editor` and were only provided inside the in-canvas
`RichText`. A standalone field like `RichTextControl` vendored its own
separate context objects, so the block-editor components found no provider
and threw `Cannot read properties of undefined (reading 'current')` on
focus — breaking the field anywhere outside the block canvas (e.g. a
DataForms title).

Move the two contexts and the two components into `@wordpress/rich-text`
private APIs, the lowest-level shared home. `@wordpress/block-editor`
re-exports them for back-compat (so `@wordpress/format-library` and native
are unchanged), and both block-editor's `RichText` and the standalone
`RichTextControl` now provide the exact same context objects the format
components read.

- rich-text: add private `keyboardShortcutContext`, `inputEventContext`,
  `RichTextShortcut`, `RichTextInputEvent` (rewritten lint-clean).
- block-editor: source the contexts from rich-text and re-export;
  `shortcut.js`/`input-event.js` become thin re-export shims. Drop their now
  stale eslint suppressions.
- rich-text-control: provide the shared contexts via `unlock`; remove the
  vendored `contexts.js`; point the test's fake shortcut at the shared
  context.
The story exercises format UI from `@wordpress/format-library` and the
inline link popover (`LinkControl`) from `@wordpress/block-editor`. Without
those package stylesheets the link popover rendered cramped and unstyled in
Storybook. Register the `richtextcontrol` component id in the package-styles
config so it lazy-loads `components`, `block-editor`, and `format-library`
styles, matching how the field renders in the editor.
…mport

Importing `style.scss` directly from the story tripped the
`@wordpress/no-non-module-stylesheet-imports` lint rule (caught by the full
`lint:js` CI step, which the changed-files-only pre-commit hook didn't run).
Drop the JS stylesheet import and load the control's own styles through the
Storybook package-styles mechanism instead: add
`rich-text-control-{ltr,rtl}.lazy.scss` and include them in the
`richtextcontrol` config entry, alongside components/block-editor/format-library.
…ivate API

The package has no wpScript/wpModuleExports, so it is a bundled npm-only
package. The private-apis lock/unlock layer exists to share private APIs
across the wp.* namespace at runtime; for a directly-imported package it
adds overhead without benefit, and project guidance advises against using
private APIs in bundled packages.

Export RichTextControl directly from the package index and update the
@wordpress/fields consumer to a plain import. The local lock-unlock helper
and the private-apis allowlist entry remain because control.js still
consumes @wordpress/rich-text's private APIs.
…om block-editor

keyboardShortcutContext and inputEventContext are private and were never part
of the public block-editor API, so the back-compat re-export was unnecessary.
The only internal consumer (block-fields RichTextControl) now reads them
directly from @wordpress/rich-text via unlock, matching how the other rich
text package consumers already work.
The richTextField definition was an orphan: nothing imports it, it is
not exported from the package's public index, and it is not registered
in any field list. The shared RichTextEdit component it referenced is
still used directly by the title field, so only the field stub is
removed.

Addresses review feedback on #78825.
…ol-package

# Conflicts:
#	packages/dataviews/CHANGELOG.md
#	packages/fields/CHANGELOG.md
@adamsilverstein
adamsilverstein enabled auto-merge (squash) August 4, 2026 06:01
@Mamaduka Mamaduka added the Backport to WP 7.1 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta label Aug 4, 2026
@adamsilverstein adamsilverstein self-assigned this Aug 4, 2026
@adamsilverstein
adamsilverstein merged commit 6dc32fc into trunk Aug 4, 2026
54 of 58 checks passed
@adamsilverstein
adamsilverstein deleted the add/notes-mention-notifications branch August 4, 2026 06:13
@github-actions github-actions Bot added this to the Gutenberg 23.8 milestone Aug 4, 2026
@github-actions github-actions Bot removed the Backport to WP 7.1 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta label Aug 4, 2026
gutenbergplugin pushed a commit that referenced this pull request Aug 4, 2026
Co-authored-by: talldan <talldanwp@git.wordpress.org>
Co-authored-by: adamsilverstein <adamsilverstein@git.wordpress.org>
Co-authored-by: westonruter <westonruter@git.wordpress.org>
Co-authored-by: Mamaduka <mamaduka@git.wordpress.org>
Co-authored-by: sethrubenstein <smrubenstein@git.wordpress.org>
@github-actions github-actions Bot added the Backported to WP Core Pull request that has been successfully merged into WP Core label Aug 4, 2026
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

I just cherry-picked this PR to the wp/7.1 branch to get it included in the next release: d8f2c5f

@t-hamano

t-hamano commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

As we are approaching the 7.1 RC1 release tomorrow, I would like to confirm whether it has been agreed to backport this PR to 7.1. The milestone for the core ticket is also not set to 7.1. My understanding was that the mention feature in the 7.1 release would only include autocomplete, with email notifications postponed.

cc @annezazu

@Mamaduka

Mamaduka commented Aug 4, 2026

Copy link
Copy Markdown
Member

I thought this (mentions) was a blessed task. Without notifications, adding a mention to a note does nothing, so the feature doesn't make sense. If we can't ship the notification, then it's better to exclude mentions as well.

@adamsilverstein

adamsilverstein commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

As we are approaching the 7.1 RC1 release tomorrow, I would like to confirm whether it has been agreed to backport this PR to 7.1. The milestone for the core ticket is also not set to 7.1. My understanding was that the mention feature in the 7.1 release would only include autocomplete, with email notifications postponed.

I did milestone the core backport for 7.1 (and update the trac ticket description) and it is mainly awaiting review. I also wasn't sure we were shipping with notifications, but I was convinced my @Mamaduka's argument that mentions are of little value without them. Also we de-scoped the feature quite a bit, removing deep links and the follow + unfollow mechanism, edit tracking and so on for now until we have a more comprehensive notification API in place.

All the feature does now is notify mentioned users when a note is created. This is pretty similar to what we added for notes themselves.

That said, I am also fine with punting the mention feature entirely @t-hamano and will let the release team decide. I do agree that if we remove the notification part, we should probably remove the mention capability entirely. what do you think?

@t-hamano

t-hamano commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

That said, I am also fine with punting the mention feature entirely @t-hamano and will let the release team decide. I do agree that if we remove the notification part, we should probably remove the mention capability entirely. what do you think?

perhaps we should be able to ship this feature if there is consensus among the release squads. Let's confirm on Slack. https://wordpress.slack.com/archives/C0B4Q0RJVAT/p1785826459345539?thread_ts=1783621633.996009&cid=C0B4Q0RJVAT

@adamsilverstein

Copy link
Copy Markdown
Member Author

I will commit the backport before RC and gather feedback.

@annezazu

annezazu commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Just for transparency, commenting here that this is good to land. I agree that this is a necessary and well scoped change to include for the @mentions feature to be more meaningful.

pento pushed a commit to WordPress/wordpress-develop that referenced this pull request Aug 4, 2026
Introduce `wp_notify_note_mentions()` on `rest_insert_comment`, alongside the existing post author notification, which parses those IDs out of the saved note and emails each mentioned user in their own locale with a link back to the post editor.

Recipients are limited to users who can `edit_comment` the note, matching `WP_REST_Comments_Controller::check_read_permission()`, so an email cannot carry note content to someone who cannot see the note in the editor. The note's own author is skipped, as is the post author, who `wp_new_comment_via_rest_notify_postauthor()` already notifies about every note. Only note creation notifies, and the existing `wp_notes_notify` option turns the whole path off.

See related Gutenberg pull request: WordPress/gutenberg#79606.

Follow-up to [62832].

Props westonruter, mamaduka.
Fixes #65639.



git-svn-id: https://develop.svn.wordpress.org/trunk@63012 602fd350-edb4-49c9-b593-d223f7449a82
markjaquith pushed a commit to markjaquith/WordPress that referenced this pull request Aug 4, 2026
Introduce `wp_notify_note_mentions()` on `rest_insert_comment`, alongside the existing post author notification, which parses those IDs out of the saved note and emails each mentioned user in their own locale with a link back to the post editor.

Recipients are limited to users who can `edit_comment` the note, matching `WP_REST_Comments_Controller::check_read_permission()`, so an email cannot carry note content to someone who cannot see the note in the editor. The note's own author is skipped, as is the post author, who `wp_new_comment_via_rest_notify_postauthor()` already notifies about every note. Only note creation notifies, and the existing `wp_notes_notify` option turns the whole path off.

See related Gutenberg pull request: WordPress/gutenberg#79606.

Follow-up to [62832].

Props westonruter, mamaduka.
Fixes #65639.


Built from https://develop.svn.wordpress.org/trunk@63012


git-svn-id: http://core.svn.wordpress.org/trunk@62231 1a063a9b-81f0-0310-95a4-ce76da25c4cd
pento pushed a commit to WordPress/wordpress-develop that referenced this pull request Aug 5, 2026
This updates the pinned commit hash of the Gutenberg repository from `fd715a6833679d098d9fee84b642f8f1bc27341b` to `f05e40e91c54f29c449b1f33d0db89f5166812d9`.

A full list of changes included in this commit can be found on GitHub: 
WordPress/gutenberg@fd715a6...f05e40e

- Writing flow: forward delete an empty paragraph without breaking apart the next block (WordPress/gutenberg#80813)
- Upload Media: Fail the item when the /finalize request fails (WordPress/gutenberg#80725)
- Fix template `modified` and `date` return value for file templates (WordPress/gutenberg#80733)
- Boot: Adjust specificity of the image reset styles so components can size their own images (WordPress/gutenberg#80845)
- Quote: Ensure paragraph placeholder appears after deleting nested blocks (WordPress/gutenberg#77151)
- Block editor: make the Group action wrap blocks with a group transform (WordPress/gutenberg#80891)
- Copy: preserve the block when its entire text is selected (WordPress/gutenberg#80994)
- Add opt-out for block style state controls (WordPress/gutenberg#80956) (WordPress/gutenberg#81004)
- Tabs: Support Home and End keys for keyboard navigation (WordPress/gutenberg#80912)
- Rename blockStatesEnabled setting to blockStatesEditingEnabled (WordPress/gutenberg#81058)
- [WP 7.1] Background: Fix the legacy gradient UI where a gradient cannot be selected (WordPress/gutenberg#81059)
- Views: honor developer-defined view config overrides (WordPress/gutenberg#80832)
- Playlist: Add track icon (WordPress/gutenberg#81078)
- Remove the CODEOWNERS file from wp/7.1. (WordPress/gutenberg#81104)
- Notes: Email users mentioned in a note (WordPress/gutenberg#79606)
- Backport 81068 80744 80642 (WordPress/gutenberg#81135)
- Site Editor: Add E2E coverage for view config extensibility (WordPress/gutenberg#80577)
- change from WordPress/gutenberg#81068 (WordPress/gutenberg#81140)
- Link Control: Restore the preview title underline (WordPress/gutenberg#81083)
- Button: Suppress UA focus ring when focused and pressed (WordPress/gutenberg#81113)
- View config: add reference docs (WordPress/gutenberg#81149)
- Editor: Fix document tools button focus ring (WordPress/gutenberg#81115)
- Interface: Increase footer breadcrumb height to prevent focus ring clipping (WordPress/gutenberg#81156)
- Post editor: Add ThemeProvider for admin color schemes (WordPress/gutenberg#81112)
- Pass Playlist controls to track blocks (WordPress/gutenberg#81158)
- Theme: Omit color properties when neither provided nor inherited (WordPress/gutenberg#80600) (WordPress/gutenberg#81172)
- Media: Improve the HEIC upload error and keep any upload errors up until dismissed (WordPress/gutenberg#81130)
- Video: Hide settings for the GIF variation (WordPress/gutenberg#81142)
- Video: clarify the Video variation description (WordPress/gutenberg#81181)
- Button: turn on the width setting by default in theme.json (WordPress/gutenberg#81196)
- Edit Widgets: Fix header toolbar button focus ring (WordPress/gutenberg#81176)
- Build: Wrap script bundles in an IIFE to contain 'use strict' (WordPress/gutenberg#79792)
- Customizer widgets: Add ThemeProvider for admin color schemes (WordPress/gutenberg#81174)
- Fix: Tabs block: Start with empty tab labels with placeholders (WordPress/gutenberg#81197)
- PanelColorSettings: Restore the missing space below the panel header (WordPress/gutenberg#81155)
- Visual revisions: add shareable urls (WordPress/gutenberg#81205)
- Notes: fix the mention notification email composition (WordPress/gutenberg#81187)
- Fix ESLint warnings for 'navigateRegionsProps' spread (WordPress/gutenberg#81208)
- Widgets editor: Add ThemeProvider for admin color schemes (WordPress/gutenberg#81173)
- Remove the editableRoot opt-in from the paragraph block (WordPress/gutenberg#81184)
- Media Attached to: Fix issue with the popover unexpectedly flipping, tweak wording (WordPress/gutenberg#81206)
- Ensure device preview is always accurate when window is zoomed in (WordPress/gutenberg#81215)

Props wildworks.
See #65529.

git-svn-id: https://develop.svn.wordpress.org/trunk@63026 602fd350-edb4-49c9-b593-d223f7449a82
markjaquith pushed a commit to markjaquith/WordPress that referenced this pull request Aug 5, 2026
This updates the pinned commit hash of the Gutenberg repository from `fd715a6833679d098d9fee84b642f8f1bc27341b` to `f05e40e91c54f29c449b1f33d0db89f5166812d9`.

A full list of changes included in this commit can be found on GitHub: 
WordPress/gutenberg@fd715a6...f05e40e

- Writing flow: forward delete an empty paragraph without breaking apart the next block (WordPress/gutenberg#80813)
- Upload Media: Fail the item when the /finalize request fails (WordPress/gutenberg#80725)
- Fix template `modified` and `date` return value for file templates (WordPress/gutenberg#80733)
- Boot: Adjust specificity of the image reset styles so components can size their own images (WordPress/gutenberg#80845)
- Quote: Ensure paragraph placeholder appears after deleting nested blocks (WordPress/gutenberg#77151)
- Block editor: make the Group action wrap blocks with a group transform (WordPress/gutenberg#80891)
- Copy: preserve the block when its entire text is selected (WordPress/gutenberg#80994)
- Add opt-out for block style state controls (WordPress/gutenberg#80956) (WordPress/gutenberg#81004)
- Tabs: Support Home and End keys for keyboard navigation (WordPress/gutenberg#80912)
- Rename blockStatesEnabled setting to blockStatesEditingEnabled (WordPress/gutenberg#81058)
- [WP 7.1] Background: Fix the legacy gradient UI where a gradient cannot be selected (WordPress/gutenberg#81059)
- Views: honor developer-defined view config overrides (WordPress/gutenberg#80832)
- Playlist: Add track icon (WordPress/gutenberg#81078)
- Remove the CODEOWNERS file from wp/7.1. (WordPress/gutenberg#81104)
- Notes: Email users mentioned in a note (WordPress/gutenberg#79606)
- Backport 81068 80744 80642 (WordPress/gutenberg#81135)
- Site Editor: Add E2E coverage for view config extensibility (WordPress/gutenberg#80577)
- change from WordPress/gutenberg#81068 (WordPress/gutenberg#81140)
- Link Control: Restore the preview title underline (WordPress/gutenberg#81083)
- Button: Suppress UA focus ring when focused and pressed (WordPress/gutenberg#81113)
- View config: add reference docs (WordPress/gutenberg#81149)
- Editor: Fix document tools button focus ring (WordPress/gutenberg#81115)
- Interface: Increase footer breadcrumb height to prevent focus ring clipping (WordPress/gutenberg#81156)
- Post editor: Add ThemeProvider for admin color schemes (WordPress/gutenberg#81112)
- Pass Playlist controls to track blocks (WordPress/gutenberg#81158)
- Theme: Omit color properties when neither provided nor inherited (WordPress/gutenberg#80600) (WordPress/gutenberg#81172)
- Media: Improve the HEIC upload error and keep any upload errors up until dismissed (WordPress/gutenberg#81130)
- Video: Hide settings for the GIF variation (WordPress/gutenberg#81142)
- Video: clarify the Video variation description (WordPress/gutenberg#81181)
- Button: turn on the width setting by default in theme.json (WordPress/gutenberg#81196)
- Edit Widgets: Fix header toolbar button focus ring (WordPress/gutenberg#81176)
- Build: Wrap script bundles in an IIFE to contain 'use strict' (WordPress/gutenberg#79792)
- Customizer widgets: Add ThemeProvider for admin color schemes (WordPress/gutenberg#81174)
- Fix: Tabs block: Start with empty tab labels with placeholders (WordPress/gutenberg#81197)
- PanelColorSettings: Restore the missing space below the panel header (WordPress/gutenberg#81155)
- Visual revisions: add shareable urls (WordPress/gutenberg#81205)
- Notes: fix the mention notification email composition (WordPress/gutenberg#81187)
- Fix ESLint warnings for 'navigateRegionsProps' spread (WordPress/gutenberg#81208)
- Widgets editor: Add ThemeProvider for admin color schemes (WordPress/gutenberg#81173)
- Remove the editableRoot opt-in from the paragraph block (WordPress/gutenberg#81184)
- Media Attached to: Fix issue with the popover unexpectedly flipping, tweak wording (WordPress/gutenberg#81206)
- Ensure device preview is always accurate when window is zoomed in (WordPress/gutenberg#81215)

Props wildworks.
See #65529.
Built from https://develop.svn.wordpress.org/trunk@63026


git-svn-id: http://core.svn.wordpress.org/trunk@62245 1a063a9b-81f0-0310-95a4-ce76da25c4cd
DanielHudson2 pushed a commit to DanielHudson2/wordpress-develop that referenced this pull request Aug 10, 2026
Introduce `wp_notify_note_mentions()` on `rest_insert_comment`, alongside the existing post author notification, which parses those IDs out of the saved note and emails each mentioned user in their own locale with a link back to the post editor.

Recipients are limited to users who can `edit_comment` the note, matching `WP_REST_Comments_Controller::check_read_permission()`, so an email cannot carry note content to someone who cannot see the note in the editor. The note's own author is skipped, as is the post author, who `wp_new_comment_via_rest_notify_postauthor()` already notifies about every note. Only note creation notifies, and the existing `wp_notes_notify` option turns the whole path off.

See related Gutenberg pull request: WordPress/gutenberg#79606.

Follow-up to [62832].

Props westonruter, mamaduka.
Fixes #65639.



git-svn-id: https://develop.svn.wordpress.org/trunk@63012 602fd350-edb4-49c9-b593-d223f7449a82
DanielHudson2 pushed a commit to DanielHudson2/wordpress-develop that referenced this pull request Aug 10, 2026
This updates the pinned commit hash of the Gutenberg repository from `fd715a6833679d098d9fee84b642f8f1bc27341b` to `f05e40e91c54f29c449b1f33d0db89f5166812d9`.

A full list of changes included in this commit can be found on GitHub: 
WordPress/gutenberg@fd715a6...f05e40e

- Writing flow: forward delete an empty paragraph without breaking apart the next block (WordPress/gutenberg#80813)
- Upload Media: Fail the item when the /finalize request fails (WordPress/gutenberg#80725)
- Fix template `modified` and `date` return value for file templates (WordPress/gutenberg#80733)
- Boot: Adjust specificity of the image reset styles so components can size their own images (WordPress/gutenberg#80845)
- Quote: Ensure paragraph placeholder appears after deleting nested blocks (WordPress/gutenberg#77151)
- Block editor: make the Group action wrap blocks with a group transform (WordPress/gutenberg#80891)
- Copy: preserve the block when its entire text is selected (WordPress/gutenberg#80994)
- Add opt-out for block style state controls (WordPress/gutenberg#80956) (WordPress/gutenberg#81004)
- Tabs: Support Home and End keys for keyboard navigation (WordPress/gutenberg#80912)
- Rename blockStatesEnabled setting to blockStatesEditingEnabled (WordPress/gutenberg#81058)
- [WP 7.1] Background: Fix the legacy gradient UI where a gradient cannot be selected (WordPress/gutenberg#81059)
- Views: honor developer-defined view config overrides (WordPress/gutenberg#80832)
- Playlist: Add track icon (WordPress/gutenberg#81078)
- Remove the CODEOWNERS file from wp/7.1. (WordPress/gutenberg#81104)
- Notes: Email users mentioned in a note (WordPress/gutenberg#79606)
- Backport 81068 80744 80642 (WordPress/gutenberg#81135)
- Site Editor: Add E2E coverage for view config extensibility (WordPress/gutenberg#80577)
- change from WordPress/gutenberg#81068 (WordPress/gutenberg#81140)
- Link Control: Restore the preview title underline (WordPress/gutenberg#81083)
- Button: Suppress UA focus ring when focused and pressed (WordPress/gutenberg#81113)
- View config: add reference docs (WordPress/gutenberg#81149)
- Editor: Fix document tools button focus ring (WordPress/gutenberg#81115)
- Interface: Increase footer breadcrumb height to prevent focus ring clipping (WordPress/gutenberg#81156)
- Post editor: Add ThemeProvider for admin color schemes (WordPress/gutenberg#81112)
- Pass Playlist controls to track blocks (WordPress/gutenberg#81158)
- Theme: Omit color properties when neither provided nor inherited (WordPress/gutenberg#80600) (WordPress/gutenberg#81172)
- Media: Improve the HEIC upload error and keep any upload errors up until dismissed (WordPress/gutenberg#81130)
- Video: Hide settings for the GIF variation (WordPress/gutenberg#81142)
- Video: clarify the Video variation description (WordPress/gutenberg#81181)
- Button: turn on the width setting by default in theme.json (WordPress/gutenberg#81196)
- Edit Widgets: Fix header toolbar button focus ring (WordPress/gutenberg#81176)
- Build: Wrap script bundles in an IIFE to contain 'use strict' (WordPress/gutenberg#79792)
- Customizer widgets: Add ThemeProvider for admin color schemes (WordPress/gutenberg#81174)
- Fix: Tabs block: Start with empty tab labels with placeholders (WordPress/gutenberg#81197)
- PanelColorSettings: Restore the missing space below the panel header (WordPress/gutenberg#81155)
- Visual revisions: add shareable urls (WordPress/gutenberg#81205)
- Notes: fix the mention notification email composition (WordPress/gutenberg#81187)
- Fix ESLint warnings for 'navigateRegionsProps' spread (WordPress/gutenberg#81208)
- Widgets editor: Add ThemeProvider for admin color schemes (WordPress/gutenberg#81173)
- Remove the editableRoot opt-in from the paragraph block (WordPress/gutenberg#81184)
- Media Attached to: Fix issue with the popover unexpectedly flipping, tweak wording (WordPress/gutenberg#81206)
- Ensure device preview is always accurate when window is zoomed in (WordPress/gutenberg#81215)

Props wildworks.
See #65529.

git-svn-id: https://develop.svn.wordpress.org/trunk@63026 602fd350-edb4-49c9-b593-d223f7449a82
shail-mehta pushed a commit that referenced this pull request Aug 12, 2026
Co-authored-by: talldan <talldanwp@git.wordpress.org>
Co-authored-by: adamsilverstein <adamsilverstein@git.wordpress.org>
Co-authored-by: westonruter <westonruter@git.wordpress.org>
Co-authored-by: Mamaduka <mamaduka@git.wordpress.org>
Co-authored-by: sethrubenstein <smrubenstein@git.wordpress.org>
@ArchitectOfRuin

ArchitectOfRuin commented Aug 23, 2026

Copy link
Copy Markdown

One concern with the current implementation is that wp_mail() is called synchronously for every mentioned user from the rest_insert_comment callback.

This means that a note mentioning many users can result in many mail operations being performed within the same REST request. A slow or failing mail transport can therefore make the request slow/fail after only part of the notification batch has been delivered.

There also doesn't appear to be persistent notification state/idempotency around these sends, so a client retry after a request timeout could potentially result in another notification batch.

Would it make sense to track this as a follow-up issue for asynchronous notification delivery, retry/backoff, and idempotency?

I think this would also make the implementation more resilient for sites with a larger number of eligible note recipients.

Another consideration is notification volume. The implementation deduplicates repeated mentions of the same user, which is good, but I don't see a notification-specific limit on the number of recipients or notification events generated by a user.

Even if the expected number of recipients is normally small, it may be worth considering a server-side bound or throttling mechanism so that notification volume cannot grow unexpectedly.

I suggest a follow-up issue covering:

  • asynchronous delivery of note notifications;
  • persistent notification delivery state;
  • retry with exponential backoff for transient mail failures;
  • idempotency to prevent duplicate notifications;
  • a reasonable upper bound/throttling for notification volume.

The current synchronous implementation seems acceptable as an initial compatibility implementation, but these would be useful safeguards for production deployments.

@adamsilverstein

Copy link
Copy Markdown
Member Author

One concern with the current implementation is that wp_mail() is called synchronously for every mentioned user from the rest_insert_comment callback.

@ArchitectOfRuin some improvements are certainly welcome here and probably an issue with your specific recommendations. Might be worth doing some research on previous efforts and also the discussion on the related effort to enable following/unfollowing discussion threads:

And:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backported to WP Core Pull request that has been successfully merged into WP Core [Feature] Notes Phase 3 of the Gutenberg roadmap around block commenting [Type] Feature New feature to highlight in changelogs.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Notify users when they are @mentioned in a note

8 participants