IconController.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-License-Identifier: AGPL-3.0-or-later
  5. */
  6. namespace OCA\Theming\Controller;
  7. use OC\IntegrityCheck\Helpers\FileAccessHelper;
  8. use OCA\Theming\IconBuilder;
  9. use OCA\Theming\ImageManager;
  10. use OCA\Theming\ThemingDefaults;
  11. use OCP\App\IAppManager;
  12. use OCP\AppFramework\Controller;
  13. use OCP\AppFramework\Http;
  14. use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
  15. use OCP\AppFramework\Http\Attribute\PublicPage;
  16. use OCP\AppFramework\Http\DataDisplayResponse;
  17. use OCP\AppFramework\Http\FileDisplayResponse;
  18. use OCP\AppFramework\Http\NotFoundResponse;
  19. use OCP\AppFramework\Http\Response;
  20. use OCP\Files\NotFoundException;
  21. use OCP\IRequest;
  22. class IconController extends Controller {
  23. /** @var ThemingDefaults */
  24. private $themingDefaults;
  25. /** @var IconBuilder */
  26. private $iconBuilder;
  27. /** @var ImageManager */
  28. private $imageManager;
  29. /** @var FileAccessHelper */
  30. private $fileAccessHelper;
  31. /** @var IAppManager */
  32. private $appManager;
  33. public function __construct(
  34. $appName,
  35. IRequest $request,
  36. ThemingDefaults $themingDefaults,
  37. IconBuilder $iconBuilder,
  38. ImageManager $imageManager,
  39. FileAccessHelper $fileAccessHelper,
  40. IAppManager $appManager
  41. ) {
  42. parent::__construct($appName, $request);
  43. $this->themingDefaults = $themingDefaults;
  44. $this->iconBuilder = $iconBuilder;
  45. $this->imageManager = $imageManager;
  46. $this->fileAccessHelper = $fileAccessHelper;
  47. $this->appManager = $appManager;
  48. }
  49. /**
  50. * Get a themed icon
  51. *
  52. * @param string $app ID of the app
  53. * @param string $image image file name (svg required)
  54. * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: 'image/svg+xml'}>|NotFoundResponse<Http::STATUS_NOT_FOUND, array{}>
  55. * @throws \Exception
  56. *
  57. * 200: Themed icon returned
  58. * 404: Themed icon not found
  59. */
  60. #[PublicPage]
  61. #[NoCSRFRequired]
  62. public function getThemedIcon(string $app, string $image): Response {
  63. if ($app !== 'core' && !$this->appManager->isEnabledForUser($app)) {
  64. $app = 'core';
  65. $image = 'favicon.png';
  66. }
  67. $color = $this->themingDefaults->getColorPrimary();
  68. try {
  69. $iconFileName = $this->imageManager->getCachedImage('icon-' . $app . '-' . $color . str_replace('/', '_', $image));
  70. } catch (NotFoundException $exception) {
  71. $icon = $this->iconBuilder->colorSvg($app, $image);
  72. if ($icon === false || $icon === '') {
  73. return new NotFoundResponse();
  74. }
  75. $iconFileName = $this->imageManager->setCachedImage('icon-' . $app . '-' . $color . str_replace('/', '_', $image), $icon);
  76. }
  77. $response = new FileDisplayResponse($iconFileName, Http::STATUS_OK, ['Content-Type' => 'image/svg+xml']);
  78. $response->cacheFor(86400, false, true);
  79. return $response;
  80. }
  81. /**
  82. * Return a 32x32 favicon as png
  83. *
  84. * @param string $app ID of the app
  85. * @return DataDisplayResponse<Http::STATUS_OK, array{Content-Type: 'image/x-icon'}>|FileDisplayResponse<Http::STATUS_OK, array{Content-Type: 'image/x-icon'}>|NotFoundResponse<Http::STATUS_NOT_FOUND, array{}>
  86. * @throws \Exception
  87. *
  88. * 200: Favicon returned
  89. * 404: Favicon not found
  90. */
  91. #[PublicPage]
  92. #[NoCSRFRequired]
  93. public function getFavicon(string $app = 'core'): Response {
  94. if ($app !== 'core' && !$this->appManager->isEnabledForUser($app)) {
  95. $app = 'core';
  96. }
  97. $response = null;
  98. $iconFile = null;
  99. try {
  100. $iconFile = $this->imageManager->getImage('favicon', false);
  101. $response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']);
  102. } catch (NotFoundException $e) {
  103. }
  104. if ($iconFile === null && $this->imageManager->shouldReplaceIcons()) {
  105. $color = $this->themingDefaults->getColorPrimary();
  106. try {
  107. $iconFile = $this->imageManager->getCachedImage('favIcon-' . $app . $color);
  108. } catch (NotFoundException $exception) {
  109. $icon = $this->iconBuilder->getFavicon($app);
  110. if ($icon === false || $icon === '') {
  111. return new NotFoundResponse();
  112. }
  113. $iconFile = $this->imageManager->setCachedImage('favIcon-' . $app . $color, $icon);
  114. }
  115. $response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']);
  116. }
  117. if ($response === null) {
  118. $fallbackLogo = \OC::$SERVERROOT . '/core/img/favicon.png';
  119. $response = new DataDisplayResponse($this->fileAccessHelper->file_get_contents($fallbackLogo), Http::STATUS_OK, ['Content-Type' => 'image/x-icon']);
  120. }
  121. $response->cacheFor(86400);
  122. return $response;
  123. }
  124. /**
  125. * Return a 512x512 icon for touch devices
  126. *
  127. * @param string $app ID of the app
  128. * @return DataDisplayResponse<Http::STATUS_OK, array{Content-Type: 'image/png'}>|FileDisplayResponse<Http::STATUS_OK, array{Content-Type: 'image/x-icon'|'image/png'}>|NotFoundResponse<Http::STATUS_NOT_FOUND, array{}>
  129. * @throws \Exception
  130. *
  131. * 200: Touch icon returned
  132. * 404: Touch icon not found
  133. */
  134. #[PublicPage]
  135. #[NoCSRFRequired]
  136. public function getTouchIcon(string $app = 'core'): Response {
  137. if ($app !== 'core' && !$this->appManager->isEnabledForUser($app)) {
  138. $app = 'core';
  139. }
  140. $response = null;
  141. try {
  142. $iconFile = $this->imageManager->getImage('favicon');
  143. $response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']);
  144. } catch (NotFoundException $e) {
  145. }
  146. if ($this->imageManager->shouldReplaceIcons()) {
  147. $color = $this->themingDefaults->getColorPrimary();
  148. try {
  149. $iconFile = $this->imageManager->getCachedImage('touchIcon-' . $app . $color);
  150. } catch (NotFoundException $exception) {
  151. $icon = $this->iconBuilder->getTouchIcon($app);
  152. if ($icon === false || $icon === '') {
  153. return new NotFoundResponse();
  154. }
  155. $iconFile = $this->imageManager->setCachedImage('touchIcon-' . $app . $color, $icon);
  156. }
  157. $response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/png']);
  158. }
  159. if ($response === null) {
  160. $fallbackLogo = \OC::$SERVERROOT . '/core/img/favicon-touch.png';
  161. $response = new DataDisplayResponse($this->fileAccessHelper->file_get_contents($fallbackLogo), Http::STATUS_OK, ['Content-Type' => 'image/png']);
  162. }
  163. $response->cacheFor(86400);
  164. return $response;
  165. }
  166. }