Completed
Push — master ( 1264e9...7cdf64 )
by Björn
09:42
created

OC_App::executeRepairSteps()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 13
nc 4
nop 2
dl 0
loc 22
rs 8.9197
c 0
b 0
f 0
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 Roeland Jago Douma <[email protected]>
25
 * @author Sam Tuke <[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) 2016, 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\Installer;
50
use OC\OCSClient;
51
use OC\Repair;
52
53
/**
54
 * This class manages the apps. It allows them to register and integrate in the
55
 * ownCloud ecosystem. Furthermore, this class is responsible for installing,
56
 * upgrading and removing apps.
57
 */
58
class OC_App {
59
	static private $appVersion = [];
60
	static private $adminForms = array();
61
	static private $personalForms = array();
62
	static private $appInfo = array();
63
	static private $appTypes = array();
64
	static private $loadedApps = array();
65
	static private $altLogin = array();
66
	const officialApp = 200;
67
68
	/**
69
	 * clean the appId
70
	 *
71
	 * @param string|boolean $app AppId that needs to be cleaned
72
	 * @return string
73
	 */
74
	public static function cleanAppId($app) {
75
		return str_replace(array('\0', '/', '\\', '..'), '', $app);
76
	}
77
78
	/**
79
	 * Check if an app is loaded
80
	 *
81
	 * @param string $app
82
	 * @return bool
83
	 */
84
	public static function isAppLoaded($app) {
85
		return in_array($app, self::$loadedApps, true);
86
	}
87
88
	/**
89
	 * loads all apps
90
	 *
91
	 * @param string[] | string | null $types
92
	 * @return bool
93
	 *
94
	 * This function walks through the ownCloud directory and loads all apps
95
	 * it can find. A directory contains an app if the file /appinfo/info.xml
96
	 * exists.
97
	 *
98
	 * if $types is set, only apps of those types will be loaded
99
	 */
100
	public static function loadApps($types = null) {
101
		if (\OC::$server->getSystemConfig()->getValue('maintenance', false)) {
102
			return false;
103
		}
104
		// Load the enabled apps here
105
		$apps = self::getEnabledApps();
106
107
		// Add each apps' folder as allowed class path
108
		foreach($apps as $app) {
109
			$path = self::getAppPath($app);
110
			if($path !== false) {
111
				self::registerAutoloading($app, $path);
112
			}
113
		}
114
115
		// prevent app.php from printing output
116
		ob_start();
117
		foreach ($apps as $app) {
118
			if ((is_null($types) or self::isType($app, $types)) && !in_array($app, self::$loadedApps)) {
119
				self::loadApp($app);
120
			}
121
		}
122
		ob_end_clean();
123
124
		return true;
125
	}
126
127
	/**
128
	 * load a single app
129
	 *
130
	 * @param string $app
131
	 * @param bool $checkUpgrade whether an upgrade check should be done
132
	 * @throws \OC\NeedsUpdateException
133
	 */
134
	public static function loadApp($app, $checkUpgrade = true) {
135
		self::$loadedApps[] = $app;
136
		$appPath = self::getAppPath($app);
137
		if($appPath === false) {
138
			return;
139
		}
140
141
		// in case someone calls loadApp() directly
142
		self::registerAutoloading($app, $appPath);
143
144
		if (is_file($appPath . '/appinfo/app.php')) {
145
			\OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app);
146
			if ($checkUpgrade and self::shouldUpgrade($app)) {
147
				throw new \OC\NeedsUpdateException();
148
			}
149
			self::requireAppFile($app);
150
			if (self::isType($app, array('authentication'))) {
151
				// since authentication apps affect the "is app enabled for group" check,
152
				// the enabled apps cache needs to be cleared to make sure that the
153
				// next time getEnableApps() is called it will also include apps that were
154
				// enabled for groups
155
				self::$enabledAppsCache = array();
156
			}
157
			\OC::$server->getEventLogger()->end('load_app_' . $app);
158
		}
159
	}
160
161
	/**
162
	 * @internal
163
	 * @param string $app
164
	 * @param string $path
165
	 */
166
	public static function registerAutoloading($app, $path) {
167
		// Register on PSR-4 composer autoloader
168
		$appNamespace = \OC\AppFramework\App::buildAppNamespace($app);
169
		\OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true);
170
		if (defined('PHPUNIT_RUN')) {
171
			\OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true);
172
		}
173
174
		// Register on legacy autoloader
175
		\OC::$loader->addValidRoot($path);
176
	}
177
178
	/**
179
	 * Load app.php from the given app
180
	 *
181
	 * @param string $app app name
182
	 */
183
	private static function requireAppFile($app) {
184
		try {
185
			// encapsulated here to avoid variable scope conflicts
186
			require_once $app . '/appinfo/app.php';
187
		} catch (Error $ex) {
0 ignored issues
show
Bug introduced by
The class Error does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
188
			\OC::$server->getLogger()->logException($ex);
189
			$blacklist = \OC::$server->getAppManager()->getAlwaysEnabledApps();
190
			if (!in_array($app, $blacklist)) {
191
				self::disable($app);
192
			}
193
		}
194
	}
195
196
	/**
197
	 * check if an app is of a specific type
198
	 *
199
	 * @param string $app
200
	 * @param string|array $types
201
	 * @return bool
202
	 */
203
	public static function isType($app, $types) {
204
		if (is_string($types)) {
205
			$types = array($types);
206
		}
207
		$appTypes = self::getAppTypes($app);
208
		foreach ($types as $type) {
209
			if (array_search($type, $appTypes) !== false) {
210
				return true;
211
			}
212
		}
213
		return false;
214
	}
