UserThemeController.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OCA\Theming\Controller;
  8. use OCA\Theming\AppInfo\Application;
  9. use OCA\Theming\ITheme;
  10. use OCA\Theming\ResponseDefinitions;
  11. use OCA\Theming\Service\BackgroundService;
  12. use OCA\Theming\Service\ThemesService;
  13. use OCA\Theming\ThemingDefaults;
  14. use OCP\AppFramework\Http;
  15. use OCP\AppFramework\Http\Attribute\NoAdminRequired;
  16. use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
  17. use OCP\AppFramework\Http\DataResponse;
  18. use OCP\AppFramework\Http\FileDisplayResponse;
  19. use OCP\AppFramework\Http\JSONResponse;
  20. use OCP\AppFramework\Http\NotFoundResponse;
  21. use OCP\AppFramework\Http\Response;
  22. use OCP\AppFramework\OCS\OCSBadRequestException;
  23. use OCP\AppFramework\OCS\OCSForbiddenException;
  24. use OCP\AppFramework\OCSController;
  25. use OCP\IConfig;
  26. use OCP\IRequest;
  27. use OCP\IUserSession;
  28. use OCP\PreConditionNotMetException;
  29. /**
  30. * @psalm-import-type ThemingBackground from ResponseDefinitions
  31. */
  32. class UserThemeController extends OCSController {
  33. protected ?string $userId = null;
  34. public function __construct(
  35. string $appName,
  36. IRequest $request,
  37. private IConfig $config,
  38. IUserSession $userSession,
  39. private ThemesService $themesService,
  40. private ThemingDefaults $themingDefaults,
  41. private BackgroundService $backgroundService,
  42. ) {
  43. parent::__construct($appName, $request);
  44. $user = $userSession->getUser();
  45. if ($user !== null) {
  46. $this->userId = $user->getUID();
  47. }
  48. }
  49. /**
  50. * Enable theme
  51. *
  52. * @param string $themeId the theme ID
  53. * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
  54. * @throws OCSBadRequestException Enabling theme is not possible
  55. * @throws PreConditionNotMetException
  56. *
  57. * 200: Theme enabled successfully
  58. */
  59. #[NoAdminRequired]
  60. public function enableTheme(string $themeId): DataResponse {
  61. $theme = $this->validateTheme($themeId);
  62. // Enable selected theme
  63. $this->themesService->enableTheme($theme);
  64. return new DataResponse();
  65. }
  66. /**
  67. * Disable theme
  68. *
  69. * @param string $themeId the theme ID
  70. * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
  71. * @throws OCSBadRequestException Disabling theme is not possible
  72. * @throws PreConditionNotMetException
  73. *
  74. * 200: Theme disabled successfully
  75. */
  76. #[NoAdminRequired]
  77. public function disableTheme(string $themeId): DataResponse {
  78. $theme = $this->validateTheme($themeId);
  79. // Enable selected theme
  80. $this->themesService->disableTheme($theme);
  81. return new DataResponse();
  82. }
  83. /**
  84. * Validate and return the matching ITheme
  85. *
  86. * Disable theme
  87. *
  88. * @param string $themeId the theme ID
  89. * @return ITheme
  90. * @throws OCSBadRequestException
  91. * @throws PreConditionNotMetException
  92. */
  93. private function validateTheme(string $themeId): ITheme {
  94. if ($themeId === '' || !$themeId) {
  95. throw new OCSBadRequestException('Invalid theme id: ' . $themeId);
  96. }
  97. $themes = $this->themesService->getThemes();
  98. if (!isset($themes[$themeId])) {
  99. throw new OCSBadRequestException('Invalid theme id: ' . $themeId);
  100. }
  101. // If trying to toggle another theme but this is enforced
  102. if ($this->config->getSystemValueString('enforce_theme', '') !== ''
  103. && $themes[$themeId]->getType() === ITheme::TYPE_THEME) {
  104. throw new OCSForbiddenException('Theme switching is disabled');
  105. }
  106. return $themes[$themeId];
  107. }
  108. /**
  109. * Get the background image
  110. * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}>|NotFoundResponse<Http::STATUS_NOT_FOUND, array{}>
  111. *
  112. * 200: Background image returned
  113. * 404: Background image not found
  114. */
  115. #[NoAdminRequired]
  116. #[NoCSRFRequired]
  117. public function getBackground(): Response {
  118. $file = $this->backgroundService->getBackground();
  119. if ($file !== null) {
  120. $response = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => $file->getMimeType()]);
  121. $response->cacheFor(24 * 60 * 60, false, true);
  122. return $response;
  123. }
  124. return new NotFoundResponse();
  125. }
  126. /**
  127. * Delete the background
  128. *
  129. * @return JSONResponse<Http::STATUS_OK, ThemingBackground, array{}>
  130. *
  131. * 200: Background deleted successfully
  132. */
  133. #[NoAdminRequired]
  134. public function deleteBackground(): JSONResponse {
  135. $currentVersion = (int)$this->config->getUserValue($this->userId, Application::APP_ID, 'userCacheBuster', '0');
  136. $this->backgroundService->deleteBackgroundImage();
  137. return new JSONResponse([
  138. 'backgroundImage' => null,
  139. 'backgroundColor' => $this->themingDefaults->getColorBackground(),
  140. 'primaryColor' => $this->themingDefaults->getColorPrimary(),
  141. 'version' => $currentVersion,
  142. ]);
  143. }
  144. /**
  145. * Set the background
  146. *
  147. * @param string $type Type of background
  148. * @param string $value Path of the background image
  149. * @param string|null $color Color for the background
  150. * @return JSONResponse<Http::STATUS_OK, ThemingBackground, array{}>|JSONResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_INTERNAL_SERVER_ERROR, array{error: string}, array{}>
  151. *
  152. * 200: Background set successfully
  153. * 400: Setting background is not possible
  154. */
  155. #[NoAdminRequired]
  156. public function setBackground(string $type = BackgroundService::BACKGROUND_DEFAULT, string $value = '', ?string $color = null): JSONResponse {
  157. $currentVersion = (int)$this->config->getUserValue($this->userId, Application::APP_ID, 'userCacheBuster', '0');
  158. // Set color if provided
  159. if ($color) {
  160. $this->backgroundService->setColorBackground($color);
  161. }
  162. // Set background image if provided
  163. try {
  164. switch ($type) {
  165. case BackgroundService::BACKGROUND_SHIPPED:
  166. $this->backgroundService->setShippedBackground($value);
  167. break;
  168. case BackgroundService::BACKGROUND_CUSTOM:
  169. $this->backgroundService->setFileBackground($value);
  170. break;
  171. case BackgroundService::BACKGROUND_DEFAULT:
  172. // Delete both background and color keys
  173. $this->backgroundService->setDefaultBackground();
  174. break;
  175. default:
  176. if (!$color) {
  177. return new JSONResponse(['error' => 'Invalid type provided'], Http::STATUS_BAD_REQUEST);
  178. }
  179. }
  180. } catch (\InvalidArgumentException $e) {
  181. return new JSONResponse(['error' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
  182. } catch (\Throwable $e) {
  183. return new JSONResponse(['error' => $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR);
  184. }
  185. $currentVersion++;
  186. $this->config->setUserValue($this->userId, Application::APP_ID, 'userCacheBuster', (string)$currentVersion);
  187. return new JSONResponse([
  188. 'backgroundImage' => $this->config->getUserValue($this->userId, Application::APP_ID, 'background_image', BackgroundService::BACKGROUND_DEFAULT),
  189. 'backgroundColor' => $this->themingDefaults->getColorBackground(),
  190. 'primaryColor' => $this->themingDefaults->getColorPrimary(),
  191. 'version' => $currentVersion,
  192. ]);
  193. }
  194. }