From d80334a7cfe8d23f76283673ac7a0ea844b76e1b Mon Sep 17 00:00:00 2001 From: wrongecho Date: Sat, 17 Aug 2024 23:24:15 +0100 Subject: [PATCH] Ticketing updates - guest view & resolved vs closed - Swap autclose for resolved to allow temporarily re-opening resolved tickets for 72 hrs after closure - Add guest view URL for tickets --- client_tickets.php | 16 +- cron.php | 95 ++------ cron_ticket_email_parser.php | 9 +- database_updates.php | 50 +++- database_version.php | 2 +- db.sql | 3 +- functions.php | 4 + get_settings.php | 1 - guest_post.php | 80 +++++++ guest_view_ticket.php | 220 ++++++++++++++++++ legacy_cron_ticket_email_parser.php | 7 +- portal/portal_functions.php | 13 +- portal/portal_post.php | 57 ++++- portal/ticket.php | 53 ++++- portal/tickets.php | 2 +- post/setting.php | 3 +- post/ticket.php | 163 +++++++++++-- settings_notifications.php | 2 +- settings_ticket.php | 12 +- setup.php | 2 +- ticket.php | 116 +++++---- ticket_bulk_reply_modal.php | 16 +- ...modal.php => ticket_bulk_resolve_modal.php | 4 +- tickets.php | 12 +- 24 files changed, 738 insertions(+), 204 deletions(-) create mode 100644 guest_view_ticket.php rename ticket_bulk_close_modal.php => ticket_bulk_resolve_modal.php (90%) diff --git a/client_tickets.php b/client_tickets.php index 1a9bb4d00..1f6895495 100644 --- a/client_tickets.php +++ b/client_tickets.php @@ -9,11 +9,11 @@ if (isset($_GET['status']) && ($_GET['status']) == 'Closed') { $status = 'Closed'; - $ticket_status_snippet = "ticket_closed_at IS NOT NULL"; + $ticket_status_snippet = "ticket_resolved_at IS NOT NULL"; } else { // Default - Show open tickets $status = 'Open'; - $ticket_status_snippet = "ticket_closed_at IS NULL"; + $ticket_status_snippet = "ticket_resolved_at IS NULL"; } if (isset($_GET['billable']) && ($_GET['billable']) == '1') { @@ -49,12 +49,12 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); //Get Total tickets open -$sql_total_tickets_open = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS total_tickets_open FROM tickets WHERE ticket_client_id = $client_id AND ticket_closed_at IS NULL"); +$sql_total_tickets_open = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS total_tickets_open FROM tickets WHERE ticket_client_id = $client_id AND ticket_resolved_at IS NULL"); $row = mysqli_fetch_array($sql_total_tickets_open); $total_tickets_open = intval($row['total_tickets_open']); //Get Total tickets closed -$sql_total_tickets_closed = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS total_tickets_closed FROM tickets WHERE ticket_client_id = $client_id AND ticket_closed_at IS NOT NULL"); +$sql_total_tickets_closed = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS total_tickets_closed FROM tickets WHERE ticket_client_id = $client_id AND ticket_resolved_at IS NOT NULL"); $row = mysqli_fetch_array($sql_total_tickets_closed); $total_tickets_closed = intval($row['total_tickets_closed']); @@ -215,7 +215,7 @@ ?> - "> + "> @@ -263,8 +263,10 @@ -
-
+ +
+
+ diff --git a/cron.php b/cron.php index 029b18b7f..87bd1691d 100644 --- a/cron.php +++ b/cron.php @@ -51,7 +51,6 @@ $config_ticket_from_name = sanitizeInput($row['config_ticket_from_name']); $config_ticket_from_email = sanitizeInput($row['config_ticket_from_email']); $config_ticket_client_general_notifications = intval($row['config_ticket_client_general_notifications']); -$config_ticket_autoclose = intval($row['config_ticket_autoclose']); $config_ticket_autoclose_hours = intval($row['config_ticket_autoclose_hours']); $config_ticket_new_ticket_notification_email = sanitizeInput($row['config_ticket_new_ticket_notification_email']); @@ -380,86 +379,32 @@ //mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Cron', log_action = 'Task', log_description = 'Cron created sent out recurring tickets'"); -// AUTO CLOSE TICKET - CLOSE -// Automatically silently closes tickets 22 hrs after the last chase +// TICKET RESOLUTION/CLOSURE PROCESS +// Changes tickets status from 'Resolved' >> 'Closed' after a defined interval -// Check to make sure auto-close is enabled -if ($config_ticket_autoclose == 1) { - $sql_tickets_to_chase = mysqli_query( - $mysqli, - "SELECT * FROM tickets - WHERE ticket_status = 4 - AND ticket_updated_at < NOW() - INTERVAL $config_ticket_autoclose_hours HOUR" - ); - - while ($row = mysqli_fetch_array($sql_tickets_to_chase)) { - - $ticket_id = $row['ticket_id']; - $ticket_prefix = sanitizeInput($row['ticket_prefix']); - $ticket_number = intval($row['ticket_number']); - $ticket_subject = sanitizeInput($row['ticket_subject']); - $ticket_status = sanitizeInput($row['ticket_status']); - $ticket_assigned_to = sanitizeInput($row['ticket_assigned_to']); - $client_id = intval($row['ticket_client_id']); - - mysqli_query($mysqli,"UPDATE tickets SET ticket_status = 5, ticket_closed_at = NOW(), ticket_closed_by = $ticket_assigned_to WHERE ticket_id = $ticket_id"); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Ticket', log_action = 'Closed', log_description = '$ticket_prefix$ticket_number auto closed', log_entity_id = $ticket_id"); - - } - - - // AUTO CLOSE TICKETS - CHASE - // Automatically sends a chaser email after approx 48 hrs/2 days - $sql_tickets_to_chase = mysqli_query( - $mysqli, - "SELECT contact_name, contact_email, ticket_id, ticket_prefix, ticket_number, ticket_subject, ticket_status, ticket_client_id FROM tickets - LEFT JOIN clients ON ticket_client_id = client_id - LEFT JOIN contacts ON ticket_contact_id = contact_id - WHERE ticket_status = 4 - AND ticket_updated_at < NOW() - INTERVAL 48 HOUR" - ); - - while ($row = mysqli_fetch_array($sql_tickets_to_chase)) { +$sql_resolved_tickets_to_close = mysqli_query( + $mysqli, + "SELECT * FROM tickets + WHERE ticket_status = 4 + AND ticket_updated_at < NOW() - INTERVAL $config_ticket_autoclose_hours HOUR" +); - $contact_name = sanitizeInput($row['contact_name']); - $contact_email = sanitizeInput($row['contact_email']); - $ticket_id = intval($row['ticket_id']); - $ticket_prefix = sanitizeInput($row['ticket_prefix']); - $ticket_number = intval($row['ticket_number']); - $ticket_subject = sanitizeInput($row['ticket_subject']); - $ticket_status = sanitizeInput( getTicketStatusName($row['ticket_status'])); - $client_id = intval($row['ticket_client_id']); - - $sql_ticket_reply = mysqli_query($mysqli, "SELECT ticket_reply FROM ticket_replies WHERE ticket_reply_type = 'Public' AND ticket_reply_ticket_id = $ticket_id ORDER BY ticket_reply_created_at DESC LIMIT 1"); - $ticket_reply_row = mysqli_fetch_array($sql_ticket_reply); - $ticket_reply = $ticket_reply_row['ticket_reply']; - - $subject = "Ticket pending closure - [$ticket_prefix$ticket_number] - $ticket_subject"; - - $body = "##- Please type your reply above this line -##

Hello, $contact_name

This is an automatic friendly reminder that your ticket regarding \"$ticket_subject\" will be closed, unless you respond.

--------------------------------
$ticket_reply--------------------------------

If your issue is resolved, you can ignore this email - the ticket will automatically close. If you need further assistance, please respond to this email.

Ticket: $ticket_prefix$ticket_number
Subject: $ticket_subject
Status: $ticket_status
Portal: https://$config_base_url/portal/ticket.php?id=$ticket_id

--
$company_name - Support
$config_ticket_from_email
$company_phone"; +while ($row = mysqli_fetch_array($sql_resolved_tickets_to_close)) { - $data = [ - [ - 'from' => $config_ticket_from_email, - 'from_name' => $config_ticket_from_name, - 'recipient' => $contact_email, - 'recipient_name' => $contact_name, - 'subject' => $subject, - 'body' => $body - ] - ]; - $mail = addToMailQueue($mysqli, $data); + $ticket_id = $row['ticket_id']; + $ticket_prefix = sanitizeInput($row['ticket_prefix']); + $ticket_number = intval($row['ticket_number']); + $ticket_subject = sanitizeInput($row['ticket_subject']); + $ticket_status = sanitizeInput($row['ticket_status']); + $ticket_assigned_to = sanitizeInput($row['ticket_assigned_to']); + $client_id = intval($row['ticket_client_id']); - if ($mail !== true) { - mysqli_query($mysqli,"INSERT INTO notifications SET notification_type = 'Mail', notification = 'Failed to send email to $contact_email'"); - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Mail', log_action = 'Error', log_description = 'Failed to send email to $contact_email regarding $subject. $mail'"); - } + mysqli_query($mysqli,"UPDATE tickets SET ticket_status = 5, ticket_closed_at = NOW(), ticket_closed_by = $ticket_assigned_to WHERE ticket_id = $ticket_id"); - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Ticket Reply', log_action = 'Create', log_description = 'Auto close chaser email sent to $contact_email for ticket $ticket_prefix$ticket_number - $ticket_subject', log_client_id = $client_id"); + //Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Ticket', log_action = 'Closed', log_description = '$ticket_prefix$ticket_number auto closed', log_entity_id = $ticket_id"); - } + //TODO: Add client notifs if $config_ticket_client_general_notifications is on } if ($config_send_invoice_reminders == 1) { diff --git a/cron_ticket_email_parser.php b/cron_ticket_email_parser.php index 672377fe2..30d1acc04 100644 --- a/cron_ticket_email_parser.php +++ b/cron_ticket_email_parser.php @@ -95,7 +95,10 @@ function addTicket($contact_id, $contact_name, $contact_email, $client_id, $date $contact_email_esc = mysqli_real_escape_string($mysqli, $contact_email); $client_id_esc = intval($client_id); - mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$ticket_prefix_esc', ticket_number = $ticket_number, ticket_subject = '$subject_esc', ticket_details = '$message_esc', ticket_priority = 'Low', ticket_status = 1, ticket_created_by = 0, ticket_contact_id = $contact_id, ticket_client_id = $client_id_esc"); + //Generate a unique URL key for clients to access + $url_key = randomString(156); + + mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$ticket_prefix_esc', ticket_number = $ticket_number, ticket_subject = '$subject_esc', ticket_details = '$message_esc', ticket_priority = 'Low', ticket_status = 1, ticket_created_by = 0, ticket_contact_id = $contact_id, ticket_url_key = '$url_key', ticket_client_id = $client_id_esc"); $id = mysqli_insert_id($mysqli); echo "Created new ticket.
"; @@ -213,7 +216,7 @@ function addReply($from_email, $date, $subject, $ticket_number, $message, $attac mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Ticket', notification = 'Email parser: $from_email attempted to re-open ticket $config_ticket_prefix_esc$ticket_number_esc (ID $ticket_id_esc) - check inbox manually to see email', notification_action = 'ticket.php?ticket_id=$ticket_id_esc', notification_client_id = $client_id_esc"); $email_subject = "Action required: This ticket is already closed"; - $email_body = "Hi there,

