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