diff options
Diffstat (limited to 'apps/dashboard/lib')
-rw-r--r-- | apps/dashboard/lib/Controller/DashboardApiController.php | 250 | ||||
-rw-r--r-- | apps/dashboard/lib/Controller/DashboardController.php | 202 | ||||
-rw-r--r-- | apps/dashboard/lib/Controller/LayoutApiController.php | 62 | ||||
-rw-r--r-- | apps/dashboard/lib/ResponseDefinitions.php | 46 | ||||
-rw-r--r-- | apps/dashboard/lib/Service/BackgroundService.php | 191 | ||||
-rw-r--r-- | apps/dashboard/lib/Service/DashboardService.php | 71 |
6 files changed, 353 insertions, 469 deletions
diff --git a/apps/dashboard/lib/Controller/DashboardApiController.php b/apps/dashboard/lib/Controller/DashboardApiController.php index bb329888b09..d31cede85b7 100644 --- a/apps/dashboard/lib/Controller/DashboardApiController.php +++ b/apps/dashboard/lib/Controller/DashboardApiController.php @@ -3,79 +3,91 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2021 Julien Veyssier <eneiluj@posteo.net> - * - * @author Julien Veyssier <eneiluj@posteo.net> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Dashboard\Controller; -use OCP\AppFramework\OCSController; +use OCA\Dashboard\ResponseDefinitions; +use OCA\Dashboard\Service\DashboardService; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\ApiRoute; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\OCSController; +use OCP\Dashboard\IAPIWidget; +use OCP\Dashboard\IAPIWidgetV2; +use OCP\Dashboard\IButtonWidget; +use OCP\Dashboard\IIconWidget; use OCP\Dashboard\IManager; +use OCP\Dashboard\IOptionWidget; +use OCP\Dashboard\IReloadableWidget; +use OCP\Dashboard\IWidget; +use OCP\Dashboard\Model\WidgetButton; +use OCP\Dashboard\Model\WidgetItem; + +use OCP\Dashboard\Model\WidgetOptions; use OCP\IConfig; use OCP\IRequest; -use OCP\Dashboard\IAPIWidget; -use OCP\Dashboard\Model\WidgetItem; - +/** + * @psalm-import-type DashboardWidget from ResponseDefinitions + * @psalm-import-type DashboardWidgetItem from ResponseDefinitions + * @psalm-import-type DashboardWidgetItems from ResponseDefinitions + */ class DashboardApiController extends OCSController { - /** @var IManager */ - private $dashboardManager; - /** @var IConfig */ - private $config; - /** @var string|null */ - private $userId; - - public function __construct(string $appName, - IRequest $request, - IManager $dashboardManager, - IConfig $config, - ?string $userId) { + public function __construct( + string $appName, + IRequest $request, + private IManager $dashboardManager, + private IConfig $config, + private ?string $userId, + private DashboardService $service, + ) { parent::__construct($appName, $request); + } + + /** + * @param string[] $widgetIds Limit widgets to given ids + * @return IWidget[] + */ + private function getShownWidgets(array $widgetIds): array { + if (empty($widgetIds)) { + $systemDefault = $this->config->getAppValue('dashboard', 'layout', 'recommendations,spreed,mail,calendar'); + $widgetIds = explode(',', $this->config->getUserValue($this->userId, 'dashboard', 'layout', $systemDefault)); + } - $this->dashboardManager = $dashboardManager; - $this->config = $config; - $this->userId = $userId; + return array_filter( + $this->dashboardManager->getWidgets(), + static function (IWidget $widget) use ($widgetIds) { + return in_array($widget->getId(), $widgetIds); + }, + ); } /** - * Example request with Curl: - * curl -u user:passwd http://my.nc/ocs/v2.php/apps/dashboard/api/v1/widget-items -H Content-Type:application/json -X GET -d '{"sinceIds":{"github_notifications":"2021-03-22T15:01:10Z"}}' + * Get the items for the widgets * - * @param array $sinceIds Array indexed by widget Ids, contains date/id from which we want the new items + * @param array<string, string> $sinceIds Array indexed by widget Ids, contains date/id from which we want the new items * @param int $limit Limit number of result items per widget + * @psalm-param int<1, 30> $limit + * @param list<string> $widgets Limit results to specific widgets + * @return DataResponse<Http::STATUS_OK, array<string, list<DashboardWidgetItem>>, array{}> * - * @NoAdminRequired - * @NoCSRFRequired + * 200: Widget items returned */ - public function getWidgetItems(array $sinceIds = [], int $limit = 7): DataResponse { + #[NoAdminRequired] + #[NoCSRFRequired] + #[ApiRoute(verb: 'GET', url: '/api/v1/widget-items')] + public function getWidgetItems(array $sinceIds = [], int $limit = 7, array $widgets = []): DataResponse { $items = []; - - $systemDefault = $this->config->getAppValue('dashboard', 'layout', 'recommendations,spreed,mail,calendar'); - $userLayout = explode(',', $this->config->getUserValue($this->userId, 'dashboard', 'layout', $systemDefault)); - - $widgets = $this->dashboardManager->getWidgets(); + $widgets = $this->getShownWidgets($widgets); foreach ($widgets as $widget) { - if ($widget instanceof IAPIWidget && in_array($widget->getId(), $userLayout)) { - $items[$widget->getId()] = array_map(function (WidgetItem $item) { + if ($widget instanceof IAPIWidget) { + $items[$widget->getId()] = array_map(static function (WidgetItem $item) { return $item->jsonSerialize(); }, $widget->getItems($this->userId, $sinceIds[$widget->getId()] ?? null, $limit)); } @@ -83,4 +95,140 @@ class DashboardApiController extends OCSController { return new DataResponse($items); } + + /** + * Get the items for the widgets + * + * @param array<string, string> $sinceIds Array indexed by widget Ids, contains date/id from which we want the new items + * @param int $limit Limit number of result items per widget, not more than 30 are allowed + * @psalm-param int<1, 30> $limit + * @param list<string> $widgets Limit results to specific widgets + * @return DataResponse<Http::STATUS_OK, array<string, DashboardWidgetItems>, array{}> + * + * 200: Widget items returned + */ + #[NoAdminRequired] + #[NoCSRFRequired] + #[ApiRoute(verb: 'GET', url: '/api/v2/widget-items')] + public function getWidgetItemsV2(array $sinceIds = [], int $limit = 7, array $widgets = []): DataResponse { + $items = []; + $widgets = $this->getShownWidgets($widgets); + foreach ($widgets as $widget) { + if ($widget instanceof IAPIWidgetV2) { + $items[$widget->getId()] = $widget + ->getItemsV2($this->userId, $sinceIds[$widget->getId()] ?? null, $limit) + ->jsonSerialize(); + } + } + + return new DataResponse($items); + } + + /** + * Get the widgets + * + * @return DataResponse<Http::STATUS_OK, array<string, DashboardWidget>, array{}> + * + * 200: Widgets returned + */ + #[NoAdminRequired] + #[NoCSRFRequired] + #[ApiRoute(verb: 'GET', url: '/api/v1/widgets')] + public function getWidgets(): DataResponse { + $widgets = $this->dashboardManager->getWidgets(); + + $items = array_map(function (IWidget $widget) { + $options = ($widget instanceof IOptionWidget) ? $widget->getWidgetOptions() : WidgetOptions::getDefault(); + $data = [ + 'id' => $widget->getId(), + 'title' => $widget->getTitle(), + 'order' => $widget->getOrder(), + 'icon_class' => $widget->getIconClass(), + 'icon_url' => ($widget instanceof IIconWidget) ? $widget->getIconUrl() : '', + 'widget_url' => $widget->getUrl(), + 'item_icons_round' => $options->withRoundItemIcons(), + 'item_api_versions' => [], + 'reload_interval' => 0, + ]; + if ($widget instanceof IButtonWidget) { + $data += [ + 'buttons' => array_map(function (WidgetButton $button) { + return [ + 'type' => $button->getType(), + 'text' => $button->getText(), + 'link' => $button->getLink(), + ]; + }, $widget->getWidgetButtons($this->userId)), + ]; + } + if ($widget instanceof IReloadableWidget) { + $data['reload_interval'] = $widget->getReloadInterval(); + } + if ($widget instanceof IAPIWidget) { + $data['item_api_versions'][] = 1; + } + if ($widget instanceof IAPIWidgetV2) { + $data['item_api_versions'][] = 2; + } + return $data; + }, $widgets); + + return new DataResponse($items); + } + + /** + * Get the layout + * + * @return DataResponse<Http::STATUS_OK, array{layout: list<string>}, array{}> + * + * 200: Layout returned + */ + #[NoAdminRequired] + #[ApiRoute(verb: 'GET', url: '/api/v3/layout')] + public function getLayout(): DataResponse { + return new DataResponse(['layout' => $this->service->getLayout()]); + } + + /** + * Update the layout + * + * @param list<string> $layout The new layout + * @return DataResponse<Http::STATUS_OK, array{layout: list<string>}, array{}> + * + * 200: Statuses updated successfully + */ + #[NoAdminRequired] + #[ApiRoute(verb: 'POST', url: '/api/v3/layout')] + public function updateLayout(array $layout): DataResponse { + $this->config->setUserValue($this->userId, 'dashboard', 'layout', implode(',', $layout)); + return new DataResponse(['layout' => $layout]); + } + + /** + * Get the statuses + * + * @return DataResponse<Http::STATUS_OK, array{statuses: list<string>}, array{}> + * + * 200: Statuses returned + */ + #[NoAdminRequired] + #[ApiRoute(verb: 'GET', url: '/api/v3/statuses')] + public function getStatuses(): DataResponse { + return new DataResponse(['statuses' => $this->service->getStatuses()]); + } + + /** + * Update the statuses + * + * @param list<string> $statuses The new statuses + * @return DataResponse<Http::STATUS_OK, array{statuses: list<string>}, array{}> + * + * 200: Statuses updated successfully + */ + #[NoAdminRequired] + #[ApiRoute(verb: 'POST', url: '/api/v3/statuses')] + public function updateStatuses(array $statuses): DataResponse { + $this->config->setUserValue($this->userId, 'dashboard', 'statuses', implode(',', $statuses)); + return new DataResponse(['statuses' => $statuses]); + } } diff --git a/apps/dashboard/lib/Controller/DashboardController.php b/apps/dashboard/lib/Controller/DashboardController.php index 3763b07e7bc..da7e0901115 100644 --- a/apps/dashboard/lib/Controller/DashboardController.php +++ b/apps/dashboard/lib/Controller/DashboardController.php @@ -3,213 +3,85 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net> - * - * @author Julien Veyssier <eneiluj@posteo.net> - * @author Julius Härtl <jus@bitgrid.net> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Dashboard\Controller; -use OCA\Dashboard\Service\BackgroundService; -use OCA\Files\Event\LoadSidebar; -use OCA\Viewer\Event\LoadViewer; +use OCA\Dashboard\Service\DashboardService; use OCP\AppFramework\Controller; -use OCP\AppFramework\Http; -use OCP\AppFramework\Http\FileDisplayResponse; -use OCP\AppFramework\Http\JSONResponse; -use OCP\AppFramework\Http\NotFoundResponse; +use OCP\AppFramework\Http\Attribute\FrontpageRoute; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; +use OCP\AppFramework\Http\Attribute\OpenAPI; +use OCP\AppFramework\Http\FeaturePolicy; use OCP\AppFramework\Http\TemplateResponse; -use OCP\App\IAppManager; use OCP\AppFramework\Services\IInitialState; +use OCP\Dashboard\IIconWidget; use OCP\Dashboard\IManager; use OCP\Dashboard\IWidget; -use OCP\Dashboard\RegisterWidgetEvent; use OCP\EventDispatcher\IEventDispatcher; use OCP\IConfig; +use OCP\IL10N; use OCP\IRequest; +use OCP\Util; +#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)] class DashboardController extends Controller { - /** @var IInitialState */ - private $inititalState; - /** @var IEventDispatcher */ - private $eventDispatcher; - /** @var IAppManager */ - private $appManager; - /** @var IManager */ - private $dashboardManager; - /** @var IConfig */ - private $config; - /** @var string */ - private $userId; - /** - * @var BackgroundService - */ - private $backgroundService; - public function __construct( string $appName, IRequest $request, - IInitialState $initialState, - IEventDispatcher $eventDispatcher, - IAppManager $appManager, - IManager $dashboardManager, - IConfig $config, - BackgroundService $backgroundService, - $userId + private IInitialState $initialState, + private IEventDispatcher $eventDispatcher, + private IManager $dashboardManager, + private IConfig $config, + private IL10N $l10n, + private ?string $userId, + private DashboardService $service, ) { parent::__construct($appName, $request); - - $this->inititalState = $initialState; - $this->eventDispatcher = $eventDispatcher; - $this->appManager = $appManager; - $this->dashboardManager = $dashboardManager; - $this->config = $config; - $this->backgroundService = $backgroundService; - $this->userId = $userId; } /** - * @NoCSRFRequired - * @NoAdminRequired * @return TemplateResponse */ + #[NoCSRFRequired] + #[NoAdminRequired] + #[FrontpageRoute(verb: 'GET', url: '/')] public function index(): TemplateResponse { - \OCP\Util::addStyle('dashboard', 'dashboard'); - \OCP\Util::addScript('dashboard', 'main', 'theming'); - - $this->eventDispatcher->dispatchTyped(new LoadSidebar()); - if (class_exists(LoadViewer::class)) { - $this->eventDispatcher->dispatchTyped(new LoadViewer()); - } - - $this->eventDispatcher->dispatchTyped(new RegisterWidgetEvent($this->dashboardManager)); + Util::addStyle('dashboard', 'dashboard'); + Util::addScript('dashboard', 'main', 'theming'); - $systemDefault = $this->config->getAppValue('dashboard', 'layout', 'recommendations,spreed,mail,calendar'); - $userLayout = explode(',', $this->config->getUserValue($this->userId, 'dashboard', 'layout', $systemDefault)); $widgets = array_map(function (IWidget $widget) { return [ 'id' => $widget->getId(), 'title' => $widget->getTitle(), 'iconClass' => $widget->getIconClass(), + 'iconUrl' => $widget instanceof IIconWidget ? $widget->getIconUrl() : '', 'url' => $widget->getUrl() ]; }, $this->dashboardManager->getWidgets()); - $configStatuses = $this->config->getUserValue($this->userId, 'dashboard', 'statuses', ''); - $statuses = json_decode($configStatuses, true); - // We avoid getting an empty array as it will not produce an object in UI's JS - // It does not matter if some statuses are missing from the array, missing ones are considered enabled - $statuses = ($statuses && count($statuses) > 0) ? $statuses : ['weather' => true]; - // if theming app is enabled and wants to override default, we pass it - $themingDefaultBackground = $this->appManager->isEnabledForUser('theming') - ? $this->config->getAppValue('theming', 'backgroundMime', '') - : ''; - $this->inititalState->provideInitialState('themingDefaultBackground', $themingDefaultBackground); - $this->inititalState->provideInitialState('panels', $widgets); - $this->inititalState->provideInitialState('statuses', $statuses); - $this->inititalState->provideInitialState('layout', $userLayout); - $this->inititalState->provideInitialState('firstRun', $this->config->getUserValue($this->userId, 'dashboard', 'firstRun', '1') === '1'); - $this->inititalState->provideInitialState('shippedBackgrounds', BackgroundService::SHIPPED_BACKGROUNDS); - $this->inititalState->provideInitialState('background', $this->config->getUserValue($this->userId, 'dashboard', 'background', 'default')); - $this->inititalState->provideInitialState('version', $this->config->getUserValue($this->userId, 'dashboard', 'backgroundVersion', 0)); + $this->initialState->provideInitialState('panels', $widgets); + $this->initialState->provideInitialState('statuses', $this->service->getStatuses()); + $this->initialState->provideInitialState('layout', $this->service->getLayout()); + $this->initialState->provideInitialState('appStoreEnabled', $this->config->getSystemValueBool('appstoreenabled', true)); + $this->initialState->provideInitialState('firstRun', $this->config->getUserValue($this->userId, 'dashboard', 'firstRun', '1') === '1'); + $this->initialState->provideInitialState('birthdate', $this->service->getBirthdate()); $this->config->setUserValue($this->userId, 'dashboard', 'firstRun', '0'); - $response = new TemplateResponse('dashboard', 'index'); + $response = new TemplateResponse('dashboard', 'index', [ + 'id-app-content' => '#app-dashboard', + 'id-app-navigation' => null, + 'pageTitle' => $this->l10n->t('Dashboard'), + ]); // For the weather widget we should allow the geolocation - $featurePolicy = new Http\FeaturePolicy(); + $featurePolicy = new FeaturePolicy(); $featurePolicy->addAllowedGeoLocationDomain('\'self\''); $response->setFeaturePolicy($featurePolicy); return $response; } - - /** - * @NoAdminRequired - * @param string $layout - * @return JSONResponse - */ - public function updateLayout(string $layout): JSONResponse { - $this->config->setUserValue($this->userId, 'dashboard', 'layout', $layout); - return new JSONResponse(['layout' => $layout]); - } - - /** - * @NoAdminRequired - * @param string $statuses - * @return JSONResponse - */ - public function updateStatuses(string $statuses): JSONResponse { - $this->config->setUserValue($this->userId, 'dashboard', 'statuses', $statuses); - return new JSONResponse(['statuses' => $statuses]); - } - - /** - * @NoAdminRequired - */ - public function setBackground(string $type = 'default', string $value = ''): JSONResponse { - $currentVersion = (int)$this->config->getUserValue($this->userId, 'dashboard', 'backgroundVersion', '0'); - try { - switch ($type) { - case 'shipped': - $this->backgroundService->setShippedBackground($value); - break; - case 'custom': - $this->backgroundService->setFileBackground($value); - break; - case 'color': - $this->backgroundService->setColorBackground($value); - break; - case 'default': - $this->backgroundService->setDefaultBackground(); - break; - default: - return new JSONResponse(['error' => 'Invalid type provided'], Http::STATUS_BAD_REQUEST); - } - } catch (\InvalidArgumentException $e) { - return new JSONResponse(['error' => $e->getMessage()], Http::STATUS_BAD_REQUEST); - } catch (\Throwable $e) { - return new JSONResponse(['error' => $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR); - } - $currentVersion++; - $this->config->setUserValue($this->userId, 'dashboard', 'backgroundVersion', (string)$currentVersion); - return new JSONResponse([ - 'type' => $type, - 'value' => $value, - 'version' => $this->config->getUserValue($this->userId, 'dashboard', 'backgroundVersion', $currentVersion) - ]); - } - - /** - * @NoAdminRequired - * @NoCSRFRequired - */ - public function getBackground(): Http\Response { - $file = $this->backgroundService->getBackground(); - if ($file !== null) { - $response = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => $file->getMimeType()]); - $response->cacheFor(24 * 60 * 60); - return $response; - } - return new NotFoundResponse(); - } } diff --git a/apps/dashboard/lib/Controller/LayoutApiController.php b/apps/dashboard/lib/Controller/LayoutApiController.php deleted file mode 100644 index 755470b7b07..00000000000 --- a/apps/dashboard/lib/Controller/LayoutApiController.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php - -declare(strict_types=1); - -/** - * @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net> - * - * @author Julius Härtl <jus@bitgrid.net> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ -namespace OCA\Dashboard\Controller; - -use OCP\AppFramework\Http\JSONResponse; -use OCP\AppFramework\OCSController; -use OCP\IConfig; -use OCP\IRequest; - -class LayoutApiController extends OCSController { - - /** @var IConfig */ - private $config; - /** @var string */ - private $userId; - - public function __construct( - string $appName, - IRequest $request, - IConfig $config, - $userId - ) { - parent::__construct($appName, $request); - - $this->config = $config; - $this->userId = $userId; - } - - /** - * @NoAdminRequired - * - * @param string $layout - * @return JSONResponse - */ - public function create(string $layout): JSONResponse { - $this->config->setUserValue($this->userId, 'dashboard', 'layout', $layout); - return new JSONResponse(['layout' => $layout]); - } -} diff --git a/apps/dashboard/lib/ResponseDefinitions.php b/apps/dashboard/lib/ResponseDefinitions.php new file mode 100644 index 00000000000..be5a4d56883 --- /dev/null +++ b/apps/dashboard/lib/ResponseDefinitions.php @@ -0,0 +1,46 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\Dashboard; + +/** + * @psalm-type DashboardWidget = array{ + * id: string, + * title: string, + * order: int, + * icon_class: string, + * icon_url: string, + * widget_url: ?string, + * item_icons_round: bool, + * item_api_versions: list<int>, + * reload_interval: int, + * buttons?: list<array{ + * type: string, + * text: string, + * link: string, + * }>, + * } + * + * @psalm-type DashboardWidgetItem = array{ + * subtitle: string, + * title: string, + * link: string, + * iconUrl: string, + * overlayIconUrl: string, + * sinceId: string, + * } + * + * @psalm-type DashboardWidgetItems = array{ + * items: list<DashboardWidgetItem>, + * emptyContentMessage: string, + * halfEmptyContentMessage: string, + * } + */ +class ResponseDefinitions { +} diff --git a/apps/dashboard/lib/Service/BackgroundService.php b/apps/dashboard/lib/Service/BackgroundService.php deleted file mode 100644 index f9bd987bdca..00000000000 --- a/apps/dashboard/lib/Service/BackgroundService.php +++ /dev/null @@ -1,191 +0,0 @@ -<?php - -declare(strict_types=1); - -/** - * @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net> - * - * @author Jan C. Borchardt <hey@jancborchardt.net> - * @author Julius Härtl <jus@bitgrid.net> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ -namespace OCA\Dashboard\Service; - -use InvalidArgumentException; -use OC\User\NoUserException; -use OCP\Files\File; -use OCP\Files\IAppData; -use OCP\Files\IRootFolder; -use OCP\Files\NotFoundException; -use OCP\Files\NotPermittedException; -use OCP\Files\SimpleFS\ISimpleFile; -use OCP\Files\SimpleFS\ISimpleFolder; -use OCP\IConfig; -use OCP\Lock\LockedException; -use OCP\PreConditionNotMetException; - -class BackgroundService { - public const THEMING_MODE_DARK = 'dark'; - - public const SHIPPED_BACKGROUNDS = [ - 'anatoly-mikhaltsov-butterfly-wing-scale.jpg' => [ - 'attribution' => 'Butterfly wing scale (Anatoly Mikhaltsov, CC BY-SA)', - 'attribution_url' => 'https://commons.wikimedia.org/wiki/File:%D0%A7%D0%B5%D1%88%D1%83%D0%B9%D0%BA%D0%B8_%D0%BA%D1%80%D1%8B%D0%BB%D0%B0_%D0%B1%D0%B0%D0%B1%D0%BE%D1%87%D0%BA%D0%B8.jpg', - ], - 'bernie-cetonia-aurata-take-off-composition.jpg' => [ - 'attribution' => 'Cetonia aurata take off composition (Bernie, Public Domain)', - 'attribution_url' => 'https://commons.wikimedia.org/wiki/File:Cetonia_aurata_take_off_composition_05172009.jpg', - 'theming' => self::THEMING_MODE_DARK, - ], - 'dejan-krsmanovic-ribbed-red-metal.jpg' => [ - 'attribution' => 'Ribbed red metal (Dejan Krsmanovic, CC BY)', - 'attribution_url' => 'https://www.flickr.com/photos/dejankrsmanovic/42971456774/', - ], - 'eduardo-neves-pedra-azul.jpg' => [ - 'attribution' => 'Pedra azul milky way (Eduardo Neves, CC BY-SA)', - 'attribution_url' => 'https://commons.wikimedia.org/wiki/File:Pedra_Azul_Milky_Way.jpg', - ], - 'european-space-agency-barents-bloom.jpg' => [ - 'attribution' => 'Barents bloom (European Space Agency, CC BY-SA)', - 'attribution_url' => 'https://www.esa.int/ESA_Multimedia/Images/2016/08/Barents_bloom', - ], - 'hannes-fritz-flippity-floppity.jpg' => [ - 'attribution' => 'Flippity floppity (Hannes Fritz, CC BY-SA)', - 'attribution_url' => 'http://hannes.photos/flippity-floppity', - ], - 'hannes-fritz-roulette.jpg' => [ - 'attribution' => 'Roulette (Hannes Fritz, CC BY-SA)', - 'attribution_url' => 'http://hannes.photos/roulette', - ], - 'hannes-fritz-sea-spray.jpg' => [ - 'attribution' => 'Sea spray (Hannes Fritz, CC BY-SA)', - 'attribution_url' => 'http://hannes.photos/sea-spray', - ], - 'kamil-porembinski-clouds.jpg' => [ - 'attribution' => 'Clouds (Kamil Porembiński, CC BY-SA)', - 'attribution_url' => 'https://www.flickr.com/photos/paszczak000/8715851521/', - ], - 'bernard-spragg-new-zealand-fern.jpg' => [ - 'attribution' => 'New zealand fern (Bernard Spragg, CC0)', - 'attribution_url' => 'https://commons.wikimedia.org/wiki/File:NZ_Fern.(Blechnum_chambersii)_(11263534936).jpg', - ], - 'rawpixel-pink-tapioca-bubbles.jpg' => [ - 'attribution' => 'Pink tapioca bubbles (Rawpixel, CC BY)', - 'attribution_url' => 'https://www.flickr.com/photos/byrawpixel/27665140298/in/photostream/', - 'theming' => self::THEMING_MODE_DARK, - ], - 'nasa-waxing-crescent-moon.jpg' => [ - 'attribution' => 'Waxing crescent moon (NASA, Public Domain)', - 'attribution_url' => 'https://www.nasa.gov/image-feature/a-waxing-crescent-moon', - ], - 'tommy-chau-already.jpg' => [ - 'attribution' => 'Cityscape (Tommy Chau, CC BY)', - 'attribution_url' => 'https://www.flickr.com/photos/90975693@N05/16910999368', - ], - 'tommy-chau-lion-rock-hill.jpg' => [ - 'attribution' => 'Lion rock hill (Tommy Chau, CC BY)', - 'attribution_url' => 'https://www.flickr.com/photos/90975693@N05/17136440246', - 'theming' => self::THEMING_MODE_DARK, - ], - 'lali-masriera-yellow-bricks.jpg' => [ - 'attribution' => 'Yellow bricks (Lali Masriera, CC BY)', - 'attribution_url' => 'https://www.flickr.com/photos/visualpanic/3982464447', - 'theming' => self::THEMING_MODE_DARK, - ] - ]; - /** - * @var IRootFolder - */ - private $rootFolder; - /** - * @var IAppData - */ - private $appData; - /** - * @var IConfig - */ - private $config; - private $userId; - - public function __construct(IRootFolder $rootFolder, IAppData $appData, IConfig $config, $userId) { - if ($userId === null) { - return; - } - $this->rootFolder = $rootFolder; - $this->appData = $appData; - $this->config = $config; - $this->userId = $userId; - } - - public function setDefaultBackground(): void { - $this->config->deleteUserValue($this->userId, 'dashboard', 'background'); - } - - /** - * @param $path - * @throws NotFoundException - * @throws NotPermittedException - * @throws LockedException - * @throws PreConditionNotMetException - * @throws NoUserException - */ - public function setFileBackground($path): void { - $this->config->setUserValue($this->userId, 'dashboard', 'background', 'custom'); - $userFolder = $this->rootFolder->getUserFolder($this->userId); - /** @var File $file */ - $file = $userFolder->get($path); - $this->getAppDataFolder()->newFile('background.jpg', $file->fopen('r')); - } - - public function setShippedBackground($fileName): void { - if (!array_key_exists($fileName, self::SHIPPED_BACKGROUNDS)) { - throw new InvalidArgumentException('The given file name is invalid'); - } - $this->config->setUserValue($this->userId, 'dashboard', 'background', $fileName); - } - - public function setColorBackground(string $color): void { - if (!preg_match('/^#([0-9a-f]{3}|[0-9a-f]{6})$/i', $color)) { - throw new InvalidArgumentException('The given color is invalid'); - } - $this->config->setUserValue($this->userId, 'dashboard', 'background', $color); - } - - public function getBackground(): ?ISimpleFile { - $background = $this->config->getUserValue($this->userId, 'dashboard', 'background', 'default'); - if ($background === 'custom') { - try { - return $this->getAppDataFolder()->getFile('background.jpg'); - } catch (NotFoundException | NotPermittedException $e) { - } - } - return null; - } - - /** - * @return ISimpleFolder - * @throws NotPermittedException - */ - private function getAppDataFolder(): ISimpleFolder { - try { - return $this->appData->getFolder($this->userId); - } catch (NotFoundException $e) { - return $this->appData->newFolder($this->userId); - } - } -} diff --git a/apps/dashboard/lib/Service/DashboardService.php b/apps/dashboard/lib/Service/DashboardService.php new file mode 100644 index 00000000000..bb5333c2cc7 --- /dev/null +++ b/apps/dashboard/lib/Service/DashboardService.php @@ -0,0 +1,71 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Dashboard\Service; + +use JsonException; +use OCP\Accounts\IAccountManager; +use OCP\Accounts\PropertyDoesNotExistException; +use OCP\IConfig; +use OCP\IUserManager; + +class DashboardService { + public function __construct( + private IConfig $config, + private ?string $userId, + private IUserManager $userManager, + private IAccountManager $accountManager, + ) { + + } + + /** + * @return list<string> + */ + public function getLayout(): array { + $systemDefault = $this->config->getAppValue('dashboard', 'layout', 'recommendations,spreed,mail,calendar'); + return array_values(array_filter(explode(',', $this->config->getUserValue($this->userId, 'dashboard', 'layout', $systemDefault)), fn (string $value) => $value !== '')); + } + + /** + * @return list<string> + */ + public function getStatuses() { + $configStatuses = $this->config->getUserValue($this->userId, 'dashboard', 'statuses', ''); + try { + // Parse the old format + /** @var array<string, bool> $statuses */ + $statuses = json_decode($configStatuses, true, 512, JSON_THROW_ON_ERROR); + // We avoid getting an empty array as it will not produce an object in UI's JS + return array_keys(array_filter($statuses, static fn (bool $value) => $value)); + } catch (JsonException $e) { + return array_values(array_filter(explode(',', $configStatuses), fn (string $value) => $value !== '')); + } + } + + public function getBirthdate(): string { + if ($this->userId === null) { + return ''; + } + + $user = $this->userManager->get($this->userId); + if ($user === null) { + return ''; + } + + $account = $this->accountManager->getAccount($user); + + try { + $birthdate = $account->getProperty(IAccountManager::PROPERTY_BIRTHDATE); + } catch (PropertyDoesNotExistException) { + return ''; + } + + return $birthdate->getValue(); + } +} |