From 01559a05713042c5a19ae108d7061c194bd6b013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julius=20H=C3=A4rtl?= Date: Thu, 29 Aug 2024 17:53:09 +0200 Subject: [PATCH] fix: Emit notification on mentions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Julius Härtl --- appinfo/routes.php | 2 + composer/composer/autoload_classmap.php | 2 + composer/composer/autoload_static.php | 2 + lib/AppInfo/Application.php | 2 + lib/Controller/MentionController.php | 66 ++++++++++++++ lib/Notification/Notifier.php | 112 ++++++++++++++++++++++++ src/mixins/uiMention.js | 39 +++++---- src/view/Office.vue | 4 +- 8 files changed, 211 insertions(+), 18 deletions(-) create mode 100644 lib/Controller/MentionController.php create mode 100644 lib/Notification/Notifier.php diff --git a/appinfo/routes.php b/appinfo/routes.php index 49986d47a9..8c38a75dcc 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -79,5 +79,7 @@ ['name' => 'TemplateField#extractFields', 'url' => '/api/v1/template/fields/extract/{fileId}', 'verb' => 'GET'], ['name' => 'TemplateField#fillFields', 'url' => '/api/v1/template/fields/fill/{fileId}', 'verb' => 'POST'], + + ['name' => 'Mention#mention', 'url' => '/api/v1/mention/{fileId}', 'verb' => 'POST'], ], ]; diff --git a/composer/composer/autoload_classmap.php b/composer/composer/autoload_classmap.php index b630b10dcf..99384ca436 100644 --- a/composer/composer/autoload_classmap.php +++ b/composer/composer/autoload_classmap.php @@ -23,6 +23,7 @@ 'OCA\\Richdocuments\\Controller\\DocumentController' => $baseDir . '/../lib/Controller/DocumentController.php', 'OCA\\Richdocuments\\Controller\\DocumentTrait' => $baseDir . '/../lib/Controller/DocumentTrait.php', 'OCA\\Richdocuments\\Controller\\FederationController' => $baseDir . '/../lib/Controller/FederationController.php', + 'OCA\\Richdocuments\\Controller\\MentionController' => $baseDir . '/../lib/Controller/MentionController.php', 'OCA\\Richdocuments\\Controller\\OCSController' => $baseDir . '/../lib/Controller/OCSController.php', 'OCA\\Richdocuments\\Controller\\SettingsController' => $baseDir . '/../lib/Controller/SettingsController.php', 'OCA\\Richdocuments\\Controller\\TargetController' => $baseDir . '/../lib/Controller/TargetController.php', @@ -59,6 +60,7 @@ 'OCA\\Richdocuments\\Migration\\Version30709Date20201111104147' => $baseDir . '/../lib/Migration/Version30709Date20201111104147.php', 'OCA\\Richdocuments\\Migration\\Version30717Date20210310164901' => $baseDir . '/../lib/Migration/Version30717Date20210310164901.php', 'OCA\\Richdocuments\\Migration\\Version50200Date20211220212457' => $baseDir . '/../lib/Migration/Version50200Date20211220212457.php', + 'OCA\\Richdocuments\\Notification\\Notifier' => $baseDir . '/../lib/Notification/Notifier.php', 'OCA\\Richdocuments\\PermissionManager' => $baseDir . '/../lib/PermissionManager.php', 'OCA\\Richdocuments\\Preview\\EMF' => $baseDir . '/../lib/Preview/EMF.php', 'OCA\\Richdocuments\\Preview\\MSExcel' => $baseDir . '/../lib/Preview/MSExcel.php', diff --git a/composer/composer/autoload_static.php b/composer/composer/autoload_static.php index f6bd8b2f9f..69c27e4de1 100644 --- a/composer/composer/autoload_static.php +++ b/composer/composer/autoload_static.php @@ -56,6 +56,7 @@ class ComposerStaticInitRichdocuments 'OCA\\Richdocuments\\Controller\\DocumentController' => __DIR__ . '/..' . '/../lib/Controller/DocumentController.php', 'OCA\\Richdocuments\\Controller\\DocumentTrait' => __DIR__ . '/..' . '/../lib/Controller/DocumentTrait.php', 'OCA\\Richdocuments\\Controller\\FederationController' => __DIR__ . '/..' . '/../lib/Controller/FederationController.php', + 'OCA\\Richdocuments\\Controller\\MentionController' => __DIR__ . '/..' . '/../lib/Controller/MentionController.php', 'OCA\\Richdocuments\\Controller\\OCSController' => __DIR__ . '/..' . '/../lib/Controller/OCSController.php', 'OCA\\Richdocuments\\Controller\\SettingsController' => __DIR__ . '/..' . '/../lib/Controller/SettingsController.php', 'OCA\\Richdocuments\\Controller\\TargetController' => __DIR__ . '/..' . '/../lib/Controller/TargetController.php', @@ -92,6 +93,7 @@ class ComposerStaticInitRichdocuments 'OCA\\Richdocuments\\Migration\\Version30709Date20201111104147' => __DIR__ . '/..' . '/../lib/Migration/Version30709Date20201111104147.php', 'OCA\\Richdocuments\\Migration\\Version30717Date20210310164901' => __DIR__ . '/..' . '/../lib/Migration/Version30717Date20210310164901.php', 'OCA\\Richdocuments\\Migration\\Version50200Date20211220212457' => __DIR__ . '/..' . '/../lib/Migration/Version50200Date20211220212457.php', + 'OCA\\Richdocuments\\Notification\\Notifier' => __DIR__ . '/..' . '/../lib/Notification/Notifier.php', 'OCA\\Richdocuments\\PermissionManager' => __DIR__ . '/..' . '/../lib/PermissionManager.php', 'OCA\\Richdocuments\\Preview\\EMF' => __DIR__ . '/..' . '/../lib/Preview/EMF.php', 'OCA\\Richdocuments\\Preview\\MSExcel' => __DIR__ . '/..' . '/../lib/Preview/MSExcel.php', diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 1b285dfe54..1bf44468a9 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -23,6 +23,7 @@ use OCA\Richdocuments\Listener\RegisterTemplateFileCreatorListener; use OCA\Richdocuments\Listener\ShareLinkListener; use OCA\Richdocuments\Middleware\WOPIMiddleware; +use OCA\Richdocuments\Notification\Notifier; use OCA\Richdocuments\Preview\EMF; use OCA\Richdocuments\Preview\MSExcel; use OCA\Richdocuments\Preview\MSWord; @@ -80,6 +81,7 @@ public function register(IRegistrationContext $context): void { $context->registerPreviewProvider(OOXML::class, OOXML::MIMETYPE_REGEX); $context->registerPreviewProvider(OpenDocument::class, OpenDocument::MIMETYPE_REGEX); $context->registerPreviewProvider(Pdf::class, Pdf::MIMETYPE_REGEX); + $context->registerNotifierService(Notifier::class); } public function boot(IBootContext $context): void { diff --git a/lib/Controller/MentionController.php b/lib/Controller/MentionController.php new file mode 100644 index 0000000000..16bea983a2 --- /dev/null +++ b/lib/Controller/MentionController.php @@ -0,0 +1,66 @@ +rootFolder->getUserFolder($this->userId); + $file = $userFolder->getFirstNodeById($fileId); + if ($file === null) { + return new DataResponse([], Http::STATUS_NOT_FOUND); + } + + $userFolder = $this->rootFolder->getUserFolder($mention); + $file = $userFolder->getFirstNodeById($fileId); + if ($file === null) { + return new DataResponse([], Http::STATUS_NOT_FOUND); + } + + $notification = $this->manager->createNotification(); + $notification->setUser($mention) + ->setApp(Application::APPNAME) + ->setSubject(Notifier::TYPE_MENTIONED, [ + Notifier::SUBJECT_MENTIONED_SOURCE_USER => $this->userId, + Notifier::SUBJECT_MENTIONED_TARGET_USER => $mention, + ]) + ->setObject('file', (string)$fileId); + ; + + if ($this->manager->getCount($notification) === 0) { + $notification->setDateTime(\DateTime::createFromImmutable($this->timeFactory->now())); + $this->manager->notify($notification); + return new DataResponse([], Http::STATUS_OK); + + } + + return new DataResponse([], Http::STATUS_NOT_FOUND); + } +} diff --git a/lib/Notification/Notifier.php b/lib/Notification/Notifier.php new file mode 100644 index 0000000000..c3922cf7cd --- /dev/null +++ b/lib/Notification/Notifier.php @@ -0,0 +1,112 @@ +getApp() !== Application::APPNAME) { + throw new InvalidArgumentException('Application should be text instead of ' . $notification->getApp()); + } + + $l = $this->factory->get(Application::APPNAME, $languageCode); + + switch ($notification->getSubject()) { + case self::TYPE_MENTIONED: + $parameters = $notification->getSubjectParameters(); + $sourceUser = $parameters[self::SUBJECT_MENTIONED_SOURCE_USER]; + $sourceUserDisplayName = $this->userManager->getDisplayName($sourceUser); + $targetUser = $notification->getUser(); + $fileId = (int)$notification->getObjectId(); + + if ($sourceUserDisplayName === null) { + throw new InvalidArgumentException(); + } + + try { + $userFolder = $this->rootFolder->getUserFolder($targetUser); + } catch (NotPermittedException|NoUserException $e) { + throw new InvalidArgumentException(); + } + $node = $userFolder->getFirstNodeById($fileId); + + if ($node === null) { + throw new InvalidArgumentException(); + } + + $fileLink = $this->urlGenerator->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $node->getId()]); + + $notification->setRichSubject($l->t('{user} has mentioned you in {node}'), [ + 'user' => [ + 'type' => 'user', + 'id' => $sourceUser, + 'name' => $sourceUserDisplayName, + ], + 'node' => [ + 'type' => 'file', + 'id' => $node->getId(), + 'name' => $node->getName(), + 'path' => $userFolder->getRelativePath($node->getPath()), + 'link' => $fileLink, + ], + ]); + break; + default: + throw new InvalidArgumentException(); + } + $notification->setIcon($this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('text', 'app-dark.svg'))); + $notification->setLink($fileLink); + $this->setParsedSubjectFromRichSubject($notification); + return $notification; + } + + protected function setParsedSubjectFromRichSubject(INotification $notification): void { + $placeholders = $replacements = []; + foreach ($notification->getRichSubjectParameters() as $placeholder => $parameter) { + $placeholders[] = '{' . $placeholder . '}'; + if ($parameter['type'] === 'file') { + $replacements[] = $parameter['path']; + } else { + $replacements[] = $parameter['name']; + } + } + + $notification->setParsedSubject(str_replace($placeholders, $replacements, $notification->getRichSubject())); + } +} diff --git a/src/mixins/uiMention.js b/src/mixins/uiMention.js index f3a79cf28f..4758a90692 100644 --- a/src/mixins/uiMention.js +++ b/src/mixins/uiMention.js @@ -10,24 +10,33 @@ import { getNextcloudUrl } from '../helpers/url.js' export default { methods: { - async uiMention(search) { - let users = [] + async uiMention({ type, text, username }) { + if (type === 'autocomplete') { + let users = [] - if (Config.get('userId') !== null) { - try { - const result = await axios.get(generateOcsUrl('core/autocomplete/get'), { - params: { search }, - }) - users = result.data.ocs.data - } catch (e) { } - } + if (Config.get('userId') !== null) { + try { + const result = await axios.get(generateOcsUrl('core/autocomplete/get'), { + params: { search: text }, + }) + users = result.data.ocs.data + } catch (e) { + } + } - const list = users.map((user) => { - const profile = window.location.protocol + '//' + getNextcloudUrl() + '/index.php/u/' + user.id - return { username: user.label, profile } - }) + const list = users.map((user) => { + const profile = window.location.protocol + '//' + getNextcloudUrl() + '/index.php/u/' + user.id + return { + username: user.label, + profile + } + }) - this.sendPostMessage('Action_Mention', { list }) + this.sendPostMessage('Action_Mention', { list }) + } + if (type === 'selected') { + await axios.post(generateOcsUrl(`apps/richdocuments/api/v1/mention/${this.fileid}`), { mention: username }) + } }, }, } diff --git a/src/view/Office.vue b/src/view/Office.vue index 6f7a507968..d26a32ddf5 100644 --- a/src/view/Office.vue +++ b/src/view/Office.vue @@ -430,9 +430,7 @@ export default { }) break case 'UI_Mention': - if (parsed.args.type === 'autocomplete') { - this.uiMention(parsed.args.text) - } + this.uiMention(parsed.args) break case 'UI_CreateFile': FilesAppIntegration.createNewFile(args.DocumentType)