Completed
Pull Request — stable8.2 (#24656)
by Joas
12:22
created

OC_App::setActiveNavigationEntry()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2
Metric Value
dl 0
loc 4
ccs 0
cts 3
cp 0
rs 10
cc 1
eloc 3
nc 1
nop 1
crap 2
1
<?php
2
/**
3
 * @author Arthur Schiwon <[email protected]>
4
 * @author Bart Visscher <[email protected]>
5
 * @author Bernhard Posselt <[email protected]>
6
 * @author Björn Schießle <[email protected]>
7
 * @author Borjan Tchakaloff <[email protected]>
8
 * @author Brice Maron <[email protected]>
9
 * @author Christopher Schäpers <[email protected]>
10
 * @author Felix Moeller <[email protected]>
11
 * @author Frank Karlitschek <[email protected]>
12
 * @author Georg Ehrke <[email protected]>
13
 * @author Jakob Sack <[email protected]>
14
 * @author Jan-Christoph Borchardt <[email protected]>
15
 * @author Joas Schilling <[email protected]>
16
 * @author Jörn Friedrich Dreyer <[email protected]>
17
 * @author Kamil Domanski <[email protected]>
18
 * @author Lukas Reschke <[email protected]>
19
 * @author Markus Goetz <[email protected]>
20
 * @author Morris Jobke <[email protected]>
21
 * @author RealRancor <[email protected]>
22
 * @author Robin Appelman <[email protected]>
23
 * @author Robin McCorkell <[email protected]>
24
 * @author Sam Tuke <[email protected]>
25
 * @author Scrutinizer Auto-Fixer <[email protected]>
26
 * @author Thomas Müller <[email protected]>
27
 * @author Thomas Tanghus <[email protected]>
28
 * @author Tom Needham <[email protected]>
29
 * @author Vincent Petry <[email protected]>
30
 *
31
 * @copyright Copyright (c) 2015, ownCloud, Inc.
32
 * @license AGPL-3.0
33
 *
34
 * This code is free software: you can redistribute it and/or modify
35
 * it under the terms of the GNU Affero General Public License, version 3,
36
 * as published by the Free Software Foundation.
37
 *
38
 * This program is distributed in the hope that it will be useful,
39
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
40
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
41
 * GNU Affero General Public License for more details.
42
 *
43
 * You should have received a copy of the GNU Affero General Public License, version 3,
44
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
45
 *
46
 */
47
use OC\App\DependencyAnalyzer;
48
use OC\App\Platform;
49
use OC\OCSClient;
50
51
/**
52
 * This class manages the apps. It allows them to register and integrate in the
53
 * ownCloud ecosystem. Furthermore, this class is responsible for installing,
54
 * upgrading and removing apps.
55
 */
56
class OC_App {
57
	static private $appVersion = [];
58
	static private $adminForms = array();
59
	static private $personalForms = array();
60
	static private $appInfo = array();
61
	static private $appTypes = array();
62
	static private $loadedApps = array();
63
	static private $altLogin = array();
64
	private static $shippedApps = null;
65
	const officialApp = 200;
66
67
	/**
68
	 * clean the appId
69
	 *
70
	 * @param string|boolean $app AppId that needs to be cleaned
71
	 * @return string
72
	 */
73 86
	public static function cleanAppId($app) {
74 86
		return str_replace(array('\0', '/', '\\', '..'), '', $app);
75
	}
76
77
	/**
78
	 * Check if an app is loaded
79
	 *
80
	 * @param string $app
81
	 * @return bool
82
	 */
83 1
	public static function isAppLoaded($app) {
84 1
		return in_array($app, self::$loadedApps, true);
85
	}
86
87
	/**
88
	 * loads all apps
89
	 *
90
	 * @param array $types
91
	 * @return bool
92
	 *
93
	 * This function walks through the ownCloud directory and loads all apps
94
	 * it can find. A directory contains an app if the file /appinfo/info.xml
95
	 * exists.
96
	 *
97
	 * if $types is set, only apps of those types will be loaded
98
	 */
99 1055
	public static function loadApps($types = null) {
100 1055
		if (OC_Config::getValue('maintenance', false)) {
101
			return false;
102
		}
103
		// Load the enabled apps here
104 1055
		$apps = self::getEnabledApps();
105
106
		// Add each apps' folder as allowed class path
107 1055
		foreach($apps as $app) {
108 1055
			$path = self::getAppPath($app);
109 1055
			if($path !== false) {
110 1055
				\OC::$loader->addValidRoot($path);
111 1055
			}
112 1055
		}
113
114
		// prevent app.php from printing output
115 1055
		ob_start();
116 1055
		foreach ($apps as $app) {
117 1055
			if ((is_null($types) or self::isType($app, $types)) && !in_array($app, self::$loadedApps)) {
118
				self::loadApp($app);
119
			}
120 1055
		}
121 1055
		ob_end_clean();
122
123 1055
		return true;
124
	}
125
126
	/**
127
	 * load a single app
128
	 *
129
	 * @param string $app
130
	 * @param bool $checkUpgrade whether an upgrade check should be done
131
	 * @throws \OC\NeedsUpdateException
132
	 */
133 5
	public static function loadApp($app, $checkUpgrade = true) {
134 5
		self::$loadedApps[] = $app;
135 5
		\OC::$loader->addValidRoot(self::getAppPath($app)); // in case someone calls loadApp() directly
0 ignored issues
show
Security Bug introduced by
It seems like self::getAppPath($app) targeting OC_App::getAppPath() can also be of type false; however, OC\Autoloader::addValidRoot() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
136 5
		if (is_file(self::getAppPath($app) . '/appinfo/app.php')) {
137 5
			\OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app);
138 5
			if ($checkUpgrade and self::shouldUpgrade($app)) {
139
				throw new \OC\NeedsUpdateException();
140
			}
141 5
			self::requireAppFile($app);
142 5
			if (self::isType($app, array('authentication'))) {
143
				// since authentication apps affect the "is app enabled for group" check,
144
				// the enabled apps cache needs to be cleared to make sure that the
145
				// next time getEnableApps() is called it will also include apps that were
146
				// enabled for groups
147
				self::$enabledAppsCache = array();
148
			}
149 5
			\OC::$server->getEventLogger()->end('load_app_' . $app);
150 5
		}
151 5
	}
152
153
	/**
154
	 * Load app.php from the given app
155
	 *
156
	 * @param string $app app name
157
	 */
158 5
	private static function requireAppFile($app) {
159
		// encapsulated here to avoid variable scope conflicts
160 5
		require_once $app . '/appinfo/app.php';
161 5
	}
162
163
	/**
164
	 * check if an app is of a specific type
165
	 *
166
	 * @param string $app
167
	 * @param string|array $types
168
	 * @return bool
169
	 */
170 1055
	public static function isType($app, $types) {
171 1055
		if (is_string($types)) {
172
			$types = array($types);
173
		}
174 1055
		$appTypes = self::getAppTypes($app);
175 1055
		foreach ($types as $type) {
176 1055
			if (array_search($type, $appTypes) !== false) {
177 1055
				return true;
178
			}
179 1055
		}
180 1055
		return false;
181
	}
182
183
	/**
184
	 * get the types of an app
185
	 *
186
	 * @param string $app
187
	 * @return array
188
	 */
189 1055
	private static function getAppTypes($app) {
190
		//load the cache
191 1055
		if (count(self::$appTypes) == 0) {
192
			self::$appTypes = \OC::$server->getAppConfig()->getValues(false, 'types');
0 ignored issues
show
Documentation Bug introduced by
It seems like \OC::$server->getAppConf...tValues(false, 'types') can also be of type false. However, the property $appTypes is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
193
		}
194
195 1055
		if (isset(self::$appTypes[$app])) {
196 1055
			return explode(',', self::$appTypes[$app]);
197
		} else {
198
			return array();
199
		}
200
	}
201
202
	/**
203
	 * read app types from info.xml and cache them in the database
204
	 */
205 2
	public static function setAppTypes($app) {
206 2
		$appData = self::getAppInfo($app);
207
208 2
		if (isset($appData['types'])) {
209 2
			$appTypes = implode(',', $appData['types']);
210 2
		} else {
211
			$appTypes = '';
212
		}
213
214 2
		\OC::$server->getAppConfig()->setValue($app, 'types', $appTypes);
215 2
	}
216
217
	/**
218
	 * check if app is shipped
219
	 *
220
	 * @param string $appId the id of the app to check
221
	 * @return bool
222
	 *
223
	 * Check if an app that is installed is a shipped app or installed from the appstore.
224
	 */
225 4
	public static function isShipped($appId) {
226 4
		if (is_null(self::$shippedApps)) {
227 1
			$shippedJson = \OC::$SERVERROOT . '/core/shipped.json';
228 1
			if (file_exists($shippedJson)) {
229 1
				self::$shippedApps = json_decode(file_get_contents($shippedJson), true);
230 1
				self::$shippedApps = self::$shippedApps['shippedApps'];
231 1
			} else {
232
				self::$shippedApps = ['files', 'encryption', 'files_external',
233
					'files_sharing', 'files_trashbin', 'files_versions', 'provisioning_api',
234
					'user_ldap', 'user_webdavauth'];
235
			}
236 1
		}
237 4
		return in_array($appId, self::$shippedApps);
238
	}
239
240
	/**
241
	 * get all enabled apps
242
	 */
243
	protected static $enabledAppsCache = array();
244
245
	/**
246
	 * Returns apps enabled for the current user.
247
	 *
248
	 * @param bool $forceRefresh whether to refresh the cache
249
	 * @param bool $all whether to return apps for all users, not only the
250
	 * currently logged in one
251
	 * @return string[]
252
	 */
253 1069
	public static function getEnabledApps($forceRefresh = false, $all = false) {
0 ignored issues
show
Unused Code introduced by
The parameter $forceRefresh is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
254 1069
		if (!OC_Config::getValue('installed', false)) {
255
			return array();
256
		}
257
		// in incognito mode or when logged out, $user will be false,
258
		// which is also the case during an upgrade
259 1069
		$appManager = \OC::$server->getAppManager();
260 1069
		if ($all) {
261 5
			$user = null;
262 5
		} else {
263 1065
			$user = \OC::$server->getUserSession()->getUser();
264
		}
265
266 1069
		if (is_null($user)) {
267 166
			$apps = $appManager->getInstalledApps();
268 166
		} else {
269 912
			$apps = $appManager->getEnabledAppsForUser($user);
270
		}
271 1069
		$apps = array_filter($apps, function ($app) {
272 1069
			return $app !== 'files';//we add this manually
273 1069
		});
274 1069
		sort($apps);
275 1069
		array_unshift($apps, 'files');
276 1069
		return $apps;
277
	}
278
279
	/**
280
	 * checks whether or not an app is enabled
281
	 *
282
	 * @param string $app app
283
	 * @return bool
284
	 *
285
	 * This function checks whether or not an app is enabled.
286
	 */
287 142
	public static function isEnabled($app) {
288 142
		if ('files' == $app) {
289 7
			return true;
290
		}
291 135
		return \OC::$server->getAppManager()->isEnabledForUser($app);
292
	}
293
294
	/**
295
	 * enables an app
296
	 *
297
	 * @param mixed $app app
298
	 * @param array $groups (optional) when set, only these groups will have access to the app
299
	 * @throws \Exception
300
	 * @return void
301
	 *
302
	 * This function set an app as enabled in appconfig.
303
	 */
304 1
	public static function enable($app, $groups = null) {
305 1
		self::$enabledAppsCache = array(); // flush
306 1
		if (!OC_Installer::isInstalled($app)) {
307
			$app = self::installApp($app);
308
		}
309
310 1
		$appManager = \OC::$server->getAppManager();
311 1
		if (!is_null($groups)) {
312
			$groupManager = \OC::$server->getGroupManager();
313
			$groupsList = [];
314
			foreach ($groups as $group) {
315
				$groupItem = $groupManager->get($group);
316
				if ($groupItem instanceof \OCP\IGroup) {
317
					$groupsList[] = $groupManager->get($group);
318
				}
319
			}
320
			$appManager->enableAppForGroups($app, $groupsList);
321
		} else {
322 1
			$appManager->enableApp($app);
323
		}
324 1
	}
325
326
	/**
327
	 * @param string $app
328
	 * @return int
329
	 */
330
	public static function downloadApp($app) {
331
		$ocsClient = new OCSClient(
332
			\OC::$server->getHTTPClientService(),
333
			\OC::$server->getConfig(),
334
			\OC::$server->getLogger()
335
		);
336
		$appData = $ocsClient->getApplication($app, \OC_Util::getVersion());
337
		$download= $ocsClient->getApplicationDownload($app, \OC_Util::getVersion());
338
		if(isset($download['downloadlink']) and $download['downloadlink']!='') {
339
			// Replace spaces in download link without encoding entire URL
340
			$download['downloadlink'] = str_replace(' ', '%20', $download['downloadlink']);
341
			$info = array('source' => 'http', 'href' => $download['downloadlink'], 'appdata' => $appData);
342
			$app = OC_Installer::installApp($info);
343
		}
344
		return $app;
345
	}
346
347
	/**
348
	 * @param string $app
349
	 * @return bool
350
	 */
351
	public static function removeApp($app) {
352
		if (self::isShipped($app)) {
353
			return false;
354
		}
355
356
		return OC_Installer::removeApp($app);
357
	}
358
359
	/**
360
	 * This function set an app as disabled in appconfig.
361
	 *
362
	 * @param string $app app
363
	 * @throws Exception
364
	 */
365
	public static function disable($app) {
366
		// Convert OCS ID to regular application identifier
367
		if(self::getInternalAppIdByOcs($app) !== false) {
368
			$app = self::getInternalAppIdByOcs($app);
369
		}
370
371
		if($app === 'files') {
372
			throw new \Exception("files can't be disabled.");
373
		}
374
		self::$enabledAppsCache = array(); // flush
375
		// check if app is a shipped app or not. if not delete
376
		\OC_Hook::emit('OC_App', 'pre_disable', array('app' => $app));
377
		$appManager = \OC::$server->getAppManager();
378
		$appManager->disableApp($app);
379
	}
380
381
	/**
382
	 * marks a navigation entry as active
383
	 *
384
	 * @param string $id id of the entry
385
	 * @return bool
386
	 *
387
	 * This function sets a navigation entry as active and removes the 'active'
388
	 * property from all other entries. The templates can use this for
389
	 * highlighting the current position of the user.
390
	 *
391
	 * @deprecated Use \OC::$server->getNavigationManager()->setActiveEntry() instead
392
	 */
393
	public static function setActiveNavigationEntry($id) {
394
		OC::$server->getNavigationManager()->setActiveEntry($id);
395
		return true;
396
	}
397
398
	/**
399
	 * gets the active Menu entry
400
	 *
401
	 * @return string id or empty string
402
	 *
403
	 * This function returns the id of the active navigation entry (set by
404
	 * setActiveNavigationEntry
405
	 *
406
	 * @deprecated Use \OC::$server->getNavigationManager()->getActiveEntry() instead
407
	 */
408
	public static function getActiveNavigationEntry() {
409
		return OC::$server->getNavigationManager()->getActiveEntry();
410
	}
411
412
	/**
413
	 * Returns the Settings Navigation
414
	 *
415
	 * @return string[]
416
	 *
417
	 * This function returns an array containing all settings pages added. The
418
	 * entries are sorted by the key 'order' ascending.
419
	 */
420
	public static function getSettingsNavigation() {
421
		$l = \OC::$server->getL10N('lib');
422
423
		$settings = array();
424
		// by default, settings only contain the help menu
425
		if (OC_Util::getEditionString() === '' &&
426
			OC_Config::getValue('knowledgebaseenabled', true) == true
427
		) {
428
			$settings = array(
429
				array(
430
					"id" => "help",
431
					"order" => 1000,
432
					"href" => OC_Helper::linkToRoute("settings_help"),
433
					"name" => $l->t("Help"),
434
					"icon" => OC_Helper::imagePath("settings", "help.svg")
435
				)
436
			);
437
		}
438
439
		// if the user is logged-in
440
		if (OC_User::isLoggedIn()) {
441
			// personal menu
442
			$settings[] = array(
443
				"id" => "personal",
444
				"order" => 1,
445
				"href" => OC_Helper::linkToRoute("settings_personal"),
446
				"name" => $l->t("Personal"),
447
				"icon" => OC_Helper::imagePath("settings", "personal.svg")
448
			);
449
450
			//SubAdmins are also allowed to access user management
451 View Code Duplication
			if (OC_SubAdmin::isSubAdmin(OC_User::getUser())) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
452
				// admin users menu
453
				$settings[] = array(
454
					"id" => "core_users",
455
					"order" => 2,
456
					"href" => OC_Helper::linkToRoute("settings_users"),
457
					"name" => $l->t("Users"),
458
					"icon" => OC_Helper::imagePath("settings", "users.svg")
459
				);
460
			}
461
462
			// if the user is an admin
463 View Code Duplication
			if (OC_User::isAdminUser(OC_User::getUser())) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
464
				// admin settings
465
				$settings[] = array(
466
					"id" => "admin",
467
					"order" => 1000,
468
					"href" => OC_Helper::linkToRoute("settings_admin"),
469
					"name" => $l->t("Admin"),
470
					"icon" => OC_Helper::imagePath("settings", "admin.svg")
471
				);
472
			}
473
		}
