Comments: Notes thread followers, and notifications when a thread is resolved or reopened - #13026
Conversation
…them of replies. Notes reach only the post author, who is emailed about every note, and users named in one specific note. Reply to a thread and the people already in it hear nothing unless the reply mentions them by name. Subscribes the users who start, reply to, or are mentioned in a thread as followers of its top-level note, stored one meta row per user so concurrent replies cannot lose an update, and emails them about later replies. Every email carries a tokenized unfollow link that works logged out. Bookkeeping is not gated on the notification option, so enabling notifications later works for threads that already exist.
…eopened. Resolving or reopening a thread posts a child note carrying the new status as comment meta. That note is bookkeeping rather than conversation, and the resolve one has no content at all, so the post author was emailed a generic new-note message with an empty body and followers heard nothing about the outcome. Announces the event to the thread's followers and the post author, and leaves the generic email alone for that note. The controller saves comment meta between rest_insert_comment and rest_after_insert_comment, so the notifier runs on the later action where the status is stored, while the earlier handlers recognize the note from the request.
Test using WordPress PlaygroundThe 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
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
|
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 Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Trac Ticket MissingThis pull request is missing a link to a Trac ticket. For a contribution to be considered, there must be a corresponding ticket in Trac. To attach a pull request to a Trac ticket, please include the ticket's full URL in your pull request description. More information about contributing to WordPress on GitHub can be found in the Core Handbook. |
Backports the Notes thread followers and resolution notification layers from Gutenberg, building on the mention notifications that landed in 7.1 (#12548, Trac #65639).
What
Two audiences that Notes currently has no way to reach.
Thread followers. Start a thread, reply to it, or get mentioned in it, and you are subscribed to it. Later replies email you, even when they do not name you. Today only the post author (a generic email for every note) and users named in one specific note (the mention email) hear anything at all, so the people already in a conversation miss its replies.
Resolution events. Resolving or reopening a thread posts a child note carrying
_wp_note_statusmeta. That note is bookkeeping, not conversation - the resolve one has no content whatsoever - but the post author is currently emailed the generic "a new note was added" message for it, with an empty body. Followers and the post author now get copy about the event instead, and the generic emails are suppressed for that note.How
Everything lands in
wp-includes/comment.phpnext to the existing note notification functions, hooked fromdefault-filters.php.rest_insert_commentwp_route_post_author_mention_notification()notify_post_author, the filter it already consults.wp_notify_note_mentions()wp_notify_note_followers()wp_notify_new_mentions_on_note_update()wp_maintain_note_followers()Resolution events run on
rest_after_insert_comment(wp_notify_note_event()).WP_REST_Comments_Controllersaves comment meta between the two REST comment actions, so on the later one_wp_note_statusis stored and the event is recognized whatever the client sent. For the same reasonwp_get_note_status_event()consults the request first and the stored meta second, which is what lets the earlier hooks recognize a system note before its meta exists.Followers are stored as one
_wp_note_followerscomment-meta row per user on the thread's top-level note, so concurrent replies subscribe users independently instead of racing over a single array value. Bookkeeping is deliberately not gated onwp_notes_notify: that option governs whether email is sent, not who is participating, so turning notifications on later works for threads that already exist.Every email carries a tokenized unfollow link -
wp_hash()over thread and user, compared withhash_equals(), handled throughadmin-post.phpincluding thenoprivvariant so it works logged out, like any email unsubscribe. It never expires, so links in old email keep working.One email per user per note insert
Security
Every recipient must pass
user_can( $user_id, 'edit_comment', $note_id )before any note content is composed for them. That is the barWP_REST_Comments_Controller::check_read_permission()already applies to reading a note; aread_postcheck would leak internal notes to, say, subscribers on a public post. Emails pinContent-Type: text/plainexplicitly so a filtered default cannot turn a body into HTML, compose in the recipient's locale, and build the editor link as the recipient.Extensibility
wp_note_notification_sentfires once per addressed recipient with the user ID, the note, why they were notified (mention,post_author_mention,follower,resolved,reopen), and whetherwp_mail()accepted it. Email is the only channel here; this is the seam another channel - or a future notifications API - can consume without reimplementing recipient resolution. Recipient, subject, and body filters are provided per notification type.Testing instructions
tests/phpunit/tests/comment/wpNotifyNoteFollowers.phpandwpNotifyNoteEvent.phpcover subscription bookkeeping, the dedupe rules, the visibility gate, the unfollow token, the meta-timing behavior, and the suppression of both generic emails. The event tests dispatch real REST requests so the controller's own hook order and meta save are part of what is being tested.Open question
wp_new_comment_via_rest_notify_postauthor()gains optional$requestand$creatingparameters here so it can recognize a thread-event note and step aside. The alternative is leaving it untouched and suppressing it through thenotify_post_authorfilter, which is what the Gutenberg plugin does, since it has to work on older versions. The direct check reads better in core, but happy to switch if the added parameters are unwelcome on an existing function.