dbXapp 2.0
RAD, CMS, Module und Runtime-IDE fuer dbXapp
Loading...
Searching...
No Matches
dbxContactAdmin.class.php
Go to the documentation of this file.
1<?php
2namespace dbx\dbxContact_admin;
3
4require_once dirname(__DIR__, 2) . '/dbxContact/include/dbxContactConfig.class.php';
5require_once dirname(__DIR__, 2) . '/dbxContact/include/dbxContactTicket.class.php';
6require_once dirname(__DIR__, 2) . '/dbxContact/include/dbxContactContentProvision.class.php';
7
8use dbx\dbxContact\dbxContactConfig;
9use dbx\dbxContact\dbxContactContentProvision;
10use dbx\dbxContact\dbxContactTicket;
11
13
14 private function db() {
15 return dbx()->get_system_obj('dbxDB');
16 }
17
18 private function tpl() {
19 return dbx()->get_system_obj('dbxTPL');
20 }
21
22 private function h($value): string {
23 return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
24 }
25
26 private function alert(string $type, string $message): string {
27 return $this->tpl()->get_tpl('dbx|alert-' . $type, array('msg' => $message));
28 }
29
30 private function schemaReady(): bool {
31 $db = $this->db();
32 $server = 'dbxContact|dbxContact.db3';
33 if (!$db->connect_db_server($server)) {
34 return false;
35 }
36
37 $tables = $db->select_query($server, "SELECT name FROM sqlite_master WHERE type='table' AND name='contact_message' LIMIT 1");
38 if (!is_array($tables) || empty($tables)) {
39 return false;
40 }
41
42 $columns = $db->select_query($server, 'PRAGMA table_info(contact_request)');
43 $names = array();
44 foreach ((array) $columns as $column) {
45 $names[] = (string) ($column['name'] ?? '');
46 }
47
48 return in_array('priority', $names, true)
49 && in_array('last_activity_date', $names, true)
50 && in_array('user_hidden', $names, true);
51 }
52
53 private function statusClass(string $status): string {
54 return array(
55 'open' => 'text-bg-primary',
56 'in_progress' => 'text-bg-info',
57 'waiting_customer' => 'text-bg-warning',
58 'answered' => 'text-bg-success',
59 'closed' => 'text-bg-secondary',
60 )[$status] ?? 'text-bg-light';
61 }
62
63 private function priorityClass(string $priority): string {
64 return array(
65 'low' => 'text-bg-secondary',
66 'normal' => 'text-bg-primary',
67 'high' => 'text-bg-warning',
68 'urgent' => 'text-bg-danger',
69 )[$priority] ?? 'text-bg-light';
70 }
71
72 private function counts(): array {
73 $db = $this->db();
74 return array(
75 'count_open' => max(0, (int) $db->count(dbxContactTicket::DD_TICKET, array('status' => 'open'))),
76 'count_progress' => max(0, (int) $db->count(dbxContactTicket::DD_TICKET, array('status' => 'in_progress'))),
77 'count_waiting' => max(0, (int) $db->count(dbxContactTicket::DD_TICKET, array('status' => 'waiting_customer'))),
78 'count_answered' => max(0, (int) $db->count(dbxContactTicket::DD_TICKET, array('status' => 'answered'))),
79 'count_closed' => max(0, (int) $db->count(dbxContactTicket::DD_TICKET, array('status' => 'closed'))),
80 );
81 }
82
83 private function reportStatusStatsHtml(): string {
84 $counts = $this->counts();
85 return ''
86 . '<span class="dbx-report-bar-stat ms-3"><span class="badge text-bg-primary">Offen: ' . (int) $counts['count_open'] . '</span></span>'
87 . '<span class="dbx-report-bar-stat"><span class="badge text-bg-info">In Bearbeitung: ' . (int) $counts['count_progress'] . '</span></span>'
88 . '<span class="dbx-report-bar-stat"><span class="badge text-bg-warning">Rueckfragen: ' . (int) $counts['count_waiting'] . '</span></span>'
89 . '<span class="dbx-report-bar-stat"><span class="badge text-bg-success">Beantwortet: ' . (int) $counts['count_answered'] . '</span></span>'
90 . '<span class="dbx-report-bar-stat"><span class="badge text-bg-secondary">Geschlossen: ' . (int) $counts['count_closed'] . '</span></span>';
91 }
92
93 private function frame(string $content, string $title = 'Kontaktanfragen und Tickets'): string {
94 $actions = ''
95 . '<a class="btn btn-outline-secondary btn-sm" href="?dbx_modul=dbxContact_admin&dbx_run1=install"><i class="bi bi-database-gear"></i> Installation</a>'
96 . '<a class="btn btn-primary btn-sm" href="?dbx_modul=dbxContact_admin&dbx_run1=list"><i class="bi bi-arrow-clockwise"></i> Aktualisieren</a>';
97
98 return $this->tpl()->get_tpl('dbxContact_admin|ticket-admin-shell', array_merge(
99 $this->counts(),
100 array(
101 'bar_title' => $this->h($title),
102 'bar_icon' => 'bi-life-preserver',
103 'bar_subtitle' => 'Anfragen, Antworten, Status und vollstaendiger Verlauf',
104 'bar_title_pre' => '',
105 'bar_title_heading_attrs' => '',
106 'bar_middle' => '',
107 'bar_extra' => '',
108 'bar_actions' => $actions,
109 'content' => $content,
110 )
111 ));
112 }
113
114 private function install(): string {
115 $dd = dbx()->get_system_obj('dbxDD');
116 $results = array();
117
118 foreach (array('contactMessage') as $name) {
119 $dd->sync_dd_to_db('dbxContact', $name, 'reset');
120 $state = array();
121 for ($i = 0; $i < 100; $i++) {
122 $state = $dd->sync_dd_to_db('dbxContact', $name, 'apply');
123 if (in_array((string) ($state['status'] ?? ''), array('finished', 'error'), true)) {
124 break;
125 }
126 }
127 $results[$name] = $state;
128 }
129
130 foreach ($results as $name => $state) {
131 if ((string) ($state['status'] ?? '') !== 'finished') {
132 return $this->frame($this->alert('danger', 'Installation von ' . $this->h($name) . ' fehlgeschlagen: ' . $this->h($state['message'] ?? 'unbekannt')));
133 }
134 }
135
136 $name = 'contactRequest';
137 $dd->sync_dd_to_db('dbxContact', $name, 'reset');
138 for ($i = 0; $i < 100; $i++) {
139 $state = $dd->sync_dd_to_db('dbxContact', $name, 'force');
140 if (in_array((string) ($state['status'] ?? ''), array('finished', 'error'), true)) {
141 break;
142 }
143 }
144 $results[$name] = $state;
145
146 foreach ($results as $name => $state) {
147 if ((string) ($state['status'] ?? '') !== 'finished') {
148 return $this->frame($this->alert('danger', 'Installation von ' . $this->h($name) . ' fehlgeschlagen: ' . $this->h($state['message'] ?? 'unbekannt')));
149 }
150 }
151
152 $db = $this->db();
153 $rows = $db->select(dbxContactTicket::DD_TICKET, '', '*', 'id', 'ASC', '', 100000, 0, 0);
154 foreach ((array) $rows as $ticket) {
156 if (trim((string) ($ticket['last_activity_date'] ?? '')) === '') {
157 $last = (string) ($ticket['create_date'] ?? date('Y-m-d H:i:s'));
158 $db->update(dbxContactTicket::DD_TICKET, array(
159 'priority' => dbxContactTicket::normalizePriority((string) ($ticket['priority'] ?? 'normal')),
160 'last_activity_date' => $last,
161 ), (int) ($ticket['id'] ?? 0), 0, 1, 1, 0);
162 }
163 }
164 $contentPageId = dbxContactContentProvision::run($db);
165
166 return $this->frame(
167 $this->alert('success', 'Ticket-Datenmodell installiert.')
168 . ($contentPageId > 0
169 ? '<div class="alert alert-info mx-3">Die dbxContent-Seite „Meine Anfragen“ ist unter <a href="meine-anfragen">meine-anfragen</a> verfuegbar.</div>'
170 : '<div class="alert alert-warning mx-3">Die dbxContent-Seite „Meine Anfragen“ konnte nicht automatisch angelegt werden.</div>')
171 . '<div class="p-3"><a class="btn btn-primary" href="?dbx_modul=dbxContact_admin&dbx_run1=list"><i class="bi bi-list-ul"></i> Tickets anzeigen</a></div>'
172 );
173 }
174
175 private function whereForReport(string $search, string $status, string $priority): string {
176 $db = $this->db();
177 $server = $db->get_dd_server(dbxContactTicket::DD_TICKET);
178 $clauses = array();
179
180 if ($status !== 'all' && array_key_exists($status, dbxContactTicket::statuses())) {
181 $clauses[] = "status = '" . $db->escape($status, $server) . "'";
182 }
183 if ($priority !== 'all' && array_key_exists($priority, dbxContactTicket::priorities())) {
184 $clauses[] = "priority = '" . $db->escape($priority, $server) . "'";
185 }
186 if ($search !== '') {
187 $needle = $db->escape($search, $server);
188 $clauses[] = "(subject LIKE '%" . $needle . "%' OR name LIKE '%" . $needle . "%' OR email LIKE '%" . $needle . "%' OR message LIKE '%" . $needle . "%')";
189 }
190
191 return implode(' AND ', $clauses);
192 }
193
194 private function decorateRows(array $rows): array {
195 $out = array();
196 $db = $this->db();
197 foreach ($rows as $row) {
198 $rid = (int) ($row['id'] ?? 0);
199 $status = dbxContactTicket::normalizeStatus((string) ($row['status'] ?? 'open'));
200 $priority = dbxContactTicket::normalizePriority((string) ($row['priority'] ?? 'normal'));
201 $row['status_view'] = '<span class="badge ' . $this->statusClass($status) . '">' . $this->h(dbxContactTicket::statusLabel($status)) . '</span>';
202 $row['priority_view'] = '<span class="badge ' . $this->priorityClass($priority) . '">' . $this->h(dbxContactTicket::priorityLabel($priority)) . '</span>';
203 $row['user_view'] = (int) ($row['uid'] ?? 0) > 0 ? '#' . (int) $row['uid'] : 'Gast';
204 $row['hidden_view'] = (int) ($row['user_hidden'] ?? 0) === 1 ? '<span class="badge text-bg-dark">Benutzer geloescht</span>' : '';
205 $row['message_count'] = $rid > 0 ? max(0, (int) $db->count(dbxContactTicket::DD_MESSAGE, array('ticket_id' => $rid))) : 0;
206 $row['action'] = $this->tpl()->get_tpl('dbxContact_admin|ticket-row-action', array('rid' => $rid));
207 $out[] = $row;
208 }
209 return $out;
210 }
211
212 private function deleteConfirmText(int $rid, int $messageCount): array {
213 $hasThread = $messageCount > 0;
214
215 return array(
216 'title' => '<i class=\'bi bi-trash\'></i> Kontaktanfrage loeschen',
217 'question' => 'Kontaktanfrage #' . $rid . ' wirklich loeschen?',
218 'hint' => $hasThread
219 ? '<small>Zu dieser Anfrage gehoert ein Verlauf mit ' . $messageCount . ' Nachricht(en). Der gesamte Thread wird geloescht.</small>'
220 : '<small>Zu dieser Anfrage ist kein separater Verlauf vorhanden. Die Anfrage wird endgueltig geloescht.</small>',
221 );
222 }
223
224 public function contactTicketReportRowActionData($report, $data): array {
225 if (!is_array($data) || (string) ($data['type'] ?? '') !== 'delete') {
226 return $data;
227 }
228
229 $rid = (int) ($data['record']['id'] ?? $data['data']['rid'] ?? 0);
230 $messageCount = max(0, (int) ($data['record']['message_count'] ?? 0));
231 $confirm = $this->deleteConfirmText($rid, $messageCount);
232 $data['data']['action'] = '?dbx_modul=dbxContact_admin&dbx_run1=delete&rid=' . $rid;
233 $data['data']['confirm_title'] = $confirm['title'];
234 $data['data']['confirm'] = $confirm['question'];
235 $data['data']['confirm_hint'] = $confirm['hint'];
236 $data['data']['tooltip'] = 'Anfrage loeschen';
237
238 return $data;
239 }
240
241 private function listTickets(string $success = '', string $error = '', ?bool $withFrame = null): string {
242 if ($withFrame === null) {
243 $withFrame = (int) dbx()->get_system_var('dbx_ajax', 0, 'int') !== 1;
244 }
245
246 $db = $this->db();
247 $report = dbx()->get_system_obj('dbxReport');
248 $report->init('contact-ticket-report', 'dbxContact_admin|ticket-report');
249 $report->_dd = dbxContactTicket::DD_TICKET;
250 $report->_action = '?dbx_modul=dbxContact_admin&dbx_run1=list';
251 $report->_pages = true;
252 $report->_create_row_select = false;
253 $report->_create_row_edit = false;
254 $report->_create_row_delete = true;
255 $report->_table_buttons = 'left';
256 $report->_msg_confirm_delete = 'Kontaktanfrage loeschen?';
257 $report->_but_pagination = 7;
258 $report->_msg_info = '';
259 $report->add_rep('report_extra_stats', $this->reportStatusStatsHtml());
260 if ($success !== '') {
261 $report->_msg_success = $success;
262 }
263 if ($error !== '') {
264 $report->_msg_err = $error;
265 }
266 $report->set_callback_owner($this);
267 $report->set_callback('row_action_data', 'contactTicketReportRowActionData');
268 $report->set_tabel_tpl('tpl_row_delete', 'modul|ticket-row-delete');
269 $report->create_selection_fields('dbxContact_admin|rpt-ticket-selection');
270
271 $search = trim((string) $report->get_fld_val('dbx_rwhere', '', 'sqlsearch|max=100'));
272 $status = (string) $report->get_fld_val('dbx_rstatus', 'all', 'parameter');
273 $priority = (string) $report->get_fld_val('dbx_rpriority', 'all', 'parameter');
274 $rowsPerPage = max(10, min(100, (int) $report->get_fld_val('dbx_rrows', 30, 'int')));
275 $position = max(0, (int) $report->get_fld_val('dbx_rpos', 0, 'int'));
276 $sort = (string) $report->get_fld_val('dbx_rsort', 'last_activity_date', 'parameter');
277 $direction = strtoupper((string) $report->get_fld_val('dbx_rdesc', 'DESC', 'parameter')) === 'ASC' ? 'ASC' : 'DESC';
278 if (!in_array($sort, array('last_activity_date', 'create_date', 'id', 'status', 'priority', 'subject'), true)) {
279 $sort = 'last_activity_date';
280 }
281
282 $where = $this->whereForReport($search, $status, $priority);
283 $cols = array('id', 'create_date', 'last_activity_date', 'status', 'priority', 'uid', 'name', 'email', 'subject', 'user_hidden');
284 $report->_rflds = array(
285 'id' => 'Ticket',
286 'last_activity_date' => 'Letzte Aktivitaet',
287 'status_view' => 'Status',
288 'priority_view' => 'Prioritaet',
289 'user_view' => 'Benutzer',
290 'name' => 'Name',
291 'email' => 'E-Mail',
292 'subject' => 'Betreff',
293 'hidden_view' => '',
294 'action' => 'Aktion',
295 );
296 $report->_rpt_format = array(
297 'last_activity_date' => 'php-datetime-usr',
298 'status_view' => 'html',
299 'priority_view' => 'html',
300 'hidden_view' => 'html',
301 'action' => 'html',
302 );
303 $report->_rrows = $rowsPerPage;
304 $report->_rpos = $position;
305 $report->_count_all = $db->count(dbxContactTicket::DD_TICKET, '');
306 $report->_rcount = $db->count(dbxContactTicket::DD_TICKET, $where);
307 $rows = $db->select(dbxContactTicket::DD_TICKET, $where, $cols, $sort . ',id', $direction, '', $rowsPerPage, $position, 0);
308 $report->_rdata = $this->decorateRows(is_array($rows) ? $rows : array());
309
310 $content = $report->run();
311 return $withFrame ? $this->frame($content) : $content;
312 }
313
314 private function deleteTicketRecord(int $rid): bool {
315 if ($rid <= 0 || !dbxContactTicket::ticket($this->db(), $rid)) {
316 return false;
317 }
318
319 $db = $this->db();
320 $ok = 0;
321
322 if ($db->begin(dbxContactTicket::DD_TICKET) === 1) {
323 $messagesOk = $db->delete(dbxContactTicket::DD_MESSAGE, array('ticket_id' => $rid), 0, 1);
324 if ($messagesOk === 1 || $db->count(dbxContactTicket::DD_MESSAGE, array('ticket_id' => $rid)) === 0) {
325 $ok = $db->delete(dbxContactTicket::DD_TICKET, $rid, 0, 1);
326 }
327 if ($ok === 1) {
328 $db->commit(dbxContactTicket::DD_TICKET);
329 } else {
330 $db->rollback(dbxContactTicket::DD_TICKET);
331 }
332 }
333
334 return $ok === 1;
335 }
336
337 private function timeline(array $ticket): string {
338 $db = $this->db();
339 dbxContactTicket::ensureInitialMessage($db, $ticket);
340 $html = '';
341
342 foreach (dbxContactTicket::messages($db, (int) $ticket['id'], true) as $message) {
343 $internal = (string) ($message['visibility'] ?? 'public') === 'internal';
344 $authorType = (string) ($message['author_type'] ?? 'system');
345 $statusTo = trim((string) ($message['status_to'] ?? ''));
346 $html .= $this->tpl()->get_tpl('dbxContact_admin|ticket-message', array(
347 'message_class' => $internal ? 'border-warning bg-warning-subtle' : ($authorType === 'admin' ? 'border-primary bg-primary-subtle' : 'bg-body-tertiary'),
348 'message_icon' => $internal ? 'bi-lock' : ($authorType === 'admin' ? 'bi-headset' : ($authorType === 'requester' ? 'bi-person' : 'bi-gear')),
349 'author_label' => $internal ? 'Interne Notiz' : ($authorType === 'admin' ? 'Support' : ($authorType === 'requester' ? 'Anfragender' : 'System')),
350 'create_date' => $this->h($message['create_date'] ?? ''),
351 'body' => nl2br($this->h($message['body'] ?? '')),
352 'visibility_badge' => $internal ? '<span class="badge text-bg-warning">Nur intern</span>' : '',
353 'status_badge' => $statusTo !== '' ? '<span class="badge ' . $this->statusClass($statusTo) . '">Status: ' . $this->h(dbxContactTicket::statusLabel($statusTo)) . '</span>' : '',
354 'mail_badge' => (int) ($message['mail_sent'] ?? 0) === 1 ? '<span class="badge text-bg-success"><i class="bi bi-envelope-check"></i> E-Mail versendet</span>' : '',
355 ));
356 }
357
358 return $html !== '' ? $html : '<div class="text-muted">Noch keine Meldungen vorhanden.</div>';
359 }
360
361 private function mailProfileOptions(array $extra = array()): array {
362 $profile = trim((string) dbx()->get_config('dbxContact', 'mail_profile'));
363 if ($profile !== '') {
364 $extra['mail_profile'] = $profile;
365 }
366 return $extra;
367 }
368
369 private function sendReplyMail(array $ticket, string $body): bool {
370 $to = trim((string) ($ticket['email'] ?? ''));
371 if ($to === '') {
372 return false;
373 }
374
375 $from = trim((string) dbx()->get_config('dbxContact', 'mail_from'));
376 $fromName = trim((string) dbx()->get_config('dbxContact', 'mail_from_name'));
377 $fromParam = $from !== '' ? array('email' => $from, 'name' => $fromName) : '';
378 $subject = 'Antwort zu Ticket #' . (int) $ticket['id'] . ': ' . (string) ($ticket['subject'] ?? 'Kontaktanfrage');
379 $html = $this->tpl()->get_tpl('dbxContact|mail-contact-reply', array(
380 'subject' => $this->h($ticket['subject'] ?? ''),
381 'body' => nl2br($this->h($body)),
382 ));
383 $text = "Antwort zu Ticket #" . (int) $ticket['id'] . "\n\n" . $body;
384
385 return (bool) dbx()->send_mail(
386 $fromParam,
387 $to,
388 $subject,
389 $html,
390 'html',
391 array(),
392 $this->mailProfileOptions(array('text' => $text))
393 );
394 }
395
396 private function replyForm(array $ticket): string {
397 $rid = (int) $ticket['id'];
398 $form = dbx()->get_system_obj('dbxForm');
399 $form->init('contact-ticket-reply', 'ticket-reply-form');
400 $form->_dd = dbxContactTicket::DD_MESSAGE;
401 $form->_fd = 'dbxContact_admin|ticket-reply';
402 $form->_action = '?dbx_modul=dbxContact_admin&dbx_run1=ticket&rid=' . $rid;
403 $form->_data = array(
404 'status' => dbxContactTicket::normalizeStatus((string) ($ticket['status'] ?? 'open')),
405 'priority' => dbxContactTicket::normalizePriority((string) ($ticket['priority'] ?? 'normal')),
406 'visibility' => 'public',
407 'body' => '',
408 'send_mail' => 1,
409 );
410 $form->add_module_bar('Antwort oder interne Notiz', 'bi-reply', 'Der gesamte Verlauf bleibt am Ticket erhalten');
411 $form->add_rep('bar_title_pre', '');
412 $form->add_rep('bar_title_heading_attrs', '');
413 $form->add_rep('bar_middle', '');
414 $form->add_rep('bar_actions', '');
415 $form->add_rep('bar_extra', '');
416 $form->add_rep('rid', $rid);
417 $messageCount = max(0, (int) $this->db()->count(dbxContactTicket::DD_MESSAGE, array('ticket_id' => $rid)));
418 $confirm = $this->deleteConfirmText($rid, $messageCount);
419 $form->add_rep('delete_action', '?dbx_modul=dbxContact_admin&dbx_run1=delete&rid=' . $rid);
420 $form->add_rep('delete_confirm_title', $confirm['title']);
421 $form->add_rep('delete_confirm', $confirm['question']);
422 $form->add_rep('delete_confirm_hint', $confirm['hint']);
423 $form->_msg_info = 'Oeffentliche Antworten koennen per E-Mail versendet werden. Interne Notizen bleiben nur in der Administration sichtbar.';
424 $form->add_fld('status');
425 $form->add_fld('priority');
426 $form->add_fld('visibility');
427 $form->add_fld('body');
428 $form->add_fld('send_mail');
429
430 if ($form->submit() && !$form->errors()) {
431 $status = dbxContactTicket::normalizeStatus((string) $form->get_post('status', 'answered', 'parameter|max=24'));
432 $priority = dbxContactTicket::normalizePriority((string) $form->get_post('priority', 'normal', 'parameter|max=16'));
433 $visibility = (string) $form->get_post('visibility', 'public', 'parameter|max=16') === 'internal' ? 'internal' : 'public';
434 $body = trim((string) $form->get_post_data('body', '', '*'));
435 $sendMail = (int) $form->get_post('send_mail', 0, 'int') === 1 && $visibility === 'public' && dbxContactConfig::mailOnReply();
436 $oldStatus = dbxContactTicket::normalizeStatus((string) ($ticket['status'] ?? 'open'));
437 $db = $this->db();
438
439 $messageId = dbxContactTicket::addMessage($db, $rid, array(
440 'author_uid' => (int) dbx()->user(),
441 'author_type' => 'admin',
442 'message_type' => $visibility === 'internal' ? 'note' : 'reply',
443 'visibility' => $visibility,
444 'body' => $body,
445 'status_from' => $oldStatus,
446 'status_to' => $status,
447 ));
448
449 if ($messageId > 0) {
450 $values = array(
451 'status' => $status,
452 'priority' => $priority,
453 'assigned_uid' => (int) dbx()->user(),
454 'closed_date' => $status === 'closed' ? date('Y-m-d H:i:s') : '',
455 );
456 dbxContactTicket::touch($db, $rid, $values);
457
458 $mailOk = false;
459 if ($sendMail) {
460 $mailOk = $this->sendReplyMail($ticket, $body);
461 if ($mailOk) {
462 $db->update(dbxContactTicket::DD_MESSAGE, array(
463 'mail_sent' => 1,
464 'mail_sent_date' => date('Y-m-d H:i:s'),
465 ), $messageId, 0, 1, 1, 1);
466 }
467 }
468
469 if ($sendMail && !$mailOk) {
470 $form->_msg_warning = 'Nachricht und Status wurden gespeichert, die E-Mail konnte jedoch nicht versendet werden.';
471 } else {
472 $form->_msg_success = $visibility === 'internal'
473 ? 'Interne Notiz und Status wurden gespeichert.'
474 : 'Antwort und Status wurden gespeichert.' . ($mailOk ? ' Die E-Mail wurde versendet.' : '');
475 }
476 $form->_data['body'] = '';
477 } else {
478 $form->_msg_error = 'Die Nachricht konnte nicht gespeichert werden.';
479 }
480 }
481
482 return $form->run();
483 }
484
485 private function ticket(): string {
486 $rid = (int) dbx()->get_modul_var('rid', 0, 'int');
487 $ticket = dbxContactTicket::ticket($this->db(), $rid);
488 if (!$ticket) {
489 return $this->frame($this->alert('warning', 'Ticket nicht gefunden.'));
490 }
491
492 $replyForm = $this->replyForm($ticket);
493 $ticket = dbxContactTicket::ticket($this->db(), $rid);
494 $status = dbxContactTicket::normalizeStatus((string) ($ticket['status'] ?? 'open'));
495 $priority = dbxContactTicket::normalizePriority((string) ($ticket['priority'] ?? 'normal'));
496 $content = $this->tpl()->get_tpl('dbxContact_admin|ticket-detail', array(
497 'rid' => $rid,
498 'subject' => $this->h($ticket['subject'] ?? ''),
499 'name' => $this->h($ticket['name'] ?? ''),
500 'email' => $this->h($ticket['email'] ?? ''),
501 'email_attr' => $this->h($ticket['email'] ?? ''),
502 'phone' => $this->h(trim((string) ($ticket['phone'] ?? '')) ?: '-'),
503 'create_date' => $this->h($ticket['create_date'] ?? ''),
504 'uid' => (int) ($ticket['uid'] ?? 0) > 0 ? '#' . (int) $ticket['uid'] : 'Gast',
505 'status_label' => $this->h(dbxContactTicket::statusLabel($status)),
506 'status_class' => $this->statusClass($status),
507 'priority_label' => $this->h(dbxContactTicket::priorityLabel($priority)),
508 'priority_class' => $this->priorityClass($priority),
509 'user_visibility' => (int) ($ticket['user_hidden'] ?? 0) === 1 ? 'Vom Benutzer ausgeblendet' : 'Im Benutzerkonto sichtbar',
510 'timeline' => $this->timeline($ticket),
511 'reply_form' => $replyForm,
512 ));
513
514 return $this->frame($content, 'Ticket #' . $rid);
515 }
516
517 private function deleteTicket(): string {
518 $rid = (int) dbx()->get_modul_var('rid', 0, 'int');
519 $withFrame = (int) dbx()->get_system_var('dbx_ajax', 0, 'int') !== 1;
520 if ($this->deleteTicketRecord($rid)) {
521 return $this->listTickets('Kontaktanfrage #' . $rid . ' und der zugehoerige Verlauf wurden geloescht.', '', $withFrame);
522 }
523
524 return $this->listTickets('', 'Kontaktanfrage #' . $rid . ' konnte nicht geloescht werden.', $withFrame);
525 }
526
527 public function run() {
528 $run = dbx()->get_modul_var('dbx_run1', 'list', 'parameter');
529 if ($run === 'install') {
530 return $this->install();
531 }
532 if (!$this->schemaReady()) {
533 return $this->alert('warning', 'Das Ticket-Datenmodell ist noch nicht installiert.')
534 . '<div class="p-3"><a class="btn btn-primary" href="?dbx_modul=dbxContact_admin&dbx_run1=install"><i class="bi bi-database-gear"></i> Jetzt installieren</a></div>';
535 }
536 if ($run === 'ticket' || $run === 'reply') {
537 return $this->ticket();
538 }
539 if ($run === 'delete') {
540 return $this->deleteTicket();
541 }
542 return $this->listTickets();
543 }
544}
static normalizeStatus(string $status, string $fallback='open')
static normalizePriority(string $priority, string $fallback='normal')
static ensureInitialMessage($db, array $ticket)
if( $syncRequest)
Definition index.php:520
DBX schema administration.
$results
Definition run_job.php:207