dbXapp 2.0
RAD, CMS, Module und Runtime-IDE fuer dbXapp
Loading...
Searching...
No Matches
dbxEdit_fd.class.php
Go to the documentation of this file.
1<?php
2namespace dbx\dbxAdmin;
3
10class dbxEdit_fd
11{
12 private $_admin_modul = 'dbxAdmin';
13 private $_fd_field = 'dbxAdmin|ddedit-field';
14
15 public function run()
16 {
17 $work = dbx()->get_modul_request_var('dbx_run2', '');
18
19 switch ($work) {
20 case 'create_form_fd':
21 return $this->create_form_fd();
22
23 case 'delete_field':
24 return $this->delete_field();
25
26 case 'save_field_order':
27 return $this->save_field_order();
28
29 case 'create_from_dd':
30 return $this->create_from_dd();
31
32 case '':
33 case 'editor':
34 default:
35 return $this->run_editor();
36 }
37 }
38
39 private function run_editor()
40 {
41 list($modul, $fd) = $this->fd_params_from_request();
42
43 if (!$modul) {
44 return $this->alert('warning', 'Kein Modul angegeben.');
45 }
46
47 if (!$fd) {
48 return $this->create_from_dd();
49 }
50
51 $model = $this->load_model($modul, $fd);
52 if (!$model) {
53 return $this->alert('danger', 'FD nicht gefunden oder nicht lesbar: ' . dbx()->esc($modul . '|' . $fd));
54 }
55
56 $instance_id = $this->instance_id($modul . '_' . $fd);
57 $work_target_id = 'dbx_fdedit_work_' . $instance_id;
58 $fields = array_values((array)($model['fields'] ?? array()));
59 $work_content = count($fields)
60 ? $this->create_form_fd($modul, $fd, '0', $model)
61 : $this->create_form_fd($modul, $fd, 'new', $model);
62
63 $data = array(
64 'i' => $instance_id,
65 'modul' => $modul,
66 'fd' => $fd,
67 'message' => '',
68 'work_target_id' => $work_target_id,
69 'create_from_dd_url' => $this->build_url('create_from_dd', $modul, $fd),
70 'work_content' => $work_content,
71 'fields_order_report'=> $this->create_fields_order_report($modul, $fd, $model, $work_target_id),
72 );
73
74 $help = dbx()->get_include_obj('dbxAdminHelp', 'dbxAdmin');
75 $oTPL = dbx()->get_system_obj('dbxTPL');
76 $reloadAction = $oTPL->get_tpl('dbx|button-bar-reload-ajax', array(
77 'bar_reload_href' => '?dbx_modul=dbxAdmin&dbx_run1=edit_fd&modul=' . rawurlencode($modul) . '&fd=' . rawurlencode($fd),
78 'bar_reload_target' => 'dbx_fdedit_' . $instance_id,
79 'bar_reload_replace' => 'target',
80 ));
81 $barData = $help->moduleBarTemplateData('edit_fd', $reloadAction);
82 $barData['bar_title'] = 'FD bearbeiten: ' . $modul . '|' . $fd;
83 $barData['bar_class'] = 'dbx-module-bar dbx-ddedit-head';
84 $data = array_merge($data, $barData);
85
86 return $oTPL->get_tpl($this->_admin_modul . '|fdedit-frame', $data);
87 }
88
89 private function create_form_fd($modul = '', $fd = '', $field_pos = null, $model = array())
90 {
91 if (!$modul || !$fd) {
92 list($modul, $fd) = $this->fd_params_from_request();
93 }
94
95 if ($field_pos === null) {
96 $field_pos = dbx()->get_modul_request_var('field_pos', 'new');
97 }
98
99 if (!$model) {
100 $model = $this->load_model($modul, $fd);
101 }
102
103 if (!$model) {
104 return $this->alert('danger', 'FD nicht gefunden: ' . dbx()->esc($modul . '|' . $fd));
105 }
106
107 $fields = array_values((array)($model['fields'] ?? array()));
108 $is_new = ((string)$field_pos === 'new');
109
110 if ($is_new) {
111 $data = $this->default_field_record();
112 } else {
113 $pos = (int)$field_pos;
114 if (!isset($fields[$pos]) || !is_array($fields[$pos])) {
115 return $this->alert('warning', 'Feldposition nicht gefunden: ' . dbx()->esc((string)$field_pos));
116 }
117 $data = $fields[$pos];
118 }
119
120 $data['modul'] = $modul;
121 $data['dd'] = $fd;
122 $data['field_pos'] = (string)$field_pos;
123 $data['old_name'] = (string)($data['name'] ?? '');
124
125 $oForm = dbx()->get_system_obj('dbxForm');
126 $oForm->init('fdedit_field_' . $this->safe_id($modul . '_' . $fd . '_' . (string)$field_pos), 'ddedit-field-form');
127 $oForm->_fd = $this->_fd_field;
128 $oForm->_data = $data;
129 $oForm->_action = $this->build_url('create_form_fd', $modul, $fd, array('field_pos' => (string)$field_pos));
130 $oForm->_msg_info = $is_new
131 ? 'Bearbeite neues Feld fuer FD ' . $fd . '.'
132 : 'Bearbeite Feld ' . (string)($data['name'] ?? $field_pos) . '.';
133 $oForm->add_flds();
134
135 if ($oForm->submit()) {
136 if (!$oForm->errors()) {
137 $field = $this->merge_record($data, $oForm->_post, $this->field_keys());
138 $field = $this->strip_editor_keys($field);
139
140 $message = '';
141 if (!$this->validate_field_record($field, $fields, $is_new ? -1 : (int)$field_pos, $message)) {
142 $oForm->_msg_error = $message;
143 return $oForm->run();
144 }
145
146 if ($is_new) {
147 $fields[] = $field;
148 $field_pos = count($fields) - 1;
149 $is_new = false;
150 } else {
151 $fields[(int)$field_pos] = $field;
152 }
153
154 $model['fields'] = array_values($fields);
155 $ok = $this->save_model($modul, $fd, $model);
156
157 if ($ok) {
158 $field['modul'] = $modul;
159 $field['dd'] = $fd;
160 $field['field_pos'] = (string)$field_pos;
161 $field['old_name'] = (string)($field['name'] ?? '');
162
163 $oForm->_data = $field;
164 $oForm->_action = $this->build_url('create_form_fd', $modul, $fd, array('field_pos' => (string)$field_pos));
165 $oForm->_msg_success = 'Feld ' . (string)($field['name'] ?? $field_pos) . ' in FD ' . $fd . ' gespeichert.';
166 } else {
167 $oForm->_msg_error = 'Feld ' . (string)($field['name'] ?? $field_pos) . ' konnte in FD ' . $fd . ' nicht gespeichert werden.';
168 }
169 } else {
170 $oForm->_msg_error = 'Feld ' . (string)($data['name'] ?? $field_pos) . ' bitte pruefen.';
171 }
172 }
173
174 $delete_url = $this->build_url('delete_field', $modul, $fd, array('field_pos' => (string)$field_pos));
175
176 return str_replace(
177 '&dbx_run2=delete_field&modul={modul}&dd={dd}&field_pos={field_pos}',
178 dbx()->esc($delete_url),
179 $oForm->run()
180 );
181 }
182
183 private function create_fields_order_report($modul, $fd, $model, $target_id = '')
184 {
185 $rows = array();
186 foreach (array_values((array)($model['fields'] ?? array())) as $pos => $field) {
187 if (!is_array($field)) {
188 continue;
189 }
190
191 $row = $this->field_row_defaults();
192 foreach ($field as $key => $value) {
193 $row[$key] = is_array($value) ? implode(',', $value) : (string)$value;
194 }
195
196 $row['modul'] = $modul;
197 $row['dd'] = $fd;
198 $row['fd'] = $fd;
199 $row['field_pos'] = (string)$pos;
200 $row['sort_no'] = (string)($pos + 1);
201 $row['target_id'] = $target_id;
202 $row['form_url'] = $this->build_url('create_form_fd', $modul, $fd, array('field_pos' => (string)$pos));
203 $rows[] = $row;
204 }
205
206 $data = array(
207 'modul' => $modul,
208 'fd' => $fd,
209 'dd' => $fd,
210 'count' => count($rows),
211 'target_id' => $target_id,
212 'new_field_url' => $this->build_url('create_form_fd', $modul, $fd, array('field_pos' => 'new')),
213 );
214
215 $oReport = dbx()->get_system_obj('dbxReport');
216 $oReport->init('fdedit_fields_order_' . $this->safe_id($modul . '_' . $fd), 'fdedit-fields-order-report');
217 $oReport->_mode = 'tpl';
218 $oReport->_data = $data;
219 $oReport->_replaces = $data;
220 $oReport->_rdata = $rows;
221 $oReport->_rcount = count($rows);
222 $oReport->_rrows = 'auto';
223 $oReport->_pages = false;
224
225 return $oReport->run();
226 }
227
228 private function delete_field()
229 {
230 list($modul, $fd) = $this->fd_params_from_request();
231 $field_pos = (int)dbx()->get_modul_request_var('field_pos', -1);
232
233 $model = $this->load_model($modul, $fd);
234 if (!$model) {
235 return $this->alert('danger', 'FD nicht gefunden.');
236 }
237
238 $fields = array_values((array)($model['fields'] ?? array()));
239 if (!isset($fields[$field_pos])) {
240 return $this->alert('warning', 'Feld nicht gefunden.');
241 }
242
243 $name = (string)($fields[$field_pos]['name'] ?? $field_pos);
244 unset($fields[$field_pos]);
245 $model['fields'] = array_values($fields);
246
247 if ($this->save_model($modul, $fd, $model)) {
248 return $this->alert('success', 'Feld geloescht: ' . dbx()->esc($name));
249 }
250
251 return $this->alert('danger', 'Feld konnte nicht geloescht werden: ' . dbx()->esc($name));
252 }
253
254 private function save_field_order()
255 {
256 list($modul, $fd) = $this->fd_params_from_request();
257 $order = $this->parse_order(dbx()->get_modul_request_var('order', array()));
258
259 $model = $this->load_model($modul, $fd);
260 if (!$model) {
261 dbx()->json_response(array('ok' => 0, 'msg' => 'FD nicht gefunden.'));
262 }
263
264 $new = $this->reorder_records(array_values((array)($model['fields'] ?? array())), $order);
265 if ($new === false) {
266 dbx()->json_response(array('ok' => 0, 'msg' => 'Ungueltige Reihenfolge.'));
267 }
268
269 $model['fields'] = $new;
270 $ok = $this->save_model($modul, $fd, $model);
271
272 dbx()->json_response(array(
273 'ok' => $ok ? 1 : 0,
274 'msg' => $ok ? 'FD-Feldreihenfolge gespeichert.' : 'FD-Feldreihenfolge konnte nicht gespeichert werden.',
275 'count' => count($new),
276 ));
277
278 return '';
279 }
280
281 private function create_from_dd()
282 {
283 list($modul, $fd) = $this->fd_params_from_request();
284 $message = '';
285
286 $source_modul = $this->sanitize_name(dbx()->get_modul_request_var('source_modul', $modul ?: 'dbx'));
287 $source_dd = $this->sanitize_name(dbx()->get_modul_request_var('source_dd', ''));
288 $target_modul = $this->sanitize_name(dbx()->get_modul_request_var('target_modul', $modul ?: 'dbx'));
289 $target_fd = $this->sanitize_name(dbx()->get_modul_request_var('target_fd', $fd));
290 $overwrite = (string)dbx()->get_modul_request_var('overwrite', '') === '1';
291
292 if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST') {
293 if (!$source_modul || !$source_dd || !$target_modul || !$target_fd) {
294 $message = $this->alert('danger', 'Bitte Quell-DD und Ziel-FD vollstaendig angeben.');
295 } elseif ($this->fd_file_exists($target_modul, $target_fd) && !$overwrite) {
296 $message = $this->alert('warning', 'FD existiert bereits. Zum Ueberschreiben bitte die Checkbox aktivieren.');
297 } else {
298 $oDD = dbx()->get_system_obj('dbxDD');
299 $dd_model = $oDD->get_dd_model($source_modul . '|' . $source_dd);
300 $fields = is_array($dd_model['fields'] ?? null) ? array_values($dd_model['fields']) : array();
301
302 if (!count($fields)) {
303 $message = $this->alert('danger', 'Quell-DD hat keine Felddefinitionen oder konnte nicht gelesen werden.');
304 } else {
305 $ok = $this->save_model($target_modul, $target_fd, array('fields' => $fields));
306 if ($ok) {
307 $url = '?dbx_modul=' . $this->_admin_modul .
308 '&dbx_run1=edit_fd&modul=' . rawurlencode($target_modul) .
309 '&fd=' . rawurlencode($target_fd);
310 $message = $this->alert(
311 'success',
312 'FD ' . dbx()->esc($target_modul . '|' . $target_fd) . ' wurde aus DD ' .
313 dbx()->esc($source_modul . '|' . $source_dd) . ' erstellt. ' .
314 '<a class="btn btn-sm btn-primary ms-2" href="' . dbx()->esc($url) .
315 '">FD bearbeiten</a>'
316 );
317 } else {
318 $message = $this->alert('danger', 'FD konnte nicht geschrieben werden.');
319 }
320 }
321 }
322 }
323
324 $data = array(
325 'i' => $this->instance_id('create_fd_' . $target_modul . '_' . $target_fd),
326 'action' => '?dbx_modul=' . $this->_admin_modul . '&dbx_run1=edit_fd&dbx_run2=create_from_dd&modul=' . rawurlencode($target_modul) . '&fd=' . rawurlencode($target_fd),
327 'message' => $message,
328 'source_modul' => $source_modul,
329 'source_dd' => $source_dd,
330 'target_modul' => $target_modul,
331 'target_fd' => $target_fd,
332 'overwrite_checked' => $overwrite ? 'checked' : '',
333 );
334
335 $oTPL = dbx()->get_system_obj('dbxTPL');
336 return $oTPL->get_tpl($this->_admin_modul . '|fdedit-create-from-dd', $data);
337 }
338
339 private function load_model($modul, $fd)
340 {
341 if (!$modul || !$fd) {
342 return array();
343 }
344
345 $file = $this->fd_file_path($modul, $fd);
346 if (!is_file($file) || !is_readable($file)) {
347 return array();
348 }
349
350 $fields = array();
351 $field = array();
352 include $file;
353
354 return array('fields' => is_array($fields) ? array_values($fields) : array());
355 }
356
357 private function save_model($modul, $fd, $model)
358 {
359 if (!$modul || !$fd || !is_array($model)) {
360 return 0;
361 }
362
363 $fields = is_array($model['fields'] ?? null) ? array_values($model['fields']) : array();
364 $dir = dbx_get_base_dir() . 'dbx/modules/' . $modul . '/fd/';
365 if (function_exists('dbx_os_path_file')) {
367 }
368
369 if (!is_dir($dir) && !mkdir($dir, 0775, true)) {
370 return 0;
371 }
372
373 $file = $this->fd_file_path($modul, $fd);
374 $this->backup_fd_file($modul, $fd);
375
376 $content = "<?php\n\n";
377 foreach ($fields as $field) {
378 if (!is_array($field)) {
379 continue;
380 }
381 $field = $this->normalize_field_for_write($field);
382 $content .= "\$field = array();\n";
383 foreach ($field as $key => $value) {
384 $content .= "\$field[" . var_export((string)$key, true) . "]=" . var_export($value, true) . ";\n";
385 }
386 $content .= "\$fields[]=\$field;\n\n";
387 }
388
389 return file_put_contents($file, $content) !== false ? 1 : 0;
390 }
391
392 private function normalize_field_for_write($field)
393 {
394 $field = $this->strip_editor_keys($field);
395 $oDD = dbx()->get_system_obj('dbxDD');
396 if (is_object($oDD) && method_exists($oDD, 'normalize_dd_field')) {
397 return $oDD->normalize_dd_field($field);
398 }
399
400 $out = array();
401 foreach ($this->field_keys() as $key) {
402 if (array_key_exists($key, $field)) {
403 $out[$key] = $field[$key];
404 }
405 }
406 return $out;
407 }
408
409 private function backup_fd_file($modul, $fd)
410 {
411 $file = $this->fd_file_path($modul, $fd);
412 if (!is_file($file)) {
413 return;
414 }
415
416 $dir = dirname($file) . DIRECTORY_SEPARATOR . '_backup';
417 if (!is_dir($dir)) {
418 mkdir($dir, 0775, true);
419 }
420
421 if (is_dir($dir)) {
422 copy($file, $dir . DIRECTORY_SEPARATOR . $fd . '.' . date('Ymd-His') . '.fd.php');
423 }
424 }
425
426 private function fd_params_from_request()
427 {
428 $modul = $this->sanitize_name(dbx()->get_modul_request_var('modul', ''));
429 $fd = $this->sanitize_name(dbx()->get_modul_request_var('fd', ''));
430
431 if (!$fd) {
432 $fd = $this->sanitize_name(dbx()->get_modul_request_var('dd', ''));
433 }
434
435 if (!$modul) {
436 $modul = $this->sanitize_name(dbx()->get_modul_request_var('xmodul', ''));
437 }
438
439 if (!$modul) {
440 $modul = $this->sanitize_name($this->get_system_var('dbx_activ_modul', 'dbx'));
441 }
442
443 return array($modul, $fd);
444 }
445
446 private function fd_file_exists($modul, $fd)
447 {
448 return is_file($this->fd_file_path($modul, $fd));
449 }
450
451 private function fd_file_path($modul, $fd)
452 {
453 $file = dbx_get_base_dir() . 'dbx/modules/' . $modul . '/fd/' . $fd . '.fd.php';
454 return function_exists('dbx_os_path_file') ? dbx_os_path_file($file) : $file;
455 }
456
457 private function build_url($run2, $modul, $fd, $extra = array())
458 {
459 $url = '?dbx_modul=' . $this->_admin_modul .
460 '&dbx_run1=edit_fd' .
461 '&dbx_run2=' . rawurlencode($run2) .
462 '&modul=' . rawurlencode($modul) .
463 '&fd=' . rawurlencode($fd);
464
465 foreach ((array)$extra as $key => $value) {
466 $url .= '&' . rawurlencode((string)$key) . '=' . rawurlencode((string)$value);
467 }
468
469 return $url;
470 }
471
472 private function get_system_var($name, $default = '')
473 {
474 if (function_exists('dbx')) {
475 $obj = dbx();
476 if (is_object($obj) && method_exists($obj, 'get_system_var')) {
477 $value = $obj->get_system_var($name);
478 if ($value !== null && $value !== '') {
479 return $value;
480 }
481 }
482 }
483 return $default;
484 }
485
486 private function merge_record($old, $post, $keys)
487 {
488 $record = is_array($old) ? $old : array();
489 foreach ((array)$keys as $key) {
490 if (array_key_exists($key, (array)$post)) {
491 $record[$key] = $this->normalize_value($post[$key]);
492 }
493 }
494 return $record;
495 }
496
497 private function strip_editor_keys($record)
498 {
499 unset($record['modul'], $record['dd'], $record['fd'], $record['field_pos'], $record['old_name']);
500 return $record;
501 }
502
503 private function normalize_value($value)
504 {
505 if (is_array($value)) {
506 return implode(',', array_map('trim', $value));
507 }
508 return trim((string)$value);
509 }
510
511 private function parse_order($raw)
512 {
513 if (is_array($raw)) {
514 return array_values(array_map('intval', $raw));
515 }
516
517 $raw = trim((string)$raw);
518 if ($raw === '') {
519 return array();
520 }
521
522 if (substr($raw, 0, 1) === '[') {
523 $decoded = json_decode($raw, true);
524 if (is_array($decoded)) {
525 return array_values(array_map('intval', $decoded));
526 }
527 }
528
529 $parts = preg_split('/[|,\s;]+/', $raw);
530 return is_array($parts) ? array_values(array_map('intval', $parts)) : array();
531 }
532
533 private function reorder_records($records, $order)
534 {
535 $records = array_values((array)$records);
536 $count = count($records);
537 if (!$count) {
538 return array();
539 }
540 if (count($order) !== $count) {
541 return false;
542 }
543
544 $seen = array();
545 $new = array();
546 foreach ($order as $pos) {
547 $pos = (int)$pos;
548 if ($pos < 0 || $pos >= $count || isset($seen[$pos])) {
549 return false;
550 }
551 $seen[$pos] = 1;
552 $new[] = $records[$pos];
553 }
554 return $new;
555 }
556
557 private function validate_field_record($field, $fields, $self_pos, &$message)
558 {
559 $name = trim((string)($field['name'] ?? ''));
560 if (!$this->is_identifier($name)) {
561 $message = 'Ungueltiger Feldname: ' . $name;
562 return false;
563 }
564
565 $names = array();
566 foreach (array_values((array)$fields) as $pos => $old) {
567 if ((int)$pos === (int)$self_pos) {
568 continue;
569 }
570
571 $old_name = strtolower(trim((string)($old['name'] ?? '')));
572 if ($old_name !== '') {
573 $names[$old_name] = 1;
574 }
575 }
576
577 if (isset($names[strtolower($name)])) {
578 $message = 'Feldname doppelt: ' . $name;
579 return false;
580 }
581
582 return true;
583 }
584
585 private function is_identifier($name)
586 {
587 return (bool)preg_match('/^[A-Za-z_][A-Za-z0-9_]*$/', (string)$name);
588 }
589
590 private function sanitize_name($name)
591 {
592 $name = trim((string)$name);
593 return preg_match('/^[A-Za-z0-9_]+$/', $name) ? $name : '';
594 }
595
596 private function safe_id($value)
597 {
598 $value = preg_replace('/[^A-Za-z0-9_]+/', '_', (string)$value);
599 $value = trim($value, '_');
600 return $value ?: 'x';
601 }
602
603 private function instance_id($seed)
604 {
605 return $this->safe_id($seed . '_' . substr(md5((string)$seed), 0, 6));
606 }
607
608 private function alert($type, $msg)
609 {
610 $type = preg_replace('/[^a-z]/', '', (string)$type);
611 if (!$type) {
612 $type = 'info';
613 }
614 return '<div class="alert alert-' . $type . '">' . $msg . '</div>';
615 }
616
617 private function field_keys()
618 {
619 $oDD = dbx()->get_system_obj('dbxDD');
620 if (is_object($oDD) && method_exists($oDD, 'dd_field_schema_keys')) {
621 return array_merge(array('modul', 'dd', 'fd', 'field_pos', 'old_name'), $oDD->dd_field_schema_keys());
622 }
623
624 return array(
625 'modul',
626 'dd',
627 'fd',
628 'field_pos',
629 'old_name',
630 'name',
631 'type',
632 'index',
633 'length',
634 'default',
635 'label',
636 'rules',
637 'tooltip',
638 'errormsg',
639 'placeholder',
640 'convert',
641 'protect',
642 'group',
643 'mask',
644 'data',
645 'options',
646 'tpl',
647 'js',
648 'prompt',
649 );
650 }
651
652 private function default_field_record()
653 {
654 return array(
655 'name' => '',
656 'type' => 'varchar',
657 'index' => '',
658 'length' => '255',
659 'default' => '',
660 'label' => '',
661 'rules' => 'text',
662 'tooltip' => '',
663 'errormsg' => '',
664 'placeholder' => '',
665 'convert' => '',
666 'protect' => '0',
667 'group' => '',
668 'mask' => '',
669 'data' => '',
670 'options' => '',
671 'tpl' => 'text-label',
672 'js' => '',
673 'prompt' => '',
674 );
675 }
676
677 private function field_row_defaults()
678 {
679 return array(
680 'modul' => '',
681 'dd' => '',
682 'fd' => '',
683 'field_pos' => '',
684 'name' => '',
685 'type' => '',
686 'index' => '',
687 'length' => '',
688 'label' => '',
689 );
690 }
691}
692
693?>
$fields[]
Definition config.dd.php:16
$field
Definition config.dd.php:4
dbx_os_path_file($path_file)
Definition index.php:164
dbx_get_base_dir($cut_Data=0)
Definition index.php:157
$_SERVER['REQUEST_URI']