474
475
		$navigation = self::proceedNavigation($settings);
476
		return $navigation;
477
	}
478
479
	// This is private as well. It simply works, so don't ask for more details
480
	private static function proceedNavigation($list) {
481
		$activeApp = OC::$server->getNavigationManager()->getActiveEntry();
482
		foreach ($list as &$navEntry) {
483
			if ($navEntry['id'] == $activeApp) {
484
				$navEntry['active'] = true;
485
			} else {
486
				$navEntry['active'] = false;
487
			}
488
		}
489
		unset($navEntry);
490
491
		usort($list, create_function('$a, $b', 'if( $a["order"] == $b["order"] ) {return 0;}elseif( $a["order"] < $b["order"] ) {return -1;}else{return 1;}'));
492
493
		return $list;
494
	}
495
496
	/**
497
	 * Get the path where to install apps
498
	 *
499
	 * @return string|false
500
	 */
501 3
	public static function getInstallPath() {
502 3
		if (OC_Config::getValue('appstoreenabled', true) == false) {
503 1
			return false;
504
		}
505
506 2
		foreach (OC::$APPSROOTS as $dir) {
507 2
			if (isset($dir['writable']) && $dir['writable'] === true) {
508 2
				return $dir['path'];
509
			}
510
		}
511
512
		\OCP\Util::writeLog('core', 'No application directories are marked as writable.', \OCP\Util::ERROR);
513
		return null;
514
	}
