Completed
Push — master ( e4992c...6d0a35 )
by
unknown
10:42
created

SettingsManager::sortOrder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Tom Needham <[email protected]>
4
 *
5
 * @copyright Copyright (c) 2018, ownCloud GmbH
6
 * @license AGPL-3.0
7
 *
8
 * This code is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Affero General Public License, version 3,
10
 * as published by the Free Software Foundation.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License, version 3,
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
19
 *
20
 */
21
22
namespace OC\Settings;
23
24
use OC\Security\CertificateManager;
25
use OC\Settings\Panels\Admin\Apps;
26
use OC\Settings\Panels\Helper;
27
use OCP\App\IAppManager;
28
use OCP\IDBConnection;
29
use OCP\L10N\IFactory;
30
use OCP\Lock\ILockingProvider;
31
use OCP\Settings\ISettingsManager;
32
use OCP\Settings\ISection;
33
use OCP\Settings\ISettings;
34
use OCP\ILogger;
35
use OCP\IL10N;
36
use OCP\IUserSession;
37
use OCP\AppFramework\QueryException;
38
use OCP\IConfig;
39
use OCP\IGroupManager;
40
use OCP\Defaults;
41
use OCP\IURLGenerator;
42
43
use OC\Settings\Panels\Personal\Profile;
44
use OC\Settings\Panels\Personal\Legacy as LegacyPersonal;
45
use OC\Settings\Panels\Admin\Legacy as LegacyAdmin;
46
use OC\Settings\Panels\Personal\Clients;
47
use OC\Settings\Panels\Personal\Version;
48
use OC\Settings\Panels\Personal\Tokens;
49
use OC\Settings\Panels\Personal\Cors;
50
use OC\Settings\Panels\Personal\Quota;
51
use OC\Settings\Panels\Admin\BackgroundJobs;
52
use OC\Settings\Panels\Admin\Certificates;
53
use OC\Settings\Panels\Admin\Encryption;
54
use OC\Settings\Panels\Admin\FileSharing;
55
use OC\Settings\Panels\Admin\Legal;
56
use OC\Settings\Panels\Admin\Mail;
57
use OC\Settings\Panels\Admin\Logging;
58
use OC\Settings\Panels\Admin\SecurityWarning;
59
use OC\Settings\Panels\Admin\Tips;
60
use OC\Settings\Panels\Admin\Status;
61
62
/*
63
 * @since 10.0
64
 */
