Passed
Push — master ( a4754c...e0c6ec )
by Nazar
05:32
created

users::admin_users___search_options()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 21
nc 1
nop 0
dl 0
loc 23
rs 9.0856
c 0
b 0
f 0
ccs 0
cts 7
cp 0
crap 2
1
<?php
2
/**
3
 * @package    CleverStyle Framework
4
 * @subpackage System module
5
 * @category   modules
6
 * @author     Nazar Mokrynskyi <[email protected]>
7
 * @copyright  Copyright (c) 2015-2017, 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\Response,
15
	cs\User;
16
17
trait users {
18
	/**
19
	 * 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
20
	 * be returned)
21
	 *
22
	 * Data will be pre-processed with `reg_date_formatted` and `reg_ip_formatted` keys added
23
	 *
24
	 * @param \cs\Request $Request
25
	 *
26
	 * @return array
27
	 *
28
	 * @throws ExitException
29
	 */
30
	public static function admin_users___get ($Request) {
31
		$User    = User::instance();
32
		$columns = static::admin_users___search_options()['columns'];
33
		$id      = $Request->route_ids(0);
34
		$ids     = $Request->query('ids');
0 ignored issues
show
Bug introduced by
'ids' of type string is incompatible with the type array<mixed,string[]>|string[] expected by parameter $name of cs\Request::query(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

34
		$ids     = $Request->query(/** @scrutinizer ignore-type */ 'ids');
Loading history...
35
		$search  = $Request->query('search');
36
		if ($id) {
37
			$result = static::admin_users___get_post_process(
38
				$User->get($columns, $id)
39
			);
40
		} elseif ($ids) {
41
			$ids    = _int(explode(',', $ids));
0 ignored issues
show
Bug introduced by
It seems like $ids can also be of type array and array; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

41
			$ids    = _int(explode(',', /** @scrutinizer ignore-type */ $ids));
Loading history...
42
			$result = [];
43
			foreach ($ids as $id) {
44
				$result[] = static::admin_users___get_post_process(
45
					$User->get($columns, $id)
46
				);
47
			}
48
		} elseif ($search) {
49
			$result = _int($User->search_users($search));
0 ignored issues
show
Bug introduced by
It seems like $search can also be of type array and array; however, parameter $search_phrase of cs\User::search_users() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

49
			$result = _int($User->search_users(/** @scrutinizer ignore-type */ $search));
Loading history...
50
		} else {
51
			throw new ExitException(400);
52
		}
53
		if (!$result) {
54
			throw new ExitException(404);
55
		}
56
		return $result;
57
	}
58
	protected static function admin_users___get_post_process ($data) {
59
		$L                          = Language::prefix('system_admin_users_');
60
		$data['reg_date_formatted'] = $data['reg_date'] ? date($L->_date, $data['reg_date']) : $L->undefined;
0 ignored issues
show
Bug Best Practice introduced by
The property _date does not exist on cs\Language\Prefix. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property undefined does not exist on cs\Language\Prefix. Since you implemented __get, consider adding a @property annotation.
Loading history...
61
		$data['reg_ip_formatted']   = hex2ip($data['reg_ip'], 10);
62
		return $data;
63
	}
64
	/**
65
	 * Update user's data
66
	 *
67
	 * @param \cs\Request $Request
68
	 *
69
	 * @throws ExitException
70
	 */
71
	public static function admin_users___patch ($Request) {
72
		$user_id = (int)$Request->route_ids(0);
73
		$user    = $Request->data('user');
0 ignored issues
show
Bug introduced by
'user' of type string is incompatible with the type array<mixed,string[]>|string[] expected by parameter $name of cs\Request::data(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

73
		$user    = $Request->data(/** @scrutinizer ignore-type */ 'user');
Loading history...
74
		if (!$user_id || !$user) {
75
			throw new ExitException(400);
76
		}
77
		$User      = User::instance();
78
		$user_data = array_filter(
79
			$user,
80
			function ($item) {
81
				return in_array($item, ['login', 'username', 'email', 'language', 'timezone', 'status', 'avatar'], true);
82
			},
83
			ARRAY_FILTER_USE_KEY
84
		);
85
		foreach ($user_data as &$d) {
86
			$d = xap($d, false);
87
		}
88
		unset($d);
89
		if (!$user_data && !isset($user['password'])) {
90
			throw new ExitException(400);
91
		}
92
		$L = Language::prefix('system_admin_users_');
93
		if (
94
			isset($user_data['login']) &&
95
			$user_data['login'] !== $User->get('login', $user_id) &&
96
			$User->get_id(hash('sha224', $user_data['login']))
97
		) {
98
			throw new ExitException($L->login_occupied, 400);
0 ignored issues
show
Bug Best Practice introduced by
The property login_occupied does not exist on cs\Language\Prefix. Since you implemented __get, consider adding a @property annotation.
Loading history...
99
		}
100
		if (
101
			isset($user_data['email']) &&
102
			$user_data['email'] !== $User->get('email', $user_id) &&
103
			$User->get_id(hash('sha224', $user_data['email']))
104
		) {
105
			throw new ExitException($L->email_occupied, 400);
0 ignored issues
show
Bug Best Practice introduced by
The property email_occupied does not exist on cs\Language\Prefix. Since you implemented __get, consider adding a @property annotation.
Loading history...
106
		}
107
		if (!$User->set($user_data, null, $user_id)) {
108
			throw new ExitException(500);
109
		}
110
		if (isset($user['password']) && !$User->set_password($user['password'], $user_id)) {
111
			throw new ExitException(500);
112
		}
113
	}
114
	/**
115
	 * Add new user
116
	 *
117
	 * @param \cs\Request $Request
118
	 *
119
	 * @return array
120
	 *
121
	 * @throws ExitException
122
	 */
123
	public static function admin_users___post ($Request) {
124
		$User  = User::instance();
125
		$email = $Request->data('email');
0 ignored issues
show
Bug introduced by
'email' of type string is incompatible with the type array<mixed,string[]>|string[] expected by parameter $name of cs\Request::data(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

125
		$email = $Request->data(/** @scrutinizer ignore-type */ 'email');
Loading history...
126
		if (!$email) {
127
			throw new ExitException(400);
128
		}
129
		$result = $User->registration($email, false, false);
0 ignored issues
show
Bug introduced by
It seems like $email can also be of type array and array; however, parameter $email of cs\User::registration() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

129
		$result = $User->registration(/** @scrutinizer ignore-type */ $email, false, false);
Loading history...
130
		if (!$result) {
131
			throw new ExitException(500);
132
		}
133
		if ($result === 'exists') {
134
			$L = Language::prefix('system_admin_users_');
135
			throw new ExitException($L->user_already_exists, 400);
0 ignored issues
show
Bug Best Practice introduced by
The property user_already_exists does not exist on cs\Language\Prefix. Since you implemented __get, consider adding a @property annotation.
Loading history...
136
		}
137
		Response::instance()->code = 201;
0 ignored issues
show
Bug introduced by
The property code does not seem to exist on cs\False_class.
Loading history...
138
		return [
139
			'login'    => $User->get('login', $result['id']),
140
			'password' => $result['password']
141
		];
142
	}
143
	/**
144
	 * Advanced search for users (users data will be returned similar to GET method)
145
	 *
146
	 * @param \cs\Request $Request
147
	 *
148
	 * @return array
149
	 *
150
	 * @throws ExitException
151
	 */
152
	public static function admin_users___search ($Request) {
153
		$options = $Request->data('mode', 'column', 'text', 'page', 'limit');
0 ignored issues
show
Bug introduced by
'mode' of type string is incompatible with the type array<mixed,string[]>|string[] expected by parameter $name of cs\Request::data(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

153
		$options = $Request->data(/** @scrutinizer ignore-type */ 'mode', 'column', 'text', 'page', 'limit');
Loading history...
154
		if (!$options) {
155
			throw new ExitException(400);
156
		}
157
		$mode           = $options['mode'];
158
		$column         = $options['column'];
159
		$text           = $options['text'];
160
		$page           = (int)$options['page'];
161
		$limit          = (int)$options['limit'];
162
		$search_options = static::admin_users___search_options();
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
		$count = $cdb->qfs(
175
			"SELECT COUNT(`id`)
0 ignored issues
show
Bug introduced by
EncapsedNode of type string is incompatible with the type string[] expected by parameter $query of cs\DB\_Abstract::qfs(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

175
			/** @scrutinizer ignore-type */ "SELECT COUNT(`id`)
Loading history...
176
			FROM `[prefix]users`
177
			WHERE $where"
178
		);
179
		if (!$count) {
180
			throw new ExitException(404);
181
		}
182
		$where = str_replace('%', '%%', $where);
183
		$ids   = $cdb->qfas(
184
			"SELECT `id`
0 ignored issues
show
Bug introduced by
EncapsedNode of type string is incompatible with the type string[] expected by parameter $query of cs\DB\_Abstract::qfas(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

184
			/** @scrutinizer ignore-type */ "SELECT `id`
Loading history...
185
			FROM `[prefix]users`
186
			WHERE $where
187
			ORDER BY `id`
188
			LIMIT %d OFFSET %d",
189
			$limit,
0 ignored issues
show
Bug introduced by
$limit of type integer is incompatible with the type string[] expected by parameter $query of cs\DB\_Abstract::qfas(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

189
			/** @scrutinizer ignore-type */ $limit,
Loading history...
190
			($page - 1) * $limit
191
		);
192
		return [
193
			'count' => $count,
194
			'users' => static::admin_users___search_get($ids, $search_options['columns'])
195
		];
196
	}
197
	/**
198
	 * @param string           $mode
199
	 * @param string           $text
200
	 * @param string|string[]  $column
201
	 * @param \cs\DB\_Abstract $cdb
202
	 *
203
	 * @return string
204
	 */
205
	protected static function admin_users___search_prepare_where ($mode, $text, $column, $cdb) {
206
		$where = '1';
207
		if ($text && $mode) {
208
			switch ($mode) {
209
				case '=':
210
				case '!=':
211
				case '>':
212
				case '<':
213
				case '>=':
214
				case '<=':
215
				case 'LIKE':
216
				case 'NOT LIKE':
217
				case 'REGEXP':
218
				case 'NOT REGEXP':
219
					$where = static::admin_users___search_prepare_where_compose(
220
						"`%s` $mode %s",
221
						$column,
222
						$cdb->s($text)
223
					);
224
					break;
225
				case 'IN':
226
				case 'NOT IN':
227
					$where = static::admin_users___search_prepare_where_compose(
228
						"`%s` $mode (%s)",
229
						$column,
230
						implode(
231
							', ',
232
							$cdb->s(
0 ignored issues
show
Bug introduced by
$cdb->s(_trim(_trim(explode(',', $text), '''))) of type string is incompatible with the type array expected by parameter $pieces of implode(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

232
							/** @scrutinizer ignore-type */ $cdb->s(
Loading history...
233
								_trim(
234
									_trim(explode(',', $text), "'")
235
								)
236
							)
237
						)
238
					);
239
					break;
240
			}
241
		}
242
		return $where;
243
	}
244
	/**
245
	 * @param string          $where
246
	 * @param string|string[] $column
247
	 * @param string          $text
248
	 *
249
	 * @return string
250
	 */
251
	protected static function admin_users___search_prepare_where_compose ($where, $column, $text) {
252
		if (is_array($column)) {
253
			$return = [];
254
			foreach ($column as $c) {
255
				$return[] = sprintf($where, $c, $text);
256
			}
257
			return '('.implode(' OR ', $return).')';
258
		}
259
		return sprintf($where, $column, $text);
260
	}
261
	/**
262
	 * @param int[]    $users
263
	 * @param string[] $columns
264
	 *
265
	 * @return array[]
266
	 */
267
	protected static function admin_users___search_get ($users, $columns) {
268
		$User = User::instance();
269
		foreach ($users as &$user) {
270
			$groups         = (array)$User->get_groups($user);
271
			$user           =
272
				$User->get($columns, $user) +
273
				[
274
					'is_user'  => in_array(User::USER_GROUP_ID, $groups),
275
					'is_admin' => in_array(User::ADMIN_GROUP_ID, $groups),
276
					'username' => $User->username($user)
277
				];
278
			$user['reg_ip'] = hex2ip($user['reg_ip'], 10);
279
		}
280
		return $users;
281
	}
282
	/**
283
	 * Get available search options
284
	 *
285
	 * @return string[][]
286
	 */
287
	public static function admin_users___search_options () {
288
		return [
289
			'modes'   => [
290
				'=',
291
				'!=',
292
				'>',
293
				'<',
294
				'>=',
295
				'<=',
296
				'LIKE',
297
				'NOT LIKE',
298
				'IN',
299
				'NOT IN',
300
				'IS NULL',
301
				'IS NOT NULL',
302
				'REGEXP',
303
				'NOT REGEXP'
304
			],
305
			'columns' => array_values(
306
				array_filter(
307
					User::instance()->get_users_columns(),
308
					function ($column) {
309
						return $column !== 'password_hash';
310
					}
311
				)
312
			)
313
		];
314
	}
315
}
316