215
216
	/**
217
	 * get the types of an app
218
	 *
219
	 * @param string $app
220
	 * @return array
221
	 */
222
	private static function getAppTypes($app) {
223
		//load the cache
224
		if (count(self::$appTypes) == 0) {
225
			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...
226
		}
227
228
		if (isset(self::$appTypes[$app])) {
229
			return explode(',', self::$appTypes[$app]);
230
		} else {
231
			return array();
232
		}
233
	}
234
235
	/**
236
	 * read app types from info.xml and cache them in the database
237
	 */
238
	public static function setAppTypes($app) {
239
		$appData = self::getAppInfo($app);
240
		if(!is_array($appData)) {
241
			return;
242
		}
243
244
		if (isset($appData['types'])) {
245
			$appTypes = implode(',', $appData['types']);
246
		} else {
247
			$appTypes = '';
248
		}
249
250
		\OC::$server->getAppConfig()->setValue($app, 'types', $appTypes);
0 ignored issues
show
Deprecated Code introduced by
The method OCP\IAppConfig::setValue() has been deprecated with message: 8.0.0 use method setAppValue of \OCP\IConfig Sets a value. If the key did not exist before it will be created.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
251
	}
252
253
	/**
254
	 * check if app is shipped
255
	 *
256
	 * @param string $appId the id of the app to check
257
	 * @return bool
258
	 *
259
	 * Check if an app that is installed is a shipped app or installed from the appstore.
260
	 */
261
	public static function isShipped($appId) {
262
		return \OC::$server->getAppManager()->isShipped($appId);
263
	}
264
265
	/**
266
	 * get all enabled apps
267
	 */
268
	protected static $enabledAppsCache = array();
269
270
	/**
271
	 * Returns apps enabled for the current user.
272
	 *
273
	 * @param bool $forceRefresh whether to refresh the cache
274
	 * @param bool $all whether to return apps for all users, not only the
275
	 * currently logged in one
276
	 * @return string[]
277
	 */
278
	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...
279
		if (!\OC::$server->getSystemConfig()->getValue('installed', false)) {
280
			return array();
281
		}
282
		// in incognito mode or when logged out, $user will be false,
283
		// which is also the case during an upgrade
284
		$appManager = \OC::$server->getAppManager();
285
		if ($all) {
286
			$user = null;
287
		} else {
288
			$user = \OC::$server->getUserSession()->getUser();
289
		}
290
291
		if (is_null($user)) {
292
			$apps = $appManager->getInstalledApps();
293
		} else {
294
			$apps = $appManager->getEnabledAppsForUser($user);
295
		}
296
		$apps = array_filter($apps, function ($app) {
297
			return $app !== 'files';//we add this manually
298
		});
299
		sort($apps);
300
		array_unshift($apps, 'files');
301
		return $apps;
302
	}
303
304
	/**
305
	 * checks whether or not an app is enabled
306
	 *
307
	 * @param string $app app
308
	 * @return bool
309
	 *
310
	 * This function checks whether or not an app is enabled.
311
	 */
312
	public static function isEnabled($app) {
313
		return \OC::$server->getAppManager()->isEnabledForUser($app);
314
	}
315
316
	/**
317
	 * enables an app
318
	 *
319
	 * @param mixed $app app
320
	 * @param array $groups (optional) when set, only these groups will have access to the app
321
	 * @throws \Exception
322
	 * @return void
323
	 *
324
	 * This function set an app as enabled in appconfig.
325
	 */
326
	public static function enable($app, $groups = null) {
327
		self::$enabledAppsCache = array(); // flush
328
		if (!Installer::isInstalled($app)) {
329
			$app = self::installApp($app);
330
		}
331
332
		$appManager = \OC::$server->getAppManager();
333
		if (!is_null($groups)) {
334
			$groupManager = \OC::$server->getGroupManager();
335
			$groupsList = [];
336
			foreach ($groups as $group) {
337
				$groupItem = $groupManager->get($group);
338
				if ($groupItem instanceof \OCP\IGroup) {
339
					$groupsList[] = $groupManager->get($group);
340
				}
341
			}
342
			$appManager->enableAppForGroups($app, $groupsList);
343
		} else {
344
			$appManager->enableApp($app);
345
		}
346
	}
347
348
	/**
349
	 * @param string $app
350
	 * @return int
351
	 */
352
	private static function downloadApp($app) {
353
		$ocsClient = new OCSClient(
354
			\OC::$server->getHTTPClientService(),
355
			\OC::$server->getConfig(),
356
			\OC::$server->getLogger()
357
		);
358
		$appData = $ocsClient->getApplication($app, \OCP\Util::getVersion());
359
		$download = $ocsClient->getApplicationDownload($app, \OCP\Util::getVersion());
360
		if(isset($download['downloadlink']) and $download['downloadlink']!='') {
361
			// Replace spaces in download link without encoding entire URL
362
			$download['downloadlink'] = str_replace(' ', '%20', $download['downloadlink']);
363
			$info = array('source' => 'http', 'href' => $download['downloadlink'], 'appdata' => $appData);
364
			$app = Installer::installApp($info);
365
		}
366
		return $app;
367
	}
368
369
	/**
370
	 * @param string $app
371
	 * @return bool
372
	 */
373
	public static function removeApp($app) {
374
		if (self::isShipped($app)) {
375
			return false;
376
		}
377
378
		return Installer::removeApp($app);
379
	}
380
381
	/**
382
	 * This function set an app as disabled in appconfig.
383
	 *
384
	 * @param string $app app
385
	 * @throws Exception
386
	 */
