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