From a7c8090dc3c47c79ed9e7e22967cba7d124f0fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Tue, 7 Feb 2023 16:56:04 +0100 Subject: [PATCH 1/8] Move loadApp function to the AppManager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/private/App/AppManager.php | 139 +++++++++++++++++++++++++++++++++ lib/private/legacy/OC_App.php | 134 ++----------------------------- lib/public/App/IAppManager.php | 14 ++++ 3 files changed, 158 insertions(+), 129 deletions(-) diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php index 0a89711f178..0b0bcecb5dc 100644 --- a/lib/private/App/AppManager.php +++ b/lib/private/App/AppManager.php @@ -39,6 +39,8 @@ namespace OC\App; use OC\AppConfig; +use OC\AppFramework\Bootstrap\Coordinator; +use OC\ServerNotAvailableException; use OCP\App\AppPathNotFoundException; use OCP\App\Events\AppDisableEvent; use OCP\App\Events\AppEnableEvent; @@ -51,6 +53,7 @@ use OCP\IGroup; use OCP\IGroupManager; use OCP\IUser; use OCP\IUserSession; +use OCP\Settings\IManager as ISettingsManager; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -108,6 +111,9 @@ class AppManager implements IAppManager { /** @var array */ private $autoDisabledApps = []; + /** @var array */ + private array $loadedApps = []; + public function __construct(IUserSession $userSession, IConfig $config, AppConfig $appConfig, @@ -310,6 +316,139 @@ class AppManager implements IAppManager { } } + public function loadApp(string $app): void { + if (isset($this->loadedApps[$app])) { + return; + } + $this->loadedApps[$app] = true; + $appPath = \OC_App::getAppPath($app); + if ($appPath === false) { + return; + } + $eventLogger = \OC::$server->get(\OCP\Diagnostics\IEventLogger::class); + $eventLogger->start("bootstrap:load_app:$app", "Load $app"); + + // in case someone calls loadApp() directly + \OC_App::registerAutoloading($app, $appPath); + + /** @var Coordinator $coordinator */ + $coordinator = \OC::$server->get(Coordinator::class); + $isBootable = $coordinator->isBootable($app); + + $hasAppPhpFile = is_file($appPath . '/appinfo/app.php'); + + 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) { + $eventLogger->start("bootstrap:load_app:$app:app.php", "Load legacy app.php app $app"); + \OC::$server->getLogger()->debug('/appinfo/app.php is deprecated, use \OCP\AppFramework\Bootstrap\IBootstrap on the application class instead.', [ + 'app' => $app, + ]); + try { + self::requireAppFile($appPath); + } 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(), + ]); + } + } + $eventLogger->end("bootstrap:load_app:$app:app.php"); + } + + $coordinator->bootApp($app); + + $eventLogger->start("bootstrap:load_app:$app:info", "Load info.xml for $app and register any services defined in it"); + $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']); + } + } + } + $eventLogger->end("bootstrap:load_app:$app:info"); + + $eventLogger->end("bootstrap:load_app:$app"); + } + /** + * Check if an app is loaded + * @param string $app app id + * @since 26.0.0 + */ + public function isAppLoaded(string $app): bool { + return isset($this->loadedApps[$app]); + } + + /** + * Load app.php from the given app + * + * @param string $app app name + * @throws \Error + */ + private static function requireAppFile(string $app): void { + // encapsulated here to avoid variable scope conflicts + require_once $app . '/appinfo/app.php'; + } + /** * Enable an app for every user * diff --git a/lib/private/legacy/OC_App.php b/lib/private/legacy/OC_App.php index 3c255e91661..01bce057881 100644 --- a/lib/private/legacy/OC_App.php +++ b/lib/private/legacy/OC_App.php @@ -53,11 +53,11 @@ declare(strict_types=1); use OCP\App\Events\AppUpdateEvent; use OCP\AppFramework\QueryException; +use OCP\App\IAppManager; use OCP\App\ManagerEvent; use OCP\Authentication\IAlternativeLogin; use OCP\EventDispatcher\IEventDispatcher; use OCP\ILogger; -use OCP\Settings\IManager as ISettingsManager; use OC\AppFramework\Bootstrap\Coordinator; use OC\App\DependencyAnalyzer; use OC\App\Platform; @@ -65,7 +65,6 @@ use OC\DB\MigrationService; use OC\Installer; use OC\Repair; use OC\Repair\Events\RepairErrorEvent; -use OC\ServerNotAvailableException; use Psr\Log\LoggerInterface; /** @@ -77,7 +76,6 @@ 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; @@ -103,7 +101,7 @@ class OC_App { * @return bool */ public static function isAppLoaded(string $app): bool { - return isset(self::$loadedApps[$app]); + return \OC::$server->get(IAppManager::class)->isAppLoaded($app); } /** @@ -128,7 +126,7 @@ class OC_App { // 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])) { + if (!self::isAppLoaded($app)) { $path = self::getAppPath($app); if ($path !== false) { self::registerAutoloading($app, $path); @@ -139,7 +137,7 @@ class OC_App { // prevent app.php from printing output ob_start(); foreach ($apps as $app) { - if (!isset(self::$loadedApps[$app]) && ($types === [] || self::isType($app, $types))) { + if (!self::isAppLoaded($app) && ($types === [] || self::isType($app, $types))) { try { self::loadApp($app); } catch (\Throwable $e) { @@ -162,118 +160,7 @@ class OC_App { * @throws Exception */ public static function loadApp(string $app): void { - if (isset(self::$loadedApps[$app])) { - return; - } - self::$loadedApps[$app] = true; - $appPath = self::getAppPath($app); - if ($appPath === false) { - return; - } - $eventLogger = \OC::$server->get(\OCP\Diagnostics\IEventLogger::class); - $eventLogger->start("bootstrap:load_app:$app", "Load $app"); - - // 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'); - - 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) { - $eventLogger->start("bootstrap:load_app:$app:app.php", "Load legacy app.php app $app"); - \OC::$server->getLogger()->debug('/appinfo/app.php is deprecated, use \OCP\AppFramework\Bootstrap\IBootstrap on the application class instead.', [ - 'app' => $app, - ]); - try { - self::requireAppFile($appPath); - } 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(), - ]); - } - } - $eventLogger->end("bootstrap:load_app:$app:app.php"); - } - - $coordinator->bootApp($app); - - $eventLogger->start("bootstrap:load_app:$app:info", "Load info.xml for $app and register any services defined in it"); - $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']); - } - } - } - - $eventLogger->end("bootstrap:load_app:$app:info"); - - $eventLogger->end("bootstrap:load_app:$app"); + \OC::$server->get(IAppManager::class)->loadApp($app); } /** @@ -306,17 +193,6 @@ 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 * diff --git a/lib/public/App/IAppManager.php b/lib/public/App/IAppManager.php index de36fafcdfe..51bd5845b7c 100644 --- a/lib/public/App/IAppManager.php +++ b/lib/public/App/IAppManager.php @@ -93,6 +93,20 @@ interface IAppManager { */ public function isDefaultEnabled(string $appId):bool; + /** + * Load an app, if not already loaded + * @param string $app app id + * @since 26.0.0 + */ + public function loadApp(string $app): void; + + /** + * Check if an app is loaded + * @param string $app app id + * @since 26.0.0 + */ + public function isAppLoaded(string $app): bool; + /** * Enable an app for every user * From 71ed968e34b595bd515db29c25a3af32901cf97c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Tue, 7 Feb 2023 17:01:13 +0100 Subject: [PATCH 2/8] Switch away from Server::get for AppManager and logger in loadApp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/private/App/AppManager.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php index 0b0bcecb5dc..0ee8dccaeb7 100644 --- a/lib/private/App/AppManager.php +++ b/lib/private/App/AppManager.php @@ -338,12 +338,12 @@ class AppManager implements IAppManager { $hasAppPhpFile = is_file($appPath . '/appinfo/app.php'); 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.', [ + $this->logger->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) { $eventLogger->start("bootstrap:load_app:$app:app.php", "Load legacy app.php app $app"); - \OC::$server->getLogger()->debug('/appinfo/app.php is deprecated, use \OCP\AppFramework\Bootstrap\IBootstrap on the application class instead.', [ + $this->logger->debug('/appinfo/app.php is deprecated, use \OCP\AppFramework\Bootstrap\IBootstrap on the application class instead.', [ 'app' => $app, ]); try { @@ -352,16 +352,16 @@ class AppManager implements IAppManager { 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(), + if (!$this->isShipped($app) && !\OC_App::isType($app, ['authentication'])) { + $this->logger->error("App $app threw an error during app.php load and will be disabled: " . $ex->getMessage(), [ + 'exception' => $ex, ]); // Only disable apps which are not shipped and that are not authentication apps - \OC::$server->getAppManager()->disableApp($app, true); + $this->disableApp($app, true); } else { - \OC::$server->getLogger()->logException($ex, [ - 'message' => "App $app threw an error during app.php load: " . $ex->getMessage(), + $this->logger->error("App $app threw an error during app.php load: " . $ex->getMessage(), [ + 'exception' => $ex, ]); } } From f0bdf2a4cd01c01fc26a2a1b94d942c0f783418e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Wed, 8 Feb 2023 08:59:35 +0100 Subject: [PATCH 3/8] Strong type private properties and methods in AppManager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/private/App/AppManager.php | 60 ++++++++++------------------------ 1 file changed, 17 insertions(+), 43 deletions(-) diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php index 0ee8dccaeb7..3799897ffdd 100644 --- a/lib/private/App/AppManager.php +++ b/lib/private/App/AppManager.php @@ -70,46 +70,32 @@ class AppManager implements IAppManager { 'prevent_group_restriction', ]; - /** @var IUserSession */ - private $userSession; - - /** @var IConfig */ - private $config; - - /** @var AppConfig */ - private $appConfig; - - /** @var IGroupManager */ - private $groupManager; - - /** @var ICacheFactory */ - private $memCacheFactory; - - /** @var EventDispatcherInterface */ - private $legacyDispatcher; - + private IUserSession $userSession; + private IConfig $config; + private AppConfig $appConfig; + private IGroupManager $groupManager; + private ICacheFactory $memCacheFactory; + private EventDispatcherInterface $legacyDispatcher; private IEventDispatcher $dispatcher; - - /** @var LoggerInterface */ - private $logger; + private LoggerInterface $logger; /** @var string[] $appId => $enabled */ - private $installedAppsCache; + private array $installedAppsCache = []; - /** @var string[] */ - private $shippedApps; + /** @var string[]|null */ + private ?array $shippedApps = null; private array $alwaysEnabled = []; private array $defaultEnabled = []; /** @var array */ - private $appInfos = []; + private array $appInfos = []; /** @var array */ - private $appVersions = []; + private array $appVersions = []; /** @var array */ - private $autoDisabledApps = []; + private array $autoDisabledApps = []; /** @var array */ private array $loadedApps = []; @@ -135,7 +121,7 @@ class AppManager implements IAppManager { /** * @return string[] $appId => $enabled */ - private function getInstalledAppsValues() { + private function getInstalledAppsValues(): array { if (!$this->installedAppsCache) { $values = $this->appConfig->getValues(false, 'enabled'); @@ -234,12 +220,7 @@ class AppManager implements IAppManager { } } - /** - * @param string $enabled - * @param IUser $user - * @return bool - */ - private function checkAppForUser($enabled, $user) { + private function checkAppForUser(string $enabled, ?IUser $user): bool { if ($enabled === 'yes') { return true; } elseif ($user === null) { @@ -267,16 +248,9 @@ class AppManager implements IAppManager { } } - /** - * @param string $enabled - * @param IGroup $group - * @return bool - */ private function checkAppForGroups(string $enabled, IGroup $group): bool { if ($enabled === 'yes') { return true; - } elseif ($group === null) { - return false; } else { if (empty($enabled)) { return false; @@ -706,7 +680,7 @@ class AppManager implements IAppManager { return in_array($appId, $this->shippedApps, true); } - private function isAlwaysEnabled($appId) { + private function isAlwaysEnabled(string $appId): bool { $alwaysEnabled = $this->getAlwaysEnabledApps(); return in_array($appId, $alwaysEnabled, true); } @@ -715,7 +689,7 @@ class AppManager implements IAppManager { * In case you change this method, also change \OC\App\CodeChecker\InfoChecker::loadShippedJson() * @throws \Exception */ - private function loadShippedJson() { + private function loadShippedJson(): void { if ($this->shippedApps === null) { $shippedJson = \OC::$SERVERROOT . '/core/shipped.json'; if (!file_exists($shippedJson)) { From a224551132baea6b75b4e14967633b9db12dc37e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Wed, 8 Feb 2023 09:22:22 +0100 Subject: [PATCH 4/8] Avoid calling Server::get more times than necessary in loadApp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/private/App/AppManager.php | 74 +++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php index 3799897ffdd..bd0d85da0ce 100644 --- a/lib/private/App/AppManager.php +++ b/lib/private/App/AppManager.php @@ -41,12 +41,16 @@ namespace OC\App; use OC\AppConfig; use OC\AppFramework\Bootstrap\Coordinator; use OC\ServerNotAvailableException; +use OCP\Activity\IManager as IActivityManager; use OCP\App\AppPathNotFoundException; use OCP\App\Events\AppDisableEvent; use OCP\App\Events\AppEnableEvent; use OCP\App\IAppManager; use OCP\App\ManagerEvent; use OCP\EventDispatcher\IEventDispatcher; +use OCP\Collaboration\AutoComplete\IManager as IAutoCompleteManager; +use OCP\Collaboration\Collaborators\ISearch as ICollaboratorSearch; +use OCP\Diagnostics\IEventLogger; use OCP\ICacheFactory; use OCP\IConfig; use OCP\IGroup; @@ -311,6 +315,8 @@ class AppManager implements IAppManager { $hasAppPhpFile = is_file($appPath . '/appinfo/app.php'); + $eventLogger = \OC::$server->get(IEventLogger::class); + $eventLogger->start('bootstrap:load_app_' . $app, 'Load app: ' . $app); if ($isBootable && $hasAppPhpFile) { $this->logger->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, @@ -345,41 +351,47 @@ class AppManager implements IAppManager { $coordinator->bootApp($app); $eventLogger->start("bootstrap:load_app:$app:info", "Load info.xml for $app and register any services defined in it"); - $info = self::getAppInfo($app); - if (!empty($info['activity']['filters'])) { - foreach ($info['activity']['filters'] as $filter) { - \OC::$server->getActivityManager()->registerFilter($filter); + $info = $this->getAppInfo($app); + if (!empty($info['activity'])) { + $activityManager = \OC::$server->get(IActivityManager::class); + if (!empty($info['activity']['filters'])) { + foreach ($info['activity']['filters'] as $filter) { + $activityManager->registerFilter($filter); + } } - } - if (!empty($info['activity']['settings'])) { - foreach ($info['activity']['settings'] as $setting) { - \OC::$server->getActivityManager()->registerSetting($setting); + if (!empty($info['activity']['settings'])) { + foreach ($info['activity']['settings'] as $setting) { + $activityManager->registerSetting($setting); + } } - } - if (!empty($info['activity']['providers'])) { - foreach ($info['activity']['providers'] as $provider) { - \OC::$server->getActivityManager()->registerProvider($provider); + if (!empty($info['activity']['providers'])) { + foreach ($info['activity']['providers'] as $provider) { + $activityManager->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'])) { + $settingsManager = \OC::$server->get(ISettingsManager::class); + if (!empty($info['settings']['admin'])) { + foreach ($info['settings']['admin'] as $setting) { + $settingsManager->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']['admin-section'])) { + foreach ($info['settings']['admin-section'] as $section) { + $settingsManager->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'])) { + foreach ($info['settings']['personal'] as $setting) { + $settingsManager->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['settings']['personal-section'])) { + foreach ($info['settings']['personal-section'] as $section) { + $settingsManager->registerSection('personal', $section); + } } } @@ -387,15 +399,19 @@ class AppManager implements IAppManager { // deal with one or many plugin entries $plugins = isset($info['collaboration']['plugins']['plugin']['@value']) ? [$info['collaboration']['plugins']['plugin']] : $info['collaboration']['plugins']['plugin']; + $collaboratorSearch = null; + $autoCompleteManager = null; foreach ($plugins as $plugin) { if ($plugin['@attributes']['type'] === 'collaborator-search') { $pluginInfo = [ 'shareType' => $plugin['@attributes']['share-type'], 'class' => $plugin['@value'], ]; - \OC::$server->getCollaboratorSearch()->registerPlugin($pluginInfo); + $collaboratorSearch ??= \OC::$server->get(ICollaboratorSearch::class); + $collaboratorSearch->registerPlugin($pluginInfo); } elseif ($plugin['@attributes']['type'] === 'autocomplete-sort') { - \OC::$server->getAutoCompleteManager()->registerSorter($plugin['@value']); + $autoCompleteManager ??= \OC::$server->get(IAutoCompleteManager::class); + $autoCompleteManager->registerSorter($plugin['@value']); } } } From 633ea018af94692cef8a196319f79eaf2467f061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Wed, 8 Feb 2023 11:13:59 +0100 Subject: [PATCH 5/8] Fix BackgroundCleanupJobTest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- tests/lib/Preview/BackgroundCleanupJobTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/lib/Preview/BackgroundCleanupJobTest.php b/tests/lib/Preview/BackgroundCleanupJobTest.php index c1c225bd179..aa15ea7f562 100644 --- a/tests/lib/Preview/BackgroundCleanupJobTest.php +++ b/tests/lib/Preview/BackgroundCleanupJobTest.php @@ -69,7 +69,7 @@ class BackgroundCleanupJobTest extends \Test\TestCase { parent::setUp(); $this->userId = $this->getUniqueID(); - $this->createUser($this->userId, $this->userId); + $user = $this->createUser($this->userId, $this->userId); $storage = new \OC\Files\Storage\Temporary([]); $this->registerMount($this->userId, $storage, ''); @@ -79,7 +79,7 @@ class BackgroundCleanupJobTest extends \Test\TestCase { $this->loginAsUser($this->userId); $appManager = \OC::$server->getAppManager(); - $this->trashEnabled = $appManager->isEnabledForUser('files_trashbin', $this->userId); + $this->trashEnabled = $appManager->isEnabledForUser('files_trashbin', $user); $appManager->disableApp('files_trashbin'); $this->connection = \OC::$server->getDatabaseConnection(); From 8dc5f8218979a8bf05ac5a8e84649f750dcab5ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Wed, 8 Feb 2023 11:46:07 +0100 Subject: [PATCH 6/8] Move isType to AppManager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/private/App/AppManager.php | 39 +++++++++++++++++++++++++++++++++- lib/private/legacy/OC_App.php | 29 ++----------------------- lib/public/App/IAppManager.php | 6 ++++++ 3 files changed, 46 insertions(+), 28 deletions(-) diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php index bd0d85da0ce..59df44cbd17 100644 --- a/lib/private/App/AppManager.php +++ b/lib/private/App/AppManager.php @@ -100,6 +100,7 @@ class AppManager implements IAppManager { /** @var array */ private array $autoDisabledApps = []; + private array $appTypes = []; /** @var array */ private array $loadedApps = []; @@ -177,6 +178,42 @@ class AppManager implements IAppManager { return array_keys($appsForGroups); } + /** + * check if an app is of a specific type + * + * @param string $app + * @param array $types + * @return bool + */ + public function isType(string $app, array $types): bool { + $appTypes = $this->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 string[] + */ + private function getAppTypes(string $app): array { + //load the cache + if (count($this->appTypes) === 0) { + $this->appTypes = \OC::$server->getAppConfig()->getValues(false, 'types'); + } + + if (isset($this->appTypes[$app])) { + return explode(',', $this->appTypes[$app]); + } + + return []; + } + /** * @return array */ @@ -332,7 +369,7 @@ class AppManager implements IAppManager { if ($ex instanceof ServerNotAvailableException) { throw $ex; } - if (!$this->isShipped($app) && !\OC_App::isType($app, ['authentication'])) { + if (!$this->isShipped($app) && !$this->isType($app, ['authentication'])) { $this->logger->error("App $app threw an error during app.php load and will be disabled: " . $ex->getMessage(), [ 'exception' => $ex, ]); diff --git a/lib/private/legacy/OC_App.php b/lib/private/legacy/OC_App.php index 01bce057881..ec57d43f9a6 100644 --- a/lib/private/legacy/OC_App.php +++ b/lib/private/legacy/OC_App.php @@ -75,7 +75,6 @@ use Psr\Log\LoggerInterface; class OC_App { private static $adminForms = []; private static $personalForms = []; - private static $appTypes = []; private static $altLogin = []; private static $alreadyRegistered = []; public const supportedApp = 300; @@ -199,34 +198,10 @@ class OC_App { * @param string $app * @param array $types * @return bool + * @deprecated 26.0.0 call \OC::$server->get(IAppManager::class)->isType($app, $types) */ 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); } /** diff --git a/lib/public/App/IAppManager.php b/lib/public/App/IAppManager.php index 51bd5845b7c..8440b757d40 100644 --- a/lib/public/App/IAppManager.php +++ b/lib/public/App/IAppManager.php @@ -196,6 +196,12 @@ interface IAppManager { */ public function isShipped($appId); + /** + * Check if an app is of a specific type + * @since 26.0.0 + */ + public function isType(string $app, array $types): bool; + /** * @return string[] * @since 9.0.0 From 78c17168185c21b6b788e40602f95c690fe441f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Wed, 8 Feb 2023 12:35:57 +0100 Subject: [PATCH 7/8] Move loadApps to the AppManager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/private/App/AppManager.php | 49 ++++++++++++++++++++++++++++++++++ lib/private/legacy/OC_App.php | 35 +++--------------------- lib/public/App/IAppManager.php | 14 ++++++++++ 3 files changed, 66 insertions(+), 32 deletions(-) diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php index 59df44cbd17..d969f1d0260 100644 --- a/lib/private/App/AppManager.php +++ b/lib/private/App/AppManager.php @@ -178,6 +178,55 @@ class AppManager implements IAppManager { return array_keys($appsForGroups); } + /** + * Loads all apps + * + * @param string[] $types + * @return bool + * + * 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 + */ + public function loadApps(array $types = []): bool { + if ($this->config->getSystemValueBool('maintenance', false)) { + return false; + } + // Load the enabled apps here + $apps = \OC_App::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 (!$this->isAppLoaded($app)) { + $path = \OC_App::getAppPath($app); + if ($path !== false) { + \OC_App::registerAutoloading($app, $path); + } + } + } + + // prevent app.php from printing output + ob_start(); + foreach ($apps as $app) { + if (!$this->isAppLoaded($app) && ($types === [] || $this->isType($app, $types))) { + try { + $this->loadApp($app); + } catch (\Throwable $e) { + $this->logger->emergency('Error during app loading: ' . $e->getMessage(), [ + 'exception' => $e, + 'app' => $app, + ]); + } + } + } + ob_end_clean(); + + return true; + } + /** * check if an app is of a specific type * diff --git a/lib/private/legacy/OC_App.php b/lib/private/legacy/OC_App.php index ec57d43f9a6..5d7941ffa3f 100644 --- a/lib/private/legacy/OC_App.php +++ b/lib/private/legacy/OC_App.php @@ -116,40 +116,11 @@ class OC_App { * if $types is set to non-empty array, only apps of those types will be loaded */ 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 (!self::isAppLoaded($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 (!self::isAppLoaded($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); } /** diff --git a/lib/public/App/IAppManager.php b/lib/public/App/IAppManager.php index 8440b757d40..5387e104fd9 100644 --- a/lib/public/App/IAppManager.php +++ b/lib/public/App/IAppManager.php @@ -196,6 +196,20 @@ interface IAppManager { */ public function isShipped($appId); + /** + * Loads all apps + * + * @param string[] $types + * @return bool + * + * 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 + */ + public function loadApps(array $types = []): bool; + /** * Check if an app is of a specific type * @since 26.0.0 From 13c71ed24a4f30dd3dfb04bd09f16648fb4fe080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Wed, 8 Feb 2023 13:29:57 +0100 Subject: [PATCH 8/8] Small cleanups for AppManager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/private/App/AppManager.php | 4 ++-- lib/private/legacy/OC_App.php | 4 +++- lib/public/App/IAppManager.php | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php index d969f1d0260..5f243a1250e 100644 --- a/lib/private/App/AppManager.php +++ b/lib/private/App/AppManager.php @@ -237,7 +237,7 @@ class AppManager implements IAppManager { public function isType(string $app, array $types): bool { $appTypes = $this->getAppTypes($app); foreach ($types as $type) { - if (array_search($type, $appTypes) !== false) { + if (in_array($type, $appTypes, true)) { return true; } } @@ -253,7 +253,7 @@ class AppManager implements IAppManager { private function getAppTypes(string $app): array { //load the cache if (count($this->appTypes) === 0) { - $this->appTypes = \OC::$server->getAppConfig()->getValues(false, 'types'); + $this->appTypes = $this->appConfig->getValues(false, 'types') ?: []; } if (isset($this->appTypes[$app])) { diff --git a/lib/private/legacy/OC_App.php b/lib/private/legacy/OC_App.php index 5d7941ffa3f..5051d3e7ab5 100644 --- a/lib/private/legacy/OC_App.php +++ b/lib/private/legacy/OC_App.php @@ -98,6 +98,7 @@ class OC_App { * * @param string $app * @return bool + * @deprecated 26.0.0 use IAppManager::isAppLoaded */ public static function isAppLoaded(string $app): bool { return \OC::$server->get(IAppManager::class)->isAppLoaded($app); @@ -128,6 +129,7 @@ class OC_App { * * @param string $app * @throws Exception + * @deprecated 26.0.0 use IAppManager::loadApp */ public static function loadApp(string $app): void { \OC::$server->get(IAppManager::class)->loadApp($app); @@ -169,7 +171,7 @@ class OC_App { * @param string $app * @param array $types * @return bool - * @deprecated 26.0.0 call \OC::$server->get(IAppManager::class)->isType($app, $types) + * @deprecated 26.0.0 use IAppManager::isType */ public static function isType(string $app, array $types): bool { return \OC::$server->get(IAppManager::class)->isType($app, $types); diff --git a/lib/public/App/IAppManager.php b/lib/public/App/IAppManager.php index 5387e104fd9..faaf871a74c 100644 --- a/lib/public/App/IAppManager.php +++ b/lib/public/App/IAppManager.php @@ -207,6 +207,7 @@ interface IAppManager { * exists. * * if $types is set to non-empty array, only apps of those types will be loaded + * @since 26.0.0 */ public function loadApps(array $types = []): bool;