Add long comments with attachment

Signed-off-by: Vitor Mattos <vitor@php.rio>
This commit is contained in:
Vitor Mattos
2021-07-26 23:58:30 -03:00
committed by Julius Härtl
parent e87c063076
commit 4561887348
5 changed files with 76 additions and 15 deletions

View File

@@ -9,14 +9,13 @@ Project management, time management or ideation, Deck makes it easier for you to
## Using Deck ## Using Deck
Overall, Deck is easy to use. You can create boards, add users, share the Deck, work collaboratively and in real time. Overall, Deck is easy to use. You can create boards, add users, share the Deck, work collaboratively and in real time.
- 1. [Create my first board](#1-create-my-first-board) 1. [Create my first board](#1-create-my-first-board)
- 2. [Create stacks and cards](#2-create-stacks-and-cards) 2. [Create stacks and cards](#2-create-stacks-and-cards)
- 3. [Handle cards options](#3-handle-cards-options) 3. [Handle cards options](#3-handle-cards-options)
- 4. [Archive old tasks](#4-archive-old-tasks) 4. [Archive old tasks](#4-archive-old-tasks)
- 5. [Manage your board](#5-manage-your-board) 5. [Manage your board](#5-manage-your-board)
- 6. [Import boards](#6-import-boards) 6. [Import boards](#6-import-boards)
- [Trello JSON](#trello-json) 7. [Search](#7-search)
- [Trello API](#trello-api)
### 1. Create my first board ### 1. Create my first board
In this example, we're going to create a board and share it with an other nextcloud user. In this example, we're going to create a board and share it with an other nextcloud user.
@@ -136,14 +135,14 @@ Example configuration file:
} }
``` ```
## Search ### 7. Search
Deck provides a global search either through the unified search in the Nextcloud header or with the inline search next to the board controls. Deck provides a global search either through the unified search in the Nextcloud header or with the inline search next to the board controls.
This search allows advanced filtering of cards across all board of the logged in user. This search allows advanced filtering of cards across all board of the logged in user.
For example the search `project tag:ToDo assigned:alice assigned:bob` will return all cards where the card title or description contains project **and** the tag ToDo is set **and** the user alice is assigned **and** the user bob is assigned. For example the search `project tag:ToDo assigned:alice assigned:bob` will return all cards where the card title or description contains project **and** the tag ToDo is set **and** the user alice is assigned **and** the user bob is assigned.
### Supported search filters #### Supported search filters
| Filter | Operators | Query | | Filter | Operators | Query |
| ----------- | ----------------- | ------------------------------------------------------------ | | ----------- | ----------------- | ------------------------------------------------------------ |

View File

@@ -29,6 +29,8 @@ use OCA\Deck\AppInfo\Application;
use OCA\Deck\BadRequestException; use OCA\Deck\BadRequestException;
use OCA\Deck\Db\AclMapper; use OCA\Deck\Db\AclMapper;
use OCA\Deck\Db\AssignmentMapper; use OCA\Deck\Db\AssignmentMapper;
use OCA\Deck\Db\Attachment;
use OCA\Deck\Db\AttachmentMapper;
use OCA\Deck\Db\Board; use OCA\Deck\Db\Board;
use OCA\Deck\Db\BoardMapper; use OCA\Deck\Db\BoardMapper;
use OCA\Deck\Db\CardMapper; use OCA\Deck\Db\CardMapper;
@@ -61,6 +63,8 @@ class BoardImportService {
private $cardMapper; private $cardMapper;
/** @var AssignmentMapper */ /** @var AssignmentMapper */
private $assignmentMapper; private $assignmentMapper;
/** @var AttachmentMapper */
private $attachmentMapper;
/** @var ICommentsManager */ /** @var ICommentsManager */
private $commentsManager; private $commentsManager;
/** @var string */ /** @var string */
@@ -96,6 +100,7 @@ class BoardImportService {
LabelMapper $labelMapper, LabelMapper $labelMapper,
StackMapper $stackMapper, StackMapper $stackMapper,
AssignmentMapper $assignmentMapper, AssignmentMapper $assignmentMapper,
AttachmentMapper $attachmentMapper,
CardMapper $cardMapper, CardMapper $cardMapper,
ICommentsManager $commentsManager ICommentsManager $commentsManager
) { ) {
@@ -107,6 +112,7 @@ class BoardImportService {
$this->stackMapper = $stackMapper; $this->stackMapper = $stackMapper;
$this->cardMapper = $cardMapper; $this->cardMapper = $cardMapper;
$this->assignmentMapper = $assignmentMapper; $this->assignmentMapper = $assignmentMapper;
$this->attachmentMapper = $attachmentMapper;
$this->commentsManager = $commentsManager; $this->commentsManager = $commentsManager;
$this->board = new Board(); $this->board = new Board();
$this->disableCommentsEvents(); $this->disableCommentsEvents();
@@ -342,6 +348,24 @@ class BoardImportService {
} }
} }
public function insertAttachment(Attachment $attachment, string $content): Attachment {
$service = \OC::$server->get(FileService::class);
$folder = $service->getFolder($attachment);
if ($folder->fileExists($attachment->getData())) {
$attachment = $this->attachmentMapper->findByData($attachment->getCardId(), $attachment->getData());
throw new ConflictException('File already exists.', $attachment);
}
$target = $folder->newFile($attachment->getData());
$target->putContent($content);
$attachment = $this->attachmentMapper->insert($attachment);
$service->extendData($attachment);
return $attachment;
}
public function setData(\stdClass $data): void { public function setData(\stdClass $data): void {
$this->data = $data; $this->data = $data;
} }

View File

@@ -26,6 +26,7 @@ namespace OCA\Deck\Service;
use OCP\Http\Client\IClient; use OCP\Http\Client\IClient;
use OCP\Http\Client\IClientService; use OCP\Http\Client\IClientService;
use OCP\IL10N; use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\IUserManager; use OCP\IUserManager;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
@@ -44,11 +45,12 @@ class BoardImportTrelloApiService extends BoardImportTrelloJsonService {
public function __construct( public function __construct(
IUserManager $userManager, IUserManager $userManager,
IURLGenerator $urlGenerator,
IL10N $l10n, IL10N $l10n,
LoggerInterface $logger, LoggerInterface $logger,
IClientService $httpClientService IClientService $httpClientService
) { ) {
parent::__construct($userManager, $l10n); parent::__construct($userManager, $urlGenerator, $l10n);
$this->logger = $logger; $this->logger = $logger;
$this->httpClient = $httpClientService->newClient(); $this->httpClient = $httpClientService->newClient();
} }

View File

@@ -27,11 +27,14 @@ use OC\Comments\Comment;
use OCA\Deck\BadRequestException; use OCA\Deck\BadRequestException;
use OCA\Deck\Db\Acl; use OCA\Deck\Db\Acl;
use OCA\Deck\Db\Assignment; use OCA\Deck\Db\Assignment;
use OCA\Deck\Db\Attachment;
use OCA\Deck\Db\Board; use OCA\Deck\Db\Board;
use OCA\Deck\Db\Card; use OCA\Deck\Db\Card;
use OCA\Deck\Db\Label; use OCA\Deck\Db\Label;
use OCA\Deck\Db\Stack; use OCA\Deck\Db\Stack;
use OCP\Comments\IComment;
use OCP\IL10N; use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\IUser; use OCP\IUser;
use OCP\IUserManager; use OCP\IUserManager;
@@ -40,6 +43,8 @@ class BoardImportTrelloJsonService extends ABoardImportService {
public static $name = 'Trello JSON'; public static $name = 'Trello JSON';
/** @var IUserManager */ /** @var IUserManager */
private $userManager; private $userManager;
/** @var IURLGenerator */
private $urlGenerator;
/** @var IL10N */ /** @var IL10N */
private $l10n; private $l10n;
/** @var IUser[] */ /** @var IUser[] */
@@ -47,9 +52,11 @@ class BoardImportTrelloJsonService extends ABoardImportService {
public function __construct( public function __construct(
IUserManager $userManager, IUserManager $userManager,
IURLGenerator $urlGenerator,
IL10N $l10n IL10N $l10n
) { ) {
$this->userManager = $userManager; $this->userManager = $userManager;
$this->urlGenerator = $urlGenerator;
$this->l10n = $l10n; $this->l10n = $l10n;
} }
@@ -113,19 +120,48 @@ class BoardImportTrelloJsonService extends ABoardImportService {
$trelloComments = array_combine($keys, $values); $trelloComments = array_combine($keys, $values);
$trelloComments = $this->sortComments($trelloComments); $trelloComments = $this->sortComments($trelloComments);
foreach ($trelloComments as $commentId => $trelloComment) { foreach ($trelloComments as $commentId => $trelloComment) {
$cardId = $this->cards[$trelloCard->id]->getId();
$comment = new Comment(); $comment = new Comment();
if (!empty($this->getImportService()->getConfig('uidRelation')->{$trelloComment->memberCreator->username})) { if (!empty($this->getImportService()->getConfig('uidRelation')->{$trelloComment->memberCreator->username})) {
$actor = $this->getImportService()->getConfig('uidRelation')->{$trelloComment->memberCreator->username}->getUID(); $actor = $this->getImportService()->getConfig('uidRelation')->{$trelloComment->memberCreator->username}->getUID();
} else { } else {
$actor = $this->getImportService()->getConfig('owner')->getUID(); $actor = $this->getImportService()->getConfig('owner')->getUID();
} }
$message = $this->replaceUsernames($trelloComment->data->text);
if (mb_strlen($message, 'UTF-8') > IComment::MAX_MESSAGE_LENGTH) {
$attachment = new Attachment();
$attachment->setCardId($cardId);
$attachment->setType('deck_file');
$attachment->setCreatedBy($actor);
$attachment->setLastModified(time());
$attachment->setCreatedAt(time());
$attachment->setData('comment_' . $commentId . '.md');
$attachment = $this->getImportService()->insertAttachment($attachment, $message);
$urlToDownloadAttachment = $this->urlGenerator->linkToRouteAbsolute(
'deck.attachment.display',
[
'cardId' => $cardId,
'attachmentId' => $attachment->getId()
]
);
$message = $this->l10n->t(
"This comment has more than %s characters.\n" .
"Added as an attachment to the card with name %s\n" .
"Accessible on URL: %s.",
[
IComment::MAX_MESSAGE_LENGTH,
'comment_' . $commentId . '.md',
$urlToDownloadAttachment
]
);
}
$comment $comment
->setActor('users', $actor) ->setActor('users', $actor)
->setMessage($this->replaceUsernames($trelloComment->data->text), 0) ->setMessage($message)
->setCreationDateTime( ->setCreationDateTime(
\DateTime::createFromFormat('Y-m-d\TH:i:s.v\Z', $trelloComment->date) \DateTime::createFromFormat('Y-m-d\TH:i:s.v\Z', $trelloComment->date)
); );
$cardId = $this->cards[$trelloCard->id]->getId();
$comments[$cardId][$commentId] = $comment; $comments[$cardId][$commentId] = $comment;
} }
} }
@@ -133,7 +169,7 @@ class BoardImportTrelloJsonService extends ABoardImportService {
} }
private function sortComments(array $comments): array { private function sortComments(array $comments): array {
$comparison = function($a, $b) { $comparison = function (\stdClass $a, \stdClass $b): int {
if ($a->date == $b->date) { if ($a->date == $b->date) {
return 0; return 0;
} }

View File

@@ -86,7 +86,7 @@ class FileService implements IAttachmentService {
* @return ISimpleFolder * @return ISimpleFolder
* @throws NotPermittedException * @throws NotPermittedException
*/ */
private function getFolder(Attachment $attachment) { public function getFolder(Attachment $attachment) {
$folderName = 'file-card-' . (int)$attachment->getCardId(); $folderName = 'file-card-' . (int)$attachment->getCardId();
try { try {
$folder = $this->appData->getFolder($folderName); $folder = $this->appData->getFolder($folderName);