88 lines
2.8 KiB
PHP
88 lines
2.8 KiB
PHP
<?php
|
|||
|
|
|
||
|
|
/**
|
||
|
|
* Save custom field values for a ticket.
|
||
|
|
*
|
||
|
|
* POST { ticket_id, values: { [field_id]: value, ... } }
|
||
|
|
*
|
||
|
|
* Only fields applicable to the ticket's current category (or category-less
|
||
|
|
* fields) are considered; anything else in `values` is ignored rather than
|
||
|
|
* persisted, so a value typed for a field that no longer applies (e.g. the
|
||
|
|
* category changed) can't linger as orphaned/misleading data.
|
||
|
|
*/
|
||
|
|
|
||
|
|
require_once __DIR__ . '/bootstrap.php';
|
||
|
|
require_once dirname(__DIR__) . '/models/TicketModel.php';
|
||
|
|
require_once dirname(__DIR__) . '/models/CustomFieldModel.php';
|
||
|
|
|
||
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||
|
|
http_response_code(405);
|
||
|
|
apiRespond(['success' => false, 'error' => 'Method not allowed']);
|
||
|
|
}
|
||
|
|
|
||
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
||
|
|
$ticketId = isset($data['ticket_id']) ? trim((string)$data['ticket_id']) : '';
|
||
|
|
$values = is_array($data['values'] ?? null) ? $data['values'] : [];
|
||
|
|
|
||
|
|
if ($ticketId === '') {
|
||
|
|
http_response_code(400);
|
||
|
|
apiRespond(['success' => false, 'error' => 'ticket_id required']);
|
||
|
|
}
|
||
|
|
|
||
|
|
$ticketModel = new TicketModel($conn);
|
||
|
|
$ticket = $ticketModel->getTicketById($ticketId);
|
||
|
|
if (!$ticket || !$ticketModel->canUserAccessTicket($ticket, $currentUser)) {
|
||
|
|
http_response_code(404);
|
||
|
|
apiRespond(['success' => false, 'error' => 'Ticket not found']);
|
||
|
|
}
|
||
|
|
|
||
|
|
$fieldModel = new CustomFieldModel($conn);
|
||
|
|
$definitions = $fieldModel->getAllDefinitions($ticket['category'], true);
|
||
|
|
|
||
|
|
$errors = [];
|
||
|
|
$toSave = [];
|
||
|
|
foreach ($definitions as $def) {
|
||
|
|
$fieldId = (int)$def['field_id'];
|
||
|
|
$raw = $values[$fieldId] ?? ($values[(string)$fieldId] ?? null);
|
||
|
|
|
||
|
|
if ($def['field_type'] === 'checkbox') {
|
||
|
|
$normalized = !empty($raw) ? '1' : '0';
|
||
|
|
} else {
|
||
|
|
$normalized = is_scalar($raw) ? trim((string)$raw) : '';
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!empty($def['is_required']) && $def['field_type'] !== 'checkbox' && $normalized === '') {
|
||
|
|
$errors[] = $def['field_label'] . ' is required';
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($def['field_type'] === 'select' && $normalized !== '') {
|
||
|
|
$allowedOptions = $def['field_options']['options'] ?? [];
|
||
|
|
if (!in_array($normalized, $allowedOptions, true)) {
|
||
|
|
$errors[] = $def['field_label'] . ' has an invalid selection';
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($def['field_type'] === 'number' && $normalized !== '' && !is_numeric($normalized)) {
|
||
|
|
$errors[] = $def['field_label'] . ' must be a number';
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
$toSave[$fieldId] = $normalized;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!empty($errors)) {
|
||
|
|
http_response_code(422);
|
||
|
|
apiRespond(['success' => false, 'error' => implode('; ', $errors)]);
|
||
|
|
}
|
||
|
|
|
||
|
|
$fieldModel->setValues($ticketId, $toSave);
|
||
|
|
|
||
|
|
require_once dirname(__DIR__) . '/models/AuditLogModel.php';
|
||
|
|
(new AuditLogModel($conn))->log($userId, 'update', 'ticket', $ticketId, [
|
||
|
|
'reason' => 'custom fields updated',
|
||
|
|
]);
|
||
|
|
|
||
|
|
apiRespond(['success' => true]);
|