IconController.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  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 FileAccessHelper */
  24. private $fileAccessHelper;
  25. public function __construct(
  26. $appName,
  27. IRequest $request,
  28. private ThemingDefaults $themingDefaults,
  29. private IconBuilder $iconBuilder,
  30. private ImageManager $imageManager,
  31. FileAccessHelper $fileAccessHelper,
  32. private IAppManager $appManager,
  33. ) {
  34. parent::__construct($appName, $request);
  35. $this->fileAccessHelper = $fileAccessHelper;
  36. }
  37. /**
  38. * Get a themed icon
  39. *
  40. * @param string $app ID of the app
  41. * @param string $image image file name (svg required)
  42. * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: 'image/svg+xml'}>|NotFoundResponse<Http::STATUS_NOT_FOUND, array{}>
  43. * @throws \Exception
  44. *
  45. * 200: Themed icon returned
  46. * 404: Themed icon not found
  47. */
  48. #[PublicPage]
  49. #[NoCSRFRequired]
  50. public function getThemedIcon(string $app, string $image): Response {
  51. if ($app !== 'core' && !$this->appManager->isEnabledForUser($app)) {
  52. $app = 'core';
  53. $image = 'favicon.png';
  54. }
  55. $color = $this->themingDefaults->getColorPrimary();
  56. try {
  57. $iconFileName = $this->imageManager->getCachedImage('icon-' . $app . '-' . $color . str_replace('/', '_', $image));
  58. } catch (NotFoundException $exception) {
  59. $icon = $this->iconBuilder->colorSvg($app, $image);
  60. if ($icon === false || $icon === '') {
  61. return new NotFoundResponse();
  62. }
  63. $iconFileName = $this->imageManager->setCachedImage('icon-' . $app . '-' . $color . str_replace('/', '_', $image), $icon);
  64. }
  65. $response = new FileDisplayResponse($iconFileName, Http::STATUS_OK, ['Content-Type' => 'image/svg+xml']);
  66. $response->cacheFor(86400, false, true);
  67. return $response;
  68. }
  69. /**
  70. * Return a 32x32 favicon as png
  71. *
  72. * @param string $app ID of the app
  73. * @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{}>
  74. * @throws \Exception
  75. *
  76. * 200: Favicon returned
  77. * 404: Favicon not found
  78. */
  79. #[PublicPage]
  80. #[NoCSRFRequired]
  81. public function getFavicon(string $app = 'core'): Response {
  82. if ($app !== 'core' && !$this->appManager->isEnabledForUser($app)) {
  83. $app = 'core';
  84. }
  85. $response = null;
  86. $iconFile = null;
  87. try {
  88. $iconFile = $this->imageManager->getImage('favicon', false);
  89. $response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']);
  90. } catch (NotFoundException $e) {
  91. }
  92. if ($iconFile === null && $this->imageManager->shouldReplaceIcons()) {
  93. $color = $this->themingDefaults->getColorPrimary();
  94. try {
  95. $iconFile = $this->imageManager->getCachedImage('favIcon-' . $app . $color);
  96. } catch (NotFoundException $exception) {
  97. $icon = $this->iconBuilder->getFavicon($app);
  98. if ($icon === false || $icon === '') {
  99. return new NotFoundResponse();
  100. }
  101. $iconFile = $this->imageManager->setCachedImage('favIcon-' . $app . $color, $icon);
  102. }
  103. $response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']);
  104. }
  105. if ($response === null) {
  106. $fallbackLogo = \OC::$SERVERROOT . '/core/img/favicon.png';
  107. $response = new DataDisplayResponse($this->fileAccessHelper->file_get_contents($fallbackLogo), Http::STATUS_OK, ['Content-Type' => 'image/x-icon']);
  108. }
  109. $response->cacheFor(86400);
  110. return $response;
  111. }
  112. /**
  113. * Return a 512x512 icon for touch devices
  114. *
  115. * @param string $app ID of the app
  116. * @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{}>
  117. * @throws \Exception
  118. *
  119. * 200: Touch icon returned
  120. * 404: Touch icon not found
  121. */
  122. #[PublicPage]
  123. #[NoCSRFRequired]
  124. public function getTouchIcon(string $app = 'core'): Response {
  125. if ($app !== 'core' && !$this->appManager->isEnabledForUser($app)) {
  126. $app = 'core';
  127. }
  128. $response = null;
  129. try {
  130. $iconFile = $this->imageManager->getImage('favicon');
  131. $response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']);
  132. } catch (NotFoundException $e) {
  133. }
  134. if ($this->imageManager->shouldReplaceIcons()) {
  135. $color = $this->themingDefaults->getColorPrimary();
  136. try {
  137. $iconFile = $this->imageManager->getCachedImage('touchIcon-' . $app . $color);
  138. } catch (NotFoundException $exception) {
  139. $icon = $this->iconBuilder->getTouchIcon($app);
  140. if ($icon === false || $icon === '') {
  141. return new NotFoundResponse();
  142. }
  143. $iconFile = $this->imageManager->setCachedImage('touchIcon-' . $app . $color, $icon);
  144. }
  145. $response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/png']);
  146. }
  147. if ($response === null) {
  148. $fallbackLogo = \OC::$SERVERROOT . '/core/img/favicon-touch.png';
  149. $response = new DataDisplayResponse($this->fileAccessHelper->file_get_contents($fallbackLogo), Http::STATUS_OK, ['Content-Type' => 'image/png']);
  150. }
  151. $response->cacheFor(86400);
  152. return $response;
  153. }
  154. }