387
	public static function disable($app) {
388
		// Convert OCS ID to regular application identifier
389
		if(self::getInternalAppIdByOcs($app) !== false) {
390
			$app = self::getInternalAppIdByOcs($app);
391
		}
392
393
		// flush
394
		self::$enabledAppsCache = array();
395
396
		// run uninstall steps
397
		$appData = OC_App::getAppInfo($app);
398
		if (!is_null($appData)) {
399
			OC_App::executeRepairSteps($app, $appData['repair-steps']['uninstall']);
400
		}
401
402
		// emit disable hook - needed anymore ?
403
		\OC_Hook::emit('OC_App', 'pre_disable', array('app' => $app));
404
405
		// finally disable it
406
		$appManager = \OC::$server->getAppManager();
407
		$appManager->disableApp($app);
408
	}
409
410
	/**
411
	 * Returns the Settings Navigation
412
	 *
413
	 * @return string[]
414
	 *
415
	 * This function returns an array containing all settings pages added. The
416
	 * entries are sorted by the key 'order' ascending.
417
	 */
418
	public static function getSettingsNavigation() {
419
		$l = \OC::$server->getL10N('lib');
420
		$urlGenerator = \OC::$server->getURLGenerator();
421
422
		$settings = array();
423
		// by default, settings only contain the help menu
424
		/*
425
		 * FIXME: Add help sidebar back once documentation is properly branded.
426
		 if (OC_Util::getEditionString() === '' &&
427
			\OC::$server->getSystemConfig()->getValue('knowledgebaseenabled', true) == true
428
		) {
429
			$settings = array(
430
				array(
431
					"id" => "help",
432
					"order" => 1000,
433
					"href" => $urlGenerator->linkToRoute('settings_help'),
434
					"name" => $l->t("Help"),
435
					"icon" => $urlGenerator->imagePath("settings", "help.svg")
436
				)
437
			);
438
		}*/
439
440
		// if the user is logged-in
441
		if (OC_User::isLoggedIn()) {
0 ignored issues
show
Deprecated Code introduced by
The method OC_User::isLoggedIn() has been deprecated with message: use \OC::$server->getUserSession()->isLoggedIn()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
442
			// personal menu
443
			$settings[] = array(
444
				"id" => "personal",
445
				"order" => 1,
446
				"href" => $urlGenerator->linkToRoute('settings_personal'),
447
				"name" => $l->t("Personal"),
448
				"icon" => $urlGenerator->imagePath("settings", "personal.svg")
449
			);
450
451
			//SubAdmins are also allowed to access user management
452
			$userObject = \OC::$server->getUserSession()->getUser();
453
			$isSubAdmin = false;
454
			if($userObject !== null) {
455
				$isSubAdmin = \OC::$server->getGroupManager()->getSubAdmin()->isSubAdmin($userObject);
456
			}
457
			if ($isSubAdmin) {
458
				// admin users menu
459
				$settings[] = array(
460
					"id" => "core_users",
461
					"order" => 2,
462
					"href" => $urlGenerator->linkToRoute('settings_users'),
463
					"name" => $l->t("Users"),
464
					"icon" => $urlGenerator->imagePath("settings", "users.svg")
465
				);
466
			}
467
468
			// if the user is an admin
469
			if (OC_User::isAdminUser(OC_User::getUser())) {
470
				// admin settings
471
				$settings[] = array(
472
					"id" => "admin",
473
					"order" => 1000,
474
					"href" => $urlGenerator->linkToRoute('settings_admin'),
475
					"name" => $l->t("Admin"),
476
					"icon" => $urlGenerator->imagePath("settings", "admin.svg")
477
				);
478
			}
479
		}
480
481
		$navigation = self::proceedNavigation($settings);
482
		return $navigation;
483
	}
484
485
	// This is private as well. It simply works, so don't ask for more details
486
	private static function proceedNavigation($list) {
487
		$activeApp = OC::$server->getNavigationManager()->getActiveEntry();
488
		foreach ($list as &$navEntry) {
489
			if ($navEntry['id'] == $activeApp) {
490
				$navEntry['active'] = true;
491
			} else {
492
				$navEntry['active'] = false;
493
			}
494
		}
495
		unset($navEntry);
496
497
		usort($list, create_function('$a, $b', 'if( $a["order"] == $b["order"] ) {return 0;}elseif( $a["order"] < $b["order"] ) {return -1;}else{return 1;}'));
498
499
		return $list;
500
	}
501
502
	/**
503
	 * Get the path where to install apps
504
	 *
505
	 * @return string|false
506
	 */
507
	public static function getInstallPath() {
508
		if (\OC::$server->getSystemConfig()->getValue('appstoreenabled', true) == false) {
509
			return false;
510
		}
511
512
		foreach (OC::$APPSROOTS as $dir) {
513
			if (isset($dir['writable']) && $dir['writable'] === true) {
514
				return $dir['path'];
515
			}
516
		}
517
518
		\OCP\Util::writeLog('core', 'No application directories are marked as writable.', \OCP\Util::ERROR);
519
		return null;
520
	}
521
522
523
	/**
524
	 * search for an app in all app-directories
525
	 *
526
	 * @param string $appId
527
	 * @return false|string
528
	 */
