Skip to content

Commit 1c918dc

Browse files
committed
feat: mail snippets
Signed-off-by: Hamza Mahjoubi <hamzamahjoubi221@gmail.com>
1 parent a10dbc6 commit 1c918dc

22 files changed

Lines changed: 2192 additions & 8 deletions

appinfo/routes.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,46 @@
490490
'url' => '/api/follow-up/check-message-ids',
491491
'verb' => 'POST',
492492
],
493+
[
494+
'name' => 'snippet#getOwnSnippets',
495+
'url' => '/api/snippets',
496+
'verb' => 'GET',
497+
],
498+
[
499+
'name' => 'snippet#getSharedSnippets',
500+
'url' => '/api/snippets/share',
501+
'verb' => 'GET',
502+
],
503+
[
504+
'name' => 'snippet#getShares',
505+
'url' => '/api/snippets/share/shares/{id}',
506+
'verb' => 'GET',
507+
],
508+
[
509+
'name' => 'snippet#create',
510+
'url' => '/api/snippets',
511+
'verb' => 'POST',
512+
],
513+
[
514+
'name' => 'snippet#update',
515+
'url' => '/api/snippets',
516+
'verb' => 'PUT',
517+
],
518+
[
519+
'name' => 'snippet#delete',
520+
'url' => '/api/snippets/{id}',
521+
'verb' => 'DELETE',
522+
],
523+
[
524+
'name' => 'snippet#share',
525+
'url' => '/api/snippets/share',
526+
'verb' => 'POST',
527+
],
528+
[
529+
'name' => 'snippet#deleteShare',
530+
'url' => '/api/snippets/share/{snippetId}/{shareWith}',
531+
'verb' => 'DELETE',
532+
],
493533
],
494534
'resources' => [
495535
'accounts' => ['url' => '/api/accounts'],
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\Mail\Controller;
11+
12+
use OCA\Mail\AppInfo\Application;
13+
use OCA\Mail\Db\SnippetShare;
14+
use OCA\Mail\Http\JsonResponse;
15+
use OCA\Mail\Http\TrapError;
16+
use OCA\Mail\Service\SnippetService;
17+
use OCP\AppFramework\Controller;
18+
use OCP\AppFramework\Db\DoesNotExistException;
19+
use OCP\AppFramework\Http;
20+
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
21+
use OCP\IRequest;
22+
23+
class SnippetController extends Controller {
24+
private ?string $uid;
25+
26+
public function __construct(
27+
IRequest $request,
28+
?string $userId,
29+
private SnippetService $snippetService,
30+
) {
31+
parent::__construct(Application::APP_ID, $request);
32+
$this->uid = $userId;
33+
}
34+
35+
/**
36+
* @NoAdminRequired
37+
*
38+
* @return JsonResponse
39+
*/
40+
#[TrapError]
41+
public function getOwnSnippets(): JsonResponse {
42+
if ($this->uid === null) {
43+
return JsonResponse::error('User not found', Http::STATUS_UNAUTHORIZED);
44+
}
45+
$snippets = $this->snippetService->findAll($this->uid);
46+
47+
return JsonResponse::success($snippets);
48+
}
49+
50+
/**
51+
* @NoAdminRequired
52+
*
53+
* @return JsonResponse
54+
*/
55+
#[TrapError]
56+
public function getSharedSnippets(): JsonResponse {
57+
if ($this->uid === null) {
58+
return JsonResponse::error('User not found', Http::STATUS_UNAUTHORIZED);
59+
}
60+
try {
61+
$snippets = $this->snippetService->findAllSharedWithMe($this->uid);
62+
} catch (DoesNotExistException $e) {
63+
return JsonResponse::error('Sharee not found', Http::STATUS_UNAUTHORIZED);
64+
}
65+
66+
return JsonResponse::success($snippets);
67+
}
68+
69+
/**
70+
* @NoAdminRequired
71+
* @param string $title
72+
* @param string $content
73+
*
74+
* @return JsonResponse
75+
*/
76+
#[TrapError]
77+
public function create(string $title, string $content): JsonResponse {
78+
if ($this->uid === null) {
79+
return JsonResponse::error('User not found', Http::STATUS_UNAUTHORIZED);
80+
}
81+
$snippet = $this->snippetService->create($this->uid, $title, $content);
82+
83+
return JsonResponse::success($snippet, Http::STATUS_CREATED);
84+
}
85+
86+
/**
87+
* @NoAdminRequired
88+
* @param int $id
89+
* @param string $title
90+
* @param string $content
91+
*
92+
* @return JsonResponse
93+
*/
94+
#[TrapError]
95+
public function update(int $id, string $title, string $content): JsonResponse {
96+
97+
if ($this->uid === null) {
98+
return JsonResponse::error('User not found', Http::STATUS_UNAUTHORIZED);
99+
}
100+
101+
$snippet = $this->snippetService->find($id, $this->uid);
102+
103+
if ($snippet === null) {
104+
return JsonResponse::error('Snippet not found', Http::STATUS_NOT_FOUND);
105+
}
106+
107+
$this->snippetService->update($id, $this->uid, $title, $content);
108+
109+
return JsonResponse::success($snippet, Http::STATUS_OK);
110+
}
111+
112+
public function delete(int $id): JsonResponse {
113+
if ($this->uid === null) {
114+
return JsonResponse::error('User not found', Http::STATUS_UNAUTHORIZED);
115+
}
116+
try {
117+
$this->snippetService->delete($id, $this->uid);
118+
return JsonResponse::success();
119+
} catch (DoesNotExistException $e) {
120+
return JsonResponse::fail('Snippet not found', Http::STATUS_NOT_FOUND);
121+
}
122+
}
123+
124+
/**
125+
* @NoAdminRequired
126+
* @param int $snippetId
127+
* @param string $shareWith
128+
* @param string $type
129+
*
130+
* @return JsonResponse
131+
*/
132+
#[TrapError]
133+
public function share(int $snippetId, string $shareWith, string $type): JsonResponse {
134+
if ($this->uid === null) {
135+
return JsonResponse::error('User not found', Http::STATUS_UNAUTHORIZED);
136+
}
137+
138+
$snippet = $this->snippetService->find($snippetId, $this->uid);
139+
140+
if ($snippet === null) {
141+
return JsonResponse::error('Snippet not found', Http::STATUS_NOT_FOUND);
142+
}
143+
144+
switch ($type) {
145+
case SnippetShare::TYPE_USER:
146+
$this->snippetService->share($snippetId, $shareWith);
147+
return JsonResponse::success();
148+
case SnippetShare::TYPE_GROUP:
149+
$this->snippetService->shareWithGroup($snippetId, $shareWith);
150+
return JsonResponse::success();
151+
default:
152+
return JsonResponse::fail('Invalid share type', Http::STATUS_BAD_REQUEST);
153+
}
154+
155+
}
156+
157+
public function getShares(int $id): JsonResponse {
158+
if ($this->uid === null) {
159+
return JsonResponse::error('User not found', Http::STATUS_UNAUTHORIZED);
160+
}
161+
162+
$snippet = $this->snippetService->find($id, $this->uid);
163+
164+
if ($snippet === null) {
165+
return JsonResponse::error('Snippet not found', Http::STATUS_NOT_FOUND);
166+
}
167+
168+
$shares = $this->snippetService->getShares($id);
169+
170+
return JsonResponse::success($shares);
171+
}
172+
173+
/**
174+
* @NoAdminRequired
175+
* @param int $snippetId
176+
* @param string $shareWith
177+
*
178+
* @return JsonResponse
179+
*/
180+
#[TrapError]
181+
public function deleteShare(int $snippetId, string $shareWith): JsonResponse {
182+
if ($this->uid === null) {
183+
return JsonResponse::error('User not found', Http::STATUS_UNAUTHORIZED);
184+
}
185+
186+
$snippet = $this->snippetService->find($snippetId, $this->uid);
187+
188+
if ($snippet === null) {
189+
return JsonResponse::error('Snippet not found', Http::STATUS_NOT_FOUND);
190+
}
191+
192+
$this->snippetService->unshare($snippetId, $shareWith);
193+
194+
return JsonResponse::success();
195+
}
196+
197+
}

lib/Db/Snippet.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\Mail\Db;
11+
12+
use JsonSerializable;
13+
use OCP\AppFramework\Db\Entity;
14+
use ReturnTypeWillChange;
15+
16+
/**
17+
* @method string getOwner()
18+
* @method void setOwner(string $owner)
19+
* @method string getTitle()
20+
* @method void setTitle(string $title)
21+
* @method string getContent()
22+
* @method void setContent(string $content)
23+
* @method string getPreview()
24+
* @method void setPreview(string $preview)
25+
*/
26+
class Snippet extends Entity implements JsonSerializable {
27+
protected $owner;
28+
protected $title;
29+
protected $content;
30+
protected $preview;
31+
32+
public function __construct() {
33+
$this->addType('owner', 'string');
34+
$this->addType('title', 'string');
35+
$this->addType('content', 'string');
36+
$this->addType('preview', 'string');
37+
}
38+
39+
#[ReturnTypeWillChange]
40+
public function jsonSerialize() {
41+
return [
42+
'id' => $this->getId(),
43+
'owner' => $this->getOwner(),
44+
'title' => $this->getTitle(),
45+
'content' => $this->getContent(),
46+
'preview' => $this->getPreview(),
47+
];
48+
}
49+
}

lib/Db/SnippetMapper.php

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\Mail\Db;
11+
12+
use OCP\AppFramework\Db\DoesNotExistException;
13+
use OCP\AppFramework\Db\QBMapper;
14+
use OCP\DB\QueryBuilder\IQueryBuilder;
15+
use OCP\IDBConnection;
16+
17+
/**
18+
* @template-extends QBMapper<Snippet>
19+
*/
20+
class SnippetMapper extends QBMapper {
21+
/**
22+
* @param IDBConnection $db
23+
*/
24+
public function __construct(IDBConnection $db) {
25+
parent::__construct($db, 'mail_snippets');
26+
}
27+
28+
/**
29+
* @param int $id
30+
* @param string $owner
31+
* @return Snippet|null
32+
*/
33+
public function find(int $id, string $owner): ?Snippet {
34+
$qb = $this->db->getQueryBuilder();
35+
$qb->select('*')
36+
->from($this->getTableName())
37+
->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
38+
->andWhere($qb->expr()->eq('owner', $qb->createNamedParameter($owner)));
39+
try {
40+
return $this->findEntity($qb);
41+
} catch (DoesNotExistException $e) {
42+
return null;
43+
}
44+
}
45+
46+
/**
47+
* @param string $owner
48+
* @return Snippet[]
49+
*/
50+
public function findAll(string $owner): array {
51+
$qb = $this->db->getQueryBuilder();
52+
$qb->select('*')
53+
->from($this->getTableName())
54+
->where(
55+
$qb->expr()->eq('owner', $qb->createNamedParameter($owner, IQueryBuilder::PARAM_STR))
56+
);
57+
58+
return $this->findEntities($qb);
59+
}
60+
61+
/**
62+
* @param string $userId
63+
* @param array $groups
64+
* @return Snippet[]
65+
*/
66+
public function findSharedWithMe(string $userId, array $groups): array {
67+
$qb = $this->db->getQueryBuilder();
68+
$qb->select('s.*')
69+
->from($this->getTableName(), 's')
70+
->join('s', 'mail_snippets_shares', 'share', $qb->expr()->eq('s.id', 'share.snippet_id'))
71+
->where($qb->expr()->andX(
72+
$qb->expr()->eq('share.share_with', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)),
73+
$qb->expr()->eq('share.type', $qb->createNamedParameter('user', IQueryBuilder::PARAM_STR))
74+
))
75+
->orWhere(
76+
$qb->expr()->andX(
77+
$qb->expr()->in('share.share_with', $qb->createNamedParameter($groups, IQueryBuilder::PARAM_STR_ARRAY)),
78+
$qb->expr()->eq('share.type', $qb->createNamedParameter('group', IQueryBuilder::PARAM_STR))
79+
)
80+
);
81+
return $this->findEntities($qb);
82+
}
83+
84+
}

0 commit comments

Comments
 (0)