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");