11 private string $ddUser =
'dbxUser';
12 private string $ddGroup =
'dbxUser_groups';
13 private string $actionError =
'';
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);
23 private function action_url($run2, $params = array()) {
24 $url = $this->url($run2, $params);
25 $securedUrl =
dbx()->action_url(
$url);
26 if ($securedUrl !==
$url) {
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);
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);
41 private function check_action_token(&$message,
$texts) {
42 $do =
dbx()->get_modul_var(
'dbx_do',
'',
'parameter');
44 $do =
dbx()->get_modul_var(
'dbx_run3',
'',
'parameter');
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)) {
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'] ??
'');
58 private function badge($label,
$class =
'secondary') {
59 return '<span class="badge bg-' .
dbx()->esc(
$class) .
'">' .
dbx()->esc($label) .
'</span>';
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>';
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>';
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>';
85 private function role_options(
$texts) {
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'),
94 $db =
dbx()->get_system_obj(
'dbxDB');
95 $groups =
$db->select($this->ddGroup,
'', array(
'name',
'description',
'active'),
'name',
'ASC',
'', 0, 0, 0);
98 $name = trim((
string)($group[
'name'] ??
''));
99 if ($name ===
'' || array_key_exists($name,
$options)) {
102 $description = trim((
string)($group[
'description'] ??
''));
103 $options[$name] = $description !==
'' ? $description : $name;
110 private function status_label($status,
$texts) {
111 if ((
string)$status ===
'0') {
112 return $this->badge(
$texts->get_fd_message(
'status_locked'),
'danger');
114 if ((
string)$status ===
'1') {
115 return $this->badge(
$texts->get_fd_message(
'status_active'),
'success');
117 if ((
string)$status ===
'2') {
118 return $this->badge(
$texts->get_fd_message(
'status_waiting'),
'warning');
120 return $this->badge(
$texts->get_fd_message(
'status_unknown'),
'secondary');
124 return ((
string)
$value ===
'1')
125 ? $this->badge(
$texts->get_fd_message(
'verified'),
'success')
126 : $this->badge(
$texts->get_fd_message(
'unverified'),
'warning');
129 private function roles_label($roles,
$texts) {
130 $items = array_filter(array_map(
'trim', explode(
',', (
string)$roles)));
132 return $this->badge(
$texts->get_fd_message(
'no_roles'),
'secondary');
136 foreach (
$items as $role) {
137 $class = ($role ===
'admin') ?
'primary' : (($role ===
'api') ?
'dark' :
'info');
143 private function user_identity($row) {
144 $name = trim((
string)($row[
'name'] ??
''));
145 $uname = trim((
string)($row[
'uname'] ??
''));
146 $email = trim((
string)($row[
'email'] ??
''));
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>';
152 $html .=
'<div class="small"><a href="mailto:' .
dbx()->esc($email) .
'">' .
dbx()->esc($email) .
'</a></div>';
157 private function user_actions($row,
$texts) {
158 $id = (int)($row[
'id'] ?? 0);
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')
172 $html .=
'<span class="badge bg-secondary me-1">'
173 .
dbx()->esc(
$texts->get_fd_message(
'action_protected'))
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'));
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');
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');
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');
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'),
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');
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'),
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>';
224 private function nav_html($active,
$texts) {
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')),
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>';
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' =>
'');
250 return array(
'change' =>
false,
'field' =>
'password_new',
'message' =>
$texts->get_fd_message(
'password_required'));
252 if ($repeat ===
'') {
253 return array(
'change' =>
false,
'field' =>
'password_new2',
'message' =>
$texts->get_fd_message(
'password_repeat_required'));
256 return array(
'change' =>
false,
'field' =>
'password_new2',
'message' =>
$texts->get_fd_message(
'password_mismatch'));
261 return array(
'change' =>
false,
'field' =>
'password_new',
'message' =>
$texts->get_fd_message(
'password_too_short'));
264 return array(
'change' =>
true,
'field' =>
'',
'message' =>
'');
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(
274 JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
278 private function user_setting_date(
$value,
$texts):
string {
280 return date(
'd.m.Y H:i:s', (
int)
$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;
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>'
296 private function user_setting_value(
$value,
$texts):
string {
299 ?
$texts->get_fd_message(
'option_yes')
300 :
$texts->get_fd_message(
'option_no');
303 return '<span class="text-muted">'
304 .
dbx()->esc(
$texts->get_fd_message(
'settings_not_set'))
308 return '<span class="text-muted">'
311 array(
'count' => count((array)
$value))
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'))
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);
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>';
333 $status =
'<span class="badge bg-warning text-dark">' .
dbx()->esc(
$texts->get_fd_message(
'settings_confirmation_pending')) .
'</span>';
335 $status =
'<span class="badge bg-secondary">' .
dbx()->esc(
$texts->get_fd_message(
'settings_no_confirmation')) .
'</span>';
338 $html =
'<div class="dbx-user-settings-view">';
339 $html .= $this->user_setting_row(
$texts->get_fd_message(
'settings_registration_status'), $status);
341 $html .= $this->user_setting_row(
342 $texts->get_fd_message(
'settings_mail_sent'),
345 $html .= $this->user_setting_row(
346 $texts->get_fd_message(
'settings_link_valid_until'),
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);
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>'
364 $html .= $this->user_setting_row($label, $display);
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'))
374 private function handle_user_action(&$message,
$texts) {
375 $db =
dbx()->get_system_obj(
'dbxDB');
376 $do =
dbx()->get_modul_var(
'dbx_do',
'',
'parameter');
378 $do =
dbx()->get_modul_var(
'dbx_run3',
'',
'parameter');
381 $rid =
dbx()->get_modul_var(
'rid', 0,
'int');
386 if ($do ===
'row_edit') {
387 return $this->edit_user($rid);
392 if ($do !==
'row_delete' && !$this->check_action_token($message,
$texts)) {
396 if ($do ===
'row_delete') {
397 if ($rid <= 1 || $rid ==
dbx()->
user()) {
398 $message =
$texts->get_fd_message(
'delete_protected');
401 $ok =
$db->delete($this->ddUser, $rid);
403 ?
$texts->get_fd_message(
'delete_success')
404 :
$texts->get_fd_message(
'delete_error');
409 if ($do ===
'verify') {
410 $data[
'is_confirm'] = 1;
412 if ($do ===
'unverify') {
413 $data[
'is_confirm'] = 0;
415 if ($do ===
'lock') {
416 if ($rid <= 1 || $rid ==
dbx()->
user()) {
417 $message =
$texts->get_fd_message(
'lock_protected');
422 if ($do ===
'unlock') {
425 if ($do ===
'reset_password') {
426 $newPassword =
dbx()->new_password(12);
427 $data[
'pass'] = password_hash($newPassword, PASSWORD_DEFAULT);
431 $ok =
$db->update($this->ddUser, $data, $rid);
432 if (
$ok && $do ===
'reset_password') {
433 $message =
$texts->format_fd_message(
435 array(
'id' => $rid,
'password' => $newPassword)
439 ?
$texts->get_fd_message(
'update_success')
440 :
$texts->get_fd_message(
'update_error');
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'] ??
'')))
459 $row[
'ops'] = $this->user_actions($row,
$texts);
465 private function report_users() {
467 $db =
dbx()->get_system_obj(
'dbxDB');
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);
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',
496 'roles_view' =>
'html',
497 'verified' =>
'html',
501 'lastvisit' =>
'php-datetime-usr',
502 'update_date' =>
'php-datetime-usr',
505 $newUserBtn = $this->modal_button(
506 $this->url(
'edit_user', array(
'rid' =>
'new')),
508 $oReport->get_fd_message(
'new_user'),
510 $oReport->get_fd_message(
'new_user'),
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');
520 if ($message !==
'') {
521 if ($this->actionError !==
'') {
522 $oReport->_msg_error = $message;
524 $oReport->_msg_success = $message;
528 if ($oReport->submit()) {
529 if (!$oReport->errors()) {
530 $oReport->_msg_success = $message ?:
'';
532 $oReport->_msg_error = $oReport->get_fd_message(
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');
547 if ($search !==
'') {
548 $where[
'search'] = array(
550 'like' => array(
'uname',
'name',
'name2',
'email',
'roles',
'ort'),
551 'equal' => array(
'id'),
552 'mode' =>
'contains',
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;
570 if (is_array($where)) {
571 $where =
$db->normalize_where($this->ddUser, $where);
573 $where = $oReport->add_rwhere_select($where);
576 $dbFields = array(
'id',
'uname',
'name',
'name2',
'email',
'roles',
'is_confirm',
'status',
'language',
'design',
'color',
'lastvisit',
'update_date');
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'),
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);
597 return $oReport->run();
600 private function edit_user($rid =
'new') {
601 $db =
dbx()->get_system_obj(
'dbxDB');
602 $isNew = ($rid ===
'new' || !$rid);
609 'password_new' =>
'',
610 'password_new2' =>
'',
615 $record =
$db->select1($this->ddUser, (
int)$rid,
'*', 0);
616 if (is_array($record)) {
617 $data = array_merge($data, $record);
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'
630 $oForm->get_fd_message(
631 $isNew ?
'form_new_subtitle' :
'form_edit_subtitle'
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'
642 $oForm->_action = $this->url(
'edit_user', array(
'rid' => ($isNew ?
'new' : (
int)$rid)));
643 $oForm->set_activ_id($isNew ? 0 : (
int)$rid);
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'
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'),
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'),
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'),
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'),
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));
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'];
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)));
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'] ??
'{}'),
720 if ($passwordChanged) {
721 $values[
'pass'] = password_hash(
$password, PASSWORD_DEFAULT);
722 $values[
'settings'] = $this->settings_after_password_change(
723 (
string)($data[
'settings'] ??
'{}')
728 $uname =
$db->escape($values[
'uname'],
$server);
729 $duplicateWhere =
"uname='$uname'";
731 $duplicateWhere .=
' AND id <> ' . (int)$rid;
733 if (
$db->count($this->ddUser, $duplicateWhere) > 0) {
734 $oForm->add_fld_error(
736 $oForm->get_fd_message(
'duplicate_login')
738 $oForm->_msg_error = $oForm->get_fd_message(
742 $ok =
$db->save($this->ddUser, $values, $isNew ? 0 : (
int)$rid);
744 $oForm->_msg_success = $oForm->get_fd_message(
748 $oForm->_msg_error = $oForm->get_fd_message(
754 if (!$oForm->_msg_error) {
755 $oForm->_msg_error = $oForm->get_fd_message(
762 return $oForm->run();
765 private function group_actions($row,
$texts) {
766 $id = (int)($row[
'id'] ?? 0);
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')
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');
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');
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'),
788 private function handle_group_action(&$message,
$texts) {
789 $db =
dbx()->get_system_obj(
'dbxDB');
790 $do =
dbx()->get_modul_var(
'dbx_do',
'',
'parameter');
792 $do =
dbx()->get_modul_var(
'dbx_run3',
'',
'parameter');
794 $rid =
dbx()->get_modul_var(
'rid', 0,
'int');
796 if ($do ===
'row_edit' && $rid) {
797 return $this->edit_group($rid);
799 if ($do && $rid && $do !==
'row_delete' && !$this->check_action_token($message,
$texts)) {
802 if ($do ===
'row_delete' && $rid) {
803 $ok =
$db->delete($this->ddGroup, $rid);
805 ?
$texts->get_fd_message(
'delete_success')
806 :
$texts->get_fd_message(
'delete_error');
808 if ($do ===
'group_activate' && $rid) {
809 $ok =
$db->update($this->ddGroup, array(
'active' => 1), $rid);
811 ?
$texts->get_fd_message(
'activate_success')
812 :
$texts->get_fd_message(
'activate_error');
814 if ($do ===
'group_deactivate' && $rid) {
815 $ok =
$db->update($this->ddGroup, array(
'active' => 0), $rid);
817 ?
$texts->get_fd_message(
'deactivate_success')
818 :
$texts->get_fd_message(
'deactivate_error');
825 $users =
dbx()->get_system_obj(
'dbxDB')->select($this->ddUser,
'', array(
'roles'),
'id',
'ASC',
'', 0, 0, 0);
827 foreach ((array)
$rows as $row) {
828 $name = (string)($row[
'name'] ??
'');
830 foreach ((array)$users as
$user) {
831 $roles = array_filter(array_map(
'trim', explode(
',', (
string)(
$user[
'roles'] ??
''))));
832 if (in_array($name, $roles,
true)) {
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);
846 private function report_groups() {
848 $db =
dbx()->get_system_obj(
'dbxDB');
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);
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',
878 $newGroupBtn = $this->modal_button(
879 $this->url(
'edit_group', array(
'rid' =>
'new')),
881 $oReport->get_fd_message(
'new_group'),
883 $oReport->get_fd_message(
'new_group'),
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;
895 $oReport->_msg_success = $message;
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');
905 if ($search !==
'') {
906 $where = array(
'search' => array(
'value' => $search,
'like' => array(
'name',
'description'),
'mode' =>
'contains'));
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'),
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);
926 return $oReport->run();
929 private function edit_group($rid =
'new') {
930 $db =
dbx()->get_system_obj(
'dbxDB');
931 $isNew = ($rid ===
'new' || !$rid);
932 $data = array(
'active' => 1);
934 $record =
$db->select1($this->ddGroup, (
int)$rid,
'*', 0);
935 if (is_array($record)) {
936 $data = array_merge($data, $record);
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'
949 $oForm->get_fd_message(
950 $isNew ?
'form_new_subtitle' :
'form_edit_subtitle'
954 $actionUrl = $this->url(
'edit_group', array(
'rid' => ($isNew ?
'new' : (
int)$rid)));
955 $oForm->add_module_bar_form_actions(array(
958 'reload_url' => $actionUrl,
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'),
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>'
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'
972 $oForm->_action = $this->url(
'edit_group', array(
'rid' => ($isNew ?
'new' : (
int)$rid)));
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'),
981 if ($oForm->submit()) {
982 if (!$oForm->errors()) {
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'),
988 $ok =
$db->save($this->ddGroup, $values, $isNew ? 0 : (
int)$rid);
990 $oForm->_msg_success = $oForm->get_fd_message(
994 $oForm->_msg_error = $oForm->get_fd_message(
999 $oForm->_msg_error = $oForm->get_fd_message(
1005 return $oForm->run();
1008 public function run($action =
'') {
1009 $run =
dbx()->get_modul_var(
'dbx_run2',
'list_user',
'parameter');
1014 return $this->report_users();
1017 return $this->edit_user(
dbx()->get_modul_var(
'rid',
'new',
'parameter'));
1021 return $this->report_groups();
1024 return $this->edit_group(
dbx()->get_modul_var(
'rid',
'new',
'parameter'));
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.'));