You've tried to reply to a ticket that is closed - we won't see your response.

Please raise a new ticket by sending a fresh e-mail to our support address below.

--
$company_name - Support
$config_ticket_from_email
$company_phone"; + $email_body = "Hi there,

You've tried to reply to a ticket that is closed - we won't see your response.

Please raise a new ticket by sending a new e-mail to our support address below.

--
$company_name - Support
$config_ticket_from_email
$company_phone"; $data = [ [ @@ -301,7 +304,7 @@ function addReply($from_email, $date, $subject, $ticket_number, $message, $attac } } - mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 2 WHERE ticket_id = $ticket_id AND ticket_client_id = $client_id LIMIT 1"); + 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"); echo "Updated existing ticket.
"; mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Update', log_description = 'Email parser: Client contact $from_email_esc updated ticket $config_ticket_prefix$ticket_number_esc ($subject)', log_client_id = $client_id"); diff --git a/database_updates.php b/database_updates.php index b4a01cfad..76df2e417 100644 --- a/database_updates.php +++ b/database_updates.php @@ -2084,16 +2084,52 @@ mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.4.2'"); } - // if (CURRENT_DATABASE_VERSION == '1.4.2') { - mysqli_query($mysqli, "ALTER TABLE `settings` ADD `config_ticket_email_parse_unknown_senders` INT(1) NOT NULL DEFAULT '0' AFTER `config_ticket_email_parse`"); + if (CURRENT_DATABASE_VERSION == '1.4.2') { + mysqli_query($mysqli, "ALTER TABLE `settings` ADD `config_ticket_email_parse_unknown_senders` INT(1) NOT NULL DEFAULT '0' AFTER `config_ticket_email_parse`"); - mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.4.3'"); - // } + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.4.3'"); + } + + if (CURRENT_DATABASE_VERSION == '1.4.3') { + + // Add ticket URL key column + mysqli_query($mysqli, "ALTER TABLE `tickets` ADD `ticket_url_key` VARCHAR(200) DEFAULT NULL AFTER `ticket_feedback`"); + // Populate pre-existing columns for open tickets + $sql_tickets_1 = mysqli_query($mysqli, "SELECT ticket_id FROM tickets WHERE tickets.ticket_closed_at IS NULL"); + foreach ($sql_tickets_1 as $row) { + $ticket_id = intval($row['ticket_id']); + $url_key = randomString(156); + mysqli_query($mysqli, "UPDATE tickets SET ticket_url_key = '$url_key' WHERE ticket_id = '$ticket_id'"); + } + + // Add ticket resolved at column + mysqli_query($mysqli, "ALTER TABLE `tickets` ADD `ticket_resolved_at` DATETIME DEFAULT NULL AFTER `ticket_updated_at`"); + // Populate pre-existing columns for closed tickets + $sql_tickets_2 = mysqli_query($mysqli, "SELECT ticket_id, ticket_updated_at, ticket_closed_at FROM tickets WHERE tickets.ticket_closed_at IS NOT NULL"); + foreach ($sql_tickets_2 as $row) { + $ticket_id = intval($row['ticket_id']); + $ticket_updated_at = sanitizeInput($row['ticket_updated_at']); // To keep old updated_at time + $ticket_closed_at = sanitizeInput($row['ticket_closed_at']); + mysqli_query($mysqli, "UPDATE tickets SET ticket_resolved_at = '$ticket_closed_at', ticket_updated_at = '$ticket_updated_at' WHERE ticket_id = '$ticket_id'"); + } + + // Change ticket status 'Auto close' to 'Resolved' + mysqli_query($mysqli, "UPDATE `ticket_statuses` SET `ticket_status_name` = 'Resolved' WHERE `ticket_statuses`.`ticket_status_id` = 4"); + + // Auto-close is no longer optional + mysqli_query($mysqli, "ALTER TABLE `settings` DROP `config_ticket_autoclose`"); + mysqli_query($mysqli, "UPDATE `settings` SET `config_ticket_autoclose_hours` = '72'"); + + // DB Version + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.4.4'"); + + + } - // if (CURRENT_DATABASE_VERSION == '1.4.3') { - // // Insert queries here required to update to DB version 1.4.4 + // if (CURRENT_DATABASE_VERSION == '1.4.4') { + // // Insert queries here required to update to DB version 1.4.5 // // Then, update the database to the next sequential version - // mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.4.4'"); + // mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.4.5'"); // } } else { diff --git a/database_version.php b/database_version.php index 08c5bdfc1..97947f7ba 100644 --- a/database_version.php +++ b/database_version.php @@ -5,4 +5,4 @@ * It is used in conjunction with database_updates.php */ -DEFINE("LATEST_DATABASE_VERSION", "1.4.3"); +DEFINE("LATEST_DATABASE_VERSION", "1.4.4"); diff --git a/db.sql b/db.sql index 9a8d8815f..853fb8b2f 100644 --- a/db.sql +++ b/db.sql @@ -1469,7 +1469,6 @@ CREATE TABLE `settings` ( `config_ticket_email_parse` tinyint(1) NOT NULL DEFAULT 0, `config_ticket_email_parse_unknown_senders` tinyint(1) NOT NULL DEFAULT 0, `config_ticket_client_general_notifications` tinyint(1) NOT NULL DEFAULT 1, - `config_ticket_autoclose` tinyint(1) NOT NULL DEFAULT 0, `config_ticket_autoclose_hours` int(5) NOT NULL DEFAULT 72, `config_ticket_new_ticket_notification_email` varchar(200) DEFAULT NULL, `config_ticket_default_billable` tinyint(1) NOT NULL DEFAULT 0, @@ -1846,8 +1845,10 @@ CREATE TABLE `tickets` ( `ticket_onsite` tinyint(1) NOT NULL DEFAULT 0, `ticket_vendor_ticket_number` varchar(255) DEFAULT NULL, `ticket_feedback` varchar(200) DEFAULT NULL, + `ticket_url_key` varchar(200) DEFAULT NULL, `ticket_created_at` datetime NOT NULL DEFAULT current_timestamp(), `ticket_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(), + `ticket_resolved_at` datetime DEFAULT NULL, `ticket_archived_at` datetime DEFAULT NULL, `ticket_closed_at` datetime DEFAULT NULL, `ticket_created_by` int(11) NOT NULL, diff --git a/functions.php b/functions.php index ae532c05d..e165ddfc9 100644 --- a/functions.php +++ b/functions.php @@ -738,6 +738,10 @@ function sanitizeForEmail($data) function timeAgo($datetime) { + if (is_null($datetime)) { + return "-"; + } + $time = strtotime($datetime); $difference = $time - time(); // Changed to handle future dates diff --git a/get_settings.php b/get_settings.php index aad912363..5aa4e3209 100644 --- a/get_settings.php +++ b/get_settings.php @@ -70,7 +70,6 @@ $config_ticket_email_parse = intval($row['config_ticket_email_parse']); $config_ticket_email_parse_unknown_senders = intval($row['config_ticket_email_parse_unknown_senders']); $config_ticket_client_general_notifications = intval($row['config_ticket_client_general_notifications']); -$config_ticket_autoclose = intval($row['config_ticket_autoclose']); $config_ticket_autoclose_hours = intval($row['config_ticket_autoclose_hours']); $config_ticket_new_ticket_notification_email = $row['config_ticket_new_ticket_notification_email']; $config_ticket_default_billable = intval($row['config_ticket_default_billable']); diff --git a/guest_post.php b/guest_post.php index ee7c63840..bc4702e85 100644 --- a/guest_post.php +++ b/guest_post.php @@ -55,3 +55,83 @@ } +if (isset($_GET['reopen_ticket'], $_GET['url_key'])) { + + $ticket_id = intval($_GET['ticket_id']); + $url_key = sanitizeInput($_GET['url_key']); + + $sql = mysqli_query($mysqli, "SELECT * FROM tickets WHERE ticket_id = $ticket_id AND ticket_url_key = '$url_key' AND ticket_resolved_at IS NOT NULL and ticket_closed_at IS NULL"); + + 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'"); + + // 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"); + + //Logging + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Replied', log_description = '$ticket_id reopened by client (guest)', log_ip = '$session_ip', log_user_agent = '$session_user_agent'"); + + $_SESSION['alert_message'] = "Ticket reopened"; + header("Location: " . $_SERVER["HTTP_REFERER"]); + + } else { + echo "Invalid!!"; + } + +} + +if (isset($_GET['close_ticket'], $_GET['url_key'])) { + + $ticket_id = intval($_GET['ticket_id']); + $url_key = sanitizeInput($_GET['url_key']); + + $sql = mysqli_query($mysqli, "SELECT * FROM tickets WHERE ticket_id = $ticket_id AND ticket_url_key = '$url_key' AND ticket_resolved_at IS NOT NULL and ticket_closed_at IS NULL"); + + if (mysqli_num_rows($sql) == 1) { + + // 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'"); + + // 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"); + + //Logging + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Replied', log_description = '$ticket_id closed by client (guest)', log_ip = '$session_ip', log_user_agent = '$session_user_agent'"); + + $_SESSION['alert_message'] = "Ticket closed"; + header("Location: " . $_SERVER["HTTP_REFERER"]); + + } else { + echo "Invalid!!"; + } + +} + +if (isset($_GET['add_ticket_feedback'], $_GET['url_key'])) { + + $ticket_id = intval($_GET['ticket_id']); + $url_key = sanitizeInput($_GET['url_key']); + $feedback = sanitizeInput($_GET['feedback']); + + $sql = mysqli_query($mysqli, "SELECT * FROM tickets WHERE ticket_id = $ticket_id AND ticket_url_key = '$url_key' AND ticket_closed_at IS NOT NULL"); + + if (mysqli_num_rows($sql) == 1) { + + // Add feedback + mysqli_query($mysqli, "UPDATE tickets SET ticket_feedback = '$feedback' WHERE ticket_id = $ticket_id AND ticket_url_key = '$url_key'"); + + // Notify on bad feedback + if ($feedback == "Bad") { + mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Feedback', notification = 'Guest rated ticket ID $ticket_id as bad'"); + } + + $_SESSION['alert_message'] = "Feedback recorded - thank you"; + header("Location: " . $_SERVER["HTTP_REFERER"]); + + } else { + echo "Invalid!!"; + } + +} diff --git a/guest_view_ticket.php b/guest_view_ticket.php new file mode 100644 index 000000000..2e12c9467 --- /dev/null +++ b/guest_view_ticket.php @@ -0,0 +1,220 @@ +set('URI.AllowedSchemes', ['data' => true, 'src' => true, 'http' => true, 'https' => true]); +$purifier = new HTMLPurifier($purifier_config); + +if (!isset($_GET['ticket_id'], $_GET['url_key'])) { + echo "

Oops, something went wrong! Please raise a ticket if you believe this is an error.

"; + require_once "guest_footer.php"; + exit(); +} + +// Company info +$company_sql_row = mysqli_fetch_array(mysqli_query($mysqli, "SELECT company_phone, company_website FROM companies, settings WHERE companies.company_id = settings.company_id AND companies.company_id = 1")); +$company_phone = formatPhoneNumber($company_sql_row['company_phone']); +$company_website = nullable_htmlentities($company_sql_row['company_website']); + +$url_key = sanitizeInput($_GET['url_key']); +$ticket_id = intval($_GET['ticket_id']); + +$ticket_sql = mysqli_query($mysqli, + "SELECT * FROM tickets + LEFT JOIN users on ticket_assigned_to = user_id + LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id + WHERE ticket_id = $ticket_id AND ticket_url_key = '$url_key'" +); + +if (mysqli_num_rows($ticket_sql) !== 1) { + // Invalid invoice/key + echo "

Oops, something went wrong! Please raise a ticket if you believe this is an error.

"; + require_once "guest_footer.php"; + + exit(); +} + +$ticket_row = mysqli_fetch_array($ticket_sql); + +if ($ticket_row) { + + $ticket_prefix = nullable_htmlentities($ticket_row['ticket_prefix']); + $ticket_number = intval($ticket_row['ticket_number']); + $ticket_status = nullable_htmlentities($ticket_row['ticket_status_name']); + $ticket_priority = nullable_htmlentities($ticket_row['ticket_priority']); + $ticket_subject = nullable_htmlentities($ticket_row['ticket_subject']); + $ticket_details = $purifier->purify($ticket_row['ticket_details']); + $ticket_assigned_to = nullable_htmlentities($ticket_row['user_name']); + $ticket_resolved_at = nullable_htmlentities($ticket_row['ticket_resolved_at']); + $ticket_closed_at = nullable_htmlentities($ticket_row['ticket_closed_at']); + $ticket_feedback = nullable_htmlentities($ticket_row['ticket_feedback']); + + ?> + + + +
+
+

+ Ticket +

+
+ +
+
Subject:
+
+

+ State: +
+ Priority: +
+ + Assigned to: + +

+ +
+
+ +
+ + + + + +
Please log in or reply to the ticket via email to respond
+ + + + +

Your ticket has been resolved

+ +
+ +
+
+ + + +

Ticket closed. Please rate your ticket

+ +
+
+
+ Good +
+ +
+ Bad +
+
+
+
+ + + +

Rated -- Thanks for your feedback!

+ + + + + +
+
+ + purify($row['ticket_reply']); + $ticket_reply_created_at = nullable_htmlentities($row['ticket_reply_created_at']); + $ticket_reply_updated_at = nullable_htmlentities($row['ticket_reply_updated_at']); + $ticket_reply_by = intval($row['ticket_reply_by']); + $ticket_reply_type = $row['ticket_reply_type']; + + if ($ticket_reply_type == "Client") { + $ticket_reply_by_display = nullable_htmlentities($row['contact_name']); + $user_initials = initials($row['contact_name']); + $user_avatar = $row['contact_photo']; + $avatar_link = "../uploads/clients/$ticket_reply_by/$user_avatar"; + } else { + $ticket_reply_by_display = nullable_htmlentities($row['user_name']); + $user_id = intval($row['user_id']); + $user_avatar = $row['user_avatar']; + $user_initials = initials($row['user_name']); + $avatar_link = "../uploads/users/$user_id/$user_avatar"; + } + ?> + +
mb-3"> +
+

+
+ + User Avatar + + + + + + + +
+ +
+ +
+
+

+
+ +
+ +
+
+ + + + + + + + + +Email from: $contact_email at $date:-

$message"); - mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$config_ticket_prefix', ticket_number = $ticket_number, ticket_subject = '$subject', ticket_details = '$message', ticket_priority = 'Low', ticket_status = 1, ticket_created_by = 0, ticket_contact_id = $contact_id, ticket_client_id = $client_id"); + //Generate a unique URL key for clients to access + $url_key = randomString(156); + + mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$config_ticket_prefix', ticket_number = $ticket_number, ticket_subject = '$subject', ticket_details = '$message', ticket_priority = 'Low', ticket_status = 1, ticket_created_by = 0, ticket_contact_id = $contact_id, ticket_url_key = '$url_key', ticket_client_id = $client_id"); $id = mysqli_insert_id($mysqli); // Logging @@ -366,7 +369,7 @@ function addReply($from_email, $date, $subject, $ticket_number, $message, $attac } // 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 = $client_id LIMIT 1"); + 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"); echo "Updated existing ticket.
"; mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Update', log_description = 'Email parser: Client contact $from_email updated ticket $config_ticket_prefix$ticket_number ($subject)', log_client_id = $client_id"); diff --git a/portal/portal_functions.php b/portal/portal_functions.php index 2b960e13c..fac071c82 100644 --- a/portal/portal_functions.php +++ b/portal/portal_functions.php @@ -23,13 +23,14 @@ function verifyContactTicketAccess($requested_ticket_id, $expected_ticket_state) } // Verify the contact has access to the provided ticket ID - $sql = mysqli_query($mysqli, "SELECT * FROM tickets WHERE ticket_id = $requested_ticket_id AND $ticket_state_snippet AND ticket_client_id = $session_client_id LIMIT 1"); - $row = mysqli_fetch_array($sql); - $ticket_id = $row['ticket_id']; + $row = mysqli_fetch_array(mysqli_query($mysqli, "SELECT * FROM tickets WHERE ticket_id = $requested_ticket_id AND $ticket_state_snippet AND ticket_client_id = $session_client_id LIMIT 1")); + if ($row) { + $ticket_id = $row['ticket_id']; - if (intval($ticket_id) && ($session_contact_id == $row['ticket_contact_id'] || $session_contact_primary == 1 || $session_contact_is_technical_contact)) { - // Client is ticket owner, primary contact, or a technical contact - return true; + if (intval($ticket_id) && ($session_contact_id == $row['ticket_contact_id'] || $session_contact_primary == 1 || $session_contact_is_technical_contact)) { + // Client is ticket owner, primary contact, or a technical contact + return true; + } } // Client is NOT ticket owner or primary/tech contact diff --git a/portal/portal_post.php b/portal/portal_post.php index b98a2555d..2fb50ae09 100644 --- a/portal/portal_post.php +++ b/portal/portal_post.php @@ -12,7 +12,7 @@ $client_id = intval($session_client_id); $contact = intval($session_contact_id); $subject = sanitizeInput($_POST['subject']); - $details = mysqli_real_escape_string($mysqli,($_POST['details'])); + $details = mysqli_real_escape_string($mysqli, ($_POST['details'])); // Get settings from get_settings.php $config_ticket_prefix = sanitizeInput($config_ticket_prefix); @@ -21,6 +21,9 @@ $config_base_url = sanitizeInput($config_base_url); $config_ticket_new_ticket_notification_email = filter_var($config_ticket_new_ticket_notification_email, FILTER_VALIDATE_EMAIL); + //Generate a unique URL key for clients to access + $url_key = randomString(156); + // Ensure priority is low/med/high (as can be user defined) if ($_POST['priority'] !== "Low" && $_POST['priority'] !== "Medium" && $_POST['priority'] !== "High") { $priority = "Low"; @@ -33,7 +36,7 @@ $new_config_ticket_next_number = $config_ticket_next_number + 1; mysqli_query($mysqli, "UPDATE settings SET config_ticket_next_number = $new_config_ticket_next_number WHERE company_id = 1"); - mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$config_ticket_prefix', ticket_number = $ticket_number, ticket_subject = '$subject', ticket_details = '$details', ticket_priority = '$priority', ticket_status = 1, ticket_created_by = 0, ticket_contact_id = $contact, ticket_client_id = $client_id"); + mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$config_ticket_prefix', ticket_number = $ticket_number, ticket_subject = '$subject', ticket_details = '$details', ticket_priority = '$priority', ticket_status = 1, ticket_created_by = 0, ticket_contact_id = $contact, ticket_url_key = '$url_key', ticket_client_id = $client_id"); $id = mysqli_insert_id($mysqli); // Notify agent DL of the new ticket, if populated with a valid email @@ -194,20 +197,66 @@ } +if (isset($_GET['resolve_ticket'])) { + $ticket_id = intval($_GET['resolve_ticket']); + + // Verify the contact has access to the provided ticket ID + if (verifyContactTicketAccess($ticket_id, "Open")) { + + // Resolve the 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"); + + // 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"); + + //Logging + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Resolved', log_description = '$ticket_id resolved by client', log_ip = '$session_ip', log_user_agent = '$session_user_agent'"); + + header("Location: ticket.php?id=" . $ticket_id); + } else { + // The client does not have access to this ticket - send them home + header("Location: index.php"); + exit(); + } +} + +if (isset($_GET['reopen_ticket'])) { + $ticket_id = intval($_GET['reopen_ticket']); + + // Verify the contact has access to the provided ticket ID + if (verifyContactTicketAccess($ticket_id, "Open")) { + + // Re-open 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"); + + // 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"); + + //Logging + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Replied', log_description = '$ticket_id reopened by client', log_ip = '$session_ip', log_user_agent = '$session_user_agent'"); + + header("Location: ticket.php?id=" . $ticket_id); + } else { + // The client does not have access to this ticket - send them home + header("Location: index.php"); + exit(); + } +} + if (isset($_GET['close_ticket'])) { $ticket_id = intval($_GET['close_ticket']); // Verify the contact has access to the provided ticket ID if (verifyContactTicketAccess($ticket_id, "Open")) { - // 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"); // 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"); //Logging - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Closed', log_description = '$ticket_id Closed by client', log_ip = '$session_ip', log_user_agent = '$session_user_agent'"); + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Closed', log_description = '$ticket_id closed by client', log_ip = '$session_ip', log_user_agent = '$session_user_agent'"); header("Location: ticket.php?id=" . $ticket_id); } else { diff --git a/portal/ticket.php b/portal/ticket.php index 7b6725b78..24ca22050 100644 --- a/portal/ticket.php +++ b/portal/ticket.php @@ -48,9 +48,23 @@ $ticket_subject = nullable_htmlentities($ticket_row['ticket_subject']); $ticket_details = $purifier->purify($ticket_row['ticket_details']); $ticket_assigned_to = nullable_htmlentities($ticket_row['user_name']); + $ticket_resolved_at = nullable_htmlentities($ticket_row['ticket_resolved_at']); $ticket_closed_at = nullable_htmlentities($ticket_row['ticket_closed_at']); $ticket_feedback = nullable_htmlentities($ticket_row['ticket_feedback']); + + // Get Tasks + $sql_tasks = mysqli_query( $mysqli, "SELECT * FROM tasks WHERE task_ticket_id = $ticket_id ORDER BY task_order ASC, task_id ASC"); + $task_count = mysqli_num_rows($sql_tasks); + + // Get Completed Task Count + $sql_tasks_completed = mysqli_query($mysqli, + "SELECT * FROM tasks + WHERE task_ticket_id = $ticket_id + AND task_completed_at IS NOT NULL" + ); + $completed_task_count = mysqli_num_rows($sql_tasks_completed); + ?>
@@ -68,8 +82,8 @@

