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