allconfig.php 13.2 KB
Newer Older
1
2
<?php
/**
Jenkins for ownCloud's avatar
Jenkins for ownCloud committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 * @author Bart Visscher <bartv@thisnet.nl>
 * @author Joas Schilling <nickvergessen@owncloud.com>
 * @author Lukas Reschke <lukas@owncloud.com>
 * @author Morris Jobke <hey@morrisjobke.de>
 * @author Robin Appelman <icewind@owncloud.com>
 * @author Thomas Müller <thomas.mueller@tmit.eu>
 * @author Vincent Petry <pvince81@owncloud.com>
 *
 * @copyright Copyright (c) 2015, ownCloud, Inc.
 * @license AGPL-3.0
 *
 * This code is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License, version 3,
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
25
 *
26
 */
27

28
namespace OC;
29
use OCP\IDBConnection;
30
use OCP\PreConditionNotMetException;
31
32
33
34
35

/**
 * Class to combine all the configuration options ownCloud offers
 */
class AllConfig implements \OCP\IConfig {
36
37
38
	/** @var SystemConfig */
	private $systemConfig;

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
	/** @var IDBConnection */
	private $connection;

	/**
	 * 3 dimensional array with the following structure:
	 * [ $userId =>
	 *     [ $appId =>
	 *         [ $key => $value ]
	 *     ]
	 * ]
	 *
	 * database table: preferences
	 *
	 * methods that use this:
	 *   - setUserValue
	 *   - getUserValue
	 *   - getUserKeys
	 *   - deleteUserValue
	 *   - deleteAllUserValues
	 *   - deleteAppFromAllUsers
	 *
	 * @var array $userCache
	 */
	private $userCache = array();

64
65
66
	/**
	 * @param SystemConfig $systemConfig
	 */
67
	function __construct(SystemConfig $systemConfig) {
68
		$this->systemConfig = $systemConfig;
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
	}

	/**
	 * TODO - FIXME This fixes an issue with base.php that cause cyclic
	 * dependencies, especially with autoconfig setup
	 *
	 * Replace this by properly injected database connection. Currently the
	 * base.php triggers the getDatabaseConnection too early which causes in
	 * autoconfig setup case a too early distributed database connection and
	 * the autoconfig then needs to reinit all already initialized dependencies
	 * that use the database connection.
	 *
	 * otherwise a SQLite database is created in the wrong directory
	 * because the database connection was created with an uninitialized config
	 */
	private function fixDIInit() {
		if($this->connection === null) {
			$this->connection = \OC::$server->getDatabaseConnection();
		}
88
89
	}

90
91
92
93
94
95
96
97
98
99
	/**
	 * Sets and deletes system wide values
	 *
	 * @param array $configs Associative array with `key => value` pairs
	 *                       If value is null, the config key will be deleted
	 */
	public function setSystemValues(array $configs) {
		$this->systemConfig->setValues($configs);
	}

100
	/**
Thomas Müller's avatar
Thomas Müller committed
101
	 * Sets a new system wide value
102
	 *
103
	 * @param string $key the key of the value, under which will be saved
104
	 * @param mixed $value the value that should be stored
105
	 */
106
	public function setSystemValue($key, $value) {
107
		$this->systemConfig->setValue($key, $value);
Lukas Reschke's avatar
Lukas Reschke committed
108
	}
109
110

	/**
Thomas Müller's avatar
Thomas Müller committed
111
	 * Looks up a system wide defined value
112
	 *
113
	 * @param string $key the key of the value, under which it was saved
114
	 * @param mixed $default the default value to be returned if the value isn't set
115
	 * @return mixed the value or $default
116
	 */
117
	public function getSystemValue($key, $default = '') {
118
		return $this->systemConfig->getValue($key, $default);
119
120
	}

121
122
123
124
125
126
	/**
	 * Delete a system wide defined value
	 *
	 * @param string $key the key of the value, under which it was saved
	 */
	public function deleteSystemValue($key) {
127
		$this->systemConfig->deleteValue($key);
128
129
	}

130
131
132
133
134
135
136
	/**
	 * Get all keys stored for an app
	 *
	 * @param string $appName the appName that we stored the value under
	 * @return string[] the keys stored for the app
	 */
	public function getAppKeys($appName) {
137
		return \OC::$server->getAppConfig()->getKeys($appName);
138
	}
139
140

