Completed
Push — master ( 4a9cb8...f72f55 )
by Morris
12:46
created

AppSettingsController::getAllCategories()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 14
nc 3
nop 0
dl 0
loc 20
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 * @copyright Copyright (c) 2016, Lukas Reschke <[email protected]>
5
 *
6
 * @author Christoph Wurst <[email protected]>
7
 * @author Joas Schilling <[email protected]>
8
 * @author Lukas Reschke <[email protected]>
9
 * @author Morris Jobke <[email protected]>
10
 * @author Thomas Müller <[email protected]>
11
 *
12
 * @license AGPL-3.0
13
 *
14
 * This code is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License, version 3,
16
 * as published by the Free Software Foundation.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
 * GNU Affero General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License, version 3,
24
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
25
 *
26
 */
27
28
namespace OC\Settings\Controller;
29
30
use OC\App\AppStore\Bundles\BundleFetcher;
31
use OC\App\AppStore\Fetcher\AppFetcher;
32
use OC\App\AppStore\Fetcher\CategoryFetcher;
33
use OC\App\AppStore\Version\VersionParser;
34
use OC\App\DependencyAnalyzer;
35
use OC\App\Platform;
36
use OCP\App\IAppManager;
37
use \OCP\AppFramework\Controller;
38
use OCP\AppFramework\Http\ContentSecurityPolicy;
39
use OCP\AppFramework\Http\JSONResponse;
40
use OCP\AppFramework\Http\TemplateResponse;
41
use OCP\INavigationManager;
42
use OCP\IRequest;
43
use OCP\IL10N;
44
use OCP\IConfig;
45
use OCP\L10N\IFactory;
46
47
/**
48
 * @package OC\Settings\Controller
49
 */
