Skip to content

Commit b832f26

Browse files
miaulalalanickvergessen
authored andcommitted
feat(mentions): allow teams to be mentioned
Signed-off-by: Anna Larch <[email protected]>
1 parent 9815309 commit b832f26

File tree

16 files changed

+404
-21
lines changed

16 files changed

+404
-21
lines changed

Diff for: lib/Chat/AutoComplete/SearchPlugin.php

+67
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ public function search($search, $limit, $offset, ISearchResult $searchResult): b
7171
$emailAttendees = [];
7272
/** @var list<Attendee> $guestAttendees */
7373
$guestAttendees = [];
74+
/** @var array<string, string> $teamIds */
75+
$teamIds = [];
7476

7577
if ($this->room->getType() === Room::TYPE_ONE_TO_ONE) {
7678
// Add potential leavers of one-to-one rooms again.
@@ -92,6 +94,8 @@ public function search($search, $limit, $offset, ISearchResult $searchResult): b
9294
$cloudIds[$attendee->getActorId()] = $attendee->getDisplayName();
9395
} elseif ($attendee->getActorType() === Attendee::ACTOR_GROUPS) {
9496
$groupIds[$attendee->getActorId()] = $attendee->getDisplayName();
97+
} elseif ($attendee->getActorType() === Attendee::ACTOR_CIRCLES) {
98+
$teamIds[$attendee->getActorId()] = $attendee->getDisplayName();
9599
}
96100
}
97101
}
@@ -101,6 +105,7 @@ public function search($search, $limit, $offset, ISearchResult $searchResult): b
101105
$this->searchGuests($search, $guestAttendees, $searchResult);
102106
$this->searchEmails($search, $emailAttendees, $searchResult);
103107
$this->searchFederatedUsers($search, $cloudIds, $searchResult);
108+
$this->searchTeams($search, $teamIds, $searchResult);
104109

105110
return false;
106111
}
@@ -352,6 +357,58 @@ protected function searchEmails(string $search, array $attendees, ISearchResult
352357
$searchResult->addResultSet($type, $matches, $exactMatches);
353358
}
354359