515
516
517
	/**
518
	 * search for an app in all app-directories
519
	 *
520
	 * @param string $appId
521
	 * @return mixed (bool|string)
522
	 */
523 1130
	protected static function findAppInDirectories($appId) {
524 1130
		static $app_dir = array();
525
526 1130
		if (isset($app_dir[$appId])) {
527 1102
			return $app_dir[$appId];
528
		}
529
530 33
		$possibleApps = array();
531 33
		foreach (OC::$APPSROOTS as $dir) {
532 33
			if (file_exists($dir['path'] . '/' . $appId)) {
533 9
				$possibleApps[] = $dir;
534 9
			}
535 33
		}
536
537 33
		if (empty($possibleApps)) {
538 24
			return false;
539 9
		} elseif (count($possibleApps) === 1) {
540 9
			$dir = array_shift($possibleApps);
541 9
			$app_dir[$appId] = $dir;
542 9
			return $dir;
543
		} else {
544
			$versionToLoad = array();
545
			foreach ($possibleApps as $possibleApp) {
546
				$version = self::getAppVersionByPath($possibleApp['path']);
547
				if (empty($versionToLoad) || version_compare($version, $versionToLoad['version'], '>')) {
548
					$versionToLoad = array(
549
						'dir' => $possibleApp,
550
						'version' => $version,
551
					);
552
				}
553
			}
554
			$app_dir[$appId] = $versionToLoad['dir'];
555
			return $versionToLoad['dir'];
556
			//TODO - write test
557
		}
558
	}
