Completed
Push — master ( df8ec4...96358d )
by Nazar
04:25
created

users::admin_users_permissions_get()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 8
rs 9.4286
cc 3
eloc 5
nc 2
nop 1
1
<?php
2
/**
3
 * @package    CleverStyle CMS
4
 * @subpackage System module
5
 * @category   modules
6
 * @author     Nazar Mokrynskyi <[email protected]>
7
 * @copyright  Copyright (c) 2015, Nazar Mokrynskyi
8
 * @license    MIT License, see license.txt
9
 */
10
namespace cs\modules\System\api\Controller\admin;
11
use
12
	cs\ExitException,
13
	cs\Language,
14
	cs\Page,
15
	cs\User;
16
trait users {
17
	/**
18
	 * Get user's data or data of several specified groups if specified in ids query parameter or allows to search for users by login or email (users id will
19
	 * be returned)
20
	 *
21
	 * Data will be pre-processed with `reg_date_formatted` and `reg_ip_formatted` keys added
22
	 *
23
	 * @param int[] $route_ids
24
	 *
25
	 * @throws ExitException
26
	 */
27
	static function admin_users___get ($route_ids) {
28
		$User    = User::instance();
29
		$Page    = Page::instance();
30
		$columns = static::admin_users___search_options_get()['columns'];
31
		if (isset($route_ids[0])) {
32
			$result = static::admin_users___get_post_process(
33
				$User->get($columns, $route_ids[0])
34
			);
35
		} elseif (isset($_GET['ids'])) {
36
			$ids    = _int(explode(',', $_GET['ids']));
37
			$result = [];
38
			foreach ($ids as $id) {
39
				$result[] = static::admin_users___get_post_process(
40
					$User->get($columns, $id)
41
				);
42
			}
43
		} elseif (isset($_GET['search'])) {
44
			$result = _int($User->search_users($_GET['search']));
45
		} else {
46
			throw new ExitException(400);
47
		}
48
		if (!$result) {
49
			throw new ExitException(404);
50
		}
51
		$Page->json($result);
52
	}
53
	protected static function admin_users___get_post_process ($data) {
54
		$L                          = Language::instance();
55
		$data['reg_date_formatted'] = $data['reg_date'] ? date($L->_date, $data['reg_date']) : $L->undefined;
56
		$data['reg_ip_formatted']   = hex2ip($data['reg_ip'], 10);
57
		return $data;
58
	}
59
	/**
60
	 * Update user's data
61
	 *
62
	 * @param int[] $route_ids
63
	 *
64
	 * @throws ExitException
65
	 */
66
	static function admin_users___patch ($route_ids) {
67
		if (!isset($route_ids[0], $_POST['user'])) {
68
			throw new ExitException(400);
69
		}
70
		$User    = User::instance();
71
		$user_id = (int)$route_ids[0];
72
		$is_bot  = in_array(User::BOT_GROUP_ID, $User->get_groups($user_id));
73
		if ($is_bot && !@$_POST['user']['login'] && !@$_POST['user']['email']) {
74
			throw new ExitException(400);
75
		}
76
		$columns_allowed_to_edit = $is_bot
77
			? ['login', 'username', 'email', 'status']
78
			: ['login', 'username', 'email', 'language', 'timezone', 'status', 'block_until', 'avatar'];
79
		$user_data               = array_filter(
80
			$_POST['user'],
81
			function ($item) use ($columns_allowed_to_edit) {
82
				return in_array($item, $columns_allowed_to_edit, true);
83
			},
84
			ARRAY_FILTER_USE_KEY
85
		);
86
		foreach ($user_data as &$d) {
87
			$d = xap($d, false);
88
		}
89
		unset($d);
90
		if (!$user_data && ($is_bot || !isset($_POST['user']['password']))) {
91
			throw new ExitException(400);
92
		}
93
		$L = Language::instance();
94
		if (
95
			isset($user_data['login']) &&
96
			$user_data['login'] !== $User->get('login', $user_id) &&
97
			$User->get_id(hash('sha224', $user_data['login']))
98
		) {
99
			throw new ExitException($L->login_occupied, 400);
100
		}
101
		if (
102
			isset($user_data['email']) &&
103
			$user_data['email'] !== $User->get('email', $user_id) &&
104
			$User->get_id(hash('sha224', $user_data['email']))
105
		) {
106
			throw new ExitException($L->email_occupied, 400);
107
		}
108
		if (!$User->set($user_data, null, $user_id)) {
109
			throw new ExitException(500);
110
		}
111
		if (!$is_bot && isset($_POST['user']['password']) && !$User->set_password($_POST['user']['password'], $user_id)) {
112
			throw new ExitException(500);
113
		}
114
	}
115
	/**
116
	 * Add new user or bot (different parameters required depending on `type` parameter)
117
	 *
118
	 * @throws ExitException
119
	 */
120
	static function admin_users___post () {
121
		if (!isset($_POST['type'])) {
122
			throw new ExitException(400);
123
		}
124
		$User = User::instance();
125
		$Page = Page::instance();
126
		if ($_POST['type'] === 'user' && isset($_POST['email'])) {
127
			$result = $User->registration($_POST['email'], false, false);
128
			if (!$result) {
129
				throw new ExitException(500);
130
			}
131
			status_code(201);
132
			$Page->json(
133
				[
134
					'login'    => $User->get('login', $result['id']),
135
					'password' => $result['password']
136
				]
137
			);
138
		} elseif ($_POST['type'] === 'bot' && isset($_POST['name'], $_POST['user_agent'], $_POST['ip'])) {
139
			if ($User->add_bot($_POST['name'], $_POST['user_agent'], $_POST['ip'])) {
140
				status_code(201);
141
			} else {
142
				throw new ExitException(500);
143
			}
144
		} else {
145
			throw new ExitException(400);
146
		}
147
	}
148
	/**
149
	 * Advanced search for users (users data will be returned similar to GET method)
150
	 *
151
	 * @throws ExitException
152
	 */
153
	static function admin_users___search () {
154
		if (!isset($_POST['mode'], $_POST['column'], $_POST['text'], $_POST['page'], $_POST['limit'])) {
155
			throw new ExitException(400);
156
		}
157
		$mode           = $_POST['mode'];
158
		$column         = $_POST['column'];
159
		$text           = $_POST['text'];
160
		$page           = (int)$_POST['page'];
161
		$limit          = (int)$_POST['limit'];
162
		$search_options = static::admin_users___search_options_get();
163
		if (
164
			!in_array($mode, $search_options['modes']) ||
165
			(
166
				$column !== '' &&
167
				!in_array($column, $search_options['columns'])
168
			)
169
		) {
170
			throw new ExitException(400);
171
		}
172
		$cdb   = User::instance()->db();
173
		$where = static::admin_users___search_prepare_where($mode, $text, $column ?: $search_options['columns'], $cdb);
174
		/**
175
		 * Deleted users do not have any email, login or password, all of them are empty strings
176
		 */
177
		$where =
178
			"$where AND
179
			(
180
				`login`	!= `password_hash` OR
181
				`email`	!= `password_hash`
182
			)";
183
		$count = $cdb->qfs(
184
			[
185
				"SELECT COUNT(`id`)
186
				FROM `[prefix]users`
187
				WHERE $where"
188
			]
189
		);
190
		if (!$count) {
191
			throw new ExitException(404);
192
		}
193
		$where = str_replace('%', '%%', $where);
194
		$ids   = $cdb->qfas(
195
			[
196
				"SELECT `id`
197
				FROM `[prefix]users`
198
				WHERE $where
199
				ORDER BY `id`
200
				LIMIT %d, %d",
201
				($page - 1) * $limit,
202
				$limit
203
			]
204
		);
205
		Page::instance()->json(
206
			[
207
				'count' => $count,
208
				'users' => static::admin_users___search_get($ids, $search_options['columns'])
209
			]
210
		);
211
	}
212
	/**
213
	 * @param string           $mode
214
	 * @param string           $text
215
	 * @param string|string[]  $column
216
	 * @param \cs\DB\_Abstract $cdb
217
	 *
218
	 * @return string
219
	 */
220
	protected static function admin_users___search_prepare_where ($mode, $text, $column, $cdb) {
221
		$where = '1';
222
		if ($text && $mode) {
223
			switch ($mode) {
224
				case '=':
225
				case '!=':
226
				case '>':
227
				case '<':
228
				case '>=':
229
				case '<=':
230
				case 'LIKE':
231
				case 'NOT LIKE':
232
				case 'REGEXP':
233
				case 'NOT REGEXP':
234
					$where = static::admin_users___search_prepare_where_compose(
235
						"`%s` $mode %s",
236
						$column,
237
						$cdb->s($text)
1 ignored issue
show
Bug introduced by
It seems like $cdb->s($text) targeting cs\DB\_Abstract::s() can also be of type array; however, cs\modules\System\api\Co...prepare_where_compose() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
238
					);
239
					break;
240
				case 'IN':
241
				case 'NOT IN':
242
					$where = static::admin_users___search_prepare_where_compose(
243
						"`%s` $mode (%s)",
244
						$column,
245
						implode(
246
							", ",
247
							$cdb->s(
248
								_trim(
249
									_trim(explode(',', $text), "'")
250
								)
251
							)
252
						)
253
					);
254
					break;
255
			}
256
		}
257
		return $where;
258
	}
259
	/**
260
	 * @param string          $where
261
	 * @param string|string[] $column
262
	 * @param string          $text
263
	 *
264
	 * @return string
265
	 */
266
	protected static function admin_users___search_prepare_where_compose ($where, $column, $text) {
267
		if (is_array($column)) {
268
			$return = [];
269
			foreach ($column as $c) {
270
				$return[] = sprintf($where, $c, $text);
271
			}
272
			return '('.implode(' OR ', $return).')';
273
		}
274
		return sprintf($where, $column, $text);
275
	}
276
	/**
277
	 * @param int[]    $users
278
	 * @param string[] $columns
279
	 *
280
	 * @return array[]
0 ignored issues
show
Documentation introduced by
Should the return type not be integer[]?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
281
	 */
282
	protected static function admin_users___search_get ($users, $columns) {
283
		$User = User::instance();
284
		foreach ($users as &$user) {
285
			$groups         = (array)$User->get_groups($user);
286
			$user           =
287
				$User->get($columns, $user) +
288
				[
289
					'is_user'  => in_array(User::USER_GROUP_ID, $groups),
290
					'is_bot'   => in_array(User::BOT_GROUP_ID, $groups),
291
					'is_admin' => in_array(User::ADMIN_GROUP_ID, $groups),
292
					'username' => $User->username($user)
293
				];
294
			$user['reg_ip'] = hex2ip($user['reg_ip'], 10);
295
		}
296
		return $users;
297
	}
298
	/**
299
	 * Get available search options
300
	 */
301
	static function admin_users___search_options () {
302
		Page::instance()->json(
303
			static::admin_users___search_options_get()
304
		);
305
	}
306
	/*
1 ignored issue
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
307
	 * @return string[][]
0 ignored issues
show
Documentation introduced by
Should the return type not be array<string,array>?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
308
	 */
309
	protected static function admin_users___search_options_get () {
310
		return [
311
			'modes'   => [
312
				'=',
313
				'!=',
314
				'>',
315
				'<',
316
				'>=',
317
				'<=',
318
				'LIKE',
319
				'NOT LIKE',
320
				'IN',
321
				'NOT IN',
322
				'IS NULL',
323
				'IS NOT NULL',
324
				'REGEXP',
325
				'NOT REGEXP'
326
			],
327
			'columns' => array_values(
328
				array_filter(
329
					User::instance()->get_users_columns(),
330
					function ($column) {
331
						return $column !== 'password_hash';
332
					}
333
				)
334
			)
335
		];
336
	}
337
	/**
338
	 * Get user's permissions
339
	 *
340
	 * @param int[] $route_ids
341
	 *
342
	 * @throws ExitException
343
	 */
344
	static function admin_users_permissions_get ($route_ids) {
345
		if (!isset($route_ids[0])) {
346
			throw new ExitException(400);
347
		}
348
		Page::instance()->json(
349
			User::instance()->get_permissions($route_ids[0]) ?: []
350
		);
351
	}
352
	/**
353
	 * Update user's permissions
354
	 *
355
	 * @param int[] $route_ids
356
	 *
357
	 * @throws ExitException
358
	 */
359
	static function admin_users_permissions_put ($route_ids) {
360
		if (!isset($route_ids[0], $_POST['permissions'])) {
361
			throw new ExitException(400);
362
		}
363
		if (!User::instance()->set_permissions($_POST['permissions'], $route_ids[0])) {
364
			throw new ExitException(500);
365
		}
366
	}
367
}
368