Ticket - Close ticket + if (empty($ticket_resolved_at) && $task_count == $completed_task_count) { ?> + Resolve ticket

@@ -82,7 +96,12 @@
Priority:
+ + + Tasks: +
+ Assigned to:

@@ -90,10 +109,12 @@ +
- + - + +
@@ -106,9 +127,27 @@
+ + + +

Your ticket has been resolved

+ +
+ +
+
+ -

Rate your ticket

+

Ticket closed. Please rate your ticket

@@ -128,7 +167,7 @@ - +

diff --git a/portal/tickets.php b/portal/tickets.php index 52dd16bde..4fe60d06f 100644 --- a/portal/tickets.php +++ b/portal/tickets.php @@ -90,7 +90,7 @@ My Open tickets | - Resolved tickets | + Closed tickets | All my tickets | ##- Please type your reply above this line -##

Hello,

You are now a watcher on a ticket regarding \"$ticket_subject\".

--------------------------------
$ticket_details--------------------------------

Ticket: $ticket_prefix$ticket_number
Subject: $ticket_subject
Status: $ticket_status
Portal: https://$config_base_url/portal/ticket.php?id=$ticket_id

--
$company_name - Support
$config_ticket_from_email
$company_phone"; - + $body = "##- Please type your reply above this line -##

Hello,

You are now a watcher on a ticket regarding \"$ticket_subject\".

--------------------------------
$ticket_details--------------------------------

Ticket: $ticket_prefix$ticket_number
Subject: $ticket_subject
Status: $ticket_status
Guest link: https://$config_base_url/guest_view_ticket.php?ticket_id=$ticket_id&url_key=$url_key

--
$company_name - Support
$config_ticket_from_email
$company_phone"; // Only add watcher to email queue if email is valid if (filter_var($watcher_email, FILTER_VALIDATE_EMAIL)) { @@ -921,8 +924,7 @@ // POST variables $ticket_reply = mysqli_escape_string($mysqli, $_POST['bulk_reply_details']); - $ticket_status = sanitizeInput($_POST['bulk_status']); - $ticket_status_name = sanitizeInput(getTicketStatusName($row['ticket_status'])); + $ticket_status = intval($_POST['bulk_status']); $private_note = intval($_POST['bulk_private_reply']); if ($private_note == 1) { $ticket_reply_type = 'Internal'; @@ -955,6 +957,12 @@ // Update Ticket Status mysqli_query($mysqli, "UPDATE tickets SET ticket_status = '$ticket_status' WHERE ticket_id = $ticket_id"); + // Resolve the ticket, if set + if ($ticket_status == 4) { + mysqli_query($mysqli, "UPDATE tickets SET ticket_resolved_at = NOW() WHERE ticket_id = $ticket_id"); + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Resolved', log_description = 'Ticket ID $ticket_id resolved', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id, log_entity_id = $ticket_id"); + } + // Logging mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket Reply', log_action = 'Create', log_description = '$session_name replied to ticket $ticket_prefix$ticket_number - $ticket_subject and was a $ticket_reply_type reply', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $ticket_reply_id"); @@ -1124,16 +1132,17 @@ $ticket_reply_id = mysqli_insert_id($mysqli); - // Update Ticket Last Response Field + // Update Ticket Status & Last Response Field mysqli_query($mysqli, "UPDATE tickets SET ticket_status = $ticket_status WHERE ticket_id = $ticket_id"); - // CLose the ticket, if set - if ($ticket_status == 5) { - mysqli_query($mysqli, "UPDATE tickets SET ticket_closed_at = NOW() WHERE ticket_id = $ticket_id"); + // Resolve the ticket, if set + if ($ticket_status == 4) { + mysqli_query($mysqli, "UPDATE tickets SET ticket_resolved_at = NOW() WHERE ticket_id = $ticket_id"); + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Resolved', log_description = 'Ticket ID $ticket_id resolved', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id, log_entity_id = $ticket_id"); } // Get Ticket Details - $ticket_sql = mysqli_query($mysqli, "SELECT contact_name, contact_email, ticket_prefix, ticket_number, ticket_subject, ticket_status, ticket_status_name, ticket_client_id, ticket_created_by, ticket_assigned_to + $ticket_sql = mysqli_query($mysqli, "SELECT contact_name, contact_email, ticket_prefix, ticket_number, ticket_subject, ticket_status, ticket_status_name, ticket_url_key, ticket_client_id, ticket_created_by, ticket_assigned_to FROM tickets LEFT JOIN clients ON ticket_client_id = client_id LEFT JOIN contacts ON ticket_contact_id = contact_id @@ -1150,6 +1159,7 @@ $ticket_subject = sanitizeInput($row['ticket_subject']); $ticket_status = intval($row['ticket_status']); $ticket_status_name = sanitizeInput($row['ticket_status_name']); + $url_key = sanitizeInput($row['ticket_url_key']); $client_id = intval($row['ticket_client_id']); $ticket_created_by = intval($row['ticket_created_by']); $ticket_assigned_to = intval($row['ticket_assigned_to']); @@ -1172,10 +1182,9 @@ // Slightly different email subject/text depending on if this update set auto-close if ($ticket_status == 4) { - // Auto-close - $subject = "Ticket update - [$ticket_prefix$ticket_number] - $ticket_subject | (pending closure)"; - $body = "##- Please type your reply above this line -##

