Skip to content

Commit 97f20f1

Browse files
committed
Fix false mention notifications for users in CC without mention tags
Users were incorrectly receiving mention email notifications when they appeared in the CC field of an activity but were not actually mentioned in the content. This commonly occurred when users followed an actor and were added to CC for federation purposes. The fix verifies that a user's actor ID appears in the activity's tag array with type "Mention" before sending a mention notification, rather than just checking if they're in the CC field. Changes: - Add validation in Mailer::mention() to check for actual mention tags - Add test case for users in CC without mention tags (should not notify) - Add test case for users properly mentioned (should notify) - Update existing test to include mention tags
1 parent b464f14 commit 97f20f1

File tree

2 files changed

+139
-2
lines changed

2 files changed

+139
-2
lines changed

includes/class-mailer.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,8 +345,13 @@ public static function mention( $activity, $user_ids ) {
345345
}
346346

347347
$actor_id = $actor->get_id();
348-
if ( \in_array( $actor_id, (array) $activity['cc'], true ) ) {
349-
$recipients[ $user_id ] = $actor_id;
348+
if ( \in_array( $actor_id, (array) $activity['cc'], true ) && isset( $activity['object']['tag'] ) ) {
349+
foreach ( (array) $activity['object']['tag'] as $tag ) {
350+
if ( object_to_uri( $tag ) === $actor_id ) {
351+
$recipients[ $user_id ] = $actor_id;
352+
break;
353+
}
354+
}
350355
}
351356
}
352357

