dbxapp 4.1.3
CMS, Shop, Workflows und modulare Geschäftsanwendungen
Loading...
Searching...
No Matches
dbxAdmin/include/dbxUser.class.php
Go to the documentation of this file.
1<?php
2namespace dbx\dbxAdmin;
3
4dbx()->use_system_class('dbxReport');
5
7}
8
9class dbxUser {
10
11 private string $ddUser = 'dbxUser';
12 private string $ddGroup = 'dbxUser_groups';
13 private string $actionError = '';
14
15 private function url($run2, $params = array()) {
16 $url = '?dbx_modul=dbxAdmin&dbx_run1=user&dbx_run2=' . rawurlencode((string)$run2);
17 foreach ($params as $key => $value) {
18 $url .= '&' . rawurlencode((string)$key) . '=' . rawurlencode((string)$value);
19 }
20 return $url;
21 }
22
23 private function action_url($run2, $params = array()) {
24 $url = $this->url($run2, $params);
25 $securedUrl = dbx()->action_url($url);
26 if ($securedUrl !== $url) {
27 return $securedUrl;
28 }
29
30 $do = (string)($params['dbx_do'] ?? $params['dbx_run3'] ?? 'action');
31 $rid = (int)($params['rid'] ?? 0);
32 $params['dbx_token'] = dbx()->action_token($this->action_token_scope($do, $rid));
33 return $this->url($run2, $params);
34 }
35
36 private function action_token_scope($do, $rid) {
37 $do = preg_replace('/[^a-z0-9_-]+/i', '', (string)$do);
38 return 'dbxAdmin.user.' . ($do !== '' ? $do : 'action') . '.' . max(0, (int)$rid);
39 }
40
41 private function check_action_token(&$message, $texts) {
42 $do = dbx()->get_modul_var('dbx_do', '', 'parameter');
43 if (!$do) {
44 $do = dbx()->get_modul_var('dbx_run3', '', 'parameter');
45 }
46 $rid = dbx()->get_modul_var('rid', 0, 'int');
47 $token = dbx()->get_modul_var('dbx_token', '', 'varchar|max=128');
48 if (dbx()->check_action_token($this->action_token_scope($do, $rid), $token)) {
49 return true;
50 }
51
52 $message = $texts->get_fd_message('token_error');
53 $this->actionError = $message;
54 dbx()->sys_msg('security', 'dbxAdmin', dbx()->user(), 'invalid admin user action token', $_SERVER['REQUEST_URI'] ?? '');
55 return false;
56 }
57
58 private function badge($label, $class = 'secondary') {
59 return '<span class="badge bg-' . dbx()->esc($class) . '">' . dbx()->esc($label) . '</span>';
60 }
61
62 private function action_button($url, $icon, $title, $class = 'btn-outline-secondary') {
63 return '<a class="btn btn-sm ' . dbx()->esc($class) . ' me-1 dbxAjax" href="' . dbx()->esc($url) . '" title="' . dbx()->esc($title) . '">'
64 . '<i class="' . dbx()->esc($icon) . '"></i></a>';
65 }
66
67 private function modal_button($url, $icon, $title, $class = 'btn-outline-primary', $label = '', $size = 'btn-sm') {
68 $size = trim((string)$size);
69 $sizeClass = $size !== '' ? $size . ' ' : '';
70 $text = $label !== '' ? ' ' . dbx()->esc($label) : '';
71 return '<a class="btn dbx-win ' . $sizeClass . dbx()->esc($class) . ' me-1" href="' . dbx()->esc($url) . '" '
72 . 'data-url="' . dbx()->esc($url) . '" data-title="' . dbx()->esc($title) . '" role="button" title="' . dbx()->esc($title) . '">'
73 . '<i class="' . dbx()->esc($icon) . '"></i>' . $text . '</a>';
74 }
75
76 private function delete_button($url, $title, $texts) {
77 return '<a class="btn btn-sm btn-outline-danger me-1 dbxAjax dbxConfirm" href="' . dbx()->esc($url) . '" '
78 . 'data-confirm-title="<i class=\'bi bi-trash\'></i> ' . dbx()->esc($title) . '" '
79 . 'data-confirm="' . dbx()->esc($texts->get_fd_message('delete_confirm')) . '" '
80 . 'data-confirm-hint="<small>' . dbx()->esc($texts->get_fd_message('delete_hint')) . '</small>" '
81 . 'data-confirm-buttons="yesno" title="' . dbx()->esc($title) . '">'
82 . '<i class="bi bi-trash"></i></a>';
83 }
84
85 private function role_options($texts) {
86 $options = array(
87 'admin' => $texts->get_fd_message('role_admin'),
88 'owner' => $texts->get_fd_message('role_owner'),
89 'member' => $texts->get_fd_message('role_member'),
90 'guest' => $texts->get_fd_message('role_guest'),
91 'api' => $texts->get_fd_message('role_api'),
92 );
93
94 $db = dbx()->get_system_obj('dbxDB');
95 $groups = $db->select($this->ddGroup, '', array('name', 'description', 'active'), 'name', 'ASC', '', 0, 0, 0);
96 if (is_array($groups)) {
97 foreach ($groups as $group) {
98 $name = trim((string)($group['name'] ?? ''));
99 if ($name === '' || array_key_exists($name, $options)) {
100 continue;
101 }
102 $description = trim((string)($group['description'] ?? ''));
103 $options[$name] = $description !== '' ? $description : $name;
104 }
105 }
106
107 return $options;
108 }
109
110 private function status_label($status, $texts) {
111 if ((string)$status === '0') {
112 return $this->badge($texts->get_fd_message('status_locked'), 'danger');
113 }
114 if ((string)$status === '1') {
115 return $this->badge($texts->get_fd_message('status_active'), 'success');
116 }
117 if ((string)$status === '2') {
118 return $this->badge($texts->get_fd_message('status_waiting'), 'warning');
119 }
120 return $this->badge($texts->get_fd_message('status_unknown'), 'secondary');
121 }
122
123 private function verified_label($value, $texts) {
124 return ((string)$value === '1')
125 ? $this->badge($texts->get_fd_message('verified'), 'success')
126 : $this->badge($texts->get_fd_message('unverified'), 'warning');
127 }
128
129 private function roles_label($roles, $texts) {
130 $items = array_filter(array_map('trim', explode(',', (string)$roles)));
131 if (!$items) {
132 return $this->badge($texts->get_fd_message('no_roles'), 'secondary');
133 }
134
135 $html = '';
136 foreach ($items as $role) {
137 $class = ($role === 'admin') ? 'primary' : (($role === 'api') ? 'dark' : 'info');
138 $html .= $this->badge($role, $class) . ' ';
139 }
140 return trim($html);
141 }
142
143 private function user_identity($row) {
144 $name = trim((string)($row['name'] ?? ''));
145 $uname = trim((string)($row['uname'] ?? ''));
146 $email = trim((string)($row['email'] ?? ''));
147
148 $title = $name !== '' ? $name : $uname;
149 $html = '<div class="fw-semibold">' . dbx()->esc($title) . '</div>';
150 $html .= '<div class="text-muted small">@' . dbx()->esc($uname) . '</div>';
151 if ($email !== '') {
152 $html .= '<div class="small"><a href="mailto:' . dbx()->esc($email) . '">' . dbx()->esc($email) . '</a></div>';
153 }
154 return $html;
155 }
156
157 private function user_actions($row, $texts) {
158 $id = (int)($row['id'] ?? 0);
159 if ($id <= 0) {
160 return '';
161 }
162
163 $html = '';
164 $protected = ($id <= 1 || $id == dbx()->user());
165 $html .= $this->modal_button(
166 $this->url('edit_user', array('rid' => $id)),
167 'bi bi-pencil-square',
168 $texts->get_fd_message('action_edit')
169 );
170
171 if ($protected) {
172 $html .= '<span class="badge bg-secondary me-1">'
173 . dbx()->esc($texts->get_fd_message('action_protected'))
174 . '</span>';
175 } else {
176 if ((string)($row['is_confirm'] ?? '') === '1') {
177 $html .= $this->action_button($this->action_url('list_user', array('dbx_do' => 'unverify', 'rid' => $id)), 'bi bi-patch-minus', $texts->get_fd_message('action_unverify'));
178 } else {
179 $html .= $this->action_button($this->action_url('list_user', array('dbx_do' => 'verify', 'rid' => $id)), 'bi bi-patch-check', $texts->get_fd_message('action_verify'), 'btn-outline-success');
180 }
181
182 if ((string)($row['status'] ?? '') === '0') {
183 $html .= $this->action_button($this->action_url('list_user', array('dbx_do' => 'unlock', 'rid' => $id)), 'bi bi-unlock', $texts->get_fd_message('action_unlock'), 'btn-outline-success');
184 } else {
185 $html .= $this->action_button($this->action_url('list_user', array('dbx_do' => 'lock', 'rid' => $id)), 'bi bi-lock', $texts->get_fd_message('action_lock'), 'btn-outline-danger');
186 }
187
188 $html .= $this->action_button($this->action_url('list_user', array('dbx_do' => 'reset_password', 'rid' => $id)), 'bi bi-key', $texts->get_fd_message('action_reset_password'), 'btn-outline-warning');
189 $html .= $this->delete_button(
190 $this->action_url('list_user', array('dbx_do' => 'row_delete', 'rid' => $id)),
191 $texts->get_fd_message('action_delete'),
192 $texts
193 );
194 }
195 return $html;
196 }
197
198 private function stats_html($texts) {
199 $db = dbx()->get_system_obj('dbxDB');
200 $all = (int)$db->count($this->ddUser);
201 $active = (int)$db->count($this->ddUser, 'status = 1');
202 $locked = (int)$db->count($this->ddUser, 'status = 0');
203 $verified = (int)$db->count($this->ddUser, 'is_confirm = 1');
204
205 $items = array(
206 array('label' => $texts->get_fd_message('stat_users'), 'value' => $all, 'class' => 'primary', 'filter' => ''),
207 array('label' => $texts->get_fd_message('stat_active'), 'value' => $active, 'class' => 'success', 'filter' => 'active'),
208 array('label' => $texts->get_fd_message('stat_locked'), 'value' => $locked, 'class' => 'danger', 'filter' => 'locked'),
209 array('label' => $texts->get_fd_message('stat_verified'), 'value' => $verified, 'class' => 'info', 'filter' => 'verified'),
210 );
211
212 $html = '<div class="row g-2 mb-3">';
213 foreach ($items as $item) {
214 $url = $item['filter'] === '' ? $this->url('list_user') : $this->url('list_user', array('filter' => $item['filter']));
215 $html .= '<div class="col-6 col-lg-3"><a class="text-decoration-none" href="' . dbx()->esc($url) . '"><div class="border rounded p-3 bg-light h-100">'
216 . '<div class="text-muted small">' . dbx()->esc($item['label']) . '</div>'
217 . '<div class="fs-4 fw-semibold text-' . dbx()->esc($item['class']) . '">' . dbx()->esc($item['value']) . '</div>'
218 . '</div></a></div>';
219 }
220 $html .= '</div>';
221 return $html;
222 }
223
224 private function nav_html($active, $texts) {
225 $tabs = array(
226 'users' => array('label' => $texts->get_fd_message('tab_users'), 'url' => $this->url('list_user')),
227 'groups' => array('label' => $texts->get_fd_message('tab_groups'), 'url' => $this->url('list_groups')),
228 );
229
230 $html = '<ul class="nav nav-tabs mb-3">';
231 foreach ($tabs as $key => $tab) {
232 $class = ($key === $active) ? ' active' : '';
233 $html .= '<li class="nav-item"><a class="nav-link' . $class . '" href="' . dbx()->esc($tab['url']) . '">' . dbx()->esc($tab['label']) . '</a></li>';
234 }
235 $html .= '</ul>';
236 return $html;
237 }
238
245 private function validate_password_change(bool $isNew, string $password, string $repeat, $texts): array {
246 if (!$isNew && $password === '' && $repeat === '') {
247 return array('change' => false, 'field' => '', 'message' => '');
248 }
249 if ($password === '') {
250 return array('change' => false, 'field' => 'password_new', 'message' => $texts->get_fd_message('password_required'));
251 }
252 if ($repeat === '') {
253 return array('change' => false, 'field' => 'password_new2', 'message' => $texts->get_fd_message('password_repeat_required'));
254 }
255 if ($password !== $repeat) {
256 return array('change' => false, 'field' => 'password_new2', 'message' => $texts->get_fd_message('password_mismatch'));
257 }
258
259 $length = function_exists('mb_strlen') ? mb_strlen($password) : strlen($password);
260 if ($length < 6) {
261 return array('change' => false, 'field' => 'password_new', 'message' => $texts->get_fd_message('password_too_short'));
262 }
263
264 return array('change' => true, 'field' => '', 'message' => '');
265 }
266
267 private function settings_after_password_change(string $raw): string {
268 $settings = json_decode($raw, true);
269 $settings = is_array($settings) ? $settings : array();
270 unset($settings['password_reset_required']);
271 $settings['password_changed_at'] = date(DATE_ATOM);
272 return (string)json_encode(
273 $settings,
274 JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
275 );
276 }
277
278 private function user_setting_date($value, $texts): string {
279 if (is_numeric($value) && (int)$value > 0) {
280 return date('d.m.Y H:i:s', (int)$value);
281 }
282
283 $value = trim((string)$value);
284 if ($value === '') return $texts->get_fd_message('settings_empty');
285 $timestamp = strtotime($value);
286 return $timestamp ? date('d.m.Y H:i:s', $timestamp) : $value;
287 }
288
289 private function user_setting_row(string $label, string $value): string {
290 return '<div class="row g-2 py-1 border-bottom">'
291 . '<div class="col-sm-5 fw-semibold">' . dbx()->esc($label) . '</div>'
292 . '<div class="col-sm-7">' . $value . '</div>'
293 . '</div>';
294 }
295
296 private function user_setting_value($value, $texts): string {
297 if (is_bool($value)) {
298 return $value
299 ? $texts->get_fd_message('option_yes')
300 : $texts->get_fd_message('option_no');
301 }
302 if ($value === null || $value === '') {
303 return '<span class="text-muted">'
304 . dbx()->esc($texts->get_fd_message('settings_not_set'))
305 . '</span>';
306 }
307 if (is_scalar($value)) return dbx()->esc((string)$value);
308 return '<span class="text-muted">'
309 . dbx()->esc($texts->format_fd_message(
310 'settings_entries',
311 array('count' => count((array)$value))
312 ))
313 . '</span>';
314 }
315
316 private function user_settings_view(array $user, $texts): string {
317 $raw = trim((string)($user['settings'] ?? ''));
318 $settings = $raw !== '' ? json_decode($raw, true) : array();
319 if (!is_array($settings)) {
320 return '<div class="alert alert-warning mb-0">'
321 . dbx()->esc($texts->get_fd_message('settings_invalid'))
322 . '</div>';
323 }
324
325 $confirm = is_array($settings['register_confirm'] ?? null) ? $settings['register_confirm'] : array();
326 $confirmed = (int)($user['is_confirm'] ?? 0) === 1;
327 $expires = (int)($confirm['expires'] ?? 0);
328 if ($confirmed) {
329 $status = '<span class="badge bg-success">' . dbx()->esc($texts->get_fd_message('settings_email_confirmed')) . '</span>';
330 } elseif ($confirm && $expires > 0 && $expires < time()) {
331 $status = '<span class="badge bg-danger">' . dbx()->esc($texts->get_fd_message('settings_link_expired')) . '</span>';
332 } elseif ($confirm) {
333 $status = '<span class="badge bg-warning text-dark">' . dbx()->esc($texts->get_fd_message('settings_confirmation_pending')) . '</span>';
334 } else {
335 $status = '<span class="badge bg-secondary">' . dbx()->esc($texts->get_fd_message('settings_no_confirmation')) . '</span>';
336 }
337
338 $html = '<div class="dbx-user-settings-view">';
339 $html .= $this->user_setting_row($texts->get_fd_message('settings_registration_status'), $status);
340 if ($confirm) {
341 $html .= $this->user_setting_row(
342 $texts->get_fd_message('settings_mail_sent'),
343 dbx()->esc($this->user_setting_date($confirm['sent'] ?? '', $texts))
344 );
345 $html .= $this->user_setting_row(
346 $texts->get_fd_message('settings_link_valid_until'),
347 dbx()->esc($this->user_setting_date($confirm['expires'] ?? '', $texts))
348 );
349 }
350
351 if (array_key_exists('password_reset_required', $settings)) {
352 $required = !empty($settings['password_reset_required'])
353 ? '<span class="badge bg-warning text-dark">' . dbx()->esc($texts->get_fd_message('option_yes')) . '</span>'
354 : '<span class="badge bg-secondary">' . dbx()->esc($texts->get_fd_message('option_no')) . '</span>';
355 $html .= $this->user_setting_row($texts->get_fd_message('settings_password_change'), $required);
356 }
357
358 foreach ($settings as $key => $value) {
359 if (in_array((string)$key, array('register_confirm', 'password_reset_required'), true)) continue;
360 $label = ucwords(str_replace(array('_', '-'), ' ', (string)$key));
361 $display = preg_match('/(^|[_-])(pass(word)?|token|hash|secret|key)($|[_-])/i', (string)$key)
362 ? '<span class="text-muted">' . dbx()->esc($texts->get_fd_message('settings_protected')) . '</span>'
363 : $this->user_setting_value($value, $texts);
364 $html .= $this->user_setting_row($label, $display);
365 }
366
367 $html .= '<div class="small text-muted mt-2"><i class="bi bi-shield-lock"></i> '
368 . dbx()->esc($texts->get_fd_message('settings_security_hint'))
369 . '</div>';
370 $html .= '</div>';
371 return $html;
372 }
373
374 private function handle_user_action(&$message, $texts) {
375 $db = dbx()->get_system_obj('dbxDB');
376 $do = dbx()->get_modul_var('dbx_do', '', 'parameter');
377 if (!$do) {
378 $do = dbx()->get_modul_var('dbx_run3', '', 'parameter');
379 }
380
381 $rid = dbx()->get_modul_var('rid', 0, 'int');
382 if (!$do || !$rid) {
383 return '';
384 }
385
386 if ($do === 'row_edit') {
387 return $this->edit_user($rid);
388 }
389
390 // row_delete ist eine dbxReport-Standardaktion und wurde bereits vor
391 // dem Modulstart zentral inklusive RID geprueft.
392 if ($do !== 'row_delete' && !$this->check_action_token($message, $texts)) {
393 return '';
394 }
395
396 if ($do === 'row_delete') {
397 if ($rid <= 1 || $rid == dbx()->user()) {
398 $message = $texts->get_fd_message('delete_protected');
399 return '';
400 }
401 $ok = $db->delete($this->ddUser, $rid);
402 $message = $ok
403 ? $texts->get_fd_message('delete_success')
404 : $texts->get_fd_message('delete_error');
405 return '';
406 }
407
408 $data = array();
409 if ($do === 'verify') {
410 $data['is_confirm'] = 1;
411 }
412 if ($do === 'unverify') {
413 $data['is_confirm'] = 0;
414 }
415 if ($do === 'lock') {
416 if ($rid <= 1 || $rid == dbx()->user()) {
417 $message = $texts->get_fd_message('lock_protected');
418 return '';
419 }
420 $data['status'] = 0;
421 }
422 if ($do === 'unlock') {
423 $data['status'] = 1;
424 }
425 if ($do === 'reset_password') {
426 $newPassword = dbx()->new_password(12);
427 $data['pass'] = password_hash($newPassword, PASSWORD_DEFAULT);
428 }
429
430 if ($data) {
431 $ok = $db->update($this->ddUser, $data, $rid);
432 if ($ok && $do === 'reset_password') {
433 $message = $texts->format_fd_message(
434 'reset_password',
435 array('id' => $rid, 'password' => $newPassword)
436 );
437 } else {
438 $message = $ok
439 ? $texts->get_fd_message('update_success')
440 : $texts->get_fd_message('update_error');
441 }
442 }
443
444 return '';
445 }
446
447 private function decorate_users($rows, $texts) {
448 $out = array();
449 foreach ((array)$rows as $row) {
450 $row['user'] = $this->user_identity($row);
451 $row['roles_view'] = $this->roles_label($row['roles'] ?? '', $texts);
452 $row['verified'] = $this->verified_label($row['is_confirm'] ?? '', $texts);
453 $row['login'] = $this->status_label($row['status'] ?? '', $texts);
454 $row['profile'] = '<span class="small text-muted">'
455 . dbx()->esc(trim((string)($row['language'] ?? '')))
456 . ' / ' . dbx()->esc(trim((string)($row['design'] ?? '')))
457 . ' / ' . dbx()->esc(trim((string)($row['color'] ?? '')))
458 . '</span>';
459 $row['ops'] = $this->user_actions($row, $texts);
460 $out[] = $row;
461 }
462 return $out;
463 }
464
465 private function report_users() {
466 $message = '';
467 $db = dbx()->get_system_obj('dbxDB');
468 $oReport = new dbxReport_AdminUser;
469 $oReport->init('report-admin-user', 'report-admin-user');
470 $oReport->_fd = 'dbxAdmin|rpt-admin-user-selection';
471 $oReport->load_fd_messages();
472 $content = $this->handle_user_action($message, $oReport);
473 if ($content !== '') {
474 return $content;
475 }
476 $oReport->add_rep('frame_use_form', '0');
477 $oReport->add_rep('report_shell_class', 'dbx-admin-users');
478 $oReport->add_rep('report_shell_attrs', 'data-dbx="lib=report|form=0"');
479 $oReport->_dd = $this->ddUser;
480 $oReport->_action = $this->url('list_user');
481 $oReport->_pages = true;
482 $oReport->_create_row_select = true;
483 $oReport->_create_row_edit = false;
484 $oReport->_create_row_delete = false;
485 $oReport->_create_sel_flds = true;
486 $oReport->_but_pagination = 7;
487 $oReport->_fld_id = 'id';
488 $oReport->_data = array('dbx_rrows' => 25, 'dbx_rsort' => 'id', 'dbx_rdesc' => 'ASC');
489 $oReport->_msg_info = '';
490 $oReport->_rpt_format = array(
491 'id' => 'html-chars',
492 'uname' => 'html-chars',
493 'name' => 'html-chars',
494 'email' => 'html-chars',
495 'user' => 'html',
496 'roles_view' => 'html',
497 'verified' => 'html',
498 'login' => 'html',
499 'profile' => 'html',
500 'ops' => 'html',
501 'lastvisit' => 'php-datetime-usr',
502 'update_date' => 'php-datetime-usr',
503 );
504
505 $newUserBtn = $this->modal_button(
506 $this->url('edit_user', array('rid' => 'new')),
507 'bi bi-person-plus',
508 $oReport->get_fd_message('new_user'),
509 'btn-success',
510 $oReport->get_fd_message('new_user'),
511 ''
512 );
513 $oReport->add_obj('new_user', 'obj-value', $newUserBtn);
514 $oReport->add_obj('bar_actions', 'obj-value', $newUserBtn);
515 $oReport->add_obj('stats', 'obj-value', $this->stats_html($oReport));
516 $oReport->add_obj('tabs', 'obj-value', $this->nav_html('users', $oReport));
517 $oReport->add_obj('rows_delete', 'obj-value', '');
518 $oReport->create_selection_fields('dbxAdmin|rpt-admin-user-selection');
519
520 if ($message !== '') {
521 if ($this->actionError !== '') {
522 $oReport->_msg_error = $message;
523 } else {
524 $oReport->_msg_success = $message;
525 }
526 }
527
528 if ($oReport->submit()) {
529 if (!$oReport->errors()) {
530 $oReport->_msg_success = $message ?: '';
531 } else {
532 $oReport->_msg_error = $oReport->get_fd_message(
533 'validation_error'
534 );
535 }
536 }
537
538 $search = $oReport->get_fld_val('dbx_rwhere', '', 'sqlsearch|max=64');
539 $rrows = $oReport->get_fld_val('dbx_rrows', 25, 'int');
540 $rpos = $oReport->get_fld_val('dbx_rpos', 0, 'int');
541 $rsort = $oReport->get_fld_val('dbx_rsort', 'id', 'parameter');
542 $rdesc = $oReport->get_fld_val('dbx_rdesc', 'ASC', 'parameter');
543 $select = $oReport->get_fld_val('dbx_rselect', 0, 'int');
544 $filter = dbx()->get_modul_var('filter', '', 'parameter');
545
546 $where = array();
547 if ($search !== '') {
548 $where['search'] = array(
549 'value' => $search,
550 'like' => array('uname', 'name', 'name2', 'email', 'roles', 'ort'),
551 'equal' => array('id'),
552 'mode' => 'contains',
553 );
554 }
555 if ($filter === 'locked') {
556 $where['status'] = 0;
557 } elseif ($filter === 'active') {
558 $where['status'] = 1;
559 } elseif ($filter === 'unverified') {
560 $where['is_confirm'] = 0;
561 } elseif ($filter === 'admin') {
562 $where['roles'] = array('op' => 'LIKE', 'value' => '%admin%');
563 } elseif ($filter === 'verified') {
564 $where['is_confirm'] = 1;
565 }
566 if (!$where) {
567 $where = '';
568 }
569 if ($select) {
570 if (is_array($where)) {
571 $where = $db->normalize_where($this->ddUser, $where);
572 }
573 $where = $oReport->add_rwhere_select($where);
574 }
575
576 $dbFields = array('id', 'uname', 'name', 'name2', 'email', 'roles', 'is_confirm', 'status', 'language', 'design', 'color', 'lastvisit', 'update_date');
577 $flds = array(
578 'id' => 'ID',
579 'user' => $oReport->get_fd_message('column_user'),
580 'roles_view' => $oReport->get_fd_message('column_roles'),
581 'verified' => $oReport->get_fd_message('column_verified'),
582 'login' => $oReport->get_fd_message('column_login'),
583 'profile' => $oReport->get_fd_message('column_profile'),
584 'lastvisit' => $oReport->get_fd_message('column_last_visit'),
585 'update_date'=> $oReport->get_fd_message('column_updated'),
586 'ops' => $oReport->get_fd_message('column_actions'),
587 );
588
589 $oReport->_rflds = $flds;
590 $oReport->_rrows = $rrows;
591 $oReport->_rpos = $rpos;
592 $oReport->_count_all = $db->count($this->ddUser);
593 $oReport->_rcount = $db->count($this->ddUser, $where);
594 $rows = $db->select($this->ddUser, $where, $dbFields, $rsort, $rdesc, '', $rrows, $rpos, 0);
595 $oReport->_rdata = $this->decorate_users($rows, $oReport);
596
597 return $oReport->run();
598 }
599
600 private function edit_user($rid = 'new') {
601 $db = dbx()->get_system_obj('dbxDB');
602 $isNew = ($rid === 'new' || !$rid);
603 $data = array(
604 'status' => 1,
605 'is_confirm' => 1,
606 'language' => 'de',
607 'roles' => 'member',
608 'edit' => 0,
609 'password_new' => '',
610 'password_new2' => '',
611 'settings' => '{}',
612 );
613
614 if (!$isNew) {
615 $record = $db->select1($this->ddUser, (int)$rid, '*', 0);
616 if (is_array($record)) {
617 $data = array_merge($data, $record);
618 }
619 }
620
621 $oForm = dbx()->get_system_obj('dbxForm');
622 $oForm->init('form-admin-user');
623 $oForm->_fd = 'dbxAdmin|rpt-admin-user-selection';
624 $oForm->load_fd_messages();
625 $oForm->add_module_bar(
626 $oForm->get_fd_message(
627 $isNew ? 'form_new_title' : 'form_edit_title'
628 ),
629 'bi-person-gear',
630 $oForm->get_fd_message(
631 $isNew ? 'form_new_subtitle' : 'form_edit_subtitle'
632 ),
633 true
634 );
635 $oForm->add_module_bar_form_actions(array('save' => true));
636 $oForm->_dd = $this->ddUser;
637 $oForm->_fld_id = 'id';
638 $oForm->_data = $data;
639 $oForm->_msg_info = $oForm->get_fd_message(
640 $isNew ? 'form_new_info' : 'form_edit_info'
641 );
642 $oForm->_action = $this->url('edit_user', array('rid' => ($isNew ? 'new' : (int)$rid)));
643 $oForm->set_activ_id($isNew ? 0 : (int)$rid);
644
645 $oForm->add_fld('uname', 'text-label', $oForm->get_fd_message('field_login'), 'alphanum|min=4|max=60');
646 $passwordHint = $oForm->get_fd_message(
647 $isNew ? 'password_hint_new' : 'password_hint_edit'
648 );
649 $oForm->add_fld('password_new', 'password-label', $oForm->get_fd_message('field_password'), 'varchar|max=128', placeholder: $passwordHint);
650 $oForm->add_fld('password_new2', 'password-label', $oForm->get_fd_message('field_password_repeat'), 'varchar|max=128', placeholder: $oForm->get_fd_message('password_repeat_hint'));
651 $oForm->add_fld('name', 'text-label', $oForm->get_fd_message('field_name'), 'parameter|max=255');
652 $oForm->add_fld('name2', 'text-label', $oForm->get_fd_message('field_name2'), 'parameter|max=255');
653 $oForm->add_fld('email', 'text-label', $oForm->get_fd_message('field_email'), 'email|max=255');
654 $oForm->add_fld('roles', 'multi-select', $oForm->get_fd_message('field_roles'), 'array|parameter', data: array('size' => 7), options: $this->role_options($oForm));
655 $oForm->add_fld('status', 'select-single-label', $oForm->get_fd_message('field_login_status'), 'int', options: array(
656 '1' => $oForm->get_fd_message('option_active'),
657 '0' => $oForm->get_fd_message('option_locked'),
658 '2' => $oForm->get_fd_message('option_waiting'),
659 ));
660 $oForm->add_fld('is_confirm', 'select-single-label', $oForm->get_fd_message('field_verified'), 'int', options: array(
661 '1' => $oForm->get_fd_message('option_verified'),
662 '0' => $oForm->get_fd_message('option_open'),
663 ));
664 $oForm->add_fld('edit', 'select-single-label', $oForm->get_fd_message('field_editor'), 'int', options: array(
665 '1' => $oForm->get_fd_message('option_yes'),
666 '0' => $oForm->get_fd_message('option_no'),
667 ));
668 $oForm->add_fld('language', 'select-single-label', $oForm->get_fd_message('field_language'), 'parameter|max=3', options: array(
669 'de' => $oForm->get_fd_message('language_de'),
670 'en' => $oForm->get_fd_message('language_en'),
671 'es' => $oForm->get_fd_message('language_es'),
672 ));
673 $oForm->add_fld('design', 'text-label', $oForm->get_fd_message('field_design'), 'parameter|max=32');
674 $oForm->add_fld('color', 'text-label', $oForm->get_fd_message('field_color'), 'parameter|max=32');
675 $oForm->add_fld('telefon', 'text-label', $oForm->get_fd_message('field_phone'), 'parameter|max=64');
676 $oForm->add_fld('handy', 'text-label', $oForm->get_fd_message('field_mobile'), 'parameter|max=64');
677 $oForm->add_fld('strasse', 'text-label', $oForm->get_fd_message('field_street'), 'parameter|max=255');
678 $oForm->add_fld('plz', 'text-label', $oForm->get_fd_message('field_postcode'), 'parameter|max=16');
679 $oForm->add_fld('ort', 'text-label', $oForm->get_fd_message('field_city'), 'parameter|max=255');
680 $oForm->add_fld('land', 'text-label', $oForm->get_fd_message('field_country'), 'parameter|max=32');
681 $oForm->add_obj('settings_view', 'obj-value', $this->user_settings_view($data, $oForm));
682
683 if ($oForm->submit()) {
684 $password = (string)$oForm->get_post_data('password_new', '', '*');
685 $passwordRepeat = (string)$oForm->get_post_data('password_new2', '', '*');
686 $passwordValidation = $this->validate_password_change($isNew, $password, $passwordRepeat, $oForm);
687 $passwordChanged = (bool)$passwordValidation['change'];
688 if ($passwordValidation['message'] !== '') {
689 $oForm->add_fld_error($passwordValidation['field'], $passwordValidation['message']);
690 $oForm->_msg_error = $passwordValidation['message'];
691 }
692
693 if (!$oForm->errors()) {
694 $roles = $oForm->get_post('roles', array(), 'array|parameter');
695 if (!is_array($roles)) {
696 $roles = array_filter(array_map('trim', explode(',', (string)$roles)));
697 }
698
699 $values = array(
700 'uname' => $oForm->get_post('uname', '', 'alphanum|max=60'),
701 'name' => $oForm->get_post('name', '', 'parameter|max=255'),
702 'name2' => $oForm->get_post('name2', '', 'parameter|max=255'),
703 'email' => $oForm->get_post('email', '', 'email|max=255'),
704 'roles' => implode(',', array_filter(array_map('trim', $roles))),
705 'status' => $oForm->get_post('status', 1, 'int'),
706 'is_confirm' => $oForm->get_post('is_confirm', 0, 'int'),
707 'edit' => $oForm->get_post('edit', 0, 'int'),
708 'language' => $oForm->get_post('language', 'de', 'parameter|max=3'),
709 'design' => $oForm->get_post('design', '', 'parameter|max=32'),
710 'color' => $oForm->get_post('color', '', 'parameter|max=32'),
711 'telefon' => $oForm->get_post('telefon', '', 'parameter|max=64'),
712 'handy' => $oForm->get_post('handy', '', 'parameter|max=64'),
713 'strasse' => $oForm->get_post('strasse', '', 'parameter|max=255'),
714 'plz' => $oForm->get_post('plz', '', 'parameter|max=16'),
715 'ort' => $oForm->get_post('ort', '', 'parameter|max=255'),
716 'land' => $oForm->get_post('land', '', 'parameter|max=32'),
717 'settings' => (string)($data['settings'] ?? '{}'),
718 );
719
720 if ($passwordChanged) {
721 $values['pass'] = password_hash($password, PASSWORD_DEFAULT);
722 $values['settings'] = $this->settings_after_password_change(
723 (string)($data['settings'] ?? '{}')
724 );
725 }
726
727 $server = $db->get_dd_server($this->ddUser);
728 $uname = $db->escape($values['uname'], $server);
729 $duplicateWhere = "uname='$uname'";
730 if (!$isNew) {
731 $duplicateWhere .= ' AND id <> ' . (int)$rid;
732 }
733 if ($db->count($this->ddUser, $duplicateWhere) > 0) {
734 $oForm->add_fld_error(
735 'uname',
736 $oForm->get_fd_message('duplicate_login')
737 );
738 $oForm->_msg_error = $oForm->get_fd_message(
739 'duplicate_login'
740 );
741 } else {
742 $ok = $db->save($this->ddUser, $values, $isNew ? 0 : (int)$rid);
743 if ($ok) {
744 $oForm->_msg_success = $oForm->get_fd_message(
745 'user_save_success'
746 );
747 } else {
748 $oForm->_msg_error = $oForm->get_fd_message(
749 'user_save_error'
750 );
751 }
752 }
753 } else {
754 if (!$oForm->_msg_error) {
755 $oForm->_msg_error = $oForm->get_fd_message(
756 'validation_error'
757 );
758 }
759 }
760 }
761
762 return $oForm->run();
763 }
764
765 private function group_actions($row, $texts) {
766 $id = (int)($row['id'] ?? 0);
767 if ($id <= 0) {
768 return '';
769 }
770 $html = $this->modal_button(
771 $this->url('edit_group', array('rid' => $id)),
772 'bi bi-pencil-square',
773 $texts->get_fd_message('action_edit')
774 );
775 if ((string)($row['active'] ?? '') === '1') {
776 $html .= $this->action_button($this->action_url('list_groups', array('dbx_do' => 'group_deactivate', 'rid' => $id)), 'bi bi-pause-circle', $texts->get_fd_message('action_deactivate'), 'btn-outline-warning');
777 } else {
778 $html .= $this->action_button($this->action_url('list_groups', array('dbx_do' => 'group_activate', 'rid' => $id)), 'bi bi-check-circle', $texts->get_fd_message('action_activate'), 'btn-outline-success');
779 }
780 $html .= $this->delete_button(
781 $this->action_url('list_groups', array('dbx_do' => 'row_delete', 'rid' => $id)),
782 $texts->get_fd_message('action_delete'),
783 $texts
784 );
785 return $html;
786 }
787
788 private function handle_group_action(&$message, $texts) {
789 $db = dbx()->get_system_obj('dbxDB');
790 $do = dbx()->get_modul_var('dbx_do', '', 'parameter');
791 if (!$do) {
792 $do = dbx()->get_modul_var('dbx_run3', '', 'parameter');
793 }
794 $rid = dbx()->get_modul_var('rid', 0, 'int');
795
796 if ($do === 'row_edit' && $rid) {
797 return $this->edit_group($rid);
798 }
799 if ($do && $rid && $do !== 'row_delete' && !$this->check_action_token($message, $texts)) {
800 return '';
801 }
802 if ($do === 'row_delete' && $rid) {
803 $ok = $db->delete($this->ddGroup, $rid);
804 $message = $ok
805 ? $texts->get_fd_message('delete_success')
806 : $texts->get_fd_message('delete_error');
807 }
808 if ($do === 'group_activate' && $rid) {
809 $ok = $db->update($this->ddGroup, array('active' => 1), $rid);
810 $message = $ok
811 ? $texts->get_fd_message('activate_success')
812 : $texts->get_fd_message('activate_error');
813 }
814 if ($do === 'group_deactivate' && $rid) {
815 $ok = $db->update($this->ddGroup, array('active' => 0), $rid);
816 $message = $ok
817 ? $texts->get_fd_message('deactivate_success')
818 : $texts->get_fd_message('deactivate_error');
819 }
820
821 return '';
822 }
823
824 private function decorate_groups($rows, $texts) {
825 $users = dbx()->get_system_obj('dbxDB')->select($this->ddUser, '', array('roles'), 'id', 'ASC', '', 0, 0, 0);
826 $out = array();
827 foreach ((array)$rows as $row) {
828 $name = (string)($row['name'] ?? '');
829 $count = 0;
830 foreach ((array)$users as $user) {
831 $roles = array_filter(array_map('trim', explode(',', (string)($user['roles'] ?? ''))));
832 if (in_array($name, $roles, true)) {
833 $count++;
834 }
835 }
836 $row['active_view'] = ((string)($row['active'] ?? '') === '1')
837 ? $this->badge($texts->get_fd_message('status_active'), 'success')
838 : $this->badge($texts->get_fd_message('status_inactive'), 'secondary');
839 $row['users'] = (string)$count;
840 $row['ops'] = $this->group_actions($row, $texts);
841 $out[] = $row;
842 }
843 return $out;
844 }
845
846 private function report_groups() {
847 $message = '';
848 $db = dbx()->get_system_obj('dbxDB');
849 $oReport = new dbxReport_AdminUser;
850 $oReport->init('report-admin-groups', 'report-admin-groups');
851 $oReport->_fd = 'dbxAdmin|rpt-admin-groups-selection';
852 $oReport->load_fd_messages();
853 $content = $this->handle_group_action($message, $oReport);
854 if ($content !== '') {
855 return $content;
856 }
857 $oReport->add_rep('frame_use_form', '0');
858 $oReport->add_rep('report_shell_class', 'dbx-admin-groups');
859 $oReport->add_rep('report_shell_attrs', 'data-dbx="lib=report|form=0"');
860 $oReport->_dd = $this->ddGroup;
861 $oReport->_action = $this->url('list_groups');
862 $oReport->_pages = true;
863 $oReport->_create_row_select = true;
864 $oReport->_create_row_edit = false;
865 $oReport->_create_row_delete = false;
866 $oReport->_create_sel_flds = true;
867 $oReport->_but_pagination = 7;
868 $oReport->_fld_id = 'id';
869 $oReport->_data = array('dbx_rrows' => 25, 'dbx_rsort' => 'name', 'dbx_rdesc' => 'ASC');
870 $oReport->_msg_info = '';
871 $oReport->_rpt_format = array(
872 'id' => 'html-chars',
873 'name' => 'html-chars',
874 'description' => 'html-chars',
875 'active_view' => 'html',
876 'ops' => 'html',
877 );
878 $newGroupBtn = $this->modal_button(
879 $this->url('edit_group', array('rid' => 'new')),
880 'bi bi-plus-circle',
881 $oReport->get_fd_message('new_group'),
882 'btn-success',
883 $oReport->get_fd_message('new_group'),
884 ''
885 );
886 $oReport->add_obj('new_group', 'obj-value', $newGroupBtn);
887 $oReport->add_obj('bar_actions', 'obj-value', $newGroupBtn);
888 $oReport->add_obj('tabs', 'obj-value', $this->nav_html('groups', $oReport));
889 $oReport->add_obj('rows_delete', 'obj-value', '');
890 $oReport->create_selection_fields('dbxAdmin|rpt-admin-groups-selection');
891 if ($message !== '') {
892 if ($this->actionError !== '') {
893 $oReport->_msg_error = $message;
894 } else {
895 $oReport->_msg_success = $message;
896 }
897 }
898
899 $search = $oReport->get_fld_val('dbx_rwhere', '', 'sqlsearch|max=64');
900 $rrows = $oReport->get_fld_val('dbx_rrows', 25, 'int');
901 $rpos = $oReport->get_fld_val('dbx_rpos', 0, 'int');
902 $rsort = $oReport->get_fld_val('dbx_rsort', 'name', 'parameter');
903 $rdesc = $oReport->get_fld_val('dbx_rdesc', 'ASC', 'parameter');
904 $where = '';
905 if ($search !== '') {
906 $where = array('search' => array('value' => $search, 'like' => array('name', 'description'), 'mode' => 'contains'));
907 }
908
909 $flds = array(
910 'id' => 'ID',
911 'name' => $oReport->get_fd_message('column_group'),
912 'description' => $oReport->get_fd_message('column_description'),
913 'active_view' => $oReport->get_fd_message('column_status'),
914 'users' => $oReport->get_fd_message('column_users'),
915 'ops' => $oReport->get_fd_message('column_actions'),
916 );
917
918 $oReport->_rflds = $flds;
919 $oReport->_rrows = $rrows;
920 $oReport->_rpos = $rpos;
921 $oReport->_count_all = $db->count($this->ddGroup);
922 $oReport->_rcount = $db->count($this->ddGroup, $where);
923 $rows = $db->select($this->ddGroup, $where, array('id', 'name', 'description', 'active'), $rsort, $rdesc, '', $rrows, $rpos, 0);
924 $oReport->_rdata = $this->decorate_groups($rows, $oReport);
925
926 return $oReport->run();
927 }
928
929 private function edit_group($rid = 'new') {
930 $db = dbx()->get_system_obj('dbxDB');
931 $isNew = ($rid === 'new' || !$rid);
932 $data = array('active' => 1);
933 if (!$isNew) {
934 $record = $db->select1($this->ddGroup, (int)$rid, '*', 0);
935 if (is_array($record)) {
936 $data = array_merge($data, $record);
937 }
938 }
939
940 $oForm = dbx()->get_system_obj('dbxForm');
941 $oForm->init('form-admin-group');
942 $oForm->_fd = 'dbxAdmin|rpt-admin-groups-selection';
943 $oForm->load_fd_messages();
944 $oForm->add_module_bar(
945 $oForm->get_fd_message(
946 $isNew ? 'form_new_title' : 'form_edit_title'
947 ),
948 'bi-people',
949 $oForm->get_fd_message(
950 $isNew ? 'form_new_subtitle' : 'form_edit_subtitle'
951 ),
952 true
953 );
954 $actionUrl = $this->url('edit_group', array('rid' => ($isNew ? 'new' : (int)$rid)));
955 $oForm->add_module_bar_form_actions(array(
956 'save' => true,
957 'reload' => true,
958 'reload_url' => $actionUrl,
959 'delete' => !$isNew,
960 'delete_url' => $isNew ? '' : $this->action_url('list_groups', array('dbx_do' => 'row_delete', 'rid' => (int)$rid)),
961 'delete_title' => $oForm->get_fd_message('action_delete'),
962 ));
963 $oForm->add_obj('bar_actions', 'obj-value',
964 '<a class="btn btn-outline-secondary btn-sm" href="' . dbx()->esc($this->url('list_groups')) . '" title="' . dbx()->esc($oForm->get_fd_message('group_list_title')) . '"><i class="bi bi-list-ul"></i></a>'
965 );
966 $oForm->_dd = $this->ddGroup;
967 $oForm->_fld_id = 'id';
968 $oForm->_data = $data;
969 $oForm->_msg_info = $oForm->get_fd_message(
970 $isNew ? 'form_new_info' : 'form_edit_info'
971 );
972 $oForm->_action = $this->url('edit_group', array('rid' => ($isNew ? 'new' : (int)$rid)));
973
974 $oForm->add_fld('name', 'text-label', $oForm->get_fd_message('field_group'), 'parameter|min=2|max=255');
975 $oForm->add_fld('description', 'textarea-label', $oForm->get_fd_message('field_description'), 'parameter|max=1000', data: array('rows' => 5));
976 $oForm->add_fld('active', 'select-single-label', $oForm->get_fd_message('field_active'), 'int', options: array(
977 '1' => $oForm->get_fd_message('option_active'),
978 '0' => $oForm->get_fd_message('option_inactive'),
979 ));
980
981 if ($oForm->submit()) {
982 if (!$oForm->errors()) {
983 $values = array(
984 'name' => $oForm->get_post('name', '', 'parameter|max=255'),
985 'description' => $oForm->get_post('description', '', 'parameter|max=1000'),
986 'active' => $oForm->get_post('active', 0, 'int'),
987 );
988 $ok = $db->save($this->ddGroup, $values, $isNew ? 0 : (int)$rid);
989 if ($ok) {
990 $oForm->_msg_success = $oForm->get_fd_message(
991 'group_save_success'
992 );
993 } else {
994 $oForm->_msg_error = $oForm->get_fd_message(
995 'group_save_error'
996 );
997 }
998 } else {
999 $oForm->_msg_error = $oForm->get_fd_message(
1000 'validation_error'
1001 );
1002 }
1003 }
1004
1005 return $oForm->run();
1006 }
1007
1008 public function run($action = '') {
1009 $run = dbx()->get_modul_var('dbx_run2', 'list_user', 'parameter');
1010
1011 switch ($run) {
1012 case '':
1013 case 'list_user':
1014 return $this->report_users();
1015
1016 case 'edit_user':
1017 return $this->edit_user(dbx()->get_modul_var('rid', 'new', 'parameter'));
1018
1019 case 'list_groups':
1020 case 'groups':
1021 return $this->report_groups();
1022
1023 case 'edit_group':
1024 return $this->edit_group(dbx()->get_modul_var('rid', 'new', 'parameter'));
1025 }
1026
1027 $oTPL = dbx()->get_system_obj('dbxTPL');
1028 return $oTPL->get_tpl('dbx|alert-warning', array('msg' => 'User Admin Work (' . dbx()->esc($run) . ') ist undefiniert.'));
1029 }
1030}
1031?>
init($fid, $tpl='', $first_page_lines=-1, $next_page_lines=-1, $current_line=-1)
user($key='id')
Liest einen Wert des aktuellen Benutzers.
Definition dbxApi.php:1305
dbx()
Liefert die zentrale dbXapp-API als Singleton.
Definition dbxApi.php:2805
esc($value)
Escaped einen Wert fuer HTML-Text und HTML-Attribute.
Definition dbxApi.php:2310
$_SERVER['REQUEST_METHOD']
if( $demoId<=0) foreach(array('create_date', 'create_uid', 'update_date', 'update_uid', 'owner',) as $systemField) $items
DBX schema administration.