From a916baff219c6b51a35923bd59afbc1fd508657a Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 27 Feb 2024 18:24:08 +0100 Subject: [PATCH] feat(federation): Support mentioning federated users Signed-off-by: Joas Schilling --- docs/chat.md | 17 +- lib/Chat/ChatManager.php | 1 + lib/Chat/Parser/UserMention.php | 39 ++- lib/Controller/ChatController.php | 10 + lib/ResponseDefinitions.php | 1 + openapi-full.json | 4 + openapi.json | 4 + src/components/NewMessage/NewMessage.vue | 1 + .../features/bootstrap/FeatureContext.php | 12 +- .../features/chat-1/mentions.feature | 282 +++++++++--------- .../features/federation/chat.feature | 32 +- tests/php/Chat/ChatManagerTest.php | 6 +- tests/php/Chat/Parser/UserMentionTest.php | 49 ++- tests/php/Controller/ChatControllerTest.php | 28 +- 14 files changed, 303 insertions(+), 183 deletions(-) diff --git a/docs/chat.md b/docs/chat.md index 5a013458dd8f..7ae05d3ea28e 100644 --- a/docs/chat.md +++ b/docs/chat.md @@ -472,14 +472,15 @@ See [OCP\RichObjectStrings\Definitions](https://github.com/nextcloud/server/blob - Data: Array of suggestions, each suggestion has at least: -| field | type | Description | -|-----------------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `id` | string | The user id which should be sent as `@` in the message (user ids that contain spaces as well as guest ids need to be wrapped in double-quotes when sending in a message: `@"space user"` and `@"guest/random-string"`) | -| `label` | string | The displayname of the user | -| `source` | string | The type of the user, currently only `users`, `guests` or `calls` (for mentioning the whole conversation | -| `status` | string | Optional: Only available with `includeStatus=true` and for users with a set status | -| `statusIcon` | string | Optional: Only available with `includeStatus=true` and for users with a set status | -| `statusMessage` | string | Optional: Only available with `includeStatus=true` and for users with a set status | +| field | type | Description | +|-----------------|--------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `insert` | string | Optional: Only available with `federation-v1` capability - the identifier which should be sent as `@` in the message (ids that contain spaces or slashes need to be wrapped in double-quotes when sending in a message: `@"space user"` and `@"guest/random-string"`) | +| `id` | string | The id of the participant to mention | +| `label` | string | The display name of the mention option | +| `source` | string | The type of the mention option, currently only `users`, `federated_users`, `group`, `guests` or `calls` (for mentioning the whole conversation) | +| `status` | string | Optional: Only available with `includeStatus=true` and for users with a set status | +| `statusIcon` | string | Optional: Only available with `includeStatus=true` and for users with a set status | +| `statusMessage` | string | Optional: Only available with `includeStatus=true` and for users with a set status | ## System messages diff --git a/lib/Chat/ChatManager.php b/lib/Chat/ChatManager.php index 747d6d035614..bee631f0ac02 100644 --- a/lib/Chat/ChatManager.php +++ b/lib/Chat/ChatManager.php @@ -920,6 +920,7 @@ public function addConversationNotify(array $results, string $search, Room $room 'id' => 'all', 'label' => $roomDisplayName, 'source' => 'calls', + 'insert' => 'all', ]); } return $results; diff --git a/lib/Chat/Parser/UserMention.php b/lib/Chat/Parser/UserMention.php index 8b557f1ce55e..257ae64eeaa6 100644 --- a/lib/Chat/Parser/UserMention.php +++ b/lib/Chat/Parser/UserMention.php @@ -39,6 +39,7 @@ use OCP\Comments\ICommentsManager; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; +use OCP\Federation\ICloudIdManager; use OCP\IGroup; use OCP\IGroupManager; use OCP\IL10N; @@ -56,6 +57,7 @@ public function __construct( protected IGroupManager $groupManager, protected GuestManager $guestManager, protected AvatarService $avatarService, + protected ICloudIdManager $cloudIdManager, protected ParticipantService $participantService, protected IL10N $l, ) { @@ -125,20 +127,29 @@ protected function parseMessage(Message $chatMessage): void { $mentionTypeCount[$mention['type']]++; $search = $mention['id']; - if ($mention['type'] === 'group') { - $search = 'group/' . $mention['id']; + if ( + $mention['type'] === 'group' || + // $mention['type'] === 'federated_group' || + // $mention['type'] === 'team' || + // $mention['type'] === 'federated_team' || + $mention['type'] === 'federated_user') { + $search = $mention['type'] . '/' . $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']]; + $mentionParameterId = 'mention-' . str_replace('_', '-', $mention['type']) . $mentionTypeCount[$mention['type']]; $message = str_replace('@"' . $search . '"', '{' . $mentionParameterId . '}', $message); if (!str_contains($search, ' ') && !str_starts_with($search, 'guest/') - && !str_starts_with($search, 'group/')) { + && !str_starts_with($search, 'group/') + // && !str_starts_with($search, 'federated_group/') + // && !str_starts_with($search, 'team/') + // && !str_starts_with($search, 'federated_team/') + && !str_starts_with($search, 'federated_user/')) { $message = str_replace('@' . $search, '{' . $mentionParameterId . '}', $message); } @@ -168,6 +179,26 @@ protected function parseMessage(Message $chatMessage): void { 'id' => $mention['id'], 'name' => $displayName, ]; + } elseif ($mention['type'] === 'federated_user') { + try { + $cloudId = $this->cloudIdManager->resolveCloudId($mention['id']); + } catch (\Throwable) { + continue; + } + + try { + $participant = $this->participantService->getParticipantByActor($chatMessage->getRoom(), Attendee::ACTOR_FEDERATED_USERS, $mention['id']); + $displayName = $participant->getAttendee()->getDisplayName() ?: $cloudId->getDisplayId(); + } catch (ParticipantNotFoundException) { + $displayName = $mention['id']; + } + + $messageParameters[$mentionParameterId] = [ + 'type' => 'user', + 'id' => $cloudId->getUser(), + 'name' => $displayName, + 'server' => $cloudId->getRemote() + ]; } elseif ($mention['type'] === 'group') { $group = $this->groupManager->get($mention['id']); if ($group instanceof IGroup) { diff --git a/lib/Controller/ChatController.php b/lib/Controller/ChatController.php index a98cebfa2436..c4d632dc12be 100644 --- a/lib/Controller/ChatController.php +++ b/lib/Controller/ChatController.php @@ -1279,6 +1279,7 @@ protected function prepareResultArray(array $results, array $statuses): array { 'id' => $result['value']['shareWith'], 'label' => $result['label'], 'source' => $type, + 'insert' => $this->createMentionString($type, $result['value']['shareWith']), ]; if ($type === Attendee::ACTOR_USERS && isset($statuses[$data['id']])) { @@ -1293,4 +1294,13 @@ protected function prepareResultArray(array $results, array $statuses): array { } return $output; } + + protected function createMentionString(string $type, string $id): string { + if ($type === Attendee::ACTOR_USERS || $type === Attendee::ACTOR_GUESTS) { + return $id; + } + + // It's "group/admin" so we have to strip off the trailing "s" from the type + return substr($type, 0, -1) . '/' . $id; + } } diff --git a/lib/ResponseDefinitions.php b/lib/ResponseDefinitions.php index de7825fd86ff..40bbe8771a6c 100644 --- a/lib/ResponseDefinitions.php +++ b/lib/ResponseDefinitions.php @@ -60,6 +60,7 @@ * id: string, * label: string, * source: string, + * insert: string, * status: ?string, * statusClearAt: ?int, * statusIcon: ?string, diff --git a/openapi-full.json b/openapi-full.json index e111edc8cb0a..7fbf8735c084 100644 --- a/openapi-full.json +++ b/openapi-full.json @@ -307,6 +307,7 @@ "id", "label", "source", + "insert", "status", "statusClearAt", "statusIcon", @@ -322,6 +323,9 @@ "source": { "type": "string" }, + "insert": { + "type": "string" + }, "status": { "type": "string", "nullable": true diff --git a/openapi.json b/openapi.json index c282ee99e83b..2e671b915b42 100644 --- a/openapi.json +++ b/openapi.json @@ -248,6 +248,7 @@ "id", "label", "source", + "insert", "status", "statusClearAt", "statusIcon", @@ -263,6 +264,9 @@ "source": { "type": "string" }, + "insert": { + "type": "string" + }, "status": { "type": "string", "nullable": true diff --git a/src/components/NewMessage/NewMessage.vue b/src/components/NewMessage/NewMessage.vue index 875c41a4374f..66c6e53c4c72 100644 --- a/src/components/NewMessage/NewMessage.vue +++ b/src/components/NewMessage/NewMessage.vue @@ -900,6 +900,7 @@ export default { } // Caching the user id data for each possible mention + possibleMention.id = possibleMention.insert this.userData[possibleMention.id] = possibleMention }) diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php index a577c03d77bc..08dfbe404d48 100644 --- a/tests/integration/features/bootstrap/FeatureContext.php +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -2874,17 +2874,27 @@ public function userGetsTheFollowingCandidateMentionsInRoomFor($user, $identifie Assert::assertRegExp('/^guest\/[0-9a-f]{40}$/', $mentions[$key]['id']); $mentions[$key]['id'] = 'GUEST_ID'; } + if ($row['insert'] === 'GUEST_ID') { + Assert::assertRegExp('/^guest\/[0-9a-f]{40}$/', $mentions[$key]['insert']); + $mentions[$key]['insert'] = 'GUEST_ID'; + } if (str_ends_with($row['id'], '@{$BASE_URL}')) { $row['id'] = str_replace('{$BASE_URL}', rtrim($this->baseUrl, '/'), $row['id']); } if (str_ends_with($row['id'], '@{$REMOTE_URL}')) { $row['id'] = str_replace('{$REMOTE_URL}', rtrim($this->baseRemoteUrl, '/'), $row['id']); } + if (str_ends_with($row['insert'], '@{$BASE_URL}')) { + $row['insert'] = str_replace('{$BASE_URL}', rtrim($this->baseUrl, '/'), $row['insert']); + } + if (str_ends_with($row['insert'], '@{$REMOTE_URL}')) { + $row['insert'] = str_replace('{$REMOTE_URL}', rtrim($this->baseRemoteUrl, '/'), $row['insert']); + } if (array_key_exists('avatar', $row)) { Assert::assertRegExp('/' . self::$identifierToToken[$row['avatar']] . '\/avatar/', $mentions[$key]['avatar']); unset($row['avatar']); } - unset($mentions[$key]['avatar'], ); + unset($mentions[$key]['avatar']); Assert::assertEquals($row, $mentions[$key]); } } diff --git a/tests/integration/features/chat-1/mentions.feature b/tests/integration/features/chat-1/mentions.feature index fcff252362ec..348b70b07fb8 100644 --- a/tests/integration/features/chat-1/mentions.feature +++ b/tests/integration/features/chat-1/mentions.feature @@ -11,22 +11,22 @@ Feature: chat/mentions | roomType | 1 | | invite | participant2 | Then user "participant1" gets the following candidate mentions in room "one-to-one room" for "" with 200 - | id | label | source | - | participant2 | participant2-displayname | users | + | id | label | source | insert | + | participant2 | participant2-displayname | users | participant2 | And user "participant2" gets the following candidate mentions in room "one-to-one room" for "" with 200 - | id | label | source | - | participant1 | participant1-displayname | users | + | id | label | source | insert | + | participant1 | participant1-displayname | users | participant1 | Scenario: get matched mentions in a one-to-one room When user "participant1" creates room "one-to-one room" (v4) | roomType | 1 | | invite | participant2 | Then user "participant1" gets the following candidate mentions in room "one-to-one room" for "part" with 200 - | id | label | source | - | participant2 | participant2-displayname | users | + | id | label | source | insert | + | participant2 | participant2-displayname | users | participant2 | And user "participant2" gets the following candidate mentions in room "one-to-one room" for "part" with 200 - | id | label | source | - | participant1 | participant1-displayname | users | + | id | label | source | insert | + | participant1 | participant1-displayname | users | participant1 | Scenario: get unmatched mentions in a one-to-one room When user "participant1" creates room "one-to-one room" (v4) @@ -48,8 +48,8 @@ Feature: chat/mentions | roomType | 2 | | roomName | room | Then user "participant1" gets the following candidate mentions in room "group room" for "" with 200 - | id | label | source | - | all | room | calls | + | id | label | source | insert | + | all | room | calls | all | Scenario: get mentions in a group room When user "participant1" creates room "group room" (v4) @@ -58,20 +58,20 @@ Feature: chat/mentions And user "participant1" adds user "participant2" to room "group room" with 200 (v4) And user "participant1" adds user "participant3" to room "group room" with 200 (v4) Then user "participant1" gets the following candidate mentions in room "group room" for "" with 200 - | id | label | source | - | all | room | calls | - | participant2 | participant2-displayname | users | - | participant3 | participant3-displayname | users | + | id | label | source | insert | + | all | room | calls | all | + | participant2 | participant2-displayname | users | participant2 | + | participant3 | participant3-displayname | users | participant3 | And user "participant2" gets the following candidate mentions in room "group room" for "" with 200 - | id | label | source | - | all | room | calls | - | participant1 | participant1-displayname | users | - | participant3 | participant3-displayname | users | + | id | label | source | insert | + | all | room | calls | all | + | participant1 | participant1-displayname | users | participant1 | + | participant3 | participant3-displayname | users | participant3 | And user "participant3" gets the following candidate mentions in room "group room" for "" with 200 - | id | label | source | - | all | room | calls | - | participant1 | participant1-displayname | users | - | participant2 | participant2-displayname | users | + | id | label | source | insert | + | all | room | calls | all | + | participant1 | participant1-displayname | users | participant1 | + | participant2 | participant2-displayname | users | participant2 | Scenario: get matched mentions in a group room When user "participant1" creates room "group room" (v4) @@ -80,17 +80,17 @@ Feature: chat/mentions And user "participant1" adds user "participant2" to room "group room" with 200 (v4) And user "participant1" adds user "participant3" to room "group room" with 200 (v4) Then user "participant1" gets the following candidate mentions in room "group room" for "part" with 200 - | id | label | source | - | participant2 | participant2-displayname | users | - | participant3 | participant3-displayname | users | + | id | label | source | insert | + | participant2 | participant2-displayname | users | participant2 | + | participant3 | participant3-displayname | users | participant3 | And user "participant2" gets the following candidate mentions in room "group room" for "part" with 200 - | id | label | source | - | participant1 | participant1-displayname | users | - | participant3 | participant3-displayname | users | + | id | label | source | insert | + | participant1 | participant1-displayname | users | participant1 | + | participant3 | participant3-displayname | users | participant3 | And user "participant3" gets the following candidate mentions in room "group room" for "part" with 200 - | id | label | source | - | participant1 | participant1-displayname | users | - | participant2 | participant2-displayname | users | + | id | label | source | insert | + | participant1 | participant1-displayname | users | participant1 | + | participant2 | participant2-displayname | users | participant2 | Scenario: get unmatched mentions in a group room When user "participant1" creates room "group room" (v4) @@ -117,8 +117,8 @@ Feature: chat/mentions | roomType | 3 | | roomName | room | Then user "participant1" gets the following candidate mentions in room "public room" for "" with 200 - | id | label | source | - | all | room | calls | + | id | label | source | insert | + | all | room | calls | all | Scenario: get mentions in a public room When user "participant1" creates room "public room" (v4) @@ -128,29 +128,29 @@ Feature: chat/mentions And user "participant3" joins room "public room" with 200 (v4) And user "guest" joins room "public room" with 200 (v4) Then user "participant1" gets the following candidate mentions in room "public room" for "" with 200 - | id | label | source | - | all | room | calls | - | participant2 | participant2-displayname | users | - | participant3 | participant3-displayname | users | - | GUEST_ID | Guest | guests | + | id | label | source | insert | + | all | room | calls | all | + | participant2 | participant2-displayname | users | participant2 | + | participant3 | participant3-displayname | users | participant3 | + | GUEST_ID | Guest | guests | GUEST_ID | And user "participant2" gets the following candidate mentions in room "public room" for "" with 200 - | id | label | source | - | all | room | calls | - | participant1 | participant1-displayname | users | - | participant3 | participant3-displayname | users | - | GUEST_ID | Guest | guests | + | id | label | source | insert | + | all | room | calls | all | + | participant1 | participant1-displayname | users | participant1 | + | participant3 | participant3-displayname | users | participant3 | + | GUEST_ID | Guest | guests | GUEST_ID | And user "participant3" gets the following candidate mentions in room "public room" for "" with 200 - | id | label | source | - | all | room | calls | - | participant1 | participant1-displayname | users | - | participant2 | participant2-displayname | users | - | GUEST_ID | Guest | guests | + | id | label | source | insert | + | all | room | calls | all | + | participant1 | participant1-displayname | users | participant1 | + | participant2 | participant2-displayname | users | participant2 | + | GUEST_ID | Guest | guests | GUEST_ID | And user "guest" gets the following candidate mentions in room "public room" for "" with 200 - | id | label | source | - | all | room | calls | - | participant1 | participant1-displayname | users | - | participant2 | participant2-displayname | users | - | participant3 | participant3-displayname | users | + | id | label | source | insert | + | all | room | calls | all | + | participant1 | participant1-displayname | users | participant1 | + | participant2 | participant2-displayname | users | participant2 | + | participant3 | participant3-displayname | users | participant3 | Scenario: get matched mentions in a public room When user "participant1" creates room "public room" (v4) @@ -160,22 +160,22 @@ Feature: chat/mentions And user "participant3" joins room "public room" with 200 (v4) And user "guest" joins room "public room" with 200 (v4) Then user "participant1" gets the following candidate mentions in room "public room" for "part" with 200 - | id | label | source | - | participant2 | participant2-displayname | users | - | participant3 | participant3-displayname | users | + | id | label | source | insert | + | participant2 | participant2-displayname | users | participant2 | + | participant3 | participant3-displayname | users | participant3 | And user "participant2" gets the following candidate mentions in room "public room" for "part" with 200 - | id | label | source | - | participant1 | participant1-displayname | users | - | participant3 | participant3-displayname | users | + | id | label | source | insert | + | participant1 | participant1-displayname | users | participant1 | + | participant3 | participant3-displayname | users | participant3 | And user "participant3" gets the following candidate mentions in room "public room" for "part" with 200 - | id | label | source | - | participant1 | participant1-displayname | users | - | participant2 | participant2-displayname | users | + | id | label | source | insert | + | participant1 | participant1-displayname | users | participant1 | + | participant2 | participant2-displayname | users | participant2 | And user "guest" gets the following candidate mentions in room "public room" for "part" with 200 - | id | label | source | - | participant1 | participant1-displayname | users | - | participant2 | participant2-displayname | users | - | participant3 | participant3-displayname | users | + | id | label | source | insert | + | participant1 | participant1-displayname | users | participant1 | + | participant2 | participant2-displayname | users | participant2 | + | participant3 | participant3-displayname | users | participant3 | Scenario: get matched guest mentions in a public room When user "participant1" creates room "public room" (v4) @@ -186,23 +186,23 @@ Feature: chat/mentions And user "guest1" joins room "public room" with 200 (v4) And user "guest2" joins room "public room" with 200 (v4) Then user "participant1" gets the following candidate mentions in room "public room" for "uest" with 200 - | id | label | source | - | GUEST_ID | Guest | guests | - | GUEST_ID | Guest | guests | + | id | label | source | insert | + | GUEST_ID | Guest | guests | GUEST_ID | + | GUEST_ID | Guest | guests | GUEST_ID | And user "participant2" gets the following candidate mentions in room "public room" for "uest" with 200 - | id | label | source | - | GUEST_ID | Guest | guests | - | GUEST_ID | Guest | guests | + | id | label | source | insert | + | GUEST_ID | Guest | guests | GUEST_ID | + | GUEST_ID | Guest | guests | GUEST_ID | And user "participant3" gets the following candidate mentions in room "public room" for "uest" with 200 - | id | label | source | - | GUEST_ID | Guest | guests | - | GUEST_ID | Guest | guests | + | id | label | source | insert | + | GUEST_ID | Guest | guests | GUEST_ID | + | GUEST_ID | Guest | guests | GUEST_ID | And user "guest1" gets the following candidate mentions in room "public room" for "uest" with 200 - | id | label | source | - | GUEST_ID | Guest | guests | + | id | label | source | insert | + | GUEST_ID | Guest | guests | GUEST_ID | And user "guest2" gets the following candidate mentions in room "public room" for "uest" with 200 - | id | label | source | - | GUEST_ID | Guest | guests | + | id | label | source | insert | + | GUEST_ID | Guest | guests | GUEST_ID | Scenario: get matched named guest mentions in a public room When user "participant1" creates room "public room" (v4) @@ -214,19 +214,19 @@ Feature: chat/mentions And guest "guest1" sets name to "FooBar" in room "public room" with 200 And user "guest2" joins room "public room" with 200 (v4) Then user "participant1" gets the following candidate mentions in room "public room" for "oob" with 200 - | id | label | source | - | GUEST_ID | FooBar | guests | + | id | label | source | insert | + | GUEST_ID | FooBar | guests | GUEST_ID | And user "participant2" gets the following candidate mentions in room "public room" for "oob" with 200 - | id | label | source | - | GUEST_ID | FooBar | guests | + | id | label | source | insert | + | GUEST_ID | FooBar | guests | GUEST_ID | And user "participant3" gets the following candidate mentions in room "public room" for "oob" with 200 - | id | label | source | - | GUEST_ID | FooBar | guests | + | id | label | source | insert | + | GUEST_ID | FooBar | guests | GUEST_ID | And user "guest1" gets the following candidate mentions in room "public room" for "oob" with 200 - | id | label | source | + | id | label | source | insert | And user "guest2" gets the following candidate mentions in room "public room" for "oob" with 200 - | id | label | source | - | GUEST_ID | FooBar | guests | + | id | label | source | insert | + | GUEST_ID | FooBar | guests | GUEST_ID | Scenario: get unmatched mentions in a public room When user "participant1" creates room "public room" (v4) @@ -260,9 +260,9 @@ Feature: chat/mentions And user "participant1" is participant of room "file welcome.txt room" (v4) And user "participant2" is not participant of room "file welcome.txt room" (v4) Then user "participant1" gets the following candidate mentions in room "file welcome.txt room" for "" with 200 - | id | label | source | - | all | welcome.txt | calls | - | participant2 | participant2-displayname | users | + | id | label | source | insert | + | all | welcome.txt | calls | all | + | participant2 | participant2-displayname | users | participant2 | And user "participant2" gets the following candidate mentions in room "file welcome.txt room" for "" with 404 Scenario: get mentions in a file room @@ -274,13 +274,13 @@ Feature: chat/mentions And user "participant1" is participant of room "file welcome (2).txt room" (v4) And user "participant2" is participant of room "file welcome (2).txt room" (v4) Then user "participant1" gets the following candidate mentions in room "file welcome (2).txt room" for "" with 200 - | id | label | source | - | all | welcome (2).txt | calls | - | participant2 | participant2-displayname | users | + | id | label | source | insert | + | all | welcome (2).txt | calls | all | + | participant2 | participant2-displayname | users | participant2 | And user "participant2" gets the following candidate mentions in room "file welcome (2).txt room" for "" with 200 - | id | label | source | - | all | welcome (2).txt | calls | - | participant1 | participant1-displayname | users | + | id | label | source | insert | + | all | welcome (2).txt | calls | all | + | participant1 | participant1-displayname | users | participant1 | Scenario: get matched mentions in a file room Given user "participant1" shares "welcome.txt" with user "participant2" with OCS 100 @@ -291,11 +291,11 @@ Feature: chat/mentions And user "participant1" is participant of room "file welcome (2).txt room" (v4) And user "participant2" is participant of room "file welcome (2).txt room" (v4) Then user "participant1" gets the following candidate mentions in room "file welcome (2).txt room" for "part" with 200 - | id | label | source | - | participant2 | participant2-displayname | users | + | id | label | source | insert | + | participant2 | participant2-displayname | users | participant2 | And user "participant2" gets the following candidate mentions in room "file welcome (2).txt room" for "part" with 200 - | id | label | source | - | participant1 | participant1-displayname | users | + | id | label | source | insert | + | participant1 | participant1-displayname | users | participant1 | Scenario: get unmatched mentions in a file room Given user "participant1" shares "welcome.txt" with user "participant2" with OCS 100 @@ -339,9 +339,9 @@ Feature: chat/mentions And user "participant1" is participant of room "file last share room" (v4) And user "participant2" is not participant of room "file last share room" (v4) Then user "participant1" gets the following candidate mentions in room "file last share room" for "" with 200 - | id | label | source | - | all | welcome.txt | calls | - | participant2 | participant2-displayname | users | + | id | label | source | insert | + | all | welcome.txt | calls | all | + | participant2 | participant2-displayname | users | participant2 | And user "participant2" gets the following candidate mentions in room "file last share room" for "" with 404 And user "participant3" gets the following candidate mentions in room "file last share room" for "" with 404 And user "guest" gets the following candidate mentions in room "file last share room" for "" with 404 @@ -370,35 +370,35 @@ Feature: chat/mentions And user "participant3" is participant of room "file last share room" (v4) And user "guest" is participant of room "file last share room" (v4) Then user "participant1" gets the following candidate mentions in room "file last share room" for "" with 200 - | id | label | source | - | all | welcome.txt | calls | - | participant2 | participant2-displayname | users | - | participant4 | participant4-displayname | users | - | participant3 | participant3-displayname | users | - | GUEST_ID | Guest | guests | + | id | label | source | insert | + | all | welcome.txt | calls | all | + | participant2 | participant2-displayname | users | participant2 | + | participant4 | participant4-displayname | users | participant4 | + | participant3 | participant3-displayname | users | participant3 | + | GUEST_ID | Guest | guests | GUEST_ID | And user "participant2" gets the following candidate mentions in room "file last share room" for "" with 200 - | id | label | source | - | all | welcome.txt | calls | - | participant1 | participant1-displayname | users | - | participant4 | participant4-displayname | users | - | participant3 | participant3-displayname | users | - | GUEST_ID | Guest | guests | + | id | label | source | insert | + | all | welcome.txt | calls | all | + | participant1 | participant1-displayname | users | participant1 | + | participant4 | participant4-displayname | users | participant4 | + | participant3 | participant3-displayname | users | participant3 | + | GUEST_ID | Guest | guests | GUEST_ID | # Self-joined users can not mention users with access to the file that have # not joined the room. And user "participant3" gets the following candidate mentions in room "file last share room" for "" with 200 - | id | label | source | - | all | welcome.txt | calls | - | participant1 | participant1-displayname | users | - | participant2 | participant2-displayname | users | - | GUEST_ID | Guest | guests | + | id | label | source | insert | + | all | welcome.txt | calls | all | + | participant1 | participant1-displayname | users | participant1 | + | participant2 | participant2-displayname | users | participant2 | + | GUEST_ID | Guest | guests | GUEST_ID | # Guests can not mention users with access to the file that have not joined # the room. And user "guest" gets the following candidate mentions in room "file last share room" for "" with 200 - | id | label | source | - | all | welcome.txt | calls | - | participant1 | participant1-displayname | users | - | participant2 | participant2-displayname | users | - | participant3 | participant3-displayname | users | + | id | label | source | insert | + | all | welcome.txt | calls | all | + | participant1 | participant1-displayname | users | participant1 | + | participant2 | participant2-displayname | users | participant2 | + | participant3 | participant3-displayname | users | participant3 | Scenario: get matched mentions in a room for a file shared by link Given user "participant1" shares "welcome.txt" with user "participant2" with OCS 100 @@ -424,28 +424,28 @@ Feature: chat/mentions And user "participant3" is participant of room "file last share room" (v4) And user "guest" is participant of room "file last share room" (v4) Then user "participant1" gets the following candidate mentions in room "file last share room" for "part" with 200 - | id | label | source | - | participant2 | participant2-displayname | users | - | participant4 | participant4-displayname | users | - | participant3 | participant3-displayname | users | + | id | label | source | insert | + | participant2 | participant2-displayname | users | participant2 | + | participant4 | participant4-displayname | users | participant4 | + | participant3 | participant3-displayname | users | participant3 | And user "participant2" gets the following candidate mentions in room "file last share room" for "part" with 200 - | id | label | source | - | participant1 | participant1-displayname | users | - | participant4 | participant4-displayname | users | - | participant3 | participant3-displayname | users | + | id | label | source | insert | + | participant1 | participant1-displayname | users | participant1 | + | participant4 | participant4-displayname | users | participant4 | + | participant3 | participant3-displayname | users | participant3 | # Self-joined users can not mention users with access to the file that have # not joined the room. And user "participant3" gets the following candidate mentions in room "file last share room" for "part" with 200 - | id | label | source | - | participant1 | participant1-displayname | users | - | participant2 | participant2-displayname | users | + | id | label | source | insert | + | participant1 | participant1-displayname | users | participant1 | + | participant2 | participant2-displayname | users | participant2 | # Guests can not mention users with access to the file that have not joined # the room. And user "guest" gets the following candidate mentions in room "file last share room" for "part" with 200 - | id | label | source | - | participant1 | participant1-displayname | users | - | participant2 | participant2-displayname | users | - | participant3 | participant3-displayname | users | + | id | label | source | insert | + | participant1 | participant1-displayname | users | participant1 | + | participant2 | participant2-displayname | users | participant2 | + | participant3 | participant3-displayname | users | participant3 | Scenario: get unmatched mentions in a room for a file shared by link Given user "participant1" shares "welcome.txt" with user "participant2" with OCS 100 diff --git a/tests/integration/features/federation/chat.feature b/tests/integration/features/federation/chat.feature index d6afbb23902e..2220b082934a 100644 --- a/tests/integration/features/federation/chat.feature +++ b/tests/integration/features/federation/chat.feature @@ -22,15 +22,15 @@ Feature: federation/chat | id | type | | room | 2 | And user "participant1" gets the following candidate mentions in room "room" for "" with 200 - | source | id | label | - | calls | all | room | - | federated_users | participant2@{$REMOTE_URL} | participant2@localhost:8180 | - | users | participant3 | participant3-displayname | + | source | id | label | insert | + | calls | all | room | all | + | federated_users | participant2@{$REMOTE_URL} | participant2@localhost:8180 | federated_user/participant2@{$REMOTE_URL} | + | users | participant3 | participant3-displayname | participant3 | And user "participant2" gets the following candidate mentions in room "LOCAL::room" for "" with 200 - | source | id | label | - | calls | all | room | - | federated_users | participant1@{$BASE_URL} | participant1-displayname | - | federated_users | participant3@{$BASE_URL} | participant3-displayname | + | source | id | label | insert | + | calls | all | room | all | + | federated_users | participant1@{$BASE_URL} | participant1-displayname | participant1 | + | federated_users | participant3@{$BASE_URL} | participant3-displayname | participant3 | Scenario: Get mention suggestions (translating federated users of the same server to local users) Given the following "spreed" app config is set @@ -56,15 +56,15 @@ Feature: federation/chat | id | type | | room | 2 | And user "participant1" gets the following candidate mentions in room "room" for "" with 200 - | source | id | label | - | calls | all | room | - | federated_users | participant2@{$REMOTE_URL} | participant2@localhost:8180 | - | federated_users | participant3@{$REMOTE_URL} | participant3@localhost:8180 | + | source | id | label | insert | + | calls | all | room | all | + | federated_users | participant2@{$REMOTE_URL} | participant2@localhost:8180 | federated_user/participant2@{$REMOTE_URL} | + | federated_users | participant3@{$REMOTE_URL} | participant3@localhost:8180 | federated_user/participant3@{$REMOTE_URL} | And user "participant2" gets the following candidate mentions in room "LOCAL::room" for "" with 200 - | source | id | label | - | calls | all | room | - | federated_users | participant1@{$BASE_URL} | participant1-displayname | - | users | participant3 | participant3-displayname | + | source | id | label | insert | + | calls | all | room | all | + | federated_users | participant1@{$BASE_URL} | participant1-displayname | participant1 | + | users | participant3 | participant3-displayname | federated_user/participant3@{$REMOTE_URL} | Scenario: Basic chatting including posting, getting, editing and deleting Given the following "spreed" app config is set diff --git a/tests/php/Chat/ChatManagerTest.php b/tests/php/Chat/ChatManagerTest.php index 93fa9593265b..330434f7df9d 100644 --- a/tests/php/Chat/ChatManagerTest.php +++ b/tests/php/Chat/ChatManagerTest.php @@ -717,7 +717,7 @@ public static function dataAddConversationNotify(): array { 'actor_type' => Attendee::ACTOR_USERS, 'actor_id' => 'user', ])], - [['id' => 'all', 'label' => 'test', 'source' => 'calls']] + [['id' => 'all', 'label' => 'test', 'source' => 'calls', 'insert' => 'all']] ], [ 'all', @@ -726,7 +726,7 @@ public static function dataAddConversationNotify(): array { 'actor_type' => Attendee::ACTOR_USERS, 'actor_id' => 'user', ])], - [['id' => 'all', 'label' => 'test', 'source' => 'calls']] + [['id' => 'all', 'label' => 'test', 'source' => 'calls', 'insert' => 'all']] ], [ 'here', @@ -735,7 +735,7 @@ public static function dataAddConversationNotify(): array { 'actor_type' => Attendee::ACTOR_GUESTS, 'actor_id' => 'guest', ])], - [['id' => 'all', 'label' => 'test', 'source' => 'calls']] + [['id' => 'all', 'label' => 'test', 'source' => 'calls', 'insert' => 'all']] ] ]; } diff --git a/tests/php/Chat/Parser/UserMentionTest.php b/tests/php/Chat/Parser/UserMentionTest.php index 599c1029644b..2cb4fd44302d 100644 --- a/tests/php/Chat/Parser/UserMentionTest.php +++ b/tests/php/Chat/Parser/UserMentionTest.php @@ -35,6 +35,8 @@ use OCA\Talk\Service\ParticipantService; use OCP\Comments\IComment; use OCP\Comments\ICommentsManager; +use OCP\Federation\ICloudId; +use OCP\Federation\ICloudIdManager; use OCP\IGroupManager; use OCP\IL10N; use OCP\IUserManager; @@ -52,6 +54,7 @@ class UserMentionTest extends TestCase { protected $guestManager; /** @var AvatarService|MockObject */ protected $avatarService; + protected ICloudIdManager|MockObject $cloudIdManager; /** @var ParticipantService|MockObject */ protected $participantService; /** @var IL10N|MockObject */ @@ -67,6 +70,7 @@ public function setUp(): void { $this->groupManager = $this->createMock(IGroupManager::class); $this->guestManager = $this->createMock(GuestManager::class); $this->avatarService = $this->createMock(AvatarService::class); + $this->cloudIdManager = $this->createMock(ICloudIdManager::class); $this->participantService = $this->createMock(ParticipantService::class); $this->l = $this->createMock(IL10N::class); @@ -76,8 +80,10 @@ public function setUp(): void { $this->groupManager, $this->guestManager, $this->avatarService, + $this->cloudIdManager, $this->participantService, - $this->l); + $this->l, + ); } /** @@ -442,6 +448,47 @@ public function testGetRichMessageWithAtAll(): void { $this->assertEquals($expectedMessageParameters, $chatMessage->getMessageParameters()); } + public function testGetRichMessageWithFederatedUserMention(): void { + $mentions = [ + ['type' => 'federated_user', 'id' => 'testUser@example.tld'], + ]; + $comment = $this->newComment($mentions); + + /** @var Room|MockObject $room */ + $room = $this->createMock(Room::class); + /** @var Participant|MockObject $participant */ + $participant = $this->createMock(Participant::class); + /** @var IL10N|MockObject $l */ + $l = $this->createMock(IL10N::class); + $chatMessage = new Message($room, $participant, $comment, $l); + $chatMessage->setMessage('Mention to @"federated_user/testUser@example.tld"', []); + + $cloudId = $this->createMock(ICloudId::class); + $cloudId->method('getUser') + ->willReturn('testUser'); + $cloudId->method('getRemote') + ->willReturn('example.tld'); + $cloudId->method('getDisplayId') + ->willReturn('Display Id'); + $this->cloudIdManager->method('resolveCloudId') + ->with('testUser@example.tld') + ->willReturn($cloudId); + + self::invokePrivate($this->parser, 'parseMessage', [$chatMessage]); + + $expectedMessageParameters = [ + 'mention-federated-user1' => [ + 'type' => 'user', + 'id' => 'testUser', + 'name' => 'Display Id', + 'server' => 'example.tld', + ] + ]; + + $this->assertEquals('Mention to {mention-federated-user1}', $chatMessage->getMessage()); + $this->assertEquals($expectedMessageParameters, $chatMessage->getMessageParameters()); + } + public function testGetRichMessageWhenAGuestWithoutNameIsMentioned(): void { $mentions = [ ['type' => 'guest', 'id' => 'guest/123456'], diff --git a/tests/php/Controller/ChatControllerTest.php b/tests/php/Controller/ChatControllerTest.php index 1ddb994b79d9..db408ed3a919 100644 --- a/tests/php/Controller/ChatControllerTest.php +++ b/tests/php/Controller/ChatControllerTest.php @@ -1106,18 +1106,28 @@ public function testWaitForNewMessagesTimeoutTooLarge() { public static function dataMentions() { return [ ['tes', 10, ['exact' => []], []], - ['foo', 20, [ - 'exact' => [ + [ + 'foo', + 20, + [ + 'exact' => [ + 'users' => [ + ['label' => 'Foo Bar', 'value' => ['shareWith' => 'foo', 'shareType' => 'user']], + ] + ], 'users' => [ - ['label' => 'Foo Bar', 'value' => ['shareWith' => 'foo', 'shareType' => 'user']], + ['label' => 'FooBar', 'value' => ['shareWith' => 'foobar', 'shareType' => 'user']], + ], + 'federated_users' => [ + ['label' => 'Fedi User', 'value' => ['shareWith' => 'foobar@example.tld', 'shareType' => 'federated_user']], ] ], - 'users' => [ - ['label' => 'FooBar', 'value' => ['shareWith' => 'foobar', 'shareType' => 'user']], - ]], [ - ['id' => 'foo', 'label' => 'Foo Bar', 'source' => 'users'], - ['id' => 'foobar', 'label' => 'FooBar', 'source' => 'users'], - ]], + [ + ['id' => 'foo', 'label' => 'Foo Bar', 'source' => 'users', 'insert' => 'foo'], + ['id' => 'foobar', 'label' => 'FooBar', 'source' => 'users', 'insert' => 'foobar'], + ['id' => 'foobar@example.tld', 'label' => 'Fedi User', 'source' => 'federated_users', 'insert' => 'federated_user/foobar@example.tld'], + ] + ], ]; }