Skip to content

Commit

Permalink
feat(mentions): allow teams to be mentioned
Browse files Browse the repository at this point in the history
Signed-off-by: Anna Larch <[email protected]>
  • Loading branch information
miaulalala committed Jan 30, 2025
1 parent 9815309 commit be7a0da
Show file tree
Hide file tree
Showing 13 changed files with 300 additions and 16 deletions.
67 changes: 67 additions & 0 deletions lib/Chat/AutoComplete/SearchPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ public function search($search, $limit, $offset, ISearchResult $searchResult): b
$emailAttendees = [];
/** @var list<Attendee> $guestAttendees */
$guestAttendees = [];
/** @var array<string, string> $teamIds */
$teamIds = [];

if ($this->room->getType() === Room::TYPE_ONE_TO_ONE) {
// Add potential leavers of one-to-one rooms again.
Expand All @@ -92,6 +94,8 @@ public function search($search, $limit, $offset, ISearchResult $searchResult): b
$cloudIds[$attendee->getActorId()] = $attendee->getDisplayName();
} elseif ($attendee->getActorType() === Attendee::ACTOR_GROUPS) {
$groupIds[$attendee->getActorId()] = $attendee->getDisplayName();
} elseif ($attendee->getActorType() === Attendee::ACTOR_CIRCLES) {
$teamIds[$attendee->getActorId()] = $attendee->getDisplayName();
}
}
}
Expand All @@ -101,6 +105,7 @@ public function search($search, $limit, $offset, ISearchResult $searchResult): b
$this->searchGuests($search, $guestAttendees, $searchResult);
$this->searchEmails($search, $emailAttendees, $searchResult);
$this->searchFederatedUsers($search, $cloudIds, $searchResult);
$this->searchTeams($search, $teamIds, $searchResult);

return false;
}
Expand Down Expand Up @@ -352,6 +357,58 @@ protected function searchEmails(string $search, array $attendees, ISearchResult
$searchResult->addResultSet($type, $matches, $exactMatches);
}

/**
* @param string $search
* @param array<string, Attendee> $attendees
* @param ISearchResult $searchResult
*/
/**
* @param array<string|int, string> $teams
*/
protected function searchTeams(string $search, array $teams, ISearchResult $searchResult): void {
$search = strtolower($search);

$type = new SearchResultType('teams');

$matches = $exactMatches = [];
foreach ($teams as $teamId => $displayName) {
if ($displayName === '') {
continue;
}

$teamId = (string)$teamId;
if ($searchResult->hasResult($type, $teamId)) {
continue;
}

if ($search === '') {
$matches[] = $this->createTeamResult($teamId, $displayName);
continue;
}

if (strtolower($teamId) === $search) {
$exactMatches[] = $this->createTeamResult($teamId, $displayName);
continue;
}

if (stripos($teamId, $search) !== false) {
$matches[] = $this->createTeamResult($teamId, $displayName);
continue;
}

if (strtolower($displayName) === $search) {
$exactMatches[] = $this->createTeamResult($teamId, $displayName);
continue;
}

if (stripos($displayName, $search) !== false) {
$matches[] = $this->createTeamResult($teamId, $displayName);
}
}

$searchResult->addResultSet($type, $matches, $exactMatches);
}