	/**
Thomas Müller's avatar
Thomas Müller committed
141
	 * Writes a new app wide value
142
	 *
143
144
145
146
147
	 * @param string $appName the appName that we want to store the value under
	 * @param string $key the key of the value, under which will be saved
	 * @param string $value the value that should be stored
	 */
	public function setAppValue($appName, $key, $value) {
148
		\OC::$server->getAppConfig()->setValue($appName, $key, $value);
149
150
151
	}

	/**
Thomas Müller's avatar
Thomas Müller committed
152
	 * Looks up an app wide defined value
153
	 *
154
155
	 * @param string $appName the appName that we stored the value under
	 * @param string $key the key of the value, under which it was saved
156
	 * @param string $default the default value to be returned if the value isn't set
157
158
	 * @return string the saved value
	 */
159
	public function getAppValue($appName, $key, $default = '') {
160
		return \OC::$server->getAppConfig()->getValue($appName, $key, $default);
161
162
	}

163
164
165
166
167
168
169
	/**
	 * Delete an app wide defined value
	 *
	 * @param string $appName the appName that we stored the value under
	 * @param string $key the key of the value, under which it was saved
	 */
	public function deleteAppValue($appName, $key) {
170
		\OC::$server->getAppConfig()->deleteKey($appName, $key);
171
172
	}

173
174
175
176
177
178
	/**
	 * Removes all keys in appconfig belonging to the app
	 *
	 * @param string $appName the appName the configs are stored under
	 */
	public function deleteAppValues($appName) {
179
		\OC::$server->getAppConfig()->deleteApp($appName);
180
181
	}

182
183
184

	/**
	 * Set a user defined value
185
	 *
186
187
188
189
	 * @param string $userId the userId of the user that we want to store the value under
	 * @param string $appName the appName that we want to store the value under
	 * @param string $key the key under which the value is being stored
	 * @param string $value the value that you want to store
190
191
	 * @param string $preCondition only update if the config value was previously the value passed as $preCondition
	 * @throws \OCP\PreConditionNotMetException if a precondition is specified and is not met
192
	 */
193
	public function setUserValue($userId, $appName, $key, $value, $preCondition = null) {
194
195
196
		// TODO - FIXME
		$this->fixDIInit();

197
198
199
200
201
		// Check if the key does exist
		$sql  = 'SELECT `configvalue` FROM `*PREFIX*preferences` '.
				'WHERE `userid` = ? AND `appid` = ? AND `configkey` = ?';
		$result = $this->connection->executeQuery($sql, array($userId, $appName, $key));
		$oldValue = $result->fetchColumn();
202
		$result->closeCursor();
203
204
205
206
207
208
209
		$exists = $oldValue !== false;

		if($oldValue === strval($value)) {
			// no changes
			return;
		}

210
		$affectedRows = 0;
211
		if (!$exists && $preCondition === null) {
212
213
214
215
216
			$this->connection->insertIfNotExist('*PREFIX*preferences', [
				'configvalue'	=> $value,
				'userid'		=> $userId,
				'appid'			=> $appName,
				'configkey'		=> $key,
217
			], ['configkey', 'userid', 'appid']);
218
			$affectedRows = 1;
219
		} elseif ($exists) {
220
221
			$data = array($value, $userId, $appName, $key);

222
			$sql  = 'UPDATE `*PREFIX*preferences` SET `configvalue` = ? '.
223
224
225
226
227
228
229
230
231
232
233
					'WHERE `userid` = ? AND `appid` = ? AND `configkey` = ? ';

			if($preCondition !== null) {
				if($this->getSystemValue('dbtype', 'sqlite') === 'oci') {
					//oracle hack: need to explicitly cast CLOB to CHAR for comparison
					$sql .= 'AND to_char(`configvalue`) = ?';
				} else {
					$sql .= 'AND `configvalue` = ?';
				}
				$data[] = $preCondition;
			}
234
			$affectedRows = $this->connection->executeUpdate($sql, $data);
235
236
237
238
239
240
241
242
243
		}

		// only add to the cache if we already loaded data for the user
		if ($affectedRows > 0 && isset($this->userCache[$userId])) {
			if (!isset($this->userCache[$userId][$appName])) {
				$this->userCache[$userId][$appName] = array();
			}
			$this->userCache[$userId][$appName][$key] = $value;
		}
244
245
246
247

		if ($preCondition !== null && $affectedRows === 0) {
			throw new PreConditionNotMetException;
		}
248
249
250
	}

