1212use OC \Config \ConfigManager ;
1313use OC \DB \MigrationService ;
1414use OC \Migration \BackgroundRepair ;
15+ use OC \NeedsUpdateException ;
16+ use OC \Repair ;
17+ use OC \Repair \Events \RepairErrorEvent ;
1518use OCP \Activity \IManager as IActivityManager ;
1619use OCP \App \AppPathNotFoundException ;
1720use OCP \App \Events \AppDisableEvent ;
@@ -153,6 +156,10 @@ private function getUrlGenerator(): IURLGenerator {
153156 * @return array<string,string> appId => enabled (may be 'yes', or a json encoded list of group ids)
154157 */
155158 private function getEnabledAppsValues (): array {
159+ if (!$ this ->config ->getSystemValueBool ('installed ' )) {
160+ return [];
161+ }
162+
156163 if (!$ this ->enabledAppsCache ) {
157164 /** @var array<string,string> */
158165 $ values = $ this ->getAppConfig ()->searchValues ('enabled ' , false , IAppConfig::VALUE_STRING );
@@ -246,25 +253,18 @@ public function getEnabledAppsForGroup(IGroup $group): array {
246253 return array_keys ($ appsForGroups );
247254 }
248255
249- /**
250- * Loads all apps
251- *
252- * @param string[] $types
253- * @return bool
254- *
255- * This function walks through the Nextcloud directory and loads all apps
256- * it can find. A directory contains an app if the file /appinfo/info.xml
257- * exists.
258- *
259- * if $types is set to non-empty array, only apps of those types will be loaded
260- */
261256 #[\Override]
262257 public function loadApps (array $ types = []): bool {
263258 if ($ this ->config ->getSystemValueBool ('maintenance ' , false )) {
264259 return false ;
265260 }
261+ if ($ this ->config ->getSystemValueBool ('installed ' ) === false ) {
262+ // can only access the apps folder after installation, so we can't load any apps before that
263+ return false ;
264+ }
265+
266266 // Load the enabled apps here
267- $ apps = \OC_App:: getEnabledApps ();
267+ $ apps = $ this -> getEnabledApps ();
268268
269269 // Add each apps' folder as allowed class path
270270 foreach ($ apps as $ app ) {
@@ -301,13 +301,6 @@ public function loadApps(array $types = []): bool {
301301 return true ;
302302 }
303303
304- /**
305- * check if an app is of a specific type
306- *
307- * @param string $app
308- * @param array $types
309- * @return bool
310- */
311304 #[\Override]
312305 public function isType (string $ app , array $ types ): bool {
313306 $ appTypes = $ this ->getAppTypes ($ app );
@@ -714,7 +707,7 @@ public function disableApp($appId, $automaticDisabled = false): void {
714707 // run uninstall steps
715708 $ appData = $ this ->getAppInfo ($ appId );
716709 if (!is_null ($ appData )) {
717- \OC_App:: executeRepairSteps ($ appId , $ appData ['repair-steps ' ]['uninstall ' ]);
710+ $ this -> executeRepairSteps ($ appId , $ appData ['repair-steps ' ]['uninstall ' ]);
718711 }
719712
720713 $ this ->dispatcher ->dispatchTyped (new AppDisableEvent ($ appId ));
@@ -1109,30 +1102,24 @@ public function upgradeApp(string $appId): bool {
11091102 $ appPath = $ this ->getAppPath ($ appId , true );
11101103
11111104 $ this ->clearAppsCache ();
1112- $ l = \OC ::$ server ->getL10N ('core ' );
1113- $ appData = $ this ->getAppInfo ($ appId , false , $ l ->getLanguageCode ());
1114- if ($ appData === null ) {
1105+ $ appInfo = $ this ->getAppInfo ($ appId );
1106+ if ($ appInfo === null ) {
11151107 throw new AppPathNotFoundException ('Could not find ' . $ appId );
11161108 }
11171109
11181110 $ ignoreMaxApps = $ this ->config ->getSystemValue ('app_install_overwrite ' , []);
11191111 $ ignoreMax = in_array ($ appId , $ ignoreMaxApps , true );
1120- \OC_App::checkAppDependencies (
1121- $ this ->config ,
1122- $ l ,
1123- $ appData ,
1124- $ ignoreMax
1125- );
1112+ $ this ->checkAppDependencies ($ appId , $ ignoreMax );
11261113
11271114 \OC_App::registerAutoloading ($ appId , $ appPath , true );
1128- \OC_App:: executeRepairSteps ($ appId , $ appData ['repair-steps ' ]['pre-migration ' ]);
1115+ $ this -> executeRepairSteps ($ appId , $ appInfo ['repair-steps ' ]['pre-migration ' ]);
11291116
11301117 $ ms = new MigrationService ($ appId , Server::get (\OC \DB \Connection::class));
11311118 $ ms ->migrate ();
11321119
1133- \OC_App:: executeRepairSteps ($ appId , $ appData ['repair-steps ' ]['post-migration ' ]);
1120+ $ this -> executeRepairSteps ($ appId , $ appInfo ['repair-steps ' ]['post-migration ' ]);
11341121 $ queue = Server::get (IJobList::class);
1135- foreach ($ appData ['repair-steps ' ]['live-migration ' ] as $ step ) {
1122+ foreach ($ appInfo ['repair-steps ' ]['live-migration ' ] as $ step ) {
11361123 $ queue ->add (BackgroundRepair::class, [
11371124 'app ' => $ appId ,
11381125 'step ' => $ step ]);
@@ -1143,19 +1130,19 @@ public function upgradeApp(string $appId): bool {
11431130 $ this ->getAppVersion ($ appId , false );
11441131
11451132 // Setup background jobs
1146- foreach ($ appData ['background-jobs ' ] as $ job ) {
1133+ foreach ($ appInfo ['background-jobs ' ] as $ job ) {
11471134 $ queue ->add ($ job );
11481135 }
11491136
11501137 //set remote/public handlers
1151- foreach ($ appData ['remote ' ] as $ name => $ path ) {
1138+ foreach ($ appInfo ['remote ' ] as $ name => $ path ) {
11521139 $ this ->config ->setAppValue ('core ' , 'remote_ ' . $ name , $ appId . '/ ' . $ path );
11531140 }
1154- foreach ($ appData ['public ' ] as $ name => $ path ) {
1141+ foreach ($ appInfo ['public ' ] as $ name => $ path ) {
11551142 $ this ->config ->setAppValue ('core ' , 'public_ ' . $ name , $ appId . '/ ' . $ path );
11561143 }
11571144
1158- $ this ->setAppTypes ($ appId , $ appData );
1145+ $ this ->setAppTypes ($ appId , $ appInfo );
11591146
11601147 $ version = $ this ->getAppVersion ($ appId );
11611148 $ this ->config ->setAppValue ($ appId , 'installed_version ' , $ version );
@@ -1237,4 +1224,57 @@ public function getAppFromNamespace(string $className): ?string {
12371224
12381225 return null ;
12391226 }
1227+
1228+ /**
1229+ * Check if all dependencies of an app are satisfied.
1230+ *
1231+ * @param string $appId - The app to check
1232+ * @param bool $ignoreMax - Whether to ignore the Nextcloud max version requirement
1233+ * @throws \Exception - If there are missing dependencies
1234+ */
1235+ public function checkAppDependencies (string $ appId , bool $ ignoreMax = false ): void {
1236+ $ info = $ this ->getAppInfo ($ appId );
1237+ if ($ info === null ) {
1238+ throw new \RuntimeException ("App $ appId not found " );
1239+ }
1240+
1241+ $ missing = $ this ->dependencyAnalyzer ->analyze ($ info , $ ignoreMax );
1242+ if ($ missing !== []) {
1243+ $ l = \OCP \Server::get (\OCP \L10N \IFactory::class)->get ('core ' );
1244+ $ missingMsg = implode (PHP_EOL , $ missing );
1245+ throw new \Exception (
1246+ $ l ->t ('App "%1$s" cannot be installed because the following dependencies are not fulfilled: %2$s ' ,
1247+ [$ info ['name ' ], $ missingMsg ]
1248+ )
1249+ );
1250+ }
1251+ }
1252+
1253+ /**
1254+ * Run repair steps for an app.
1255+ *
1256+ * @param string $appId - The app to run the repair steps for
1257+ * @param string[] $steps - The repair steps to run
1258+ * @throws NeedsUpdateException
1259+ */
1260+ public function executeRepairSteps (string $ appId , array $ steps ): void {
1261+ if ($ steps === []) {
1262+ return ;
1263+ }
1264+
1265+ $ this ->loadApp ($ appId );
1266+
1267+ // load the steps
1268+ $ r = Server::get (Repair::class);
1269+ foreach ($ steps as $ step ) {
1270+ try {
1271+ $ r ->addStep ($ step );
1272+ } catch (\Exception $ ex ) {
1273+ $ this ->dispatcher ->dispatchTyped (new RepairErrorEvent ($ ex ->getMessage ()));
1274+ $ this ->logger ->error ('Failed to add app migration step ' . $ step , ['exception ' => $ ex ]);
1275+ }
1276+ }
1277+ // run the steps
1278+ $ r ->run ();
1279+ }
12401280}
0 commit comments