Skip to content

Commit b1b2cf6

Browse files
authored
Merge pull request #7983 from nextcloud/fix/comment_mention_outsiders_notifications
fix(notifciations)
2 parents ff1427b + 5d1cbbb commit b1b2cf6

2 files changed

Lines changed: 92 additions & 8 deletions

File tree

lib/Notification/NotificationHelper.php

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -176,12 +176,28 @@ public function sendBoardShared(int $boardId, Acl $acl, bool $markAsRead = false
176176
}
177177

178178
public function sendMention(IComment $comment): void {
179-
foreach ($comment->getMentions() as $mention) {
180-
if ((string)$mention['id'] === $this->userId) {
179+
$mentions = $comment->getMentions();
180+
if (empty($mentions)) {
181+
return;
182+
}
183+
184+
$card = $this->cardMapper->find($comment->getObjectId());
185+
$boardId = $this->cardMapper->findBoardId($card->getId());
186+
$boardUsers = $this->permissionService->findUsers($boardId);
187+
188+
foreach ($mentions as $mention) {
189+
if (($mention['type'] ?? 'users') !== 'users') {
190+
// skip non-user mentions
191+
continue;
192+
}
193+
$mentionedUserId = (string)$mention['id'];
194+
if ($mentionedUserId === $this->userId) {
195+
continue;
196+
}
197+
if (!array_key_exists($mentionedUserId, $boardUsers)) {
198+
// skip users that don't have access to the board
181199
continue;
182200
}
183-
$card = $this->cardMapper->find($comment->getObjectId());
184-
$boardId = $this->cardMapper->findBoardId($card->getId());
185201
$notification = $this->notificationManager->createNotification();
186202
$notification
187203
->setApp('deck')

tests/unit/Notification/NotificationHelperTest.php

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -489,8 +489,8 @@ public function testSendMention() {
489489
$comment->expects($this->once())
490490
->method('getMentions')
491491
->willReturn([
492-
['id' => 'user1'],
493-
['id' => 'user2']
492+
['id' => 'user1', 'type' => 'users'],
493+
['id' => 'user2', 'type' => 'users'],
494494
]);
495495
$card = new Card();
496496
$card->setId(123);
@@ -503,6 +503,13 @@ public function testSendMention() {
503503
->method('findBoardId')
504504
->with(123)
505505
->willReturn(1);
506+
$this->permissionService->expects($this->once())
507+
->method('findUsers')
508+
->with(1)
509+
->willReturn([
510+
'user1' => $this->createUserMock('user1'),
511+
'user2' => $this->createUserMock('user2'),
512+
]);
506513

507514
$notification1 = $this->createMock(INotification::class);
508515
$notification1->expects($this->once())->method('setApp')->with('deck')->willReturn($notification1);
@@ -543,8 +550,8 @@ public function testSendMentionSkipsSelf() {
543550
$comment->expects($this->once())
544551
->method('getMentions')
545552
->willReturn([
546-
['id' => 'admin'],
547-
['id' => 'user1']
553+
['id' => 'admin', 'type' => 'users'],
554+
['id' => 'user1', 'type' => 'users'],
548555
]);
549556
$card = new Card();
550557
$card->setId(123);
@@ -557,6 +564,13 @@ public function testSendMentionSkipsSelf() {
557564
->method('findBoardId')
558565
->with(123)
559566
->willReturn(1);
567+
$this->permissionService->expects($this->once())
568+
->method('findUsers')
569+
->with(1)
570+
->willReturn([
571+
'admin' => $this->createUserMock('admin'),
572+
'user1' => $this->createUserMock('user1'),
573+
]);
560574

561575
// Only one notification should be created — for user1, not for admin
562576
$notification = $this->createMock(INotification::class);
@@ -643,4 +657,58 @@ public function testSendBoardSharedSelfMarkAsReadStillCleansUp() {
643657

644658
$this->notificationHelper->sendBoardShared(123, $acl, true);
645659
}
660+
661+
/**
662+
* A mention of a user who is not a board member must not trigger a notification.
663+
*/
664+
public function testSendMentionSkipsNonBoardMembers(): void {
665+
$comment = $this->createMock(IComment::class);
666+
$comment->expects($this->any())
667+
->method('getObjectId')
668+
->willReturn(123);
669+
$comment->expects($this->any())
670+
->method('getMessage')
671+
->willReturn('@user1 @outsider This is a message.');
672+
$comment->expects($this->once())
673+
->method('getMentions')
674+
->willReturn([
675+
['id' => 'user1', 'type' => 'users'],
676+
['id' => 'outsider', 'type' => 'users'],
677+
]);
678+
679+
$card = new Card();
680+
$card->setId(123);
681+
$card->setTitle('MyCard');
682+
$this->cardMapper->expects($this->once())
683+
->method('find')
684+
->with(123)
685+
->willReturn($card);
686+
$this->cardMapper->expects($this->once())
687+
->method('findBoardId')
688+
->with(123)
689+
->willReturn(1);
690+
// Only user1 is a board member; outsider is not
691+
$this->permissionService->expects($this->once())
692+
->method('findUsers')
693+
->with(1)
694+
->willReturn([
695+
'user1' => $this->createUserMock('user1'),
696+
]);
697+
698+
// Exactly one notification must be created and sent — for user1 only
699+
$notification = $this->createMock(INotification::class);
700+
$notification->expects($this->once())->method('setApp')->with('deck')->willReturn($notification);
701+
$notification->expects($this->once())->method('setUser')->with('user1')->willReturn($notification);
702+
$notification->expects($this->once())->method('setObject')->with('card', 123)->willReturn($notification);
703+
$notification->expects($this->once())->method('setSubject')->with('card-comment-mentioned', ['MyCard', 1, 'admin'])->willReturn($notification);
704+
$notification->expects($this->once())->method('setDateTime')->willReturn($notification);
705+
706+
$this->notificationManager->expects($this->once())
707+
->method('createNotification')
708+
->willReturn($notification);
709+
$this->notificationManager->expects($this->once())
710+
->method('notify')
711+
->with($notification);
712+
$this->notificationHelper->sendMention($comment);
713+
}
646714
}

0 commit comments

Comments
 (0)