Skip to content

Comments: Support Notes @mentions: kses allowance and notifications#12503

Open
adamsilverstein wants to merge 8 commits into
WordPress:trunkfrom
adamsilverstein:add/notes-mention-kses
Open

Comments: Support Notes @mentions: kses allowance and notifications#12503
adamsilverstein wants to merge 8 commits into
WordPress:trunkfrom
adamsilverstein:add/notes-mention-kses

Conversation

@adamsilverstein

@adamsilverstein adamsilverstein commented Jul 13, 2026

Copy link
Copy Markdown
Member

Backports the PHP changes from the Gutenberg Notes @mention work: the mention kses allowance (WordPress/gutenberg#79604, WordPress/gutenberg#80221) and the mention/follower notifications (WordPress/gutenberg#79606).

What

Mention markup survives sanitization (kses)

Allows the note mention markup (a class attribute on links) in comment content, scoped to note comments only:

  • _wp_kses_allow_note_mention_attributes() (new, private) extends the pre_comment_content allowlist with class on a elements.
  • wp_filter_comment() attaches it immediately before applying the pre_comment_content filter and removes it immediately after, and only when $commentdata['comment_type'] is note.

The pre_comment_content kses context itself is unchanged: wp_kses_allowed_html( 'pre_comment_content' ) still returns the default $allowedtags, and regular (including anonymous front-end) comments are sanitized exactly as before.

Because the scoping lives inside wp_filter_comment() - the single choke point both REST write paths pass through with comment_type already resolved - core does not need the separate REST-create arming fix the plugin shim required (WordPress/gutenberg#80221).

Mention and follower notifications

On rest_insert_comment for note comments (alongside the existing post-author notification), wp_notify_note_mentions():

  1. Parses mentions out of the saved content via wp_get_note_mentioned_user_ids(): only anchors carrying both the wp-note-mention class and a user-N class token are treated as mentions, so ordinary links cannot be used to address notifications.
  2. Notifies each mentioned user plus any existing followers of the thread, with a short email linking back to the post (wp_send_note_notification()).
  3. Subscribes the note author and everyone they mention to the thread. Followers are stored as one _wp_note_followers meta row per user on the thread's top-level note - so concurrent replies cannot clobber each other - and the meta is registered for REST (editable by users who can edit_comment the note) so follower management UI can build on it.

Audience rules:

  • The post author is excluded - wp_new_comment_via_rest_notify_postauthor() already notifies them of every note.
  • The note's own author is never notified about their own note.
  • Recipients are limited to users who can edit_comment the note, matching WP_REST_Comments_Controller::check_read_permission() for notes, so emails cannot leak note content to users who cannot see the note in the editor.
  • Everything honors the existing wp_notes_notify option.

New filters: wp_note_notification_recipients, wp_note_notification_subject, wp_note_notification_text.

Why

The Notes @mention completer stores a mention as a link to the mentioned user's author page, <a class="wp-note-mention user-N" href="…">@Name</a>, the user-N class carrying the mentioned user's ID. The default comment kses allowlist ($allowedtags) permits a with only href and title, so for users without unfiltered_html the mention classes are stripped when the note is saved.

An earlier revision of this PR added the attributes to the pre_comment_content context unconditionally. That loosened sanitization for every comment on every site: class is a CSS/JS selector hook, so any commenter - including anonymous visitors - could publish text styled by theme classes (e.g. disguised as a button) or reachable by delegated script handlers. Scoping the allowance to the moment a note comment passes through wp_filter_comment() avoids that: notes can only be written by logged-in users with edit_post on the target post and never render on the front end.

Mentioning a collaborator in a note is only useful if they hear about it: the notification half closes that loop, and the follower model keeps everyone who has participated in a thread informed of later replies without requiring them to be re-mentioned.

Testing

Unit tests in tests/phpunit/tests/kses.php:

  • test_wp_kses_allowed_html_pre_comment_content_disallows_mention_attributes verifies the pre_comment_content context still returns the stock $allowedtags.
  • test_note_mention_allows_only_class_on_note_links verifies every attribute other than class and the default link attributes (e.g. onclick, style, data-*) is still stripped from note links.
  • test_note_mention_markup_stripped_from_regular_comment_content verifies the class is stripped from the same markup in a regular comment.
  • test_note_mention_allowance_does_not_leak_after_note_filtering verifies a regular comment filtered after a note gets the default rules and the allowlist is restored.

Unit tests in tests/phpunit/tests/comment/wpNotifyNoteMentions.php cover mention parsing (plain links ignored, IDs deduped), thread-root resolution, the mentioned-user email + auto-subscribe, author self-exclusion, post-author exclusion, capability gating (a mentioned subscriber is not emailed), follower-of-reply notification, the wp_notes_notify off switch, the recipients filter, follower removal, and the REST meta registration.

npm run test:php -- --group notes

Trac ticket: https://core.trac.wordpress.org/ticket/65622

The notes @-mention completer stores a mention as
`<a class="wp-note-mention" data-user-id="N" href="...">@name</a>`.
The default comment kses allowlist only keeps `href` and `title` on
links, so for users without `unfiltered_html` the attributes that make
a mention a mention (the chip class and the mentioned user's ID) are
stripped on save.

Add a 'pre_comment_content' context to wp_kses_allowed_html() that
allows `class` and `data-user-id` on links so saved mentions survive
sanitization. Both attributes are inert markup.

Backports the PHP changes from the Gutenberg mentions PR:
WordPress/gutenberg#79604
@github-actions

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props adamsilverstein.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@github-actions

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

Comment thread src/wp-includes/kses.php Outdated
Allowing class and data-user-id on links in the pre_comment_content
context loosened sanitization for every comment, including anonymous
front-end comments. Both attributes are CSS/JS selector hooks, so that
would let any commenter publish links styled by theme classes or
reachable by delegated script handlers.

Attach the extended allowlist in wp_filter_comment() only while a note
comment's content is being filtered instead. Notes can only be written
by logged-in users who can edit the post and never render on the front
end, and the sanitization of regular comments is unchanged.
Design review on the Gutenberg side changed the stored mention from a
link to a plain span, since a mention marks a person rather than
offering navigation. Allow span.class and span.data-user-id instead of
the link attributes; the allowance is still attached only while a note
comment is filtered.
… attribute

The notes mention completer now stores a mention as
`<span class="wp-note-mention user-N">@name</span>`, so the kses
allowance for note comments shrinks to the single `class` attribute
and no longer needs `data-user-id`. Add a test asserting that any
other attribute is still stripped from note spans.

Trac ticket: https://core.trac.wordpress.org/ticket/65622
@adamsilverstein adamsilverstein force-pushed the add/notes-mention-kses branch from 1fc1250 to f74c68b Compare July 13, 2026 18:26
Mentions are now inserted as links to the mentioned user's author page
- <a class="wp-note-mention user-N" href="..."> - instead of spans, so
the note allowance moves from span.class to a.class. Note content now
also picks up wp_rel_ugc()'s rel="nofollow ugc" like any other comment
link, which the tests cover with a deterministic external href.
@adamsilverstein adamsilverstein self-assigned this Jul 14, 2026
@adamsilverstein adamsilverstein added the Gutenberg Sync Pull requests syncing changes from WordPress/Gutenberg. label Jul 14, 2026
Port the notification layer from the Gutenberg Notes @mention work
(WordPress/gutenberg#79606). Parse user-N mention classes out of saved
note content, email mentioned users and existing thread followers on
rest_insert_comment, and subscribe the note author and mentioned users
to the thread via _wp_note_followers comment meta (one row per user so
concurrent replies cannot clobber each other). The post author is
excluded because wp_new_comment_via_rest_notify_postauthor() already
notifies them, and recipients are limited to users who can edit_comment
the note, matching the REST API's read permission for notes.
@adamsilverstein adamsilverstein requested a review from t-hamano July 14, 2026 15:51
@adamsilverstein adamsilverstein changed the title Comments: allow note mention attributes in comment content Comments: Support Notes @mentions: kses allowance and notifications Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Gutenberg Sync Pull requests syncing changes from WordPress/Gutenberg.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant