Completed
Pull Request — master (#841)
by Blizzz
10:54 queued 01:57
created

Manager   B

Complexity

Total Complexity 42

Size/Duplication

Total Lines 309
Duplicated Lines 39.16 %

Coupling/Cohesion

Components 1
Dependencies 16

Importance

Changes 4
Bugs 1 Features 0
Metric Value
dl 121
loc 309
rs 7.0366
c 4
b 1
f 0
wmc 42
lcom 1
cbo 16

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 15 1
A setupSettings() 0 8 3
B setupAdminSection() 25 25 5
A addAdminSection() 7 7 1
A addAdminSettings() 7 7 1
A add() 0 8 1
A updateAdminSettings() 11 11 1
A updateAdminSection() 11 11 1
A update() 0 11 2
A hasAdminSection() 0 3 1
A hasAdminSettings() 0 3 1
A has() 13 13 1
B setupAdminSettings() 27 27 5
A query() 0 8 2
B getAdminSections() 10 32 4
C getBuiltInAdminSettings() 0 33 7
B getAdminSettingsFromDB() 10 22 4
A getAdminSettings() 0 5 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Manager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Manager, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016 Arthur Schiwon <[email protected]>
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 *
7
 * @license GNU AGPL version 3 or any later version
8
 *
9
 * This program is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Affero General Public License as
11
 * published by the Free Software Foundation, either version 3 of the
12
 * License, or (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 *
22
 */
23
24
namespace OC\Settings;
25
26
use OCP\AppFramework\QueryException;
27
use OCP\Encryption\IManager as EncryptionManager;
28
use OCP\IConfig;
29
use OCP\IDBConnection;
30
use OCP\IL10N;
31
use OCP\ILogger;
32
use OCP\IUserManager;
33
use OCP\Settings\ISettings;
34
use OCP\Settings\IManager;
35
use OCP\Settings\ISection;
36
37
class Manager implements IManager {
38
	const TABLE_ADMIN_SETTINGS = 'admin_settings';
39
	const TABLE_ADMIN_SECTIONS = 'admin_sections';
40
41
	/** @var ILogger */
42
	/** @var ILogger */
43
	private $log;
44
45
	/** @var IDBConnection */
46
	private $dbc;
47
48
	/** @var IL10N */
49
	private $l;
50
51
	/** @var IConfig */
52
	private $config;
53
54
	/** @var EncryptionManager */
55
	private $encryptionManager;
56
57
	/** @var IUserManager */
58
	private $userManager;
59
60
	public function __construct(
61
		ILogger $log,
62
		IDBConnection $dbc,
63
		IL10N $l,
64
		IConfig $config,
65
		EncryptionManager $encryptionManager,
66
		IUserManager $userManager
67
	) {
68
		$this->log = $log;
69
		$this->dbc = $dbc;
70
		$this->l = $l;
71
		$this->config = $config;
72
		$this->encryptionManager = $encryptionManager;
73
		$this->userManager = $userManager;
74
	}
75
76
	/**
77
	 * @inheritdoc
78
	 */
79
	public function setupSettings(array $settings) {
80
		if(isset($settings[IManager::KEY_ADMIN_SECTION])) {
81
			$this->setupAdminSection($settings[IManager::KEY_ADMIN_SECTION]);
82
		}
83
		if(isset($settings[IManager::KEY_ADMIN_SETTINGS])) {
84
			$this->setupAdminSettings($settings[IManager::KEY_ADMIN_SETTINGS]);
85
		}
86
	}
87
88 View Code Duplication
	private function setupAdminSection($sectionClassName) {
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...
89
		if(!class_exists($sectionClassName)) {
90
			$this->log->debug('Could not find admin section class ' . $sectionClassName);
91
			return;
92
		}
93
		try {
94
			$section = $this->query($sectionClassName);
95
		} catch (QueryException $e) {
96
			// cancel
97
			return;
98
		}
99
100
		if(!$section instanceof ISection) {
101
			$this->log->error(
102
				'Admin section instance must implement \OCP\ISection. Invalid class: {class}',
103
				['class' => $sectionClassName]
104
			);
105
			return;
106
		}
107
		if(!$this->hasAdminSection(get_class($section))) {
108
			$this->addAdminSection($section);
109
		} else {
110
			$this->updateAdminSection($section);
111
		}
112
	}
113
114 View Code Duplication
	private function addAdminSection(ISection $section) {
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...
115
		$this->add(self::TABLE_ADMIN_SECTIONS, [
116
			'id' => $section->getID(),
117
			'class' => get_class($section),
118
			'priority' => $section->getPriority(),
119
		]);
120
	}
121
122 View Code Duplication
	private function addAdminSettings(ISettings $settings) {
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...
123
		$this->add(self::TABLE_ADMIN_SETTINGS, [
124
			'class' => get_class($settings),
125
			'section' => $settings->getSection(),
126
			'priority' => $settings->getPriority(),
127
		]);
128
	}
129
130
	private function add($table, $values) {
131
		$query = $this->dbc->getQueryBuilder();
132
		$values = array_map(function($value) use ($query) {
133
			return $query->createNamedParameter($value);
134
		}, $values);
135
		$query->insert($table)->values($values);
136
		$query->execute();
137
	}
138
139 View Code Duplication
	private function updateAdminSettings(ISettings $settings) {
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...
140
		$this->update(
141
			self::TABLE_ADMIN_SETTINGS,
142
			'class',
143
			get_class($settings),
144
			[
145
				'section' => $settings->getSection(),
146
				'priority' => $settings->getPriority(),
147
			]
148
		);
149
	}
150
151 View Code Duplication
	private function updateAdminSection(ISection $section) {
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...
152
		$this->update(
153
			self::TABLE_ADMIN_SECTIONS,
154
			'class',
155
			get_class($section),
156
			[
157
				'id'       => $section->getID(),
158
				'priority' => $section->getPriority(),
159
			]
160
		);
161
	}
162
163
	private function update($table, $idCol, $id, $values) {
164
		$query = $this->dbc->getQueryBuilder();
165
		$query->update($table);
166
		foreach($values as $key => $value) {
167
			$query->set($key, $query->createNamedParameter($value));
168
		}
169
		$query
170
			->where($query->expr()->eq($idCol, $query->createParameter($idCol)))
171
			->setParameter($idCol, $id)
172
			->execute();
173
	}
174
175
	/**
176
	 * @param string $className
177
	 * @return bool
178
	 */
179
	private function hasAdminSection($className) {
180
		return $this->has(self::TABLE_ADMIN_SECTIONS, $className);
181
	}
182
183
	/**
184
	 * @param string $className
185
	 * @return bool
186
	 */
187
	private function hasAdminSettings($className) {
188
		return $this->has(self::TABLE_ADMIN_SETTINGS, $className);
189
	}
190
191
192 View Code Duplication
	private function has($table, $className) {
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...
193
		$query = $this->dbc->getQueryBuilder();
194
		$query->select('class')
195
			->from($table)
196
			->where($query->expr()->eq('class', $query->createNamedParameter($className)))
197
			->setMaxResults(1);
198
199
		$result = $query->execute();
200
		$row = $result->fetch();
201
		$result->closeCursor();
202
203
		return (bool) $row;
204
	}
205
206 View Code Duplication
	private function setupAdminSettings($settingsClassName) {
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...
207
		if(!class_exists($settingsClassName)) {
208
			$this->log->debug('Could not find admin section class ' . $settingsClassName);
209
			return;
210
		}
211
212
		try {
213
			/** @var ISettings $settings */
214
			$settings = $this->query($settingsClassName);
215
		} catch (QueryException $e) {
216
			// cancel
217
			return;
218
		}
219
220
		if(!$settings instanceof ISettings) {
221
			$this->log->error(
222
				'Admin section instance must implement \OCP\ISection. Invalid class: {class}',
223
				['class' => $settingsClassName]
224
			);
225
			return;
226
		}
227
		if(!$this->hasAdminSettings(get_class($settings))) {
228
			$this->addAdminSettings($settings);
229
		} else {
230
			$this->updateAdminSettings($settings);
231
		}
232
	}
233
234
	private function query($className) {
235
		try {
236
			return \OC::$server->query($className);
237
		} catch (QueryException $e) {
238
			$this->log->logException($e);
239
			throw $e;
240
		}
241
	}
242
243
	/**
244
	 * returns a list of the admin sections
245
	 *
246
	 * @return ISection[]
247
	 */
248
	public function getAdminSections() {
249
		$query = $this->dbc->getQueryBuilder();
250
		$query->select(['class', 'priority'])
251
			->from(self::TABLE_ADMIN_SECTIONS);
252
253
		// built-in sections
254
		$sections = [
255
			 0 => [new Section('server',        $this->l->t('Server Settings'), 0)],
256
			 5 => [new Section('sharing',       $this->l->t('Sharing'), 0)],
257
			15 => [new Section('collaboration', $this->l->t('Collaboration'), 0)],
258
			45 => [new Section('encryption',    $this->l->t('Encryption'), 0)],
259
			90 => [new Section('logging',       $this->l->t('Logging'), 0)],
260
			98 => [new Section('additional',    $this->l->t('Additional Settings'), 0)],
261
			99 => [new Section('tips-tricks',   $this->l->t('Tips & Tricks'), 0)],
262
		];
263
264
		$result = $query->execute();
265 View Code Duplication
		while($row = $result->fetch()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
266
			if(!isset($sections[$row['priority']])) {
267
				$sections[$row['priority']] = [];
268
			}
269
			try {
270
				$sections[$row['priority']][] = $this->query($row['class']);
271
			} catch (QueryException $e) {
272
				// skip
273
			}
274
		}
275
		$result->closeCursor();
276
277
		ksort($sections);
278
		return $sections;
279
	}
280
281
	private function getBuiltInAdminSettings($section) {
282
		$forms = [];
283
		try {
284
			if($section === 'server') {
285
				/** @var ISettings $form */
286
				$form = new Admin\Server($this->dbc, $this->config);
287
				$forms[$form->getPriority()] = [$form];
288
			}
289
			if($section === 'encryption') {
290
				/** @var ISettings $form */
291
				$form = new Admin\Encryption($this->encryptionManager, $this->userManager);
0 ignored issues
show
Compatibility introduced by
$this->encryptionManager of type object<OCP\Encryption\IManager> is not a sub-type of object<OC\Encryption\Manager>. It seems like you assume a concrete implementation of the interface OCP\Encryption\IManager to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
292
				$forms[$form->getPriority()] = [$form];
293
			}
294
			if($section === 'sharing') {
295
				/** @var ISettings $form */
296
				$form = new Admin\Sharing($this->config);
297
				$forms[$form->getPriority()] = [$form];
298
			}
299
			if($section === 'logging') {
300
				/** @var ISettings $form */
301
				$form = new Admin\Logging($this->config);
302
				$forms[$form->getPriority()] = [$form];
303
			}
304
			if($section === 'tips-tricks') {
305
				/** @var ISettings $form */
306
				$form = new Admin\TipsTricks($this->config);
307
				$forms[$form->getPriority()] = [$form];
308
			}
309
		} catch (QueryException $e) {
310
			// skip
311
		}
312
		return $forms;
313
	}
314
315
	private function getAdminSettingsFromDB($section, &$settings) {
316
		$query = $this->dbc->getQueryBuilder();
317
		$query->select(['class', 'priority'])
318
			->from(self::TABLE_ADMIN_SETTINGS)
319
			->where($query->expr()->eq('section', $this->dbc->getQueryBuilder()->createParameter('section')))
320
			->setParameter('section', $section);
321
322
		$result = $query->execute();
323 View Code Duplication
		while($row = $result->fetch()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
324
			if(!isset($settings[$row['priority']])) {
325
				$settings[$row['priority']] = [];
326
			}
327
			try {
328
				$settings[$row['priority']][] = $this->query($row['class']);
329
			} catch (QueryException $e) {
330
				// skip
331
			}
332
		}
333
		$result->closeCursor();
334
335
		ksort($settings);
336
	}
337
338
	public function getAdminSettings($section) {
339
		$settings = $this->getBuiltInAdminSettings($section);
340
		$this->getAdminSettingsFromDB($section, $settings);
341
		return $settings;
342
	}
343
344
345
}
346