diff options
Diffstat (limited to 'lib/private/legacy/OC_App.php')
-rw-r--r-- | lib/private/legacy/OC_App.php | 643 |
1 files changed, 102 insertions, 541 deletions
diff --git a/lib/private/legacy/OC_App.php b/lib/private/legacy/OC_App.php index f290b7a610c..10b78e2a7ef 100644 --- a/lib/private/legacy/OC_App.php +++ b/lib/private/legacy/OC_App.php @@ -1,79 +1,36 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Borjan Tchakaloff <borjan@tchakaloff.fr> - * @author Brice Maron <brice@bmaron.net> - * @author Christopher Schäpers <kondou@ts.unde.re> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Rudolf <github.com@daniel-rudolf.de> - * @author Frank Karlitschek <frank@karlitschek.de> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Jakob Sack <mail@jakobsack.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Haertl <jus@bitgrid.net> - * @author Julius Härtl <jus@bitgrid.net> - * @author Kamil Domanski <kdomanski@kdemail.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Markus Goetz <markus@woboq.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author RealRancor <Fisch.666@gmx.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Sam Tuke <mail@samtuke.com> - * @author Sebastian Wessalowski <sebastian@wessalowski.org> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * @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 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ use OC\App\DependencyAnalyzer; use OC\App\Platform; use OC\AppFramework\Bootstrap\Coordinator; +use OC\Config\ConfigManager; use OC\DB\MigrationService; use OC\Installer; use OC\Repair; -use OC\ServerNotAvailableException; +use OC\Repair\Events\RepairErrorEvent; +use OCP\App\Events\AppUpdateEvent; +use OCP\App\IAppManager; use OCP\App\ManagerEvent; -use OCP\AppFramework\QueryException; use OCP\Authentication\IAlternativeLogin; -use OCP\ILogger; -use OCP\Settings\IManager as ISettingsManager; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\IAppConfig; +use OCP\Server; +use Psr\Container\ContainerExceptionInterface; use Psr\Log\LoggerInterface; +use function OCP\Log\logger; /** * This class manages the apps. It allows them to register and integrate in the - * ownCloud ecosystem. Furthermore, this class is responsible for installing, + * Nextcloud ecosystem. Furthermore, this class is responsible for installing, * upgrading and removing apps. */ class OC_App { - private static $adminForms = []; - private static $personalForms = []; - private static $appTypes = []; - private static $loadedApps = []; private static $altLogin = []; private static $alreadyRegistered = []; public const supportedApp = 300; @@ -84,12 +41,13 @@ class OC_App { * * @psalm-taint-escape file * @psalm-taint-escape include + * @psalm-taint-escape html + * @psalm-taint-escape has_quotes * - * @param string $app AppId that needs to be cleaned - * @return string + * @deprecated 31.0.0 use IAppManager::cleanAppId */ public static function cleanAppId(string $app): string { - return str_replace(['\0', '/', '\\', '..'], '', $app); + return str_replace(['<', '>', '"', "'", '\0', '/', '\\', '..'], '', $app); } /** @@ -97,9 +55,10 @@ class OC_App { * * @param string $app * @return bool + * @deprecated 27.0.0 use IAppManager::isAppLoaded */ public static function isAppLoaded(string $app): bool { - return isset(self::$loadedApps[$app]); + return \OC::$server->get(IAppManager::class)->isAppLoaded($app); } /** @@ -108,47 +67,20 @@ class OC_App { * @param string[] $types * @return bool * - * This function walks through the ownCloud directory and loads all apps + * This function walks through the Nextcloud directory and loads all apps * it can find. A directory contains an app if the file /appinfo/info.xml * exists. * * if $types is set to non-empty array, only apps of those types will be loaded + * + * @deprecated 29.0.0 use IAppManager::loadApps instead */ public static function loadApps(array $types = []): bool { - if ((bool) \OC::$server->getSystemConfig()->getValue('maintenance', false)) { + if (!\OC::$server->getSystemConfig()->getValue('installed', false)) { + // This should be done before calling this method so that appmanager can be used return false; } - // Load the enabled apps here - $apps = self::getEnabledApps(); - - // Add each apps' folder as allowed class path - foreach ($apps as $app) { - // If the app is already loaded then autoloading it makes no sense - if (!isset(self::$loadedApps[$app])) { - $path = self::getAppPath($app); - if ($path !== false) { - self::registerAutoloading($app, $path); - } - } - } - - // prevent app.php from printing output - ob_start(); - foreach ($apps as $app) { - if (!isset(self::$loadedApps[$app]) && ($types === [] || self::isType($app, $types))) { - try { - self::loadApp($app); - } catch (\Throwable $e) { - \OC::$server->get(LoggerInterface::class)->emergency('Error during app loading: ' . $e->getMessage(), [ - 'exception' => $e, - 'app' => $app, - ]); - } - } - } - ob_end_clean(); - - return true; + return \OC::$server->get(IAppManager::class)->loadApps($types); } /** @@ -156,110 +88,10 @@ class OC_App { * * @param string $app * @throws Exception + * @deprecated 27.0.0 use IAppManager::loadApp */ - public static function loadApp(string $app) { - self::$loadedApps[$app] = true; - $appPath = self::getAppPath($app); - if ($appPath === false) { - return; - } - - // in case someone calls loadApp() directly - self::registerAutoloading($app, $appPath); - - /** @var Coordinator $coordinator */ - $coordinator = \OC::$server->query(Coordinator::class); - $isBootable = $coordinator->isBootable($app); - - $hasAppPhpFile = is_file($appPath . '/appinfo/app.php'); - - \OC::$server->getEventLogger()->start('bootstrap:load_app_' . $app, 'Load app: ' . $app); - if ($isBootable && $hasAppPhpFile) { - \OC::$server->getLogger()->error('/appinfo/app.php is not loaded when \OCP\AppFramework\Bootstrap\IBootstrap on the application class is used. Migrate everything from app.php to the Application class.', [ - 'app' => $app, - ]); - } elseif ($hasAppPhpFile) { - \OC::$server->getLogger()->debug('/appinfo/app.php is deprecated, use \OCP\AppFramework\Bootstrap\IBootstrap on the application class instead.', [ - 'app' => $app, - ]); - try { - self::requireAppFile($app); - } catch (Throwable $ex) { - if ($ex instanceof ServerNotAvailableException) { - throw $ex; - } - if (!\OC::$server->getAppManager()->isShipped($app) && !self::isType($app, ['authentication'])) { - \OC::$server->getLogger()->logException($ex, [ - 'message' => "App $app threw an error during app.php load and will be disabled: " . $ex->getMessage(), - ]); - - // Only disable apps which are not shipped and that are not authentication apps - \OC::$server->getAppManager()->disableApp($app, true); - } else { - \OC::$server->getLogger()->logException($ex, [ - 'message' => "App $app threw an error during app.php load: " . $ex->getMessage(), - ]); - } - } - } - \OC::$server->getEventLogger()->end('bootstrap:load_app_' . $app); - - $coordinator->bootApp($app); - - $info = self::getAppInfo($app); - if (!empty($info['activity']['filters'])) { - foreach ($info['activity']['filters'] as $filter) { - \OC::$server->getActivityManager()->registerFilter($filter); - } - } - if (!empty($info['activity']['settings'])) { - foreach ($info['activity']['settings'] as $setting) { - \OC::$server->getActivityManager()->registerSetting($setting); - } - } - if (!empty($info['activity']['providers'])) { - foreach ($info['activity']['providers'] as $provider) { - \OC::$server->getActivityManager()->registerProvider($provider); - } - } - - if (!empty($info['settings']['admin'])) { - foreach ($info['settings']['admin'] as $setting) { - \OC::$server->get(ISettingsManager::class)->registerSetting('admin', $setting); - } - } - if (!empty($info['settings']['admin-section'])) { - foreach ($info['settings']['admin-section'] as $section) { - \OC::$server->get(ISettingsManager::class)->registerSection('admin', $section); - } - } - if (!empty($info['settings']['personal'])) { - foreach ($info['settings']['personal'] as $setting) { - \OC::$server->get(ISettingsManager::class)->registerSetting('personal', $setting); - } - } - if (!empty($info['settings']['personal-section'])) { - foreach ($info['settings']['personal-section'] as $section) { - \OC::$server->get(ISettingsManager::class)->registerSection('personal', $section); - } - } - - if (!empty($info['collaboration']['plugins'])) { - // deal with one or many plugin entries - $plugins = isset($info['collaboration']['plugins']['plugin']['@value']) ? - [$info['collaboration']['plugins']['plugin']] : $info['collaboration']['plugins']['plugin']; - foreach ($plugins as $plugin) { - if ($plugin['@attributes']['type'] === 'collaborator-search') { - $pluginInfo = [ - 'shareType' => $plugin['@attributes']['share-type'], - 'class' => $plugin['@value'], - ]; - \OC::$server->getCollaboratorSearch()->registerPlugin($pluginInfo); - } elseif ($plugin['@attributes']['type'] === 'autocomplete-sort') { - \OC::$server->getAutoCompleteManager()->registerSorter($plugin['@value']); - } - } - } + public static function loadApp(string $app): void { + \OC::$server->get(IAppManager::class)->loadApp($app); } /** @@ -284,8 +116,6 @@ class OC_App { require_once $path . '/composer/autoload.php'; } else { \OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true); - // Register on legacy autoloader - \OC::$loader->addValidRoot($path); } // Register Test namespace only when testing @@ -295,50 +125,15 @@ class OC_App { } /** - * Load app.php from the given app - * - * @param string $app app name - * @throws Error - */ - private static function requireAppFile(string $app) { - // encapsulated here to avoid variable scope conflicts - require_once $app . '/appinfo/app.php'; - } - - /** * check if an app is of a specific type * * @param string $app * @param array $types * @return bool + * @deprecated 27.0.0 use IAppManager::isType */ public static function isType(string $app, array $types): bool { - $appTypes = self::getAppTypes($app); - foreach ($types as $type) { - if (array_search($type, $appTypes) !== false) { - return true; - } - } - return false; - } - - /** - * get the types of an app - * - * @param string $app - * @return array - */ - private static function getAppTypes(string $app): array { - //load the cache - if (count(self::$appTypes) == 0) { - self::$appTypes = \OC::$server->getAppConfig()->getValues(false, 'types'); - } - - if (isset(self::$appTypes[$app])) { - return explode(',', self::$appTypes[$app]); - } - - return []; + return \OC::$server->get(IAppManager::class)->isType($app, $types); } /** @@ -374,8 +169,8 @@ class OC_App { * * @param bool $forceRefresh whether to refresh the cache * @param bool $all whether to return apps for all users, not only the - * currently logged in one - * @return string[] + * currently logged in one + * @return list<string> */ public static function getEnabledApps(bool $forceRefresh = false, bool $all = false): array { if (!\OC::$server->getSystemConfig()->getValue('installed', false)) { @@ -391,7 +186,7 @@ class OC_App { } if (is_null($user)) { - $apps = $appManager->getInstalledApps(); + $apps = $appManager->getEnabledApps(); } else { $apps = $appManager->getEnabledAppsForUser($user); } @@ -404,19 +199,6 @@ class OC_App { } /** - * checks whether or not an app is enabled - * - * @param string $app app - * @return bool - * @deprecated 13.0.0 use \OC::$server->getAppManager()->isEnabledForUser($appId) - * - * This function checks whether or not an app is enabled. - */ - public static function isEnabled(string $app): bool { - return \OC::$server->getAppManager()->isEnabledForUser($app); - } - - /** * enables an app * * @param string $appId @@ -427,11 +209,10 @@ class OC_App { * This function set an app as enabled in appconfig. */ public function enable(string $appId, - array $groups = []) { - + array $groups = []) { // Check if app is already downloaded /** @var Installer $installer */ - $installer = \OC::$server->query(Installer::class); + $installer = Server::get(Installer::class); $isDownloaded = $installer->isDownloaded($appId); if (!$isDownloaded) { @@ -457,36 +238,22 @@ class OC_App { } /** - * Get the path where to install apps + * Find the apps root for an app id. * - * @return string|false - */ - public static function getInstallPath() { - foreach (OC::$APPSROOTS as $dir) { - if (isset($dir['writable']) && $dir['writable'] === true) { - return $dir['path']; - } - } - - \OCP\Util::writeLog('core', 'No application directories are marked as writable.', ILogger::ERROR); - return null; - } - - - /** - * search for an app in all app-directories + * If multiple copies are found, the apps root the latest version is returned. * * @param string $appId - * @return false|string + * @param bool $ignoreCache ignore cache and rebuild it + * @return false|array{path: string, url: string} the apps root shape */ - public static function findAppInDirectories(string $appId) { + public static function findAppInDirectories(string $appId, bool $ignoreCache = false) { $sanitizedAppId = self::cleanAppId($appId); if ($sanitizedAppId !== $appId) { return false; } static $app_dir = []; - if (isset($app_dir[$appId])) { + if (isset($app_dir[$appId]) && !$ignoreCache) { return $app_dir[$appId]; } @@ -527,15 +294,19 @@ class OC_App { * @psalm-taint-specialize * * @param string $appId + * @param bool $refreshAppPath should be set to true only during install/upgrade * @return string|false - * @deprecated 11.0.0 use \OC::$server->getAppManager()->getAppPath() + * @deprecated 11.0.0 use Server::get(IAppManager)->getAppPath() */ - public static function getAppPath(string $appId) { - if ($appId === null || trim($appId) === '') { + public static function getAppPath(string $appId, bool $refreshAppPath = false) { + $appId = self::cleanAppId($appId); + if ($appId === '') { return false; + } elseif ($appId === 'core') { + return __DIR__ . '/../../../core'; } - if (($dir = self::findAppInDirectories($appId)) != false) { + if (($dir = self::findAppInDirectories($appId, $refreshAppPath)) != false) { return $dir['path'] . '/' . $appId; } return false; @@ -557,18 +328,6 @@ class OC_App { } /** - * get the last version of the app from appinfo/info.xml - * - * @param string $appId - * @param bool $useCache - * @return string - * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppVersion() - */ - public static function getAppVersion(string $appId, bool $useCache = true): string { - return \OC::$server->getAppManager()->getAppVersion($appId, $useCache); - } - - /** * get app's version based on it's path * * @param string $path @@ -576,51 +335,8 @@ class OC_App { */ public static function getAppVersionByPath(string $path): string { $infoFile = $path . '/appinfo/info.xml'; - $appData = \OC::$server->getAppManager()->getAppInfo($infoFile, true); - return isset($appData['version']) ? $appData['version'] : ''; - } - - - /** - * Read all app metadata from the info.xml file - * - * @param string $appId id of the app or the path of the info.xml file - * @param bool $path - * @param string $lang - * @return array|null - * @note all data is read from info.xml, not just pre-defined fields - * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppInfo() - */ - public static function getAppInfo(string $appId, bool $path = false, string $lang = null) { - return \OC::$server->getAppManager()->getAppInfo($appId, $path, $lang); - } - - /** - * Returns the navigation - * - * @return array - * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll() - * - * This function returns an array containing all entries added. The - * entries are sorted by the key 'order' ascending. Additional to the keys - * given for each app the following keys exist: - * - active: boolean, signals if the user is on this navigation entry - */ - public static function getNavigation(): array { - return OC::$server->getNavigationManager()->getAll(); - } - - /** - * Returns the Settings Navigation - * - * @return string[] - * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll('settings') - * - * This function returns an array containing all settings pages added. The - * entries are sorted by the key 'order' ascending. - */ - public static function getSettingsNavigation(): array { - return OC::$server->getNavigationManager()->getAll('settings'); + $appData = Server::get(IAppManager::class)->getAppInfoByPath($infoFile); + return $appData['version'] ?? ''; } /** @@ -657,52 +373,11 @@ class OC_App { } /** - * @param string $type - * @return array - */ - public static function getForms(string $type): array { - $forms = []; - switch ($type) { - case 'admin': - $source = self::$adminForms; - break; - case 'personal': - $source = self::$personalForms; - break; - default: - return []; - } - foreach ($source as $form) { - $forms[] = include $form; - } - return $forms; - } - - /** - * register an admin form to be shown - * - * @param string $app - * @param string $page - */ - public static function registerAdmin(string $app, string $page) { - self::$adminForms[] = $app . '/' . $page . '.php'; - } - - /** - * register a personal form to be shown - * @param string $app - * @param string $page - */ - public static function registerPersonal(string $app, string $page) { - self::$personalForms[] = $app . '/' . $page . '.php'; - } - - /** * @param array $entry * @deprecated 20.0.0 Please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface */ public static function registerLogIn(array $entry) { - \OC::$server->getLogger()->debug('OC_App::registerLogIn() is deprecated, please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface'); + Server::get(LoggerInterface::class)->debug('OC_App::registerLogIn() is deprecated, please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface'); self::$altLogin[] = $entry; } @@ -711,11 +386,11 @@ class OC_App { */ public static function getAlternativeLogIns(): array { /** @var Coordinator $bootstrapCoordinator */ - $bootstrapCoordinator = \OC::$server->query(Coordinator::class); + $bootstrapCoordinator = Server::get(Coordinator::class); foreach ($bootstrapCoordinator->getRegistrationContext()->getAlternativeLogins() as $registration) { if (!in_array(IAlternativeLogin::class, class_implements($registration->getService()), true)) { - \OC::$server->getLogger()->error('Alternative login option {option} does not implement {interface} and is therefore ignored.', [ + Server::get(LoggerInterface::class)->error('Alternative login option {option} does not implement {interface} and is therefore ignored.', [ 'option' => $registration->getService(), 'interface' => IAlternativeLogin::class, 'app' => $registration->getAppId(), @@ -725,13 +400,14 @@ class OC_App { try { /** @var IAlternativeLogin $provider */ - $provider = \OC::$server->query($registration->getService()); - } catch (QueryException $e) { - \OC::$server->getLogger()->logException($e, [ - 'message' => 'Alternative login option {option} can not be initialised.', - 'option' => $registration->getService(), - 'app' => $registration->getAppId(), - ]); + $provider = Server::get($registration->getService()); + } catch (ContainerExceptionInterface $e) { + Server::get(LoggerInterface::class)->error('Alternative login option {option} can not be initialized.', + [ + 'exception' => $e, + 'option' => $registration->getService(), + 'app' => $registration->getAppId(), + ]); } try { @@ -740,14 +416,15 @@ class OC_App { self::$altLogin[] = [ 'name' => $provider->getLabel(), 'href' => $provider->getLink(), - 'style' => $provider->getClass(), + 'class' => $provider->getClass(), ]; } catch (Throwable $e) { - \OC::$server->getLogger()->logException($e, [ - 'message' => 'Alternative login option {option} had an error while loading.', - 'option' => $registration->getService(), - 'app' => $registration->getAppId(), - ]); + Server::get(LoggerInterface::class)->error('Alternative login option {option} had an error while loading.', + [ + 'exception' => $e, + 'option' => $registration->getService(), + 'app' => $registration->getAppId(), + ]); } } @@ -758,40 +435,19 @@ class OC_App { * get a list of all apps in the apps folder * * @return string[] an array of app names (string IDs) - * @todo: change the name of this method to getInstalledApps, which is more accurate + * @deprecated 31.0.0 Use IAppManager::getAllAppsInAppsFolders instead */ public static function getAllApps(): array { - $apps = []; - - foreach (OC::$APPSROOTS as $apps_dir) { - if (!is_readable($apps_dir['path'])) { - \OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], ILogger::WARN); - continue; - } - $dh = opendir($apps_dir['path']); - - if (is_resource($dh)) { - while (($file = readdir($dh)) !== false) { - if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) { - $apps[] = $file; - } - } - } - } - - $apps = array_unique($apps); - - return $apps; + return Server::get(IAppManager::class)->getAllAppsInAppsFolders(); } /** * List all supported apps * - * @return array + * @deprecated 32.0.0 Use \OCP\Support\Subscription\IRegistry::delegateGetSupportedApps instead */ public function getSupportedApps(): array { - /** @var \OCP\Support\Subscription\IRegistry $subscriptionRegistry */ - $subscriptionRegistry = \OC::$server->query(\OCP\Support\Subscription\IRegistry::class); + $subscriptionRegistry = Server::get(\OCP\Support\Subscription\IRegistry::class); $supportedApps = $subscriptionRegistry->delegateGetSupportedApps(); return $supportedApps; } @@ -802,9 +458,9 @@ class OC_App { * @return array */ public function listAllApps(): array { - $installedApps = OC_App::getAllApps(); - $appManager = \OC::$server->getAppManager(); + + $installedApps = $appManager->getAllAppsInAppsFolders(); //we don't want to show configuration for these $blacklist = $appManager->getAlwaysEnabledApps(); $appList = []; @@ -813,15 +469,15 @@ class OC_App { $supportedApps = $this->getSupportedApps(); foreach ($installedApps as $app) { - if (array_search($app, $blacklist) === false) { - $info = OC_App::getAppInfo($app, false, $langCode); + if (!in_array($app, $blacklist)) { + $info = $appManager->getAppInfo($app, false, $langCode); if (!is_array($info)) { - \OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', ILogger::ERROR); + Server::get(LoggerInterface::class)->error('Could not read app info file for app "' . $app . '"', ['app' => 'core']); continue; } if (!isset($info['name'])) { - \OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', ILogger::ERROR); + Server::get(LoggerInterface::class)->error('App id "' . $app . '" has no name in appinfo', ['app' => 'core']); continue; } @@ -878,7 +534,7 @@ class OC_App { } } - $info['version'] = OC_App::getAppVersion($app); + $info['version'] = $appManager->getAppVersion($app); $appList[] = $info; } } @@ -888,7 +544,7 @@ class OC_App { public static function shouldUpgrade(string $app): bool { $versions = self::getAppVersions(); - $currentVersion = OC_App::getAppVersion($app); + $currentVersion = Server::get(\OCP\App\IAppManager::class)->getAppVersion($app); if ($currentVersion && isset($versions[$app])) { $installedVersion = $versions[$app]; if (!version_compare($currentVersion, $installedVersion, '=')) { @@ -921,7 +577,7 @@ class OC_App { } /** - * Check whether the current ownCloud version matches the given + * Check whether the current Nextcloud version matches the given * application's version requirements. * * The comparison is made based on the number of parts that the @@ -931,7 +587,7 @@ class OC_App { * This means that it's possible to specify "requiremin" => 6 * and "requiremax" => 6 and it will still match ownCloud 6.0.3. * - * @param string $ocVersion ownCloud version to check against + * @param string $ocVersion Nextcloud version to check against * @param array $appInfo app info (from xml) * * @return boolean true if compatible, otherwise false @@ -974,15 +630,10 @@ class OC_App { /** * get the installed version of all apps + * @deprecated 32.0.0 Use IAppManager::getAppInstalledVersions or IAppConfig::getAppInstalledVersions instead */ - public static function getAppVersions() { - static $versions; - - if (!$versions) { - $appConfig = \OC::$server->getAppConfig(); - $versions = $appConfig->getValues(false, 'installed_version'); - } - return $versions; + public static function getAppVersions(): array { + return Server::get(IAppConfig::class)->getAppInstalledVersions(); } /** @@ -992,19 +643,21 @@ class OC_App { * @return bool */ public static function updateApp(string $appId): bool { - $appPath = self::getAppPath($appId); + // for apps distributed with core, we refresh app path in case the downloaded version + // have been installed in custom apps and not in the default path + $appPath = self::getAppPath($appId, true); if ($appPath === false) { return false; } if (is_file($appPath . '/appinfo/database.xml')) { - \OC::$server->getLogger()->error('The appinfo/database.xml file is not longer supported. Used in ' . $appId); + Server::get(LoggerInterface::class)->error('The appinfo/database.xml file is not longer supported. Used in ' . $appId); return false; } \OC::$server->getAppManager()->clearAppsCache(); $l = \OC::$server->getL10N('core'); - $appData = self::getAppInfo($appId, false, $l->getLanguageCode()); + $appData = Server::get(\OCP\App\IAppManager::class)->getAppInfo($appId, false, $l->getLanguageCode()); $ignoreMaxApps = \OC::$server->getConfig()->getSystemValue('app_install_overwrite', []); $ignoreMax = in_array($appId, $ignoreMaxApps, true); @@ -1044,10 +697,15 @@ class OC_App { self::setAppTypes($appId); - $version = \OC_App::getAppVersion($appId); + $version = Server::get(\OCP\App\IAppManager::class)->getAppVersion($appId); \OC::$server->getConfig()->setAppValue($appId, 'installed_version', $version); - \OC::$server->getEventDispatcher()->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent( + // migrate eventual new config keys in the process + /** @psalm-suppress InternalMethod */ + Server::get(ConfigManager::class)->migrateConfigLexiconKeys($appId); + + \OC::$server->get(IEventDispatcher::class)->dispatchTyped(new AppUpdateEvent($appId)); + \OC::$server->get(IEventDispatcher::class)->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent( ManagerEvent::EVENT_APP_UPDATE, $appId )); @@ -1066,16 +724,16 @@ class OC_App { // load the app self::loadApp($appId); - $dispatcher = OC::$server->getEventDispatcher(); + $dispatcher = Server::get(IEventDispatcher::class); // load the steps - $r = new Repair([], $dispatcher, \OC::$server->get(LoggerInterface::class)); + $r = Server::get(Repair::class); foreach ($steps as $step) { try { $r->addStep($step); } catch (Exception $ex) { - $r->emit('\OC\Repair', 'error', [$ex->getMessage()]); - \OC::$server->getLogger()->logException($ex); + $dispatcher->dispatchTyped(new RepairErrorEvent($ex->getMessage())); + logger('core')->error('Failed to add app migration step ' . $step, ['exception' => $ex]); } } // run the steps @@ -1103,103 +761,6 @@ class OC_App { } /** - * @param string $appId - * @return \OC\Files\View|false - */ - public static function getStorage(string $appId) { - if (\OC::$server->getAppManager()->isEnabledForUser($appId)) { //sanity check - if (\OC::$server->getUserSession()->isLoggedIn()) { - $view = new \OC\Files\View('/' . OC_User::getUser()); - if (!$view->file_exists($appId)) { - $view->mkdir($appId); - } - return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId); - } else { - \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', ILogger::ERROR); - return false; - } - } else { - \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', ILogger::ERROR); - return false; - } - } - - protected static function findBestL10NOption(array $options, string $lang): string { - // only a single option - if (isset($options['@value'])) { - return $options['@value']; - } - - $fallback = $similarLangFallback = $englishFallback = false; - - $lang = strtolower($lang); - $similarLang = $lang; - if (strpos($similarLang, '_')) { - // For "de_DE" we want to find "de" and the other way around - $similarLang = substr($lang, 0, strpos($lang, '_')); - } - - foreach ($options as $option) { - if (is_array($option)) { - if ($fallback === false) { - $fallback = $option['@value']; - } - - if (!isset($option['@attributes']['lang'])) { - continue; - } - - $attributeLang = strtolower($option['@attributes']['lang']); - if ($attributeLang === $lang) { - return $option['@value']; - } - - if ($attributeLang === $similarLang) { - $similarLangFallback = $option['@value']; - } elseif (strpos($attributeLang, $similarLang . '_') === 0) { - if ($similarLangFallback === false) { - $similarLangFallback = $option['@value']; - } - } - } else { - $englishFallback = $option; - } - } - - if ($similarLangFallback !== false) { - return $similarLangFallback; - } elseif ($englishFallback !== false) { - return $englishFallback; - } - return (string) $fallback; - } - - /** - * parses the app data array and enhanced the 'description' value - * - * @param array $data the app data - * @param string $lang - * @return array improved app data - */ - public static function parseAppInfo(array $data, $lang = null): array { - if ($lang && isset($data['name']) && is_array($data['name'])) { - $data['name'] = self::findBestL10NOption($data['name'], $lang); - } - if ($lang && isset($data['summary']) && is_array($data['summary'])) { - $data['summary'] = self::findBestL10NOption($data['summary'], $lang); - } - if ($lang && isset($data['description']) && is_array($data['description'])) { - $data['description'] = trim(self::findBestL10NOption($data['description'], $lang)); - } elseif (isset($data['description']) && is_string($data['description'])) { - $data['description'] = trim($data['description']); - } else { - $data['description'] = ''; - } - - return $data; - } - - /** * @param \OCP\IConfig $config * @param \OCP\IL10N $l * @param array $info |