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