UserThemeController.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @copyright Copyright (c) 2018 John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
  5. * @copyright Copyright (c) 2019 Janis Köhr <janiskoehr@icloud.com>
  6. *
  7. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  8. * @author Daniel Kesselberg <mail@danielkesselberg.de>
  9. * @author Janis Köhr <janis.koehr@novatec-gmbh.de>
  10. * @author John Molakvoæ <skjnldsv@protonmail.com>
  11. * @author Roeland Jago Douma <roeland@famdouma.nl>
  12. * @author Kate Döen <kate.doeen@nextcloud.com>
  13. *
  14. * @license GNU AGPL version 3 or any later version
  15. *
  16. * This program is free software: you can redistribute it and/or modify
  17. * it under the terms of the GNU Affero General Public License as
  18. * published by the Free Software Foundation, either version 3 of the
  19. * License, or (at your option) any later version.
  20. *
  21. * This program is distributed in the hope that it will be useful,
  22. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  24. * GNU Affero General Public License for more details.
  25. *
  26. * You should have received a copy of the GNU Affero General Public License
  27. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  28. *
  29. */
  30. namespace OCA\Theming\Controller;
  31. use OCA\Theming\AppInfo\Application;
  32. use OCA\Theming\ITheme;
  33. use OCA\Theming\ResponseDefinitions;
  34. use OCA\Theming\Service\BackgroundService;
  35. use OCA\Theming\Service\ThemesService;
  36. use OCA\Theming\ThemingDefaults;
  37. use OCP\AppFramework\Http;
  38. use OCP\AppFramework\Http\DataResponse;
  39. use OCP\AppFramework\Http\FileDisplayResponse;
  40. use OCP\AppFramework\Http\JSONResponse;
  41. use OCP\AppFramework\Http\NotFoundResponse;
  42. use OCP\AppFramework\OCS\OCSBadRequestException;
  43. use OCP\AppFramework\OCS\OCSForbiddenException;
  44. use OCP\AppFramework\OCSController;
  45. use OCP\IConfig;
  46. use OCP\IRequest;
  47. use OCP\IUserSession;
  48. use OCP\PreConditionNotMetException;
  49. /**
  50. * @psalm-import-type ThemingBackground from ResponseDefinitions
  51. */
  52. class UserThemeController extends OCSController {
  53. protected ?string $userId = null;
  54. private IConfig $config;
  55. private ThemesService $themesService;
  56. private ThemingDefaults $themingDefaults;
  57. private BackgroundService $backgroundService;
  58. public function __construct(string $appName,
  59. IRequest $request,
  60. IConfig $config,
  61. IUserSession $userSession,
  62. ThemesService $themesService,
  63. ThemingDefaults $themingDefaults,
  64. BackgroundService $backgroundService) {
  65. parent::__construct($appName, $request);
  66. $this->config = $config;
  67. $this->themesService = $themesService;
  68. $this->themingDefaults = $themingDefaults;
  69. $this->backgroundService = $backgroundService;
  70. $user = $userSession->getUser();
  71. if ($user !== null) {
  72. $this->userId = $user->getUID();
  73. }
  74. }
  75. /**
  76. * @NoAdminRequired
  77. *
  78. * Enable theme
  79. *
  80. * @param string $themeId the theme ID
  81. * @return DataResponse<Http::STATUS_OK, array<empty>, array{}>
  82. * @throws OCSBadRequestException Enabling theme is not possible
  83. * @throws PreConditionNotMetException
  84. *
  85. * 200: Theme enabled successfully
  86. */
  87. public function enableTheme(string $themeId): DataResponse {
  88. $theme = $this->validateTheme($themeId);
  89. // Enable selected theme
  90. $this->themesService->enableTheme($theme);
  91. return new DataResponse();
  92. }
  93. /**
  94. * @NoAdminRequired
  95. *
  96. * Disable theme
  97. *
  98. * @param string $themeId the theme ID
  99. * @return DataResponse<Http::STATUS_OK, array<empty>, array{}>
  100. * @throws OCSBadRequestException Disabling theme is not possible
  101. * @throws PreConditionNotMetException
  102. *
  103. * 200: Theme disabled successfully
  104. */
  105. public function disableTheme(string $themeId): DataResponse {
  106. $theme = $this->validateTheme($themeId);
  107. // Enable selected theme
  108. $this->themesService->disableTheme($theme);
  109. return new DataResponse();
  110. }
  111. /**
  112. * Validate and return the matching ITheme
  113. *
  114. * Disable theme
  115. *
  116. * @param string $themeId the theme ID
  117. * @return ITheme
  118. * @throws OCSBadRequestException
  119. * @throws PreConditionNotMetException
  120. */
  121. private function validateTheme(string $themeId): ITheme {
  122. if ($themeId === '' || !$themeId) {
  123. throw new OCSBadRequestException('Invalid theme id: ' . $themeId);
  124. }
  125. $themes = $this->themesService->getThemes();
  126. if (!isset($themes[$themeId])) {
  127. throw new OCSBadRequestException('Invalid theme id: ' . $themeId);
  128. }
  129. // If trying to toggle another theme but this is enforced
  130. if ($this->config->getSystemValueString('enforce_theme', '') !== ''
  131. && $themes[$themeId]->getType() === ITheme::TYPE_THEME) {
  132. throw new OCSForbiddenException('Theme switching is disabled');
  133. }
  134. return $themes[$themeId];
  135. }
  136. /**
  137. * @NoAdminRequired
  138. * @NoCSRFRequired
  139. *
  140. * Get the background image
  141. * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}>|NotFoundResponse<Http::STATUS_NOT_FOUND, array{}>
  142. *
  143. * 200: Background image returned
  144. * 404: Background image not found
  145. */
  146. public function getBackground(): Http\Response {
  147. $file = $this->backgroundService->getBackground();
  148. if ($file !== null) {
  149. $response = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => $file->getMimeType()]);
  150. $response->cacheFor(24 * 60 * 60, false, true);
  151. return $response;
  152. }
  153. return new NotFoundResponse();
  154. }
  155. /**
  156. * @NoAdminRequired
  157. *
  158. * Delete the background
  159. *
  160. * @return JSONResponse<Http::STATUS_OK, ThemingBackground, array{}>
  161. *
  162. * 200: Background deleted successfully
  163. */
  164. public function deleteBackground(): JSONResponse {
  165. $currentVersion = (int)$this->config->getUserValue($this->userId, Application::APP_ID, 'userCacheBuster', '0');
  166. $this->backgroundService->deleteBackgroundImage();
  167. return new JSONResponse([
  168. 'backgroundImage' => null,
  169. 'backgroundColor' => $this->themingDefaults->getColorBackground(),
  170. 'primaryColor' => $this->themingDefaults->getColorPrimary(),
  171. 'version' => $currentVersion,
  172. ]);
  173. }
  174. /**
  175. * @NoAdminRequired
  176. *
  177. * Set the background
  178. *
  179. * @param string $type Type of background
  180. * @param string $value Path of the background image
  181. * @param string|null $color Color for the background
  182. * @return JSONResponse<Http::STATUS_OK, ThemingBackground, array{}>|JSONResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_INTERNAL_SERVER_ERROR, array{error: string}, array{}>
  183. *
  184. * 200: Background set successfully
  185. * 400: Setting background is not possible
  186. */
  187. public function setBackground(string $type = BackgroundService::BACKGROUND_DEFAULT, string $value = '', ?string $color = null): JSONResponse {
  188. $currentVersion = (int)$this->config->getUserValue($this->userId, Application::APP_ID, 'userCacheBuster', '0');
  189. // Set color if provided
  190. if ($color) {
  191. $this->backgroundService->setColorBackground($color);
  192. }
  193. // Set background image if provided
  194. try {
  195. switch ($type) {
  196. case BackgroundService::BACKGROUND_SHIPPED:
  197. $this->backgroundService->setShippedBackground($value);
  198. break;
  199. case BackgroundService::BACKGROUND_CUSTOM:
  200. $this->backgroundService->setFileBackground($value);
  201. break;
  202. case BackgroundService::BACKGROUND_DEFAULT:
  203. // Delete both background and color keys
  204. $this->backgroundService->setDefaultBackground();
  205. break;
  206. default:
  207. if (!$color) {
  208. return new JSONResponse(['error' => 'Invalid type provided'], Http::STATUS_BAD_REQUEST);
  209. }
  210. }
  211. } catch (\InvalidArgumentException $e) {
  212. return new JSONResponse(['error' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
  213. } catch (\Throwable $e) {
  214. return new JSONResponse(['error' => $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR);
  215. }
  216. $currentVersion++;
  217. $this->config->setUserValue($this->userId, Application::APP_ID, 'userCacheBuster', (string)$currentVersion);
  218. return new JSONResponse([
  219. 'backgroundImage' => $this->config->getUserValue($this->userId, Application::APP_ID, 'background_image', BackgroundService::BACKGROUND_DEFAULT),
  220. 'backgroundColor' => $this->themingDefaults->getColorBackground(),
  221. 'primaryColor' => $this->themingDefaults->getColorPrimary(),
  222. 'version' => $currentVersion,
  223. ]);
  224. }
  225. }