Completed
Push — master ( 281937...5a0dc1 )
by Blizzz
34s
created

AllConfig   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 394
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 5

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 394
rs 8.3396
wmc 44
lcom 2
cbo 5

21 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A fixDIInit() 0 5 2
A setSystemValues() 0 3 1
A setSystemValue() 0 3 1
A getSystemValue() 0 3 1
A getFilteredSystemValue() 0 3 1
A deleteSystemValue() 0 3 1
A getAppKeys() 0 3 1
A setAppValue() 0 3 1
A getAppValue() 0 3 1
A deleteAppValue() 0 3 1
A deleteAppValues() 0 3 1
A getUserValue() 0 8 3
A getUserKeys() 0 8 2
A deleteUserValue() 0 12 3
A deleteAllUserValues() 0 10 1
A deleteAppFromAllUsers() 0 12 2
A getUserValues() 0 20 4
B getUserValueForUsers() 0 33 6
A getUsersForUserValue() 0 23 3
C setUserValue() 0 31 7

How to fix   Complexity   

Complex Class

Complex classes like AllConfig 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 AllConfig, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @author Bart Visscher <[email protected]>
4
 * @author Joas Schilling <[email protected]>
5
 * @author Lukas Reschke <[email protected]>
6
 * @author Morris Jobke <[email protected]>
7
 * @author Robin Appelman <[email protected]>
8
 * @author Robin McCorkell <[email protected]>
9
 * @author Thomas Müller <[email protected]>
10
 * @author Vincent Petry <[email protected]>
11
 *
12
 * @copyright Copyright (c) 2016, ownCloud, Inc.
13
 * @license AGPL-3.0
14
 *
15
 * This code is free software: you can redistribute it and/or modify
16
 * it under the terms of the GNU Affero General Public License, version 3,
17
 * as published by the Free Software Foundation.
18
 *
19
 * This program is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
 * GNU Affero General Public License for more details.
23
 *
24
 * You should have received a copy of the GNU Affero General Public License, version 3,
25
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
26
 *
27
 */
28
29
namespace OC;
30
use OCP\IDBConnection;
31
use OCP\PreConditionNotMetException;
32
33
/**
34
 * Class to combine all the configuration options ownCloud offers
35
 */
36
class AllConfig implements \OCP\IConfig {
37
	/** @var SystemConfig */
38
	private $systemConfig;
39
40
	/** @var IDBConnection */
41
	private $connection;
42
43
	/**
44
	 * 3 dimensional array with the following structure:
45
	 * [ $userId =>
46
	 *     [ $appId =>
47
	 *         [ $key => $value ]
48
	 *     ]
49
	 * ]
50
	 *
51
	 * database table: preferences
52
	 *
53
	 * methods that use this:
54
	 *   - setUserValue
55
	 *   - getUserValue
56
	 *   - getUserKeys
57
	 *   - deleteUserValue
58
	 *   - deleteAllUserValues
59
	 *   - deleteAppFromAllUsers
60
	 *
61
	 * @var array $userCache
62
	 */
63
	private $userCache = array();
64
65
	/**
66
	 * @param SystemConfig $systemConfig
67
	 */
68
	function __construct(SystemConfig $systemConfig) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
69
		$this->systemConfig = $systemConfig;
70
	}
71
72
	/**
73
	 * TODO - FIXME This fixes an issue with base.php that cause cyclic
74
	 * dependencies, especially with autoconfig setup
75
	 *
76
	 * Replace this by properly injected database connection. Currently the
77
	 * base.php triggers the getDatabaseConnection too early which causes in
78
	 * autoconfig setup case a too early distributed database connection and
79
	 * the autoconfig then needs to reinit all already initialized dependencies
80
	 * that use the database connection.
81
	 *
82
	 * otherwise a SQLite database is created in the wrong directory
83
	 * because the database connection was created with an uninitialized config
84
	 */
85
	private function fixDIInit() {
86
		if($this->connection === null) {
87
			$this->connection = \OC::$server->getDatabaseConnection();
88
		}
89
	}
90
91
	/**
92
	 * Sets and deletes system wide values
93
	 *
94
	 * @param array $configs Associative array with `key => value` pairs
95
	 *                       If value is null, the config key will be deleted
96
	 */
97
	public function setSystemValues(array $configs) {
98
		$this->systemConfig->setValues($configs);
99
	}
100
101
	/**
102
	 * Sets a new system wide value
103
	 *
104
	 * @param string $key the key of the value, under which will be saved
105
	 * @param mixed $value the value that should be stored
106
	 */
107
	public function setSystemValue($key, $value) {
108
		$this->systemConfig->setValue($key, $value);
109
	}
110
111
	/**
112
	 * Looks up a system wide defined value
113
	 *
114
	 * @param string $key the key of the value, under which it was saved
115
	 * @param mixed $default the default value to be returned if the value isn't set
116
	 * @return mixed the value or $default
117
	 */
118
	public function getSystemValue($key, $default = '') {
119
		return $this->systemConfig->getValue($key, $default);
120
	}
121
122
	/**
123
	 * Looks up a system wide defined value and filters out sensitive data
124
	 *
125
	 * @param string $key the key of the value, under which it was saved
126
	 * @param mixed $default the default value to be returned if the value isn't set
127
	 * @return mixed the value or $default
128
	 */
129
	public function getFilteredSystemValue($key, $default = '') {
130
		return $this->systemConfig->getFilteredValue($key, $default);
131
	}
132
133
	/**
134
	 * Delete a system wide defined value
135
	 *
136
	 * @param string $key the key of the value, under which it was saved
137
	 */
138
	public function deleteSystemValue($key) {
139
		$this->systemConfig->deleteValue($key);
140
	}
141
142
	/**
143
	 * Get all keys stored for an app
144
	 *
145
	 * @param string $appName the appName that we stored the value under
146
	 * @return string[] the keys stored for the app
147
	 */
148
	public function getAppKeys($appName) {
149
		return \OC::$server->getAppConfig()->getKeys($appName);
0 ignored issues
show
Deprecated Code introduced by
The method OCP\IAppConfig::getKeys() has been deprecated with message: 8.0.0 use method getAppKeys of \OCP\IConfig This function gets all keys of an app. Please note that the values are
not returned.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
150
	}
151
152
	/**
153
	 * Writes a new app wide value
154
	 *
155
	 * @param string $appName the appName that we want to store the value under
156
	 * @param string $key the key of the value, under which will be saved
157
	 * @param string|float|int $value the value that should be stored
158
	 */
159
	public function setAppValue($appName, $key, $value) {
160
		\OC::$server->getAppConfig()->setValue($appName, $key, $value);
0 ignored issues
show
Deprecated Code introduced by
The method OCP\IAppConfig::setValue() has been deprecated with message: 8.0.0 use method setAppValue of \OCP\IConfig Sets a value. If the key did not exist before it will be created.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
161
	}
162
163
	/**
164
	 * Looks up an app wide defined value
165
	 *
166
	 * @param string $appName the appName that we stored the value under
167
	 * @param string $key the key of the value, under which it was saved
168
	 * @param string $default the default value to be returned if the value isn't set
169
	 * @return string the saved value
170
	 */
171
	public function getAppValue($appName, $key, $default = '') {
172
		return \OC::$server->getAppConfig()->getValue($appName, $key, $default);
0 ignored issues
show
Deprecated Code introduced by
The method OCP\IAppConfig::getValue() has been deprecated with message: 8.0.0 use method getAppValue of \OCP\IConfig This function gets a value from the appconfig table. If the key does
not exist the default value will be returned

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
173
	}
174
175
	/**
176
	 * Delete an app wide defined value
177
	 *
178
	 * @param string $appName the appName that we stored the value under
179
	 * @param string $key the key of the value, under which it was saved
180
	 */
181
	public function deleteAppValue($appName, $key) {
182
		\OC::$server->getAppConfig()->deleteKey($appName, $key);
0 ignored issues
show
Deprecated Code introduced by
The method OCP\IAppConfig::deleteKey() has been deprecated with message: 8.0.0 use method deleteAppValue of \OCP\IConfig

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
183
	}
184
185
	/**
186
	 * Removes all keys in appconfig belonging to the app
187
	 *
188
	 * @param string $appName the appName the configs are stored under
189
	 */
190
	public function deleteAppValues($appName) {
191
		\OC::$server->getAppConfig()->deleteApp($appName);
0 ignored issues
show
Deprecated Code introduced by
The method OCP\IAppConfig::deleteApp() has been deprecated with message: 8.0.0 use method deleteAppValue of \OCP\IConfig Removes all keys in appconfig belonging to the app.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
192
	}
