Completed
Push — master ( 3eaf23...f8e4d0 )
by Björn
25:10 queued 24:36
created
lib/private/legacy/app.php 1 patch
Indentation   +1205 added lines, -1205 removed lines patch added patch discarded remove patch
@@ -61,1209 +61,1209 @@
 block discarded – undo
61 61
  * upgrading and removing apps.
62 62
  */
63 63
 class OC_App {
64
-	static private $appVersion = [];
65
-	static private $adminForms = array();
66
-	static private $personalForms = array();
67
-	static private $appInfo = array();
68
-	static private $appTypes = array();
69
-	static private $loadedApps = array();
70
-	static private $altLogin = array();
71
-	static private $alreadyRegistered = [];
72
-	const officialApp = 200;
73
-
74
-	/**
75
-	 * clean the appId
76
-	 *
77
-	 * @param string|boolean $app AppId that needs to be cleaned
78
-	 * @return string
79
-	 */
80
-	public static function cleanAppId($app) {
81
-		return str_replace(array('\0', '/', '\\', '..'), '', $app);
82
-	}
83
-
84
-	/**
85
-	 * Check if an app is loaded
86
-	 *
87
-	 * @param string $app
88
-	 * @return bool
89
-	 */
90
-	public static function isAppLoaded($app) {
91
-		return in_array($app, self::$loadedApps, true);
92
-	}
93
-
94
-	/**
95
-	 * loads all apps
96
-	 *
97
-	 * @param string[] | string | null $types
98
-	 * @return bool
99
-	 *
100
-	 * This function walks through the ownCloud directory and loads all apps
101
-	 * it can find. A directory contains an app if the file /appinfo/info.xml
102
-	 * exists.
103
-	 *
104
-	 * if $types is set, only apps of those types will be loaded
105
-	 */
106
-	public static function loadApps($types = null) {
107
-		if (\OC::$server->getSystemConfig()->getValue('maintenance', false)) {
108
-			return false;
109
-		}
110
-		// Load the enabled apps here
111
-		$apps = self::getEnabledApps();
112
-
113
-		// Add each apps' folder as allowed class path
114
-		foreach($apps as $app) {
115
-			$path = self::getAppPath($app);
116
-			if($path !== false) {
117
-				self::registerAutoloading($app, $path);
118
-			}
119
-		}
120
-
121
-		// prevent app.php from printing output
122
-		ob_start();
123
-		foreach ($apps as $app) {
124
-			if ((is_null($types) or self::isType($app, $types)) && !in_array($app, self::$loadedApps)) {
125
-				self::loadApp($app);
126
-			}
127
-		}
128
-		ob_end_clean();
129
-
130
-		return true;
131
-	}
132
-
133
-	/**
134
-	 * load a single app
135
-	 *
136
-	 * @param string $app
137
-	 */
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
-			self::requireAppFile($app);
151
-			if (self::isType($app, array('authentication'))) {
152
-				// since authentication apps affect the "is app enabled for group" check,
153
-				// the enabled apps cache needs to be cleared to make sure that the
154
-				// next time getEnableApps() is called it will also include apps that were
155
-				// enabled for groups
156
-				self::$enabledAppsCache = array();
157
-			}
158
-			\OC::$server->getEventLogger()->end('load_app_' . $app);
159
-		}
160
-
161
-		$info = self::getAppInfo($app);
162
-		if (!empty($info['activity']['filters'])) {
163
-			foreach ($info['activity']['filters'] as $filter) {
164
-				\OC::$server->getActivityManager()->registerFilter($filter);
165
-			}
166
-		}
167
-		if (!empty($info['activity']['settings'])) {
168
-			foreach ($info['activity']['settings'] as $setting) {
169
-				\OC::$server->getActivityManager()->registerSetting($setting);
170
-			}
171
-		}
172
-		if (!empty($info['activity']['providers'])) {
173
-			foreach ($info['activity']['providers'] as $provider) {
174
-				\OC::$server->getActivityManager()->registerProvider($provider);
175
-			}
176
-		}
177
-		if (!empty($info['collaboration']['plugins'])) {
178
-			// deal with one or many plugin entries
179
-			$plugins = isset($info['collaboration']['plugins']['plugin']['@value']) ?
180
-				[$info['collaboration']['plugins']['plugin']] : $info['collaboration']['plugins']['plugin'];
181
-			foreach ($plugins as $plugin) {
182
-				if($plugin['@attributes']['type'] === 'collaborator-search') {
183
-					$pluginInfo = [
184
-						'shareType' => $plugin['@attributes']['share-type'],
185
-						'class' => $plugin['@value'],
186
-					];
187
-					\OC::$server->getCollaboratorSearch()->registerPlugin($pluginInfo);
188
-				}
189
-			}
190
-		}
191
-	}
192
-
193
-	/**
194
-	 * @internal
195
-	 * @param string $app
196
-	 * @param string $path
197
-	 */
198
-	public static function registerAutoloading($app, $path) {
199
-		$key = $app . '-' . $path;
200
-		if(isset(self::$alreadyRegistered[$key])) {
201
-			return;
202
-		}
203
-
204
-		self::$alreadyRegistered[$key] = true;
205
-
206
-		// Register on PSR-4 composer autoloader
207
-		$appNamespace = \OC\AppFramework\App::buildAppNamespace($app);
208
-		\OC::$server->registerNamespace($app, $appNamespace);
209
-
210
-		if (file_exists($path . '/composer/autoload.php')) {
211
-			require_once $path . '/composer/autoload.php';
212
-		} else {
213
-			\OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true);
214
-			// Register on legacy autoloader
215
-			\OC::$loader->addValidRoot($path);
216
-		}
217
-
218
-		// Register Test namespace only when testing
219
-		if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) {
220
-			\OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true);
221
-		}
222
-	}
223
-
224
-	/**
225
-	 * Load app.php from the given app
226
-	 *
227
-	 * @param string $app app name
228
-	 */
229
-	private static function requireAppFile($app) {
230
-		try {
231
-			// encapsulated here to avoid variable scope conflicts
232
-			require_once $app . '/appinfo/app.php';
233
-		} catch (Error $ex) {
234
-			\OC::$server->getLogger()->logException($ex);
235
-			$blacklist = \OC::$server->getAppManager()->getAlwaysEnabledApps();
236
-			if (!in_array($app, $blacklist)) {
237
-				self::disable($app);
238
-			}
239
-		}
240
-	}
241
-
242
-	/**
243
-	 * check if an app is of a specific type
244
-	 *
245
-	 * @param string $app
246
-	 * @param string|array $types
247
-	 * @return bool
248
-	 */
249
-	public static function isType($app, $types) {
250
-		if (is_string($types)) {
251
-			$types = array($types);
252
-		}
253
-		$appTypes = self::getAppTypes($app);
254
-		foreach ($types as $type) {
255
-			if (array_search($type, $appTypes) !== false) {
256
-				return true;
257
-			}
258
-		}
259
-		return false;
260
-	}
261
-
262
-	/**
263
-	 * get the types of an app
264
-	 *
265
-	 * @param string $app
266
-	 * @return array
267
-	 */
268
-	private static function getAppTypes($app) {
269
-		//load the cache
270
-		if (count(self::$appTypes) == 0) {
271
-			self::$appTypes = \OC::$server->getAppConfig()->getValues(false, 'types');
272
-		}
273
-
274
-		if (isset(self::$appTypes[$app])) {
275
-			return explode(',', self::$appTypes[$app]);
276
-		} else {
277
-			return array();
278
-		}
279
-	}
280
-
281
-	/**
282
-	 * read app types from info.xml and cache them in the database
283
-	 */
284
-	public static function setAppTypes($app) {
285
-		$appData = self::getAppInfo($app);
286
-		if(!is_array($appData)) {
287
-			return;
288
-		}
289
-
290
-		if (isset($appData['types'])) {
291
-			$appTypes = implode(',', $appData['types']);
292
-		} else {
293
-			$appTypes = '';
294
-			$appData['types'] = [];
295
-		}
296
-
297
-		\OC::$server->getAppConfig()->setValue($app, 'types', $appTypes);
298
-
299
-		if (\OC::$server->getAppManager()->hasProtectedAppType($appData['types'])) {
300
-			$enabled = \OC::$server->getAppConfig()->getValue($app, 'enabled', 'yes');
301
-			if ($enabled !== 'yes' && $enabled !== 'no') {
302
-				\OC::$server->getAppConfig()->setValue($app, 'enabled', 'yes');
303
-			}
304
-		}
305
-	}
306
-
307
-	/**
308
-	 * get all enabled apps
309
-	 */
310
-	protected static $enabledAppsCache = array();
311
-
312
-	/**
313
-	 * Returns apps enabled for the current user.
314
-	 *
315
-	 * @param bool $forceRefresh whether to refresh the cache
316
-	 * @param bool $all whether to return apps for all users, not only the
317
-	 * currently logged in one
318
-	 * @return string[]
319
-	 */
320
-	public static function getEnabledApps($forceRefresh = false, $all = false) {
321
-		if (!\OC::$server->getSystemConfig()->getValue('installed', false)) {
322
-			return array();
323
-		}
324
-		// in incognito mode or when logged out, $user will be false,
325
-		// which is also the case during an upgrade
326
-		$appManager = \OC::$server->getAppManager();
327
-		if ($all) {
328
-			$user = null;
329
-		} else {
330
-			$user = \OC::$server->getUserSession()->getUser();
331
-		}
332
-
333
-		if (is_null($user)) {
334
-			$apps = $appManager->getInstalledApps();
335
-		} else {
336
-			$apps = $appManager->getEnabledAppsForUser($user);
337
-		}
338
-		$apps = array_filter($apps, function ($app) {
339
-			return $app !== 'files';//we add this manually
340
-		});
341
-		sort($apps);
342
-		array_unshift($apps, 'files');
343
-		return $apps;
344
-	}
345
-
346
-	/**
347
-	 * checks whether or not an app is enabled
348
-	 *
349
-	 * @param string $app app
350
-	 * @return bool
351
-	 * @deprecated 13.0.0 use \OC::$server->getAppManager()->isEnabledForUser($appId)
352
-	 *
353
-	 * This function checks whether or not an app is enabled.
354
-	 */
355
-	public static function isEnabled($app) {
356
-		return \OC::$server->getAppManager()->isEnabledForUser($app);
357
-	}
358
-
359
-	/**
360
-	 * enables an app
361
-	 *
362
-	 * @param string $appId
363
-	 * @param array $groups (optional) when set, only these groups will have access to the app
364
-	 * @throws \Exception
365
-	 * @return void
366
-	 *
367
-	 * This function set an app as enabled in appconfig.
368
-	 */
369
-	public function enable($appId,
370
-						   $groups = null) {
371
-		self::$enabledAppsCache = []; // flush
372
-
373
-		// Check if app is already downloaded
374
-		$installer = new Installer(
375
-			\OC::$server->getAppFetcher(),
376
-			\OC::$server->getHTTPClientService(),
377
-			\OC::$server->getTempManager(),
378
-			\OC::$server->getLogger(),
379
-			\OC::$server->getConfig()
380
-		);
381
-		$isDownloaded = $installer->isDownloaded($appId);
382
-
383
-		if(!$isDownloaded) {
384
-			$installer->downloadApp($appId);
385
-		}
386
-
387
-		$installer->installApp($appId);
388
-
389
-		$appManager = \OC::$server->getAppManager();
390
-		if (!is_null($groups)) {
391
-			$groupManager = \OC::$server->getGroupManager();
392
-			$groupsList = [];
393
-			foreach ($groups as $group) {
394
-				$groupItem = $groupManager->get($group);
395
-				if ($groupItem instanceof \OCP\IGroup) {
396
-					$groupsList[] = $groupManager->get($group);
397
-				}
398
-			}
399
-			$appManager->enableAppForGroups($appId, $groupsList);
400
-		} else {
401
-			$appManager->enableApp($appId);
402
-		}
403
-	}
404
-
405
-	/**
406
-	 * @param string $app
407
-	 * @return bool
408
-	 */
409
-	public static function removeApp($app) {
410
-		if (\OC::$server->getAppManager()->isShipped($app)) {
411
-			return false;
412
-		}
413
-
414
-		$installer = new Installer(
415
-			\OC::$server->getAppFetcher(),
416
-			\OC::$server->getHTTPClientService(),
417
-			\OC::$server->getTempManager(),
418
-			\OC::$server->getLogger(),
419
-			\OC::$server->getConfig()
420
-		);
421
-		return $installer->removeApp($app);
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
-	 */
580
-	public static function getAppVersion($appId, $useCache = true) {
581
-		if($useCache && isset(self::$appVersion[$appId])) {
582
-			return self::$appVersion[$appId];
583
-		}
584
-
585
-		$file = self::getAppPath($appId);
586
-		self::$appVersion[$appId] = ($file !== false) ? self::getAppVersionByPath($file) : '0';
587
-		return self::$appVersion[$appId];
588
-	}
589
-
590
-	/**
591
-	 * get app's version based on it's path
592
-	 *
593
-	 * @param string $path
594
-	 * @return string
595
-	 */
596
-	public static function getAppVersionByPath($path) {
597
-		$infoFile = $path . '/appinfo/info.xml';
598
-		$appData = self::getAppInfo($infoFile, true);
599
-		return isset($appData['version']) ? $appData['version'] : '';
600
-	}
601
-
602
-
603
-	/**
604
-	 * Read all app metadata from the info.xml file
605
-	 *
606
-	 * @param string $appId id of the app or the path of the info.xml file
607
-	 * @param bool $path
608
-	 * @param string $lang
609
-	 * @return array|null
610
-	 * @note all data is read from info.xml, not just pre-defined fields
611
-	 */
612
-	public static function getAppInfo($appId, $path = false, $lang = null) {
613
-		if ($path) {
614
-			$file = $appId;
615
-		} else {
616
-			if ($lang === null && isset(self::$appInfo[$appId])) {
617
-				return self::$appInfo[$appId];
618
-			}
619
-			$appPath = self::getAppPath($appId);
620
-			if($appPath === false) {
621
-				return null;
622
-			}
623
-			$file = $appPath . '/appinfo/info.xml';
624
-		}
625
-
626
-		$parser = new InfoParser(\OC::$server->getMemCacheFactory()->createLocal('core.appinfo'));
627
-		$data = $parser->parse($file);
628
-
629
-		if (is_array($data)) {
630
-			$data = OC_App::parseAppInfo($data, $lang);
631
-		}
632
-		if(isset($data['ocsid'])) {
633
-			$storedId = \OC::$server->getConfig()->getAppValue($appId, 'ocsid');
634
-			if($storedId !== '' && $storedId !== $data['ocsid']) {
635
-				$data['ocsid'] = $storedId;
636
-			}
637
-		}
638
-
639
-		if ($lang === null) {
640
-			self::$appInfo[$appId] = $data;
641
-		}
642
-
643
-		return $data;
644
-	}
645
-
646
-	/**
647
-	 * Returns the navigation
648
-	 *
649
-	 * @return array
650
-	 *
651
-	 * This function returns an array containing all entries added. The
652
-	 * entries are sorted by the key 'order' ascending. Additional to the keys
653
-	 * given for each app the following keys exist:
654
-	 *   - active: boolean, signals if the user is on this navigation entry
655
-	 */
656
-	public static function getNavigation() {
657
-		$entries = OC::$server->getNavigationManager()->getAll();
658
-		return self::proceedNavigation($entries);
659
-	}
660
-
661
-	/**
662
-	 * Returns the Settings Navigation
663
-	 *
664
-	 * @return string[]
665
-	 *
666
-	 * This function returns an array containing all settings pages added. The
667
-	 * entries are sorted by the key 'order' ascending.
668
-	 */
669
-	public static function getSettingsNavigation() {
670
-		$entries = OC::$server->getNavigationManager()->getAll('settings');
671
-		return self::proceedNavigation($entries);
672
-	}
673
-
674
-	/**
675
-	 * get the id of loaded app
676
-	 *
677
-	 * @return string
678
-	 */
679
-	public static function getCurrentApp() {
680
-		$request = \OC::$server->getRequest();
681
-		$script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1);
682
-		$topFolder = substr($script, 0, strpos($script, '/'));
683
-		if (empty($topFolder)) {
684
-			$path_info = $request->getPathInfo();
685
-			if ($path_info) {
686
-				$topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1);
687
-			}
688
-		}
689
-		if ($topFolder == 'apps') {
690
-			$length = strlen($topFolder);
691
-			return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1);
692
-		} else {
693
-			return $topFolder;
694
-		}
695
-	}
696
-
697
-	/**
698
-	 * @param string $type
699
-	 * @return array
700
-	 */
701
-	public static function getForms($type) {
702
-		$forms = array();
703
-		switch ($type) {
704
-			case 'admin':
705
-				$source = self::$adminForms;
706
-				break;
707
-			case 'personal':
708
-				$source = self::$personalForms;
709
-				break;
710
-			default:
711
-				return array();
712
-		}
713
-		foreach ($source as $form) {
714
-			$forms[] = include $form;
715
-		}
716
-		return $forms;
717
-	}
718
-
719
-	/**
720
-	 * register an admin form to be shown
721
-	 *
722
-	 * @param string $app
723
-	 * @param string $page
724
-	 */
725
-	public static function registerAdmin($app, $page) {
726
-		self::$adminForms[] = $app . '/' . $page . '.php';
727
-	}
728
-
729
-	/**
730
-	 * register a personal form to be shown
731
-	 * @param string $app
732
-	 * @param string $page
733
-	 */
734
-	public static function registerPersonal($app, $page) {
735
-		self::$personalForms[] = $app . '/' . $page . '.php';
736
-	}
737
-
738
-	/**
739
-	 * @param array $entry
740
-	 */
741
-	public static function registerLogIn(array $entry) {
742
-		self::$altLogin[] = $entry;
743
-	}
744
-
745
-	/**
746
-	 * @return array
747
-	 */
748
-	public static function getAlternativeLogIns() {
749
-		return self::$altLogin;
750
-	}
751
-
752
-	/**
753
-	 * get a list of all apps in the apps folder
754
-	 *
755
-	 * @return array an array of app names (string IDs)
756
-	 * @todo: change the name of this method to getInstalledApps, which is more accurate
757
-	 */
758
-	public static function getAllApps() {
759
-
760
-		$apps = array();
761
-
762
-		foreach (OC::$APPSROOTS as $apps_dir) {
763
-			if (!is_readable($apps_dir['path'])) {
764
-				\OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], \OCP\Util::WARN);
765
-				continue;
766
-			}
767
-			$dh = opendir($apps_dir['path']);
768
-
769
-			if (is_resource($dh)) {
770
-				while (($file = readdir($dh)) !== false) {
771
-
772
-					if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
773
-
774
-						$apps[] = $file;
775
-					}
776
-				}
777
-			}
778
-		}
779
-
780
-		$apps = array_unique($apps);
781
-
782
-		return $apps;
783
-	}
784
-
785
-	/**
786
-	 * List all apps, this is used in apps.php
787
-	 *
788
-	 * @return array
789
-	 */
790
-	public function listAllApps() {
791
-		$installedApps = OC_App::getAllApps();
792
-
793
-		$appManager = \OC::$server->getAppManager();
794
-		//we don't want to show configuration for these
795
-		$blacklist = $appManager->getAlwaysEnabledApps();
796
-		$appList = array();
797
-		$langCode = \OC::$server->getL10N('core')->getLanguageCode();
798
-		$urlGenerator = \OC::$server->getURLGenerator();
799
-
800
-		foreach ($installedApps as $app) {
801
-			if (array_search($app, $blacklist) === false) {
802
-
803
-				$info = OC_App::getAppInfo($app, false, $langCode);
804
-				if (!is_array($info)) {
805
-					\OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', \OCP\Util::ERROR);
806
-					continue;
807
-				}
808
-
809
-				if (!isset($info['name'])) {
810
-					\OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', \OCP\Util::ERROR);
811
-					continue;
812
-				}
813
-
814
-				$enabled = \OC::$server->getAppConfig()->getValue($app, 'enabled', 'no');
815
-				$info['groups'] = null;
816
-				if ($enabled === 'yes') {
817
-					$active = true;
818
-				} else if ($enabled === 'no') {
819
-					$active = false;
820
-				} else {
821
-					$active = true;
822
-					$info['groups'] = $enabled;
823
-				}
824
-
825
-				$info['active'] = $active;
826
-
827
-				if ($appManager->isShipped($app)) {
828
-					$info['internal'] = true;
829
-					$info['level'] = self::officialApp;
830
-					$info['removable'] = false;
831
-				} else {
832
-					$info['internal'] = false;
833
-					$info['removable'] = true;
834
-				}
835
-
836
-				$appPath = self::getAppPath($app);
837
-				if($appPath !== false) {
838
-					$appIcon = $appPath . '/img/' . $app . '.svg';
839
-					if (file_exists($appIcon)) {
840
-						$info['preview'] = $urlGenerator->imagePath($app, $app . '.svg');
841
-						$info['previewAsIcon'] = true;
842
-					} else {
843
-						$appIcon = $appPath . '/img/app.svg';
844
-						if (file_exists($appIcon)) {
845
-							$info['preview'] = $urlGenerator->imagePath($app, 'app.svg');
846
-							$info['previewAsIcon'] = true;
847
-						}
848
-					}
849
-				}
850
-				// fix documentation
851
-				if (isset($info['documentation']) && is_array($info['documentation'])) {
852
-					foreach ($info['documentation'] as $key => $url) {
853
-						// If it is not an absolute URL we assume it is a key
854
-						// i.e. admin-ldap will get converted to go.php?to=admin-ldap
855
-						if (stripos($url, 'https://') !== 0 && stripos($url, 'http://') !== 0) {
856
-							$url = $urlGenerator->linkToDocs($url);
857
-						}
858
-
859
-						$info['documentation'][$key] = $url;
860
-					}
861
-				}
862
-
863
-				$info['version'] = OC_App::getAppVersion($app);
864
-				$appList[] = $info;
865
-			}
866
-		}
867
-
868
-		return $appList;
869
-	}
870
-
871
-	/**
872
-	 * Returns the internal app ID or false
873
-	 * @param string $ocsID
874
-	 * @return string|false
875
-	 */
876
-	public static function getInternalAppIdByOcs($ocsID) {
877
-		if(is_numeric($ocsID)) {
878
-			$idArray = \OC::$server->getAppConfig()->getValues(false, 'ocsid');
879
-			if(array_search($ocsID, $idArray)) {
880
-				return array_search($ocsID, $idArray);
881
-			}
882
-		}
883
-		return false;
884
-	}
885
-
886
-	public static function shouldUpgrade($app) {
887
-		$versions = self::getAppVersions();
888
-		$currentVersion = OC_App::getAppVersion($app);
889
-		if ($currentVersion && isset($versions[$app])) {
890
-			$installedVersion = $versions[$app];
891
-			if (!version_compare($currentVersion, $installedVersion, '=')) {
892
-				return true;
893
-			}
894
-		}
895
-		return false;
896
-	}
897
-
898
-	/**
899
-	 * Adjust the number of version parts of $version1 to match
900
-	 * the number of version parts of $version2.
901
-	 *
902
-	 * @param string $version1 version to adjust
903
-	 * @param string $version2 version to take the number of parts from
904
-	 * @return string shortened $version1
905
-	 */
906
-	private static function adjustVersionParts($version1, $version2) {
907
-		$version1 = explode('.', $version1);
908
-		$version2 = explode('.', $version2);
909
-		// reduce $version1 to match the number of parts in $version2
910
-		while (count($version1) > count($version2)) {
911
-			array_pop($version1);
912
-		}
913
-		// if $version1 does not have enough parts, add some
914
-		while (count($version1) < count($version2)) {
915
-			$version1[] = '0';
916
-		}
917
-		return implode('.', $version1);
918
-	}
919
-
920
-	/**
921
-	 * Check whether the current ownCloud version matches the given
922
-	 * application's version requirements.
923
-	 *
924
-	 * The comparison is made based on the number of parts that the
925
-	 * app info version has. For example for ownCloud 6.0.3 if the
926
-	 * app info version is expecting version 6.0, the comparison is
927
-	 * made on the first two parts of the ownCloud version.
928
-	 * This means that it's possible to specify "requiremin" => 6
929
-	 * and "requiremax" => 6 and it will still match ownCloud 6.0.3.
930
-	 *
931
-	 * @param string $ocVersion ownCloud version to check against
932
-	 * @param array $appInfo app info (from xml)
933
-	 *
934
-	 * @return boolean true if compatible, otherwise false
935
-	 */
936
-	public static function isAppCompatible($ocVersion, $appInfo) {
937
-		$requireMin = '';
938
-		$requireMax = '';
939
-		if (isset($appInfo['dependencies']['nextcloud']['@attributes']['min-version'])) {
940
-			$requireMin = $appInfo['dependencies']['nextcloud']['@attributes']['min-version'];
941
-		} elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) {
942
-			$requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version'];
943
-		} else if (isset($appInfo['requiremin'])) {
944
-			$requireMin = $appInfo['requiremin'];
945
-		} else if (isset($appInfo['require'])) {
946
-			$requireMin = $appInfo['require'];
947
-		}
948
-
949
-		if (isset($appInfo['dependencies']['nextcloud']['@attributes']['max-version'])) {
950
-			$requireMax = $appInfo['dependencies']['nextcloud']['@attributes']['max-version'];
951
-		} elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) {
952
-			$requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version'];
953
-		} else if (isset($appInfo['requiremax'])) {
954
-			$requireMax = $appInfo['requiremax'];
955
-		}
956
-
957
-		if (is_array($ocVersion)) {
958
-			$ocVersion = implode('.', $ocVersion);
959
-		}
960
-
961
-		if (!empty($requireMin)
962
-			&& version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<')
963
-		) {
964
-
965
-			return false;
966
-		}
967
-
968
-		if (!empty($requireMax)
969
-			&& version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>')
970
-		) {
971
-			return false;
972
-		}
973
-
974
-		return true;
975
-	}
976
-
977
-	/**
978
-	 * get the installed version of all apps
979
-	 */
980
-	public static function getAppVersions() {
981
-		static $versions;
982
-
983
-		if(!$versions) {
984
-			$appConfig = \OC::$server->getAppConfig();
985
-			$versions = $appConfig->getValues(false, 'installed_version');
986
-		}
987
-		return $versions;
988
-	}
989
-
990
-	/**
991
-	 * @param string $app
992
-	 * @param \OCP\IConfig $config
993
-	 * @param \OCP\IL10N $l
994
-	 * @return bool
995
-	 *
996
-	 * @throws Exception if app is not compatible with this version of ownCloud
997
-	 * @throws Exception if no app-name was specified
998
-	 */
999
-	public function installApp($app,
1000
-							   \OCP\IConfig $config,
1001
-							   \OCP\IL10N $l) {
1002
-		if ($app !== false) {
1003
-			// check if the app is compatible with this version of ownCloud
1004
-			$info = self::getAppInfo($app);
1005
-			if(!is_array($info)) {
1006
-				throw new \Exception(
1007
-					$l->t('App "%s" cannot be installed because appinfo file cannot be read.',
1008
-						[$info['name']]
1009
-					)
1010
-				);
1011
-			}
1012
-
1013
-			$version = \OCP\Util::getVersion();
1014
-			if (!self::isAppCompatible($version, $info)) {
1015
-				throw new \Exception(
1016
-					$l->t('App "%s" cannot be installed because it is not compatible with this version of the server.',
1017
-						array($info['name'])
1018
-					)
1019
-				);
1020
-			}
1021
-
1022
-			// check for required dependencies
1023
-			self::checkAppDependencies($config, $l, $info);
1024
-
1025
-			$config->setAppValue($app, 'enabled', 'yes');
1026
-			if (isset($appData['id'])) {
1027
-				$config->setAppValue($app, 'ocsid', $appData['id']);
1028
-			}
1029
-
1030
-			if(isset($info['settings']) && is_array($info['settings'])) {
1031
-				$appPath = self::getAppPath($app);
1032
-				self::registerAutoloading($app, $appPath);
1033
-				\OC::$server->getSettingsManager()->setupSettings($info['settings']);
1034
-			}
1035
-
1036
-			\OC_Hook::emit('OC_App', 'post_enable', array('app' => $app));
1037
-		} else {
1038
-			if(empty($appName) ) {
1039
-				throw new \Exception($l->t("No app name specified"));
1040
-			} else {
1041
-				throw new \Exception($l->t("App '%s' could not be installed!", $appName));
1042
-			}
1043
-		}
1044
-
1045
-		return $app;
1046
-	}
1047
-
1048
-	/**
1049
-	 * update the database for the app and call the update script
1050
-	 *
1051
-	 * @param string $appId
1052
-	 * @return bool
1053
-	 */
1054
-	public static function updateApp($appId) {
1055
-		$appPath = self::getAppPath($appId);
1056
-		if($appPath === false) {
1057
-			return false;
1058
-		}
1059
-		self::registerAutoloading($appId, $appPath);
1060
-
1061
-		$appData = self::getAppInfo($appId);
1062
-		self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']);
1063
-
1064
-		if (file_exists($appPath . '/appinfo/database.xml')) {
1065
-			OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml');
1066
-		} else {
1067
-			$ms = new MigrationService($appId, \OC::$server->getDatabaseConnection());
1068
-			$ms->migrate();
1069
-		}
1070
-
1071
-		self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']);
1072
-		self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']);
1073
-		unset(self::$appVersion[$appId]);
1074
-
1075
-		// run upgrade code
1076
-		if (file_exists($appPath . '/appinfo/update.php')) {
1077
-			self::loadApp($appId);
1078
-			include $appPath . '/appinfo/update.php';
1079
-		}
1080
-		self::setupBackgroundJobs($appData['background-jobs']);
1081
-		if(isset($appData['settings']) && is_array($appData['settings'])) {
1082
-			\OC::$server->getSettingsManager()->setupSettings($appData['settings']);
1083
-		}
1084
-
1085
-		//set remote/public handlers
1086
-		if (array_key_exists('ocsid', $appData)) {
1087
-			\OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']);
1088
-		} elseif(\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
1089
-			\OC::$server->getConfig()->deleteAppValue($appId, 'ocsid');
1090
-		}
1091
-		foreach ($appData['remote'] as $name => $path) {
1092
-			\OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
1093
-		}
1094
-		foreach ($appData['public'] as $name => $path) {
1095
-			\OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path);
1096
-		}
1097
-
1098
-		self::setAppTypes($appId);
1099
-
1100
-		$version = \OC_App::getAppVersion($appId);
1101
-		\OC::$server->getAppConfig()->setValue($appId, 'installed_version', $version);
1102
-
1103
-		\OC::$server->getEventDispatcher()->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent(
1104
-			ManagerEvent::EVENT_APP_UPDATE, $appId
1105
-		));
1106
-
1107
-		return true;
1108
-	}
1109
-
1110
-	/**
1111
-	 * @param string $appId
1112
-	 * @param string[] $steps
1113
-	 * @throws \OC\NeedsUpdateException
1114
-	 */
1115
-	public static function executeRepairSteps($appId, array $steps) {
1116
-		if (empty($steps)) {
1117
-			return;
1118
-		}
1119
-		// load the app
1120
-		self::loadApp($appId);
1121
-
1122
-		$dispatcher = OC::$server->getEventDispatcher();
1123
-
1124
-		// load the steps
1125
-		$r = new Repair([], $dispatcher);
1126
-		foreach ($steps as $step) {
1127
-			try {
1128
-				$r->addStep($step);
1129
-			} catch (Exception $ex) {
1130
-				$r->emit('\OC\Repair', 'error', [$ex->getMessage()]);
1131
-				\OC::$server->getLogger()->logException($ex);
1132
-			}
1133
-		}
1134
-		// run the steps
1135
-		$r->run();
1136
-	}
1137
-
1138
-	public static function setupBackgroundJobs(array $jobs) {
1139
-		$queue = \OC::$server->getJobList();
1140
-		foreach ($jobs as $job) {
1141
-			$queue->add($job);
1142
-		}
1143
-	}
1144
-
1145
-	/**
1146
-	 * @param string $appId
1147
-	 * @param string[] $steps
1148
-	 */
1149
-	private static function setupLiveMigrations($appId, array $steps) {
1150
-		$queue = \OC::$server->getJobList();
1151
-		foreach ($steps as $step) {
1152
-			$queue->add('OC\Migration\BackgroundRepair', [
1153
-				'app' => $appId,
1154
-				'step' => $step]);
1155
-		}
1156
-	}
1157
-
1158
-	/**
1159
-	 * @param string $appId
1160
-	 * @return \OC\Files\View|false
1161
-	 */
1162
-	public static function getStorage($appId) {
1163
-		if (\OC::$server->getAppManager()->isEnabledForUser($appId)) { //sanity check
1164
-			if (\OC::$server->getUserSession()->isLoggedIn()) {
1165
-				$view = new \OC\Files\View('/' . OC_User::getUser());
1166
-				if (!$view->file_exists($appId)) {
1167
-					$view->mkdir($appId);
1168
-				}
1169
-				return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId);
1170
-			} else {
1171
-				\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', \OCP\Util::ERROR);
1172
-				return false;
1173
-			}
1174
-		} else {
1175
-			\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', \OCP\Util::ERROR);
1176
-			return false;
1177
-		}
1178
-	}
1179
-
1180
-	protected static function findBestL10NOption($options, $lang) {
1181
-		$fallback = $similarLangFallback = $englishFallback = false;
1182
-
1183
-		$lang = strtolower($lang);
1184
-		$similarLang = $lang;
1185
-		if (strpos($similarLang, '_')) {
1186
-			// For "de_DE" we want to find "de" and the other way around
1187
-			$similarLang = substr($lang, 0, strpos($lang, '_'));
1188
-		}
1189
-
1190
-		foreach ($options as $option) {
1191
-			if (is_array($option)) {
1192
-				if ($fallback === false) {
1193
-					$fallback = $option['@value'];
1194
-				}
1195
-
1196
-				if (!isset($option['@attributes']['lang'])) {
1197
-					continue;
1198
-				}
1199
-
1200
-				$attributeLang = strtolower($option['@attributes']['lang']);
1201
-				if ($attributeLang === $lang) {
1202
-					return $option['@value'];
1203
-				}
1204
-
1205
-				if ($attributeLang === $similarLang) {
1206
-					$similarLangFallback = $option['@value'];
1207
-				} else if (strpos($attributeLang, $similarLang . '_') === 0) {
1208
-					if ($similarLangFallback === false) {
1209
-						$similarLangFallback =  $option['@value'];
1210
-					}
1211
-				}
1212
-			} else {
1213
-				$englishFallback = $option;
1214
-			}
1215
-		}
1216
-
1217
-		if ($similarLangFallback !== false) {
1218
-			return $similarLangFallback;
1219
-		} else if ($englishFallback !== false) {
1220
-			return $englishFallback;
1221
-		}
1222
-		return (string) $fallback;
1223
-	}
1224
-
1225
-	/**
1226
-	 * parses the app data array and enhanced the 'description' value
1227
-	 *
1228
-	 * @param array $data the app data
1229
-	 * @param string $lang
1230
-	 * @return array improved app data
1231
-	 */
1232
-	public static function parseAppInfo(array $data, $lang = null) {
1233
-
1234
-		if ($lang && isset($data['name']) && is_array($data['name'])) {
1235
-			$data['name'] = self::findBestL10NOption($data['name'], $lang);
1236
-		}
1237
-		if ($lang && isset($data['summary']) && is_array($data['summary'])) {
1238
-			$data['summary'] = self::findBestL10NOption($data['summary'], $lang);
1239
-		}
1240
-		if ($lang && isset($data['description']) && is_array($data['description'])) {
1241
-			$data['description'] = trim(self::findBestL10NOption($data['description'], $lang));
1242
-		} else if (isset($data['description']) && is_string($data['description'])) {
1243
-			$data['description'] = trim($data['description']);
1244
-		} else  {
1245
-			$data['description'] = '';
1246
-		}
1247
-
1248
-		return $data;
1249
-	}
1250
-
1251
-	/**
1252
-	 * @param \OCP\IConfig $config
1253
-	 * @param \OCP\IL10N $l
1254
-	 * @param array $info
1255
-	 * @throws \Exception
1256
-	 */
1257
-	public static function checkAppDependencies($config, $l, $info) {
1258
-		$dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l);
1259
-		$missing = $dependencyAnalyzer->analyze($info);
1260
-		if (!empty($missing)) {
1261
-			$missingMsg = implode(PHP_EOL, $missing);
1262
-			throw new \Exception(
1263
-				$l->t('App "%s" cannot be installed because the following dependencies are not fulfilled: %s',
1264
-					[$info['name'], $missingMsg]
1265
-				)
1266
-			);
1267
-		}
1268
-	}
64
+    static private $appVersion = [];
65
+    static private $adminForms = array();
66
+    static private $personalForms = array();
67
+    static private $appInfo = array();
68
+    static private $appTypes = array();
69
+    static private $loadedApps = array();
70
+    static private $altLogin = array();
71
+    static private $alreadyRegistered = [];
72
+    const officialApp = 200;
73
+
74
+    /**
75
+     * clean the appId
76
+     *
77
+     * @param string|boolean $app AppId that needs to be cleaned
78
+     * @return string
79
+     */
80
+    public static function cleanAppId($app) {
81
+        return str_replace(array('\0', '/', '\\', '..'), '', $app);
82
+    }
83
+
84
+    /**
85
+     * Check if an app is loaded
86
+     *
87
+     * @param string $app
88
+     * @return bool
89
+     */
90
+    public static function isAppLoaded($app) {
91
+        return in_array($app, self::$loadedApps, true);
92
+    }
93
+
94
+    /**
95
+     * loads all apps
96
+     *
97
+     * @param string[] | string | null $types
98
+     * @return bool
99
+     *
100
+     * This function walks through the ownCloud directory and loads all apps
101
+     * it can find. A directory contains an app if the file /appinfo/info.xml
102
+     * exists.
103
+     *
104
+     * if $types is set, only apps of those types will be loaded
105
+     */
106
+    public static function loadApps($types = null) {
107
+        if (\OC::$server->getSystemConfig()->getValue('maintenance', false)) {
108
+            return false;
109
+        }
110
+        // Load the enabled apps here
111
+        $apps = self::getEnabledApps();
112
+
113
+        // Add each apps' folder as allowed class path
114
+        foreach($apps as $app) {
115
+            $path = self::getAppPath($app);
116
+            if($path !== false) {
117
+                self::registerAutoloading($app, $path);
118
+            }
119
+        }
120
+
121
+        // prevent app.php from printing output
122
+        ob_start();
123
+        foreach ($apps as $app) {
124
+            if ((is_null($types) or self::isType($app, $types)) && !in_array($app, self::$loadedApps)) {
125
+                self::loadApp($app);
126
+            }
127
+        }
128
+        ob_end_clean();
129
+
130
+        return true;
131
+    }
132
+
133
+    /**
134
+     * load a single app
135
+     *
136
+     * @param string $app
137
+     */
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
+            self::requireAppFile($app);
151
+            if (self::isType($app, array('authentication'))) {
152
+                // since authentication apps affect the "is app enabled for group" check,
153
+                // the enabled apps cache needs to be cleared to make sure that the
154
+                // next time getEnableApps() is called it will also include apps that were
155
+                // enabled for groups
156
+                self::$enabledAppsCache = array();
157
+            }
158
+            \OC::$server->getEventLogger()->end('load_app_' . $app);
159
+        }
160
+
161
+        $info = self::getAppInfo($app);
162
+        if (!empty($info['activity']['filters'])) {
163
+            foreach ($info['activity']['filters'] as $filter) {
164
+                \OC::$server->getActivityManager()->registerFilter($filter);
165
+            }
166
+        }
167
+        if (!empty($info['activity']['settings'])) {
168
+            foreach ($info['activity']['settings'] as $setting) {
169
+                \OC::$server->getActivityManager()->registerSetting($setting);
170
+            }
171
+        }
172
+        if (!empty($info['activity']['providers'])) {
173
+            foreach ($info['activity']['providers'] as $provider) {
174
+                \OC::$server->getActivityManager()->registerProvider($provider);
175
+            }
176
+        }
177
+        if (!empty($info['collaboration']['plugins'])) {
178
+            // deal with one or many plugin entries
179
+            $plugins = isset($info['collaboration']['plugins']['plugin']['@value']) ?
180
+                [$info['collaboration']['plugins']['plugin']] : $info['collaboration']['plugins']['plugin'];
181
+            foreach ($plugins as $plugin) {
182
+                if($plugin['@attributes']['type'] === 'collaborator-search') {
183
+                    $pluginInfo = [
184
+                        'shareType' => $plugin['@attributes']['share-type'],
185
+                        'class' => $plugin['@value'],
186
+                    ];
187
+                    \OC::$server->getCollaboratorSearch()->registerPlugin($pluginInfo);
188
+                }
189
+            }
190
+        }
191
+    }
192
+
193
+    /**
194
+     * @internal
195
+     * @param string $app
196
+     * @param string $path
197
+     */
198
+    public static function registerAutoloading($app, $path) {
199
+        $key = $app . '-' . $path;
200
+        if(isset(self::$alreadyRegistered[$key])) {
201
+            return;
202
+        }
203
+
204
+        self::$alreadyRegistered[$key] = true;
205
+
206
+        // Register on PSR-4 composer autoloader
207
+        $appNamespace = \OC\AppFramework\App::buildAppNamespace($app);
208
+        \OC::$server->registerNamespace($app, $appNamespace);
209
+
210
+        if (file_exists($path . '/composer/autoload.php')) {
211
+            require_once $path . '/composer/autoload.php';
212
+        } else {
213
+            \OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true);
214
+            // Register on legacy autoloader
215
+            \OC::$loader->addValidRoot($path);
216
+        }
217
+
218
+        // Register Test namespace only when testing
219
+        if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) {
220
+            \OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true);
221
+        }
222
+    }
223
+
224
+    /**
225
+     * Load app.php from the given app
226
+     *
227
+     * @param string $app app name
228
+     */
229
+    private static function requireAppFile($app) {
230
+        try {
231
+            // encapsulated here to avoid variable scope conflicts
232
+            require_once $app . '/appinfo/app.php';
233
+        } catch (Error $ex) {
234
+            \OC::$server->getLogger()->logException($ex);
235
+            $blacklist = \OC::$server->getAppManager()->getAlwaysEnabledApps();
236
+            if (!in_array($app, $blacklist)) {
237
+                self::disable($app);
238
+            }
239
+        }
240
+    }
241
+
242
+    /**
243
+     * check if an app is of a specific type
244
+     *
245
+     * @param string $app
246
+     * @param string|array $types
247
+     * @return bool
248
+     */
249
+    public static function isType($app, $types) {
250
+        if (is_string($types)) {
251
+            $types = array($types);
252
+        }
253
+        $appTypes = self::getAppTypes($app);
254
+        foreach ($types as $type) {
255
+            if (array_search($type, $appTypes) !== false) {
256
+                return true;
257
+            }
258
+        }
259
+        return false;
260
+    }
261
+
262
+    /**
263
+     * get the types of an app
264
+     *
265
+     * @param string $app
266
+     * @return array
267
+     */
268
+    private static function getAppTypes($app) {
269
+        //load the cache
270
+        if (count(self::$appTypes) == 0) {
271
+            self::$appTypes = \OC::$server->getAppConfig()->getValues(false, 'types');
272
+        }
273
+
274
+        if (isset(self::$appTypes[$app])) {
275
+            return explode(',', self::$appTypes[$app]);
276
+        } else {
277
+            return array();
278
+        }
279
+    }
280
+
281
+    /**
282
+     * read app types from info.xml and cache them in the database
283
+     */
284
+    public static function setAppTypes($app) {
285
+        $appData = self::getAppInfo($app);
286
+        if(!is_array($appData)) {
287
+            return;
288
+        }
289
+
290
+        if (isset($appData['types'])) {
291
+            $appTypes = implode(',', $appData['types']);
292
+        } else {
293
+            $appTypes = '';
294
+            $appData['types'] = [];
295
+        }
296
+
297
+        \OC::$server->getAppConfig()->setValue($app, 'types', $appTypes);
298
+
299
+        if (\OC::$server->getAppManager()->hasProtectedAppType($appData['types'])) {
300
+            $enabled = \OC::$server->getAppConfig()->getValue($app, 'enabled', 'yes');
301
+            if ($enabled !== 'yes' && $enabled !== 'no') {
302
+                \OC::$server->getAppConfig()->setValue($app, 'enabled', 'yes');
303
+            }
304
+        }
305
+    }
306
+
307
+    /**
308
+     * get all enabled apps
309
+     */
310
+    protected static $enabledAppsCache = array();
311
+
312
+    /**
313
+     * Returns apps enabled for the current user.
314
+     *
315
+     * @param bool $forceRefresh whether to refresh the cache
316
+     * @param bool $all whether to return apps for all users, not only the
317
+     * currently logged in one
318
+     * @return string[]
319
+     */
320
+    public static function getEnabledApps($forceRefresh = false, $all = false) {
321
+        if (!\OC::$server->getSystemConfig()->getValue('installed', false)) {
322
+            return array();
323
+        }
324
+        // in incognito mode or when logged out, $user will be false,
325
+        // which is also the case during an upgrade
326
+        $appManager = \OC::$server->getAppManager();
327
+        if ($all) {
328
+            $user = null;
329
+        } else {
330
+            $user = \OC::$server->getUserSession()->getUser();
331
+        }
332
+
333
+        if (is_null($user)) {
334
+            $apps = $appManager->getInstalledApps();
335
+        } else {
336
+            $apps = $appManager->getEnabledAppsForUser($user);
337
+        }
338
+        $apps = array_filter($apps, function ($app) {
339
+            return $app !== 'files';//we add this manually
340
+        });
341
+        sort($apps);
342
+        array_unshift($apps, 'files');
343
+        return $apps;
344
+    }
345
+
346
+    /**
347
+     * checks whether or not an app is enabled
348
+     *
349
+     * @param string $app app
350
+     * @return bool
351
+     * @deprecated 13.0.0 use \OC::$server->getAppManager()->isEnabledForUser($appId)
352
+     *
353
+     * This function checks whether or not an app is enabled.
354
+     */
355
+    public static function isEnabled($app) {
356
+        return \OC::$server->getAppManager()->isEnabledForUser($app);
357
+    }
358
+
359
+    /**
360
+     * enables an app
361
+     *
362
+     * @param string $appId
363
+     * @param array $groups (optional) when set, only these groups will have access to the app
364
+     * @throws \Exception
365
+     * @return void
366
+     *
367
+     * This function set an app as enabled in appconfig.
368
+     */
369
+    public function enable($appId,
370
+                            $groups = null) {
371
+        self::$enabledAppsCache = []; // flush
372
+
373
+        // Check if app is already downloaded
374
+        $installer = new Installer(
375
+            \OC::$server->getAppFetcher(),
376
+            \OC::$server->getHTTPClientService(),
377
+            \OC::$server->getTempManager(),
378
+            \OC::$server->getLogger(),
379
+            \OC::$server->getConfig()
380
+        );
381
+        $isDownloaded = $installer->isDownloaded($appId);
382
+
383
+        if(!$isDownloaded) {
384
+            $installer->downloadApp($appId);
385
+        }
386
+
387
+        $installer->installApp($appId);
388
+
389
+        $appManager = \OC::$server->getAppManager();
390
+        if (!is_null($groups)) {
391
+            $groupManager = \OC::$server->getGroupManager();
392
+            $groupsList = [];
393
+            foreach ($groups as $group) {
394
+                $groupItem = $groupManager->get($group);
395
+                if ($groupItem instanceof \OCP\IGroup) {
396
+                    $groupsList[] = $groupManager->get($group);
397
+                }
398
+            }
399
+            $appManager->enableAppForGroups($appId, $groupsList);
400
+        } else {
401
+            $appManager->enableApp($appId);
402
+        }
403
+    }
404
+
405
+    /**
406
+     * @param string $app
407
+     * @return bool
408
+     */
409
+    public static function removeApp($app) {
410
+        if (\OC::$server->getAppManager()->isShipped($app)) {
411
+            return false;
412
+        }
413
+
414
+        $installer = new Installer(
415
+            \OC::$server->getAppFetcher(),
416
+            \OC::$server->getHTTPClientService(),
417
+            \OC::$server->getTempManager(),
418
+            \OC::$server->getLogger(),
419
+            \OC::$server->getConfig()
420
+        );
421
+        return $installer->removeApp($app);
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
+     */
580
+    public static function getAppVersion($appId, $useCache = true) {
581
+        if($useCache && isset(self::$appVersion[$appId])) {
582
+            return self::$appVersion[$appId];
583
+        }
584
+
585
+        $file = self::getAppPath($appId);
586
+        self::$appVersion[$appId] = ($file !== false) ? self::getAppVersionByPath($file) : '0';
587
+        return self::$appVersion[$appId];
588
+    }
589
+
590
+    /**
591
+     * get app's version based on it's path
592
+     *
593
+     * @param string $path
594
+     * @return string
595
+     */
596
+    public static function getAppVersionByPath($path) {
597
+        $infoFile = $path . '/appinfo/info.xml';
598
+        $appData = self::getAppInfo($infoFile, true);
599
+        return isset($appData['version']) ? $appData['version'] : '';
600
+    }
601
+
602
+
603
+    /**
604
+     * Read all app metadata from the info.xml file
605
+     *
606
+     * @param string $appId id of the app or the path of the info.xml file
607
+     * @param bool $path
608
+     * @param string $lang
609
+     * @return array|null
610
+     * @note all data is read from info.xml, not just pre-defined fields
611
+     */
612
+    public static function getAppInfo($appId, $path = false, $lang = null) {
613
+        if ($path) {
614
+            $file = $appId;
615
+        } else {
616
+            if ($lang === null && isset(self::$appInfo[$appId])) {
617
+                return self::$appInfo[$appId];
618
+            }
619
+            $appPath = self::getAppPath($appId);
620
+            if($appPath === false) {
621
+                return null;
622
+            }
623
+            $file = $appPath . '/appinfo/info.xml';
624
+        }
625
+
626
+        $parser = new InfoParser(\OC::$server->getMemCacheFactory()->createLocal('core.appinfo'));
627
+        $data = $parser->parse($file);
628
+
629
+        if (is_array($data)) {
630
+            $data = OC_App::parseAppInfo($data, $lang);
631
+        }
632
+        if(isset($data['ocsid'])) {
633
+            $storedId = \OC::$server->getConfig()->getAppValue($appId, 'ocsid');
634
+            if($storedId !== '' && $storedId !== $data['ocsid']) {
635
+                $data['ocsid'] = $storedId;
636
+            }
637
+        }
638
+
639
+        if ($lang === null) {
640
+            self::$appInfo[$appId] = $data;
641
+        }
642
+
643
+        return $data;
644
+    }
645
+
646
+    /**
647
+     * Returns the navigation
648
+     *
649
+     * @return array
650
+     *
651
+     * This function returns an array containing all entries added. The
652
+     * entries are sorted by the key 'order' ascending. Additional to the keys
653
+     * given for each app the following keys exist:
654
+     *   - active: boolean, signals if the user is on this navigation entry
655
+     */
656
+    public static function getNavigation() {
657
+        $entries = OC::$server->getNavigationManager()->getAll();
658
+        return self::proceedNavigation($entries);
659
+    }
660
+
661
+    /**
662
+     * Returns the Settings Navigation
663
+     *
664
+     * @return string[]
665
+     *
666
+     * This function returns an array containing all settings pages added. The
667
+     * entries are sorted by the key 'order' ascending.
668
+     */
669
+    public static function getSettingsNavigation() {
670
+        $entries = OC::$server->getNavigationManager()->getAll('settings');
671
+        return self::proceedNavigation($entries);
672
+    }
673
+
674
+    /**
675
+     * get the id of loaded app
676
+     *
677
+     * @return string
678
+     */
679
+    public static function getCurrentApp() {
680
+        $request = \OC::$server->getRequest();
681
+        $script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1);
682
+        $topFolder = substr($script, 0, strpos($script, '/'));
683
+        if (empty($topFolder)) {
684
+            $path_info = $request->getPathInfo();
685
+            if ($path_info) {
686
+                $topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1);
687
+            }
688
+        }
689
+        if ($topFolder == 'apps') {
690
+            $length = strlen($topFolder);
691
+            return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1);
692
+        } else {
693
+            return $topFolder;
694
+        }
695
+    }
696
+
697
+    /**
698
+     * @param string $type
699
+     * @return array
700
+     */
701
+    public static function getForms($type) {
702
+        $forms = array();
703
+        switch ($type) {
704
+            case 'admin':
705
+                $source = self::$adminForms;
706
+                break;
707
+            case 'personal':
708
+                $source = self::$personalForms;
709
+                break;
710
+            default:
711
+                return array();
712
+        }
713
+        foreach ($source as $form) {
714
+            $forms[] = include $form;
715
+        }
716
+        return $forms;
717
+    }
718
+
719
+    /**
720
+     * register an admin form to be shown
721
+     *
722
+     * @param string $app
723
+     * @param string $page
724
+     */
725
+    public static function registerAdmin($app, $page) {
726
+        self::$adminForms[] = $app . '/' . $page . '.php';
727
+    }
728
+
729
+    /**
730
+     * register a personal form to be shown
731
+     * @param string $app
732
+     * @param string $page
733
+     */
734
+    public static function registerPersonal($app, $page) {
735
+        self::$personalForms[] = $app . '/' . $page . '.php';
736
+    }
737
+
738
+    /**
739
+     * @param array $entry
740
+     */
741
+    public static function registerLogIn(array $entry) {
742
+        self::$altLogin[] = $entry;
743
+    }
744
+
745
+    /**
746
+     * @return array
747
+     */
748
+    public static function getAlternativeLogIns() {
749
+        return self::$altLogin;
750
+    }
751
+
752
+    /**
753
+     * get a list of all apps in the apps folder
754
+     *
755
+     * @return array an array of app names (string IDs)
756
+     * @todo: change the name of this method to getInstalledApps, which is more accurate
757
+     */
758
+    public static function getAllApps() {
759
+
760
+        $apps = array();
761
+
762
+        foreach (OC::$APPSROOTS as $apps_dir) {
763
+            if (!is_readable($apps_dir['path'])) {
764
+                \OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], \OCP\Util::WARN);
765
+                continue;
766
+            }
767
+            $dh = opendir($apps_dir['path']);
768
+
769
+            if (is_resource($dh)) {
770
+                while (($file = readdir($dh)) !== false) {
771
+
772
+                    if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
773
+
774
+                        $apps[] = $file;
775
+                    }
776
+                }
777
+            }
778
+        }
779
+
780
+        $apps = array_unique($apps);
781
+
782
+        return $apps;
783
+    }
784
+
785
+    /**
786
+     * List all apps, this is used in apps.php
787
+     *
788
+     * @return array
789
+     */
790
+    public function listAllApps() {
791
+        $installedApps = OC_App::getAllApps();
792
+
793
+        $appManager = \OC::$server->getAppManager();
794
+        //we don't want to show configuration for these
795
+        $blacklist = $appManager->getAlwaysEnabledApps();
796
+        $appList = array();
797
+        $langCode = \OC::$server->getL10N('core')->getLanguageCode();
798
+        $urlGenerator = \OC::$server->getURLGenerator();
799
+
800
+        foreach ($installedApps as $app) {
801
+            if (array_search($app, $blacklist) === false) {
802
+
803
+                $info = OC_App::getAppInfo($app, false, $langCode);
804
+                if (!is_array($info)) {
805
+                    \OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', \OCP\Util::ERROR);
806
+                    continue;
807
+                }
808
+
809
+                if (!isset($info['name'])) {
810
+                    \OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', \OCP\Util::ERROR);
811
+                    continue;
812
+                }
813
+
814
+                $enabled = \OC::$server->getAppConfig()->getValue($app, 'enabled', 'no');
815
+                $info['groups'] = null;
816
+                if ($enabled === 'yes') {
817
+                    $active = true;
818
+                } else if ($enabled === 'no') {
819
+                    $active = false;
820
+                } else {
821
+                    $active = true;
822
+                    $info['groups'] = $enabled;
823
+                }
824
+
825
+                $info['active'] = $active;
826
+
827
+                if ($appManager->isShipped($app)) {
828
+                    $info['internal'] = true;
829
+                    $info['level'] = self::officialApp;
830
+                    $info['removable'] = false;
831
+                } else {
832
+                    $info['internal'] = false;
833
+                    $info['removable'] = true;
834
+                }
835
+
836
+                $appPath = self::getAppPath($app);
837
+                if($appPath !== false) {
838
+                    $appIcon = $appPath . '/img/' . $app . '.svg';
839
+                    if (file_exists($appIcon)) {
840
+                        $info['preview'] = $urlGenerator->imagePath($app, $app . '.svg');
841
+                        $info['previewAsIcon'] = true;
842
+                    } else {
843
+                        $appIcon = $appPath . '/img/app.svg';
844
+                        if (file_exists($appIcon)) {
845
+                            $info['preview'] = $urlGenerator->imagePath($app, 'app.svg');
846
+                            $info['previewAsIcon'] = true;
847
+                        }
848
+                    }
849
+                }
850
+                // fix documentation
851
+                if (isset($info['documentation']) && is_array($info['documentation'])) {
852
+                    foreach ($info['documentation'] as $key => $url) {
853
+                        // If it is not an absolute URL we assume it is a key
854
+                        // i.e. admin-ldap will get converted to go.php?to=admin-ldap
855
+                        if (stripos($url, 'https://') !== 0 && stripos($url, 'http://') !== 0) {
856
+                            $url = $urlGenerator->linkToDocs($url);
857
+                        }
858
+
859
+                        $info['documentation'][$key] = $url;
860
+                    }
861
+                }
862
+
863
+                $info['version'] = OC_App::getAppVersion($app);
864
+                $appList[] = $info;
865
+            }
866
+        }
867
+
868
+        return $appList;
869
+    }
870
+
871
+    /**
872
+     * Returns the internal app ID or false
873
+     * @param string $ocsID
874
+     * @return string|false
875
+     */
876
+    public static function getInternalAppIdByOcs($ocsID) {
877
+        if(is_numeric($ocsID)) {
878
+            $idArray = \OC::$server->getAppConfig()->getValues(false, 'ocsid');
879
+            if(array_search($ocsID, $idArray)) {
880
+                return array_search($ocsID, $idArray);
881
+            }
882
+        }
883
+        return false;
884
+    }
885
+
886
+    public static function shouldUpgrade($app) {
887
+        $versions = self::getAppVersions();
888
+        $currentVersion = OC_App::getAppVersion($app);
889
+        if ($currentVersion && isset($versions[$app])) {
890
+            $installedVersion = $versions[$app];
891
+            if (!version_compare($currentVersion, $installedVersion, '=')) {
892
+                return true;
893
+            }
894
+        }
895
+        return false;
896
+    }
897
+
898
+    /**
899
+     * Adjust the number of version parts of $version1 to match
900
+     * the number of version parts of $version2.
901
+     *
902
+     * @param string $version1 version to adjust
903
+     * @param string $version2 version to take the number of parts from
904
+     * @return string shortened $version1
905
+     */
906
+    private static function adjustVersionParts($version1, $version2) {
907
+        $version1 = explode('.', $version1);
908
+        $version2 = explode('.', $version2);
909
+        // reduce $version1 to match the number of parts in $version2
910
+        while (count($version1) > count($version2)) {
911
+            array_pop($version1);
912
+        }
913
+        // if $version1 does not have enough parts, add some
914
+        while (count($version1) < count($version2)) {
915
+            $version1[] = '0';
916
+        }
917
+        return implode('.', $version1);
918
+    }
919
+
920
+    /**
921
+     * Check whether the current ownCloud version matches the given
922
+     * application's version requirements.
923
+     *
924
+     * The comparison is made based on the number of parts that the
925
+     * app info version has. For example for ownCloud 6.0.3 if the
926
+     * app info version is expecting version 6.0, the comparison is
927
+     * made on the first two parts of the ownCloud version.
928
+     * This means that it's possible to specify "requiremin" => 6
929
+     * and "requiremax" => 6 and it will still match ownCloud 6.0.3.
930
+     *
931
+     * @param string $ocVersion ownCloud version to check against
932
+     * @param array $appInfo app info (from xml)
933
+     *
934
+     * @return boolean true if compatible, otherwise false
935
+     */
936
+    public static function isAppCompatible($ocVersion, $appInfo) {
937
+        $requireMin = '';
938
+        $requireMax = '';
939
+        if (isset($appInfo['dependencies']['nextcloud']['@attributes']['min-version'])) {
940
+            $requireMin = $appInfo['dependencies']['nextcloud']['@attributes']['min-version'];
941
+        } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) {
942
+            $requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version'];
943
+        } else if (isset($appInfo['requiremin'])) {
944
+            $requireMin = $appInfo['requiremin'];
945
+        } else if (isset($appInfo['require'])) {
946
+            $requireMin = $appInfo['require'];
947
+        }
948
+
949
+        if (isset($appInfo['dependencies']['nextcloud']['@attributes']['max-version'])) {
950
+            $requireMax = $appInfo['dependencies']['nextcloud']['@attributes']['max-version'];
951
+        } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) {
952
+            $requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version'];
953
+        } else if (isset($appInfo['requiremax'])) {
954
+            $requireMax = $appInfo['requiremax'];
955
+        }
956
+
957
+        if (is_array($ocVersion)) {
958
+            $ocVersion = implode('.', $ocVersion);
959
+        }
960
+
961
+        if (!empty($requireMin)
962
+            && version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<')
963
+        ) {
964
+
965
+            return false;
966
+        }
967
+
968
+        if (!empty($requireMax)
969
+            && version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>')
970
+        ) {
971
+            return false;
972
+        }
973
+
974
+        return true;
975
+    }
976
+
977
+    /**
978
+     * get the installed version of all apps
979
+     */
980
+    public static function getAppVersions() {
981
+        static $versions;
982
+
983
+        if(!$versions) {
984
+            $appConfig = \OC::$server->getAppConfig();
985
+            $versions = $appConfig->getValues(false, 'installed_version');
986
+        }
987
+        return $versions;
988
+    }
989
+
990
+    /**
991
+     * @param string $app
992
+     * @param \OCP\IConfig $config
993
+     * @param \OCP\IL10N $l
994
+     * @return bool
995
+     *
996
+     * @throws Exception if app is not compatible with this version of ownCloud
997
+     * @throws Exception if no app-name was specified
998
+     */
999
+    public function installApp($app,
1000
+                                \OCP\IConfig $config,
1001
+                                \OCP\IL10N $l) {
1002
+        if ($app !== false) {
1003
+            // check if the app is compatible with this version of ownCloud
1004
+            $info = self::getAppInfo($app);
1005
+            if(!is_array($info)) {
1006
+                throw new \Exception(
1007
+                    $l->t('App "%s" cannot be installed because appinfo file cannot be read.',
1008
+                        [$info['name']]
1009
+                    )
1010
+                );
1011
+            }
1012
+
1013
+            $version = \OCP\Util::getVersion();
1014
+            if (!self::isAppCompatible($version, $info)) {
1015
+                throw new \Exception(
1016
+                    $l->t('App "%s" cannot be installed because it is not compatible with this version of the server.',
1017
+                        array($info['name'])
1018
+                    )
1019
+                );
1020
+            }
1021
+
1022
+            // check for required dependencies
1023
+            self::checkAppDependencies($config, $l, $info);
1024
+
1025
+            $config->setAppValue($app, 'enabled', 'yes');
1026
+            if (isset($appData['id'])) {
1027
+                $config->setAppValue($app, 'ocsid', $appData['id']);
1028
+            }
1029
+
1030
+            if(isset($info['settings']) && is_array($info['settings'])) {
1031
+                $appPath = self::getAppPath($app);
1032
+                self::registerAutoloading($app, $appPath);
1033
+                \OC::$server->getSettingsManager()->setupSettings($info['settings']);
1034
+            }
1035
+
1036
+            \OC_Hook::emit('OC_App', 'post_enable', array('app' => $app));
1037
+        } else {
1038
+            if(empty($appName) ) {
1039
+                throw new \Exception($l->t("No app name specified"));
1040
+            } else {
1041
+                throw new \Exception($l->t("App '%s' could not be installed!", $appName));
1042
+            }
1043
+        }
1044
+
1045
+        return $app;
1046
+    }
1047
+
1048
+    /**
1049
+     * update the database for the app and call the update script
1050
+     *
1051
+     * @param string $appId
1052
+     * @return bool
1053
+     */
1054
+    public static function updateApp($appId) {
1055
+        $appPath = self::getAppPath($appId);
1056
+        if($appPath === false) {
1057
+            return false;
1058
+        }
1059
+        self::registerAutoloading($appId, $appPath);
1060
+
1061
+        $appData = self::getAppInfo($appId);
1062
+        self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']);
1063
+
1064
+        if (file_exists($appPath . '/appinfo/database.xml')) {
1065
+            OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml');
1066
+        } else {
1067
+            $ms = new MigrationService($appId, \OC::$server->getDatabaseConnection());
1068
+            $ms->migrate();
1069
+        }
1070
+
1071
+        self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']);
1072
+        self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']);
1073
+        unset(self::$appVersion[$appId]);
1074
+
1075
+        // run upgrade code
1076
+        if (file_exists($appPath . '/appinfo/update.php')) {
1077
+            self::loadApp($appId);
1078
+            include $appPath . '/appinfo/update.php';
1079
+        }
1080
+        self::setupBackgroundJobs($appData['background-jobs']);
1081
+        if(isset($appData['settings']) && is_array($appData['settings'])) {
1082
+            \OC::$server->getSettingsManager()->setupSettings($appData['settings']);
1083
+        }
1084
+
1085
+        //set remote/public handlers
1086
+        if (array_key_exists('ocsid', $appData)) {
1087
+            \OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']);
1088
+        } elseif(\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
1089
+            \OC::$server->getConfig()->deleteAppValue($appId, 'ocsid');
1090
+        }
1091
+        foreach ($appData['remote'] as $name => $path) {
1092
+            \OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
1093
+        }
1094
+        foreach ($appData['public'] as $name => $path) {
1095
+            \OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path);
1096
+        }
1097
+
1098
+        self::setAppTypes($appId);
1099
+
1100
+        $version = \OC_App::getAppVersion($appId);
1101
+        \OC::$server->getAppConfig()->setValue($appId, 'installed_version', $version);
1102
+
1103
+        \OC::$server->getEventDispatcher()->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent(
1104
+            ManagerEvent::EVENT_APP_UPDATE, $appId
1105
+        ));
1106
+
1107
+        return true;
1108
+    }
1109
+
1110
+    /**
1111
+     * @param string $appId
1112
+     * @param string[] $steps
1113
+     * @throws \OC\NeedsUpdateException
1114
+     */
1115
+    public static function executeRepairSteps($appId, array $steps) {
1116
+        if (empty($steps)) {
1117
+            return;
1118
+        }
1119
+        // load the app
1120
+        self::loadApp($appId);
1121
+
1122
+        $dispatcher = OC::$server->getEventDispatcher();
1123
+
1124
+        // load the steps
1125
+        $r = new Repair([], $dispatcher);
1126
+        foreach ($steps as $step) {
1127
+            try {
1128
+                $r->addStep($step);
1129
+            } catch (Exception $ex) {
1130
+                $r->emit('\OC\Repair', 'error', [$ex->getMessage()]);
1131
+                \OC::$server->getLogger()->logException($ex);
1132
+            }
1133
+        }
1134
+        // run the steps
1135
+        $r->run();
1136
+    }
1137
+
1138
+    public static function setupBackgroundJobs(array $jobs) {
1139
+        $queue = \OC::$server->getJobList();
1140
+        foreach ($jobs as $job) {
1141
+            $queue->add($job);
1142
+        }
1143
+    }
1144
+
1145
+    /**
1146
+     * @param string $appId
1147
+     * @param string[] $steps
1148
+     */
1149
+    private static function setupLiveMigrations($appId, array $steps) {
1150
+        $queue = \OC::$server->getJobList();
1151
+        foreach ($steps as $step) {
1152
+            $queue->add('OC\Migration\BackgroundRepair', [
1153
+                'app' => $appId,
1154
+                'step' => $step]);
1155
+        }
1156
+    }
1157
+
1158
+    /**
1159
+     * @param string $appId
1160
+     * @return \OC\Files\View|false
1161
+     */
1162
+    public static function getStorage($appId) {
1163
+        if (\OC::$server->getAppManager()->isEnabledForUser($appId)) { //sanity check
1164
+            if (\OC::$server->getUserSession()->isLoggedIn()) {
1165
+                $view = new \OC\Files\View('/' . OC_User::getUser());
1166
+                if (!$view->file_exists($appId)) {
1167
+                    $view->mkdir($appId);
1168
+                }
1169
+                return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId);
1170
+            } else {
1171
+                \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', \OCP\Util::ERROR);
1172
+                return false;
1173
+            }
1174
+        } else {
1175
+            \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', \OCP\Util::ERROR);
1176
+            return false;
1177
+        }
1178
+    }
1179
+
1180
+    protected static function findBestL10NOption($options, $lang) {
1181
+        $fallback = $similarLangFallback = $englishFallback = false;
1182
+
1183
+        $lang = strtolower($lang);
1184
+        $similarLang = $lang;
1185
+        if (strpos($similarLang, '_')) {
1186
+            // For "de_DE" we want to find "de" and the other way around
1187
+            $similarLang = substr($lang, 0, strpos($lang, '_'));
1188
+        }
1189
+
1190
+        foreach ($options as $option) {
1191
+            if (is_array($option)) {
1192
+                if ($fallback === false) {
1193
+                    $fallback = $option['@value'];
1194
+                }
1195
+
1196
+                if (!isset($option['@attributes']['lang'])) {
1197
+                    continue;
1198
+                }
1199
+
1200
+                $attributeLang = strtolower($option['@attributes']['lang']);
1201
+                if ($attributeLang === $lang) {
1202
+                    return $option['@value'];
1203
+                }
1204
+
1205
+                if ($attributeLang === $similarLang) {
1206
+                    $similarLangFallback = $option['@value'];
1207
+                } else if (strpos($attributeLang, $similarLang . '_') === 0) {
1208
+                    if ($similarLangFallback === false) {
1209
+                        $similarLangFallback =  $option['@value'];
1210
+                    }
1211
+                }
1212
+            } else {
1213
+                $englishFallback = $option;
1214
+            }
1215
+        }
1216
+
1217
+        if ($similarLangFallback !== false) {
1218
+            return $similarLangFallback;
1219
+        } else if ($englishFallback !== false) {
1220
+            return $englishFallback;
1221
+        }
1222
+        return (string) $fallback;
1223
+    }
1224
+
1225
+    /**
1226
+     * parses the app data array and enhanced the 'description' value
1227
+     *
1228
+     * @param array $data the app data
1229
+     * @param string $lang
1230
+     * @return array improved app data
1231
+     */
1232
+    public static function parseAppInfo(array $data, $lang = null) {
1233
+
1234
+        if ($lang && isset($data['name']) && is_array($data['name'])) {
1235
+            $data['name'] = self::findBestL10NOption($data['name'], $lang);
1236
+        }
1237
+        if ($lang && isset($data['summary']) && is_array($data['summary'])) {
1238
+            $data['summary'] = self::findBestL10NOption($data['summary'], $lang);
1239
+        }
1240
+        if ($lang && isset($data['description']) && is_array($data['description'])) {
1241
+            $data['description'] = trim(self::findBestL10NOption($data['description'], $lang));
1242
+        } else if (isset($data['description']) && is_string($data['description'])) {
1243
+            $data['description'] = trim($data['description']);
1244
+        } else  {
1245
+            $data['description'] = '';
1246
+        }
1247
+
1248
+        return $data;
1249
+    }
1250
+
1251
+    /**
1252
+     * @param \OCP\IConfig $config
1253
+     * @param \OCP\IL10N $l
1254
+     * @param array $info
1255
+     * @throws \Exception
1256
+     */
1257
+    public static function checkAppDependencies($config, $l, $info) {
1258
+        $dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l);
1259
+        $missing = $dependencyAnalyzer->analyze($info);
1260
+        if (!empty($missing)) {
1261
+            $missingMsg = implode(PHP_EOL, $missing);
1262
+            throw new \Exception(
1263
+                $l->t('App "%s" cannot be installed because the following dependencies are not fulfilled: %s',
1264
+                    [$info['name'], $missingMsg]
1265
+                )
1266
+            );
1267
+        }
1268
+    }
1269 1269
 }
Please login to merge, or discard this patch.