summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoas Schilling <coding@schilljs.com>2024-03-15 12:41:25 +0100
committerJoas Schilling <coding@schilljs.com>2024-03-15 15:31:36 +0100
commiteb45b2eb8787207d072e5399da50f24f000df658 (patch)
treeb135894b3e4ee2136b36654a1f3b1f8e78c86238
parent7736524344560a378922c573f04485bc84eef4ce (diff)
feat(federation): Implement reminders for federated messages
Signed-off-by: Joas Schilling <coding@schilljs.com>
-rw-r--r--docs/chat.md3
-rw-r--r--lib/Controller/ChatController.php31
-rw-r--r--lib/Manager.php22
-rw-r--r--lib/Model/ReminderMapper.php3
-rw-r--r--lib/Notification/Notifier.php6
-rw-r--r--lib/Service/ReminderService.php63
6 files changed, 108 insertions, 20 deletions
diff --git a/docs/chat.md b/docs/chat.md
index 7280253e7..161aceb18 100644
--- a/docs/chat.md
+++ b/docs/chat.md
@@ -351,6 +351,7 @@ See [OCP\RichObjectStrings\Definitions](https://github.com/nextcloud/server/blob
## Set reminder for chat message
* Required capability: `remind-me-later`
+* Federation capability: `federation-v1`
* Method: `POST`
* Endpoint: `/chat/{token}/{messageId}/reminder`
* Data:
@@ -379,6 +380,7 @@ See [OCP\RichObjectStrings\Definitions](https://github.com/nextcloud/server/blob
## Get reminder for chat message
* Required capability: `remind-me-later`
+* Federation capability: `federation-v1`
* Method: `GET`
* Endpoint: `/chat/{token}/{messageId}/reminder`
@@ -403,6 +405,7 @@ See [OCP\RichObjectStrings\Definitions](https://github.com/nextcloud/server/blob
## Delete reminder for chat message
* Required capability: `remind-me-later`
+* Federation capability: `federation-v1`
* Method: `DELETE`
* Endpoint: `/chat/{token}/{messageId}/reminder`
diff --git a/lib/Controller/ChatController.php b/lib/Controller/ChatController.php
index bad6ee7ea..0b1a6bb72 100644
--- a/lib/Controller/ChatController.php
+++ b/lib/Controller/ChatController.php
@@ -46,6 +46,7 @@ use OCA\Talk\Model\Attachment;
use OCA\Talk\Model\Attendee;
use OCA\Talk\Model\Bot;
use OCA\Talk\Model\Message;
+use OCA\Talk\Model\ProxyCacheMessageMapper;
use OCA\Talk\Model\Session;
use OCA\Talk\Participant;
use OCA\Talk\ResponseDefinitions;
@@ -127,6 +128,7 @@ class ChatController extends AEnvironmentAwareController {
protected ITrustedDomainHelper $trustedDomainHelper,
private IL10N $l,
protected Authenticator $federationAuthenticator,
+ protected ProxyCacheMessageMapper $proxyCacheMapper,
) {
parent::__construct($appName, $request);
}
@@ -919,14 +921,15 @@ class ChatController extends AEnvironmentAwareController {
* 201: Reminder created successfully
* 404: Message not found
*/
+ #[FederationSupported]
#[NoAdminRequired]
#[RequireModeratorOrNoLobby]
#[RequireLoggedInParticipant]
#[UserRateLimit(limit: 60, period: 3600)]
public function setReminder(int $messageId, int $timestamp): DataResponse {
try {
- $this->chatManager->getComment($this->room, (string) $messageId);
- } catch (NotFoundException) {
+ $this->validateMessageExists($messageId);
+ } catch (DoesNotExistException) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
@@ -951,19 +954,21 @@ class ChatController extends AEnvironmentAwareController {
* 404: No reminder found
* 404: Message not found
*/
+ #[FederationSupported]
#[NoAdminRequired]
#[RequireModeratorOrNoLobby]
#[RequireLoggedInParticipant]
public function getReminder(int $messageId): DataResponse {
try {
- $this->chatManager->getComment($this->room, (string) $messageId);
- } catch (NotFoundException) {
+ $this->validateMessageExists($messageId);
+ } catch (DoesNotExistException) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
try {
$reminder = $this->reminderService->getReminder(
$this->participant->getAttendee()->getActorId(),
+ $this->room->getToken(),
$messageId,
);
return new DataResponse($reminder->jsonSerialize(), Http::STATUS_OK);
@@ -982,13 +987,14 @@ class ChatController extends AEnvironmentAwareController {
* 200: Reminder deleted successfully
* 404: Message not found
*/
+ #[FederationSupported]
#[NoAdminRequired]
#[RequireModeratorOrNoLobby]
#[RequireLoggedInParticipant]
public function deleteReminder(int $messageId): DataResponse {
try {
- $this->chatManager->getComment($this->room, (string) $messageId);
- } catch (NotFoundException) {
+ $this->validateMessageExists($messageId);
+ } catch (DoesNotExistException) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
@@ -1001,6 +1007,19 @@ class ChatController extends AEnvironmentAwareController {
return new DataResponse([], Http::STATUS_OK);
}
+ protected function validateMessageExists(int $messageId): void {
+ if ($this->room->isFederatedConversation()) {
+ $this->proxyCacheMapper->findByRemote($this->room->getRemoteServer(), $this->room->getRemoteToken(), $messageId);
+ return;
+ }
+
+ try {
+ $this->chatManager->getComment($this->room, (string)$messageId);
+ } catch (NotFoundException $e) {
+ throw new DoesNotExistException($e->getMessage());
+ }
+ }
+
/**
* Clear the chat history
*
diff --git a/lib/Manager.php b/lib/Manager.php
index b9a81c65e..5b6f7c183 100644
--- a/lib/Manager.php
+++ b/lib/Manager.php
@@ -883,6 +883,28 @@ class Manager {
}
/**
+ * @param string[] $tokens
+ * @return array<string, Room>
+ */
+ public function getRoomsByToken(array $tokens): array {
+ $query = $this->db->getQueryBuilder();
+ $helper = new SelectHelper();
+ $helper->selectRoomsTable($query);
+ $query->from('talk_rooms', 'r')
+ ->where($query->expr()->in('r.token', $query->createNamedParameter($tokens, IQueryBuilder::PARAM_STR_ARRAY)));
+
+ $result = $query->executeQuery();
+ $rooms = [];
+ while ($row = $result->fetch()) {
+ $room = $this->createRoomObject($row);
+ $rooms[$room->getToken()] = $room;
+ }
+ $result->closeCursor();
+
+ return $rooms;
+ }
+
+ /**
* @param string|null $userId
* @param string|null $sessionId
* @return Room
diff --git a/lib/Model/ReminderMapper.php b/lib/Model/ReminderMapper.php
index 51fada9b9..c14a9273f 100644
--- a/lib/Model/ReminderMapper.php
+++ b/lib/Model/ReminderMapper.php
@@ -49,11 +49,12 @@ class ReminderMapper extends QBMapper {
/**
* @throws DoesNotExistException
*/
- public function findForUserAndMessage(string $userId, int $messageId): Reminder {
+ public function findForUserAndMessage(string $userId, string $token, int $messageId): Reminder {
$query = $this->db->getQueryBuilder();
$query->select('*')
->from($this->getTableName())
->where($query->expr()->eq('user_id', $query->createNamedParameter($userId, IQueryBuilder::PARAM_STR)))
+ ->andWhere($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR)))
->andWhere($query->expr()->eq('message_id', $query->createNamedParameter($messageId, IQueryBuilder::PARAM_INT)));
return $this->findEntity($query);
diff --git a/lib/Notification/Notifier.php b/lib/Notification/Notifier.php
index 7896ac6cf..88e93aa64 100644
--- a/lib/Notification/Notifier.php
+++ b/lib/Notification/Notifier.php
@@ -625,7 +625,7 @@ class Notifier implements INotifier {
'name' => $shortenMessage,
];
if ($notification->getSubject() === 'reminder') {
- if ($comment->getActorId() === $notification->getUser()) {
+ if ($message->getActorId() === $notification->getUser()) {
// TRANSLATORS Reminder for a message you sent in the conversation {call}
$subject = $l->t('Reminder: You in {call}') . "\n{message}";
} elseif ($room->getType() === Room::TYPE_ONE_TO_ONE || $room->getType() === Room::TYPE_ONE_TO_ONE_FORMER) {
@@ -716,7 +716,7 @@ class Notifier implements INotifier {
}
} elseif ($notification->getSubject() === 'reminder') {
if ($room->getType() === Room::TYPE_ONE_TO_ONE || $room->getType() === Room::TYPE_ONE_TO_ONE_FORMER) {
- if ($comment->getActorId() === $notification->getUser()) {
+ if ($message->getActorId() === $notification->getUser()) {
$subject = $l->t('Reminder: You in private conversation {call}');
} elseif ($room->getType() === Room::TYPE_ONE_TO_ONE_FORMER) {
$subject = $l->t('Reminder: A deleted user in private conversation {call}');
@@ -724,7 +724,7 @@ class Notifier implements INotifier {
$subject = $l->t('Reminder: {user} in private conversation');
}
} elseif ($richSubjectUser) {
- if ($comment->getActorId() === $notification->getUser()) {
+ if ($message->getActorId() === $notification->getUser()) {
$subject = $l->t('Reminder: You in conversation {call}');
} else {
$subject = $l->t('Reminder: {user} in conversation {call}');
diff --git a/lib/Service/ReminderService.php b/lib/Service/ReminderService.php
index f88bbdf36..4d3f66c09 100644
--- a/lib/Service/ReminderService.php
+++ b/lib/Service/ReminderService.php
@@ -28,6 +28,9 @@ namespace OCA\Talk\Service;
use OCA\Talk\AppInfo\Application;
use OCA\Talk\Chat\ChatManager;
+use OCA\Talk\Manager;
+use OCA\Talk\Model\ProxyCacheMessage;
+use OCA\Talk\Model\ProxyCacheMessageMapper;
use OCA\Talk\Model\Reminder;
use OCA\Talk\Model\ReminderMapper;
use OCP\AppFramework\Db\DoesNotExistException;
@@ -38,12 +41,14 @@ class ReminderService {
protected IManager $notificationManager,
protected ReminderMapper $reminderMapper,
protected ChatManager $chatManager,
+ protected ProxyCacheMessageMapper $proxyCacheMapper,
+ protected Manager $manager,
) {
}
public function setReminder(string $userId, string $token, int $messageId, int $timestamp): Reminder {
try {
- $reminder = $this->reminderMapper->findForUserAndMessage($userId, $messageId);
+ $reminder = $this->reminderMapper->findForUserAndMessage($userId, $token, $messageId);
$reminder->setDateTime(new \DateTime('@' . $timestamp));
$this->reminderMapper->update($reminder);
@@ -62,13 +67,13 @@ class ReminderService {
/**
* @throws DoesNotExistException
*/
- public function getReminder(string $userId, int $messageId): Reminder {
- return $this->reminderMapper->findForUserAndMessage($userId, $messageId);
+ public function getReminder(string $userId, string $token, int $messageId): Reminder {
+ return $this->reminderMapper->findForUserAndMessage($userId, $token, $messageId);
}
public function deleteReminder(string $userId, string $token, int $messageId): void {
try {
- $reminder = $this->reminderMapper->findForUserAndMessage($userId, $messageId);
+ $reminder = $this->reminderMapper->findForUserAndMessage($userId, $token, $messageId);
$this->reminderMapper->delete($reminder);
} catch (DoesNotExistException) {
// When the reminder does not exist anymore, the notification could be there
@@ -92,19 +97,59 @@ class ReminderService {
$shouldFlush = $this->notificationManager->defer();
+ $roomTokens = [];
+ foreach ($reminders as $reminder) {
+ $roomTokens[] = $reminder->getToken();
+ }
+ $roomTokens = array_unique($roomTokens);
+ $rooms = $this->manager->getRoomsByToken($roomTokens);
+
+ /** @var array<string, ProxyCacheMessage> $proxyMessages */
+ $proxyMessages = [];
$messageIds = [];
foreach ($reminders as $reminder) {
- $messageIds[] = $reminder->getMessageId();
+ if (!isset($rooms[$reminder->getToken()])) {
+ continue;
+ }
+
+ $room = $rooms[$reminder->getToken()];
+ if (!$room->isFederatedConversation()) {
+ $messageIds[] = $reminder->getMessageId();
+ } else {
+ $key = json_encode([$room->getRemoteServer(), $room->getRemoteToken(), $reminder->getMessageId()]);
+ if (!isset($proxyMessages[$key])) {
+ try {
+ $proxyMessages[$key] = $this->proxyCacheMapper->findByRemote($room->getRemoteServer(), $room->getRemoteToken(), $reminder->getMessageId());
+ } catch (DoesNotExistException) {
+ }
+ }
+ }
}
+
$messageIds = array_unique($messageIds);
$messages = $this->chatManager->getMessagesById($messageIds);
foreach ($reminders as $reminder) {
- if (!isset($messages[$reminder->getMessageId()])) {
+ $room = $rooms[$reminder->getToken()];
+ if (!$room->isFederatedConversation()) {
+ $key = $reminder->getMessageId();
+ $messageList = $messages;
+ $messageParameters = [
+ 'commentId' => $reminder->getMessageId(),
+ ];
+ } else {
+ $key = json_encode([$room->getRemoteServer(), $room->getRemoteToken(), $reminder->getMessageId()]);
+ $messageList = $proxyMessages;
+ $messageParameters = [
+ 'proxyId' => $reminder->getMessageId(),
+ ];
+ }
+
+ if (!isset($messageList[$key])) {
continue;
}
- $message = $messages[$reminder->getMessageId()];
+ $message = $messageList[$key];
$notification = $this->notificationManager->createNotification();
$notification->setApp(Application::APP_ID)
@@ -117,9 +162,7 @@ class ReminderService {
'userType' => $message->getActorType(),
'userId' => $message->getActorId(),
])
- ->setMessage('reminder', [
- 'commentId' => $reminder->getMessageId(),
- ]);
+ ->setMessage('reminder', $messageParameters);
$this->notificationManager->notify($notification);
}