65
class SettingsManager implements ISettingsManager {
66
67
	/** @var IL10N */
68
	protected $l;
69
	/** @var IAppManager */
70
	protected $appManager;
71
	/** @var ILogger */
72
	protected $logger;
73
	/** @var IURLGenerator */
74
	protected $urlGenerator;
75
	/** @var Defaults  */
76
	protected $defaults;
77
	/** @var IUserSession  */
78
	protected $userSession;
79
	/** @var ILogger  */
80
	protected $log;
81
	/** @var IConfig  */
82
	protected $config;
83
	/** @var IGroupManager  */
84
	protected $groupManager;
85
	/** @var Helper */
86
	protected $helper;
87
	/** @var IFactory  */
88
	protected $lfactory;
89
	/** @var IDBConnection  */
90
	protected $dbconnection;
91
	/** @var ILockingProvider  */
92
	protected $lockingProvider;
93
	/** @var CertificateManager  */
94
	protected $certificateManager;
95
96
	/**
97
	 * Holds a cache of ISettings with keys for type
98
	 */
99
	protected $panels = [];
100
101
	/**
102
	 * Holds an array of sections
103
	 */
104
	protected $sections = [];
105
106
	/**
107
	 * @param IL10N $l
108
	 * @param IAppManager $appManager
109
	 * @param IUserSession $userSession
110
	 * @param ILogger $logger
111
	 * @param IGroupManager $groupManager
112
	 * @param IConfig $config
113
	 * @param Defaults $defaults
114
	 * @param IURLGenerator $urlGenerator
115
	 * @param Helper $helper
116
	 * @param ILockingProvider $lockingProvider
117
	 * @param IDBConnection $dbconnection
118
	 * @param CertificateManager $certificateManager
119
	 * @param IFactory $lfactory
120
	 */
121
	public function __construct(IL10N $l,
122
								IAppManager $appManager,
123
								IUserSession $userSession,
124
								ILogger $logger,
125
								IGroupManager $groupManager,
126
								IConfig $config,
127
								Defaults $defaults,
128
								IURLGenerator $urlGenerator,
129
								Helper $helper,
130
								ILockingProvider $lockingProvider,
131
								IDBConnection $dbconnection,
132
								$certificateManager,
133
								IFactory $lfactory) {
134
		$this->l = $l;
135
		$this->appManager = $appManager;
136
		$this->userSession = $userSession;
137
		$this->config = $config;
138
		$this->groupManager = $groupManager;
139
		$this->log = $logger;
140
		$this->defaults = $defaults;
141
		$this->urlGenerator = $urlGenerator;
142
		$this->helper = $helper;
143
		$this->lockingProvider = $lockingProvider;
144
		$this->dbconnection = $dbconnection;
145
		$this->certificateManager = $certificateManager;
146
		$this->lfactory = $lfactory;
147
	}
148
149
	public function getPersonalSections() {
150
		// Trigger a load of all personal panels to discover sections
151
		$this->loadPanels('personal');
152
		return $this->sections['personal'];
153
	}
154
155
	public function getAdminSections() {
156
		// Trigger a load of all admin panels to discover sections
157
		$this->loadPanels('admin');
158
		return $this->sections['admin'];
159
	}
160
161
	/**
162
	 * Returns ISettings for the personal settings in the given section
163
	 * @param string $sectionID
164
	 * @return array of ISection
165
	 */
166 View Code Duplication
	public function getPersonalPanels($sectionID) {
167
		// Trigger a load of all personal panels to discover sections
168
		$this->loadPanels('personal');
169
		if (isset($this->panels['personal'][$sectionID])) {
170
			return $this->panels['personal'][$sectionID];
171
		} else {
172
			return [];
173
		}
174
	}
175
176
	/**
177
	 * Returns ISettings for the admin settings in the given section
178
	 * @param string $sectionID
179
	 * @return array of ISection
180
	 */
181 View Code Duplication
	public function getAdminPanels($sectionID) {
182
		// Trigger a load of all admin panels to discover sections
183
		$this->loadPanels('admin');
184
		if (isset($this->panels['admin'][$sectionID])) {
185
			return $this->panels['admin'][$sectionID];
186
		} else {
187
			return [];
188
		}
189
	}
190
191
	/**
192
	 * Returns the default set of ISections used in core
193
	 * @param string $type the type of sections to return
194
	 * @return array of ISection
195
	 */
196
	private function getBuiltInSections($type) {
197
		if ($type === 'admin') {
198
			return [
199
				new Section('apps', $this->l->t('Apps'), 105, 'list'),
200
				new Section('general', $this->l->t('General'), 100),
201
				new Section('storage', $this->l->t('Storage'), 95, 'folder'),
202
				new Section('security', $this->l->t('Security'), 90, 'shield'),
203
				new Section('authentication', $this->l->t('User Authentication'), 87, 'user'),
204
				new Section('encryption', $this->l->t('Encryption'), 85, 'password'),
205
				new Section('workflow', $this->l->t('Workflows & Tags'), 85, 'workflows'),
206
				new Section('sharing', $this->l->t('Sharing'), 80, 'share'),
207
				new Section('search', $this->l->t('Search'), 75, 'search'),
208
				new Section('help', $this->l->t('Help & Tips'), -5, 'info'),
209
				new Section('additional', $this->l->t('Additional'), -10, 'more'),
210
			];
211
		} elseif ($type === 'personal') {
212
			return [
213
				new Section('general', $this->l->t('General'), 100, 'user'),
214
				new Section('storage', $this->l->t('Storage'), 50, 'folder'),
215
				new Section('security', $this->l->t('Security'), 30, 'shield'),
216
				new Section('encryption', $this->l->t('Encryption'), 20),
217
				new Section('additional', $this->l->t('Additional'), -10, 'more'),
218
			];
219
		}
220
	}
221
222
	/**
223
	 * Returns an array of classnames for built in settings panels
224
	 * @param string $type the type of panels to load
225
	 * @return array of strings
226
	 */
227
	private function getBuiltInPanels($type) {
228
		if ($type === 'admin') {
229
			return [
230
				LegacyAdmin::class,
231
				BackgroundJobs::class,
232
				Logging::class,
233
				Tips::class,
234
				SecurityWarning::class,
235
				Mail::class,
236
				FileSharing::class,
237
				Encryption::class,
238
				Certificates::class,
239
				Apps::class,
240
				Legal::class,
241
				Status::class
242
			];
243
		} elseif ($type === 'personal') {
244
			return [
245
				Profile::class,
246
				Clients::class,
247
				LegacyPersonal::class,
248
				Version::class,
249
				Tokens::class,
250
				Cors::class,
251
				Quota::class
252
			];
253
		}
254
	}
255
256
	/**
257
	 * Gets panel objects with dependencies instantiated from the container
258
	 * @param string $className
259
	 * @return array|false
260
	 */
261
	public function getBuiltInPanel($className) {
262
		$panels = [
263
			// Personal
264
			Profile::class => new Profile(
265
				$this->config,
266
				$this->groupManager,
267
				$this->userSession,
268
				$this->lfactory),
269
			LegacyPersonal::class => new LegacyPersonal($this->helper),
270
			Clients::class => new Clients($this->config, $this->defaults),
271
			Version::class => new Version(),
272
			Tokens::class => new Tokens(),
273
			Cors::class => new Cors(
274
				$this->userSession,
275
				$this->urlGenerator,
276
				$this->config),
277
			Quota::class => new Quota($this->helper),
278
			// Admin
279
			BackgroundJobs::class => new BackgroundJobs($this->config),
280
			Certificates::class => new Certificates(
281
				$this->config,
282
				$this->urlGenerator,
283
				$this->certificateManager),
284
			Encryption::class => new Encryption(),
285
			FileSharing::class => new FileSharing($this->config, $this->helper, $this->l),
286
			Logging::class => new Logging($this->config, $this->urlGenerator, $this->helper),
287
			Mail::class => new Mail($this->config, $this->helper),
288
			SecurityWarning::class => new SecurityWarning(
289
				$this->l,
290
				$this->config,
291
				$this->dbconnection,
292
				$this->helper,
293
				$this->lockingProvider),
294
			Tips::class => new Tips(),
295
			LegacyAdmin::class => new LegacyAdmin($this->helper),
296
			Apps::class => new Apps($this->config),
297
			Legal::class => new Legal($this->config)
298
		];
299
		if (isset($panels[$className])) {
300
			return $panels[$className];
301
		} else {
302
			return false;
303
		}
304
	}
305
306
	/**
307
	 * Gets all the panels for ownCloud
308
	 * @param string $type the type of panels to return
309
	 * @return array of strings
310
	 */
311
	public function getPanelsList($type) {
312
		return \array_merge($this->findRegisteredPanels($type), $this->getBuiltInPanels($type));
313
	}
314
315
	/**
316
	 * Searches through the currently enabled apps and returns the panels registered
317
	 * @param string $type the type of panels to return
318
	 * @return array of strings with panel class names
319
	 */
320
	protected function findRegisteredPanels($type) {
321
		$panels = [];
322
		foreach ($this->appManager->getEnabledAppsForUser($this->userSession->getUser()) as $app) {
323
			if (isset($this->appManager->getAppInfo($app)['settings'])) {
324
				foreach ($this->appManager->getAppInfo($app)['settings'] as $t => $detected) {
325
					if ($t === $type) {
326
						// Allow app to register multiple panels of the same type
327
						$detected = \is_array($detected) ? $detected : [$detected];
328
						$panels = \array_merge($panels, $detected);
329
					}
330
				}
331
			}
332
		}
333
		return $panels;
334
	}
335
336
	/**
337
	 * Searches through the currently enabled apps and returns the sections registered
338
	 * @param string $type the type of sections to return
339
	 * @return ISection[]
340
	 */
341
	protected function findRegisteredSections($type) {
342
		$sections = [];
343
		foreach ($this->appManager->getEnabledAppsForUser($this->userSession->getUser()) as $app) {
344
			if (isset($this->appManager->getAppInfo($app)['settings-sections'])) {
345
				foreach ($this->appManager->getAppInfo($app)['settings-sections'] as $t => $section) {
346
					if ($t === $type) {
347
						try {
348
							$sections[] = \OC::$server->query($section);
349
						} catch (QueryException $e) {
350
							$this->logger->error('Settings section not found: '.$section);
351
						}
352
					}
353
				}
354
			}
355
		}
356
		return $sections;
357
	}
358
359
	/**
360
	 * Attempts to load a ISettings using the class name
361
	 * @param string $className
362
	 * @throws QueryException
363
	 * @return ISettings
364
	 */
365
	protected function loadPanel($className) {
366
		try {
367
			if (!$panel = $this->getBuiltInPanel($className)) {
368
				$panel = \OC::$server->query($className);
369
			}
370
			if (!$panel instanceof ISettings) {
371
				$this->log->error(
372
					'Class: {class} not an instance of OCP\Settings\ISettings',
373
					['class' => $className]);
374
			} else {
375
				return $panel;
376
			}
377
		} catch (QueryException $e) {
378
			$this->log->error(
379
				'Failed to load panel: {class} with error: {error}',
380
				[
381
					'class' => $className,
382
					'error' => $e->getMessage()
383
				]);
384
			throw $e;
385
		}
386
	}
387
388
	/**
389
	 * Find and return ISettings for the given type
390
	 * @param string $type of panels to load
391
	 * @return array of ISettings
392
	 */
393
	public function loadPanels($type) {
394
		// If already loaded just return
395
		if (!empty($this->panels[$type])) {
396
			return $this->panels[$type];
397
		}
398
		// Find the panels from info xml
399
		$panels = $this->getPanelsList($type);
400
		// Load the classes using the server container
401
		if (empty($panels)) {
402
			return [];
403
		}
404
		foreach ($panels as $panelClassName) {
405
			// Attempt to load the panel
406
			try {
407
				$panel = $this->loadPanel($panelClassName);
408
				$section = $this->loadSection($type, $panel->getSectionID());
409
				$this->panels[$type][$section->getID()][] = $panel;
410
				$this->sections[$type][$section->getID()] = $section;
411
				// Now try and initialise the ISection from the panel
412
			} catch (QueryException $e) {
413
				// Just skip this panel, either its section or panel could not be loaded
414
			}
415
		}
416
		// Return the panel array sorted
417
		foreach ($this->panels[$type] as $sectionID => $section) {
418
			$this->panels[$type][$sectionID] = $this->sortOrder($this->panels[$type][$sectionID]);
419
		}
420
		// sort section array
421
		$this->sections[$type] = $this->sortOrder($this->sections[$type]);
422
		return $this->panels[$type];
423
	}
424
425
	/**
426
	 * Return the section object for the corresponding type and sectionID
427
	 * @param string $type
428
	 * @param string $sectionID
429
	 * @throws QueryException
430
	 * @return ISection
431
	 */
432
	protected function loadSection($type, $sectionID) {
433
		// Load built in sections
434
		foreach ($this->getBuiltInSections($type) as $section) {
0 ignored issues
show
Bug introduced by
The expression $this->getBuiltInSections($type) of type array<integer,object<OC\...tings\\Section>"}>|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
435
			if ($section->getID() === $sectionID) {
436
				return $section;
437
			}
438
		}
439
440
		// Load sections from registered list
441
		foreach ($this->findRegisteredSections($type) as $section) {
442
			if ($section->getID() === $sectionID) {
443
				return $section;
444
			}
445
		}
446
447
		$this->log->error('Section id not found: "'.$sectionID.'". Apps should register settings sections in info.xml');
448
		throw new QueryException();
449
	}
450
451
	/**
452
	 * Sort the array of ISettings or ISections by their priority attribute
453
	 * @param array $objects (ISections of ISettings)
454
	 * @return array
455
	 */
456
	protected function sortOrder($objects) {
457
		\usort($objects, function ($a, $b) {
458
			/** @var ISection | ISettings $a */
459
			/** @var ISection | ISettings $b */
460
			return $a->getPriority() < $b->getPriority();
461
		});
462
		return $objects;
463
	}
464
}
465