refactor: Fix psalm issues

- 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>
This commit is contained in:
Carl Schwan
2025-09-23 16:59:09 +02:00
parent 64741e455d
commit 5cf486150a
77 changed files with 4257 additions and 1393 deletions

View File

@@ -516,7 +516,7 @@ class ActivityManager {
];
}
private function findDetailsForCard($cardId, $subject = null) {
private function findDetailsForCard(int $cardId, ?string $subject = null): array {
$card = $this->cardMapper->find($cardId);
$stack = $this->stackMapper->find($card->getStackId());
$board = $this->boardMapper->find($stack->getBoardId());

View File

@@ -7,6 +7,9 @@
namespace OCA\Deck\Activity;
/**
* @psalm-api SettingComment
*/
class SettingComment extends SettingBase {
/**

View File

@@ -6,9 +6,13 @@
*/
namespace OCA\Deck\Controller;
use OCA\Deck\Db\Attachment;
use OCA\Deck\Service\AttachmentService;
use OCP\AppFramework\ApiController;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\CORS;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
@@ -21,72 +25,52 @@ class AttachmentApiController extends ApiController {
parent::__construct($appName, $request);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
*/
public function getAll($apiVersion) {
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function getAll(string $apiVersion): DataResponse {
$attachment = $this->attachmentService->findAll($this->request->getParam('cardId'), true);
if ($apiVersion === '1.0') {
$attachment = array_filter($attachment, function ($attachment) {
return $attachment->getType() === 'deck_file';
});
$attachment = array_filter($attachment, fn (Attachment $attachment): bool => $attachment->getType() === 'deck_file');
}
return new DataResponse($attachment, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
*/
public function display($cardId, $attachmentId, $type = 'deck_file') {
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function display(int $cardId, int $attachmentId, string $type = 'deck_file') {
return $this->attachmentService->display($cardId, $attachmentId, $type);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
*/
public function create($cardId, $type, $data) {
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function create(int $cardId, string $type, string $data): DataResponse {
$attachment = $this->attachmentService->create($cardId, $type, $data);
return new DataResponse($attachment, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
*/
public function update($cardId, $attachmentId, $data, $type = 'deck_file') {
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function update(int $cardId, int $attachmentId, string $data, string $type = 'deck_file'): DataResponse {
$attachment = $this->attachmentService->update($cardId, $attachmentId, $data, $type);
return new DataResponse($attachment, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
*/
public function delete($cardId, $attachmentId, $type = 'deck_file') {
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function delete(int $cardId, int $attachmentId, string $type = 'deck_file'): DataResponse {
$attachment = $this->attachmentService->delete($cardId, $attachmentId, $type);
return new DataResponse($attachment, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
*/
public function restore($cardId, $attachmentId, $type = 'deck_file') {
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function restore(int $cardId, int $attachmentId, string $type = 'deck_file'): DataResponse {
$attachment = $this->attachmentService->restore($cardId, $attachmentId, $type);
return new DataResponse($attachment, HTTP::STATUS_OK);
}

View File

@@ -7,8 +7,13 @@
namespace OCA\Deck\Controller;
use OCA\Deck\BadRequestException;
use OCA\Deck\Db\Attachment;
use OCA\Deck\Service\AttachmentService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\Response;
use OCP\IRequest;
class AttachmentController extends Controller {
@@ -20,74 +25,66 @@ class AttachmentController extends Controller {
parent::__construct($appName, $request);
}
/**
* @NoAdminRequired
*/
public function getAll($cardId) {
#[NoAdminRequired]
public function getAll(int $cardId): array {
return $this->attachmentService->findAll($cardId, true);
}
/**
* @param $cardId
* @param $attachmentId
* @NoCSRFRequired
* @NoAdminRequired
* @return \OCP\AppFramework\Http\Response
* @throws \OCA\Deck\NotFoundException
*/
public function display($cardId, $attachmentId) {
if (!str_contains($attachmentId, ':')) {
$type = 'deck_file';
} else {
[$type, $attachmentId] = explode(':', $attachmentId);
}
#[NoAdminRequired]
#[NoCSRFRequired]
public function display(int $cardId, string $attachmentId): Response {
['type' => $type, 'attachmentId' => $attachmentId] = $this->extractTypeAndAttachmentId($attachmentId);
return $this->attachmentService->display($cardId, $attachmentId, $type);
}
/**
* @NoAdminRequired
*/
public function create($cardId) {
#[NoAdminRequired]
public function create(int $cardId): Attachment {
return $this->attachmentService->create(
$cardId,
$this->request->getParam('type'),
$this->request->getParam('data')
$this->request->getParam('data') ?? '',
);
}
/**
* @NoAdminRequired
*/
public function update($cardId, $attachmentId) {
if (!str_contains($attachmentId, ':')) {
$type = 'deck_file';
} else {
[$type, $attachmentId] = explode(':', $attachmentId);
}
return $this->attachmentService->update($cardId, $attachmentId, $this->request->getParam('data'), $type);
#[NoAdminRequired]
public function update(int $cardId, string $attachmentId): Attachment {
['type' => $type, 'attachmentId' => $attachmentId] = $this->extractTypeAndAttachmentId($attachmentId);
return $this->attachmentService->update($cardId, $attachmentId, $this->request->getParam('data') ?? '', $type);
}
/**
* @NoAdminRequired
*/
public function delete($cardId, $attachmentId) {
if (!str_contains($attachmentId, ':')) {
$type = 'deck_file';
} else {
[$type, $attachmentId] = explode(':', $attachmentId);
}
#[NoAdminRequired]
public function delete(int $cardId, string $attachmentId): Attachment {
['type' => $type, 'attachmentId' => $attachmentId] = $this->extractTypeAndAttachmentId($attachmentId);
return $this->attachmentService->delete($cardId, $attachmentId, $type);
}
#[NoAdminRequired]
public function restore(int $cardId, string $attachmentId): Attachment {
['type' => $type, 'attachmentId' => $attachmentId] = $this->extractTypeAndAttachmentId($attachmentId);
return $this->attachmentService->restore($cardId, $attachmentId, $type);
}
/**
* @NoAdminRequired
* @return array{type: string, attachmentId: int}
* @throws BadRequestException
*/
public function restore($cardId, $attachmentId) {
private function extractTypeAndAttachmentId(string $attachmentId): array {
if (!str_contains($attachmentId, ':')) {
$type = 'deck_file';
} else {
[$type, $attachmentId] = explode(':', $attachmentId);
[$type, $attachmentId] = [...explode(':', $attachmentId), '', ''];
}
return $this->attachmentService->restore($cardId, $attachmentId, $type);
if ($type === '' || !is_numeric($attachmentId)) {
throw new BadRequestException('Invalid attachment id');
}
return [
'type' => $type,
'attachmentId' => (int)$attachmentId,
];
}
}

View File

@@ -12,10 +12,13 @@ use OCA\Deck\Service\BoardService;
use OCA\Deck\StatusException;
use OCP\AppFramework\ApiController;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\CORS;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
use Sabre\HTTP\Util;
use function Sabre\HTTP\parseDate;
/**
* Class BoardApiController
@@ -36,21 +39,18 @@ class BoardApiController extends ApiController {
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Return all of the boards that the current user has access to.
*
* @param bool $details
* Return all the boards that the current user has access to.
* @throws StatusException
*/
public function index(bool $details = false) {
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function index(bool $details = false): DataResponse {
$modified = $this->request->getHeader('If-Modified-Since');
if ($modified === null || $modified === '') {
if ($modified === '') {
$boards = $this->boardService->findAll(0, $details === true);
} else {
$date = Util::parseHTTPDate($modified);
$date = parseDate($modified);
if (!$date) {
throw new StatusException('Invalid If-Modified-Since header provided.');
}
@@ -64,14 +64,12 @@ class BoardApiController extends ApiController {
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
*
* Return the board specified by $this->request->getParam('boardId').
*/
public function get() {
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function get(): DataResponse {
$board = $this->boardService->find($this->request->getParam('boardId'));
$response = new DataResponse($board, HTTP::STATUS_OK);
$response->setETag($board->getEtag());
@@ -79,68 +77,53 @@ class BoardApiController extends ApiController {
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* @params $title
* @params $color
*
* Create a board with the specified title and color.
*/
public function create($title, $color) {
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function create(string $title, string $color): DataResponse {
$board = $this->boardService->create($title, $this->userId, $color);
return new DataResponse($board, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* @params $title
* @params $color
* @params $archived
*
* Update a board with the specified boardId, title and color, and archived state.
*/
public function update($title, $color, $archived = false) {
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function update(string $title, string $color, bool $archived = false): DataResponse {
$board = $this->boardService->update($this->request->getParam('boardId'), $title, $color, $archived);
return new DataResponse($board, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
*
* Delete the board specified by $boardId. Return the board that was deleted.
*/
public function delete() {
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function delete(): DataResponse {
$board = $this->boardService->delete($this->request->getParam('boardId'));
return new DataResponse($board, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
*
* Undo the deletion of the board specified by $boardId.
*/
public function undoDelete() {
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function undoDelete(): DataResponse {
$board = $this->boardService->deleteUndo($this->request->getParam('boardId'));
return new DataResponse($board, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*/
public function addAcl($boardId, $type, $participant, $permissionEdit, $permissionShare, $permissionManage) {
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function addAcl(int $boardId, $type, $participant, $permissionEdit, $permissionShare, $permissionManage) {
$acl = $this->boardService->addAcl($boardId, $type, $participant, $permissionEdit, $permissionShare, $permissionManage);
return new DataResponse($acl, HTTP::STATUS_OK);
}

View File

@@ -14,6 +14,7 @@ use OCA\Deck\Service\Importer\BoardImportService;
use OCA\Deck\Service\PermissionService;
use OCP\AppFramework\ApiController;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\IL10N;
use OCP\IRequest;
@@ -31,68 +32,38 @@ class BoardController extends ApiController {
parent::__construct($appName, $request);
}
/**
* @NoAdminRequired
*/
#[NoAdminRequired]
public function index() {
return $this->boardService->findAll();
}
/**
* @NoAdminRequired
* @param $boardId
* @return \OCP\AppFramework\Db\Entity
*/
public function read(int $boardId) {
#[NoAdminRequired]
public function read(int $boardId): Board {
return $this->boardService->find($boardId);
}
/**
* @NoAdminRequired
* @param $title
* @param $color
* @return \OCP\AppFramework\Db\Entity
*/
public function create($title, $color) {
#[NoAdminRequired]
public function create(string $title, string $color): Board {
return $this->boardService->create($title, $this->userId, $color);
}
/**
* @NoAdminRequired
* @param $id
* @param $title
* @param $color
* @param $archived
* @return \OCP\AppFramework\Db\Entity
*/
public function update($id, $title, $color, $archived) {
#[NoAdminRequired]
public function update(int $id, string $title, string $color, bool $archived): Board {
return $this->boardService->update($id, $title, $color, $archived);
}
/**
* @NoAdminRequired
* @param $boardId
* @return \OCP\AppFramework\Db\Entity
*/
public function delete($boardId) {
#[NoAdminRequired]
public function delete(int $boardId): Board {
return $this->boardService->delete($boardId);
}
/**
* @NoAdminRequired
* @param $boardId
* @return \OCP\AppFramework\Db\Entity
*/
public function deleteUndo($boardId) {
#[NoAdminRequired]
public function deleteUndo(int $boardId): Board {
return $this->boardService->deleteUndo($boardId);
}
/**
* @NoAdminRequired
* @param $boardId
* @return array|bool
* @internal param $userId
*/
public function getUserPermissions($boardId) {
#[NoAdminRequired]
public function getUserPermissions(int $boardId): array {
$permissions = $this->permissionService->getPermissions($boardId);
return [
'PERMISSION_READ' => $permissions[Acl::PERMISSION_READ],
@@ -103,16 +74,10 @@ class BoardController extends ApiController {
}
/**
* @NoAdminRequired
* @param $boardId
* @param $type
* @param $participant
* @param $permissionEdit
* @param $permissionShare
* @param $permissionManage
* @return \OCP\AppFramework\Db\Entity
*/
public function addAcl($boardId, $type, $participant, $permissionEdit, $permissionShare, $permissionManage) {
#[NoAdminRequired]
public function addAcl(int $boardId, int $type, $participant, bool $permissionEdit, bool $permissionShare, bool $permissionManage): Acl {
return $this->boardService->addAcl($boardId, $type, $participant, $permissionEdit, $permissionShare, $permissionManage);
}

View File

@@ -9,6 +9,9 @@ namespace OCA\Deck\Controller;
use OCA\Deck\Service\Importer\BoardImportService;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\CORS;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IRequest;
@@ -23,11 +26,9 @@ class BoardImportApiController extends OCSController {
parent::__construct($appName, $request);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*/
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function import(string $system, array $config, array $data): DataResponse {
$this->boardImportService->setSystem($system);
$config = json_decode(json_encode($config));
@@ -38,21 +39,17 @@ class BoardImportApiController extends OCSController {
return new DataResponse($this->boardImportService->getBoard(), Http::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*/
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function getAllowedSystems(): DataResponse {
$allowedSystems = $this->boardImportService->getAllowedImportSystems();
return new DataResponse($allowedSystems, Http::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*/
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function getConfigSchema(string $name): DataResponse {
$this->boardImportService->setSystem($name);
$this->boardImportService->validateSystem();

View File

@@ -12,6 +12,9 @@ use OCA\Deck\Service\AssignmentService;
use OCA\Deck\Service\CardService;
use OCP\AppFramework\ApiController;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\CORS;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
@@ -27,7 +30,7 @@ class CardApiController extends ApiController {
* @param IRequest $request
* @param CardService $cardService
* @param AssignmentService $assignmentService
* @param $userId
* @param string $userId
*/
public function __construct(
string $appName,
@@ -80,112 +83,102 @@ class CardApiController extends ApiController {
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
*
* Update a card
*/
public function update($title, $type, $owner, $description = '', $order = 0, $duedate = null, $archived = null) {
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function update(string $title, $type, string $owner, string $description = '', int $order = 0, $duedate = null, $archived = null): DataResponse {
$done = array_key_exists('done', $this->request->getParams()) ? new OptionalNullableValue($this->request->getParam('done', null)) : null;
$card = $this->cardService->update($this->request->getParam('cardId'), $title, $this->request->getParam('stackId'), $type, $owner, $description, $order, $duedate, 0, $archived, $done);
return new DataResponse($card, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Delete a specific card.
*/
public function delete() {
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function delete(): DataResponse {
$card = $this->cardService->delete($this->request->getParam('cardId'));
return new DataResponse($card, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Assign a label to a card.
*/
public function assignLabel($labelId) {
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function assignLabel(int $labelId): DataResponse {
$card = $this->cardService->assignLabel($this->request->getParam('cardId'), $labelId);
return new DataResponse($card, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Assign a label to a card.
*/
public function removeLabel($labelId) {
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function removeLabel(int $labelId): DataResponse {
$card = $this->cardService->removeLabel($this->request->getParam('cardId'), $labelId);
return new DataResponse($card, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Assign a user to a card
*/
public function assignUser($cardId, $userId, $type = 0) {
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function assignUser(int $cardId, string $userId, int $type = 0): DataResponse {
$card = $this->assignmentService->assignUser($cardId, $userId, $type);
return new DataResponse($card, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Unassign a user from a card
*/
public function unassignUser($cardId, $userId, $type = 0) {
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function unassignUser(int $cardId, string $userId, int $type = 0): DataResponse {
$card = $this->assignmentService->unassignUser($cardId, $userId, $type);
return new DataResponse($card, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Archive card
*/
public function archive($cardId) {
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function archive(int $cardId): DataResponse {
$card = $this->cardService->archive($cardId);
return new DataResponse($card, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Unarchive card
*/
public function unarchive($cardId) {
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function unarchive(int $cardId): DataResponse {
$card = $this->cardService->unarchive($cardId);
return new DataResponse($card, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Reorder cards
*/
public function reorder($stackId, $order) {
$card = $this->cardService->reorder((int)$this->request->getParam('cardId'), (int)$stackId, (int)$order);
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function reorder(int $stackId, int $order): DataResponse {
$card = $this->cardService->reorder((int)$this->request->getParam('cardId'), $stackId, $order);
return new DataResponse($card, HTTP::STATUS_OK);
}
}

View File

@@ -7,9 +7,12 @@
namespace OCA\Deck\Controller;
use OCA\Deck\Db\Assignment;
use OCA\Deck\Db\Card;
use OCA\Deck\Service\AssignmentService;
use OCA\Deck\Service\CardService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\IRequest;
class CardController extends Controller {
@@ -23,45 +26,26 @@ class CardController extends Controller {
parent::__construct($appName, $request);
}
/**
* @NoAdminRequired
* @param $cardId
* @return \OCP\AppFramework\Db\Entity
*/
public function read($cardId) {
#[NoAdminRequired]
public function read(int $cardId): Card {
return $this->cardService->find($cardId);
}
/**
* @NoAdminRequired
* @param $cardId
* @param $stackId
* @param $order
* @return array
* @return Card[]
*/
public function reorder($cardId, $stackId, $order) {
return $this->cardService->reorder((int)$cardId, (int)$stackId, (int)$order);
#[NoAdminRequired]
public function reorder(int $cardId, int $stackId, int $order): array {
return $this->cardService->reorder($cardId, $stackId, $order);
}
/**
* @NoAdminRequired
* @param $cardId
* @param $title
* @return \OCP\AppFramework\Db\Entity
*/
public function rename($cardId, $title) {
#[NoAdminRequired]
public function rename(int $cardId, string $title): Card {
return $this->cardService->rename($cardId, $title);
}
/**
* @NoAdminRequired
* @param $title
* @param $stackId
* @param $type
* @param int $order
* @return \OCP\AppFramework\Db\Entity
*/
public function create($title, $stackId, $type = 'plain', $order = 999, string $description = '', $duedate = null, $labels = [], $users = []) {
#[NoAdminRequired]
public function create(string $title, int $stackId, string $type = 'plain', int $order = 999, string $description = '', $duedate = null, array $labels = [], array $users = []): Card {
$card = $this->cardService->create($title, $stackId, $type, $order, $this->userId, $description, $duedate);
foreach ($labels as $label) {
@@ -76,113 +60,68 @@ class CardController extends Controller {
}
/**
* @NoAdminRequired
* @param $id
* @param $title
* @param $stackId
* @param $type
* @param $order
* @param $description
* @param $duedate
* @param $deletedAt
* @return \OCP\AppFramework\Db\Entity
*/
public function update($id, $title, $stackId, $type, $order, $description, $duedate, $deletedAt) {
#[NoAdminRequired]
public function update(int $id, string $title, int $stackId, string $type, int $order, string $description, $duedate, $deletedAt): Card {
return $this->cardService->update($id, $title, $stackId, $type, $this->userId, $description, $order, $duedate, $deletedAt);
}
/**
* @NoAdminRequired
* @param $cardId
* @param $targetStackId
* @return \OCP\AppFramework\Db\Entity
*/
public function clone(int $cardId, ?int $targetStackId = null) {
#[NoAdminRequired]
public function clone(int $cardId, ?int $targetStackId = null): Card {
return $this->cardService->cloneCard($cardId, $targetStackId);
}
/**
* @NoAdminRequired
* @param $cardId
* @return \OCP\AppFramework\Db\Entity
*/
public function delete($cardId) {
#[NoAdminRequired]
public function delete(int $cardId): Card {
return $this->cardService->delete($cardId);
}
/**
* @NoAdminRequired
* @param $boardId
* @return \OCP\AppFramework\Db\Entity
* @return Card[]
*/
public function deleted($boardId) {
#[NoAdminRequired]
public function deleted(int $boardId): array {
return $this->cardService->fetchDeleted($boardId);
}
/**
* @NoAdminRequired
* @param $cardId
* @return \OCP\AppFramework\Db\Entity
*/
#[NoAdminRequired]
public function archive($cardId) {
return $this->cardService->archive($cardId);
}
/**
* @NoAdminRequired
* @param $cardId
* @return \OCP\AppFramework\Db\Entity
*/
public function unarchive($cardId) {
#[NoAdminRequired]
public function unarchive(int $cardId): Card {
return $this->cardService->unarchive($cardId);
}
/**
* @NoAdminRequired
* @param $cardId
* @return \OCP\AppFramework\Db\Entity
*/
public function done(int $cardId) {
#[NoAdminRequired]
public function done(int $cardId): Card {
return $this->cardService->done($cardId);
}
/**
* @NoAdminRequired
* @param $cardId
* @return \OCP\AppFramework\Db\Entity
*/
public function undone(int $cardId) {
#[NoAdminRequired]
public function undone(int $cardId): Card {
return $this->cardService->undone($cardId);
}
/**
* @NoAdminRequired
* @param $cardId
* @param $labelId
*/
public function assignLabel($cardId, $labelId) {
#[NoAdminRequired]
public function assignLabel(int $cardId, int $labelId): void {
$this->cardService->assignLabel($cardId, $labelId);
}
/**
* @NoAdminRequired
* @param $cardId
* @param $labelId
*/
public function removeLabel($cardId, $labelId) {
#[NoAdminRequired]
public function removeLabel(int $cardId, int $labelId): void {
$this->cardService->removeLabel($cardId, $labelId);
}
/**
* @NoAdminRequired
*/
public function assignUser($cardId, $userId, $type = 0) {
#[NoAdminRequired]
public function assignUser(int $cardId, string $userId, int $type = 0): Assignment {
return $this->assignmentService->assignUser($cardId, $userId, $type);
}
/**
* @NoAdminRequired
*/
public function unassignUser($cardId, $userId, $type = 0) {
#[NoAdminRequired]
public function unassignUser(int $cardId, string $userId, int $type = 0): Assignment {
return $this->assignmentService->unassignUser($cardId, $userId, $type);
}
}

View File

@@ -9,11 +9,15 @@ namespace OCA\Deck\Controller;
use OCA\Deck\Service\CommentService;
use OCA\Deck\StatusException;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IRequest;
/**
* @psalm-api
*/
class CommentsApiController extends OCSController {
public function __construct(
string $appName,
@@ -27,33 +31,33 @@ class CommentsApiController extends OCSController {
}
/**
* @NoAdminRequired
* @throws StatusException
*/
public function list(string $cardId, int $limit = 20, int $offset = 0): DataResponse {
#[NoAdminRequired]
public function list(int $cardId, int $limit = 20, int $offset = 0): DataResponse {
return $this->commentService->list($cardId, $limit, $offset);
}
/**
* @NoAdminRequired
* @throws StatusException
*/
#[NoAdminRequired]
public function create(int $cardId, string $message, int $parentId = 0): DataResponse {
return $this->commentService->create($cardId, $message, $parentId);
}
/**
* @NoAdminRequired
* @throws StatusException
*/
#[NoAdminRequired]
public function update(int $cardId, int $commentId, string $message): DataResponse {
return $this->commentService->update($cardId, $commentId, $message);
}
/**
* @NoAdminRequired
* @throws StatusException
*/
#[NoAdminRequired]
public function delete(int $cardId, int $commentId): DataResponse {
return $this->commentService->delete($cardId, $commentId);
}

View File

@@ -8,6 +8,8 @@
namespace OCA\Deck\Controller;
use OCA\Deck\Service\ConfigService;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\NotFoundResponse;
use OCP\AppFramework\OCSController;
@@ -22,19 +24,15 @@ class ConfigController extends OCSController {
parent::__construct($AppName, $request);
}
/**
* @NoCSRFRequired
* @NoAdminRequired
*/
#[NoAdminRequired]
#[NoCSRFRequired]
public function get(): DataResponse {
return new DataResponse($this->configService->getAll());
}
/**
* @NoCSRFRequired
* @NoAdminRequired
*/
public function setValue(string $key, $value) {
#[NoAdminRequired]
#[NoCSRFRequired]
public function setValue(string $key, mixed $value): DataResponse|NotFoundResponse {
$result = $this->configService->set($key, $value);
if ($result === null) {
return new NotFoundResponse();

View File

@@ -10,6 +10,9 @@ namespace OCA\Deck\Controller;
use OCA\Deck\Service\LabelService;
use OCP\AppFramework\ApiController;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\CORS;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
@@ -26,59 +29,50 @@ class LabelApiController extends ApiController {
$appName,
IRequest $request,
private LabelService $labelService,
private $userId,
) {
parent::__construct($appName, $request);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Get a specific label.
*/
public function get() {
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function get(): DataResponse {
$label = $this->labelService->find($this->request->getParam('labelId'));
return new DataResponse($label, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* @params $title
* @params $color
* Create a new label
*/
public function create($title, $color) {
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function create(string $title, string $color): DataResponse {
$label = $this->labelService->create($title, $color, $this->request->getParam('boardId'));
return new DataResponse($label, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* @params $title
* @params $color
* Update a specific label
*/
public function update($title, $color) {
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function update(string $title, string $color): DataResponse {
$label = $this->labelService->update($this->request->getParam('labelId'), $title, $color);
return new DataResponse($label, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Delete a specific label
*/
public function delete() {
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function delete(): DataResponse {
$label = $this->labelService->delete($this->request->getParam('labelId'));
return new DataResponse($label, HTTP::STATUS_OK);
}

View File

@@ -7,8 +7,10 @@
namespace OCA\Deck\Controller;
use OCA\Deck\Db\Label;
use OCA\Deck\Service\LabelService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\IRequest;
class LabelController extends Controller {
@@ -20,34 +22,18 @@ class LabelController extends Controller {
parent::__construct($appName, $request);
}
/**
* @NoAdminRequired
* @param $title
* @param $color
* @param $boardId
* @return \OCP\AppFramework\Db\Entity
*/
public function create($title, $color, $boardId) {
#[NoAdminRequired]
public function create(string $title, string $color, int $boardId): Label {
return $this->labelService->create($title, $color, $boardId);
}
/**
* @NoAdminRequired
* @param $id
* @param $title
* @param $color
* @return \OCP\AppFramework\Db\Entity
*/
public function update($id, $title, $color) {
#[NoAdminRequired]
public function update(int $id, string $title, string $color): Label {
return $this->labelService->update($id, $title, $color);
}
/**
* @NoAdminRequired
* @param $labelId
* @return \OCP\AppFramework\Db\Entity
*/
public function delete($labelId) {
#[NoAdminRequired]
public function delete(int $labelId): Label {
return $this->labelService->delete($labelId);
}
}

View File

@@ -10,6 +10,7 @@ declare(strict_types=1);
namespace OCA\Deck\Controller;
use OCA\Deck\Service\OverviewService;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IRequest;
@@ -24,9 +25,7 @@ class OverviewApiController extends OCSController {
parent::__construct($appName, $request);
}
/**
* @NoAdminRequired
*/
#[NoAdminRequired]
public function upcomingCards(): DataResponse {
return new DataResponse($this->dashboardService->findUpcomingCards($this->userId));
}

View File

@@ -13,6 +13,7 @@ namespace OCA\Deck\Controller;
use OCA\Deck\Db\Card;
use OCA\Deck\Model\CardDetails;
use OCA\Deck\Service\SearchService;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IRequest;
@@ -26,9 +27,7 @@ class SearchController extends OCSController {
parent::__construct($appName, $request);
}
/**
* @NoAdminRequired
*/
#[NoAdminRequired]
public function search(string $term, ?int $limit = null, ?int $cursor = null): DataResponse {
$cards = $this->searchService->searchCards($term, $limit, $cursor);
return new DataResponse(array_map(function (Card $card) {

View File

@@ -7,14 +7,16 @@
namespace OCA\Deck\Controller;
use OCA\Deck\Service\BoardService;
use OCA\Deck\Service\StackService;
use OCA\Deck\StatusException;
use OCP\AppFramework\ApiController;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\CORS;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
use Sabre\HTTP\Util;
use function Sabre\HTTP\parseDate;
/**
* Class StackApiController
@@ -29,23 +31,21 @@ class StackApiController extends ApiController {
$appName,
IRequest $request,
private StackService $stackService,
private BoardService $boardService,
) {
parent::__construct($appName, $request);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Return all of the stacks in the specified board.
* Return all the stacks in the specified board.
*/
public function index() {
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function index(): DataResponse {
$since = 0;
$modified = $this->request->getHeader('If-Modified-Since');
if ($modified !== null && $modified !== '') {
$date = Util::parseHTTPDate($modified);
if ($modified !== '') {
$date = parseDate($modified);
if (!$date) {
throw new StatusException('Invalid If-Modified-Since header provided.');
}
@@ -56,13 +56,12 @@ class StackApiController extends ApiController {
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Return all of the stacks in the specified board.
* Return all the stacks in the specified board.
*/
public function get() {
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function get(): DataResponse {
$stack = $this->stackService->find($this->request->getParam('stackId'));
$response = new DataResponse($stack, HTTP::STATUS_OK);
$response->setETag($stack->getETag());
@@ -70,55 +69,45 @@ class StackApiController extends ApiController {
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* @params $title
* @params $order
*
* Create a stack with the specified title and order.
*/
public function create($title, $order) {
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function create(string $title, int $order): DataResponse {
$stack = $this->stackService->create($title, $this->request->getParam('boardId'), $order);
return new DataResponse($stack, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* @params $title
* @params $order
*
* Update a stack by the specified stackId and boardId with the values that were put.
*/
public function update($title, $order) {
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function update(string $title, int $order) {
$stack = $this->stackService->update($this->request->getParam('stackId'), $title, $this->request->getParam('boardId'), $order, 0);
return new DataResponse($stack, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Delete the stack specified by $this->request->getParam('stackId').
*/
public function delete() {
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function delete(): DataResponse {
$stack = $this->stackService->delete($this->request->getParam('stackId'));
return new DataResponse($stack, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* get the stacks that have been archived.
* Get the stacks that have been archived.
*/
public function getArchived() {
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function getArchived(): DataResponse {
$stacks = $this->stackService->findAllArchived($this->request->getParam('boardId'));
return new DataResponse($stacks, HTTP::STATUS_OK);
}

View File

@@ -7,10 +7,12 @@
namespace OCA\Deck\Controller;
use OCA\Deck\Db\Stack;
use OCA\Deck\Service\StackService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\IRequest;
class StackController extends Controller {
@@ -18,78 +20,54 @@ class StackController extends Controller {
string $appName,
IRequest $request,
private StackService $stackService,
private $userId,
) {
parent::__construct($appName, $request);
}
/**
* @NoAdminRequired
* @param $boardId
* @return array
* @return Stack[]
*/
public function index($boardId) {
#[NoAdminRequired]
public function index(int $boardId): array {
return $this->stackService->findAll($boardId);
}
/**
* @NoAdminRequired
* @param $boardId
* @return array
* @return Stack[]
*/
public function archived($boardId) {
#[NoAdminRequired]
public function archived(int $boardId): array {
return $this->stackService->findAllArchived($boardId);
}
/**
* @NoAdminRequired
* @param $title
* @param $boardId
* @param int $order
* @return \OCP\AppFramework\Db\Entity
*/
public function create($title, $boardId, $order = 999) {
#[NoAdminRequired]
public function create(string $title, int $boardId, int $order = 999): Stack {
return $this->stackService->create($title, $boardId, $order);
}
/**
* @NoAdminRequired
* @param $id
* @param $title
* @param $boardId
* @param $order
* @param $deletedAt
* @return \OCP\AppFramework\Db\Entity
*/
public function update($id, $title, $boardId, $order, $deletedAt) {
#[NoAdminRequired]
public function update(int $id, string $title, int $boardId, int $order, ?int $deletedAt = null): Stack {
return $this->stackService->update($id, $title, $boardId, $order, $deletedAt);
}
/**
* @NoAdminRequired
* @param $stackId
* @param $order
* @return array
* @return array<int, Stack>
*/
public function reorder($stackId, $order) {
return $this->stackService->reorder((int)$stackId, (int)$order);
#[NoAdminRequired]
public function reorder(int $stackId, int $order): array {
return $this->stackService->reorder($stackId, $order);
}
/**
* @NoAdminRequired
* @param $stackId
* @return \OCP\AppFramework\Db\Entity
*/
public function delete($stackId) {
#[NoAdminRequired]
public function delete(int $stackId): Stack {
return $this->stackService->delete($stackId);
}
/**
* @NoAdminRequired
* @param $boardId
* @return \OCP\AppFramework\Db\Entity
* @return Stack[]
*/
public function deleted($boardId) {
#[NoAdminRequired]
public function deleted(int $boardId): array {
return $this->stackService->fetchDeleted($boardId);
}
}

View File

@@ -7,6 +7,20 @@
namespace OCA\Deck\Db;
/**
* @method int getBoardId()
* @method bool isPermissionEdit()
* @method void setPermissionEdit(bool $permissionEdit)
* @method bool isPermissionShare()
* @method void setPermissionShare(bool $permissionShare)
* @method bool isPermissionManage()
* @method void setPermissionManage(bool $permissionManage)
* @method int getType()
* @method void setType(int $type)
* @method bool isOwner()
* @method void setOwner(int $owner)
*
*/
class Acl extends RelationalEntity {
public const PERMISSION_READ = 0;
public const PERMISSION_EDIT = 1;
@@ -37,17 +51,13 @@ class Acl extends RelationalEntity {
$this->addResolvable('participant');
}
public function getPermission($permission) {
switch ($permission) {
case self::PERMISSION_READ:
return true;
case self::PERMISSION_EDIT:
return $this->getPermissionEdit();
case self::PERMISSION_SHARE:
return $this->getPermissionShare();
case self::PERMISSION_MANAGE:
return $this->getPermissionManage();
}
return false;
public function getPermission(int $permission): bool {
return match ($permission) {
self::PERMISSION_READ => true,
self::PERMISSION_EDIT => $this->getPermissionEdit(),
self::PERMISSION_SHARE => $this->getPermissionShare(),
self::PERMISSION_MANAGE => $this->getPermissionManage(),
default => false,
};
}
}

View File

@@ -19,13 +19,10 @@ class AclMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $boardId
* @param int|null $limit
* @param int|null $offset
* @return Acl[]
* @throws \OCP\DB\Exception
*/
public function findAll($boardId, $limit = null, $offset = null) {
public function findAll(int $boardId, ?int $limit = null, ?int $offset = null) {
$qb = $this->db->getQueryBuilder();
$qb->select('id', 'board_id', 'type', 'participant', 'permission_edit', 'permission_share', 'permission_manage')
->from('deck_board_acl')
@@ -51,12 +48,9 @@ class AclMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $userId
* @param numeric $id
* @return bool
* @throws \OCP\DB\Exception
*/
public function isOwner($userId, $id): bool {
public function isOwner(string $userId, int $id): bool {
$aclId = $id;
$qb = $this->db->getQueryBuilder();
$qb->select('acl.id')
@@ -68,11 +62,7 @@ class AclMapper extends DeckMapper implements IPermissionMapper {
return count($qb->executeQuery()->fetchAll()) > 0;
}
/**
* @param numeric $id
* @return int|null
*/
public function findBoardId($id): ?int {
public function findBoardId(int $id): ?int {
try {
$entity = $this->find($id);
return $entity->getBoardId();
@@ -87,7 +77,7 @@ class AclMapper extends DeckMapper implements IPermissionMapper {
* @return Acl[]
* @throws \OCP\DB\Exception
*/
public function findByParticipant($type, $participant): array {
public function findByParticipant(int $type, string $participant): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')

View File

@@ -107,11 +107,11 @@ class AssignmentMapper extends DeckMapper implements IPermissionMapper {
}
public function isOwner($userId, $id): bool {
public function isOwner(string $userId, int $id): bool {
return $this->cardMapper->isOwner($userId, $id);
}
public function findBoardId($id): ?int {
public function findBoardId(int $id): ?int {
return $this->cardMapper->findBoardId($id);
}
@@ -123,6 +123,9 @@ class AssignmentMapper extends DeckMapper implements IPermissionMapper {
* @throws NotFoundException
*/
public function insert(Entity $entity): Entity {
if (!($entity instanceof Assignment)) {
throw new \LogicException('Trying to insert a ' . get_class($entity) . ' in the assignment mapper');
}
$origin = $this->getOrigin($entity);
if ($origin === null) {
throw new NotFoundException('No origin found for assignment');
@@ -141,7 +144,7 @@ class AssignmentMapper extends DeckMapper implements IPermissionMapper {
});
}
public function isUserAssigned($cardId, $userId): bool {
public function isUserAssigned(int $cardId, string $userId): bool {
$assignments = $this->findAll($cardId);
foreach ($assignments as $assignment) {
$origin = $this->getOrigin($assignment);

View File

@@ -36,13 +36,11 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param int $id
* @return Attachment
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
* @throws \OCP\DB\Exception
*/
public function find($id) {
public function find(int $id): Attachment {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
@@ -52,14 +50,11 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param int $cardId
* @param string $data
* @return Attachment
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
* @throws \OCP\DB\Exception
*/
public function findByData($cardId, $data) {
public function findByData(int $cardId, string $data): Attachment {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
@@ -70,11 +65,10 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param $cardId
* @return Entity[]
* @throws \OCP\DB\Exception
*/
public function findAll($cardId) {
public function findAll(int $cardId): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
@@ -86,11 +80,9 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param null $cardId
* @param bool $withOffset
* @return array
* @return Attachment[]
*/
public function findToDelete($cardId = null, $withOffset = true) {
public function findToDelete(?int $cardId = null, bool $withOffset = true): array {
// add buffer of 5 min
$timeLimit = time() - (60 * 5);
$qb = $this->db->getQueryBuilder();
@@ -112,12 +104,8 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
/**
* Check if $userId is owner of Entity with $id
*
* @param $userId string userId
* @param $id int|string unique entity identifier
* @return boolean
*/
public function isOwner($userId, $id): bool {
public function isOwner(string $userId, int $id): bool {
try {
$attachment = $this->find($id);
return $this->cardMapper->isOwner($userId, $attachment->getCardId());
@@ -130,10 +118,10 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
/**
* Query boardId for Entity of given $id
*
* @param $id int|string unique entity identifier
* @param $id int unique entity identifier
* @return int|null id of Board
*/
public function findBoardId($id): ?int {
public function findBoardId(int $id): ?int {
try {
$attachment = $this->find($id);
} catch (\Exception $e) {

View File

@@ -10,10 +10,20 @@ namespace OCA\Deck\Db;
/**
* @method int getId()
* @method string getTitle()
* @method void setTitle(string $title)
* @method int getShared()
* @method void setShared(int $shared)
* @method bool isArchived()
* @method bool getArchived()
* @method void setArchived(bool $archived)
* @method int getDeletedAt()
* @method void setDeletedAt(int $deletedAt)
* @method int getLastModified()
* @method void setLastModified(int $lastModified)
* @method string getOwner()
* @method void setOwner(string $owner)
* @method string getColor()
* @method void setColor(string $color)
*/
class Board extends RelationalEntity {
protected $title;

View File

@@ -469,16 +469,16 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
return parent::delete($entity);
}
public function isOwner($userId, $id): bool {
public function isOwner(string $userId, int $id): bool {
$board = $this->find($id);
return ($board->getOwner() === $userId);
}
public function findBoardId($id): ?int {
public function findBoardId(int $id): ?int {
return $id;
}
public function mapAcl(Acl &$acl) {
public function mapAcl(Acl &$acl): void {
$acl->resolveRelation('participant', function ($participant) use (&$acl) {
if ($acl->getType() === Acl::PERMISSION_TYPE_USER) {
if ($this->userManager->userExists($acl->getParticipant())) {

View File

@@ -15,13 +15,18 @@ use Sabre\VObject\Component\VCalendar;
/**
* @method string getTitle()
* @method void setTitle(string $title)
* @method string getDescription()
* @method string getDescriptionPrev()
* @method int getStackId()
* @method void setStackId(int $stackId)
* @method int getOrder()
* @method void setOrder(int $order)
* @method int getLastModified()
* @method int getCreatedAt()
* @method bool getArchived()
* @method string getType()
* @method void setType(string $type)
* @method int getDeletedAt()
* @method void setDeletedAt(int $deletedAt)
* @method bool getNotified()
@@ -68,8 +73,8 @@ class Card extends RelationalEntity {
protected $createdAt;
protected $labels;
protected $assignedUsers;
protected $attachments;
protected $attachmentCount;
protected array $attachments = [];
protected int $attachmentCount = 0;
protected $owner;
protected $order;
protected $archived = false;

View File

@@ -86,16 +86,15 @@ class CardMapper extends QBMapper implements IPermissionMapper {
$updatedFields = $entity->getUpdatedFields();
if (isset($updatedFields['duedate']) && $updatedFields['duedate']) {
try {
/** @var Card $existing */
$existing = $this->find($entity->getId());
if ($existing && $entity->getDuedate() !== $existing->getDuedate()) {
if ($entity->getDueDate() !== $existing->getDueDate()) {
$entity->setNotified(false);
}
// remove pending notifications
$notification = $this->notificationManager->createNotification();
$notification
->setApp('deck')
->setObject('card', $entity->getId());
->setObject('card', (string)$entity->getId());
$this->notificationManager->markProcessed($notification);
} catch (Exception $e) {
}
@@ -135,7 +134,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
* @return Card[]
* @throws \OCP\DB\Exception
*/
public function findAll($stackId, ?int $limit = null, ?int $offset = null, int $since = -1) {
public function findAll($stackId, ?int $limit = null, int $offset = 0, int $since = -1) {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from('deck_cards')
@@ -155,7 +154,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
* @return array<int, null|Card[]>
* @throws \OCP\DB\Exception
*/
public function findAllForStacks(array $stackIds, ?int $limit = null, ?int $offset = null, int $since = -1): array {
public function findAllForStacks(array $stackIds, ?int $limit = null, int $offset = 0, int $since = -1): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from('deck_cards')
@@ -194,7 +193,10 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return $qb;
}
public function findToDelete($timeLimit, $limit = null) {
/**
* @return Card[]
*/
public function findToDelete(int $timeLimit, ?int $limit = null): array {
$qb = $this->db->getQueryBuilder();
$qb->select('id', 'title', 'owner', 'archived', 'deleted_at', 'last_modified')
->from('deck_cards')
@@ -205,7 +207,10 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return $this->findEntities($qb);
}
public function findDeleted($boardId, $limit = null, $offset = null) {
/**
* @return Card[]
*/
public function findDeleted(int $boardId, ?int $limit = null, int $offset = 0): array {
$qb = $this->queryCardsByBoard($boardId);
$qb->andWhere($qb->expr()->neq('c.deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
->setMaxResults($limit)
@@ -215,7 +220,10 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return $this->findEntities($qb);
}
public function findCalendarEntries($boardId, $limit = null, $offset = null) {
/**
* @return Card[]
*/
public function findCalendarEntries(int $boardId, ?int $limit = null, $offset = 0): array {
$qb = $this->db->getQueryBuilder();
$qb->select('c.*')
->from('deck_cards', 'c')
@@ -270,7 +278,11 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return $this->findEntities($qb);
}
public function findAllWithDue(array $boardIds) {
/**
* @param int[] $boardIds
* @return Card[]
*/
public function findAllWithDue(array $boardIds): array {
$qb = $this->db->getQueryBuilder();
$qb->select('c.*')
->from('deck_cards', 'c')
@@ -287,7 +299,11 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return $this->findEntities($qb);
}
public function findToMeOrNotAssignedCards(array $boardIds, string $username) {
/**
* @param int[] $boardIds
* @return Card[]
*/
public function findToMeOrNotAssignedCards(array $boardIds, string $username): array {
$qb = $this->db->getQueryBuilder();
$qb->select('c.*')
->from('deck_cards', 'c')
@@ -309,7 +325,10 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return $this->findEntities($qb);
}
public function findOverdue() {
/**
* @return Card[]
*/
public function findOverdue(): array {
$qb = $this->db->getQueryBuilder();
$qb->select('id', 'title', 'duedate', 'notified')
->from('deck_cards')
@@ -321,6 +340,9 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return $this->findEntities($qb);
}
/**
* @return Card[]
*/
public function findUnexposedDescriptionChances() {
$qb = $this->db->getQueryBuilder();
$qb->select('id', 'title', 'duedate', 'notified', 'description_prev', 'last_editor', 'description')
@@ -329,6 +351,9 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return $this->findEntities($qb);
}
/**
* @return Card[]
*/
public function search(array $boardIds, SearchQuery $query, ?int $limit = null, ?int $offset = null): array {
$qb = $this->queryCardsByBoards($boardIds);
$this->extendQueryByFilter($qb, $query);
@@ -363,7 +388,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
$qb->andWhere($qb->expr()->lt('c.last_modified', $qb->createNamedParameter($offset, IQueryBuilder::PARAM_INT)));
}
$result = $qb->execute();
$result = $qb->executeQuery();
$entities = [];
while ($row = $result->fetch()) {
$entities[] = Card::fromRow($row);
@@ -406,7 +431,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
$qb->andWhere($qb->expr()->lt('comments.id', $qb->createNamedParameter($offset, IQueryBuilder::PARAM_INT)));
}
$result = $qb->execute();
$result = $qb->executeQuery();
$entities = $result->fetchAll();
$result->closeCursor();
return $entities;
@@ -502,7 +527,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
});
$groups = $this->groupManager->search($assignment->getValue());
foreach ($searchUsers as $user) {
$groups = array_merge($groups, $this->groupManager->getUserIdGroups($user->getUID()));
$groups = array_merge($groups, $this->groupManager->getUserGroups($user));
}
$assignmentSearches = [];
@@ -555,7 +580,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
if ($offset !== null) {
$qb->setFirstResult($offset);
}
$result = $qb->execute();
$result = $qb->executeQuery();
$all = $result->fetchAll();
$result->closeCursor();
return $all;
@@ -567,32 +592,32 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return parent::delete($entity);
}
public function deleteByStack($stackId) {
public function deleteByStack($stackId): void {
$cards = $this->findAllByStack($stackId);
foreach ($cards as $card) {
$this->delete($card);
}
}
public function assignLabel($card, $label) {
public function assignLabel(int $card, int $label): void {
$qb = $this->db->getQueryBuilder();
$qb->insert('deck_assigned_labels')
->values([
'label_id' => $qb->createNamedParameter($label, IQueryBuilder::PARAM_INT),
'card_id' => $qb->createNamedParameter($card, IQueryBuilder::PARAM_INT),
]);
$qb->execute();
$qb->executeStatement();
}
public function removeLabel($card, $label) {
public function removeLabel(int $card, int $label): void {
$qb = $this->db->getQueryBuilder();
$qb->delete('deck_assigned_labels')
->where($qb->expr()->eq('card_id', $qb->createNamedParameter($card, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('label_id', $qb->createNamedParameter($label, IQueryBuilder::PARAM_INT)));
$qb->execute();
$qb->executeStatement();
}
public function isOwner($userId, $id): bool {
public function isOwner(string $userId, int $id): bool {
$qb = $this->db->getQueryBuilder();
$qb->select('c.id')
->from($this->getTableName(), 'c')
@@ -604,7 +629,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return count($qb->executeQuery()->fetchAll()) > 0;
}
public function findBoardId($id): ?int {
public function findBoardId(int $id): ?int {
$result = $this->cache->get('findBoardId:' . $id);
if ($result === null) {
try {
@@ -634,13 +659,11 @@ class CardMapper extends QBMapper implements IPermissionMapper {
}
public function transferOwnership(string $ownerId, string $newOwnerId, ?int $boardId = null): void {
$params = [
'owner' => $ownerId,
'newOwner' => $newOwnerId
];
$sql = "UPDATE `*PREFIX*{$this->tableName}` SET `owner` = :newOwner WHERE `owner` = :owner";
$stmt = $this->db->executeQuery($sql, $params);
$stmt->closeCursor();
$qb = $this->db->getQueryBuilder();
$qb->update($this->getTableName())
->set('owner', $qb->createNamedParameter($newOwnerId, IQueryBuilder::PARAM_STR))
->where('owner', $qb->createNamedParameter($ownerId, IQueryBuilder::PARAM_STR))
->executeStatement();
}
public function remapCardOwner(int $boardId, string $userId, string $newUserId): void {

View File

@@ -19,12 +19,11 @@ use OCP\DB\QueryBuilder\IQueryBuilder;
abstract class DeckMapper extends QBMapper {
/**
* @param $id
* @return T
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws \OCP\AppFramework\Db\DoesNotExistException
*/
public function find($id) {
public function find(int $id): Entity {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
@@ -37,7 +36,7 @@ abstract class DeckMapper extends QBMapper {
* Helper function to split passed array into chunks of 1000 elements and
* call a given callback for fetching query results
*
* Can be useful to limit to 1000 results per query for oracle compatiblity
* Can be useful to limit to 1000 results per query for oracle compatibility
* but still iterate over all results
*/
public function chunkQuery(array $ids, callable $callback): Generator {

View File

@@ -8,22 +8,25 @@
namespace OCA\Deck\Db;
/**
*
*/
interface IPermissionMapper {
/**
* Check if $userId is owner of Entity with $id
*
* @param $userId string userId
* @param $id int|string unique entity identifier
* @param $id int unique entity identifier
* @return boolean
*/
public function isOwner($userId, $id): bool;
public function isOwner(string $userId, int $id): bool;
/**
* Query boardId for Entity of given $id
*
* @param $id int|string unique entity identifier
* @return int|null id of Board
* @param $id int unique entity identifier
* @return ?int id of Board
*/
public function findBoardId($id): ?int;
public function findBoardId(int $id): ?int;
}

View File

@@ -8,7 +8,16 @@
namespace OCA\Deck\Db;
/**
* @method getTitle(): string
* @method string getTitle()
* @method void setTitle(string $title)
* @method string getColor()
* @method void setColor(string $color)
* @method int getBoardId()
* @method void setBoardId(int $boardId)
* @method int getCardId()
* @method void setCardId(int $cardId)
* @method int getLastModified()
* @method void setLastModified(int $lastModified)
*/
class Label extends RelationalEntity {
protected $title;
@@ -24,7 +33,7 @@ class Label extends RelationalEntity {
$this->addType('lastModified', 'integer');
}
public function getETag() {
public function getETag(): string {
return md5((string)$this->getLastModified());
}
}

View File

@@ -20,13 +20,10 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $boardId
* @param int|null $limit
* @param int|null $offset
* @return Label[]
* @throws \OCP\DB\Exception
*/
public function findAll($boardId, $limit = null, $offset = null): array {
public function findAll(int $boardId, ?int $limit = null, int $offset = 0): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
@@ -44,13 +41,10 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $cardId
* @param int|null $limit
* @param int|null $offset
* @return Label[]
* @throws \OCP\DB\Exception
*/
public function findAssignedLabelsForCard($cardId, $limit = null, $offset = null): array {
public function findAssignedLabelsForCard(int $cardId, ?int $limit = null, int $offset = 0): array {
$qb = $this->db->getQueryBuilder();
$qb->select('l.*', 'card_id')
->from($this->getTableName(), 'l')
@@ -63,7 +57,7 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
return $this->findEntities($qb);
}
public function findAssignedLabelsForCards($cardIds, $limit = null, $offset = null): array {
public function findAssignedLabelsForCards(array $cardIds, ?int $limit = null, int $offset = 0): array {
$qb = $this->db->getQueryBuilder();
$qb->select('l.*', 'card_id')
->from($this->getTableName(), 'l')
@@ -77,13 +71,10 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $boardId
* @param int|null $limit
* @param int|null $offset
* @return Label[]
* @throws \OCP\DB\Exception
*/
public function findAssignedLabelsForBoard($boardId, $limit = null, $offset = null): array {
public function findAssignedLabelsForBoard(int $boardId, ?int $limit = null, int $offset = 0): array {
$qb = $this->db->getQueryBuilder();
$qb->select('l.id as id', 'l.title as title', 'l.color as color')
->selectAlias('c.id', 'card_id')
@@ -113,11 +104,10 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $boardId
* @return array
* @return array<int, list<Label>>
* @throws \OCP\DB\Exception
*/
public function getAssignedLabelsForBoard($boardId) {
public function getAssignedLabelsForBoard(int $boardId): array {
$labels = $this->findAssignedLabelsForBoard($boardId);
$result = [];
foreach ($labels as $label) {
@@ -130,11 +120,9 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $labelId
* @return void
* @throws \OCP\DB\Exception
*/
public function deleteLabelAssignments($labelId) {
public function deleteLabelAssignments(int $labelId): void {
$qb = $this->db->getQueryBuilder();
$qb->delete('deck_assigned_labels')
->where($qb->expr()->eq('label_id', $qb->createNamedParameter($labelId, IQueryBuilder::PARAM_INT)));
@@ -142,11 +130,9 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $cardId
* @return void
* @throws \OCP\DB\Exception
*/
public function deleteLabelAssignmentsForCard($cardId) {
public function deleteLabelAssignmentsForCard(int $cardId): void {
$qb = $this->db->getQueryBuilder();
$qb->delete('deck_assigned_labels')
->where($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)));
@@ -154,33 +140,25 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param string $userId
* @param numeric $labelId
* @return bool
* @throws \OCP\DB\Exception
*/
public function isOwner($userId, $labelId): bool {
public function isOwner(string $userId, int $id): bool {
$qb = $this->db->getQueryBuilder();
$qb->select('l.id')
->from($this->getTableName(), 'l')
->innerJoin('l', 'deck_boards', 'b', 'l.board_id = b.id')
->where($qb->expr()->eq('l.id', $qb->createNamedParameter($labelId, IQueryBuilder::PARAM_INT)))
->where($qb->expr()->eq('l.id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('b.owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
return count($qb->executeQuery()->fetchAll()) > 0;
}
/**
* @param numeric $id
* @return int|null
*/
public function findBoardId($id): ?int {
public function findBoardId(int $id): ?int {
try {
$entity = $this->find($id);
return $entity->getBoardId();
} catch (DoesNotExistException $e) {
} catch (MultipleObjectsReturnedException $e) {
} catch (DoesNotExistException|MultipleObjectsReturnedException) {
return null;
}
return null;
}
}

View File

@@ -11,10 +11,16 @@ use Sabre\VObject\Component\VCalendar;
/**
* @method int getId()
* @method string getTitle()
* @method void setTitle(string $title)
* @method int getBoardId()
* @method void setBoardId(int $boardId)
* @method int getDeletedAt()
* @method void setDeletedAt(int $deletedAt)
* @method int getLastModified()
* @method int getOrder()
* @method void setLastModified(int $lastModified)
* @method \int getOrder()
* @method void setOrder(int $order)
* @method Card[] getCards()
*/
class Stack extends RelationalEntity {

View File

@@ -35,13 +35,11 @@ class StackMapper extends DeckMapper implements IPermissionMapper {
/**
* @param numeric $id
* @return Stack
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
* @throws \OCP\DB\Exception
*/
public function find($id): Stack {
public function find(int $id): Stack {
if (isset($this->stackCache[(string)$id])) {
return $this->stackCache[(string)$id];
}
@@ -56,11 +54,9 @@ class StackMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param $cardId
* @return Stack|null
* @throws \OCP\DB\Exception
*/
public function findStackFromCardId($cardId): ?Stack {
public function findStackFromCardId(int $cardId): ?Stack {
$qb = $this->db->getQueryBuilder();
$qb->select('s.*')
->from($this->getTableName(), 's')
@@ -76,11 +72,10 @@ class StackMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $boardId
* @return Stack[]
* @throws \OCP\DB\Exception
*/
public function findAll($boardId, ?int $limit = null, ?int $offset = null): array {
public function findAll(int $boardId, ?int $limit = null, int $offset = 0): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
@@ -93,13 +88,9 @@ class StackMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $boardId
* @param int|null $limit
* @param int|null $offset
* @return Stack[]
* @throws \OCP\DB\Exception
*/
public function findDeleted($boardId, $limit = null, $offset = null) {
public function findDeleted(int $boardId, ?int $limit = null, int $offset = 0): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
@@ -125,12 +116,9 @@ class StackMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $userId
* @param numeric $stackId
* @return bool
* @throws \OCP\DB\Exception
*/
public function isOwner($userId, $id): bool {
public function isOwner(string $userId, int $id): bool {
$qb = $this->db->getQueryBuilder();
$qb->select('s.id')
->from($this->getTableName(), 's')
@@ -142,11 +130,9 @@ class StackMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $id
* @return int|null
* @throws \OCP\DB\Exception
*/
public function findBoardId($id): ?int {
public function findBoardId(int $id): ?int {
$result = $this->cache->get('findBoardId:' . $id);
if ($result !== null) {
return $result !== false ? $result : null;
@@ -163,6 +149,10 @@ class StackMapper extends DeckMapper implements IPermissionMapper {
return $result !== false ? $result : null;
}
/**
* @return array<Stack>
* @throws \OCP\DB\Exception
*/
public function findToDelete(): array {
// add buffer of 5 min
$timeLimit = time() - (60 * 5);

View File

@@ -35,6 +35,10 @@ class BeforeTemplateRenderedListener implements IEventListener {
Util::addStyle('deck', 'deck');
$pathInfo = $this->request->getPathInfo();
if (!$pathInfo) {
return;
}
if (str_starts_with($pathInfo, '/apps/calendar')) {
Util::addScript('deck', 'deck-calendar');
}

View File

@@ -18,14 +18,10 @@ use OCP\EventDispatcher\IEventListener;
/** @template-implements IEventListener<Event|AclDeletedEvent|AclCreatedEvent> */
class ResourceListener implements IEventListener {
/** @var IManager */
private $resourceManager;
/** @var ResourceProviderCard */
private $resourceProviderCard;
public function __construct(IManager $resourceManager, ResourceProviderCard $resourceProviderCard) {
$this->resourceManager = $resourceManager;
$this->resourceProviderCard = $resourceProviderCard;
public function __construct(
private readonly IManager $resourceManager,
private readonly ResourceProviderCard $resourceProviderCard,
) {
}
public function handle(Event $event): void {
@@ -38,10 +34,10 @@ class ResourceListener implements IEventListener {
$this->resourceManager->invalidateAccessCacheForProvider($this->resourceProviderCard);
try {
$resource = $this->resourceManager->getResourceForUser(ResourceProvider::RESOURCE_TYPE, $boardId, null);
$resource = $this->resourceManager->getResourceForUser(ResourceProvider::RESOURCE_TYPE, (string)$boardId, null);
$this->resourceManager->invalidateAccessCacheForResource($resource);
} catch (ResourceException $e) {
// If there is no resource we don't need to invalidate anything, but this should not happen anyways
// If there is no resource we don't need to invalidate anything, but this should not happen anyway
}
}
}

View File

@@ -11,6 +11,7 @@ use OCA\Deck\Db\Board;
class BoardSummary extends Board {
private Board $board;
/** @psalm-suppress ConstructorSignatureMismatch */
public function __construct(Board $board) {
parent::__construct();
$this->board = $board;
@@ -27,7 +28,7 @@ class BoardSummary extends Board {
return $this->board->getter($name);
}
public function __call($name, $arguments) {
return $this->board->__call($name, $arguments);
public function __call($methodName, $args) {
return $this->board->__call($methodName, $args);
}
}

View File

@@ -15,6 +15,7 @@ class CardDetails extends Card {
private ?Board $board;
private ?Reference $referenceData = null;
/** @psalm-suppress ConstructorSignatureMismatch */
public function __construct(Card $card, ?Board $board = null) {
parent::__construct();
$this->card = $card;

View File

@@ -95,7 +95,7 @@ class NotificationHelper {
$shouldNotify = $notificationSetting === ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ALL;
if ($user->getUID() === $board->getOwner() && count($board->getAcl()) === 0) {
if ($user->getUID() === $board->getOwner() && count($board->getAcl() ?? []) === 0) {
// Notify if all or assigned is configured for unshared boards
$shouldNotify = true;
} elseif ($notificationSetting === ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED && $this->assignmentMapper->isUserAssigned($card->getId(), $user->getUID())) {

View File

@@ -21,7 +21,6 @@ use OCA\Deck\NotFoundException;
use OCA\Deck\Notification\NotificationHelper;
use OCA\Deck\Validators\AssignmentServiceValidator;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\EventDispatcher\IEventDispatcher;
@@ -92,15 +91,12 @@ class AssignmentService {
}
/**
* @param $cardId
* @param $userId
* @return bool|null|Entity
* @throws BadRequestException
* @throws NoPermissionException
* @throws MultipleObjectsReturnedException
* @throws DoesNotExistException
*/
public function assignUser($cardId, $userId, int $type = Assignment::TYPE_USER) {
public function assignUser(int $cardId, string $userId, int $type = Assignment::TYPE_USER): Assignment {
$this->assignmentServiceValidator->check(compact('cardId', 'userId'));
if ($type !== Assignment::TYPE_USER && $type !== Assignment::TYPE_GROUP) {
@@ -144,16 +140,13 @@ class AssignmentService {
}
/**
* @param $cardId
* @param $userId
* @return Entity
* @throws BadRequestException
* @throws NotFoundException
* @throws NoPermissionException
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
*/
public function unassignUser($cardId, $userId, $type = 0) {
public function unassignUser(int $cardId, string $userId, int $type = 0): Assignment {
$this->assignmentServiceValidator->check(compact('cardId', 'userId'));
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);

View File

@@ -25,6 +25,7 @@ use OCP\AppFramework\Db\IMapperException;
use OCP\AppFramework\Http\Response;
use OCP\IL10N;
use OCP\IUserManager;
use Psr\Container\ContainerExceptionInterface;
class AttachmentService {
private $attachmentMapper;
@@ -80,20 +81,16 @@ class AttachmentService {
}
/**
* @param string $type
* @param string $class
* @throws \OCP\AppFramework\QueryException
* @throws ContainerExceptionInterface
*/
public function registerAttachmentService($type, $class) {
$this->services[$type] = $this->application->getContainer()->query($class);
public function registerAttachmentService(string $type, string $class): void {
$this->services[$type] = $this->application->getContainer()->get($class);
}
/**
* @param string $type
* @return IAttachmentService
* @throws InvalidAttachmentType
*/
public function getService($type) {
public function getService(string $type): IAttachmentService {
if (isset($this->services[$type])) {
return $this->services[$type];
}
@@ -101,16 +98,11 @@ class AttachmentService {
}
/**
* @param $cardId
* @return array
* @return Attachment[]
* @throws \OCA\Deck\NoPermissionException
* @throws BadRequestException
*/
public function findAll($cardId, $withDeleted = false) {
if (is_numeric($cardId) === false) {
throw new BadRequestException('card id must be a number');
}
public function findAll(int $cardId, bool $withDeleted = false): array {
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ);
$attachments = $this->attachmentMapper->findAll($cardId);
@@ -122,7 +114,7 @@ class AttachmentService {
/** @var IAttachmentService $service */
$service = $this->getService($attachmentType);
if ($service instanceof ICustomAttachmentService) {
$attachments = array_merge($attachments, $service->listAttachments((int)$cardId));
$attachments = array_merge($attachments, $service->listAttachments($cardId));
}
}
@@ -140,49 +132,40 @@ class AttachmentService {
}
/**
* @param $cardId
* @return int|mixed
* @throws BadRequestException
* @throws InvalidAttachmentType
* @throws \OCP\DB\Exception
*/
public function count($cardId) {
if (is_numeric($cardId) === false) {
throw new BadRequestException('card id must be a number');
}
$count = $this->attachmentCacheHelper->getAttachmentCount((int)$cardId);
public function count(int $cardId): int {
$count = $this->attachmentCacheHelper->getAttachmentCount($cardId);
if ($count === null) {
$count = count($this->attachmentMapper->findAll($cardId));
foreach (array_keys($this->services) as $attachmentType) {
$service = $this->getService($attachmentType);
if ($service instanceof ICustomAttachmentService) {
$count += $service->getAttachmentCount((int)$cardId);
$count += $service->getAttachmentCount($cardId);
}
}
$this->attachmentCacheHelper->setAttachmentCount((int)$cardId, $count);
$this->attachmentCacheHelper->setAttachmentCount($cardId, $count);
}
return $count;
}
/**
* @param $cardId
* @param $type
* @param $data
* @return Attachment|\OCP\AppFramework\Db\Entity
* @throws NoPermissionException
* @throws StatusException
* @throws BadRequestException
*/
public function create($cardId, $type, $data) {
public function create(int $cardId, string $type, string $data) {
$this->attachmentServiceValidator->check(compact('cardId', 'type'));
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
$this->attachmentCacheHelper->clearAttachmentCount((int)$cardId);
$this->attachmentCacheHelper->clearAttachmentCount($cardId);
$attachment = new Attachment();
$attachment->setCardId($cardId);
$attachment->setType($type);
@@ -218,12 +201,10 @@ class AttachmentService {
/**
* Display the attachment
*
* @param $attachmentId
* @return Response
* @throws NoPermissionException
* @throws NotFoundException
*/
public function display($cardId, $attachmentId, $type = 'deck_file') {
public function display(int $cardId, int $attachmentId, string $type = 'deck_file'): Response {
try {
$service = $this->getService($type);
} catch (InvalidAttachmentType $e) {
@@ -257,13 +238,10 @@ class AttachmentService {
/**
* Update an attachment with custom data
*
* @param $attachmentId
* @param $data
* @return mixed
* @throws BadRequestException
* @throws NoPermissionException
*/
public function update($cardId, $attachmentId, $data, $type = 'deck_file') {
public function update(int $cardId, int $attachmentId, string $data, string $type = 'deck_file'): Attachment {
$this->attachmentServiceValidator->check(compact('cardId', 'type', 'data'));
try {
@@ -384,8 +362,6 @@ class AttachmentService {
}
/**
* @param Attachment $attachment
* @return Attachment
* @throws \ReflectionException
*/
private function addCreator(Attachment $attachment): Attachment {

View File

@@ -75,8 +75,6 @@ class BoardService {
/**
* Set a different user than the current one, e.g. when no user is available in occ
*
* @param string $userId
*/
public function setUserId(string $userId): void {
$this->userId = $userId;
@@ -117,22 +115,18 @@ class BoardService {
}
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ);
/** @var Board $board */
$board = $this->boardMapper->find($boardId, true, true, $allowDeleted);
[$board] = $this->enrichBoards([$board], $fullDetails);
return $board;
}
/**
* @param $mapper
* @param $id
* @return bool
* @throws DoesNotExistException
* @throws NoPermissionException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function isArchived($mapper, $id) {
public function isArchived($mapper, int $id): bool {
$this->boardServiceValidator->check(compact('id'));
try {
@@ -151,15 +145,12 @@ class BoardService {
}
/**
* @param $mapper
* @param $id
* @return bool
* @throws DoesNotExistException
* @throws NoPermissionException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function isDeleted($mapper, $id) {
public function isDeleted($mapper, int $id): bool {
$this->boardServiceValidator->check(compact('mapper', 'id'));
try {
@@ -179,13 +170,9 @@ class BoardService {
/**
* @param $title
* @param $userId
* @param $color
* @return \OCP\AppFramework\Db\Entity
* @throws BadRequestException
*/
public function create($title, $userId, $color) {
public function create(string $title, string $userId, string $color): Board {
$this->boardServiceValidator->check(compact('title', 'userId', 'color'));
if (!$this->permissionService->canCreate()) {
@@ -196,7 +183,8 @@ class BoardService {
$board->setTitle($title);
$board->setOwner($userId);
$board->setColor($color);
$new_board = $this->boardMapper->insert($board);
/** @var Board $board */
$board = $this->boardMapper->insert($board);
// create new labels
$default_labels = [
@@ -210,33 +198,31 @@ class BoardService {
$label = new Label();
$label->setColor($labelColor);
$label->setTitle($labelTitle);
$label->setBoardId($new_board->getId());
$label->setBoardId($board->getId());
$labels[] = $this->labelMapper->insert($label);
}
$new_board->setLabels($labels);
$this->boardMapper->mapOwner($new_board);
$permissions = $this->permissionService->matchPermissions($new_board);
$new_board->setPermissions([
$board->setLabels($labels);
$this->boardMapper->mapOwner($board);
$permissions = $this->permissionService->matchPermissions($board);
$board->setPermissions([
'PERMISSION_READ' => $permissions[Acl::PERMISSION_READ] ?? false,
'PERMISSION_EDIT' => $permissions[Acl::PERMISSION_EDIT] ?? false,
'PERMISSION_MANAGE' => $permissions[Acl::PERMISSION_MANAGE] ?? false,
'PERMISSION_SHARE' => $permissions[Acl::PERMISSION_SHARE] ?? false
]);
$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_BOARD, $new_board, ActivityManager::SUBJECT_BOARD_CREATE, [], $userId);
$this->changeHelper->boardChanged($new_board->getId());
$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_BOARD, $board, ActivityManager::SUBJECT_BOARD_CREATE, [], $userId);
$this->changeHelper->boardChanged($board->getId());
return $new_board;
return $board;
}
/**
* @param $id
* @return Board
* @throws DoesNotExistException
* @throws NoPermissionException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function delete($id) {
public function delete(int $id): Board {
$this->boardServiceValidator->check(compact('id'));
$this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_MANAGE);
@@ -253,13 +239,11 @@ class BoardService {
}
/**
* @param $id
* @return \OCP\AppFramework\Db\Entity
* @throws DoesNotExistException
* @throws NoPermissionException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
*/
public function deleteUndo($id) {
public function deleteUndo(int $id): Board {
$this->boardServiceValidator->check(compact('id'));
$this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_MANAGE);
@@ -273,14 +257,12 @@ class BoardService {
}
/**
* @param $id
* @return \OCP\AppFramework\Db\Entity
* @throws DoesNotExistException
* @throws NoPermissionException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function deleteForce($id) {
public function deleteForce(int $id): Board {
$this->boardServiceValidator->check(compact('id'));
$this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_MANAGE);
@@ -291,17 +273,12 @@ class BoardService {
}
/**
* @param $id
* @param $title
* @param $color
* @param $archived
* @return \OCP\AppFramework\Db\Entity
* @throws DoesNotExistException
* @throws NoPermissionException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function update($id, $title, $color, $archived) {
public function update(int $id, string $title, string $color, bool $archived): Board {
$this->boardServiceValidator->check(compact('id', 'title', 'color', 'archived'));
$this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_MANAGE);
@@ -320,7 +297,7 @@ class BoardService {
return $board;
}
private function applyPermissions($boardId, $edit, $share, $manage, $oldAcl = null) {
private function applyPermissions(int $boardId, bool $edit, bool $share, bool $manage, ?Acl $oldAcl = null): array {
try {
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_MANAGE);
} catch (NoPermissionException $e) {
@@ -332,7 +309,7 @@ class BoardService {
return [$edit, $share, $manage];
}
public function enrichWithBoardSettings(Board $board) {
public function enrichWithBoardSettings(Board $board): void {
$globalCalendarConfig = (bool)$this->config->getUserValue($this->userId, Application::APP_ID, 'calendar', true);
$settings = [
'notify-due' => $this->config->getUserValue($this->userId, Application::APP_ID, 'board:' . $board->getId() . ':notify-due', ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED),
@@ -341,7 +318,7 @@ class BoardService {
$board->setSettings($settings);
}
public function enrichWithActiveSessions(Board $board) {
public function enrichWithActiveSessions(Board $board): void {
$sessions = $this->sessionMapper->findAllActive($board->getId());
$board->setActiveSessions(array_values(
@@ -354,17 +331,11 @@ class BoardService {
}
/**
* @param $boardId
* @param $type
* @param $participant
* @param $edit
* @param $share
* @param $manage
* @return \OCP\AppFramework\Db\Entity
* @param Acl::PERMISSION_TYPE_* $type
* @throws BadRequestException
* @throws NoPermissionException
*/
public function addAcl($boardId, $type, $participant, $edit, $share, $manage) {
public function addAcl(int $boardId, int $type, $participant, bool $edit, bool $share, bool $manage): Acl {
$this->boardServiceValidator->check(compact('boardId', 'type', 'participant', 'edit', 'share', 'manage'));
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_SHARE);
@@ -400,17 +371,12 @@ class BoardService {
}
/**
* @param $id
* @param $edit
* @param $share
* @param $manage
* @return \OCP\AppFramework\Db\Entity
* @throws DoesNotExistException
* @throws NoPermissionException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function updateAcl($id, $edit, $share, $manage) {
public function updateAcl(int $id, bool $edit, bool $share, bool $manage): Acl {
$this->boardServiceValidator->check(compact('id', 'edit', 'share', 'manage'));
$this->permissionService->checkPermission($this->aclMapper, $id, Acl::PERMISSION_SHARE);
@@ -422,12 +388,12 @@ class BoardService {
$acl->setPermissionShare($share);
$acl->setPermissionManage($manage);
$this->boardMapper->mapAcl($acl);
$board = $this->aclMapper->update($acl);
$acl = $this->aclMapper->update($acl);
$this->changeHelper->boardChanged($acl->getBoardId());
$this->eventDispatcher->dispatchTyped(new AclUpdatedEvent($acl));
return $board;
return $acl;
}
/**
@@ -579,27 +545,23 @@ class BoardService {
}
/**
* @param $id
* @return Board
* @throws DoesNotExistException
* @throws NoPermissionException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function export($id) : Board {
if (is_numeric($id) === false) {
throw new BadRequestException('board id must be a number');
}
public function export(int $id): Board {
$this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_READ);
$board = $this->boardMapper->find((int)$id);
$board = $this->boardMapper->find($id);
$this->enrichWithCards($board);
$this->enrichWithLabels($board);
return $board;
}
/** @param Board[] $boards */
/**
* @param Board[] $boards
* @return Board[]
*/
private function enrichBoards(array $boards, bool $fullDetails = true): array {
$result = [];
foreach ($boards as $board) {
@@ -715,8 +677,8 @@ class BoardService {
}
}
private function enrichWithStacks($board, $since = -1) {
$stacks = $this->stackMapper->findAll($board->getId(), null, null, $since);
private function enrichWithStacks(Board $board): void {
$stacks = $this->stackMapper->findAll($board->getId());
if (\count($stacks) === 0) {
return;
@@ -725,8 +687,8 @@ class BoardService {
$board->setStacks($stacks);
}
private function enrichWithLabels($board, $since = -1) {
$labels = $this->labelMapper->findAll($board->getId(), null, null, $since);
private function enrichWithLabels(Board $board): void {
$labels = $this->labelMapper->findAll($board->getId());
if (\count($labels) === 0) {
return;
@@ -735,7 +697,7 @@ class BoardService {
$board->setLabels($labels);
}
private function enrichWithUsers($board, $since = -1) {
private function enrichWithUsers(Board $board): void {
$boardUsers = $this->permissionService->findUsers($board->getId());
if ($boardUsers === null || \count($boardUsers) === 0) {
return;
@@ -746,7 +708,7 @@ class BoardService {
/**
* Clean a given board data from the Cache
*/
private function clearBoardFromCache(Board $board) {
private function clearBoardFromCache(Board $board): void {
$boardId = $board->getId();
$boardOwnerId = $board->getOwner();
@@ -755,7 +717,7 @@ class BoardService {
unset($this->boardsCachePartial[$boardId]);
}
private function enrichWithCards($board) {
private function enrichWithCards(Board $board): void {
$stacks = $this->stackMapper->findAll($board->getId());
foreach ($stacks as $stack) {
$cards = $this->cardMapper->findAllByStack($stack->getId());

View File

@@ -64,10 +64,14 @@ class CardService {
) {
}
public function enrichCards($cards) {
/**
* @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) {
$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);
@@ -120,7 +124,8 @@ class CardService {
);
}
public function fetchDeleted($boardId) {
/** @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);
@@ -129,13 +134,12 @@ class CardService {
}
/**
* @return \OCA\Deck\Db\RelationalEntity
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function find(int $cardId) {
public function find(int $cardId): Card {
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ);
$card = $this->cardMapper->find($cardId);
[$card] = $this->enrichCards([$card]);
@@ -152,7 +156,10 @@ class CardService {
return $card;
}
public function findCalendarEntries($boardId) {
/**
* @return Card[]
*/
public function findCalendarEntries(int $boardId): array {
try {
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ);
} catch (NoPermissionException $e) {
@@ -163,20 +170,13 @@ class CardService {
}
/**
* @param $title
* @param $stackId
* @param $type
* @param integer $order
* @param $description
* @param $owner
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadrequestException
*/
public function create($title, $stackId, $type, $order, $owner, $description = '', $duedate = null) {
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);
@@ -203,19 +203,13 @@ class CardService {
}
/**
* @param $id
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function delete($id) {
if (is_numeric($id) === false) {
throw new BadRequestException('card id must be a number');
}
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.');
@@ -233,25 +227,13 @@ class CardService {
}
/**
* @param $id
* @param $title
* @param $stackId
* @param $type
* @param $owner
* @param $description
* @param $order
* @param $duedate
* @param $deletedAt
* @param $archived
* @param $done
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function update($id, $title, $stackId, $type, $owner, $description = '', $order = 0, $duedate = null, $deletedAt = null, $archived = null, ?OptionalNullableValue $done = null) {
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);
@@ -398,16 +380,13 @@ class CardService {
}
/**
* @param $id
* @param $title
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function rename($id, $title) {
public function rename(int $id, string $title): Card {
$this->cardServiceValidator->check(compact('id', 'title'));
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
@@ -429,17 +408,14 @@ class CardService {
}
/**
* @param $id
* @param $stackId
* @param $order
* @return array
* @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) {
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);
@@ -488,15 +464,13 @@ class CardService {
}
/**
* @param $id
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function archive($id) {
public function archive(int $id): Card {
$this->cardServiceValidator->check(compact('id'));
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
@@ -517,15 +491,13 @@ class CardService {
}
/**
* @param $id
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function unarchive($id) {
public function unarchive(int $id): Card {
$this->cardServiceValidator->check(compact('id'));
@@ -546,8 +518,6 @@ class CardService {
}
/**
* @param $id
* @return \OCA\Deck\Db\Card
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
@@ -599,15 +569,13 @@ class CardService {
}
/**
* @param $cardId
* @param $labelId
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function assignLabel($cardId, $labelId) {
public function assignLabel(int $cardId, int $labelId): Card {
$this->cardServiceValidator->check(compact('cardId', 'labelId'));
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
@@ -629,18 +597,16 @@ class CardService {
$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_LABEL_ASSIGN, ['label' => $label]);
$this->eventDispatcher->dispatchTyped(new CardUpdatedEvent($card));
return $card;
}
/**
* @param $cardId
* @param $labelId
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function removeLabel($cardId, $labelId) {
public function removeLabel(int $cardId, int $labelId): Card {
$this->cardServiceValidator->check(compact('cardId', 'labelId'));
@@ -660,6 +626,7 @@ class CardService {
$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 {

View File

@@ -66,7 +66,7 @@ class CirclesService {
$circlesManager->startSession($federatedUser);
$circle = $circlesManager->getCircle($circleId);
$member = $circle->getInitiator();
$isUserInCircle = $member !== null && $member->getLevel() >= Member::LEVEL_MEMBER;
$isUserInCircle = $member->getLevel() >= Member::LEVEL_MEMBER;
if (!isset($this->userCircleCache[$circleId])) {
$this->userCircleCache[$circleId] = [];

View File

@@ -22,8 +22,6 @@ use OCP\IUserManager;
use OutOfBoundsException;
use Psr\Log\LoggerInterface;
use function is_numeric;
class CommentService {
public function __construct(
@@ -36,12 +34,9 @@ class CommentService {
) {
}
public function list(string $cardId, int $limit = 20, int $offset = 0): DataResponse {
if (!is_numeric($cardId)) {
throw new BadRequestException('A valid card id must be provided');
}
public function list(int $cardId, int $limit = 20, int $offset = 0): DataResponse {
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ);
$comments = $this->commentsManager->getForObject(Application::COMMENT_ENTITY_TYPE, $cardId, $limit, $offset);
$comments = $this->commentsManager->getForObject(Application::COMMENT_ENTITY_TYPE, (string)$cardId, $limit, $offset);
$result = [];
foreach ($comments as $comment) {
$formattedComment = $this->formatComment($comment);
@@ -96,13 +91,13 @@ class CommentService {
* @throws BadRequestException
* @throws NotFoundException|NoPermissionException
*/
public function create(int $cardId, string $message, string $replyTo = '0'): DataResponse {
public function create(int $cardId, string $message, int $replyTo = 0): DataResponse {
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ);
// Check if parent is a comment on the same card
if ($replyTo !== '0') {
if ($replyTo !== 0) {
try {
$comment = $this->commentsManager->get($replyTo);
$comment = $this->commentsManager->get((string)$replyTo);
if ($comment->getObjectType() !== Application::COMMENT_ENTITY_TYPE || (int)$comment->getObjectId() !== $cardId) {
throw new CommentNotFoundException();
}
@@ -115,7 +110,7 @@ class CommentService {
$comment = $this->commentsManager->create('users', $this->userId, Application::COMMENT_ENTITY_TYPE, (string)$cardId);
$comment->setMessage($message);
$comment->setVerb('comment');
$comment->setParentId($replyTo);
$comment->setParentId((string)$replyTo);
$this->commentsManager->save($comment);
return new DataResponse($this->formatComment($comment, true));
} catch (\InvalidArgumentException $e) {
@@ -128,14 +123,8 @@ class CommentService {
}
}
public function update(string $cardId, string $commentId, string $message): DataResponse {
if (!is_numeric($cardId)) {
throw new BadRequestException('A valid card id must be provided');
}
if (!is_numeric($commentId)) {
throw new BadRequestException('A valid comment id must be provided');
}
$comment = $this->get((int)$cardId, (int)$commentId);
public function update(int $cardId, int $commentId, string $message): DataResponse {
$comment = $this->get($cardId, $commentId);
if ($comment->getActorType() !== 'users' || $comment->getActorId() !== $this->userId) {
throw new NoPermissionException('Only authors are allowed to edit their comment.');
}
@@ -145,18 +134,12 @@ class CommentService {
return new DataResponse($this->formatComment($comment));
}
public function delete(string $cardId, string $commentId): DataResponse {
if (!is_numeric($cardId)) {
throw new BadRequestException('A valid card id must be provided');
}
if (!is_numeric($commentId)) {
throw new BadRequestException('A valid comment id must be provided');
}
public function delete(int $cardId, int $commentId): DataResponse {
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ);
try {
$comment = $this->commentsManager->get($commentId);
if ($comment->getObjectType() !== Application::COMMENT_ENTITY_TYPE || $comment->getObjectId() !== $cardId) {
$comment = $this->commentsManager->get((string)$commentId);
if ($comment->getObjectType() !== Application::COMMENT_ENTITY_TYPE || (int)$comment->getObjectId() !== $cardId) {
throw new CommentNotFoundException();
}
} catch (CommentNotFoundException $e) {
@@ -165,11 +148,11 @@ class CommentService {
if ($comment->getActorType() !== 'users' || $comment->getActorId() !== $this->userId) {
throw new NoPermissionException('Only authors are allowed to edit their comment.');
}
$this->commentsManager->delete($commentId);
$this->commentsManager->delete((string)$commentId);
return new DataResponse([]);
}
private function formatComment(IComment $comment, $addReplyTo = false): array {
private function formatComment(IComment $comment, bool $addReplyTo = false): array {
$actorDisplayName = $this->userManager->getDisplayName($comment->getActorId()) ?? $comment->getActorId();
$formattedComment = [

View File

@@ -48,7 +48,8 @@ class ConfigService {
}
public function getAll(): array {
if ($this->getUserId() === null) {
$userId = $this->getUserId();
if ($userId === null) {
return [];
}
@@ -57,7 +58,7 @@ class ConfigService {
'cardDetailsInModal' => $this->isCardDetailsInModal(),
'cardIdBadge' => $this->isCardIdBadgeEnabled()
];
if ($this->groupManager->isAdmin($this->getUserId())) {
if ($this->groupManager->isAdmin($userId)) {
$data['groupLimit'] = $this->get('groupLimit');
}
return $data;
@@ -95,44 +96,48 @@ class ConfigService {
}
public function isCalendarEnabled(?int $boardId = null): bool {
if ($this->getUserId() === null) {
$userId = $this->getUserId();
if ($userId === null) {
return false;
}
$appConfigState = $this->config->getAppValue(Application::APP_ID, 'calendar', 'yes') === 'yes';
$defaultState = (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'calendar', $appConfigState);
$defaultState = (bool)$this->config->getUserValue($userId, Application::APP_ID, 'calendar', $appConfigState);
if ($boardId === null) {
return $defaultState;
}
return (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'board:' . $boardId . ':calendar', $defaultState);
return (bool)$this->config->getUserValue($userId, Application::APP_ID, 'board:' . $boardId . ':calendar', $defaultState);
}
public function isCardDetailsInModal(?int $boardId = null): bool {
if ($this->getUserId() === null) {
$userId = $this->getUserId();
if ($userId === null) {
return false;
}
$defaultState = (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'cardDetailsInModal', true);
$defaultState = (bool)$this->config->getUserValue($userId, Application::APP_ID, 'cardDetailsInModal', true);
if ($boardId === null) {
return $defaultState;
}
return (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'board:' . $boardId . ':cardDetailsInModal', $defaultState);
return (bool)$this->config->getUserValue($userId, Application::APP_ID, 'board:' . $boardId . ':cardDetailsInModal', $defaultState);
}
public function isCardIdBadgeEnabled(): bool {
if ($this->getUserId() === null) {
$userId = $this->getUserId();
if ($userId === null) {
return false;
}
$appConfigState = $this->config->getAppValue(Application::APP_ID, 'cardIdBadge', 'yes') === 'no';
$defaultState = (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'cardIdBadge', $appConfigState);
$defaultState = (bool)$this->config->getUserValue($userId, Application::APP_ID, 'cardIdBadge', $appConfigState);
return (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'cardIdBadge', $defaultState);
return (bool)$this->config->getUserValue($userId, Application::APP_ID, 'cardIdBadge', $defaultState);
}
public function set($key, $value) {
if ($this->getUserId() === null) {
$userId = $this->getUserId();
if ($userId === null) {
throw new NoPermissionException('Must be logged in to set user config');
}
@@ -140,21 +145,21 @@ class ConfigService {
[$scope] = explode(':', $key, 2);
switch ($scope) {
case 'groupLimit':
if (!$this->groupManager->isAdmin($this->getUserId())) {
if (!$this->groupManager->isAdmin($userId)) {
throw new NoPermissionException('You must be admin to set the group limit');
}
$result = $this->setGroupLimit($value);
break;
case 'calendar':
$this->config->setUserValue($this->getUserId(), Application::APP_ID, 'calendar', (string)$value);
$this->config->setUserValue($userId, Application::APP_ID, 'calendar', (string)$value);
$result = $value;
break;
case 'cardDetailsInModal':
$this->config->setUserValue($this->getUserId(), Application::APP_ID, 'cardDetailsInModal', (string)$value);
$this->config->setUserValue($userId, Application::APP_ID, 'cardDetailsInModal', (string)$value);
$result = $value;
break;
case 'cardIdBadge':
$this->config->setUserValue($this->getUserId(), Application::APP_ID, 'cardIdBadge', (string)$value);
$this->config->setUserValue($userId, Application::APP_ID, 'cardIdBadge', (string)$value);
$result = $value;
break;
case 'board':
@@ -162,7 +167,7 @@ class ConfigService {
if ($boardConfigKey === 'notify-due' && !in_array($value, [self::SETTING_BOARD_NOTIFICATION_DUE_ALL, self::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED, self::SETTING_BOARD_NOTIFICATION_DUE_OFF], true)) {
throw new BadRequestException('Board notification option must be one of: off, assigned, all');
}
$this->config->setUserValue($this->getUserId(), Application::APP_ID, $key, (string)$value);
$this->config->setUserValue($userId, Application::APP_ID, $key, (string)$value);
$result = $value;
}
return $result;

View File

@@ -124,18 +124,15 @@ class FullTextSearchService {
/**
* @param int $cardId
*
* @return Board
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
*/
public function getBoardFromCardId(int $cardId): Board {
$boardId = (int)$this->cardMapper->findBoardId($cardId);
/** @var Board $board */
$board = $this->boardMapper->find($boardId);
return $board;
$boardId = $this->cardMapper->findBoardId($cardId);
if ($boardId === null) {
throw new DoesNotExistException("Board '$cardId' does not exist");
}
return $this->boardMapper->find($boardId);
}
@@ -145,7 +142,7 @@ class FullTextSearchService {
* @return Card[]
*/
private function getCardsFromStack(int $stackId): array {
return $this->cardMapper->findAll($stackId, null, null);
return $this->cardMapper->findAll($stackId);
}
@@ -155,7 +152,7 @@ class FullTextSearchService {
* @return Stack[]
*/
private function getStacksFromBoard(int $boardId): array {
return $this->stackMapper->findAll($boardId, null, null);
return $this->stackMapper->findAll($boardId);
}
@@ -165,6 +162,6 @@ class FullTextSearchService {
* @return Board[]
*/
private function getBoardsFromUser(string $userId): array {
return $this->boardMapper->findAllByUser($userId, null, null, null);
return $this->boardMapper->findAllByUser($userId);
}
}

View File

@@ -19,29 +19,25 @@ use OCP\Comments\IComment;
abstract class ABoardImportService {
/** @var string */
public static $name = '';
/** @var BoardImportService */
private $boardImportService;
/** @var bool */
protected $needValidateData = true;
private BoardImportService $boardImportService;
protected bool $needValidateData = true;
/** @var Stack[] */
protected $stacks = [];
protected array $stacks = [];
/** @var Label[] */
protected $labels = [];
protected array $labels = [];
/** @var Card[] */
protected $cards = [];
protected array $cards = [];
/** @var Acl[] */
protected $acls = [];
protected array $acls = [];
/** @var IComment[][] */
protected $comments = [];
protected array $comments = [];
/** @var Assignment[] */
protected $assignments = [];
/** @var string[][] */
protected $labelCardAssignments = [];
protected array $assignments = [];
/** @var int[][] */
protected array $labelCardAssignments = [];
/**
* Configure import service
*
* @return void
*/
abstract public function bootstrap(): void;
@@ -68,10 +64,13 @@ abstract class ABoardImportService {
abstract public function getCardAssignments(): array;
/**
* @return array<int, array<int, int>>
*/
abstract public function getCardLabelAssignment(): array;
/**
* @return IComment[][]|array
* @return array<int, array<string, IComment>>
*/
abstract public function getComments(): array;
@@ -98,16 +97,16 @@ abstract class ABoardImportService {
$this->acls[$code] = $acl;
}
public function updateComment(string $cardId, string $commentId, IComment $comment): void {
public function updateComment(int $cardId, string $commentId, IComment $comment): void {
$this->comments[$cardId][$commentId] = $comment;
}
public function updateCardAssignment(string $cardId, string $assignmentId, Entity $assignment): void {
public function updateCardAssignment(int $cardId, int $assignmentId, Entity $assignment): void {
$this->assignments[$cardId][$assignmentId] = $assignment;
}
public function updateCardLabelsAssignment(string $cardId, string $assignmentId, string $assignment): void {
$this->labelCardAssignments[$cardId][$assignmentId] = $assignment;
public function updateCardLabelsAssignment(int $cardId, int $assignmentId, int $labelId): void {
$this->labelCardAssignments[$cardId][$assignmentId] = $labelId;
}
public function setImportService(BoardImportService $service): void {

View File

@@ -209,14 +209,15 @@ class BoardImportService {
public function importBoard(): void {
$board = $this->getImportSystem()->getBoard();
if ($board === null) {
throw new \LogicException('Import board not found');
}
if (!$this->userManager->userExists($board->getOwner())) {
throw new \Exception('Target owner ' . $board->getOwner() . ' not found. Please provide a mapping through the import config.');
}
if ($board) {
$this->boardMapper->insert($board);
$this->board = $board;
}
$this->boardMapper->insert($board);
$this->board = $board;
}
public function getBoard(bool $reset = false): Board {
@@ -292,12 +293,7 @@ class BoardImportService {
}
}
/**
* @param mixed $cardId
* @param mixed $labelId
* @return self
*/
public function assignCardToLabel($cardId, $labelId): self {
public function assignCardToLabel(int $cardId, int $labelId): self {
$this->cardMapper->assignLabel(
$cardId,
$labelId
@@ -307,14 +303,14 @@ class BoardImportService {
public function assignCardsToLabels(): void {
$data = $this->getImportSystem()->getCardLabelAssignment();
foreach ($data as $cardId => $assignemnt) {
foreach ($assignemnt as $assignmentId => $labelId) {
foreach ($data as $cardId => $assignment) {
foreach ($assignment as $assignmentId => $labelId) {
try {
$this->assignCardToLabel(
$cardId,
(int)$cardId,
$labelId
);
$this->getImportSystem()->updateCardLabelsAssignment($cardId, $assignmentId, $labelId);
$this->getImportSystem()->updateCardLabelsAssignment((int)$cardId, (int)$assignmentId, $labelId);
} catch (\Exception $e) {
$this->addError('Failed to assign label ' . $labelId . ' to ' . $cardId, $e);
}
@@ -326,20 +322,20 @@ class BoardImportService {
$allComments = $this->getImportSystem()->getComments();
foreach ($allComments as $cardId => $comments) {
foreach ($comments as $commentId => $comment) {
$this->insertComment($cardId, $comment);
$this->getImportSystem()->updateComment($cardId, $commentId, $comment);
$this->insertComment((int)$cardId, $comment);
$this->getImportSystem()->updateComment((int)$cardId, $commentId, $comment);
}
}
}
private function insertComment(string $cardId, IComment $comment): void {
$comment->setObject('deckCard', $cardId);
private function insertComment(int $cardId, IComment $comment): void {
$comment->setObject('deckCard', (string)$cardId);
$comment->setVerb('comment');
// Check if parent is a comment on the same card
if ($comment->getParentId() !== '0') {
try {
$parent = $this->commentsManager->get($comment->getParentId());
if ($parent->getObjectType() !== Application::COMMENT_ENTITY_TYPE || $parent->getObjectId() !== $cardId) {
if ($parent->getObjectType() !== Application::COMMENT_ENTITY_TYPE || (int)$parent->getObjectId() !== $cardId) {
throw new CommentNotFoundException();
}
} catch (CommentNotFoundException $e) {
@@ -362,7 +358,7 @@ class BoardImportService {
foreach ($assignments as $assignment) {
try {
$assignment = $this->assignmentMapper->insert($assignment);
$this->getImportSystem()->updateCardAssignment($cardId, (string)$assignment->getId(), $assignment);
$this->getImportSystem()->updateCardAssignment((int)$cardId, $assignment->getId(), $assignment);
$this->addOutput('Assignment ' . $assignment->getParticipant() . ' added');
} catch (NotFoundException $e) {
$this->addError('No origin or mapping found for card "' . $cardId . '" and ' . $assignment->getTypeString() . ' assignment "' . $assignment->getParticipant(), $e);

View File

@@ -16,6 +16,7 @@ use OCA\Deck\Db\Card;
use OCA\Deck\Db\Label;
use OCA\Deck\Db\Stack;
use OCA\Deck\Service\Importer\ABoardImportService;
use OCP\Comments\IComment;
use OCP\IUser;
use OCP\IUserManager;
@@ -118,6 +119,7 @@ class DeckJsonService extends ABoardImportService {
$comments[$this->cards[$sourceCard->id]->getId()][$commentOriginal->id] = $comment;
}
}
/** @var array<int, array<string, IComment>> */
return $comments;
}

View File

@@ -17,7 +17,7 @@ use Psr\Log\LoggerInterface;
class TrelloApiService extends TrelloJsonService {
/** @var string */
public static $name = 'Trello API';
protected $needValidateData = false;
protected bool $needValidateData = false;
/** @var IClient */
private $httpClient;
/** @var LoggerInterface */
@@ -176,7 +176,7 @@ class TrelloApiService extends TrelloJsonService {
if (empty($queryString['limit'])) {
return [];
}
if (count($data) < $queryString['limit']) {
if ((count($data) < $queryString['limit']) || (count($data) === 0)) {
return [];
}
$queryString['before'] = end($data)->id;

View File

@@ -159,6 +159,7 @@ class TrelloJsonService extends ABoardImportService {
$comments[$cardId][$commentId] = $comment;
}
}
/** @var array<int, array<string, IComment>> */
return $comments;
}

View File

@@ -43,33 +43,24 @@ class LabelService {
}
/**
* @param $labelId
* @return \OCP\AppFramework\Db\Entity
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function find($labelId) {
if (is_numeric($labelId) === false) {
throw new BadRequestException('label id must be a number');
}
public function find(int $labelId): Label {
$this->permissionService->checkPermission($this->labelMapper, $labelId, Acl::PERMISSION_READ);
return $this->labelMapper->find($labelId);
}
/**
* @param $title
* @param $color
* @param $boardId
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function create($title, $color, $boardId) {
public function create(string $title, string $color, int $boardId): Label {
$this->labelServiceValidator->check(compact('title', 'color', 'boardId'));
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_MANAGE);
@@ -106,15 +97,13 @@ class LabelService {
}
/**
* @param $id
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function delete($id) {
public function delete(int $id): Label {
$this->labelServiceValidator->check(compact('id'));
$this->permissionService->checkPermission($this->labelMapper, $id, Acl::PERMISSION_MANAGE);
@@ -127,17 +116,13 @@ class LabelService {
}
/**
* @param $id
* @param $title
* @param $color
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function update($id, $title, $color) {
public function update(int $id, string $title, string $color): Label {
$this->labelServiceValidator->check(compact('title', 'color', 'id'));
$this->permissionService->checkPermission($this->labelMapper, $id, Acl::PERMISSION_MANAGE);

View File

@@ -9,53 +9,28 @@ declare(strict_types=1);
namespace OCA\Deck\Service;
use OCA\Deck\Db\AssignmentMapper;
use OCA\Deck\Db\Board;
use OCA\Deck\Db\BoardMapper;
use OCA\Deck\Db\CardMapper;
use OCA\Deck\Db\LabelMapper;
use OCA\Deck\Model\CardDetails;
use OCP\Comments\ICommentsManager;
use OCP\IUserManager;
class OverviewService {
private CardService $cardService;
private BoardMapper $boardMapper;
private LabelMapper $labelMapper;
private CardMapper $cardMapper;
private AssignmentMapper $assignedUsersMapper;
private IUserManager $userManager;
private ICommentsManager $commentsManager;
private AttachmentService $attachmentService;
public function __construct(
CardService $cardService,
BoardMapper $boardMapper,
LabelMapper $labelMapper,
CardMapper $cardMapper,
AssignmentMapper $assignedUsersMapper,
IUserManager $userManager,
ICommentsManager $commentsManager,
AttachmentService $attachmentService,
private readonly CardService $cardService,
private readonly BoardMapper $boardMapper,
private readonly CardMapper $cardMapper,
) {
$this->cardService = $cardService;
$this->boardMapper = $boardMapper;
$this->labelMapper = $labelMapper;
$this->cardMapper = $cardMapper;
$this->assignedUsersMapper = $assignedUsersMapper;
$this->userManager = $userManager;
$this->commentsManager = $commentsManager;
$this->attachmentService = $attachmentService;
}
public function findUpcomingCards(string $userId): array {
$userBoards = $this->boardMapper->findAllForUser($userId);
$boardOwnerIds = array_filter(array_map(function (Board $board) {
return count($board->getAcl()) === 0 ? $board->getId() : null;
return count($board->getAcl() ?? []) === 0 ? $board->getId() : null;
}, $userBoards));
$boardSharedIds = array_filter(array_map(function (Board $board) {
return count($board->getAcl()) > 0 ? $board->getId() : null;
return count($board->getAcl() ?? []) > 0 ? $board->getId() : null;
}, $userBoards));
$foundCards = array_merge(

View File

@@ -29,6 +29,7 @@ class PermissionService {
private array $users = [];
private CappedMemoryCache $boardCache;
/** @var CappedMemoryCache<array<Acl::PERMISSION_*, bool>> */
private CappedMemoryCache $permissionCache;
public function __construct(
@@ -49,15 +50,16 @@ class PermissionService {
/**
* Get current user permissions for a board by id
*
* @return bool|array
* @return array<Acl::PERMISSION_*, bool>
*/
public function getPermissions(int $boardId, ?string $userId = null) {
public function getPermissions(int $boardId, ?string $userId = null): array {
if ($userId === null) {
$userId = $this->userId;
}
$cacheKey = $boardId . '-' . $userId;
if ($cached = $this->permissionCache->get($cacheKey)) {
/** @var array<Acl::PERMISSION_*, bool> $cached */
return $cached;
}

View File

@@ -77,7 +77,7 @@ class StackService {
/** @param Stack[] $stacks */
private function enrichStacksWithCards(array $stacks, $since = -1): void {
$cardsByStackId = $this->cardMapper->findAllForStacks(array_map(fn (Stack $stack) => $stack->getId(), $stacks), null, null, $since);
$cardsByStackId = $this->cardMapper->findAllForStacks(array_map(fn (Stack $stack) => $stack->getId(), $stacks), null, 0, $since);
foreach ($cardsByStackId as $stackId => $cards) {
if (!$cards) {
@@ -95,18 +95,11 @@ class StackService {
}
/**
* @param $stackId
*
* @return \OCP\AppFramework\Db\Entity
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function find($stackId) {
if (is_numeric($stackId) === false) {
throw new BadRequestException('stack id must be a number');
}
public function find(int $stackId): Stack {
$this->permissionService->checkPermission($this->stackMapper, $stackId, Acl::PERMISSION_READ);
$stack = $this->stackMapper->find($stackId);
@@ -127,17 +120,11 @@ class StackService {
}
/**
* @param mixed $boardId
*
* @return Stack[]
* @throws \OCA\Deck\NoPermissionException
* @throws BadRequestException
*/
public function findAll($boardId, $since = -1) {
if (is_numeric($boardId) === false) {
throw new BadRequestException('boardId must be a number');
}
public function findAll(int $boardId, int $since = -1): array {
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_READ);
$stacks = $this->stackMapper->findAll($boardId);
$this->enrichStacksWithCards($stacks, $since);
@@ -145,7 +132,11 @@ class StackService {
return $stacks;
}
public function findCalendarEntries($boardId) {
/**
* @return Stack[]
* @throws \OCP\DB\Exception
*/
public function findCalendarEntries(int $boardId): array {
try {
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_READ);
} catch (NoPermissionException $e) {
@@ -155,7 +146,11 @@ class StackService {
return $this->stackMapper->findAll($boardId);
}
public function fetchDeleted($boardId) {
/**
* @return Stack[]
* @throws \OCP\DB\Exception
*/
public function fetchDeleted(int $boardId): array {
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ);
$stacks = $this->stackMapper->findDeleted($boardId);
$this->enrichStacksWithCards($stacks);
@@ -164,17 +159,11 @@ class StackService {
}
/**
* @param $boardId
*
* @return array
* @return Stack[]
* @throws \OCA\Deck\NoPermissionException
* @throws BadRequestException
*/
public function findAllArchived($boardId) {
if (is_numeric($boardId) === false) {
throw new BadRequestException('board id must be a number');
}
public function findAllArchived(int $boardId): array {
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_READ);
$stacks = $this->stackMapper->findAll($boardId);
$labels = $this->labelMapper->getAssignedLabelsForBoard($boardId);
@@ -189,22 +178,18 @@ class StackService {
$stacks[$stackIndex]->setCards($cards);
}
/** @var Stack[] $stacks */
return $stacks;
}
/**
* @param $title
* @param $boardId
* @param integer $order
*
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function create($title, $boardId, $order) {
public function create(string $title, int $boardId, int $order): Stack {
$this->stackServiceValidator->check(compact('title', 'boardId', 'order'));
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_MANAGE);
@@ -226,19 +211,13 @@ class StackService {
}
/**
* @param $id
*
* @return \OCP\AppFramework\Db\Entity
* @return Stack The deleted stack.
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function delete($id) {
if (is_numeric($id) === false) {
throw new BadRequestException('stack id must be a number');
}
public function delete(int $id): Stack {
$this->permissionService->checkPermission($this->stackMapper, $id, Acl::PERMISSION_MANAGE);
$stack = $this->stackMapper->find($id);
@@ -256,20 +235,13 @@ class StackService {
}
/**
* @param $id
* @param $title
* @param $boardId
* @param $order
* @param $deletedAt
*
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function update($id, $title, $boardId, $order, $deletedAt) {
public function update(int $id, string $title, int $boardId, int $order, ?int $deletedAt): Stack {
$this->stackServiceValidator->check(compact('id', 'title', 'boardId', 'order'));
$this->permissionService->checkPermission($this->stackMapper, $id, Acl::PERMISSION_MANAGE);
@@ -297,16 +269,13 @@ class StackService {
}
/**
* @param $id
* @param $order
*
* @return array
* @return array<int, Stack> The stacks in the correct order.
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function reorder($id, $order) {
public function reorder(int $id, int $order): array {
$this->stackServiceValidator->check(compact('id', 'order'));
$this->permissionService->checkPermission($this->stackMapper, $id, Acl::PERMISSION_MANAGE);
@@ -327,7 +296,7 @@ class StackService {
if ($stack->id !== $id) {
$stack->setOrder($i++);
}
$this->stackMapper->update($stack);
$stack = $this->stackMapper->update($stack);
$result[$stack->getOrder()] = $stack;
}
$this->changeHelper->boardChanged($stackToSort->getBoardId());

View File

@@ -13,7 +13,6 @@ namespace OCA\Deck\Sharing;
use OC\Files\Cache\Cache;
use OCA\Deck\Cache\AttachmentCacheHelper;
use OCA\Deck\Db\Acl;
use OCA\Deck\Db\Board;
use OCA\Deck\Db\BoardMapper;
use OCA\Deck\Db\CardMapper;
use OCA\Deck\Db\User;
@@ -99,9 +98,13 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
* @inheritDoc
*/
public function create(IShare $share) {
$cardId = $share->getSharedWith();
$cardId = (int)$share->getSharedWith();
$boardId = $this->cardMapper->findBoardId($cardId);
$valid = $boardId !== null;
if (!$valid) {
throw new GenericShareException('Card not found', $this->l->t('Card not found'), 404);
}
try {
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_EDIT);
} catch (NoPermissionException $e) {
@@ -147,7 +150,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
);
$data = $this->getRawShare($shareId);
$this->attachmentCacheHelper->clearAttachmentCount((int)$cardId);
$this->attachmentCacheHelper->clearAttachmentCount($cardId);
return $this->createShareObject($data);
}
@@ -213,7 +216,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
->from('share')
->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
$cursor = $qb->execute();
$cursor = $qb->executeQuery();
$data = $cursor->fetch();
$cursor->closeCursor();
@@ -297,7 +300,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
->execute();
->executeStatement();
/*
* Update all user defined group shares
@@ -310,7 +313,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
->execute();
->executeStatement();
/*
* Now update the permissions for all children that have not set it to 0
@@ -320,7 +323,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0)))
->set('permissions', $qb->createNamedParameter($share->getPermissions()))
->execute();
->executeStatement();
return $share;
}
@@ -335,7 +338,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
$qb->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())));
$qb->execute();
$qb->executeStatement();
$this->attachmentCacheHelper->clearAttachmentCount((int)$share->getSharedWith());
}
@@ -355,7 +358,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
))
->execute();
->executeQuery();
$data = $stmt->fetch();
$stmt->closeCursor();
@@ -376,14 +379,14 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
'file_target' => $qb->createNamedParameter($share->getTarget()),
'permissions' => $qb->createNamedParameter(0),
'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
])->execute();
])->executeStatement();
} elseif ($data['permissions'] !== 0) {
// Already a userroom share. Update it.
$qb = $this->dbConnection->getQueryBuilder();
$qb->update('share')
->set('permissions', $qb->createNamedParameter(0))
->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
->execute();
->executeStatement();
}
}
@@ -397,7 +400,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
->where(
$qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))
);
$cursor = $qb->execute();
$cursor = $qb->executeQuery();
$data = $cursor->fetch();
$cursor->closeCursor();
@@ -414,7 +417,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
$qb->expr()->eq('share_with', $qb->createNamedParameter($recipient))
);
$qb->execute();
$qb->executeStatement();
return $this->getShareById((int)$share->getId(), $recipient);
}
@@ -435,7 +438,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
))
->setMaxResults(1)
->execute();
->executeQuery();
$data = $stmt->fetch();
$stmt->closeCursor();
@@ -509,7 +512,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
$qb->orderBy('s.id');
$cursor = $qb->execute();
$cursor = $qb->executeQuery();
$shares = [];
while ($data = $cursor->fetch()) {
$shares[$data['fileid']][] = $this->createShareObject($data);
@@ -554,7 +557,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
$qb->setFirstResult($offset);
$qb->orderBy('id');
$cursor = $qb->execute();
$cursor = $qb->executeQuery();
$shares = [];
while ($data = $cursor->fetch()) {
$shares[] = $this->createShareObject($data);
@@ -582,7 +585,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
->where($qb->expr()->eq('s.id', $qb->createNamedParameter($id)))
->andWhere($qb->expr()->eq('s.share_type', $qb->createNamedParameter(IShare::TYPE_DECK)));
$cursor = $qb->execute();
$cursor = $qb->executeQuery();
$data = $cursor->fetch();
$cursor->closeCursor();
@@ -647,7 +650,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
));
$stmt = $query->execute();
$stmt = $query->executeQuery();
while ($data = $stmt->fetch()) {
$this->applyBoardPermission($shareMap[$data['parent']], (int)$data['permissions'], $userId);
@@ -677,7 +680,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
->from('share')
->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_DECK)))
->execute();
->executeQuery();
$shares = [];
while ($data = $cursor->fetch()) {
@@ -749,7 +752,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
$qb->andWhere($qb->expr()->eq('dc.deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
$cursor = $qb->execute();
$cursor = $qb->executeQuery();
while ($data = $cursor->fetch()) {
if (!$this->isAccessibleResult($data)) {
continue;
@@ -842,6 +845,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
/**
* @inheritDoc
* @psalm-suppress InvalidReturnType Not returning anything
*/
public function getShareByToken($token) {
throw new ShareNotFound();
@@ -851,7 +855,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
->from('share')
->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_ROOM)))
->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
->execute();
->executeQuery();
$data = $cursor->fetch();
@@ -1015,7 +1019,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_DECK)))
->orderBy('id');
$cursor = $qb->execute();
$cursor = $qb->executeQuery();
while ($data = $cursor->fetch()) {
$children[] = $this->createShareObject($data);
}
@@ -1038,7 +1042,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
)
);
$cursor = $qb->execute();
$cursor = $qb->executeQuery();
while ($data = $cursor->fetch()) {
$share = $this->createShareObject($data);
@@ -1055,7 +1059,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
->where($qb->expr()->eq('s.share_type', $qb->createNamedParameter(IShare::TYPE_DECK)))
->andWhere($qb->expr()->notIn('s.share_with', $qb->createNamedParameter($allCardIds, IQueryBuilder::PARAM_STR_ARRAY)));
$cursor = $qb->execute();
$cursor = $qb->executeQuery();
$shares = [];
while ($data = $cursor->fetch()) {
$shares[] = $this->createShareObject($data);