From 75028ba09ab383afd5e5434945aea6abdf415a9e Mon Sep 17 00:00:00 2001 From: johnnyq Date: Sat, 1 Aug 2026 14:33:37 -0400 Subject: [PATCH] Fix some Ticket History gaps --- admin/post/users.php | 31 +++++++++++++++++-- agent/ajax.php | 5 +++ agent/post/ticket.php | 52 ++++++++++++++++++++++++++++++-- api/v1/ticket_replies/create.php | 5 +++ api/v1/tickets/resolve.php | 2 ++ client/post.php | 4 +++ cron/nightly_tasks.php | 2 ++ cron/ticket_email_parser.php | 2 ++ functions/app.php | 28 +++++++++++++---- functions/logging.php | 40 ++++++++++++++++++++++++ guest/guest_post.php | 2 ++ 11 files changed, 163 insertions(+), 10 deletions(-) diff --git a/admin/post/users.php b/admin/post/users.php index 39b2f7a4..65991472 100644 --- a/admin/post/users.php +++ b/admin/post/users.php @@ -216,10 +216,21 @@ if (isset($_GET['disable_user'])) { mysqli_query($mysqli, "UPDATE users SET user_status = 0 WHERE user_id = $user_id"); - // Un-assign tickets + // Un-assign tickets. Collect them first so each one gets a history entry - + // otherwise a whole queue silently goes unassigned with nothing to explain it + $affected_ticket_ids = array(); + $sql_affected_tickets = mysqli_query($mysqli, "SELECT ticket_id FROM tickets WHERE ticket_assigned_to = $user_id AND ticket_closed_at IS NULL"); + while ($affected_ticket_row = mysqli_fetch_assoc($sql_affected_tickets)) { + $affected_ticket_ids[] = intval($affected_ticket_row['ticket_id']); + } + mysqli_query($mysqli, "UPDATE tickets SET ticket_assigned_to = 0 WHERE ticket_assigned_to = $user_id AND ticket_closed_at IS NULL"); mysqli_query($mysqli, "UPDATE recurring_tickets SET recurring_ticket_assigned_to = 0 WHERE recurring_ticket_assigned_to = $user_id"); + foreach ($affected_ticket_ids as $affected_ticket_id) { + logTicketHistory($affected_ticket_id, "$session_name unassigned the ticket when $user_name was disabled"); + } + logAudit("User", "Disable", "$session_name disabled user $name", 0, $user_id); flashAlert("User $user_name disabled", 'error'); @@ -256,10 +267,26 @@ if (isset($_POST['archive_user'])) { $user_name = escapeSql(getFieldById('users', $user_id, 'user_name')); - // Un-assign / Re-assign tickets + // Un-assign / Re-assign tickets. Same as above - collect them first so the + // move shows up on each ticket's history + $affected_ticket_ids = array(); + $sql_affected_tickets = mysqli_query($mysqli, "SELECT ticket_id FROM tickets WHERE ticket_assigned_to = $user_id AND ticket_closed_at IS NULL AND ticket_resolved_at IS NULL"); + while ($affected_ticket_row = mysqli_fetch_assoc($sql_affected_tickets)) { + $affected_ticket_ids[] = intval($affected_ticket_row['ticket_id']); + } + mysqli_query($mysqli, "UPDATE tickets SET ticket_assigned_to = $ticket_assign WHERE ticket_assigned_to = $user_id AND ticket_closed_at IS NULL AND ticket_resolved_at IS NULL"); mysqli_query($mysqli, "UPDATE recurring_tickets SET recurring_ticket_assigned_to = $ticket_assign WHERE recurring_ticket_assigned_to = $user_id"); + $reassigned_to_name = $ticket_assign ? escapeSql(getFieldById('users', $ticket_assign, 'user_name', 'raw')) : ''; + foreach ($affected_ticket_ids as $affected_ticket_id) { + if ($reassigned_to_name) { + logTicketHistory($affected_ticket_id, "$session_name reassigned the ticket to $reassigned_to_name when $user_name was archived"); + } else { + logTicketHistory($affected_ticket_id, "$session_name unassigned the ticket when $user_name was archived"); + } + } + // Archive user query mysqli_query($mysqli, "UPDATE users SET user_name = '$user_name (archived)', user_password = '$password', user_status = 0, user_specific_encryption_ciphertext = '', user_archived_at = NOW() WHERE user_id = $user_id"); diff --git a/agent/ajax.php b/agent/ajax.php index 7a9a5e42..e62df700 100644 --- a/agent/ajax.php +++ b/agent/ajax.php @@ -545,6 +545,8 @@ if (isset($_POST['update_kanban_ticket'])) { mysqli_query($mysqli, "UPDATE tickets SET ticket_order = $kanban, ticket_status = $status, ticket_resolved_at = NULL WHERE ticket_id = $ticket_id"); resetTicketResolutionSla($ticket_id); syncTicketSlaClock($ticket_id); + $new_status_name = getTicketStatusName($status, 'sql'); + logTicketHistory($ticket_id, "$session_name reopened the ticket to $new_status_name from the kanban"); triggerCustomAction('ticket_update', $ticket_id); } elseif ($status === $statuses['Resolved']) { // If the ticket was moved to a resolved status, we need to update ticket_resolved_at @@ -554,6 +556,7 @@ if (isset($_POST['update_kanban_ticket'])) { setTicketFirstResponse($ticket_id); setTicketResolutionSlaMet($ticket_id); syncTicketSlaClock($ticket_id); + logTicketHistory($ticket_id, "$session_name resolved the ticket from the kanban"); triggerCustomAction('ticket_update', $ticket_id); // Client notification email @@ -635,6 +638,8 @@ if (isset($_POST['update_kanban_ticket'])) { // If the ticket was moved from any status to another status mysqli_query($mysqli, "UPDATE tickets SET ticket_order = $kanban, ticket_status = $status WHERE ticket_id = $ticket_id"); syncTicketSlaClock($ticket_id); + $new_status_name = getTicketStatusName($status, 'sql'); + logTicketHistory($ticket_id, "$session_name set the status to $new_status_name from the kanban"); triggerCustomAction('ticket_update', $ticket_id); } } diff --git a/agent/post/ticket.php b/agent/post/ticket.php index 976303a6..0099c00e 100644 --- a/agent/post/ticket.php +++ b/agent/post/ticket.php @@ -127,7 +127,7 @@ if (isset($_POST['add_ticket'])) { $ticket_details = mysqli_escape_string($mysqli, $row['ticket_details']); $ticket_priority = escapeSql($row['ticket_priority']); $ticket_status = escapeSql($row['ticket_status']); - $ticket_status_name = escapeSql(getTicketStatusName($row['ticket_status'])); + $ticket_status_name = getTicketStatusName($row['ticket_status'], 'sql'); $client_id = intval($row['ticket_client_id']); $ticket_created_by = intval($row['ticket_created_by']); $ticket_assigned_to = intval($row['ticket_assigned_to']); @@ -245,9 +245,31 @@ if (isset($_POST['edit_ticket'])) { enforceClientAccess(); } + /* + * The edit modal can change priority and assignment, which the dedicated + * priority and assign handlers record - so read the originals first and + * record the same changes when they come through here instead + */ + $original_row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT ticket_priority, ticket_assigned_to FROM tickets WHERE ticket_id = $ticket_id")); + $original_priority = escapeSql($original_row['ticket_priority']); + $original_assigned_to = intval($original_row['ticket_assigned_to']); + mysqli_query($mysqli, "UPDATE tickets SET ticket_category = $category_id, ticket_subject = '$ticket_subject', ticket_priority = '$ticket_priority', ticket_billable = $billable, ticket_details = '$details', ticket_due_at = $due, ticket_vendor_ticket_number = '$vendor_ticket_number', ticket_contact_id = $contact_id, ticket_assigned_to = $assigned_to, ticket_vendor_id = $vendor_id, ticket_location_id = $location_id, ticket_asset_id = $asset_id, ticket_project_id = $project_id WHERE ticket_id = $ticket_id"); applyTicketSla($ticket_id); + if ($original_priority !== $ticket_priority) { + logTicketHistory($ticket_id, "$session_name changed priority from $original_priority to $ticket_priority"); + } + + if ($original_assigned_to !== $assigned_to) { + if ($assigned_to) { + $new_agent_name = escapeSql(getFieldById('users', $assigned_to, 'user_name', 'raw')); + logTicketHistory($ticket_id, "$session_name assigned the ticket to $new_agent_name"); + } else { + logTicketHistory($ticket_id, "$session_name unassigned the ticket"); + } + } + // Add Additional Assets if (isset($_POST['additional_assets'])) { mysqli_query($mysqli, "DELETE FROM ticket_assets WHERE ticket_id = $ticket_id"); @@ -875,6 +897,8 @@ if (isset($_POST['assign_ticket'])) { mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = '$ticket_reply', ticket_reply_type = 'Internal', ticket_reply_time_worked = '00:01:00', ticket_reply_by = $session_user_id, ticket_reply_ticket_id = $ticket_id"); + logTicketHistory($ticket_id, "$session_name assigned the ticket to $agent_name"); + logAudit("Ticket", "Edit", "$session_name reassigned $ticket_prefix$ticket_number to $agent_name", $client_id, $ticket_id); // Notification @@ -1083,6 +1107,8 @@ if (isset($_POST['bulk_assign_ticket'])) { mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = '$ticket_reply', ticket_reply_type = 'Internal', ticket_reply_time_worked = '00:01:00', ticket_reply_by = $session_user_id, ticket_reply_ticket_id = $ticket_id"); + logTicketHistory($ticket_id, "$session_name assigned the ticket to $agent_name"); + logAudit("Ticket", "Edit", "$session_name reassigned ticket $ticket_prefix$ticket_number to $agent_name", $client_id, $ticket_id); triggerCustomAction('ticket_assign', $ticket_id); @@ -1292,7 +1318,9 @@ if (isset($_POST['bulk_merge_tickets'])) { // Update new parent ticket mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = 'Ticket $ticket_prefix$ticket_number was bulk merged into this ticket with comment: $merge_comment.

$ticket_subject
$ticket_details', ticket_reply_time_worked = '00:01:00', ticket_reply_type = 'Internal', ticket_reply_by = $session_user_id, ticket_reply_ticket_id = $merge_into_ticket_id"); - logAudit("Ticket", "Merged", "$session_name Merged ticket $ticket_prefix$ticket_number into $ticket_prefix$merge_into_ticket_number", $client_id, $ticket_id); + logTicketHistory($ticket_id, "$session_name merged this ticket into $ticket_prefix$merge_into_ticket_number and closed it"); + + logAudit("Ticket", "Merged", "$session_name Merged ticket $ticket_prefix$ticket_number into $ticket_prefix$merge_into_ticket_number", $client_id, $ticket_id); // Custom action/notif handler triggerCustomAction('ticket_merge', $ticket_id); @@ -1372,6 +1400,8 @@ if (isset($_POST['bulk_resolve_tickets'])) { mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = '$details', ticket_reply_type = '$ticket_reply_type', ticket_reply_time_worked = '$ticket_reply_time_worked', ticket_reply_by = $session_user_id, ticket_reply_ticket_id = $ticket_id"); + logTicketHistory($ticket_id, "$session_name resolved the ticket"); + logAudit("Ticket", "Resolve", "$session_name resolved $ticket_prefix$ticket_number - $ticket_subject", $client_id, $ticket_id); triggerCustomAction('ticket_resolve', $ticket_id); @@ -1518,6 +1548,9 @@ if (isset($_POST['bulk_ticket_reply'])) { mysqli_query($mysqli, "UPDATE tickets SET ticket_status = '$ticket_status' WHERE ticket_id = $ticket_id"); syncTicketSlaClock($ticket_id); + $new_status_name = getTicketStatusName($ticket_status, 'sql'); + logTicketHistory($ticket_id, "$session_name set the status to $new_status_name"); + logAudit("Ticket", "Reply", "$session_name replied to ticket $ticket_prefix$ticket_number - $ticket_subject and was a $ticket_reply_type reply", $client_id, $ticket_id); // Custom action/notif handler @@ -1533,6 +1566,8 @@ if (isset($_POST['bulk_ticket_reply'])) { setTicketResolutionSlaMet($ticket_id); // Logging + logTicketHistory($ticket_id, "$session_name resolved the ticket"); + logAudit("Ticket", "Resolved", "$session_name resolved Ticket $ticket_prefix$ticket_number", $client_id, $ticket_id); triggerCustomAction('ticket_resolve', $ticket_id); @@ -1840,6 +1875,8 @@ if (isset($_POST['add_ticket_reply'])) { mysqli_query($mysqli, "UPDATE tickets SET ticket_resolved_at = NOW() WHERE ticket_id = $ticket_id"); setTicketResolutionSlaMet($ticket_id); + logTicketHistory($ticket_id, "$session_name resolved the ticket"); + logAudit("Ticket", "Resolved", "$session_name resolved Ticket ticket ID $ticket_id", $client_id, $ticket_id); } @@ -1995,6 +2032,9 @@ if (isset($_POST['add_ticket_reply'])) { flashAlert("Stored on the ticket but too large to email: " . implode(', ', $skipped_names) . "", 'error'); } + $new_status_name = getTicketStatusName($ticket_status, 'sql'); + logTicketHistory($ticket_id, "$session_name set the status to $new_status_name"); + logAudit("Ticket", "Reply", "$session_name replied to ticket $ticket_prefix$ticket_number - $ticket_subject and was a $ticket_reply_type reply", $client_id, $ticket_id); redirect(); @@ -2213,6 +2253,8 @@ if (isset($_POST['merge_ticket'])) { mysqli_query($mysqli, "UPDATE tickets SET ticket_updated_at = NOW() WHERE ticket_id = $merge_into_ticket_id"); + logTicketHistory($ticket_id, "$session_name merged this ticket into $ticket_prefix$merge_into_ticket_number and closed it"); + logAudit("Ticket", "Merged", "$session_name Merged ticket $ticket_prefix$ticket_number into $ticket_prefix$merge_into_ticket_number"); triggerCustomAction('ticket_merge', $ticket_id); @@ -2285,6 +2327,8 @@ if (isset($_GET['resolve_ticket'])) { syncTicketSlaClock($ticket_id); setTicketResolutionSlaMet($ticket_id); + logTicketHistory($ticket_id, "$session_name resolved the ticket"); + logAudit("Ticket", "Resolved", "$session_name resolved ticket $ticket_prefix$ticket_number (ID: $ticket_id)", $client_id, $ticket_id); triggerCustomAction('ticket_resolve', $ticket_id); @@ -2389,6 +2433,8 @@ if (isset($_GET['close_ticket'])) { mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = 'Ticket closed.', ticket_reply_type = 'Internal', ticket_reply_time_worked = '00:01:00', ticket_reply_by = $session_user_id, ticket_reply_ticket_id = $ticket_id"); + logTicketHistory($ticket_id, "$session_name closed the ticket"); + logAudit("Ticket", "Closed", "$session_name closed ticket ID $ticket_id", $client_id, $ticket_id); triggerCustomAction('ticket_close', $ticket_id); @@ -2489,6 +2535,8 @@ if (isset($_GET['reopen_ticket'])) { syncTicketSlaClock($ticket_id); resetTicketResolutionSla($ticket_id); + logTicketHistory($ticket_id, "$session_name reopened the ticket"); + logAudit("Ticket", "Reopened", "$session_name reopened ticket ID $ticket_id", $client_id, $ticket_id); triggerCustomAction('ticket_update', $ticket_id); diff --git a/api/v1/ticket_replies/create.php b/api/v1/ticket_replies/create.php index e40c3358..b94184b6 100644 --- a/api/v1/ticket_replies/create.php +++ b/api/v1/ticket_replies/create.php @@ -87,11 +87,16 @@ if (!empty($ticket_id) && !empty($reply)) { if (!empty($reply_ticket_status)) { mysqli_query($mysqli, "UPDATE tickets SET ticket_status = $reply_ticket_status WHERE ticket_id = $ticket_id LIMIT 1"); + $new_status_name = getTicketStatusName($reply_ticket_status, 'sql'); + logTicketHistory($ticket_id, "Status set to $new_status_name via the API ($api_key_name)"); + // Resolve the ticket, if set if ($reply_ticket_status == 4) { mysqli_query($mysqli, "UPDATE tickets SET ticket_resolved_at = NOW() WHERE ticket_id = $ticket_id AND ticket_resolved_at IS NULL LIMIT 1"); setTicketResolutionSlaMet($ticket_id); + logTicketHistory($ticket_id, "Resolved via the API ($api_key_name)"); + logAudit("Ticket", "Resolved", "Resolved ticket $ticket_prefix$ticket_number via API ($api_key_name)", $client_id, $ticket_id); triggerCustomAction('ticket_resolve', $ticket_id); diff --git a/api/v1/tickets/resolve.php b/api/v1/tickets/resolve.php index f2d40c27..3790f0ec 100644 --- a/api/v1/tickets/resolve.php +++ b/api/v1/tickets/resolve.php @@ -38,6 +38,8 @@ if (!empty($ticket_id)) { $update_count = mysqli_affected_rows($mysqli); // Logging + logTicketHistory($ticket_id, "Resolved via the API ($api_key_name)"); + logAudit("Ticket", "Resolved", "$ticket_prefix$ticket_number ticket via API ($api_key_name)", $client_id, $ticket_id); logAudit("API", "Success", "Resolved ticket $ticket_prefix$ticket_number via API ($api_key_name)", $client_id); } diff --git a/client/post.php b/client/post.php index 4b64d9f6..3b333d7b 100644 --- a/client/post.php +++ b/client/post.php @@ -107,6 +107,7 @@ if (isset($_POST['add_ticket_comment'])) { // Update Ticket Last Response Field & set ticket to open as client has replied mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 2 WHERE ticket_id = $ticket_id AND ticket_client_id = $session_client_id LIMIT 1"); syncTicketSlaClock($ticket_id); + logTicketHistory($ticket_id, "$session_contact_name replied from the client portal, reopening the ticket"); // Get ticket details & Notify the assigned tech (if any) @@ -247,6 +248,7 @@ if (isset($_GET['resolve_ticket'])) { mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 4, ticket_resolved_at = NOW() WHERE ticket_id = $ticket_id AND ticket_client_id = $session_client_id"); setTicketResolutionSlaMet($ticket_id); syncTicketSlaClock($ticket_id); + logTicketHistory($ticket_id, "$session_contact_name resolved the ticket from the client portal"); // Add reply mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = 'Ticket resolved by $session_contact_name.', ticket_reply_type = 'Client', ticket_reply_by = $session_contact_id, ticket_reply_ticket_id = $ticket_id"); @@ -284,6 +286,7 @@ if (isset($_GET['reopen_ticket'])) { mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 2, ticket_resolved_at = NULL WHERE ticket_id = $ticket_id AND ticket_client_id = $session_client_id"); resetTicketResolutionSla($ticket_id); syncTicketSlaClock($ticket_id); + logTicketHistory($ticket_id, "$session_contact_name reopened the ticket from the client portal"); // Add reply mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = 'Ticket reopened by $session_contact_name.', ticket_reply_type = 'Client', ticket_reply_by = $session_contact_id, ticket_reply_ticket_id = $ticket_id"); @@ -320,6 +323,7 @@ if (isset($_GET['close_ticket'])) { // Fully close ticket mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 5, ticket_closed_at = NOW() WHERE ticket_id = $ticket_id AND ticket_client_id = $session_client_id"); syncTicketSlaClock($ticket_id); + logTicketHistory($ticket_id, "$session_contact_name closed the ticket from the client portal"); // Add reply mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = 'Ticket closed by $session_contact_name.', ticket_reply_type = 'Client', ticket_reply_by = $session_contact_id, ticket_reply_ticket_id = $ticket_id"); diff --git a/cron/nightly_tasks.php b/cron/nightly_tasks.php index 759047f4..107895d9 100644 --- a/cron/nightly_tasks.php +++ b/cron/nightly_tasks.php @@ -527,6 +527,8 @@ while ($row = mysqli_fetch_assoc($sql_resolved_tickets_to_close)) { syncTicketSlaClock($ticket_id); //Logging + logTicketHistory($ticket_id, "Automatically closed after $config_ticket_autoclose_hours hours with no activity"); + logAudit("Ticket", "Closed", "$ticket_prefix$ticket_number auto closed", $client_id, $ticket_id); triggerCustomAction('ticket_close', $ticket_id); diff --git a/cron/ticket_email_parser.php b/cron/ticket_email_parser.php index 9d677c99..84960609 100644 --- a/cron/ticket_email_parser.php +++ b/cron/ticket_email_parser.php @@ -350,6 +350,8 @@ function addReply($from_email, $date, $subject, $ticket_number, $message, $attac mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 2, ticket_resolved_at = NULL WHERE ticket_id = $ticket_id AND ticket_client_id = $client_id LIMIT 1"); resetTicketResolutionSla($ticket_id); + logTicketHistory($ticket_id, "$from_email_esc replied by email, reopening the ticket"); + logAudit("Ticket", "Edit", "Email parser: Client contact $from_email_esc updated ticket $config_ticket_prefix$ticket_number_esc ($subject)", $client_id, $ticket_id); triggerCustomAction('ticket_reply_client', $ticket_id); return true; diff --git a/functions/app.php b/functions/app.php index d5a25e8f..1e90d82c 100644 --- a/functions/app.php +++ b/functions/app.php @@ -55,19 +55,35 @@ function getInvoiceBadgeColor($invoice_status) { return $invoice_badge_color; } -function getTicketStatusName($ticket_status) { +/* + * The display name for a ticket status id. + * + * $escape_method follows getFieldById() - 'html' (the default, unchanged for + * existing callers), 'sql' for interpolating into a query, or 'raw'. The one + * caller that wanted SQL was wrapping this in escapeSql(), which escaped the + * already-HTML-escaped string and mangled names containing & or an apostrophe. + */ +function getTicketStatusName($ticket_status, $escape_method = 'html') { global $mysqli; $status_id = intval($ticket_status); - $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT * FROM ticket_statuses WHERE ticket_status_id = $status_id LIMIT 1")); + $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT ticket_status_name FROM ticket_statuses WHERE ticket_status_id = $status_id LIMIT 1")); - if ($row) { - return escapeHtml($row['ticket_status_name']); + if (!$row) { + // Default return + return "Unknown"; } - // Default return - return "Unknown"; + if ($escape_method === 'sql') { + return escapeSql($row['ticket_status_name']); + } + + if ($escape_method === 'raw') { + return $row['ticket_status_name']; + } + + return escapeHtml($row['ticket_status_name']); } diff --git a/functions/logging.php b/functions/logging.php index 61ac9803..21424def 100644 --- a/functions/logging.php +++ b/functions/logging.php @@ -55,6 +55,46 @@ function logAudit($type, $action, $description, $client_id = 0, $entity_id = 0) mysqli_query($mysqli, "INSERT INTO logs SET log_type = '$type', log_action = '$action', log_description = '$description', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $entity_id"); } +/* + * Records a ticket history entry - the per-ticket change trail shown in the + * History card on agent/ticket.php. + * + * Follows the same convention as logAudit() above: the description is + * interpolated as-is, so callers pass values that are already SQL-safe. + * + * Call this AFTER the change has been written - the status stamped on the entry + * is whatever the ticket is in at the time of the call. + */ +function logTicketHistory($ticket_id, $description) { + global $mysqli; + + $ticket_id = intval($ticket_id); + + // ticket_history_description is varchar(255) and NOT NULL, so an over-long + // description would be an error under strict mode rather than a truncation + $description = substr($description, 0, 255); + + // The description arrives already escaped, and cutting it at 255 can split a + // \' pair - the leftover backslash would escape this query's closing quote + $trailing_backslashes = strlen($description) - strlen(rtrim($description, '\\')); + if ($trailing_backslashes % 2 === 1) { + $description = substr($description, 0, -1); + } + + $status_name = ''; + $sql = mysqli_query( + $mysqli, + "SELECT ticket_status_name FROM tickets + LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id + WHERE ticket_id = $ticket_id" + ); + if ($sql && mysqli_num_rows($sql)) { + $status_name = escapeSql(mysqli_fetch_assoc($sql)['ticket_status_name']); + } + + mysqli_query($mysqli, "INSERT INTO ticket_history SET ticket_history_status = '$status_name', ticket_history_description = '$description', ticket_history_ticket_id = $ticket_id"); +} + function logApp($category, $type, $details) { global $mysqli; diff --git a/guest/guest_post.php b/guest/guest_post.php index e79818a7..b23a1ae2 100644 --- a/guest/guest_post.php +++ b/guest/guest_post.php @@ -168,6 +168,7 @@ if (isset($_GET['reopen_ticket'], $_GET['url_key'])) { if (mysqli_num_rows($sql) == 1) { // Update the ticket mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 2, ticket_resolved_at = NULL WHERE ticket_id = $ticket_id AND ticket_url_key = '$url_key'"); + logTicketHistory($ticket_id, "The client reopened the ticket from the guest link"); // Add reply mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = 'Ticket reopened by client (guest URL).', ticket_reply_type = 'Internal', ticket_reply_by = 0, ticket_reply_ticket_id = $ticket_id"); @@ -196,6 +197,7 @@ if (isset($_GET['close_ticket'], $_GET['url_key'])) { // Update the ticket mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 5, ticket_closed_at = NOW() WHERE ticket_id = $ticket_id AND ticket_url_key = '$url_key'"); + logTicketHistory($ticket_id, "The client closed the ticket from the guest link"); // Add reply mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = 'Ticket closed by client (guest URL).', ticket_reply_type = 'Internal', ticket_reply_by = 0, ticket_reply_ticket_id = $ticket_id");