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