529
	protected static function findAppInDirectories($appId) {
530
		$sanitizedAppId = self::cleanAppId($appId);
531
		if($sanitizedAppId !== $appId) {
532
			return false;
533
		}
534
		static $app_dir = array();
535
536
		if (isset($app_dir[$appId])) {
537
			return $app_dir[$appId];
538
		}
539
540
		$possibleApps = array();
541
		foreach (OC::$APPSROOTS as $dir) {
542
			if (file_exists($dir['path'] . '/' . $appId)) {
543
				$possibleApps[] = $dir;
544
			}
545
		}
546
547
		if (empty($possibleApps)) {
548
			return false;
549
		} elseif (count($possibleApps) === 1) {
550
			$dir = array_shift($possibleApps);
551
			$app_dir[$appId] = $dir;
552
			return $dir;
553
		} else {
554
			$versionToLoad = array();
555
			foreach ($possibleApps as $possibleApp) {
556
				$version = self::getAppVersionByPath($possibleApp['path']);
557
				if (empty($versionToLoad) || version_compare($version, $versionToLoad['version'], '>')) {
558
					$versionToLoad = array(
559
						'dir' => $possibleApp,
560
						'version' => $version,
561
					);
562
				}
563
			}
564
			$app_dir[$appId] = $versionToLoad['dir'];
565
			return $versionToLoad['dir'];
566
			//TODO - write test
567
		}
568
	}
569
570
	/**
571
	 * Get the directory for the given app.
572
	 * If the app is defined in multiple directories, the first one is taken. (false if not found)
573
	 *
574
	 * @param string $appId
575
	 * @return string|false
576
	 */
577
	public static function getAppPath($appId) {
578
		if ($appId === null || trim($appId) === '') {
579
			return false;
580
		}
581
582
		if (($dir = self::findAppInDirectories($appId)) != false) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $dir = self::findAppInDirectories($appId) of type false|string against false; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
583
			return $dir['path'] . '/' . $appId;
584
		}
585
		return false;
586
	}
587
588
589
	/**
590
	 * check if an app's directory is writable
591
	 *
592
	 * @param string $appId
593
	 * @return bool
594
	 */
595
	public static function isAppDirWritable($appId) {
596
		$path = self::getAppPath($appId);
597
		return ($path !== false) ? is_writable($path) : false;
598
	}
599
600
	/**
601
	 * Get the path for the given app on the access
602
	 * If the app is defined in multiple directories, the first one is taken. (false if not found)
603
	 *
604
	 * @param string $appId
605
	 * @return string|false
606
	 */
607
	public static function getAppWebPath($appId) {
608
		if (($dir = self::findAppInDirectories($appId)) != false) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $dir = self::findAppInDirectories($appId) of type false|string against false; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
609
			return OC::$WEBROOT . $dir['url'] . '/' . $appId;
610
		}
611
		return false;
612
	}
613
614
	/**
615
	 * get the last version of the app from appinfo/info.xml
616
	 *
617
	 * @param string $appId
618
	 * @return string
619
	 */
620
	public static function getAppVersion($appId) {
621
		if (!isset(self::$appVersion[$appId])) {
622
			$file = self::getAppPath($appId);
623
			self::$appVersion[$appId] = ($file !== false) ? self::getAppVersionByPath($file) : '0';
624
		}
625
		return self::$appVersion[$appId];
626
	}
627
628
	/**
629
	 * get app's version based on it's path
630
	 *
631
	 * @param string $path
632
	 * @return string
633
	 */
634
	public static function getAppVersionByPath($path) {
635
		$infoFile = $path . '/appinfo/info.xml';
636
		$appData = self::getAppInfo($infoFile, true);
637
		return isset($appData['version']) ? $appData['version'] : '';
638
	}
639
640
641
	/**
642
	 * Read all app metadata from the info.xml file
643
	 *
644
	 * @param string $appId id of the app or the path of the info.xml file
645
	 * @param boolean $path (optional)
646
	 * @return array|null
647
	 * @note all data is read from info.xml, not just pre-defined fields
648
	 */
649
	public static function getAppInfo($appId, $path = false) {
650
		if ($path) {
651
			$file = $appId;
652
		} else {
653
			if (isset(self::$appInfo[$appId])) {
654
				return self::$appInfo[$appId];
655
			}
656
			$appPath = self::getAppPath($appId);
657
			if($appPath === false) {
658
				return null;
659
			}
660
			$file = $appPath . '/appinfo/info.xml';
661
		}
662
663
		$parser = new \OC\App\InfoParser(\OC::$server->getURLGenerator());
664
		$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 678 which is incompatible with the return type documented by OC_App::getAppInfo of type array|null.
Loading history...
665
666
		if (is_array($data)) {
667
			$data = OC_App::parseAppInfo($data);
668
		}
669
		if(isset($data['ocsid'])) {
670
			$storedId = \OC::$server->getConfig()->getAppValue($appId, 'ocsid');
671
			if($storedId !== '' && $storedId !== $data['ocsid']) {
672
				$data['ocsid'] = $storedId;
673
			}
674
		}
675
676
		self::$appInfo[$appId] = $data;
677
678
		return $data;
679
	}
680
681
	/**
682
	 * Returns the navigation
683
	 *
684
	 * @return array
685
	 *
686
	 * This function returns an array containing all entries added. The
687
	 * entries are sorted by the key 'order' ascending. Additional to the keys
688
	 * given for each app the following keys exist:
689
	 *   - active: boolean, signals if the user is on this navigation entry
690
	 */
691
	public static function getNavigation() {
692
		$entries = OC::$server->getNavigationManager()->getAll();
693
		$navigation = self::proceedNavigation($entries);
694
		return $navigation;
695
	}
696
697
	/**
698
	 * get the id of loaded app
699
	 *
700
	 * @return string
701
	 */
702
	public static function getCurrentApp() {
703
		$request = \OC::$server->getRequest();
704
		$script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1);
705
		$topFolder = substr($script, 0, strpos($script, '/'));
706
		if (empty($topFolder)) {
707
			$path_info = $request->getPathInfo();
708
			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...
709
				$topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1);
710
			}
711
		}
