Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: mail snippets #10276

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ The rating depends on the installed text processing backend. See [the rating ove

Learn more about the Nextcloud Ethical AI Rating [in our blog](https://nextcloud.com/blog/nextcloud-ethical-ai-rating/).
]]></description>
<version>4.2.0-alpha.1</version>
<version>4.2.0-alpha.2</version>
<licence>agpl</licence>
<author homepage="https://github.com/ChristophWurst">Christoph Wurst</author>
<author homepage="https://github.com/GretaD">GretaD</author>
Expand Down
40 changes: 40 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -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/{id}',
'verb' => 'GET',
],
[
'name' => 'snippet#create',
'url' => '/api/snippets',
'verb' => 'POST',
],
[
'name' => 'snippet#update',
'url' => '/api/snippets',
'verb' => 'PUT',
],
[
'name' => 'snippet#delete',
'url' => '/api/snippets/{id}',
'verb' => 'DELETE',
],
[
'name' => 'snippet#share',
'url' => '/api/snippets/share',
'verb' => 'POST',
],
[
'name' => 'snippet#deleteShare',
'url' => '/api/snippets/share/{snippetId}/{shareWith}',
'verb' => 'DELETE',
],
],
'resources' => [
'accounts' => ['url' => '/api/accounts'],
Expand Down
168 changes: 168 additions & 0 deletions lib/Controller/SnippetController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Mail\Controller;

use OCA\Mail\AppInfo\Application;
use OCA\Mail\Db\SnippetShare;
use OCA\Mail\Http\JsonResponse;
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;

class SnippetController extends Controller {
private ?string $uid;

public function __construct(

Check warning on line 26 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L26

Added line #L26 was not covered by tests
IRequest $request,
?string $userId,
private SnippetService $snippetService,
) {
parent::__construct(Application::APP_ID, $request);
$this->uid = $userId;

Check warning on line 32 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L31-L32

Added lines #L31 - L32 were not covered by tests
}

/**
* @NoAdminRequired
*
* @return JsonResponse
*/
#[TrapError]

Check warning on line 40 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L40

Added line #L40 was not covered by tests
public function getOwnSnippets(): JsonResponse {
$snippets = $this->snippetService->findAll($this->uid);

Check failure on line 42 in lib/Controller/SnippetController.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-master

PossiblyNullArgument

lib/Controller/SnippetController.php:42:46: PossiblyNullArgument: Argument 1 of OCA\Mail\Service\SnippetService::findAll cannot be null, possibly null value provided (see https://psalm.dev/078)

Check warning on line 42 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L42

Added line #L42 was not covered by tests

return JsonResponse::success($snippets);

Check warning on line 44 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L44

Added line #L44 was not covered by tests
}

/**
* @NoAdminRequired
*
* @return JsonResponse
*/
#[TrapError]

Check warning on line 52 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L52

Added line #L52 was not covered by tests
public function getSharedSnippets(): JsonResponse {
$snippets = $this->snippetService->findAllSharedWithMe($this->uid);

Check failure on line 54 in lib/Controller/SnippetController.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-master

PossiblyNullArgument

lib/Controller/SnippetController.php:54:58: PossiblyNullArgument: Argument 1 of OCA\Mail\Service\SnippetService::findAllSharedWithMe cannot be null, possibly null value provided (see https://psalm.dev/078)

Check warning on line 54 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L54

Added line #L54 was not covered by tests

return JsonResponse::success($snippets);

Check warning on line 56 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L56

Added line #L56 was not covered by tests
}

/**
* @NoAdminRequired
* @param string $title
* @param string $content
*
* @return JsonResponse
*/
#[TrapError]

Check warning on line 66 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L66

Added line #L66 was not covered by tests
public function create(string $title, string $content): JsonResponse {
if ($this->uid === null) {
return JsonResponse::error('User not found', Http::STATUS_UNAUTHORIZED);

Check warning on line 69 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L68-L69

Added lines #L68 - L69 were not covered by tests
}
$snippet = $this->snippetService->create($this->uid, $title, $content);

Check warning on line 71 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L71

Added line #L71 was not covered by tests

return JsonResponse::success($snippet, Http::STATUS_CREATED);

Check warning on line 73 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L73

Added line #L73 was not covered by tests
}

/**
* @NoAdminRequired
* @param int $id
* @param string $title
* @param string $content
*
* @return JsonResponse
*/
#[TrapError]

Check warning on line 84 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L84

Added line #L84 was not covered by tests
public function update(int $id, string $title, string $content): JsonResponse {

$snippet = $this->snippetService->find($id, $this->uid);

Check failure on line 87 in lib/Controller/SnippetController.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-master

PossiblyNullArgument

lib/Controller/SnippetController.php:87:47: PossiblyNullArgument: Argument 2 of OCA\Mail\Service\SnippetService::find cannot be null, possibly null value provided (see https://psalm.dev/078)

Check warning on line 87 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L87

Added line #L87 was not covered by tests

if ($snippet === null) {

Check failure on line 89 in lib/Controller/SnippetController.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-master

TypeDoesNotContainNull

lib/Controller/SnippetController.php:89:7: TypeDoesNotContainNull: OCA\Mail\Db\Snippet does not contain null (see https://psalm.dev/090)
return JsonResponse::error('Snippet not found', Http::STATUS_NOT_FOUND);

Check warning on line 90 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L89-L90

Added lines #L89 - L90 were not covered by tests
}

$this->snippetService->update($id, $this->uid, $title, $content);

Check warning on line 93 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L93

Added line #L93 was not covered by tests

return JsonResponse::success($snippet, Http::STATUS_OK);

Check warning on line 95 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L95

Added line #L95 was not covered by tests
}

public function delete(int $id): JsonResponse {

Check warning on line 98 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L98

Added line #L98 was not covered by tests
try {
$this->snippetService->delete($id, $this->uid);

Check failure on line 100 in lib/Controller/SnippetController.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-master

PossiblyNullArgument

lib/Controller/SnippetController.php:100:39: PossiblyNullArgument: Argument 2 of OCA\Mail\Service\SnippetService::delete cannot be null, possibly null value provided (see https://psalm.dev/078)
return JsonResponse::success();
} catch (DoesNotExistException $e) {
return JsonResponse::fail('Snippet not found', Http::STATUS_NOT_FOUND);

Check warning on line 103 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L100-L103

Added lines #L100 - L103 were not covered by tests
}
}

/**
* @NoAdminRequired
* @param int $snippetId
* @param string $shareWith
* @param string $type
*
* @return JsonResponse
*/
#[TrapError]

Check warning on line 115 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L115

Added line #L115 was not covered by tests
public function share(int $snippetId, string $shareWith, string $type): JsonResponse {
$snippet = $this->snippetService->find($snippetId, $this->uid);

Check failure on line 117 in lib/Controller/SnippetController.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-master

PossiblyNullArgument

lib/Controller/SnippetController.php:117:54: PossiblyNullArgument: Argument 2 of OCA\Mail\Service\SnippetService::find cannot be null, possibly null value provided (see https://psalm.dev/078)

Check warning on line 117 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L117

Added line #L117 was not covered by tests

if ($snippet === null) {

Check failure on line 119 in lib/Controller/SnippetController.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-master

TypeDoesNotContainNull

lib/Controller/SnippetController.php:119:7: TypeDoesNotContainNull: OCA\Mail\Db\Snippet does not contain null (see https://psalm.dev/090)
return JsonResponse::error('Snippet not found', Http::STATUS_NOT_FOUND);

Check warning on line 120 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L119-L120

Added lines #L119 - L120 were not covered by tests
}

switch ($type) {
case SnippetShare::TYPE_USER:
$this->snippetService->share($snippetId, $shareWith);
return JsonResponse::success();
case SnippetShare::TYPE_GROUP:
$this->snippetService->shareWithGroup($snippetId, $shareWith);
return JsonResponse::success();

Check warning on line 129 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L124-L129

Added lines #L124 - L129 were not covered by tests
default:
return JsonResponse::fail('Invalid share type', Http::STATUS_BAD_REQUEST);

Check warning on line 131 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L131

Added line #L131 was not covered by tests
}

}

public function getShares(int $id): JsonResponse {
$snippet = $this->snippetService->find($id, $this->uid);

Check failure on line 137 in lib/Controller/SnippetController.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-master

PossiblyNullArgument

lib/Controller/SnippetController.php:137:47: PossiblyNullArgument: Argument 2 of OCA\Mail\Service\SnippetService::find cannot be null, possibly null value provided (see https://psalm.dev/078)

Check warning on line 137 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L136-L137

Added lines #L136 - L137 were not covered by tests

if ($snippet === null) {

Check failure on line 139 in lib/Controller/SnippetController.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-master

TypeDoesNotContainNull

lib/Controller/SnippetController.php:139:7: TypeDoesNotContainNull: OCA\Mail\Db\Snippet does not contain null (see https://psalm.dev/090)
return JsonResponse::error('Snippet not found', Http::STATUS_NOT_FOUND);

Check warning on line 140 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L139-L140

Added lines #L139 - L140 were not covered by tests
}

$shares = $this->snippetService->getShares($this->uid, $id);

Check warning on line 143 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L143

Added line #L143 was not covered by tests

return JsonResponse::success($shares);

Check warning on line 145 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L145

Added line #L145 was not covered by tests
}

/**
* @NoAdminRequired
* @param int $snippetId
* @param string $shareWith
*
* @return JsonResponse
*/
#[TrapError]

Check warning on line 155 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L155

Added line #L155 was not covered by tests
public function deleteShare(int $snippetId, string $shareWith): JsonResponse {
$snippet = $this->snippetService->find($snippetId, $this->uid);

Check failure on line 157 in lib/Controller/SnippetController.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-master

PossiblyNullArgument

lib/Controller/SnippetController.php:157:54: PossiblyNullArgument: Argument 2 of OCA\Mail\Service\SnippetService::find cannot be null, possibly null value provided (see https://psalm.dev/078)

Check warning on line 157 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L157

Added line #L157 was not covered by tests

if ($snippet === null) {
return JsonResponse::error('Snippet not found', Http::STATUS_NOT_FOUND);

Check warning on line 160 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L159-L160

Added lines #L159 - L160 were not covered by tests
}

$this->snippetService->unshare($snippetId, $shareWith);

Check warning on line 163 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L163

Added line #L163 was not covered by tests

return JsonResponse::success();

Check warning on line 165 in lib/Controller/SnippetController.php

View check run for this annotation

Codecov / codecov/patch

lib/Controller/SnippetController.php#L165

Added line #L165 was not covered by tests
}

}
49 changes: 49 additions & 0 deletions lib/Db/Snippet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Mail\Db;

use JsonSerializable;
use OCP\AppFramework\Db\Entity;
use ReturnTypeWillChange;

/**
* @method string getOwner()
* @method void setOwner(string $owner)
* @method string getTitle()
* @method void setTitle(string $title)
* @method string getContent()
* @method void setContent(string $content)
* @method string getPreview()
* @method void setPreview(string $preview)
*/
class Snippet extends Entity implements JsonSerializable {
protected $owner;
protected $title;
protected $content;
protected $preview;

public function __construct() {
$this->addType('owner', 'string');
$this->addType('title', 'string');
$this->addType('content', 'string');
$this->addType('preview', 'string');

Check warning on line 36 in lib/Db/Snippet.php

View check run for this annotation

Codecov / codecov/patch

lib/Db/Snippet.php#L32-L36

Added lines #L32 - L36 were not covered by tests
}

#[ReturnTypeWillChange]

Check warning on line 39 in lib/Db/Snippet.php

View check run for this annotation

Codecov / codecov/patch

lib/Db/Snippet.php#L39

Added line #L39 was not covered by tests
public function jsonSerialize() {
return [
'id' => $this->getId(),
'owner' => $this->getOwner(),
'title' => $this->getTitle(),
'content' => $this->getContent(),
'preview' => $this->getPreview(),
];

Check warning on line 47 in lib/Db/Snippet.php

View check run for this annotation

Codecov / codecov/patch

lib/Db/Snippet.php#L41-L47

Added lines #L41 - L47 were not covered by tests
}
}
83 changes: 83 additions & 0 deletions lib/Db/SnippetMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Mail\Db;

use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;

/**
* @template-extends QBMapper<Snippet>
*/
class SnippetMapper extends QBMapper {
/**
* @param IDBConnection $db
*/
public function __construct(IDBConnection $db) {
parent::__construct($db, 'mail_snippets');

Check warning on line 25 in lib/Db/SnippetMapper.php

View check run for this annotation

Codecov / codecov/patch

lib/Db/SnippetMapper.php#L24-L25

Added lines #L24 - L25 were not covered by tests
}

/**
* @param int $id
* @param string $owner
* @return Snippet
*
* @throws DoesNotExistException
*/
public function find(int $id, string $owner): Snippet {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
->andWhere($qb->expr()->eq('owner', $qb->createNamedParameter($owner)));

Check warning on line 40 in lib/Db/SnippetMapper.php

View check run for this annotation

Codecov / codecov/patch

lib/Db/SnippetMapper.php#L35-L40

Added lines #L35 - L40 were not covered by tests

return $this->findEntity($qb);

Check warning on line 42 in lib/Db/SnippetMapper.php

View check run for this annotation

Codecov / codecov/patch

lib/Db/SnippetMapper.php#L42

Added line #L42 was not covered by tests
}

/**
* @param string $owner
* @return Snippet[]
*/
public function findAll(string $owner): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where(
$qb->expr()->eq('owner', $qb->createNamedParameter($owner, IQueryBuilder::PARAM_STR))
);

Check warning on line 55 in lib/Db/SnippetMapper.php

View check run for this annotation

Codecov / codecov/patch

lib/Db/SnippetMapper.php#L49-L55

Added lines #L49 - L55 were not covered by tests

return $this->findEntities($qb);

Check warning on line 57 in lib/Db/SnippetMapper.php

View check run for this annotation

Codecov / codecov/patch

lib/Db/SnippetMapper.php#L57

Added line #L57 was not covered by tests
}

/**
* @param string $userId
* @param array $groups
* @return Snippet[]
*/
public function findSharedWithMe(string $userId, array $groups): array {
$qb = $this->db->getQueryBuilder();
$qb->select('s.*')
->from($this->getTableName(), 's')
->join('s', 'mail_snippets_shares', 'share', $qb->expr()->eq('s.id', 'share.snippet_id'))
->where($qb->expr()->andX(
$qb->expr()->eq('share.share_with', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)),
$qb->expr()->eq('share.type', $qb->createNamedParameter('user', IQueryBuilder::PARAM_STR))
))
->orWhere(
$qb->expr()->andX(
$qb->expr()->in('share.share_with', $qb->createNamedParameter($groups, IQueryBuilder::PARAM_STR_ARRAY)),
$qb->expr()->eq('share.type', $qb->createNamedParameter('group', IQueryBuilder::PARAM_STR))
)
);
return $this->findEntities($qb);

Check warning on line 80 in lib/Db/SnippetMapper.php

View check run for this annotation

Codecov / codecov/patch

lib/Db/SnippetMapper.php#L65-L80

Added lines #L65 - L80 were not covered by tests
}

}
Loading
Loading