3dbx()->use_system_class(
'dbxForm');
10 $this->oTPL=
dbx()->get_system_obj(
'dbxTPL');
13 private function texts() {
18 $texts->set_form_help_enabled(
false);
19 $texts->_fd =
'dbxMenu|menu-ui';
20 $texts->load_fd_messages();
26 private function get_menu_tpl($menu=
'undef') {
28 if (strpos($menu,
'_admin')) $access=
dbx()->can(
'admin');
29 if (strpos($menu,
'-admin')) $access=
dbx()->can(
'admin');
30 if (strpos($menu,
'admin_')) $access=
dbx()->can(
'admin');
31 if (strpos($menu,
'admin-')) $access=
dbx()->can(
'admin');
33 dbx()->debug(
"load get_menu_tpl=($menu)");
37 $oWeb =
dbx()->get_system_obj(
'dbxWebApp');
38 $baseSelf = $oWeb->normalize_self_url(
dbx()->get_system_var(
'dbx_self_url',
'?',
'*'));
39 $data[
'self'] = $oWeb->route_param_prefix($baseSelf);
40 $edit = (int)
dbx()->get_system_var(
'dbx_edit', 0,
'int');
41 $nextEdit = $this->next_editor_level($edit);
42 $data[
'edit_level'] = $edit;
43 $data[
'edit_next_level'] = $nextEdit;
44 $data[
'edit_toggle_url'] = $this->editor_level_url($baseSelf, $nextEdit);
45 $data[
'edit_level_menu'] = $this->render_editor_level_menu($baseSelf, $edit);
46 $data[
'edit_state_class'] = $edit ?
'is-active' :
'is-disabled';
47 $data[
'edit_toggle_title'] = $this->texts()->format_fd_message(
49 array(
'level' => $edit,
'label' => $this->editor_level_label($edit))
51 $lng = strtolower(trim((
string)
dbx()->get_system_var(
'dbx_lng',
'de')));
52 $lngMeta = $this->language_meta(
$lng);
53 $data[
'lng_active'] =
$lng;
54 $data[
'lng_active_flag'] = $lngMeta[
'flag'];
55 $data[
'lng_active_label'] = $lngMeta[
'label'];
56 $data[
'lng_menu'] = $this->render_language_menu($baseSelf,
$lng);
57 $data[
'lng_toggle_title'] = $this->texts()->format_fd_message(
59 array(
'language' => $lngMeta[
'label'])
61 $docsDisplay = stripos((
string)$menu,
'dbx-docs-main') !==
false;
62 $activeDesign = $this->active_frontend_design();
63 $data[
'design_active'] = $activeDesign;
64 $data[
'design_active_label'] = $docsDisplay ?
'dbxapp (Blau)' : $this->design_label($activeDesign);
65 $activeSkin = $this->normalize_design_skin($activeDesign,
dbx()->
get_skin());
66 $data[
'design_skin_menu'] = $this->render_design_skin_menu(
72 $data[
'design_toggle_title'] = $this->texts()->format_fd_message(
75 'design' => $data[
'design_active_label'],
76 'skin' => $docsDisplay
77 ? ($activeSkin ===
'dunkel' ?
'Dark' :
'Light')
78 : $this->skin_label($activeDesign, $activeSkin),
81 $openContactCount = $this->open_contact_count($menu);
82 $data[
'contact_open_count'] = $openContactCount;
83 $contactKey = $openContactCount === 1
85 :
'contact_open_many';
86 $data[
'contact_open_title'] = $this->texts()->format_fd_message(
88 array(
'count' => $openContactCount)
90 $data[
'contact_open_badge'] = $openContactCount > 0
91 ?
'<span class="dbx-contact-open-badge" aria-label="' . $this->h($data[
'contact_open_title']) .
'">'
92 . $openContactCount .
'</span>'
94 $cartCount = $this->shop_cart_count();
95 $data[
'shop_cart_count'] = $cartCount;
96 $data[
'shop_cart_badge'] = $this->shop_cart_badge($cartCount);
97 $i =
dbx()->next_id();
98 $content=$this->oTPL->get_tpl($menu,$data,
'htm',$i);
105 private function open_contact_count(
string $menu): int {
106 if (stripos($menu,
'admin') === false || !
dbx()->
can(
'admin')) {
111 $tickets =
dbx()->get_include_obj(
'dbxContactTicket',
'dbxContact');
112 if (is_object($tickets) && method_exists($tickets,
'openCount')) {
113 return max(0, (
int) $tickets->openCount());
115 }
catch (\Throwable $e) {
116 dbx()->debug(
'[dbxMenu] offene Kontaktanfragen konnten nicht gezaehlt werden: ' . $e->getMessage());
122 private function next_editor_level(
int $edit): int {
123 $levels = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
124 $idx = array_search($edit, $levels,
true);
126 if ($idx ===
false) {
130 return $levels[($idx + 1) % count($levels)];
133 private function editor_level_label(
int $edit): string {
134 if ($edit >= 0 && $edit <= 9) {
135 return $this->texts()->get_fd_message(
'editor_level_' . $edit);
138 return $this->texts()->get_fd_message(
'editor_level_reserve');
141 private function editor_level_icon(
int $edit): string {
143 case 0:
return 'bi-x-circle';
146 case 3:
return 'bi-pencil-square';
147 case 4:
return 'bi-card-checklist';
148 case 5:
return 'bi-database';
149 case 6:
return 'bi-braces';
150 case 7:
return 'bi-cpu';
151 case 8:
return 'bi-gear';
152 case 9:
return 'bi-files';
155 return 'bi-question-circle';
158 private function render_editor_level_menu(
string $baseSelf,
int $activeEdit): string {
159 $levels = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
162 foreach ($levels as $level) {
163 $class = $level === $activeEdit ?
' class="is-active"' :
'';
164 $url = htmlspecialchars($this->editor_level_url($baseSelf, $level), ENT_QUOTES,
'UTF-8');
165 $label = htmlspecialchars($this->editor_level_label($level), ENT_QUOTES,
'UTF-8');
166 $icon = htmlspecialchars($this->editor_level_icon($level), ENT_QUOTES,
'UTF-8');
169 $title = $this->h($this->texts()->format_fd_message(
170 'editor_level_title',
171 array(
'level' => $level,
'label' => $this->editor_level_label($level))
173 $item = $this->h($this->texts()->format_fd_message(
175 array(
'level' => $level,
'label' => $this->editor_level_label($level))
177 $html .=
'<a class="dbxEditLevel" href="' .
$url .
'" title="' . $title .
'">';
178 $html .=
'<i class="bi ' . $icon .
'"></i>';
179 $html .=
'<span>' . $item .
'</span>';
187 private function editor_level_url(
string $url,
int $edit): string {
188 $oWeb =
dbx()->get_system_obj(
'dbxWebApp');
189 return $oWeb->append_route_params(
190 $oWeb->normalize_self_url(
$url),
191 array(
'dbx_edit' => (
string) $edit)
195 private function language_catalog(): array {
197 'de' => array(
'label' =>
'Deutsch',
'flag' =>
'de'),
198 'en' => array(
'label' =>
'English',
'flag' =>
'gb'),
199 'es' => array(
'label' =>
'Español',
'flag' =>
'es'),
203 private function language_options(): array {
205 $accessible =
$config[
'accessible_lng'] ??
'de,en,es';
207 if (!is_array($accessible)) {
208 $accessible = array_filter(array_map(
'trim', explode(
',', (
string) $accessible)));
211 $catalog = $this->language_catalog();
214 foreach ($accessible as $code) {
215 $code = strtolower(trim((
string) $code));
216 if ($code ===
'' || !isset(
$catalog[$code])) {
229 private function language_meta(
string $lng): array {
230 $options = $this->language_options();
240 private function language_url(
string $url,
string $lng): string {
241 $oWeb =
dbx()->get_system_obj(
'dbxWebApp');
242 return $oWeb->append_route_params(
243 $oWeb->normalize_self_url(
$url),
244 array(
'dbx_lng' => strtolower(trim(
$lng)))
248 private function render_language_menu(
string $baseSelf,
string $activeLng): string {
249 $activeLng = strtolower(trim($activeLng));
252 foreach ($this->language_options() as $code => $meta) {
253 $class = $code === $activeLng ?
' class="is-active"' :
'';
254 $url = htmlspecialchars($this->language_url($baseSelf, $code), ENT_QUOTES,
'UTF-8');
255 $label = htmlspecialchars($meta[
'label'], ENT_QUOTES,
'UTF-8');
256 $flag = htmlspecialchars($meta[
'flag'], ENT_QUOTES,
'UTF-8');
257 $codeLabel = htmlspecialchars(strtoupper($code), ENT_QUOTES,
'UTF-8');
260 $html .=
'<a class="dbxLngOpt" href="' .
$url .
'" title="' . $label .
'">';
261 $html .=
'<span class="dbx-lng-flag" data-dbx-flag="' . $flag .
'" aria-hidden="true"></span>';
262 $html .=
'<span class="dbx-lng-code">' . $codeLabel .
'</span>';
274 private function frontend_design_options(): array {
279 $name = basename(
$dir);
280 if ($name ===
'' || $name[0] ===
'_' || $name[0] ===
'-') {
283 if (!preg_match(
'/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/', $name)) {
290 $options[$name] = $this->design_label($name);
297 uasort(
$options,
static function ($a, $b) {
298 return strnatcasecmp((
string)$a, (
string)$b);
304 private function design_label(
string $design): string {
308 if (strtolower(
$design) ===
'dbxdocs') {
309 return 'dbXapp Dokumentation';
312 $label = trim(str_replace(array(
'-',
'_'),
' ',
$design));
313 return $label !==
'' ? ucfirst($label) :
$design;
316 private function active_frontend_design(): string {
318 $design = trim((
string)
dbx()->get_system_var(
'dbx_design',
$config[
'default_design_user'] ??
'dbxapp'));
322 } elseif (
$design ===
'admin') {
326 $options = $this->frontend_design_options();
328 $default = (string)(
$config[
'default_design_user'] ??
'dbxapp');
339 private function design_skin_url(
string $url,
string $design,
string $skin): string {
340 $oWeb =
dbx()->get_system_obj(
'dbxWebApp');
342 return $oWeb->append_route_params(
343 $oWeb->normalize_self_url(
$url),
346 'dbx_color' => $this->normalize_design_skin(
$design, $skin),
355 private function skin_options(
string $design): array {
357 'hell' => array(
'label' => $this->texts()->get_fd_message(
'skin_light'),
'icon' =>
'bi-sun'),
358 'gelb' => array(
'label' => $this->texts()->get_fd_message(
'skin_yellow'),
'icon' =>
'bi-brightness-high'),
359 'rot' => array(
'label' => $this->texts()->get_fd_message(
'skin_red'),
'icon' =>
'bi-droplet-fill'),
360 'gruen' => array(
'label' => $this->texts()->get_fd_message(
'skin_green'),
'icon' =>
'bi-tree'),
361 'blau' => array(
'label' => $this->texts()->get_fd_message(
'skin_blue'),
'icon' =>
'bi-droplet'),
362 'dunkel' => array(
'label' => $this->texts()->get_fd_message(
'skin_dark'),
'icon' =>
'bi-moon'),
367 $options[$skin] = $known[$skin] ?? array(
368 'label' => ucfirst(str_replace(array(
'-',
'_'),
' ', $skin)),
369 'icon' =>
'bi-palette',
376 private function normalize_design_skin(
string $design,
string $skin): string {
380 private function skin_label(
string $design,
string $skin): string {
382 return (
string)(
$options[$skin][
'label'] ?? $skin);
389 private function render_design_skin_menu(
391 string $activeDesign,
393 bool $docsDisplay =
false
396 $designs = $docsDisplay
397 ? array(
'dbxdocs' =>
'dbxapp (Blau)')
398 : $this->frontend_design_options();
400 foreach ($designs as
$design => $designLabel) {
401 $designActive =
$design === $activeDesign;
402 $designEsc = $this->h(
$design);
405 $options = array_intersect_key(
$options, array(
'blau' =>
true,
'dunkel' =>
true));
407 $options[
'blau'][
'label'] =
'Light';
410 $options[
'dunkel'][
'label'] =
'Dark';
413 $designUrl = $this->h($this->design_skin_url($baseSelf,
$design, $activeSkin));
414 $html .=
'<li class="dbx-design-skin-group' . ($designActive ?
' is-active' :
'')
415 .
'" data-design="' . $designEsc .
'">';
416 $html .=
'<a class="dbx-design-opt' . ($designActive ?
' is-active' :
'')
417 .
'" href="' . $designUrl .
'" data-design="' . $designEsc
418 .
'" title="Design ' . $this->h($designLabel) .
'">';
419 $html .=
'<i class="bi bi-window-sidebar dbx-design-icon" aria-hidden="true"></i>';
420 $html .=
'<span>' . $this->h($designLabel) .
'</span>';
422 $html .=
'<i class="bi bi-check2 dbx-design-check" aria-hidden="true"></i>';
432 foreach (
$options as $skin => $meta) {
433 $active = $designActive && $skin === $activeSkin;
434 $skinEsc = $this->h($skin);
435 $labelEsc = $this->h((
string)$meta[
'label']);
436 $iconEsc = $this->h((
string)$meta[
'icon']);
437 $url = $this->h($this->design_skin_url($baseSelf,
$design, $skin));
438 $classes =
'dbx-design-skin-opt';
440 $classes .=
' dbx-skin-opt';
443 $classes .=
' is-active';
446 $html .=
'<li class="dbx-design-skin-item' . ($active ?
' is-active' :
'') .
'">';
447 $html .=
'<a href="' .
$url .
'" class="' . $classes .
'" data-design="' . $designEsc
448 .
'" data-skin="' . $skinEsc .
'" title="' . $this->h($designLabel .
': ' . $meta[
'label']) .
'">';
449 $html .=
'<i class="bi ' . $iconEsc .
'" aria-hidden="true"></i><span>' . $labelEsc .
'</span>';
451 $html .=
'<i class="bi bi-check2 dbx-skin-check" aria-hidden="true"></i>';
453 $html .=
'</a></li>';
455 $html .=
'</ul></li>';
461 private function h(
$value): string {
462 return htmlspecialchars((string)
$value, ENT_QUOTES,
'UTF-8');
465 private function shop_cart_count(): int {
466 if (session_status() === PHP_SESSION_NONE) {
467 if (headers_sent()) {
473 if (!is_array(
$cart)) {
477 foreach (
$cart as $qty) {
478 $count += max(0, (
int)$qty);
483 private function shop_cart_badge(
int $count): string {
487 $label = $this->texts()->format_fd_message(
488 $count === 1 ?
'cart_item_one' :
'cart_item_many',
491 return '<span class="dbx-shop-cart-menu-badge" aria-label="' . $this->h($label) .
'">' . $this->h((
string)
$count) .
'</span>';
494 private function user_avatar_url(
int $uid): string {
497 $db =
dbx()->get_system_obj(
'dbxDB');
498 $rec =
$db->select1(
'dbxUser', $uid,
'avatar', 0);
499 if (is_array($rec)) {
500 $file = trim((
string)($rec[
'avatar'] ??
''));
502 }
catch (\Throwable $e) {
506 if (
$file ===
'' || !preg_match(
'/^avatar-[0-9]+\.(?:webp|png|jpe?g|gif)$/i',
$file)) {
507 $file =
'avatar-0.png';
511 $public =
'files/user/avatar/' .
$file;
513 return dbx()->get_base_url() . $public;
521 return dbx()->get_base_url() .
'files/user/avatar/avatar-0.png';
524 private function user_menu_html(
int $uid): string {
525 $avatar = $this->h($this->user_avatar_url($uid));
526 $cartCount = $this->shop_cart_count();
527 $cartBadge = $this->shop_cart_badge($cartCount);
530 return '<li class="align-right dbx-user-avatar-menu">'
531 .
'<a title="' .
$user .
'" style="cursor:pointer;">'
532 .
'<span class="dbx-user-menu-avatar-wrap">'
533 .
'<img class="dbx-user-menu-avatar" src="' . $avatar .
'?' . time() .
'" alt="' .
$user .
'">'
534 .
'<span class="dbx-user-menu-mail" aria-hidden="true"><i class="bi bi-envelope-fill"></i></span>'
536 .
'<span class="dbx-menu-mobile-label">' .
$user .
'</span>'
539 .
'<li><a href="?dbx_modul=dbxShop&dbx_run1=catalog"><i class="bi bi-bag"></i> ' . $this->h(
$texts->get_fd_message(
'shop')) .
'</a></li>'
540 .
'<li><a class="dbx-shop-cart-menu-link" href="?dbx_modul=dbxShop&dbx_run1=cart"><span class="dbx-shop-cart-menu-icon"><i class="bi bi-basket"></i>' . $cartBadge .
'</span> ' . $this->h(
$texts->get_fd_message(
'cart')) .
'</a></li>'
541 .
'<li><a href="?dbx_modul=dbxShop&dbx_run1=orders"><i class="bi bi-receipt"></i> ' . $this->h(
$texts->get_fd_message(
'orders')) .
'</a></li>'
542 .
'<li><a href="?dbx_modul=dbxShop&dbx_run1=withdrawal"><i class="bi bi-arrow-counterclockwise"></i> ' . $this->h(
$texts->get_fd_message(
'withdrawal')) .
'</a></li>'
543 .
'<li><a href="?dbx_modul=dbxContact&dbx_run1=form"><i class="bi bi-envelope-plus"></i> ' . $this->h(
$texts->get_fd_message(
'new_request')) .
'</a></li>'
544 .
'<li><a href="?dbx_modul=dbxContact&dbx_run1=my"><i class="bi bi-envelope"></i> ' . $this->h(
$texts->get_fd_message(
'my_requests')) .
'</a></li>'
545 .
'<li><a href="?dbx_modul=dbxUser&dbx_run1=user&dbx_run2=edit_profil"><i class="bi bi-person-circle"></i> ' . $this->h(
$texts->get_fd_message(
'my_profile')) .
'</a></li>'
550 private function replace_cms_placeholders(
$content) {
555 $hasFlatMarker = stripos(
$content,
'[cms:flat]') !==
false;
556 $flatMarkerToken =
'###DBX_CMS_FLAT_MARKER_' . md5((
string)microtime(
true)) .
'###';
557 $flatMarkerHtml =
'';
559 $content = preg_replace_callback(
'/\[cms:([^\]]*)\]/i',
function ($match) use ($hasFlatMarker, $flatMarkerToken, &$flatMarkerHtml) {
560 $raw = trim((
string)$match[1]);
562 if (preg_match(
'/^flat$/i', $raw)) {
563 return $flatMarkerToken;
567 parse_str(str_replace(
'&',
'&', $raw), $params);
569 $root = trim((
string)($params[
'root'] ??
'0'));
570 if (ctype_digit(
$root)) {
573 $flat = (int)($params[
'flat'] ?? 1);
574 $splitFlat = $hasFlatMarker && $flat === 1;
576 $obj =
dbx()->get_include_obj(
'dbxContent_menu');
577 if ($obj && method_exists($obj,
'render_placeholder')) {
578 if ($splitFlat && method_exists($obj,
'render_flat_marker')) {
579 $flatMarkerHtml .= $obj->render_flat_marker(
$root, $flat);
581 return $obj->render_placeholder(
$root, $flat, $splitFlat);
587 return str_replace($flatMarkerToken, $flatMarkerHtml,
$content);
591 $action=
dbx()->get_modul_var(
'dbx_run1' ,
'a-undef',
'*');
592 $menu =
dbx()->get_modul_var(
'tpl' ,
'b-undef',
'*');
594 $mid =
dbx()->get_system_var(
'dbx_activ_modul_id',-1);
597 dbx()->debug(
"#Modul Menu# action=($action) Menu=($menu) Modul-id=($mid)");
601 $content=$this->get_menu_tpl($menu);
605 $obj=
dbx()->get_include_obj(
'dbxContent_menu');
612 $LoginOut =
'LogOut'; $login_out =
'logout'; $login_icon =
'bi-power';
614 $LoginOut =
'LogIn' ; $login_out =
'login'; $login_icon =
'bi-person-circle';
616 $ProfileLink = $this->user_menu_html((
int)$uid);
619 $design=
dbx()->get_system_var(
'dbx_activ_design');
620 $page =
dbx()->get_system_var(
'dbx_activ_page');
621 $lng =
dbx()->get_system_var(
'dbx_activ_lng');
623 if (
dbx()->
can(
'admin')) $AdminMenu=
"Admin";
user($key='id')
Liest einen Wert des aktuellen Benutzers.
dbx()
Liefert die zentrale dbXapp-API als Singleton.
os_path(string $path)
Normalisiert einen Pfad fuer das aktuelle Betriebssystem.
get_design_skin_ids(string $design='')
Liefert die Skin-IDs, die ein Design durch eigene skin-*.css-Dateien tatsaechlich bereitstellt.
normalize_skin(string $skin='', string $design='')
Normalisiert Skin-/Farbnamen auf gueltige Skin-IDs.
get_skin()
Aktiver Skin aus Systemvariable dbx_color.
can($access_groups='', $user_groups='')
Prueft Gruppenrechte gegen den aktuellen oder uebergebenen Benutzer.
if($resolved !==$expectedBase . 'files/test/') $config
if(trim($second) !=='second') $design
if(!defined( 'IMG_WEBP')) define( 'IMG_WEBP'
DBX schema administration.
if($cinematic===''||!str_contains($cinematic, 'data-dbx-cinema')) $page