UserThemeController.php 6.9 KB

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