Passed
Push — master ( 3d0e0f...a65c00 )
by Morris
09:35 queued 10s
created
lib/private/App/AppManager.php 2 patches
Indentation   +482 added lines, -482 removed lines patch added patch discarded remove patch
@@ -46,486 +46,486 @@
 block discarded – undo
46 46
 
47 47
 class AppManager implements IAppManager {
48 48
 
49
-	/**
50
-	 * Apps with these types can not be enabled for certain groups only
51
-	 * @var string[]
52
-	 */
53
-	protected $protectedAppTypes = [
54
-		'filesystem',
55
-		'prelogin',
56
-		'authentication',
57
-		'logging',
58
-		'prevent_group_restriction',
59
-	];
60
-
61
-	/** @var IUserSession */
62
-	private $userSession;
63
-
64
-	/** @var AppConfig */
65
-	private $appConfig;
66
-
67
-	/** @var IGroupManager */
68
-	private $groupManager;
69
-
70
-	/** @var ICacheFactory */
71
-	private $memCacheFactory;
72
-
73
-	/** @var EventDispatcherInterface */
74
-	private $dispatcher;
75
-
76
-	/** @var ILogger */
77
-	private $logger;
78
-
79
-	/** @var string[] $appId => $enabled */
80
-	private $installedAppsCache;
81
-
82
-	/** @var string[] */
83
-	private $shippedApps;
84
-
85
-	/** @var string[] */
86
-	private $alwaysEnabled;
87
-
88
-	/** @var array */
89
-	private $appInfos = [];
90
-
91
-	/** @var array */
92
-	private $appVersions = [];
93
-
94
-	/**
95
-	 * @param IUserSession $userSession
96
-	 * @param AppConfig $appConfig
97
-	 * @param IGroupManager $groupManager
98
-	 * @param ICacheFactory $memCacheFactory
99
-	 * @param EventDispatcherInterface $dispatcher
100
-	 */
101
-	public function __construct(IUserSession $userSession,
102
-								AppConfig $appConfig,
103
-								IGroupManager $groupManager,
104
-								ICacheFactory $memCacheFactory,
105
-								EventDispatcherInterface $dispatcher,
106
-								ILogger $logger) {
107
-		$this->userSession = $userSession;
108
-		$this->appConfig = $appConfig;
109
-		$this->groupManager = $groupManager;
110
-		$this->memCacheFactory = $memCacheFactory;
111
-		$this->dispatcher = $dispatcher;
112
-		$this->logger = $logger;
113
-	}
114
-
115
-	/**
116
-	 * @return string[] $appId => $enabled
117
-	 */
118
-	private function getInstalledAppsValues() {
119
-		if (!$this->installedAppsCache) {
120
-			$values = $this->appConfig->getValues(false, 'enabled');
121
-
122
-			$alwaysEnabledApps = $this->getAlwaysEnabledApps();
123
-			foreach($alwaysEnabledApps as $appId) {
124
-				$values[$appId] = 'yes';
125
-			}
126
-
127
-			$this->installedAppsCache = array_filter($values, function ($value) {
128
-				return $value !== 'no';
129
-			});
130
-			ksort($this->installedAppsCache);
131
-		}
132
-		return $this->installedAppsCache;
133
-	}
134
-
135
-	/**
136
-	 * List all installed apps
137
-	 *
138
-	 * @return string[]
139
-	 */
140
-	public function getInstalledApps() {
141
-		return array_keys($this->getInstalledAppsValues());
142
-	}
143
-
144
-	/**
145
-	 * List all apps enabled for a user
146
-	 *
147
-	 * @param \OCP\IUser $user
148
-	 * @return string[]
149
-	 */
150
-	public function getEnabledAppsForUser(IUser $user) {
151
-		$apps = $this->getInstalledAppsValues();
152
-		$appsForUser = array_filter($apps, function ($enabled) use ($user) {
153
-			return $this->checkAppForUser($enabled, $user);
154
-		});
155
-		return array_keys($appsForUser);
156
-	}
157
-
158
-	/**
159
-	 * @param \OCP\IGroup $group
160
-	 * @return array
161
-	 */
162
-	public function getEnabledAppsForGroup(IGroup $group): array {
163
-		$apps = $this->getInstalledAppsValues();
164
-		$appsForGroups = array_filter($apps, function ($enabled) use ($group) {
165
-			return $this->checkAppForGroups($enabled, $group);
166
-		});
167
-		return array_keys($appsForGroups);
168
-	}
169
-
170
-	/**
171
-	 * @param string $appId
172
-	 * @return array
173
-	 */
174
-	public function getAppRestriction(string $appId): array {
175
-		$values = $this->getInstalledAppsValues();
176
-
177
-		if (!isset($values[$appId])) {
178
-			return [];
179
-		}
180
-
181
-		if ($values[$appId] === 'yes' || $values[$appId] === 'no') {
182
-			return [];
183
-		}
184
-		return json_decode($values[$appId]);
185
-	}
186
-
187
-
188
-	/**
189
-	 * Check if an app is enabled for user
190
-	 *
191
-	 * @param string $appId
192
-	 * @param \OCP\IUser $user (optional) if not defined, the currently logged in user will be used
193
-	 * @return bool
194
-	 */
195
-	public function isEnabledForUser($appId, $user = null) {
196
-		if ($this->isAlwaysEnabled($appId)) {
197
-			return true;
198
-		}
199
-		if ($user === null) {
200
-			$user = $this->userSession->getUser();
201
-		}
202
-		$installedApps = $this->getInstalledAppsValues();
203
-		if (isset($installedApps[$appId])) {
204
-			return $this->checkAppForUser($installedApps[$appId], $user);
205
-		} else {
206
-			return false;
207
-		}
208
-	}
209
-
210
-	/**
211
-	 * @param string $enabled
212
-	 * @param IUser $user
213
-	 * @return bool
214
-	 */
215
-	private function checkAppForUser($enabled, $user) {
216
-		if ($enabled === 'yes') {
217
-			return true;
218
-		} elseif ($user === null) {
219
-			return false;
220
-		} else {
221
-			if(empty($enabled)){
222
-				return false;
223
-			}
224
-
225
-			$groupIds = json_decode($enabled);
226
-
227
-			if (!is_array($groupIds)) {
228
-				$jsonError = json_last_error();
229
-				$this->logger->warning('AppManger::checkAppForUser - can\'t decode group IDs: ' . print_r($enabled, true) . ' - json error code: ' . $jsonError, ['app' => 'lib']);
230
-				return false;
231
-			}
232
-
233
-			$userGroups = $this->groupManager->getUserGroupIds($user);
234
-			foreach ($userGroups as $groupId) {
235
-				if (in_array($groupId, $groupIds, true)) {
236
-					return true;
237
-				}
238
-			}
239
-			return false;
240
-		}
241
-	}
242
-
243
-	/**
244
-	 * @param string $enabled
245
-	 * @param IGroup $group
246
-	 * @return bool
247
-	 */
248
-	private function checkAppForGroups(string $enabled, IGroup $group): bool {
249
-		if ($enabled === 'yes') {
250
-			return true;
251
-		} elseif ($group === null) {
252
-			return false;
253
-		} else {
254
-			if (empty($enabled)) {
255
-				return false;
256
-			}
257
-
258
-			$groupIds = json_decode($enabled);
259
-
260
-			if (!is_array($groupIds)) {
261
-				$jsonError = json_last_error();
262
-				$this->logger->warning('AppManger::checkAppForUser - can\'t decode group IDs: ' . print_r($enabled, true) . ' - json error code: ' . $jsonError, ['app' => 'lib']);
263
-				return false;
264
-			}
265
-
266
-			return in_array($group->getGID(), $groupIds);
267
-		}
268
-	}
269
-
270
-	/**
271
-	 * Check if an app is enabled in the instance
272
-	 *
273
-	 * Notice: This actually checks if the app is enabled and not only if it is installed.
274
-	 *
275
-	 * @param string $appId
276
-	 * @param \OCP\IGroup[]|String[] $groups
277
-	 * @return bool
278
-	 */
279
-	public function isInstalled($appId) {
280
-		$installedApps = $this->getInstalledAppsValues();
281
-		return isset($installedApps[$appId]);
282
-	}
283
-
284
-	/**
285
-	 * Enable an app for every user
286
-	 *
287
-	 * @param string $appId
288
-	 * @throws AppPathNotFoundException
289
-	 */
290
-	public function enableApp($appId) {
291
-		// Check if app exists
292
-		$this->getAppPath($appId);
293
-
294
-		$this->installedAppsCache[$appId] = 'yes';
295
-		$this->appConfig->setValue($appId, 'enabled', 'yes');
296
-		$this->dispatcher->dispatch(ManagerEvent::EVENT_APP_ENABLE, new ManagerEvent(
297
-			ManagerEvent::EVENT_APP_ENABLE, $appId
298
-		));
299
-		$this->clearAppsCache();
300
-	}
301
-
302
-	/**
303
-	 * Whether a list of types contains a protected app type
304
-	 *
305
-	 * @param string[] $types
306
-	 * @return bool
307
-	 */
308
-	public function hasProtectedAppType($types) {
309
-		if (empty($types)) {
310
-			return false;
311
-		}
312
-
313
-		$protectedTypes = array_intersect($this->protectedAppTypes, $types);
314
-		return !empty($protectedTypes);
315
-	}
316
-
317
-	/**
318
-	 * Enable an app only for specific groups
319
-	 *
320
-	 * @param string $appId
321
-	 * @param \OCP\IGroup[] $groups
322
-	 * @throws \InvalidArgumentException if app can't be enabled for groups
323
-	 * @throws AppPathNotFoundException
324
-	 */
325
-	public function enableAppForGroups($appId, $groups) {
326
-		// Check if app exists
327
-		$this->getAppPath($appId);
328
-
329
-		$info = $this->getAppInfo($appId);
330
-		if (!empty($info['types']) && $this->hasProtectedAppType($info['types'])) {
331
-			throw new \InvalidArgumentException("$appId can't be enabled for groups.");
332
-		}
333
-
334
-		$groupIds = array_map(function ($group) {
335
-			/** @var \OCP\IGroup $group */
336
-			return ($group instanceof IGroup)
337
-				? $group->getGID()
338
-				: $group;
339
-		}, $groups);
340
-
341
-		$this->installedAppsCache[$appId] = json_encode($groupIds);
342
-		$this->appConfig->setValue($appId, 'enabled', json_encode($groupIds));
343
-		$this->dispatcher->dispatch(ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, new ManagerEvent(
344
-			ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, $appId, $groups
345
-		));
346
-		$this->clearAppsCache();
347
-
348
-	}
349
-
350
-	/**
351
-	 * Disable an app for every user
352
-	 *
353
-	 * @param string $appId
354
-	 * @throws \Exception if app can't be disabled
355
-	 */
356
-	public function disableApp($appId) {
357
-		if ($this->isAlwaysEnabled($appId)) {
358
-			throw new \Exception("$appId can't be disabled.");
359
-		}
360
-		unset($this->installedAppsCache[$appId]);
361
-		$this->appConfig->setValue($appId, 'enabled', 'no');
362
-
363
-		// run uninstall steps
364
-		$appData = $this->getAppInfo($appId);
365
-		if (!is_null($appData)) {
366
-			\OC_App::executeRepairSteps($appId, $appData['repair-steps']['uninstall']);
367
-		}
368
-
369
-		$this->dispatcher->dispatch(ManagerEvent::EVENT_APP_DISABLE, new ManagerEvent(
370
-			ManagerEvent::EVENT_APP_DISABLE, $appId
371
-		));
372
-		$this->clearAppsCache();
373
-	}
374
-
375
-	/**
376
-	 * Get the directory for the given app.
377
-	 *
378
-	 * @param string $appId
379
-	 * @return string
380
-	 * @throws AppPathNotFoundException if app folder can't be found
381
-	 */
382
-	public function getAppPath($appId) {
383
-		$appPath = \OC_App::getAppPath($appId);
384
-		if($appPath === false) {
385
-			throw new AppPathNotFoundException('Could not find path for ' . $appId);
386
-		}
387
-		return $appPath;
388
-	}
389
-
390
-	/**
391
-	 * Clear the cached list of apps when enabling/disabling an app
392
-	 */
393
-	public function clearAppsCache() {
394
-		$settingsMemCache = $this->memCacheFactory->createDistributed('settings');
395
-		$settingsMemCache->clear('listApps');
396
-		$this->appInfos = [];
397
-	}
398
-
399
-	/**
400
-	 * Returns a list of apps that need upgrade
401
-	 *
402
-	 * @param string $version Nextcloud version as array of version components
403
-	 * @return array list of app info from apps that need an upgrade
404
-	 *
405
-	 * @internal
406
-	 */
407
-	public function getAppsNeedingUpgrade($version) {
408
-		$appsToUpgrade = [];
409
-		$apps = $this->getInstalledApps();
410
-		foreach ($apps as $appId) {
411
-			$appInfo = $this->getAppInfo($appId);
412
-			$appDbVersion = $this->appConfig->getValue($appId, 'installed_version');
413
-			if ($appDbVersion
414
-				&& isset($appInfo['version'])
415
-				&& version_compare($appInfo['version'], $appDbVersion, '>')
416
-				&& \OC_App::isAppCompatible($version, $appInfo)
417
-			) {
418
-				$appsToUpgrade[] = $appInfo;
419
-			}
420
-		}
421
-
422
-		return $appsToUpgrade;
423
-	}
424
-
425
-	/**
426
-	 * Returns the app information from "appinfo/info.xml".
427
-	 *
428
-	 * @param string $appId app id
429
-	 *
430
-	 * @param bool $path
431
-	 * @param null $lang
432
-	 * @return array|null app info
433
-	 */
434
-	public function getAppInfo(string $appId, bool $path = false, $lang = null) {
435
-		if ($path) {
436
-			$file = $appId;
437
-		} else {
438
-			if ($lang === null && isset($this->appInfos[$appId])) {
439
-				return $this->appInfos[$appId];
440
-			}
441
-			try {
442
-				$appPath = $this->getAppPath($appId);
443
-			} catch (AppPathNotFoundException $e) {
444
-				return null;
445
-			}
446
-			$file = $appPath . '/appinfo/info.xml';
447
-		}
448
-
449
-		$parser = new InfoParser($this->memCacheFactory->createLocal('core.appinfo'));
450
-		$data = $parser->parse($file);
451
-
452
-		if (is_array($data)) {
453
-			$data = \OC_App::parseAppInfo($data, $lang);
454
-		}
455
-
456
-		if ($lang === null) {
457
-			$this->appInfos[$appId] = $data;
458
-		}
459
-
460
-		return $data;
461
-	}
462
-
463
-	public function getAppVersion(string $appId, bool $useCache = true): string {
464
-		if(!$useCache || !isset($this->appVersions[$appId])) {
465
-			$appInfo = $this->getAppInfo($appId);
466
-			$this->appVersions[$appId] = ($appInfo !== null && isset($appInfo['version'])) ? $appInfo['version'] : '0';
467
-		}
468
-		return $this->appVersions[$appId];
469
-	}
470
-
471
-	/**
472
-	 * Returns a list of apps incompatible with the given version
473
-	 *
474
-	 * @param string $version Nextcloud version as array of version components
475
-	 *
476
-	 * @return array list of app info from incompatible apps
477
-	 *
478
-	 * @internal
479
-	 */
480
-	public function getIncompatibleApps(string $version): array {
481
-		$apps = $this->getInstalledApps();
482
-		$incompatibleApps = array();
483
-		foreach ($apps as $appId) {
484
-			$info = $this->getAppInfo($appId);
485
-			if ($info === null) {
486
-				$incompatibleApps[] = ['id' => $appId];
487
-			} else if (!\OC_App::isAppCompatible($version, $info)) {
488
-				$incompatibleApps[] = $info;
489
-			}
490
-		}
491
-		return $incompatibleApps;
492
-	}
493
-
494
-	/**
495
-	 * @inheritdoc
496
-	 * In case you change this method, also change \OC\App\CodeChecker\InfoChecker::isShipped()
497
-	 */
498
-	public function isShipped($appId) {
499
-		$this->loadShippedJson();
500
-		return in_array($appId, $this->shippedApps, true);
501
-	}
502
-
503
-	private function isAlwaysEnabled($appId) {
504
-		$alwaysEnabled = $this->getAlwaysEnabledApps();
505
-		return in_array($appId, $alwaysEnabled, true);
506
-	}
507
-
508
-	/**
509
-	 * In case you change this method, also change \OC\App\CodeChecker\InfoChecker::loadShippedJson()
510
-	 * @throws \Exception
511
-	 */
512
-	private function loadShippedJson() {
513
-		if ($this->shippedApps === null) {
514
-			$shippedJson = \OC::$SERVERROOT . '/core/shipped.json';
515
-			if (!file_exists($shippedJson)) {
516
-				throw new \Exception("File not found: $shippedJson");
517
-			}
518
-			$content = json_decode(file_get_contents($shippedJson), true);
519
-			$this->shippedApps = $content['shippedApps'];
520
-			$this->alwaysEnabled = $content['alwaysEnabled'];
521
-		}
522
-	}
523
-
524
-	/**
525
-	 * @inheritdoc
526
-	 */
527
-	public function getAlwaysEnabledApps() {
528
-		$this->loadShippedJson();
529
-		return $this->alwaysEnabled;
530
-	}
49
+    /**
50
+     * Apps with these types can not be enabled for certain groups only
51
+     * @var string[]
52
+     */
53
+    protected $protectedAppTypes = [
54
+        'filesystem',
55
+        'prelogin',
56
+        'authentication',
57
+        'logging',
58
+        'prevent_group_restriction',
59
+    ];
60
+
61
+    /** @var IUserSession */
62
+    private $userSession;
63
+
64
+    /** @var AppConfig */
65
+    private $appConfig;
66
+
67
+    /** @var IGroupManager */
68
+    private $groupManager;
69
+
70
+    /** @var ICacheFactory */
71
+    private $memCacheFactory;
72
+
73
+    /** @var EventDispatcherInterface */
74
+    private $dispatcher;
75
+
76
+    /** @var ILogger */
77
+    private $logger;
78
+
79
+    /** @var string[] $appId => $enabled */
80
+    private $installedAppsCache;
81
+
82
+    /** @var string[] */
83
+    private $shippedApps;
84
+
85
+    /** @var string[] */
86
+    private $alwaysEnabled;
87
+
88
+    /** @var array */
89
+    private $appInfos = [];
90
+
91
+    /** @var array */
92
+    private $appVersions = [];
93
+
94
+    /**
95
+     * @param IUserSession $userSession
96
+     * @param AppConfig $appConfig
97
+     * @param IGroupManager $groupManager
98
+     * @param ICacheFactory $memCacheFactory
99
+     * @param EventDispatcherInterface $dispatcher
100
+     */
101
+    public function __construct(IUserSession $userSession,
102
+                                AppConfig $appConfig,
103
+                                IGroupManager $groupManager,
104
+                                ICacheFactory $memCacheFactory,
105
+                                EventDispatcherInterface $dispatcher,
106
+                                ILogger $logger) {
107
+        $this->userSession = $userSession;
108
+        $this->appConfig = $appConfig;
109
+        $this->groupManager = $groupManager;
110
+        $this->memCacheFactory = $memCacheFactory;
111
+        $this->dispatcher = $dispatcher;
112
+        $this->logger = $logger;
113
+    }
114
+
115
+    /**
116
+     * @return string[] $appId => $enabled
117
+     */
118
+    private function getInstalledAppsValues() {
119
+        if (!$this->installedAppsCache) {
120
+            $values = $this->appConfig->getValues(false, 'enabled');
121
+
122
+            $alwaysEnabledApps = $this->getAlwaysEnabledApps();
123
+            foreach($alwaysEnabledApps as $appId) {
124
+                $values[$appId] = 'yes';
125
+            }
126
+
127
+            $this->installedAppsCache = array_filter($values, function ($value) {
128
+                return $value !== 'no';
129
+            });
130
+            ksort($this->installedAppsCache);
131
+        }
132
+        return $this->installedAppsCache;
133
+    }
134
+
135
+    /**
136
+     * List all installed apps
137
+     *
138
+     * @return string[]
139
+     */
140
+    public function getInstalledApps() {
141
+        return array_keys($this->getInstalledAppsValues());
142
+    }
143
+
144
+    /**
145
+     * List all apps enabled for a user
146
+     *
147
+     * @param \OCP\IUser $user
148
+     * @return string[]
149
+     */
150
+    public function getEnabledAppsForUser(IUser $user) {
151
+        $apps = $this->getInstalledAppsValues();
152
+        $appsForUser = array_filter($apps, function ($enabled) use ($user) {
153
+            return $this->checkAppForUser($enabled, $user);
154
+        });
155
+        return array_keys($appsForUser);
156
+    }
157
+
158
+    /**
159
+     * @param \OCP\IGroup $group
160
+     * @return array
161
+     */
162
+    public function getEnabledAppsForGroup(IGroup $group): array {
163
+        $apps = $this->getInstalledAppsValues();
164
+        $appsForGroups = array_filter($apps, function ($enabled) use ($group) {
165
+            return $this->checkAppForGroups($enabled, $group);
166
+        });
167
+        return array_keys($appsForGroups);
168
+    }
169
+
170
+    /**
171
+     * @param string $appId
172
+     * @return array
173
+     */
174
+    public function getAppRestriction(string $appId): array {
175
+        $values = $this->getInstalledAppsValues();
176
+
177
+        if (!isset($values[$appId])) {
178
+            return [];
179
+        }
180
+
181
+        if ($values[$appId] === 'yes' || $values[$appId] === 'no') {
182
+            return [];
183
+        }
184
+        return json_decode($values[$appId]);
185
+    }
186
+
187
+
188
+    /**
189
+     * Check if an app is enabled for user
190
+     *
191
+     * @param string $appId
192
+     * @param \OCP\IUser $user (optional) if not defined, the currently logged in user will be used
193
+     * @return bool
194
+     */
195
+    public function isEnabledForUser($appId, $user = null) {
196
+        if ($this->isAlwaysEnabled($appId)) {
197
+            return true;
198
+        }
199
+        if ($user === null) {
200
+            $user = $this->userSession->getUser();
201
+        }
202
+        $installedApps = $this->getInstalledAppsValues();
203
+        if (isset($installedApps[$appId])) {
204
+            return $this->checkAppForUser($installedApps[$appId], $user);
205
+        } else {
206
+            return false;
207
+        }
208
+    }
209
+
210
+    /**
211
+     * @param string $enabled
212
+     * @param IUser $user
213
+     * @return bool
214
+     */
215
+    private function checkAppForUser($enabled, $user) {
216
+        if ($enabled === 'yes') {
217
+            return true;
218
+        } elseif ($user === null) {
219
+            return false;
220
+        } else {
221
+            if(empty($enabled)){
222
+                return false;
223
+            }
224
+
225
+            $groupIds = json_decode($enabled);
226
+
227
+            if (!is_array($groupIds)) {
228
+                $jsonError = json_last_error();
229
+                $this->logger->warning('AppManger::checkAppForUser - can\'t decode group IDs: ' . print_r($enabled, true) . ' - json error code: ' . $jsonError, ['app' => 'lib']);
230
+                return false;
231
+            }
232
+
233
+            $userGroups = $this->groupManager->getUserGroupIds($user);
234
+            foreach ($userGroups as $groupId) {
235
+                if (in_array($groupId, $groupIds, true)) {
236
+                    return true;
237
+                }
238
+            }
239
+            return false;
240
+        }
241
+    }
242
+
243
+    /**
244
+     * @param string $enabled
245
+     * @param IGroup $group
246
+     * @return bool
247
+     */
248
+    private function checkAppForGroups(string $enabled, IGroup $group): bool {
249
+        if ($enabled === 'yes') {
250
+            return true;
251
+        } elseif ($group === null) {
252
+            return false;
253
+        } else {
254
+            if (empty($enabled)) {
255
+                return false;
256
+            }
257
+
258
+            $groupIds = json_decode($enabled);
259
+
260
+            if (!is_array($groupIds)) {
261
+                $jsonError = json_last_error();
262
+                $this->logger->warning('AppManger::checkAppForUser - can\'t decode group IDs: ' . print_r($enabled, true) . ' - json error code: ' . $jsonError, ['app' => 'lib']);
263
+                return false;
264
+            }
265
+
266
+            return in_array($group->getGID(), $groupIds);
267
+        }
268
+    }
269
+
270
+    /**
271
+     * Check if an app is enabled in the instance
272
+     *
273
+     * Notice: This actually checks if the app is enabled and not only if it is installed.
274
+     *
275
+     * @param string $appId
276
+     * @param \OCP\IGroup[]|String[] $groups
277
+     * @return bool
278
+     */
279
+    public function isInstalled($appId) {
280
+        $installedApps = $this->getInstalledAppsValues();
281
+        return isset($installedApps[$appId]);
282
+    }
283
+
284
+    /**
285
+     * Enable an app for every user
286
+     *
287
+     * @param string $appId
288
+     * @throws AppPathNotFoundException
289
+     */
290
+    public function enableApp($appId) {
291
+        // Check if app exists
292
+        $this->getAppPath($appId);
293
+
294
+        $this->installedAppsCache[$appId] = 'yes';
295
+        $this->appConfig->setValue($appId, 'enabled', 'yes');
296
+        $this->dispatcher->dispatch(ManagerEvent::EVENT_APP_ENABLE, new ManagerEvent(
297
+            ManagerEvent::EVENT_APP_ENABLE, $appId
298
+        ));
299
+        $this->clearAppsCache();
300
+    }
301
+
302
+    /**
303
+     * Whether a list of types contains a protected app type
304
+     *
305
+     * @param string[] $types
306
+     * @return bool
307
+     */
308
+    public function hasProtectedAppType($types) {
309
+        if (empty($types)) {
310
+            return false;
311
+        }
312
+
313
+        $protectedTypes = array_intersect($this->protectedAppTypes, $types);
314
+        return !empty($protectedTypes);
315
+    }
316
+
317
+    /**
318
+     * Enable an app only for specific groups
319
+     *
320
+     * @param string $appId
321
+     * @param \OCP\IGroup[] $groups
322
+     * @throws \InvalidArgumentException if app can't be enabled for groups
323
+     * @throws AppPathNotFoundException
324
+     */
325
+    public function enableAppForGroups($appId, $groups) {
326
+        // Check if app exists
327
+        $this->getAppPath($appId);
328
+
329
+        $info = $this->getAppInfo($appId);
330
+        if (!empty($info['types']) && $this->hasProtectedAppType($info['types'])) {
331
+            throw new \InvalidArgumentException("$appId can't be enabled for groups.");
332
+        }
333
+
334
+        $groupIds = array_map(function ($group) {
335
+            /** @var \OCP\IGroup $group */
336
+            return ($group instanceof IGroup)
337
+                ? $group->getGID()
338
+                : $group;
339
+        }, $groups);
340
+
341
+        $this->installedAppsCache[$appId] = json_encode($groupIds);
342
+        $this->appConfig->setValue($appId, 'enabled', json_encode($groupIds));
343
+        $this->dispatcher->dispatch(ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, new ManagerEvent(
344
+            ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, $appId, $groups
345
+        ));
346
+        $this->clearAppsCache();
347
+
348
+    }
349
+
350
+    /**
351
+     * Disable an app for every user
352
+     *
353
+     * @param string $appId
354
+     * @throws \Exception if app can't be disabled
355
+     */
356
+    public function disableApp($appId) {
357
+        if ($this->isAlwaysEnabled($appId)) {
358
+            throw new \Exception("$appId can't be disabled.");
359
+        }
360
+        unset($this->installedAppsCache[$appId]);
361
+        $this->appConfig->setValue($appId, 'enabled', 'no');
362
+
363
+        // run uninstall steps
364
+        $appData = $this->getAppInfo($appId);
365
+        if (!is_null($appData)) {
366
+            \OC_App::executeRepairSteps($appId, $appData['repair-steps']['uninstall']);
367
+        }
368
+
369
+        $this->dispatcher->dispatch(ManagerEvent::EVENT_APP_DISABLE, new ManagerEvent(
370
+            ManagerEvent::EVENT_APP_DISABLE, $appId
371
+        ));
372
+        $this->clearAppsCache();
373
+    }
374
+
375
+    /**
376
+     * Get the directory for the given app.
377
+     *
378
+     * @param string $appId
379
+     * @return string
380
+     * @throws AppPathNotFoundException if app folder can't be found
381
+     */
382
+    public function getAppPath($appId) {
383
+        $appPath = \OC_App::getAppPath($appId);
384
+        if($appPath === false) {
385
+            throw new AppPathNotFoundException('Could not find path for ' . $appId);
386
+        }
387
+        return $appPath;
388
+    }
389
+
390
+    /**
391
+     * Clear the cached list of apps when enabling/disabling an app
392
+     */
393
+    public function clearAppsCache() {
394
+        $settingsMemCache = $this->memCacheFactory->createDistributed('settings');
395
+        $settingsMemCache->clear('listApps');
396
+        $this->appInfos = [];
397
+    }
398
+
399
+    /**
400
+     * Returns a list of apps that need upgrade
401
+     *
402
+     * @param string $version Nextcloud version as array of version components
403
+     * @return array list of app info from apps that need an upgrade
404
+     *
405
+     * @internal
406
+     */
407
+    public function getAppsNeedingUpgrade($version) {
408
+        $appsToUpgrade = [];
409
+        $apps = $this->getInstalledApps();
410
+        foreach ($apps as $appId) {
411
+            $appInfo = $this->getAppInfo($appId);
412
+            $appDbVersion = $this->appConfig->getValue($appId, 'installed_version');
413
+            if ($appDbVersion
414
+                && isset($appInfo['version'])
415
+                && version_compare($appInfo['version'], $appDbVersion, '>')
416
+                && \OC_App::isAppCompatible($version, $appInfo)
417
+            ) {
418
+                $appsToUpgrade[] = $appInfo;
419
+            }
420
+        }
421
+
422
+        return $appsToUpgrade;
423
+    }
424
+
425
+    /**
426
+     * Returns the app information from "appinfo/info.xml".
427
+     *
428
+     * @param string $appId app id
429
+     *
430
+     * @param bool $path
431
+     * @param null $lang
432
+     * @return array|null app info
433
+     */
434
+    public function getAppInfo(string $appId, bool $path = false, $lang = null) {
435
+        if ($path) {
436
+            $file = $appId;
437
+        } else {
438
+            if ($lang === null && isset($this->appInfos[$appId])) {
439
+                return $this->appInfos[$appId];
440
+            }
441
+            try {
442
+                $appPath = $this->getAppPath($appId);
443
+            } catch (AppPathNotFoundException $e) {
444
+                return null;
445
+            }
446
+            $file = $appPath . '/appinfo/info.xml';
447
+        }
448
+
449
+        $parser = new InfoParser($this->memCacheFactory->createLocal('core.appinfo'));
450
+        $data = $parser->parse($file);
451
+
452
+        if (is_array($data)) {
453
+            $data = \OC_App::parseAppInfo($data, $lang);
454
+        }
455
+
456
+        if ($lang === null) {
457
+            $this->appInfos[$appId] = $data;
458
+        }
459
+
460
+        return $data;
461
+    }
462
+
463
+    public function getAppVersion(string $appId, bool $useCache = true): string {
464
+        if(!$useCache || !isset($this->appVersions[$appId])) {
465
+            $appInfo = $this->getAppInfo($appId);
466
+            $this->appVersions[$appId] = ($appInfo !== null && isset($appInfo['version'])) ? $appInfo['version'] : '0';
467
+        }
468
+        return $this->appVersions[$appId];
469
+    }
470
+
471
+    /**
472
+     * Returns a list of apps incompatible with the given version
473
+     *
474
+     * @param string $version Nextcloud version as array of version components
475
+     *
476
+     * @return array list of app info from incompatible apps
477
+     *
478
+     * @internal
479
+     */
480
+    public function getIncompatibleApps(string $version): array {
481
+        $apps = $this->getInstalledApps();
482
+        $incompatibleApps = array();
483
+        foreach ($apps as $appId) {
484
+            $info = $this->getAppInfo($appId);
485
+            if ($info === null) {
486
+                $incompatibleApps[] = ['id' => $appId];
487
+            } else if (!\OC_App::isAppCompatible($version, $info)) {
488
+                $incompatibleApps[] = $info;
489
+            }
490
+        }
491
+        return $incompatibleApps;
492
+    }
493
+
494
+    /**
495
+     * @inheritdoc
496
+     * In case you change this method, also change \OC\App\CodeChecker\InfoChecker::isShipped()
497
+     */
498
+    public function isShipped($appId) {
499
+        $this->loadShippedJson();
500
+        return in_array($appId, $this->shippedApps, true);
501
+    }
502
+
503
+    private function isAlwaysEnabled($appId) {
504
+        $alwaysEnabled = $this->getAlwaysEnabledApps();
505
+        return in_array($appId, $alwaysEnabled, true);
506
+    }
507
+
508
+    /**
509
+     * In case you change this method, also change \OC\App\CodeChecker\InfoChecker::loadShippedJson()
510
+     * @throws \Exception
511
+     */
512
+    private function loadShippedJson() {
513
+        if ($this->shippedApps === null) {
514
+            $shippedJson = \OC::$SERVERROOT . '/core/shipped.json';
515
+            if (!file_exists($shippedJson)) {
516
+                throw new \Exception("File not found: $shippedJson");
517
+            }
518
+            $content = json_decode(file_get_contents($shippedJson), true);
519
+            $this->shippedApps = $content['shippedApps'];
520
+            $this->alwaysEnabled = $content['alwaysEnabled'];
521
+        }
522
+    }
523
+
524
+    /**
525
+     * @inheritdoc
526
+     */
527
+    public function getAlwaysEnabledApps() {
528
+        $this->loadShippedJson();
529
+        return $this->alwaysEnabled;
530
+    }
531 531
 }
