CollaborationResourcesController.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @copyright Copyright (c) 2018 Joas Schilling <coding@schilljs.com>
  5. *
  6. * @author Joas Schilling <coding@schilljs.com>
  7. * @author Julius Härtl <jus@bitgrid.net>
  8. * @author Roeland Jago Douma <roeland@famdouma.nl>
  9. * @author Kate Döen <kate.doeen@nextcloud.com>
  10. *
  11. * @license GNU AGPL version 3 or any later version
  12. *
  13. * This program is free software: you can redistribute it and/or modify
  14. * it under the terms of the GNU Affero General Public License as
  15. * published by the Free Software Foundation, either version 3 of the
  16. * License, or (at your option) any later version.
  17. *
  18. * This program is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU Affero General Public License for more details.
  22. *
  23. * You should have received a copy of the GNU Affero General Public License
  24. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  25. *
  26. */
  27. namespace OC\Core\Controller;
  28. use Exception;
  29. use OCA\Core\ResponseDefinitions;
  30. use OCP\AppFramework\Http;
  31. use OCP\AppFramework\Http\Attribute\ApiRoute;
  32. use OCP\AppFramework\Http\DataResponse;
  33. use OCP\AppFramework\OCSController;
  34. use OCP\Collaboration\Resources\CollectionException;
  35. use OCP\Collaboration\Resources\ICollection;
  36. use OCP\Collaboration\Resources\IManager;
  37. use OCP\Collaboration\Resources\IResource;
  38. use OCP\Collaboration\Resources\ResourceException;
  39. use OCP\IRequest;
  40. use OCP\IUserSession;
  41. use Psr\Log\LoggerInterface;
  42. /**
  43. * @psalm-import-type CoreResource from ResponseDefinitions
  44. * @psalm-import-type CoreCollection from ResponseDefinitions
  45. */
  46. class CollaborationResourcesController extends OCSController {
  47. public function __construct(
  48. string $appName,
  49. IRequest $request,
  50. private IManager $manager,
  51. private IUserSession $userSession,
  52. private LoggerInterface $logger,
  53. ) {
  54. parent::__construct($appName, $request);
  55. }
  56. /**
  57. * @param int $collectionId
  58. * @return ICollection
  59. * @throws CollectionException when the collection was not found for the user
  60. */
  61. protected function getCollection(int $collectionId): ICollection {
  62. $collection = $this->manager->getCollectionForUser($collectionId, $this->userSession->getUser());
  63. if (!$collection->canAccess($this->userSession->getUser())) {
  64. throw new CollectionException('Not found');
  65. }
  66. return $collection;
  67. }
  68. /**
  69. * @NoAdminRequired
  70. *
  71. * Get a collection
  72. *
  73. * @param int $collectionId ID of the collection
  74. * @return DataResponse<Http::STATUS_OK, CoreCollection, array{}>|DataResponse<Http::STATUS_NOT_FOUND|Http::STATUS_INTERNAL_SERVER_ERROR, array<empty>, array{}>
  75. *
  76. * 200: Collection returned
  77. * 404: Collection not found
  78. */
  79. #[ApiRoute(verb: 'GET', url: '/resources/collections/{collectionId}', root: '/collaboration')]
  80. public function listCollection(int $collectionId): DataResponse {
  81. try {
  82. $collection = $this->getCollection($collectionId);
  83. } catch (CollectionException $e) {
  84. return new DataResponse([], Http::STATUS_NOT_FOUND);
  85. }
  86. return $this->respondCollection($collection);
  87. }
  88. /**
  89. * @NoAdminRequired
  90. *
  91. * Search for collections
  92. *
  93. * @param string $filter Filter collections
  94. * @return DataResponse<Http::STATUS_OK, CoreCollection[], array{}>|DataResponse<Http::STATUS_NOT_FOUND, array<empty>, array{}>
  95. *
  96. * 200: Collections returned
  97. * 404: Collection not found
  98. */
  99. #[ApiRoute(verb: 'GET', url: '/resources/collections/search/{filter}', root: '/collaboration')]
  100. public function searchCollections(string $filter): DataResponse {
  101. try {
  102. $collections = $this->manager->searchCollections($this->userSession->getUser(), $filter);
  103. } catch (CollectionException $e) {
  104. return new DataResponse([], Http::STATUS_NOT_FOUND);
  105. }
  106. return new DataResponse($this->prepareCollections($collections));
  107. }
  108. /**
  109. * @NoAdminRequired
  110. *
  111. * Add a resource to a collection
  112. *
  113. * @param int $collectionId ID of the collection
  114. * @param string $resourceType Name of the resource
  115. * @param string $resourceId ID of the resource
  116. * @return DataResponse<Http::STATUS_OK, CoreCollection, array{}>|DataResponse<Http::STATUS_NOT_FOUND|Http::STATUS_INTERNAL_SERVER_ERROR, array<empty>, array{}>
  117. *
  118. * 200: Collection returned
  119. * 404: Collection not found or resource inaccessible
  120. */
  121. #[ApiRoute(verb: 'POST', url: '/resources/collections/{collectionId}', root: '/collaboration')]
  122. public function addResource(int $collectionId, string $resourceType, string $resourceId): DataResponse {
  123. try {
  124. $collection = $this->getCollection($collectionId);
  125. } catch (CollectionException $e) {
  126. return new DataResponse([], Http::STATUS_NOT_FOUND);
  127. }
  128. $resource = $this->manager->createResource($resourceType, $resourceId);
  129. if (!$resource->canAccess($this->userSession->getUser())) {
  130. return new DataResponse([], Http::STATUS_NOT_FOUND);
  131. }
  132. try {
  133. $collection->addResource($resource);
  134. } catch (ResourceException $e) {
  135. }
  136. return $this->respondCollection($collection);
  137. }
  138. /**
  139. * @NoAdminRequired
  140. *
  141. * Remove a resource from a collection
  142. *
  143. * @param int $collectionId ID of the collection
  144. * @param string $resourceType Name of the resource
  145. * @param string $resourceId ID of the resource
  146. * @return DataResponse<Http::STATUS_OK, CoreCollection, array{}>|DataResponse<Http::STATUS_NOT_FOUND|Http::STATUS_INTERNAL_SERVER_ERROR, array<empty>, array{}>
  147. *
  148. * 200: Collection returned
  149. * 404: Collection or resource not found
  150. */
  151. #[ApiRoute(verb: 'DELETE', url: '/resources/collections/{collectionId}', root: '/collaboration')]
  152. public function removeResource(int $collectionId, string $resourceType, string $resourceId): DataResponse {
  153. try {
  154. $collection = $this->getCollection($collectionId);
  155. } catch (CollectionException $e) {
  156. return new DataResponse([], Http::STATUS_NOT_FOUND);
  157. }
  158. try {
  159. $resource = $this->manager->getResourceForUser($resourceType, $resourceId, $this->userSession->getUser());
  160. } catch (CollectionException $e) {
  161. return new DataResponse([], Http::STATUS_NOT_FOUND);
  162. }
  163. $collection->removeResource($resource);
  164. return $this->respondCollection($collection);
  165. }
  166. /**
  167. * @NoAdminRequired
  168. *
  169. * Get collections by resource
  170. *
  171. * @param string $resourceType Type of the resource
  172. * @param string $resourceId ID of the resource
  173. * @return DataResponse<Http::STATUS_OK, CoreCollection[], array{}>|DataResponse<Http::STATUS_NOT_FOUND, array<empty>, array{}>
  174. *
  175. * 200: Collections returned
  176. * 404: Resource not accessible
  177. */
  178. #[ApiRoute(verb: 'GET', url: '/resources/{resourceType}/{resourceId}', root: '/collaboration')]
  179. public function getCollectionsByResource(string $resourceType, string $resourceId): DataResponse {
  180. try {
  181. $resource = $this->manager->getResourceForUser($resourceType, $resourceId, $this->userSession->getUser());
  182. } catch (ResourceException $e) {
  183. $resource = $this->manager->createResource($resourceType, $resourceId);
  184. }
  185. if (!$resource->canAccess($this->userSession->getUser())) {
  186. return new DataResponse([], Http::STATUS_NOT_FOUND);
  187. }
  188. return new DataResponse($this->prepareCollections($resource->getCollections()));
  189. }
  190. /**
  191. * @NoAdminRequired
  192. *
  193. * Create a collection for a resource
  194. *
  195. * @param string $baseResourceType Type of the base resource
  196. * @param string $baseResourceId ID of the base resource
  197. * @param string $name Name of the collection
  198. * @return DataResponse<Http::STATUS_OK, CoreCollection, array{}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_NOT_FOUND|Http::STATUS_INTERNAL_SERVER_ERROR, array<empty>, array{}>
  199. *
  200. * 200: Collection returned
  201. * 400: Creating collection is not possible
  202. * 404: Resource inaccessible
  203. */
  204. #[ApiRoute(verb: 'POST', url: '/resources/{baseResourceType}/{baseResourceId}', root: '/collaboration')]
  205. public function createCollectionOnResource(string $baseResourceType, string $baseResourceId, string $name): DataResponse {
  206. if (!isset($name[0]) || isset($name[64])) {
  207. return new DataResponse([], Http::STATUS_BAD_REQUEST);
  208. }
  209. try {
  210. $resource = $this->manager->createResource($baseResourceType, $baseResourceId);
  211. } catch (CollectionException $e) {
  212. return new DataResponse([], Http::STATUS_NOT_FOUND);
  213. }
  214. if (!$resource->canAccess($this->userSession->getUser())) {
  215. return new DataResponse([], Http::STATUS_NOT_FOUND);
  216. }
  217. $collection = $this->manager->newCollection($name);
  218. $collection->addResource($resource);
  219. return $this->respondCollection($collection);
  220. }
  221. /**
  222. * @NoAdminRequired
  223. *
  224. * Rename a collection
  225. *
  226. * @param int $collectionId ID of the collection
  227. * @param string $collectionName New name
  228. * @return DataResponse<Http::STATUS_OK, CoreCollection, array{}>|DataResponse<Http::STATUS_NOT_FOUND|Http::STATUS_INTERNAL_SERVER_ERROR, array<empty>, array{}>
  229. *
  230. * 200: Collection returned
  231. * 404: Collection not found
  232. */
  233. #[ApiRoute(verb: 'PUT', url: '/resources/collections/{collectionId}', root: '/collaboration')]
  234. public function renameCollection(int $collectionId, string $collectionName): DataResponse {
  235. try {
  236. $collection = $this->getCollection($collectionId);
  237. } catch (CollectionException $exception) {
  238. return new DataResponse([], Http::STATUS_NOT_FOUND);
  239. }
  240. $collection->setName($collectionName);
  241. return $this->respondCollection($collection);
  242. }
  243. /**
  244. * @return DataResponse<Http::STATUS_OK, CoreCollection, array{}>|DataResponse<Http::STATUS_NOT_FOUND|Http::STATUS_INTERNAL_SERVER_ERROR, array<empty>, array{}>
  245. */
  246. protected function respondCollection(ICollection $collection): DataResponse {
  247. try {
  248. return new DataResponse($this->prepareCollection($collection));
  249. } catch (CollectionException $e) {
  250. return new DataResponse([], Http::STATUS_NOT_FOUND);
  251. } catch (Exception $e) {
  252. $this->logger->critical($e->getMessage(), ['exception' => $e, 'app' => 'core']);
  253. return new DataResponse([], Http::STATUS_INTERNAL_SERVER_ERROR);
  254. }
  255. }
  256. /**
  257. * @return CoreCollection[]
  258. */
  259. protected function prepareCollections(array $collections): array {
  260. $result = [];
  261. foreach ($collections as $collection) {
  262. try {
  263. $result[] = $this->prepareCollection($collection);
  264. } catch (CollectionException $e) {
  265. } catch (Exception $e) {
  266. $this->logger->critical($e->getMessage(), ['exception' => $e, 'app' => 'core']);
  267. }
  268. }
  269. return $result;
  270. }
  271. /**
  272. * @return CoreCollection
  273. */
  274. protected function prepareCollection(ICollection $collection): array {
  275. if (!$collection->canAccess($this->userSession->getUser())) {
  276. throw new CollectionException('Can not access collection');
  277. }
  278. return [
  279. 'id' => $collection->getId(),
  280. 'name' => $collection->getName(),
  281. 'resources' => $this->prepareResources($collection->getResources()),
  282. ];
  283. }
  284. /**
  285. * @return CoreResource[]
  286. */
  287. protected function prepareResources(array $resources): array {
  288. $result = [];
  289. foreach ($resources as $resource) {
  290. try {
  291. $result[] = $this->prepareResource($resource);
  292. } catch (ResourceException $e) {
  293. } catch (Exception $e) {
  294. $this->logger->critical($e->getMessage(), ['exception' => $e, 'app' => 'core']);
  295. }
  296. }
  297. return $result;
  298. }
  299. /**
  300. * @return CoreResource
  301. */
  302. protected function prepareResource(IResource $resource): array {
  303. if (!$resource->canAccess($this->userSession->getUser())) {
  304. throw new ResourceException('Can not access resource');
  305. }
  306. return $resource->getRichObject();
  307. }
  308. }