559
560
	/**
561
	 * Get the directory for the given app.
562
	 * If the app is defined in multiple directories, the first one is taken. (false if not found)
563
	 *
564
	 * @param string $appId
565
	 * @return string|false
566
	 */
567 1130
	public static function getAppPath($appId) {
568 1130
		if ($appId === null || trim($appId) === '') {
569 9
			return false;
570
		}
571
572 1130
		if (($dir = self::findAppInDirectories($appId)) != false) {
573 1109
			return $dir['path'] . '/' . $appId;
574
		}
575 24
		return false;
576
	}
577
578
579
	/**
580
	 * check if an app's directory is writable
581
	 *
582
	 * @param string $appId
583
	 * @return bool
584
	 */
585
	public static function isAppDirWritable($appId) {
586
		$path = self::getAppPath($appId);
587
		return ($path !== false) ? is_writable($path) : false;
588
	}
589
590
	/**
591
	 * Get the path for the given app on the access
592
	 * If the app is defined in multiple directories, the first one is taken. (false if not found)
593
	 *
594
	 * @param string $appId
595
	 * @return string|false
596
	 */
597 4
	public static function getAppWebPath($appId) {
598 4
		if (($dir = self::findAppInDirectories($appId)) != false) {
599 4
			return OC::$WEBROOT . $dir['url'] . '/' . $appId;
600
		}
601
		return false;
602
	}
603
604
	/**
605
	 * get the last version of the app, either from appinfo/version or from appinfo/info.xml
606
	 *
607
	 * @param string $appId
608
	 * @return string
609
	 */
610 15
	public static function getAppVersion($appId) {
611 15
		if (!isset(self::$appVersion[$appId])) {
612 3
			$file = self::getAppPath($appId);
613 3
			self::$appVersion[$appId] = ($file !== false) ? self::getAppVersionByPath($file) : '0';
614 3
		}
615 15
		return self::$appVersion[$appId];
616
	}
617
618
	/**
619
	 * get app's version based on it's path
620
	 *
621
	 * @param string $path
622
	 * @return string
623
	 */
624 3
	public static function getAppVersionByPath($path) {
625 3
		$versionFile = $path . '/appinfo/version';
626 3
		$infoFile = $path . '/appinfo/info.xml';
627 3
		if (is_file($versionFile)) {
628 2
			return trim(file_get_contents($versionFile));
629
		} else {
630 1
			$appData = self::getAppInfo($infoFile, true);
631 1
			return isset($appData['version']) ? $appData['version'] : '';
632
		}
633
	}