193
194
195
	/**
196
	 * Set a user defined value
197
	 *
198
	 * @param string $userId the userId of the user that we want to store the value under
199
	 * @param string $appName the appName that we want to store the value under
200
	 * @param string $key the key under which the value is being stored
201
	 * @param string|float|int $value the value that you want to store
202
	 * @param string $preCondition only update if the config value was previously the value passed as $preCondition
203
	 * @throws \OCP\PreConditionNotMetException if a precondition is specified and is not met
204
	 * @throws \UnexpectedValueException when trying to store an unexpected value
205
	 */
206
	public function setUserValue($userId, $appName, $key, $value, $preCondition = null) {
207
		if (!is_int($value) && !is_float($value) && !is_string($value)) {
208
			throw new \UnexpectedValueException('Only integers, floats and strings are allowed as value');
209
		}
210
211
		// TODO - FIXME
212
		$this->fixDIInit();
213
214
		$preconditionArray = [];
215
		if (isset($preCondition)) {
216
			$preconditionArray = [
217
				'configvalue' => $preCondition,
218
			];
219
		}
220
221
		$this->connection->setValues('preferences', [
222
			'userid' => $userId,
223
			'appid' => $appName,
224
			'configkey' => $key,
225
		], [
226
			'configvalue' => $value,
227
		], $preconditionArray);
228
229
		// only add to the cache if we already loaded data for the user
230
		if (isset($this->userCache[$userId])) {
231
			if (!isset($this->userCache[$userId][$appName])) {
232
				$this->userCache[$userId][$appName] = array();
233
			}
234
			$this->userCache[$userId][$appName][$key] = $value;
235
		}
236
	}
237
238
	/**
239
	 * Getting a user defined value
240
	 *
241
	 * @param string $userId the userId of the user that we want to store the value under
242
	 * @param string $appName the appName that we stored the value under
243
	 * @param string $key the key under which the value is being stored
244
	 * @param mixed $default the default value to be returned if the value isn't set
245
	 * @return string
246
	 */
247
	public function getUserValue($userId, $appName, $key, $default = '') {
248
		$data = $this->getUserValues($userId);
249
		if (isset($data[$appName]) and isset($data[$appName][$key])) {
250
			return $data[$appName][$key];
251
		} else {
252
			return $default;
253
		}
254
	}
255
256
	/**
257
	 * Get the keys of all stored by an app for the user
258
	 *
259
	 * @param string $userId the userId of the user that we want to store the value under
260
	 * @param string $appName the appName that we stored the value under
261
	 * @return string[]
262
	 */
263
	public function getUserKeys($userId, $appName) {
264
		$data = $this->getUserValues($userId);
265
		if (isset($data[$appName])) {
266
			return array_keys($data[$appName]);
267
		} else {
268
			return array();
269
		}
270
	}
271
272
	/**
273
	 * Delete a user value
274
	 *
275
	 * @param string $userId the userId of the user that we want to store the value under
276
	 * @param string $appName the appName that we stored the value under
277
	 * @param string $key the key under which the value is being stored
278
	 */
279
	public function deleteUserValue($userId, $appName, $key) {
280
		// TODO - FIXME
281
		$this->fixDIInit();
282
283
		$sql  = 'DELETE FROM `*PREFIX*preferences` '.
284
				'WHERE `userid` = ? AND `appid` = ? AND `configkey` = ?';
285
		$this->connection->executeUpdate($sql, array($userId, $appName, $key));
286
287
		if (isset($this->userCache[$userId]) and isset($this->userCache[$userId][$appName])) {
288
			unset($this->userCache[$userId][$appName][$key]);
289
		}
290
	}
291
292
	/**
293
	 * Delete all user values
294
	 *
295
	 * @param string $userId the userId of the user that we want to remove all values from
296
	 */
297
	public function deleteAllUserValues($userId) {
298
		// TODO - FIXME
299
		$this->fixDIInit();
300
301
		$sql  = 'DELETE FROM `*PREFIX*preferences` '.
302
			'WHERE `userid` = ?';
303
		$this->connection->executeUpdate($sql, array($userId));
304
305
		unset($this->userCache[$userId]);
306
	}
307
308
	/**
309
	 * Delete all user related values of one app
310
	 *
311
	 * @param string $appName the appName of the app that we want to remove all values from
312
	 */
