Completed
Push — master ( f6005e...7ab4f4 )
by Thomas
27:01
created

SettingsManager::getBuiltInSections()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 20
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 24
rs 8.9713
1
<?php
2
/**
3
 * @author Tom Needham <[email protected]>
4
 *
5
 * @copyright Copyright (c) 2017, 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\Quota;
50
use OC\Settings\Panels\Admin\BackgroundJobs;
51
use OC\Settings\Panels\Admin\Certificates;
52
use OC\Settings\Panels\Admin\Encryption;
53
use OC\Settings\Panels\Admin\FileSharing;
54
use OC\Settings\Panels\Admin\Mail;
55
use OC\Settings\Panels\Admin\Logging;
56
use OC\Settings\Panels\Admin\SecurityWarning;
57
use OC\Settings\Panels\Admin\Updater;
58
use OC\Settings\Panels\Admin\Tips;
59
use OC\Settings\Panels\Admin\Status;
60
61
/*
62
 * @since 10.0
63
 */
64
class SettingsManager implements ISettingsManager {
65
66
	/** @var IL10N */
67
	protected $l;
68
	/** @var IAppManager */
69
	protected $appManager;
70
	/** @var ILogger */
71
	protected $logger;
72
	/** @var IURLGenerator */
73
	protected $urlGenerator;
74
	/** @var Defaults  */
75
	protected $defaults;
76
	/** @var IUserSession  */
77
	protected $userSession;
78
	/** @var ILogger  */
79
	protected $log;
80
	/** @var IConfig  */
81
	protected $config;
82
	/** @var IGroupManager  */
83
	protected $groupManager;
84
	/** @var Helper */
85
	protected $helper;
86
	/** @var IFactory  */
87
	protected $lfactory;
88
	/** @var IDBConnection  */
89
	protected $dbconnection;
90
	/** @var ILockingProvider  */
91
	protected $lockingProvider;
92
	/** @var CertificateManager  */
93
	protected $certificateManager;
94
95
	/**
96
	 * Holds a cache of ISettings with keys for type
97
	 */
98
	protected $panels = [];
99
100
	/**
101
	 * Holds an array of sections
102
	 */
103
	protected $sections = [];
104
105
	/**
106
	 * @param IL10N $l
107
	 * @param IAppManager $appManager
108
	 * @param IUserSession $userSession
109
	 * @param ILogger $logger
110
	 * @param IGroupManager $groupManager
111
	 * @param IConfig $config
112
	 * @param Defaults $defaults
113
	 * @param IURLGenerator $urlGenerator
114
	 * @param Helper $helper
115
	 * @param ILockingProvider $lockingProvider
116
	 * @param IDBConnection $dbconnection
117
	 * @param CertificateManager $certificateManager
118
	 * @param IFactory $lfactory
119
	 */
120
	public function __construct(IL10N $l,
121
								IAppManager $appManager,
122
								IUserSession $userSession,
123
								ILogger $logger,
124
								IGroupManager $groupManager,
125
								IConfig $config,
126
								Defaults $defaults,
127
								IURLGenerator $urlGenerator,
128
								Helper $helper,
129
								ILockingProvider $lockingProvider,
130
								IDBConnection $dbconnection,
131
								$certificateManager,
132
								IFactory $lfactory) {
133
		$this->l = $l;
134
		$this->appManager = $appManager;
135
		$this->userSession = $userSession;
136
		$this->config = $config;
137
		$this->groupManager = $groupManager;
138
		$this->log = $logger;
139
		$this->defaults = $defaults;
140
		$this->urlGenerator = $urlGenerator;
141
		$this->helper = $helper;
142
		$this->lockingProvider = $lockingProvider;
143
		$this->dbconnection = $dbconnection;
144
		$this->certificateManager = $certificateManager;
145
		$this->lfactory = $lfactory;
146
	}
147
148
	public function getPersonalSections() {
149
		// Trigger a load of all personal panels to discover sections
150
		$this->loadPanels('personal');
151
		return $this->sections['personal'];
152
	}
153
154
	public function getAdminSections() {
155
		// Trigger a load of all admin panels to discover sections
156
		$this->loadPanels('admin');
157
		return $this->sections['admin'];
158
	}
159
160
	/**
161
	 * Returns ISettings for the personal settings in the given section
162
	 * @param string $sectionID
163
	 * @return array of ISection
164
	 */
165 View Code Duplication
	public function getPersonalPanels($sectionID) {
166
		// Trigger a load of all personal panels to discover sections
167
		$this->loadPanels('personal');
168
		if(isset($this->panels['personal'][$sectionID])) {
169
			return $this->panels['personal'][$sectionID];
170
		} else {
171
			return [];
172
		}
173
	}
174
175
	/**
176
	 * Returns ISettings for the admin settings in the given section
177
	 * @param string $sectionID
178
	 * @return array of ISection
179
	 */
180 View Code Duplication
	public function getAdminPanels($sectionID) {
181
		// Trigger a load of all admin panels to discover sections
182
		$this->loadPanels('admin');
183
		if(isset($this->panels['admin'][$sectionID])) {
184
			return $this->panels['admin'][$sectionID];
185
		} else {
186
			return [];
187
		}
188
	}
189
190
	/**
191
	 * Returns the default set of ISections used in core
192
	 * @param string $type the type of sections to return
193
	 * @return array of ISection
194
	 */
195
	private function getBuiltInSections($type) {
196
		if($type === 'admin') {
197
			return [
198
				new Section('general', $this->l->t('General'), 100),
199
				new Section('storage', $this->l->t('Storage'), 95, 'folder'),
200
				new Section('security', $this->l->t('Security'), 90, 'password'),
201
				new Section('authentication', $this->l->t('Authentication'), 87, 'user'),
202
				new Section('encryption', $this->l->t('Encryption'), 85, 'password'),
203
				new Section('sharing', $this->l->t('Sharing'), 80, 'share'),
204
				new Section('monitoring', $this->l->t('Monitoring'), 75, 'search'),
205
				new Section('apps', $this->l->t('Apps'), 70, 'list'),
206
				new Section('updates', $this->l->t('Updates'), 20, 'update'),
207
				new Section('additional', $this->l->t('Additional'), -10, 'more'),
208
			];
209
		} else if($type === 'personal') {
210
			return [
211
				new Section('general', $this->l->t('General'), 100, 'user'),
212
				new Section('storage', $this->l->t('Storage'), 50, 'folder'),
213
				new Section('security', $this->l->t('Security'), 30, 'password'),
214
				new Section('encryption', $this->l->t('Encryption'), 20),
215
				new Section('additional', $this->l->t('Additional'), 5, 'more'),
216
			];
217
		}
218
	}
219
220
	/**
221
	 * Returns an array of classnames for built in settings panels
222
	 * @return array of strings
223
	 */
224
	private function getBuiltInPanels() {
225
		return [
226
			'personal' => [
227
				Profile::class,
228
				Clients::class,
229
				LegacyPersonal::class,
230
				Version::class,
231
				Tokens::class,
232
				Quota::class,
233
			],
234
			'admin' => [
235
				LegacyAdmin::class,
236
				BackgroundJobs::class,
237
				Logging::class,
238
				Tips::class,
239
				SecurityWarning::class,
240
				Mail::class,
241
				FileSharing::class,
242
				Encryption::class,
243
				Certificates::class,
244
				Apps::class,
245
				Status::class
246
			]
247
		];
248
	}
249
250
	/**
251
	 * Gets panel objects with dependencies instantiated from the container
252
	 * @param string $className
253
	 * @return array|false
254
	 */
255
	public function getBuiltInPanel($className) {
256
		$panels = [
257
			// Personal
258
			Profile::class => new Profile(
259
				$this->config,
260
				$this->groupManager,
261
				$this->userSession,
262
				$this->lfactory),
263
			LegacyPersonal::class => new LegacyPersonal($this->helper),
264
			Clients::class => new Clients($this->config, $this->defaults),
265
			Version::class => new Version(),
266
			Tokens::class => new Tokens(),
267
			Quota::class => new Quota($this->helper),
268
			// Admin
269
			BackgroundJobs::class => new BackgroundJobs($this->config),
270
			Certificates::class => new Certificates(
271
				$this->config,
272
				$this->urlGenerator,
273
				$this->certificateManager),
274
			Encryption::class => new Encryption(),
275
			FileSharing::class => new FileSharing($this->config, $this->helper),
276
			Logging::class => new Logging($this->config, $this->urlGenerator, $this->helper),
277
			Mail::class => new Mail($this->config, $this->helper),
278
			SecurityWarning::class => new SecurityWarning(
279
				$this->l,
280
				$this->config,
281
				$this->dbconnection,
282
				$this->helper,
283
				$this->lockingProvider),
284
			Tips::class => new Tips(),
285
			LegacyAdmin::class => new LegacyAdmin($this->helper),
286
			Apps::class => new Apps($this->config)
287
		];
288
		if(isset($panels[$className])) {
289
			return $panels[$className];
290
		} else {
291
			return false;
292
		}
293
	}
294
295
	/**
296
	 * Gets all the panels for ownCloud
297
	 * @param string $type the type of sections to return
298
	 * @return array of strings
299
	 */
300
	public function getPanelsList($type) {
301
		$registered = isset($this->findRegisteredPanels()[$type]) ? $this->findRegisteredPanels()[$type] : [];
302
		$builtIn = isset($this->getBuiltInPanels()[$type]) ? $this->getBuiltInPanels()[$type] : [];
303
		return array_merge($registered, $builtIn);
304
	}
305
306
307
	/**
308
	 * Searches through the currently enabled apps and returns the panels registered
309
	 * @return array of strings
310
	 */
311
	protected function findRegisteredPanels() {
312
		$panels = [];
313
		foreach($this->appManager->getEnabledAppsForUser($this->userSession->getUser()) as $app) {
0 ignored issues
show
Bug introduced by
It seems like $this->userSession->getUser() can be null; however, getEnabledAppsForUser() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
314
			if(isset($this->appManager->getAppInfo($app)['settings'])) {
315
				foreach($this->appManager->getAppInfo($app)['settings'] as $type => $panel) {
316
					$panels[$type][] = (string) $panel;
317
				}
318
			}
319
		}
320
		return $panels;
321
	}
322
323
	/**
324
	 * Attempts to load a ISettings using the class name
325
	 * @param string $className
326
	 * @throws QueryException
327
	 * @return ISettings
328
	 */
329
	protected function loadPanel($className) {
330
		try {
331
			if(!$panel = $this->getBuiltInPanel($className)) {
332
				$panel = \OC::$server->query($className);
333
			}
334
			if(!$panel instanceof ISettings) {
335
				$this->log->error(
336
					'Class: {class} not an instance of OCP\Settings\ISettings',
337
					['class' => $className]);
338
			} else {
339
				return $panel;
340
			}
341
		} catch (QueryException $e) {
342
			$this->log->error(
343
				'Failed to load panel: {class} with error: {error}',
344
				[
345
					'class' => $className,
346
					'error' => $e->getMessage()
347
				]);
348
			throw $e;
349
		}
350
	}
351
352
	/**
353
	 * Find and return ISettings for the given type
354
	 * @param string $type of panels to load
355
	 * @return array of ISettings
356
	 */
357
	public function loadPanels($type) {
358
		// If already loaded just return
359
		if(!empty($this->panels[$type])) {
360
			return $this->panels[$type];
361
		}
362
		// Find the panels from info xml
363
		$panels = $this->getPanelsList($type);
364
		// Load the classes using the server container
365
		if(empty($panels)) {
366
			return [];
367
		}
368
		foreach($panels as $panelClassName) {
369
			// Attempt to load the panel
370
			try {
371
				$panel = $this->loadPanel($panelClassName);
372
				$section = $this->loadSection($type, $panel->getSectionID());
373
				$this->panels[$type][$section->getID()][] = $panel;
374
				$this->sections[$type][$section->getID()] = $section;
375
				// Now try and initialise the ISection from the panel
376
			} catch (QueryException $e) {
377
				// Just skip this panel, either its section of panel could not be loaded
378
			}
379
		}
380
		// Return the panel array sorted
381
		foreach($this->panels[$type] as $sectionID => $section) {
382
			$this->panels[$type][$sectionID] = $this->sortOrder($this->panels[$type][$sectionID]);
383
		}
384
		// sort section array
385
		$this->sections[$type] = $this->sortOrder($this->sections[$type]);
386
		return $this->panels[$type];
387
	}
388
389
	/**
390
	 * Return the section object for the corresponding type and sectionID
391
	 * @param string $type
392
	 * @param string $sectionID
393
	 * @throws QueryException
394
	 * @return ISection
395
	 */
396
	protected function loadSection($type, $sectionID) {
397
		// Load sections from default list
398
		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...
399
			if($section->getID() === $sectionID) {
400
				return $section;
401
			}
402
		}
403
		$this->log->error(
404
			'Failed to load section with id: {id}',
405
			['id' => $sectionID]);
406
		return new Section($sectionID, ucfirst($sectionID), -9);
407
	}
408
409
	/**
410
	 * Sort the array of ISettings or ISections by their priority attribute
411
	 * @param array $objects (ISections of ISettings)
412
	 * @return array
413
	 */
414
	protected function sortOrder($objects) {
415
		usort($objects, function($a, $b) {
416
			/** @var ISection | ISettings $a */
417
			/** @var ISection | ISettings $b */
418
			return $a->getPriority() < $b->getPriority();
419
		});
420
		return $objects;
421
	}
422
423
}
424