634
635
636
	/**
637
	 * Read all app metadata from the info.xml file
638
	 *
639
	 * @param string $appId id of the app or the path of the info.xml file
640
	 * @param boolean $path (optional)
641
	 * @return array|null
642
	 * @note all data is read from info.xml, not just pre-defined fields
643
	 */
644 8
	public static function getAppInfo($appId, $path = false) {
645 8
		if ($path) {
646 3
			$file = $appId;
647 3
		} else {
648 8
			if (isset(self::$appInfo[$appId])) {
649 6
				return self::$appInfo[$appId];
650
			}
651 4
			$file = self::getAppPath($appId) . '/appinfo/info.xml';
652
		}
653
654 5
		$parser = new \OC\App\InfoParser(\OC::$server->getHTTPHelper(), \OC::$server->getURLGenerator());
655 5
		$data = $parser->parse($file);
0 ignored issues
show
Bug Compatibility introduced by
The expression $parser->parse($file); of type null|string|array adds the type string to the return on line 669 which is incompatible with the return type documented by OC_App::getAppInfo of type array|null.
Loading history...
656
657 5
		if (is_array($data)) {
658 4
			$data = OC_App::parseAppInfo($data);
659 4
		}
660 5
		if(isset($data['ocsid'])) {
661 1
			$storedId = \OC::$server->getConfig()->getAppValue($appId, 'ocsid');
662 1
			if($storedId !== '' && $storedId !== $data['ocsid']) {
663
				$data['ocsid'] = $storedId;
664
			}
665 1
		}
666
667 5
		self::$appInfo[$appId] = $data;
668
669 5
		return $data;
670
	}
671
672
	/**
673
	 * Returns the navigation
674
	 *
675
	 * @return array
676
	 *
677
	 * This function returns an array containing all entries added. The
678
	 * entries are sorted by the key 'order' ascending. Additional to the keys
679
	 * given for each app the following keys exist:
680
	 *   - active: boolean, signals if the user is on this navigation entry
681
	 */
682
	public static function getNavigation() {
683
		$entries = OC::$server->getNavigationManager()->getAll();
684
		$navigation = self::proceedNavigation($entries);
685
		return $navigation;
686
	}
687
688
	/**
689
	 * get the id of loaded app
690
	 *
691
	 * @return string
692
	 */
693
	public static function getCurrentApp() {
694
		$request = \OC::$server->getRequest();
695
		$script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1);
696
		$topFolder = substr($script, 0, strpos($script, '/'));
697
		if (empty($topFolder)) {
698
			$path_info = $request->getPathInfo();
699
			if ($path_info) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $path_info of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
700
				$topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1);
701
			}
702
		}
703
		if ($topFolder == 'apps') {
704
			$length = strlen($topFolder);
705
			return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1);
706
		} else {
707
			return $topFolder;
708
		}
709
	}
710
711
	/**
712
	 * @param string $type
713
	 * @return array
714
	 */
715
	public static function getForms($type) {
716
		$forms = array();
717
		switch ($type) {
718
			case 'admin':
719
				$source = self::$adminForms;
720
				break;
721
			case 'personal':
722
				$source = self::$personalForms;
723
				break;
724
			default:
725
				return array();
726
		}
727
		foreach ($source as $form) {
728
			$forms[] = include $form;
729
		}
730
		return $forms;
731
	}
732
733
	/**
734
	 * register an admin form to be shown
735
	 *
736
	 * @param string $app
737
	 * @param string $page
738
	 */
739
	public static function registerAdmin($app, $page) {
740
		self::$adminForms[] = $app . '/' . $page . '.php';
741
	}
742
743
	/**
744
	 * register a personal form to be shown
745
	 * @param string $app
746
	 * @param string $page
747
	 */
748
	public static function registerPersonal($app, $page) {
749
		self::$personalForms[] = $app . '/' . $page . '.php';
750
	}
751
752
	/**
753
	 * @param array $entry
754
	 */
755
	public static function registerLogIn(array $entry) {
756
		self::$altLogin[] = $entry;
757
	}
758
759
	/**
760
	 * @return array
761
	 */
762
	public static function getAlternativeLogIns() {
763
		return self::$altLogin;
764
	}
765
766
	/**
767
	 * get a list of all apps in the apps folder
768
	 *
769
	 * @return array an array of app names (string IDs)
770
	 * @todo: change the name of this method to getInstalledApps, which is more accurate
771
	 */
772 4
	public static function getAllApps() {
773
774 4
		$apps = array();
775
776 4
		foreach (OC::$APPSROOTS as $apps_dir) {
777 4
			if (!is_readable($apps_dir['path'])) {
778
				\OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], \OCP\Util::WARN);
779
				continue;
780
			}
781 4
			$dh = opendir($apps_dir['path']);
782
783 4
			if (is_resource($dh)) {
784 4
				while (($file = readdir($dh)) !== false) {
785
786 4
					if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
787
788 4
						$apps[] = $file;
789
790 4
					}
791
792 4
				}
793 4
			}
794
795 4
		}
796
797 4
		return $apps;
798
	}
799
800
	/**
801
	 * List all apps, this is used in apps.php
802
	 *
803
	 * @param bool $onlyLocal
804
	 * @param bool $includeUpdateInfo Should we check whether there is an update
805
	 *                                in the app store?
806
	 * @return array
807
	 */