712
		if ($topFolder == 'apps') {
713
			$length = strlen($topFolder);
714
			return substr($script, $length + 1, strpos($script, '/', $length + 1) - $length - 1);
715
		} else {
716
			return $topFolder;
717
		}
718
	}
719
720
	/**
721
	 * @param string $type
722
	 * @return array
723
	 */
724
	public static function getForms($type) {
725
		$forms = array();
726
		switch ($type) {
727
			case 'admin':
728
				$source = self::$adminForms;
729
				break;
730
			case 'personal':
731
				$source = self::$personalForms;
732
				break;
733
			default:
734
				return array();
735
		}
736
		foreach ($source as $form) {
737
			$forms[] = include $form;
738
		}
739
		return $forms;
740
	}
741
742
	/**
743
	 * register an admin form to be shown
744
	 *
745
	 * @param string $app
746
	 * @param string $page
747
	 */
748
	public static function registerAdmin($app, $page) {
749
		self::$adminForms[] = $app . '/' . $page . '.php';
750
	}
751
752
	/**
753
	 * register a personal form to be shown
754
	 * @param string $app
755
	 * @param string $page
756
	 */
757
	public static function registerPersonal($app, $page) {
758
		self::$personalForms[] = $app . '/' . $page . '.php';
759
	}
760
761
	/**
762
	 * @param array $entry
763
	 */
764
	public static function registerLogIn(array $entry) {
765
		self::$altLogin[] = $entry;
766
	}
767
768
	/**
769
	 * @return array
770
	 */
771
	public static function getAlternativeLogIns() {
772
		return self::$altLogin;
773
	}
774
775
	/**
776
	 * get a list of all apps in the apps folder
777
	 *
778
	 * @return array an array of app names (string IDs)
779
	 * @todo: change the name of this method to getInstalledApps, which is more accurate
780
	 */
781
	public static function getAllApps() {
782
783
		$apps = array();
784
785
		foreach (OC::$APPSROOTS as $apps_dir) {
786
			if (!is_readable($apps_dir['path'])) {
787
				\OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], \OCP\Util::WARN);
788
				continue;
789
			}
790
			$dh = opendir($apps_dir['path']);
791
792
			if (is_resource($dh)) {
793
				while (($file = readdir($dh)) !== false) {
794
795
					if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) {
796
797
						$apps[] = $file;
798
					}
799
				}
800
			}
801
		}
802
803
		return $apps;
804
	}
805
806
	/**
807
	 * List all apps, this is used in apps.php
808
	 *
809
	 * @param bool $onlyLocal
810
	 * @param bool $includeUpdateInfo Should we check whether there is an update
811
	 *                                in the app store?
812
	 * @param OCSClient $ocsClient
813
	 * @return array
814
	 */
815
	public static function listAllApps($onlyLocal = false,
816
									   $includeUpdateInfo = true,
817
									   OCSClient $ocsClient) {
818
		$installedApps = OC_App::getAllApps();
819
820
		//TODO which apps do we want to blacklist and how do we integrate
821
		// blacklisting with the multi apps folder feature?
822
823
		//we don't want to show configuration for these
824
		$blacklist = \OC::$server->getAppManager()->getAlwaysEnabledApps();
825
		$appList = array();
826
827
		foreach ($installedApps as $app) {
828
			if (array_search($app, $blacklist) === false) {
829
830
				$info = OC_App::getAppInfo($app);
831
				if (!is_array($info)) {
832
					\OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', \OCP\Util::ERROR);
833
					continue;
834
				}
835
836
				if (!isset($info['name'])) {
837
					\OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', \OCP\Util::ERROR);
838
					continue;
839
				}
840
841
				$enabled = \OC::$server->getAppConfig()->getValue($app, 'enabled', 'no');
0 ignored issues
show
Deprecated Code introduced by
The method OCP\IAppConfig::getValue() has been deprecated with message: 8.0.0 use method getAppValue of \OCP\IConfig This function gets a value from the appconfig table. If the key does
not exist the default value will be returned

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
842
				$info['groups'] = null;
843
				if ($enabled === 'yes') {
844
					$active = true;
845
				} else if ($enabled === 'no') {
846
					$active = false;
847
				} else {
848
					$active = true;
849
					$info['groups'] = $enabled;
850
				}
851
852
				$info['active'] = $active;
853
854
				if (self::isShipped($app)) {
855
					$info['internal'] = true;
856
					$info['level'] = self::officialApp;
857
					$info['removable'] = false;
858
				} else {
859
					$info['internal'] = false;
860
					$info['removable'] = true;
861
				}
862
863
				$info['update'] = ($includeUpdateInfo) ? Installer::isUpdateAvailable($app) : null;
864
865
				$appPath = self::getAppPath($app);
866
				if($appPath !== false) {
867
					$appIcon = $appPath . '/img/' . $app . '.svg';
868
					if (file_exists($appIcon)) {
869
						$info['preview'] = \OC::$server->getURLGenerator()->imagePath($app, $app . '.svg');
870
						$info['previewAsIcon'] = true;
871
					} else {
872
						$appIcon = $appPath . '/img/app.svg';
873
						if (file_exists($appIcon)) {
874
							$info['preview'] = \OC::$server->getURLGenerator()->imagePath($app, 'app.svg');
875
							$info['previewAsIcon'] = true;
876
						}
877
					}
878
				}
879
				$info['version'] = OC_App::getAppVersion($app);
880
				$appList[] = $info;
881
			}
882
		}
883
		if ($onlyLocal) {
884
			$remoteApps = [];
885
		} else {
886
			$remoteApps = OC_App::getAppstoreApps('approved', null, $ocsClient);
887
		}
888
		if ($remoteApps) {
889
			// Remove duplicates
890
			foreach ($appList as $app) {
891
				foreach ($remoteApps AS $key => $remote) {
892
					if ($app['name'] === $remote['name'] ||
893
						(isset($app['ocsid']) &&
894
							$app['ocsid'] === $remote['id'])
895
					) {
896
						unset($remoteApps[$key]);
897
					}
898
				}
899
			}
900
			$combinedApps = array_merge($appList, $remoteApps);
901
		} else {
902
			$combinedApps = $appList;
903
		}
904
905
		return $combinedApps;
906
	}