Hello $contact_name,

Your ticket regarding $ticket_subject has been updated and is pending closure.

--------------------------------
$ticket_reply
--------------------------------

If your request/issue is resolved, you can simply ignore this email. If you need further assistance, please respond to let us know!

Ticket: $ticket_prefix$ticket_number
Subject: $ticket_subject
Status: $ticket_status_name
Portal: https://$config_base_url/portal/ticket.php?id=$ticket_id

--
$company_name - Support
$config_ticket_from_email
$company_phone"; - } else { + // Resolved + $subject = "Ticket resolved - [$ticket_prefix$ticket_number] - $ticket_subject | (pending closure)"; + $body = "##- Please type your reply above this line -##

Hello $contact_name,

Your ticket regarding $ticket_subject has been marked as solved and is pending closure.

--------------------------------
$ticket_reply
--------------------------------

If your request/issue is resolved, you can simply ignore this email. If you need further assistance, please reply or re-open to let us know!

Ticket: $ticket_prefix$ticket_number
Subject: $ticket_subject
Status: $ticket_status_name
Portal: https://$config_base_url/portal/ticket.php?id=$ticket_id

--
$company_name - Support
$config_ticket_from_email
$company_phone"; } else { // Anything else $subject = "Ticket update - [$ticket_prefix$ticket_number] - $ticket_subject"; $body = "##- Please type your reply above this line -##