808 4
	public static function listAllApps($onlyLocal = false, $includeUpdateInfo = true) {
809 4
		$installedApps = OC_App::getAllApps();
810
811
		//TODO which apps do we want to blacklist and how do we integrate
812
		// blacklisting with the multi apps folder feature?
813
814 4
		$blacklist = array('files'); //we don't want to show configuration for these
815 4
		$appList = array();
816 4
		$l = \OC::$server->getL10N('core');
0 ignored issues
show
Unused Code introduced by
$l is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
817
818 4
		foreach ($installedApps as $app) {
819 4
			if (array_search($app, $blacklist) === false) {
820
821 4
				$info = OC_App::getAppInfo($app);
822
823 4
				if (!isset($info['name'])) {
824
					\OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', \OCP\Util::ERROR);
825
					continue;
826
				}
827
828 4
				$enabled = \OC::$server->getAppConfig()->getValue($app, 'enabled', 'no');
829 4
				$info['groups'] = null;
830 4
				if ($enabled === 'yes') {
831 4
					$active = true;
832 4
				} else if ($enabled === 'no') {
833 4
					$active = false;
834 4
				} else {
835
					$active = true;
836
					$info['groups'] = $enabled;
837
				}
838
839 4
				$info['active'] = $active;
840
841 4
				if (self::isShipped($app)) {
842 4
					$info['internal'] = true;
843 4
					$info['level'] = self::officialApp;
844 4
					$info['removable'] = false;
845 4
				} else {
846
					$info['internal'] = false;
847
					$info['removable'] = true;
848
				}
849
850 4
				$info['update'] = ($includeUpdateInfo) ? OC_Installer::isUpdateAvailable($app) : null;
851
852 4
				$appIcon = self::getAppPath($app) . '/img/' . $app . '.svg';
853 4
				if (file_exists($appIcon)) {
854
					$info['preview'] = OC_Helper::imagePath($app, $app . '.svg');
855
					$info['previewAsIcon'] = true;
856
				} else {
857 4
					$appIcon = self::getAppPath($app) . '/img/app.svg';
858 4
					if (file_exists($appIcon)) {
859 4
						$info['preview'] = OC_Helper::imagePath($app, 'app.svg');
860 4
						$info['previewAsIcon'] = true;
861 4
					}
862
				}
863 4
				$info['version'] = OC_App::getAppVersion($app);
864 4
				$appList[] = $info;
865 4
			}
866 4
		}
867 4
		if ($onlyLocal) {
868
			$remoteApps = [];
869
		} else {
870 4
			$remoteApps = OC_App::getAppstoreApps();
871
		}
872 4
		if ($remoteApps) {
873
			// Remove duplicates
874
			foreach ($appList as $app) {
875
				foreach ($remoteApps AS $key => $remote) {
876
					if ($app['name'] === $remote['name'] ||
877
						(isset($app['ocsid']) &&
878
							$app['ocsid'] === $remote['id'])
879
					) {
880
						unset($remoteApps[$key]);
881
					}
882
				}
883
			}
884
			$combinedApps = array_merge($appList, $remoteApps);
885
		} else {
886 4
			$combinedApps = $appList;
887
		}
888
889 4
		return $combinedApps;
890
	}
891
892
	/**
893
	 * Returns the internal app ID or false
894
	 * @param string $ocsID
895
	 * @return string|false
896
	 */
897
	protected static function getInternalAppIdByOcs($ocsID) {
898
		if(is_numeric($ocsID)) {
899
			$idArray = \OC::$server->getAppConfig()->getValues(false, 'ocsid');
900
			if(array_search($ocsID, $idArray)) {
901
				return array_search($ocsID, $idArray);
902
			}
903
		}
904
		return false;
905
	}
906
907
	/**
908
	 * Get a list of all apps on the appstore
909
	 * @param string $filter
910
	 * @param string $category
911
	 * @return array|bool  multi-dimensional array of apps.
912
	 *                     Keys: id, name, type, typename, personid, license, detailpage, preview, changed, description
913
	 */
914 4
	public static function getAppstoreApps($filter = 'approved', $category = null) {
915 4
		$categories = [$category];
916
917 4
		$ocsClient = new OCSClient(
918 4
			\OC::$server->getHTTPClientService(),
919 4
			\OC::$server->getConfig(),
920 4
			\OC::$server->getLogger()
921 4
		);
922
923
924 4
		if (is_null($category)) {
925 4
			$categoryNames = $ocsClient->getCategories(\OC_Util::getVersion());
926 4
			if (is_array($categoryNames)) {
927
				// Check that categories of apps were retrieved correctly
928
				if (!$categories = array_keys($categoryNames)) {
929
					return false;
930
				}
931
			} else {
932 4
				return false;
933
			}
934
		}
935
936
		$page = 0;
937
		$remoteApps = $ocsClient->getApplications($categories, $page, $filter, \OC_Util::getVersion());
938
		$apps = [];
939
		$i = 0;
940
		$l = \OC::$server->getL10N('core');
941
		foreach ($remoteApps as $app) {
942
			$potentialCleanId = self::getInternalAppIdByOcs($app['id']);
943
			// enhance app info (for example the description)
944
			$apps[$i] = OC_App::parseAppInfo($app);
945
			$apps[$i]['author'] = $app['personid'];
946
			$apps[$i]['ocs_id'] = $app['id'];
947
			$apps[$i]['internal'] = 0;
948
			$apps[$i]['active'] = ($potentialCleanId !== false) ? self::isEnabled($potentialCleanId) : false;
949
			$apps[$i]['update'] = false;
950
			$apps[$i]['groups'] = false;
951
			$apps[$i]['score'] = $app['score'];
952
			$apps[$i]['removable'] = false;
953
			if ($app['label'] == 'recommended') {
954
				$apps[$i]['internallabel'] = (string)$l->t('Recommended');
955
				$apps[$i]['internalclass'] = 'recommendedapp';
956
			}
957
958
			$i++;
959
		}
960
961
962
963
		if (empty($apps)) {
964
			return false;
965
		} else {
966
			return $apps;
967
		}
968
	}
