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