diff options
Diffstat (limited to 'apps/updatenotification/lib/Controller')
3 files changed, 189 insertions, 116 deletions
diff --git a/apps/updatenotification/lib/Controller/APIController.php b/apps/updatenotification/lib/Controller/APIController.php index 9d5d1c2d764..4360d814dd2 100644 --- a/apps/updatenotification/lib/Controller/APIController.php +++ b/apps/updatenotification/lib/Controller/APIController.php @@ -3,30 +3,14 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * - * @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: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\UpdateNotification\Controller; use OC\App\AppStore\Fetcher\AppFetcher; +use OCA\UpdateNotification\Manager; +use OCA\UpdateNotification\ResponseDefinitions; use OCP\App\AppPathNotFoundException; use OCP\App\IAppManager; use OCP\AppFramework\Http; @@ -37,45 +21,50 @@ use OCP\IRequest; use OCP\IUserSession; use OCP\L10N\IFactory; +/** + * @psalm-import-type UpdateNotificationApp from ResponseDefinitions + */ class APIController extends OCSController { - /** @var IConfig */ - protected $config; - - /** @var IAppManager */ - protected $appManager; - - /** @var AppFetcher */ - protected $appFetcher; - - /** @var IFactory */ - protected $l10nFactory; + protected ?string $language = null; - /** @var IUserSession */ - protected $userSession; - - /** @var string */ - protected $language; - - public function __construct(string $appName, - IRequest $request, - IConfig $config, - IAppManager $appManager, - AppFetcher $appFetcher, - IFactory $l10nFactory, - IUserSession $userSession) { + /** + * List of apps that were in the appstore but are now shipped and don't have + * a compatible update available. + * + * @var array<string, int> + */ + protected array $appsShippedInFutureVersion = [ + 'bruteforcesettings' => 25, + 'suspicious_login' => 25, + 'twofactor_totp' => 25, + 'files_downloadlimit' => 29, + 'twofactor_nextcloud_notification' => 30, + 'app_api' => 30, + ]; + + public function __construct( + string $appName, + IRequest $request, + protected IConfig $config, + protected IAppManager $appManager, + protected AppFetcher $appFetcher, + protected IFactory $l10nFactory, + protected IUserSession $userSession, + protected Manager $manager, + ) { parent::__construct($appName, $request); - - $this->config = $config; - $this->appManager = $appManager; - $this->appFetcher = $appFetcher; - $this->l10nFactory = $l10nFactory; - $this->userSession = $userSession; } /** - * @param string $newVersion - * @return DataResponse + * List available updates for apps + * + * @param string $newVersion Server version to check updates for + * + * @return DataResponse<Http::STATUS_OK, array{missing: list<UpdateNotificationApp>, available: list<UpdateNotificationApp>}, array{}>|DataResponse<Http::STATUS_NOT_FOUND, array{appstore_disabled: bool, already_on_latest?: bool}, array{}> + * + * 200: Apps returned + * 404: New versions not found */ public function getAppList(string $newVersion): DataResponse { if (!$this->config->getSystemValue('appstoreenabled', true)) { @@ -85,14 +74,14 @@ class APIController extends OCSController { } // Get list of installed custom apps - $installedApps = $this->appManager->getInstalledApps(); + $installedApps = $this->appManager->getEnabledApps(); $installedApps = array_filter($installedApps, function ($app) { try { $this->appManager->getAppPath($app); } catch (AppPathNotFoundException $e) { return false; } - return !$this->appManager->isShipped($app); + return !$this->appManager->isShipped($app) && !isset($this->appsShippedInFutureVersion[$app]); }); if (empty($installedApps)) { @@ -105,7 +94,7 @@ class APIController extends OCSController { $this->appFetcher->setVersion($newVersion, 'future-apps.json', false); // Apps available on the app store for that version - $availableApps = array_map(static function (array $app) { + $availableApps = array_map(static function (array $app): string { return $app['id']; }, $this->appFetcher->get()); @@ -116,7 +105,14 @@ class APIController extends OCSController { ], Http::STATUS_NOT_FOUND); } - $this->language = $this->l10nFactory->getUserLanguage($this->userSession->getUser()); + // Ignore apps that are deployed from git + $installedApps = array_filter($installedApps, function (string $appId) { + try { + return !file_exists($this->appManager->getAppPath($appId) . '/.git'); + } catch (AppPathNotFoundException $e) { + return true; + } + }); $missing = array_diff($installedApps, $availableApps); $missing = array_map([$this, 'getAppDetails'], $missing); @@ -136,13 +132,66 @@ class APIController extends OCSController { * Get translated app name * * @param string $appId - * @return string[] + * @return UpdateNotificationApp */ protected function getAppDetails(string $appId): array { $app = $this->appManager->getAppInfo($appId, false, $this->language); + $name = $app['name'] ?? $appId; return [ 'appId' => $appId, - 'appName' => $app['name'] ?? $appId, + 'appName' => $name, ]; } + + protected function getLanguage(): string { + if ($this->language === null) { + $this->language = $this->l10nFactory->getUserLanguage($this->userSession->getUser()); + } + return $this->language; + } + + /** + * Get changelog entry for an app + * + * @param string $appId App to search changelog entry for + * @param string|null $version The version to search the changelog entry for (defaults to the latest installed) + * + * @return DataResponse<Http::STATUS_OK, array{appName: string, content: string, version: string}, array{}>|DataResponse<Http::STATUS_NOT_FOUND, array{}, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{}, array{}> + * + * 200: Changelog entry returned + * 400: The `version` parameter is not a valid version format + * 404: No changelog found + */ + public function getAppChangelogEntry(string $appId, ?string $version = null): DataResponse { + $version = $version ?? $this->appManager->getAppVersion($appId); + // handle pre-release versions + $matches = []; + $result = preg_match('/^(\d+\.\d+(\.\d+)?)/', $version, $matches); + if ($result === false || $result === 0) { + return new DataResponse([], Http::STATUS_BAD_REQUEST); + } + $shortVersion = $matches[0]; + + $changes = $this->manager->getChangelog($appId, $shortVersion); + + if ($changes === null) { + return new DataResponse([], Http::STATUS_NOT_FOUND); + } + + // Remove version headline + /** @var string[] */ + $changes = explode("\n", $changes, 2); + $changes = trim(end($changes)); + + // Get app info for localized app name + $info = $this->appManager->getAppInfo($appId) ?? []; + /** @var string */ + $appName = $info['name'] ?? $appId; + + return new DataResponse([ + 'appName' => $appName, + 'content' => $changes, + 'version' => $version, + ]); + } } diff --git a/apps/updatenotification/lib/Controller/AdminController.php b/apps/updatenotification/lib/Controller/AdminController.php index b13ba66efd5..26745948890 100644 --- a/apps/updatenotification/lib/Controller/AdminController.php +++ b/apps/updatenotification/lib/Controller/AdminController.php @@ -3,36 +3,19 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OCA\UpdateNotification\Controller; -use OCA\UpdateNotification\ResetTokenBackgroundJob; +use OCA\UpdateNotification\BackgroundJob\ResetToken; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Utility\ITimeFactory; use OCP\BackgroundJob\IJobList; +use OCP\IAppConfig; use OCP\IConfig; use OCP\IL10N; use OCP\IRequest; @@ -40,43 +23,22 @@ use OCP\Security\ISecureRandom; use OCP\Util; class AdminController extends Controller { - /** @var IJobList */ - private $jobList; - /** @var ISecureRandom */ - private $secureRandom; - /** @var IConfig */ - private $config; - /** @var ITimeFactory */ - private $timeFactory; - /** @var IL10N */ - private $l10n; - /** - * @param string $appName - * @param IRequest $request - * @param IJobList $jobList - * @param ISecureRandom $secureRandom - * @param IConfig $config - * @param ITimeFactory $timeFactory - * @param IL10N $l10n - */ - public function __construct($appName, - IRequest $request, - IJobList $jobList, - ISecureRandom $secureRandom, - IConfig $config, - ITimeFactory $timeFactory, - IL10N $l10n) { + public function __construct( + string $appName, + IRequest $request, + private IJobList $jobList, + private ISecureRandom $secureRandom, + private IConfig $config, + private IAppConfig $appConfig, + private ITimeFactory $timeFactory, + private IL10N $l10n, + ) { parent::__construct($appName, $request); - $this->jobList = $jobList; - $this->secureRandom = $secureRandom; - $this->config = $config; - $this->timeFactory = $timeFactory; - $this->l10n = $l10n; } - private function isUpdaterEnabled() { - return !$this->config->getSystemValue('upgrade.disable-web', false); + private function isUpdaterEnabled(): bool { + return !$this->config->getSystemValueBool('upgrade.disable-web'); } /** @@ -85,7 +47,7 @@ class AdminController extends Controller { */ public function setChannel(string $channel): DataResponse { Util::setChannel($channel); - $this->config->setAppValue('core', 'lastupdatedat', 0); + $this->appConfig->setValueInt('core', 'lastupdatedat', 0); return new DataResponse(['status' => 'success', 'data' => ['message' => $this->l10n->t('Channel updated')]]); } @@ -98,8 +60,8 @@ class AdminController extends Controller { } // Create a new job and store the creation date - $this->jobList->add(ResetTokenBackgroundJob::class); - $this->config->setAppValue('core', 'updater.secret.created', $this->timeFactory->getTime()); + $this->jobList->add(ResetToken::class); + $this->appConfig->setValueInt('core', 'updater.secret.created', $this->timeFactory->getTime()); // Create a new token $newToken = $this->secureRandom->generate(64); diff --git a/apps/updatenotification/lib/Controller/ChangelogController.php b/apps/updatenotification/lib/Controller/ChangelogController.php new file mode 100644 index 00000000000..a274ed3d2b2 --- /dev/null +++ b/apps/updatenotification/lib/Controller/ChangelogController.php @@ -0,0 +1,62 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\UpdateNotification\Controller; + +use OCA\UpdateNotification\Manager; +use OCP\App\IAppManager; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; +use OCP\AppFramework\Http\Attribute\OpenAPI; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Services\IInitialState; +use OCP\IRequest; +use OCP\Util; + +#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)] +class ChangelogController extends Controller { + + public function __construct( + string $appName, + IRequest $request, + private Manager $manager, + private IAppManager $appManager, + private IInitialState $initialState, + ) { + parent::__construct($appName, $request); + } + + /** + * This page is only used for clients not support showing the app changelog feature in-app and thus need to show it on a dedicated page. + * @param string $app App to show the changelog for + * @param string|null $version Version entry to show (defaults to latest installed) + */ + #[NoAdminRequired] + #[NoCSRFRequired] + public function showChangelog(string $app, ?string $version = null): TemplateResponse { + $version = $version ?? $this->appManager->getAppVersion($app); + $appInfo = $this->appManager->getAppInfo($app) ?? []; + $appName = $appInfo['name'] ?? $app; + + $changes = $this->manager->getChangelog($app, $version) ?? ''; + // Remove version headline + /** @var string[] */ + $changes = explode("\n", $changes, 2); + $changes = trim(end($changes)); + + $this->initialState->provideInitialState('changelog', [ + 'appName' => $appName, + 'appVersion' => $version, + 'text' => $changes, + ]); + + Util::addScript($this->appName, 'view-changelog-page'); + return new TemplateResponse($this->appName, 'empty'); + } +} |