50
class AppSettingsController extends Controller {
51
	const CAT_ENABLED = 0;
52
	const CAT_DISABLED = 1;
53
	const CAT_ALL_INSTALLED = 2;
54
	const CAT_APP_BUNDLES = 3;
55
56
	/** @var \OCP\IL10N */
57
	private $l10n;
58
	/** @var IConfig */
59
	private $config;
60
	/** @var INavigationManager */
61
	private $navigationManager;
62
	/** @var IAppManager */
63
	private $appManager;
64
	/** @var CategoryFetcher */
65
	private $categoryFetcher;
66
	/** @var AppFetcher */
67
	private $appFetcher;
68
	/** @var IFactory */
69
	private $l10nFactory;
70
	/** @var BundleFetcher */
71
	private $bundleFetcher;
72
73
	/**
74
	 * @param string $appName
75
	 * @param IRequest $request
76
	 * @param IL10N $l10n
77
	 * @param IConfig $config
78
	 * @param INavigationManager $navigationManager
79
	 * @param IAppManager $appManager
80
	 * @param CategoryFetcher $categoryFetcher
81
	 * @param AppFetcher $appFetcher
82
	 * @param IFactory $l10nFactory
83
	 * @param BundleFetcher $bundleFetcher
84
	 */
85 View Code Duplication
	public function __construct($appName,
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
86
								IRequest $request,
87
								IL10N $l10n,
88
								IConfig $config,
89
								INavigationManager $navigationManager,
90
								IAppManager $appManager,
91
								CategoryFetcher $categoryFetcher,
92
								AppFetcher $appFetcher,
93
								IFactory $l10nFactory,
94
								BundleFetcher $bundleFetcher) {
95
		parent::__construct($appName, $request);
96
		$this->l10n = $l10n;
97
		$this->config = $config;
98
		$this->navigationManager = $navigationManager;
99
		$this->appManager = $appManager;
100
		$this->categoryFetcher = $categoryFetcher;
101
		$this->appFetcher = $appFetcher;
102
		$this->l10nFactory = $l10nFactory;
103
		$this->bundleFetcher = $bundleFetcher;
104
	}
105
106
	/**
107
	 * @NoCSRFRequired
108
	 *
109
	 * @param string $category
110
	 * @return TemplateResponse
111
	 */
112
	public function viewApps($category = '') {
113
		if ($category === '') {
114
			$category = 'installed';
115
		}
116
117
		$params = [];
118
		$params['category'] = $category;
119
		$params['appstoreEnabled'] = $this->config->getSystemValue('appstoreenabled', true) === true;
120
		$this->navigationManager->setActiveEntry('core_apps');
121
122
		$templateResponse = new TemplateResponse($this->appName, 'apps', $params, 'user');
123
		$policy = new ContentSecurityPolicy();
124
		$policy->addAllowedImageDomain('https://usercontent.apps.nextcloud.com');
125
		$templateResponse->setContentSecurityPolicy($policy);
126
127
		return $templateResponse;
128
	}
129
130
	private function getAllCategories() {
131
		$currentLanguage = substr($this->l10nFactory->findLanguage(), 0, 2);
132
133
		$formattedCategories = [
134
			['id' => self::CAT_ALL_INSTALLED, 'ident' => 'installed', 'displayName' => (string)$this->l10n->t('Your apps')],
135
			['id' => self::CAT_ENABLED, 'ident' => 'enabled', 'displayName' => (string)$this->l10n->t('Enabled apps')],
136
			['id' => self::CAT_DISABLED, 'ident' => 'disabled', 'displayName' => (string)$this->l10n->t('Disabled apps')],
137
			['id' => self::CAT_APP_BUNDLES, 'ident' => 'app-bundles', 'displayName' => (string)$this->l10n->t('App bundles')],
138
		];
139
		$categories = $this->categoryFetcher->get();
140
		foreach($categories as $category) {
141
			$formattedCategories[] = [
142
				'id' => $category['id'],
143
				'ident' => $category['id'],
144
				'displayName' => isset($category['translations'][$currentLanguage]['name']) ? $category['translations'][$currentLanguage]['name'] : $category['translations']['en']['name'],
145
			];
146
		}
147
148
		return $formattedCategories;
149
	}
150
151
	/**
152
	 * Get all available categories
153
	 *
154
	 * @return JSONResponse
155
	 */
156
	public function listCategories() {
157
		return new JSONResponse($this->getAllCategories());
158
	}
159
160
	/**
161
	 * Get all apps for a category
162
	 *
163
	 * @param string $requestedCategory
164
	 * @return array
165
	 */
166
	private function getAppsForCategory($requestedCategory) {
167
		$versionParser = new VersionParser();
168
		$formattedApps = [];
169
		$apps = $this->appFetcher->get();
170
		foreach($apps as $app) {
171
			if (isset($app['isFeatured'])) {
172
				$app['featured'] = $app['isFeatured'];
173
			}
174
175
			// Skip all apps not in the requested category
176
			$isInCategory = false;
177
			foreach($app['categories'] as $category) {
178
				if($category === $requestedCategory) {
179
					$isInCategory = true;
180
				}
181
			}
182
			if(!$isInCategory) {
183
				continue;
184
			}
185
186
			$nextCloudVersion = $versionParser->getVersion($app['releases'][0]['rawPlatformVersionSpec']);
187
			$nextCloudVersionDependencies = [];
188
			if($nextCloudVersion->getMinimumVersion() !== '') {
189
				$nextCloudVersionDependencies['nextcloud']['@attributes']['min-version'] = $nextCloudVersion->getMinimumVersion();
190
			}
191
			if($nextCloudVersion->getMaximumVersion() !== '') {
192
				$nextCloudVersionDependencies['nextcloud']['@attributes']['max-version'] = $nextCloudVersion->getMaximumVersion();
193
			}
194
			$phpVersion = $versionParser->getVersion($app['releases'][0]['rawPhpVersionSpec']);
195
			$existsLocally = (\OC_App::getAppPath($app['id']) !== false) ? true : false;
196
			$phpDependencies = [];
197
			if($phpVersion->getMinimumVersion() !== '') {
198
				$phpDependencies['php']['@attributes']['min-version'] = $phpVersion->getMinimumVersion();
199
			}
200
			if($phpVersion->getMaximumVersion() !== '') {
201
				$phpDependencies['php']['@attributes']['max-version'] = $phpVersion->getMaximumVersion();
202
			}
203
			if(isset($app['releases'][0]['minIntSize'])) {
204
				$phpDependencies['php']['@attributes']['min-int-size'] = $app['releases'][0]['minIntSize'];
205
			}
206
			$authors = '';
207
			foreach($app['authors'] as $key => $author) {
208
				$authors .= $author['name'];
209
				if($key !== count($app['authors']) - 1) {
210
					$authors .= ', ';
211
				}
212
			}
213
214
			$currentLanguage = substr(\OC::$server->getL10NFactory()->findLanguage(), 0, 2);
215
			$enabledValue = $this->config->getAppValue($app['id'], 'enabled', 'no');
216
			$groups = null;
217
			if($enabledValue !== 'no' && $enabledValue !== 'yes') {
218
				$groups = $enabledValue;
219
			}
220
221
			$currentVersion = '';
222
			if($this->appManager->isInstalled($app['id'])) {
223
				$currentVersion = \OC_App::getAppVersion($app['id']);
224
			} else {
225
				$currentLanguage = $app['releases'][0]['version'];
226
			}
227
228
			$formattedApps[] = [
229
				'id' => $app['id'],
230
				'name' => isset($app['translations'][$currentLanguage]['name']) ? $app['translations'][$currentLanguage]['name'] : $app['translations']['en']['name'],
231
				'description' => isset($app['translations'][$currentLanguage]['description']) ? $app['translations'][$currentLanguage]['description'] : $app['translations']['en']['description'],
232
				'license' => $app['releases'][0]['licenses'],
233
				'author' => $authors,
234
				'shipped' => false,
235
				'version' => $currentVersion,
236
				'default_enable' => '',
237
				'types' => [],
238
				'documentation' => [
239
					'admin' => $app['adminDocs'],
240
					'user' => $app['userDocs'],
241
					'developer' => $app['developerDocs']
242
				],
243
				'website' => $app['website'],
244
				'bugs' => $app['issueTracker'],
245
				'detailpage' => $app['website'],
246
				'dependencies' => array_merge(
247
					$nextCloudVersionDependencies,
248
					$phpDependencies
249
				),
250
				'level' => ($app['featured'] === true) ? 200 : 100,
251
				'missingMaxOwnCloudVersion' => false,
252
				'missingMinOwnCloudVersion' => false,
253
				'canInstall' => true,
254
				'preview' => isset($app['screenshots'][0]['url']) ? 'https://usercontent.apps.nextcloud.com/'.base64_encode($app['screenshots'][0]['url']) : '',
255
				'score' => $app['ratingOverall'],
256
				'ratingNumOverall' => $app['ratingNumOverall'],
257
				'ratingNumThresholdReached' => $app['ratingNumOverall'] > 5 ? true : false,
258
				'removable' => $existsLocally,
259
				'active' => $this->appManager->isEnabledForUser($app['id']),
260
				'needsDownload' => !$existsLocally,
261
				'groups' => $groups,
262
				'fromAppStore' => true,
263
			];
264
265
266
			$appFetcher = \OC::$server->getAppFetcher();
267
			$newVersion = \OC\Installer::isUpdateAvailable($app['id'], $appFetcher);
268
			if($newVersion && $this->appManager->isInstalled($app['id'])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $newVersion of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
269
				$formattedApps[count($formattedApps)-1]['update'] = $newVersion;
270
			}
271
		}
272
273
		return $formattedApps;
274
	}
275
276
	/**
277
	 * Get all available apps in a category
278
	 *
279
	 * @param string $category
280
	 * @return JSONResponse
281
	 */
282
	public function listApps($category = '') {
283
		$appClass = new \OC_App();
284
285
		switch ($category) {
286
			// installed apps
287
			case 'installed':
288
				$apps = $appClass->listAllApps();
289
290 View Code Duplication
				foreach($apps as $key => $app) {
291
					$newVersion = \OC\Installer::isUpdateAvailable($app['id'], $this->appFetcher);
292
					$apps[$key]['update'] = $newVersion;
293
				}
294
295 View Code Duplication
				usort($apps, function ($a, $b) {
296
					$a = (string)$a['name'];
297
					$b = (string)$b['name'];
298
					if ($a === $b) {
299
						return 0;
300
					}
301
					return ($a < $b) ? -1 : 1;
302
				});
303
				break;
304
			// enabled apps
305
			case 'enabled':
306
				$apps = $appClass->listAllApps();
307
				$apps = array_filter($apps, function ($app) {
308
					return $app['active'];
309
				});
310
311 View Code Duplication
				foreach($apps as $key => $app) {
312
					$newVersion = \OC\Installer::isUpdateAvailable($app['id'], $this->appFetcher);
313
					$apps[$key]['update'] = $newVersion;
314
				}
315
316 View Code Duplication
				usort($apps, function ($a, $b) {
317
					$a = (string)$a['name'];
318
					$b = (string)$b['name'];
319
					if ($a === $b) {
320
						return 0;
321
					}
322
					return ($a < $b) ? -1 : 1;
323
				});
324
				break;
325
			// disabled  apps
326
			case 'disabled':
327
				$apps = $appClass->listAllApps();
328
				$apps = array_filter($apps, function ($app) {
329
					return !$app['active'];
330
				});
331
332
				$apps = array_map(function ($app) {
333
					$newVersion = \OC\Installer::isUpdateAvailable($app['id'], $this->appFetcher);
334
					if ($newVersion !== false) {
335
						$app['update'] = $newVersion;
336
					}
337
					return $app;
338
				}, $apps);
339
340 View Code Duplication
				usort($apps, function ($a, $b) {
341
					$a = (string)$a['name'];
342
					$b = (string)$b['name'];
343
					if ($a === $b) {
344
						return 0;
345
					}
346
					return ($a < $b) ? -1 : 1;
347
				});
348
				break;
349
			case 'app-bundles':
350
				$bundles = $this->bundleFetcher->getBundles();
351
				$apps = [];
352
				foreach($bundles as $bundle) {
353
					$newCategory = true;
354
					$allApps = $appClass->listAllApps();
355
					$categories = $this->getAllCategories();
356
					foreach($categories as $singleCategory) {
357
						$newApps = $this->getAppsForCategory($singleCategory['id']);
358
						foreach($allApps as $app) {
359
							foreach($newApps as $key => $newApp) {
360
								if($app['id'] === $newApp['id']) {
361
									unset($newApps[$key]);
362
								}
363
							}
364
						}
365
						$allApps = array_merge($allApps, $newApps);
366
					}
367
368
					foreach($bundle->getAppIdentifiers() as $identifier) {
369
						foreach($allApps as $app) {
370
							if($app['id'] === $identifier) {
371
								if($newCategory) {
372
									$app['newCategory'] = true;
373
									$app['categoryName'] = $bundle->getName();
374
								}
375
								$app['bundleId'] = $bundle->getIdentifier();
376
								$newCategory = false;
377
								$apps[] = $app;
378
								continue;
379
							}
380
						}
381
					}
382
				}
383
				break;
384
			default:
385
				$apps = $this->getAppsForCategory($category);
386
387
				// sort by score
388
				usort($apps, function ($a, $b) {
389
					$a = (int)$a['score'];
390
					$b = (int)$b['score'];
391
					if ($a === $b) {
392
						return 0;
393
					}
394
					return ($a > $b) ? -1 : 1;
395
				});
396
				break;
397
		}
398
399
		// fix groups to be an array
400
		$dependencyAnalyzer = new DependencyAnalyzer(new Platform($this->config), $this->l10n);
401
		$apps = array_map(function($app) use ($dependencyAnalyzer) {
402
403
			// fix groups
404
			$groups = array();
405
			if (is_string($app['groups'])) {
406
				$groups = json_decode($app['groups']);
407
			}
408
			$app['groups'] = $groups;
409
			$app['canUnInstall'] = !$app['active'] && $app['removable'];
410
411
			// fix licence vs license
412
			if (isset($app['license']) && !isset($app['licence'])) {
413
				$app['licence'] = $app['license'];
414
			}
415
416
			// analyse dependencies
417
			$missing = $dependencyAnalyzer->analyze($app);
418
			$app['canInstall'] = empty($missing);
419
			$app['missingDependencies'] = $missing;
420
421
			$app['missingMinOwnCloudVersion'] = !isset($app['dependencies']['nextcloud']['@attributes']['min-version']);
422
			$app['missingMaxOwnCloudVersion'] = !isset($app['dependencies']['nextcloud']['@attributes']['max-version']);
423
424
			return $app;
425
		}, $apps);
426
427
		return new JSONResponse(['apps' => $apps, 'status' => 'success']);
428
	}
429
}
430