IconController.php 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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\DataDisplayResponse;
  15. use OCP\AppFramework\Http\FileDisplayResponse;
  16. use OCP\AppFramework\Http\NotFoundResponse;
  17. use OCP\AppFramework\Http\Response;
  18. use OCP\Files\NotFoundException;
  19. use OCP\IRequest;
  20. class IconController extends Controller {
  21. /** @var ThemingDefaults */
  22. private $themingDefaults;
  23. /** @var IconBuilder */
  24. private $iconBuilder;
  25. /** @var ImageManager */
  26. private $imageManager;
  27. /** @var FileAccessHelper */
  28. private $fileAccessHelper;
  29. /** @var IAppManager */
  30. private $appManager;
  31. public function __construct(
  32. $appName,
  33. IRequest $request,
  34. ThemingDefaults $themingDefaults,
  35. IconBuilder $iconBuilder,
  36. ImageManager $imageManager,
  37. FileAccessHelper $fileAccessHelper,
  38. IAppManager $appManager
  39. ) {
  40. parent::__construct($appName, $request);
  41. $this->themingDefaults = $themingDefaults;
  42. $this->iconBuilder = $iconBuilder;
  43. $this->imageManager = $imageManager;
  44. $this->fileAccessHelper = $fileAccessHelper;
  45. $this->appManager = $appManager;
  46. }
  47. /**
  48. * @PublicPage
  49. * @NoCSRFRequired
  50. *
  51. * Get a themed icon
  52. *
  53. * @param string $app ID of the app
  54. * @param string $image image file name (svg required)
  55. * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: 'image/svg+xml'}>|NotFoundResponse<Http::STATUS_NOT_FOUND, array{}>
  56. * @throws \Exception
  57. *
  58. * 200: Themed icon returned
  59. * 404: Themed icon not found
  60. */
  61. public function getThemedIcon(string $app, string $image): Response {
  62. if ($app !== 'core' && !$this->appManager->isEnabledForUser($app)) {
  63. $app = 'core';
  64. $image = 'favicon.png';
  65. }
  66. $color = $this->themingDefaults->getColorPrimary();
  67. try {
  68. $iconFileName = $this->imageManager->getCachedImage('icon-' . $app . '-' . $color . str_replace('/', '_', $image));
  69. } catch (NotFoundException $exception) {
  70. $icon = $this->iconBuilder->colorSvg($app, $image);
  71. if ($icon === false || $icon === '') {
  72. return new NotFoundResponse();
  73. }
  74. $iconFileName = $this->imageManager->setCachedImage('icon-' . $app . '-' . $color . str_replace('/', '_', $image), $icon);
  75. }
  76. $response = new FileDisplayResponse($iconFileName, Http::STATUS_OK, ['Content-Type' => 'image/svg+xml']);
  77. $response->cacheFor(86400, false, true);
  78. return $response;
  79. }
  80. /**
  81. * Return a 32x32 favicon as png
  82. *
  83. * @PublicPage
  84. * @NoCSRFRequired
  85. *
  86. * @param string $app ID of the app
  87. * @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{}>
  88. * @throws \Exception
  89. *
  90. * 200: Favicon returned
  91. * 404: Favicon not found
  92. */
  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. * @PublicPage
  128. * @NoCSRFRequired
  129. *
  130. * @param string $app ID of the app
  131. * @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{}>
  132. * @throws \Exception
  133. *
  134. * 200: Touch icon returned
  135. * 404: Touch icon not found
  136. */
  137. public function getTouchIcon(string $app = 'core'): Response {
  138. if ($app !== 'core' && !$this->appManager->isEnabledForUser($app)) {
  139. $app = 'core';
  140. }
  141. $response = null;
  142. try {
  143. $iconFile = $this->imageManager->getImage('favicon');
  144. $response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']);
  145. } catch (NotFoundException $e) {
  146. }
  147. if ($this->imageManager->shouldReplaceIcons()) {
  148. $color = $this->themingDefaults->getColorPrimary();
  149. try {
  150. $iconFile = $this->imageManager->getCachedImage('touchIcon-' . $app . $color);
  151. } catch (NotFoundException $exception) {
  152. $icon = $this->iconBuilder->getTouchIcon($app);
  153. if ($icon === false || $icon === '') {
  154. return new NotFoundResponse();
  155. }
  156. $iconFile = $this->imageManager->setCachedImage('touchIcon-' . $app . $color, $icon);
  157. }
  158. $response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/png']);
  159. }
  160. if ($response === null) {
  161. $fallbackLogo = \OC::$SERVERROOT . '/core/img/favicon-touch.png';
  162. $response = new DataDisplayResponse($this->fileAccessHelper->file_get_contents($fallbackLogo), Http::STATUS_OK, ['Content-Type' => 'image/png']);
  163. }
  164. $response->cacheFor(86400);
  165. return $response;
  166. }
  167. }