969
970 9
	public static function shouldUpgrade($app) {
971 9
		$versions = self::getAppVersions();
972 9
		$currentVersion = OC_App::getAppVersion($app);
973 9
		if ($currentVersion && isset($versions[$app])) {
974 9
			$installedVersion = $versions[$app];
975 9
			if (version_compare($currentVersion, $installedVersion, '>')) {
976
				return true;
977
			}
978 9
		}
979 9
		return false;
980
	}
981
982
	/**
983
	 * Adjust the number of version parts of $version1 to match
984
	 * the number of version parts of $version2.
985
	 *
986
	 * @param string $version1 version to adjust
987
	 * @param string $version2 version to take the number of parts from
988
	 * @return string shortened $version1
989
	 */
990 31
	private static function adjustVersionParts($version1, $version2) {
991 31
		$version1 = explode('.', $version1);
992 31
		$version2 = explode('.', $version2);
993
		// reduce $version1 to match the number of parts in $version2
994 31
		while (count($version1) > count($version2)) {
995 24
			array_pop($version1);
996 24
		}
997
		// if $version1 does not have enough parts, add some
998 31
		while (count($version1) < count($version2)) {
999 1
			$version1[] = '0';
1000 1
		}
1001 31
		return implode('.', $version1);
1002
	}
1003
1004
	/**
1005
	 * Check whether the current ownCloud version matches the given
1006
	 * application's version requirements.
1007
	 *
1008
	 * The comparison is made based on the number of parts that the
1009
	 * app info version has. For example for ownCloud 6.0.3 if the
1010
	 * app info version is expecting version 6.0, the comparison is
1011
	 * made on the first two parts of the ownCloud version.
1012
	 * This means that it's possible to specify "requiremin" => 6
1013
	 * and "requiremax" => 6 and it will still match ownCloud 6.0.3.
1014
	 *
1015
	 * @param string $ocVersion ownCloud version to check against
1016
	 * @param array $appInfo app info (from xml)
1017
	 *
1018
	 * @return boolean true if compatible, otherwise false
1019
	 */
1020 31
	public static function isAppCompatible($ocVersion, $appInfo) {
1021 31
		$requireMin = '';
1022 31
		$requireMax = '';
1023 31
		if (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) {
1024 3
			$requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version'];
1025 31
		} else if (isset($appInfo['requiremin'])) {
1026 9
			$requireMin = $appInfo['requiremin'];
1027 28
		} else if (isset($appInfo['require'])) {
1028 15
			$requireMin = $appInfo['require'];
1029 15
		}
1030
1031 31
		if (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) {
1032 3
			$requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version'];
1033 31
		} else if (isset($appInfo['requiremax'])) {
1034 11
			$requireMax = $appInfo['requiremax'];
1035 11
		}
1036
1037 31
		if (is_array($ocVersion)) {
1038 3
			$ocVersion = implode('.', $ocVersion);
1039 3
		}
1040
1041 31 View Code Duplication
		if (!empty($requireMin)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1042 31
			&& version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<')
1043 31
		) {
1044
1045 10
			return false;
1046
		}
1047
1048 23 View Code Duplication
		if (!empty($requireMax)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1049 23
			&& version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>')
1050 23
		) {
1051
1052 4
			return false;
1053
		}
1054
1055 20
		return true;
1056
	}
1057
1058
	/**
1059
	 * get the installed version of all apps
1060
	 */
1061 9
	public static function getAppVersions() {
1062 9
		static $versions;
1063 9
		if (isset($versions)) { // simple cache, needs to be fixed
1064 9
			return $versions; // when function is used besides in checkUpgrade
1065
		}
1066
		$versions = array();
1067
		try {
1068
			$query = OC_DB::prepare('SELECT `appid`, `configvalue` FROM `*PREFIX*appconfig`'
1069
				. ' WHERE `configkey` = \'installed_version\'');
1070
			$result = $query->execute();
1071
			while ($row = $result->fetchRow()) {
1072
				$versions[$row['appid']] = $row['configvalue'];
1073
			}
1074
			return $versions;
1075
		} catch (\Exception $e) {
1076
			return array();
1077
		}
1078
	}
1079
1080
1081
	/**
1082
	 * @param mixed $app
1083
	 * @return bool
1084
	 * @throws Exception if app is not compatible with this version of ownCloud
1085
	 * @throws Exception if no app-name was specified
1086
	 */
