diff options
author | Joas Schilling <213943+nickvergessen@users.noreply.github.com> | 2023-08-09 11:30:49 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-09 11:30:49 +0200 |
commit | 2abdd461b49e9e566ca59a90147a395e229b822f (patch) | |
tree | 9ab12936e4fb3f5d44c35ee4ae93fcd4ee872aa4 | |
parent | 02f0551f3174369788a5a828b7188214561faaa5 (diff) | |
parent | ce11d0602c9598e242e52364ded319c239a61a2c (diff) |
Merge pull request #10152 from nextcloud/backport/10146/stable27
[stable27] feat(chat): Option to get and clear reminders
-rw-r--r-- | appinfo/info.xml | 11 | ||||
-rw-r--r-- | appinfo/routes/routesChatController.php | 10 | ||||
-rw-r--r-- | docs/chat.md | 71 | ||||
-rw-r--r-- | lib/BackgroundJob/ChatMessageReminder.php | 97 | ||||
-rw-r--r-- | lib/BackgroundJob/Reminder.php | 49 | ||||
-rw-r--r-- | lib/Chat/ChatManager.php | 37 | ||||
-rw-r--r-- | lib/Controller/ChatController.php | 52 | ||||
-rw-r--r-- | lib/Migration/Version18000Date20230504205823.php | 2 | ||||
-rw-r--r-- | lib/Migration/Version18000Date20230808120823.php | 74 | ||||
-rw-r--r-- | lib/Model/Reminder.php | 61 | ||||
-rw-r--r-- | lib/Model/ReminderMapper.php | 81 | ||||
-rw-r--r-- | lib/Notification/Notifier.php | 2 | ||||
-rw-r--r-- | lib/Service/ReminderService.php | 132 | ||||
-rw-r--r-- | tests/integration/features/bootstrap/FeatureContext.php | 10 | ||||
-rw-r--r-- | tests/integration/features/chat-2/reminder.feature | 33 | ||||
-rw-r--r-- | tests/integration/spreedcheats/lib/Controller/ApiController.php | 3 | ||||
-rw-r--r-- | tests/php/Chat/ChatManagerTest.php | 7 | ||||
-rw-r--r-- | tests/php/Controller/ChatControllerTest.php | 5 |
18 files changed, 553 insertions, 184 deletions
diff --git a/appinfo/info.xml b/appinfo/info.xml index a93c4205c..29bc9b786 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -16,7 +16,7 @@ And in the works for the [coming versions](https://github.com/nextcloud/spreed/m ]]></description> - <version>17.1.0-dev.1</version> + <version>17.1.0-dev.2</version> <licence>agpl</licence> <author>Daniel Calviño Sánchez</author> @@ -56,13 +56,14 @@ And in the works for the [coming versions](https://github.com/nextcloud/spreed/m </dependencies> <background-jobs> - <job>OCA\Talk\BackgroundJob\ExpireSignalingMessage</job> - <job>OCA\Talk\BackgroundJob\RemoveEmptyRooms</job> - <job>OCA\Talk\BackgroundJob\ResetAssignedSignalingServer</job> - <job>OCA\Talk\BackgroundJob\CheckReferenceIdColumn</job> <job>OCA\Talk\BackgroundJob\CheckHostedSignalingServer</job> <job>OCA\Talk\BackgroundJob\CheckMatterbridges</job> + <job>OCA\Talk\BackgroundJob\CheckReferenceIdColumn</job> <job>OCA\Talk\BackgroundJob\ExpireChatMessages</job> + <job>OCA\Talk\BackgroundJob\ExpireSignalingMessage</job> + <job>OCA\Talk\BackgroundJob\Reminder</job> + <job>OCA\Talk\BackgroundJob\RemoveEmptyRooms</job> + <job>OCA\Talk\BackgroundJob\ResetAssignedSignalingServer</job> </background-jobs> <repair-steps> diff --git a/appinfo/routes/routesChatController.php b/appinfo/routes/routesChatController.php index 39a66f7d8..8ef8ac2a9 100644 --- a/appinfo/routes/routesChatController.php +++ b/appinfo/routes/routesChatController.php @@ -46,10 +46,12 @@ return [ ['name' => 'Chat#deleteMessage', 'url' => '/api/{apiVersion}/chat/{token}/{messageId}', 'verb' => 'DELETE', 'requirements' => $requirementsWithMessageId], /** @see \OCA\Talk\Controller\ChatController::getMessageContext() */ ['name' => 'Chat#getMessageContext', 'url' => '/api/{apiVersion}/chat/{token}/{messageId}/context', 'verb' => 'GET', 'requirements' => $requirementsWithMessageId], - /** @see \OCA\Talk\Controller\ChatController::remindLater() */ - ['name' => 'Chat#remindLater', 'url' => '/api/{apiVersion}/chat/{token}/{messageId}/reminder', 'verb' => 'POST', 'requirements' => $requirementsWithMessageId], - /** @see \OCA\Talk\Controller\ChatController::dismissReminder() */ - ['name' => 'Chat#dismissReminder', 'url' => '/api/{apiVersion}/chat/{token}/{messageId}/reminder', 'verb' => 'DELETE', 'requirements' => $requirementsWithMessageId], + /** @see \OCA\Talk\Controller\ChatController::setReminder() */ + ['name' => 'Chat#setReminder', 'url' => '/api/{apiVersion}/chat/{token}/{messageId}/reminder', 'verb' => 'POST', 'requirements' => $requirementsWithMessageId], + /** @see \OCA\Talk\Controller\ChatController::getReminder() */ + ['name' => 'Chat#getReminder', 'url' => '/api/{apiVersion}/chat/{token}/{messageId}/reminder', 'verb' => 'GET', 'requirements' => $requirementsWithMessageId], + /** @see \OCA\Talk\Controller\ChatController::deleteReminder() */ + ['name' => 'Chat#deleteReminder', 'url' => '/api/{apiVersion}/chat/{token}/{messageId}/reminder', 'verb' => 'DELETE', 'requirements' => $requirementsWithMessageId], /** @see \OCA\Talk\Controller\ChatController::setReadMarker() */ ['name' => 'Chat#setReadMarker', 'url' => '/api/{apiVersion}/chat/{token}/read', 'verb' => 'POST', 'requirements' => $requirements], /** @see \OCA\Talk\Controller\ChatController::markUnread() */ diff --git a/docs/chat.md b/docs/chat.md index c69f67684..654dc6c9b 100644 --- a/docs/chat.md +++ b/docs/chat.md @@ -94,12 +94,12 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`: since Nextcloud 13 | `limit` | int | Number of chat messages to receive into each direction (50 by default, 100 at most) | * Response: - - Status code: - + `200 OK` - + `404 Not Found` When the conversation could not be found for the participant - + `412 Precondition Failed` When the lobby is active and the user is not a moderator + - Status code: + + `200 OK` + + `404 Not Found` When the conversation could not be found for the participant + + `412 Precondition Failed` When the lobby is active and the user is not a moderator - - Header: + - Header: | field | type | Description | |---------------------------|------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| @@ -300,7 +300,7 @@ See [OCP\RichObjectStrings\Definitions](https://github.com/nextcloud/server/blob The parent message is the object of the deleted message with the replaced text "Message deleted by you". This message should **NOT** be displayed to the user but instead be used to remove the original message from any cache/storage of the device. -## Remind me later +## Set reminder for chat message * Required capability: `remind-me-later` * Method: `POST` @@ -312,26 +312,59 @@ See [OCP\RichObjectStrings\Definitions](https://github.com/nextcloud/server/blob | `timestamp` | int | Timestamp when the notification should be triggered. Preferable options for 6pm today, 8am tomorrow, Saturday 8am and Monday 8am should be offered. | * Response: - - Status code: - + `201 Created` - + `401 Unauthorized` when the user is not logged in - + `404 Not Found` When the message could not be found in the room - + `404 Not Found` When the room could not be found for the participant, - or the participant is a guest. + - Status code: + + `201 Created` + + `401 Unauthorized` when the user is not logged in + + `404 Not Found` When the message could not be found in the room + + `404 Not Found` When the room could not be found for the participant, + or the participant is a guest. + - Data: + Array with the details of the reminder + +| field | type | Description | +|-------------|--------|----------------------------------------------| +| `userId` | string | The user id of the user | +| `token` | string | The token of the conversation of the message | +| `messageId` | int | The message id this reminder is for | +| `timestamp` | int | The timestamp when the reminder is triggered | -## Delete reminder notification +## Get reminder for chat message + +* Required capability: `remind-me-later` +* Method: `GET` +* Endpoint: `/chat/{token}/{messageId}/reminder` + +* Response: + - Status code: + + `200 OK` + + `401 Unauthorized` when the user is not logged in + + `404 Not Found` When the message could not be found in the room + + `404 Not Found` When the room could not be found for the participant, + or the participant is a guest. + + `404 Not Found` When the user has no reminder for this message + - Data: + Array with the details of the reminder + +| field | type | Description | +|-------------|--------|----------------------------------------------| +| `userId` | string | The user id of the user | +| `token` | string | The token of the conversation of the message | +| `messageId` | int | The message id this reminder is for | +| `timestamp` | int | The timestamp when the reminder is triggered | + +## Delete reminder for chat message * Required capability: `remind-me-later` * Method: `DELETE` * Endpoint: `/chat/{token}/{messageId}/reminder` * Response: - - Status code: - + `200 OK` - + `401 Unauthorized` when the user is not logged in - + `404 Not Found` When the message could not be found in the room - + `404 Not Found` When the room could not be found for the participant, - or the participant is a guest. + - Status code: + + `200 OK` + + `401 Unauthorized` when the user is not logged in + + `404 Not Found` When the message could not be found in the room + + `404 Not Found` When the room could not be found for the participant, + or the participant is a guest. ## Mark chat as read diff --git a/lib/BackgroundJob/ChatMessageReminder.php b/lib/BackgroundJob/ChatMessageReminder.php deleted file mode 100644 index 4c23ddf32..000000000 --- a/lib/BackgroundJob/ChatMessageReminder.php +++ /dev/null @@ -1,97 +0,0 @@ -<?php - -declare(strict_types=1); - -/* - * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -namespace OCA\Talk\BackgroundJob; - -use OCA\Talk\AppInfo\Application; -use OCP\AppFramework\Utility\ITimeFactory; -use OCP\BackgroundJob\IJobList; -use OCP\BackgroundJob\Job; -use OCP\DB\QueryBuilder\IQueryBuilder; -use OCP\IDBConnection; -use OCP\Notification\IManager; -use Psr\Log\LoggerInterface; - -class ChatMessageReminder extends Job { - public function __construct( - ITimeFactory $time, - protected IDBConnection $connection, - protected IManager $notificationManager, - protected LoggerInterface $logger, - ) { - parent::__construct($time); - } - - public function start(IJobList $jobList): void { - $timeUntilExecution = $this->argument['execute-after'] - $this->time->getTime(); - - if ($timeUntilExecution <= 0) { - parent::start($jobList); - $jobList->remove($this, $this->argument); - } elseif ($timeUntilExecution > 900) { - // Execution is quite far in the future. In order to not check the - // job too often, we update it's test time to be closer to the execution - $this->setLastRunCloseToExecutionTime( - $this->argument['execute-after'], - $this->argument['token'], - $this->argument['user'], - $this->argument['message_id'], - ); - } - } - - /** - * @psalm-param array{token: string, message_id: string, message_actor_type: string, message_actor_id: string, user: string, execute-after: int} $argument - */ - protected function run($argument): void { - $notification = $this->notificationManager->createNotification(); - $notification->setApp(Application::APP_ID) - ->setUser($argument['user']) - ->setObject('reminder', $argument['token']) - ->setDateTime($this->time->getDateTime('@' . $this->argument['execute-after'])) - ->setSubject('reminder', [ - 'token' => $argument['token'], - 'message' => $argument['message_id'], - 'userType' => $argument['message_actor_type'], - 'userId' => $argument['message_actor_id'], - ]) - ->setMessage('reminder', [ - 'commentId' => $argument['message_id'], - ]); - $this->notificationManager->notify($notification); - } - - protected function setLastRunCloseToExecutionTime(int $timestamp, string $token, string $userId, string $message): void { - $query = $this->connection->getQueryBuilder(); - - $query->update('jobs') - ->set('last_run', $query->createNamedParameter($timestamp, IQueryBuilder::PARAM_INT)) - ->where($query->expr()->eq('id', $query->createNamedParameter($this->getId(), IQueryBuilder::PARAM_INT))); - $query->executeStatement(); - - $this->logger->debug('Updated chat message reminder last_run to ' . $timestamp . ' for token "' . $token . '" user "' . $userId . '" message ' . $message); - } -} diff --git a/lib/BackgroundJob/Reminder.php b/lib/BackgroundJob/Reminder.php new file mode 100644 index 000000000..96eacf741 --- /dev/null +++ b/lib/BackgroundJob/Reminder.php @@ -0,0 +1,49 @@ +<?php + +declare(strict_types=1); + +/* + * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com> + * + * @author Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Talk\BackgroundJob; + +use OCA\Talk\Service\ReminderService; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\TimedJob; + +class Reminder extends TimedJob { + public function __construct( + ITimeFactory $time, + protected ReminderService $reminderService, + ) { + parent::__construct($time); + // Every minute + $this->setInterval(60); + } + + /** + * @inheritDoc + */ + protected function run($argument): void { + $this->reminderService->executeReminders($this->time->getDateTime()); + } +} diff --git a/lib/Chat/ChatManager.php b/lib/Chat/ChatManager.php index 104d5fcd7..d438a9880 100644 --- a/lib/Chat/ChatManager.php +++ b/lib/Chat/ChatManager.php @@ -27,8 +27,6 @@ namespace OCA\Talk\Chat; use DateInterval; use OC\Memcache\ArrayCache; use OC\Memcache\NullCache; -use OCA\Talk\AppInfo\Application; -use OCA\Talk\BackgroundJob\ChatMessageReminder; use OCA\Talk\Events\ChatEvent; use OCA\Talk\Events\ChatParticipantEvent; use OCA\Talk\Exceptions\ParticipantNotFoundException; @@ -43,10 +41,8 @@ use OCA\Talk\Service\RoomService; use OCA\Talk\Share\RoomShareProvider; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Utility\ITimeFactory; -use OCP\BackgroundJob\IJobList; use OCP\Collaboration\Reference\IReferenceManager; use OCP\Comments\IComment; -use OCP\Comments\ICommentsManager; use OCP\Comments\NotFoundException; use OCP\EventDispatcher\IEventDispatcher; use OCP\ICache; @@ -117,7 +113,6 @@ class ChatManager { ITimeFactory $timeFactory, AttachmentService $attachmentService, IReferenceManager $referenceManager, - protected IJobList $jobList, ) { $this->commentsManager = $commentsManager; $this->dispatcher = $dispatcher; @@ -739,13 +734,13 @@ class ChatManager { } /** - * Search for comments with a given content + * Get messages for the given chat by ID * * @param Room $chat * @param int[] $commentIds * @return IComment[] */ - public function getMessagesById(Room $chat, array $commentIds): array { + public function getMessagesForRoomById(Room $chat, array $commentIds): array { $comments = $this->commentsManager->getCommentsById(array_map('strval', $commentIds)); $comments = array_filter($comments, static function (IComment $comment) use ($chat) { @@ -756,26 +751,14 @@ class ChatManager { return $comments; } - public function queueRemindLaterBackgroundJob(Room $chat, IComment $comment, Attendee $attendee, int $timestamp): void { - $this->jobList->add(ChatMessageReminder::class, [ - 'execute-after' => $timestamp, - 'token' => $chat->getToken(), - 'message_id' => $comment->getId(), - 'message_actor_type' => $comment->getActorType(), - 'message_actor_id' => $comment->getActorId(), - 'user' => $attendee->getActorId(), - ]); - } - - public function dismissReminderNotification(Room $chat, IComment $comment, Attendee $attendee): void { - $notification = $this->notificationManager->createNotification(); - $notification->setApp(Application::APP_ID) - ->setUser($attendee->getActorId()) - ->setObject('reminder', $chat->getToken()) - ->setMessage('reminder', [ - 'commentId' => $comment->getId(), - ]); - $this->notificationManager->markProcessed($notification); + /** + * Get messages by ID + * + * @param int[] $commentIds + * @return IComment[] Key is the message id + */ + public function getMessagesById(array $commentIds): array { + return $this->commentsManager->getCommentsById(array_map('strval', $commentIds)); } /** diff --git a/lib/Controller/ChatController.php b/lib/Controller/ChatController.php index 784396d23..477fa49e0 100644 --- a/lib/Controller/ChatController.php +++ b/lib/Controller/ChatController.php @@ -46,9 +46,11 @@ use OCA\Talk\Room; use OCA\Talk\Service\AttachmentService; use OCA\Talk\Service\AvatarService; use OCA\Talk\Service\ParticipantService; +use OCA\Talk\Service\ReminderService; use OCA\Talk\Service\SessionService; use OCA\Talk\Share\RoomShareProvider; use OCP\App\IAppManager; +use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\PublicPage; @@ -110,6 +112,7 @@ class ChatController extends AEnvironmentAwareController { SessionService $sessionService, AttachmentService $attachmentService, avatarService $avatarService, + protected ReminderService $reminderService, GuestManager $guestManager, MessageParser $messageParser, RoomShareProvider $shareProvider, @@ -734,37 +737,58 @@ class ChatController extends AEnvironmentAwareController { #[RequireModeratorOrNoLobby] #[RequireLoggedInParticipant] #[UserRateLimit(limit: 60, period: 3600)] - public function remindLater(int $messageId, int $timestamp): DataResponse { + public function setReminder(int $messageId, int $timestamp): DataResponse { try { - $message = $this->chatManager->getComment($this->room, (string) $messageId); + $this->chatManager->getComment($this->room, (string) $messageId); } catch (NotFoundException) { return new DataResponse([], Http::STATUS_NOT_FOUND); } - $this->chatManager->queueRemindLaterBackgroundJob( - $this->room, - $message, - $this->participant->getAttendee(), + $reminder = $this->reminderService->setReminder( + $this->participant->getAttendee()->getActorId(), + $this->room->getToken(), + $messageId, $timestamp ); - return new DataResponse([], Http::STATUS_CREATED); + return new DataResponse($reminder->jsonSerialize(), Http::STATUS_CREATED); } #[NoAdminRequired] #[RequireModeratorOrNoLobby] #[RequireLoggedInParticipant] - public function dismissReminder(int $messageId): DataResponse { + public function getReminder(int $messageId): DataResponse { try { - $message = $this->chatManager->getComment($this->room, (string) $messageId); + $this->chatManager->getComment($this->room, (string) $messageId); } catch (NotFoundException) { return new DataResponse([], Http::STATUS_NOT_FOUND); } - $this->chatManager->dismissReminderNotification( - $this->room, - $message, - $this->participant->getAttendee() + try { + $reminder = $this->reminderService->getReminder( + $this->participant->getAttendee()->getActorId(), + $messageId, + ); + return new DataResponse($reminder->jsonSerialize(), Http::STATUS_OK); + } catch (DoesNotExistException) { + return new DataResponse([], Http::STATUS_NOT_FOUND); + } + } + + #[NoAdminRequired] + #[RequireModeratorOrNoLobby] + #[RequireLoggedInParticipant] + public function deleteReminder(int $messageId): DataResponse { + try { + $this->chatManager->getComment($this->room, (string) $messageId); + } catch (NotFoundException) { + return new DataResponse([], Http::STATUS_NOT_FOUND); + } + + $this->reminderService->deleteReminder( + $this->par |