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