diff --git a/lib/Chat/ChatManager.php b/lib/Chat/ChatManager.php index 0abeab6806c..1c2dc7562bf 100644 --- a/lib/Chat/ChatManager.php +++ b/lib/Chat/ChatManager.php @@ -149,6 +149,7 @@ public function addSystemMessage( ?IComment $replyTo = null, bool $shouldSkipLastMessageUpdate = false, bool $silent = false, + int $threadId = 0, ): IComment { if ($chat->isFederatedConversation()) { $e = new MessagingNotAllowedException(); @@ -167,6 +168,8 @@ public function addSystemMessage( } if ($replyTo !== null) { $comment->setParentId($replyTo->getId()); + } elseif ($threadId !== 0) { + $comment->setParentId((string)$threadId); } $messageDecoded = json_decode($message, true); diff --git a/lib/Chat/Parser/SystemMessage.php b/lib/Chat/Parser/SystemMessage.php index beb57a10935..afde3b767fe 100644 --- a/lib/Chat/Parser/SystemMessage.php +++ b/lib/Chat/Parser/SystemMessage.php @@ -574,11 +574,24 @@ protected function parseMessage(Message $chatMessage, $allowInaccurate): void { } } elseif ($message === 'thread_created') { $parsedMessage = $this->l->t('{actor} created thread {title}'); + if ($currentUserIsActor) { + $parsedMessage = $this->l->t('You created thread {title}'); + } $parsedParameters['title'] = [ 'type' => 'highlight', 'id' => 'thread/' . $parameters['thread'], 'name' => $parameters['title'] ?? (string)$parameters['thread'], ]; + } elseif ($message === 'thread_renamed') { + $parsedMessage = $this->l->t('{actor} renamed thread {title}'); + if ($currentUserIsActor) { + $parsedMessage = $this->l->t('You renamed thread {title}'); + } + $parsedParameters['title'] = [ + 'type' => 'highlight', + 'id' => 'thread/' . $parameters['thread'], + 'name' => $parameters['title'], + ]; } elseif ($message === 'matterbridge_config_edited') { $parsedMessage = $this->l->t('{actor} updated the Matterbridge configuration'); if ($currentUserIsActor) { diff --git a/lib/Chat/SystemMessage/Listener.php b/lib/Chat/SystemMessage/Listener.php index 4b5858cedaa..ec26301afec 100644 --- a/lib/Chat/SystemMessage/Listener.php +++ b/lib/Chat/SystemMessage/Listener.php @@ -419,12 +419,12 @@ protected function fixMimeTypeOfVoiceMessage(ShareCreatedEvent|BeforeDuplicateSh ); if ($threadTitle !== '' && $comment->getTopmostParentId() === '0') { - $this->threadService->createThread($room, (int)$comment->getId(), $threadTitle); + $thread = $this->threadService->createThread($room, (int)$comment->getId(), $threadTitle); $this->sendSystemMessage( $room, 'thread_created', - ['thread' => (int)$comment->getId(), 'title' => $threadTitle], + ['thread' => (int)$comment->getId(), 'title' => $thread->getName()], shouldSkipLastMessageUpdate: true, silent: true, parent: $comment, diff --git a/lib/Controller/ChatController.php b/lib/Controller/ChatController.php index 9e6da96e5c7..ed19f22a71e 100644 --- a/lib/Controller/ChatController.php +++ b/lib/Controller/ChatController.php @@ -264,13 +264,13 @@ public function sendMessage(string $message, string $actorDisplayName = '', stri try { $comment = $this->chatManager->sendMessage($this->room, $this->participant, $actorType, $actorId, $message, $creationDateTime, $parent, $referenceId, $silent); if ($replyTo === 0 && $threadTitle !== '') { - $this->threadService->createThread($this->room, (int)$comment->getId(), $threadTitle); + $thread = $this->threadService->createThread($this->room, (int)$comment->getId(), $threadTitle); $this->chatManager->addSystemMessage( $this->room, $this->participant->getAttendee()->getActorType(), $this->participant->getAttendee()->getActorId(), - json_encode(['message' => 'thread_created', 'parameters' => ['thread' => (int)$comment->getId(), 'title' => $threadTitle]]), + json_encode(['message' => 'thread_created', 'parameters' => ['thread' => (int)$comment->getId(), 'title' => $thread->getName()]]), $this->timeFactory->getDateTime(), false, null, diff --git a/lib/Controller/ThreadController.php b/lib/Controller/ThreadController.php index 043420c7236..b46eb6cf698 100644 --- a/lib/Controller/ThreadController.php +++ b/lib/Controller/ThreadController.php @@ -28,6 +28,7 @@ use OCP\AppFramework\Http\Attribute\RequestHeader; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Comments\NotFoundException; use OCP\EventDispatcher\IEventDispatcher; use OCP\IL10N; use OCP\IRequest; @@ -128,6 +129,83 @@ public function getThread(int $threadId): DataResponse { return new DataResponse($threadInfo); } + /** + * Rename a thread + * + * Required capability: `threads` + * + * @param int $threadId The thread ID to get the info for + * @psalm-param non-negative-int $threadId + * @param string $threadTitle New thread title, must not be empty + * @return DataResponse|DataResponse|DataResponse + * + * 200: Thread renamed successfully + * 400: When the provided title is empty + * 404: Thread not found + */ + #[FederationSupported] + #[PublicPage] + #[RequireModeratorOrNoLobby] + #[RequireParticipant] + #[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)] + #[ApiRoute(verb: 'PUT', url: '/api/{apiVersion}/chat/{token}/threads/{threadId}', requirements: [ + 'apiVersion' => '(v1)', + 'token' => '[a-z0-9]{4,30}', + 'threadId' => '[0-9]+', + ])] + public function renameThread(int $threadId, string $threadTitle): DataResponse { + $threadTitle = trim($threadTitle); + if ($this->room->isFederatedConversation()) { + /** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\ThreadController $proxy */ + $proxy = \OCP\Server::get(\OCA\Talk\Federation\Proxy\TalkV1\Controller\ThreadController::class); + return $proxy->renameThread($this->room, $this->participant, $threadId, $threadTitle); + } + + try { + $thread = $this->threadService->findByThreadId($threadId); + } catch (DoesNotExistException) { + return new DataResponse(['error' => 'thread'], Http::STATUS_NOT_FOUND); + } + + if ($thread->getRoomId() !== $this->room->getId()) { + return new DataResponse(['error' => 'thread'], Http::STATUS_NOT_FOUND); + } + + # FIXME Only allow for moderator and original author + + try { + $this->threadService->renameThread($thread, $threadTitle); + } catch (\InvalidArgumentException $e) { + return new DataResponse(['error' => 'title'], Http::STATUS_BAD_REQUEST); + } + + try { + $comment = $this->chatManager->getComment($this->room, (string)$threadId); + } catch (NotFoundException) { + // Root message expired, continuing without replying + $comment = null; + } + + $this->chatManager->addSystemMessage( + $this->room, + $this->participant->getAttendee()->getActorType(), + $this->participant->getAttendee()->getActorId(), + json_encode(['message' => 'thread_renamed', 'parameters' => ['thread' => $threadId, 'title' => $thread->getName()]]), + $this->timeFactory->getDateTime(), + false, + null, + $comment, + true, + true, + $threadId, + ); + + $list = $this->prepareListOfThreads([$thread]); + /** @var TalkThreadInfo $threadInfo */ + $threadInfo = array_shift($list); + return new DataResponse($threadInfo); + } + /** * @param list $threads * @param ?list $attendees diff --git a/lib/Federation/Proxy/TalkV1/Controller/ThreadController.php b/lib/Federation/Proxy/TalkV1/Controller/ThreadController.php index 647e8932b37..afcfadb3091 100644 --- a/lib/Federation/Proxy/TalkV1/Controller/ThreadController.php +++ b/lib/Federation/Proxy/TalkV1/Controller/ThreadController.php @@ -101,6 +101,55 @@ public function getThread(Room $room, Participant $participant, int $threadId): return new DataResponse($data, Http::STATUS_OK); } + /** + * @see \OCA\Talk\Controller\ThreadController::setNotificationLevel() + * + * @psalm-param non-negative-int $threadId + * @return DataResponse|DataResponse|DataResponse + * @throws CannotReachRemoteException + * + * 200: Thread renamed successfully + * 400: When the provided title is empty + * 404: Thread not found + */ + public function renameThread(Room $room, Participant $participant, int $threadId, string $threadTitle): DataResponse { + $proxy = $this->proxy->put( + $participant->getAttendee()->getInvitedCloudId(), + $participant->getAttendee()->getAccessToken(), + $room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/chat/' . $room->getRemoteToken() . '/threads/' . $threadId, + ['threadTitle' => $threadTitle], + ); + + $statusCode = $proxy->getStatusCode(); + if ($statusCode !== Http::STATUS_OK) { + if (!in_array($statusCode, [ + Http::STATUS_BAD_REQUEST, + Http::STATUS_NOT_FOUND, + ], true)) { + $statusCode = $this->proxy->logUnexpectedStatusCode(__METHOD__, $statusCode); + $data = ['error' => 'thread']; + } elseif ($statusCode === Http::STATUS_BAD_REQUEST) { + /** @var array{error: 'title'} $data */ + $data = $this->proxy->getOCSData($proxy, [ + Http::STATUS_BAD_REQUEST, + ]); + } else { + /** @var array{error: 'thread'} $data */ + $data = $this->proxy->getOCSData($proxy, [ + Http::STATUS_NOT_FOUND, + ]); + } + return new DataResponse($data, $statusCode); + } + + /** @var TalkThreadInfo $data */ + $data = $this->proxy->getOCSData($proxy); + $data = $this->userConverter->convertThreadInfo($room, $data); + + return new DataResponse($data, Http::STATUS_OK); + } + + /** * @see \OCA\Talk\Controller\ThreadController::setNotificationLevel() * diff --git a/lib/Service/ThreadService.php b/lib/Service/ThreadService.php index 435d58360f6..6ea64e8c275 100644 --- a/lib/Service/ThreadService.php +++ b/lib/Service/ThreadService.php @@ -31,6 +31,9 @@ public function __construct( } public function createThread(Room $room, int $threadId, string $title): Thread { + if (mb_strlen($title) > 203) { + $title = mb_substr($title, 0, 200) . '…'; + } $thread = new Thread(); $thread->setId($threadId); $thread->setName($title); @@ -49,6 +52,22 @@ public function findByThreadId(int $threadId): Thread { return $this->threadMapper->findById($threadId); } + /** + * @throws \InvalidArgumentException When the title is empty + */ + public function renameThread(Thread $thread, string $title): Thread { + if ($title === '') { + throw new \InvalidArgumentException('name'); + } + if (mb_strlen($title) > 203) { + $title = mb_substr($title, 0, 200) . '…'; + } + $thread->setName($title); + $this->threadMapper->update($thread); + + return $thread; + } + /** * @param int<1, 50> $limit * @return list diff --git a/openapi-full.json b/openapi-full.json index fd28c32970e..d8545f2e055 100644 --- a/openapi-full.json +++ b/openapi-full.json @@ -21122,6 +21122,208 @@ } } } + }, + "put": { + "operationId": "thread-rename-thread", + "summary": "Rename a thread", + "description": "Required capability: `threads`", + "tags": [ + "thread" + ], + "security": [ + {}, + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "threadTitle" + ], + "properties": { + "threadTitle": { + "type": "string", + "description": "New thread title, must not be empty" + } + } + } + } + } + }, + "parameters": [ + { + "name": "apiVersion", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "v1" + ], + "default": "v1" + } + }, + { + "name": "token", + "in": "path", + "required": true, + "schema": { + "type": "string", + "pattern": "^[a-z0-9]{4,30}$" + } + }, + { + "name": "threadId", + "in": "path", + "description": "The thread ID to get the info for", + "required": true, + "schema": { + "type": "integer", + "format": "int64", + "minimum": 0 + } + }, + { + "name": "x-nextcloud-federation", + "in": "header", + "description": "Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request", + "schema": { + "type": "string" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Thread renamed successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "$ref": "#/components/schemas/ThreadInfo" + } + } + } + } + } + } + } + }, + "400": { + "description": "When the provided title is empty", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string", + "enum": [ + "title" + ] + } + } + } + } + } + } + } + } + } + }, + "404": { + "description": "Thread not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string", + "enum": [ + "thread" + ] + } + } + } + } + } + } + } + } + } + } + } } }, "/ocs/v2.php/apps/spreed/api/{apiVersion}/chat/{token}/threads/{messageId}/notify": { diff --git a/openapi.json b/openapi.json index de5f8ccdc6c..b4c5a209798 100644 --- a/openapi.json +++ b/openapi.json @@ -21027,6 +21027,208 @@ } } } + }, + "put": { + "operationId": "thread-rename-thread", + "summary": "Rename a thread", + "description": "Required capability: `threads`", + "tags": [ + "thread" + ], + "security": [ + {}, + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "threadTitle" + ], + "properties": { + "threadTitle": { + "type": "string", + "description": "New thread title, must not be empty" + } + } + } + } + } + }, + "parameters": [ + { + "name": "apiVersion", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "v1" + ], + "default": "v1" + } + }, + { + "name": "token", + "in": "path", + "required": true, + "schema": { + "type": "string", + "pattern": "^[a-z0-9]{4,30}$" + } + }, + { + "name": "threadId", + "in": "path", + "description": "The thread ID to get the info for", + "required": true, + "schema": { + "type": "integer", + "format": "int64", + "minimum": 0 + } + }, + { + "name": "x-nextcloud-federation", + "in": "header", + "description": "Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request", + "schema": { + "type": "string" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Thread renamed successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "$ref": "#/components/schemas/ThreadInfo" + } + } + } + } + } + } + } + }, + "400": { + "description": "When the provided title is empty", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string", + "enum": [ + "title" + ] + } + } + } + } + } + } + } + } + } + }, + "404": { + "description": "Thread not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string", + "enum": [ + "thread" + ] + } + } + } + } + } + } + } + } + } + } + } } }, "/ocs/v2.php/apps/spreed/api/{apiVersion}/chat/{token}/threads/{messageId}/notify": { diff --git a/src/types/openapi/openapi-full.ts b/src/types/openapi/openapi-full.ts index e91cfc5ca32..51f3af9679c 100644 --- a/src/types/openapi/openapi-full.ts +++ b/src/types/openapi/openapi-full.ts @@ -1625,7 +1625,11 @@ export type paths = { * @description Required capability: `threads` */ get: operations["thread-get-thread"]; - put?: never; + /** + * Rename a thread + * @description Required capability: `threads` + */ + put: operations["thread-rename-thread"]; post?: never; delete?: never; options?: never; @@ -10324,6 +10328,82 @@ export interface operations { }; }; }; + "thread-rename-thread": { + parameters: { + query?: never; + header: { + /** @description Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request */ + "x-nextcloud-federation"?: string; + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + apiVersion: "v1"; + token: string; + /** @description The thread ID to get the info for */ + threadId: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @description New thread title, must not be empty */ + threadTitle: string; + }; + }; + }; + responses: { + /** @description Thread renamed successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ocs: { + meta: components["schemas"]["OCSMeta"]; + data: components["schemas"]["ThreadInfo"]; + }; + }; + }; + }; + /** @description When the provided title is empty */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ocs: { + meta: components["schemas"]["OCSMeta"]; + data: { + /** @enum {string} */ + error: "title"; + }; + }; + }; + }; + }; + /** @description Thread not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ocs: { + meta: components["schemas"]["OCSMeta"]; + data: { + /** @enum {string} */ + error: "thread"; + }; + }; + }; + }; + }; + }; + }; "thread-set-notification-level": { parameters: { query?: never; diff --git a/src/types/openapi/openapi.ts b/src/types/openapi/openapi.ts index b6ab4f5e9e8..8d7b9c17278 100644 --- a/src/types/openapi/openapi.ts +++ b/src/types/openapi/openapi.ts @@ -1625,7 +1625,11 @@ export type paths = { * @description Required capability: `threads` */ get: operations["thread-get-thread"]; - put?: never; + /** + * Rename a thread + * @description Required capability: `threads` + */ + put: operations["thread-rename-thread"]; post?: never; delete?: never; options?: never; @@ -9786,6 +9790,82 @@ export interface operations { }; }; }; + "thread-rename-thread": { + parameters: { + query?: never; + header: { + /** @description Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request */ + "x-nextcloud-federation"?: string; + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + apiVersion: "v1"; + token: string; + /** @description The thread ID to get the info for */ + threadId: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @description New thread title, must not be empty */ + threadTitle: string; + }; + }; + }; + responses: { + /** @description Thread renamed successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ocs: { + meta: components["schemas"]["OCSMeta"]; + data: components["schemas"]["ThreadInfo"]; + }; + }; + }; + }; + /** @description When the provided title is empty */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ocs: { + meta: components["schemas"]["OCSMeta"]; + data: { + /** @enum {string} */ + error: "title"; + }; + }; + }; + }; + }; + /** @description Thread not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ocs: { + meta: components["schemas"]["OCSMeta"]; + data: { + /** @enum {string} */ + error: "thread"; + }; + }; + }; + }; + }; + }; + }; "thread-set-notification-level": { parameters: { query?: never; diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php index d038c34cd1e..0edfa3788df 100644 --- a/tests/integration/features/bootstrap/FeatureContext.php +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -1969,6 +1969,19 @@ public function userPostThreadToRoom(string $user, string $sendingMode, string $ } } + #[Then('/^user "([^"]*)" renames thread "([^"]*)" to "([^"]*)" in room "([^"]*)" with (\d+)(?: \((v1)\))?$/')] + public function userRenamesThreadInRoom(string $user, string $oldTitle, string $newTitle, string $identifier, int $statusCode, string $apiVersion = 'v1'): void { + $threadId = self::$titleToThreadId[$oldTitle]; + $this->setCurrentUser($user); + $this->sendRequest( + 'PUT', '/apps/spreed/api/' . $apiVersion . '/chat/' . self::$identifierToToken[$identifier] . '/threads/' . $threadId, + new TableNode([['threadTitle', $newTitle]]) + ); + $this->assertStatusCode($this->response, $statusCode); + self::$titleToThreadId[$newTitle] = $threadId; + self::$threadIdToTitle[$threadId] = $newTitle; + } + #[Then('/^user "([^"]*)" edits message ("[^"]*"|\'[^\']*\') in room "([^"]*)" to ("[^"]*"|\'[^\']*\') with (\d+)(?: \((v1)\))?$/')] public function userEditsMessageToRoom(string $user, string $oldMessage, string $identifier, string $newMessage, int $statusCode, string $apiVersion = 'v1', ?TableNode $formData = null): void { $oldMessage = substr($oldMessage, 1, -1); diff --git a/tests/integration/features/chat-4/threads.feature b/tests/integration/features/chat-4/threads.feature index 0c7a52d2812..24c41bdab3b 100644 --- a/tests/integration/features/chat-4/threads.feature +++ b/tests/integration/features/chat-4/threads.feature @@ -29,9 +29,48 @@ Feature: chat-4/threads | room | users | participant1 | participant1-displayname | Message 1 | [] | | Then user "participant1" sees the following system messages in room "room" with 200 | room | actorType | actorId | systemMessage | message | silent | messageParameters | + | room | users | participant1 | thread_created | You created thread {title} | true | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname","mention-id":"participant1"},"title":{"type":"highlight","id":THREAD_ID(Thread 1),"name":"Thread 1"}} | + | room | users | participant1 | user_added | You added {user} | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname","mention-id":"participant1"},"user":{"type":"user","id":"participant2","name":"participant2-displayname","mention-id":"participant2"}} | + | room | users | participant1 | conversation_created | You created the conversation | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname","mention-id":"participant1"}} | + And user "participant1" renames thread "Thread 1" to "Thredited 1" in room "room" with 200 + Then user "participant1" sees the following recent threads in room "room" with 200 + | t.id | t.title | t.numReplies | t.lastMessage | a.notificationLevel | firstMessage | lastMessage | + | Message 1 | Thredited 1 | 1 | Message 1-1 | 0 | Message 1 | Message 1-1 | + Then user "participant1" sees the following messages in room "room" with 200 + | room | actorType | actorId | actorDisplayName | message | messageParameters | parentMessage | + | room | users | participant2 | participant2-displayname | Message 1-1 | [] | Message 1 | + | room | users | participant1 | participant1-displayname | Message 1 | [] | | + Then user "participant2" sees the following system messages in room "room" with 200 + | room | actorType | actorId | systemMessage | message | silent | messageParameters | + | room | users | participant1 | thread_renamed | {actor} renamed thread {title} | true | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname","mention-id":"participant1"},"title":{"type":"highlight","id":THREAD_ID(Thread 1),"name":"Thredited 1"}} | | room | users | participant1 | thread_created | {actor} created thread {title} | true | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname","mention-id":"participant1"},"title":{"type":"highlight","id":THREAD_ID(Thread 1),"name":"Thread 1"}} | + | room | users | participant1 | user_added | {actor} added you | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname","mention-id":"participant1"},"user":{"type":"user","id":"participant2","name":"participant2-displayname","mention-id":"participant2"}} | + | room | users | participant1 | conversation_created | {actor} created the conversation | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname","mention-id":"participant1"}} | + + Scenario: Thread titles are trimmed + Given 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" sends thread "More than 200 chars 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789" with message "Message 1" to room "room" with 201 + Then user "participant1" sees the following recent threads in room "room" with 200 + | t.id | t.title | t.numReplies | t.lastMessage | a.notificationLevel | firstMessage | lastMessage | + | Message 1 | More than 200 chars 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 … | 0 | 0 | 0 | Message 1 | NULL | + Then user "participant1" sees the following system messages in room "room" with 200 + | room | actorType | actorId | systemMessage | message | silent | messageParameters | + | room | users | participant1 | thread_created | You created thread {title} | true | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname","mention-id":"participant1"},"title":{"type":"highlight","id":THREAD_ID(More than 200 chars 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789),"name":"More than 200 chars 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 \u2026"}} | | room | users | participant1 | user_added | You added {user} | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname","mention-id":"participant1"},"user":{"type":"user","id":"participant2","name":"participant2-displayname","mention-id":"participant2"}} | | room | users | participant1 | conversation_created | You created the conversation | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname","mention-id":"participant1"}} | + And user "participant1" renames thread "More than 200 chars 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789" to "Still more than 200 chars 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789" in room "room" with 200 + Then user "participant1" sees the following recent threads in room "room" with 200 + | t.id | t.title | t.numReplies | t.lastMessage | a.notificationLevel | firstMessage | lastMessage | + | Message 1 | Still more than 200 chars 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234… | 0 | 0 | 0 | Message 1 | NULL | + Then user "participant2" sees the following system messages in room "room" with 200 + | room | actorType | actorId | systemMessage | message | silent | messageParameters | + | room | users | participant1 | thread_renamed | {actor} renamed thread {title} | true | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname","mention-id":"participant1"},"title":{"type":"highlight","id":THREAD_ID(More than 200 chars 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789),"name":"Still more than 200 chars 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234\u2026"}} | + | room | users | participant1 | thread_created | {actor} created thread {title} | true | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname","mention-id":"participant1"},"title":{"type":"highlight","id":THREAD_ID(More than 200 chars 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789),"name":"More than 200 chars 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 \u2026"}} | + | room | users | participant1 | user_added | {actor} added you | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname","mention-id":"participant1"},"user":{"type":"user","id":"participant2","name":"participant2-displayname","mention-id":"participant2"}} | + | room | users | participant1 | conversation_created | {actor} created the conversation | !ISSET | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname","mention-id":"participant1"}} | Scenario: Recent threads are sorted by last activity Given user "participant1" creates room "room" (v4) diff --git a/tests/php/Chat/SystemMessage/ListenerTest.php b/tests/php/Chat/SystemMessage/ListenerTest.php index fa3949b31e5..ced6932b314 100644 --- a/tests/php/Chat/SystemMessage/ListenerTest.php +++ b/tests/php/Chat/SystemMessage/ListenerTest.php @@ -242,6 +242,7 @@ public function testAfterUsersAdd(int $roomType, string $objectType, array $part null, false, false, + 0, ]; } if (!empty($consecutive)) { @@ -343,6 +344,7 @@ public function testAfterParticipantTypeSet(string $actorType, int $oldParticipa null, false, false, + 0, ]; } if (isset($consecutive)) {