Hello $contact_name,

Your ticket regarding $ticket_subject has been updated.

--------------------------------
$ticket_reply
--------------------------------

Ticket: $ticket_prefix$ticket_number
Subject: $ticket_subject
Status: $ticket_status_name
Portal: https://$config_base_url/portal/ticket.php?id=$ticket_id

--
$company_name - Support
$config_ticket_from_email
$company_phone"; @@ -1321,7 +1330,7 @@ // Update current ticket mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = 'Ticket $ticket_prefix$ticket_number merged into $ticket_prefix$merge_into_ticket_number. Comment: $merge_comment', ticket_reply_time_worked = '00:01:00', ticket_reply_type = '$ticket_reply_type', ticket_reply_by = $session_user_id, ticket_reply_ticket_id = $ticket_id"); - mysqli_query($mysqli, "UPDATE tickets SET ticket_status = '5', ticket_closed_at = NOW() WHERE ticket_id = $ticket_id") or die(mysqli_error($mysqli)); + mysqli_query($mysqli, "UPDATE tickets SET ticket_status = '5', ticket_resolved_at = NOW(), ticket_closed_at = NOW() WHERE ticket_id = $ticket_id") or die(mysqli_error($mysqli)); //Update new parent ticket mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = 'Ticket $ticket_prefix$ticket_number was merged into this ticket with comment: $merge_comment.

