diff --git a/lib/Chat/AutoComplete/SearchPlugin.php b/lib/Chat/AutoComplete/SearchPlugin.php index 1ca840db782..72c8efbb471 100644 --- a/lib/Chat/AutoComplete/SearchPlugin.php +++ b/lib/Chat/AutoComplete/SearchPlugin.php @@ -32,11 +32,14 @@ use OCP\Collaboration\Collaborators\ISearchPlugin; use OCP\Collaboration\Collaborators\ISearchResult; use OCP\Collaboration\Collaborators\SearchResultType; +use OCP\IGroup; +use OCP\IGroupManager; use OCP\IL10N; use OCP\IUserManager; class SearchPlugin implements ISearchPlugin { protected IUserManager $userManager; + protected IGroupManager $groupManager; protected GuestManager $guestManager; protected TalkSession $talkSession; protected ParticipantService $participantService; @@ -47,6 +50,7 @@ class SearchPlugin implements ISearchPlugin { protected ?Room $room = null; public function __construct(IUserManager $userManager, + IGroupManager $groupManager, GuestManager $guestManager, TalkSession $talkSession, ParticipantService $participantService, @@ -54,6 +58,7 @@ public function __construct(IUserManager $userManager, ?string $userId, IL10N $l) { $this->userManager = $userManager; + $this->groupManager = $groupManager; $this->guestManager = $guestManager; $this->talkSession = $talkSession; $this->participantService = $participantService; @@ -82,7 +87,7 @@ public function search($search, $limit, $offset, ISearchResult $searchResult) { } } - $userIds = $guestAttendees = []; + $userIds = $groupIds = $guestAttendees = []; if ($this->room->getType() === Room::TYPE_ONE_TO_ONE) { // Add potential leavers of one-to-one rooms again. $participants = json_decode($this->room->getName(), true); @@ -97,11 +102,14 @@ public function search($search, $limit, $offset, ISearchResult $searchResult) { $guestAttendees[] = $attendee; } elseif ($attendee->getActorType() === Attendee::ACTOR_USERS) { $userIds[] = $attendee->getActorId(); + } elseif ($attendee->getActorType() === Attendee::ACTOR_GROUPS) { + $groupIds[] = $attendee->getActorId(); } } } $this->searchUsers($search, $userIds, $searchResult); + $this->searchGroups($search, $groupIds, $searchResult); $this->searchGuests($search, $guestAttendees, $searchResult); return false; @@ -157,6 +165,51 @@ protected function searchUsers(string $search, array $userIds, ISearchResult $se $searchResult->addResultSet($type, $matches, $exactMatches); } + protected function searchGroups(string $search, array $groupIds, ISearchResult $searchResult): void { + $search = strtolower($search); + + $type = new SearchResultType('groups'); + + $matches = $exactMatches = []; + foreach ($groupIds as $groupId) { + if ($searchResult->hasResult($type, $groupId)) { + continue; + } + + if ($search === '') { + $matches[] = $this->createGroupResult($groupId); + continue; + } + + if (strtolower($groupId) === $search) { + $exactMatches[] = $this->createGroupResult($groupId); + continue; + } + + if (stripos($groupId, $search) !== false) { + $matches[] = $this->createGroupResult($groupId); + continue; + } + + $group = $this->groupManager->get($groupId); + if (!$group instanceof IGroup) { + continue; + } + + if (strtolower($group->getDisplayName()) === $search) { + $exactMatches[] = $this->createGroupResult($group->getGID(), $group->getDisplayName()); + continue; + } + + if (stripos($group->getDisplayName(), $search) !== false) { + $matches[] = $this->createGroupResult($group->getGID(), $group->getDisplayName()); + continue; + } + } + + $searchResult->addResultSet($type, $matches, $exactMatches); + } + /** * @param string $search * @param Attendee[] $attendees @@ -218,6 +271,25 @@ protected function createResult(string $type, string $uid, string $name): array ]; } + protected function createGroupResult(string $groupId, string $name = ''): array { + if ($name === '') { + $group = $this->groupManager->get($groupId); + if ($group instanceof IGroup) { + $name = $group->getDisplayName(); + } else { + $name = $groupId; + } + } + + return [ + 'label' => $name, + 'value' => [ + 'shareType' => 'group', + 'shareWith' => 'group/' . $groupId, + ], + ]; + } + protected function createGuestResult(string $actorId, string $name): array { return [ 'label' => $name, diff --git a/lib/Chat/Notifier.php b/lib/Chat/Notifier.php index 82067338ea6..4bbc4e53d45 100644 --- a/lib/Chat/Notifier.php +++ b/lib/Chat/Notifier.php @@ -36,6 +36,8 @@ use OCP\AppFramework\Utility\ITimeFactory; use OCP\Comments\IComment; use OCP\IConfig; +use OCP\IGroup; +use OCP\IGroupManager; use OCP\IUserManager; use OCP\Notification\IManager as INotificationManager; use OCP\Notification\INotification; @@ -50,6 +52,7 @@ class Notifier { private INotificationManager $notificationManager; private IUserManager $userManager; + private IGroupManager $groupManager; private ParticipantService $participantService; private Manager $manager; private IConfig $config; @@ -58,6 +61,7 @@ class Notifier { public function __construct(INotificationManager $notificationManager, IUserManager $userManager, + IGroupManager $groupManager, ParticipantService $participantService, Manager $manager, IConfig $config, @@ -65,6 +69,7 @@ public function __construct(INotificationManager $notificationManager, Util $util) { $this->notificationManager = $notificationManager; $this->userManager = $userManager; + $this->groupManager = $groupManager; $this->participantService = $participantService; $this->manager = $manager; $this->config = $config; @@ -84,9 +89,9 @@ public function __construct(INotificationManager $notificationManager, * @param Room $chat * @param IComment $comment * @param array[] $alreadyNotifiedUsers - * @psalm-param array $alreadyNotifiedUsers + * @psalm-param array $alreadyNotifiedUsers * @return string[] Users that were mentioned - * @psalm-return array + * @psalm-return array */ public function notifyMentionedUsers(Room $chat, IComment $comment, array $alreadyNotifiedUsers, bool $silent): array { $usersToNotify = $this->getUsersToNotify($chat, $comment, $alreadyNotifiedUsers); @@ -98,13 +103,22 @@ public function notifyMentionedUsers(Room $chat, IComment $comment, array $alrea $shouldFlush = false; if (!$silent) { $notification = $this->createNotification($chat, $comment, 'mention'); + $parameters = $notification->getSubjectParameters(); $shouldFlush = $this->notificationManager->defer(); } + foreach ($usersToNotify as $mentionedUser) { if ($this->shouldMentionedUserBeNotified($mentionedUser['id'], $comment, $chat, $mentionedUser['attendee'] ?? null)) { if (!$silent) { $notification->setUser($mentionedUser['id']); + if (isset($mentionedUser['reason'])) { + $notification->setSubject('mention_' . $mentionedUser['reason'], array_merge($parameters, [ + 'sourceId' => $mentionedUser['sourceId'] ?? null, + ])); + } else { + $notification->setSubject('mention', $parameters); + } $this->notificationManager->notify($notification); } $alreadyNotifiedUsers[] = $mentionedUser; @@ -122,30 +136,31 @@ public function notifyMentionedUsers(Room $chat, IComment $comment, array $alrea * @param Room $chat * @param IComment $comment * @param array $alreadyNotifiedUsers - * @psalm-param array $alreadyNotifiedUsers + * @psalm-param array $alreadyNotifiedUsers * @return array - * @psalm-return array + * @psalm-return array */ private function getUsersToNotify(Room $chat, IComment $comment, array $alreadyNotifiedUsers): array { $usersToNotify = $this->getMentionedUsers($comment); - $usersToNotify = $this->removeAlreadyNotifiedUsers($usersToNotify, $alreadyNotifiedUsers); + $usersToNotify = $this->getMentionedGroupMembers($chat, $comment, $usersToNotify); $usersToNotify = $this->addMentionAllToList($chat, $usersToNotify); + $usersToNotify = $this->removeAlreadyNotifiedUsers($usersToNotify, $alreadyNotifiedUsers); return $usersToNotify; } /** * @param array $usersToNotify - * @psalm-param array $usersToNotify + * @psalm-param array $usersToNotify * @param array $alreadyNotifiedUsers - * @psalm-param array $alreadyNotifiedUsers + * @psalm-param array $alreadyNotifiedUsers * @return array - * @psalm-return array + * @psalm-return array */ private function removeAlreadyNotifiedUsers(array $usersToNotify, array $alreadyNotifiedUsers): array { return array_filter($usersToNotify, static function (array $userToNotify) use ($alreadyNotifiedUsers): bool { foreach ($alreadyNotifiedUsers as $alreadyNotified) { - if ($alreadyNotified === $userToNotify) { + if ($alreadyNotified['id'] === $userToNotify['id'] && $alreadyNotified['type'] === $userToNotify['type']) { return false; } } @@ -156,13 +171,13 @@ private function removeAlreadyNotifiedUsers(array $usersToNotify, array $already /** * @param Room $chat * @param array $list - * @psalm-param array $list + * @psalm-param array $list * @return array - * @psalm-return array + * @psalm-return array */ private function addMentionAllToList(Room $chat, array $list): array { - $usersToNotify = array_filter($list, static function (array $user): bool { - return $user['id'] !== 'all'; + $usersToNotify = array_filter($list, static function (array $entry): bool { + return $entry['type'] !== Attendee::ACTOR_USERS || $entry['id'] !== 'all'; }); if (count($list) === count($usersToNotify)) { @@ -182,6 +197,7 @@ private function addMentionAllToList(Room $chat, array $list): array { 'id' => $attendee->getActorId(), 'type' => $attendee->getActorType(), 'attendee' => $attendee, + 'reason' => 'all', ]; } @@ -202,7 +218,7 @@ private function addMentionAllToList(Room $chat, array $list): array { * @param IComment $replyTo * @param bool $silent * @return array[] Actor that was replied to - * @psalm-return array + * @psalm-return array */ public function notifyReplyToAuthor(Room $chat, IComment $comment, IComment $replyTo, bool $silent): array { if ($replyTo->getActorType() !== Attendee::ACTOR_USERS) { @@ -224,6 +240,7 @@ public function notifyReplyToAuthor(Room $chat, IComment $comment, IComment $rep [ 'id' => $replyTo->getActorId(), 'type' => $replyTo->getActorType(), + 'reason' => 'reply', ], ]; } @@ -241,7 +258,7 @@ public function notifyReplyToAuthor(Room $chat, IComment $comment, IComment $rep * @param IComment $comment * @param array[] $alreadyNotifiedUsers * @param bool $silent - * @psalm-param array $alreadyNotifiedUsers + * @psalm-param array $alreadyNotifiedUsers */ public function notifyOtherParticipant(Room $chat, IComment $comment, array $alreadyNotifiedUsers, bool $silent): void { if ($silent) { @@ -260,7 +277,7 @@ public function notifyOtherParticipant(Room $chat, IComment $comment, array $alr $this->notificationManager->notify($notification); } - // Also notify default participants in one2one chats or when the admin default is "always" + // Also notify default participants in one-to-one chats or when the admin default is "always" if ($this->getDefaultGroupNotification() === Participant::NOTIFY_ALWAYS || $chat->getType() === Room::TYPE_ONE_TO_ONE) { $participants = $this->participantService->getParticipantsByNotificationLevel($chat, Participant::NOTIFY_DEFAULT); foreach ($participants as $participant) { @@ -346,7 +363,7 @@ public function removePendingNotificationsForRoom(Room $chat, bool $chatOnly = f * Removes all the pending mention notifications for the room * * @param Room $chat - * @param string $userId + * @param ?string $userId */ public function markMentionNotificationsRead(Room $chat, ?string $userId): void { if ($userId === null || $userId === '') { @@ -383,7 +400,7 @@ public function getMentionedUserIds(IComment $comment): array { /** * @param IComment $comment * @return array[] - * @psalm-return array + * @psalm-return array */ private function getMentionedUsers(IComment $comment): array { $mentions = $comment->getMentions(); @@ -394,16 +411,75 @@ private function getMentionedUsers(IComment $comment): array { $mentionedUsers = []; foreach ($mentions as $mention) { - if ($mention['type'] === 'user') { - $mentionedUsers[] = [ - 'id' => $mention['id'], - 'type' => 'users' - ]; + if ($mention['type'] !== 'user') { + continue; } + + $mentionedUsers[] = [ + 'id' => $mention['id'], + 'type' => 'users', + 'reason' => 'direct', + ]; } return $mentionedUsers; } + /** + * @param Room $chat + * @param IComment $comment + * @param array $list + * @psalm-param array $list + * @return array[] + * @psalm-return array + */ + private function getMentionedGroupMembers(Room $chat, IComment $comment, array $list): array { + $mentions = $comment->getMentions(); + + if (empty($mentions)) { + return []; + } + + $alreadyMentionedUserIds = array_filter( + array_map(static fn (array $entry) => $entry['type'] === Attendee::ACTOR_USERS ? $entry['id'] : null, $list), + static fn ($userId) => $userId !== null + ); + $alreadyMentionedUserIds = array_flip($alreadyMentionedUserIds); + + foreach ($mentions as $mention) { + if ($mention['type'] !== 'group') { + continue; + } + + $group = $this->groupManager->get($mention['id']); + if (!$group instanceof IGroup) { + continue; + } + + try { + $this->participantService->getParticipantByActor($chat, Attendee::ACTOR_GROUPS, $group->getGID()); + } catch (ParticipantNotFoundException $e) { + continue; + } + + $members = $group->getUsers(); + foreach ($members as $member) { + if (isset($alreadyMentionedUserIds[$member->getUID()])) { + continue; + } + + $list[] = [ + 'id' => $member->getUID(), + 'type' => Attendee::ACTOR_USERS, + 'reason' => 'group', + 'sourceId' => $group->getGID(), + ]; + $alreadyMentionedUserIds[$member->getUID()] = true; + } + } + + return $list; + } + /** * Creates a notification for the given chat message comment and mentioned * user ID. @@ -510,7 +586,7 @@ protected function shouldMentionedUserBeNotified(string $userId, IComment $comme * @param Participant $participant * @param IComment $comment * @param array $alreadyNotifiedUsers - * @psalm-param array $alreadyNotifiedUsers + * @psalm-param array $alreadyNotifiedUsers * @return bool */ protected function shouldParticipantBeNotified(Participant $participant, IComment $comment, array $alreadyNotifiedUsers): bool { diff --git a/lib/Chat/Parser/UserMention.php b/lib/Chat/Parser/UserMention.php index f767343abe9..44bd6376628 100644 --- a/lib/Chat/Parser/UserMention.php +++ b/lib/Chat/Parser/UserMention.php @@ -31,6 +31,8 @@ use OCA\Talk\Room; use OCA\Talk\Service\ParticipantService; use OCP\Comments\ICommentsManager; +use OCP\IGroup; +use OCP\IGroupManager; use OCP\IL10N; use OCP\IUserManager; @@ -45,17 +47,20 @@ class UserMention { */ protected ICommentsManager $commentsManager; protected IUserManager $userManager; + protected IGroupManager $groupManager; protected GuestManager $guestManager; protected ParticipantService $participantService; protected IL10N $l; public function __construct(ICommentsManager $commentsManager, IUserManager $userManager, + IGroupManager $groupManager, GuestManager $guestManager, ParticipantService $participantService, IL10N $l) { $this->commentsManager = $commentsManager; $this->userManager = $userManager; + $this->groupManager = $groupManager; $this->guestManager = $guestManager; $this->participantService = $participantService; $this->l = $l; @@ -104,15 +109,22 @@ public function parseMessage(Message $chatMessage): void { } $mentionTypeCount[$mention['type']]++; + $search = $mention['id']; + if ($mention['type'] === 'group') { + $search = 'group/' . $mention['id']; + } + // To keep a limited character set in parameter IDs ([a-zA-Z0-9-]) // the mention parameter ID does not include the mention ID (which // could contain characters like '@' for user IDs) but a one-based // index of the mentions of that type. $mentionParameterId = 'mention-' . $mention['type'] . $mentionTypeCount[$mention['type']]; - $message = str_replace('@"' . $mention['id'] . '"', '{' . $mentionParameterId . '}', $message); - if (strpos($mention['id'], ' ') === false && strpos($mention['id'], 'guest/') !== 0) { - $message = str_replace('@' . $mention['id'], '{' . $mentionParameterId . '}', $message); + $message = str_replace('@"' . $search . '"', '{' . $mentionParameterId . '}', $message); + if (strpos($search, ' ') === false + && strpos($search, 'guest/') !== 0 + && strpos($search, 'group/') !== 0) { + $message = str_replace('@' . $search, '{' . $mentionParameterId . '}', $message); } if ($mention['type'] === 'call') { @@ -140,6 +152,19 @@ public function parseMessage(Message $chatMessage): void { 'id' => $mention['id'], 'name' => $displayName, ]; + } elseif ($mention['type'] === 'group') { + $group = $this->groupManager->get($mention['id']); + if ($group instanceof IGroup) { + $displayName = $group->getDisplayName(); + } else { + $displayName = $mention['id']; + } + + $messageParameters[$mentionParameterId] = [ + 'type' => 'user-group', + 'id' => $mention['id'], + 'name' => $displayName, + ]; } else { try { $displayName = $this->commentsManager->resolveDisplayName($mention['type'], $mention['id']); diff --git a/lib/Notification/Notifier.php b/lib/Notification/Notifier.php index 740c4a9e8b1..f59cfb143c5 100644 --- a/lib/Notification/Notifier.php +++ b/lib/Notification/Notifier.php @@ -43,6 +43,7 @@ use OCP\Comments\NotFoundException; use OCP\Files\IRootFolder; use OCP\HintException; +use OCP\IGroupManager; use OCP\IL10N; use OCP\IURLGenerator; use OCP\IUser; @@ -63,6 +64,7 @@ class Notifier implements INotifier { protected IURLGenerator $url; protected Config $config; protected IUserManager $userManager; + protected IGroupManager $groupManager; protected GuestManager $guestManager; private IShareManager $shareManager; protected Manager $manager; @@ -85,6 +87,7 @@ public function __construct(IFactory $lFactory, IURLGenerator $url, Config $config, IUserManager $userManager, + IGroupManager $groupManager, GuestManager $guestManager, IShareManager $shareManager, Manager $manager, @@ -101,6 +104,7 @@ public function __construct(IFactory $lFactory, $this->url = $url; $this->config = $config; $this->userManager = $userManager; + $this->groupManager = $groupManager; $this->guestManager = $guestManager; $this->shareManager = $shareManager; $this->manager = $manager; @@ -277,7 +281,7 @@ public function prepare(INotification $notification, string $languageCode): INot } return $this->parseCall($notification, $room, $l); } - if ($subject === 'reply' || $subject === 'mention' || $subject === 'chat' || $subject === 'reaction') { + if ($subject === 'reply' || $subject === 'mention' || $subject === 'mention_direct' || $subject === 'mention_group' || $subject === 'mention_all' || $subject === 'chat' || $subject === 'reaction') { if ($room->getLobbyState() !== Webinary::LOBBY_NONE && $participant instanceof Participant && !($participant->getPermissions() & Attendee::PERMISSIONS_LOBBY_IGNORE)) { @@ -609,15 +613,67 @@ protected function parseChatMessage(INotification $notification, Room $room, Par } elseif ($room->getType() === Room::TYPE_ONE_TO_ONE || $room->getType() === Room::TYPE_ONE_TO_ONE_FORMER) { $subject = $l->t('{user} mentioned you in a private conversation'); } elseif ($richSubjectUser) { - $subject = $l->t('{user} mentioned you in conversation {call}'); + if ($notification->getSubject() === 'mention_group') { + $groupName = $this->groupManager->getDisplayName($subjectParameters['sourceId']) ?? $subjectParameters['sourceId']; + $richSubjectParameters['group'] = [ + 'type' => 'user-group', + 'id' => $subjectParameters['sourceId'], + 'name' => $groupName, + ]; + + $subject = $l->t('{user} mentioned group {group} in conversation {call}'); + } elseif ($notification->getSubject() === 'mention_all') { + $subject = $l->t('{user} mentioned everyone in conversation {call}'); + } else { + $subject = $l->t('{user} mentioned you in conversation {call}'); + } } elseif (!$isGuest) { - $subject = $l->t('A deleted user mentioned you in conversation {call}'); + if ($notification->getSubject() === 'mention_group') { + $groupName = $this->groupManager->getDisplayName($subjectParameters['sourceId']) ?? $subjectParameters['sourceId']; + $richSubjectParameters['group'] = [ + 'type' => 'user-group', + 'id' => $subjectParameters['sourceId'], + 'name' => $groupName, + ]; + + $subject = $l->t('A deleted user mentioned group {group} in conversation {call}'); + } elseif ($notification->getSubject() === 'mention_all') { + $subject = $l->t('A deleted user mentioned everyone in conversation {call}'); + } else { + $subject = $l->t('A deleted user mentioned you in conversation {call}'); + } } else { try { $richSubjectParameters['guest'] = $this->getGuestParameter($room, $comment->getActorId()); - $subject = $l->t('{guest} (guest) mentioned you in conversation {call}'); + if ($notification->getSubject() === 'mention_group') { + $groupName = $this->groupManager->getDisplayName($subjectParameters['sourceId']) ?? $subjectParameters['sourceId']; + $richSubjectParameters['group'] = [ + 'type' => 'user-group', + 'id' => $subjectParameters['sourceId'], + 'name' => $groupName, + ]; + + $subject = $l->t('{guest} (guest) mentioned group {group} in conversation {call}'); + } elseif ($notification->getSubject() === 'mention_all') { + $subject = $l->t('{guest} (guest) mentioned everyone in conversation {call}'); + } else { + $subject = $l->t('{guest} (guest) mentioned you in conversation {call}'); + } } catch (ParticipantNotFoundException $e) { - $subject = $l->t('A guest mentioned you in conversation {call}'); + if ($notification->getSubject() === 'mention_group') { + $groupName = $this->groupManager->getDisplayName($subjectParameters['sourceId']) ?? $subjectParameters['sourceId']; + $richSubjectParameters['group'] = [ + 'type' => 'user-group', + 'id' => $subjectParameters['sourceId'], + 'name' => $groupName, + ]; + + $subject = $l->t('A guest mentioned group {group} in conversation {call}'); + } elseif ($notification->getSubject() === 'mention_all') { + $subject = $l->t('A guest mentioned everyone in conversation {call}'); + } else { + $subject = $l->t('A guest mentioned you in conversation {call}'); + } } } $notification = $this->addActionButton($notification, $l->t('View chat'), false); diff --git a/src/components/MessagesList/MessagesGroup/Message/Message.vue b/src/components/MessagesList/MessagesGroup/Message/Message.vue index b789d298805..b2cac3332fd 100644 --- a/src/components/MessagesList/MessagesGroup/Message/Message.vue +++ b/src/components/MessagesList/MessagesGroup/Message/Message.vue @@ -518,7 +518,7 @@ export default { Object.keys(this.messageParameters).forEach(function(p) { const type = this.messageParameters[p].type const mimetype = this.messageParameters[p].mimetype - if (type === 'user' || type === 'call' || type === 'guest') { + if (type === 'user' || type === 'call' || type === 'guest' || type === 'group') { richParameters[p] = { component: Mention, props: this.messageParameters[p], diff --git a/src/components/MessagesList/MessagesGroup/Message/MessagePart/Mention.vue b/src/components/MessagesList/MessagesGroup/Message/MessagePart/Mention.vue index a133bd62916..4bffda11c12 100644 --- a/src/components/MessagesList/MessagesGroup/Message/MessagePart/Mention.vue +++ b/src/components/MessagesList/MessagesGroup/Message/MessagePart/Mention.vue @@ -25,6 +25,9 @@ :display-name="name" :avatar-image="'icon-group-forced-white'" :primary="true" /> + getDataFromResponse($this->response); if (isset($response['id'])) { self::$textToMessageId['shared::' . $type . '::' . $id] = $response['id']; + self::$messageIdToText[$response['id']] = 'shared::' . $type . '::' . $id; } } @@ -2026,13 +2028,14 @@ public function userSendsMessageWithReferenceIdToRoom($user, $message, $referenc $response = $this->getDataFromResponse($this->response); if (isset($response['id'])) { self::$textToMessageId[$message] = $response['id']; + self::$messageIdToText[$response['id']] = $message; } Assert::assertStringStartsWith($response['referenceId'], $referenceId); } /** - * @Then /^user "([^"]*)" sends reply "([^"]*)" on message "([^"]*)" to room "([^"]*)" with (\d+)(?: \((v1)\))?$/ + * @Then /^user "([^"]*)" sends reply ("[^"]*"|'[^']*') on message ("[^"]*"|'[^']*') to room "([^"]*)" with (\d+)(?: \((v1)\))?$/ * * @param string $user * @param string $reply @@ -2042,6 +2045,8 @@ public function userSendsMessageWithReferenceIdToRoom($user, $message, $referenc * @param string $apiVersion */ public function userSendsReplyToRoom($user, $reply, $message, $identifier, $statusCode, $apiVersion = 'v1') { + $reply = substr($reply, 1, -1); + $message = substr($message, 1, -1); $replyTo = self::$textToMessageId[$message]; $this->setCurrentUser($user); @@ -2055,6 +2060,7 @@ public function userSendsReplyToRoom($user, $reply, $message, $identifier, $stat $response = $this->getDataFromResponse($this->response); if (isset($response['id'])) { self::$textToMessageId[$reply] = $response['id']; + self::$messageIdToText[$response['id']] = $reply; } } @@ -2196,6 +2202,7 @@ protected function compareDataResponse(TableNode $formData = null) { self::$textToMessageId[$message['message']] = $message['id']; if ($message['message'] === '{file}' && isset($message['messageParameters']['file']['name'])) { self::$textToMessageId['shared::file::' . $message['messageParameters']['file']['name']] = $message['id']; + self::$messageIdToText[$message['id']] = 'shared::file::' . $message['messageParameters']['file']['name']; } } @@ -2308,6 +2315,7 @@ public function userSeesTheFollowingSystemMessagesInRoom($user, $identifier, $st // Include the received system messages in the list of messages used // for replies. self::$textToMessageId[$systemMessage['systemMessage']] = $systemMessage['id']; + self::$messageIdToText[$systemMessage['id']] = $systemMessage['systemMessage']; } if ($formData === null) { diff --git a/tests/integration/features/chat/notifications.feature b/tests/integration/features/chat/notifications.feature index 5dfa5d9ea50..527f6201d68 100644 --- a/tests/integration/features/chat/notifications.feature +++ b/tests/integration/features/chat/notifications.feature @@ -3,6 +3,8 @@ Feature: chat/notifications Background: Given user "participant1" exists Given user "participant2" exists + And group "attendees1" exists + And user "participant2" is member of group "attendees1" Scenario: Normal message when recipient is online in the one-to-one When user "participant1" creates room "one-to-one room" (v4) @@ -235,8 +237,8 @@ Feature: chat/notifications Given user "participant2" joins room "room" with 200 (v4) When user "participant1" sends message "Hi @all bye" to room "room" with 201 Then user "participant2" has the following notifications - | app | object_type | object_id | subject | - | spreed | chat | room/Hi @all bye | participant1-displayname mentioned you in conversation room | + | app | object_type | object_id | subject | + | spreed | chat | room/Hi @all bye | participant1-displayname mentioned everyone in conversation room | Scenario: At-all when recipient is offline in the group room When user "participant1" creates room "room" (v4) @@ -248,8 +250,8 @@ Feature: chat/notifications Given user "participant2" leaves room "room" with 200 (v4) When user "participant1" sends message "Hi @all bye" to room "room" with 201 Then user "participant2" has the following notifications - | app | object_type | object_id | subject | - | spreed | chat | room/Hi @all bye | participant1-displayname mentioned you in conversation room | + | app | object_type | object_id | subject | + | spreed | chat | room/Hi @all bye | participant1-displayname mentioned everyone in conversation room | Scenario: Silent at-all when recipient is offline in the group room When user "participant1" creates room "room" (v4) @@ -276,6 +278,84 @@ Feature: chat/notifications Then user "participant2" has the following notifications | app | object_type | object_id | subject | + Scenario: Group-mention when recipient is online in the group room + When user "participant1" creates room "room" (v4) + | roomType | 2 | + | roomName | room | + And user "participant1" adds user "participant2" to room "room" with 200 (v4) + And user "participant1" adds group "attendees1" to room "room" with 200 (v4) + Given user "participant2" joins room "room" with 200 (v4) + When user "participant1" sends message 'Hi @"group/attendees1" bye' to room "room" with 201 + Then user "participant2" has the following notifications + | app | object_type | object_id | subject | + | spreed | chat | room/Hi @"group/attendees1" bye | participant1-displayname mentioned group attendees1 in conversation room | + + Scenario: Group-mention when group is not a member of the room + When user "participant1" creates room "room" (v4) + | roomType | 2 | + | roomName | room | + And user "participant1" adds user "participant2" to room "room" with 200 (v4) + Given user "participant2" joins room "room" with 200 (v4) + When user "participant1" sends message 'Hi @"group/attendees1" bye' to room "room" with 201 + Then user "participant2" has the following notifications + | app | object_type | object_id | subject | + + Scenario: Group-mention when recipient is offline in the group room + When user "participant1" creates room "room" (v4) + | roomType | 2 | + | roomName | room | + And user "participant1" adds user "participant2" to room "room" with 200 (v4) + And user "participant1" adds group "attendees1" to room "room" with 200 (v4) + # Join and leave to clear the invite notification + Given user "participant2" joins room "room" with 200 (v4) + Given user "participant2" leaves room "room" with 200 (v4) + When user "participant1" sends message 'Hi @"group/attendees1" bye' to room "room" with 201 + Then user "participant2" has the following notifications + | app | object_type | object_id | subject | + | spreed | chat | room/Hi @"group/attendees1" bye | participant1-displayname mentioned group attendees1 in conversation room | + + Scenario: Silent group-mention when recipient is offline in the group room + When user "participant1" creates room "room" (v4) + | roomType | 2 | + | roomName | room | + And user "participant1" adds user "participant2" to room "room" with 200 (v4) + And user "participant1" adds group "attendees1" to room "room" with 200 (v4) + # Join and leave to clear the invite notification + Given user "participant2" joins room "room" with 200 (v4) + Given user "participant2" leaves room "room" with 200 (v4) + When user "participant1" silent sends message 'Hi @"group/attendees1" bye' to room "room" with 201 + Then user "participant2" has the following notifications + | app | object_type | object_id | subject | + + Scenario: Group-mention when recipient with disabled notifications in the group room + When user "participant1" creates room "room" (v4) + | roomType | 2 | + | roomName | room | + And user "participant1" adds user "participant2" to room "room" with 200 (v4) + And user "participant1" adds group "attendees1" to room "room" with 200 (v4) + # Join and leave to clear the invite notification + Given user "participant2" joins room "room" with 200 (v4) + Given user "participant2" leaves room "room" with 200 (v4) + And user "participant2" sets notifications to disabled for room "room" (v4) + When user "participant1" sends message 'Hi @"group/attendees1" bye' to room "room" with 201 + Then user "participant2" has the following notifications + | app | object_type | object_id | subject | + + Scenario: Replying with all mention types only gives a reply notification + When user "participant1" creates room "room" (v4) + | roomType | 2 | + | roomName | room | + And user "participant1" adds user "participant2" to room "room" with 200 (v4) + And user "participant1" adds group "attendees1" to room "room" with 200 (v4) + # Join and leave to clear the invite notification + Given user "participant2" joins room "room" with 200 (v4) + Given user "participant2" leaves room "room" with 200 (v4) + When user "participant2" sends message "Hi part 1" to room "room" with 201 + When user "participant1" sends reply 'Hi @all @participant2 @"group/attendees1" bye' on message "Hi part 1" to room "room" with 201 + Then user "participant2" has the following notifications + | app | object_type | object_id | subject | + | spreed | chat | room/Hi @all @participant2 @"group/attendees1" bye | participant1-displayname replied to your message in conversation room | + Scenario: Delete notification when the message is deleted When user "participant1" creates room "one-to-one room" (v4) | roomType | 1 | @@ -355,7 +435,7 @@ Feature: chat/notifications | app | object_type | object_id | subject | | spreed | chat | room/Hi @participant2 | participant1-displayname mentioned you in conversation room | | spreed | chat | room/Message 1 | participant1-displayname reacted with 🚀 to your message in conversation room | - | spreed | chat | room/Hi @all bye | participant1-displayname mentioned you in conversation room | + | spreed | chat | room/Hi @all bye | participant1-displayname mentioned everyone in conversation room | Scenario: Lobby: Notifications for moderators Given user "participant1" creates room "room" (v4) @@ -376,7 +456,7 @@ Feature: chat/notifications | app | object_type | object_id | subject | | spreed | chat | room/Hi @participant2 | participant1-displayname mentioned you in conversation room | | spreed | chat | room/Message 1 | participant1-displayname reacted with 🚀 to your message in conversation room | - | spreed | chat | room/Hi @all bye | participant1-displayname mentioned you in conversation room | + | spreed | chat | room/Hi @all bye | participant1-displayname mentioned everyone in conversation room | Scenario: Lobby: Wipe notifications when being blocked by the lobby Given user "participant1" creates room "room" (v4) @@ -395,7 +475,7 @@ Feature: chat/notifications | app | object_type | object_id | subject | | spreed | chat | room/Hi @participant2 | participant1-displayname mentioned you in conversation room | | spreed | chat | room/Message 1 | participant1-displayname reacted with 🚀 to your message in conversation room | - | spreed | chat | room/Hi @all bye | participant1-displayname mentioned you in conversation room | + | spreed | chat | room/Hi @all bye | participant1-displayname mentioned everyone in conversation room | When user "participant1" sets lobby state for room "room" to "non moderators" with 200 (v4) Then user "participant2" has the following notifications | app | object_type | object_id | subject | diff --git a/tests/php/Chat/AutoComplete/SearchPluginTest.php b/tests/php/Chat/AutoComplete/SearchPluginTest.php index 0b281e53f48..441007e107b 100644 --- a/tests/php/Chat/AutoComplete/SearchPluginTest.php +++ b/tests/php/Chat/AutoComplete/SearchPluginTest.php @@ -21,6 +21,7 @@ namespace OCA\Talk\Tests\php\Chat\AutoComplete; +use OC\Collaboration\Collaborators\SearchResult; use OCA\Talk\Chat\AutoComplete\SearchPlugin; use OCA\Talk\Files\Util; use OCA\Talk\GuestManager; @@ -31,6 +32,8 @@ use OCA\Talk\Service\ParticipantService; use OCA\Talk\TalkSession; use OCP\Collaboration\Collaborators\ISearchResult; +use OCP\IGroup; +use OCP\IGroupManager; use OCP\IL10N; use OCP\IUser; use OCP\IUserManager; @@ -42,6 +45,8 @@ class SearchPluginTest extends TestCase { protected $userManager; /** @var GuestManager|MockObject */ protected $guestManager; + /** @var IGroupManager|MockObject */ + protected $groupManager; /** @var TalkSession|MockObject */ protected $talkSession; /** @var ParticipantService|MockObject */ @@ -57,6 +62,7 @@ public function setUp(): void { parent::setUp(); $this->userManager = $this->createMock(IUserManager::class); + $this->groupManager = $this->createMock(IGroupManager::class); $this->guestManager = $this->createMock(GuestManager::class); $this->talkSession = $this->createMock(TalkSession::class); $this->participantService = $this->createMock(ParticipantService::class); @@ -78,6 +84,7 @@ protected function getPlugin(array $methods = []) { if (empty($methods)) { return new SearchPlugin( $this->userManager, + $this->groupManager, $this->guestManager, $this->talkSession, $this->participantService, @@ -90,6 +97,7 @@ protected function getPlugin(array $methods = []) { return $this->getMockBuilder(SearchPlugin::class) ->setConstructorArgs([ $this->userManager, + $this->groupManager, $this->guestManager, $this->talkSession, $this->participantService, @@ -313,4 +321,54 @@ public function testCreateGuestResult(string $actorId, string $name, array $expe $plugin = $this->getPlugin(); $this->assertEquals($expected, self::invokePrivate($plugin, 'createGuestResult', [$actorId, $name])); } + + /** + * @dataProvider dataSearchGroups + */ + public function testSearchGroups($search, $groupIds, $isGroup, $displayName, $totalMatches, $totalExactMatches): void { + $this->groupManager + ->method('get') + ->willReturnCallback(function ($groupId) use ($isGroup, $displayName) { + if ($isGroup) { + $group = $this->createMock(IGroup::class); + $group + ->method('getDisplayName') + ->willReturn($displayName); + $group + ->method('getGID') + ->willReturn($groupId); + return $group; + } + }); + $plugin = $this->getPlugin(['createGroupResult']); + $plugin->expects($this->any()) + ->method('createGroupResult') + ->willReturnCallback(function ($groupId) { + return [ + 'label' => $groupId, + 'value' => [ + 'shareType' => 'group', + 'shareWith' => 'group/' . $groupId, + ], + ]; + }); + $searchResult = new SearchResult(); + self::invokePrivate($plugin, 'searchGroups', [$search, $groupIds, $searchResult]); + $actual = $searchResult->asArray(); + $this->assertCount($totalMatches, $actual['groups']); + $this->assertCount($totalExactMatches, $actual['exact']['groups']); + } + + public function dataSearchGroups(): array { + return [ + // $search, $groupIds, $isGroup, $displayName, $totalMatches, $totalExactMatches + ['', ['groupid'], true, 'group', 1, 0], + ['groupid', ['groupid'], true, 'group', 0, 1], + ['gro', ['groupid'], true, 'group', 1, 0], + ['not', ['groupid'], false, 'group', 0, 0], + ['name', ['groupid'], true, 'name', 0, 1], + ['na', ['groupid'], true, 'name', 1, 0], + ['not', ['groupid'], true, 'group', 0, 0], + ]; + } } diff --git a/tests/php/Chat/NotifierTest.php b/tests/php/Chat/NotifierTest.php index 9443210799c..2249ba6c545 100644 --- a/tests/php/Chat/NotifierTest.php +++ b/tests/php/Chat/NotifierTest.php @@ -36,6 +36,7 @@ use OCP\AppFramework\Utility\ITimeFactory; use OCP\Comments\IComment; use OCP\IConfig; +use OCP\IGroupManager; use OCP\Notification\IManager as INotificationManager; use OCP\Notification\INotification; use OCP\IUserManager; @@ -47,6 +48,8 @@ class NotifierTest extends TestCase { protected $notificationManager; /** @var IUserManager|MockObject */ protected $userManager; + /** @var IGroupManager|MockObject */ + protected $groupManager; /** @var ParticipantService|MockObject */ protected $participantService; /** @var Manager|MockObject */ @@ -69,6 +72,7 @@ public function setUp(): void { ->willReturnCallback(function ($userId) { return $userId !== 'unknownUser'; }); + $this->groupManager = $this->createMock(IGroupManager::class); $this->participantService = $this->createMock(ParticipantService::class); $this->manager = $this->createMock(Manager::class); @@ -87,6 +91,7 @@ protected function getNotifier(array $methods = []) { ->setConstructorArgs([ $this->notificationManager, $this->userManager, + $this->groupManager, $this->participantService, $this->manager, $this->config, @@ -99,6 +104,7 @@ protected function getNotifier(array $methods = []) { return new Notifier( $this->notificationManager, $this->userManager, + $this->groupManager, $this->participantService, $this->manager, $this->config, @@ -164,18 +170,6 @@ public function testNotifyMentionedUsers(string $message, array $alreadyNotified $room = $this->getRoom(); $comment = $this->newComment('108', 'users', 'testUser', new \DateTime('@' . 1000000016), $message); $notifier = $this->getNotifier([]); - $alreadyNotifiedUsers = array_map(function ($user): array { - return [ - 'id' => $user, - 'type' => Attendee::ACTOR_USERS, - ]; - }, $alreadyNotifiedUsers); - $expectedReturn = array_map(function ($user): array { - return [ - 'id' => $user, - 'type' => Attendee::ACTOR_USERS, - ]; - }, $expectedReturn); $actual = $notifier->notifyMentionedUsers($room, $comment, $alreadyNotifiedUsers, false); $this->assertEqualsCanonicalizing($expectedReturn, $actual); @@ -186,20 +180,20 @@ public function dataNotifyMentionedUsers(): array { 'no notifications' => [ 'No mentions', [], [], [], ], - 'notify an unotified user' => [ - 'Mention @anotherUser', [], ['anotherUser'], ['anotherUser'], + 'notify a mentioned user' => [ + 'Mention @anotherUser', [], [['id' => 'anotherUser', 'type' => 'users', 'reason' => 'direct']], [['id' => 'anotherUser', 'type' => 'users', 'reason' => 'direct']], ], 'not notify mentioned user if already notified' => [ - 'Mention @anotherUser', ['anotherUser'], [], ['anotherUser'], + 'Mention @anotherUser', [['id' => 'anotherUser', 'type' => 'users', 'reason' => 'reply']], [], [['id' => 'anotherUser', 'type' => 'users', 'reason' => 'reply']], ], 'notify mentioned Users With Long Message Start Mention' => [ - '123456789 @anotherUserWithOddLengthName 123456789-123456789-123456789-123456789-123456789-123456789', [], ['anotherUserWithOddLengthName'], ['anotherUserWithOddLengthName'], + '123456789 @anotherUserWithOddLengthName 123456789-123456789-123456789-123456789-123456789-123456789', [], [['id' => 'anotherUserWithOddLengthName', 'type' => 'users', 'reason' => 'direct']], [['id' => 'anotherUserWithOddLengthName', 'type' => 'users', 'reason' => 'direct']], ], 'notify mentioned users with long message middle mention' => [ - '123456789-123456789-123456789-1234 @anotherUserWithOddLengthName 6789-123456789-123456789-123456789', [], ['anotherUserWithOddLengthName'], ['anotherUserWithOddLengthName'], + '123456789-123456789-123456789-1234 @anotherUserWithOddLengthName 6789-123456789-123456789-123456789', [], [['id' => 'anotherUserWithOddLengthName', 'type' => 'users', 'reason' => 'direct']], [['id' => 'anotherUserWithOddLengthName', 'type' => 'users', 'reason' => 'direct']], ], 'notify mentioned users with long message end mention' => [ - '123456789-123456789-123456789-123456789-123456789-123456789 @anotherUserWithOddLengthName 123456789', [], ['anotherUserWithOddLengthName'], ['anotherUserWithOddLengthName'], + '123456789-123456789-123456789-123456789-123456789-123456789 @anotherUserWithOddLengthName 123456789', [], [['id' => 'anotherUserWithOddLengthName', 'type' => 'users', 'reason' => 'direct']], [['id' => 'anotherUserWithOddLengthName', 'type' => 'users', 'reason' => 'direct']], ], 'mention herself' => [ 'Mention @testUser', [], [], [], @@ -208,7 +202,9 @@ public function dataNotifyMentionedUsers(): array { 'Mention @unknownUser', [], [], [], ], 'notify mentioned users several mentions' => [ - 'Mention @anotherUser, and @unknownUser, and @testUser, and @userAbleToJoin', [], ['anotherUser', 'userAbleToJoin'], ['anotherUser', 'userAbleToJoin'], + 'Mention @anotherUser, and @unknownUser, and @testUser, and @userAbleToJoin', [], + [['id' => 'anotherUser', 'type' => 'users', 'reason' => 'direct'], ['id' => 'userAbleToJoin', 'type' => 'users', 'reason' => 'direct']], + [['id' => 'anotherUser', 'type' => 'users', 'reason' => 'direct'], ['id' => 'userAbleToJoin', 'type' => 'users', 'reason' => 'direct']], ], 'notify mentioned users to user not invited to chat' => [ 'Mention @userNotInOneToOneChat', [], [], [], @@ -332,7 +328,7 @@ public function testAddMentionAllToList(array $usersToNotify, array $participant ->method('getActorsByType') ->willReturn($participants); - $actual = $this->invokePrivate($this->getNotifier(), 'addMentionAllToList', [$room, $usersToNotify]); + $actual = self::invokePrivate($this->getNotifier(), 'addMentionAllToList', [$room, $usersToNotify]); $this->assertCount(count($return), $actual); foreach ($actual as $key => $value) { $this->assertIsArray($value); @@ -353,25 +349,25 @@ public function dataAddMentionAllToList(): array { ], 'preserve notify list and do not notify all' => [ [ - ['id' => 'user1', 'type' => Attendee::ACTOR_USERS], + ['id' => 'user1', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'], ], [], [ - ['id' => 'user1', 'type' => Attendee::ACTOR_USERS], + ['id' => 'user1', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'], ], ], 'mention all' => [ [ - ['id' => 'user1', 'type' => Attendee::ACTOR_USERS], - ['id' => 'all'], + ['id' => 'user1', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'], + ['id' => 'all', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'], ], [ Attendee::fromRow(['actor_id' => 'user1', 'actor_type' => Attendee::ACTOR_USERS]), Attendee::fromRow(['actor_id' => 'user2', 'actor_type' => Attendee::ACTOR_USERS]), ], [ - ['id' => 'user1', 'type' => Attendee::ACTOR_USERS], - ['id' => 'user2', 'type' => Attendee::ACTOR_USERS], + ['id' => 'user1', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'], + ['id' => 'user2', 'type' => Attendee::ACTOR_USERS, 'reason' => 'all'], ], ], ]; @@ -420,7 +416,7 @@ public function dataNotifyReacted(): array { */ public function testGetMentionedUsers(string $message, array $expectedReturn): void { $comment = $this->newComment('108', 'users', 'testUser', new \DateTime('@' . 1000000016), $message); - $actual = $this->invokePrivate($this->getNotifier(), 'getMentionedUsers', [$comment]); + $actual = self::invokePrivate($this->getNotifier(), 'getMentionedUsers', [$comment]); $this->assertEqualsCanonicalizing($expectedReturn, $actual); } @@ -429,27 +425,27 @@ public function dataGetMentionedUsers(): array { 'mention one user' => [ 'Mention @anotherUser', [ - ['id' => 'anotherUser', 'type' => 'users'], + ['id' => 'anotherUser', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'], ], ], 'mention two user' => [ 'Mention @anotherUser, and @unknownUser', [ - ['id' => 'anotherUser', 'type' => 'users'], - ['id' => 'unknownUser', 'type' => 'users'], + ['id' => 'anotherUser', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'], + ['id' => 'unknownUser', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'], ], ], 'mention all' => [ 'Mention @all', [ - ['id' => 'all', 'type' => 'users'], + ['id' => 'all', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'], ], ], 'mention user, all, guest and group' => [ 'mention @test, @all, @"guest/1" @"group/1"', [ - ['id' => 'test', 'type' => 'users'], - ['id' => 'all', 'type' => 'users'], + ['id' => 'test', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'], + ['id' => 'all', 'type' => Attendee::ACTOR_USERS, 'reason' => 'direct'], ], ], ]; @@ -460,7 +456,7 @@ public function dataGetMentionedUsers(): array { */ public function testGetMentionedUserIds(string $message, array $expectedReturn): void { $comment = $this->newComment('108', 'users', 'testUser', new \DateTime('@' . 1000000016), $message); - $actual = $this->invokePrivate($this->getNotifier(), 'getMentionedUserIds', [$comment]); + $actual = self::invokePrivate($this->getNotifier(), 'getMentionedUserIds', [$comment]); $this->assertEqualsCanonicalizing($expectedReturn, $actual); } diff --git a/tests/php/Chat/Parser/UserMentionTest.php b/tests/php/Chat/Parser/UserMentionTest.php index 4627041120a..ea86699bf0b 100644 --- a/tests/php/Chat/Parser/UserMentionTest.php +++ b/tests/php/Chat/Parser/UserMentionTest.php @@ -34,6 +34,7 @@ use OCA\Talk\Service\ParticipantService; use OCP\Comments\IComment; use OCP\Comments\ICommentsManager; +use OCP\IGroupManager; use OCP\IL10N; use OCP\IUserManager; use PHPUnit\Framework\MockObject\MockObject; @@ -44,6 +45,8 @@ class UserMentionTest extends TestCase { protected $commentsManager; /** @var IUserManager|MockObject */ protected $userManager; + /** @var IGroupManager */ + protected $groupManager; /** @var GuestManager|MockObject */ protected $guestManager; /** @var ParticipantService|MockObject */ @@ -58,6 +61,7 @@ public function setUp(): void { $this->commentsManager = $this->createMock(ICommentsManager::class); $this->userManager = $this->createMock(IUserManager::class); + $this->groupManager = $this->createMock(IGroupManager::class); $this->guestManager = $this->createMock(GuestManager::class); $this->participantService = $this->createMock(ParticipantService::class); $this->l = $this->createMock(IL10N::class); @@ -65,6 +69,7 @@ public function setUp(): void { $this->parser = new UserMention( $this->commentsManager, $this->userManager, + $this->groupManager, $this->guestManager, $this->participantService, $this->l); diff --git a/tests/php/Notification/NotifierTest.php b/tests/php/Notification/NotifierTest.php index 7c1b29419c1..9bf14b38908 100644 --- a/tests/php/Notification/NotifierTest.php +++ b/tests/php/Notification/NotifierTest.php @@ -38,6 +38,7 @@ use OCP\AppFramework\Utility\ITimeFactory; use OCP\Comments\IComment; use OCP\Files\IRootFolder; +use OCP\IGroupManager; use OCP\IL10N; use OCP\IURLGenerator; use OCP\IUser; @@ -60,6 +61,8 @@ class NotifierTest extends TestCase { protected $config; /** @var IUserManager|MockObject */ protected $userManager; + /** @var IGroupManager|MockObject */ + protected $groupManager; /** @var GuestManager|MockObject */ protected $guestManager; /** @var IShareManager|MockObject */ @@ -93,6 +96,7 @@ public function setUp(): void { $this->url = $this->createMock(IURLGenerator::class); $this->config = $this->createMock(Config::class); $this->userManager = $this->createMock(IUserManager::class); + $this->groupManager = $this->createMock(IGroupManager::class); $this->guestManager = $this->createMock(GuestManager::class); $this->shareManager = $this->createMock(IShareManager::class); $this->manager = $this->createMock(Manager::class); @@ -111,6 +115,7 @@ public function setUp(): void { $this->url, $this->config, $this->userManager, + $this->groupManager, $this->guestManager, $this->shareManager, $this->manager,