UserThemeController.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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\DataResponse;
  16. use OCP\AppFramework\Http\FileDisplayResponse;
  17. use OCP\AppFramework\Http\JSONResponse;
  18. use OCP\AppFramework\Http\NotFoundResponse;
  19. use OCP\AppFramework\OCS\OCSBadRequestException;
  20. use OCP\AppFramework\OCS\OCSForbiddenException;
  21. use OCP\AppFramework\OCSController;
  22. use OCP\IConfig;
  23. use OCP\IRequest;
  24. use OCP\IUserSession;
  25. use OCP\PreConditionNotMetException;
  26. /**
  27. * @psalm-import-type ThemingBackground from ResponseDefinitions
  28. */
  29. class UserThemeController extends OCSController {
  30. protected ?string $userId = null;
  31. private IConfig $config;
  32. private ThemesService $themesService;
  33. private ThemingDefaults $themingDefaults;
  34. private BackgroundService $backgroundService;
  35. public function __construct(string $appName,
  36. IRequest $request,
  37. IConfig $config,
  38. IUserSession $userSession,
  39. ThemesService $themesService,
  40. ThemingDefaults $themingDefaults,
  41. BackgroundService $backgroundService) {
  42. parent::__construct($appName, $request);
  43. $this->config = $config;
  44. $this->themesService = $themesService;
  45. $this->themingDefaults = $themingDefaults;
  46. $this->backgroundService = $backgroundService;
  47. $user = $userSession->getUser();
  48. if ($user !== null) {
  49. $this->userId = $user->getUID();
  50. }
  51. }
  52. /**
  53. * @NoAdminRequired
  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. public function enableTheme(string $themeId): DataResponse {
  65. $theme = $this->validateTheme($themeId);
  66. // Enable selected theme
  67. $this->themesService->enableTheme($theme);
  68. return new DataResponse();
  69. }
  70. /**
  71. * @NoAdminRequired
  72. *
  73. * Disable theme
  74. *
  75. * @param string $themeId the theme ID
  76. * @return DataResponse<Http::STATUS_OK, array<empty>, array{}>
  77. * @throws OCSBadRequestException Disabling theme is not possible
  78. * @throws PreConditionNotMetException
  79. *
  80. * 200: Theme disabled successfully
  81. */
  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. * @NoAdminRequired
  115. * @NoCSRFRequired
  116. *
  117. * Get the background image
  118. * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}>|NotFoundResponse<Http::STATUS_NOT_FOUND, array{}>
  119. *
  120. * 200: Background image returned
  121. * 404: Background image not found
  122. */
  123. public function getBackground(): Http\Response {
  124. $file = $this->backgroundService->getBackground();
  125. if ($file !== null) {
  126. $response = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => $file->getMimeType()]);
  127. $response->cacheFor(24 * 60 * 60, false, true);
  128. return $response;
  129. }
  130. return new NotFoundResponse();
  131. }
  132. /**
  133. * @NoAdminRequired
  134. *
  135. * Delete the background
  136. *
  137. * @return JSONResponse<Http::STATUS_OK, ThemingBackground, array{}>
  138. *
  139. * 200: Background deleted successfully
  140. */
  141. public function deleteBackground(): JSONResponse {
  142. $currentVersion = (int)$this->config->getUserValue($this->userId, Application::APP_ID, 'userCacheBuster', '0');
  143. $this->backgroundService->deleteBackgroundImage();
  144. return new JSONResponse([
  145. 'backgroundImage' => null,
  146. 'backgroundColor' => $this->themingDefaults->getColorBackground(),
  147. 'primaryColor' => $this->themingDefaults->getColorPrimary(),
  148. 'version' => $currentVersion,
  149. ]);
  150. }
  151. /**
  152. * @NoAdminRequired
  153. *
  154. * Set the background
  155. *
  156. * @param string $type Type of background
  157. * @param string $value Path of the background image
  158. * @param string|null $color Color for the background
  159. * @return JSONResponse<Http::STATUS_OK, ThemingBackground, array{}>|JSONResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_INTERNAL_SERVER_ERROR, array{error: string}, array{}>
  160. *
  161. * 200: Background set successfully
  162. * 400: Setting background is not possible
  163. */
  164. public function setBackground(string $type = BackgroundService::BACKGROUND_DEFAULT, string $value = '', ?string $color = null): JSONResponse {
  165. $currentVersion = (int)$this->config->getUserValue($this->userId, Application::APP_ID, 'userCacheBuster', '0');
  166. // Set color if provided
  167. if ($color) {
  168. $this->backgroundService->setColorBackground($color);
  169. }
  170. // Set background image if provided
  171. try {
  172. switch ($type) {
  173. case BackgroundService::BACKGROUND_SHIPPED:
  174. $this->backgroundService->setShippedBackground($value);
  175. break;
  176. case BackgroundService::BACKGROUND_CUSTOM:
  177. $this->backgroundService->setFileBackground($value);
  178. break;
  179. case BackgroundService::BACKGROUND_DEFAULT:
  180. // Delete both background and color keys
  181. $this->backgroundService->setDefaultBackground();
  182. break;
  183. default:
  184. if (!$color) {
  185. return new JSONResponse(['error' => 'Invalid type provided'], Http::STATUS_BAD_REQUEST);
  186. }
  187. }
  188. } catch (\InvalidArgumentException $e) {
  189. return new JSONResponse(['error' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
  190. } catch (\Throwable $e) {
  191. return new JSONResponse(['error' => $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR);
  192. }
  193. $currentVersion++;
  194. $this->config->setUserValue($this->userId, Application::APP_ID, 'userCacheBuster', (string)$currentVersion);
  195. return new JSONResponse([
  196. 'backgroundImage' => $this->config->getUserValue($this->userId, Application::APP_ID, 'background_image', BackgroundService::BACKGROUND_DEFAULT),
  197. 'backgroundColor' => $this->themingDefaults->getColorBackground(),
  198. 'primaryColor' => $this->themingDefaults->getColorPrimary(),
  199. 'version' => $currentVersion,
  200. ]);
  201. }
  202. }