diff --git a/appinfo/routes.php b/appinfo/routes.php index 4b273c464..5f8a0cdc4 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -80,59 +80,66 @@ return [ ['name' => 'label#delete', 'url' => '/labels/{labelId}', 'verb' => 'DELETE'], // api - ['name' => 'board_api#index', 'url' => '/api/v1.0/boards', 'verb' => 'GET'], - ['name' => 'board_api#get', 'url' => '/api/v1.0/boards/{boardId}', 'verb' => 'GET'], - ['name' => 'board_api#create', 'url' => '/api/v1.0/boards', 'verb' => 'POST'], - ['name' => 'board_api#delete', 'url' => '/api/v1.0/boards/{boardId}', 'verb' => 'DELETE'], - ['name' => 'board_api#update', 'url' => '/api/v1.0/boards/{boardId}', 'verb' => 'PUT'], - ['name' => 'board_api#undo_delete', 'url' => '/api/v1.0/boards/{boardId}/undo_delete', 'verb' => 'POST'], - ['name' => 'board_api#addAcl', 'url' => '/api/v1.0/boards/{boardId}/acl', 'verb' => 'POST'], - ['name' => 'board_api#deleteAcl', 'url' => '/api/v1.0/boards/{boardId}/acl/{aclId}', 'verb' => 'DELETE'], - ['name' => 'board_api#updateAcl', 'url' => '/api/v1.0/boards/{boardId}/acl/{aclId}', 'verb' => 'PUT'], + ['name' => 'board_api#index', 'url' => '/api/v{apiVersion}/boards', 'verb' => 'GET'], + ['name' => 'board_api#get', 'url' => '/api/v{apiVersion}/boards/{boardId}', 'verb' => 'GET'], + ['name' => 'board_api#create', 'url' => '/api/v{apiVersion}/boards', 'verb' => 'POST'], + ['name' => 'board_api#delete', 'url' => '/api/v{apiVersion}/boards/{boardId}', 'verb' => 'DELETE'], + ['name' => 'board_api#update', 'url' => '/api/v{apiVersion}/boards/{boardId}', 'verb' => 'PUT'], + ['name' => 'board_api#undo_delete', 'url' => '/api/v{apiVersion}/boards/{boardId}/undo_delete', 'verb' => 'POST'], + ['name' => 'board_api#addAcl', 'url' => '/api/v{apiVersion}/boards/{boardId}/acl', 'verb' => 'POST'], + ['name' => 'board_api#deleteAcl', 'url' => '/api/v{apiVersion}/boards/{boardId}/acl/{aclId}', 'verb' => 'DELETE'], + ['name' => 'board_api#updateAcl', 'url' => '/api/v{apiVersion}/boards/{boardId}/acl/{aclId}', 'verb' => 'PUT'], - ['name' => 'stack_api#index', 'url' => '/api/v1.0/boards/{boardId}/stacks', 'verb' => 'GET'], - ['name' => 'stack_api#getArchived', 'url' => '/api/v1.0/boards/{boardId}/stacks/archived', 'verb' => 'GET'], - ['name' => 'stack_api#get', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}', 'verb' => 'GET'], - ['name' => 'stack_api#create', 'url' => '/api/v1.0/boards/{boardId}/stacks', 'verb' => 'POST'], - ['name' => 'stack_api#update', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}', 'verb' => 'PUT'], - ['name' => 'stack_api#delete', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}', 'verb' => 'DELETE'], + ['name' => 'stack_api#index', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks', 'verb' => 'GET'], + ['name' => 'stack_api#getArchived', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/archived', 'verb' => 'GET'], + ['name' => 'stack_api#get', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}', 'verb' => 'GET'], + ['name' => 'stack_api#create', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks', 'verb' => 'POST'], + ['name' => 'stack_api#update', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}', 'verb' => 'PUT'], + ['name' => 'stack_api#delete', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}', 'verb' => 'DELETE'], - ['name' => 'card_api#get', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}', 'verb' => 'GET'], - ['name' => 'card_api#create', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards', 'verb' => 'POST'], - ['name' => 'card_api#update', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}', 'verb' => 'PUT'], - ['name' => 'card_api#assignLabel', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/assignLabel', 'verb' => 'PUT'], - ['name' => 'card_api#removeLabel', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/removeLabel', 'verb' => 'PUT'], - ['name' => 'card_api#assignUser', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/assignUser', 'verb' => 'PUT'], - ['name' => 'card_api#unassignUser', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/unassignUser', 'verb' => 'PUT'], - ['name' => 'card_api#reorder', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/reorder', 'verb' => 'PUT'], - ['name' => 'card_api#delete', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}', 'verb' => 'DELETE'], + ['name' => 'card_api#get', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}/cards/{cardId}', 'verb' => 'GET'], + ['name' => 'card_api#create', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}/cards', 'verb' => 'POST'], + ['name' => 'card_api#update', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}/cards/{cardId}', 'verb' => 'PUT'], + ['name' => 'card_api#assignLabel', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}/cards/{cardId}/assignLabel', 'verb' => 'PUT'], + ['name' => 'card_api#removeLabel', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}/cards/{cardId}/removeLabel', 'verb' => 'PUT'], + ['name' => 'card_api#assignUser', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}/cards/{cardId}/assignUser', 'verb' => 'PUT'], + ['name' => 'card_api#unassignUser', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}/cards/{cardId}/unassignUser', 'verb' => 'PUT'], + ['name' => 'card_api#reorder', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}/cards/{cardId}/reorder', 'verb' => 'PUT'], + ['name' => 'card_api#delete', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}/cards/{cardId}', 'verb' => 'DELETE'], - ['name' => 'card_api#findAllWithDue', 'url' => '/api/v1.0/dashboard/due', 'verb' => 'GET'], + ['name' => 'card_api#findAllWithDue', 'url' => '/api/v{apiVersion}/dashboard/due', 'verb' => 'GET'], - ['name' => 'label_api#get', 'url' => '/api/v1.0/boards/{boardId}/labels/{labelId}', 'verb' => 'GET'], - ['name' => 'label_api#create', 'url' => '/api/v1.0/boards/{boardId}/labels', 'verb' => 'POST'], - ['name' => 'label_api#update', 'url' => '/api/v1.0/boards/{boardId}/labels/{labelId}', 'verb' => 'PUT'], - ['name' => 'label_api#delete', 'url' => '/api/v1.0/boards/{boardId}/labels/{labelId}', 'verb' => 'DELETE'], + ['name' => 'label_api#get', 'url' => '/api/v{apiVersion}/boards/{boardId}/labels/{labelId}', 'verb' => 'GET'], + ['name' => 'label_api#create', 'url' => '/api/v{apiVersion}/boards/{boardId}/labels', 'verb' => 'POST'], + ['name' => 'label_api#update', 'url' => '/api/v{apiVersion}/boards/{boardId}/labels/{labelId}', 'verb' => 'PUT'], + ['name' => 'label_api#delete', 'url' => '/api/v{apiVersion}/boards/{boardId}/labels/{labelId}', 'verb' => 'DELETE'], - ['name' => 'attachment_api#getAll', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments', 'verb' => 'GET'], - ['name' => 'attachment_api#display', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId}', 'verb' => 'GET'], - ['name' => 'attachment_api#create', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments', 'verb' => 'POST'], - ['name' => 'attachment_api#update', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId}', 'verb' => 'PUT'], - ['name' => 'attachment_api#delete', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId}', 'verb' => 'DELETE'], - ['name' => 'attachment_api#restore', 'url' => '/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId}/restore', 'verb' => 'PUT'], + ['name' => 'attachment_api#getAll', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments', 'verb' => 'GET', 'requirements' => ['apiVersion' => '1.0']], + ['name' => 'attachment_api#display', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId}', 'verb' => 'GET', 'requirements' => ['apiVersion' => '1.0']], + ['name' => 'attachment_api#create', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments', 'verb' => 'POST', 'requirements' => ['apiVersion' => '1.0']], + ['name' => 'attachment_api#update', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId}', 'verb' => 'PUT', 'requirements' => ['apiVersion' => '1.0']], + ['name' => 'attachment_api#delete', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId}', 'verb' => 'DELETE', 'requirements' => ['apiVersion' => '1.0']], + ['name' => 'attachment_api#restore', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{attachmentId}/restore', 'verb' => 'PUT', 'requirements' => ['apiVersion' => '1.0']], - ['name' => 'board_api#preflighted_cors', 'url' => '/api/v1.0/{path}','verb' => 'OPTIONS', 'requirements' => ['path' => '.+']], + ['name' => 'attachment_api_v11#getAll', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments', 'verb' => 'GET', 'requirements' => ['apiVersion' => '1.1']], + ['name' => 'attachment_api_v11#display', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{type}/{attachmentId}', 'verb' => 'GET', 'requirements' => ['apiVersion' => '1.1']], + ['name' => 'attachment_api_v11#create', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments', 'verb' => 'POST', 'requirements' => ['apiVersion' => '1.1']], + ['name' => 'attachment_api_v11#update', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{type}/{attachmentId}', 'verb' => 'PUT', 'requirements' => ['apiVersion' => '1.1']], + ['name' => 'attachment_api_v11#delete', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{type}/{attachmentId}', 'verb' => 'DELETE', 'requirements' => ['apiVersion' => '1.1']], + ['name' => 'attachment_api_v11#restore', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/{stackId}/cards/{cardId}/attachments/{type}/{attachmentId}/restore', 'verb' => 'PUT', 'requirements' => ['apiVersion' => '1.1']], + + ['name' => 'board_api#preflighted_cors', 'url' => '/api/v{apiVersion}/{path}','verb' => 'OPTIONS', 'requirements' => ['path' => '.+']], ], 'ocs' => [ - ['name' => 'Config#get', 'url' => '/api/v1.0/config', 'verb' => 'GET'], - ['name' => 'Config#setValue', 'url' => '/api/v1.0/config/{key}', 'verb' => 'POST'], + ['name' => 'Config#get', 'url' => '/api/v{apiVersion}/config', 'verb' => 'GET'], + ['name' => 'Config#setValue', 'url' => '/api/v{apiVersion}/config/{key}', 'verb' => 'POST'], - ['name' => 'comments_api#list', 'url' => '/api/v1.0/cards/{cardId}/comments', 'verb' => 'GET'], - ['name' => 'comments_api#create', 'url' => '/api/v1.0/cards/{cardId}/comments', 'verb' => 'POST'], - ['name' => 'comments_api#update', 'url' => '/api/v1.0/cards/{cardId}/comments/{commentId}', 'verb' => 'PUT'], - ['name' => 'comments_api#delete', 'url' => '/api/v1.0/cards/{cardId}/comments/{commentId}', 'verb' => 'DELETE'], + ['name' => 'comments_api#list', 'url' => '/api/v{apiVersion}/cards/{cardId}/comments', 'verb' => 'GET'], + ['name' => 'comments_api#create', 'url' => '/api/v{apiVersion}/cards/{cardId}/comments', 'verb' => 'POST'], + ['name' => 'comments_api#update', 'url' => '/api/v{apiVersion}/cards/{cardId}/comments/{commentId}', 'verb' => 'PUT'], + ['name' => 'comments_api#delete', 'url' => '/api/v{apiVersion}/cards/{cardId}/comments/{commentId}', 'verb' => 'DELETE'], - ['name' => 'overview_api#upcomingCards', 'url' => '/api/v1.0/overview/upcoming', 'verb' => 'GET'], + ['name' => 'overview_api#upcomingCards', 'url' => '/api/v{apiVersion}/overview/upcoming', 'verb' => 'GET'], ] ]; diff --git a/lib/Activity/ActivityManager.php b/lib/Activity/ActivityManager.php index 9d282fdd6..11d03c318 100644 --- a/lib/Activity/ActivityManager.php +++ b/lib/Activity/ActivityManager.php @@ -380,7 +380,7 @@ class ActivityManager { case self::SUBJECT_ATTACHMENT_UPDATE: case self::SUBJECT_ATTACHMENT_DELETE: case self::SUBJECT_ATTACHMENT_RESTORE: - $subjectParams = $this->findDetailsForAttachment($entity->getId()); + $subjectParams = $this->findDetailsForAttachment($entity); break; case self::SUBJECT_BOARD_SHARE: case self::SUBJECT_BOARD_UNSHARE: @@ -527,8 +527,7 @@ class ActivityManager { ]; } - private function findDetailsForAttachment($attachmentId) { - $attachment = $this->attachmentMapper->find($attachmentId); + private function findDetailsForAttachment($attachment) { $data = $this->findDetailsForCard($attachment->getCardId()); return array_merge($data, ['attachment' => $attachment]); } diff --git a/lib/Capabilities.php b/lib/Capabilities.php index 6fb37eebc..491b5a7fc 100644 --- a/lib/Capabilities.php +++ b/lib/Capabilities.php @@ -50,7 +50,11 @@ class Capabilities implements ICapability { return [ 'deck' => [ 'version' => $this->appManager->getAppVersion('deck'), - 'canCreateBoards' => $this->permissionService->canCreate() + 'canCreateBoards' => $this->permissionService->canCreate(), + 'apiVersions' => [ + '1.0', + '1.1' + ] ] ]; } diff --git a/lib/Controller/AttachmentApiController.php b/lib/Controller/AttachmentApiController.php index 54741613b..debf93f6f 100644 --- a/lib/Controller/AttachmentApiController.php +++ b/lib/Controller/AttachmentApiController.php @@ -42,8 +42,13 @@ class AttachmentApiController extends ApiController { * @NoCSRFRequired * */ - public function getAll() { + public function getAll($apiVersion) { $attachment = $this->attachmentService->findAll($this->request->getParam('cardId'), true); + if ($apiVersion === '1.0') { + $attachment = array_filter($attachment, function ($attachment) { + return $attachment->getType() === 'deck_file'; + }); + } return new DataResponse($attachment, HTTP::STATUS_OK); } @@ -53,8 +58,8 @@ class AttachmentApiController extends ApiController { * @NoCSRFRequired * */ - public function display() { - return $this->attachmentService->display($this->request->getParam('attachmentId')); + public function display($cardId, $attachmentId, $type = 'deck_file') { + return $this->attachmentService->display($cardId, $attachmentId, $type); } /** @@ -63,8 +68,8 @@ class AttachmentApiController extends ApiController { * @NoCSRFRequired * */ - public function create($type, $data) { - $attachment = $this->attachmentService->create($this->request->getParam('cardId'), $type, $data); + public function create($cardId, $type, $data) { + $attachment = $this->attachmentService->create($cardId, $type, $data); return new DataResponse($attachment, HTTP::STATUS_OK); } @@ -74,8 +79,8 @@ class AttachmentApiController extends ApiController { * @NoCSRFRequired * */ - public function update($data) { - $attachment = $this->attachmentService->update($this->request->getParam('attachmentId'), $data); + public function update($cardId, $attachmentId, $data, $type = 'deck_file') { + $attachment = $this->attachmentService->update($cardId, $attachmentId, $data, $type); return new DataResponse($attachment, HTTP::STATUS_OK); } @@ -85,8 +90,8 @@ class AttachmentApiController extends ApiController { * @NoCSRFRequired * */ - public function delete() { - $attachment = $this->attachmentService->delete($this->request->getParam('attachmentId')); + public function delete($cardId, $attachmentId, $type = 'deck_file') { + $attachment = $this->attachmentService->delete($cardId, $attachmentId, $type); return new DataResponse($attachment, HTTP::STATUS_OK); } @@ -96,8 +101,8 @@ class AttachmentApiController extends ApiController { * @NoCSRFRequired * */ - public function restore() { - $attachment = $this->attachmentService->restore($this->request->getParam('attachmentId')); + public function restore($cardId, $attachmentId, $type = 'deck_file') { + $attachment = $this->attachmentService->restore($cardId, $attachmentId, $type); return new DataResponse($attachment, HTTP::STATUS_OK); } } diff --git a/lib/Controller/AttachmentApiV11Controller.php b/lib/Controller/AttachmentApiV11Controller.php new file mode 100644 index 000000000..7b593bccc --- /dev/null +++ b/lib/Controller/AttachmentApiV11Controller.php @@ -0,0 +1,26 @@ + + * + * @author Ryan Fletcher + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +namespace OCA\Deck\Controller; + +class AttachmentApiV11Controller extends AttachmentApiController { +} diff --git a/lib/Controller/AttachmentController.php b/lib/Controller/AttachmentController.php index 18cab5761..38334199a 100644 --- a/lib/Controller/AttachmentController.php +++ b/lib/Controller/AttachmentController.php @@ -52,8 +52,13 @@ class AttachmentController extends Controller { * @return \OCP\AppFramework\Http\Response * @throws \OCA\Deck\NotFoundException */ - public function display($attachmentId) { - return $this->attachmentService->display($attachmentId); + public function display($cardId, $attachmentId) { + if (strpos($attachmentId, ':') === false) { + $type = 'deck_file'; + } else { + [$type, $attachmentId] = explode(':', $attachmentId); + } + return $this->attachmentService->display($cardId, $attachmentId, $type); } /** @@ -70,21 +75,36 @@ class AttachmentController extends Controller { /** * @NoAdminRequired */ - public function update($attachmentId) { - return $this->attachmentService->update($attachmentId, $this->request->getParam('data')); + public function update($cardId, $attachmentId) { + if (strpos($attachmentId, ':') === false) { + $type = 'deck_file'; + } else { + [$type, $attachmentId] = explode(':', $attachmentId); + } + return $this->attachmentService->update($cardId, $attachmentId, $this->request->getParam('data'), $type); } /** * @NoAdminRequired */ - public function delete($attachmentId) { - return $this->attachmentService->delete($attachmentId); + public function delete($cardId, $attachmentId) { + if (strpos($attachmentId, ':') === false) { + $type = 'deck_file'; + } else { + [$type, $attachmentId] = explode(':', $attachmentId); + } + return $this->attachmentService->delete($cardId, $attachmentId, $type); } /** * @NoAdminRequired */ - public function restore($attachmentId) { - return $this->attachmentService->restore($attachmentId); + public function restore($cardId, $attachmentId) { + if (strpos($attachmentId, ':') === false) { + $type = 'deck_file'; + } else { + [$type, $attachmentId] = explode(':', $attachmentId); + } + return $this->attachmentService->restore($cardId, $attachmentId, $type); } } diff --git a/lib/Service/AttachmentService.php b/lib/Service/AttachmentService.php index 67b3dd0d2..820b846cf 100644 --- a/lib/Service/AttachmentService.php +++ b/lib/Service/AttachmentService.php @@ -225,8 +225,14 @@ class AttachmentService { * @throws NoPermissionException * @throws NotFoundException */ - public function display($attachmentId) { - if (is_numeric($attachmentId)) { + public function display($cardId, $attachmentId, $type = 'deck_file') { + try { + $service = $this->getService($type); + } catch (InvalidAttachmentType $e) { + throw new NotFoundException(); + } + + if (!$service instanceof ICustomAttachmentService) { try { $attachment = $this->attachmentMapper->find($attachmentId); } catch (\Exception $e) { @@ -236,23 +242,18 @@ class AttachmentService { try { $service = $this->getService($attachment->getType()); - return $service->display($attachment); } catch (InvalidAttachmentType $e) { throw new NotFoundException(); } - } - - [$type, $attachmentId] = explode(':', $attachmentId); - - try { + } else { $attachment = new Attachment(); $attachment->setId($attachmentId); $attachment->setType($type); - $service = $this->getService($type); - return $service->display($attachment); - } catch (\Exception $e) { - throw new NotFoundException(); + $attachment->setCardId($cardId); + $this->permissionService->checkPermission($this->cardMapper, $attachment->getCardId(), Acl::PERMISSION_READ); } + + return $service->display($attachment); } /** @@ -264,17 +265,23 @@ class AttachmentService { * @throws BadRequestException * @throws NoPermissionException */ - public function update($attachmentId, $data) { - if (!is_numeric($attachmentId)) { - [$type, $attachmentId] = explode(':', $attachmentId); + public function update($cardId, $attachmentId, $data, $type = 'deck_file') { + try { + $service = $this->getService($type); + } catch (InvalidAttachmentType $e) { + throw new NotFoundException(); + } + if ($service instanceof ICustomAttachmentService) { try { $attachment = new Attachment(); $attachment->setId($attachmentId); $attachment->setType($type); $attachment->setData($data); - $service = $this->getService($type); - return $service->update($attachment); + $attachment->setCardId($cardId); + $service->update($attachment); + $this->changeHelper->cardChanged($attachment->getCardId()); + return $attachment; } catch (\Exception $e) { throw new NotFoundException(); } @@ -302,12 +309,8 @@ class AttachmentService { $attachment->setLastModified(time()); $this->attachmentMapper->update($attachment); // extend data so the frontend can use it properly after creating - try { - $service = $this->getService($attachment->getType()); - $service->extendData($attachment); - } catch (InvalidAttachmentType $e) { - // just store the data - } + $service->extendData($attachment); + $this->changeHelper->cardChanged($attachment->getCardId()); $this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $attachment, ActivityManager::SUBJECT_ATTACHMENT_UPDATE); return $attachment; @@ -324,19 +327,23 @@ class AttachmentService { * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException * @throws BadRequestException */ - public function delete($attachmentId) { - if (!is_numeric($attachmentId)) { - [$type, $attachmentId] = explode(':', $attachmentId); + public function delete($cardId, $attachmentId, $type = 'deck_file') { + try { + $service = $this->getService($type); + } catch (InvalidAttachmentType $e) { + throw new NotFoundException(); + } - try { - $attachment = new Attachment(); - $attachment->setId($attachmentId); - $attachment->setType($type); - $service = $this->getService($type); - return $service->delete($attachment); - } catch (\Exception $e) { - throw new NotFoundException(); - } + if ($service instanceof ICustomAttachmentService) { + $attachment = new Attachment(); + $attachment->setId($attachmentId); + $attachment->setType($type); + $attachment->setCardId($cardId); + $service->extendData($attachment); + $service->delete($attachment); + $this->changeHelper->cardChanged($attachment->getCardId()); + $this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $attachment, ActivityManager::SUBJECT_ATTACHMENT_DELETE); + return $attachment; } try { @@ -348,25 +355,21 @@ class AttachmentService { $this->permissionService->checkPermission($this->cardMapper, $attachment->getCardId(), Acl::PERMISSION_EDIT); $this->cache->clear('card-' . $attachment->getCardId()); - try { - $service = $this->getService($attachment->getType()); - if ($service->allowUndo()) { - $service->markAsDeleted($attachment); - $this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $attachment, ActivityManager::SUBJECT_ATTACHMENT_DELETE); - $this->changeHelper->cardChanged($attachment->getCardId()); - return $this->attachmentMapper->update($attachment); - } - $service->delete($attachment); - } catch (InvalidAttachmentType $e) { - // just delete without further action + if ($service->allowUndo()) { + $service->markAsDeleted($attachment); + $this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $attachment, ActivityManager::SUBJECT_ATTACHMENT_DELETE); + $this->changeHelper->cardChanged($attachment->getCardId()); + return $this->attachmentMapper->update($attachment); } + $service->delete($attachment); + $attachment = $this->attachmentMapper->delete($attachment); $this->changeHelper->cardChanged($attachment->getCardId()); $this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $attachment, ActivityManager::SUBJECT_ATTACHMENT_DELETE); return $attachment; } - public function restore($attachmentId) { + public function restore($cardId, $attachmentId, $type = 'deck_file') { if (is_numeric($attachmentId) === false) { throw new BadRequestException('attachment id must be a number'); } diff --git a/lib/Service/CardService.php b/lib/Service/CardService.php index fc3cbc005..e0961151a 100644 --- a/lib/Service/CardService.php +++ b/lib/Service/CardService.php @@ -150,6 +150,11 @@ class CardService { $card = $this->cardMapper->find($cardId); $assignedUsers = $this->assignedUsersMapper->findAll($card->getId()); $attachments = $this->attachmentService->findAll($cardId, true); + if (\OC::$server->getRequest()->getParam('apiVersion') === '1.0') { + $attachments = array_filter($attachments, function ($attachment) { + return $attachment->getType() === 'deck_file'; + }); + } $card->setAssignedUsers($assignedUsers); $card->setAttachments($attachments); $this->enrich($card); diff --git a/lib/Service/FilesAppService.php b/lib/Service/FilesAppService.php index 9e7a988a2..460a6b669 100644 --- a/lib/Service/FilesAppService.php +++ b/lib/Service/FilesAppService.php @@ -26,7 +26,6 @@ namespace OCA\Deck\Service; use OCA\Deck\Db\Attachment; use OCA\Deck\Sharing\DeckShareProvider; use OCA\Deck\StatusException; -use OCA\Deck\Exceptions\ConflictException; use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\FileDisplayResponse; use OCP\AppFramework\Http\StreamResponse; @@ -50,6 +49,7 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService { private $configService; private $l10n; private $preview; + private $permissionService; public function __construct( IRequest $request, @@ -59,6 +59,7 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService { ConfigService $configService, DeckShareProvider $shareProvider, IPreview $preview, + PermissionService $permissionService, string $userId = null ) { $this->request = $request; @@ -72,15 +73,15 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService { } public function listAttachments(int $cardId): array { - $userFolder = $this->rootFolder->getUserFolder($this->userId); $shares = $this->shareProvider->getSharedWithByType($cardId, IShare::TYPE_DECK, -1, 0); + $shares = array_filter($shares, function ($share) { + return $share->getPermissions() > 0; + }); return array_map(function (IShare $share) use ($cardId, $userFolder) { $file = $share->getNode(); - $nodes = $userFolder->getById($file->getId()); - $file = array_shift($nodes); $attachment = new Attachment(); $attachment->setType('file'); - $attachment->setId($file->getId()); + $attachment->setId($share->getId()); $attachment->setCardId($cardId); $attachment->setCreatedBy($share->getSharedBy()); $attachment->setData($file->getName()); @@ -95,8 +96,11 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService { /** @var IDBConnection $qb */ $db = \OC::$server->getDatabaseConnection(); $qb = $db->getQueryBuilder(); - $qb->select($qb->createFunction('count(s.id)')) + $qb->select('s.id', 'f.fileid', 'f.path') + ->selectAlias('st.id', 'storage_string_id') ->from('share', 's') + ->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid')) + ->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id')) ->andWhere($qb->expr()->eq('s.share_type', $qb->createNamedParameter(IShare::TYPE_DECK))) ->andWhere($qb->expr()->eq('s.share_with', $qb->createNamedParameter($cardId))) ->andWhere($qb->expr()->isNull('s.parent')) @@ -105,16 +109,21 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService { $qb->expr()->eq('s.item_type', $qb->createNamedParameter('folder')) )); + $count = 0; $cursor = $qb->execute(); - $count = $cursor->fetchColumn(0); + while ($data = $cursor->fetch()) { + if ($this->shareProvider->isAccessibleResult($data)) { + $count++; + } + } $cursor->closeCursor(); return $count; } public function extendData(Attachment $attachment) { $userFolder = $this->rootFolder->getUserFolder($this->userId); - $nodes = $userFolder->getById($attachment->getId()); - $file = array_shift($nodes); + $share = $this->shareProvider->getShareById($attachment->getId()); + $file = $share->getNode(); $attachment->setExtendedData([ 'path' => $userFolder->getRelativePath($file->getPath()), 'fileid' => $file->getId(), @@ -128,10 +137,13 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService { } public function display(Attachment $attachment) { - $userFolder = $this->rootFolder->getUserFolder($this->userId); - $nodes = $userFolder->getById($attachment->getId()); - $file = array_shift($nodes); - if ($file === null) { + try { + $share = $this->shareProvider->getShareById($attachment->getId()); + } catch (Share\Exceptions\ShareNotFound $e) { + throw new NotFoundException('File not found'); + } + $file = $share->getNode(); + if ($file === null || $share->getSharedWith() !== (string)$attachment->getCardId()) { throw new NotFoundException('File not found'); } if (method_exists($file, 'fopen')) { @@ -160,7 +172,6 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService { $userFolder = $this->rootFolder->getUserFolder($this->userId); $folder = $userFolder->get($this->configService->getAttachmentFolder()); - // FIXME: Add to docs that conflict handling is different here, no ConflictException will be thrown $fileName = $folder->getNonExistingName($fileName); $target = $folder->newFile($fileName); $content = fopen($file['tmp_name'], 'rb'); @@ -178,9 +189,10 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService { $share->setSharedWith((string)$attachment->getCardId()); $share->setPermissions(Constants::PERMISSION_READ); $share->setSharedBy($this->userId); - $this->shareManager->createShare($share); - $attachment->setId($target->getId()); + $share = $this->shareManager->createShare($share); + $attachment->setId($share->getId()); $attachment->setData($target->getName()); + return $attachment; } /** @@ -214,13 +226,29 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService { } public function update(Attachment $attachment) { - // TODO: Implement update() method. + $share = $this->shareProvider->getShareById($attachment->getId()); + $target = $share->getNode(); + $file = $this->getUploadedFile(); + $fileName = $file['name']; + $attachment->setData($fileName); + + $content = fopen($file['tmp_name'], 'rb'); + if ($content === false) { + throw new StatusException('Could not read file'); + } + $target->putContent($content); + if (is_resource($content)) { + fclose($content); + } + + $attachment->setLastModified(time()); + return $attachment; } public function delete(Attachment $attachment) { - $userFolder = $this->rootFolder->getUserFolder($this->userId); - $nodes = $userFolder->getById($attachment->getId()); - $file = array_shift($nodes); + $share = $this->shareProvider->getShareById($attachment->getId()); + $file = $share->getNode(); + $attachment->setData($file->getName()); if ($file === null) { throw new NotFoundException('File not found'); } @@ -230,14 +258,7 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService { return; } - // FIXME: only with manage permissions - $shares = $this->shareProvider->getSharedWithByType($attachment->getCardId(), IShare::TYPE_DECK, -1, 0); - foreach ($shares as $share) { - if ($share->getNode()->getId() === $attachment->getId()) { - $this->shareManager->deleteShare($share); - return; - } - } + $this->shareManager->deleteFromSelf($share, $this->userId); } public function allowUndo() { diff --git a/lib/Sharing/DeckShareProvider.php b/lib/Sharing/DeckShareProvider.php index bf2b63937..3b2aa460b 100644 --- a/lib/Sharing/DeckShareProvider.php +++ b/lib/Sharing/DeckShareProvider.php @@ -810,7 +810,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider { return $shares; } - private function isAccessibleResult(array $data): bool { + public function isAccessibleResult(array $data): bool { // exclude shares leading to deleted file entries if ($data['fileid'] === null || $data['path'] === null) { return false;