* * @author Christoph Wurst * @author Joas Schilling * @author Ferdinand Thiessen * * @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 . * */ 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; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; use OCP\IConfig; use OCP\IRequest; use OCP\IUserSession; use OCP\L10N\IFactory; /** * @psalm-import-type UpdateNotificationApp from ResponseDefinitions */ class APIController extends OCSController { /** @var string */ protected $language; /** * List of apps that were in the appstore but are now shipped and don't have * a compatible update available. * * @var array */ 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); } /** * List available updates for apps * * @param string $newVersion Server version to check updates for * * @return DataResponse|DataResponse * * 200: Apps returned * 404: New versions not found */ public function getAppList(string $newVersion): DataResponse { if (!$this->config->getSystemValue('appstoreenabled', true)) { return new DataResponse([ 'appstore_disabled' => true, ], Http::STATUS_NOT_FOUND); } // Get list of installed custom apps $installedApps = $this->appManager->getInstalledApps(); $installedApps = array_filter($installedApps, function ($app) { try { $this->appManager->getAppPath($app); } catch (AppPathNotFoundException $e) { return false; } return !$this->appManager->isShipped($app) && !isset($this->appsShippedInFutureVersion[$app]); }); if (empty($installedApps)) { return new DataResponse([ 'missing' => [], 'available' => [], ]); } $this->appFetcher->setVersion($newVersion, 'future-apps.json', false); // Apps available on the app store for that version $availableApps = array_map(static function (array $app) { return $app['id']; }, $this->appFetcher->get()); if (empty($availableApps)) { return new DataResponse([ 'appstore_disabled' => false, 'already_on_latest' => false, ], 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); sort($missing); $available = array_intersect($installedApps, $availableApps); $available = array_map([$this, 'getAppDetails'], $available); sort($available); return new DataResponse([ 'missing' => $missing, 'available' => $available, ]); } /** * Get translated app name * * @param string $appId * @return UpdateNotificationApp */ protected function getAppDetails(string $appId): array { $app = $this->appManager->getAppInfo($appId, false, $this->language); /** @var ?string $name */ $name = $app['name']; return [ 'appId' => $appId, 'appName' => $name ?? $appId, ]; } /** * 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|DataResponse * * 200: Changelog entry returned * 404: No changelog found */ public function getAppChangelogEntry(string $appId, ?string $version = null): DataResponse { $version = $version ?? $this->appManager->getAppVersion($appId); $changes = $this->manager->getChangelog($appId, $version); 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, ]); } }