Passed
Push — master ( 38cbd1...e64e2d )
by Christoph
11:23 queued 12s
created
lib/public/AppFramework/Bootstrap/IBootstrap.php 1 patch
Indentation   +19 added lines, -19 removed lines patch added patch discarded remove patch
@@ -30,24 +30,24 @@
 block discarded – undo
30 30
  */
31 31
 interface IBootstrap {
32 32
 
33
-	/**
34
-	 * @param IRegistrationContext $context
35
-	 *
36
-	 * @since 20.0.0
37
-	 */
38
-	public function register(IRegistrationContext $context): void;
33
+    /**
34
+     * @param IRegistrationContext $context
35
+     *
36
+     * @since 20.0.0
37
+     */
38
+    public function register(IRegistrationContext $context): void;
39 39
 
40
-	/**
41
-	 * Boot the application
42
-	 *
43
-	 * At this stage you can assume that all services are registered and the DI
44
-	 * container(s) are ready to be queried.
45
-	 *
46
-	 * This is also the state where an optional `appinfo/app.php` was loaded.
47
-	 *
48
-	 * @param IBootContext $context
49
-	 *
50
-	 * @since 20.0.0
51
-	 */
52
-	public function boot(IBootContext $context): void;
40
+    /**
41
+     * Boot the application
42
+     *
43
+     * At this stage you can assume that all services are registered and the DI
44
+     * container(s) are ready to be queried.
45
+     *
46
+     * This is also the state where an optional `appinfo/app.php` was loaded.
47
+     *
48
+     * @param IBootContext $context
49
+     *
50
+     * @since 20.0.0
51
+     */
52
+    public function boot(IBootContext $context): void;
53 53
 }
Please login to merge, or discard this patch.
lib/private/legacy/OC_App.php 1 patch
Indentation   +1056 added lines, -1056 removed lines patch added patch discarded remove patch
@@ -65,1060 +65,1060 @@
 block discarded – undo
65 65
  * upgrading and removing apps.
66 66
  */