	/**
251
	 * Getting a user defined value
252
	 *
253
254
255
	 * @param string $userId the userId of the user that we want to store the value under
	 * @param string $appName the appName that we stored the value under
	 * @param string $key the key under which the value is being stored
256
257
	 * @param string $default the default value to be returned if the value isn't set
	 * @return string
258
	 */
259
	public function getUserValue($userId, $appName, $key, $default = '') {
260
261
262
263
264
265
		$data = $this->getUserValues($userId);
		if (isset($data[$appName]) and isset($data[$appName][$key])) {
			return $data[$appName][$key];
		} else {
			return $default;
		}
266
	}
267
268
269
270
271
272
273
274
275

	/**
	 * Get the keys of all stored by an app for the user
	 *
	 * @param string $userId the userId of the user that we want to store the value under
	 * @param string $appName the appName that we stored the value under
	 * @return string[]
	 */
	public function getUserKeys($userId, $appName) {
276
277
278
279
280
281
		$data = $this->getUserValues($userId);
		if (isset($data[$appName])) {
			return array_keys($data[$appName]);
		} else {
			return array();
		}
282
283
284
285
286
287
288
289
290
291
	}

	/**
	 * Delete a user value
	 *
	 * @param string $userId the userId of the user that we want to store the value under
	 * @param string $appName the appName that we stored the value under
	 * @param string $key the key under which the value is being stored
	 */
	public function deleteUserValue($userId, $appName, $key) {
292
293
294
		// TODO - FIXME
		$this->fixDIInit();

295
296
297
298
299
300
301
		$sql  = 'DELETE FROM `*PREFIX*preferences` '.
				'WHERE `userid` = ? AND `appid` = ? AND `configkey` = ?';
		$this->connection->executeUpdate($sql, array($userId, $appName, $key));

		if (isset($this->userCache[$userId]) and isset($this->userCache[$userId][$appName])) {
			unset($this->userCache[$userId][$appName][$key]);
		}
302
	}
Morris Jobke's avatar
Morris Jobke committed
303
304
305
306
307
308
309

	/**
	 * Delete all user values
	 *
	 * @param string $userId the userId of the user that we want to remove all values from
	 */
	public function deleteAllUserValues($userId) {
310
311
312
		// TODO - FIXME
		$this->fixDIInit();

313
314
315
316
317
318
319
320
321
322
323
324
325
		$sql  = 'DELETE FROM `*PREFIX*preferences` '.
			'WHERE `userid` = ?';
		$this->connection->executeUpdate($sql, array($userId));

		unset($this->userCache[$userId]);
	}

	/**
	 * Delete all user related values of one app
	 *
	 * @param string $appName the appName of the app that we want to remove all values from
	 */
	public function deleteAppFromAllUsers($appName) {
326
327
328
		// TODO - FIXME
		$this->fixDIInit();

329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
		$sql  = 'DELETE FROM `*PREFIX*preferences` '.
				'WHERE `appid` = ?';
		$this->connection->executeUpdate($sql, array($appName));

		foreach ($this->userCache as &$userCache) {
			unset($userCache[$appName]);
		}
	}

