8 $this->oTPL=
dbx()->get_system_obj(
'dbxTPL');
12 private function get_menu_tpl($menu=
'undef') {
13 $access=1; $content=
'';
14 if (strpos($menu,
'_admin')) $access=
dbx()->can(
'admin');
15 if (strpos($menu,
'-admin')) $access=
dbx()->can(
'admin');
16 if (strpos($menu,
'admin_')) $access=
dbx()->can(
'admin');
17 if (strpos($menu,
'admin-')) $access=
dbx()->can(
'admin');
19 dbx()->debug(
"load get_menu_tpl=($menu)");
23 $oWeb =
dbx()->get_system_obj(
'dbxWebApp');
24 $baseSelf = $oWeb->normalize_self_url(
dbx()->get_system_var(
'dbx_self_url',
'?',
'*'));
25 $data[
'self'] = $oWeb->route_param_prefix($baseSelf);
26 $edit = (int)
dbx()->get_system_var(
'dbx_edit', 0,
'int');
27 $nextEdit = $this->next_editor_level($edit);
28 $data[
'edit_level'] = $edit;
29 $data[
'edit_next_level'] = $nextEdit;
30 $data[
'edit_toggle_url'] = $this->editor_level_url($baseSelf, $nextEdit);
31 $data[
'edit_level_menu'] = $this->render_editor_level_menu($baseSelf, $edit);
32 $data[
'edit_state_class'] = $edit ?
'is-active' :
'is-disabled';
33 $data[
'edit_toggle_title'] =
'Editor Level ' . $edit .
': ' . $this->editor_level_label($edit) .
35 $lng = strtolower(trim((
string)
dbx()->get_system_var(
'dbx_lng',
'de')));
36 $lngMeta = $this->language_meta($lng);
37 $data[
'lng_active'] = $lng;
38 $data[
'lng_active_flag'] = $lngMeta[
'flag'];
39 $data[
'lng_active_label'] = $lngMeta[
'label'];
40 $data[
'lng_menu'] = $this->render_language_menu($baseSelf, $lng);
41 $data[
'lng_toggle_title'] =
'Sprache: ' . $lngMeta[
'label'];
42 $activeDesign = $this->active_frontend_design();
43 $data[
'design_active'] = $activeDesign;
44 $data[
'design_active_label'] = $this->design_label($activeDesign);
45 $data[
'design_menu'] = $this->render_design_menu($baseSelf, $activeDesign);
46 $activeSkin = $this->normalize_design_skin($activeDesign,
dbx()->get_skin());
47 $data[
'skin_menu'] = $this->render_skin_menu($activeDesign, $activeSkin);
48 $data[
'skin_section_label'] = $activeDesign ===
'fleurop' ?
'Skin' :
'Farbschema';
49 $data[
'design_toggle_title'] =
'Darstellung: ' . $data[
'design_active_label'] .
' / ' .
50 $this->skin_label($activeDesign, $activeSkin);
51 $openContactCount = $this->open_contact_count($menu);
52 $data[
'contact_open_count'] = $openContactCount;
53 $data[
'contact_open_title'] = $openContactCount === 1
54 ?
'1 offene Kontaktanfrage'
55 : $openContactCount .
' offene Kontaktanfragen';
56 $data[
'contact_open_badge'] = $openContactCount > 0
57 ?
'<span class="dbx-contact-open-badge" aria-label="' . $openContactCount .
' offene Kontaktanfragen">'
58 . $openContactCount .
'</span>'
60 $cartCount = $this->shop_cart_count();
61 $data[
'shop_cart_count'] = $cartCount;
62 $data[
'shop_cart_badge'] = $this->shop_cart_badge($cartCount);
63 $i =
dbx()->next_id();
64 $content=$this->oTPL->get_tpl($menu,$data,
'htm',$i);
65 $content=$this->replace_cms_placeholders($content);
71 private function open_contact_count(
string $menu): int {
72 if (stripos($menu,
'admin') === false || !dbx()->can(
'admin')) {
77 $tickets = dbx()->get_include_obj(
'dbxContactTicket',
'dbxContact');
78 if (is_object($tickets) && method_exists($tickets,
'openCount')) {
79 return max(0, (
int) $tickets->openCount());
81 }
catch (\Throwable $e) {
82 dbx()->debug(
'[dbxMenu] offene Kontaktanfragen konnten nicht gezaehlt werden: ' . $e->getMessage());
88 private function next_editor_level(
int $edit): int {
89 $levels = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
90 $idx = array_search($edit, $levels,
true);
96 return $levels[($idx + 1) % count($levels)];
99 private function editor_level_label(
int $edit): string {
101 case 0:
return 'aus';
102 case 1:
return 'Templates ohne dbx';
103 case 2:
return 'Templates dbx';
104 case 3:
return 'alle Modul-Templates';
105 case 4:
return 'FD Definitionen';
106 case 5:
return 'DD Definitionen';
107 case 6:
return 'Modul-/Include-Class';
108 case 7:
return 'myX System-Class';
109 case 8:
return 'Config';
110 case 9:
return 'alles';
116 private function editor_level_icon(
int $edit): string {
118 case 0:
return 'bi-x-circle';
121 case 3:
return 'bi-pencil-square';
122 case 4:
return 'bi-card-checklist';
123 case 5:
return 'bi-database';
124 case 6:
return 'bi-braces';
125 case 7:
return 'bi-cpu';
126 case 8:
return 'bi-gear';
127 case 9:
return 'bi-files';
130 return 'bi-question-circle';
133 private function render_editor_level_menu(
string $baseSelf,
int $activeEdit): string {
134 $levels = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
137 foreach ($levels as $level) {
138 $class = $level === $activeEdit ?
' class="is-active"' :
'';
139 $url = htmlspecialchars($this->editor_level_url($baseSelf, $level), ENT_QUOTES,
'UTF-8');
140 $label = htmlspecialchars($this->editor_level_label($level), ENT_QUOTES,
'UTF-8');
141 $icon = htmlspecialchars($this->editor_level_icon($level), ENT_QUOTES,
'UTF-8');
143 $html .=
'<li' . $class .
'>';
144 $html .=
'<a class="dbxEditLevel" href="' . $url .
'" title="Editor Level ' . $level .
': ' . $label .
'">';
145 $html .=
'<i class="bi ' . $icon .
'"></i>';
146 $html .=
'<span>Level ' . $level .
' - ' . $label .
'</span>';
154 private function editor_level_url(
string $url,
int $edit): string {
155 $oWeb =
dbx()->get_system_obj(
'dbxWebApp');
156 return $oWeb->append_route_params(
157 $oWeb->normalize_self_url($url),
158 array(
'dbx_edit' => (
string) $edit)
162 private function language_catalog(): array {
164 'de' => array(
'label' =>
'Deutsch',
'flag' =>
'de'),
165 'en' => array(
'label' =>
'English',
'flag' =>
'gb'),
166 'es' => array(
'label' =>
'Español',
'flag' =>
'es'),
170 private function language_options(): array {
172 $accessible =
$config[
'accessible_lng'] ??
'de,en,es';
174 if (!is_array($accessible)) {
175 $accessible = array_filter(array_map(
'trim', explode(
',', (
string) $accessible)));
178 $catalog = $this->language_catalog();
181 foreach ($accessible as $code) {
182 $code = strtolower(trim((
string) $code));
183 if ($code ===
'' || !isset($catalog[$code])) {
186 $options[$code] = $catalog[$code];
189 if (empty($options)) {
190 $options[
'de'] = $catalog[
'de'];
196 private function language_meta(
string $lng): array {
197 $options = $this->language_options();
198 $lng = strtolower(trim($lng));
200 if (isset($options[$lng])) {
201 return $options[$lng];
204 return reset($options);
207 private function language_url(
string $url,
string $lng): string {
208 $oWeb =
dbx()->get_system_obj(
'dbxWebApp');
209 return $oWeb->append_route_params(
210 $oWeb->normalize_self_url($url),
211 array(
'dbx_lng' => strtolower(trim($lng)))
215 private function render_language_menu(
string $baseSelf,
string $activeLng): string {
216 $activeLng = strtolower(trim($activeLng));
219 foreach ($this->language_options() as $code => $meta) {
220 $class = $code === $activeLng ?
' class="is-active"' :
'';
221 $url = htmlspecialchars($this->language_url($baseSelf, $code), ENT_QUOTES,
'UTF-8');
222 $label = htmlspecialchars($meta[
'label'], ENT_QUOTES,
'UTF-8');
223 $flag = htmlspecialchars($meta[
'flag'], ENT_QUOTES,
'UTF-8');
224 $codeLabel = htmlspecialchars(strtoupper($code), ENT_QUOTES,
'UTF-8');
226 $html .=
'<li' . $class .
'>';
227 $html .=
'<a class="dbxLngOpt" href="' . $url .
'" title="' . $label .
'">';
228 $html .=
'<span class="dbx-lng-flag" data-dbx-flag="' . $flag .
'" aria-hidden="true"></span>';
229 $html .=
'<span class="dbx-lng-code">' . $codeLabel .
'</span>';
241 private function frontend_design_options(): array {
245 foreach (glob($designRoot .
'/*', GLOB_ONLYDIR) ?: array() as
$dir) {
246 $name = basename(
$dir);
247 if ($name ===
'' || $name[0] ===
'_' || $name[0] ===
'-') {
250 if (!preg_match(
'/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/', $name)) {
257 $options[$name] = $this->design_label($name);
260 if (empty($options)) {
261 $options[
'dbxapp'] =
'dbXapp';
264 uasort($options,
static function ($a, $b) {
265 return strnatcasecmp((
string)$a, (
string)$b);
271 private function design_label(
string $design): string {
272 if (strtolower($design) ===
'dbxapp') {
276 $label = trim(str_replace(array(
'-',
'_'),
' ', $design));
277 return $label !==
'' ? ucfirst($label) : $design;
280 private function active_frontend_design(): string {
282 $design = trim((
string)
dbx()->get_system_var(
'dbx_design',
$config[
'default_design_user'] ??
'dbxapp'));
284 if ($design ===
'user') {
285 $design = (string)(
$config[
'default_design_user'] ??
'dbxapp');
286 } elseif ($design ===
'admin') {
287 $design = (string)(
$config[
'default_design_admin'] ??
'dbxapp');
290 $options = $this->frontend_design_options();
291 if (!isset($options[$design])) {
292 $default = (string)(
$config[
'default_design_user'] ??
'dbxapp');
293 $design = isset($options[$default]) ? $default : (string)array_key_first($options);
299 private function design_url(
string $url,
string $design): string {
300 $oWeb =
dbx()->get_system_obj(
'dbxWebApp');
301 $params = array(
'dbx_design' => $design);
303 if (strtolower($design) ===
'fleurop') {
304 $params[
'dbx_color'] = $this->normalize_design_skin($design,
dbx()->get_skin());
307 return $oWeb->append_route_params(
308 $oWeb->normalize_self_url($url),
313 private function render_design_menu(
string $baseSelf,
string $activeDesign): string {
316 foreach ($this->frontend_design_options() as $design => $label) {
317 $active = $design === $activeDesign;
318 $class = $active ?
' class="is-active"' :
'';
319 $url = htmlspecialchars($this->design_url($baseSelf, $design), ENT_QUOTES,
'UTF-8');
320 $designEsc = htmlspecialchars($design, ENT_QUOTES,
'UTF-8');
321 $labelEsc = htmlspecialchars($label, ENT_QUOTES,
'UTF-8');
323 $html .=
'<li' . $class .
'>';
324 $html .=
'<a class="dbx-design-opt' . ($active ?
' is-active' :
'') .
'" href="' . $url .
'" data-design="' . $designEsc .
'" title="Design ' . $labelEsc .
'">';
325 $html .=
'<i class="bi bi-window-sidebar dbx-design-icon" aria-hidden="true"></i>';
326 $html .=
'<span>' . $labelEsc .
'</span>';
328 $html .=
'<i class="bi bi-check2 dbx-design-check" aria-hidden="true"></i>';
337 private function skin_options(
string $design): array {
338 if (strtolower($design) ===
'fleurop') {
340 'hell' => array(
'label' =>
'Light',
'icon' =>
'bi-sun'),
341 'dunkel' => array(
'label' =>
'Dark',
'icon' =>
'bi-moon-stars'),
346 'hell' => array(
'label' =>
'Hell',
'icon' =>
'bi-sun'),
347 'gelb' => array(
'label' =>
'Gelb',
'icon' =>
'bi-brightness-high'),
348 'rot' => array(
'label' =>
'Rot',
'icon' =>
'bi-droplet-fill'),
349 'gruen' => array(
'label' =>
'Gruen',
'icon' =>
'bi-tree'),
350 'blau' => array(
'label' =>
'Blau',
'icon' =>
'bi-droplet'),
351 'dunkel' => array(
'label' =>
'Dunkel',
'icon' =>
'bi-moon'),
355 private function normalize_design_skin(
string $design,
string $skin): string {
356 if (strtolower($design) ===
'fleurop') {
357 return $skin ===
'dunkel' ?
'dunkel' :
'hell';
360 return array_key_exists($skin, $this->skin_options($design)) ? $skin :
'blau';
363 private function skin_label(
string $design,
string $skin): string {
364 $options = $this->skin_options($design);
365 return (
string)($options[$skin][
'label'] ?? $skin);
368 private function render_skin_menu(
string $design,
string $activeSkin): string {
371 foreach ($this->skin_options($design) as $skin => $meta) {
372 $active = $skin === $activeSkin;
373 $skinEsc = htmlspecialchars($skin, ENT_QUOTES,
'UTF-8');
374 $labelEsc = htmlspecialchars((
string)$meta[
'label'], ENT_QUOTES,
'UTF-8');
375 $iconEsc = htmlspecialchars((
string)$meta[
'icon'], ENT_QUOTES,
'UTF-8');
377 $html .=
'<li' . ($active ?
' class="is-active"' :
'') .
'>';
378 $html .=
'<a href="#" class="dbx-skin-opt' . ($active ?
' is-active' :
'') .
'" data-skin="' . $skinEsc .
'">';
379 $html .=
'<i class="bi ' . $iconEsc .
'" aria-hidden="true"></i><span>' . $labelEsc .
'</span>';
381 $html .=
'<i class="bi bi-check2 dbx-skin-check" aria-hidden="true"></i>';
383 $html .=
'</a></li>';
389 private function h($value): string {
390 return htmlspecialchars((string)$value, ENT_QUOTES,
'UTF-8');
393 private function shop_cart_count(): int {
394 if (session_status() === PHP_SESSION_NONE) {
395 if (headers_sent()) {
400 $cart = $_SESSION[
'dbxShop_cart'] ?? array();
401 if (!is_array($cart)) {
405 foreach ($cart as $qty) {
406 $count += max(0, (
int)$qty);
411 private function shop_cart_badge(
int $count): string {
415 $label =
$count === 1 ?
'1 Artikel im Warenkorb' :
$count .
' Artikel im Warenkorb';
416 return '<span class="dbx-shop-cart-menu-badge" aria-label="' . $this->h($label) .
'">' . $this->h((
string)
$count) .
'</span>';
419 private function user_avatar_url(
int $uid): string {
422 $db =
dbx()->get_system_obj(
'dbxDB');
423 $rec =
$db->select1(
'dbxUser', $uid,
'avatar', 0);
424 if (is_array($rec)) {
425 $file = trim((
string)($rec[
'avatar'] ??
''));
427 }
catch (\Throwable $e) {
428 $file = trim((
string)
dbx()->user(
'avatar'));
431 if ($file ===
'' || !preg_match(
'/^avatar-[0-9]+\.(?:webp|png|jpe?g|gif)$/i', $file)) {
432 $file =
'avatar-0.png';
436 $public =
'files/user/avatar/' . $file;
438 return dbx()->get_base_url() . $public;
441 $legacy =
'dbx/modules/dbxUser/img/avatar/' . $file;
443 return dbx()->get_base_url() . $legacy;
446 return dbx()->get_base_url() .
'files/user/avatar/avatar-0.png';
449 private function user_menu_html(
int $uid): string {
450 $avatar = $this->h($this->user_avatar_url($uid));
451 $cartCount = $this->shop_cart_count();
452 $cartBadge = $this->shop_cart_badge($cartCount);
453 return '<li class="align-right dbx-user-avatar-menu">'
454 .
'<a title="Benutzer" style="cursor:pointer;">'
455 .
'<span class="dbx-user-menu-avatar-wrap">'
456 .
'<img class="dbx-user-menu-avatar" src="' . $avatar .
'?' . time() .
'" alt="Benutzer">'
457 .
'<span class="dbx-user-menu-mail" aria-hidden="true"><i class="bi bi-envelope-fill"></i></span>'
461 .
'<li><a href="?dbx_modul=dbxShop&dbx_run1=catalog"><i class="bi bi-bag"></i> Shop</a></li>'
462 .
'<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> Warenkorb</a></li>'
463 .
'<li><a href="?dbx_modul=dbxShop&dbx_run1=orders"><i class="bi bi-receipt"></i> Meine Bestellungen</a></li>'
464 .
'<li><a href="?dbx_modul=dbxShop&dbx_run1=withdrawal"><i class="bi bi-arrow-counterclockwise"></i> Widerruf / Retoure</a></li>'
465 .
'<li><a href="?dbx_modul=dbxContact&dbx_run1=form"><i class="bi bi-envelope-plus"></i> Neue Anfrage</a></li>'
466 .
'<li><a href="?dbx_modul=dbxContact&dbx_run1=my"><i class="bi bi-envelope"></i> Meine Anfragen</a></li>'
467 .
'<li><a href="?dbx_modul=dbxUser&dbx_run1=user&dbx_run2=edit_profil"><i class="bi bi-person-circle"></i> Mein Profil</a></li>'
472 private function replace_cms_placeholders($content) {
473 if (!is_string($content) || strpos($content,
'[cms:') ===
false) {
477 $hasFlatMarker = stripos($content,
'[cms:flat]') !==
false;
478 $flatMarkerToken =
'###DBX_CMS_FLAT_MARKER_' . md5((
string)microtime(
true)) .
'###';
479 $flatMarkerHtml =
'';
481 $content = preg_replace_callback(
'/\[cms:([^\]]*)\]/i',
function ($match) use ($hasFlatMarker, $flatMarkerToken, &$flatMarkerHtml) {
482 $raw = trim((
string)$match[1]);
484 if (preg_match(
'/^flat$/i', $raw)) {
485 return $flatMarkerToken;
489 parse_str(str_replace(
'&',
'&', $raw), $params);
491 $root = (int)($params[
'root'] ?? 0);
492 $flat = (int)($params[
'flat'] ?? 1);
493 $splitFlat = $hasFlatMarker && $flat === 1;
495 $obj =
dbx()->get_include_obj(
'dbxContent_menu');
496 if ($obj && method_exists($obj,
'render_placeholder')) {
497 if ($splitFlat && method_exists($obj,
'render_flat_marker')) {
498 $flatMarkerHtml .= $obj->render_flat_marker(
$root, $flat);
500 return $obj->render_placeholder(
$root, $flat, $splitFlat);
506 return str_replace($flatMarkerToken, $flatMarkerHtml, $content);
510 $action=
dbx()->get_modul_var(
'dbx_run1' ,
'a-undef',
'*');
511 $menu =
dbx()->get_modul_var(
'tpl' ,
'b-undef',
'*');
513 $mid =
dbx()->get_system_var(
'dbx_activ_modul_id',-1);
514 $content=
"Menu ($menu) not found";
516 dbx()->debug(
"#Modul Menu# action=($action) Menu=($menu) Modul-id=($mid)");
520 $content=$this->get_menu_tpl($menu);
524 $obj=
dbx()->get_include_obj(
'dbxContent_menu');
525 $content=$obj->run();
531 $LoginOut =
'LogOut'; $login_out =
'logout'; $login_icon =
'bi-power';
533 $LoginOut =
'LogIn' ; $login_out =
'login'; $login_icon =
'bi-person-circle';
535 $ProfileLink = $this->user_menu_html((
int)$uid);
538 $design=
dbx()->get_system_var(
'dbx_activ_design');
539 $page =
dbx()->get_system_var(
'dbx_activ_page');
540 $lng =
dbx()->get_system_var(
'dbx_activ_lng');
542 if (
dbx()->can(
'admin')) $AdminMenu=
"Admin";
543 $content=str_replace(
'{dbx:Admin}' ,$AdminMenu,$content);
544 $content=str_replace(
'{dbx:LogInOut}' ,$LoginOut ,$content);
545 $content=str_replace(
'{dbx:login_out}',$login_out,$content);
546 $content=str_replace(
'{dbx:login_icon}',$login_icon,$content);
547 $content=str_replace(
'{dbx:profile_link}',$ProfileLink,$content);
548 $content=str_replace(
'{dbx:design}' ,$design ,$content);
549 $content=str_replace(
'{dbx:page}' ,$page ,$content);
550 $content=str_replace(
'{dbx:lng}' ,$lng ,$content);
dbx_os_path_file($path_file)
dbx_get_base_dir($cut_Data=0)
DBX schema administration.