67 67
 class OC_App {
68
-	private static $adminForms = [];
69
-	private static $personalForms = [];
70
-	private static $appTypes = [];
71
-	private static $loadedApps = [];
72
-	private static $altLogin = [];
73
-	private static $alreadyRegistered = [];
74
-	public const supportedApp = 300;
75
-	public const officialApp = 200;
76
-
77
-	/**
78
-	 * clean the appId
79
-	 *
80
-	 * @param string $app AppId that needs to be cleaned
81
-	 * @return string
82
-	 */
83
-	public static function cleanAppId(string $app): string {
84
-		return str_replace(['\0', '/', '\\', '..'], '', $app);
85
-	}
86
-
87
-	/**
88
-	 * Check if an app is loaded
89
-	 *
90
-	 * @param string $app
91
-	 * @return bool
92
-	 */
93
-	public static function isAppLoaded(string $app): bool {
94
-		return in_array($app, self::$loadedApps, true);
95
-	}
96
-
97
-	/**
98
-	 * loads all apps
99
-	 *
100
-	 * @param string[] $types
101
-	 * @return bool
102
-	 *
103
-	 * This function walks through the ownCloud directory and loads all apps
104
-	 * it can find. A directory contains an app if the file /appinfo/info.xml
105
-	 * exists.
106
-	 *
107
-	 * if $types is set to non-empty array, only apps of those types will be loaded
108
-	 */
109
-	public static function loadApps(array $types = []): bool {
110
-		if ((bool) \OC::$server->getSystemConfig()->getValue('maintenance', false)) {
111
-			return false;
112
-		}
113
-		// Load the enabled apps here
114
-		$apps = self::getEnabledApps();
115
-
116
-		// Add each apps' folder as allowed class path
117
-		foreach ($apps as $app) {
118
-			$path = self::getAppPath($app);
119
-			if ($path !== false) {
120
-				self::registerAutoloading($app, $path);
121
-			}
122
-		}
123
-
124
-		// prevent app.php from printing output
125
-		ob_start();
126
-		foreach ($apps as $app) {
127
-			if (($types === [] or self::isType($app, $types)) && !in_array($app, self::$loadedApps)) {
128
-				self::loadApp($app);
129
-			}
130
-		}
131
-		ob_end_clean();
132
-
133
-		return true;
134
-	}
135
-
136
-	/**
137
-	 * load a single app
138
-	 *
139
-	 * @param string $app
140
-	 * @throws Exception
141
-	 */
142
-	public static function loadApp(string $app) {
143
-		self::$loadedApps[] = $app;
144
-		$appPath = self::getAppPath($app);
145
-		if ($appPath === false) {
146
-			return;
147
-		}
148
-
149
-		// in case someone calls loadApp() directly
150
-		self::registerAutoloading($app, $appPath);
151
-
152
-		if (is_file($appPath . '/appinfo/app.php')) {
153
-			\OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app);
154
-			try {
155
-				self::requireAppFile($app);
156
-			} catch (Throwable $ex) {
157
-				if ($ex instanceof ServerNotAvailableException) {
158
-					throw $ex;
159
-				}
160
-				if (!\OC::$server->getAppManager()->isShipped($app) && !self::isType($app, ['authentication'])) {
161
-					\OC::$server->getLogger()->logException($ex, [
162
-						'message' => "App $app threw an error during app.php load and will be disabled: " . $ex->getMessage(),
163
-					]);
164
-
165
-					// Only disable apps which are not shipped and that are not authentication apps
166
-					\OC::$server->getAppManager()->disableApp($app, true);
167
-				} else {
168
-					\OC::$server->getLogger()->logException($ex, [
169
-						'message' => "App $app threw an error during app.php load: " . $ex->getMessage(),
170
-					]);
171
-				}
172
-			}
173
-			\OC::$server->getEventLogger()->end('load_app_' . $app);
174
-		}
175
-
176
-		/** @var \OC\AppFramework\Bootstrap\Coordinator $coordinator */
177
-		$coordinator = \OC::$server->query(\OC\AppFramework\Bootstrap\Coordinator::class);
178
-		$coordinator->bootApp($app);
179
-
180
-		$info = self::getAppInfo($app);
181
-		if (!empty($info['activity']['filters'])) {
182
-			foreach ($info['activity']['filters'] as $filter) {
183
-				\OC::$server->getActivityManager()->registerFilter($filter);
184
-			}
185
-		}
186
-		if (!empty($info['activity']['settings'])) {
187
-			foreach ($info['activity']['settings'] as $setting) {
188
-				\OC::$server->getActivityManager()->registerSetting($setting);
189
-			}
190
-		}
191
-		if (!empty($info['activity']['providers'])) {
192
-			foreach ($info['activity']['providers'] as $provider) {
193
-				\OC::$server->getActivityManager()->registerProvider($provider);
194
-			}
195
-		}
196
-
197
-		if (!empty($info['settings']['admin'])) {
198
-			foreach ($info['settings']['admin'] as $setting) {
199
-				\OC::$server->getSettingsManager()->registerSetting('admin', $setting);
200
-			}
201
-		}
202
-		if (!empty($info['settings']['admin-section'])) {
203
-			foreach ($info['settings']['admin-section'] as $section) {
204
-				\OC::$server->getSettingsManager()->registerSection('admin', $section);
205
-			}
206
-		}
207
-		if (!empty($info['settings']['personal'])) {
208
-			foreach ($info['settings']['personal'] as $setting) {
209
-				\OC::$server->getSettingsManager()->registerSetting('personal', $setting);
210
-			}
211
-		}
212
-		if (!empty($info['settings']['personal-section'])) {
213
-			foreach ($info['settings']['personal-section'] as $section) {
214
-				\OC::$server->getSettingsManager()->registerSection('personal', $section);
215
-			}
216
-		}
217
-
218
-		if (!empty($info['collaboration']['plugins'])) {
219
-			// deal with one or many plugin entries
220
-			$plugins = isset($info['collaboration']['plugins']['plugin']['@value']) ?
221
-				[$info['collaboration']['plugins']['plugin']] : $info['collaboration']['plugins']['plugin'];
222
-			foreach ($plugins as $plugin) {
223
-				if ($plugin['@attributes']['type'] === 'collaborator-search') {
224
-					$pluginInfo = [
225
-						'shareType' => $plugin['@attributes']['share-type'],
226
-						'class' => $plugin['@value'],
227
-					];
228
-					\OC::$server->getCollaboratorSearch()->registerPlugin($pluginInfo);
229
-				} elseif ($plugin['@attributes']['type'] === 'autocomplete-sort') {
230
-					\OC::$server->getAutoCompleteManager()->registerSorter($plugin['@value']);
231
-				}
232
-			}
233
-		}
234
-	}
235
-
236
-	/**
237
-	 * @internal
238
-	 * @param string $app
239
-	 * @param string $path
240
-	 * @param bool $force
241
-	 */
242
-	public static function registerAutoloading(string $app, string $path, bool $force = false) {
243
-		$key = $app . '-' . $path;
244
-		if (!$force && isset(self::$alreadyRegistered[$key])) {
245
-			return;
246
-		}
247
-
248
-		self::$alreadyRegistered[$key] = true;
249
-
250
-		// Register on PSR-4 composer autoloader
251
-		$appNamespace = \OC\AppFramework\App::buildAppNamespace($app);
252
-		\OC::$server->registerNamespace($app, $appNamespace);
253
-
254
-		if (file_exists($path . '/composer/autoload.php')) {
255
-			require_once $path . '/composer/autoload.php';
256
-		} else {
257
-			\OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true);
258
-			// Register on legacy autoloader
259
-			\OC::$loader->addValidRoot($path);
260
-		}
261
-
262
-		// Register Test namespace only when testing
263
-		if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) {
264
-			\OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true);
265
-		}
266
-	}
267
-
268
-	/**
269
-	 * Load app.php from the given app
270
-	 *
271
-	 * @param string $app app name
272
-	 * @throws Error
273
-	 */
274
-	private static function requireAppFile(string $app) {
275
-		// encapsulated here to avoid variable scope conflicts
276
-		require_once $app . '/appinfo/app.php';
277
-	}
278
-
279
-	/**
280
-	 * check if an app is of a specific type
281
-	 *
282
-	 * @param string $app
283
-	 * @param array $types
284
-	 * @return bool
285
-	 */
286
-	public static function isType(string $app, array $types): bool {
287
-		$appTypes = self::getAppTypes($app);
288
-		foreach ($types as $type) {
289
-			if (array_search($type, $appTypes) !== false) {
290
-				return true;
291
-			}
292
-		}
293
-		return false;
294
-	}
295
-
296
-	/**
297
-	 * get the types of an app
298
-	 *
299
-	 * @param string $app
300
-	 * @return array
301
-	 */
302
-	private static function getAppTypes(string $app): array {
303
-		//load the cache
304
-		if (count(self::$appTypes) == 0) {
305
-			self::$appTypes = \OC::$server->getAppConfig()->getValues(false, 'types');
306
-		}
307
-
308
-		if (isset(self::$appTypes[$app])) {
309
-			return explode(',', self::$appTypes[$app]);
310
-		}
311
-
312
-		return [];
313
-	}
314
-
315
-	/**
316
-	 * read app types from info.xml and cache them in the database
317
-	 */
318
-	public static function setAppTypes(string $app) {
319
-		$appManager = \OC::$server->getAppManager();
320
-		$appData = $appManager->getAppInfo($app);
321
-		if (!is_array($appData)) {
322
-			return;
323
-		}
324
-
325
-		if (isset($appData['types'])) {
326
-			$appTypes = implode(',', $appData['types']);
327
-		} else {
328
-			$appTypes = '';
329
-			$appData['types'] = [];
330
-		}
331
-
332
-		$config = \OC::$server->getConfig();
333
-		$config->setAppValue($app, 'types', $appTypes);
334
-
335
-		if ($appManager->hasProtectedAppType($appData['types'])) {
336
-			$enabled = $config->getAppValue($app, 'enabled', 'yes');
337
-			if ($enabled !== 'yes' && $enabled !== 'no') {
338
-				$config->setAppValue($app, 'enabled', 'yes');
339
-			}
340
-		}
341
-	}
342
-
343
-	/**
344
-	 * Returns apps enabled for the current user.
345
-	 *
346
-	 * @param bool $forceRefresh whether to refresh the cache
347
-	 * @param bool $all whether to return apps for all users, not only the
348
-	 * currently logged in one
349
-	 * @return string[]
350
-	 */
351
-	public static function getEnabledApps(bool $forceRefresh = false, bool $all = false): array {
352
-		if (!\OC::$server->getSystemConfig()->getValue('installed', false)) {
353
-			return [];
354
-		}
355
-		// in incognito mode or when logged out, $user will be false,
356
-		// which is also the case during an upgrade
357
-		$appManager = \OC::$server->getAppManager();
358
-		if ($all) {
359
-			$user = null;
360
-		} else {
361
-			$user = \OC::$server->getUserSession()->getUser();
362
-		}
363
-
364
-		if (is_null($user)) {
365
-			$apps = $appManager->getInstalledApps();
366
-		} else {
367
-			$apps = $appManager->getEnabledAppsForUser($user);
368
-		}
369
-		$apps = array_filter($apps, function ($app) {
370
-			return $app !== 'files';//we add this manually
371
-		});
372
-		sort($apps);
373
-		array_unshift($apps, 'files');
374
-		return $apps;
375
-	}
376
-
377
-	/**
378
-	 * checks whether or not an app is enabled
379
-	 *
380
-	 * @param string $app app
381
-	 * @return bool
382
-	 * @deprecated 13.0.0 use \OC::$server->getAppManager()->isEnabledForUser($appId)
383
-	 *
384
-	 * This function checks whether or not an app is enabled.
385
-	 */
386
-	public static function isEnabled(string $app): bool {
387
-		return \OC::$server->getAppManager()->isEnabledForUser($app);
388
-	}
389
-
390
-	/**
391
-	 * enables an app
392
-	 *
393
-	 * @param string $appId
394
-	 * @param array $groups (optional) when set, only these groups will have access to the app
395
-	 * @throws \Exception
396
-	 * @return void
397
-	 *
398
-	 * This function set an app as enabled in appconfig.
399
-	 */
400
-	public function enable(string $appId,
401
-						   array $groups = []) {
402
-
403
-		// Check if app is already downloaded
404
-		/** @var Installer $installer */
405
-		$installer = \OC::$server->query(Installer::class);
406
-		$isDownloaded = $installer->isDownloaded($appId);
407
-
408
-		if (!$isDownloaded) {
409
-			$installer->downloadApp($appId);
410
-		}
411
-
412
-		$installer->installApp($appId);
413
-
414
-		$appManager = \OC::$server->getAppManager();
415
-		if ($groups !== []) {
416
-			$groupManager = \OC::$server->getGroupManager();
417
-			$groupsList = [];
418
-			foreach ($groups as $group) {
419
-				$groupItem = $groupManager->get($group);
420
-				if ($groupItem instanceof \OCP\IGroup) {
421
-					$groupsList[] = $groupManager->get($group);
422
-				}
423
-			}
424
-			$appManager->enableAppForGroups($appId, $groupsList);
425
-		} else {
426
-			$appManager->enableApp($appId);
427
-		}
428
-	}
429
-
430
-	/**
431
-	 * Get the path where to install apps
432
-	 *
433
-	 * @return string|false
434
-	 */
435
-	public static function getInstallPath() {
436
-		if (\OC::$server->getSystemConfig()->getValue('appstoreenabled', true) == false) {
437
-			return false;
438
-		}
439
-
440
-		foreach (OC::$APPSROOTS as $dir) {
441
-			if (isset($dir['writable']) && $dir['writable'] === true) {
442
-				return $dir['path'];
443
-			}
444
-		}
445
-
446
-		\OCP\Util::writeLog('core', 'No application directories are marked as writable.', ILogger::ERROR);
447
-		return null;
448
-	}
449
-
450
-
451
-	/**
452
-	 * search for an app in all app-directories
453
-	 *
454
-	 * @param string $appId
455
-	 * @return false|string
456
-	 */
457
-	public static function findAppInDirectories(string $appId) {
458
-		$sanitizedAppId = self::cleanAppId($appId);
459
-		if ($sanitizedAppId !== $appId) {
460
-			return false;
461
-		}
462
-		static $app_dir = [];
463
-
464
-		if (isset($app_dir[$appId])) {
465
-			return $app_dir[$appId];
466
-		}
467
-
468
-		$possibleApps = [];
469
-		foreach (OC::$APPSROOTS as $dir) {
470
-			if (file_exists($dir['path'] . '/' . $appId)) {
471
-				$possibleApps[] = $dir;
472
-			}
473
-		}
474
-
475
-		if (empty($possibleApps)) {
476
-			return false;
477
-		} elseif (count($possibleApps) === 1) {
478
-			$dir = array_shift($possibleApps);
479
-			$app_dir[$appId] = $dir;
480
-			return $dir;
481
-		} else {
482
-			$versionToLoad = [];
483
-			foreach ($possibleApps as $possibleApp) {
484
-				$version = self::getAppVersionByPath($possibleApp['path'] . '/' . $appId);
485
-				if (empty($versionToLoad) || version_compare($version, $versionToLoad['version'], '>')) {
486
-					$versionToLoad = [
487
-						'dir' => $possibleApp,
488
-						'version' => $version,
489
-					];
490
-				}
491
-			}
492
-			$app_dir[$appId] = $versionToLoad['dir'];
493
-			return $versionToLoad['dir'];
494
-			//TODO - write test
495
-		}
496
-	}
497
-
498
-	/**
499
-	 * Get the directory for the given app.
500
-	 * If the app is defined in multiple directories, the first one is taken. (false if not found)
501
-	 *
502
-	 * @param string $appId
503
-	 * @return string|false
504
-	 * @deprecated 11.0.0 use \OC::$server->getAppManager()->getAppPath()
505
-	 */
506
-	public static function getAppPath(string $appId) {
507
-		if ($appId === null || trim($appId) === '') {
508
-			return false;
509
-		}
510
-
511
-		if (($dir = self::findAppInDirectories($appId)) != false) {
512
-			return $dir['path'] . '/' . $appId;
513
-		}
514
-		return false;
515
-	}
516
-
517
-	/**
518
-	 * Get the path for the given app on the access
519
-	 * If the app is defined in multiple directories, the first one is taken. (false if not found)
520
-	 *
521
-	 * @param string $appId
522
-	 * @return string|false
523
-	 * @deprecated 18.0.0 use \OC::$server->getAppManager()->getAppWebPath()
524
-	 */
525
-	public static function getAppWebPath(string $appId) {
526
-		if (($dir = self::findAppInDirectories($appId)) != false) {
527
-			return OC::$WEBROOT . $dir['url'] . '/' . $appId;
528
-		}
529
-		return false;
530
-	}
531
-
532
-	/**
533
-	 * get the last version of the app from appinfo/info.xml
534
-	 *
535
-	 * @param string $appId
536
-	 * @param bool $useCache
537
-	 * @return string
538
-	 * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppVersion()
539
-	 */
540
-	public static function getAppVersion(string $appId, bool $useCache = true): string {
541
-		return \OC::$server->getAppManager()->getAppVersion($appId, $useCache);
542
-	}
543
-
544
-	/**
545
-	 * get app's version based on it's path
546
-	 *
547
-	 * @param string $path
548
-	 * @return string
549
-	 */
550
-	public static function getAppVersionByPath(string $path): string {
551
-		$infoFile = $path . '/appinfo/info.xml';
552
-		$appData = \OC::$server->getAppManager()->getAppInfo($infoFile, true);
553
-		return isset($appData['version']) ? $appData['version'] : '';
554
-	}
555
-
556
-
557
-	/**
558
-	 * Read all app metadata from the info.xml file
559
-	 *
560
-	 * @param string $appId id of the app or the path of the info.xml file
561
-	 * @param bool $path
562
-	 * @param string $lang
563
-	 * @return array|null
564
-	 * @note all data is read from info.xml, not just pre-defined fields
565
-	 * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppInfo()
566
-	 */
567
-	public static function getAppInfo(string $appId, bool $path = false, string $lang = null) {
568
-		return \OC::$server->getAppManager()->getAppInfo($appId, $path, $lang);
569
-	}
570
-
571
-	/**
572
-	 * Returns the navigation
573
-	 *
574
-	 * @return array
575
-	 * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll()
576
-	 *
577
-	 * This function returns an array containing all entries added. The
578
-	 * entries are sorted by the key 'order' ascending. Additional to the keys
579
-	 * given for each app the following keys exist:
580
-	 *   - active: boolean, signals if the user is on this navigation entry
581
-	 */
582
-	public static function getNavigation(): array {
583
-		return OC::$server->getNavigationManager()->getAll();
584
-	}
585
-
586
-	/**
587
-	 * Returns the Settings Navigation
588
-	 *
589
-	 * @return string[]
590
-	 * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll('settings')
591
-	 *
592
-	 * This function returns an array containing all settings pages added. The
593
-	 * entries are sorted by the key 'order' ascending.
594
-	 */
595
-	public static function getSettingsNavigation(): array {
596
-		return OC::$server->getNavigationManager()->getAll('settings');
597
-	}
598
-
599
-	/**
600
-	 * get the id of loaded app
601
-	 *
602
-	 * @return string
603
-	 */
604
-	public static function getCurrentApp(): string {
605
-		$request = \OC::$server->getRequest();
606
-		$script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1);
607
-		$topFolder = substr($script, 0, strpos($script, '/') ?: 0);
608
-		if (empty($topFolder)) {
609
-			$path_info = $request->getPathInfo();
610
-			if ($path_info) {
611
-				$topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1);
612
-			}
613
-		}
614
-		if ($topFolder == 'apps') {
615
-			$length = strlen($topFolder);
616
-			return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1) ?: '';
617
-		} else {
618
-			return $topFolder;
619
-		}
620
-	}
621
-
622
-	/**
623
-	 * @param string $type
624
-	 * @return array
625
-	 */
626
-	public static function getForms(string $type): array {
627
-		$forms = [];
628
-		switch ($type) {
629
-			case 'admin':
630
-				$source = self::$adminForms;
631
-				break;
632
-			case 'personal':
633
-				$source = self::$personalForms;
634
-				break;
635
-			default:
636
-				return [];
637
-		}
638
-		foreach ($source as $form) {
639
-			$forms[] = include $form;
640
-		}
641
-		return $forms;
642
-	}
643
-
644
-	/**
645
-	 * register an admin form to be shown
646
-	 *
647
-	 * @param string $app
648
-	 * @param string $page
649
-	 */
650
-	public static function registerAdmin(string $app, string $page) {
651
-		self::$adminForms[] = $app . '/' . $page . '.php';
652
-	}
653
-
654
-	/**
655
-	 * register a personal form to be shown
656
-	 * @param string $app
657
-	 * @param string $page
658
-	 */
659
-	public static function registerPersonal(string $app, string $page) {
660
-		self::$personalForms[] = $app . '/' . $page . '.php';
661
-	}
662
-
663
-	/**
664
-	 * @param array $entry
665
-	 */
666
-	public static function registerLogIn(array $entry) {
667
-		self::$altLogin[] = $entry;
668
-	}
669
-
670
-	/**
671
-	 * @return array
672
-	 */
673
-	public static function getAlternativeLogIns(): array {
674
-		return self::$altLogin;
675
-	}
676
-
677
-	/**
678
-	 * get a list of all apps in the apps folder
679
-	 *
680
-	 * @return string[] an array of app names (string IDs)
681
-	 * @todo: change the name of this method to getInstalledApps, which is more accurate
682
-	 */
683
-	public static function getAllApps(): array {
684
-		$apps = [];
685
-
686
-		foreach (OC::$APPSROOTS as $apps_dir) {
687
-			if (!is_readable($apps_dir['path'])) {
688
-				\OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], ILogger::WARN);
689
-				continue;
690
-			}
691
-			$dh = opendir($apps_dir['path']);
692
-
693
-			if (is_resource($dh)) {
694
-				while (($file = readdir($dh)) !== false) {
695
-					if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
696
-						$apps[] = $file;
697
-					}
698
-				}
699
-			}
700
-		}
701
-
702
-		$apps = array_unique($apps);
703
-
704
-		return $apps;
705
-	}
706
-
707
-	/**
708
-	 * List all apps, this is used in apps.php
709
-	 *
710
-	 * @return array
711
-	 */
712
-	public function listAllApps(): array {
713
-		$installedApps = OC_App::getAllApps();
714
-
715
-		$appManager = \OC::$server->getAppManager();
716
-		//we don't want to show configuration for these
717
-		$blacklist = $appManager->getAlwaysEnabledApps();
718
-		$appList = [];
719
-		$langCode = \OC::$server->getL10N('core')->getLanguageCode();
720
-		$urlGenerator = \OC::$server->getURLGenerator();
721
-		/** @var \OCP\Support\Subscription\IRegistry $subscriptionRegistry */
722
-		$subscriptionRegistry = \OC::$server->query(\OCP\Support\Subscription\IRegistry::class);
723
-		$supportedApps = $subscriptionRegistry->delegateGetSupportedApps();
724
-
725
-		foreach ($installedApps as $app) {
726
-			if (array_search($app, $blacklist) === false) {
727
-				$info = OC_App::getAppInfo($app, false, $langCode);
728
-				if (!is_array($info)) {
729
-					\OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', ILogger::ERROR);
730
-					continue;
731
-				}
732
-
733
-				if (!isset($info['name'])) {
734
-					\OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', ILogger::ERROR);
735
-					continue;
736
-				}
737
-
738
-				$enabled = \OC::$server->getConfig()->getAppValue($app, 'enabled', 'no');
739
-				$info['groups'] = null;
740
-				if ($enabled === 'yes') {
741
-					$active = true;
742
-				} elseif ($enabled === 'no') {
743
-					$active = false;
744
-				} else {
745
-					$active = true;
746
-					$info['groups'] = $enabled;
747
-				}
748
-
749
-				$info['active'] = $active;
750
-
751
-				if ($appManager->isShipped($app)) {
752
-					$info['internal'] = true;
753
-					$info['level'] = self::officialApp;
754
-					$info['removable'] = false;
755
-				} else {
756
-					$info['internal'] = false;
757
-					$info['removable'] = true;
758
-				}
759
-
760
-				if (in_array($app, $supportedApps)) {
761
-					$info['level'] = self::supportedApp;
762
-				}
763
-
764
-				$appPath = self::getAppPath($app);
765
-				if ($appPath !== false) {
766
-					$appIcon = $appPath . '/img/' . $app . '.svg';
767
-					if (file_exists($appIcon)) {
768
-						$info['preview'] = $urlGenerator->imagePath($app, $app . '.svg');
769
-						$info['previewAsIcon'] = true;
770
-					} else {
771
-						$appIcon = $appPath . '/img/app.svg';
772
-						if (file_exists($appIcon)) {
773
-							$info['preview'] = $urlGenerator->imagePath($app, 'app.svg');
774
-							$info['previewAsIcon'] = true;
775
-						}
776
-					}
777
-				}
778
-				// fix documentation
779
-				if (isset($info['documentation']) && is_array($info['documentation'])) {
780
-					foreach ($info['documentation'] as $key => $url) {
781
-						// If it is not an absolute URL we assume it is a key
782
-						// i.e. admin-ldap will get converted to go.php?to=admin-ldap
783
-						if (stripos($url, 'https://') !== 0 && stripos($url, 'http://') !== 0) {
784
-							$url = $urlGenerator->linkToDocs($url);
785
-						}
786
-
787
-						$info['documentation'][$key] = $url;
788
-					}
789
-				}
790
-
791
-				$info['version'] = OC_App::getAppVersion($app);
792
-				$appList[] = $info;
793
-			}
794
-		}
795
-
796
-		return $appList;
797
-	}
798
-
799
-	public static function shouldUpgrade(string $app): bool {
800
-		$versions = self::getAppVersions();
801
-		$currentVersion = OC_App::getAppVersion($app);
802
-		if ($currentVersion && isset($versions[$app])) {
803
-			$installedVersion = $versions[$app];
804
-			if (!version_compare($currentVersion, $installedVersion, '=')) {
805
-				return true;
806
-			}
807
-		}
808
-		return false;
809
-	}
810
-
811
-	/**
812
-	 * Adjust the number of version parts of $version1 to match
813
-	 * the number of version parts of $version2.
814
-	 *
815
-	 * @param string $version1 version to adjust
816
-	 * @param string $version2 version to take the number of parts from
817
-	 * @return string shortened $version1
818
-	 */
819
-	private static function adjustVersionParts(string $version1, string $version2): string {
820
-		$version1 = explode('.', $version1);
821
-		$version2 = explode('.', $version2);
822
-		// reduce $version1 to match the number of parts in $version2
823
-		while (count($version1) > count($version2)) {
824
-			array_pop($version1);
825
-		}
826
-		// if $version1 does not have enough parts, add some
827
-		while (count($version1) < count($version2)) {
828
-			$version1[] = '0';
829
-		}
830
-		return implode('.', $version1);
831
-	}
832
-
833
-	/**
834
-	 * Check whether the current ownCloud version matches the given
835
-	 * application's version requirements.
836
-	 *
837
-	 * The comparison is made based on the number of parts that the
838
-	 * app info version has. For example for ownCloud 6.0.3 if the
839
-	 * app info version is expecting version 6.0, the comparison is
840
-	 * made on the first two parts of the ownCloud version.
841
-	 * This means that it's possible to specify "requiremin" => 6
842
-	 * and "requiremax" => 6 and it will still match ownCloud 6.0.3.
843
-	 *
844
-	 * @param string $ocVersion ownCloud version to check against
845
-	 * @param array $appInfo app info (from xml)
846
-	 *
847
-	 * @return boolean true if compatible, otherwise false
848
-	 */
849
-	public static function isAppCompatible(string $ocVersion, array $appInfo, bool $ignoreMax = false): bool {
850
-		$requireMin = '';
851
-		$requireMax = '';
852
-		if (isset($appInfo['dependencies']['nextcloud']['@attributes']['min-version'])) {
853
-			$requireMin = $appInfo['dependencies']['nextcloud']['@attributes']['min-version'];
854
-		} elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) {
855
-			$requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version'];
856
-		} elseif (isset($appInfo['requiremin'])) {
857
-			$requireMin = $appInfo['requiremin'];
858
-		} elseif (isset($appInfo['require'])) {
859
-			$requireMin = $appInfo['require'];
860
-		}
861
-
862
-		if (isset($appInfo['dependencies']['nextcloud']['@attributes']['max-version'])) {
863
-			$requireMax = $appInfo['dependencies']['nextcloud']['@attributes']['max-version'];
864
-		} elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) {
865
-			$requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version'];
866
-		} elseif (isset($appInfo['requiremax'])) {
867
-			$requireMax = $appInfo['requiremax'];
868
-		}
869
-
870
-		if (!empty($requireMin)
871
-			&& version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<')
872
-		) {
873
-			return false;
874
-		}
875
-
876
-		if (!$ignoreMax && !empty($requireMax)
877
-			&& version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>')
878
-		) {
879
-			return false;
880
-		}
881
-
882
-		return true;
883
-	}
884
-
885
-	/**
886
-	 * get the installed version of all apps
887
-	 */
888
-	public static function getAppVersions() {
889
-		static $versions;
890
-
891
-		if (!$versions) {
892
-			$appConfig = \OC::$server->getAppConfig();
893
-			$versions = $appConfig->getValues(false, 'installed_version');
894
-		}
895
-		return $versions;
896
-	}
897
-
898
-	/**
899
-	 * update the database for the app and call the update script
900
-	 *
901
-	 * @param string $appId
902
-	 * @return bool
903
-	 */
904
-	public static function updateApp(string $appId): bool {
905
-		$appPath = self::getAppPath($appId);
906
-		if ($appPath === false) {
907
-			return false;
908
-		}
909
-
910
-		\OC::$server->getAppManager()->clearAppsCache();
911
-		$appData = self::getAppInfo($appId);
912
-
913
-		self::registerAutoloading($appId, $appPath, true);
914
-		self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']);
915
-
916
-		if (file_exists($appPath . '/appinfo/database.xml')) {
917
-			OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml');
918
-		} else {
919
-			$ms = new MigrationService($appId, \OC::$server->getDatabaseConnection());
920
-			$ms->migrate();
921
-		}
922
-
923
-		self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']);
924
-		self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']);
925
-		// update appversion in app manager
926
-		\OC::$server->getAppManager()->clearAppsCache();
927
-		\OC::$server->getAppManager()->getAppVersion($appId, false);
928
-
929
-		// run upgrade code
930
-		if (file_exists($appPath . '/appinfo/update.php')) {
931
-			self::loadApp($appId);
932
-			include $appPath . '/appinfo/update.php';
933
-		}
934
-		self::setupBackgroundJobs($appData['background-jobs']);
935
-
936
-		//set remote/public handlers
937
-		if (array_key_exists('ocsid', $appData)) {
938
-			\OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']);
939
-		} elseif (\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
940
-			\OC::$server->getConfig()->deleteAppValue($appId, 'ocsid');
941
-		}
942
-		foreach ($appData['remote'] as $name => $path) {
943
-			\OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
944
-		}
945
-		foreach ($appData['public'] as $name => $path) {
946
-			\OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path);
947
-		}
948
-
949
-		self::setAppTypes($appId);
950
-
951
-		$version = \OC_App::getAppVersion($appId);
952
-		\OC::$server->getConfig()->setAppValue($appId, 'installed_version', $version);
953
-
954
-		\OC::$server->getEventDispatcher()->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent(
955
-			ManagerEvent::EVENT_APP_UPDATE, $appId
956
-		));
957
-
958
-		return true;
959
-	}
960
-
961
-	/**
962
-	 * @param string $appId
963
-	 * @param string[] $steps
964
-	 * @throws \OC\NeedsUpdateException
965
-	 */
966
-	public static function executeRepairSteps(string $appId, array $steps) {
967
-		if (empty($steps)) {
968
-			return;
969
-		}
970
-		// load the app
971
-		self::loadApp($appId);
972
-
973
-		$dispatcher = OC::$server->getEventDispatcher();
974
-
975
-		// load the steps
976
-		$r = new Repair([], $dispatcher);
977
-		foreach ($steps as $step) {
978
-			try {
979
-				$r->addStep($step);
980
-			} catch (Exception $ex) {
981
-				$r->emit('\OC\Repair', 'error', [$ex->getMessage()]);
982
-				\OC::$server->getLogger()->logException($ex);
983
-			}
984
-		}
985
-		// run the steps
986
-		$r->run();
987
-	}
988
-
989
-	public static function setupBackgroundJobs(array $jobs) {
990
-		$queue = \OC::$server->getJobList();
991
-		foreach ($jobs as $job) {
992
-			$queue->add($job);
993
-		}
994
-	}
995
-
996
-	/**
997
-	 * @param string $appId
998
-	 * @param string[] $steps
999
-	 */
1000
-	private static function setupLiveMigrations(string $appId, array $steps) {
1001
-		$queue = \OC::$server->getJobList();
1002
-		foreach ($steps as $step) {
1003
-			$queue->add('OC\Migration\BackgroundRepair', [
1004
-				'app' => $appId,
1005
-				'step' => $step]);
1006
-		}
1007
-	}
1008
-
1009
-	/**
1010
-	 * @param string $appId
1011
-	 * @return \OC\Files\View|false
1012
-	 */
1013
-	public static function getStorage(string $appId) {
1014
-		if (\OC::$server->getAppManager()->isEnabledForUser($appId)) { //sanity check
1015
-			if (\OC::$server->getUserSession()->isLoggedIn()) {
1016
-				$view = new \OC\Files\View('/' . OC_User::getUser());
1017
-				if (!$view->file_exists($appId)) {
1018
-					$view->mkdir($appId);
1019
-				}
1020
-				return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId);
1021
-			} else {
1022
-				\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', ILogger::ERROR);
1023
-				return false;
1024
-			}
1025
-		} else {
1026
-			\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', ILogger::ERROR);
1027
-			return false;
1028
-		}
1029
-	}
1030
-
1031
-	protected static function findBestL10NOption(array $options, string $lang): string {
1032
-		// only a single option
1033
-		if (isset($options['@value'])) {
1034
-			return $options['@value'];
1035
-		}
1036
-
1037
-		$fallback = $similarLangFallback = $englishFallback = false;
1038
-
1039
-		$lang = strtolower($lang);
1040
-		$similarLang = $lang;
1041
-		if (strpos($similarLang, '_')) {
1042
-			// For "de_DE" we want to find "de" and the other way around
1043
-			$similarLang = substr($lang, 0, strpos($lang, '_'));
1044
-		}
1045
-
1046
-		foreach ($options as $option) {
1047
-			if (is_array($option)) {
1048
-				if ($fallback === false) {
1049
-					$fallback = $option['@value'];
1050
-				}
1051
-
1052
-				if (!isset($option['@attributes']['lang'])) {
1053
-					continue;
1054
-				}
1055
-
1056
-				$attributeLang = strtolower($option['@attributes']['lang']);
1057
-				if ($attributeLang === $lang) {
1058
-					return $option['@value'];
1059
-				}
1060
-
1061
-				if ($attributeLang === $similarLang) {
1062
-					$similarLangFallback = $option['@value'];
1063
-				} elseif (strpos($attributeLang, $similarLang . '_') === 0) {
1064
-					if ($similarLangFallback === false) {
1065
-						$similarLangFallback =  $option['@value'];
1066
-					}
1067
-				}
1068
-			} else {
1069
-				$englishFallback = $option;
1070
-			}
1071
-		}
1072
-
1073
-		if ($similarLangFallback !== false) {
1074
-			return $similarLangFallback;
1075
-		} elseif ($englishFallback !== false) {
1076
-			return $englishFallback;
1077
-		}
1078
-		return (string) $fallback;
1079
-	}
1080
-
1081
-	/**
1082
-	 * parses the app data array and enhanced the 'description' value
1083
-	 *
1084
-	 * @param array $data the app data
1085
-	 * @param string $lang
1086
-	 * @return array improved app data
1087
-	 */
1088
-	public static function parseAppInfo(array $data, $lang = null): array {
1089
-		if ($lang && isset($data['name']) && is_array($data['name'])) {
1090
-			$data['name'] = self::findBestL10NOption($data['name'], $lang);
1091
-		}
1092
-		if ($lang && isset($data['summary']) && is_array($data['summary'])) {
1093
-			$data['summary'] = self::findBestL10NOption($data['summary'], $lang);
1094
-		}
1095
-		if ($lang && isset($data['description']) && is_array($data['description'])) {
1096
-			$data['description'] = trim(self::findBestL10NOption($data['description'], $lang));
1097
-		} elseif (isset($data['description']) && is_string($data['description'])) {
1098
-			$data['description'] = trim($data['description']);
1099
-		} else {
1100
-			$data['description'] = '';
1101
-		}
1102
-
1103
-		return $data;
1104
-	}
1105
-
1106
-	/**
1107
-	 * @param \OCP\IConfig $config
1108
-	 * @param \OCP\IL10N $l
1109
-	 * @param array $info
1110
-	 * @throws \Exception
1111
-	 */
1112
-	public static function checkAppDependencies(\OCP\IConfig $config, \OCP\IL10N $l, array $info, bool $ignoreMax) {
1113
-		$dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l);
1114
-		$missing = $dependencyAnalyzer->analyze($info, $ignoreMax);
1115
-		if (!empty($missing)) {
1116
-			$missingMsg = implode(PHP_EOL, $missing);
1117
-			throw new \Exception(
1118
-				$l->t('App "%1$s" cannot be installed because the following dependencies are not fulfilled: %2$s',
1119
-					[$info['name'], $missingMsg]
1120
-				)
1121
-			);
1122
-		}
1123
-	}
68
+    private static $adminForms = [];
69
+    private static $personalForms = [];
70
+    private static $appTypes = [];
71
+    private static $loadedApps = [];
72
+    private static $altLogin = [];
73
+    private static $alreadyRegistered = [];
74
+    public const supportedApp = 300;
75
+    public const officialApp = 200;
76
+
77
+    /**
78
+     * clean the appId
79
+     *
80
+     * @param string $app AppId that needs to be cleaned
81
+     * @return string
82
+     */
83
+    public static function cleanAppId(string $app): string {
84
+        return str_replace(['\0', '/', '\\', '..'], '', $app);
85
+    }
86
+
87
+    /**
88
+     * Check if an app is loaded
89
+     *
90
+     * @param string $app
91
+     * @return bool
92
+     */
93
+    public static function isAppLoaded(string $app): bool {
94
+        return in_array($app, self::$loadedApps, true);
95
+    }
96
+
97
+    /**
98
+     * loads all apps
99
+     *
100
+     * @param string[] $types
101
+     * @return bool
102
+     *
103
+     * This function walks through the ownCloud directory and loads all apps
104
+     * it can find. A directory contains an app if the file /appinfo/info.xml
105
+     * exists.
106
+     *
107
+     * if $types is set to non-empty array, only apps of those types will be loaded
108
+     */
109
+    public static function loadApps(array $types = []): bool {
110
+        if ((bool) \OC::$server->getSystemConfig()->getValue('maintenance', false)) {
111
+            return false;
112
+        }
113
+        // Load the enabled apps here
114
+        $apps = self::getEnabledApps();
115
+
116
+        // Add each apps' folder as allowed class path
117
+        foreach ($apps as $app) {
118
+            $path = self::getAppPath($app);
119
+            if ($path !== false) {
120
+                self::registerAutoloading($app, $path);
121
+            }
122
+        }
123
+
124
+        // prevent app.php from printing output
125
+        ob_start();
126
+        foreach ($apps as $app) {
127
+            if (($types === [] or self::isType($app, $types)) && !in_array($app, self::$loadedApps)) {
128
+                self::loadApp($app);
129
+            }
130
+        }
131
+        ob_end_clean();
132
+
133
+        return true;
134
+    }
135
+
136
+    /**
137
+     * load a single app
138
+     *
139
+     * @param string $app
140
+     * @throws Exception
141
+     */
142
+    public static function loadApp(string $app) {
143
+        self::$loadedApps[] = $app;
144
+        $appPath = self::getAppPath($app);
145
+        if ($appPath === false) {
146
+            return;
147
+        }
148
+
149
+        // in case someone calls loadApp() directly
150
+        self::registerAutoloading($app, $appPath);
151
+
152
+        if (is_file($appPath . '/appinfo/app.php')) {
153
+            \OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app);
154
+            try {
155
+                self::requireAppFile($app);
156
+            } catch (Throwable $ex) {
157
+                if ($ex instanceof ServerNotAvailableException) {
158
+                    throw $ex;
159
+                }
160
+                if (!\OC::$server->getAppManager()->isShipped($app) && !self::isType($app, ['authentication'])) {
161
+                    \OC::$server->getLogger()->logException($ex, [
162
+                        'message' => "App $app threw an error during app.php load and will be disabled: " . $ex->getMessage(),
163
+                    ]);
164
+
165
+                    // Only disable apps which are not shipped and that are not authentication apps
166
+                    \OC::$server->getAppManager()->disableApp($app, true);
167
+                } else {
168
+                    \OC::$server->getLogger()->logException($ex, [
169
+                        'message' => "App $app threw an error during app.php load: " . $ex->getMessage(),
170
+                    ]);
171
+                }
172
+            }
173
+            \OC::$server->getEventLogger()->end('load_app_' . $app);
174
+        }
175
+
176
+        /** @var \OC\AppFramework\Bootstrap\Coordinator $coordinator */
177
+        $coordinator = \OC::$server->query(\OC\AppFramework\Bootstrap\Coordinator::class);
178
+        $coordinator->bootApp($app);
179
+
180
+        $info = self::getAppInfo($app);
181
+        if (!empty($info['activity']['filters'])) {
182
+            foreach ($info['activity']['filters'] as $filter) {
183
+                \OC::$server->getActivityManager()->registerFilter($filter);
184
+            }
185
+        }
186
+        if (!empty($info['activity']['settings'])) {
187
+            foreach ($info['activity']['settings'] as $setting) {
188
+                \OC::$server->getActivityManager()->registerSetting($setting);
189
+            }
190
+        }
191
+        if (!empty($info['activity']['providers'])) {
192
+            foreach ($info['activity']['providers'] as $provider) {
193
+                \OC::$server->getActivityManager()->registerProvider($provider);
194
+            }
195
+        }
196
+
197
+        if (!empty($info['settings']['admin'])) {
198
+            foreach ($info['settings']['admin'] as $setting) {
199
+                \OC::$server->getSettingsManager()->registerSetting('admin', $setting);
200
+            }
201
+        }
202
+        if (!empty($info['settings']['admin-section'])) {
203
+            foreach ($info['settings']['admin-section'] as $section) {
204
+                \OC::$server->getSettingsManager()->registerSection('admin', $section);
205
+            }
206
+        }
207
+        if (!empty($info['settings']['personal'])) {
208
+            foreach ($info['settings']['personal'] as $setting) {
209
+                \OC::$server->getSettingsManager()->registerSetting('personal', $setting);
210
+            }
211
+        }
212
+        if (!empty($info['settings']['personal-section'])) {
213
+            foreach ($info['settings']['personal-section'] as $section) {
214
+                \OC::$server->getSettingsManager()->registerSection('personal', $section);
215
+            }
216
+        }
217
+
218
+        if (!empty($info['collaboration']['plugins'])) {
219
+            // deal with one or many plugin entries
220
+            $plugins = isset($info['collaboration']['plugins']['plugin']['@value']) ?
221
+                [$info['collaboration']['plugins']['plugin']] : $info['collaboration']['plugins']['plugin'];
222
+            foreach ($plugins as $plugin) {
223
+                if ($plugin['@attributes']['type'] === 'collaborator-search') {
224
+                    $pluginInfo = [
225
+                        'shareType' => $plugin['@attributes']['share-type'],
226
+                        'class' => $plugin['@value'],
227
+                    ];
228
+                    \OC::$server->getCollaboratorSearch()->registerPlugin($pluginInfo);
229
+                } elseif ($plugin['@attributes']['type'] === 'autocomplete-sort') {
230
+                    \OC::$server->getAutoCompleteManager()->registerSorter($plugin['@value']);
231
+                }
232
+            }
233
+        }
234
+    }
235
+
236
+    /**
237
+     * @internal
238
+     * @param string $app
239
+     * @param string $path
240
+     * @param bool $force
241
+     */
242
+    public static function registerAutoloading(string $app, string $path, bool $force = false) {
243
+        $key = $app . '-' . $path;
244
+        if (!$force && isset(self::$alreadyRegistered[$key])) {
245
+            return;
246
+        }
247
+
248
+        self::$alreadyRegistered[$key] = true;
249
+
250
+        // Register on PSR-4 composer autoloader
251
+        $appNamespace = \OC\AppFramework\App::buildAppNamespace($app);
252
+        \OC::$server->registerNamespace($app, $appNamespace);
253
+
254
+        if (file_exists($path . '/composer/autoload.php')) {
255
+            require_once $path . '/composer/autoload.php';
256
+        } else {
257
+            \OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true);
258
+            // Register on legacy autoloader
259
+            \OC::$loader->addValidRoot($path);
260
+        }
261
+
262
+        // Register Test namespace only when testing
263
+        if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) {
264
+            \OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true);
265
+        }
266
+    }
267
+
268
+    /**
269
+     * Load app.php from the given app
270
+     *
271
+     * @param string $app app name
272
+     * @throws Error
273
+     */
274
+    private static function requireAppFile(string $app) {
275
+        // encapsulated here to avoid variable scope conflicts
276
+        require_once $app . '/appinfo/app.php';
277
+    }
278
+
279
+    /**
280
+     * check if an app is of a specific type
281
+     *
282
+     * @param string $app
283
+     * @param array $types
284
+     * @return bool
285
+     */
286
+    public static function isType(string $app, array $types): bool {
287
+        $appTypes = self::getAppTypes($app);
288
+        foreach ($types as $type) {
289
+            if (array_search($type, $appTypes) !== false) {
290
+                return true;
291
+            }
292
+        }
293
+        return false;
294
+    }
295
+
296
+    /**
297
+     * get the types of an app
298
+     *
299
+     * @param string $app
300
+     * @return array
301
+     */
302
+    private static function getAppTypes(string $app): array {
303
+        //load the cache
304
+        if (count(self::$appTypes) == 0) {
305
+            self::$appTypes = \OC::$server->getAppConfig()->getValues(false, 'types');
306
+        }
307
+
308
+        if (isset(self::$appTypes[$app])) {
309
+            return explode(',', self::$appTypes[$app]);
310
+        }
311
+
312
+        return [];
313
+    }
314
+
315
+    /**
316
+     * read app types from info.xml and cache them in the database
317
+     */
318
+    public static function setAppTypes(string $app) {
319
+        $appManager = \OC::$server->getAppManager();
320
+        $appData = $appManager->getAppInfo($app);
321
+        if (!is_array($appData)) {
322
+            return;
323
+        }
324
+
325
+        if (isset($appData['types'])) {
326
+            $appTypes = implode(',', $appData['types']);
327
+        } else {
328
+            $appTypes = '';
329
+            $appData['types'] = [];
330
+        }
331
+
332
+        $config = \OC::$server->getConfig();
333
+        $config->setAppValue($app, 'types', $appTypes);
334
+
335
+        if ($appManager->hasProtectedAppType($appData['types'])) {
336
+            $enabled = $config->getAppValue($app, 'enabled', 'yes');
337
+            if ($enabled !== 'yes' && $enabled !== 'no') {
338
+                $config->setAppValue($app, 'enabled', 'yes');
339
+            }
340
+        }
341
+    }
342
+
343
+    /**
344
+     * Returns apps enabled for the current user.
345
+     *
346
+     * @param bool $forceRefresh whether to refresh the cache
347
+     * @param bool $all whether to return apps for all users, not only the
348
+     * currently logged in one
349
+     * @return string[]
350
+     */
351
+    public static function getEnabledApps(bool $forceRefresh = false, bool $all = false): array {
352
+        if (!\OC::$server->getSystemConfig()->getValue('installed', false)) {
353
+            return [];
354
+        }
355
+        // in incognito mode or when logged out, $user will be false,
356
+        // which is also the case during an upgrade
357
+        $appManager = \OC::$server->getAppManager();
358
+        if ($all) {
359
+            $user = null;
360
+        } else {
361
+            $user = \OC::$server->getUserSession()->getUser();
362
+        }
363
+
364
+        if (is_null($user)) {
365
+            $apps = $appManager->getInstalledApps();
366
+        } else {
367
+            $apps = $appManager->getEnabledAppsForUser($user);
368
+        }
369
+        $apps = array_filter($apps, function ($app) {
370
+            return $app !== 'files';//we add this manually
371
+        });
372
+        sort($apps);
373
+        array_unshift($apps, 'files');
374
+        return $apps;
375
+    }
376
+
377
+    /**
378
+     * checks whether or not an app is enabled
379
+     *
380
+     * @param string $app app
381
+     * @return bool
382
+     * @deprecated 13.0.0 use \OC::$server->getAppManager()->isEnabledForUser($appId)
383
+     *
384
+     * This function checks whether or not an app is enabled.
385
+     */
386
+    public static function isEnabled(string $app): bool {
387
+        return \OC::$server->getAppManager()->isEnabledForUser($app);
388
+    }
389
+
390
+    /**
391
+     * enables an app
392
+     *
393
+     * @param string $appId
394
+     * @param array $groups (optional) when set, only these groups will have access to the app
395
+     * @throws \Exception
396
+     * @return void
397
+     *
398
+     * This function set an app as enabled in appconfig.
399
+     */
400
+    public function enable(string $appId,
401
+                            array $groups = []) {
402
+
403
+        // Check if app is already downloaded
404
+        /** @var Installer $installer */
405
+        $installer = \OC::$server->query(Installer::class);
406
+        $isDownloaded = $installer->isDownloaded($appId);
407
+
408
+        if (!$isDownloaded) {
409
+            $installer->downloadApp($appId);
410
+        }
411
+
412
+        $installer->installApp($appId);
413
+
414
+        $appManager = \OC::$server->getAppManager();
415
+        if ($groups !== []) {
416
+            $groupManager = \OC::$server->getGroupManager();
417
+            $groupsList = [];
418
+            foreach ($groups as $group) {
419
+                $groupItem = $groupManager->get($group);
420
+                if ($groupItem instanceof \OCP\IGroup) {
421
+                    $groupsList[] = $groupManager->get($group);
422
+                }
423
+            }
424
+            $appManager->enableAppForGroups($appId, $groupsList);
425
+        } else {
426
+            $appManager->enableApp($appId);
427
+        }
428
+    }
429
+
430
+    /**
431
+     * Get the path where to install apps
432
+     *
433
+     * @return string|false
434
+     */
435
+    public static function getInstallPath() {
436
+        if (\OC::$server->getSystemConfig()->getValue('appstoreenabled', true) == false) {
437
+            return false;
438
+        }
439
+
440
+        foreach (OC::$APPSROOTS as $dir) {
441
+            if (isset($dir['writable']) && $dir['writable'] === true) {
442
+                return $dir['path'];
443
+            }
444
+        }
445
+
446
+        \OCP\Util::writeLog('core', 'No application directories are marked as writable.', ILogger::ERROR);
447
+        return null;
448
+    }
449
+
450
+
451
+    /**
452
+     * search for an app in all app-directories
453
+     *
454
+     * @param string $appId
455
+     * @return false|string
456
+     */
457
+    public static function findAppInDirectories(string $appId) {
458
+        $sanitizedAppId = self::cleanAppId($appId);
459
+        if ($sanitizedAppId !== $appId) {
460
+            return false;
461
+        }
462
+        static $app_dir = [];
463
+
464
+        if (isset($app_dir[$appId])) {
465
+            return $app_dir[$appId];
466
+        }
467
+
468
+        $possibleApps = [];
469
+        foreach (OC::$APPSROOTS as $dir) {
470
+            if (file_exists($dir['path'] . '/' . $appId)) {
471
+                $possibleApps[] = $dir;
472
+            }
473
+        }
474
+
475
+        if (empty($possibleApps)) {
476
+            return false;
477
+        } elseif (count($possibleApps) === 1) {
478
+            $dir = array_shift($possibleApps);
479
+            $app_dir[$appId] = $dir;
480
+            return $dir;
481
+        } else {
482
+            $versionToLoad = [];
483
+            foreach ($possibleApps as $possibleApp) {
484
+                $version = self::getAppVersionByPath($possibleApp['path'] . '/' . $appId);
485
+                if (empty($versionToLoad) || version_compare($version, $versionToLoad['version'], '>')) {
486
+                    $versionToLoad = [
487
+                        'dir' => $possibleApp,
488
+                        'version' => $version,
489
+                    ];
490
+                }
491
+            }
492
+            $app_dir[$appId] = $versionToLoad['dir'];
493
+            return $versionToLoad['dir'];
494
+            //TODO - write test
495
+        }
496
+    }
497
+
498
+    /**
499
+     * Get the directory for the given app.
500
+     * If the app is defined in multiple directories, the first one is taken. (false if not found)
501
+     *
502
+     * @param string $appId
503
+     * @return string|false
504
+     * @deprecated 11.0.0 use \OC::$server->getAppManager()->getAppPath()
505
+     */
506
+    public static function getAppPath(string $appId) {
507
+        if ($appId === null || trim($appId) === '') {
508
+            return false;
509
+        }
510
+
511
+        if (($dir = self::findAppInDirectories($appId)) != false) {
512
+            return $dir['path'] . '/' . $appId;
513
+        }
514
+        return false;
515
+    }
516
+
517
+    /**
518
+     * Get the path for the given app on the access
519
+     * If the app is defined in multiple directories, the first one is taken. (false if not found)
520
+     *
521
+     * @param string $appId
522
+     * @return string|false
523
+     * @deprecated 18.0.0 use \OC::$server->getAppManager()->getAppWebPath()
524
+     */
525
+    public static function getAppWebPath(string $appId) {
526
+        if (($dir = self::findAppInDirectories($appId)) != false) {
527
+            return OC::$WEBROOT . $dir['url'] . '/' . $appId;
528
+        }
529
+        return false;
530
+    }
531
+
532
+    /**
533
+     * get the last version of the app from appinfo/info.xml
534
+     *
535
+     * @param string $appId
536
+     * @param bool $useCache
537
+     * @return string
538
+     * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppVersion()
539
+     */
540
+    public static function getAppVersion(string $appId, bool $useCache = true): string {
541
+        return \OC::$server->getAppManager()->getAppVersion($appId, $useCache);
542
+    }
543
+
544
+    /**
545
+     * get app's version based on it's path
546
+     *
547
+     * @param string $path
548
+     * @return string
549
+     */
550
+    public static function getAppVersionByPath(string $path): string {
551
+        $infoFile = $path . '/appinfo/info.xml';
552
+        $appData = \OC::$server->getAppManager()->getAppInfo($infoFile, true);
553
+        return isset($appData['version']) ? $appData['version'] : '';
554
+    }
555
+
556
+
557
+    /**
558
+     * Read all app metadata from the info.xml file
559
+     *
560
+     * @param string $appId id of the app or the path of the info.xml file
561
+     * @param bool $path
562
+     * @param string $lang
563
+     * @return array|null
564
+     * @note all data is read from info.xml, not just pre-defined fields
565
+     * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppInfo()
566
+     */
567
+    public static function getAppInfo(string $appId, bool $path = false, string $lang = null) {
568
+        return \OC::$server->getAppManager()->getAppInfo($appId, $path, $lang);
569
+    }
570
+
571
+    /**
572
+     * Returns the navigation
573
+     *
574
+     * @return array
575
+     * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll()
576
+     *
577
+     * This function returns an array containing all entries added. The
578
+     * entries are sorted by the key 'order' ascending. Additional to the keys
579
+     * given for each app the following keys exist:
580
+     *   - active: boolean, signals if the user is on this navigation entry
581
+     */
582
+    public static function getNavigation(): array {
583
+        return OC::$server->getNavigationManager()->getAll();
584
+    }
585
+
586
+    /**
587
+     * Returns the Settings Navigation
588
+     *
589
+     * @return string[]
590
+     * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll('settings')
591
+     *
592
+     * This function returns an array containing all settings pages added. The
593
+     * entries are sorted by the key 'order' ascending.
594
+     */
595
+    public static function getSettingsNavigation(): array {
596
+        return OC::$server->getNavigationManager()->getAll('settings');
597
+    }
598
+
599
+    /**
600
+     * get the id of loaded app
601
+     *
602
+     * @return string
603
+     */
604
+    public static function getCurrentApp(): string {
605
+        $request = \OC::$server->getRequest();
606
+        $script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1);
607
+        $topFolder = substr($script, 0, strpos($script, '/') ?: 0);
608
+        if (empty($topFolder)) {
609
+            $path_info = $request->getPathInfo();
610
+            if ($path_info) {
611
+                $topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1);
612
+            }
613
+        }
614
+        if ($topFolder == 'apps') {
615
+            $length = strlen($topFolder);
616
+            return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1) ?: '';
617
+        } else {
618
+            return $topFolder;
619
+        }
620
+    }
621
+
622
+    /**
623
+     * @param string $type
624
+     * @return array
625
+     */
626
+    public static function getForms(string $type): array {
627
+        $forms = [];
628
+        switch ($type) {
629
+            case 'admin':
630
+                $source = self::$adminForms;
631
+                break;
632
+            case 'personal':
633
+                $source = self::$personalForms;
634
+                break;
635
+            default:
636
+                return [];
637
+        }
638
+        foreach ($source as $form) {
639
+            $forms[] = include $form;
640
+        }
641
+        return $forms;
642
+    }
643
+
644
+    /**
645
+     * register an admin form to be shown
646
+     *
647
+     * @param string $app
648
+     * @param string $page
649
+     */
650
+    public static function registerAdmin(string $app, string $page) {
651
+        self::$adminForms[] = $app . '/' . $page . '.php';
652
+    }
653
+
654
+    /**
655
+     * register a personal form to be shown
656
+     * @param string $app
657
+     * @param string $page
658
+     */
659
+    public static function registerPersonal(string $app, string $page) {
660
+        self::$personalForms[] = $app . '/' . $page . '.php';
661
+    }
662
+
663
+    /**
664
+     * @param array $entry
665
+     */
666
+    public static function registerLogIn(array $entry) {
667
+        self::$altLogin[] = $entry;
668
+    }
669
+
670
+    /**
671
+     * @return array
672
+     */
673
+    public static function getAlternativeLogIns(): array {
674
+        return self::$altLogin;
675
+    }
676
+
677
+    /**
678
+     * get a list of all apps in the apps folder
679
+     *
680
+     * @return string[] an array of app names (string IDs)
681
+     * @todo: change the name of this method to getInstalledApps, which is more accurate
682
+     */
683
+    public static function getAllApps(): array {
684
+        $apps = [];
685
+
686
+        foreach (OC::$APPSROOTS as $apps_dir) {
687
+            if (!is_readable($apps_dir['path'])) {
688
+                \OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], ILogger::WARN);
689
+                continue;
690
+            }
691
+            $dh = opendir($apps_dir['path']);
692
+
693
+            if (is_resource($dh)) {
694
+                while (($file = readdir($dh)) !== false) {
695
+                    if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
696
+                        $apps[] = $file;
697
+                    }
698
+                }
699
+            }
700
+        }
701
+
702
+        $apps = array_unique($apps);
703
+
704
+        return $apps;
705
+    }
706
+
707
+    /**
708
+     * List all apps, this is used in apps.php
709
+     *
710
+     * @return array
711
+     */
712
+    public function listAllApps(): array {
713
+        $installedApps = OC_App::getAllApps();
714
+
715
+        $appManager = \OC::$server->getAppManager();
716
+        //we don't want to show configuration for these
717
+        $blacklist = $appManager->getAlwaysEnabledApps();
718
+        $appList = [];
719
+        $langCode = \OC::$server->getL10N('core')->getLanguageCode();
720
+        $urlGenerator = \OC::$server->getURLGenerator();
721
+        /** @var \OCP\Support\Subscription\IRegistry $subscriptionRegistry */
722
+        $subscriptionRegistry = \OC::$server->query(\OCP\Support\Subscription\IRegistry::class);
723
+        $supportedApps = $subscriptionRegistry->delegateGetSupportedApps();
724
+
725
+        foreach ($installedApps as $app) {
726
+            if (array_search($app, $blacklist) === false) {
727
+                $info = OC_App::getAppInfo($app, false, $langCode);
728
+                if (!is_array($info)) {
729
+                    \OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', ILogger::ERROR);
730
+                    continue;
731
+                }
732
+
733
+                if (!isset($info['name'])) {
734
+                    \OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', ILogger::ERROR);
735
+                    continue;
736
+                }
737
+
738
+                $enabled = \OC::$server->getConfig()->getAppValue($app, 'enabled', 'no');
739
+                $info['groups'] = null;
740
+                if ($enabled === 'yes') {
741
+                    $active = true;
742
+                } elseif ($enabled === 'no') {
743
+                    $active = false;
744
+                } else {
745
+                    $active = true;
746
+                    $info['groups'] = $enabled;
747
+                }
748
+
749
+                $info['active'] = $active;
750
+
751
+                if ($appManager->isShipped($app)) {
752
+                    $info['internal'] = true;
753
+                    $info['level'] = self::officialApp;
754
+                    $info['removable'] = false;
755
+                } else {
756
+                    $info['internal'] = false;
757
+                    $info['removable'] = true;
758
+                }
759
+
760
+                if (in_array($app, $supportedApps)) {
761
+                    $info['level'] = self::supportedApp;
762
+                }
763
+
764
+                $appPath = self::getAppPath($app);
765
+                if ($appPath !== false) {
766
+                    $appIcon = $appPath . '/img/' . $app . '.svg';
767
+                    if (file_exists($appIcon)) {
768
+                        $info['preview'] = $urlGenerator->imagePath($app, $app . '.svg');
769
+                        $info['previewAsIcon'] = true;
770
+                    } else {
771
+                        $appIcon = $appPath . '/img/app.svg';
772
+                        if (file_exists($appIcon)) {
773
+                            $info['preview'] = $urlGenerator->imagePath($app, 'app.svg');
774
+                            $info['previewAsIcon'] = true;
775
+                        }
776
+                    }
777
+                }
778
+                // fix documentation
779
+                if (isset($info['documentation']) && is_array($info['documentation'])) {
780
+                    foreach ($info['documentation'] as $key => $url) {
781
+                        // If it is not an absolute URL we assume it is a key
782
+                        // i.e. admin-ldap will get converted to go.php?to=admin-ldap
783
+                        if (stripos($url, 'https://') !== 0 && stripos($url, 'http://') !== 0) {
784
+                            $url = $urlGenerator->linkToDocs($url);
785
+                        }
786
+
787
+                        $info['documentation'][$key] = $url;
788
+                    }
789
+                }
790
+
791
+                $info['version'] = OC_App::getAppVersion($app);
792
+                $appList[] = $info;
793
+            }
794
+        }
795
+
796
+        return $appList;
797
+    }
798
+
799
+    public static function shouldUpgrade(string $app): bool {
800
+        $versions = self::getAppVersions();
801
+        $currentVersion = OC_App::getAppVersion($app);
802
+        if ($currentVersion && isset($versions[$app])) {
803
+            $installedVersion = $versions[$app];
804
+            if (!version_compare($currentVersion, $installedVersion, '=')) {
805
+                return true;
806
+            }
807
+        }
808
+        return false;
809
+    }
810
+
811
+    /**
812
+     * Adjust the number of version parts of $version1 to match
813
+     * the number of version parts of $version2.
814
+     *
815
+     * @param string $version1 version to adjust
816
+     * @param string $version2 version to take the number of parts from
817
+     * @return string shortened $version1
818
+     */
819
+    private static function adjustVersionParts(string $version1, string $version2): string {
820
+        $version1 = explode('.', $version1);
821
+        $version2 = explode('.', $version2);
822
+        // reduce $version1 to match the number of parts in $version2
823
+        while (count($version1) > count($version2)) {
824
+            array_pop($version1);
825
+        }
826
+        // if $version1 does not have enough parts, add some
827
+        while (count($version1) < count($version2)) {
828
+            $version1[] = '0';
829
+        }
830
+        return implode('.', $version1);
831
+    }
832
+
833
+    /**
834
+     * Check whether the current ownCloud version matches the given
835
+     * application's version requirements.
836
+     *
837
+     * The comparison is made based on the number of parts that the
838
+     * app info version has. For example for ownCloud 6.0.3 if the
839
+     * app info version is expecting version 6.0, the comparison is
840
+     * made on the first two parts of the ownCloud version.
841
+     * This means that it's possible to specify "requiremin" => 6
842
+     * and "requiremax" => 6 and it will still match ownCloud 6.0.3.
843
+     *
844
+     * @param string $ocVersion ownCloud version to check against
845
+     * @param array $appInfo app info (from xml)
846
+     *
847
+     * @return boolean true if compatible, otherwise false
848
+     */
849
+    public static function isAppCompatible(string $ocVersion, array $appInfo, bool $ignoreMax = false): bool {
850
+        $requireMin = '';
851
+        $requireMax = '';
852
+        if (isset($appInfo['dependencies']['nextcloud']['@attributes']['min-version'])) {
853
+            $requireMin = $appInfo['dependencies']['nextcloud']['@attributes']['min-version'];
854
+        } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) {
855
+            $requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version'];
856
+        } elseif (isset($appInfo['requiremin'])) {
857
+            $requireMin = $appInfo['requiremin'];
858
+        } elseif (isset($appInfo['require'])) {
859
+            $requireMin = $appInfo['require'];
860
+        }
861
+
862
+        if (isset($appInfo['dependencies']['nextcloud']['@attributes']['max-version'])) {
863
+            $requireMax = $appInfo['dependencies']['nextcloud']['@attributes']['max-version'];
864
+        } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) {
865
+            $requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version'];
866
+        } elseif (isset($appInfo['requiremax'])) {
867
+            $requireMax = $appInfo['requiremax'];
868
+        }
869
+
870
+        if (!empty($requireMin)
871
+            && version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<')
872
+        ) {
873
+            return false;
874
+        }
875
+
876
+        if (!$ignoreMax && !empty($requireMax)
877
+            && version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>')
878
+        ) {
879
+            return false;
880
+        }
881
+
882
+        return true;
883
+    }
884
+
885
+    /**
886
+     * get the installed version of all apps
887
+     */
888
+    public static function getAppVersions() {
889
+        static $versions;
890
+
891
+        if (!$versions) {
892
+            $appConfig = \OC::$server->getAppConfig();
893
+            $versions = $appConfig->getValues(false, 'installed_version');
894
+        }
895
+        return $versions;
896
+    }
897
+
898
+    /**
899
+     * update the database for the app and call the update script
900
+     *
901
+     * @param string $appId
902
+     * @return bool
903
+     */
904
+    public static function updateApp(string $appId): bool {
905
+        $appPath = self::getAppPath($appId);
906
+        if ($appPath === false) {
907
+            return false;
908
+        }
909
+
910
+        \OC::$server->getAppManager()->clearAppsCache();
911
+        $appData = self::getAppInfo($appId);
912
+
913
+        self::registerAutoloading($appId, $appPath, true);
914
+        self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']);
915
+
916
+        if (file_exists($appPath . '/appinfo/database.xml')) {
917
+            OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml');
918
+        } else {
919
+            $ms = new MigrationService($appId, \OC::$server->getDatabaseConnection());
920
+            $ms->migrate();
921
+        }
922
+
923
+        self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']);
924
+        self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']);
925
+        // update appversion in app manager
926
+        \OC::$server->getAppManager()->clearAppsCache();
927
+        \OC::$server->getAppManager()->getAppVersion($appId, false);
928
+
929
+        // run upgrade code
930
+        if (file_exists($appPath . '/appinfo/update.php')) {
931
+            self::loadApp($appId);
932
+            include $appPath . '/appinfo/update.php';
933
+        }
934
+        self::setupBackgroundJobs($appData['background-jobs']);
935
+
936
+        //set remote/public handlers
937
+        if (array_key_exists('ocsid', $appData)) {
938
+            \OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']);
939
+        } elseif (\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
940
+            \OC::$server->getConfig()->deleteAppValue($appId, 'ocsid');
941
+        }
942
+        foreach ($appData['remote'] as $name => $path) {
943
+            \OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
944
+        }
945
+        foreach ($appData['public'] as $name => $path) {
946
+            \OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path);
947
+        }
948
+
949
+        self::setAppTypes($appId);
950
+
951
+        $version = \OC_App::getAppVersion($appId);
952
+        \OC::$server->getConfig()->setAppValue($appId, 'installed_version', $version);
953
+
954
+        \OC::$server->getEventDispatcher()->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent(
955
+            ManagerEvent::EVENT_APP_UPDATE, $appId
956
+        ));
957
+
958
+        return true;
959
+    }
960
+
961
+    /**
962
+     * @param string $appId
963
+     * @param string[] $steps
964
+     * @throws \OC\NeedsUpdateException
965
+     */
966
+    public static function executeRepairSteps(string $appId, array $steps) {
967
+        if (empty($steps)) {
968
+            return;
969
+        }
970
+        // load the app
971
+        self::loadApp($appId);
972
+
973
+        $dispatcher = OC::$server->getEventDispatcher();
974
+
975
+        // load the steps
976
+        $r = new Repair([], $dispatcher);
977
+        foreach ($steps as $step) {
978
+            try {
979
+                $r->addStep($step);
980
+            } catch (Exception $ex) {
981
+                $r->emit('\OC\Repair', 'error', [$ex->getMessage()]);
982
+                \OC::$server->getLogger()->logException($ex);
983
+            }
984
+        }
985
+        // run the steps
986
+        $r->run();
987
+    }
988
+
989
+    public static function setupBackgroundJobs(array $jobs) {
990
+        $queue = \OC::$server->getJobList();
991
+        foreach ($jobs as $job) {
992
+            $queue->add($job);
993
+        }
994
+    }
995
+
996
+    /**
997
+     * @param string $appId
998
+     * @param string[] $steps
999
+     */
1000
+    private static function setupLiveMigrations(string $appId, array $steps) {
1001
+        $queue = \OC::$server->getJobList();
1002
+        foreach ($steps as $step) {
1003
+            $queue->add('OC\Migration\BackgroundRepair', [
1004
+                'app' => $appId,
1005
+                'step' => $step]);
1006
+        }
1007
+    }
1008
+
1009
+    /**
1010
+     * @param string $appId
1011
+     * @return \OC\Files\View|false
1012
+     */
1013
+    public static function getStorage(string $appId) {
1014
+        if (\OC::$server->getAppManager()->isEnabledForUser($appId)) { //sanity check
1015
+            if (\OC::$server->getUserSession()->isLoggedIn()) {
1016
+                $view = new \OC\Files\View('/' . OC_User::getUser());
1017
+                if (!$view->file_exists($appId)) {
1018
+                    $view->mkdir($appId);
1019
+                }
1020
+                return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId);
1021
+            } else {
1022
+                \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', ILogger::ERROR);
1023
+                return false;
1024
+            }
1025
+        } else {
1026
+            \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', ILogger::ERROR);
1027
+            return false;
1028
+        }
1029
+    }
1030
+
1031
+    protected static function findBestL10NOption(array $options, string $lang): string {
1032
+        // only a single option
1033
+        if (isset($options['@value'])) {
1034
+            return $options['@value'];
1035
+        }
1036
+
1037
+        $fallback = $similarLangFallback = $englishFallback = false;
1038
+
1039
+        $lang = strtolower($lang);
1040
+        $similarLang = $lang;
1041
+        if (strpos($similarLang, '_')) {
1042
+            // For "de_DE" we want to find "de" and the other way around
1043
+            $similarLang = substr($lang, 0, strpos($lang, '_'));
1044
+        }
1045
+
1046
+        foreach ($options as $option) {
1047
+            if (is_array($option)) {
1048
+                if ($fallback === false) {
1049
+                    $fallback = $option['@value'];
1050
+                }
1051
+
1052
+                if (!isset($option['@attributes']['lang'])) {
1053
+                    continue;
1054
+                }
1055
+
1056
+                $attributeLang = strtolower($option['@attributes']['lang']);
1057
+                if ($attributeLang === $lang) {
1058
+                    return $option['@value'];
1059
+                }
1060
+
1061
+                if ($attributeLang === $similarLang) {
1062
+                    $similarLangFallback = $option['@value'];
1063
+                } elseif (strpos($attributeLang, $similarLang . '_') === 0) {
1064
+                    if ($similarLangFallback === false) {
1065
+                        $similarLangFallback =  $option['@value'];
1066
+                    }
1067
+                }
1068
+            } else {
1069
+                $englishFallback = $option;
1070
+            }
1071
+        }
1072
+
1073
+        if ($similarLangFallback !== false) {
1074
+            return $similarLangFallback;
1075
+        } elseif ($englishFallback !== false) {
1076
+            return $englishFallback;
1077
+        }
1078
+        return (string) $fallback;
1079
+    }
1080
+
1081
+    /**
1082
+     * parses the app data array and enhanced the 'description' value
1083
+     *
1084
+     * @param array $data the app data
1085
+     * @param string $lang
1086
+     * @return array improved app data
1087
+     */
1088
+    public static function parseAppInfo(array $data, $lang = null): array {
1089
+        if ($lang && isset($data['name']) && is_array($data['name'])) {
1090
+            $data['name'] = self::findBestL10NOption($data['name'], $lang);
1091
+        }
1092
+        if ($lang && isset($data['summary']) && is_array($data['summary'])) {
1093
+            $data['summary'] = self::findBestL10NOption($data['summary'], $lang);
1094
+        }
1095
+        if ($lang && isset($data['description']) && is_array($data['description'])) {
1096
+            $data['description'] = trim(self::findBestL10NOption($data['description'], $lang));
1097
+        } elseif (isset($data['description']) && is_string($data['description'])) {
1098
+            $data['description'] = trim($data['description']);
1099
+        } else {
1100
+            $data['description'] = '';
1101
+        }
1102
+
1103
+        return $data;
1104
+    }
1105
+
1106
+    /**
1107
+     * @param \OCP\IConfig $config
1108
+     * @param \OCP\IL10N $l
1109
+     * @param array $info
1110
+     * @throws \Exception
1111
+     */
1112
+    public static function checkAppDependencies(\OCP\IConfig $config, \OCP\IL10N $l, array $info, bool $ignoreMax) {
1113
+        $dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l);
1114
+        $missing = $dependencyAnalyzer->analyze($info, $ignoreMax);
1115
+        if (!empty($missing)) {
1116
+            $missingMsg = implode(PHP_EOL, $missing);
1117
+            throw new \Exception(
1118
+                $l->t('App "%1$s" cannot be installed because the following dependencies are not fulfilled: %2$s',
1119
+                    [$info['name'], $missingMsg]
1120
+                )
1121
+            );
1122
+        }
1123
+    }
1124 1124
 }
Please login to merge, or discard this patch.