1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @copyright Copyright (c) 2016, ownCloud, Inc. |
4
|
|
|
* |
5
|
|
|
* @author Arthur Schiwon <[email protected]> |
6
|
|
|
* @author Bjoern Schiessle <[email protected]> |
7
|
|
|
* @author Christoph Schaefer "christophł@wolkesicher.de" |
8
|
|
|
* @author Christoph Wurst <[email protected]> |
9
|
|
|
* @author Joas Schilling <[email protected]> |
10
|
|
|
* @author Julius Haertl <[email protected]> |
11
|
|
|
* @author Lukas Reschke <[email protected]> |
12
|
|
|
* @author Morris Jobke <[email protected]> |
13
|
|
|
* @author Robin Appelman <[email protected]> |
14
|
|
|
* @author Thomas Müller <[email protected]> |
15
|
|
|
* @author Vincent Petry <[email protected]> |
16
|
|
|
* |
17
|
|
|
* @license AGPL-3.0 |
18
|
|
|
* |
19
|
|
|
* This code is free software: you can redistribute it and/or modify |
20
|
|
|
* it under the terms of the GNU Affero General Public License, version 3, |
21
|
|
|
* as published by the Free Software Foundation. |
22
|
|
|
* |
23
|
|
|
* This program is distributed in the hope that it will be useful, |
24
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
25
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
26
|
|
|
* GNU Affero General Public License for more details. |
27
|
|
|
* |
28
|
|
|
* You should have received a copy of the GNU Affero General Public License, version 3, |
29
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/> |
30
|
|
|
* |
31
|
|
|
*/ |
32
|
|
|
|
33
|
|
|
namespace OC\App; |
34
|
|
|
|
35
|
|
|
use OC\AppConfig; |
36
|
|
|
use OCP\App\AppPathNotFoundException; |
37
|
|
|
use OCP\App\IAppManager; |
38
|
|
|
use OCP\App\ManagerEvent; |
39
|
|
|
use OCP\ICacheFactory; |
40
|
|
|
use OCP\IGroup; |
41
|
|
|
use OCP\IGroupManager; |
42
|
|
|
use OCP\ILogger; |
43
|
|
|
use OCP\IUser; |
44
|
|
|
use OCP\IUserSession; |
45
|
|
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
46
|
|
|
|
47
|
|
|
class AppManager implements IAppManager { |
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 = \OC::$server->getAppManager()->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
|
|
|
} |
532
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.