dbxapp 4.1.3
CMS, Shop, Workflows und modulare Geschäftsanwendungen
Loading...
Searching...
No Matches
dbxTrace.class.php
Go to the documentation of this file.
1<?php
2namespace dbx\dbxAdmin;
3
4dbx()->get_system_obj('dbxReport', 'use');
5
6class dbxTrace {
7
8 private $_trace_sys_fields = array(
9 'create_date' => 1,
10 'create_uid' => 1,
11 'update_date' => 1,
12 'update_uid' => 1,
13 'owner' => 1,
14 );
15
16 private function value_to_text($value) {
17 if ($value === null) {
18 return '';
19 }
20
21 if (is_bool($value)) {
22 return $value ? 'true' : 'false';
23 }
24
25 if (is_array($value) || is_object($value)) {
26 return json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
27 }
28
29 return (string) $value;
30 }
31
32 private function is_empty_value($value) {
33 if ($value === null) {
34 return true;
35 }
36
37 if (is_string($value) && trim($value) === '') {
38 return true;
39 }
40
41 return false;
42 }
43
44 private function is_trace_sys_field($key) {
45 return isset($this->_trace_sys_fields[$key]);
46 }
47
48 private function get_visible_values($data, $hideEmpty = true) {
49 $values = array();
50 $hiddenSys = 0;
51
52 if (!is_array($data)) {
53 return array($values, $hiddenSys);
54 }
55
56 foreach ($data as $key => $value) {
57 if ($this->is_trace_sys_field($key)) {
58 $hiddenSys++;
59 continue;
60 }
61
62 if ($hideEmpty && $this->is_empty_value($value)) {
63 continue;
64 }
65
66 $values[$key] = $value;
67 }
68
69 return array($values, $hiddenSys);
70 }
71
72 private function make_trace_summary($json, $action) {
73 $data = json_decode((string) $json, true);
74
75 if (!is_array($data)) {
76 return array('summary' => 'Rohdaten', 'fields' => '', 'detail' => (string) $json);
77 }
78
79 $traceAction = $data['action'] ?? $action;
80 $before = $data['before'] ?? null;
81 $delta = $data['delta'] ?? null;
82 $detail = array();
83
84 if ($traceAction == 'delete') {
85 list($values, $hiddenSys) = $this->get_visible_values($before);
86 $count = count($values);
87
88 foreach ($values as $key => $value) {
89 $detail[] = $key . ': ' . $this->value_to_text($value);
90 }
91
92 return array(
93 'summary' => $count ? $count . ' Werte geloescht' : ($hiddenSys ? 'nur Systemfelder' : 'keine Werte'),
94 'fields' => implode(', ', array_slice(array_keys($values), 0, 4)),
95 'detail' => implode("\n", $detail),
96 );
97 }
98
99 if ($traceAction == 'insert') {
100 list($values, $hiddenSys) = $this->get_visible_values($delta);
101 $count = count($values);
102
103 foreach ($values as $key => $value) {
104 $detail[] = $key . ': ' . $this->value_to_text($value);
105 }
106
107 return array(
108 'summary' => $count ? $count . ' Werte gesetzt' : ($hiddenSys ? 'nur Systemfelder' : 'keine Werte'),
109 'fields' => implode(', ', array_slice(array_keys($values), 0, 4)),
110 'detail' => implode("\n", $detail),
111 );
112 }
113
114 if (!is_array($delta)) {
115 return array('summary' => 'keine Aenderung', 'fields' => '', 'detail' => '');
116 }
117
118 if (!is_array($before)) {
119 $before = array();
120 }
121
122 $fields = array();
123 $hiddenSys = 0;
124
125 foreach ($delta as $key => $newValue) {
126 if ($this->is_trace_sys_field($key)) {
127 $hiddenSys++;
128 continue;
129 }
130
131 $fields[] = $key;
132 $oldValue = array_key_exists($key, $before) ? $before[$key] : null;
133 $detail[] = $key . ': ' . $this->value_to_text($oldValue) . ' -> ' . $this->value_to_text($newValue);
134 }
135
136 $count = count($fields);
137
138 return array(
139 'summary' => $count ? $count . ' Felder geaendert' : ($hiddenSys ? 'nur Systemfelder' : 'keine Aenderung'),
140 'fields' => implode(', ', array_slice($fields, 0, 4)),
141 'detail' => implode("\n", $detail),
142 );
143 }
144
145 private function render_trace_json($json, $action) {
146 $summary = $this->make_trace_summary($json, $action);
147 $title = $summary['detail'];
148 $payload = json_decode((string) $json, true);
149
150 if (is_array($payload)) {
151 $context = array();
152
153 if (!empty($payload['source'])) {
154 $context[] = 'source: ' . $payload['source'];
155 }
156
157 if (!empty($payload['modul'])) {
158 $run = $payload['modul'];
159
160 if (!empty($payload['run1'])) $run .= '/' . $payload['run1'];
161 if (!empty($payload['run2'])) $run .= '/' . $payload['run2'];
162 if (!empty($payload['run3'])) $run .= '/' . $payload['run3'];
163
164 $context[] = 'run: ' . $run;
165 }
166
167 if ($context) {
168 $title = implode("\n", $context) . ($title ? "\n\n" . $title : '');
169 }
170 }
171
172 if ($title === '') {
173 $title = $summary['summary'];
174 }
175
176 $html = '<span class="dbx-trace-summary" title="' . dbx()->esc($title) . '">';
177 $html .= '<strong>' . dbx()->esc($summary['summary']) . '</strong>';
178
179 if ($summary['fields']) {
180 $html .= '<span class="dbx-trace-summary-fields">' . dbx()->esc($summary['fields']) . '</span>';
181 }
182
183 $html .= '</span>';
184
185 return $html;
186 }
187
188 public function report_trace_row_action_data($oObj, $data) {
189 if (!is_array($data) || ($data['type'] ?? '') != 'show') {
190 return $data;
191 }
192
193 $record = $data['record'] ?? array();
194
195 if (!is_array($record)) {
196 return $data;
197 }
198
199 $dd = isset($record['dd']) ? urlencode((string) $record['dd']) : '';
200 $rid = isset($record['record_id']) ? urlencode((string) $record['record_id']) : '';
201
202 $data['data']['action'] = '?dbx_modul=dbxAdmin&dbx_run1=trace&dbx_run2=record_history&trace_dd=' . $dd . '&trace_rid=' . $rid;
203 $data['data']['class'] = 'openWin';
204 $data['data']['tooltip'] = 'Historie anzeigen';
205
206 return $data;
207 }
208
209 public function report_trace_body($oObj, $content) {
210 $record = $oObj->get_record();
211 $action = $record['action'] ?? '';
212
213 if (isset($record['action'])) {
214 $badge = 'secondary';
215
216 if ($action == 'insert') $badge = 'success';
217 if ($action == 'update') $badge = 'primary';
218 if ($action == 'delete') $badge = 'danger';
219
220 $record['action'] = '<span class="badge bg-' . $badge . '">' . dbx()->esc($action) . '</span>';
221 }
222
223 if (isset($record['data_json'])) {
224 $record['data_json'] = $this->render_trace_json($record['data_json'], $action);
225 }
226
227 $oObj->_class_body['data_json'] = 'dbx-trace-data';
228 $oObj->set_record($record);
229
230 return $content;
231 }
232
233 private function get_trace_payload($json) {
234 $data = json_decode((string) $json, true);
235
236 return is_array($data) ? $data : array();
237 }
238
239 private function render_history_payload($row) {
240 $payload = $this->get_trace_payload($row['data_json'] ?? '');
241 $before = $payload['before'] ?? array();
242 $delta = $payload['delta'] ?? array();
243 $action = $payload['action'] ?? ($row['action'] ?? '');
244
245 if (!is_array($before)) {
246 $before = array();
247 }
248
249 if (!is_array($delta)) {
250 $delta = array();
251 }
252
253 $html = '<div class="dbx-trace-history-detail">';
254
255 if ($action == 'update') {
256 foreach ($delta as $key => $newValue) {
257 $oldValue = array_key_exists($key, $before) ? $before[$key] : '';
258 $html .= '<div class="dbx-trace-history-row">';
259 $html .= '<strong>' . dbx()->esc($key) . '</strong>';
260 $html .= '<span class="dbx-trace-old">' . dbx()->esc($this->value_to_text($oldValue)) . '</span>';
261 $html .= '<span class="dbx-trace-arrow">&rarr;</span>';
262 $html .= '<span class="dbx-trace-new">' . dbx()->esc($this->value_to_text($newValue)) . '</span>';
263 $html .= '</div>';
264 }
265 } else {
266 $values = ($action == 'delete') ? $before : $delta;
267
268 foreach ($values as $key => $value) {
269 if ($value === null || (is_string($value) && trim($value) === '')) {
270 continue;
271 }
272
273 $html .= '<div class="dbx-trace-history-row">';
274 $html .= '<strong>' . dbx()->esc($key) . '</strong>';
275 $html .= '<span>' . dbx()->esc($this->value_to_text($value)) . '</span>';
276 $html .= '</div>';
277 }
278 }
279
280 $html .= '</div>';
281
282 return $html;
283 }
284
285 private function get_compare_fields($before, $after) {
286 $fields = array();
287
288 if (is_array($before)) {
289 foreach ($before as $key => $value) {
290 $fields[$key] = 1;
291 }
292 }
293
294 if (is_array($after)) {
295 foreach ($after as $key => $value) {
296 $fields[$key] = 1;
297 }
298 }
299
300 return array_keys($fields);
301 }
302
303 private function render_compare_value($value) {
304 $text = $this->value_to_text($value);
305
306 if ($text === '') {
307 return '<span class="dbx-trace-empty">leer</span>';
308 }
309
310 return dbx()->esc($text);
311 }
312
313 private function trace_detail($trace_id) {
314 $row = $this->get_trace_row($trace_id);
315
316 if (!$row) {
317 return '<div class="container-fluid dbxReport dbx-trace-detail"><div class="alert alert-danger">Trace-Eintrag nicht gefunden.</div></div>';
318 }
319
320 $payload = $this->get_trace_payload($row['data_json'] ?? '');
321 $action = $payload['action'] ?? ($row['action'] ?? '');
322 $before = $payload['before'] ?? array();
323 $after = $payload['delta'] ?? array();
324 $source = $payload['source'] ?? '';
325 $modul = $payload['modul'] ?? '';
326 $run1 = $payload['run1'] ?? '';
327 $run2 = $payload['run2'] ?? '';
328 $run3 = $payload['run3'] ?? '';
329
330 if (!is_array($before)) {
331 $before = array();
332 }
333
334 if (!is_array($after)) {
335 $after = array();
336 }
337
338 $fields = $this->get_compare_fields($before, $after);
339
340 $html = '<div class="container-fluid dbxReport dbx-trace-detail">';
341 $html .= '<h3>' . dbx()->esc($row['dd'] ?? '') . ' #' . dbx()->esc($row['record_id'] ?? '') . ' - ' . dbx()->esc($action) . '</h3>';
342 $runPath = $modul;
343 if ($run1) $runPath .= '/' . $run1;
344 if ($run2) $runPath .= '/' . $run2;
345 if ($run3) $runPath .= '/' . $run3;
346
347 $html .= '<div class="mb-2 text-muted">' . dbx()->esc($row['create_date'] ?? '');
348
349 if ($source || $runPath) {
350 $html .= ' | ';
351 if ($source) $html .= 'source: ' . dbx()->esc($source);
352 if ($source && $runPath) $html .= ' | ';
353 if ($runPath) $html .= 'run: ' . dbx()->esc($runPath);
354 }
355
356 $html .= '</div>';
357 $html .= '<table class="table table-striped table-bordered table-light table-hover">';
358 $html .= '<thead><tr><th>Feld</th><th>Vorher</th><th>Nachher</th></tr></thead><tbody>';
359
360 foreach ($fields as $field) {
361 $oldValue = array_key_exists($field, $before) ? $before[$field] : '';
362 $newValue = array_key_exists($field, $after) ? $after[$field] : '';
363
364 if ($action == 'delete') {
365 $newValue = '';
366 }
367
368 $html .= '<tr>';
369 $html .= '<th class="text-nowrap">' . dbx()->esc($field) . '</th>';
370 $html .= '<td class="dbx-trace-before">' . $this->render_compare_value($oldValue) . '</td>';
371 $html .= '<td class="dbx-trace-after">' . $this->render_compare_value($newValue) . '</td>';
372 $html .= '</tr>';
373 }
374
375 if (!count($fields)) {
376 $html .= '<tr><td colspan="3">Keine Feldwerte vorhanden.</td></tr>';
377 }
378
379 $html .= '</tbody></table>';
380 $html .= '<div class="d-flex gap-2 justify-content-end">';
381
382 if ($action == 'delete') {
383 $html .= '<a class="btn btn-primary" href="?dbx_modul=dbxAdmin&dbx_run1=trace&dbx_run2=undelete&trace_id=' . dbx()->esc($row['id'] ?? '') . '">Undelete</a>';
384 } elseif ($action == 'update') {
385 $html .= '<a class="btn btn-primary" href="?dbx_modul=dbxAdmin&dbx_run1=trace&dbx_run2=restore&trace_id=' . dbx()->esc($row['id'] ?? '') . '">Vorherigen Stand wiederherstellen</a>';
386 }
387
388 $html .= '</div>';
389 $html .= '</div>';
390
391 return $html;
392 }
393
394 private function get_trace_row($trace_id) {
395 $db = dbx()->get_system_obj('dbxDB');
396 $rows = $db->select('dbxTrace', array('id' => $trace_id), '*', 'id', 'DESC', '', 1, 0);
397
398 if (is_array($rows) && isset($rows[0])) {
399 return $rows[0];
400 }
401
402 return array();
403 }
404
405 private function restore_trace_before($trace_id) {
406 $db = dbx()->get_system_obj('dbxDB');
407 $row = $this->get_trace_row($trace_id);
408
409 if (!$row) {
410 return '<div class="alert alert-danger">Trace-Eintrag nicht gefunden.</div>';
411 }
412
413 $payload = $this->get_trace_payload($row['data_json'] ?? '');
414 $before = $payload['before'] ?? array();
415 $dd = $row['dd'] ?? '';
416 $rid = $row['record_id'] ?? 0;
417
418 if (!$dd || !$rid || !is_array($before) || !count($before)) {
419 return '<div class="alert alert-warning">Dieser Stand kann nicht wiederhergestellt werden.</div>';
420 }
421
422 $pk = $db->get_dd_primary($dd);
423 if (!$pk) {
424 $pk = 'id';
425 }
426
427 $ok = $db->update($dd, $before, array($pk => $rid));
428
429 if ($ok > 0) {
430 return '<div class="alert alert-success">Stand wurde wiederhergestellt.</div>' . $this->record_history($dd, $rid);
431 }
432
433 return '<div class="alert alert-danger">Stand konnte nicht wiederhergestellt werden.</div>' . $this->record_history($dd, $rid);
434 }
435
436 private function undelete_trace_row($trace_id) {
437 $db = dbx()->get_system_obj('dbxDB');
438 $row = $this->get_trace_row($trace_id);
439
440 if (!$row) {
441 return '<div class="alert alert-danger">Trace-Eintrag nicht gefunden.</div>';
442 }
443
444 $payload = $this->get_trace_payload($row['data_json'] ?? '');
445 $before = $payload['before'] ?? array();
446 $dd = $row['dd'] ?? '';
447 $rid = $row['record_id'] ?? 0;
448
449 if (($row['action'] ?? '') != 'delete' || !$dd || !is_array($before) || !count($before)) {
450 return '<div class="alert alert-warning">Dieser Datensatz kann nicht per Undelete reaktiviert werden.</div>';
451 }
452
453 $ok = $db->insert($dd, $before);
454
455 if ($ok > 0) {
456 return '<div class="alert alert-success">Datensatz wurde reaktiviert.</div>' . $this->record_history($dd, $rid);
457 }
458
459 return '<div class="alert alert-danger">Datensatz konnte nicht reaktiviert werden.</div>' . $this->record_history($dd, $rid);
460 }
461
462 private function record_history($dd = '', $rid = 0) {
463 $db = dbx()->get_system_obj('dbxDB');
464
465 if (!$dd) {
466 $dd = dbx()->get_modul_var('trace_dd', '', 'parameter');
467 }
468
469 if (!$rid) {
470 $rid = dbx()->get_modul_var('trace_rid', 0, 'int');
471 }
472
473 $where = array(
474 'dd' => $dd,
475 'record_id' => $rid,
476 );
477
478 $rows = $db->select('dbxTrace', $where, '*', 'create_date', 'DESC', '', 200, 0);
479
480 if (!is_array($rows)) {
481 $rows = array();
482 }
483
484 $html = '<div class="container-fluid dbxReport dbx-trace-history">';
485 $html .= '<h3>Historie ' . dbx()->esc($dd) . ' #' . dbx()->esc($rid) . '</h3>';
486 $html .= '<table class="table table-striped table-bordered table-light table-hover">';
487 $html .= '<thead><tr><th></th><th>Zeit</th><th>Aktion</th><th>Aenderung</th></tr></thead><tbody>';
488
489 foreach ($rows as $row) {
490 $action = $row['action'] ?? '';
491 $detailUrl = '?dbx_modul=dbxAdmin&dbx_run1=trace&dbx_run2=trace_detail&trace_id=' . urlencode((string) ($row['id'] ?? ''));
492
493 $html .= '<tr>';
494 $html .= '<td class="td-show text-center align-middle">';
495 $html .= '<a class="btn-inline" href="' . dbx()->esc($detailUrl) . '" data-dbx-tooltip="Trace-Detail anzeigen" data-url="' . dbx()->esc($detailUrl) . '" data-title="Trace Detail" data-width="1200" role="button"><i class="bi bi-search"></i></a>';
496 $html .= '</td>';
497 $html .= '<td class="text-nowrap">' . dbx()->esc($row['create_date'] ?? '') . '</td>';
498 $html .= '<td>' . dbx()->esc($action) . '</td>';
499 $html .= '<td>' . $this->render_history_payload($row) . '</td>';
500 $html .= '</tr>';
501 }
502
503 if (!count($rows)) {
504 $html .= '<tr><td colspan="4">Keine Historie gefunden.</td></tr>';
505 }
506
507 $html .= '</tbody></table></div>';
508
509 return $html;
510 }
511
512 private function report_trace() {
513 $dd = 'dbxTrace';
514 $db = dbx()->get_system_obj('dbxDB');
515 $oReport = dbx()->get_system_obj('dbxReport');
516
517 $oReport->init('report-trace');
518 $oReport->_fd = 'dbxAdmin|rpt-trace-selection';
519 $oReport->load_fd_messages();
520 $oReport->add_module_bar(
521 $oReport->get_fd_message('bar_title'),
522 'bi-clock-history',
523 $oReport->get_fd_message('bar_subtitle')
524 );
525 $oReport->_dd = $dd;
526 $oReport->set_tabel_tpl('tpl_row_show', 'dbxAdmin|table_row_trace_show');
527
528 $work = dbx()->get_modul_var('dbx_do', '', 'parameter');
529 $rid = dbx()->get_modul_var('rid', 0, 'int');
530
531 if ($work == 'row_delete' && $rid) {
532 $ok = $oReport->del_selected($rid);
533 $ok = $db->delete($dd, $rid);
534
535 if ($ok) {
536 $oReport->_msg_success = $oReport->get_fd_message(
537 'row_delete_success'
538 );
539 } else {
540 $oReport->_msg_error = $oReport->get_fd_message(
541 'row_delete_error'
542 );
543 }
544 }
545
546 if ($work == 'multi_delete') {
547 $result = $oReport->delete_multi_selected_records($dd);
548 $oReport->apply_multi_delete_result($result);
549 }
550
551 if ($work == 'delete_tab') {
552 $ok = $db->delete_tab($dd);
553 $optimizeOk = $ok ? $db->optimize_tab($dd) : 0;
554 dbx()->debug("##delete tab dd=($dd) ok=($ok)");
555 dbx()->debug("##optimize tab dd=($dd) ok=($optimizeOk)");
556 if ($ok && $optimizeOk) {
557 $oReport->_msg_success = $oReport->get_fd_message(
558 'delete_tab_success'
559 );
560 } elseif ($ok) {
561 $oReport->_msg_success = $oReport->get_fd_message(
562 'delete_tab_partial'
563 );
564 } else {
565 $oReport->_msg_error = $oReport->get_fd_message(
566 'delete_tab_error'
567 );
568 }
569 }
570
571 $flds['id'] = 'ID';
572 $flds['create_date'] = $oReport->get_fd_message('column_created');
573 $flds['action'] = $oReport->get_fd_message('column_action');
574 $flds['dd'] = $oReport->get_fd_message('column_dd');
575 $flds['record_id'] = $oReport->get_fd_message('column_record');
576 $flds['owner'] = $oReport->get_fd_message('column_owner');
577 $flds['data_json'] = $oReport->get_fd_message('column_change');
578
579 $rformat['create_date'] = 'php-datetime-usr';
580 $rformat['action'] = 'html';
581 $rformat['data_json'] = 'html';
582 $oReport->_rpt_format = $rformat;
583
584 $oReport->_action = '?dbx_modul=dbxAdmin&dbx_run1=trace&dbx_run2=list_trace';
585 $oReport->_pages = true;
586 $oReport->_create_row_select = true;
587 $oReport->_create_row_edit = false;
588 $oReport->_create_row_show = true;
589 $oReport->_create_row_delete = true;
590 $oReport->_create_sel_flds = true;
591 $oReport->_but_pagination = 5;
592 $oReport->set_callback_owner($this);
593 $oReport->set_body_callback('report_trace_body');
594 $oReport->set_callback('row_action_data', 'report_trace_row_action_data');
595
596 $oReport->enable_delete_tab($dd);
597
598 $oReport->add_action('rows_delete' , 'action_button_delete' , '&dbx_do=multi_delete');
599
600 $oReport->create_selection_fields('dbxAdmin|rpt-trace-selection');
601 $oReport->add_module_bar(
602 $oReport->get_fd_message('bar_title'),
603 'bi-clock-history',
604 $oReport->get_fd_message('bar_subtitle')
605 );
606
607 if ($work != 'delete_tab' && $work != 'multi_delete' && $work != 'row_delete' && $oReport->submit()) {
608 if (!$oReport->errors()) {
609 $oReport->_msg_success = '';
610 } else {
611 $oReport->_msg_error = $oReport->get_fd_message(
612 'validation_error'
613 );
614 }
615 }
616
617 $rwhere = $oReport->get_fld_val('dbx_rwhere' , '' , 'sqlsearch|max=64');
618 $rrows = $oReport->get_fld_val('dbx_rrows' , 15 , 'int');
619 $rpos = $oReport->get_fld_val('dbx_rpos' , 0 , 'int');
620 $rsort = $oReport->get_fld_val('dbx_rsort' , 'create_date', 'parameter');
621 $rdesc = $oReport->get_fld_val('dbx_rdesc' , 'DESC' , 'parameter');
622 $select = $oReport->get_fld_val('dbx_rselect', 0 , 'int');
623 $rgroup = '';
624
625 if ($rwhere != '') {
626 $rwhere = array(
627 'search' => array(
628 'value' => $rwhere,
629 'like' => array('action', 'dd', 'data_json'),
630 'equal' => array('record_id', 'owner'),
631 'mode' => 'contains',
632 ),
633 );
634 }
635
636 if ($select) {
637 if (is_array($rwhere)) {
638 $rwhere = $db->normalize_where($dd, $rwhere);
639 }
640
641 $rwhere = $oReport->add_rwhere_select($rwhere);
642 }
643
644 $oReport->_rflds = $flds;
645 $oReport->_rrows = $rrows;
646 $oReport->_rpos = $rpos;
647 $oReport->_count_all = $db->count($dd);
648 $oReport->_rcount = $db->count($dd, $rwhere);
649 $oReport->_rdata = $db->select($dd, $rwhere, $flds, $rsort, $rdesc, $rgroup, $rrows, $rpos);
650
651 return $oReport->run();
652 }
653
654 public function run() {
655 $run2 = dbx()->get_modul_var('dbx_run2', '', 'parameter');
656
657 if ($run2 == 'record_history') {
658 return $this->record_history();
659 }
660
661 if ($run2 == 'trace_detail') {
662 $trace_id = dbx()->get_modul_var('trace_id', 0, 'int');
663 return $this->trace_detail($trace_id);
664 }
665
666 if ($run2 == 'restore') {
667 $trace_id = dbx()->get_modul_var('trace_id', 0, 'int');
668 return $this->restore_trace_before($trace_id);
669 }
670
671 if ($run2 == 'undelete') {
672 $trace_id = dbx()->get_modul_var('trace_id', 0, 'int');
673 return $this->undelete_trace_row($trace_id);
674 }
675
676 return $this->report_trace();
677 }
678}
679?>
if($updated !==1) $after
report_trace_row_action_data($oObj, $data)
report_trace_body($oObj, $content)
dbx()
Liefert die zentrale dbXapp-API als Singleton.
Definition dbxApi.php:2805
if(!empty( $validation[ 'errors'])) if($service->isAllowedDesignFile('../escape.php')|| $service->isAllowedDesignFile('htm/evil.php')) $delta
DBX schema administration.