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