6 private function esc($value) {
7 return htmlspecialchars((
string)$value, ENT_QUOTES,
'UTF-8');
10 private function modules_root() {
14 private function backup_root() {
17 @mkdir(
$dir, 0777,
true);
22 private function valid_name($name) {
23 return is_string($name) && preg_match(
'/^[A-Za-z][A-Za-z0-9_]{1,63}$/', $name);
26 private function valid_file_name($name) {
27 return is_string($name) && preg_match(
'/^[A-Za-z0-9_.-]+$/', $name) && strpos($name,
'..') ===
false;
30 private function module_dir($modul) {
31 return dbx_os_path_file($this->modules_root() . $modul . DIRECTORY_SEPARATOR);
34 private function module_path($modul, $rel) {
35 $rel = str_replace(array(
'\\',
"\0"), array(
'/',
''), (
string)$rel);
36 $rel = ltrim($rel,
'/');
37 if ($rel ===
'' || strpos($rel,
'../') !==
false || strpos($rel,
'..\\') !==
false) {
40 $base = $this->module_dir($modul);
42 $baseNorm = str_replace(
'\\',
'/', rtrim(
$base,
'/\\') .
'/');
43 $pathNorm = str_replace(
'\\',
'/', $path);
44 if (strpos($pathNorm, $baseNorm) !== 0) {
50 private function module_options($withNew =
false) {
51 $options = $withNew ? array(
'' =>
'Bitte waehlen') : array();
52 $root = $this->modules_root();
53 foreach (glob(
$root .
'*', GLOB_ONLYDIR) ?: array() as
$dir) {
54 $name = basename(
$dir);
55 if ($this->valid_name($name)) {
56 $options[$name] = $name;
63 private function dd_options($withEmpty =
true) {
64 $options = $withEmpty ? array(
'' =>
'Bitte waehlen / manuell') : array();
65 foreach (glob($this->modules_root() .
'*', GLOB_ONLYDIR) ?: array() as
$dir) {
66 $modul = basename(
$dir);
67 if (!$this->valid_name($modul)) {
71 $dd = basename($file,
'.dd.php');
72 if ($this->valid_name($dd)) {
73 $options[$modul .
'|' . $dd] = $modul .
' | ' . $dd;
81 private function options($items) {
82 return is_array($items) ? $items : array();
85 private function collect_input($form =
null) {
86 $get =
function($name, $default =
'', $rules =
'parameter') use ($form) {
87 if ($form && isset($form->_post[$name])) {
88 return $form->_post[$name];
90 return dbx()->get_modul_var($name, $default, $rules);
93 $target = (string)$get(
'target_mode',
'new');
94 $existingModul = trim((
string)$get(
'existing_modul',
''));
95 $modul = trim((
string)$get(
'xmodul',
''));
96 if ($target ===
'existing' && $existingModul !==
'') {
97 $modul = $existingModul;
100 $modul = trim((
string)$get(
'modul',
''));
103 $ddRef = trim((
string)$get(
'dd_ref',
''));
104 $title = trim((
string)$get(
'title', $modul));
105 $dd = trim((
string)$get(
'dd_name', $modul ? $modul .
'Data' :
''));
106 if ($ddRef !==
'' && strpos($ddRef,
'|') !==
false) {
107 list($refModul, $refDd) = explode(
'|', $ddRef, 2);
108 if ($target ===
'existing' && $modul ===
'' && $this->valid_name($refModul)) {
111 if ($this->valid_name($refDd)) {
115 $table = trim((
string)$get(
'table_name', $dd));
116 $dbFile = trim((
string)$get(
'db_file', $modul ? $modul .
'.db3' :
'modul.db3'));
119 'target_mode' => in_array($target, array(
'new',
'existing'),
true) ? $target :
'new',
120 'existing_modul' => $existingModul,
123 'title' => $title !==
'' ? $title : $modul,
124 'default_run1' => trim((
string)$get(
'default_run1',
'run')),
125 'default_run2' => trim((
string)$get(
'default_run2',
'')),
126 'module_template' => (
string)$get(
'module_template',
'form_report'),
127 'dd_mode' => (
string)$get(
'dd_mode',
'new'),
130 'db_file' => $dbFile,
131 'field_preset' => (
string)$get(
'field_preset',
'basic'),
132 'create_include' => (
int)$get(
'create_include', 1,
'int'),
133 'create_form' => (
int)$get(
'create_form', 1,
'int'),
134 'create_report' => (
int)$get(
'create_report', 1,
'int'),
135 'create_templates' => (
int)$get(
'create_templates', 1,
'int'),
136 'overwrite' => (
int)$get(
'overwrite', 0,
'int'),
137 'backup' => (
int)$get(
'backup', 1,
'int'),
138 'sync_mode' => (
string)$get(
'sync_mode',
'link'),
139 'ki_package' => (
int)$get(
'ki_package', 1,
'int'),
143 private function validate_input(array $in, &$errors) {
145 if (!$this->valid_name($in[
'xmodul'])) {
146 $errors[] =
'Modulname ungueltig. Erlaubt: Buchstaben, Zahlen, Unterstrich; Beginn mit Buchstabe.';
148 if (!in_array($in[
'module_template'], array(
'blank',
'form',
'report',
'form_report',
'api'),
true)) {
149 $errors[] =
'Modulvorlage ungueltig.';
151 if (!in_array($in[
'dd_mode'], array(
'none',
'new',
'existing'),
true)) {
152 $errors[] =
'DD-Auswahl ungueltig.';
154 if ($in[
'dd_mode'] !==
'none' && !$this->valid_name($in[
'dd_name'])) {
155 $errors[] =
'DD-Name ungueltig.';
157 if ($in[
'dd_mode'] !==
'none' && !$this->valid_name($in[
'table_name'])) {
158 $errors[] =
'Tabellenname ungueltig.';
160 if ($in[
'dd_mode'] !==
'none' && (!$this->valid_file_name($in[
'db_file']) || !preg_match(
'/\.db3$/i', $in[
'db_file']))) {
161 $errors[] =
'SQLite-Datei muss eine einfache *.db3-Datei sein.';
163 if ($in[
'dd_mode'] ===
'existing') {
164 if ($in[
'dd_ref'] !==
'' && strpos($in[
'dd_ref'],
'|') !==
false) {
165 list($refModul, $refDd) = explode(
'|', $in[
'dd_ref'], 2);
166 if ($refModul !== $in[
'xmodul'] || $refDd !== $in[
'dd_name']) {
167 $errors[] =
'Vorhandenes DD muss zum ausgewaehlten Modul gehoeren.';
170 $ddFile = $this->module_path($in[
'xmodul'],
'dd/' . $in[
'dd_name'] .
'.dd.php');
171 if ($ddFile ===
'' || !is_file($ddFile)) {
172 $errors[] =
'Das ausgewaehlte DD existiert im Modul nicht.';
176 $dir = $this->module_dir($in[
'xmodul']);
177 if ($in[
'target_mode'] ===
'new' && is_dir(
$dir)) {
178 $errors[] =
'Das Modul existiert bereits. Bitte Ziel "Bestehendes Modul" waehlen.';
180 if ($in[
'target_mode'] ===
'existing' && !is_dir(
$dir)) {
181 $errors[] =
'Das ausgewaehlte Modul existiert nicht.';
184 return !count($errors);
187 private function ensure_dirs($modul) {
188 foreach (array(
'',
'cfg',
'dd',
'db',
'fd',
'include',
'tpl',
'tpl/htm',
'tpl/mod',
'tpl/css',
'tpl/js',
'tpl/img') as
$dir) {
189 $path = $this->module_path($modul,
$dir);
190 if ($path && !is_dir($path)) {
191 @mkdir($path, 0777,
true);
196 private function write_module_file($modul, $rel, $content, $overwrite, array &$log) {
197 $path = $this->module_path($modul, $rel);
199 $log[] = array(
'type' =>
'error',
'text' =>
'Pfad blockiert: ' . $rel);
202 $dir = dirname($path);
204 @mkdir(
$dir, 0777,
true);
206 if (is_file($path) && !$overwrite) {
207 $log[] = array(
'type' =>
'skip',
'text' => $rel .
' existiert bereits.');
210 $ok = file_put_contents($path, $content) !==
false;
211 $log[] = array(
'type' => $ok ?
'ok' :
'error',
'text' => ($ok ?
'geschrieben: ' :
'Fehler: ') . $rel);
215 private function backup_module($modul) {
216 if (!class_exists(
'\\ZipArchive')) {
219 $dir = $this->module_dir($modul);
223 $file = date(
'Ymd-His') .
'__' . $modul .
'.zip';
224 $path = $this->backup_root() . $file;
225 $zip = new \ZipArchive();
226 if ($zip->open($path, \ZipArchive::OVERWRITE) !==
true) {
229 $it = new \RecursiveIteratorIterator(
230 new \RecursiveDirectoryIterator(
$dir, \FilesystemIterator::SKIP_DOTS),
231 \RecursiveIteratorIterator::SELF_FIRST
233 foreach ($it as $item) {
234 $full = $item->getPathname();
235 $rel = str_replace(
'\\',
'/', substr($full, strlen(rtrim(
$dir,
'/\\')) + 1));
236 if ($item->isDir()) {
237 $zip->addEmptyDir($rel);
239 $zip->addFile($full, $rel);
246 private function restore_module_backup() {
247 $modul =
dbx()->get_modul_var(
'xmodul',
'',
'parameter');
248 $file = basename((
string)
dbx()->get_modul_var(
'file',
'',
'parameter+.-_'));
249 $confirm = (int)
dbx()->get_modul_var(
'confirm', 0,
'int');
250 if (!$this->valid_name($modul) || !$this->valid_file_name($file)) {
251 return '<div class="alert alert-danger">Restore nicht moeglich: ungueltige Parameter.</div>';
253 $zipPath = $this->backup_root() . $file;
254 if (!is_file($zipPath)) {
255 return '<div class="alert alert-warning">Backup-ZIP nicht gefunden.</div>';
258 $url =
'?dbx_modul=dbxAdmin&dbx_run1=modules&dbx_run2=modul_new&dbx_run3=restore_backup&xmodul=' . rawurlencode($modul) .
'&file=' . rawurlencode($file) .
'&confirm=1';
259 return '<div class="p-3"><h3>Modul wiederherstellen</h3><p>Restore ersetzt den Inhalt von <code>dbx/modules/' . $this->esc($modul) .
'</code> durch das Backup <code>' . $this->esc($file) .
'</code>.</p><a class="btn btn-danger" href="' . $this->esc($url) .
'">Restore starten</a></div>';
262 $currentBackup = $this->backup_module($modul);
263 $dir = $this->module_dir($modul);
265 @mkdir(
$dir, 0777,
true);
267 $this->empty_dir(
$dir);
268 $zip = new \ZipArchive();
269 if ($zip->open($zipPath) !==
true) {
270 return '<div class="alert alert-danger">Backup konnte nicht geoeffnet werden.</div>';
272 for ($i = 0; $i < $zip->numFiles; $i++) {
273 $name = str_replace(
'\\',
'/', (
string)$zip->getNameIndex($i));
274 if ($name ===
'' || strpos($name,
'../') !==
false || strpos($name,
'..\\') !==
false || $name[0] ===
'/') {
276 return '<div class="alert alert-danger">Backup enthaelt ungueltige Pfade.</div>';
279 $zip->extractTo(
$dir);
281 return '<div class="alert alert-success">Modul wiederhergestellt. Sicherheitsbackup vorher: ' . $this->esc($currentBackup) .
'</div>';
284 private function empty_dir(
$dir) {
285 if (!is_dir(
$dir))
return;
286 $it = new \RecursiveIteratorIterator(
287 new \RecursiveDirectoryIterator(
$dir, \FilesystemIterator::SKIP_DOTS),
288 \RecursiveIteratorIterator::CHILD_FIRST
290 foreach ($it as $item) {
291 if ($item->isDir()) {
292 @rmdir($item->getPathname());
294 @unlink($item->getPathname());
299 private function field_defs($preset) {
301 array(
'id',
'int',
'11',
'',
'id',
'int',
'integer-label',
'PRI',
''),
302 array(
'create_date',
'datetime',
'-1',
'',
'create_date',
'datetime',
'textarea-label',
'',
''),
303 array(
'create_uid',
'int',
'11',
'0',
'create_uid',
'int',
'integer-label',
'',
''),
304 array(
'update_date',
'datetime',
'-1',
'',
'update_date',
'datetime',
'textarea-label',
'',
''),
305 array(
'update_uid',
'int',
'11',
'0',
'update_uid',
'int',
'integer-label',
'',
''),
306 array(
'activ',
'int',
'1',
'1',
'Aktiv',
'int',
'checkbox-label',
'',
''),
307 array(
'sorter',
'varchar',
'16',
'',
'Sortierung',
'varchar',
'text-label',
'',
''),
308 array(
'title',
'varchar',
'254',
'',
'Titel',
'varchar|min=1|max=254',
'text-label',
'MUL',
''),
309 array(
'description',
'text',
'-1',
'',
'Beschreibung',
'text',
'textarea-label',
'',
''),
311 if (in_array($preset, array(
'content',
'workflow'),
true)) {
312 $fields[] = array(
'content',
'text',
'-1',
'',
'Inhalt',
'text',
'textarea-label',
'',
'');
314 if (in_array($preset, array(
'status',
'workflow'),
true)) {
315 $fields[] = array(
'status',
'varchar',
'32',
'open',
'Status',
'parameter|max=32',
'select-single-label',
'MUL', array(
'open' =>
'Offen',
'review' =>
'Pruefung',
'done' =>
'Erledigt',
'archived' =>
'Archiv'));
320 private function dd_field_php(array $f) {
321 list($name, $type, $length, $default, $label, $rules, $tpl,
$index) = array_pad($f, 8,
'');
322 $options = $f[8] ??
'';
323 return "\$field['name']=" . var_export($name,
true) .
";\n"
324 .
"\$field['type']=" . var_export($type,
true) .
";\n"
325 .
"\$field['index']=" . var_export(
$index,
true) .
";\n"
326 .
"\$field['length']=" . var_export($length,
true) .
";\n"
327 .
"\$field['default']=" . var_export($default,
true) .
";\n"
328 .
"\$field['label']=" . var_export($label,
true) .
";\n"
329 .
"\$field['rules']=" . var_export($rules,
true) .
";\n"
330 .
"\$field['tooltip']='';\n\$field['errormsg']='';\n\$field['placeholder']='';\n\$field['convert']='';\n\$field['protect']='0';\n\$field['group']='';\n\$field['mask']='';\n\$field['data']='';\n"
331 .
"\$field['options']=" . var_export($options,
true) .
";\n"
332 .
"\$field['tpl']=" . var_export($tpl,
true) .
";\n"
333 .
"\$field['js']='';\n\$field['prompt']='';\n\$fields[]=\$field;\n\n";
336 private function generate_dd(array $in) {
338 $out .=
"/* =========================================================\n TABLE\n ========================================================= */\n";
339 $out .=
"\$table['server']=" . var_export($in[
'xmodul'] .
'|' . $in[
'db_file'],
true) .
";\n";
340 $out .=
"\$table['table']=" . var_export($in[
'table_name'],
true) .
";\n";
341 $out .=
"\$table['datadic']=" . var_export($in[
'dd_name'],
true) .
";\n";
342 $out .=
"\$table['primary']='';\n\$table['language']='';\n\$table['version']='1.0';\n\$table['autosync']='0';\n\$table['cache']='0';\n\$table['trash']='1';\n\$table['trace']='0';\n\$table['update_sql']='';\n\$table['default_sort']='title';\n\$table['form-dd-table']='';\n\$table['read']='admin';\n\$table['create']='admin';\n\$table['update']='admin';\n\$table['delete']='admin';\n\$table['read_owner']='owner,admin';\n\$table['create_owner']='owner,admin';\n\$table['update_owner']='owner,admin';\n\$table['delete_owner']='owner,admin';\n\n";
343 $out .=
"/* =========================================================\n FIELDS\n ========================================================= */\n";
344 foreach ($this->field_defs($in[
'field_preset']) as
$field) {
345 $out .= $this->dd_field_php(
$field);
347 $out .=
"/* =========================================================\n INDEXES\n ========================================================= */\n";
349 array(
'idx_' . $in[
'table_name'] .
'_title',
'title'),
350 array(
'idx_' . $in[
'table_name'] .
'_activ',
'activ'),
352 if (in_array($in[
'field_preset'], array(
'status',
'workflow'),
true)) {
353 $indexes[] = array(
'idx_' . $in[
'table_name'] .
'_status',
'status');
356 $out .=
"\$index['name']=" . var_export($idx[0],
true) .
";\n\$index['type']='INDEX';\n\$index['fields']=" . var_export($idx[1],
true) .
";\n\$index['unique']='0';\n\$index['comment']='module wizard';\n\$indexes[]=\$index;\n\n";
361 private function generate_form_fd(array $in) {
363 foreach ($this->field_defs($in[
'field_preset']) as $f) {
364 if (in_array($f[0], array(
'id',
'create_date',
'create_uid',
'update_date',
'update_uid'),
true)) {
367 $out .= $this->dd_field_php($f);
372 private function generate_report_fd() {
375$field[
'name']=
'dbx_rrows';
377$field[
'tpl']=
'select-single-label';
379$field[
'label']=
'Anz.Seite';
381$field[
'options']=
'10=10&20=20&50=50&100=100&0=alle';
384$field[
'name']=
'dbx_rsort';
386$field[
'tpl']=
'select-single-label';
388$field[
'label']=
'Sortierung';
389$field[
'rules']=
'sqlsearch';
390$field[
'options']=
'id=id&title=Titel&update_date=Update&sorter=Sortierung&activ=Aktiv';
393$field[
'name']=
'dbx_rdesc';
395$field[
'tpl']=
'select-single-label';
398$field[
'rules']=
'parameter';
399$field[
'options']=
'ASC=Aufsteigend&DESC=Absteigend';
402$field[
'name']=
'dbx_rwhere';
404$field[
'tpl']=
'dbx|search';
407$field[
'rules']=
'parameter';
411$field[
'name']=
'dbx_rselect';
413$field[
'tpl']=
'select-single-label';
417$field[
'options']=
'0=Alle&1=Ausgewaehlte';
422 private function generate_main_class(array $in) {
423 $modul = $in[
'xmodul'];
424 $service = $modul .
'Service';
425 $default = $in[
'default_run1'] ?:
'run';
426 $startContent = $this->esc(
'Modul ' . $modul .
' ist bereit.');
427 $startMethod =
" private function start() {\n \$tpl = dbx()->get_system_obj('dbxTPL');\n return \$tpl->get_tpl('$modul|start', array('content' => " . var_export($startContent,
true) .
"));\n }\n";
428 if ((
int)$in[
'create_templates'] !== 1) {
429 $startMethod =
" private function start() {\n return '<div class=\"p-3\"><h2>" . $this->esc($in[
'title']) .
"</h2><p>" . $startContent .
"</p></div>';\n }\n";
432 $cases .=
" case " . var_export($default,
true) .
":\n \$content = \$this->start();\n break;\n\n";
433 if ($in[
'create_form']) {
434 $cases .=
" case 'form':\n \$content = \$this->service()->form();\n break;\n\n";
436 if ($in[
'create_report']) {
437 $cases .=
" case 'report':\n \$content = \$this->service()->report();\n break;\n\n";
438 $cases .=
" case 'detail':\n \$content = \$this->service()->detail();\n break;\n\n";
440 if ($in[
'module_template'] ===
'api' || $in[
'create_include']) {
441 $cases .=
" case 'api':\n \$this->service()->api();\n break;\n\n";
443 return "<?php\nnamespace dbx\\$modul;\n\nclass $modul {\n\n private function service() {\n return dbx()->get_include_obj('$service', '$modul');\n }\n\n$startMethod\n public function run() {\n \$run = dbx()->get_modul_var('dbx_run1', " . var_export($default,
true) .
", 'parameter');\n \$content = '';\n switch (\$run) {\n$cases default:\n \$content = '<div class=\"alert alert-warning\">Unbekannter Aufruf: ' . htmlspecialchars((string)\$run, ENT_QUOTES, 'UTF-8') . '</div>';\n }\n return \$content;\n }\n}\n";
446 private function generate_service_class(array $in) {
447 $modul = $in[
'xmodul'];
448 $class = $modul .
'Service';
449 $ddRef = $in[
'dd_mode'] !==
'none' ? $modul .
'|' . $in[
'dd_name'] :
'';
450 $formFd = $modul .
'|' . $in[
'dd_name'] .
'-form';
451 $reportFd = $modul .
'|rpt-' . $in[
'dd_name'] .
'-selection';
453 foreach ($this->field_defs($in[
'field_preset']) as $f) {
454 if (in_array($f[0], array(
'id',
'title',
'description',
'content',
'status',
'activ',
'update_date'),
true)) {
458 $fieldsExport = var_export(
$fields,
true);
461namespace dbx\__MODUL__;
476 private $dd = __DD_REF__;
477 private $formFd = __FORM_FD__;
478 private $reportFd = __REPORT_FD__;
480 private function db() {
481 return dbx()->get_system_obj(
'dbxDB');
484 private function tpl() {
485 return dbx()->get_system_obj(
'dbxTPL');
488 private function base_url($run1 =
'report', array $params = array()) {
489 $url =
'?dbx_modul=__MODUL__&dbx_run1=' . rawurlencode($run1);
490 foreach ($params as $key => $value) {
491 $url .=
'&' . rawurlencode((
string)$key) .
'=' . rawurlencode((
string)$value);
506 public function form($rid =
null) {
507 if ($this->dd ===
'')
return '<div class="alert alert-info">Kein DD konfiguriert.</div>';
509 $rid = $rid ===
null ? (int)
dbx()->get_modul_var(
'rid', 0,
'int') : (int)$rid;
510 $do =
dbx()->get_modul_var(
'dbx_do',
'',
'parameter');
512 if ($do ===
'delete' && $rid > 0) {
513 return $this->delete_record($rid);
516 $data = $rid > 0 ? $this->db()->select1($this->dd, $rid) : array(
'activ' => 1);
517 if (!is_array($data)) $data = array(
'activ' => 1);
519 $form =
dbx()->get_system_obj(
'dbxForm');
520 $form->init(
'__MODUL__-form');
521 $form->set_form_callback_owner($this);
522 $form->set_init_callback(
'form_init_callback');
523 $form->set_submit_callback(
'form_submit_callback');
524 $form->set_run_callback(
'form_run_callback');
526 $form->_dd = $this->dd;
527 $form->_fd = $this->formFd;
528 $form->_data = $data;
530 $form->_action = $this->base_url(
'form', $rid > 0 ? array(
'rid' => $rid) : array());
531 $form->add_rep(
'bar_title', $rid > 0 ?
'Datensatz bearbeiten' :
'Neuer Datensatz');
532 $form->add_rep(
'bar_subtitle', $this->dd);
533 $form->add_obj(
'bar_actions',
'obj-value', $this->form_action_buttons($rid));
535 $this->configure_form_fields($form);
537 if ($form->submit() && !$form->errors()) {
538 $ok = $form->save_post($this->dd, $rid > 0 ? $rid :
'new', $this->form_save_defaults($rid));
540 $form->_msg_success =
'Datensatz gespeichert.';
542 $form->_msg_error =
'Datensatz konnte nicht gespeichert werden.';
549 private function form_action_buttons($rid) {
550 $html =
'<button class="btn btn-primary btn-sm" type="submit"><i class="bi bi-save"></i> Speichern</button>';
552 $delete = $this->base_url(
'form', array(
'rid' => $rid,
'dbx_do' =>
'delete'));
553 $html .=
' <a class="btn btn-outline-danger btn-sm dbxConfirm" href="' . htmlspecialchars($delete, ENT_QUOTES,
'UTF-8') .
'" data-confirm-title="Datensatz loeschen" data-confirm="Diesen Datensatz wirklich loeschen?" data-confirm-buttons="yesno"><i class="bi bi-trash"></i> Loeschen</a>';
555 $html .=
' <a class="btn btn-outline-secondary btn-sm" href="' . htmlspecialchars($this->base_url(
'report'), ENT_QUOTES,
'UTF-8') .
'"><i class="bi bi-table"></i> Report</a>';
559 private function configure_form_fields($form) {
576 private function form_save_defaults($rid) {
577 $uid = (int)
dbx()->user();
578 $now = date(
'Y-m-d H:i:s');
579 $defaults = array(
'update_date' => $now,
'update_uid' => $uid);
580 if ((
int)$rid <= 0) {
581 $defaults[
'create_date'] = $now;
582 $defaults[
'create_uid'] = $uid;
587 public function delete_record($rid) {
589 if ($rid <= 0)
return '<div class="alert alert-warning">Kein Datensatz gewaehlt.</div>';
590 $ok = $this->db()->delete($this->dd, $rid);
592 return '<div class="alert alert-success">Datensatz geloescht.</div>' . $this->report();
594 return '<div class="alert alert-danger">Datensatz konnte nicht geloescht werden.</div>' . $this->report();
597 public function detail($rid =
null) {
598 $rid = $rid ===
null ? (int)
dbx()->get_modul_var(
'rid', 0,
'int') : (int)$rid;
599 if ($rid <= 0)
return '<div class="alert alert-warning">Kein Datensatz gewaehlt.</div>';
600 $record = $this->db()->select1($this->dd, $rid);
601 if (!is_array($record))
return '<div class="alert alert-warning">Datensatz nicht gefunden.</div>';
604 foreach ($record as $key => $value) {
605 $rows .=
'<tr><th>' . htmlspecialchars((
string)$key, ENT_QUOTES,
'UTF-8') .
'</th><td>' . htmlspecialchars((
string)$value, ENT_QUOTES,
'UTF-8') .
'</td></tr>';
607 return '<div class="p-3"><h3>Detail #' . $rid .
'</h3><table class="table table-sm table-bordered">' . $rows .
'</table><p><a class="btn btn-primary btn-sm" href="' . htmlspecialchars($this->base_url(
'form', array(
'rid' => $rid)), ENT_QUOTES,
'UTF-8') .
'">Bearbeiten</a> <a class="btn btn-outline-secondary btn-sm" href="' . htmlspecialchars($this->base_url(
'report'), ENT_QUOTES,
'UTF-8') .
'">Zurueck</a></p></div>';
619 public function report() {
620 if ($this->dd ===
'')
return '<div class="alert alert-info">Kein DD konfiguriert.</div>';
622 $report =
dbx()->get_system_obj(
'dbxReport');
623 $report->init(
'__MODUL__-report');
624 $report->set_callback_owner($this);
625 $report->set_report_header_callback(
'report_header_callback');
626 $report->set_page_header_callback(
'report_page_header_callback');
627 $report->set_header_callback(
'report_table_header_callback');
628 $report->set_body_callback(
'report_body_callback');
629 $report->set_footer_callback(
'report_table_footer_callback');
630 $report->set_page_footer_callback(
'report_page_footer_callback');
631 $report->set_report_footer_callback(
'report_footer_callback');
632 $report->set_next_record_callback(
'report_next_record_callback');
633 $report->set_callback(
'row_action_data',
'report_row_action_data_callback');
635 $report->_dd = $this->dd;
636 $report->_action = $this->base_url(
'report');
637 $report->_pages =
true;
638 $report->_mode =
'table';
639 $report->_but_pagination = 9;
640 $report->_multi_page_select = 1;
641 $report->_create_sel_flds =
true;
642 $report->_create_row_select =
true;
643 $report->_create_row_edit =
true;
644 $report->_create_row_show =
true;
645 $report->_create_row_delete =
true;
646 $report->_msg_confirm_delete =
'Diesen Datensatz wirklich loeschen?';
648 $report->add_action(
'rows_select',
'action_button_select',
'&dbx_do=rows_select');
649 $report->add_action(
'rows_deselect',
'action_button_deselect',
'&dbx_do=clear_selects');
650 $report->add_action(
'rows_delete',
'action_button_delete',
'&dbx_do=multi_delete');
651 $report->create_selection_fields($this->reportFd);
653 $actionContent = $this->handle_report_action($report);
654 if ($actionContent !==
'') {
655 return $actionContent;
658 $rwhere = $report->get_fld_val(
'dbx_rwhere',
'',
'sqlsearch|max=64');
659 $rsort = $report->get_fld_val(
'dbx_rsort',
'title',
'parameter');
660 $rdesc = $report->get_fld_val(
'dbx_rdesc',
'ASC',
'parameter');
661 $rrows = $report->get_fld_val(
'dbx_rrows', 20,
'int');
662 $rpos = $report->get_fld_val(
'dbx_rpos', 0,
'int');
663 $rselect = $report->get_fld_val(
'dbx_rselect', 0,
'int');
665 if ($rwhere !==
'') {
666 $rwhere = array(
'search' => array(
'value' => $rwhere,
'like' => array(
'title',
'description'),
'mode' =>
'contains'));
669 $rwhere = $report->add_rwhere_select(is_string($rwhere) ? $rwhere :
'');
672 $flds = $this->report_fields();
673 $report->_rflds = $flds;
674 $report->_rrows = $rrows;
675 $report->_rpos = $rpos;
676 $report->_count_all = $this->db()->count($this->dd);
677 $report->_rcount = $this->db()->count($this->dd, $rwhere);
678 $report->_rdata = $this->db()->select($this->dd, $rwhere, $flds, $rsort, $rdesc,
'', $rrows, $rpos);
680 return $report->run();
683 private function handle_report_action($report) {
684 $do =
dbx()->get_modul_var(
'dbx_do',
'',
'parameter');
685 $rid = (int)
dbx()->get_modul_var(
'rid', 0,
'int');
687 if ($do ===
'row_edit' && $rid > 0) {
688 return $this->form($rid);
690 if (($do ===
'row_show' || $do ===
'detail') && $rid > 0) {
691 return $this->detail($rid);
693 if ($do ===
'row_delete' && $rid > 0) {
694 $ok = $this->db()->delete($this->dd, $rid);
695 $report->del_selected($rid);
696 $report->_msg_success = $ok ?
'Datensatz geloescht.' :
'';
697 $report->_msg_error = $ok ?
'' :
'Datensatz konnte nicht geloescht werden.';
700 if ($do ===
'multi_delete') {
701 $result = $report->delete_multi_selected_records($this->dd);
702 $report->apply_multi_delete_result(
$result);
708 private function report_fields() {
709 return __FIELDS_EXPORT__;
712 public function report_header_callback($report, $content) {
716 public function report_page_header_callback($report, $content) {
720 public function report_table_header_callback($report, $content) {
724 public function report_body_callback($report, $content) {
728 public function report_table_footer_callback($report, $content) {
732 public function report_page_footer_callback($report, $content) {
736 public function report_footer_callback($report, $content) {
740 public function report_next_record_callback($report, $record) {
741 if (is_array($record) && isset($record[
'status'])) {
742 $record[
'status'] = strtoupper((
string)$record[
'status']);
747 public function report_row_action_data_callback($report, $payload) {
748 if (is_array($payload) && isset($payload[
'data'], $payload[
'type'])) {
749 if ($payload[
'type'] ===
'show') {
750 $payload[
'data'][
'tooltip'] =
'Details anzeigen';
756 public function form_init_callback($form, $value) {
760 public function form_submit_callback($form, $value) {
764 public function form_run_callback($form, $content) {
768 public function api() {
769 dbx()->json_response(array(
'ok' => 1,
'module' =>
'__MODUL__',
'dd' => $this->dd));
775 array(
'__MODUL__',
'__CLASS__',
'__DD_REF__',
'__FORM_FD__',
'__REPORT_FD__',
'__FIELDS_EXPORT__'),
776 array($modul, $class, var_export($ddRef,
true), var_export($formFd,
true), var_export($reportFd,
true), $fieldsExport),
781 private function generate_result_template(array $in) {
782 return '<div class="p-3"><h2>' . $this->esc($in[
'title']) .
'</h2><p>{content}</p></div>';
785 private function generate_module_readme(array $in) {
786 $modul = $in[
'xmodul'];
787 $dd = $in[
'dd_name'];
789# __MODUL__ Modul-Wizard Dokumentation
791Diese Datei wurde vom
dbxAdmin Modul-Wizard erzeugt. Sie ist absichtlich knapp und technisch, damit Mensch und KI die Struktur direkt verstehen.
795- Modulwurzel: `
dbx/modules/__MODUL__/`
796- KI darf nur innerhalb dieser Modulwurzel arbeiten.
797- DD-Dateien muessen vollstaendig sein, keine DD-Includes.
798- Templates werden in PHP ueber `
dbx()->get_system_obj(
'dbxTPL')->get_tpl(
'__MODUL__|template', $data)` gerendert.
799- DD->DB Sync nur fuer `__MODUL__|__DD__`.
803- `__MODUL__.class.php`: Einstieg und Routing nach `dbx_run1`.
804- `include/__MODUL__Service.class.php`: Formular, Report, Detail, API und Callbacks.
805- `dd/__DD__.dd.php`: Data Dictionary und DB-Struktur.
806- `fd/__DD__-form.fd.php`: Formularfelder.
807- `fd/rpt-__DD__-selection.fd.php`: Report-Filter und Report-Auswahl.
808- `tpl/htm/start.htm`: Start-Template.
812Route: `?dbx_modul=__MODUL__&dbx_run1=form`
816- `save_post()` fuer Insert/Update.
817- `dbx_do=
delete` fuer Delete.
818- `form_action_buttons()` fuer Save/Delete/Report Buttons.
819- `configure_form_fields()` als zentraler Ort fuer Felder.
821 - `form_init_callback($form, $value)`
822 - `form_submit_callback($form, $value)`
823 - `form_run_callback($form, $content)`
825Gängige Feldtemplates:
827- `text-label`: einzeiliger Text.
828- `textarea-label`: mehrzeiliger Text, auch fuer HTML-Felder geeignet wenn Regeln es zulassen.
829- `checkbox-label`: 0/1 Checkbox.
830- `select-single-label`: einfache Auswahl mit `options`.
831- `select-multible-label` oder `multiselect2`: Mehrfachauswahl.
832- `date-label`: Datum.
833- `integer-label`: Zahlen.
834- `password-label`: Passwort.
835- `hidden`: verstecktes Feld.
839Route: `?dbx_modul=__MODUL__&dbx_run1=report`
844- Multi-Delete ueber `delete_multi_selected_records()`.
845- Row-Edit ueber `dbx_do=row_edit`.
846- Row-Detail ueber `dbx_do=row_show` oder `dbx_do=detail`.
847- Row-Delete ueber `dbx_do=row_delete`.
848- Filter `dbx_rselect=1` zeigt nur ausgewaehlte IDs.
852- `report_header_callback($report, $content)`
853- `report_page_header_callback($report, $content)`
854- `report_table_header_callback($report, $content)`
855- `report_body_callback($report, $content)`
856- `report_table_footer_callback($report, $content)`
857- `report_page_footer_callback($report, $content)`
858- `report_footer_callback($report, $content)`
859- `report_next_record_callback($report, $record)`
860- `report_row_action_data_callback($report, $payload)`
864Route: `?dbx_modul=__MODUL__&dbx_run1=api`
866Die API-Methode ist ein Platzhalter und liefert JSON. Erweiterungen bleiben im Modul.
869 return str_replace(array(
'__MODUL__',
'__DD__'), array($modul, $dd), $template);
872 private function generate_module(array $in, array &$log) {
873 $modul = $in[
'xmodul'];
874 if ($in[
'target_mode'] ===
'new' && !is_dir($this->module_dir($modul))) {
875 @mkdir($this->module_dir($modul), 0777,
true);
877 $this->ensure_dirs($modul);
878 $overwrite = (int)$in[
'overwrite'] === 1;
880 if ((
int)$in[
'backup'] === 1 && $in[
'target_mode'] ===
'existing') {
881 $backup = $this->backup_module($modul);
883 $log[] = array(
'type' =>
'ok',
'text' =>
'Backup erstellt: ' . $backup);
884 $restore =
'?dbx_modul=dbxAdmin&dbx_run1=modules&dbx_run2=modul_new&dbx_run3=restore_backup&xmodul=' . rawurlencode($modul) .
'&file=' . rawurlencode($backup);
885 $log[] = array(
'type' =>
'link',
'text' =>
'Restore-Link: ' . $restore);
887 $log[] = array(
'type' =>
'skip',
'text' =>
'Backup nicht erstellt (ZipArchive nicht verfuegbar oder Modul leer).');
891 $config =
"<?php\n\$config['dbxConfig_modul']='secure';\n\$config['groups']='admin,';\n\$config['title']=" . var_export($in[
'title'],
true) .
";\n\$config['default_run1']=" . var_export($in[
'default_run1'],
true) .
";\n\$config['default_run2']=" . var_export($in[
'default_run2'],
true) .
";\n";
892 $this->write_module_file($modul,
'cfg/config.php',
$config, $overwrite || $in[
'target_mode'] ===
'new', $log);
893 $this->write_module_file($modul, $modul .
'.class.php', $this->generate_main_class($in), $overwrite || $in[
'target_mode'] ===
'new', $log);
895 if ((
int)$in[
'create_include'] === 1) {
896 $this->write_module_file($modul,
'include/' . $modul .
'Service.class.php', $this->generate_service_class($in), $overwrite || $in[
'target_mode'] ===
'new', $log);
898 $this->write_module_file($modul,
'README-MODUL-WIZARD.md', $this->generate_module_readme($in), $overwrite || $in[
'target_mode'] ===
'new', $log);
899 if ($in[
'dd_mode'] ===
'new') {
900 $this->write_module_file($modul,
'dd/' . $in[
'dd_name'] .
'.dd.php', $this->generate_dd($in), $overwrite || $in[
'target_mode'] ===
'new', $log);
902 if ((
int)$in[
'create_form'] === 1 && $in[
'dd_mode'] !==
'none') {
903 $this->write_module_file($modul,
'fd/' . $in[
'dd_name'] .
'-form.fd.php', $this->generate_form_fd($in), $overwrite || $in[
'target_mode'] ===
'new', $log);
905 if ((
int)$in[
'create_report'] === 1 && $in[
'dd_mode'] !==
'none') {
906 $this->write_module_file($modul,
'fd/rpt-' . $in[
'dd_name'] .
'-selection.fd.php', $this->generate_report_fd(), $overwrite || $in[
'target_mode'] ===
'new', $log);
908 if ((
int)$in[
'create_templates'] === 1) {
909 $this->write_module_file($modul,
'tpl/htm/modul-help.htm',
'<p>' . $this->esc($in[
'title']) .
'</p>', $overwrite || $in[
'target_mode'] ===
'new', $log);
910 $this->write_module_file($modul,
'tpl/htm/start.htm', $this->generate_result_template($in), $overwrite || $in[
'target_mode'] ===
'new', $log);
913 if ($in[
'dd_mode'] !==
'none') {
914 $syncUrl =
'?dbx_modul=dbxAdmin&dbx_run1=dd&dbx_run2=sync_dd_to_db&modul=' . rawurlencode($modul) .
'&dd=' . rawurlencode($in[
'dd_name']) .
'&mode=apply&reset=1';
915 if ($in[
'sync_mode'] ===
'apply') {
916 $state = $this->sync_dd_to_db($modul, $in[
'dd_name']);
917 $log[] = array(
'type' => (($state[
'status'] ??
'') ===
'finished') ?
'ok' :
'skip',
'text' =>
'DD->DB Sync: ' . (
string)($state[
'message'] ?? ($state[
'status'] ??
'')));
918 } elseif ($in[
'sync_mode'] ===
'link') {
919 $log[] = array(
'type' =>
'link',
'text' =>
'DD->DB Sync: ' . $syncUrl);
923 $startUrl =
'?dbx_modul=' . rawurlencode($modul) .
'&dbx_run1=' . rawurlencode($in[
'default_run1'] ?:
'run');
924 $log[] = array(
'type' =>
'link',
'text' =>
'Modul starten: ' . $startUrl);
925 if ((
int)$in[
'create_form'] === 1) {
926 $log[] = array(
'type' =>
'link',
'text' =>
'Formular oeffnen: ?dbx_modul=' . rawurlencode($modul) .
'&dbx_run1=form');
928 if ((
int)$in[
'create_report'] === 1) {
929 $log[] = array(
'type' =>
'link',
'text' =>
'Report oeffnen: ?dbx_modul=' . rawurlencode($modul) .
'&dbx_run1=report');
931 if ((
int)$in[
'create_include'] === 1) {
932 $log[] = array(
'type' =>
'link',
'text' =>
'API pruefen: ?dbx_modul=' . rawurlencode($modul) .
'&dbx_run1=api');
934 if ($in[
'dd_mode'] !==
'none') {
935 $log[] = array(
'type' =>
'link',
'text' =>
'DD bearbeiten: ?dbx_modul=dbxAdmin&dbx_run1=edit_dd&modul=' . rawurlencode($modul) .
'&dd=' . rawurlencode($in[
'dd_name']));
936 if ((
int)$in[
'create_form'] === 1) {
937 $log[] = array(
'type' =>
'link',
'text' =>
'Form-FD bearbeiten: ?dbx_modul=dbxAdmin&dbx_run1=edit_fd&modul=' . rawurlencode($modul) .
'&fd=' . rawurlencode($in[
'dd_name'] .
'-form'));
939 if ((
int)$in[
'create_report'] === 1) {
940 $log[] = array(
'type' =>
'link',
'text' =>
'Report-FD bearbeiten: ?dbx_modul=dbxAdmin&dbx_run1=edit_fd&modul=' . rawurlencode($modul) .
'&fd=' . rawurlencode(
'rpt-' . $in[
'dd_name'] .
'-selection'));
944 if ((
int)$in[
'ki_package'] === 1) {
945 $kiUrl =
'?dbx_modul=dbxKi&dbx_run1=briefing_module&xmodul=' . rawurlencode($modul) .
'&dd_name=' . rawurlencode($in[
'dd_name']);
946 $apiUrl =
'?dbx_modul=dbxKi&dbx_run1=module_api&action=module.describe&xmodul=' . rawurlencode($modul);
947 $log[] = array(
'type' =>
'link',
'text' =>
'KI-Modulauftrag in dbxKi: ' . $kiUrl);
948 $log[] = array(
'type' =>
'link',
'text' =>
'dbxKi Modul-API: ' . $apiUrl);
952 private function sync_dd_to_db($modul, $dd) {
953 $oDD =
dbx()->get_system_obj(
'dbxDD');
954 $oDD->sync_dd_to_db($modul, $dd,
'reset');
955 $state = array(
'status' =>
'running',
'message' =>
'');
956 for ($i = 0; $i < 20; $i++) {
957 $state = $oDD->sync_dd_to_db($modul, $dd,
'apply');
958 if (in_array((
string)($state[
'status'] ??
''), array(
'finished',
'error',
'cancelled'),
true)) {
962 return is_array($state) ? $state : array(
'status' =>
'error',
'message' =>
'Sync ohne Status');
965 private function log_html(array $log) {
966 if (!$log)
return '';
967 $html =
'<div class="dbx-admin-dashboard-panel mt-3"><div class="p-3"><h3>Ergebnis</h3><ul class="list-unstyled mb-0">';
968 foreach ($log as $row) {
969 $type = (string)($row[
'type'] ??
'info');
970 $text = (string)($row[
'text'] ??
'');
971 $class = $type ===
'error' ?
'text-danger' : ($type ===
'skip' ?
'text-muted' :
'text-success');
972 if ($type ===
'link' && strpos($text,
': ?') !==
false) {
973 list($label, $url) = explode(
': ', $text, 2);
974 $html .=
'<li class="' . $class .
'"><a href="' . $this->esc($url) .
'">' . $this->esc($label) .
'</a></li>';
976 $html .=
'<li class="' . $class .
'">' . $this->esc($text) .
'</li>';
979 return $html .
'</ul></div></div>';
982 private function ki_api() {
983 $modul =
dbx()->get_modul_var(
'xmodul',
'',
'parameter');
984 $dd =
dbx()->get_modul_var(
'dd_name',
'',
'parameter');
985 if (!$this->valid_name($modul)) {
986 dbx()->json_response(array(
'ok' => 0,
'error' =>
'ungueltiges Modul'),
true);
988 dbx()->json_response($this->ki_manifest($modul, $dd),
true);
991 private function ki_manifest($modul, $dd) {
994 'bundle_version' =>
'module-wizard.v1',
998 'root' =>
'dbx/modules/' . $modul .
'/',
999 'hard_rules' => array(
1000 'Nur Dateien unter dbx/modules/' . $modul .
'/ bearbeiten.',
1001 'Keine globalen Configs, keine anderen Module, keine Dateien in dbx/include aendern.',
1002 'Templates ueber dbxapp-Template-APIs lesen/rendern, z.B. dbx()->get_system_obj(\'dbxTPL\')->get_tpl(...).',
1003 'Template-Aenderungen nur als Modul-Template-Aktion liefern; keine freien Schreibzugriffe ausserhalb von dbx/modules/' . $modul .
'/tpl/.',
1004 'Vor destruktiven Aenderungen ein Modul-ZIP erzeugen.',
1005 'Antwort als ZIP mit manifest.json und job.json liefern.',
1007 'allowed_actions' => array(
1008 'module.file.write' =>
'Datei unter dem Modul schreiben',
1009 'module.file.delete' =>
'Datei unter dem Modul entfernen',
1010 'module.dd.write' =>
'DD unter dd/*.dd.php schreiben',
1011 'module.dd.sync' =>
'DD->DB Sync fuer Modul-DD anfordern',
1012 'module.template.get' =>
'Template ueber dbxTPL/get_tpl lesen oder rendern',
1013 'module.template.set' =>
'Template unter tpl/* ueber die dbxapp-Template-Mechanik schreiben',
1014 'module.php.write' =>
'PHP unter Modulroot oder include schreiben',
1015 'module.form.extend' =>
'Formular mit Save/Delete/Feldtemplates erweitern',
1016 'module.report.extend' =>
'Report mit Callbacks, Multi-Select, Multi-Delete, Edit und Detail erweitern',
1021 private function send_ki_zip() {
1022 if (!class_exists(
'\\ZipArchive')) {
1023 dbx()->json_response(array(
'ok' => 0,
'error' =>
'ZipArchive nicht verfuegbar.'),
true);
1025 $modul =
dbx()->get_modul_var(
'xmodul',
'',
'parameter');
1026 $dd =
dbx()->get_modul_var(
'dd_name',
'',
'parameter');
1027 if (!$this->valid_name($modul)) {
1028 dbx()->json_response(array(
'ok' => 0,
'error' =>
'ungueltiges Modul'),
true);
1030 $manifest = $this->ki_manifest($modul, $dd);
1032 'job_version' =>
'module-wizard.v1',
1035 array(
'id' =>
'backup',
'action' =>
'module.backup',
'params' => array(
'module' => $modul)),
1036 array(
'id' =>
'plan',
'action' =>
'module.plan',
'params' => array(
'module' => $modul,
'dd' => $dd)),
1039 $auftrag =
"# KI-Auftrag Modulprogrammierung\n\n"
1040 .
"Arbeite ausschliesslich im Modul `dbx/modules/$modul/`.\n\n"
1041 .
"## Harte Regeln\n\n"
1042 .
"- Keine Aenderungen ausserhalb von `dbx/modules/$modul/`.\n"
1043 .
"- Kein Umbau von Core, dbx/include, globaler config.php oder anderen Modulen.\n"
1044 .
"- DD-Dateien vollstaendig schreiben, keine include-basierten DDs.\n"
1045 .
"- Templates ueber die dbxapp-Template-Schicht lesen/rendern, z.B. `dbx()->get_system_obj('dbxTPL')->get_tpl(...)`.\n"
1046 .
"- Template-Aenderungen als Modul-Template-Set-Aktion liefern; keine Pfade ausserhalb von `dbx/modules/$modul/tpl/`.\n"
1047 .
"- Formular-Muster mit Save/Delete, Feldtemplates und Callback-Kommentaren erhalten oder sauber erweitern.\n"
1048 .
"- Report-Muster mit Multi-Select, Multi-Delete, Edit, Detail und Callbacks erhalten oder sauber erweitern.\n"
1049 .
"- README-MODUL-WIZARD.md aktualisieren, wenn Formular-, Report-, DD- oder API-Verhalten geaendert wird.\n"
1050 .
"- Wenn DB-Sync noetig ist, nur fuer `$modul|$dd` anfordern.\n"
1051 .
"- Liefere `antwort.zip` mit `manifest.json`, `job.json`, geaenderten Dateien und README.\n\n"
1052 .
"## Aufgabe\n\nBeschreibe im Chat die gewuenschte Modulaufgabe und nutze diese ZIP als Arbeitskontext.\n";
1053 $tmp = tempnam(sys_get_temp_dir(),
'dbxmodki');
1054 $zip = new \ZipArchive();
1055 $zip->open($tmp, \ZipArchive::OVERWRITE);
1056 $zip->addFromString(
'manifest.json', json_encode($manifest, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
1057 $zip->addFromString(
'job.vorlage.json', json_encode($job, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
1058 $zip->addFromString(
'KI-AUFTRAG.md', $auftrag);
1059 $zip->addFromString(
'README.md',
"Modul-KI-Auftrag fuer $modul.\n");
1061 $name =
'dbxki-modul-' . preg_replace(
'/[^A-Za-z0-9_-]+/',
'-', $modul) .
'.zip';
1062 header(
'Content-Type: application/zip');
1063 header(
'Content-Disposition: attachment; filename="' . $name .
'"');
1064 header(
'Content-Length: ' . filesize($tmp));
1071 $in = $this->collect_input();
1073 $oForm =
dbx()->get_system_obj(
'dbxForm');
1074 $oForm->init(
'form-wizzard-new');
1075 $oForm->_action =
'?dbx_modul=dbxAdmin&dbx_run1=modules&dbx_run2=modul_new';
1076 $oForm->_data = array_merge(array(
1077 'target_mode' =>
'new',
1078 'module_template' =>
'form_report',
1080 'field_preset' =>
'basic',
1081 'create_include' => 1,
1083 'create_report' => 1,
1084 'create_templates' => 1,
1087 'sync_mode' =>
'link',
1090 $oForm->add_rep(
'bar_title',
'Modul-Wizard');
1091 $oForm->add_rep(
'bar_subtitle',
'Struktur, DD, Formular, Report und KI-Auftrag');
1092 $oForm->add_obj(
'bar_actions',
'obj-value',
'<button class="btn btn-primary btn-sm" type="submit"><i class="bi bi-magic"></i> Erstellen</button>');
1093 $oForm->_msg_info =
'Wizard erzeugt nur Dateien innerhalb des gewaehlten Moduls. Bestehende Dateien werden ohne Ueberschreiben nicht ersetzt.';
1095 $oForm->add_fld(
'target_mode',
'select-single-label', label:
'Ziel', rules:
'parameter', options: $this->options(array(
'new' =>
'Neues Modul',
'existing' =>
'Bestehendes Modul erweitern')));
1096 $oForm->add_fld(
'existing_modul',
'select-single-label', label:
'Bestehendes Modul', rules:
'parameter', options: $this->options($this->module_options(
true)));
1097 $oForm->add_fld(
'xmodul',
'text-label', label:
'Neuer Modulname', rules:
'parameter|max=63', tooltip:
'Technischer Modulname, z.B. myShop');
1098 $oForm->add_fld(
'title',
'text-label', label:
'Titel', rules:
'varchar|max=120');
1099 $oForm->add_fld(
'module_template',
'select-single-label', label:
'Vorlage', rules:
'parameter', options: $this->options(array(
'form_report' =>
'Formular + Report',
'form' =>
'Nur Formular',
'report' =>
'Nur Report',
'api' =>
'API + Service',
'blank' =>
'Leeres Grundmodul')));
1100 $oForm->add_fld(
'default_run1',
'text-label', label:
'Start run1', rules:
'parameter|max=32');
1101 $oForm->add_fld(
'default_run2',
'text-label', label:
'Start run2', rules:
'parameter|max=32');
1102 $oForm->add_fld(
'dd_mode',
'select-single-label', label:
'DD', rules:
'parameter', options: $this->options(array(
'new' =>
'Neues DD erstellen',
'existing' =>
'Vorhandenes DD nutzen',
'none' =>
'Kein DD')));
1103 $oForm->add_fld(
'dd_ref',
'select-single-label', label:
'Vorhandenes DD', rules:
'parameter', options: $this->options($this->dd_options(
true)));
1104 $oForm->add_fld(
'dd_name',
'text-label', label:
'DD Name', rules:
'parameter|max=63');
1105 $oForm->add_fld(
'table_name',
'text-label', label:
'Tabelle', rules:
'parameter|max=63');
1106 $oForm->add_fld(
'db_file',
'text-label', label:
'SQLite *.db3', rules:
'parameter+.-_|max=80');
1107 $oForm->add_fld(
'field_preset',
'select-single-label', label:
'Felder', rules:
'parameter', options: $this->options(array(
'basic' =>
'Basis: Titel/Beschreibung',
'content' =>
'Content: Textfeld',
'status' =>
'Status',
'workflow' =>
'Workflow einfach')));
1108 $oForm->add_fld(
'create_include',
'checkbox-label', label:
'Service-Include', rules:
'int');
1109 $oForm->add_fld(
'create_form',
'checkbox-label', label:
'Formular erzeugen', rules:
'int');
1110 $oForm->add_fld(
'create_report',
'checkbox-label', label:
'Report erzeugen', rules:
'int');
1111 $oForm->add_fld(
'create_templates',
'checkbox-label', label:
'Templates erzeugen', rules:
'int');
1112 $oForm->add_fld(
'overwrite',
'checkbox-label', label:
'Bestehende Dateien ueberschreiben', rules:
'int');
1113 $oForm->add_fld(
'backup',
'checkbox-label', label:
'Vorher Modul-ZIP sichern', rules:
'int');
1114 $oForm->add_fld(
'sync_mode',
'select-single-label', label:
'DD->DB Sync', rules:
'parameter', options: $this->options(array(
'link' =>
'Sync-Link anzeigen',
'none' =>
'Nicht synchronisieren',
'apply' =>
'Direkt anwenden')));
1115 $oForm->add_fld(
'ki_package',
'checkbox-label', label:
'KI-Auftrags-ZIP anbieten', rules:
'int');
1117 if ($oForm->submit()) {
1118 $in = $this->collect_input($oForm);
1120 if ($this->validate_input($in, $errors) && !$oForm->errors()) {
1121 $this->generate_module($in, $log);
1122 $oForm->_msg_success =
'Wizard abgeschlossen.';
1124 foreach ($errors as $err) {
1125 $oForm->add_fld_error(
'xmodul', $err);
1127 $oForm->_msg_error =
'Bitte Eingaben pruefen.';
1131 return $oForm->run() . $this->log_html($log);
1134 public function run($run =
'') {
1135 $run3 =
dbx()->get_modul_var(
'dbx_run3',
'',
'parameter');
1136 if ($run3 ===
'restore_backup') {
1137 return $this->restore_module_backup();
1139 if ($run3 ===
'ki_zip') {
1140 $this->send_ki_zip();
1142 if ($run3 ===
'ki_api') {