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