- Add typing for most of the services, controllers and mappers - Add api doc for mappers - Use vendor-bin for psalm - Use attributes for controllers - Fix upload of attachments Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
642 lines
24 KiB
PHP
642 lines
24 KiB
PHP
<?php
|
|
|
|
/**
|
|
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
namespace OCA\Deck\Service;
|
|
|
|
use OCA\Deck\Activity\ActivityManager;
|
|
use OCA\Deck\Activity\ChangeSet;
|
|
use OCA\Deck\BadRequestException;
|
|
use OCA\Deck\Db\Acl;
|
|
use OCA\Deck\Db\Assignment;
|
|
use OCA\Deck\Db\AssignmentMapper;
|
|
use OCA\Deck\Db\BoardMapper;
|
|
use OCA\Deck\Db\Card;
|
|
use OCA\Deck\Db\CardMapper;
|
|
use OCA\Deck\Db\ChangeHelper;
|
|
use OCA\Deck\Db\Label;
|
|
use OCA\Deck\Db\LabelMapper;
|
|
use OCA\Deck\Db\StackMapper;
|
|
use OCA\Deck\Event\CardCreatedEvent;
|
|
use OCA\Deck\Event\CardDeletedEvent;
|
|
use OCA\Deck\Event\CardUpdatedEvent;
|
|
use OCA\Deck\Model\CardDetails;
|
|
use OCA\Deck\Model\OptionalNullableValue;
|
|
use OCA\Deck\NoPermissionException;
|
|
use OCA\Deck\Notification\NotificationHelper;
|
|
use OCA\Deck\StatusException;
|
|
use OCA\Deck\Validators\CardServiceValidator;
|
|
use OCP\Collaboration\Reference\IReferenceManager;
|
|
use OCP\Comments\ICommentsManager;
|
|
use OCP\EventDispatcher\IEventDispatcher;
|
|
use OCP\IRequest;
|
|
use OCP\IURLGenerator;
|
|
use OCP\IUserManager;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
class CardService {
|
|
public function __construct(
|
|
private CardMapper $cardMapper,
|
|
private StackMapper $stackMapper,
|
|
private BoardMapper $boardMapper,
|
|
private LabelMapper $labelMapper,
|
|
private LabelService $labelService,
|
|
private PermissionService $permissionService,
|
|
private BoardService $boardService,
|
|
private NotificationHelper $notificationHelper,
|
|
private AssignmentMapper $assignedUsersMapper,
|
|
private AttachmentService $attachmentService,
|
|
private ActivityManager $activityManager,
|
|
private ICommentsManager $commentsManager,
|
|
private IUserManager $userManager,
|
|
private ChangeHelper $changeHelper,
|
|
private IEventDispatcher $eventDispatcher,
|
|
private IURLGenerator $urlGenerator,
|
|
private LoggerInterface $logger,
|
|
private IRequest $request,
|
|
private CardServiceValidator $cardServiceValidator,
|
|
private AssignmentService $assignmentService,
|
|
private IReferenceManager $referenceManager,
|
|
private ?string $userId,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* @param Card[] $cards
|
|
* @return CardDetails[]
|
|
*/
|
|
public function enrichCards(array $cards): array {
|
|
$user = $this->userManager->get($this->userId);
|
|
|
|
$cardIds = array_map(function (Card $card) use ($user): int {
|
|
// Everything done in here might be heavy as it is executed for every card
|
|
$cardId = $card->getId();
|
|
$this->cardMapper->mapOwner($card);
|
|
|
|
$card->setAttachmentCount($this->attachmentService->count($cardId));
|
|
|
|
// TODO We should find a better way just to get the comment count so we can save 1-3 queries per card here
|
|
$countComments = $this->commentsManager->getNumberOfCommentsForObject('deckCard', (string)$card->getId());
|
|
$lastRead = $countComments > 0 ? $this->commentsManager->getReadMark('deckCard', (string)$card->getId(), $user) : null;
|
|
$countUnreadComments = $lastRead ? $this->commentsManager->getNumberOfCommentsForObject('deckCard', (string)$card->getId(), $lastRead) : 0;
|
|
$card->setCommentsUnread($countUnreadComments);
|
|
$card->setCommentsCount($countComments);
|
|
|
|
$stack = $this->stackMapper->find($card->getStackId());
|
|
$board = $this->boardService->find($stack->getBoardId(), false);
|
|
$card->setRelatedStack($stack);
|
|
$card->setRelatedBoard($board);
|
|
|
|
return $card->getId();
|
|
}, $cards);
|
|
|
|
$assignedLabels = $this->labelMapper->findAssignedLabelsForCards($cardIds);
|
|
$assignedUsers = $this->assignedUsersMapper->findIn($cardIds);
|
|
|
|
foreach ($cards as $card) {
|
|
$cardLabels = array_values(array_filter($assignedLabels, function (Label $label) use ($card) {
|
|
return $label->getCardId() === $card->getId();
|
|
}));
|
|
$cardAssignedUsers = array_values(array_filter($assignedUsers, function (Assignment $assignment) use ($card) {
|
|
return $assignment->getCardId() === $card->getId();
|
|
}));
|
|
$card->setLabels($cardLabels);
|
|
$card->setAssignedUsers($cardAssignedUsers);
|
|
}
|
|
|
|
return array_map(
|
|
function (Card $card): CardDetails {
|
|
$cardDetails = new CardDetails($card);
|
|
|
|
$references = $this->referenceManager->extractReferences($card->getTitle());
|
|
$reference = array_shift($references);
|
|
if ($reference) {
|
|
$referenceData = $this->referenceManager->resolveReference($reference);
|
|
$cardDetails->setReferenceData($referenceData);
|
|
}
|
|
|
|
return $cardDetails;
|
|
},
|
|
$cards
|
|
);
|
|
}
|
|
|
|
/** @return Card[] */
|
|
public function fetchDeleted($boardId): array {
|
|
$this->cardServiceValidator->check(compact('boardId'));
|
|
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ);
|
|
$cards = $this->cardMapper->findDeleted($boardId);
|
|
$this->enrichCards($cards);
|
|
return $cards;
|
|
}
|
|
|
|
/**
|
|
* @throws \OCA\Deck\NoPermissionException
|
|
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
|
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
|
* @throws BadRequestException
|
|
*/
|
|
public function find(int $cardId): Card {
|
|
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ);
|
|
$card = $this->cardMapper->find($cardId);
|
|
[$card] = $this->enrichCards([$card]);
|
|
|
|
// Attachments are only enriched on individual card fetching
|
|
$attachments = $this->attachmentService->findAll($cardId, true);
|
|
if ($this->request->getParam('apiVersion') === '1.0') {
|
|
$attachments = array_filter($attachments, function ($attachment) {
|
|
return $attachment->getType() === 'deck_file';
|
|
});
|
|
}
|
|
$card->setAttachments($attachments);
|
|
|
|
return $card;
|
|
}
|
|
|
|
/**
|
|
* @return Card[]
|
|
*/
|
|
public function findCalendarEntries(int $boardId): array {
|
|
try {
|
|
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ);
|
|
} catch (NoPermissionException $e) {
|
|
$this->logger->error('Unable to check permission for a previously obtained board ' . $boardId, ['exception' => $e]);
|
|
return [];
|
|
}
|
|
return $this->cardMapper->findCalendarEntries($boardId);
|
|
}
|
|
|
|
/**
|
|
* @throws StatusException
|
|
* @throws \OCA\Deck\NoPermissionException
|
|
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
|
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
|
* @throws BadrequestException
|
|
*/
|
|
public function create(string $title, int $stackId, string $type, int $order, string $owner, string $description = '', $duedate = null): Card {
|
|
$this->cardServiceValidator->check(compact('title', 'stackId', 'type', 'order', 'owner'));
|
|
|
|
$this->permissionService->checkPermission($this->stackMapper, $stackId, Acl::PERMISSION_EDIT);
|
|
if ($this->boardService->isArchived($this->stackMapper, $stackId)) {
|
|
throw new StatusException('Operation not allowed. This board is archived.');
|
|
}
|
|
$card = new Card();
|
|
$card->setTitle($title);
|
|
$card->setStackId($stackId);
|
|
$card->setType($type);
|
|
$card->setOrder($order);
|
|
$card->setOwner($owner);
|
|
$card->setDescription($description);
|
|
$card->setDuedate($duedate);
|
|
$card = $this->cardMapper->insert($card);
|
|
|
|
$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_CARD_CREATE);
|
|
$this->changeHelper->cardChanged($card->getId(), false);
|
|
$this->eventDispatcher->dispatchTyped(new CardCreatedEvent($card));
|
|
|
|
[$card] = $this->enrichCards([$card]);
|
|
|
|
return $card;
|
|
}
|
|
|
|
/**
|
|
* @throws StatusException
|
|
* @throws \OCA\Deck\NoPermissionException
|
|
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
|
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
|
* @throws BadRequestException
|
|
*/
|
|
public function delete(int $id): Card {
|
|
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
|
|
if ($this->boardService->isArchived($this->cardMapper, $id)) {
|
|
throw new StatusException('Operation not allowed. This board is archived.');
|
|
}
|
|
$card = $this->cardMapper->find($id);
|
|
$card->setDeletedAt(time());
|
|
$this->cardMapper->update($card);
|
|
|
|
$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_CARD_DELETE);
|
|
$this->notificationHelper->markDuedateAsRead($card);
|
|
$this->changeHelper->cardChanged($card->getId(), false);
|
|
$this->eventDispatcher->dispatchTyped(new CardDeletedEvent($card));
|
|
|
|
return $card;
|
|
}
|
|
|
|
/**
|
|
* @throws StatusException
|
|
* @throws \OCA\Deck\NoPermissionException
|
|
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
|
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
|
* @throws BadRequestException
|
|
*/
|
|
public function update(int $id, string $title, int $stackId, string $type, string $owner, string $description = '', int $order = 0, ?string $duedate = null, ?int $deletedAt = null, ?bool $archived = null, ?OptionalNullableValue $done = null): Card {
|
|
$this->cardServiceValidator->check(compact('id', 'title', 'stackId', 'type', 'owner', 'order'));
|
|
|
|
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT, allowDeletedCard: true);
|
|
$this->permissionService->checkPermission($this->stackMapper, $stackId, Acl::PERMISSION_EDIT);
|
|
|
|
if ($this->boardService->isArchived($this->cardMapper, $id)) {
|
|
throw new StatusException('Operation not allowed. This board is archived.');
|
|
}
|
|
$card = $this->cardMapper->find($id);
|
|
if ($archived !== null && $card->getArchived() && $archived === true) {
|
|
throw new StatusException('Operation not allowed. This card is archived.');
|
|
}
|
|
|
|
if ($card->getDeletedAt() !== 0) {
|
|
if ($deletedAt === null || $deletedAt > 0) {
|
|
// Only allow operations when restoring the card
|
|
throw new NoPermissionException('Operation not allowed. This card was deleted.');
|
|
}
|
|
}
|
|
|
|
$changes = new ChangeSet($card);
|
|
if ($card->getLastEditor() !== $this->userId && $card->getLastEditor() !== null) {
|
|
$this->activityManager->triggerEvent(
|
|
ActivityManager::DECK_OBJECT_CARD,
|
|
$card,
|
|
ActivityManager::SUBJECT_CARD_UPDATE_DESCRIPTION,
|
|
[
|
|
'before' => $card->getDescriptionPrev(),
|
|
'after' => $card->getDescription()
|
|
],
|
|
$card->getLastEditor()
|
|
);
|
|
|
|
$card->setDescriptionPrev($card->getDescription());
|
|
$card->setLastEditor($this->userId);
|
|
}
|
|
$card->setTitle($title);
|
|
$card->setStackId($stackId);
|
|
$card->setType($type);
|
|
$card->setOrder($order);
|
|
$card->setOwner($owner);
|
|
$card->setDuedate($duedate ? new \DateTime($duedate) : null);
|
|
$resetDuedateNotification = false;
|
|
if (
|
|
$card->getDuedate() === null ||
|
|
($card->getDuedate()) != ($changes->getBefore()->getDuedate())
|
|
) {
|
|
$card->setNotified(false);
|
|
$resetDuedateNotification = true;
|
|
}
|
|
|
|
if ($deletedAt !== null) {
|
|
$card->setDeletedAt($deletedAt);
|
|
}
|
|
if ($archived !== null) {
|
|
$card->setArchived($archived);
|
|
}
|
|
if ($done !== null) {
|
|
$card->setDone($done->getValue());
|
|
} else {
|
|
$card->setDone(null);
|
|
}
|
|
|
|
|
|
// Trigger update events before setting description as it is handled separately
|
|
$changes->setAfter($card);
|
|
$this->activityManager->triggerUpdateEvents(ActivityManager::DECK_OBJECT_CARD, $changes, ActivityManager::SUBJECT_CARD_UPDATE);
|
|
|
|
if ($card->getDescriptionPrev() === null) {
|
|
$card->setDescriptionPrev($card->getDescription());
|
|
}
|
|
$card->setDescription($description);
|
|
|
|
// @var Card $card
|
|
$card = $this->cardMapper->update($card);
|
|
$oldBoardId = $this->stackMapper->findBoardId($changes->getBefore()->getStackId());
|
|
$boardId = $this->cardMapper->findBoardId($card->getId());
|
|
if ($boardId !== $oldBoardId) {
|
|
$stack = $this->stackMapper->find($card->getStackId());
|
|
$board = $this->boardService->find($this->cardMapper->findBoardId($card->getId()));
|
|
$boardLabels = $board->getLabels() ?? [];
|
|
foreach ($card->getLabels() as $cardLabel) {
|
|
$this->removeLabel($card->getId(), $cardLabel->getId());
|
|
$label = $this->labelMapper->find($cardLabel->getId());
|
|
$filteredLabels = array_values(array_filter($boardLabels, fn ($item) => $item->getTitle() === $label->getTitle()));
|
|
// clone labels that are assigned to card but don't exist in new board
|
|
if (empty($filteredLabels)) {
|
|
if ($this->permissionService->getPermissions($boardId)[Acl::PERMISSION_MANAGE] === true) {
|
|
$newLabel = $this->labelService->create($label->getTitle(), $label->getColor(), $board->getId());
|
|
$boardLabels[] = $label;
|
|
$this->assignLabel($card->getId(), $newLabel->getId());
|
|
}
|
|
} else {
|
|
$this->assignLabel($card->getId(), $filteredLabels[0]->getId());
|
|
}
|
|
}
|
|
$board->setLabels($boardLabels);
|
|
$this->boardMapper->update($board);
|
|
$this->changeHelper->boardChanged($board->getId());
|
|
}
|
|
|
|
if ($resetDuedateNotification) {
|
|
$this->notificationHelper->markDuedateAsRead($card);
|
|
}
|
|
$this->changeHelper->cardChanged($card->getId());
|
|
|
|
$this->eventDispatcher->dispatchTyped(new CardUpdatedEvent($card, $changes->getBefore()));
|
|
|
|
[$card] = $this->enrichCards([$card]);
|
|
|
|
return $card;
|
|
}
|
|
|
|
public function cloneCard(int $id, ?int $targetStackId = null):Card {
|
|
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_READ);
|
|
$originCard = $this->cardMapper->find($id);
|
|
if ($targetStackId === null) {
|
|
$targetStackId = $originCard->getStackId();
|
|
}
|
|
$this->permissionService->checkPermission($this->stackMapper, $targetStackId, Acl::PERMISSION_EDIT);
|
|
$newCard = $this->create($originCard->getTitle(), $targetStackId, $originCard->getType(), $originCard->getOrder(), $originCard->getOwner());
|
|
$boardId = $this->stackMapper->findBoardId($targetStackId);
|
|
foreach ($this->labelMapper->findAssignedLabelsForCard($id) as $label) {
|
|
if ($boardId != $this->stackMapper->findBoardId($originCard->getStackId())) {
|
|
try {
|
|
$label = $this->labelService->cloneLabelIfNotExists($label->getId(), $boardId);
|
|
} catch (NoPermissionException $e) {
|
|
break;
|
|
}
|
|
}
|
|
$this->assignLabel($newCard->getId(), $label->getId());
|
|
}
|
|
foreach ($this->assignedUsersMapper->findAll($id) as $assignement) {
|
|
try {
|
|
$this->permissionService->checkPermission($this->cardMapper, $newCard->getId(), Acl::PERMISSION_READ, $assignement->getParticipant());
|
|
} catch (NoPermissionException $e) {
|
|
continue;
|
|
}
|
|
$this->assignmentService->assignUser($newCard->getId(), $assignement->getParticipant());
|
|
}
|
|
$newCard->setDescription($originCard->getDescription());
|
|
$card = $this->enrichCards([$this->cardMapper->update($newCard)]);
|
|
return $card[0];
|
|
}
|
|
|
|
/**
|
|
* @throws StatusException
|
|
* @throws \OCA\Deck\NoPermissionException
|
|
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
|
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
|
* @throws BadRequestException
|
|
*/
|
|
public function rename(int $id, string $title): Card {
|
|
$this->cardServiceValidator->check(compact('id', 'title'));
|
|
|
|
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
|
|
if ($this->boardService->isArchived($this->cardMapper, $id)) {
|
|
throw new StatusException('Operation not allowed. This board is archived.');
|
|
}
|
|
$card = $this->cardMapper->find($id);
|
|
if ($card->getArchived()) {
|
|
throw new StatusException('Operation not allowed. This card is archived.');
|
|
}
|
|
$changes = new ChangeSet($card);
|
|
$card->setTitle($title);
|
|
$this->changeHelper->cardChanged($card->getId(), false);
|
|
$update = $this->cardMapper->update($card);
|
|
|
|
$this->eventDispatcher->dispatchTyped(new CardUpdatedEvent($card, $changes->getBefore()));
|
|
|
|
return $update;
|
|
}
|
|
|
|
/**
|
|
* @return list<Card>
|
|
* @throws StatusException
|
|
* @throws \OCA\Deck\NoPermissionException
|
|
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
|
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
|
* @throws BadRequestException
|
|
*/
|
|
public function reorder(int $id, int $stackId, int $order): array {
|
|
$this->cardServiceValidator->check(compact('id', 'stackId', 'order'));
|
|
|
|
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
|
|
$this->permissionService->checkPermission($this->stackMapper, $stackId, Acl::PERMISSION_EDIT);
|
|
|
|
if ($this->boardService->isArchived($this->cardMapper, $id)) {
|
|
throw new StatusException('Operation not allowed. This board is archived.');
|
|
}
|
|
|
|
$card = $this->cardMapper->find($id);
|
|
if ($card->getArchived()) {
|
|
throw new StatusException('Operation not allowed. This card is archived.');
|
|
}
|
|
$changes = new ChangeSet($card);
|
|
$card->setStackId($stackId);
|
|
$this->cardMapper->update($card);
|
|
$changes->setAfter($card);
|
|
$this->activityManager->triggerUpdateEvents(ActivityManager::DECK_OBJECT_CARD, $changes, ActivityManager::SUBJECT_CARD_UPDATE);
|
|
|
|
$cardsToReorder = $this->cardMapper->findAll($stackId);
|
|
$result = [];
|
|
$i = 0;
|
|
foreach ($cardsToReorder as $cardToReorder) {
|
|
if ($cardToReorder->getArchived()) {
|
|
throw new StatusException('Operation not allowed. This card is archived.');
|
|
}
|
|
if ($cardToReorder->id === $id) {
|
|
$cardToReorder->setOrder($order);
|
|
$cardToReorder->setLastModified(time());
|
|
}
|
|
|
|
if ($i === $order) {
|
|
$i++;
|
|
}
|
|
|
|
if ($cardToReorder->id !== $id) {
|
|
$cardToReorder->setOrder($i++);
|
|
}
|
|
$this->cardMapper->update($cardToReorder);
|
|
$result[$cardToReorder->getOrder()] = $cardToReorder;
|
|
}
|
|
$this->changeHelper->cardChanged($id, false);
|
|
$this->eventDispatcher->dispatchTyped(new CardUpdatedEvent($card, $changes->getBefore()));
|
|
|
|
return array_values($result);
|
|
}
|
|
|
|
/**
|
|
* @throws StatusException
|
|
* @throws \OCA\Deck\NoPermissionException
|
|
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
|
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
|
* @throws BadRequestException
|
|
*/
|
|
public function archive(int $id): Card {
|
|
$this->cardServiceValidator->check(compact('id'));
|
|
|
|
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
|
|
if ($this->boardService->isArchived($this->cardMapper, $id)) {
|
|
throw new StatusException('Operation not allowed. This board is archived.');
|
|
}
|
|
$card = $this->cardMapper->find($id);
|
|
$changes = new ChangeSet($card);
|
|
$card->setArchived(true);
|
|
$newCard = $this->cardMapper->update($card);
|
|
$this->notificationHelper->markDuedateAsRead($card);
|
|
$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $newCard, ActivityManager::SUBJECT_CARD_UPDATE_ARCHIVE);
|
|
$this->changeHelper->cardChanged($id, false);
|
|
|
|
$this->eventDispatcher->dispatchTyped(new CardUpdatedEvent($card, $changes->getBefore()));
|
|
|
|
return $newCard;
|
|
}
|
|
|
|
/**
|
|
* @throws StatusException
|
|
* @throws \OCA\Deck\NoPermissionException
|
|
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
|
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
|
* @throws BadRequestException
|
|
*/
|
|
public function unarchive(int $id): Card {
|
|
$this->cardServiceValidator->check(compact('id'));
|
|
|
|
|
|
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
|
|
if ($this->boardService->isArchived($this->cardMapper, $id)) {
|
|
throw new StatusException('Operation not allowed. This board is archived.');
|
|
}
|
|
$card = $this->cardMapper->find($id);
|
|
$changes = new ChangeSet($card);
|
|
$card->setArchived(false);
|
|
$newCard = $this->cardMapper->update($card);
|
|
$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $newCard, ActivityManager::SUBJECT_CARD_UPDATE_UNARCHIVE);
|
|
$this->changeHelper->cardChanged($id, false);
|
|
|
|
$this->eventDispatcher->dispatchTyped(new CardUpdatedEvent($card, $changes->getBefore()));
|
|
|
|
return $newCard;
|
|
}
|
|
|
|
/**
|
|
* @throws StatusException
|
|
* @throws \OCA\Deck\NoPermissionException
|
|
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
|
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
|
* @throws BadRequestException
|
|
*/
|
|
public function done(int $id): Card {
|
|
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
|
|
if ($this->boardService->isArchived($this->cardMapper, $id)) {
|
|
throw new StatusException('Operation not allowed. This board is archived.');
|
|
}
|
|
$card = $this->cardMapper->find($id);
|
|
$changes = new ChangeSet($card);
|
|
$card->setDone(new \DateTime());
|
|
$newCard = $this->cardMapper->update($card);
|
|
$this->notificationHelper->markDuedateAsRead($card);
|
|
$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $newCard, ActivityManager::SUBJECT_CARD_UPDATE_DONE);
|
|
$this->changeHelper->cardChanged($id, false);
|
|
|
|
$this->eventDispatcher->dispatchTyped(new CardUpdatedEvent($card, $changes->getBefore()));
|
|
|
|
return $newCard;
|
|
}
|
|
|
|
/**
|
|
* @param $id
|
|
* @return \OCA\Deck\Db\Card
|
|
* @throws StatusException
|
|
* @throws \OCA\Deck\NoPermissionException
|
|
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
|
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
|
* @throws BadRequestException
|
|
*/
|
|
public function undone(int $id): Card {
|
|
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
|
|
if ($this->boardService->isArchived($this->cardMapper, $id)) {
|
|
throw new StatusException('Operation not allowed. This board is archived.');
|
|
}
|
|
$card = $this->cardMapper->find($id);
|
|
$changes = new ChangeSet($card);
|
|
$card->setDone(null);
|
|
$newCard = $this->cardMapper->update($card);
|
|
$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $newCard, ActivityManager::SUBJECT_CARD_UPDATE_UNDONE);
|
|
$this->changeHelper->cardChanged($id, false);
|
|
|
|
$this->eventDispatcher->dispatchTyped(new CardUpdatedEvent($card, $changes->getBefore()));
|
|
|
|
return $newCard;
|
|
}
|
|
|
|
/**
|
|
* @throws StatusException
|
|
* @throws \OCA\Deck\NoPermissionException
|
|
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
|
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
|
* @throws BadRequestException
|
|
*/
|
|
public function assignLabel(int $cardId, int $labelId): Card {
|
|
$this->cardServiceValidator->check(compact('cardId', 'labelId'));
|
|
|
|
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
|
|
$this->permissionService->checkPermission($this->labelMapper, $labelId, Acl::PERMISSION_READ);
|
|
|
|
if ($this->boardService->isArchived($this->cardMapper, $cardId)) {
|
|
throw new StatusException('Operation not allowed. This board is archived.');
|
|
}
|
|
$card = $this->cardMapper->find($cardId);
|
|
if ($card->getArchived()) {
|
|
throw new StatusException('Operation not allowed. This card is archived.');
|
|
}
|
|
$label = $this->labelMapper->find($labelId);
|
|
if ($label->getBoardId() !== $this->cardMapper->findBoardId($card->getId())) {
|
|
throw new StatusException('Operation not allowed. Label does not exist.');
|
|
}
|
|
$this->cardMapper->assignLabel($cardId, $labelId);
|
|
$this->changeHelper->cardChanged($cardId);
|
|
$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_LABEL_ASSIGN, ['label' => $label]);
|
|
|
|
$this->eventDispatcher->dispatchTyped(new CardUpdatedEvent($card));
|
|
return $card;
|
|
}
|
|
|
|
/**
|
|
* @throws \OCA\Deck\NoPermissionException
|
|
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
|
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
|
* @throws BadRequestException
|
|
*/
|
|
public function removeLabel(int $cardId, int $labelId): Card {
|
|
$this->cardServiceValidator->check(compact('cardId', 'labelId'));
|
|
|
|
|
|
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
|
|
$this->permissionService->checkPermission($this->labelMapper, $labelId, Acl::PERMISSION_READ);
|
|
|
|
if ($this->boardService->isArchived($this->cardMapper, $cardId)) {
|
|
throw new StatusException('Operation not allowed. This board is archived.');
|
|
}
|
|
$card = $this->cardMapper->find($cardId);
|
|
if ($card->getArchived()) {
|
|
throw new StatusException('Operation not allowed. This card is archived.');
|
|
}
|
|
$label = $this->labelMapper->find($labelId);
|
|
$this->cardMapper->removeLabel($cardId, $labelId);
|
|
$this->changeHelper->cardChanged($cardId);
|
|
$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_LABEL_UNASSING, ['label' => $label]);
|
|
|
|
$this->eventDispatcher->dispatchTyped(new CardUpdatedEvent($card));
|
|
return $card;
|
|
}
|
|
|
|
public function getCardUrl(int $cardId): string {
|
|
$boardId = $this->cardMapper->findBoardId($cardId);
|
|
|
|
return $this->urlGenerator->linkToRouteAbsolute('deck.page.indexCard', ['boardId' => $boardId, 'cardId' => $cardId]);
|
|
}
|
|
|
|
public function getRedirectUrlForCard(int $cardId): string {
|
|
return $this->urlGenerator->linkToRouteAbsolute('deck.page.redirectToCard', ['cardId' => $cardId]);
|
|
}
|
|
}
|