313
	public function deleteAppFromAllUsers($appName) {
314
		// TODO - FIXME
315
		$this->fixDIInit();
316
317
		$sql  = 'DELETE FROM `*PREFIX*preferences` '.
318
				'WHERE `appid` = ?';
319
		$this->connection->executeUpdate($sql, array($appName));
320
321
		foreach ($this->userCache as &$userCache) {
322
			unset($userCache[$appName]);
323
		}
324
	}
325
326
	/**
327
	 * Returns all user configs sorted by app of one user
328
	 *
329
	 * @param string $userId the user ID to get the app configs from
330
	 * @return array[] - 2 dimensional array with the following structure:
331
	 *     [ $appId =>
332
	 *         [ $key => $value ]
333
	 *     ]
334
	 */
335
	private function getUserValues($userId) {
336
		// TODO - FIXME
337
		$this->fixDIInit();
338
339
		if (isset($this->userCache[$userId])) {
340
			return $this->userCache[$userId];
341
		}
342
		$data = array();
343
		$query = 'SELECT `appid`, `configkey`, `configvalue` FROM `*PREFIX*preferences` WHERE `userid` = ?';
344
		$result = $this->connection->executeQuery($query, array($userId));
345
		while ($row = $result->fetch()) {
346
			$appId = $row['appid'];
347
			if (!isset($data[$appId])) {
348
				$data[$appId] = array();
349
			}
350
			$data[$appId][$row['configkey']] = $row['configvalue'];
351
		}
352
		$this->userCache[$userId] = $data;
353
		return $data;
354
	}
355
356
	/**
357
	 * Fetches a mapped list of userId -> value, for a specified app and key and a list of user IDs.
358
	 *
359
	 * @param string $appName app to get the value for
360
	 * @param string $key the key to get the value for
361
	 * @param array $userIds the user IDs to fetch the values for
362
	 * @return array Mapped values: userId => value
363
	 */
364
	public function getUserValueForUsers($appName, $key, $userIds) {
365
		// TODO - FIXME
366
		$this->fixDIInit();
367
368
		if (empty($userIds) || !is_array($userIds)) {
369
			return array();
370
		}
371
372
		$chunkedUsers = array_chunk($userIds, 50, true);
373
		$placeholders50 = implode(',', array_fill(0, 50, '?'));
374
375
		$userValues = array();
376
		foreach ($chunkedUsers as $chunk) {
377
			$queryParams = $chunk;
378
			// create [$app, $key, $chunkedUsers]
379
			array_unshift($queryParams, $key);
380
			array_unshift($queryParams, $appName);
381
382
			$placeholders = (sizeof($chunk) == 50) ? $placeholders50 :  implode(',', array_fill(0, sizeof($chunk), '?'));
383
384
			$query    = 'SELECT `userid`, `configvalue` ' .
385
						'FROM `*PREFIX*preferences` ' .
386
						'WHERE `appid` = ? AND `configkey` = ? ' .
387
						'AND `userid` IN (' . $placeholders . ')';
388
			$result = $this->connection->executeQuery($query, $queryParams);
389
390
			while ($row = $result->fetch()) {
391
				$userValues[$row['userid']] = $row['configvalue'];
392
			}
393
		}
394
395
		return $userValues;
396
	}
397
398
	/**
399
	 * Determines the users that have the given value set for a specific app-key-pair
400
	 *
401
	 * @param string $appName the app to get the user for
402
	 * @param string $key the key to get the user for
403
	 * @param string $value the value to get the user for
404
	 * @return array of user IDs
405
	 */
406
	public function getUsersForUserValue($appName, $key, $value) {
407
		// TODO - FIXME
408
		$this->fixDIInit();
409
410
		$sql  = 'SELECT `userid` FROM `*PREFIX*preferences` ' .
411
				'WHERE `appid` = ? AND `configkey` = ? ';
412
413
		if($this->getSystemValue('dbtype', 'sqlite') === 'oci') {
414
			//oracle hack: need to explicitly cast CLOB to CHAR for comparison
415
			$sql .= 'AND to_char(`configvalue`) = ?';
416
		} else {
417
			$sql .= 'AND `configvalue` = ?';
418
		}
419
420
		$result = $this->connection->executeQuery($sql, array($appName, $key, $value));
421
422
		$userIDs = array();
423
		while ($row = $result->fetch()) {
424
			$userIDs[] = $row['userid'];
425
		}
426
427
		return $userIDs;
428
	}
429
}
430