360+
/**
361+
* @param string $search
362+
* @param array<string, Attendee> $attendees
363+
* @param ISearchResult $searchResult
364+
*/
365+
/**
366+
* @param array<string|int, string> $teams
367+
*/
368+
protected function searchTeams(string $search, array $teams, ISearchResult $searchResult): void {
369+
$search = strtolower($search);
370+
371+
$type = new SearchResultType('teams');
372+
373+
$matches = $exactMatches = [];
374+
foreach ($teams as $teamId => $displayName) {
375+
if ($displayName === '') {
376+
continue;
377+
}
378+
379+
$teamId = (string)$teamId;
380+
if ($searchResult->hasResult($type, $teamId)) {
381+
continue;
382+
}
383+
384+
if ($search === '') {
385+
$matches[] = $this->createTeamResult($teamId, $displayName);
386+
continue;
387+
}
388+
389+
if (strtolower($teamId) === $search) {
390+
$exactMatches[] = $this->createTeamResult($teamId, $displayName);
391+
continue;
392+
}
393+
394+
if (stripos($teamId, $search) !== false) {
395+
$matches[] = $this->createTeamResult($teamId, $displayName);
396+
continue;
397+
}
398+
399+
if (strtolower($displayName) === $search) {
400+
$exactMatches[] = $this->createTeamResult($teamId, $displayName);
401+
continue;
402+
}
403+
404+
if (stripos($displayName, $search) !== false) {
405+
$matches[] = $this->createTeamResult($teamId, $displayName);
406+
}
407+
}
408+
409+
$searchResult->addResultSet($type, $matches, $exactMatches);
410+
}
411+
355412
protected function createResult(string $type, string $uid, string $name): array {
356413
if ($type === 'user' && $name === '') {
357414
$name = $this->userManager->getDisplayName($uid) ?? $uid;
@@ -401,4 +458,14 @@ protected function createEmailResult(string $actorId, string $name, ?string $ema
401458

402459
return $data;
403460
}
461+
462+
protected function createTeamResult(string $actorId, string $name): array {
463+
return [
464+
'label' => $name,
465+
'value' => [
466+
'shareType' => 'team',
467+
'shareWith' => 'team/' . $actorId,
468+
],
469+
];
470+
}
404471
}

Diff for: lib/Chat/Notifier.php

+46
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ public function notifyMentionedUsers(Room $chat, IComment $comment, array $alrea
112112
public function getUsersToNotify(Room $chat, IComment $comment, array $alreadyNotifiedUsers, ?Participant $participant = null): array {
113113
$usersToNotify = $this->getMentionedUsers($comment);
114114
$usersToNotify = $this->getMentionedGroupMembers($chat, $comment, $usersToNotify);
115+
$usersToNotify = $this->getMentionedTeamMembers($chat, $comment, $usersToNotify);
115116
$usersToNotify = $this->addMentionAllToList($chat, $usersToNotify, $participant);
116117
$usersToNotify = $this->removeAlreadyNotifiedUsers($usersToNotify, $alreadyNotifiedUsers);
117118

@@ -532,6 +533,51 @@ private function getMentionedGroupMembers(Room $chat, IComment $comment, array $
532533
return $list;
533534
}
534535

536+
/**
537+
* @param Room $chat
538+
* @param IComment $comment
539+
* @param array $list
540+
* @psalm-param array<int, array{type: string, id: string, reason: string, sourceId?: string}> $list
541+
* @return array[]
542+
* @psalm-return array<int, array{type: string, id: string, reason: string, sourceId?: string}>
543+
*/
544+
private function getMentionedTeamMembers(Room $chat, IComment $comment, array $list): array {
545+
$mentions = $comment->getMentions();
546+
547+
if (empty($mentions)) {
548+
return [];
549+
}
550+
551+
$alreadyMentionedUserIds = array_filter(
552+
array_map(static fn (array $entry) => $entry['type'] === Attendee::ACTOR_USERS ? $entry['id'] : null, $list),
553+
static fn ($userId) => $userId !== null
554+
);
555+
$alreadyMentionedUserIds = array_flip($alreadyMentionedUserIds);
556+
557+
foreach ($mentions as $mention) {
558+
if ($mention['type'] !== 'team') {
559+
continue;
560+
}
561+
562+
$members = $this->participantService->getCircleMembers($mention['id']);
563+
if (empty($members)) {
564+
continue;
565+
}
566+
567+
foreach ($members as $member) {
568+
$list[] = [
569+
'id' => $member->getUserId(),
570+
'type' => Attendee::ACTOR_USERS,
571+
'reason' => 'team',
572+
'sourceId' => $mention['id'],
573+
];
574+
$alreadyMentionedUserIds[$member->getUserId()] = true;
575+
}
576+
}
577+
578+
return $list;
579+
}
580+
535581
/**
536582
* Creates a notification for the given chat message comment and mentioned
537583
* user ID.

Diff for: lib/Chat/Parser/UserMention.php

+55-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
namespace OCA\Talk\Chat\Parser;
1010

11+
use OCA\Circles\CirclesManager;
1112
use OCA\Talk\Chat\ChatManager;
1213
use OCA\Talk\Events\MessageParseEvent;
1314
use OCA\Talk\Exceptions\ParticipantNotFoundException;
@@ -17,6 +18,7 @@
1718
use OCA\Talk\Room;
1819
use OCA\Talk\Service\AvatarService;
1920
use OCA\Talk\Service\ParticipantService;
21+
use OCP\App\IAppManager;
2022
use OCP\Comments\ICommentsManager;
2123
use OCP\EventDispatcher\Event;
2224
use OCP\EventDispatcher\IEventListener;
@@ -25,14 +27,20 @@
2527
use OCP\IGroupManager;
2628
use OCP\IL10N;
2729
use OCP\IUserManager;
30+
use OCP\Server;
2831

2932
/**
3033
* Helper class to get a rich message from a plain text message.
3134
* @template-implements IEventListener<Event>
3235
*/
3336
class UserMention implements IEventListener {
37+
/** @var array<string, string> */
38+
protected array $circleNames = [];
39+
/** @var array<string, string> */
40+
protected array $circleLinks = [];
3441

3542
public function __construct(
43+
protected IAppManager $appManager,
3644
protected ICommentsManager $commentsManager,
3745
protected IUserManager $userManager,
3846
protected IGroupManager $groupManager,
@@ -117,7 +125,7 @@ protected function parseMessage(Message $chatMessage): void {
117125
$mention['type'] === 'email' ||
118126
$mention['type'] === 'group' ||
119127
// $mention['type'] === 'federated_group' ||
120-
// $mention['type'] === 'team' ||
128+
$mention['type'] === 'team' ||
121129
// $mention['type'] === 'federated_team' ||
122130
$mention['type'] === 'federated_user') {
123131
$search = $mention['type'] . '/' . $mention['id'];
@@ -135,7 +143,7 @@ protected function parseMessage(Message $chatMessage): void {
135143
&& !str_starts_with($search, 'email/')
136144
&& !str_starts_with($search, 'group/')
137145
// && !str_starts_with($search, 'federated_group/')
138-
// && !str_starts_with($search, 'team/')
146+
&& !str_starts_with($search, 'team/')
139147
// && !str_starts_with($search, 'federated_team/')
140148
&& !str_starts_with($search, 'federated_user/')) {
141149
$message = str_replace('@' . $search, '{' . $mentionParameterId . '}', $message);
@@ -213,6 +221,8 @@ protected function parseMessage(Message $chatMessage): void {
213221
'id' => $mention['id'],
214222
'name' => $displayName,
215223
];
224+
} elseif ($mention['type'] === 'team') {
225+
$messageParameters[$mentionParameterId] = $this->getCircle($mention['id']);
216226
} else {
217227
try {
218228
$displayName = $this->commentsManager->resolveDisplayName($mention['type'], $mention['id']);
@@ -256,4 +266,47 @@ protected function getRoomType(Room $room): string {
256266
throw new \InvalidArgumentException('Unknown room type');
257267
}
258268
}
269+
270+
protected function getCircle(string $circleId): array {
271+
if (!$this->appManager->isEnabledForUser('circles')) {
272+
return [
273+
'type' => 'highlight',
274+
'id' => $circleId,
275+
'name' => $circleId,
276+
];
277+
}
278+
279+
if (!isset($this->circleNames[$circleId])) {
280+
$this->loadCircleDetails($circleId);
281+
}
282+
283+
if (!isset($this->circleNames[$circleId])) {
284+
return [
285+
'type' => 'highlight',
286+
'id' => $circleId,
287+
'name' => $circleId,
288+
];
289+
}
290+
291+
return [
292+
'type' => 'circle',
293+
'id' => $circleId,
294+
'name' => $this->circleNames[$circleId],
295+
'link' => $this->circleLinks[$circleId],
296+
];
297+
}
298+
299+
protected function loadCircleDetails(string $circleId): void {
300+
try {
301+
$circlesManager = Server::get(CirclesManager::class);
302+
$circlesManager->startSuperSession();
303+
$circle = $circlesManager->getCircle($circleId);
304+
305+
$this->circleNames[$circleId] = $circle->getDisplayName();
306+
$this->circleLinks[$circleId] = $circle->getUrl();
307+
} catch (\Exception) {
308+
} finally {
309+
$circlesManager?->stopSession();
310+
}
311+
}
259312
}

Diff for: lib/Controller/RoomController.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -1147,7 +1147,7 @@ protected function formatParticipantList(array $participants, bool $includeStatu
11471147
* Add a participant to a room
11481148
*
11491149
* @param string $newParticipant New participant
1150-
* @param 'users'|'groups'|'circles'|'emails'|'federated_users'|'phones' $source Source of the participant
1150+
* @param 'users'|'groups'|'circles'|'emails'|'federated_users'|'phones'|'teams' $source Source of the participant
11511151
* @return DataResponse<Http::STATUS_OK, array{type?: int}, array{}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_NOT_FOUND|Http::STATUS_NOT_IMPLEMENTED, array{error: 'ban'|'cloud-id'|'federation'|'moderator'|'new-participant'|'outgoing'|'reach-remote'|'room-type'|'sip'|'source'|'trusted-servers'}, array{}>
11521152
*
11531153
* 200: Participant successfully added
@@ -1215,7 +1215,7 @@ public function addParticipantToRoom(string $newParticipant, string $source = 'u
12151215
}
12161216

12171217
$this->participantService->addGroup($this->room, $group, $participants);
1218-
} elseif ($source === 'circles') {
1218+
} elseif ($source === 'circles' || $source === 'teams') {
12191219
if (!$this->appManager->isEnabledForUser('circles')) {
12201220
return new DataResponse(['error' => 'new-participant'], Http::STATUS_BAD_REQUEST);
12211221
}

0 commit comments

Comments
 (0)