diff --git a/appinfo/routes.php b/appinfo/routes.php index eddd1a5ee3..9bcbcf8a60 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -490,6 +490,46 @@ 'url' => '/api/follow-up/check-message-ids', 'verb' => 'POST', ], + [ + 'name' => 'snippet#getOwnSnippets', + 'url' => '/api/snippets', + 'verb' => 'GET', + ], + [ + 'name' => 'snippet#getSharedSnippets', + 'url' => '/api/snippets/share', + 'verb' => 'GET', + ], + [ + 'name' => 'snippet#getShares', + 'url' => '/api/snippets/share/shares', + 'verb' => 'GET', + ], + [ + 'name' => 'snippet#create', + 'url' => '/api/snippets', + 'verb' => 'POST', + ], + [ + 'name' => 'snippet#update', + 'url' => '/api/snippets', + 'verb' => 'PUT', + ], + [ + 'name' => 'snippet#delete', + 'url' => '/api/snippets', + 'verb' => 'DELETE', + ], + [ + 'name' => 'snippet#share', + 'url' => '/api/snippets/share', + 'verb' => 'POST', + ], + [ + 'name' => 'snippet#deleteShare', + 'url' => '/api/snippets/share', + 'verb' => 'DELETE', + ], ], 'resources' => [ 'accounts' => ['url' => '/api/accounts'], diff --git a/lib/Controller/SnippetController.php b/lib/Controller/SnippetController.php index 93b6ae38f8..c9c6ec64c2 100644 --- a/lib/Controller/SnippetController.php +++ b/lib/Controller/SnippetController.php @@ -15,6 +15,7 @@ use OCA\Mail\Http\TrapError; use OCA\Mail\Service\SnippetService; use OCP\AppFramework\Controller; +use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\IRequest; @@ -96,7 +97,24 @@ public function update(int $snippetId, string $title, string $content): JsonResp return JsonResponse::success($snippet, Http::STATUS_OK); } + public function delete($snippetId): JsonResponse { + try { + $this->snippetService->delete($snippetId, $this->uid); + return JsonResponse::success(); + } catch (DoesNotExistException $e) { + return JsonResponse::fail('Snippet not found', Http::STATUS_NOT_FOUND); + } + } + /** + * @NoAdminRequired + * @param int $snippetId + * @param string $shareWith + * @param string $type + * + * @return JsonResponse + */ + #[TrapError] public function share(int $snippetId, string $shareWith, string $type): JsonResponse { $snippet = $this->snippetService->find($snippetId, $this->uid); @@ -117,4 +135,36 @@ public function share(int $snippetId, string $shareWith, string $type): JsonResp } + public function getShares($id): JsonResponse { + $snippet = $this->snippetService->find($snippetId, $this->uid); + + if ($snippet === null) { + return JsonResponse::error('Snippet not found', Http::STATUS_NOT_FOUND); + } + + $shares = $this->snippetService->getShares($this->uid, $snippetId); + + return JsonResponse::success($shares); + } + + /** + * @NoAdminRequired + * @param int $snippetId + * @param string $shareWith + * + * @return JsonResponse + */ + #[TrapError] + public function deleteShare(int $snippetId, string $shareWith): JsonResponse { + $snippet = $this->snippetService->find($snippetId, $this->uid); + + if ($snippet === null) { + return JsonResponse::error('Snippet not found', Http::STATUS_NOT_FOUND); + } + + $this->snippetService->unshare($snippetId, $shareWith); + + return JsonResponse::success(); + } + } diff --git a/lib/Db/SnippetShareMapper.php b/lib/Db/SnippetShareMapper.php index 9af37c0741..3386a0326b 100644 --- a/lib/Db/SnippetShareMapper.php +++ b/lib/Db/SnippetShareMapper.php @@ -82,7 +82,7 @@ public function findAllShares(string $owner): array { * * @return SnippetShare[] */ - public function findSnippetShares(string $owner, string $snippetId): array { + public function findSnippetShares(string $owner, int $snippetId): array { $qb = $this->db->getQueryBuilder(); $qb->select('sshare.*') ->from($this->getTableName(), 'sshare') @@ -90,7 +90,7 @@ public function findSnippetShares(string $owner, string $snippetId): array { $qb->expr()->eq('s.owner', $qb->createNamedParameter($owner, IQueryBuilder::PARAM_STR)) ) ->andWhere( - $qb->expr()->eq('sshare.snippet_id', $qb->createNamedParameter($snippetId, IQueryBuilder::PARAM_STR)) + $qb->expr()->eq('sshare.snippet_id', $qb->createNamedParameter($snippetId, IQueryBuilder::PARAM_INT)) ); return $this->findEntities($qb); diff --git a/lib/Service/SnippetService.php b/lib/Service/SnippetService.php index cc40ceccb5..d791c88207 100644 --- a/lib/Service/SnippetService.php +++ b/lib/Service/SnippetService.php @@ -49,10 +49,11 @@ public function findAll(string $userId): array { /** * @param string + * @return Snippet[] */ public function findAllSharedWithMe(string $userId): array { $groups = $this->groupManager->getUserGroupIds($userId); - return $this->snippetShareMapper->findSharedWithMe($userId, $groups); + return $this->snippetMapper->findSharedWithMe($userId, $groups); } /** * @param int $snippetId @@ -103,7 +104,13 @@ public function delete(int $snippetId, string $userId): void { $this->snippetMapper->delete($snippet); } - //TODO: run owner check on controller level + + /** + * @param int $snippetId + * @param string $shareWith + * @throws DoesNotExistException + * @throws NotPermittedException + */ public function share(int $snippetId, string $shareWith): void { $sharee = $this->userManager->get($shareWith); @@ -120,6 +127,16 @@ public function share(int $snippetId, string $shareWith): void { $this->snippetShareMapper->insert($share); } + public function getShares(string $uid, int $snippetId): array { + return $this->snippetShareMapper->findSnippetShares($uid, $snippetId); + } + + /** + * @param int $snippetId + * @param string $groupId + * @throws DoesNotExistException + * @throws NotPermittedException + */ public function shareWithGroup(int $snippetId, string $groupId): void { if (!$this->groupManager->groupExists($groupId)) { throw new DoesNotExistException('Group does not exist'); @@ -134,6 +151,11 @@ public function shareWithGroup(int $snippetId, string $groupId): void { $this->snippetShareMapper->insert($share); } + /** + * @param int $snippetId + * @param string $shareWith + * @throws DoesNotExistException + */ public function unshare(int $snippetId, string $shareWith): void { $share = $this->snippetShareMapper->find($snippetId, $shareWith); $this->snippetShareMapper->delete($share); diff --git a/src/components/AppSettingsMenu.vue b/src/components/AppSettingsMenu.vue index a62928d796..8e31368674 100755 --- a/src/components/AppSettingsMenu.vue +++ b/src/components/AppSettingsMenu.vue @@ -293,9 +293,10 @@ {{ t('mail','My snippets') }} - + {{ t('mail','Shared with me') }} - + @@ -319,7 +320,7 @@ import TrustedSenders from './TrustedSenders.vue' import InternalAddress from './InternalAddress.vue' import isMobile from '@nextcloud/vue/dist/Mixins/isMobile.js' import { mapGetters } from 'vuex' -import ListItem from './snippets/ListItem.vue' +import List from './snippets/List.vue' export default { name: 'AppSettingsMenu', @@ -338,7 +339,7 @@ export default { CompactMode, VerticalSplit, HorizontalSplit, - ListItem, + List, }, mixins: [isMobile], props: { @@ -407,6 +408,12 @@ export default { layoutMode() { return this.$store.getters.getPreference('layout-mode', 'vertical-split') }, + mySnippets() { + return this.$store.getters.getSnippets() + }, + shareSnippet() { + return this.$store.getters.getSharedSnippets() + }, }, watch: { showSettings(value) { diff --git a/src/components/Composer.vue b/src/components/Composer.vue index 8357ce8048..edd76170aa 100644 --- a/src/components/Composer.vue +++ b/src/components/Composer.vue @@ -243,6 +243,7 @@ :placeholder="t('mail', 'Write message …')" :focus="isReply || !isFirstOpen" :bus="bus" + :snippets="snippets" @input="onEditorInput" @ready="onEditorReady" @mention="handleMention" @@ -889,6 +890,11 @@ export default { return missingCertificates }, + + snippets() { + return this.$store.getters.getSharedSnippets.map(snippet => ({ title: snippet.title, content: snippet.content })) + .concat(this.$store.getters.getMySnippets.map(snippet => ({ title: snippet.title, content: snippet.content }))) + }, }, watch: { '$route.params.threadId'() { @@ -987,6 +993,10 @@ export default { if (this.sendAt && this.isSendAtCustom) { this.selectedDate = new Date(this.sendAt) } + if (this.snippets.length === 0) { + this.$store.dispatch('fetchSharedSnippets') + this.$store.dispatch('fetchMySnippets') + } }, beforeDestroy() { window.removeEventListener('mailvelope', this.onMailvelopeLoaded) diff --git a/src/components/TextEditor.vue b/src/components/TextEditor.vue index 35dca92b4a..ed01e459be 100644 --- a/src/components/TextEditor.vue +++ b/src/components/TextEditor.vue @@ -78,6 +78,10 @@ export default { type: Boolean, default: false, }, + snippets: { + type: Array, + default: () => [], + }, }, data() { const plugins = [ diff --git a/src/components/snippets/List.vue b/src/components/snippets/List.vue index e7e9f3fd3f..77d8978af2 100644 --- a/src/components/snippets/List.vue +++ b/src/components/snippets/List.vue @@ -3,22 +3,28 @@ - SPDX-License-Identifier: AGPL-3.0-or-later --> + + + {{ t('mail', 'No snippets available') }} +