$ticket_subject
$ticket_details', ticket_reply_time_worked = '00:01:00', ticket_reply_type = '$ticket_reply_type', ticket_reply_by = $session_user_id, ticket_reply_ticket_id = $merge_into_ticket_id"); @@ -1356,10 +1365,105 @@ header("Location: " . $_SERVER["HTTP_REFERER"]); } +if (isset($_GET['resolve_ticket'])) { + + validateTechRole(); + + // CSRF Check + validateCSRFToken($_GET['csrf_token']); + + $ticket_id = intval($_GET['resolve_ticket']); + + mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 4, ticket_resolved_at = NOW() WHERE ticket_id = $ticket_id"); + + //Logging + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Resolved', log_description = 'Ticket ID $ticket_id resolved', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id, log_entity_id = $ticket_id"); + + // Client notification email + if (!empty($config_smtp_host) && $config_ticket_client_general_notifications == 1) { + + // Get details + $ticket_sql = mysqli_query($mysqli, "SELECT contact_name, contact_email, ticket_prefix, ticket_number, ticket_subject, ticket_status_name, ticket_assigned_to, ticket_url_key, ticket_client_id FROM tickets + LEFT JOIN clients ON ticket_client_id = client_id + LEFT JOIN contacts ON ticket_contact_id = contact_id + LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id + WHERE ticket_id = $ticket_id + "); + $row = mysqli_fetch_array($ticket_sql); + + $contact_name = sanitizeInput($row['contact_name']); + $contact_email = sanitizeInput($row['contact_email']); + $ticket_prefix = sanitizeInput($row['ticket_prefix']); + $ticket_number = intval($row['ticket_number']); + $ticket_subject = sanitizeInput($row['ticket_subject']); + $client_id = intval($row['ticket_client_id']); + $ticket_assigned_to = intval($row['ticket_assigned_to']); + $ticket_status = sanitizeInput($row['ticket_status_name']); + $url_key = sanitizeInput($row['ticket_url_key']); + + // Sanitize Config vars from get_settings.php + $config_ticket_from_name = sanitizeInput($config_ticket_from_name); + $config_ticket_from_email = sanitizeInput($config_ticket_from_email); + $config_base_url = sanitizeInput($config_base_url); + + // Get Company Info + $sql = mysqli_query($mysqli, "SELECT company_name, company_phone FROM companies WHERE company_id = 1"); + $row = mysqli_fetch_array($sql); + $company_name = sanitizeInput($row['company_name']); + $company_phone = sanitizeInput(formatPhoneNumber($row['company_phone'])); + + // Check email valid + if (filter_var($contact_email, FILTER_VALIDATE_EMAIL)) { + + $data = []; + + $subject = "Ticket resolved - [$ticket_prefix$ticket_number] - $ticket_subject | (pending closure)"; + $body = "##- Please type your reply above this line -##