907
908
	/**
909
	 * Returns the internal app ID or false
910
	 * @param string $ocsID
911
	 * @return string|false
912
	 */
913
	public static function getInternalAppIdByOcs($ocsID) {
914
		if(is_numeric($ocsID)) {
915
			$idArray = \OC::$server->getAppConfig()->getValues(false, 'ocsid');
916
			if(array_search($ocsID, $idArray)) {
917
				return array_search($ocsID, $idArray);
918
			}
919
		}
920
		return false;
921
	}
922
923
	/**
924
	 * Get a list of all apps on the appstore
925
	 * @param string $filter
926
	 * @param string|null $category
927
	 * @param OCSClient $ocsClient
928
	 * @return array|bool  multi-dimensional array of apps.
929
	 *                     Keys: id, name, type, typename, personid, license, detailpage, preview, changed, description
930
	 */
931
	public static function getAppstoreApps($filter = 'approved',
932
										   $category = null,
933
										   OCSClient $ocsClient) {
934
		$categories = [$category];
935
936
		if (is_null($category)) {
937
			$categoryNames = $ocsClient->getCategories(\OCP\Util::getVersion());
938
			if (is_array($categoryNames)) {
939
				// Check that categories of apps were retrieved correctly
940
				if (!$categories = array_keys($categoryNames)) {
941
					return false;
942
				}
943
			} else {
944
				return false;
945
			}
946
		}
947
948
		$page = 0;
949
		$remoteApps = $ocsClient->getApplications($categories, $page, $filter, \OCP\Util::getVersion());
950
		$apps = [];
951
		$i = 0;
952
		$l = \OC::$server->getL10N('core');
953
		foreach ($remoteApps as $app) {
954
			$potentialCleanId = self::getInternalAppIdByOcs($app['id']);
955
			// enhance app info (for example the description)
956
			$apps[$i] = OC_App::parseAppInfo($app);
957
			$apps[$i]['author'] = $app['personid'];
958
			$apps[$i]['ocs_id'] = $app['id'];
959
			$apps[$i]['internal'] = 0;
960
			$apps[$i]['active'] = ($potentialCleanId !== false) ? self::isEnabled($potentialCleanId) : false;
961
			$apps[$i]['update'] = false;
962
			$apps[$i]['groups'] = false;
963
			$apps[$i]['score'] = $app['score'];
964
			$apps[$i]['removable'] = false;
965
			if ($app['label'] == 'recommended') {
966
				$apps[$i]['internallabel'] = (string)$l->t('Recommended');
967
				$apps[$i]['internalclass'] = 'recommendedapp';
968
			}
969
970
			// Apps from the appstore are always assumed to be compatible with the
971
			// the current release as the initial filtering is done on the appstore
972
			$apps[$i]['dependencies']['owncloud']['@attributes']['min-version'] = implode('.', \OCP\Util::getVersion());
973
			$apps[$i]['dependencies']['owncloud']['@attributes']['max-version'] = implode('.', \OCP\Util::getVersion());
974
975
			$i++;
976
		}
977
978
979
980
		if (empty($apps)) {
981
			return false;
982
		} else {
983
			return $apps;
984
		}
985
	}
986
987
	public static function shouldUpgrade($app) {
988
		$versions = self::getAppVersions();
989
		$currentVersion = OC_App::getAppVersion($app);
990
		if ($currentVersion && isset($versions[$app])) {
991
			$installedVersion = $versions[$app];
992
			if (version_compare($currentVersion, $installedVersion, '>')) {
993
				return true;
994
			}
995
		}
996
		return false;
997
	}
998
999
	/**
1000
	 * Adjust the number of version parts of $version1 to match
1001
	 * the number of version parts of $version2.
1002
	 *
1003
	 * @param string $version1 version to adjust
1004
	 * @param string $version2 version to take the number of parts from
1005
	 * @return string shortened $version1
1006
	 */
1007
	private static function adjustVersionParts($version1, $version2) {
1008
		$version1 = explode('.', $version1);
1009
		$version2 = explode('.', $version2);
1010
		// reduce $version1 to match the number of parts in $version2
1011
		while (count($version1) > count($version2)) {
1012
			array_pop($version1);
1013
		}
1014
		// if $version1 does not have enough parts, add some
1015
		while (count($version1) < count($version2)) {
1016
			$version1[] = '0';
1017
		}
1018
		return implode('.', $version1);
1019
	}
1020
1021
	/**
1022
	 * Check whether the current ownCloud version matches the given
1023
	 * application's version requirements.
1024
	 *
1025
	 * The comparison is made based on the number of parts that the
1026
	 * app info version has. For example for ownCloud 6.0.3 if the
1027
	 * app info version is expecting version 6.0, the comparison is
1028
	 * made on the first two parts of the ownCloud version.
1029
	 * This means that it's possible to specify "requiremin" => 6
1030
	 * and "requiremax" => 6 and it will still match ownCloud 6.0.3.
1031
	 *
1032
	 * @param string $ocVersion ownCloud version to check against
1033
	 * @param array $appInfo app info (from xml)
1034
	 *
1035
	 * @return boolean true if compatible, otherwise false
1036
	 */
