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