tests/phpunit/tests/includes/class-test-mailer.php

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,13 @@ public function test_mention_with_array() {
852852
'object' => array(
853853
'id' => 'https://example.com/post/1',
854854
'content' => 'Test mention',
855+
'tag' => array(
856+
array(
857+
'type' => 'Mention',
858+
'href' => Actors::get_by_id( $user_id )->get_id(),
859+
'name' => '@test',
860+
),
861+
),
855862
),
856863
'cc' => array( Actors::get_by_id( $user_id )->get_id() ),
857864
);
@@ -978,6 +985,13 @@ public function test_mention_filters_recipients() {
978985
'object' => array(
979986
'id' => 'https://example.com/post/1',
980987
'content' => 'Test mention',
988+
'tag' => array(
989+
array(
990+
'type' => 'Mention',
991+
'href' => Actors::get_by_id( $user_id )->get_id(),
992+
'name' => '@test',
993+
),
994+
),
981995
),
982996
// Only user_id is in CC, not other_user_id.
983997
'cc' => array( Actors::get_by_id( $user_id )->get_id() ),
@@ -1018,4 +1032,122 @@ function ( $args ) use ( $user_id, $other_user_id ) {
10181032
\remove_all_filters( 'wp_mail' );
10191033
\wp_delete_user( $other_user_id );
10201034
}
1035+
1036+
/**
1037+
* Test that users in CC without actual mention tags do not receive mention notifications.
1038+
*
1039+
* This tests the bug fix where users added to CC (e.g., because they follow the actor)
1040+
* were incorrectly receiving mention notifications even when not actually mentioned.
1041+
*
1042+
* @covers ::mention
1043+
*/
1044+
public function test_mention_requires_tag_not_just_cc() {
1045+
$user_id = self::$user_id;
1046+
1047+
// Activity with user in CC but NOT mentioned in tags.
1048+
$activity = array(
1049+
'actor' => 'https://example.com/sports-account',
1050+
'object' => array(
1051+
'id' => 'https://example.com/sports-account/posts/123',
1052+
'type' => 'Note',
1053+
'content' => '<p>Join @user1 and @user2 on our stream...</p>',
1054+
'tag' => array(
1055+
// Other users mentioned, but NOT the local user.
1056+
array(
1057+
'type' => 'Mention',
1058+
'href' => 'https://example.com/user1',
1059+
'name' => 'user1@example.com',
1060+
),
1061+
array(
1062+
'type' => 'Mention',
1063+
'href' => 'https://example.com/user2',
1064+
'name' => 'user2@example.com',
1065+
),
1066+
),
1067+
),
1068+
// User is in CC (e.g., because they follow the actor).
1069+
'cc' => array( Actors::get_by_id( $user_id )->get_id() ),
1070+
);
1071+
1072+
// Mock remote metadata.
1073+
$metadata_filter = function () {
1074+
return array(
1075+
'name' => 'Sports Account',
1076+
'url' => 'https://example.com/sports-account',
1077+
);
1078+
};
1079+
\add_filter( 'pre_get_remote_metadata_by_actor', $metadata_filter );
1080+
1081+
$mock = new \MockAction();
1082+
\add_filter( 'wp_mail', array( $mock, 'filter' ), 1 );
1083+
1084+
// Trigger mention notification.
1085+
Mailer::mention( $activity, $user_id );
1086+
1087+
// Should NOT send any email because user is not actually mentioned in tags.
1088+
$this->assertEquals( 0, $mock->get_call_count(), 'User in CC without mention tag should not receive notification' );
1089+
1090+
// Clean up.
1091+
\remove_filter( 'pre_get_remote_metadata_by_actor', $metadata_filter );
1092+
\remove_filter( 'wp_mail', array( $mock, 'filter' ), 1 );
1093+
}
1094+
1095+
/**
1096+
* Test that users with actual mention tags DO receive mention notifications.
1097+
*
1098+
* @covers ::mention
1099+
*/
1100+
public function test_mention_with_tag_sends_notification() {
1101+
$user_id = self::$user_id;
1102+
1103+
// Activity with user properly mentioned in both CC and tags.
1104+
$activity = array(
1105+
'actor' => 'https://example.com/author',
1106+
'object' => array(
1107+
'id' => 'https://example.com/post/1',
1108+
'type' => 'Note',
1109+
'content' => '<p>Hello @testuser, how are you?</p>',
1110+
'tag' => array(
1111+
array(
1112+
'type' => 'Mention',
1113+
'href' => Actors::get_by_id( $user_id )->get_id(),
1114+
'name' => '@testuser',
1115+
),
1116+
),
1117+
),
1118+
'cc' => array( Actors::get_by_id( $user_id )->get_id() ),
1119+
);
1120+
1121+
// Mock remote metadata.
1122+
$metadata_filter = function () {
1123+
return array(
1124+
'name' => 'Test Author',
1125+
'url' => 'https://example.com/author',
1126+
);
1127+
};
1128+
\add_filter( 'pre_get_remote_metadata_by_actor', $metadata_filter );
1129+
1130+
$mock = new \MockAction();
1131+
\add_filter( 'wp_mail', array( $mock, 'filter' ), 1 );
1132+
1133+
// Capture email.
1134+
$mail_filter = function ( $args ) use ( $user_id ) {
1135+
$this->assertStringContainsString( 'Mention', $args['subject'] );
1136+
$this->assertStringContainsString( 'Test Author', $args['subject'] );
1137+
$this->assertEquals( \get_user_by( 'id', $user_id )->user_email, $args['to'] );
1138+
return $args;
1139+
};
1140+
\add_filter( 'wp_mail', $mail_filter );
1141+
1142+
// Trigger mention notification.
1143+
Mailer::mention( $activity, $user_id );
1144+
1145+
// Should send 1 email because user is properly mentioned.
1146+
$this->assertEquals( 1, $mock->get_call_count(), 'User properly mentioned in tags should receive notification' );
1147+
1148+
// Clean up.
1149+
\remove_filter( 'pre_get_remote_metadata_by_actor', $metadata_filter );
1150+
\remove_filter( 'wp_mail', array( $mock, 'filter' ), 1 );
1151+
\remove_filter( 'wp_mail', $mail_filter );
1152+
}
10211153
}

0 commit comments

Comments
 (0)