1037
	public static function isAppCompatible($ocVersion, $appInfo) {
1038
		$requireMin = '';
1039
		$requireMax = '';
1040
		if (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) {
1041
			$requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version'];
1042
		} else if (isset($appInfo['requiremin'])) {
1043
			$requireMin = $appInfo['requiremin'];
1044
		} else if (isset($appInfo['require'])) {
1045
			$requireMin = $appInfo['require'];
1046
		}
1047
1048
		if (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) {
1049
			$requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version'];
1050
		} else if (isset($appInfo['requiremax'])) {
1051
			$requireMax = $appInfo['requiremax'];
1052
		}
1053
1054
		if (is_array($ocVersion)) {
1055
			$ocVersion = implode('.', $ocVersion);
1056
		}
1057
1058 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...
1059
			&& version_compare(self::adjustVersionParts($ocVersion, $requireMin), $requireMin, '<')
1060
		) {
1061
1062
			return false;
1063
		}
1064
1065 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...
1066
			&& version_compare(self::adjustVersionParts($ocVersion, $requireMax), $requireMax, '>')
1067
		) {
1068
			return false;
1069
		}
1070
1071
		return true;
1072
	}
1073
1074
	/**
1075
	 * get the installed version of all apps
1076
	 */
1077
	public static function getAppVersions() {
1078
		static $versions;
1079
1080
		if(!$versions) {
1081
			$appConfig = \OC::$server->getAppConfig();
1082
			$versions = $appConfig->getValues(false, 'installed_version');
1083
		}
1084
		return $versions;
1085
	}
1086
1087
	/**
1088
	 * @param string $app
1089
	 * @return bool
1090
	 * @throws Exception if app is not compatible with this version of ownCloud
1091
	 * @throws Exception if no app-name was specified
1092
	 */
1093
	public static function installApp($app) {
1094
		$appName = $app; // $app will be overwritten, preserve name for error logging
1095
		$l = \OC::$server->getL10N('core');
1096
		$config = \OC::$server->getConfig();
1097
		$ocsClient = new OCSClient(
1098
			\OC::$server->getHTTPClientService(),
1099
			$config,
1100
			\OC::$server->getLogger()
1101
		);
1102
		$appData = $ocsClient->getApplication($app, \OCP\Util::getVersion());
1103
1104
		// check if app is a shipped app or not. OCS apps have an integer as id, shipped apps use a string
1105
		if (!is_numeric($app)) {
1106
			$shippedVersion = self::getAppVersion($app);
1107
			if ($appData && version_compare($shippedVersion, $appData['version'], '<')) {
1108
				$app = self::downloadApp($app);
1109
			} else {
1110
				$app = Installer::installShippedApp($app);
1111
			}
1112
		} else {
1113
			// Maybe the app is already installed - compare the version in this
1114
			// case and use the local already installed one.
1115
			// FIXME: This is a horrible hack. I feel sad. The god of code cleanness may forgive me.
1116
			$internalAppId = self::getInternalAppIdByOcs($app);
1117
			if($internalAppId !== false) {
1118
				if($appData && version_compare(\OC_App::getAppVersion($internalAppId), $appData['version'], '<')) {
1119
					$app = self::downloadApp($app);
1120
				} else {
1121
					self::enable($internalAppId);
1122
					$app = $internalAppId;
1123
				}
1124
			} else {
1125
				$app = self::downloadApp($app);
1126
			}
1127
		}
1128
1129
		if ($app !== false) {
1130
			// check if the app is compatible with this version of ownCloud
1131
			$info = self::getAppInfo($app);
1132
			if(!is_array($info)) {
1133
				throw new \Exception(
1134
					$l->t('App "%s" cannot be installed because appinfo file cannot be read.',
1135
						[$info['name']]
1136
					)
1137
				);
1138
			}
1139
1140
			$version = \OCP\Util::getVersion();
1141
			if (!self::isAppCompatible($version, $info)) {
0 ignored issues
show
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...
1142
				throw new \Exception(
1143
					$l->t('App "%s" cannot be installed because it is not compatible with this version of the server.',
1144
						array($info['name'])
1145
					)
1146
				);
1147
			}
1148
1149
			// check for required dependencies
1150
			$dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l);
1151
			$missing = $dependencyAnalyzer->analyze($info);
1152
			if (!empty($missing)) {
1153
				$missingMsg = join(PHP_EOL, $missing);
1154
				throw new \Exception(
1155
					$l->t('App "%s" cannot be installed because the following dependencies are not fulfilled: %s',
1156
						array($info['name'], $missingMsg)
1157
					)
1158
				);
1159
			}
1160
1161
			$config->setAppValue($app, 'enabled', 'yes');
1162
			if (isset($appData['id'])) {
1163
				$config->setAppValue($app, 'ocsid', $appData['id']);
1164
			}
1165
			\OC_Hook::emit('OC_App', 'post_enable', array('app' => $app));
1166
		} else {
1167
			if(empty($appName) ) {
1168
				throw new \Exception($l->t("No app name specified"));
1169
			} else {
1170
				throw new \Exception($l->t("App '%s' could not be installed!", $appName));
0 ignored issues
show
Documentation introduced by
$appName is of type string, but the function expects a array.

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...
1171
			}
1172
		}
1173
1174
		return $app;
1175
	}
1176
1177
	/**
1178
	 * update the database for the app and call the update script
1179
	 *
1180
	 * @param string $appId
1181
	 * @return bool
1182
	 */
