PreviewController.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OC\Core\Controller;
  8. use OCA\Files_Sharing\SharedStorage;
  9. use OCP\AppFramework\Controller;
  10. use OCP\AppFramework\Http;
  11. use OCP\AppFramework\Http\Attribute\FrontpageRoute;
  12. use OCP\AppFramework\Http\DataResponse;
  13. use OCP\AppFramework\Http\FileDisplayResponse;
  14. use OCP\AppFramework\Http\RedirectResponse;
  15. use OCP\Files\File;
  16. use OCP\Files\IRootFolder;
  17. use OCP\Files\Node;
  18. use OCP\Files\NotFoundException;
  19. use OCP\IPreview;
  20. use OCP\IRequest;
  21. use OCP\Preview\IMimeIconProvider;
  22. class PreviewController extends Controller {
  23. public function __construct(
  24. string $appName,
  25. IRequest $request,
  26. private IPreview $preview,
  27. private IRootFolder $root,
  28. private ?string $userId,
  29. private IMimeIconProvider $mimeIconProvider,
  30. ) {
  31. parent::__construct($appName, $request);
  32. }
  33. /**
  34. * @NoAdminRequired
  35. * @NoCSRFRequired
  36. *
  37. * Get a preview by file path
  38. *
  39. * @param string $file Path of the file
  40. * @param int $x Width of the preview. A width of -1 will use the original image width.
  41. * @param int $y Height of the preview. A height of -1 will use the original image height.
  42. * @param bool $a Preserve the aspect ratio
  43. * @param bool $forceIcon Force returning an icon
  44. * @param 'fill'|'cover' $mode How to crop the image
  45. * @param bool $mimeFallback Whether to fallback to the mime icon if no preview is available
  46. * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, array<empty>, array{}>|RedirectResponse<Http::STATUS_SEE_OTHER, array{}>
  47. *
  48. * 200: Preview returned
  49. * 303: Redirect to the mime icon url if mimeFallback is true
  50. * 400: Getting preview is not possible
  51. * 403: Getting preview is not allowed
  52. * 404: Preview not found
  53. */
  54. #[FrontpageRoute(verb: 'GET', url: '/core/preview.png')]
  55. public function getPreview(
  56. string $file = '',
  57. int $x = 32,
  58. int $y = 32,
  59. bool $a = false,
  60. bool $forceIcon = true,
  61. string $mode = 'fill',
  62. bool $mimeFallback = false): Http\Response {
  63. if ($file === '' || $x === 0 || $y === 0) {
  64. return new DataResponse([], Http::STATUS_BAD_REQUEST);
  65. }
  66. try {
  67. $userFolder = $this->root->getUserFolder($this->userId);
  68. $node = $userFolder->get($file);
  69. } catch (NotFoundException $e) {
  70. return new DataResponse([], Http::STATUS_NOT_FOUND);
  71. }
  72. return $this->fetchPreview($node, $x, $y, $a, $forceIcon, $mode, $mimeFallback);
  73. }
  74. /**
  75. * @NoAdminRequired
  76. * @NoCSRFRequired
  77. *
  78. * Get a preview by file ID
  79. *
  80. * @param int $fileId ID of the file
  81. * @param int $x Width of the preview. A width of -1 will use the original image width.
  82. * @param int $y Height of the preview. A height of -1 will use the original image height.
  83. * @param bool $a Preserve the aspect ratio
  84. * @param bool $forceIcon Force returning an icon
  85. * @param 'fill'|'cover' $mode How to crop the image
  86. * @param bool $mimeFallback Whether to fallback to the mime icon if no preview is available
  87. * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, array<empty>, array{}>|RedirectResponse<Http::STATUS_SEE_OTHER, array{}>
  88. *
  89. * 200: Preview returned
  90. * 303: Redirect to the mime icon url if mimeFallback is true
  91. * 400: Getting preview is not possible
  92. * 403: Getting preview is not allowed
  93. * 404: Preview not found
  94. */
  95. #[FrontpageRoute(verb: 'GET', url: '/core/preview')]
  96. public function getPreviewByFileId(
  97. int $fileId = -1,
  98. int $x = 32,
  99. int $y = 32,
  100. bool $a = false,
  101. bool $forceIcon = true,
  102. string $mode = 'fill',
  103. bool $mimeFallback = false) {
  104. if ($fileId === -1 || $x === 0 || $y === 0) {
  105. return new DataResponse([], Http::STATUS_BAD_REQUEST);
  106. }
  107. $userFolder = $this->root->getUserFolder($this->userId);
  108. $node = $userFolder->getFirstNodeById($fileId);
  109. if (!$node) {
  110. return new DataResponse([], Http::STATUS_NOT_FOUND);
  111. }
  112. return $this->fetchPreview($node, $x, $y, $a, $forceIcon, $mode, $mimeFallback);
  113. }
  114. /**
  115. * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, array<empty>, array{}>|RedirectResponse<Http::STATUS_SEE_OTHER, array{}>
  116. */
  117. private function fetchPreview(
  118. Node $node,
  119. int $x,
  120. int $y,
  121. bool $a,
  122. bool $forceIcon,
  123. string $mode,
  124. bool $mimeFallback = false) : Http\Response {
  125. if (!($node instanceof File) || (!$forceIcon && !$this->preview->isAvailable($node))) {
  126. return new DataResponse([], Http::STATUS_NOT_FOUND);
  127. }
  128. if (!$node->isReadable()) {
  129. return new DataResponse([], Http::STATUS_FORBIDDEN);
  130. }
  131. $storage = $node->getStorage();
  132. if ($storage->instanceOfStorage(SharedStorage::class)) {
  133. /** @var SharedStorage $storage */
  134. $share = $storage->getShare();
  135. $attributes = $share->getAttributes();
  136. if ($attributes !== null && $attributes->getAttribute('permissions', 'download') === false) {
  137. return new DataResponse([], Http::STATUS_FORBIDDEN);
  138. }
  139. }
  140. try {
  141. $f = $this->preview->getPreview($node, $x, $y, !$a, $mode);
  142. $response = new FileDisplayResponse($f, Http::STATUS_OK, [
  143. 'Content-Type' => $f->getMimeType(),
  144. ]);
  145. $response->cacheFor(3600 * 24, false, true);
  146. return $response;
  147. } catch (NotFoundException $e) {
  148. // If we have no preview enabled, we can redirect to the mime icon if any
  149. if ($mimeFallback) {
  150. if ($url = $this->mimeIconProvider->getMimeIconUrl($node->getMimeType())) {
  151. return new RedirectResponse($url);
  152. }
  153. }
  154. return new DataResponse([], Http::STATUS_NOT_FOUND);
  155. } catch (\InvalidArgumentException $e) {
  156. return new DataResponse([], Http::STATUS_BAD_REQUEST);
  157. }
  158. }
  159. }