Passed
Push — master ( 3fa63f...61a02b )
by Roeland
10:49 queued 11s
created
apps/settings/lib/Controller/AppSettingsController.php 1 patch
Indentation   +502 added lines, -502 removed lines patch added patch discarded remove patch
@@ -54,507 +54,507 @@
 block discarded – undo
54 54
 
55 55
 class AppSettingsController extends Controller {
56 56
 
57
-	/** @var \OCP\IL10N */
58
-	private $l10n;
59
-	/** @var IConfig */
60
-	private $config;
61
-	/** @var INavigationManager */
62
-	private $navigationManager;
63
-	/** @var IAppManager */
64
-	private $appManager;
65
-	/** @var CategoryFetcher */
66
-	private $categoryFetcher;
67
-	/** @var AppFetcher */
68
-	private $appFetcher;
69
-	/** @var IFactory */
70
-	private $l10nFactory;
71
-	/** @var BundleFetcher */
72
-	private $bundleFetcher;
73
-	/** @var Installer */
74
-	private $installer;
75
-	/** @var IURLGenerator */
76
-	private $urlGenerator;
77
-	/** @var ILogger */
78
-	private $logger;
79
-
80
-	/** @var array */
81
-	private $allApps = [];
82
-
83
-	/**
84
-	 * @param string $appName
85
-	 * @param IRequest $request
86
-	 * @param IL10N $l10n
87
-	 * @param IConfig $config
88
-	 * @param INavigationManager $navigationManager
89
-	 * @param IAppManager $appManager
90
-	 * @param CategoryFetcher $categoryFetcher
91
-	 * @param AppFetcher $appFetcher
92
-	 * @param IFactory $l10nFactory
93
-	 * @param BundleFetcher $bundleFetcher
94
-	 * @param Installer $installer
95
-	 * @param IURLGenerator $urlGenerator
96
-	 * @param ILogger $logger
97
-	 */
98
-	public function __construct(string $appName,
99
-								IRequest $request,
100
-								IL10N $l10n,
101
-								IConfig $config,
102
-								INavigationManager $navigationManager,
103
-								IAppManager $appManager,
104
-								CategoryFetcher $categoryFetcher,
105
-								AppFetcher $appFetcher,
106
-								IFactory $l10nFactory,
107
-								BundleFetcher $bundleFetcher,
108
-								Installer $installer,
109
-								IURLGenerator $urlGenerator,
110
-								ILogger $logger) {
111
-		parent::__construct($appName, $request);
112
-		$this->l10n = $l10n;
113
-		$this->config = $config;
114
-		$this->navigationManager = $navigationManager;
115
-		$this->appManager = $appManager;
116
-		$this->categoryFetcher = $categoryFetcher;
117
-		$this->appFetcher = $appFetcher;
118
-		$this->l10nFactory = $l10nFactory;
119
-		$this->bundleFetcher = $bundleFetcher;
120
-		$this->installer = $installer;
121
-		$this->urlGenerator = $urlGenerator;
122
-		$this->logger = $logger;
123
-	}
124
-
125
-	/**
126
-	 * @NoCSRFRequired
127
-	 *
128
-	 * @return TemplateResponse
129
-	 */
130
-	public function viewApps(): TemplateResponse {
131
-		\OC_Util::addScript('settings', 'apps');
132
-		$params = [];
133
-		$params['appstoreEnabled'] = $this->config->getSystemValue('appstoreenabled', true) === true;
134
-		$params['updateCount'] = count($this->getAppsWithUpdates());
135
-		$params['developerDocumentation'] = $this->urlGenerator->linkToDocs('developer-manual');
136
-		$params['bundles'] = $this->getBundles();
137
-		$this->navigationManager->setActiveEntry('core_apps');
138
-
139
-		$templateResponse = new TemplateResponse('settings', 'settings-vue', ['serverData' => $params]);
140
-		$policy = new ContentSecurityPolicy();
141
-		$policy->addAllowedImageDomain('https://usercontent.apps.nextcloud.com');
142
-		$templateResponse->setContentSecurityPolicy($policy);
143
-
144
-		return $templateResponse;
145
-	}
146
-
147
-	private function getAppsWithUpdates() {
148
-		$appClass = new \OC_App();
149
-		$apps = $appClass->listAllApps();
150
-		foreach($apps as $key => $app) {
151
-			$newVersion = $this->installer->isUpdateAvailable($app['id']);
152
-			if($newVersion === false) {
153
-				unset($apps[$key]);
154
-			}
155
-		}
156
-		return $apps;
157
-	}
158
-
159
-	private function getBundles() {
160
-		$result = [];
161
-		$bundles = $this->bundleFetcher->getBundles();
162
-		foreach ($bundles as $bundle) {
163
-			$result[] = [
164
-				'name' => $bundle->getName(),
165
-				'id' => $bundle->getIdentifier(),
166
-				'appIdentifiers' => $bundle->getAppIdentifiers()
167
-			];
168
-		}
169
-		return $result;
170
-
171
-	}
172
-
173
-	/**
174
-	 * Get all available categories
175
-	 *
176
-	 * @return JSONResponse
177
-	 */
178
-	public function listCategories(): JSONResponse {
179
-		return new JSONResponse($this->getAllCategories());
180
-	}
181
-
182
-	private function getAllCategories() {
183
-		$currentLanguage = substr($this->l10nFactory->findLanguage(), 0, 2);
184
-
185
-		$formattedCategories = [];
186
-		$categories = $this->categoryFetcher->get();
187
-		foreach($categories as $category) {
188
-			$formattedCategories[] = [
189
-				'id' => $category['id'],
190
-				'ident' => $category['id'],
191
-				'displayName' => isset($category['translations'][$currentLanguage]['name']) ? $category['translations'][$currentLanguage]['name'] : $category['translations']['en']['name'],
192
-			];
193
-		}
194
-
195
-		return $formattedCategories;
196
-	}
197
-
198
-	private function fetchApps() {
199
-		$appClass = new \OC_App();
200
-		$apps = $appClass->listAllApps();
201
-		foreach ($apps as $app) {
202
-			$app['installed'] = true;
203
-			$this->allApps[$app['id']] = $app;
204
-		}
205
-
206
-		$apps = $this->getAppsForCategory('');
207
-		foreach ($apps as $app) {
208
-			$app['appstore'] = true;
209
-			if (!array_key_exists($app['id'], $this->allApps)) {
210
-				$this->allApps[$app['id']] = $app;
211
-			} else {
212
-				$this->allApps[$app['id']] = array_merge($app, $this->allApps[$app['id']]);
213
-			}
214
-		}
215
-
216
-		// add bundle information
217
-		$bundles = $this->bundleFetcher->getBundles();
218
-		foreach($bundles as $bundle) {
219
-			foreach($bundle->getAppIdentifiers() as $identifier) {
220
-				foreach($this->allApps as &$app) {
221
-					if($app['id'] === $identifier) {
222
-						$app['bundleIds'][] = $bundle->getIdentifier();
223
-						continue;
224
-					}
225
-				}
226
-			}
227
-		}
228
-	}
229
-
230
-	private function getAllApps() {
231
-		return $this->allApps;
232
-	}
233
-	/**
234
-	 * Get all available apps in a category
235
-	 *
236
-	 * @param string $category
237
-	 * @return JSONResponse
238
-	 * @throws \Exception
239
-	 */
240
-	public function listApps(): JSONResponse {
241
-
242
-		$this->fetchApps();
243
-		$apps = $this->getAllApps();
244
-
245
-		$dependencyAnalyzer = new DependencyAnalyzer(new Platform($this->config), $this->l10n);
246
-
247
-		// Extend existing app details
248
-		$apps = array_map(function($appData) use ($dependencyAnalyzer) {
249
-			if (isset($appData['appstoreData'])) {
250
-				$appstoreData = $appData['appstoreData'];
251
-				$appData['screenshot'] = isset($appstoreData['screenshots'][0]['url']) ? 'https://usercontent.apps.nextcloud.com/' . base64_encode($appstoreData['screenshots'][0]['url']) : '';
252
-				$appData['category'] = $appstoreData['categories'];
253
-			}
254
-
255
-			$newVersion = $this->installer->isUpdateAvailable($appData['id']);
256
-			if($newVersion) {
257
-				$appData['update'] = $newVersion;
258
-			}
259
-
260
-			// fix groups to be an array
261
-			$groups = array();
262
-			if (is_string($appData['groups'])) {
263
-				$groups = json_decode($appData['groups']);
264
-			}
265
-			$appData['groups'] = $groups;
266
-			$appData['canUnInstall'] = !$appData['active'] && $appData['removable'];
267
-
268
-			// fix licence vs license
269
-			if (isset($appData['license']) && !isset($appData['licence'])) {
270
-				$appData['licence'] = $appData['license'];
271
-			}
272
-
273
-			$ignoreMaxApps = $this->config->getSystemValue('app_install_overwrite', []);
274
-			if (!is_array($ignoreMaxApps)) {
275
-				$this->logger->warning('The value given for app_install_overwrite is not an array. Ignoring...');
276
-				$ignoreMaxApps = [];
277
-			}
278
-			$ignoreMax = in_array($appData['id'], $ignoreMaxApps);
279
-
280
-			// analyse dependencies
281
-			$missing = $dependencyAnalyzer->analyze($appData, $ignoreMax);
282
-			$appData['canInstall'] = empty($missing);
283
-			$appData['missingDependencies'] = $missing;
284
-
285
-			$appData['missingMinOwnCloudVersion'] = !isset($appData['dependencies']['nextcloud']['@attributes']['min-version']);
286
-			$appData['missingMaxOwnCloudVersion'] = !isset($appData['dependencies']['nextcloud']['@attributes']['max-version']);
287
-			$appData['isCompatible'] = $dependencyAnalyzer->isMarkedCompatible($appData);
288
-
289
-			return $appData;
290
-		}, $apps);
291
-
292
-		usort($apps, [$this, 'sortApps']);
293
-
294
-		return new JSONResponse(['apps' => $apps, 'status' => 'success']);
295
-	}
296
-
297
-	/**
298
-	 * Get all apps for a category from the app store
299
-	 *
300
-	 * @param string $requestedCategory
301
-	 * @return array
302
-	 * @throws \Exception
303
-	 */
304
-	private function getAppsForCategory($requestedCategory = ''): array {
305
-		$versionParser = new VersionParser();
306
-		$formattedApps = [];
307
-		$apps = $this->appFetcher->get();
308
-		foreach($apps as $app) {
309
-			// Skip all apps not in the requested category
310
-			if ($requestedCategory !== '') {
311
-				$isInCategory = false;
312
-				foreach($app['categories'] as $category) {
313
-					if($category === $requestedCategory) {
314
-						$isInCategory = true;
315
-					}
316
-				}
317
-				if(!$isInCategory) {
318
-					continue;
319
-				}
320
-			}
321
-
322
-			if (!isset($app['releases'][0]['rawPlatformVersionSpec'])) {
323
-				continue;
324
-			}
325
-			$nextCloudVersion = $versionParser->getVersion($app['releases'][0]['rawPlatformVersionSpec']);
326
-			$nextCloudVersionDependencies = [];
327
-			if($nextCloudVersion->getMinimumVersion() !== '') {
328
-				$nextCloudVersionDependencies['nextcloud']['@attributes']['min-version'] = $nextCloudVersion->getMinimumVersion();
329
-			}
330
-			if($nextCloudVersion->getMaximumVersion() !== '') {
331
-				$nextCloudVersionDependencies['nextcloud']['@attributes']['max-version'] = $nextCloudVersion->getMaximumVersion();
332
-			}
333
-			$phpVersion = $versionParser->getVersion($app['releases'][0]['rawPhpVersionSpec']);
334
-			$existsLocally = \OC_App::getAppPath($app['id']) !== false;
335
-			$phpDependencies = [];
336
-			if($phpVersion->getMinimumVersion() !== '') {
337
-				$phpDependencies['php']['@attributes']['min-version'] = $phpVersion->getMinimumVersion();
338
-			}
339
-			if($phpVersion->getMaximumVersion() !== '') {
340
-				$phpDependencies['php']['@attributes']['max-version'] = $phpVersion->getMaximumVersion();
341
-			}
342
-			if(isset($app['releases'][0]['minIntSize'])) {
343
-				$phpDependencies['php']['@attributes']['min-int-size'] = $app['releases'][0]['minIntSize'];
344
-			}
345
-			$authors = '';
346
-			foreach($app['authors'] as $key => $author) {
347
-				$authors .= $author['name'];
348
-				if($key !== count($app['authors']) - 1) {
349
-					$authors .= ', ';
350
-				}
351
-			}
352
-
353
-			$currentLanguage = substr(\OC::$server->getL10NFactory()->findLanguage(), 0, 2);
354
-			$enabledValue = $this->config->getAppValue($app['id'], 'enabled', 'no');
355
-			$groups = null;
356
-			if($enabledValue !== 'no' && $enabledValue !== 'yes') {
357
-				$groups = $enabledValue;
358
-			}
359
-
360
-			$currentVersion = '';
361
-			if($this->appManager->isInstalled($app['id'])) {
362
-				$currentVersion = $this->appManager->getAppVersion($app['id']);
363
-			} else {
364
-				$currentLanguage = $app['releases'][0]['version'];
365
-			}
366
-
367
-			$formattedApps[] = [
368
-				'id' => $app['id'],
369
-				'name' => isset($app['translations'][$currentLanguage]['name']) ? $app['translations'][$currentLanguage]['name'] : $app['translations']['en']['name'],
370
-				'description' => isset($app['translations'][$currentLanguage]['description']) ? $app['translations'][$currentLanguage]['description'] : $app['translations']['en']['description'],
371
-				'summary' => isset($app['translations'][$currentLanguage]['summary']) ? $app['translations'][$currentLanguage]['summary'] : $app['translations']['en']['summary'],
372
-				'license' => $app['releases'][0]['licenses'],
373
-				'author' => $authors,
374
-				'shipped' => false,
375
-				'version' => $currentVersion,
376
-				'default_enable' => '',
377
-				'types' => [],
378
-				'documentation' => [
379
-					'admin' => $app['adminDocs'],
380
-					'user' => $app['userDocs'],
381
-					'developer' => $app['developerDocs']
382
-				],
383
-				'website' => $app['website'],
384
-				'bugs' => $app['issueTracker'],
385
-				'detailpage' => $app['website'],
386
-				'dependencies' => array_merge(
387
-					$nextCloudVersionDependencies,
388
-					$phpDependencies
389
-				),
390
-				'level' => ($app['isFeatured'] === true) ? 200 : 100,
391
-				'missingMaxOwnCloudVersion' => false,
392
-				'missingMinOwnCloudVersion' => false,
393
-				'canInstall' => true,
394
-				'screenshot' => isset($app['screenshots'][0]['url']) ? 'https://usercontent.apps.nextcloud.com/'.base64_encode($app['screenshots'][0]['url']) : '',
395
-				'score' => $app['ratingOverall'],
396
-				'ratingNumOverall' => $app['ratingNumOverall'],
397
-				'ratingNumThresholdReached' => $app['ratingNumOverall'] > 5,
398
-				'removable' => $existsLocally,
399
-				'active' => $this->appManager->isEnabledForUser($app['id']),
400
-				'needsDownload' => !$existsLocally,
401
-				'groups' => $groups,
402
-				'fromAppStore' => true,
403
-				'appstoreData' => $app,
404
-			];
405
-		}
406
-
407
-		return $formattedApps;
408
-	}
409
-
410
-	/**
411
-	 * @PasswordConfirmationRequired
412
-	 *
413
-	 * @param string $appId
414
-	 * @param array $groups
415
-	 * @return JSONResponse
416
-	 */
417
-	public function enableApp(string $appId, array $groups = []): JSONResponse {
418
-		return $this->enableApps([$appId], $groups);
419
-	}
420
-
421
-	/**
422
-	 * Enable one or more apps
423
-	 *
424
-	 * apps will be enabled for specific groups only if $groups is defined
425
-	 *
426
-	 * @PasswordConfirmationRequired
427
-	 * @param array $appIds
428
-	 * @param array $groups
429
-	 * @return JSONResponse
430
-	 */
431
-	public function enableApps(array $appIds, array $groups = []): JSONResponse {
432
-		try {
433
-			$updateRequired = false;
434
-
435
-			foreach ($appIds as $appId) {
436
-				$appId = OC_App::cleanAppId($appId);
437
-
438
-				// Check if app is already downloaded
439
-				/** @var Installer $installer */
440
-				$installer = \OC::$server->query(Installer::class);
441
-				$isDownloaded = $installer->isDownloaded($appId);
442
-
443
-				if(!$isDownloaded) {
444
-					$installer->downloadApp($appId);
445
-				}
446
-
447
-				$installer->installApp($appId);
448
-
449
-				if (count($groups) > 0) {
450
-					$this->appManager->enableAppForGroups($appId, $this->getGroupList($groups));
451
-				} else {
452
-					$this->appManager->enableApp($appId);
453
-				}
454
-				if (\OC_App::shouldUpgrade($appId)) {
455
-					$updateRequired = true;
456
-				}
457
-			}
458
-			return new JSONResponse(['data' => ['update_required' => $updateRequired]]);
459
-
460
-		} catch (\Exception $e) {
461
-			$this->logger->logException($e);
462
-			return new JSONResponse(['data' => ['message' => $e->getMessage()]], Http::STATUS_INTERNAL_SERVER_ERROR);
463
-		}
464
-	}
465
-
466
-	private function getGroupList(array $groups) {
467
-		$groupManager = \OC::$server->getGroupManager();
468
-		$groupsList = [];
469
-		foreach ($groups as $group) {
470
-			$groupItem = $groupManager->get($group);
471
-			if ($groupItem instanceof \OCP\IGroup) {
472
-				$groupsList[] = $groupManager->get($group);
473
-			}
474
-		}
475
-		return $groupsList;
476
-	}
477
-
478
-	/**
479
-	 * @PasswordConfirmationRequired
480
-	 *
481
-	 * @param string $appId
482
-	 * @return JSONResponse
483
-	 */
484
-	public function disableApp(string $appId): JSONResponse {
485
-		return $this->disableApps([$appId]);
486
-	}
487
-
488
-	/**
489
-	 * @PasswordConfirmationRequired
490
-	 *
491
-	 * @param array $appIds
492
-	 * @return JSONResponse
493
-	 */
494
-	public function disableApps(array $appIds): JSONResponse {
495
-		try {
496
-			foreach ($appIds as $appId) {
497
-				$appId = OC_App::cleanAppId($appId);
498
-				$this->appManager->disableApp($appId);
499
-			}
500
-			return new JSONResponse([]);
501
-		} catch (\Exception $e) {
502
-			$this->logger->logException($e);
503
-			return new JSONResponse(['data' => ['message' => $e->getMessage()]], Http::STATUS_INTERNAL_SERVER_ERROR);
504
-		}
505
-	}
506
-
507
-	/**
508
-	 * @PasswordConfirmationRequired
509
-	 *
510
-	 * @param string $appId
511
-	 * @return JSONResponse
512
-	 */
513
-	public function uninstallApp(string $appId): JSONResponse {
514
-		$appId = OC_App::cleanAppId($appId);
515
-		$result = $this->installer->removeApp($appId);
516
-		if($result !== false) {
517
-			$this->appManager->clearAppsCache();
518
-			return new JSONResponse(['data' => ['appid' => $appId]]);
519
-		}
520
-		return new JSONResponse(['data' => ['message' => $this->l10n->t('Couldn\'t remove app.')]], Http::STATUS_INTERNAL_SERVER_ERROR);
521
-	}
522
-
523
-	/**
524
-	 * @param string $appId
525
-	 * @return JSONResponse
526
-	 */
527
-	public function updateApp(string $appId): JSONResponse {
528
-		$appId = OC_App::cleanAppId($appId);
529
-
530
-		$this->config->setSystemValue('maintenance', true);
531
-		try {
532
-			$result = $this->installer->updateAppstoreApp($appId);
533
-			$this->config->setSystemValue('maintenance', false);
534
-		} catch (\Exception $ex) {
535
-			$this->config->setSystemValue('maintenance', false);
536
-			return new JSONResponse(['data' => ['message' => $ex->getMessage()]], Http::STATUS_INTERNAL_SERVER_ERROR);
537
-		}
538
-
539
-		if ($result !== false) {
540
-			return new JSONResponse(['data' => ['appid' => $appId]]);
541
-		}
542
-		return new JSONResponse(['data' => ['message' => $this->l10n->t('Couldn\'t update app.')]], Http::STATUS_INTERNAL_SERVER_ERROR);
543
-	}
544
-
545
-	private function sortApps($a, $b) {
546
-		$a = (string)$a['name'];
547
-		$b = (string)$b['name'];
548
-		if ($a === $b) {
549
-			return 0;
550
-		}
551
-		return ($a < $b) ? -1 : 1;
552
-	}
553
-
554
-	public function force(string $appId): JSONResponse {
555
-		$appId = OC_App::cleanAppId($appId);
556
-		$this->appManager->ignoreNextcloudRequirementForApp($appId);
557
-		return new JSONResponse();
558
-	}
57
+    /** @var \OCP\IL10N */
58
+    private $l10n;
59
+    /** @var IConfig */
60
+    private $config;
61
+    /** @var INavigationManager */
62
+    private $navigationManager;
63
+    /** @var IAppManager */
64
+    private $appManager;
65
+    /** @var CategoryFetcher */
66
+    private $categoryFetcher;
67
+    /** @var AppFetcher */
68
+    private $appFetcher;
69
+    /** @var IFactory */
70
+    private $l10nFactory;
71
+    /** @var BundleFetcher */
72
+    private $bundleFetcher;
73
+    /** @var Installer */
74
+    private $installer;
75
+    /** @var IURLGenerator */
76
+    private $urlGenerator;
77
+    /** @var ILogger */
78
+    private $logger;
79
+
80
+    /** @var array */
81
+    private $allApps = [];
82
+
83
+    /**
84
+     * @param string $appName
85
+     * @param IRequest $request
86
+     * @param IL10N $l10n
87
+     * @param IConfig $config
88
+     * @param INavigationManager $navigationManager
89
+     * @param IAppManager $appManager
90
+     * @param CategoryFetcher $categoryFetcher
91
+     * @param AppFetcher $appFetcher
92
+     * @param IFactory $l10nFactory
93
+     * @param BundleFetcher $bundleFetcher
94
+     * @param Installer $installer
95
+     * @param IURLGenerator $urlGenerator
96
+     * @param ILogger $logger
97
+     */
98
+    public function __construct(string $appName,
99
+                                IRequest $request,
100
+                                IL10N $l10n,
101
+                                IConfig $config,
102
+                                INavigationManager $navigationManager,
103
+                                IAppManager $appManager,
104
+                                CategoryFetcher $categoryFetcher,
105
+                                AppFetcher $appFetcher,
106
+                                IFactory $l10nFactory,
107
+                                BundleFetcher $bundleFetcher,
108
+                                Installer $installer,
109
+                                IURLGenerator $urlGenerator,
110
+                                ILogger $logger) {
111
+        parent::__construct($appName, $request);
112
+        $this->l10n = $l10n;
113
+        $this->config = $config;
114
+        $this->navigationManager = $navigationManager;
115
+        $this->appManager = $appManager;
116
+        $this->categoryFetcher = $categoryFetcher;
117
+        $this->appFetcher = $appFetcher;
118
+        $this->l10nFactory = $l10nFactory;
119
+        $this->bundleFetcher = $bundleFetcher;
120
+        $this->installer = $installer;
121
+        $this->urlGenerator = $urlGenerator;
122
+        $this->logger = $logger;
123
+    }
124
+
125
+    /**
126
+     * @NoCSRFRequired
127
+     *
128
+     * @return TemplateResponse
129
+     */
130
+    public function viewApps(): TemplateResponse {
131
+        \OC_Util::addScript('settings', 'apps');
132
+        $params = [];
133
+        $params['appstoreEnabled'] = $this->config->getSystemValue('appstoreenabled', true) === true;
134
+        $params['updateCount'] = count($this->getAppsWithUpdates());
135
+        $params['developerDocumentation'] = $this->urlGenerator->linkToDocs('developer-manual');
136
+        $params['bundles'] = $this->getBundles();
137
+        $this->navigationManager->setActiveEntry('core_apps');
138
+
139
+        $templateResponse = new TemplateResponse('settings', 'settings-vue', ['serverData' => $params]);
140
+        $policy = new ContentSecurityPolicy();
141
+        $policy->addAllowedImageDomain('https://usercontent.apps.nextcloud.com');
142
+        $templateResponse->setContentSecurityPolicy($policy);
143
+
144
+        return $templateResponse;
145
+    }
146
+
147
+    private function getAppsWithUpdates() {
148
+        $appClass = new \OC_App();
149
+        $apps = $appClass->listAllApps();
150
+        foreach($apps as $key => $app) {
151
+            $newVersion = $this->installer->isUpdateAvailable($app['id']);
152
+            if($newVersion === false) {
153
+                unset($apps[$key]);
154
+            }
155
+        }
156
+        return $apps;
157
+    }
158
+
159
+    private function getBundles() {
160
+        $result = [];
161
+        $bundles = $this->bundleFetcher->getBundles();
162
+        foreach ($bundles as $bundle) {
163
+            $result[] = [
164
+                'name' => $bundle->getName(),
165
+                'id' => $bundle->getIdentifier(),
166
+                'appIdentifiers' => $bundle->getAppIdentifiers()
167
+            ];
168
+        }
169
+        return $result;
170
+
171
+    }
172
+
173
+    /**
174
+     * Get all available categories
175
+     *
176
+     * @return JSONResponse
177
+     */
178
+    public function listCategories(): JSONResponse {
179
+        return new JSONResponse($this->getAllCategories());
180
+    }
181
+
182
+    private function getAllCategories() {
183
+        $currentLanguage = substr($this->l10nFactory->findLanguage(), 0, 2);
184
+
185
+        $formattedCategories = [];
186
+        $categories = $this->categoryFetcher->get();
187
+        foreach($categories as $category) {
188
+            $formattedCategories[] = [
189
+                'id' => $category['id'],
190
+                'ident' => $category['id'],
191
+                'displayName' => isset($category['translations'][$currentLanguage]['name']) ? $category['translations'][$currentLanguage]['name'] : $category['translations']['en']['name'],
192
+            ];
193
+        }
194
+
195
+        return $formattedCategories;
196
+    }
197
+
198
+    private function fetchApps() {
199
+        $appClass = new \OC_App();
200
+        $apps = $appClass->listAllApps();
201
+        foreach ($apps as $app) {
202
+            $app['installed'] = true;
203
+            $this->allApps[$app['id']] = $app;
204
+        }
205
+
206
+        $apps = $this->getAppsForCategory('');
207
+        foreach ($apps as $app) {
208
+            $app['appstore'] = true;
209
+            if (!array_key_exists($app['id'], $this->allApps)) {
210
+                $this->allApps[$app['id']] = $app;
211
+            } else {
212
+                $this->allApps[$app['id']] = array_merge($app, $this->allApps[$app['id']]);
213
+            }
214
+        }
215
+
216
+        // add bundle information
217
+        $bundles = $this->bundleFetcher->getBundles();
218
+        foreach($bundles as $bundle) {
219
+            foreach($bundle->getAppIdentifiers() as $identifier) {
220
+                foreach($this->allApps as &$app) {
221
+                    if($app['id'] === $identifier) {
222
+                        $app['bundleIds'][] = $bundle->getIdentifier();
223
+                        continue;
224
+                    }
225
+                }
226
+            }
227
+        }
228
+    }
229
+
230
+    private function getAllApps() {
231
+        return $this->allApps;
232
+    }
233
+    /**
234
+     * Get all available apps in a category
235
+     *
236
+     * @param string $category
237
+     * @return JSONResponse
238
+     * @throws \Exception
239
+     */
240
+    public function listApps(): JSONResponse {
241
+
242
+        $this->fetchApps();
243
+        $apps = $this->getAllApps();
244
+
245
+        $dependencyAnalyzer = new DependencyAnalyzer(new Platform($this->config), $this->l10n);
246
+
247
+        // Extend existing app details
248
+        $apps = array_map(function($appData) use ($dependencyAnalyzer) {
249
+            if (isset($appData['appstoreData'])) {
250
+                $appstoreData = $appData['appstoreData'];
251
+                $appData['screenshot'] = isset($appstoreData['screenshots'][0]['url']) ? 'https://usercontent.apps.nextcloud.com/' . base64_encode($appstoreData['screenshots'][0]['url']) : '';
252
+                $appData['category'] = $appstoreData['categories'];
253
+            }
254
+
255
+            $newVersion = $this->installer->isUpdateAvailable($appData['id']);
256
+            if($newVersion) {
257
+                $appData['update'] = $newVersion;
258
+            }
259
+
260
+            // fix groups to be an array
261
+            $groups = array();
262
+            if (is_string($appData['groups'])) {
263
+                $groups = json_decode($appData['groups']);
264
+            }
265
+            $appData['groups'] = $groups;
266
+            $appData['canUnInstall'] = !$appData['active'] && $appData['removable'];
267
+
268
+            // fix licence vs license
269
+            if (isset($appData['license']) && !isset($appData['licence'])) {
270
+                $appData['licence'] = $appData['license'];
271
+            }
272
+
273
+            $ignoreMaxApps = $this->config->getSystemValue('app_install_overwrite', []);
274
+            if (!is_array($ignoreMaxApps)) {
275
+                $this->logger->warning('The value given for app_install_overwrite is not an array. Ignoring...');
276
+                $ignoreMaxApps = [];
277
+            }
278
+            $ignoreMax = in_array($appData['id'], $ignoreMaxApps);
279
+
280
+            // analyse dependencies
281
+            $missing = $dependencyAnalyzer->analyze($appData, $ignoreMax);
282
+            $appData['canInstall'] = empty($missing);
283
+            $appData['missingDependencies'] = $missing;
284
+
285
+            $appData['missingMinOwnCloudVersion'] = !isset($appData['dependencies']['nextcloud']['@attributes']['min-version']);
286
+            $appData['missingMaxOwnCloudVersion'] = !isset($appData['dependencies']['nextcloud']['@attributes']['max-version']);
287
+            $appData['isCompatible'] = $dependencyAnalyzer->isMarkedCompatible($appData);
288
+
289
+            return $appData;
290
+        }, $apps);
291
+
292
+        usort($apps, [$this, 'sortApps']);
293
+
294
+        return new JSONResponse(['apps' => $apps, 'status' => 'success']);
295
+    }
296
+
297
+    /**
298
+     * Get all apps for a category from the app store
299
+     *
300
+     * @param string $requestedCategory
301
+     * @return array
302
+     * @throws \Exception
303
+     */
304
+    private function getAppsForCategory($requestedCategory = ''): array {
305
+        $versionParser = new VersionParser();
306
+        $formattedApps = [];
307
+        $apps = $this->appFetcher->get();
308
+        foreach($apps as $app) {
309
+            // Skip all apps not in the requested category
310
+            if ($requestedCategory !== '') {
311
+                $isInCategory = false;
312
+                foreach($app['categories'] as $category) {
313
+                    if($category === $requestedCategory) {
314
+                        $isInCategory = true;
315
+                    }
316
+                }
317
+                if(!$isInCategory) {
318
+                    continue;
319
+                }
320
+            }
321
+
322
+            if (!isset($app['releases'][0]['rawPlatformVersionSpec'])) {
323
+                continue;
324
+            }
325
+            $nextCloudVersion = $versionParser->getVersion($app['releases'][0]['rawPlatformVersionSpec']);
326
+            $nextCloudVersionDependencies = [];
327
+            if($nextCloudVersion->getMinimumVersion() !== '') {
328
+                $nextCloudVersionDependencies['nextcloud']['@attributes']['min-version'] = $nextCloudVersion->getMinimumVersion();
329
+            }
330
+            if($nextCloudVersion->getMaximumVersion() !== '') {
331
+                $nextCloudVersionDependencies['nextcloud']['@attributes']['max-version'] = $nextCloudVersion->getMaximumVersion();
332
+            }
333
+            $phpVersion = $versionParser->getVersion($app['releases'][0]['rawPhpVersionSpec']);
334
+            $existsLocally = \OC_App::getAppPath($app['id']) !== false;
335
+            $phpDependencies = [];
336
+            if($phpVersion->getMinimumVersion() !== '') {
337
+                $phpDependencies['php']['@attributes']['min-version'] = $phpVersion->getMinimumVersion();
338
+            }
339
+            if($phpVersion->getMaximumVersion() !== '') {
340
+                $phpDependencies['php']['@attributes']['max-version'] = $phpVersion->getMaximumVersion();
341
+            }
342
+            if(isset($app['releases'][0]['minIntSize'])) {
343
+                $phpDependencies['php']['@attributes']['min-int-size'] = $app['releases'][0]['minIntSize'];
344
+            }
345
+            $authors = '';
346
+            foreach($app['authors'] as $key => $author) {
347
+                $authors .= $author['name'];
348
+                if($key !== count($app['authors']) - 1) {
349
+                    $authors .= ', ';
350
+                }
351
+            }
352
+
353
+            $currentLanguage = substr(\OC::$server->getL10NFactory()->findLanguage(), 0, 2);
354
+            $enabledValue = $this->config->getAppValue($app['id'], 'enabled', 'no');
355
+            $groups = null;
356
+            if($enabledValue !== 'no' && $enabledValue !== 'yes') {
357
+                $groups = $enabledValue;
358
+            }
359
+
360
+            $currentVersion = '';
361
+            if($this->appManager->isInstalled($app['id'])) {
362
+                $currentVersion = $this->appManager->getAppVersion($app['id']);
363
+            } else {
364
+                $currentLanguage = $app['releases'][0]['version'];
365
+            }
366
+
367
+            $formattedApps[] = [
368
+                'id' => $app['id'],
369
+                'name' => isset($app['translations'][$currentLanguage]['name']) ? $app['translations'][$currentLanguage]['name'] : $app['translations']['en']['name'],
370
+                'description' => isset($app['translations'][$currentLanguage]['description']) ? $app['translations'][$currentLanguage]['description'] : $app['translations']['en']['description'],
371
+                'summary' => isset($app['translations'][$currentLanguage]['summary']) ? $app['translations'][$currentLanguage]['summary'] : $app['translations']['en']['summary'],
372
+                'license' => $app['releases'][0]['licenses'],
373
+                'author' => $authors,
374
+                'shipped' => false,
375
+                'version' => $currentVersion,
376
+                'default_enable' => '',
377
+                'types' => [],
378
+                'documentation' => [
379
+                    'admin' => $app['adminDocs'],
380
+                    'user' => $app['userDocs'],
381
+                    'developer' => $app['developerDocs']
382
+                ],
383
+                'website' => $app['website'],
384
+                'bugs' => $app['issueTracker'],
385
+                'detailpage' => $app['website'],
386
+                'dependencies' => array_merge(
387
+                    $nextCloudVersionDependencies,
388
+                    $phpDependencies
389
+                ),
390
+                'level' => ($app['isFeatured'] === true) ? 200 : 100,
391
+                'missingMaxOwnCloudVersion' => false,
392
+                'missingMinOwnCloudVersion' => false,
393
+                'canInstall' => true,
394
+                'screenshot' => isset($app['screenshots'][0]['url']) ? 'https://usercontent.apps.nextcloud.com/'.base64_encode($app['screenshots'][0]['url']) : '',
395
+                'score' => $app['ratingOverall'],
396
+                'ratingNumOverall' => $app['ratingNumOverall'],
397
+                'ratingNumThresholdReached' => $app['ratingNumOverall'] > 5,
398
+                'removable' => $existsLocally,
399
+                'active' => $this->appManager->isEnabledForUser($app['id']),
400
+                'needsDownload' => !$existsLocally,
401
+                'groups' => $groups,
402
+                'fromAppStore' => true,
403
+                'appstoreData' => $app,
404
+            ];
405
+        }
406
+
407
+        return $formattedApps;
408
+    }
409
+
410
+    /**
411
+     * @PasswordConfirmationRequired
412
+     *
413
+     * @param string $appId
414
+     * @param array $groups
415
+     * @return JSONResponse
416
+     */
417
+    public function enableApp(string $appId, array $groups = []): JSONResponse {
418
+        return $this->enableApps([$appId], $groups);
419
+    }
420
+
421
+    /**
422
+     * Enable one or more apps
423
+     *
424
+     * apps will be enabled for specific groups only if $groups is defined
425
+     *
426
+     * @PasswordConfirmationRequired
427
+     * @param array $appIds
428
+     * @param array $groups
429
+     * @return JSONResponse
430
+     */
431
+    public function enableApps(array $appIds, array $groups = []): JSONResponse {
432
+        try {
433
+            $updateRequired = false;
434
+
435
+            foreach ($appIds as $appId) {
436
+                $appId = OC_App::cleanAppId($appId);
437
+
438
+                // Check if app is already downloaded
439
+                /** @var Installer $installer */
440
+                $installer = \OC::$server->query(Installer::class);
441
+                $isDownloaded = $installer->isDownloaded($appId);
442
+
443
+                if(!$isDownloaded) {
444
+                    $installer->downloadApp($appId);
445
+                }
446
+
447
+                $installer->installApp($appId);
448
+
449
+                if (count($groups) > 0) {
450
+                    $this->appManager->enableAppForGroups($appId, $this->getGroupList($groups));
451
+                } else {
452
+                    $this->appManager->enableApp($appId);
453
+                }
454
+                if (\OC_App::shouldUpgrade($appId)) {
455
+                    $updateRequired = true;
456
+                }
457
+            }
458
+            return new JSONResponse(['data' => ['update_required' => $updateRequired]]);
459
+
460
+        } catch (\Exception $e) {
461
+            $this->logger->logException($e);
462
+            return new JSONResponse(['data' => ['message' => $e->getMessage()]], Http::STATUS_INTERNAL_SERVER_ERROR);
463
+        }
464
+    }
465
+
466
+    private function getGroupList(array $groups) {
467
+        $groupManager = \OC::$server->getGroupManager();
468
+        $groupsList = [];
469
+        foreach ($groups as $group) {
470
+            $groupItem = $groupManager->get($group);
471
+            if ($groupItem instanceof \OCP\IGroup) {
472
+                $groupsList[] = $groupManager->get($group);
473
+            }
474
+        }
475
+        return $groupsList;
476
+    }
477
+
478
+    /**
479
+     * @PasswordConfirmationRequired
480
+     *
481
+     * @param string $appId
482
+     * @return JSONResponse
483
+     */
484
+    public function disableApp(string $appId): JSONResponse {
485
+        return $this->disableApps([$appId]);
486
+    }
487
+
488
+    /**
489
+     * @PasswordConfirmationRequired
490
+     *
491
+     * @param array $appIds
492
+     * @return JSONResponse
493
+     */
494
+    public function disableApps(array $appIds): JSONResponse {
495
+        try {
496
+            foreach ($appIds as $appId) {
497
+                $appId = OC_App::cleanAppId($appId);
498
+                $this->appManager->disableApp($appId);
499
+            }
500
+            return new JSONResponse([]);
501
+        } catch (\Exception $e) {
502
+            $this->logger->logException($e);
503
+            return new JSONResponse(['data' => ['message' => $e->getMessage()]], Http::STATUS_INTERNAL_SERVER_ERROR);
504
+        }
505
+    }
506
+
507
+    /**
508
+     * @PasswordConfirmationRequired
509
+     *
510
+     * @param string $appId
511
+     * @return JSONResponse
512
+     */
513
+    public function uninstallApp(string $appId): JSONResponse {
514
+        $appId = OC_App::cleanAppId($appId);
515
+        $result = $this->installer->removeApp($appId);
516
+        if($result !== false) {
517
+            $this->appManager->clearAppsCache();
518
+            return new JSONResponse(['data' => ['appid' => $appId]]);
519
+        }
520
+        return new JSONResponse(['data' => ['message' => $this->l10n->t('Couldn\'t remove app.')]], Http::STATUS_INTERNAL_SERVER_ERROR);
521
+    }
522
+
523
+    /**
524
+     * @param string $appId
525
+     * @return JSONResponse
526
+     */
527
+    public function updateApp(string $appId): JSONResponse {
528
+        $appId = OC_App::cleanAppId($appId);
529
+
530
+        $this->config->setSystemValue('maintenance', true);
531
+        try {
532
+            $result = $this->installer->updateAppstoreApp($appId);
533
+            $this->config->setSystemValue('maintenance', false);
534
+        } catch (\Exception $ex) {
535
+            $this->config->setSystemValue('maintenance', false);
536
+            return new JSONResponse(['data' => ['message' => $ex->getMessage()]], Http::STATUS_INTERNAL_SERVER_ERROR);
537
+        }
538
+
539
+        if ($result !== false) {
540
+            return new JSONResponse(['data' => ['appid' => $appId]]);
541
+        }
542
+        return new JSONResponse(['data' => ['message' => $this->l10n->t('Couldn\'t update app.')]], Http::STATUS_INTERNAL_SERVER_ERROR);
543
+    }
544
+
545
+    private function sortApps($a, $b) {
546
+        $a = (string)$a['name'];
547
+        $b = (string)$b['name'];
548
+        if ($a === $b) {
549
+            return 0;
550
+        }
551
+        return ($a < $b) ? -1 : 1;
552
+    }
553
+
554
+    public function force(string $appId): JSONResponse {
555
+        $appId = OC_App::cleanAppId($appId);
556
+        $this->appManager->ignoreNextcloudRequirementForApp($appId);
557
+        return new JSONResponse();
558
+    }
559 559
 
560 560
 }
Please login to merge, or discard this patch.