1183
	public static function updateApp($appId) {
1184
		$appPath = self::getAppPath($appId);
1185
		if($appPath === false) {
1186
			return false;
1187
		}
1188
		$appData = self::getAppInfo($appId);
1189
		self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']);
1190
		if (file_exists($appPath . '/appinfo/database.xml')) {
1191
			OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml');
1192
		}
1193
		self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']);
1194
		self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']);
1195
		unset(self::$appVersion[$appId]);
1196
		// run upgrade code
1197
		if (file_exists($appPath . '/appinfo/update.php')) {
1198
			self::loadApp($appId, false);
1199
			include $appPath . '/appinfo/update.php';
1200
		}
1201
		self::setupBackgroundJobs($appData['background-jobs']);
1202
1203
		//set remote/public handlers
1204
		if (array_key_exists('ocsid', $appData)) {
1205
			\OC::$server->getConfig()->setAppValue($appId, 'ocsid', $appData['ocsid']);
1206 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...
1207
			\OC::$server->getConfig()->deleteAppValue($appId, 'ocsid');
1208
		}
1209
		foreach ($appData['remote'] as $name => $path) {
1210
			\OC::$server->getConfig()->setAppValue('core', 'remote_' . $name, $appId . '/' . $path);
1211
		}
1212 View Code Duplication
		foreach ($appData['public'] as $name => $path) {
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...
1213
			\OC::$server->getConfig()->setAppValue('core', 'public_' . $name, $appId . '/' . $path);
1214
		}
1215
1216
		self::setAppTypes($appId);
1217
1218
		$version = \OC_App::getAppVersion($appId);
1219
		\OC::$server->getAppConfig()->setValue($appId, 'installed_version', $version);
0 ignored issues
show
Deprecated Code introduced by
The method OCP\IAppConfig::setValue() has been deprecated with message: 8.0.0 use method setAppValue of \OCP\IConfig Sets a value. If the key did not exist before it will be created.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1220
1221
		return true;
1222
	}
1223
1224
	/**
1225
	 * @param string $appId
1226
	 * @param string[] $steps
1227
	 * @throws \OC\NeedsUpdateException
1228
	 */
1229
	public static function executeRepairSteps($appId, array $steps) {
1230
		if (empty($steps)) {
1231
			return;
1232
		}
1233
		// load the app
1234
		self::loadApp($appId, false);
1235
1236
		$dispatcher = OC::$server->getEventDispatcher();
1237
1238
		// load the steps
1239
		$r = new Repair([], $dispatcher);
1240
		foreach ($steps as $step) {
1241
			try {
1242
				$r->addStep($step);
1243
			} catch (Exception $ex) {
1244
				$r->emit('\OC\Repair', 'error', [$ex->getMessage()]);
1245
				\OC::$server->getLogger()->logException($ex);
1246
			}
1247
		}
1248
		// run the steps
1249
		$r->run();
1250
	}
1251
1252
	public static function setupBackgroundJobs(array $jobs) {
1253
		$queue = \OC::$server->getJobList();
1254
		foreach ($jobs as $job) {
1255
			$queue->add($job);
1256
		}
1257
	}
1258
1259
	/**
1260
	 * @param string $appId
1261
	 * @param string[] $steps
1262
	 */
1263
	private static function setupLiveMigrations($appId, array $steps) {
1264
		$queue = \OC::$server->getJobList();
1265
		foreach ($steps as $step) {
1266
			$queue->add('OC\Migration\BackgroundRepair', [
1267
				'app' => $appId,
1268
				'step' => $step]);
1269
		}
1270
	}
1271
1272
	/**
1273
	 * @param string $appId
1274
	 * @return \OC\Files\View|false
1275
	 */
1276
	public static function getStorage($appId) {
1277
		if (OC_App::isEnabled($appId)) { //sanity check
1278
			if (OC_User::isLoggedIn()) {
0 ignored issues
show
Deprecated Code introduced by
The method OC_User::isLoggedIn() has been deprecated with message: use \OC::$server->getUserSession()->isLoggedIn()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1279
				$view = new \OC\Files\View('/' . OC_User::getUser());
1280
				if (!$view->file_exists($appId)) {
1281
					$view->mkdir($appId);
1282
				}
1283
				return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId);
1284
			} else {
1285
				\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', \OCP\Util::ERROR);
1286
				return false;
1287
			}
1288
		} else {
1289
			\OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', \OCP\Util::ERROR);
1290
			return false;
1291
		}
1292
	}
1293
1294
	/**
1295
	 * parses the app data array and enhanced the 'description' value
1296
	 *
1297
	 * @param array $data the app data
1298
	 * @return array improved app data
1299
	 */
1300
	public static function parseAppInfo(array $data) {
1301
1302
		// just modify the description if it is available
1303
		// otherwise this will create a $data element with an empty 'description'
1304
		if (isset($data['description'])) {
1305
			if (is_string($data['description'])) {
1306
				// sometimes the description contains line breaks and they are then also
1307
				// shown in this way in the app management which isn't wanted as HTML
1308
				// manages line breaks itself
1309
1310
				// first of all we split on empty lines
1311
				$paragraphs = preg_split("!\n[[:space:]]*\n!mu", $data['description']);
1312
1313
				$result = [];
1314
				foreach ($paragraphs as $value) {
1315
					// replace multiple whitespace (tabs, space, newlines) inside a paragraph
1316
					// with a single space - also trims whitespace
1317
					$result[] = trim(preg_replace('![[:space:]]+!mu', ' ', $value));
1318
				}
1319
1320
				// join the single paragraphs with a empty line in between
1321
				$data['description'] = implode("\n\n", $result);
1322
1323
			} else {
1324
				$data['description'] = '';
1325
			}
1326
		}
1327
1328
		return $data;
1329
	}
1330
}
1331