	/**
	 * Returns all user configs sorted by app of one user
	 *
	 * @param string $userId the user ID to get the app configs from
	 * @return array[] - 2 dimensional array with the following structure:
	 *     [ $appId =>
	 *         [ $key => $value ]
	 *     ]
	 */
	private function getUserValues($userId) {
348
349
350
		// TODO - FIXME
		$this->fixDIInit();

351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
		if (isset($this->userCache[$userId])) {
			return $this->userCache[$userId];
		}
		$data = array();
		$query = 'SELECT `appid`, `configkey`, `configvalue` FROM `*PREFIX*preferences` WHERE `userid` = ?';
		$result = $this->connection->executeQuery($query, array($userId));
		while ($row = $result->fetch()) {
			$appId = $row['appid'];
			if (!isset($data[$appId])) {
				$data[$appId] = array();
			}
			$data[$appId][$row['configkey']] = $row['configvalue'];
		}
		$this->userCache[$userId] = $data;
		return $data;
	}

	/**
	 * Fetches a mapped list of userId -> value, for a specified app and key and a list of user IDs.
	 *
371
372
373
	 * @param string $appName app to get the value for
	 * @param string $key the key to get the value for
	 * @param array $userIds the user IDs to fetch the values for
374
375
376
	 * @return array Mapped values: userId => value
	 */
	public function getUserValueForUsers($appName, $key, $userIds) {
377
378
379
		// TODO - FIXME
		$this->fixDIInit();

380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
		if (empty($userIds) || !is_array($userIds)) {
			return array();
		}

		$chunkedUsers = array_chunk($userIds, 50, true);
		$placeholders50 = implode(',', array_fill(0, 50, '?'));

		$userValues = array();
		foreach ($chunkedUsers as $chunk) {
			$queryParams = $chunk;
			// create [$app, $key, $chunkedUsers]
			array_unshift($queryParams, $key);
			array_unshift($queryParams, $appName);

			$placeholders = (sizeof($chunk) == 50) ? $placeholders50 :  implode(',', array_fill(0, sizeof($chunk), '?'));

			$query    = 'SELECT `userid`, `configvalue` ' .
						'FROM `*PREFIX*preferences` ' .
						'WHERE `appid` = ? AND `configkey` = ? ' .
						'AND `userid` IN (' . $placeholders . ')';
			$result = $this->connection->executeQuery($query, $queryParams);

			while ($row = $result->fetch()) {
				$userValues[$row['userid']] = $row['configvalue'];
			}
		}

		return $userValues;
	}

	/**
	 * Determines the users that have the given value set for a specific app-key-pair
	 *
	 * @param string $appName the app to get the user for
	 * @param string $key the key to get the user for
	 * @param string $value the value to get the user for
	 * @return array of user IDs
	 */
	public function getUsersForUserValue($appName, $key, $value) {
		// TODO - FIXME
		$this->fixDIInit();

		$sql  = 'SELECT `userid` FROM `*PREFIX*preferences` ' .
				'WHERE `appid` = ? AND `configkey` = ? ';

		if($this->getSystemValue('dbtype', 'sqlite') === 'oci') {
			//oracle hack: need to explicitly cast CLOB to CHAR for comparison
			$sql .= 'AND to_char(`configvalue`) = ?';
		} else {
			$sql .= 'AND `configvalue` = ?';
		}

		$result = $this->connection->executeQuery($sql, array($appName, $key, $value));

		$userIDs = array();
		while ($row = $result->fetch()) {
			$userIDs[] = $row['userid'];
		}

		return $userIDs;
Morris Jobke's avatar
Morris Jobke committed
440
	}
441
}