Hello $contact_name,

Your ticket regarding $ticket_subject has been marked as solved and is pending closure.

If your request/issue is resolved, you can simply ignore this email. If you need further assistance, please reply or re-open to let us know!

Ticket: $ticket_prefix$ticket_number
Subject: $ticket_subject
Status: $ticket_status
Portal: https://$config_base_url/portal/ticket.php?id=$ticket_id

--
$company_name - Support
$config_ticket_from_email
$company_phone"; + + // Email Ticket Contact + // Queue Mail + + $data[] = [ + 'from' => $config_ticket_from_email, + 'from_name' => $config_ticket_from_name, + 'recipient' => $contact_email, + 'recipient_name' => $contact_name, + 'subject' => $subject, + 'body' => $body + ]; + + // Also Email all the watchers + $sql_watchers = mysqli_query($mysqli, "SELECT watcher_email FROM ticket_watchers WHERE watcher_ticket_id = $ticket_id"); + $body .= "

----------------------------------------
DO NOT REPLY - YOU ARE RECEIVING THIS EMAIL BECAUSE YOU ARE A WATCHER"; + while ($row = mysqli_fetch_array($sql_watchers)) { + $watcher_email = sanitizeInput($row['watcher_email']); + + // Queue Mail + $data[] = [ + 'from' => $config_ticket_from_email, + 'from_name' => $config_ticket_from_name, + 'recipient' => $watcher_email, + 'recipient_name' => $watcher_email, + 'subject' => $subject, + 'body' => $body + ]; + } + addToMailQueue($mysqli, $data); + } + } + //End Mail IF + + $_SESSION['alert_message'] = "Ticket resolved"; + header("Location: " . $_SERVER["HTTP_REFERER"]); +} + if (isset($_GET['close_ticket'])) { validateTechRole(); + // CSRF Check + validateCSRFToken($_GET['csrf_token']); + $ticket_id = intval($_GET['close_ticket']); mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 5, ticket_closed_at = NOW(), ticket_closed_by = $session_user_id WHERE ticket_id = $ticket_id") or die(mysqli_error($mysqli)); @@ -1373,7 +1477,7 @@ if (!empty($config_smtp_host) && $config_ticket_client_general_notifications == 1) { // Get details - $ticket_sql = mysqli_query($mysqli, "SELECT contact_name, contact_email, ticket_prefix, ticket_number, ticket_subject FROM tickets + $ticket_sql = mysqli_query($mysqli, "SELECT contact_name, contact_email, ticket_prefix, ticket_number, ticket_subject, ticket_url_key FROM tickets LEFT JOIN clients ON ticket_client_id = client_id LEFT JOIN contacts ON ticket_contact_id = contact_id WHERE ticket_id = $ticket_id @@ -1385,10 +1489,7 @@ $ticket_prefix = sanitizeInput($row['ticket_prefix']); $ticket_number = intval($row['ticket_number']); $ticket_subject = sanitizeInput($row['ticket_subject']); - $ticket_details = sanitizeInput($row['ticket_details']); - $client_id = intval($row['ticket_client_id']); - $ticket_created_by = intval($row['ticket_created_by']); - $ticket_assigned_to = intval($row['ticket_assigned_to']); + $url_key = sanitizeInput($row['ticket_url_key']); // Sanitize Config vars from get_settings.php $config_ticket_from_name = sanitizeInput($config_ticket_from_name); @@ -1407,7 +1508,8 @@ $data = []; $subject = "Ticket closed - [$ticket_prefix$ticket_number] - $ticket_subject | (do not reply)"; - $body = "Hello $contact_name,

Your ticket regarding \"$ticket_subject\" has been closed.

We hope the request/issue was resolved to your satisfaction. If you need further assistance, please raise a new ticket using the below details. Please do not reply to this email.

Ticket: $ticket_prefix$ticket_number
Subject: $ticket_subject
Portal: https://$config_base_url/portal/ticket.php?id=$ticket_id

--
$company_name - Support
$config_ticket_from_email
$company_phone"; + //$body = "Hello $contact_name,

Your ticket regarding \"$ticket_subject\" has been closed.

We hope the request/issue was resolved to your satisfaction. If you need further assistance, please raise a new ticket using the below details. Please do not reply to this email.

Ticket: $ticket_prefix$ticket_number
Subject: $ticket_subject
Portal: https://$config_base_url/portal/ticket.php?id=$ticket_id

--
$company_name - Support
$config_ticket_from_email
$company_phone"; + $body = "Hello $contact_name,

Your ticket regarding \"$ticket_subject\" has been closed.

We hope the request/issue was resolved to your satisfaction, please provide your feedback here.
If you need further assistance, please raise a new ticket using the below details. Please do not reply to this email.

Ticket: $ticket_prefix$ticket_number
Subject: $ticket_subject
Portal: https://$config_base_url/portal/ticket.php?id=$ticket_id

--
$company_name - Support
$config_ticket_from_email
$company_phone"; // Email Ticket Contact // Queue Mail @@ -1446,6 +1548,21 @@ header("Location: " . $_SERVER["HTTP_REFERER"]); } +if (isset($_GET['reopen_ticket'])) { + + validateTechRole(); + + $ticket_id = intval($_GET['reopen_ticket']); + + mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 2, ticket_resolved_at = NULL WHERE ticket_id = $ticket_id"); + + //Logging + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Reopened', log_description = 'Ticket ID $ticket_id reopened', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id, log_entity_id = $ticket_id"); + + $_SESSION['alert_message'] = "Ticket re-opened"; + header("Location: " . $_SERVER["HTTP_REFERER"]); +} + if (isset($_POST['add_invoice_from_ticket'])) { $invoice_id = intval($_POST['invoice_id']); diff --git a/settings_notifications.php b/settings_notifications.php index 854fe95a4..f222175e4 100644 --- a/settings_notifications.php +++ b/settings_notifications.php @@ -20,7 +20,7 @@
value="1" id="enableCronSwitch"> - +
diff --git a/settings_ticket.php b/settings_ticket.php index 0cb81964a..d2570c7ec 100644 --- a/settings_ticket.php +++ b/settings_ticket.php @@ -11,7 +11,6 @@ -
@@ -58,19 +57,12 @@
-
- value="1" id="ticketAutoCloseSwitch"> - -
-
- -
- +
- +
diff --git a/setup.php b/setup.php index a40658e2b..94cbdd0cb 100644 --- a/setup.php +++ b/setup.php @@ -300,7 +300,7 @@ mysqli_query($mysqli, "INSERT INTO ticket_statuses SET ticket_status_name = 'New', ticket_status_color = '#dc3545'"); // Default ID for new tickets is 1 mysqli_query($mysqli, "INSERT INTO ticket_statuses SET ticket_status_name = 'Open', ticket_status_color = '#007bff'"); // 2 mysqli_query($mysqli, "INSERT INTO ticket_statuses SET ticket_status_name = 'On Hold', ticket_status_color = '#28a745'"); // 3 - mysqli_query($mysqli, "INSERT INTO ticket_statuses SET ticket_status_name = 'Auto Close', ticket_status_color = '#343a40'"); // 4 + mysqli_query($mysqli, "INSERT INTO ticket_statuses SET ticket_status_name = 'Resolved', ticket_status_color = '#343a40'"); // 4 - was auto-close, now resolved mysqli_query($mysqli, "INSERT INTO ticket_statuses SET ticket_status_name = 'Closed', ticket_status_color = '#343a40'"); // 5 // Add default roles diff --git a/ticket.php b/ticket.php index dc96f2799..1adf25ce3 100644 --- a/ticket.php +++ b/ticket.php @@ -84,7 +84,9 @@ $ticket_created_at = nullable_htmlentities($row['ticket_created_at']); $ticket_date = date('Y-m-d', strtotime($ticket_created_at)); $ticket_updated_at = nullable_htmlentities($row['ticket_updated_at']); + $ticket_resolved_at = nullable_htmlentities($row['ticket_resolved_at']); $ticket_closed_at = nullable_htmlentities($row['ticket_closed_at']); + $ticket_closed_by = intval($row['ticket_closed_by']); $ticket_assigned_to = intval($row['ticket_assigned_to']); if (empty($ticket_assigned_to)) { @@ -299,7 +301,7 @@ $completed_task_count = mysqli_num_rows($sql_tasks_completed); // Tasks Completed Percent - if($task_count) { + if ($task_count) { $tasks_completed_percent = round(($completed_task_count / $task_count) * 100); } @@ -348,9 +350,13 @@
Closed by: @@ -358,6 +364,9 @@
Closed at:
+
+ +
Feedback: @@ -365,7 +374,7 @@ @@ -479,9 +488,23 @@ - - - Close + + + + Reopen + +   + + + + + Resolve + + + + + + Close @@ -540,7 +563,7 @@
- + @@ -581,7 +604,12 @@ -
-
-
- -
- -
- + + + + +
+
+
+ +
+ +
+ +
-
- + + + diff --git a/ticket_bulk_reply_modal.php b/ticket_bulk_reply_modal.php index 93f428995..2e2b2fa57 100644 --- a/ticket_bulk_reply_modal.php +++ b/ticket_bulk_reply_modal.php @@ -15,10 +15,20 @@
- + + + + + + + +
diff --git a/ticket_bulk_close_modal.php b/ticket_bulk_resolve_modal.php similarity index 90% rename from ticket_bulk_close_modal.php rename to ticket_bulk_resolve_modal.php index bb8c7910b..db70b7f28 100644 --- a/ticket_bulk_close_modal.php +++ b/ticket_bulk_resolve_modal.php @@ -2,7 +2,7 @@