Please login to merge, or discard this patch.
Spacing   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -120,11 +120,11 @@  discard block
 block discarded – undo
120 120
 			$values = $this->appConfig->getValues(false, 'enabled');
121 121
 
122 122
 			$alwaysEnabledApps = $this->getAlwaysEnabledApps();
123
-			foreach($alwaysEnabledApps as $appId) {
123
+			foreach ($alwaysEnabledApps as $appId) {
124 124
 				$values[$appId] = 'yes';
125 125
 			}
126 126
 
127
-			$this->installedAppsCache = array_filter($values, function ($value) {
127
+			$this->installedAppsCache = array_filter($values, function($value) {
128 128
 				return $value !== 'no';
129 129
 			});
130 130
 			ksort($this->installedAppsCache);
@@ -149,7 +149,7 @@  discard block
 block discarded – undo
149 149
 	 */
150 150
 	public function getEnabledAppsForUser(IUser $user) {
151 151
 		$apps = $this->getInstalledAppsValues();
152
-		$appsForUser = array_filter($apps, function ($enabled) use ($user) {
152
+		$appsForUser = array_filter($apps, function($enabled) use ($user) {
153 153
 			return $this->checkAppForUser($enabled, $user);
154 154
 		});
155 155
 		return array_keys($appsForUser);
@@ -161,7 +161,7 @@  discard block
 block discarded – undo
161 161
 	 */
162 162
 	public function getEnabledAppsForGroup(IGroup $group): array {
163 163
 		$apps = $this->getInstalledAppsValues();
164
-		$appsForGroups = array_filter($apps, function ($enabled) use ($group) {
164
+		$appsForGroups = array_filter($apps, function($enabled) use ($group) {
165 165
 			return $this->checkAppForGroups($enabled, $group);
166 166
 		});
167 167
 		return array_keys($appsForGroups);
@@ -218,7 +218,7 @@  discard block
 block discarded – undo
218 218
 		} elseif ($user === null) {
219 219
 			return false;
220 220
 		} else {
221
-			if(empty($enabled)){
221
+			if (empty($enabled)) {
222 222
 				return false;
223 223
 			}
224 224
 
@@ -226,7 +226,7 @@  discard block
 block discarded – undo
226 226
 
227 227
 			if (!is_array($groupIds)) {
228 228
 				$jsonError = json_last_error();
229
-				$this->logger->warning('AppManger::checkAppForUser - can\'t decode group IDs: ' . print_r($enabled, true) . ' - json error code: ' . $jsonError, ['app' => 'lib']);
229
+				$this->logger->warning('AppManger::checkAppForUser - can\'t decode group IDs: '.print_r($enabled, true).' - json error code: '.$jsonError, ['app' => 'lib']);
230 230
 				return false;
231 231
 			}
232 232
 
@@ -259,7 +259,7 @@  discard block
 block discarded – undo
259 259
 
260 260
 			if (!is_array($groupIds)) {
261 261
 				$jsonError = json_last_error();
262
-				$this->logger->warning('AppManger::checkAppForUser - can\'t decode group IDs: ' . print_r($enabled, true) . ' - json error code: ' . $jsonError, ['app' => 'lib']);
262
+				$this->logger->warning('AppManger::checkAppForUser - can\'t decode group IDs: '.print_r($enabled, true).' - json error code: '.$jsonError, ['app' => 'lib']);
263 263
 				return false;
264 264
 			}
265 265
 
@@ -331,7 +331,7 @@  discard block
 block discarded – undo
331 331
 			throw new \InvalidArgumentException("$appId can't be enabled for groups.");
332 332
 		}
333 333
 
334
-		$groupIds = array_map(function ($group) {
334
+		$groupIds = array_map(function($group) {
335 335
 			/** @var \OCP\IGroup $group */
336 336
 			return ($group instanceof IGroup)
337 337
 				? $group->getGID()
@@ -381,8 +381,8 @@  discard block
 block discarded – undo
381 381
 	 */
382 382
 	public function getAppPath($appId) {
383 383
 		$appPath = \OC_App::getAppPath($appId);
384
-		if($appPath === false) {
385
-			throw new AppPathNotFoundException('Could not find path for ' . $appId);
384
+		if ($appPath === false) {
385
+			throw new AppPathNotFoundException('Could not find path for '.$appId);
386 386
 		}
387 387
 		return $appPath;
388 388
 	}
@@ -443,7 +443,7 @@  discard block
 block discarded – undo
443 443
 			} catch (AppPathNotFoundException $e) {
444 444
 				return null;
445 445
 			}
446
-			$file = $appPath . '/appinfo/info.xml';
446
+			$file = $appPath.'/appinfo/info.xml';
447 447
 		}
448 448
 
449 449
 		$parser = new InfoParser($this->memCacheFactory->createLocal('core.appinfo'));
@@ -461,7 +461,7 @@  discard block
 block discarded – undo
461 461
 	}
462 462
 
463 463
 	public function getAppVersion(string $appId, bool $useCache = true): string {
464
-		if(!$useCache || !isset($this->appVersions[$appId])) {
464
+		if (!$useCache || !isset($this->appVersions[$appId])) {
465 465
 			$appInfo = $this->getAppInfo($appId);
466 466
 			$this->appVersions[$appId] = ($appInfo !== null && isset($appInfo['version'])) ? $appInfo['version'] : '0';
467 467
 		}
@@ -511,7 +511,7 @@  discard block
 block discarded – undo
511 511
 	 */
512 512
 	private function loadShippedJson() {
513 513
 		if ($this->shippedApps === null) {
514
-			$shippedJson = \OC::$SERVERROOT . '/core/shipped.json';
514
+			$shippedJson = \OC::$SERVERROOT.'/core/shipped.json';
515 515
 			if (!file_exists($shippedJson)) {
516 516
 				throw new \Exception("File not found: $shippedJson");
517 517
 			}
Please login to merge, or discard this patch.