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