|
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) |
|
|
|
|
|
|
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[] |
|
|
|
|
|
|
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
|
|
|
/* |
|
|
|
|
|
|
307
|
|
|
* @return string[][] |
|
|
|
|
|
|
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
|
|
|
|
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.