Upload via GUI (335 Dateien)
This commit is contained in:
@@ -297,6 +297,14 @@ function api_is_following(int $ticketId): bool
|
||||
return isset($mine[$ticketId]);
|
||||
}
|
||||
|
||||
/** Cc-Empfänger eines Tickets für die Apps. */
|
||||
function api_ticket_collaborators(PDO $pdo, int $ticketId): array
|
||||
{
|
||||
$s = $pdo->prepare('SELECT id, email, name FROM ticket_collaborators WHERE ticket_id = ? ORDER BY created_at, id');
|
||||
$s->execute([$ticketId]);
|
||||
return array_map(fn($r) => ['id' => (int) $r['id'], 'email' => (string) $r['email'], 'name' => (string) ($r['name'] ?? '')], $s->fetchAll());
|
||||
}
|
||||
|
||||
function api_add_ticket_message(PDO $pdo, array $staff, array $ticket, string $type, string $rawMessage, ?string $setStatus, ?string $signatureChoice = null): int
|
||||
{
|
||||
$permission = $type === 'note' ? 'post_note' : 'reply_ticket';
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/common.php';
|
||||
|
||||
/**
|
||||
* Auswertung der Zufriedenheitsabfrage.
|
||||
* GET ?days=30 (1 bis 365) → {enabled, days, stats:{sent,answered,avg,rate,dist,byStaff,recent}}
|
||||
* Mitarbeiter sehen nur die Abteilungen, in denen sie arbeiten.
|
||||
*/
|
||||
api_require_method('GET');
|
||||
$staff = api_require_staff();
|
||||
$pdo = get_db();
|
||||
$days = max(1, min(365, (int) ($_GET['days'] ?? 30)));
|
||||
$from = date('Y-m-d 00:00:00', time() - ($days - 1) * 86400);
|
||||
$to = date('Y-m-d 23:59:59');
|
||||
$stats = null;
|
||||
try {
|
||||
$stats = csat_stats($pdo, $from, $to, staff_department_ids($staff));
|
||||
} catch (Throwable $e) {
|
||||
$stats = ['sent' => 0, 'answered' => 0, 'avg' => null, 'rate' => null, 'dist' => [1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0], 'byStaff' => [], 'recent' => []];
|
||||
}
|
||||
$stats['dist'] = array_map(fn($k, $v) => ['rating' => $k, 'count' => $v], array_keys($stats['dist']), $stats['dist']);
|
||||
api_json(['enabled' => csat_enabled(), 'days' => $days, 'stats' => $stats]);
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/common.php';
|
||||
|
||||
/**
|
||||
* Statusseite für Kunden.
|
||||
* GET → Zusammenfassung (Gesamtzustand, Dienste, Meldungen); für alle Mitarbeiter lesbar
|
||||
* POST (nur Administratoren):
|
||||
* {action:"set_component", id, status}
|
||||
* {action:"create_incident", title, body?, status?, componentId?, componentState?}
|
||||
* {action:"update_incident", id, status, body?}
|
||||
* {action:"delete_incident", id}
|
||||
* {action:"set_enabled", enabled}
|
||||
*/
|
||||
api_require_method('GET', 'POST');
|
||||
$staff = api_require_staff();
|
||||
$pdo = get_db();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
api_require_admin($staff);
|
||||
$b = api_request_body();
|
||||
$action = (string) ($b['action'] ?? '');
|
||||
if ($action === 'set_component') {
|
||||
if (!status_set_component($pdo, (int) ($b['id'] ?? 0), (string) ($b['status'] ?? ''))) api_error(422, 'INVALID_STATUS', 'Ungültiger Dienst oder Zustand.');
|
||||
} elseif ($action === 'create_incident') {
|
||||
$cid = (int) ($b['componentId'] ?? 0) ?: null;
|
||||
$cs = isset($b['componentState']) && $b['componentState'] !== '' ? (string) $b['componentState'] : null;
|
||||
$r = status_create_incident($pdo, (string) ($b['title'] ?? ''), (string) ($b['body'] ?? ''), (string) ($b['status'] ?? 'investigating'), $cid, $cs, (int) $staff['id']);
|
||||
if (!$r['ok']) api_error(422, 'INVALID_INCIDENT', $r['error']);
|
||||
audit_log($staff['id'], 'Statusseite: Meldung veröffentlicht (App)');
|
||||
} elseif ($action === 'update_incident') {
|
||||
$r = status_update_incident($pdo, (int) ($b['id'] ?? 0), (string) ($b['status'] ?? ''), (string) ($b['body'] ?? ''), (int) $staff['id'], true);
|
||||
if (!$r['ok']) api_error(422, 'INVALID_INCIDENT', $r['error']);
|
||||
} elseif ($action === 'delete_incident') {
|
||||
status_delete_incident($pdo, (int) ($b['id'] ?? 0));
|
||||
} elseif ($action === 'set_enabled') {
|
||||
set_setting('status_page_enabled', !empty($b['enabled']) ? '1' : '0');
|
||||
} else {
|
||||
api_error(422, 'UNKNOWN_ACTION', 'Unbekannte Aktion.');
|
||||
}
|
||||
}
|
||||
api_json(['status' => status_summary($pdo)] + ['canManage' => $staff['role'] === 'admin']);
|
||||
@@ -126,4 +126,13 @@ try {
|
||||
} catch (Throwable $e) {
|
||||
$summary['similar'] = ['articles' => [], 'tickets' => []];
|
||||
}
|
||||
// Cc-Empfänger, Beobachter und Bewertung des Kunden
|
||||
try {
|
||||
$summary['collaborators'] = api_ticket_collaborators($pdo, (int) $ticket['id']);
|
||||
$summary['followers'] = array_map(fn($f) => ['id' => (int) $f['id'], 'name' => (string) $f['name']], ticket_followers($pdo, (int) $ticket['id']));
|
||||
$__csat = csat_for_ticket($pdo, (int) $ticket['id']);
|
||||
$summary['csat'] = $__csat ? ['rating' => $__csat['rating'] !== null ? (int) $__csat['rating'] : null, 'comment' => (string) ($__csat['comment'] ?? ''), 'sentAt' => $__csat['sent_at'], 'answeredAt' => $__csat['answered_at']] : null;
|
||||
} catch (Throwable $e) {
|
||||
$summary += ['collaborators' => [], 'followers' => [], 'csat' => null];
|
||||
}
|
||||
api_json(['ticket' => $summary]);
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/common.php';
|
||||
|
||||
/**
|
||||
* Cc-Empfänger (Kollaboratoren) eines Tickets: sie bekommen öffentliche Antworten per Mail in Kopie.
|
||||
* GET ?number=… Liste
|
||||
* POST {number, action:"add", email, name?}
|
||||
* POST {number, action:"remove", id}
|
||||
* Beobachter (Mitarbeiter) werden über ticket_update.php ("following") gesetzt und kommen mit ticket.php.
|
||||
*/
|
||||
api_require_method('GET', 'POST');
|
||||
$staff = api_require_staff();
|
||||
$pdo = get_db();
|
||||
$isPost = $_SERVER['REQUEST_METHOD'] === 'POST';
|
||||
$body = $isPost ? api_request_body() : [];
|
||||
$number = trim((string) ($isPost ? ($body['number'] ?? '') : ($_GET['number'] ?? '')));
|
||||
if ($number === '') api_error(422, 'TICKET_NUMBER_REQUIRED', 'Eine Ticketnummer ist erforderlich.');
|
||||
$ticket = api_find_ticket($pdo, $staff, $number);
|
||||
|
||||
if ($isPost) {
|
||||
$action = (string) ($body['action'] ?? '');
|
||||
if ($action === 'add') {
|
||||
$email = trim((string) ($body['email'] ?? ''));
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) api_error(422, 'INVALID_EMAIL', 'Bitte eine gültige E-Mail-Adresse angeben.');
|
||||
$name = trim(mb_substr((string) ($body['name'] ?? ''), 0, 120));
|
||||
$dup = $pdo->prepare('SELECT COUNT(*) FROM ticket_collaborators WHERE ticket_id = ? AND LOWER(email) = LOWER(?)');
|
||||
$dup->execute([$ticket['id'], $email]);
|
||||
if ((int) $dup->fetchColumn() === 0) {
|
||||
$pdo->prepare('INSERT INTO ticket_collaborators (ticket_id, email, name, added_by, created_at) VALUES (?,?,?,?,?)')
|
||||
->execute([$ticket['id'], $email, $name !== '' ? $name : null, $staff['id'], db_now()]);
|
||||
log_activity($pdo, (int) $ticket['id'], (int) $staff['id'], "Kollaborator hinzugefügt: {$email}");
|
||||
}
|
||||
} elseif ($action === 'remove') {
|
||||
$s = $pdo->prepare('SELECT email FROM ticket_collaborators WHERE id = ? AND ticket_id = ?');
|
||||
$s->execute([(int) ($body['id'] ?? 0), $ticket['id']]);
|
||||
$email = $s->fetchColumn();
|
||||
if ($email !== false) {
|
||||
$pdo->prepare('DELETE FROM ticket_collaborators WHERE id = ? AND ticket_id = ?')->execute([(int) $body['id'], $ticket['id']]);
|
||||
log_activity($pdo, (int) $ticket['id'], (int) $staff['id'], "Kollaborator entfernt: {$email}");
|
||||
}
|
||||
} else {
|
||||
api_error(422, 'UNKNOWN_ACTION', 'Unbekannte Aktion.');
|
||||
}
|
||||
}
|
||||
api_json(['collaborators' => api_ticket_collaborators($pdo, (int) $ticket['id'])]);
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/common.php';
|
||||
|
||||
/**
|
||||
* Ticket-Vorlagen zum Vorbelegen des Formulars „Neues Ticket“ in den Apps.
|
||||
* GET → {templates:[{id,name,description,subject,body,departmentId,helpTopicId,priorityId,isPublic}]}
|
||||
* Gepflegt werden die Vorlagen im Webbereich (Adminportal → Ticket-Vorlagen).
|
||||
*/
|
||||
api_require_method('GET');
|
||||
api_require_staff();
|
||||
$pdo = get_db();
|
||||
$list = [];
|
||||
try {
|
||||
$list = array_map('ticket_template_payload', ticket_templates_list($pdo));
|
||||
} catch (Throwable $e) {
|
||||
// Datenbank noch nicht aktualisiert: keine Vorlagen
|
||||
}
|
||||
api_json(['templates' => $list]);
|
||||
Reference in New Issue
Block a user