protected function createResult(string $type, string $uid, string $name): array {
if ($type === 'user' && $name === '') {
$name = $this->userManager->getDisplayName($uid) ?? $uid;
Expand Down Expand Up @@ -401,4 +458,14 @@ protected function createEmailResult(string $actorId, string $name, ?string $ema

return $data;
}

protected function createTeamResult(string $actorId, string $name): array {
return [
'label' => $name,
'value' => [
'shareType' => 'team',
'shareWith' => 'team/' . $actorId,
],
];
}
}
46 changes: 46 additions & 0 deletions lib/Chat/Notifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ public function notifyMentionedUsers(Room $chat, IComment $comment, array $alrea
public function getUsersToNotify(Room $chat, IComment $comment, array $alreadyNotifiedUsers, ?Participant $participant = null): array {
$usersToNotify = $this->getMentionedUsers($comment);
$usersToNotify = $this->getMentionedGroupMembers($chat, $comment, $usersToNotify);
$usersToNotify = $this->getMentionedTeamMembers($chat, $comment, $usersToNotify);
$usersToNotify = $this->addMentionAllToList($chat, $usersToNotify, $participant);
$usersToNotify = $this->removeAlreadyNotifiedUsers($usersToNotify, $alreadyNotifiedUsers);

Expand Down Expand Up @@ -532,6 +533,51 @@ private function getMentionedGroupMembers(Room $chat, IComment $comment, array $
return $list;
}

/**
* @param Room $chat
* @param IComment $comment
* @param array $list
* @psalm-param array<int, array{type: string, id: string, reason: string, sourceId?: string}> $list
* @return array[]
* @psalm-return array<int, array{type: string, id: string, reason: string, sourceId?: string}>
*/
private function getMentionedTeamMembers(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'] !== 'team') {
continue;
}

$members = $this->participantService->getCircleMembers($mention['id']);
if (empty($members)) {
continue;
}

foreach ($members as $member) {
$list[] = [
'id' => $member->getUserId(),
'type' => Attendee::ACTOR_USERS,
'reason' => 'team',
'sourceId' => $mention['id'],
];
$alreadyMentionedUserIds[$member->getUserId()] = true;
}
}

return $list;
}

/**
* Creates a notification for the given chat message comment and mentioned
* user ID.
Expand Down
4 changes: 2 additions & 2 deletions lib/Controller/RoomController.php
Original file line number Diff line number Diff line change
Expand Up @@ -1147,7 +1147,7 @@ protected function formatParticipantList(array $participants, bool $includeStatu
* Add a participant to a room
*
* @param string $newParticipant New participant
* @param 'users'|'groups'|'circles'|'emails'|'federated_users'|'phones' $source Source of the participant
* @param 'users'|'groups'|'circles'|'emails'|'federated_users'|'phones'|'teams' $source Source of the participant
* @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{}>
*
* 200: Participant successfully added
Expand Down Expand Up @@ -1215,7 +1215,7 @@ public function addParticipantToRoom(string $newParticipant, string $source = 'u
}

$this->participantService->addGroup($this->room, $group, $participants);
} elseif ($source === 'circles') {
} elseif ($source === 'circles' || $source === 'teams') {
if (!$this->appManager->isEnabledForUser('circles')) {
return new DataResponse(['error' => 'new-participant'], Http::STATUS_BAD_REQUEST);
}
Expand Down
40 changes: 39 additions & 1 deletion lib/Notification/Notifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ public function prepare(INotification $notification, string $languageCode): INot
}
return $this->parseCall($notification, $room, $l);
}
if ($subject === 'reply' || $subject === 'mention' || $subject === 'mention_direct' || $subject === 'mention_group' || $subject === 'mention_all' || $subject === 'chat' || $subject === 'reaction' || $subject === 'reminder') {
if ($subject === 'reply' || $subject === 'mention' || $subject === 'mention_direct' || $subject === 'mention_group' || $subject === 'mention_team' || $subject === 'mention_all' || $subject === 'chat' || $subject === 'reaction' || $subject === 'reminder') {
if ($participant instanceof Participant &&
$room->getLobbyState() !== Webinary::LOBBY_NONE &&
!($participant->getPermissions() & Attendee::PERMISSIONS_LOBBY_IGNORE)) {
Expand Down Expand Up @@ -774,6 +774,16 @@ protected function parseChatMessage(INotification $notification, Room $room, Par
];

$subject = $l->t('{user} mentioned group {group} in conversation {call}');
} elseif ($notification->getSubject() === 'mention_team') {
// $circlesManager = OC::$server->getCircleManager();
// $groupName = $this->groupManager->getDisplayName($subjectParameters['sourceId']) ?? $subjectParameters['sourceId'];
$richSubjectParameters['team'] = [
'type' => 'circle',
'id' => $subjectParameters['sourceId'],
'name' => $subjectParameters['sourceId'],
];

$subject = $l->t('{user} mentioned team {team} in conversation {call}');
} elseif ($notification->getSubject() === 'mention_all') {
$subject = $l->t('{user} mentioned everyone in conversation {call}');
} else {
Expand All @@ -789,6 +799,16 @@ protected function parseChatMessage(INotification $notification, Room $room, Par
];

$subject = $l->t('A deleted user mentioned group {group} in conversation {call}');
} elseif ($notification->getSubject() === 'mention_team') {
// $circlesManager = OC::$server->getCircleManager();
// $groupName = $this->groupManager->getDisplayName($subjectParameters['sourceId']) ?? $subjectParameters['sourceId'];
$richSubjectParameters['team'] = [
'type' => 'circle',
'id' => $subjectParameters['sourceId'],
'name' => $subjectParameters['sourceId'],
];

$subject = $l->t('A deleted user mentioned team {team} in conversation {call}');
} elseif ($notification->getSubject() === 'mention_all') {
$subject = $l->t('A deleted user mentioned everyone in conversation {call}');
} else {
Expand All @@ -806,6 +826,15 @@ protected function parseChatMessage(INotification $notification, Room $room, Par
];

$subject = $l->t('{guest} (guest) mentioned group {group} in conversation {call}');
} elseif ($notification->getSubject() === 'mention_team') {
// $groupName = $this->groupManager->getDisplayName($subjectParameters['sourceId']) ?? $subjectParameters['sourceId'];
$richSubjectParameters['team'] = [
'type' => 'circle',
'id' => $subjectParameters['sourceId'],
'name' => $subjectParameters['sourceId'],
];

$subject = $l->t('{guest} (guest) mentioned team {team} in conversation {call}');
} elseif ($notification->getSubject() === 'mention_all') {
$subject = $l->t('{guest} (guest) mentioned everyone in conversation {call}');
} else {
Expand All @@ -821,6 +850,15 @@ protected function parseChatMessage(INotification $notification, Room $room, Par
];

$subject = $l->t('A guest mentioned group {group} in conversation {call}');
} elseif ($notification->getSubject() === 'mention_team') {
// $groupName = $this->groupManager->getDisplayName($subjectParameters['sourceId']) ?? $subjectParameters['sourceId'];
$richSubjectParameters['team'] = [
'type' => 'circle',
'id' => $subjectParameters['sourceId'],
'name' => $subjectParameters['sourceId'],
];

$subject = $l->t('A guest mentioned team {team} in conversation {call}');
} elseif ($notification->getSubject() === 'mention_all') {
$subject = $l->t('A guest mentioned everyone in conversation {call}');
} else {
Expand Down
35 changes: 31 additions & 4 deletions lib/Service/ParticipantService.php
Original file line number Diff line number Diff line change
Expand Up @@ -719,16 +719,43 @@ public function getCircle(string $circleId, string $userId): Circle {

$circlesManager->startSession($federatedUser);
try {
$circle = $circlesManager->getCircle($circleId);
$circlesManager->stopSession();
return $circle;
return $circlesManager->getCircle($circleId);
} catch (\Exception $e) {
} finally {
$circlesManager->stopSession();
}

$circlesManager->stopSession();
throw new ParticipantNotFoundException('Circle not found or not a member');
}

/**
* @param string $circleId
* @param string $userId
* @return Member[]
* @throws ParticipantNotFoundException
*/
public function getCircleMembers(string $circleId): array {
try {
$circlesManager = Server::get(CirclesManager::class);
} catch (\Exception) {
throw new ParticipantNotFoundException('Circle not found');
}

$circlesManager->startSuperSession();
try {
$circle = $circlesManager->getCircle($circleId);
} catch (\Exception) {
throw new ParticipantNotFoundException('Circle not found');
} finally {
$circlesManager->stopSession();
}

$members = $circle->getInheritedMembers();
return array_filter($members, static function (Member $member) {
return $member->getUserType() === Member::TYPE_USER;
});
}

/**
* @param Room $room
* @param Circle $circle
Expand Down
3 changes: 2 additions & 1 deletion openapi-full.json
Original file line number Diff line number Diff line change
Expand Up @@ -13791,7 +13791,8 @@
"circles",
"emails",
"federated_users",
"phones"
"phones",
"teams"
],
"description": "Source of the participant"
}
Expand Down
3 changes: 2 additions & 1 deletion openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -13949,7 +13949,8 @@
"circles",
"emails",
"federated_users",
"phones"
"phones",
"teams"
],
"description": "Source of the participant"
}
Expand Down
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ export const MENTION = {
// Parsed to another types
FEDERATED_USER: 'federated_user',
GROUP: 'group',
TEAM: 'team',
},
}

Expand Down
2 changes: 1 addition & 1 deletion src/types/openapi/openapi-full.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7227,7 +7227,7 @@ export interface operations {
* @default users
* @enum {string}
*/
source?: "users" | "groups" | "circles" | "emails" | "federated_users" | "phones";
source?: "users" | "groups" | "circles" | "emails" | "federated_users" | "phones" | "teams";
};
};
};
Expand Down
2 changes: 1 addition & 1 deletion src/types/openapi/openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6811,7 +6811,7 @@ export interface operations {
* @default users
* @enum {string}
*/
source?: "users" | "groups" | "circles" | "emails" | "federated_users" | "phones";
source?: "users" | "groups" | "circles" | "emails" | "federated_users" | "phones" | "teams";
};
};
};
Expand Down
Loading

0 comments on commit be7a0da

Please sign in to comment.