dbxapp 4.1.3
CMS, Shop, Workflows und modulare Geschäftsanwendungen
Loading...
Searching...
No Matches
dbxForm.class.php
Go to the documentation of this file.
1<?php
2
206class dbxForm extends \dbxObj {
207
210
212 public $oTPL;
213
215 public $oDB;
216
218 public $_dbx_view = '';
219
221 public $_view_sync = '';
222
224 public $_view_mode = '';
225
227 public $_mode = 'mix';
228
230 public $_ajax = true;
231
233 public $_store_mode = 'session';
234
236 public $_dbx_modul_id = 0;
237
239 public $_dbx_modul = 'modul';
240
242 public $_dbx_lng = '';
243
245 public $_dbx_design = '';
246
248 public $_dbx_action = '';
249
251 public $_dbx_work = '';
252
254 public $_dbx_page = '';
255
257 public $_rid = 0;
258
260 public $_action = '';
261
263 public $_tpl = '';
264
266 public $_fid = '';
267
269 public $_next_i = 0;
270
272 public $_dd = '';
273
275 public $_fd = '';
276
278 public $_editor_files = array();
279
282
284 public $_data = array();
285
287 public $_post = array();
288
290 public $_sys = array();
291
293 public $_obj = array();
294
296 public $_js = array();
297
299 public $_css = array();
300
302 public $_flds = array();
303
310 public $_messages = array();
311
313 public $_infos = array();
314
316 public $_errors = array();
317
319 protected $_validation_results = array();
320
322 public $_warnings = array();
323
325 public $_replaces = array();
326
328 public $_general_error = '';
329
331 public $_msg_info = '#form_msg_info#';
332
334 public $_msg_success = '#form_msg_success#';
335
337 public $_msg_error = '#form_msg_error#';
338
340 public $_msg_warning = '#form_msg_warning#';
341
343 public $_tpl_form_info = 'form-alert-info';
344
346 public $_tpl_form_success = 'form-alert-success';
347
349 public $_tpl_form_error = 'form-alert-danger';
350
352 public $_tpl_form_warning = 'form-alert-warning';
353
355 public $_tpl_fld_info = 'fld-alert-info';
356
358 public $_tpl_fld_success = 'fld-alert-success';
359
361 public $_tpl_fld_error = 'fld-alert-danger';
362
364 public $_tpl_fld_warning = 'fld-alert-warning';
365
367 public $_tpl_max_try = 'form-alert-maxtry';
368
370 public $_try_reset = 120;
371
373 public $_try_count_reset = 600;
374
376 public $_try_max = 20;
377
379 public $_try_msg = 'Max {try_count} try. Suspend for {sec} seconds';
380
382 public $_create_flds = 1;
383
385 public $_reload_record = 1;
386
388 public $_reload_run = 0;
389
392
394 protected $_module_bar_form_actions = array();
395
397 protected $_form_help_button = '';
398
401
403 public $_reload_suffix = '_rlo';
404
406 public $_fld_change_state = 'fld';
407
409 public $_fld_changes = -1;
410
412 public $_form_submit = -1;
413
415 public $_form_validate = -1;
416
418 public $_editor_fld = '';
419
421 public $_page_reset = 1;
422
424 public $_confirm_delete = '';
425
427 public $_confirm_copy = '';
428
430 public $_activ_id = 0;
431
433 public $_fld_id = 'id';
434
436 public $_workflow_scope = '';
437
439 public $_workflow_state = array();
440
459 public function __construct($id = '', $tpl = '') {
460 $this->oValidator = dbx()->get_system_obj('dbxValidator');
461 $this->oTPL = dbx()->get_system_obj('dbxTPL');
462 $this->oDB = dbx()->get_system_obj('dbxDB');
463
464 if (!$tpl) {
465 $tpl = $id;
466 }
467
468 if ($id) {
469 $this->init($id, $tpl);
470 }
471 }
472
486 public function __destruct() {
487 }
488
504 public function clear_sys() {
505 $this->_sys = array();
506 }
507
529 public function clear() {
530 $this->_forward_clear();
531 }
532
533
534
566 public function _forward_clear() {
567 $this->_reload_record = 0;
568 $this->_reload_run = 0;
569
570 $this->_obj = array();
571 $this->_js = array();
572 $this->_css = array();
573 $this->_flds = array();
574 $this->_messages = array();
575 $this->_infos = array();
576 $this->_errors = array();
577 $this->_validation_results = array();
578 $this->_warnings = array();
579 $this->_post = array();
580 $this->_sys = array();
581 $this->_data = array();
582 $this->_replaces = array();
583 $this->_module_bar_form_actions = array();
584 $this->_form_help_button = '';
585 $this->_form_help_enabled = 1;
586
587 $this->_fld_changes = -1;
588 $this->_form_submit = -1;
589 $this->_form_validate = -1;
590 $this->_general_error = '';
591 $this->_msg_info = '#form_msg_info#';
592 $this->_msg_success = '#form_msg_success#';
593 $this->_msg_error = '#form_msg_error#';
594 $this->_msg_warning = '#form_msg_warning#';
595
596 $this->_rid = 0;
597 $this->_action = '';
598 $this->_next_i = 0;
599 $this->_fld_id = 'id';
600 $this->_callback_owner = null;
601 $this->_callback_id = '';
602 $this->_callbacks = array();
603
604 $this->_dd = '';
605 $this->_fd = '';
606 $this->_editor_files = array();
607 }
608
615 public function set_editor_class_file(string $file) {
616 $this->_editor_class_file = $file;
617 $this->add_editor_file('class', $file);
618 }
619
627 protected function add_editor_file(string $kind, string $file) {
628 $file = trim($file);
629 $kind = strtolower(trim($kind));
630
631 if ($file === '' || $kind === '') {
632 return;
633 }
634
635 $path = dbx()->editor_file_path($file);
636 $key = $kind . '|' . $path;
637 $this->_editor_files[$key] = array('kind' => $kind, 'file' => $path);
638 dbx()->register_editor_file($kind, $path);
639 }
640
647 protected function add_editor_markers(string $content): string {
648 $edit = (int) dbx()->get_system_var('dbx_edit', 0, 'int');
649
650 if (($edit >= 4 && $edit <= 8) || $edit === 9) {
651 return $content;
652 }
653
654 if ($this->_editor_class_file !== '') {
655 $this->add_editor_file('class', $this->_editor_class_file);
656 }
657
658 foreach (dbx()->get_editor_files() as $file) {
659 if (isset($file['kind'], $file['file'])) {
660 $this->add_editor_file($file['kind'], $file['file']);
661 }
662 }
663
664 if ($content === '' || !$this->_editor_files) {
665 return $content;
666 }
667
668 $markers = '';
669
670 foreach ($this->_editor_files as $file) {
671 $kind = strtolower((string) ($file['kind'] ?? ''));
672
673 // FD/DD-Marker werden pro Feld in create_fld() gesetzt.
674 if ($kind === 'fd' || $kind === 'dd') {
675 continue;
676 }
677
678 $marker = dbx()->editor_marker($file['kind'], $file['file']);
679 $needle = trim($marker);
680
681 if ($marker !== '' && strpos($content, $needle) === false && strpos($markers, $needle) === false) {
682 $markers .= $marker;
683 }
684 }
685
686 if ($markers === '') {
687 return $content;
688 }
689
690 return $markers . $content . "\n<!-- DBX-EDITOR-END -->\n";
691 }
692
693 public function set_form_callback_owner($owner): void {
695 }
696
697 public function set_init_callback(string $callback): void {
698 $this->set_callback('init', $callback);
699 }
700
701 public function set_submit_callback(string $callback): void {
702 $this->set_callback('submit', $callback);
703 }
704
705 public function set_run_callback(string $callback): void {
706 $this->set_callback('run', $callback);
707 }
708
719 protected function resolve_init_callback_owner() {
720 $trace = debug_backtrace(
721 DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS,
722 8
723 );
724 foreach ($trace as $frame) {
725 $owner = $frame['object'] ?? null;
726 if (is_object($owner)
727 && $owner !== $this
728 && !($owner instanceof dbxApi)) {
729 return $owner;
730 }
731 }
732
733 return dbx()->get_current_owner();
734 }
735
736
737
769 public function forward_init($fid, $tpl = '') {
770 $this->clear();
771
772 // Der beim Aufbau aktive Modul-/Service-Owner ist der sichere Default
773 // fuer alle spaeteren Form- und Report-Callbacks. Explizites
774 // set_callback_owner() kann diesen Wert weiterhin ueberschreiben.
776 if (is_object($owner) && $owner !== $this) {
778 }
779
780 if ($tpl == '') {
781 $tpl = $fid;
782 }
783
784 $i = $this->next_i();
785
786 $this->_fid = $fid;
787 $this->set_callback_id($fid);
788 $this->_dbx_modul = dbx()->get_system_var('dbx_activ_modul', 'modul');
789 $this->_dbx_action = dbx()->get_system_var('dbx_activ_action', 'run');
790 $this->_dbx_page = dbx()->get_system_var('dbx_page', 'default');
791 $this->_dbx_design = dbx()->get_system_var('dbx_design', 'default');
792 $this->_dbx_lng = dbx()->get_system_var('dbx_lng', 'de');
793 $this->_dbx_modul_id = dbx()->get_system_var('dbx_activ_modul_id', 1);
794 $this->_tpl = $tpl;
795 $this->_data = array();
796 $this->_next_i = $i;
797 $this->_form_validate = -1;
798 $this->_form_submit = -1;
799
800
801 $init = array(
802 'fid' => $this->_fid,
803 'tpl' => $this->_tpl,
804 'i' => $this->_next_i,
805 );
806
807 $init = $this->callback('init', $init);
808
809 if (is_array($init)) {
810 if (isset($init['fid'])) $this->_fid = $init['fid'];
811 if (isset($init['tpl'])) $this->_tpl = $init['tpl'];
812 if (isset($init['i'])) $this->_next_i = $init['i'];
813 }
814
815 $this->_sys = $this->load_sysdata();
816
817 $this->load_workflow_state();
818
819 $secure_fld = $this->secure_fld_name();
820 $this->add_fld($secure_fld, 'dbx|hidden', $secure_fld, rules: 'parameter', dd: '');
821 $this->sync_secure_field($secure_fld, $this->secure_token($secure_fld));
822 $this->attachAdminHelpButton();
823 $this->add_default_search_rep();
824 }
825
840 public function add_module_bar(
841 $title,
842 $icon = 'bi-grid',
843 $subtitle = '',
844 $replace = false
845 ) {
846 if ($replace || !isset($this->_replaces['bar_title']) || (string)$this->_replaces['bar_title'] === '') {
847 $this->add_rep('bar_title', (string)$title);
848 }
849 if ($replace || !isset($this->_replaces['bar_icon']) || (string)$this->_replaces['bar_icon'] === '') {
850 $this->add_rep('bar_icon', (string)$icon);
851 }
852 if ($replace || !isset($this->_replaces['bar_subtitle']) || (string)$this->_replaces['bar_subtitle'] === '') {
853 $this->add_rep('bar_subtitle', (string)$subtitle);
854 }
855 }
856
861 public function set_form_help_enabled(bool $enabled = true): self {
862 $this->_form_help_enabled = $enabled ? 1 : 0;
863 if (!$enabled) {
864 $this->_form_help_button = '';
865 unset($this->_obj['help_button']);
866 }
867 return $this;
868 }
869
870 public function add_module_bar_form_actions(array $options = array()) {
871 $defaults = array(
872 'save' => true,
873 'delete' => false,
874 'reload' => true,
875 'reload_url' => '',
876 'delete_url' => '',
877 'delete_title' => 'Datensatz loeschen',
878 'delete_hint' => 'Dieser Vorgang kann nicht rueckgaengig gemacht werden.',
879 );
880 $this->_module_bar_form_actions = array_merge($defaults, $options);
881 }
882
883 public function prepare_form_shell(array $options = array()) {
884 if (!isset($this->_replaces['form_shell_class'])) {
885 $this->add_rep('form_shell_class', (string)($options['class'] ?? ''));
886 }
887 if (!isset($this->_replaces['form_class'])) {
888 $this->add_rep('form_class', (string)($options['form_class'] ?? ''));
889 }
890 if (!isset($this->_replaces['form_attrs'])) {
891 $this->add_rep('form_attrs', (string)($options['form_attrs'] ?? ''));
892 }
893 }
894
895 protected function defaultModuleBarReplaces(): array {
896 return array(
897 'bar_class' => 'dbx-module-bar',
898 'bar_title_class' => 'dbx-module-bar-titleblock',
899 'bar_actions_class' => 'dbx-module-bar-actions',
900 'bar_title' => '',
901 'bar_icon' => 'bi-grid',
902 'bar_subtitle' => '',
903 'bar_title_pre' => '',
904 'bar_title_heading_attrs' => '',
905 'bar_actions' => '',
906 'bar_extra' => '',
907 'bar_middle' => '',
908 );
909 }
910
911 protected function prepareFormFrameReplaces(int $i): void {
912 $formClass = trim((string)($this->_replaces['form_class'] ?? ''));
913 $formAttrs = trim((string)($this->_replaces['form_attrs'] ?? ''));
914 $shellClass = trim((string)($this->_replaces['form_shell_class'] ?? ''));
915 $panelClass = trim('dbxForm_wrapper ' . $shellClass);
916 $frameId = trim((string)($this->_replaces['frame_id'] ?? ''));
917 $formAction = dbx()->action_url((string)$this->_action);
918 if ($frameId === '') {
919 $frameId = 'dbx_target_' . $i;
920 }
921
922 $this->add_rep('frame_id', $frameId);
923 if (trim((string)($this->_replaces['frame_panel_class'] ?? '')) === '') {
924 $this->add_rep('frame_panel_class', $panelClass);
925 }
926 $this->add_rep('frame_panel_attrs', (string)($this->_replaces['frame_panel_attrs'] ?? ''));
927 $this->add_rep('frame_subbar', '');
928 $this->add_rep('frame_body_class', '');
929 if ((string)($this->_replaces['frame_skip_form_wrap'] ?? '') !== '1') {
930 $this->add_rep('frame_form_open', '<form action="' . htmlspecialchars($formAction, ENT_QUOTES) . '" method="post" id="dbx_form_' . $i . '" class="dbxAjax' . ($formClass !== '' ? ' ' . $formClass : '') . '"' . ($formAttrs !== '' ? ' ' . $formAttrs : '') . '>');
931 $this->add_rep('frame_body_head', '<div class="row"><div class="col">{obj:form_msg}</div></div>');
932 $this->add_rep('frame_form_close', '</form>');
933 } else {
934 if (!isset($this->_replaces['frame_form_open'])) {
935 $this->add_rep('frame_form_open', '');
936 }
937 if (!isset($this->_replaces['frame_body_head'])) {
938 $this->add_rep('frame_body_head', '');
939 }
940 if (!isset($this->_replaces['frame_form_close'])) {
941 $this->add_rep('frame_form_close', '');
942 }
943 }
944 $this->add_rep('frame_body_tail', '');
945 }
946
947 protected function applyModuleBarReplaces(array $values = array()): void {
948 $defaults = array_merge($this->defaultModuleBarReplaces(), $values);
949
950 foreach ($defaults as $key => $value) {
951 $this->add_rep($key, (string) $value);
952 }
953 }
954
955 protected function buildModuleBarFormActionsHtml() {
956 if (!$this->_module_bar_form_actions) {
957 return '';
958 }
959
960 $opts = $this->_module_bar_form_actions;
961 $i = (int)$this->_next_i;
962 if ($i <= 0) {
963 $i = (int)$this->next_i();
964 }
965
966 $formId = 'dbx_form_' . $i;
967 $html = '';
968
969 if (!empty($opts['save'])) {
970 $html .= $this->get_tpl('dbx|button-bar-save', array(
971 'bar_form_id' => $formId,
972 ));
973 }
974
975 if (!empty($opts['delete']) && trim((string)($opts['delete_url'] ?? '')) !== '') {
976 $html .= $this->get_tpl('dbx|button-bar-delete', array(
977 'bar_delete_url' => htmlspecialchars((string)($opts['delete_url'] ?? ''), ENT_QUOTES),
978 'bar_delete_title' => htmlspecialchars((string)($opts['delete_title'] ?? 'Datensatz loeschen'), ENT_QUOTES),
979 'bar_delete_hint' => htmlspecialchars((string)($opts['delete_hint'] ?? ''), ENT_QUOTES),
980 ));
981 }
982
983 if (!empty($opts['reload'])) {
984 $reloadUrl = trim((string)($opts['reload_url'] ?? ''));
985 if ($reloadUrl === '') {
986 $reloadUrl = (string)$this->_action;
987 }
988
989 $html .= $this->get_tpl('dbx|button-bar-reload', array(
990 'bar_reload_url' => htmlspecialchars($reloadUrl, ENT_QUOTES),
991 ));
992 }
993
994 return $html;
995 }
996
997 protected function buildModuleBarObj() {
998 $modul = (string)dbx()->get_system_var('dbx_modul', '');
999
1000 try {
1001 $help = dbx()->get_include_obj('dbxAdminHelp', 'dbxAdmin');
1002 $topic = $help->resolveTopic($modul);
1003
1004 $actions = trim((string)($this->_replaces['bar_actions'] ?? ''));
1005 if ($actions === '' && isset($this->_obj['bar_actions'])) {
1006 $actions = (string)$this->_obj['bar_actions'];
1007 }
1008
1009 $formActions = $this->buildModuleBarFormActionsHtml();
1010 if ($formActions !== '') {
1011 $actions = ($actions !== '') ? ($actions . ' ' . $formActions) : $formActions;
1012 }
1013
1014 $title = (string)($this->_replaces['bar_title'] ?? '');
1015 $icon = (string)($this->_replaces['bar_icon'] ?? 'bi-grid');
1016 $subtitle = (string)($this->_replaces['bar_subtitle'] ?? '');
1017
1018 $fdTitle = $this->get_fd_message('bar_title', '');
1019 $fdSubtitle = $this->get_fd_message('bar_subtitle', '');
1020 $fdIcon = $this->get_fd_message('bar_icon', '');
1021 $meta = array();
1022
1023 if ($topic !== '') {
1024 $meta = $help->barMeta($topic);
1025 if (is_array($meta) && $meta) {
1026 if ($title === '') {
1027 $title = (string)($meta['title'] ?? $topic);
1028 }
1029 if ($icon === '' || $icon === 'bi-grid') {
1030 $icon = (string)($meta['icon'] ?? 'bi-grid');
1031 }
1032 if ($subtitle === '') {
1033 $subtitle = (string)($meta['subtitle'] ?? '');
1034 }
1035 }
1036 }
1037
1038 // Eine aktive FD ist für Formulare und Reports die verbindliche
1039 // sprachabhängige Quelle. Sie ersetzt automatisch leere oder von
1040 // der generischen Admin-Hilfe stammende Metadaten. Ein Modul darf
1041 // weiterhin bewusst einen spezielleren, ebenfalls aus der FD
1042 // gelesenen Formulartitel setzen.
1043 $metaTitle = (string)($meta['title'] ?? '');
1044 $metaSubtitle = (string)($meta['subtitle'] ?? '');
1045 if (
1046 $fdTitle !== '' &&
1047 ($title === '' || $title === $metaTitle || $title === $fdTitle)
1048 ) {
1049 $title = $fdTitle;
1050 }
1051 if (
1052 $fdSubtitle !== '' &&
1053 (
1054 $subtitle === '' ||
1055 $subtitle === $metaSubtitle ||
1056 $subtitle === $fdSubtitle
1057 )
1058 ) {
1059 $subtitle = $fdSubtitle;
1060 }
1061 if ($fdIcon !== '') {
1062 $icon = $fdIcon;
1063 }
1064
1065 $helpButton = '';
1066 if ($this->_form_help_enabled) {
1067 if ($topic !== '') {
1068 $helpButton = $help->button($topic);
1069 } elseif (isset($this->_obj['help_button'])) {
1070 $helpButton = (string)$this->_obj['help_button'];
1071 }
1072 if ($helpButton === '' && method_exists($help, 'formButton')) {
1073 $helpButton = $help->formButton($modul, (string)$this->_fid, $title);
1074 }
1075 }
1076 $this->_form_help_button = $helpButton;
1077
1078 $barExtra = trim((string)($this->_replaces['bar_extra'] ?? ''));
1079 $visibleBarActions = $actions . $barExtra;
1080 if ($helpButton !== '' && strpos($visibleBarActions, 'bi-question-circle') === false) {
1081 $barExtra .= $helpButton;
1082 }
1083
1084 $barClass = trim((string)($this->_replaces['bar_class'] ?? ''));
1085 if ($barClass === '') {
1086 $barClass = 'dbx-module-bar';
1087 }
1088
1089 $this->applyModuleBarReplaces(array(
1090 'bar_class' => $barClass,
1091 'bar_title' => $title,
1092 'bar_icon' => $icon !== '' ? $icon : 'bi-grid',
1093 'bar_subtitle' => $subtitle,
1094 'bar_actions' => $actions,
1095 'bar_extra' => $barExtra,
1096 ));
1097 } catch (\Throwable $e) {
1098 }
1099 }
1100
1107 protected function ensureFormHelpBar(string $content): string {
1108 if (!$this->_form_help_enabled || $content === '' || $this->_form_help_button === '' || stripos($content, '<form') === false) {
1109 return $content;
1110 }
1111 if (strpos($content, 'bi-question-circle') !== false
1112 || strpos($content, 'dbx_run1=help') !== false) {
1113 return $content;
1114 }
1115
1116 $title = trim((string)($this->_replaces['bar_title'] ?? ''));
1117 if ($title === '') {
1118 $title = ucwords(str_replace(array('-', '_', '.'), ' ', (string)$this->_fid));
1119 }
1120 $bar = $this->get_tpl('dbx|form-help-bar', array(
1121 'form_help_title' => htmlspecialchars($title, ENT_QUOTES, 'UTF-8'),
1122 'form_help_icon' => htmlspecialchars((string)($this->_replaces['bar_icon'] ?? 'bi-ui-checks'), ENT_QUOTES, 'UTF-8'),
1123 'form_help_button' => $this->_form_help_button,
1124 ));
1125 if (trim($bar) === '') {
1126 return $content;
1127 }
1128
1129 return (string)preg_replace_callback('/<form\b[^>]*>/i', static function ($match) use ($bar) {
1130 return $match[0] . $bar;
1131 }, $content, 1);
1132 }
1133
1134 protected function attachAdminHelpButton($topic = '') {
1135 $modul = (string)dbx()->get_system_var('dbx_modul', '');
1136 if ($modul !== 'dbxAdmin' && !str_ends_with($modul, '_admin')) {
1137 return;
1138 }
1139
1140 try {
1141 $help = dbx()->get_include_obj('dbxAdminHelp', 'dbxAdmin');
1142 if ($topic === '') {
1143 $topic = $help->resolveTopic($modul);
1144 }
1145 if ($topic === '') {
1146 return;
1147 }
1148 $this->add_obj('help_button', $help->button($topic));
1149
1150 $barMeta = $help->barMeta($topic);
1151 if (is_array($barMeta) && $barMeta) {
1152 $this->add_module_bar(
1153 (string)($barMeta['title'] ?? ''),
1154 (string)($barMeta['icon'] ?? 'bi-grid'),
1155 (string)($barMeta['subtitle'] ?? '')
1156 );
1157 }
1158 } catch (\Throwable $e) {
1159 }
1160 }
1161
1162 private function get_token(): string {
1163 try {
1164 return bin2hex(random_bytes(32));
1165 } catch (\Throwable $e) {
1166 return hash('sha256', uniqid('', true) . '|' . mt_rand());
1167 }
1168 }
1169
1170 private function secure_fld_name(): string {
1171 return '_' . (string)$this->_fid;
1172 }
1173
1174 private function secure_token(string $fld): string {
1175 if (!isset($this->_sys['_csrf']) || !is_array($this->_sys['_csrf'])) {
1176 $this->_sys['_csrf'] = array();
1177 }
1178
1179 $token = (string)($this->_sys['_csrf'][$fld] ?? $this->_sys[$fld] ?? '');
1180 if (!preg_match('/^[a-f0-9]{64}$/', $token)) {
1181 $token = $this->get_token();
1182 }
1183
1184 $this->_sys['_csrf'][$fld] = $token;
1185 $this->_sys[$fld] = $token;
1186 $this->_data[$fld] = $token;
1187
1188 return $token;
1189 }
1190
1191 private function sync_secure_field(string $fld, string $token): void {
1192 $this->_sys['_csrf'][$fld] = $token;
1193 $this->_sys[$fld] = $token;
1194 $this->_data[$fld] = $token;
1195
1196 if (isset($this->_flds[$fld])) {
1197 $this->_flds[$fld]['value'] = $token;
1198 $this->_flds[$fld]['origin'] = 'csrf';
1199 $this->_flds[$fld]['verify'] = 1;
1200 $this->_flds[$fld]['error'] = 0;
1201 }
1202 }
1203
1204 private function rotate_secure_token(string $fld): void {
1205 $this->sync_secure_field($fld, $this->get_token());
1206 }
1207
1218 public function get_security_data(): array {
1219 $fld = $this->secure_fld_name();
1220 $token = $this->secure_token($fld);
1221
1222 // AJAX-gesteuerte Formulare rufen nicht zwingend run() auf. Ohne
1223 // diese Speicherung würde ein hier erstmals erzeugter oder nach
1224 // submit() rotierter Token nur im aktuellen Objekt existieren und
1225 // der nächste Request wieder den alten Sessionstand laden.
1226 $this->store_sysdata();
1227
1228 return array(
1229 'name' => $fld,
1230 'value' => $token,
1231 );
1232 }
1233
1234
1250 public function url_to_array($data) {
1251 if (!is_array($data)) {
1252 $first = substr((string) $data, 0, 1);
1253
1254 if ($data && $first != '=') {
1255 if (strpos((string) $data, '=') !== false) {
1256 parse_str($data, $xdata);
1257 $data = $xdata;
1258 }
1259 }
1260 }
1261
1262 return $data;
1263 }
1264
1272 public function set_msg_info($msg) {
1273 $this->_msg_info = $msg;
1274 }
1275
1283 public function set_msg_ok($msg) {
1284 $this->_msg_success = $msg;
1285 }
1286
1294 public function set_msg_error($msg) {
1295 $this->_msg_error = $msg;
1296 }
1297
1310 public function set_error($msg) {
1311 $this->_msg_error = $msg;
1312 $this->_general_error = $msg;
1313 }
1314
1322 public function set_msg_warning($msg) {
1323 $this->_msg_warning = $msg;
1324 }
1325
1338 public function get_fd_message(string $key, string $default = ''): string {
1339 if (!array_key_exists($key, $this->_messages)) {
1340 return $default;
1341 }
1342
1343 $message = $this->_messages[$key];
1344
1345 return is_scalar($message) ? (string)$message : $default;
1346 }
1347
1362 public function format_fd_message(
1363 string $key,
1364 array $values = array(),
1365 string $default = ''
1366 ): string {
1367 $message = $this->get_fd_message($key, $default);
1368 if ($message === '' || $values === array()) {
1369 return $message;
1370 }
1371
1372 $replacements = array();
1373 foreach ($values as $placeholder => $value) {
1374 if (!is_scalar($value) && $value !== null) {
1375 continue;
1376 }
1377 $replacements['{' . trim((string)$placeholder, '{}') . '}'] =
1378 (string)$value;
1379 }
1380
1381 return strtr($message, $replacements);
1382 }
1383
1402 public function load_fd_messages(string $fd = ''): array {
1403 $source = trim($fd);
1404 if ($source === '') {
1405 $source = trim((string)$this->_fd);
1406 }
1407 if ($source === '') {
1408 return $this->_messages;
1409 }
1410 if (strpos($source, 'fd:') !== 0) {
1411 $source = 'fd:' . $source;
1412 }
1413
1414 $this->get_dd_fields_source($source);
1415
1416 return $this->_messages;
1417 }
1418
1430 private function apply_fd_messages(array $messages): void {
1431 if (
1432 !isset($messages['save_success']) &&
1433 isset($messages['save_succeass'])
1434 ) {
1435 $messages['save_success'] = $messages['save_succeass'];
1436 }
1437
1438 if (
1439 !isset($messages['save_succeass']) &&
1440 isset($messages['save_success'])
1441 ) {
1442 $messages['save_succeass'] = $messages['save_success'];
1443 }
1444
1445 $this->_messages = array_merge($this->_messages, $messages);
1446 }
1447
1460 public function get_fld_id($fld) {
1461 return dbx()->part_select(' name="', '"', $fld);
1462 }
1463
1481 public function add_norep($content) {
1482 $oTPL = dbx()->get_system_obj('dbxTPL');
1483 if (is_object($oTPL) && method_exists($oTPL, 'cleanup_optional_placeholders')) {
1484 $content = $oTPL->cleanup_optional_placeholders((string)$content);
1485 }
1486
1487 if (isset($_SESSION['dbx']['norep']) && is_array($_SESSION['dbx']['norep'])) {
1488 $xnorep = $_SESSION['dbx']['norep'];
1489
1490 for ($i = 0; $i < 2; $i++) {
1491 foreach ($xnorep as $id => $norep) {
1492 $xid = '[' . $id . ']';
1493 $content = str_replace($xid, $norep, $content);
1494 }
1495 }
1496 }
1497
1498 return $content;
1499 }
1500
1519 public function fast_response($response, $interpreter = 0) {
1520 if ($interpreter) {
1521 $oInterpreter = dbx()->get_system_obj('dbxInterpreter');
1522 $response = $oInterpreter->run($response);
1523 $response = $this->add_norep($response);
1524 }
1525
1526 echo $response;
1527 exit;
1528 }
1529
1545 public function get_tpl($tpl, $data = '', $type = 'htm', $i = 0) {
1546 return $this->oTPL->get_tpl($tpl, $data, $type, $i);
1547 }
1548
1564 public function obv_value($content, $id, $value) {
1565 $rep = '{obv:' . $id . '}';
1566 $val = '';
1567
1568 if (is_string($value)) {
1569 $val = htmlspecialchars($value, ENT_QUOTES);
1570 $content = str_replace($rep, $val, $content);
1571 }
1572
1573 return $content;
1574 }
1575
1594 public function merge_tpl_data($tpl, $i = 0) {
1595 $editor = dbx()->get_system_var('dbx_editor', 0, 'int');
1596
1597 if (!$i && !$editor) {
1598 $i = $this->_next_i;
1599 }
1600
1601 $replaces = array();
1602 $replaces['dbx_modul'] = $this->_dbx_modul;
1603 $replaces['dbx_run1'] = $this->_dbx_action;
1604 $replaces['dbx_page'] = $this->_dbx_page;
1605 $replaces['dbx_design'] = $this->_dbx_design;
1606 $replaces['dbx_lng'] = $this->_dbx_lng;
1607 $replaces['action'] = dbx()->action_url((string)$this->_action);
1608 $replaces['fid'] = $this->_fid;
1609 $replaces['rid'] = $this->_rid;
1610 $replaces['self'] = dbx()->get_self_url();
1611
1612 if ($i) {
1613 $replaces['i'] = $i;
1614 }
1615
1616 return $this->oTPL->replaces($tpl, $replaces);
1617 }
1618
1636 public function merge_obj($content, $i = 0) {
1637 if (is_array($this->_obj)) {
1638 foreach ($this->_obj as $id => $obj) {
1639 $fid = '{obj:' . $id . '}';
1640 $obj = $this->oTPL->replaces($obj, $this->_replaces);
1641 $content = str_replace($fid, $obj, $content);
1642 }
1643 }
1644
1645 return $content;
1646 }
1647
1663 public function store_sysdata() {
1664 $section = $this->_fid;
1665 $modul = $this->_dbx_modul;
1666 $mode = $this->_store_mode;
1667 $value = $this->_sys;
1668 $key = 'sysdata';
1669
1670 if ($section && $mode == 'session') {
1671 dbx()->set_session_var($key, $value, $section, $modul);
1672 }
1673 }
1674
1690 public function load_sysdata() {
1691 $sysdata = array();
1692 $section = $this->_fid;
1693 $modul = $this->_dbx_modul;
1694 $key = 'sysdata';
1695 $empty = array();
1696
1697 if ($section) {
1698 $sysdata = dbx()->get_session_var($key, $empty, $section, $modul);
1699 }
1700
1701 return is_array($sysdata) ? $sysdata : array();
1702 }
1703
1720 public function get_activ_id($key = '') {
1721 if (!$key) {
1722 $key = $this->_dd;
1723 }
1724
1725 $xkey = '_activ-row_id_' . $key;
1726 return dbx()->get_remember_var($xkey, 0, 'dbx');
1727 }
1728
1737 public function set_activ_id($value, $key = '') {
1738 if (!$key) {
1739 $key = $this->_dd;
1740 }
1741
1742 $xkey = '_activ-row_id_' . $key;
1743 dbx()->set_remember_var($xkey, $value, 'dbx');
1744 }
1745
1758 public function add_rep($key, $val) {
1759 $this->_replaces[$key] = $val;
1760 }
1761
1773 public function replaces($content, $replaces = null): string {
1774 if ($replaces === null) {
1775 $replaces = $this->_replaces;
1776 }
1777
1778 return $this->oTPL->replaces(
1779 (string)$content,
1780 is_array($replaces) ? $replaces : array()
1781 );
1782 }
1783
1800 public function add_js_code($javascript) {
1801 if ($javascript !== '') {
1802 $this->_js[] = $javascript;
1803 }
1804 }
1805
1826 public function add_js_call($target, $function, $args = '') {
1827 $js = $function . "('" . addslashes((string) $target) . "'";
1828
1829 if ($args !== '') {
1830 $js .= ',' . $args;
1831 }
1832
1833 $js .= ');';
1834
1835 $this->_js[] = $js;
1836 }
1837
1856 public function update_error_fld($name, $msg) {
1857 foreach ($this->_flds as $no => $fld) {
1858 if (($fld['name'] ?? '') == $name) {
1859 $fld['error'] = 1;
1860 $fld['verify'] = 1;
1861 $fld['data']['class'] = trim(($fld['data']['class'] ?? '') . ' fld-error');
1862 $fld['data']['errormsg'] = $msg;
1863 $this->_flds[$no] = $fld;
1864 break;
1865 }
1866 }
1867 }
1868
1882 public function add_fld_error($name, $msg = '') {
1883 dbx()->debug("add fld-error fld=($name) Msg=($msg)");
1884 $this->_errors[$name] = $msg;
1885 $this->update_error_fld($name, $msg);
1886 }
1887
1898 public function get_validation_result(string $name = ''): array {
1899 if ($name === '') {
1900 return $this->_validation_results;
1901 }
1902
1903 $result = $this->_validation_results[$name] ?? array();
1904 return is_array($result) ? $result : array();
1905 }
1906
1910 protected function remember_validation_result(string $name, array $result): void {
1911 if ($name !== '') {
1912 $this->_validation_results[$name] = $result;
1913 }
1914 }
1915
1924 public function add_fld_warning($name, $msg = '') {
1925 $this->_warnings[$name] = $msg;
1926 }
1927
1945 private function resolve_fld_val($name, $default = '', $rules = '', $useRequest = true, $fallbackToState = true) {
1946 $stateValue = $default;
1947 $origin = 'default';
1948
1949 if (isset($this->_data[$name])) {
1950 $stateValue = $this->_data[$name];
1951 $origin = 'data';
1952 }
1953
1954 if (isset($this->_sys[$name])) {
1955 $stateValue = $this->_sys[$name];
1956 $origin = 'sys';
1957 }
1958
1959 $value = $fallbackToState ? $stateValue : $default;
1960
1961 if (!$fallbackToState) {
1962 $origin = 'default';
1963 }
1964
1965 if ($useRequest) {
1966 if (isset($_POST[$name])) {
1967 $value = $_POST[$name];
1968 $origin = 'post';
1969 } elseif (isset($_GET[$name])) {
1970 $value = $_GET[$name];
1971 $origin = 'get';
1972 }
1973 }
1974
1975 if ($value === null) {
1976 $value = '';
1977 }
1978
1979 $ok = true;
1980 $validation = array();
1981
1982 if ($rules) {
1983 $validation = $this->oValidator->validateResult($value, $rules, $name);
1984 $ok = (bool)($validation['valid'] ?? false);
1985 $this->remember_validation_result((string)$name, $validation);
1986
1987 // Normalisierung bleibt ausdrücklich opt-in. Dadurch ändern
1988 // bestehende Regeln keine Datentypen oder gespeicherten Werte.
1989 if ($ok && preg_match('/(?:^|\|)trim(?:\||$)/', (string)$rules)) {
1990 $value = $validation['normalized'] ?? $value;
1991 }
1992 }
1993
1994 return array(
1995 'value' => $value,
1996 'origin' => $origin,
1997 'ok' => $ok ? 1 : 0,
1998 'validation' => $validation,
1999 );
2000 }
2001
2002 public function get_fld_val($name, $default = '', $rules = '', $submit = -1) {
2003 $resolved = $this->resolve_fld_val($name, $default, $rules, true, true);
2004 $value = $resolved['value'];
2005
2006 if (!$resolved['ok']) {
2007 $this->add_fld_error($name, 'f:' . $rules);
2008 $value = $default;
2009 }
2010
2011 $this->_data[$name] = $value;
2012 $this->_sys[$name] = $value;
2013
2014 if (isset($this->_flds[$name])) {
2015 $this->_flds[$name]['value'] = $value;
2016 $this->_flds[$name]['origin'] = $resolved['origin'];
2017 }
2018
2019 return $value;
2020 }
2021 public function get_fld_value($name, $default = '', $rules = '', $submit = -1) {
2022 return $this->get_fld_val($name, $default, $rules, $submit);
2023 }
2024
2034 public function get_post_data($name, $default = '', $rules = 'parameter') {
2035 $danger_value = $this->_get_post_data($name, $default);
2036
2037 if ($rules) {
2038 $validation = $this->oValidator->validateResult($danger_value, $rules, $name);
2039 $ok = (bool)($validation['valid'] ?? false);
2040 $this->remember_validation_result((string)$name, $validation);
2041
2042 if (!$ok) {
2043 $this->add_fld_error($name, 'p:' . $rules);
2044 $danger_value = $default;
2045 } elseif (preg_match('/(?:^|\|)trim(?:\||$)/', (string)$rules)) {
2046 $danger_value = $validation['normalized'] ?? $danger_value;
2047 }
2048 }
2049
2050 return $danger_value;
2051 }
2052
2062 public function get_post($name, $default = '', $rules = 'alphanum') {
2063 $danger_value = $this->_get_post($name, $default);
2064
2065 if ($rules) {
2066 $validation = $this->oValidator->validateResult($danger_value, $rules, $name);
2067 $ok = (bool)($validation['valid'] ?? false);
2068 $this->remember_validation_result((string)$name, $validation);
2069
2070 if (!$ok) {
2071 $this->add_fld_error($name, 'p:' . $rules);
2072 $danger_value = $default;
2073 } elseif (preg_match('/(?:^|\|)trim(?:\||$)/', (string)$rules)) {
2074 $danger_value = $validation['normalized'] ?? $danger_value;
2075 }
2076 }
2077
2078 return $danger_value;
2079 }
2080
2094 private function _get_post_data($name, $default = '') {
2095 $set = 0;
2096 $value = $default;
2097
2098 if (isset($_POST[$name])) {
2099 $value = $_POST[$name];
2100 $set = 1;
2101 } elseif (isset($_GET[$name])) {
2102 $value = $_GET[$name];
2103 $set = 1;
2104 }
2105
2106 if (!$set && isset($this->_data[$name])) {
2107 $value = $this->_data[$name];
2108 }
2109
2110 return $value;
2111 }
2112
2125 private function _get_post($name, $default = '') {
2126 $value = $default;
2127
2128 if (isset($_POST[$name])) {
2129 $value = $_POST[$name];
2130 } elseif (isset($_GET[$name])) {
2131 $value = $_GET[$name];
2132 }
2133
2134 return $value;
2135 }
2136
2156 public function add_obj($obj, $tpl, $data = '', $data2 = '') {
2157 if ($tpl != 'obj-value' && $tpl != 'obv-value') {
2158 $tpl = $this->get_tpl($tpl, $data);
2159 } else {
2160 if ($tpl == 'obv-value') {
2161 $tpl = htmlspecialchars((string) $data, ENT_QUOTES);
2162 }
2163
2164 if ($tpl == 'obj-value') {
2165 $tpl = $data;
2166 }
2167
2168 $tpl = $this->oTPL->replaces((string) $tpl, $data2);
2169 }
2170
2171 $tpl = str_replace('{class}', '', $tpl);
2172 $tpl = str_replace('{tooltip}', '', $tpl);
2173
2174 $this->_obj[$obj] = $tpl;
2175 }
2176
2192 public function add_action($obj, $tpl, $action = '', $data = '') {
2193 $xaction = $this->_action;
2194
2195 if ($action !== '' && $action[0] == '&') {
2196 $x_action = $xaction . $action;
2197 } else {
2198 $x_action = $action;
2199 }
2200
2201 $xdata = array();
2202 $xdata['action'] = dbx()->action_url((string)$x_action);
2203
2204 if (is_array($data)) {
2205 $xdata = array_merge($xdata, $data);
2206 }
2207
2208 $tpl = $this->get_tpl($tpl, $xdata);
2209 $tpl = str_replace('{class}', '', $tpl);
2210 $tpl = str_replace('{tooltip}', '', $tpl);
2211
2212 $this->_obj[$obj] = $tpl;
2213 }
2214
2239 private function get_active_field_source($source = 'fd::'): string {
2240 if ($source === 'fd::') {
2241 if ($this->_fd) {
2242 return (string) $this->_fd;
2243 }
2244
2245 return (string) $this->_dd;
2246 }
2247
2248 if ($source === 'dd::') {
2249 return (string) $this->_dd;
2250 }
2251
2252 return (string) $source;
2253 }
2254
2286 private function get_fld_marker_value_from_records($value, string $var, $field_record = array(), $dd_record = array()) {
2287 if (!is_array($field_record)) {
2288 $field_record = array();
2289 }
2290
2291 if (!is_array($dd_record)) {
2292 $dd_record = array();
2293 }
2294
2295 if ($value === 'fd::') {
2296 return $field_record[$var] ?? '';
2297 }
2298
2299 if ($value === 'dd::') {
2300 return $dd_record[$var] ?? '';
2301 }
2302
2303 return $value;
2304 }
2305
2306
2307
2337 private function get_dd_fields_source(string $dd): array {
2338 $dd = trim($dd);
2339
2340 if ($dd === '' || $dd === 'dd') {
2341 return array();
2342 }
2343
2344 $mod = 'dd';
2345 $name = $dd;
2346
2347 foreach (array('cfg:', 'def:', 'mod:', 'fd:', 'dd:') as $prefix) {
2348 if (strpos($dd, $prefix) === 0) {
2349 $mod = substr($prefix, 0, -1);
2350 $name = substr($dd, strlen($prefix));
2351 break;
2352 }
2353 }
2354
2355 if ($mod === 'dd' && strpos($dd, '|') !== false) {
2356 $mod = 'fd';
2357 $name = $dd;
2358 }
2359
2360 if ($mod === 'dd') {
2361 $fields = $this->oDB->get_dd_fields($name);
2362 if (method_exists($this->oDB, 'get_dd_file')) {
2363 $this->add_editor_file('dd', $this->oDB->get_dd_file($name));
2364 }
2365 return is_array($fields) ? $fields : array();
2366 }
2367
2368 $dd_file = '';
2369
2370 switch ($mod) {
2371 case 'cfg':
2372 $dd_file = dbx()->get_base_dir() . "dbx/modules/$name/cfg/config.dd.php";
2373 break;
2374
2375 case 'def':
2376 $dd_file = dbx()->get_base_dir() . "dbx/modules/$name/dd/$name.dd.php";
2377 break;
2378
2379 case 'mod':
2380 $modul = dbx()->get_system_var('dbx_activ_modul', 'dbx');
2381 $dd_file = dbx()->get_base_dir() . "dbx/modules/$modul/dd/$name.dd.php";
2382 break;
2383
2384 case 'fd':
2385 $fd_modul = $this->_dbx_modul ? $this->_dbx_modul : dbx()->get_system_var('dbx_activ_modul', 'dbx');
2386 $fd_name = $name;
2387
2388 if (strpos($name, '|') !== false) {
2389 $parts = explode('|', $name, 2);
2390 $fd_modul = trim($parts[0]);
2391 $fd_name = trim($parts[1]);
2392 }
2393
2394 if ($fd_modul === '') {
2395 $fd_modul = dbx()->get_system_var('dbx_activ_modul', 'dbx');
2396 }
2397
2398 $dd_file = function_exists('dbx_lng_resolve_file')
2400 dbx()->get_base_dir() . "dbx/modules/$fd_modul/fd/",
2401 $fd_name,
2402 'fd.php',
2403 $this->_dbx_lng,
2404 true
2405 )
2406 : dbx()->get_base_dir() . "dbx/modules/$fd_modul/fd/$fd_name.fd.php";
2407 if ($dd_file === '' || !is_file($dd_file)) {
2408 $dd_file = dbx()->get_base_dir() . "dbx/modules/$fd_modul/fd/$fd_name.fd.php";
2409 }
2410 break;
2411 }
2412
2413 return $this->read_dd_fields_direct($dd_file);
2414 }
2415
2445 private function read_dd_fields_direct(string $dd_file): array {
2446 $fields = array();
2447 $table = array();
2448 $indexes = array();
2449 $messages = array();
2450
2451 $dd_file = dbx()->os_path($dd_file);
2452
2453 if (!file_exists($dd_file)) {
2454 return array();
2455 }
2456
2457 $real_file = realpath($dd_file);
2458 if (!$real_file) {
2459 $real_file = $dd_file;
2460 }
2461
2462 $normalized = str_replace('\\', '/', $real_file);
2463 $kind = (substr($normalized, -7) === '.fd.php') ? 'fd' : 'dd';
2464 $mtime = @filemtime($real_file);
2465 $size = @filesize($real_file);
2466 $cache_key = md5($normalized);
2467
2468 $this->add_editor_file($kind, $real_file);
2469 $runtime_cache =& $this->session_cache_section('form', 'field_source_runtime');
2470 $field_source_cache =& $this->session_cache_section('form', 'field_source');
2471
2472 if (isset($runtime_cache[$cache_key])) {
2473 $entry = $runtime_cache[$cache_key];
2474
2475 if (
2476 is_array($entry) &&
2477 ($entry['mtime'] ?? 0) === $mtime &&
2478 ($entry['size'] ?? 0) === $size &&
2479 isset($entry['fields']) &&
2480 is_array($entry['fields']) &&
2481 isset($entry['messages']) &&
2482 is_array($entry['messages'])
2483 ) {
2484 if ($kind === 'fd') {
2485 $this->apply_fd_messages($entry['messages']);
2486 }
2487 return $entry['fields'];
2488 }
2489 }
2490
2491 if (
2492 !isset($field_source_cache[$cache_key]) &&
2493 isset($_SESSION['dbx']['cache']['field_source'][$cache_key]) &&
2494 is_array($_SESSION['dbx']['cache']['field_source'][$cache_key])
2495 ) {
2496 $field_source_cache[$cache_key] = $_SESSION['dbx']['cache']['field_source'][$cache_key];
2497 unset($_SESSION['dbx']['cache']['field_source'][$cache_key]);
2498 }
2499
2500 if (isset($field_source_cache[$cache_key])) {
2501 $entry = $field_source_cache[$cache_key];
2502
2503 if (
2504 is_array($entry) &&
2505 ($entry['mtime'] ?? 0) === $mtime &&
2506 ($entry['size'] ?? 0) === $size &&
2507 isset($entry['fields']) &&
2508 is_array($entry['fields']) &&
2509 isset($entry['messages']) &&
2510 is_array($entry['messages'])
2511 ) {
2512 $runtime_cache[$cache_key] = $entry;
2513 if ($kind === 'fd') {
2514 $this->apply_fd_messages($entry['messages']);
2515 }
2516 return $entry['fields'];
2517 }
2518 }
2519
2520 include $real_file;
2521
2522 if (!is_array($fields)) {
2523 $fields = array();
2524 }
2525 if (!is_array($messages)) {
2526 $messages = array();
2527 }
2528
2529 $entry = array(
2530 'path' => $normalized,
2531 'kind' => $kind,
2532 'mtime' => $mtime,
2533 'size' => $size,
2534 'fields' => $fields,
2535 'messages' => $messages,
2536 );
2537
2538 $runtime_cache[$cache_key] = $entry;
2539 $field_source_cache[$cache_key] = $entry;
2540
2541 if ($kind === 'fd') {
2542 $this->apply_fd_messages($messages);
2543 }
2544
2545 return $fields;
2546 }
2547
2561 private function get_dd_fld(array $fields, string $fld): array {
2562 foreach ($fields as $record) {
2563 if (isset($record['name']) && $record['name'] === $fld) {
2564 return $record;
2565 }
2566 }
2567
2568 return array();
2569 }
2570
2571 private function &session_cache_section(string $bereich, string $section) {
2572 if (!isset($_SESSION['dbx']) || !is_array($_SESSION['dbx'])) {
2573 $_SESSION['dbx'] = array();
2574 }
2575
2576 if (!isset($_SESSION['dbx']['cache']) || !is_array($_SESSION['dbx']['cache'])) {
2577 $_SESSION['dbx']['cache'] = array();
2578 }
2579
2580 if (!isset($_SESSION['dbx']['cache'][$bereich]) || !is_array($_SESSION['dbx']['cache'][$bereich])) {
2581 $_SESSION['dbx']['cache'][$bereich] = array();
2582 }
2583
2584 if (!isset($_SESSION['dbx']['cache'][$bereich][$section]) || !is_array($_SESSION['dbx']['cache'][$bereich][$section])) {
2585 $_SESSION['dbx']['cache'][$bereich][$section] = array();
2586 }
2587
2588 return $_SESSION['dbx']['cache'][$bereich][$section];
2589 }
2590
2614 public function get_dd(string $dd, string $fld, string $var) {
2615 if ($dd === '' || $dd === 'dd') {
2616 return '';
2617 }
2618
2619 if ($fld === '') {
2620 return '';
2621 }
2622
2623 $cache_key = md5($dd . "\0" . $fld);
2624 $field_cache =& $this->session_cache_section('form', 'runtime_field');
2625
2626 if (isset($field_cache[$cache_key]) && is_array($field_cache[$cache_key])) {
2627 $field = $field_cache[$cache_key];
2628 } else {
2629 $fields = $this->get_dd_fields_source($dd);
2630
2631 if (!is_array($fields)) {
2632 $field_cache[$cache_key] = array();
2633 return '';
2634 }
2635
2636 $field = $this->get_dd_fld($fields, $fld);
2637
2638 if (!is_array($field)) {
2639 $field = array();
2640 }
2641
2642 $field_cache[$cache_key] = $field;
2643 }
2644
2645 if (!$field) {
2646 return '';
2647 }
2648
2649 if ($var === '*') {
2650 return $field;
2651 }
2652
2653 return $field[$var] ?? '';
2654 }
2655
2677 private function sql_dd_file_exists($modul, $dd) {
2678 $modul = trim((string) $modul);
2679 $dd = trim((string) $dd);
2680
2681 if ($dd === '') {
2682 return false;
2683 }
2684
2685 if ($modul === '') {
2686 $modul = dbx()->get_system_var('dbx_activ_modul', 'dbx');
2687 }
2688
2689 $file1 = dbx()->os_path(dbx()->get_base_dir() . 'dbx/modules/' . $modul . '/dd/' . $dd . '.dd.php');
2690 $file2 = dbx()->os_path(dbx()->get_base_dir() . 'dbx/modules/dbx/dd/' . $dd . '.dd.php');
2691
2692 return file_exists($file1) || file_exists($file2);
2693 }
2694
2695 private function normalize_sql_dd_name($modul, $source) {
2696 $source = trim((string) $source);
2697
2698 if ($source === '') {
2699 return '';
2700 }
2701
2702 $candidates = array(
2703 $source,
2704 str_replace('dbx_', 'dbx', $source),
2705 str_replace(' ', '', ucwords(str_replace('_', ' ', $source))),
2706 lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $source))))
2707 );
2708
2709 foreach ($candidates as $candidate) {
2710 if ($this->sql_dd_file_exists($modul, $candidate)) {
2711 return $candidate;
2712 }
2713 }
2714
2715 return $source;
2716 }
2717
2718 private function sql_to_array($data) {
2719 $dd = '';
2720 $xkey = '';
2721 $flds = '*';
2722 $where = '';
2723 $order = '';
2724 $limit = 888;
2725 $asc_desc = 'ASC';
2726 $xdata = array();
2727 $compact_sql_syntax = false;
2728
2729 $data = str_replace('sql:', '', (string) $data);
2730 $work = explode('|', $data);
2731
2732 if (isset($work[0])) {
2733 $dd = $work[0];
2734 }
2735
2736 if (isset($work[1])) {
2737 $xkey = $work[1];
2738 }
2739
2740 if ($dd && $xkey && strpos($xkey, ',') !== false) {
2741 $sqlSource = array_map('trim', explode(',', $xkey));
2742 $dd = $this->normalize_sql_dd_name($dd, $sqlSource[0] ?? '');
2743 $xkey = $sqlSource[1] ?? 'id';
2744 $flds = $sqlSource[2] ?? $xkey;
2745 $compact_sql_syntax = true;
2746 }
2747
2748 if (isset($work[2])) {
2749 if ($compact_sql_syntax) {
2750 $where = $work[2];
2751 } else {
2752 $flds = $work[2];
2753 }
2754 }
2755
2756 if (isset($work[3])) {
2757 if ($compact_sql_syntax) {
2758 $order = $work[3];
2759 } else {
2760 $where = $work[3];
2761 }
2762 }
2763
2764 if (isset($work[4])) {
2765 if ($compact_sql_syntax) {
2766 if ((int) $work[4] > 0) {
2767 $limit = (int) $work[4];
2768 }
2769 } else {
2770 $order = $work[4];
2771 }
2772 }
2773
2774 if (!$compact_sql_syntax && isset($work[5]) && (int) $work[5] > 0) {
2775 $limit = (int) $work[5];
2776 }
2777
2778 $xdata[0] = 'Bitte auswählen';
2779
2780 if ($order && strpos($order, ' DESC') !== false) {
2781 $asc_desc = 'DESC';
2782 $order = str_replace(' DESC', '', $order);
2783 }
2784
2785 if ($order && strpos($order, ' ASC') !== false) {
2786 $asc_desc = 'ASC';
2787 $order = str_replace(' ASC', '', $order);
2788 }
2789
2790 if ($dd) {
2791 $xflds = $flds;
2792
2793 if (!$xkey) {
2794 $xkey = 'id';
2795 }
2796
2797 if (strpos(',' . $xflds . ',', ',' . $xkey . ',') === false && $xflds !== '*') {
2798 $xflds .= ',' . $xkey;
2799 }
2800
2801 $data = $this->oDB->select($dd, $where, $xflds, $order, $asc_desc, '', $limit, 0, 0);
2802
2803 if (is_array($data)) {
2804 $displayFields = ($flds === '*') ? array() : array_map('trim', explode(',', $flds));
2805
2806 foreach ($data as $record) {
2807 $value = '';
2808
2809 foreach ($record as $fld => $val) {
2810 $use = ($flds === '*') || in_array($fld, $displayFields, true);
2811
2812 if ($use) {
2813 if ($value !== '') {
2814 $value .= ' | ';
2815 }
2816
2817 $value .= $val;
2818 }
2819 }
2820
2821 if (isset($record[$xkey])) {
2822 $xdata[$record[$xkey]] = $value;
2823 }
2824 }
2825 }
2826 }
2827
2828 return $xdata;
2829 }
2830
2831
2865 public function add_flds($dd = 'fd::') {
2866 $source = $this->get_active_field_source($dd);
2867
2868 if ($source === '' || $source === 'dd') {
2869 return 0;
2870 }
2871
2872 $fields = $this->get_dd_fields_source($source);
2873
2874 if (!is_array($fields) || !$fields) {
2875 return 0;
2876 }
2877
2878 $count = 0;
2879
2880 foreach ($fields as $record) {
2881 if (!is_array($record)) {
2882 continue;
2883 }
2884
2885 if (!isset($record['name']) || trim((string) $record['name']) === '') {
2886 continue;
2887 }
2888
2889 $name = trim((string) $record['name']);
2890 $this->add_fld($name, dd: $source);
2891 $count++;
2892 }
2893
2894 return $count;
2895 }
2896
2897
2952 public function add_fld($name, $tpl = 'fd::', $label = 'fd::', $rules = 'fd::', $class = 'fd::', $data = 'fd::', $options = 'fd::', $tooltip = 'fd::', $placeholder = 'fd::', $errormsg = 'fd::', $dd = 'fd::', $remap = 'fd::') {
2953 $field_source = $this->get_active_field_source($dd);
2954 $dd_source = $this->get_active_field_source('dd::');
2955 $field_record = $field_source ? $this->get_dd($field_source, $name, '*') : array();
2956 $dd_record = $dd_source ? $this->get_dd($dd_source, $name, '*') : array();
2957 $source_data = array();
2958 $source_options = array();
2959 $dd_data = array();
2960 $dd_options = array();
2961
2962 if (is_array($field_record) && $field_record) {
2963 $source_data = $this->process_array($field_record['data'] ?? '');
2964 $source_options = $this->process_array($field_record['options'] ?? '');
2965 }
2966
2967 if (is_array($dd_record) && $dd_record) {
2968 $dd_data = $this->process_array($dd_record['data'] ?? '');
2969 $dd_options = $this->process_array($dd_record['options'] ?? '');
2970 }
2971
2972 if ($data === 'fd::' || $data === '' || $data === null) {
2973 $data = $source_data;
2974 } elseif ($data === 'dd::') {
2975 $data = $dd_data;
2976 } else {
2977 $data = $this->merge_arrays($this->process_array($data), $source_data);
2978 }
2979
2980 if ($options === 'fd::' || $options === '' || $options === null) {
2981 $options = $source_options;
2982 } elseif ($options === 'dd::') {
2983 $options = $dd_options;
2984 } else {
2985 $options = $this->merge_arrays($this->process_array($options), $source_options);
2986 }
2987
2988 if (isset($data['dd'])) {
2989 $field_source = $this->get_active_field_source($data['dd']);
2990 $field_record = $field_source ? $this->get_dd($field_source, $name, '*') : array();
2991 }
2992
2993 if ($label === 'fd::' && isset($data['label'])) {
2994 $label = $data['label'];
2995 }
2996
2997 if ($rules === 'fd::' && isset($data['rules'])) {
2998 $rules = $data['rules'];
2999 }
3000
3001 if ($class === 'fd::' && isset($data['class'])) {
3002 $class = $data['class'];
3003 }
3004
3005 if ($tooltip === 'fd::' && isset($data['tooltip'])) {
3006 $tooltip = $data['tooltip'];
3007 }
3008
3009 if ($placeholder === 'fd::' && isset($data['placeholder'])) {
3010 $placeholder = $data['placeholder'];
3011 }
3012
3013 if ($errormsg === 'fd::' && isset($data['errormsg'])) {
3014 $errormsg = $data['errormsg'];
3015 }
3016
3017 if ($remap === 'fd::' && isset($data['remap'])) {
3018 $remap = $data['remap'];
3019 }
3020
3021 $data['name'] = $name;
3022 $data['tpl'] = $this->get_fld_marker_value_from_records($tpl, 'tpl', $field_record, $dd_record);
3023 $data['label'] = $this->get_fld_marker_value_from_records($label, 'label', $field_record, $dd_record);
3024 $data['rules'] = $this->get_fld_marker_value_from_records($rules, 'rules', $field_record, $dd_record);
3025 $data['class'] = $this->get_fld_marker_value_from_records($class, 'class', $field_record, $dd_record);
3026 $data['tooltip'] = $this->get_fld_marker_value_from_records($tooltip, 'tooltip', $field_record, $dd_record);
3027 $data['placeholder'] = $this->get_fld_marker_value_from_records($placeholder, 'placeholder', $field_record, $dd_record);
3028 $data['errormsg'] = $this->get_fld_marker_value_from_records($errormsg, 'errormsg', $field_record, $dd_record);
3029 $data['remap'] = $this->get_fld_marker_value_from_records($remap, 'remap', $field_record, $dd_record);
3030
3031 $fld = array(
3032 'name' => $name,
3033 'data' => $data,
3034 'options' => $options,
3035 'label' => $data['label'],
3036 'tpl' => $data['tpl'],
3037 'rules' => $data['rules'],
3038 'remap' => $data['remap'],
3039 'value' => '',
3040 'origin' => '',
3041 'errormsg' => $data['errormsg'],
3042 'error' => 0,
3043 'changed' => 0,
3044 'verify' => 0,
3045 'dd' => $field_source,
3046 );
3047
3048 $this->_flds[$name] = $fld;
3049 $this->touch_request_state();
3050 }
3051
3065 private function process_array($input) {
3066 if (!is_array($input)) {
3067 if ($input) {
3068 $data_first = substr((string) $input, 0, 4);
3069
3070 if ($data_first === 'sql:') {
3071 $input = $this->sql_to_array($input);
3072 } else {
3073 $input = $this->url_to_array($input);
3074 }
3075 } else {
3076 $input = array();
3077 }
3078 }
3079
3080 return is_array($input) ? $input : array();
3081 }
3082
3096 private function merge_arrays($primary, $secondary) {
3097 if (!is_array($primary)) {
3098 $primary = array();
3099 }
3100
3101 if (!is_array($secondary)) {
3102 $secondary = array();
3103 }
3104
3105 foreach ($secondary as $key => $value) {
3106 if (!array_key_exists($key, $primary)) {
3107 $primary[$key] = $value;
3108 }
3109 }
3110
3111 return $primary;
3112 }
3113
3125 private function touch_request_state() {
3126 $this->_form_validate = -1;
3127 $this->_fld_changes = -1;
3128 $this->_form_submit = -1;
3129 }
3130
3131
3132
3146 public function check_fld_data($submit, $fld) {
3147 if (($fld['verify'] ?? 0)) {
3148 return $fld;
3149 }
3150
3151 $errormsg = 'Bitte Eingabe pruefen';
3152 $name = $fld['name'] ?? '';
3153 $fld_rules = $fld['rules'] ?? '';
3154
3155 if (!$submit) {
3156 $resolved = $this->resolve_fld_val($name, '', '', false, true);
3157
3158 $fld['value'] = $resolved['value'];
3159 $fld['origin'] = $resolved['origin'];
3160 $fld['changed'] = 0;
3161 $fld['error'] = 0;
3162 $fld['verify'] = 1;
3163
3164 return $fld;
3165 }
3166
3167 $old = $this->resolve_fld_val($name, '', '', false, true);
3168 $old_value = $old['value'];
3169
3170 $resolved = $this->resolve_fld_val($name, '', $fld_rules, true, false);
3171 $value = $resolved['value'];
3172 $ok = (bool) $resolved['ok'];
3173 $validation = is_array($resolved['validation'] ?? null)
3174 ? $resolved['validation']
3175 : array();
3176
3177 $fld['origin'] = $resolved['origin'];
3178 $fld['error'] = $ok ? 0 : 1;
3179 $fld['value'] = $value;
3180 $fld['validation'] = $validation;
3181
3182 if (!empty($fld['data']['errormsg'])) {
3183 $errormsg = $fld['data']['errormsg'];
3184 } elseif (!$ok && !empty($validation['message'])) {
3185 $errormsg = (string)$validation['message'];
3186 }
3187
3188 if ($fld['error']) {
3189 $this->add_fld_error($name, $errormsg);
3190 $fld['data']['errormsg'] = $errormsg;
3191 }
3192
3193 $value_compare = $value;
3194
3195 if (is_array($value_compare)) {
3196 $values = '';
3197
3198 foreach ($value_compare as $keyval) {
3199 if ($values !== '') {
3200 $values .= ',';
3201 }
3202
3203 $values .= $keyval;
3204 }
3205
3206 $value_compare = $values;
3207 }
3208
3209 $change = $this->_fld_change_state;
3210
3211 if ($value_compare != $old_value || $change == '*') {
3212 $fld['changed'] = 1;
3213 } else {
3214 $fld['changed'] = 0;
3215 }
3216
3217 if (!$fld['error'] && $fld['changed']) {
3218 $this->_post[$name] = $value_compare;
3219 }
3220
3221 $fld['verify'] = 1;
3222
3223 return $fld;
3224 }
3225
3226
3239 public function check_flds_data($submit) {
3240 if ($this->_form_validate == 1) {
3241 return;
3242 }
3243
3244 $this->_post = array();
3245 $this->_errors = array();
3246 $this->_warnings = array();
3247
3248 foreach ($this->_flds as $no => $fld) {
3249 $fld = $this->check_fld_data($submit, $fld);
3250 $this->_flds[$no] = $fld;
3251 }
3252
3253 $this->_form_validate = 1;
3254 $this->_fld_changes = -1;
3255 }
3256
3272 private function evaluate_request() {
3273 if ($this->_form_validate == 1 && $this->_form_submit > -1) {
3274 dbx()->debug("dbxForm submit cached: fid=({$this->_fid}) submit=({$this->_form_submit})");
3275 return;
3276 }
3277
3278 $submit = 0;
3279 $fld = $this->secure_fld_name();
3280 $secure = $this->secure_token($fld);
3281 $posted = '';
3282 $hasPost = 0;
3283 $match = 0;
3284
3285 if ($fld !== '' && isset($_POST[$fld])) {
3286 $posted = (string)$_POST[$fld];
3287 $hasPost = 1;
3288
3289 if ($secure !== '' && hash_equals($secure, $posted)) {
3290 $submit = 1;
3291 $match = 1;
3292 }
3293 }
3294
3295 dbx()->debug(
3296 "dbxForm submit evaluate: fid=({$this->_fid}) field=($fld) post=($hasPost) match=($match) submit=($submit)"
3297 );
3298
3299 $this->_form_submit = $submit;
3300 $this->check_flds_data($submit);
3301
3302 if ($submit) {
3303 $this->rotate_secure_token($fld);
3304 } else {
3305 $this->sync_secure_field($fld, $secure);
3306 }
3307 }
3308
3317 public function get_form_msg($mode, $msg = '') {
3318 if (!$msg) {
3319 return '';
3320 }
3321
3322 $file = $this->_tpl_form_info;
3323 $tpl = '';
3324
3325 if ($mode == 'success') {
3326 $file = $this->_tpl_form_success;
3327 }
3328
3329 if ($mode == 'error') {
3330 $file = $this->_tpl_form_error;
3331 }
3332
3333 if ($mode == 'info') {
3334 $file = $this->_tpl_form_info;
3335 }
3336
3337 if ($mode == 'warning') {
3338 $file = $this->_tpl_form_warning;
3339 }
3340
3341 if ($file) {
3342 $tpl = $this->get_tpl($file);
3343 $tpl = str_replace('{msg}', $msg, $tpl);
3344 }
3345
3346 return str_replace('{class}', $mode, $tpl);
3347 }
3348
3357 public function get_fld_msg($mode, $msg = '') {
3358 $file = '';
3359 $tpl = '';
3360
3361 if ($mode == 'success') {
3362 $file = $this->_tpl_fld_success;
3363 }
3364
3365 if ($mode == 'error') {
3366 $file = $this->_tpl_fld_error;
3367 }
3368
3369 if ($mode == 'info') {
3370 $file = $this->_tpl_fld_info;
3371 }
3372
3373 if ($mode == 'warning') {
3374 $file = $this->_tpl_fld_warning;
3375 }
3376
3377 if ($file) {
3378 $tpl = $this->get_tpl($file);
3379 $tpl = str_replace('{msg}', $msg, $tpl);
3380 }
3381
3382 return $tpl;
3383 }
3384
3392 public function get_quartal($datum) {
3393 return '3/25';
3394 }
3395
3403 public function php_date_usr($value) {
3404 if (trim((string) $value) !== '') {
3405 $timestamp = strtotime((string) $value);
3406
3407 if ($timestamp !== false) {
3408 $value = date('d.m.Y', $timestamp);
3409 $value = substr($value, 0, 10);
3410 }
3411 }
3412
3413 return (string) $value;
3414 }
3415
3423 public function php_datetime_usr($value) {
3424 $raw = trim((string) $value);
3425
3426 if ($raw !== '') {
3427 $ms = '';
3428
3429 if (preg_match('/\.(\d+)$/', $raw, $match)) {
3430 $ms = '.' . $match[1];
3431 }
3432
3433 $timestamp = strtotime($raw);
3434
3435 if ($timestamp !== false) {
3436 if (preg_match('/^\d{2}:\d{2}(:\d{2})?(\.\d+)?$/', $raw)) {
3437 $value = date('H:i' . (substr_count($raw, ':') == 2 ? ':s' : ''), $timestamp) . $ms;
3438 } elseif (preg_match('/^\d{4}-\d{2}-\d{2}$|^\d{2}\.\d{2}\.\d{4}$/', $raw)) {
3439 $value = date('d.m.Y', $timestamp);
3440 } else {
3441 $value = date('d.m.Y H:i' . (substr_count($raw, ':') >= 2 ? ':s' : ''), $timestamp) . $ms;
3442 }
3443 }
3444 }
3445
3446 return (string) $value;
3447 }
3448
3456 public function php_date($value) {
3457 if ($value) {
3458 if ($value != 'today') {
3459 $timestamp = strtotime((string) $value);
3460 } else {
3461 $timestamp = time();
3462 }
3463
3464 $value = date('Y-m-d', $timestamp);
3465 }
3466
3467 return (string) $value;
3468 }
3469
3477 public function add_css($css = '') {
3478 if ($css) {
3479 $this->_css[] = $css;
3480 }
3481 }
3482
3493 public function forward_submit() {
3494 $this->evaluate_request();
3495 $submit = ($this->_form_submit == 1) ? 1 : 0;
3496 $submit = $this->callback('submit', $submit);
3497 $result = ($submit == 1) ? 1 : 0;
3498
3499 dbx()->debug("dbxForm submit result: fid=({$this->_fid}) result=($result)");
3500
3501 return $result;
3502 }
3503
3509 public function submit() {
3510 return $this->forward_submit();
3511 }
3512
3518 public function errors() {
3519 $this->evaluate_request();
3520
3521 if ($this->_general_error > '') {
3522 $this->_errors['general'] = 1;
3523 }
3524
3525 return count($this->_errors);
3526 }
3527
3533 public function warnings() {
3534 $this->evaluate_request();
3535 return count($this->_warnings);
3536 }
3537
3545 public function changed($changed = 0) {
3546 $this->evaluate_request();
3547
3548 if ($this->_form_submit != 1) {
3549 $this->_fld_changes = 0;
3550 return 0;
3551 }
3552
3553 if ($this->_fld_changes != -1) {
3554 return $this->_fld_changes;
3555 }
3556
3557 foreach ($this->_flds as $fld) {
3558 $changed += (int) ($fld['changed'] ?? 0);
3559 }
3560
3561 $this->_fld_changes = $changed;
3562
3563 return $changed;
3564 }
3565
3571 public function get_rid() {
3572 $empty = array();
3573 $rid = dbx()->get_modul_var('rid', -1, 'int');
3574 $dbx_view = $this->_dbx_view;
3575 $dbx_modul = $this->_dbx_modul;
3576
3577 if ($dbx_view) {
3578 $viewsys = dbx()->get_session_var($dbx_view, $empty, 'view-sys', $dbx_modul);
3579
3580 if (isset($viewsys['value'])) {
3581 $rid = $viewsys['value'];
3582 }
3583 }
3584
3585 $this->_rid = (int) $rid;
3586 $this->set_state_value('rid', (int) $rid);
3587
3588 return (int) $rid;
3589 }
3590
3598 public function view_sync($rid) {
3599 $empty = array();
3600 $dbx_view = $this->_dbx_view;
3601 $dbx_modul = $this->_dbx_modul;
3602
3603 if ($dbx_view) {
3604 $viewsys = dbx()->get_session_var($dbx_view, $empty, 'view-sys', $dbx_modul);
3605 $viewsys['value'] = $rid;
3606 dbx()->set_session_var($dbx_view, $viewsys, 'view-sys', $dbx_modul);
3607 }
3608 }
3609
3619 public function wait() {
3620 $wait = 0;
3621 $tpl = $this->_tpl;
3622
3623 if (strpos((string) $tpl, '_wait') !== false) {
3624 $wait = 1;
3625 }
3626
3627 return $wait;
3628 }
3629
3643 public function set_post($key, $val) {
3644 if ($key != 'form-dd-field') {
3645 $this->_post[$key] = $val;
3646 }
3647
3648 $this->_fld_changes = -1;
3649 }
3650
3676 public function save_post($dd, $rid, $pv = '', $reread = 1) {
3677 $ok = 0;
3678
3679 if ($rid === 'new') {
3680 $rid = 0;
3681 }
3682
3683 if (is_array($pv)) {
3684 foreach ($pv as $key => $value) {
3685 $this->_post[$key] = $value;
3686 $this->_data[$key] = $value;
3687 }
3688 }
3689
3690 if ($rid) {
3691 $this->_rid = (int) $rid;
3692 }
3693
3694 $rid = (int) $this->_rid;
3695 $post = $this->_post;
3696
3697 $this->oDB->_fld_id = $this->_fld_id;
3698 $ok = $this->oDB->save($dd, $post, $rid);
3699
3700 if (!$ok) {
3701 dbx()->debug("DB-Error=(" . $this->oDB->_error . ")\nQuery=(" . $this->oDB->_query . ")");
3702 $saveError = $this->get_fd_message('save_error');
3703 if ($saveError !== '') {
3704 $this->_msg_error = $saveError;
3705 }
3706 $this->_general_error = $this->_msg_error;
3707 $this->set_form_saved(false);
3708 return $ok;
3709 }
3710
3711 $saveSuccess = $this->get_fd_message('save_success');
3712 if ($saveSuccess !== '') {
3713 $this->_msg_success = $saveSuccess;
3714 }
3715
3716 if (!$rid) {
3717 $rid = (int) $this->oDB->_insert_id;
3718 $this->_rid = $rid;
3719 }
3720
3721 if ($rid > 0) {
3722 $this->set_state_value('rid', $rid);
3723 }
3724
3725 if ($rid && $ok && $reread) {
3726 $new = $this->oDB->select1($dd, $rid);
3727
3728 if (is_array($new)) {
3729 foreach ($new as $key => $value) {
3730 $this->_data[$key] = $value;
3731 }
3732
3733 $this->_reload_run = 1;
3734
3735 foreach ($this->_flds as $no => $fld) {
3736 $key = $fld['name'] ?? '';
3737
3738 if ($key !== '' && array_key_exists($key, $this->_data)) {
3739 $this->_flds[$no]['value'] = $this->_data[$key];
3740 $this->_flds[$no]['origin'] = 'reload';
3741 $this->_flds[$no]['verify'] = 1;
3742 $this->_flds[$no]['error'] = 0;
3743 }
3744 }
3745 }
3746 }
3747
3748 $this->set_form_saved(true);
3749 $this->set_form_complete(true);
3750 $this->set_form_valid(true);
3751 $this->bump_form_version();
3752 $this->store_workflow_state();
3753
3754 dbx()->debug("#DD FORM-save# Tab=($dd) rid=($rid)");
3755
3756 return $ok;
3757 }
3758
3766 public function next_i($add = 1) {
3767 $i = $this->_next_i;
3768
3769 if (!$i) {
3770 $i = dbx()->next_id($add);
3771 $this->_next_i = $i;
3772 }
3773
3774 $this->add_rep('i', $i);
3775
3776 return $i;
3777 }
3778
3787 public function set_fld_val($fld_name, $fld_val) {
3788 $this->_data[$fld_name] = $fld_val;
3789 $this->_sys[$fld_name] = $fld_val;
3790
3791 if (isset($this->_flds[$fld_name])) {
3792 $this->_flds[$fld_name]['value'] = $fld_val;
3793 $this->_flds[$fld_name]['origin'] = 'set';
3794 $this->_flds[$fld_name]['verify'] = 1;
3795 }
3796 }
3797
3804 protected function add_default_search_rep(array $overrides = array()) {
3805 $data = $this->prepare_search_tpl_data(array_merge(array(
3806 'extra_attrs' => 'data-dbx="grid-search"',
3807 ), $overrides));
3808 $template = trim((string)($data['label'] ?? '')) === ''
3809 ? 'dbx|search'
3810 : 'dbx|search-label';
3811 $this->add_rep('dbx_search', $this->get_tpl($template, $data, 'htm', (int)$data['i']));
3812 }
3813
3814 private function is_search_field_tpl($tpl): bool {
3815 $tpl = strtolower(trim((string) $tpl));
3816
3817 return $tpl === 'dbx|search' || $tpl === 'search' || substr($tpl, -7) === '|search';
3818 }
3819
3825 private function prepare_search_tpl_data(
3826 array $data,
3827 ?string $name = null,
3828 ?string $value = null,
3829 ?int $i = null
3830 ): array {
3831 $data = dbx()->search_defaults($data);
3832 $data['name'] = $name !== null ? $name : (string)($data['name'] ?? '');
3833 $data['value'] = $value !== null ? $value : (string)($data['value'] ?? '');
3834 $data['i'] = $i !== null ? $i : (int)($data['i'] ?? $this->_next_i);
3835 $data['label'] = (string)($data['label'] ?? '');
3836 $data['class'] = (string)($data['class'] ?? '');
3837 $data['style'] = (string)($data['style'] ?? '');
3838 if (trim((string)($data['tooltip'] ?? '')) === '') {
3839 $data['tooltip'] = (string)($data['title'] ?? 'Suchen');
3840 }
3841 return $data;
3842 }
3843
3852 private function prepare_report_search_fld($name, &$tpl, array &$data): void {
3853 if ($name !== 'dbx_rwhere') {
3854 return;
3855 }
3856
3857 $tpl = 'dbx|search';
3858 $defaults = dbx()->search_defaults();
3859
3860 $data['label'] = '';
3861 $data['placeholder'] = $defaults['placeholder'];
3862 $data['title'] = trim((string) ($data['tooltip'] ?? '')) !== ''
3863 ? (string) $data['tooltip']
3864 : $defaults['title'];
3865 $data['input_class'] = $defaults['input_class'];
3866 $data['wrap_class'] = $defaults['wrap_class'];
3867 $data['data_role'] = $defaults['data_role'];
3868 $data['extra_attrs'] = $defaults['extra_attrs'];
3869 }
3870
3886 public function create_fld($fld, $i = 0) {
3887 if (!$i) {
3888 $i = $this->_next_i;
3889 }
3890
3891 if (!($fld['verify'] ?? 0)) {
3892 $fld = $this->check_fld_data($this->submit(), $fld);
3893 }
3894
3895 $tpl = $fld['tpl'] ?? '';
3896 $data = is_array($fld['data'] ?? null) ? $fld['data'] : array();
3897 $options = is_array($fld['options'] ?? null) ? $fld['options'] : array();
3898 $fld_value = $fld['value'] ?? '';
3899 $error = (int) ($fld['error'] ?? 0);
3900 $name = $fld['name'] ?? '';
3901
3902 if ($error) {
3903 $data['class'] = trim(($data['class'] ?? '') . ' fld-error');
3904
3905 if (!isset($data['errormsg']) || $data['errormsg'] === '') {
3906 $data['errormsg'] = 'Eingabe bitte prüfen !';
3907 }
3908 } else {
3909 $data['errormsg'] = '';
3910 }
3911
3912 if (!is_array($fld_value)) {
3913 $fld_value = htmlspecialchars((string) $fld_value, ENT_QUOTES, 'UTF-8');
3914
3915 if (!isset($data['checked']) && $fld_value !== '' && $fld_value !== '0') {
3916 $data['checked'] = 'checked';
3917 }
3918 }
3919 if (!isset($data['checked'])) {
3920 $data['checked'] = '';
3921 }
3922
3923 // dbxForm validates submitted data itself; native HTML5 required would
3924 // mark empty fields invalid before submit and can block the request.
3925 $required = '';
3926
3927 $data['required'] = $required;
3928 $data['class'] = $data['class'] ?? '';
3929 $data['style'] = $data['style'] ?? '';
3930 $data['tooltip'] = $data['tooltip'] ?? '';
3931 $data['errormsg'] = $data['errormsg'] ?? '';
3932
3933 foreach (array('placeholder') as $htmlKey) {
3934 if (isset($data[$htmlKey]) && !is_array($data[$htmlKey])) {
3935 $data[$htmlKey] = htmlspecialchars((string)$data[$htmlKey], ENT_QUOTES, 'UTF-8');
3936 }
3937 }
3938
3939 foreach (array('tooltip', 'errormsg') as $htmlAttributeKey) {
3940 if (isset($data[$htmlAttributeKey]) && !is_array($data[$htmlAttributeKey])) {
3941 $data[$htmlAttributeKey] = htmlspecialchars(str_replace('"', "'", (string)$data[$htmlAttributeKey]), ENT_QUOTES, 'UTF-8');
3942 }
3943 }
3944
3945 $this->prepare_report_search_fld($name, $tpl, $data);
3946
3947 if ($this->is_search_field_tpl($tpl)) {
3948 $data = $this->prepare_search_tpl_data(
3949 $data,
3950 (string)$name,
3951 is_array($fld_value) ? '' : (string)$fld_value,
3952 (int)$i
3953 );
3954 $tpl = trim((string)($data['label'] ?? '')) === ''
3955 ? 'dbx|search'
3956 : 'dbx|search-label';
3957 }
3958 $tpl = $this->get_tpl($tpl, $data, 'htm', $i);
3959
3960 if (is_array($options)) {
3961 $xoptions = '';
3962 $oid = $name . '_options';
3963 $options_vals = $fld_value;
3964
3965 if (!is_array($options_vals)) {
3966 $options_vals = explode(',', (string) $options_vals);
3967 }
3968
3969 $selected_lookup = array();
3970 foreach ($options_vals as $keyval) {
3971 $selected_lookup[(string) $keyval] = true;
3972 }
3973
3974 foreach ($options as $key => $description) {
3975 $selected = isset($selected_lookup[(string) $key]) ? 'selected' : '';
3976
3977 $xoptions .= '<option value="' . htmlspecialchars((string) $key, ENT_QUOTES, 'UTF-8') . '" ' . $selected . '>' .
3978 htmlspecialchars((string) $description, ENT_QUOTES, 'UTF-8') .
3979 "</option>\n";
3980 }
3981
3982 $tpl = str_replace('{' . $oid . '}', $xoptions, $tpl);
3983 }
3984
3985 $tpl = $this->oTPL->replaces($tpl, $this->_replaces);
3986
3987 if (!is_array($fld_value)) {
3988 $tpl = str_replace('{src}', $fld_value, $tpl);
3989 $tpl = str_replace('{value}', $fld_value, $tpl);
3990 }
3991
3992 $tpl = dbx()->norep($tpl, $i);
3993
3994 $field_markers = $this->get_field_editor_markers();
3995
3996 if ($field_markers !== '') {
3997 $tpl = $field_markers . $tpl;
3998 }
3999
4000 return $tpl;
4001 }
4002
4022 public function merge_fld_data($content, $i = 0) {
4023 $form = '';
4024 $editor = dbx()->get_system_var('dbx_editor', 0, 'int');
4025
4026 if (!$i && !$editor) {
4027 $i = $this->_next_i;
4028 }
4029
4030 foreach ($this->_flds as $fld_name => $fld) {
4031 $slot = $fld_name;
4032
4033 if (!empty($fld['remap'])) {
4034 $slot = $fld['remap'];
4035 }
4036
4037 $fid = '{obj:' . $slot . '}';
4038 $fld_content = $this->create_fld($fld, $i);
4039
4040 if (strpos($content, $fid) !== false) {
4041 $content = str_replace($fid, $fld_content, $content);
4042 } else {
4043 $form .= $fld_content . "\n";
4044 }
4045 }
4046
4047 if (strpos($content, '[dbx:form]') !== false) {
4048 $content = str_replace('[dbx:form]', $form, $content);
4049 } else {
4050 $content = str_replace('</form>', $form . '</form>', $content);
4051 }
4052
4053 return $content;
4054 }
4055
4065 public function check_try_count($submit, $errors, $allways = 1) {
4066 $content = '';
4067 $clear = 0;
4068 $reset = $this->_try_reset;
4069 $max = $this->_try_max;
4070 $msg = $this->_try_msg;
4071 $now = dbx()->timestamp();
4072 $self = dbx()->get_self_url();
4073 $ip = (string)($_SERVER['REMOTE_ADDR'] ?? '');
4074 $sys = isset($this->_sys['_try_sys']) ? $this->_sys['_try_sys'] : array();
4075
4076 if (($sys['dbx_try_ip'] ?? '') !== $ip) {
4077 $sys = array(
4078 'dbx_try_ip' => $ip,
4079 );
4080 }
4081
4082 $sys['dbx_try_count'] = $sys['dbx_try_count'] ?? 0;
4083 $sys['dbx_run_count'] = $sys['dbx_run_count'] ?? 0;
4084 $sys['dbx_try_lock'] = $sys['dbx_try_lock'] ?? 0;
4085 $sys['dbx_try_ip'] = $ip;
4086
4087 $countReset = max(0, (int) $this->_try_count_reset);
4088 $lastTry = (float) ($sys['dbx_try_last'] ?? 0);
4089
4090 if ($countReset > 0 && $lastTry > 0 && ($now - $lastTry) > $countReset) {
4091 unset(
4092 $sys['dbx_try_first'],
4093 $sys['dbx_try_last'],
4094 $sys['dbx_try_stop'],
4095 $sys['dbx_try_run']
4096 );
4097 $sys['dbx_try_count'] = 0;
4098 $sys['dbx_try_lock'] = 0;
4099 }
4100
4101 $sys['dbx_run_count']++;
4102
4103 if ($submit) {
4104 if ($errors) {
4105 $sys['dbx_try_first'] = $sys['dbx_try_first'] ?? $now;
4106 $sys['dbx_try_last'] = $now;
4107 $sys['dbx_try_count']++;
4108
4109 if ($sys['dbx_try_count'] >= $max) {
4110 if (!isset($sys['dbx_try_stop'])) {
4111 $sys['dbx_try_lock']++;
4112
4113 for ($i = 1; $i < $sys['dbx_try_lock']; $i++) {
4114 $reset = (int) ($reset * 2);
4115 }
4116
4117 $sys['dbx_try_stop'] = $now;
4118 $sys['dbx_try_run'] = dbx()->timestamp($reset);
4119 }
4120 }
4121
4122 if (isset($sys['dbx_try_run']) && $now > $sys['dbx_try_run']) {
4123 $clear = 1;
4124 }
4125 } else {
4126 $clear = 1;
4127 }
4128 }
4129
4130 if (($submit || $allways) && !$clear) {
4131 if (($sys['dbx_try_count'] ?? 0) >= $max && ($sys['dbx_try_run'] ?? 0) > 0) {
4132 $diff = dbx()->time_diff($now, $sys['dbx_try_run']);
4133
4134 if ($diff > 0) {
4135 $data = array(
4136 'sec' => (int) $diff,
4137 'self' => $self,
4138 'try_count' => $sys['dbx_try_count'],
4139 'run_count' => $sys['dbx_run_count'],
4140 );
4141 $data['msg'] = $this->oTPL->replaces((string) $msg, $data);
4142
4143 $content = $this->get_tpl($this->_tpl_max_try, $data);
4144 } else {
4145 $clear = 1;
4146 }
4147 }
4148 }
4149
4150 if ($clear) {
4151 unset($sys['dbx_try_stop'], $sys['dbx_try_run']);
4152 $sys['dbx_try_count'] = 0;
4153 }
4154
4155 $this->_sys['_try_sys'] = $sys;
4156
4157 return $content;
4158 }
4159
4177 public function forward_run() {
4178 $content = '';
4179 $editor = dbx()->get_system_var('dbx_editor', 0, 'int');
4180
4181 $this->evaluate_request();
4182
4183 $submit = $this->submit();
4184 $errors = $this->errors();
4185
4186 $tryContent = $this->check_try_count($submit, $errors, 1);
4187
4188 $i = $this->next_i();
4189 $this->_next_i = $i;
4190
4191 $this->buildModuleBarObj();
4192 $this->prepareFormFrameReplaces($i);
4193
4194 $replaces = $this->_replaces;
4195 $replaces['form-id'] = $this->_dbx_modul . '-' . $this->_fid;
4196 foreach (array('form_shell_class' => '', 'form_class' => '', 'form_attrs' => '') as $shellKey => $shellDefault) {
4197 if (!isset($replaces[$shellKey]) || (string)$replaces[$shellKey] === '') {
4198 $replaces[$shellKey] = $shellDefault;
4199 }
4200 }
4201 foreach (array('shell_panel_class' => '', 'shell_panel_attrs' => '', 'shell_body_class' => '') as $shellKey => $shellDefault) {
4202 if (!isset($replaces[$shellKey]) || (string)$replaces[$shellKey] === '') {
4203 $replaces[$shellKey] = $shellDefault;
4204 }
4205 }
4206
4207 // Ein explizites "modul|template" bleibt erhalten. Das erlaubt
4208 // gemeinsam genutzte dbxForm-Templates auch dann, wenn das aufrufende
4209 // Modul ein anderes ist.
4210 $templateRef = strpos((string)$this->_tpl, '|') !== false
4211 ? (string)$this->_tpl
4212 : 'modul|' . $this->_tpl;
4213 $content = $this->get_tpl($templateRef, $replaces, 'htm', $i);
4214 $content = $this->merge_tpl_data($content, $i);
4215 $content = $this->merge_fld_data($content, $i);
4216 $content = $this->merge_obj($content, $i);
4218
4219 $form_msg = '';
4220
4221 if ($tryContent !== '') {
4222 $form_msg = $tryContent;
4223 } else {
4224 if ($submit) {
4225 if ($this->errors()) {
4226 $form_msg = $this->get_form_msg('error', $this->_msg_error);
4227 } elseif ($this->warnings()) {
4228 $form_msg = $this->get_form_msg('warning', $this->_msg_warning);
4229 } else {
4230 $form_msg = $this->get_form_msg('success', $this->_msg_success);
4231 }
4232 } else {
4233 $form_msg = $this->get_form_msg('info', $this->_msg_info);
4234 }
4235 }
4236
4237 $content = str_replace('{obj:form_msg}', $form_msg, $content);
4238
4239 $norep_ids = '';
4240
4241 if (is_array($this->_js)) {
4242 foreach ($this->_js as $javascript) {
4243 $javascript = str_replace('{i}', $this->_next_i, $javascript);
4244 $norep = '<script>' . $javascript . '</script> ';
4245 $norep_ids .= dbx()->norep($norep, $i);
4246 }
4247 }
4248
4249 $content = str_replace('[dbx:js]', $norep_ids, $content);
4250
4251 if (!$editor && $content && $i) {
4252 $content = str_replace('{i}', $i, $content);
4253 }
4254
4255 $content = $this->callback('run', $content);
4256
4257 $this->store_sysdata();
4258 $this->store_workflow_state();
4259
4260 return $this->add_editor_markers($content);
4261 }
4262
4271 protected function resolve_fd_dd_editor_file(string $source): string {
4272 $source = trim($source);
4273
4274 if ($source === '') {
4275 return '';
4276 }
4277
4278 $mod = 'dd';
4279 $name = $source;
4280
4281 foreach (array('cfg:', 'def:', 'mod:', 'fd:', 'dd:') as $prefix) {
4282 if (strpos($source, $prefix) === 0) {
4283 $mod = substr($prefix, 0, -1);
4284 $name = substr($source, strlen($prefix));
4285 break;
4286 }
4287 }
4288
4289 if ($mod === 'dd' && strpos($source, '|') !== false) {
4290 $mod = 'fd';
4291 $name = $source;
4292 }
4293
4294 if ($mod === 'dd') {
4295 if (method_exists($this->oDB, 'get_dd_file')) {
4296 return (string) $this->oDB->get_dd_file($name);
4297 }
4298
4299 $modul = $this->_dbx_modul ? $this->_dbx_modul : dbx()->get_system_var('dbx_activ_modul', 'dbx');
4300
4301 return dbx()->get_base_dir() . "dbx/modules/$modul/dd/$name.dd.php";
4302 }
4303
4304 $dd_file = '';
4305
4306 switch ($mod) {
4307 case 'cfg':
4308 $dd_file = dbx()->get_base_dir() . "dbx/modules/$name/cfg/config.dd.php";
4309 break;
4310
4311 case 'def':
4312 $dd_file = dbx()->get_base_dir() . "dbx/modules/$name/dd/$name.dd.php";
4313 break;
4314
4315 case 'mod':
4316 $modul = dbx()->get_system_var('dbx_activ_modul', 'dbx');
4317 $dd_file = dbx()->get_base_dir() . "dbx/modules/$modul/dd/$name.dd.php";
4318 break;
4319
4320 case 'fd':
4321 $fd_modul = $this->_dbx_modul ? $this->_dbx_modul : dbx()->get_system_var('dbx_activ_modul', 'dbx');
4322 $fd_name = $name;
4323
4324 if (strpos($name, '|') !== false) {
4325 $parts = explode('|', $name, 2);
4326 $fd_modul = trim($parts[0]);
4327 $fd_name = trim($parts[1]);
4328 }
4329
4330 if ($fd_modul === '') {
4331 $fd_modul = dbx()->get_system_var('dbx_activ_modul', 'dbx');
4332 }
4333
4334 $dd_file = function_exists('dbx_lng_resolve_file')
4336 dbx()->get_base_dir() . "dbx/modules/$fd_modul/fd/",
4337 $fd_name,
4338 'fd.php',
4339 $this->_dbx_lng,
4340 true
4341 )
4342 : dbx()->get_base_dir() . "dbx/modules/$fd_modul/fd/$fd_name.fd.php";
4343 if ($dd_file === '' || !is_file($dd_file)) {
4344 $dd_file = dbx()->get_base_dir() . "dbx/modules/$fd_modul/fd/$fd_name.fd.php";
4345 }
4346 break;
4347 }
4348
4349 if ($dd_file === '') {
4350 return '';
4351 }
4352
4353 $real_file = realpath(dbx()->os_path($dd_file));
4354
4355 return $real_file ? $real_file : $dd_file;
4356 }
4357
4363 protected function get_field_editor_markers(): string {
4364 $markers = '';
4365
4366 foreach (array('fd' => $this->_fd, 'dd' => $this->_dd) as $kind => $source) {
4368
4369 if ($file === '') {
4370 continue;
4371 }
4372
4373 $marker = dbx()->editor_marker($kind, $file);
4374
4375 if ($marker !== '' && strpos($markers, trim($marker)) === false) {
4376 $markers .= $marker;
4377 }
4378 }
4379
4380 return $markers;
4381 }
4382
4391 public function init($fid, $tpl = '') {
4392 $this->forward_init($fid, $tpl);
4393 }
4394
4400 public function run() {
4401 return $this->forward_run();
4402 }
4403
4404 /* =====================================================
4405 * WORKFLOW / REMEMBER STATE
4406 * ===================================================== */
4407
4426 public function set_workflow_scope(string $scope) {
4427 $this->_workflow_scope = trim($scope);
4428 $this->load_workflow_state();
4429 }
4430
4436 public function get_workflow_scope(): string {
4437 return $this->_workflow_scope;
4438 }
4439
4447 private function normalize_state_token(string $token): string {
4448 $token = trim($token);
4449
4450 if ($token === '') {
4451 return 'undef';
4452 }
4453
4454 return preg_replace('/[^a-zA-Z0-9._-]+/', '_', $token);
4455 }
4456
4472 public function get_workflow_state_key(): string {
4473 $modul = $this->normalize_state_token((string) $this->_dbx_modul);
4474 $scope = $this->normalize_state_token((string) $this->_workflow_scope);
4475 $fid = $this->normalize_state_token((string) $this->_fid);
4476
4477 return 'dbx.form.state.' . $modul . '.' . $scope . '.' . $fid;
4478 }
4479
4485 private function get_default_workflow_state(): array {
4486 return array(
4487 'rid' => 0,
4488 'draft_id' => '',
4489 'is_complete' => 0,
4490 'is_valid' => 0,
4491 'is_locked' => 0,
4492 'is_saved' => 0,
4493 'version' => 0,
4494 'depends_on' => array(),
4495 'depends_version' => array(),
4496 );
4497 }
4498
4504 public function load_workflow_state(): array {
4505 $key = $this->get_workflow_state_key();
4506 $state = dbx()->get_remember_var($key, $this->get_default_workflow_state(), 'dbx');
4507
4508 if (!is_array($state)) {
4509 $state = $this->get_default_workflow_state();
4510 }
4511
4512 $this->_workflow_state = array_merge($this->get_default_workflow_state(), $state);
4513
4514 if ((int) ($this->_workflow_state['rid'] ?? 0) > 0 && !$this->_rid) {
4515 $this->_rid = (int) $this->_workflow_state['rid'];
4516 }
4517
4518 return $this->_workflow_state;
4519 }
4520
4526 public function store_workflow_state() {
4527 $key = $this->get_workflow_state_key();
4528 dbx()->set_remember_var($key, $this->_workflow_state, 'dbx');
4529 }
4530
4536 public function clear_workflow_state() {
4537 $this->_workflow_state = $this->get_default_workflow_state();
4538 $this->store_workflow_state();
4539 }
4540
4549 public function set_state_value(string $key, $value) {
4550 $this->_workflow_state[$key] = $value;
4551 $this->store_workflow_state();
4552 }
4553
4562 public function get_state_value(string $key, $default = null) {
4563 if (!array_key_exists($key, $this->_workflow_state)) {
4564 $this->load_workflow_state();
4565 }
4566
4567 return $this->_workflow_state[$key] ?? $default;
4568 }
4569
4583 public function get_draft_id(bool $createIfMissing = false): string {
4584 $draftId = (string) $this->get_state_value('draft_id', '');
4585
4586 if ($draftId === '' && $createIfMissing) {
4587 $draftId = $this->create_draft_id();
4588 $this->set_state_value('draft_id', $draftId);
4589 }
4590
4591 return $draftId;
4592 }
4593
4601 public function set_draft_id(string $draftId) {
4602 $this->set_state_value('draft_id', $draftId);
4603 }
4604
4610 private function create_draft_id(): string {
4611 return 'dft_' . md5($this->_dbx_modul . '|' . $this->_fid . '|' . microtime(true) . '|' . mt_rand());
4612 }
4613
4621 public function set_form_valid(bool $state) {
4622 $this->set_state_value('is_valid', $state ? 1 : 0);
4623 }
4624
4630 public function is_form_valid(): bool {
4631 return ((int) $this->get_state_value('is_valid', 0) === 1);
4632 }
4633
4641 public function set_form_complete(bool $state) {
4642 $this->set_state_value('is_complete', $state ? 1 : 0);
4643 }
4644
4650 public function is_form_complete(): bool {
4651 return ((int) $this->get_state_value('is_complete', 0) === 1);
4652 }
4653
4661 public function set_form_locked(bool $state) {
4662 $this->set_state_value('is_locked', $state ? 1 : 0);
4663 }
4664
4670 public function is_form_locked(): bool {
4671 return ((int) $this->get_state_value('is_locked', 0) === 1);
4672 }
4673
4681 public function set_form_saved(bool $state) {
4682 $this->set_state_value('is_saved', $state ? 1 : 0);
4683 }
4684
4690 public function is_form_saved(): bool {
4691 return ((int) $this->get_state_value('is_saved', 0) === 1);
4692 }
4693
4699 public function get_form_version(): int {
4700 return (int) $this->get_state_value('version', 0);
4701 }
4702
4713 public function bump_form_version(): int {
4714 $version = $this->get_form_version() + 1;
4715 $this->set_state_value('version', $version);
4716 return $version;
4717 }
4718
4732 public function set_dependencies(array $formIds) {
4733 $deps = array();
4734
4735 foreach ($formIds as $formId) {
4736 $deps[] = $this->normalize_state_token((string) $formId);
4737 }
4738
4739 $this->set_state_value('depends_on', $deps);
4740 }
4741
4754 public function remember_current_dependencies(?array $formIds = null) {
4755 if ($formIds === null) {
4756 $formIds = $this->get_state_value('depends_on', array());
4757 }
4758
4759 $versions = array();
4760
4761 foreach ($formIds as $formId) {
4762 $state = $this->get_external_form_state((string) $formId);
4763 $versions[$formId] = (int) ($state['version'] ?? 0);
4764 }
4765
4766 $this->set_state_value('depends_version', $versions);
4767 }
4768
4779 public function dependencies_are_current(): bool {
4780 $dependsOn = $this->get_state_value('depends_on', array());
4781 $dependsVersion = $this->get_state_value('depends_version', array());
4782
4783 if (!is_array($dependsOn) || !$dependsOn) {
4784 return true;
4785 }
4786
4787 foreach ($dependsOn as $formId) {
4788 $state = $this->get_external_form_state((string) $formId);
4789 $currentVersion = (int) ($state['version'] ?? 0);
4790 $savedVersion = (int) ($dependsVersion[$formId] ?? -1);
4791
4792 if ($currentVersion !== $savedVersion) {
4793 return false;
4794 }
4795
4796 if ((int) ($state['is_valid'] ?? 0) !== 1) {
4797 return false;
4798 }
4799 }
4800
4801 return true;
4802 }
4803
4811 public function get_external_form_state(string $fid): array {
4812 $modul = $this->normalize_state_token((string) $this->_dbx_modul);
4813 $scope = $this->normalize_state_token((string) $this->_workflow_scope);
4814 $fid = $this->normalize_state_token($fid);
4815
4816 $key = 'dbx.form.state.' . $modul . '.' . $scope . '.' . $fid;
4817 $state = dbx()->get_remember_var($key, $this->get_default_workflow_state(), 'dbx');
4818
4819 return is_array($state) ? $state : $this->get_default_workflow_state();
4820 }
4821
4827 public function refresh_dependency_lock(): bool {
4828 $locked = !$this->dependencies_are_current();
4829 $this->set_form_locked($locked);
4830 return $locked;
4831 }
4832}
4833
4834?>
$table['server']
Definition .dd.php:6
$indexes[]
Definition .dd.php:167
Zentrale Laufzeit-API von dbXapp.
Definition dbxApi.php:39
$_ajax
AJAX-fähiges Formular.
$_reload_run
1 = Reload wurde ausgeführt
$_form_help_button
Help-Button des aktuellen Formulars fuer Templates ohne eigenen Help-Slot.
load_workflow_state()
Lädt den Remember-basierten Workflow-State.
$_tpl_form_info
Template für Info-Meldungen.
get_quartal($datum)
Platzhalter-Helfer.
get_activ_id($key='')
Liefert die aktive Remember-ID des aktuellen DD-Kontexts.
clear_workflow_state()
Löscht den Workflow-State.
is_form_complete()
Gibt zurück, ob das Formular als vollständig markiert ist.
$_tpl_form_error
Template für Fehlermeldungen.
dependencies_are_current()
Prüft, ob die gespeicherten Dependency-Versionen noch aktuell sind.
view_sync($rid)
Synchronisiert eine RID in den View-State.
$_dbx_action
Aktive DBX-Aktion.
resolve_init_callback_owner()
Ermittelt den aufrufenden Modul-/Service-Owner fuer Callback-Defaults.
add_fld($name, $tpl='fd::', $label='fd::', $rules='fd::', $class='fd::', $data='fd::', $options='fd::', $tooltip='fd::', $placeholder='fd::', $errormsg='fd::', $dd='fd::', $remap='fd::')
Fügt ein Feld zur Formular-Definition hinzu.
$_errors
Feld-/Formfehler.
create_fld($fld, $i=0)
Erzeugt HTML für ein Feld.
$_tpl_form_warning
Template für Warnmeldungen.
forward_submit()
Gibt zurück, ob das Formular abgesendet wurde.
update_error_fld($name, $msg)
Markiert ein Feld mit Fehlerstatus.
$_tpl_fld_error
Template für Feld-Fehler.
$_fld_id
Primärschlüsselfeld.
$_action
Formular-Action.
$_create_flds
1 = Felder sollen erzeugt werden
set_draft_id(string $draftId)
Setzt die Draft-ID explizit.
$_dbx_modul
Aktives Modul.
$_general_error
Allgemeiner Fehlertext.
set_dependencies(array $formIds)
Setzt die Abhängigkeiten dieses Formulars.
$_try_max
Maximale Fehlversuche.
load_sysdata()
Lädt Sysdata des Formulars.
php_date_usr($value)
Wandelt Datumswerte in deutsches Format um.
store_workflow_state()
Speichert den Workflow-State.
$_fid
Formular-ID.
$_msg_error
Standard-Fehlertext.
__construct($id='', $tpl='')
Initialisiert Validator, TPL und DB-Service.
add_js_code($javascript)
Fügt JS-Code hinzu.
get_state_value(string $key, $default=null)
Liest einen Wert aus dem Workflow-State.
set_msg_ok($msg)
Setzt die Formular-Erfolgsmeldung.
$oValidator
Zentraler Validator.
$_infos
Info-Meldungen.
get_draft_id(bool $createIfMissing=false)
Gibt die Draft-ID zurück, optional mit automatischer Erzeugung.
init($fid, $tpl='')
Öffentliche Init-Methode.
add_editor_markers(string $content)
Fuegt passende Editor-Marker um den gerenderten Formularinhalt.
set_msg_error($msg)
Setzt die Formular-Fehlermeldung.
$_dbx_lng
Aktive Sprache.
$_next_i
Laufende Formularinstanz-ID.
set_run_callback(string $callback)
defaultModuleBarReplaces()
$_reload_transform
Historischer Reload-Transform-Flag.
$_dbx_page
Aktive Seite.
next_i($add=1)
Liefert oder erzeugt die laufende Formularinstanz-ID.
php_date($value)
Wandelt Datumswerte in DB-Format um.
buildModuleBarFormActionsHtml()
$_msg_success
Standard-Erfolgstext.
$_warnings
Warnungen.
fast_response($response, $interpreter=0)
Sendet eine direkte Fast-Response.
$_editor_class_file
Modul-/Include-Class-Datei fuer den Frontend-Editor.
$_try_count_reset
Sekunden ohne Fehlversuch, nach denen Try-Zaehler und Sperrstufe neu beginnen.
wait()
Historischer Wait-Check.
$_fld_change_state
Feldmodus fuer die Changed-Logik: fld oder *.
$_msg_info
Standard-Infotext.
warnings()
Gibt die Anzahl der Warnungen zurück.
$_form_submit
-1 = unbekannt, 0 = nein, 1 = ja
add_norep($content)
Ersetzt norep-Platzhalter.
store_sysdata()
Speichert Sysdata des Formulars.
add_default_search_rep(array $overrides=array())
Standard-Suchfeld fuer Grid-Toolbars ({dbx_search}).
$_workflow_scope
Workflow-Scope für Remember-State.
is_form_saved()
Gibt zurück, ob das Formular bereits gespeichert wurde.
$_tpl_form_success
Template für Erfolgsmeldungen.
merge_fld_data($content, $i=0)
Fügt Felder in den Template-Inhalt ein.
set_form_help_enabled(bool $enabled=true)
Schaltet die automatische Formular-Hilfeleiste gezielt ein oder aus.
$_reload_record
1 = Datensatz nach Save neu lesen
obv_value($content, $id, $value)
Historische obv-Hilfe.
set_form_locked(bool $state)
Setzt den Lock-Status des Formulars.
$_dbx_work
Optionaler Work-Kontext.
$_data
Aktuelle Formulardaten / Record-Daten.
check_fld_data($submit, $fld)
Prüft ein einzelnes Feld und baut seinen Laufzustand auf.
add_action($obj, $tpl, $action='', $data='')
Fügt ein Objekt hinzu, dessen Action aus _action abgeleitet wird.
set_form_complete(bool $state)
Setzt den Vollständigkeitsstatus des Formulars.
$_css
CSS-Dateien oder Marker.
$_dbx_view
Optionaler View-Schlüssel.
ensureFormHelpBar(string $content)
Ergaenzt bei aelteren Formular-Templates ohne eigene Leiste eine kompakte Standardleiste.
$_js
JS-Code-Blöcke.
get_post_data($name, $default='', $rules='parameter')
Liest POST/GET-Daten mit Default-Rules parameter.
add_js_call($target, $function, $args='')
Fügt einen einfachen JS-Aufruf hinzu.
is_form_locked()
Gibt zurück, ob das Formular gesperrt ist.
merge_tpl_data($tpl, $i=0)
Führt grundlegende Form-Replaces in Templates aus.
$_editor_files
Dateien, die fuer den Frontend-Editor markiert werden.
add_fld_error($name, $msg='')
Fügt einen Feldfehler hinzu.
$_messages
Meldungen der aktuell geladenen FD.
set_workflow_scope(string $scope)
Setzt einen Workflow-Scope.
$_validation_results
Strukturierte Validator-Ergebnisse, indiziert nach Feldname.
set_init_callback(string $callback)
get_workflow_scope()
Gibt den Workflow-Scope zurück.
prepareFormFrameReplaces(int $i)
$_page_reset
Historischer Page-Reset-Flag.
format_fd_message(string $key, array $values=array(), string $default='')
Liefert eine FD-Meldung und ersetzt benannte Platzhalter.
set_editor_class_file(string $file)
Merkt sich die geladene Modul-/Include-Class fuer den Frontend-Editor.
is_form_valid()
Gibt zurück, ob das Formular als gültig markiert ist.
refresh_dependency_lock()
Markiert das Formular aufgrund veralteter Abhängigkeiten als gesperrt.
replaces($content, $replaces=null)
Wendet dbxForm-Replacements auf einen beliebigen Inhalt an.
$oTPL
Zentrale Template-Engine.
get_tpl($tpl, $data='', $type='htm', $i=0)
Wrapper auf dbxTPL->get_tpl().
$_form_help_enabled
1 = automatische Formularhilfe rendern; 0 = Hilfe kommt aus der umgebenden Bereichsleiste.
prepare_form_shell(array $options=array())
get_field_editor_markers()
Erzeugt FD-/DD-Editor-Marker direkt am Feld-Slot.
$_sys
Form-interne Zustände / Sysdata.
$_rid
Aktuelle Record-ID.
$_activ_id
Optional aktive ID.
$_dbx_modul_id
Aktive Modul-ID.
$_fld_changes
Gecachter Changed-Wert.
get_form_msg($mode, $msg='')
Gibt die Formular-Infobox zurück.
$_fd
Optionale FD-Felddefinitionsquelle.
check_flds_data($submit)
Prüft alle Felder des Formulars genau einmal pro Request-Zyklus.
$_form_validate
-1 = noch nicht ausgewertet, 1 = ausgewertet
add_fld_warning($name, $msg='')
Fügt eine Feldwarnung hinzu.
$_module_bar_form_actions
Optionen fuer Standard-Aktionen in der Modul-Bar.
remember_validation_result(string $name, array $result)
Merkt sich ein Validator-Ergebnis im aktuellen Formularlauf.
$_store_mode
Aktueller Sysdata-Speichermodus.
forward_init($fid, $tpl='')
Initialisiert den Form-Kontext.
add_module_bar( $title, $icon='bi-grid', $subtitle='', $replace=false)
Setzt die einheitliche Modul-/Formularleiste.
$_tpl
Template-Name.
attachAdminHelpButton($topic='')
$_dbx_design
Aktives Design.
forward_run()
Führt die Ausgabephase aus.
submit()
Öffentliche Submit-Abfrage.
get_validation_result(string $name='')
Liefert das strukturierte Validator-Ergebnis eines Feldes.
$oDB
Zentraler DB-/DD-Service.
set_post($key, $val)
Setzt einen _post-Wert manuell.
get_fld_id($fld)
Extrahiert den Feldnamen aus HTML.
get_workflow_state_key()
Baut den Remember-Schlüssel für den Form-Workflow-State.
$_reload_suffix
Historischer Reload-Suffix.
add_obj($obj, $tpl, $data='', $data2='')
Fügt ein {obj:*}-Objekt hinzu.
$_tpl_fld_warning
Template für Feld-Warnung.
set_form_callback_owner($owner)
merge_obj($content, $i=0)
Ersetzt {obj:*}-Platzhalter.
add_editor_file(string $kind, string $file)
Registriert eine genutzte FD-/DD-/Class-Datei fuer Editor-Marker.
clear()
Öffentlicher Reset-Einstieg.
url_to_array($data)
Wandelt Query-/URL-Daten in ein Array um.
$_workflow_state
Remember-basierter Form-Status.
get_fld_msg($mode, $msg='')
Gibt eine Feldmeldung zurück.
$_confirm_copy
Optionaler Copy-Bestätigungstext.
get_external_form_state(string $fid)
Liest den Workflow-State eines anderen Formulars im selben Scope.
_forward_clear()
Setzt den internen Formularzustand zurück.
get_fd_message(string $key, string $default='')
Liefert eine Meldung aus der aktuell geladenen FD.
$_tpl_fld_info
Template für Feld-Info.
resolve_fd_dd_editor_file(string $source)
Liefert den absoluten Pfad einer FD-/DD-Quelle fuer Editor-Marker.
get_dd(string $dd, string $fld, string $var)
Liest einen Feldwert oder das ganze Feld aus einer DD-/FD-Quelle.
load_fd_messages(string $fd='')
Lädt ausschließlich den Meldungsvertrag einer sprachabhängigen FD.
get_rid()
Liest die aktuelle RID, optional view-synchronisiert.
$_mode
tpl|php|mix
changed($changed=0)
Gibt die Anzahl geänderter Felder zurück.
get_fld_value($name, $default='', $rules='', $submit=-1)
$_try_msg
Sperr-Text.
get_security_data()
Liefert Feldname und aktuellen Wert des dbxForm-Security-Tokens.
set_msg_warning($msg)
Setzt die Formular-Warnmeldung.
set_form_valid(bool $state)
Setzt den Validitätsstatus des Formulars.
$_view_sync
Optionaler View-Sync-Status.
add_css($css='')
Fügt CSS hinzu.
set_form_saved(bool $state)
Setzt den Save-Status des Formulars.
set_activ_id($value, $key='')
Setzt die aktive Remember-ID des aktuellen DD-Kontexts.
run()
Öffentliche Run-Methode.
php_datetime_usr($value)
Wandelt Datums-/Zeitwerte in deutsches Format um.
errors()
Gibt die Anzahl der Fehler zurück.
add_module_bar_form_actions(array $options=array())
$_try_reset
Sekunden bis Try-Reset.
set_error($msg)
Setzt eine aktive allgemeine Formular-Fehlermeldung.
$_post
Validierte/geänderte POST-Werte.
set_fld_val($fld_name, $fld_val)
Setzt einen Feldwert direkt im Feldzustand.
__destruct()
Destruktor.
clear_sys()
Leert nur den Sysdata-Bereich.
$_confirm_delete
Optionaler Lösch-Bestätigungstext.
$_msg_warning
Standard-Warntext.
bump_form_version()
Erhöht die Formularversion um 1.
$_tpl_max_try
Template für Max-Try-Sperre.
set_state_value(string $key, $value)
Setzt einen Wert im Workflow-State.
remember_current_dependencies(?array $formIds=null)
Speichert die aktuell gültigen Versionsstände abhängiger Formulare.
check_try_count($submit, $errors, $allways=1)
Prüft Fehlversuche und baut ggf.
$_view_mode
Optionaler View-Modus.
$_flds
Felddefinitionen.
$_replaces
Allgemeine Replace-Werte.
set_submit_callback(string $callback)
add_rep($key, $val)
Fügt einen allgemeinen Replace-Wert hinzu.
$_tpl_fld_success
Template für Feld-Erfolg.
get_fld_val($name, $default='', $rules='', $submit=-1)
applyModuleBarReplaces(array $values=array())
add_flds($dd='fd::')
Fügt alle Felder der aktiven DD-/FD-Felddefinitionsquelle hinzu.
$_obj
Platzhalter-Objekte.
$_dd
Zugeordnetes DD.
save_post($dd, $rid, $pv='', $reread=1)
Speichert _post über dbxDB.
get_post($name, $default='', $rules='alphanum')
Liest POST/GET-Daten mit Default-Rules alphanum.
set_msg_info($msg)
Setzt die Formular-Info-Meldung.
$_editor_fld
Optionales Editor-Feld.
get_form_version()
Gibt die aktuelle Formularversion zurück.
Gemeinsame Basisklasse fuer dbXapp-System-, Modul- und Include-Objekte.
Definition dbxKernel.php:63
set_callback_id(string $id)
Setzt den Namenspraefix fuer konventionelle Callback-Methoden.
Definition dbxKernel.php:96
set_callback(string $event, string $callback)
Ordnet einem Event einen konkreten Methodennamen zu.
set_callback_owner($owner)
Setzt ein explizites Owner-Objekt fuer Callback-Aufrufe.
Definition dbxKernel.php:82
callback(string $event, mixed $value)
Fuehrt einen Callback aus und gibt dessen Rueckgabewert zurueck.
$messages
Definition config.fd.php:2
dbx()
Liefert die zentrale dbXapp-API als Singleton.
Definition dbxApi.php:2805
os_path(string $path)
Normalisiert einen Pfad fuer das aktuelle Betriebssystem.
Definition dbxApi.php:1538
get_base_dir(int $cutData=0)
Liefert das Basisverzeichnis der Installation.
Definition dbxApi.php:1507
search_defaults(array $overrides=array())
Standard-Optionen fuer dbx|search (wie Content-Grid).
Definition dbxApi.php:2326
dbx_lng_resolve_file(string $dir, string $name, string $ext, string $lng='', bool $fallback=true)
Datei mit Sprachsuffix aufloesen: name_lng.ext mit Fallback name.ext.
Definition dbxApi.php:3109
$_SERVER['REQUEST_METHOD']
if($stored !=='files/test/') $resolved
if(($result['name'] ?? '') !=='customer-design'||!is_dir($target)) if(substr_count( $defaultHtml, '[dbx:content]') !==1) foreach(array('[dbx:logo]', '[dbx:branding]', '[dbx:footer]') as $slot) if(( $metadata[ 'contract'] ?? '') !==\dbx\dbxDesign_admin\dbxDesignService::CONTRACT) if((dbx() ->config['dbx']['default_design_user'] ?? '') !=='customer-design') $validation
if(preg_match('/core\.js\? foreach[^"\']*v=(\d+)/', $designTemplate, $assetMatch) !== 1 || !str_contains($shopReference, 'dbxapp-Asset-Version ' . $assetMatch[1])) foreach (array( 'reference\\archive', 'provision_docs_content.php', 'dbxSelfTest') as $needle)(array('Installation'=> $installation, 'Installations-Tutorial'=> $installationTutorial, 'SelfTest'=> $selfTest) as $label=> $html)
$formattedMessage _messages
$messageOnlyForm _fd
foreach( $it as $file) if($checked===0) $formClass
$form _try_count_reset
$form _sys['_try_sys']
if($web->content_permalink_redirect_target() !=='') $tpl
if(!defined( 'IMG_WEBP')) define( 'IMG_WEBP'
foreach( $valid as $email) foreach($invalid as $email) $fd
exit
Definition index.php:146
DBX schema administration.