1087
	public static function installApp($app) {
1088
		$l = \OC::$server->getL10N('core');
1089
		$config = \OC::$server->getConfig();
1090
		$ocsClient = new OCSClient(
1091
			\OC::$server->getHTTPClientService(),
1092
			$config,
1093
			\OC::$server->getLogger()
1094
		);
1095
		$appData = $ocsClient->getApplication($app, \OC_Util::getVersion());
1096
1097
		// check if app is a shipped app or not. OCS apps have an integer as id, shipped apps use a string
1098
		if (!is_numeric($app)) {
1099
			$shippedVersion = self::getAppVersion($app);
1100
			if ($appData && version_compare($shippedVersion, $appData['version'], '<')) {
1101
				$app = self::downloadApp($app);
1102
			} else {
1103
				$app = OC_Installer::installShippedApp($app);
1104
			}
1105
		} else {
1106
			// Maybe the app is already installed - compare the version in this
1107
			// case and use the local already installed one.
1108
			// FIXME: This is a horrible hack. I feel sad. The god of code cleanness may forgive me.
1109
			$internalAppId = self::getInternalAppIdByOcs($app);
1110
			if($internalAppId !== false) {
1111
				if($appData && version_compare(\OC_App::getAppVersion($internalAppId), $appData['version'], '<')) {
1112
					$app = self::downloadApp($app);
1113
				} else {
1114
					self::enable($internalAppId);
1115
					$app = $internalAppId;
1116
				}
1117
			} else {
1118
				$app = self::downloadApp($app);
1119
			}
1120
		}
1121
1122
		if ($app !== false) {
1123
			// check if the app is compatible with this version of ownCloud
1124
			$info = self::getAppInfo($app);
1125
			$version = OC_Util::getVersion();
1126
			if (!self::isAppCompatible($version, $info)) {
0 ignored issues
show
Bug introduced by
It seems like $info defined by self::getAppInfo($app) on line 1124 can also be of type null; however, OC_App::isAppCompatible() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Documentation introduced by
$version is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1127
				throw new \Exception(
1128
					$l->t('App "%s" cannot be installed because it is not compatible with this version of ownCloud.',
1129
						array($info['name'])
1130
					)
1131
				);
1132
			}
1133
1134
			// check for required dependencies
1135
			$dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l);
1136
			$missing = $dependencyAnalyzer->analyze($info);
0 ignored issues
show
Bug introduced by
It seems like $info defined by self::getAppInfo($app) on line 1124 can also be of type null; however, OC\App\DependencyAnalyzer::analyze() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1137
			if (!empty($missing)) {
1138
				$missingMsg = join(PHP_EOL, $missing);
1139
				throw new \Exception(
1140
					$l->t('App "%s" cannot be installed because the following dependencies are not fulfilled: %s',
1141
						array($info['name'], $missingMsg)
1142
					)
1143
				);
1144
			}
1145
1146
			$config->setAppValue($app, 'enabled', 'yes');
1147
			if (isset($appData['id'])) {
1148
				$config->setAppValue($app, 'ocsid', $appData['id']);
1149
			}
1150
			\OC_Hook::emit('OC_App', 'post_enable', array('app' => $app));
1151
		} else {
1152
			throw new \Exception($l->t("No app name specified"));
1153
		}
1154
1155
		return $app;
1156
	}
1157
1158
	/**
1159
	 * update the database for the app and call the update script
1160
	 *
1161
	 * @param string $appId
1162
	 * @return bool
1163
	 */
1164 1
	public static function updateApp($appId) {
1165 1
		if (file_exists(self::getAppPath($appId) . '/appinfo/database.xml')) {
1166
			OC_DB::updateDbFromStructure(self::getAppPath($appId) . '/appinfo/database.xml');
1167
		}
1168 1
		unset(self::$appVersion[$appId]);
1169
		// run upgrade code
1170 1
		if (file_exists(self::getAppPath($appId) . '/appinfo/update.php')) {
1171
			self::loadApp($appId, false);
1172
			include self::getAppPath($appId) . '/appinfo/update.php';
1173
		}
1174
1175
		//set remote/public handlers
1176 1
		$appData = self::getAppInfo($appId);
1177 1
		if (array_key_exists('ocsid', $appData)) {
1178
			\OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']);
1179 1 View Code Duplication
		} elseif(\OC::$server->getConfig()->getAppValue($appId, 'ocsid', null) !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1180
			\OC::$server->getConfig()->deleteAppValue($appId, 'ocsid');
1181
		}
1182 1
		foreach ($appData['remote'] as $name => $path) {
1183
			\OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
1184 1
		}
1185 1
		foreach ($appData['public'] as $name => $path) {
1186
			\OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path);
1187 1
		}
1188
1189 1
		self::setAppTypes($appId);
1190
1191 1
		$version = \OC_App::getAppVersion($appId);
1192 1
		\OC::$server->getAppConfig()->setValue($appId, 'installed_version', $version);
1193
1194 1
		return true;
1195
	}
1196
1197
	/**
1198
	 * @param string $appId
1199
	 * @return \OC\Files\View|false
1200
	 */
1201
	public static function getStorage($appId) {
1202
		if (OC_App::isEnabled($appId)) { //sanity check
1203
			if (OC_User::isLoggedIn()) {
1204
				$view = new \OC\Files\View('/' . OC_User::getUser());
1205
				if (!$view->file_exists($appId)) {
1206
					$view->mkdir($appId);
1207
				}
1208
				return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId);
1209
			} else {
1210
				\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', \OCP\Util::ERROR);
1211
				return false;
1212
			}
1213
		} else {
1214
			\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', \OCP\Util::ERROR);
1215
			return false;
1216
		}
1217
	}
1218
1219
	/**
1220
	 * parses the app data array and enhanced the 'description' value
1221
	 *
1222
	 * @param array $data the app data
1223
	 * @return array improved app data
1224
	 */
1225 9
	public static function parseAppInfo(array $data) {
1226
1227
		// just modify the description if it is available
1228
		// otherwise this will create a $data element with an empty 'description'
1229 9
		if (isset($data['description'])) {
1230 8
			if (is_string($data['description'])) {
1231
				// sometimes the description contains line breaks and they are then also
1232
				// shown in this way in the app management which isn't wanted as HTML
1233
				// manages line breaks itself
1234
1235
				// first of all we split on empty lines
1236 7
				$paragraphs = preg_split("!\n[[:space:]]*\n!mu", $data['description']);
1237
1238 7
				$result = [];
1239 7
				foreach ($paragraphs as $value) {
1240
					// replace multiple whitespace (tabs, space, newlines) inside a paragraph
1241
					// with a single space - also trims whitespace
1242 7
					$result[] = trim(preg_replace('![[:space:]]+!mu', ' ', $value));
1243 7
				}
1244
1245
				// join the single paragraphs with a empty line in between
1246 7
				$data['description'] = implode("\n\n", $result);
1247
1248 7
			} else {
1249 1
				$data['description'] = '';
1250
			}
1251 8
		}
1252
1253 9
		return $data;
1254
	}
1255
}
1256