Merge branch '200-new-audit-trail' into '3-2-0'

Resolve "Move Audit Trail to separate table"

See merge request mike-koch/Mods-for-HESK!72
master
Mike Koch 7 years ago
commit e56943d659

1
.gitignore vendored

@ -8,7 +8,6 @@ admin/archive.php
admin/custom_statuses.php admin/custom_statuses.php
admin/email_templates.php admin/email_templates.php
admin/generate_spam_question.php admin/generate_spam_question.php
admin/priority.php
admin/test_connection.php admin/test_connection.php
attachments/index.htm attachments/index.htm
cache/ cache/

@ -189,15 +189,20 @@ if ($hesk_settings['attachments']['use'] && !empty($attachments)) {
// Add reply // Add reply
$html = $modsForHesk_settings['rich_text_for_tickets']; $html = $modsForHesk_settings['rich_text_for_tickets'];
if ($submit_as_customer) { if ($submit_as_customer) {
hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "replies` (`replyto`,`name`,`message`,`dt`,`attachments`,`html`) VALUES ('" . intval($replyto) . "','" . hesk_dbEscape(addslashes($ticket['name'])) . "','" . hesk_dbEscape($message . "<br /><br /><i>{$hesklang['creb']} {$_SESSION['name']}</i>") . "',NOW(),'" . hesk_dbEscape($myattachments) . "', '" . $html . "')"); hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "replies` (`replyto`,`name`,`message`,`dt`,`attachments`,`html`) VALUES ('" . intval($replyto) . "','" . hesk_dbEscape(addslashes($ticket['name'])) . "','" . hesk_dbEscape($message . "<br /><br /><i>{$hesklang['creb']} {$_SESSION['name']}</i>") . "','" . hesk_dbEscape(hesk_date()) . "','" . hesk_dbEscape($myattachments) . "', '" . $html . "')");
} else { } else {
hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "replies` (`replyto`,`name`,`message`,`dt`,`attachments`,`staffid`,`html`) VALUES ('" . intval($replyto) . "','" . hesk_dbEscape(addslashes($_SESSION['name'])) . "','" . hesk_dbEscape($message) . "',NOW(),'" . hesk_dbEscape($myattachments) . "','" . intval($_SESSION['id']) . "', '" . $html . "')"); hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "replies` (`replyto`,`name`,`message`,`dt`,`attachments`,`staffid`,`html`) VALUES ('" . intval($replyto) . "','" . hesk_dbEscape(addslashes($_SESSION['name'])) . "','" . hesk_dbEscape($message) . "','" . hesk_dbEscape(hesk_date()) . "','" . hesk_dbEscape($myattachments) . "','" . intval($_SESSION['id']) . "', '" . $html . "')");
} }
/* Track ticket status changes for history */ /* Track ticket status changes for history */
$revision = ''; $revision = '';
/* Change the status of priority? */ /* Change the status of priority? */
$audit_priority = null;
$audit_closed = null;
$audit_status = null;
$audit_customer_status = null;
$audit_assigned_self = null;
if (!empty($_POST['set_priority'])) { if (!empty($_POST['set_priority'])) {
$priority = intval(hesk_POST('priority')); $priority = intval(hesk_POST('priority'));
if ($priority < 0 || $priority > 3) { if ($priority < 0 || $priority > 3) {
@ -211,9 +216,17 @@ if (!empty($_POST['set_priority'])) {
3 => $hesklang['low'] 3 => $hesklang['low']
); );
$revision = sprintf($hesklang['thist8'], hesk_date(), $options[$priority], $_SESSION['name'] . ' (' . $_SESSION['user'] . ')'); $plain_options = array(
0 => 'critical',
1 => 'high',
2 => 'medium',
3 => 'low'
);
$priority_sql = ",`priority`='$priority' ";
$priority_sql = ",`priority`='$priority', `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') "; $audit_priority = array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')',
1 => $plain_options[$priority]);
} else { } else {
$priority_sql = ""; $priority_sql = "";
} }
@ -238,8 +251,11 @@ if ($ticket['locked']) {
$newStatus = hesk_dbFetchAssoc($newStatusRs); $newStatus = hesk_dbFetchAssoc($newStatusRs);
if ($newStatus['IsClosed'] && hesk_checkPermission('can_resolve', 0)) { if ($newStatus['IsClosed'] && hesk_checkPermission('can_resolve', 0)) {
$revision = sprintf($hesklang['thist3'], hesk_date(), $_SESSION['name'] . ' (' . $_SESSION['user'] . ')'); $audit_closed = array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
$sql_status = " , `closedat`=NOW(), `closedby`=" . intval($_SESSION['id']) . ", `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') "; $audit_status = array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')',
1 => mfh_getDisplayTextForStatusId($new_status)
);
$sql_status = " , `closedat`=NOW(), `closedby`=" . intval($_SESSION['id']) . " ";
// Lock the ticket if customers are not allowed to reopen tickets // Lock the ticket if customers are not allowed to reopen tickets
if ($hesk_settings['custopen'] != 1) { if ($hesk_settings['custopen'] != 1) {
@ -247,8 +263,8 @@ if ($ticket['locked']) {
} }
} else { } else {
// Ticket isn't being closed, just add the history to the sql query (or tried to close but doesn't have permission) // Ticket isn't being closed, just add the history to the sql query (or tried to close but doesn't have permission)
$revision = sprintf($hesklang['thist9'], hesk_date(), $hesklang[$newStatus['Key']], $_SESSION['name'] . ' (' . $_SESSION['user'] . ')'); $audit_status = array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')',
$sql_status = " , `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') "; 1 => mfh_getDisplayTextForStatusId($new_status));
} }
} }
} // -> Submit as Customer reply } // -> Submit as Customer reply
@ -259,8 +275,8 @@ elseif ($submit_as_customer) {
$new_status = $customerReplyStatus['ID']; $new_status = $customerReplyStatus['ID'];
if ($ticket['status'] != $new_status) { if ($ticket['status'] != $new_status) {
$revision = sprintf($hesklang['thist9'], hesk_date(), $hesklang['wait_reply'], $_SESSION['name'] . ' (' . $_SESSION['user'] . ')'); $audit_customer_status = array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')',
$sql_status = " , `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') "; 1 => mfh_getDisplayTextForStatusId($new_status));
} }
} // -> Default: submit as "Replied by staff" } // -> Default: submit as "Replied by staff"
else { else {
@ -282,8 +298,8 @@ if ($time_worked == '00:00:00') {
} }
if (!empty($_POST['assign_self']) && (hesk_checkPermission('can_assign_self', 0) || (isset($_REQUEST['isManager']) && $_REQUEST['isManager']))) { if (!empty($_POST['assign_self']) && (hesk_checkPermission('can_assign_self', 0) || (isset($_REQUEST['isManager']) && $_REQUEST['isManager']))) {
$revision = sprintf($hesklang['thist2'], hesk_date(), $_SESSION['name'] . ' (' . $_SESSION['user'] . ')', $_SESSION['name'] . ' (' . $_SESSION['user'] . ')'); $audit_assigned_self = array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
$sql .= " , `owner`=" . intval($_SESSION['id']) . ", `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') "; $sql .= " , `owner`=" . intval($_SESSION['id']) . " ";
} }
$sql .= " $priority_sql "; $sql .= " $priority_sql ";
@ -306,6 +322,29 @@ unset($sql);
/* Update number of replies in the users table */ /* Update number of replies in the users table */
hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "users` SET `replies`=`replies`+1 WHERE `id`='" . intval($_SESSION['id']) . "'"); hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "users` SET `replies`=`replies`+1 WHERE `id`='" . intval($_SESSION['id']) . "'");
//-- Insert necessary audit trail records
if ($audit_priority != null) {
mfh_insert_audit_trail_record($replyto, 'TICKET', 'audit_priority', hesk_date(), $audit_priority);
}
if ($audit_closed != null) {
mfh_insert_audit_trail_record($replyto, 'TICKET', 'audit_closed', hesk_date(), $audit_closed);
}
if ($audit_status != null) {
mfh_insert_audit_trail_record($replyto, 'TICKET', 'audit_status', hesk_date(), $audit_status);
}
if ($audit_customer_status != null) {
mfh_insert_audit_trail_record($replyto, 'TICKET', 'audit_status', hesk_date(),
$audit_customer_status);
}
if ($audit_assigned_self != null) {
mfh_insert_audit_trail_record($replyto, 'TICKET', 'audit_assigned_self', hesk_date(), $audit_assigned_self);
}
// --> Prepare reply message // --> Prepare reply message
// 1. Generate the array with ticket info that can be used in emails // 1. Generate the array with ticket info that can be used in emails

@ -176,11 +176,11 @@ foreach ($hesk_settings['custom_fields'] as $k=>$v) {
$tmpvar['trackid'] = hesk_createID(); $tmpvar['trackid'] = hesk_createID();
// Log who submitted ticket // Log who submitted ticket
$tmpvar['history'] = sprintf($hesklang['thist7'], hesk_date(), $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
$tmpvar['openedby'] = $_SESSION['id']; $tmpvar['openedby'] = $_SESSION['id'];
// Owner // Owner
$tmpvar['owner'] = 0; $tmpvar['owner'] = 0;
$autoassign_owner = null;
if (hesk_checkPermission('can_assign_others', 0)) { if (hesk_checkPermission('can_assign_others', 0)) {
$tmpvar['owner'] = intval(hesk_POST('owner')); $tmpvar['owner'] = intval(hesk_POST('owner'));
@ -192,7 +192,6 @@ if (hesk_checkPermission('can_assign_others', 0)) {
$autoassign_owner = hesk_autoAssignTicket($tmpvar['category']); $autoassign_owner = hesk_autoAssignTicket($tmpvar['category']);
if ($autoassign_owner) { if ($autoassign_owner) {
$tmpvar['owner'] = intval($autoassign_owner['id']); $tmpvar['owner'] = intval($autoassign_owner['id']);
$tmpvar['history'] .= sprintf($hesklang['thist10'], hesk_date(), $autoassign_owner['name'] . ' (' . $autoassign_owner['user'] . ')');
} else { } else {
$tmpvar['owner'] = 0; $tmpvar['owner'] = 0;
} }
@ -315,6 +314,14 @@ $tmpvar['screen_resolution_width'] = "NULL";
// Insert ticket to database // Insert ticket to database
$ticket = hesk_newTicket($tmpvar); $ticket = hesk_newTicket($tmpvar);
mfh_insert_audit_trail_record($ticket['id'], 'TICKET', 'audit_created', hesk_date(),
array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')'));
if ($autoassign_owner) {
mfh_insert_audit_trail_record($ticket['id'], 'TICKET', 'audit_autoassigned', hesk_date(),
array(0 => $autoassign_owner['name'] . ' (' . $autoassign_owner['user'] . ')'));
}
// Notify the customer about the ticket? // Notify the customer about the ticket?
if ($notify && $email_available) { if ($notify && $email_available) {
hesk_notifyCustomer($modsForHesk_settings); hesk_notifyCustomer($modsForHesk_settings);

@ -97,6 +97,37 @@ if (!$ticket['owner'] && !$can_view_unassigned) {
hesk_error($hesklang['ycovtay']); hesk_error($hesklang['ycovtay']);
} }
// Get audit information
$audit_sort = $hesk_settings['new_top'] ? "ASC" : "DESC";
$auditRes = hesk_dbQuery("SELECT `audit`.`id`, `audit`.`language_key`, `audit`.`date`,
`values`.`replacement_index`, `values`.`replacement_value`
FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "audit_trail` AS `audit`
LEFT JOIN `" . hesk_dbEscape($hesk_settings['db_pfix']) . "audit_trail_to_replacement_values` AS `values`
ON `audit`.`id` = `values`.`audit_trail_id`
WHERE `entity_type` = 'TICKET' AND `entity_id` = " . intval($ticket['id']) . "
ORDER BY `audit`.`date` {$audit_sort}");
$audit_records = array();
$current_audit_record = null;
while ($row = hesk_dbFetchAssoc($auditRes)) {
if ($current_audit_record == null || $current_audit_record['id'] != $row['id']) {
if ($current_audit_record != null) {
$audit_records[] = $current_audit_record;
}
$current_audit_record['id'] = $row['id'];
$current_audit_record['language_key'] = $row['language_key'];
$current_audit_record['date'] = $row['date'];
$current_audit_record['replacement_values'] = array();
}
if ($row['replacement_index'] != null) {
$current_audit_record['replacement_values'][intval($row['replacement_index'])] = $row['replacement_value'];
}
}
if ($current_audit_record != null) {
$audit_records[] = $current_audit_record;
}
/* Set last replier name */ /* Set last replier name */
if ($ticket['lastreplier']) { if ($ticket['lastreplier']) {
if (empty($ticket['repliername'])) { if (empty($ticket['repliername'])) {
@ -439,8 +470,10 @@ if ($hesk_settings['time_worked'] && ($can_reply || $can_edit) && isset($_POST['
$time_worked = hesk_getTime($h . ':' . $m . ':' . $s); $time_worked = hesk_getTime($h . ':' . $m . ':' . $s);
/* Update database */ /* Update database */
$revision = sprintf($hesklang['thist14'], hesk_date(), $time_worked, $_SESSION['name'] . ' (' . $_SESSION['user'] . ')'); hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `time_worked`='" . hesk_dbEscape($time_worked) . "' WHERE `trackid`='" . hesk_dbEscape($trackingID) . "'");
hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `time_worked`='" . hesk_dbEscape($time_worked) . "', `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') WHERE `trackid`='" . hesk_dbEscape($trackingID) . "'"); mfh_insert_audit_trail_record($ticket['id'], 'TICKET', 'audit_time_worked', hesk_date(),
array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')',
1 => $time_worked));
/* Show ticket */ /* Show ticket */
hesk_process_messages($hesklang['twu'], 'admin_ticket.php?track=' . $trackingID . '&Refresh=' . mt_rand(10000, 99999), 'SUCCESS'); hesk_process_messages($hesklang['twu'], 'admin_ticket.php?track=' . $trackingID . '&Refresh=' . mt_rand(10000, 99999), 'SUCCESS');
@ -476,13 +509,26 @@ if (($can_reply || $can_edit) && isset($_POST['childTrackingId'])) {
} }
hesk_dbQuery('UPDATE `' . hesk_dbEscape($hesk_settings['db_pfix']) . 'tickets` SET `parent` = ' . intval($ticket['id']) . ' WHERE `trackid` = \'' . hesk_dbEscape(hesk_POST('childTrackingId')) . '\''); hesk_dbQuery('UPDATE `' . hesk_dbEscape($hesk_settings['db_pfix']) . 'tickets` SET `parent` = ' . intval($ticket['id']) . ' WHERE `trackid` = \'' . hesk_dbEscape(hesk_POST('childTrackingId')) . '\'');
mfh_insert_audit_trail_record($ticket['id'], 'TICKET', 'audit_linked_ticket', hesk_date(),
array(
0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')',
1 => hesk_POST('childTrackingId')
));
hesk_process_messages(sprintf($hesklang['link_added'], $_POST['childTrackingId']), 'admin_ticket.php?track=' . $trackingID . '&Refresh=' . mt_rand(10000, 99999), 'SUCCESS'); hesk_process_messages(sprintf($hesklang['link_added'], $_POST['childTrackingId']), 'admin_ticket.php?track=' . $trackingID . '&Refresh=' . mt_rand(10000, 99999), 'SUCCESS');
} }
/* Delete child action */ /* Delete child action */
if (($can_reply || $can_edit) && isset($_GET['deleteChild'])) { if (($can_reply || $can_edit) && isset($_GET['deleteChild'])) {
//-- Delete the relationship //-- Delete the relationship
$innerTrackingRs = hesk_dbQuery("SELECT `trackid` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` WHERE `id` = " . hesk_dbEscape($_GET['deleteChild']));
$innerTrackingId = hesk_dbFetchAssoc($innerTrackingRs);
hesk_dbQuery('UPDATE `' . hesk_dbEscape($hesk_settings['db_pfix']) . 'tickets` SET `parent` = NULL WHERE `ID` = ' . hesk_dbEscape($_GET['deleteChild'])); hesk_dbQuery('UPDATE `' . hesk_dbEscape($hesk_settings['db_pfix']) . 'tickets` SET `parent` = NULL WHERE `ID` = ' . hesk_dbEscape($_GET['deleteChild']));
mfh_insert_audit_trail_record($ticket['id'], 'TICKET', 'audit_unlinked_ticket', hesk_date(),
array(
0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')',
1 => $innerTrackingId['trackid']
));
hesk_process_messages($hesklang['ticket_no_longer_linked'], 'admin_ticket.php?track=' . $trackingID . '&Refresh=' . mt_rand(10000, 99999), 'SUCCESS'); hesk_process_messages($hesklang['ticket_no_longer_linked'], 'admin_ticket.php?track=' . $trackingID . '&Refresh=' . mt_rand(10000, 99999), 'SUCCESS');
} elseif (($can_reply || $can_edit) && isset($_GET['deleteParent'])) { } elseif (($can_reply || $can_edit) && isset($_GET['deleteParent'])) {
@ -528,7 +574,6 @@ if (isset($_GET['delatt']) && hesk_token_check()) {
hesk_dbQuery("DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "attachments` WHERE `att_id`='" . intval($att_id) . "'"); hesk_dbQuery("DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "attachments` WHERE `att_id`='" . intval($att_id) . "'");
/* Update ticket or reply in the database */ /* Update ticket or reply in the database */
$revision = sprintf($hesklang['thist12'], hesk_date(), $att['real_name'], $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
if ($reply) { if ($reply) {
hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "replies` SET `attachments`=REPLACE(`attachments`,'" . hesk_dbEscape($att_id . '#' . $att['real_name'] . '#' . $att['saved_name']) . ",','') WHERE `id`='" . intval($reply) . "'"); hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "replies` SET `attachments`=REPLACE(`attachments`,'" . hesk_dbEscape($att_id . '#' . $att['real_name'] . '#' . $att['saved_name']) . ",','') WHERE `id`='" . intval($reply) . "'");
hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') WHERE `id`='" . intval($ticket['id']) . "'"); hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') WHERE `id`='" . intval($ticket['id']) . "'");
@ -539,6 +584,9 @@ if (isset($_GET['delatt']) && hesk_token_check()) {
hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `attachments`=REPLACE(`attachments`,'" . hesk_dbEscape($att_id . '#' . $att['real_name'] . '#' . $att['saved_name']) . ",','') WHERE `id`='" . intval($ticket['id']) . "'"); hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `attachments`=REPLACE(`attachments`,'" . hesk_dbEscape($att_id . '#' . $att['real_name'] . '#' . $att['saved_name']) . ",','') WHERE `id`='" . intval($ticket['id']) . "'");
hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `attachments`=REPLACE(`attachments`,'" . hesk_dbEscape($att_id . '#' . $att['real_name']) . ",',''), `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') WHERE `id`='" . intval($ticket['id']) . "'"); hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `attachments`=REPLACE(`attachments`,'" . hesk_dbEscape($att_id . '#' . $att['real_name']) . ",',''), `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') WHERE `id`='" . intval($ticket['id']) . "'");
} }
mfh_insert_audit_trail_record($ticket['id'], 'TICKET', 'audit_attachment_deleted', hesk_date(),
array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')',
1 => $att['real_name']));
hesk_process_messages($hesklang['kb_att_rem'], 'admin_ticket.php?track=' . $trackingID . '&Refresh=' . mt_rand(10000, 99999), 'SUCCESS'); hesk_process_messages($hesklang['kb_att_rem'], 'admin_ticket.php?track=' . $trackingID . '&Refresh=' . mt_rand(10000, 99999), 'SUCCESS');
} }
@ -1605,7 +1653,7 @@ function print_form()
} // End print_form() } // End print_form()
function mfh_print_message() { function mfh_print_message() {
global $ticket, $hesklang, $hesk_settings, $can_ban_emails, $can_ban_ips, $trackingID, $modsForHesk_settings; global $ticket, $hesklang, $hesk_settings, $can_ban_emails, $can_ban_ips, $can_unban_emails, $can_unban_ips, $trackingID, $modsForHesk_settings;
?> ?>
<li><i class="fa fa-comment bg-red" data-toggle="tooltip" title="<?php echo $hesklang['original_message']; ?>"></i> <li><i class="fa fa-comment bg-red" data-toggle="tooltip" title="<?php echo $hesklang['original_message']; ?>"></i>
<div class="timeline-item"> <div class="timeline-item">
@ -1766,7 +1814,44 @@ function mfh_print_message() {
function hesk_printTicketReplies() function hesk_printTicketReplies()
{ {
global $hesklang, $hesk_settings, $result, $reply; global $hesklang, $hesk_settings, $result, $reply, $audit_records;
// Sort replies and audit messages. They'll be in the proper order already
$combined_records = array();
foreach ($audit_records as $audit_record) {
$audit_record['SORT_TYPE'] = 'AUDIT_RECORD';
$combined_records[] = $audit_record;
}
while ($reply = hesk_dbFetchAssoc($result)) {
$reply['SORT_TYPE'] = 'REPLY';
$combined_records[] = $reply;
}
// Re-sort them so they're in order by date
usort($combined_records, function ($a, $b) {
$a_date = null;
$b_date = null;
if ($a['SORT_TYPE'] == 'REPLY') {
$a_date = strtotime($a['dt']);
} else {
$a_date = strtotime($a['date']);
}
if ($b['SORT_TYPE'] == 'REPLY') {
$b_date = strtotime($b['dt']);
} else {
$b_date = strtotime($b['date']);
}
if ($a_date === $b_date && $a['SORT_TYPE'] != $b['SORT_TYPE']) {
if ($a['SORT_TYPE'] != $b['SORT_TYPE']) {
return $a['SORT_TYPE'] == 'REPLY' ? -1 : 1;
}
}
return $a_date - $b_date;
});
echo '<ul class="timeline">'; echo '<ul class="timeline">';
if (!$hesk_settings['new_top']) { if (!$hesk_settings['new_top']) {
@ -1775,7 +1860,28 @@ function hesk_printTicketReplies()
echo '<li class="today-top"><i class="fa fa-clock-o bg-gray" data-toggle="tooltip" title="' . $hesklang['timeline_today'] . '"></i></li>'; echo '<li class="today-top"><i class="fa fa-clock-o bg-gray" data-toggle="tooltip" title="' . $hesklang['timeline_today'] . '"></i></li>';
} }
while ($reply = hesk_dbFetchAssoc($result)) { foreach ($combined_records as $record) {
if ($record['SORT_TYPE'] == 'REPLY') {
mfh_print_reply($record);
} else {
mfh_print_audit_record($record);
}
}
if ($hesk_settings['new_top']) {
mfh_print_message();
} else {
echo '<li><i class="fa fa-clock-o bg-gray" data-toggle="tooltip" title="' . $hesklang['timeline_today'] . '"></i></li>';
}
echo '</ul>';
return;
} // End hesk_printTicketReplies()
function mfh_print_reply($reply) {
global $hesklang, $hesk_settings;
$reply['dt'] = hesk_date($reply['dt'], true); $reply['dt'] = hesk_date($reply['dt'], true);
?> ?>
<li> <li>
@ -1835,18 +1941,108 @@ function hesk_printTicketReplies()
</div> </div>
</li> </li>
<?php <?php
} }
if ($hesk_settings['new_top']) { function mfh_print_audit_record($record) {
mfh_print_message(); global $hesklang;
$record['date'] = hesk_date($record['date'], true);
$font_icon = null;
switch ($record['language_key']) {
case 'audit_moved_category':
$font_icon = 'fa-pie-chart';
break;
case 'audit_assigned':
case 'audit_assigned_self':
$font_icon = 'fa-user-plus';
break;
case 'audit_unassigned':
$font_icon = 'fa-user-times';
break;
case 'audit_autoassigned':
$font_icon = 'fa-bolt';
break;
case 'audit_closed':
case 'audit_automatically_closed':
$font_icon = 'fa-check-circle';
break;
case 'audit_opened':
$font_icon = 'fa-circle-o';
break;
case 'audit_locked':
case 'audit_automatically_locked':
$font_icon = 'fa-lock';
break;
case 'audit_unlocked':
$font_icon = 'fa-unlock-alt';
break;
case 'audit_created':
case 'audit_submitted_by':
$font_icon = 'fa-user';
break;
case 'audit_priority':
// The new priority is in arg[1]
$priority = $record['replacement_values'][1];
if ($priority === 'critical') {
$font_icon = 'fa-long-arrow-up';
} elseif ($priority === 'high') {
$font_icon = 'fa-angle-double-up';
} elseif ($priority === 'medium') {
$font_icon = 'fa-angle-double-down';
} else { } else {
echo '<li><i class="fa fa-clock-o bg-gray" data-toggle="tooltip" title="' . $hesklang['timeline_today'] . '"></i></li>'; $font_icon = 'fa-long-arrow-down';
} }
echo '</ul>';
return; // Now localize the text for display
$record['replacement_values'][1] = $hesklang[$priority];
break;
case 'audit_status':
$font_icon = 'fa-exchange';
break;
case 'audit_submitted_via_piping':
case 'audit_submitted_via_pop':
$font_icon = 'fa-envelope-o';
break;
case 'audit_attachment_deleted':
$font_icon = 'fa-paperclip';
break;
case 'audit_merged':
$font_icon = 'fa-code-fork';
break;
case 'audit_time_worked':
$font_icon = 'fa fa-clock-o';
break;
case 'audit_due_date_removed':
$font_icon = 'fa fa-calendar-minus-o';
break;
case 'audit_due_date_changed':
$font_icon = 'fa fa-calendar';
} // End hesk_printTicketReplies() //-- Format the date
$record['replacement_values'][1] = date('Y-m-d', strtotime($record['replacement_values'][1]));
break;
case 'audit_linked_ticket':
$font_icon = 'fa fa-link';
break;
case 'audit_unlinked_ticket':
$font_icon = 'fa fa-chain-broken';
break;
default:
$font_icon = 'fa-question-circle';
break;
}
?>
<li>
<i class="fa <?php echo $font_icon; ?> bg-gray"></i>
<div class="timeline-item">
<span class="time"><i class="fa fa-clock-o"></i> <?php echo $record['date']; ?></span>
<h3 class="timeline-header audit-record">
<?php echo vsprintf($hesklang[$record['language_key']], $record['replacement_values']); ?>
</h3>
</div>
</li>
<?php
}
function hesk_printReplyForm() function hesk_printReplyForm()

@ -52,8 +52,9 @@ $owner = intval(hesk_REQUEST('owner'));
/* If ID is -1 the ticket will be unassigned */ /* If ID is -1 the ticket will be unassigned */
if ($owner == -1) { if ($owner == -1) {
$revision = sprintf($hesklang['thist2'], hesk_date(), '<i>' . $hesklang['unas'] . '</i>', $_SESSION['name'] . ' (' . $_SESSION['user'] . ')'); $res = hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `owner`=0 WHERE `trackid`='" . hesk_dbEscape($trackingID) . "'");
$res = hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `owner`=0 , `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') WHERE `trackid`='" . hesk_dbEscape($trackingID) . "'"); mfh_insert_audit_trail_record($ticket['id'], 'TICKET', 'audit_unassigned', hesk_date(),
array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')'));
hesk_process_messages($hesklang['tunasi2'], $_SERVER['PHP_SELF'], 'SUCCESS'); hesk_process_messages($hesklang['tunasi2'], $_SERVER['PHP_SELF'], 'SUCCESS');
} elseif ($owner < 1) { } elseif ($owner < 1) {
@ -96,8 +97,17 @@ if ($ticket['owner'] && $ticket['owner'] != $owner && hesk_REQUEST('unassigned')
/* Assigning to self? */ /* Assigning to self? */
if ($can_assign_others || ($owner == $_SESSION['id'] && $can_assign_self)) { if ($can_assign_others || ($owner == $_SESSION['id'] && $can_assign_self)) {
$revision = sprintf($hesklang['thist2'], hesk_date(), $row['name'] . ' (' . $row['user'] . ')', $_SESSION['name'] . ' (' . $_SESSION['user'] . ')'); $res = hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `owner`={$owner} WHERE `trackid`='" . hesk_dbEscape($trackingID) . "'");
$res = hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `owner`={$owner} , `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') WHERE `trackid`='" . hesk_dbEscape($trackingID) . "'");
if ($owner == $_SESSION['id'] && $can_assign_self) {
mfh_insert_audit_trail_record($ticket['id'], 'TICKET', 'audit_assigned_self', hesk_date(),
array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')'));
} else {
// current user -> assigned user
mfh_insert_audit_trail_record($ticket['id'], 'TICKET', 'audit_assigned', hesk_date(),
array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')',
1 => $row['name'] . ' (' . $row['user'] . ')'));
}
if ($owner != $_SESSION['id'] && !hesk_checkPermission('can_view_ass_others', 0)) { if ($owner != $_SESSION['id'] && !hesk_checkPermission('can_view_ass_others', 0)) {
$_SERVER['PHP_SELF'] = 'admin_main.php'; $_SERVER['PHP_SELF'] = 'admin_main.php';

@ -37,6 +37,10 @@ hesk_token_check();
/* Ticket ID */ /* Ticket ID */
$trackingID = hesk_cleanID() or die($hesklang['int_error'] . ': ' . $hesklang['no_trackID']); $trackingID = hesk_cleanID() or die($hesklang['int_error'] . ': ' . $hesklang['no_trackID']);
$ticket_id_rs = hesk_dbQuery("SELECT `id` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` WHERE `trackid` = '" . hesk_dbEscape($trackingID) . "'");
$ticket_id_row = hesk_dbFetchAssoc($ticket_id_rs);
$ticket_id = $ticket_id_row['id'];
/* Valid statuses */ /* Valid statuses */
$statusSql = "SELECT `ID` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "statuses`"; $statusSql = "SELECT `ID` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "statuses`";
$status_options = array(); $status_options = array();
@ -54,6 +58,11 @@ if (!isset($status_options[$status])) {
$locked = 0; $locked = 0;
$audit_closed = null;
$audit_locked = null;
$audit_status = null;
$audit_opened = null;
$statusRow = hesk_dbFetchAssoc(hesk_dbQuery("SELECT `ID`, `IsClosed` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "statuses` WHERE ID = " . $status)); $statusRow = hesk_dbFetchAssoc(hesk_dbQuery("SELECT `ID`, `IsClosed` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "statuses` WHERE ID = " . $status));
if ($statusRow['IsClosed']) // Closed if ($statusRow['IsClosed']) // Closed
{ {
@ -62,10 +71,14 @@ if ($statusRow['IsClosed']) // Closed
} }
$action = $hesklang['ticket_been'] . ' ' . $hesklang['close']; $action = $hesklang['ticket_been'] . ' ' . $hesklang['close'];
$revision = sprintf($hesklang['thist3'], hesk_date(), $_SESSION['name'] . ' (' . $_SESSION['user'] . ')'); $audit_closed = array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
$audit_status = array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')',
1 => $status_options[$status]);
if ($hesk_settings['custopen'] != 1) { if ($hesk_settings['custopen'] != 1) {
$locked = 1; $locked = 1;
$audit_locked = array();
} }
// Notify customer of closed ticket? // Notify customer of closed ticket?
@ -91,21 +104,43 @@ if ($statusRow['IsClosed']) // Closed
} elseif ($statusRow['IsNewTicketStatus'] == 0) //Ticket is still open, but not new } elseif ($statusRow['IsNewTicketStatus'] == 0) //Ticket is still open, but not new
{ {
$action = sprintf($hesklang['tsst'], $status_options[$status]); $action = sprintf($hesklang['tsst'], $status_options[$status]);
$revision = sprintf($hesklang['thist9'], hesk_date(), $status_options[$status], $_SESSION['name'] . ' (' . $_SESSION['user'] . ')'); $audit_status = array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')',
1 => $status_options[$status]);
// Ticket is not resolved // Ticket is not resolved
$closedby_sql = ' , `closedat`=NULL, `closedby`=NULL '; $closedby_sql = ' , `closedat`=NULL, `closedby`=NULL ';
} else // Ticket is marked as "NEW" } else // Ticket is marked as "NEW"
{ {
$action = $hesklang['ticket_been'] . ' ' . $hesklang['opened']; $action = $hesklang['ticket_been'] . ' ' . $hesklang['opened'];
$revision = sprintf($hesklang['thist4'], hesk_date(), $_SESSION['name'] . ' (' . $_SESSION['user'] . ')'); $audit_opened = array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
// Ticket is not resolved // Ticket is not resolved
$closedby_sql = ' , `closedat`=NULL, `closedby`=NULL '; $closedby_sql = ' , `closedat`=NULL, `closedby`=NULL ';
} }
hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `status`='{$status}', `locked`='{$locked}' $closedby_sql , `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') WHERE `trackid`='" . hesk_dbEscape($trackingID) . "'"); hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `status`='{$status}', `locked`='{$locked}' $closedby_sql WHERE `trackid`='" . hesk_dbEscape($trackingID) . "'");
if ($audit_status !== null) {
mfh_insert_audit_trail_record($ticket_id, 'TICKET', 'audit_status', hesk_date(),
$audit_status);
}
if ($audit_closed !== null) {
mfh_insert_audit_trail_record($ticket_id, 'TICKET', 'audit_closed', hesk_date(),
$audit_closed);
}
if ($audit_locked !== null) {
mfh_insert_audit_trail_record($ticket_id, 'TICKET', 'audit_automatically_locked', hesk_date(),
array());
}
if ($audit_opened !== null) {
mfh_insert_audit_trail_record($ticket_id, 'TICKET', 'audit_opened', hesk_date(),
$audit_opened);
}
if (hesk_dbAffectedRows() != 1) { if (hesk_dbAffectedRows() != 1) {
hesk_error("$hesklang[int_error]: $hesklang[trackID_not_found]."); hesk_error("$hesklang[int_error]: $hesklang[trackID_not_found].");

@ -81,10 +81,10 @@ $i = 0;
// Possible priorities // Possible priorities
$priorities = array( $priorities = array(
'critical' => array('value' => 0, 'text' => $hesklang['critical'], 'formatted' => '<font class="critical">' . $hesklang['critical'] . '</font>'), 'critical' => array('value' => 0, 'lang' => 'critical', 'text' => $hesklang['critical'], 'formatted' => '<font class="critical">' . $hesklang['critical'] . '</font>'),
'high' => array('value' => 1, 'text' => $hesklang['high'], 'formatted' => '<font class="important">' . $hesklang['high'] . '</font>'), 'high' => array('value' => 1, 'lang' => 'high', 'text' => $hesklang['high'], 'formatted' => '<font class="important">' . $hesklang['high'] . '</font>'),
'medium' => array('value' => 2, 'text' => $hesklang['medium'], 'formatted' => '<font class="medium">' . $hesklang['medium'] . '</font>'), 'medium' => array('value' => 2, 'lang' => 'medium', 'text' => $hesklang['medium'], 'formatted' => '<font class="medium">' . $hesklang['medium'] . '</font>'),
'low' => array('value' => 3, 'text' => $hesklang['low'], 'formatted' => $hesklang['low']), 'low' => array('value' => 3, 'lang' => 'low', 'text' => $hesklang['low'], 'formatted' => $hesklang['low']),
); );
// Change priority // Change priority
@ -113,8 +113,10 @@ if (array_key_exists($_POST['a'], $priorities)) {
hesk_okCategory($ticket['category']); hesk_okCategory($ticket['category']);
$revision = sprintf($hesklang['thist8'], hesk_date(), $priority['formatted'], $_SESSION['name'] . ' (' . $_SESSION['user'] . ')'); hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `priority`='{$priority['value']}' WHERE `id`={$this_id}");
hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `priority`='{$priority['value']}', `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') WHERE `id`={$this_id}"); mfh_insert_audit_trail_record($this_id, 'TICKET', 'audit_priority', hesk_date(),
array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')',
1 => $priority['lang']));
$i++; $i++;
} }
@ -133,8 +135,6 @@ elseif ($_POST['a'] == 'delete') {
require(HESK_PATH . 'inc/email_functions.inc.php'); require(HESK_PATH . 'inc/email_functions.inc.php');
} }
$revision = sprintf($hesklang['thist3'], hesk_date(), $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
foreach ($_POST['id'] as $this_id) { foreach ($_POST['id'] as $this_id) {
if (is_array($this_id)) { if (is_array($this_id)) {
continue; continue;
@ -222,8 +222,6 @@ else {
hesk_token_check('POST'); hesk_token_check('POST');
require(HESK_PATH . 'inc/email_functions.inc.php'); require(HESK_PATH . 'inc/email_functions.inc.php');
$revision = sprintf($hesklang['thist3'], hesk_date(), $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
foreach ($_POST['id'] as $this_id) { foreach ($_POST['id'] as $this_id) {
if (is_array($this_id)) { if (is_array($this_id)) {
continue; continue;
@ -239,7 +237,11 @@ else {
$closedStatusRS = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "statuses` WHERE `IsStaffClosedOption` = 1"); $closedStatusRS = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "statuses` WHERE `IsStaffClosedOption` = 1");
$closedStatus = hesk_dbFetchAssoc($closedStatusRS); $closedStatus = hesk_dbFetchAssoc($closedStatusRS);
hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `status`='" . $closedStatus['ID'] . "', `closedat`=NOW(), `closedby`=" . intval($_SESSION['id']) . ", `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') WHERE `id`='" . intval($this_id) . "'"); hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `status`='" . $closedStatus['ID'] . "', `closedat`=NOW(), `closedby`=" . intval($_SESSION['id']) . " WHERE `id`='" . intval($this_id) . "'");
mfh_insert_audit_trail_record($this_id, 'TICKET', 'audit_closed', hesk_date(),
array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')'));
$i++; $i++;
// Notify customer of closed ticket? // Notify customer of closed ticket?
@ -284,6 +286,14 @@ function hesk_fullyDeleteTicket()
/* Delete ticket notes */ /* Delete ticket notes */
hesk_dbQuery("DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "notes` WHERE `ticket`='" . intval($ticket['id']) . "'"); hesk_dbQuery("DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "notes` WHERE `ticket`='" . intval($ticket['id']) . "'");
/* Delete audit trail records */
hesk_dbQuery("DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "audit_trail_to_replacement_values`
WHERE `audit_trail_id` IN (
SELECT `id` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "audit_trail`
WHERE `entity_type` = 'TICKET' AND `entity_id` = " . intval($ticket['id']) . ")");
hesk_dbQuery("DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "audit_trail` WHERE `entity_type`='TICKET'
AND `entity_id` = " . intval($ticket['id']));
/* Delete ticket reply drafts */ /* Delete ticket reply drafts */
hesk_dbQuery("DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "reply_drafts` WHERE `ticket`=" . intval($ticket['id'])); hesk_dbQuery("DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "reply_drafts` WHERE `ticket`=" . intval($ticket['id']));

@ -202,7 +202,6 @@ function do_login()
/* Close any old tickets here so Cron jobs aren't necessary */ /* Close any old tickets here so Cron jobs aren't necessary */
if ($hesk_settings['autoclose']) { if ($hesk_settings['autoclose']) {
$revision = sprintf($hesklang['thist3'], hesk_date(), $hesklang['auto']);
$dt = date('Y-m-d H:i:s', time() - $hesk_settings['autoclose'] * 86400); $dt = date('Y-m-d H:i:s', time() - $hesk_settings['autoclose'] * 86400);
@ -210,9 +209,7 @@ function do_login()
$closedStatus = hesk_dbFetchAssoc($closedStatusRs); $closedStatus = hesk_dbFetchAssoc($closedStatusRs);
// Are we allowed to close tickets in this status? // Are we allowed to close tickets in this status?
if ($closedStatus['Closable'] == 'yes' || $closedStatus['Closable'] == 'sonly') { if ($closedStatus['Closable'] == 'yes' || $closedStatus['Closable'] == 'sonly') {
// Notify customer of closed ticket?
if ($hesk_settings['notify_closed']) {
// Get list of tickets
$result = hesk_dbQuery("SELECT * FROM `" . $hesk_settings['db_pfix'] . "tickets` WHERE `status` = " . $closedStatus['ID'] . " AND `lastchange` <= '" . hesk_dbEscape($dt) . "' "); $result = hesk_dbQuery("SELECT * FROM `" . $hesk_settings['db_pfix'] . "tickets` WHERE `status` = " . $closedStatus['ID'] . " AND `lastchange` <= '" . hesk_dbEscape($dt) . "' ");
if (hesk_dbNumRows($result) > 0) { if (hesk_dbNumRows($result) > 0) {
global $ticket; global $ticket;
@ -226,6 +223,11 @@ function do_login()
$ticket['dt'] = hesk_date($ticket['dt'], true); $ticket['dt'] = hesk_date($ticket['dt'], true);
$ticket['lastchange'] = hesk_date($ticket['lastchange'], true); $ticket['lastchange'] = hesk_date($ticket['lastchange'], true);
$ticket = hesk_ticketToPlain($ticket, 1, 0); $ticket = hesk_ticketToPlain($ticket, 1, 0);
mfh_insert_audit_trail_record($ticket['id'], 'TICKET', 'audit_automatically_closed', hesk_date(), array());
// Notify customer of closed ticket?
if ($hesk_settings['notify_closed']) {
// Get list of tickets
hesk_notifyCustomer($modsForHesk_settings, 'ticket_closed'); hesk_notifyCustomer($modsForHesk_settings, 'ticket_closed');
} }
} }
@ -234,7 +236,7 @@ function do_login()
// Update ticket statuses and history in database if we're allowed to do so // Update ticket statuses and history in database if we're allowed to do so
$defaultCloseRs = hesk_dbQuery('SELECT `ID` FROM `' . hesk_dbEscape($hesk_settings['db_pfix']) . 'statuses` WHERE `IsAutocloseOption` = 1'); $defaultCloseRs = hesk_dbQuery('SELECT `ID` FROM `' . hesk_dbEscape($hesk_settings['db_pfix']) . 'statuses` WHERE `IsAutocloseOption` = 1');
$defaultCloseStatus = hesk_dbFetchAssoc($defaultCloseRs); $defaultCloseStatus = hesk_dbFetchAssoc($defaultCloseRs);
hesk_dbQuery("UPDATE `" . $hesk_settings['db_pfix'] . "tickets` SET `status`=" . intval($defaultCloseStatus['ID']) . ", `closedat`=NOW(), `closedby`='-1', `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') WHERE `status` = '" . $closedStatus['ID'] . "' AND `lastchange` <= '" . hesk_dbEscape($dt) . "' "); hesk_dbQuery("UPDATE `" . $hesk_settings['db_pfix'] . "tickets` SET `status`=" . intval($defaultCloseStatus['ID']) . ", `closedat`=NOW(), `closedby`='-1' WHERE `status` = " . $closedStatus['ID'] . " AND `lastchange` <= '" . hesk_dbEscape($dt) . "' ");
} }
} }

@ -37,27 +37,31 @@ hesk_token_check();
/* Ticket ID */ /* Ticket ID */
$trackingID = hesk_cleanID() or die($hesklang['int_error'] . ': ' . $hesklang['no_trackID']); $trackingID = hesk_cleanID() or die($hesklang['int_error'] . ': ' . $hesklang['no_trackID']);
// Get ticket info
$result = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` WHERE `trackid`='" . hesk_dbEscape($trackingID) . "' LIMIT 1");
if (hesk_dbNumRows($result) != 1) {
hesk_error($hesklang['ticket_not_found']);
}
$ticket = hesk_dbFetchAssoc($result);
$audit_unlocked = null;
$audit_locked = null;
/* New locked status */ /* New locked status */
if (empty($_GET['locked'])) { if (empty($_GET['locked'])) {
$status = 0; $status = 0;
$tmp = $hesklang['tunlock']; $tmp = $hesklang['tunlock'];
$revision = sprintf($hesklang['thist6'], hesk_date(), $_SESSION['name'] . ' (' . $_SESSION['user'] . ')'); $audit_unlocked = array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
$closedby_sql = ' , `closedat`=NULL, `closedby`=NULL '; $closedby_sql = ' , `closedat`=NULL, `closedby`=NULL ';
} else { } else {
$status = 1; $status = 1;
$tmp = $hesklang['tlock']; $tmp = $hesklang['tlock'];
$revision = sprintf($hesklang['thist5'], hesk_date(), $_SESSION['name'] . ' (' . $_SESSION['user'] . ')'); $audit_locked = array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
$closedby_sql = ' , `closedat`=NOW(), `closedby`=' . intval($_SESSION['id']) . ' '; $closedby_sql = ' , `closedat`=NOW(), `closedby`=' . intval($_SESSION['id']) . ' ';
// Notify customer of closed ticket? // Notify customer of closed ticket?
if ($hesk_settings['notify_closed']) { if ($hesk_settings['notify_closed']) {
// Get ticket info
$result = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` WHERE `trackid`='" . hesk_dbEscape($trackingID) . "' LIMIT 1");
if (hesk_dbNumRows($result) != 1) {
hesk_error($hesklang['ticket_not_found']);
}
$ticket = hesk_dbFetchAssoc($result);
$closedStatusRS = hesk_dbQuery('SELECT `ID` FROM `' . hesk_dbEscape($hesk_settings['db_pfix']) . 'statuses` WHERE `IsClosed` = 1'); $closedStatusRS = hesk_dbQuery('SELECT `ID` FROM `' . hesk_dbEscape($hesk_settings['db_pfix']) . 'statuses` WHERE `IsClosed` = 1');
$ticketIsOpen = true; $ticketIsOpen = true;
while ($row = hesk_dbFetchAssoc($closedStatusRS)) { while ($row = hesk_dbFetchAssoc($closedStatusRS)) {
@ -82,7 +86,17 @@ $statusRs = hesk_dbQuery($statusSql);
$statusRow = hesk_dbFetchAssoc($statusRs); $statusRow = hesk_dbFetchAssoc($statusRs);
$statusId = $statusRow['ID']; $statusId = $statusRow['ID'];
hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `status`= {$statusId},`locked`='{$status}' $closedby_sql , `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') WHERE `trackid`='" . hesk_dbEscape($trackingID) . "'"); hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `status`= {$statusId},`locked`='{$status}' $closedby_sql WHERE `trackid`='" . hesk_dbEscape($trackingID) . "'");
if ($audit_unlocked) {
mfh_insert_audit_trail_record($ticket['id'], 'TICKET', 'audit_unlocked', hesk_date(),
$audit_unlocked);
}
if ($audit_locked) {
mfh_insert_audit_trail_record($ticket['id'], 'TICKET', 'audit_locked', hesk_date(),
$audit_locked);
}
/* Back to ticket page and show a success message */ /* Back to ticket page and show a success message */
hesk_process_messages($tmp, 'admin_ticket.php?track=' . $trackingID . '&Refresh=' . rand(10000, 99999), 'SUCCESS'); hesk_process_messages($tmp, 'admin_ticket.php?track=' . $trackingID . '&Refresh=' . rand(10000, 99999), 'SUCCESS');

@ -70,8 +70,6 @@ if (hesk_dbNumRows($res) != 1) {
} }
$ticket = hesk_dbFetchAssoc($res); $ticket = hesk_dbFetchAssoc($res);
/* Log that ticket is being moved */
$history = sprintf($hesklang['thist1'], hesk_date(), $row['name'], $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
/* Is the ticket assigned to someone? If yes, check that the user has access to category or change to unassigned */ /* Is the ticket assigned to someone? If yes, check that the user has access to category or change to unassigned */
$need_to_reassign = 0; $need_to_reassign = 0;
@ -92,18 +90,30 @@ if ($ticket['owner']) {
} }
/* Reassign automatically if possible */ /* Reassign automatically if possible */
$autoassign_owner = null;
if ($need_to_reassign || !$ticket['owner']) { if ($need_to_reassign || !$ticket['owner']) {
$need_to_reassign = 1; $need_to_reassign = 1;
$autoassign_owner = hesk_autoAssignTicket($category); $autoassign_owner = hesk_autoAssignTicket($category);
if ($autoassign_owner) { if ($autoassign_owner) {
$ticket['owner'] = $autoassign_owner['id']; $ticket['owner'] = $autoassign_owner['id'];
$history .= sprintf($hesklang['thist10'], hesk_date(), $autoassign_owner['name'] . ' (' . $autoassign_owner['user'] . ')');
} else { } else {
$ticket['owner'] = 0; $ticket['owner'] = 0;
} }
} }
hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `category`='" . intval($category) . "', `owner`='" . intval($ticket['owner']) . "' , `history`=CONCAT(`history`,'" . hesk_dbEscape($history) . "') WHERE `trackid`='" . hesk_dbEscape($trackingID) . "'"); hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `category`='" . intval($category) . "', `owner`='" . intval($ticket['owner']) . "' WHERE `trackid`='" . hesk_dbEscape($trackingID) . "'");
/* Log that ticket is being moved */
mfh_insert_audit_trail_record($ticket['id'], 'TICKET', 'audit_moved_category', hesk_date(), array(
0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')',
1 => $row['name']
));
if ($autoassign_owner) {
mfh_insert_audit_trail_record($ticket['id'], 'TICKET', 'audit_autoassigned', hesk_date(), array(
0 => $autoassign_owner['name'] . ' (' . $autoassign_owner['user'] . ')'
));
}
$ticket['category'] = $category; $ticket['category'] = $category;

@ -0,0 +1,73 @@
<?php
/**
*
* This file is part of HESK - PHP Help Desk Software.
*
* (c) Copyright Klemen Stirn. All rights reserved.
* https://www.hesk.com
*
* For the full copyright and license agreement information visit
* https://www.hesk.com/eula.php
*
*/
define('IN_SCRIPT',1);
define('HESK_PATH','../');
/* Get all the required files and functions */
require(HESK_PATH . 'hesk_settings.inc.php');
require(HESK_PATH . 'inc/common.inc.php');
require(HESK_PATH . 'inc/admin_functions.inc.php');
hesk_load_database_functions();
hesk_session_start();
hesk_dbConnect();
hesk_isLoggedIn();
/* Check permissions for this feature */
hesk_checkPermission('can_view_tickets');
hesk_checkPermission('can_reply_tickets');
/* A security check */
hesk_token_check('POST');
/* Ticket ID */
$trackingID = hesk_cleanID() or die($hesklang['int_error'].': '.$hesklang['no_trackID']);
$priority = intval( hesk_POST('priority') );
if ($priority < 0 || $priority > 3)
{
hesk_process_messages($hesklang['inpr'],'admin_ticket.php?track='.$trackingID.'&Refresh='.mt_rand(10000,99999),'NOTICE');
}
$options = array(
0 => '<font class="critical">'.$hesklang['critical'].'</font>',
1 => '<font class="important">'.$hesklang['high'].'</font>',
2 => '<font class="medium">'.$hesklang['medium'].'</font>',
3 => $hesklang['low']
);
$plain_options = array(
0 => 'critical',
1 => 'high',
2 => 'medium',
3 => 'low'
);
$ticketRs = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` WHERE `trackid` = '" . hesk_dbEscape($trackingID) . "'");
$ticket = hesk_dbFetchAssoc($ticketRs);
hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` SET `priority`='{$priority}' WHERE `trackid`='".hesk_dbEscape($trackingID)."'");
mfh_insert_audit_trail_record($ticket['id'], 'TICKET', 'audit_priority', hesk_date(), array(
0 => $_SESSION['name'].' ('.$_SESSION['user'].')',
1 => $plain_options[$priority]
));
if (hesk_dbAffectedRows() != 1)
{
hesk_process_messages($hesklang['inpr'],'admin_ticket.php?track='.$trackingID.'&Refresh='.mt_rand(10000,99999),'NOTICE');
}
hesk_process_messages(sprintf($hesklang['chpri2'],$options[$priority]),'admin_ticket.php?track='.$trackingID.'&Refresh='.mt_rand(10000,99999),'SUCCESS');
?>

@ -0,0 +1,19 @@
<?php
namespace BusinessLogic;
class DateTimeHelpers {
static function heskDate($heskSettings, $dt = '', $isStr = true, $return_str = true) {
if (!$dt) {
$dt = time();
} elseif ($isStr) {
$dt = strtotime($dt);
}
// Return formatted date
return $return_str ? date($heskSettings['timeformat'], $dt) : $dt;
}
}

@ -243,7 +243,7 @@ class EmailTemplateParser extends \BaseClass {
$msg = str_replace('%%PRIORITY%%', $priority, $msg); $msg = str_replace('%%PRIORITY%%', $priority, $msg);
$msg = str_replace('%%OWNER%%', $ownerName, $msg); $msg = str_replace('%%OWNER%%', $ownerName, $msg);
$msg = str_replace('%%STATUS%%', $statusName, $msg); $msg = str_replace('%%STATUS%%', $statusName, $msg);
$msg = str_replace('%%EMAIL%%', implode(';',$ticket->email), $msg); $msg = str_replace('%%EMAIL%%', implode(';', $ticket->email), $msg);
$msg = str_replace('%%CREATED%%', $ticket->dateCreated, $msg); $msg = str_replace('%%CREATED%%', $ticket->dateCreated, $msg);
$msg = str_replace('%%UPDATED%%', $ticket->lastChanged, $msg); $msg = str_replace('%%UPDATED%%', $ticket->lastChanged, $msg);
$msg = str_replace('%%ID%%', $ticket->id, $msg); $msg = str_replace('%%ID%%', $ticket->id, $msg);

@ -55,6 +55,21 @@ class UserContext extends \BaseClass {
/* @var $active bool */ /* @var $active bool */
public $active; public $active;
static function buildAnonymousUser() {
$userContext = new UserContext();
$userContext->id = -1;
$userContext->username = "API - ANONYMOUS USER"; // Usernames can't have spaces, so no one will take this username
$userContext->admin = false;
$userContext->name = "ANONYMOUS USER";
$userContext->email = "anonymous-user@example.com";
$userContext->categories = array();
$userContext->permissions = array();
$userContext->autoAssign = false;
$userContext->active = true;
return $userContext;
}
/** /**
* Builds a user context based on the current session. **The session must be active!** * Builds a user context based on the current session. **The session must be active!**
* @param $dataRow array the $_SESSION superglobal or the hesk_users result set * @param $dataRow array the $_SESSION superglobal or the hesk_users result set

@ -0,0 +1,24 @@
<?php
namespace BusinessLogic\Tickets;
class AuditTrail extends \BaseClass {
/* @var $id int */
public $id;
/* @var $entityId int */
public $entityId;
/* @var $entityType string */
public $entityType;
/* @var $languageKey string */
public $languageKey;
/* @var $date string */
public $date;
/* @var $replacementValues string[] */
public $replacementValues;
}

@ -0,0 +1,8 @@
<?php
namespace BusinessLogic\Tickets;
class AuditTrailEntityType extends \BaseClass {
const TICKET = 'TICKET';
}

@ -4,7 +4,7 @@ namespace BusinessLogic\Tickets;
class Ticket extends \BaseClass { class Ticket extends \BaseClass {
static function fromDatabaseRow($row, $linkedTicketsRs, $repliesRs, $heskSettings) { static function fromDatabaseRow($row, $linkedTicketsRs, $repliesRs, $auditRecords, $heskSettings) {
$ticket = new Ticket(); $ticket = new Ticket();
$ticket->id = intval($row['id']); $ticket->id = intval($row['id']);
$ticket->trackingId = $row['trackid']; $ticket->trackingId = $row['trackid'];
@ -143,6 +143,7 @@ class Ticket extends \BaseClass {
$replies[$reply->id] = $reply; $replies[$reply->id] = $reply;
} }
$ticket->replies = $replies; $ticket->replies = $replies;
$ticket->auditTrail = $auditRecords;
return $ticket; return $ticket;
} }
@ -163,7 +164,7 @@ class Ticket extends \BaseClass {
public $name; public $name;
/** /**
* @var array|null * @var string[]|null
*/ */
public $email; public $email;
@ -309,6 +310,11 @@ class Ticket extends \BaseClass {
*/ */
public $auditTrailHtml; public $auditTrailHtml;
/**
* @var AuditTrail
*/
public $auditTrail;
/** /**
* @var string[] * @var string[]
*/ */

@ -2,11 +2,13 @@
namespace BusinessLogic\Tickets; namespace BusinessLogic\Tickets;
use BusinessLogic\DateTimeHelpers;
use BusinessLogic\Emails\Addressees; use BusinessLogic\Emails\Addressees;
use BusinessLogic\Emails\EmailSenderHelper; use BusinessLogic\Emails\EmailSenderHelper;
use BusinessLogic\Emails\EmailTemplateRetriever; use BusinessLogic\Emails\EmailTemplateRetriever;
use BusinessLogic\Exceptions\ValidationException; use BusinessLogic\Exceptions\ValidationException;
use BusinessLogic\Statuses\DefaultStatusForAction; use BusinessLogic\Statuses\DefaultStatusForAction;
use DataAccess\AuditTrail\AuditTrailGateway;
use DataAccess\Security\UserGateway; use DataAccess\Security\UserGateway;
use DataAccess\Settings\ModsForHeskSettingsGateway; use DataAccess\Settings\ModsForHeskSettingsGateway;
use DataAccess\Statuses\StatusGateway; use DataAccess\Statuses\StatusGateway;
@ -56,6 +58,9 @@ class TicketCreator extends \BaseClass {
/* @var $modsForHeskSettingsGateway ModsForHeskSettingsGateway */ /* @var $modsForHeskSettingsGateway ModsForHeskSettingsGateway */
private $modsForHeskSettingsGateway; private $modsForHeskSettingsGateway;
/* @var $auditTrailGateway AuditTrailGateway */
private $auditTrailGateway;
function __construct(NewTicketValidator $newTicketValidator, function __construct(NewTicketValidator $newTicketValidator,
TrackingIdGenerator $trackingIdGenerator, TrackingIdGenerator $trackingIdGenerator,
Autoassigner $autoassigner, Autoassigner $autoassigner,
@ -64,7 +69,8 @@ class TicketCreator extends \BaseClass {
VerifiedEmailChecker $verifiedEmailChecker, VerifiedEmailChecker $verifiedEmailChecker,
EmailSenderHelper $emailSenderHelper, EmailSenderHelper $emailSenderHelper,
UserGateway $userGateway, UserGateway $userGateway,
ModsForHeskSettingsGateway $modsForHeskSettingsGateway) { ModsForHeskSettingsGateway $modsForHeskSettingsGateway,
AuditTrailGateway $auditTrailGateway) {
$this->newTicketValidator = $newTicketValidator; $this->newTicketValidator = $newTicketValidator;
$this->trackingIdGenerator = $trackingIdGenerator; $this->trackingIdGenerator = $trackingIdGenerator;
$this->autoassigner = $autoassigner; $this->autoassigner = $autoassigner;
@ -74,6 +80,7 @@ class TicketCreator extends \BaseClass {
$this->emailSenderHelper = $emailSenderHelper; $this->emailSenderHelper = $emailSenderHelper;
$this->userGateway = $userGateway; $this->userGateway = $userGateway;
$this->modsForHeskSettingsGateway = $modsForHeskSettingsGateway; $this->modsForHeskSettingsGateway = $modsForHeskSettingsGateway;
$this->auditTrailGateway = $auditTrailGateway;
} }
/** /**
@ -113,7 +120,7 @@ class TicketCreator extends \BaseClass {
// Transform one-to-one properties // Transform one-to-one properties
$ticket->name = $ticketRequest->name; $ticket->name = $ticketRequest->name;
$ticket->email = $ticketRequest->email; $ticket->email = $this->getAddressees($ticketRequest->email);
$ticket->priorityId = $ticketRequest->priority; $ticket->priorityId = $ticketRequest->priority;
$ticket->categoryId = $ticketRequest->category; $ticket->categoryId = $ticketRequest->category;
$ticket->subject = $ticketRequest->subject; $ticket->subject = $ticketRequest->subject;
@ -147,8 +154,13 @@ class TicketCreator extends \BaseClass {
$ticket->timeWorked = '00:00:00'; $ticket->timeWorked = '00:00:00';
$ticket->lastReplier = 0; $ticket->lastReplier = 0;
$this->auditTrailGateway->insertAuditTrailRecord($ticket->id, AuditTrailEntityType::TICKET,
'audit_created', DateTimeHelpers::heskDate($heskSettings), array(
0 => $ticket->name
), $heskSettings);
$addressees = new Addressees(); $addressees = new Addressees();
$addressees->to = $this->getAddressees($ticket->email); $addressees->to = $ticket->email;
if ($ticketRequest->sendEmailToCustomer && $emailVerified) { if ($ticketRequest->sendEmailToCustomer && $emailVerified) {
$this->emailSenderHelper->sendEmailForTicket(EmailTemplateRetriever::NEW_TICKET, $ticketRequest->language, $addressees, $ticket, $heskSettings, $modsForHeskSettings); $this->emailSenderHelper->sendEmailForTicket(EmailTemplateRetriever::NEW_TICKET, $ticketRequest->language, $addressees, $ticket, $heskSettings, $modsForHeskSettings);

@ -0,0 +1,28 @@
<?php
namespace DataAccess\AuditTrail;
use DataAccess\CommonDao;
class AuditTrailGateway extends CommonDao {
function insertAuditTrailRecord($entityId, $entityType, $languageKey, $date, $replacementValues, $heskSettings) {
$this->init();
hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($heskSettings['db_pfix']) . "audit_trail` (`entity_id`, `entity_type`,
`language_key`, `date`) VALUES (" . intval($entityId) . ", '" . hesk_dbEscape($entityType) . "',
'" . hesk_dbEscape($languageKey) . "', '" . hesk_dbEscape($date) . "')");
$auditId = hesk_dbInsertID();
foreach ($replacementValues as $replacementIndex => $replacementValue) {
hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($heskSettings['db_pfix']) . "audit_trail_to_replacement_values`
(`audit_trail_id`, `replacement_index`, `replacement_value`) VALUES (" . intval($auditId) . ",
" . intval($replacementIndex) . ", '" . hesk_dbEscape($replacementValue) . "')");
}
$this->close();
return $auditId;
}
}

@ -5,6 +5,8 @@ namespace DataAccess\Tickets;
use BusinessLogic\Attachments\AttachmentType; use BusinessLogic\Attachments\AttachmentType;
use BusinessLogic\Tickets\Attachment; use BusinessLogic\Tickets\Attachment;
use BusinessLogic\Tickets\AuditTrail;
use BusinessLogic\Tickets\AuditTrailEntityType;
use BusinessLogic\Tickets\Ticket; use BusinessLogic\Tickets\Ticket;
use BusinessLogic\Tickets\TicketGatewayGeneratedFields; use BusinessLogic\Tickets\TicketGatewayGeneratedFields;
use DataAccess\CommonDao; use DataAccess\CommonDao;
@ -29,7 +31,42 @@ class TicketGateway extends CommonDao {
$repliesRs = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "replies` WHERE `replyto` = " . intval($id) . " ORDER BY `id` ASC"); $repliesRs = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "replies` WHERE `replyto` = " . intval($id) . " ORDER BY `id` ASC");
$ticket = Ticket::fromDatabaseRow($row, $linkedTicketsRs, $repliesRs, $heskSettings); $auditTrailRs = hesk_dbQuery("SELECT `audit`.`id`, `audit`.`language_key`, `audit`.`date`,
`values`.`replacement_index`, `values`.`replacement_value`
FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "audit_trail` AS `audit`
LEFT JOIN `" . hesk_dbEscape($heskSettings['db_pfix']) . "audit_trail_to_replacement_values` AS `values`
ON `audit`.`id` = `values`.`audit_trail_id`
WHERE `entity_type` = 'TICKET' AND `entity_id` = " . intval($id) . "
ORDER BY `audit`.`date` ASC");
$auditRecords = array();
/* @var $currentAuditRecord AuditTrail|null */
$currentAuditRecord = null;
while ($auditRow = hesk_dbFetchAssoc($auditTrailRs)) {
if ($currentAuditRecord == null || $currentAuditRecord->id != $auditRow['id']) {
if ($currentAuditRecord != null) {
$auditRecords[] = $currentAuditRecord;
}
$currentAuditRecord = new AuditTrail();
$currentAuditRecord->id = $auditRow['id'];
$currentAuditRecord->entityId = $id;
$currentAuditRecord->entityType = AuditTrailEntityType::TICKET;
$currentAuditRecord->languageKey = $auditRow['language_key'];
$currentAuditRecord->date = $auditRow['date'];
$currentAuditRecord->replacementValues = array();
}
if ($auditRow['replacement_index'] != null) {
$currentAuditRecord->replacementValues[intval($auditRow['replacement_index'])] = $auditRow['replacement_value'];
}
}
if ($currentAuditRecord != null) {
$auditRecords[] = $currentAuditRecord;
}
$ticket = Ticket::fromDatabaseRow($row, $linkedTicketsRs, $repliesRs, $auditRecords, $heskSettings);
$this->close(); $this->close();
@ -92,7 +129,7 @@ class TicketGateway extends CommonDao {
function getTicketByTrackingId($trackingId, $heskSettings) { function getTicketByTrackingId($trackingId, $heskSettings) {
$this->init(); $this->init();
$rs = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "tickets` WHERE `trackid` = " . intval($trackingId)); $rs = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "tickets` WHERE `trackid` = '" . hesk_dbEscape($trackingId) . "'");
if (hesk_dbNumRows($rs) === 0) { if (hesk_dbNumRows($rs) === 0) {
return null; return null;
} }
@ -101,7 +138,40 @@ class TicketGateway extends CommonDao {
$linkedTicketsRs = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "tickets` WHERE `parent` = " . intval($trackingId)); $linkedTicketsRs = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "tickets` WHERE `parent` = " . intval($trackingId));
$repliesRs = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "replies` WHERE `replyto` = " . intval($row['id']) . " ORDER BY `id` ASC"); $repliesRs = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "replies` WHERE `replyto` = " . intval($row['id']) . " ORDER BY `id` ASC");
$ticket = Ticket::fromDatabaseRow($row, $linkedTicketsRs, $repliesRs, $heskSettings); $audiTrailRs = hesk_dbQuery("SELECT `audit`.`id`, `audit`.`language_key`, `audit`.`date`,
`values`.`replacement_index`, `values`.`replacement_value`
FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "audit_trail` AS `audit`
LEFT JOIN `" . hesk_dbEscape($heskSettings['db_pfix']) . "audit_trail_to_replacement_values` AS `values`
ON `audit`.`id` = `values`.`audit_trail_id`
WHERE `entity_type` = 'TICKET' AND `entity_id` = " . intval($row['id']));
$auditRecords = array();
/* @var $currentAuditRecord AuditTrail */
$currentAuditRecord = null;
while ($auditRow = hesk_dbFetchAssoc($audiTrailRs)) {
if ($currentAuditRecord == null || $currentAuditRecord->id != $auditRow['id']) {
if ($currentAuditRecord != null) {
$auditRecords[] = $currentAuditRecord;
}
$currentAuditRecord = new AuditTrail();
$currentAuditRecord->id = $auditRow['id'];
$currentAuditRecord->entityId = $row['id'];
$currentAuditRecord->entityType = AuditTrailEntityType::TICKET;
$currentAuditRecord->languageKey = $auditRow['language_key'];
$currentAuditRecord->date = $auditRow['date'];
$currentAuditRecord->replacementValues = array();
}
if ($auditRow['replacement_index'] != null) {
$currentAuditRecord->replacementValues[intval($auditRow['replacement_index'])] = $auditRow['replacement_value'];
}
}
if ($currentAuditRecord != null) {
$auditRecords[] = $currentAuditRecord;
}
$ticket = Ticket::fromDatabaseRow($row, $linkedTicketsRs, $repliesRs, $auditRecords, $heskSettings);
$this->close(); $this->close();
@ -170,6 +240,8 @@ class TicketGateway extends CommonDao {
$ipAddress = $ticket->ipAddress !== null $ipAddress = $ticket->ipAddress !== null
&& $ticket->ipAddress !== '' ? $ticket->ipAddress : ''; && $ticket->ipAddress !== '' ? $ticket->ipAddress : '';
$emailAddresses = implode(';', $ticket->email);
$tableName = $isEmailVerified ? 'tickets' : 'stage_tickets'; $tableName = $isEmailVerified ? 'tickets' : 'stage_tickets';
$sql = "INSERT INTO `" . hesk_dbEscape($heskSettings['db_pfix']) . $tableName ."` $sql = "INSERT INTO `" . hesk_dbEscape($heskSettings['db_pfix']) . $tableName ."`
@ -205,7 +277,7 @@ class TicketGateway extends CommonDao {
( (
'" . hesk_dbEscape($ticket->trackingId) . "', '" . hesk_dbEscape($ticket->trackingId) . "',
'" . hesk_dbEscape($ticket->name) . "', '" . hesk_dbEscape($ticket->name) . "',
'" . hesk_dbEscape($ticket->email) . "', '" . hesk_dbEscape($emailAddresses) . "',
'" . intval($ticket->categoryId) . "', '" . intval($ticket->categoryId) . "',
'" . intval($ticket->priorityId) . "', '" . intval($ticket->priorityId) . "',
'" . hesk_dbEscape($ticket->subject) . "', '" . hesk_dbEscape($ticket->subject) . "',

@ -18,6 +18,7 @@ use BusinessLogic\Tickets\TrackingIdGenerator;
use BusinessLogic\Tickets\VerifiedEmailChecker; use BusinessLogic\Tickets\VerifiedEmailChecker;
use BusinessLogic\ValidationModel; use BusinessLogic\ValidationModel;
use Core\Constants\Priority; use Core\Constants\Priority;
use DataAccess\AuditTrail\AuditTrailGateway;
use DataAccess\Security\UserGateway; use DataAccess\Security\UserGateway;
use DataAccess\Settings\ModsForHeskSettingsGateway; use DataAccess\Settings\ModsForHeskSettingsGateway;
use DataAccess\Statuses\StatusGateway; use DataAccess\Statuses\StatusGateway;
@ -97,6 +98,9 @@ class CreateTicketTest extends TestCase {
/* @var $modsForHeskSettings array */ /* @var $modsForHeskSettings array */
private $modsForHeskSettings; private $modsForHeskSettings;
/* @var $auditTrailGateway \PHPUnit_Framework_MockObject_MockObject|AuditTrailGateway */
private $auditTrailGateway;
protected function setUp() { protected function setUp() {
$this->ticketGateway = $this->createMock(TicketGateway::clazz()); $this->ticketGateway = $this->createMock(TicketGateway::clazz());
$this->newTicketValidator = $this->createMock(NewTicketValidator::clazz()); $this->newTicketValidator = $this->createMock(NewTicketValidator::clazz());
@ -107,10 +111,11 @@ class CreateTicketTest extends TestCase {
$this->emailSenderHelper = $this->createMock(EmailSenderHelper::clazz()); $this->emailSenderHelper = $this->createMock(EmailSenderHelper::clazz());
$this->userGateway = $this->createMock(UserGateway::clazz()); $this->userGateway = $this->createMock(UserGateway::clazz());
$this->modsForHeskSettingsGateway = $this->createMock(ModsForHeskSettingsGateway::clazz()); $this->modsForHeskSettingsGateway = $this->createMock(ModsForHeskSettingsGateway::clazz());
$this->auditTrailGateway = $this->createMock(AuditTrailGateway::clazz());
$this->ticketCreator = new TicketCreator($this->newTicketValidator, $this->trackingIdGenerator, $this->ticketCreator = new TicketCreator($this->newTicketValidator, $this->trackingIdGenerator,
$this->autoassigner, $this->statusGateway, $this->ticketGateway, $this->verifiedEmailChecker, $this->autoassigner, $this->statusGateway, $this->ticketGateway, $this->verifiedEmailChecker,
$this->emailSenderHelper, $this->userGateway, $this->modsForHeskSettingsGateway); $this->emailSenderHelper, $this->userGateway, $this->modsForHeskSettingsGateway, $this->auditTrailGateway);
$this->ticketRequest = new CreateTicketByCustomerModel(); $this->ticketRequest = new CreateTicketByCustomerModel();
$this->ticketRequest->name = 'Name'; $this->ticketRequest->name = 'Name';
@ -126,7 +131,8 @@ class CreateTicketTest extends TestCase {
'require_subject' => 1, 'require_subject' => 1,
'require_message' => 1, 'require_message' => 1,
'custom_fields' => array(), 'custom_fields' => array(),
'autoassign' => 0 'autoassign' => 0,
'timeformat' => 'Y-m-d',
); );
$this->modsForHeskSettings = array( $this->modsForHeskSettings = array(
'customer_email_verification_required' => false 'customer_email_verification_required' => false
@ -222,7 +228,7 @@ class CreateTicketTest extends TestCase {
//-- Assert //-- Assert
self::assertThat($ticket->ticket->name, self::equalTo($this->ticketRequest->name)); self::assertThat($ticket->ticket->name, self::equalTo($this->ticketRequest->name));
self::assertThat($ticket->ticket->email, self::equalTo($this->ticketRequest->email)); self::assertThat($ticket->ticket->email[0], self::equalTo($this->ticketRequest->email));
self::assertThat($ticket->ticket->priorityId, self::equalTo($this->ticketRequest->priority)); self::assertThat($ticket->ticket->priorityId, self::equalTo($this->ticketRequest->priority));
self::assertThat($ticket->ticket->categoryId, self::equalTo($this->ticketRequest->category)); self::assertThat($ticket->ticket->categoryId, self::equalTo($this->ticketRequest->category));
self::assertThat($ticket->ticket->subject, self::equalTo($this->ticketRequest->subject)); self::assertThat($ticket->ticket->subject, self::equalTo($this->ticketRequest->subject));

28
api/composer.lock generated

@ -829,12 +829,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/php-http/discovery.git", "url": "https://github.com/php-http/discovery.git",
"reference": "7b50ab4d6c9fdaa1ed53ae310c955900af6e3372" "reference": "d39a8798c473b6b896059a8de576598e4d17d992"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/php-http/discovery/zipball/7b50ab4d6c9fdaa1ed53ae310c955900af6e3372", "url": "https://api.github.com/repos/php-http/discovery/zipball/d39a8798c473b6b896059a8de576598e4d17d992",
"reference": "7b50ab4d6c9fdaa1ed53ae310c955900af6e3372", "reference": "d39a8798c473b6b896059a8de576598e4d17d992",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -883,7 +883,7 @@
"message", "message",
"psr7" "psr7"
], ],
"time": "2017-08-03 10:12:53" "time": "2017-09-13 14:06:45"
}, },
{ {
"name": "php-http/guzzle6-adapter", "name": "php-http/guzzle6-adapter",
@ -1624,23 +1624,21 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/doctrine/instantiator.git", "url": "https://github.com/doctrine/instantiator.git",
"reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" "reference": "7af8066e48b8a4cbd90849bbe9234ab444057968"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", "url": "https://api.github.com/repos/doctrine/instantiator/zipball/7af8066e48b8a4cbd90849bbe9234ab444057968",
"reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", "reference": "7af8066e48b8a4cbd90849bbe9234ab444057968",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.1" "php": "^7.1"
}, },
"require-dev": { "require-dev": {
"athletic/athletic": "~0.1.8",
"ext-pdo": "*", "ext-pdo": "*",
"ext-phar": "*", "ext-phar": "*",
"phpunit/phpunit": "^6.2.3", "phpunit/phpunit": "^6.2.3"
"squizlabs/php_codesniffer": "^3.0.2"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
@ -1670,7 +1668,7 @@
"constructor", "constructor",
"instantiate" "instantiate"
], ],
"time": "2017-07-22 11:58:36" "time": "2017-09-19T12:41:22+00:00"
}, },
{ {
"name": "myclabs/deep-copy", "name": "myclabs/deep-copy",
@ -1822,12 +1820,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpDocumentor/ReflectionCommon.git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
"reference": "a046af61c36e9162372f205de091a1cab7340f1c" "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/a046af61c36e9162372f205de091a1cab7340f1c", "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
"reference": "a046af61c36e9162372f205de091a1cab7340f1c", "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1868,7 +1866,7 @@
"reflection", "reflection",
"static analysis" "static analysis"
], ],
"time": "2017-04-30 11:58:12" "time": "2017-09-11T18:02:19+00:00"
}, },
{ {
"name": "phpdocumentor/reflection-docblock", "name": "phpdocumentor/reflection-docblock",

@ -43,7 +43,10 @@ function internalOrAuthHandler() {
} }
function publicHandler() { function publicHandler() {
//-- No-op global $userContext;
//-- Create an "anonymous" UserContext
$userContext = \BusinessLogic\Security\UserContext::buildAnonymousUser();
} }
function assertApiIsEnabled() { function assertApiIsEnabled() {
@ -188,7 +191,7 @@ Link::all(array(
'/v1/categories/{i}' => action(\Controllers\Categories\CategoryController::clazz(), array(RequestMethod::GET, RequestMethod::PUT, RequestMethod::DELETE), SecurityHandler::INTERNAL_OR_AUTH_TOKEN), '/v1/categories/{i}' => action(\Controllers\Categories\CategoryController::clazz(), array(RequestMethod::GET, RequestMethod::PUT, RequestMethod::DELETE), SecurityHandler::INTERNAL_OR_AUTH_TOKEN),
'/v1-internal/categories/{i}/sort/{s}' => action(\Controllers\Categories\CategoryController::clazz() . '::sort', array(RequestMethod::POST), SecurityHandler::INTERNAL), '/v1-internal/categories/{i}/sort/{s}' => action(\Controllers\Categories\CategoryController::clazz() . '::sort', array(RequestMethod::POST), SecurityHandler::INTERNAL),
// Tickets // Tickets
'/v1/tickets' => action(\Controllers\Tickets\CustomerTicketController::clazz(), RequestMethod::all()), '/v1/tickets' => action(\Controllers\Tickets\CustomerTicketController::clazz(), RequestMethod::all(), SecurityHandler::OPEN),
// Tickets - Staff // Tickets - Staff
'/v1/staff/tickets/{i}' => action(\Controllers\Tickets\StaffTicketController::clazz(), RequestMethod::all()), '/v1/staff/tickets/{i}' => action(\Controllers\Tickets\StaffTicketController::clazz(), RequestMethod::all()),
// Attachments // Attachments

@ -52,7 +52,7 @@ if ($status == 3) // Closed
} }
$status = $closedStatus; $status = $closedStatus;
$action = $hesklang['closed']; $action = $hesklang['closed'];
$revision = sprintf($hesklang['thist3'], hesk_date(), $hesklang['customer']); $revision_key = 'audit_closed';
if ($hesk_settings['custopen'] != 1) { if ($hesk_settings['custopen'] != 1) {
$locked = 1; $locked = 1;
@ -73,7 +73,7 @@ if ($status == 3) // Closed
$status = $statusRow['ID']; $status = $statusRow['ID'];
$action = $hesklang['opened']; $action = $hesklang['opened'];
$revision = sprintf($hesklang['thist4'], hesk_date(), $hesklang['customer']); $revision_key = 'audit_opened';
// We will ask the customer why is the ticket being reopened // We will ask the customer why is the ticket being reopened
$_SESSION['force_form_top'] = true; $_SESSION['force_form_top'] = true;
@ -94,12 +94,9 @@ hesk_verifyEmailMatch($trackingID);
$_SESSION['t_track'] = $trackingID; $_SESSION['t_track'] = $trackingID;
$_SESSION['t_email'] = $hesk_settings['e_email']; $_SESSION['t_email'] = $hesk_settings['e_email'];
// Load statuses
require_once(HESK_PATH . 'inc/statuses.inc.php');
// Is current ticket status even changeable by customers? // Is current ticket status even changeable by customers?
$ticket = hesk_dbFetchAssoc( hesk_dbQuery( "SELECT `status`, `staffreplies`, `lastreplier` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `trackid`='".hesk_dbEscape($trackingID)."' LIMIT 1") ); $ticket = hesk_dbFetchAssoc( hesk_dbQuery( "SELECT `id`, `status`, `staffreplies`, `lastreplier` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `trackid`='".hesk_dbEscape($trackingID)."' LIMIT 1") );
if (!hesk_can_customer_change_status($ticket['status'])) { if (!mfh_can_customer_change_status($ticket['status'])) {
hesk_process_messages($hesklang['scno'],'ticket.php'); hesk_process_messages($hesklang['scno'],'ticket.php');
} }
@ -121,7 +118,10 @@ if ($oldStatus == 2) {
// Modify values in the database // Modify values in the database
hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `status`='{$status}', `locked`='{$locked}' $closedby_sql , `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') WHERE `trackid`='" . hesk_dbEscape($trackingID) . "' AND `locked` != '1'"); hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `status`='{$status}', `locked`='{$locked}' $closedby_sql WHERE `trackid`='" . hesk_dbEscape($trackingID) . "' AND `locked` != '1'");
// Insert audit trail record
mfh_insert_audit_trail_record($ticket['id'], 'TICKET', $revision_key, hesk_date(), array(0 => $hesklang['customer']));
// Did we modify anything* // Did we modify anything*
if (hesk_dbAffectedRows() != 1) { if (hesk_dbAffectedRows() != 1) {

@ -351,3 +351,8 @@ div.ticket-info {
box-shadow: none; box-shadow: none;
outline: 0; outline: 0;
} }
.timeline > li > .timeline-item > .timeline-header.audit-record {
font-size: 12px;
border-bottom: none;
}

@ -201,7 +201,10 @@ function hesk_mergeTickets($merge_these, $merge_into)
hesk_dbQuery("DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` WHERE `id`='" . intval($row['id']) . "'"); hesk_dbQuery("DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` WHERE `id`='" . intval($row['id']) . "'");
/* Log that ticket has been merged */ /* Log that ticket has been merged */
$history .= sprintf($hesklang['thist13'], hesk_date(), $row['trackid'], $_SESSION['name'] . ' (' . $_SESSION['user'] . ')'); mfh_insert_audit_trail_record($merge_into, 'TICKET', 'audit_merged', hesk_date(), array(
0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')',
1 => $row['trackid']
));
/* Add old ticket ID to target ticket "merged" field */ /* Add old ticket ID to target ticket "merged" field */
$merged .= '#' . $row['trackid']; $merged .= '#' . $row['trackid'];
@ -234,7 +237,7 @@ function hesk_mergeTickets($merge_these, $merge_into)
} }
/* Update history (log) and merged IDs of target ticket */ /* Update history (log) and merged IDs of target ticket */
hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET $replies_sql `time_worked`=ADDTIME(`time_worked`, '" . hesk_dbEscape($sec_worked) . "'), `merged`=CONCAT(`merged`,'" . hesk_dbEscape($merged . '#') . "'), `history`=CONCAT(`history`,'" . hesk_dbEscape($history) . "') WHERE `id`='" . intval($merge_into) . "'"); hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET $replies_sql `time_worked`=ADDTIME(`time_worked`, '" . hesk_dbEscape($sec_worked) . "'), `merged`=CONCAT(`merged`,'" . hesk_dbEscape($merged . '#') . "') WHERE `id`='" . intval($merge_into) . "'");
return true; return true;
@ -419,14 +422,17 @@ function hesk_autoLogin($noredirect = 0)
hesk_setcookie('hesk_p', "$hash", strtotime('+1 year')); hesk_setcookie('hesk_p', "$hash", strtotime('+1 year'));
/* Close any old tickets here so Cron jobs aren't necessary */ /* Close any old tickets here so Cron jobs aren't necessary */
if ($hesk_settings['autoclose']) { $closedStatusRs = hesk_dbQuery('SELECT `ID`, `Closable` FROM `' . hesk_dbEscape($hesk_settings['db_pfix']) . 'statuses` WHERE `IsDefaultStaffReplyStatus` = 1');
$revision = sprintf($hesklang['thist3'], hesk_date(), $hesklang['auto']); $closedStatus = hesk_dbFetchAssoc($closedStatusRs);
// Are we allowed to close tickets in this status?
if (($closedStatus['Closable'] == 'yes' || $closedStatus['Closable'] == 'sonly') && $hesk_settings['autoclose']) {
$dt = date('Y-m-d H:i:s', time() - $hesk_settings['autoclose'] * 86400); $dt = date('Y-m-d H:i:s', time() - $hesk_settings['autoclose'] * 86400);
// Notify customer of closed ticket? // Notify customer of closed ticket?
if ($hesk_settings['notify_closed']) { if ($hesk_settings['notify_closed']) {
// Get list of tickets // Get list of tickets
$result = hesk_dbQuery("SELECT * FROM `" . $hesk_settings['db_pfix'] . "tickets` WHERE `status` = '2' AND `lastchange` <= '" . hesk_dbEscape($dt) . "' "); $result = hesk_dbQuery("SELECT * FROM `" . $hesk_settings['db_pfix'] . "tickets` WHERE `status` = " . $closedStatus['ID'] . " AND `lastchange` <= '" . hesk_dbEscape($dt) . "' ");
if (hesk_dbNumRows($result) > 0) { if (hesk_dbNumRows($result) > 0) {
global $ticket; global $ticket;
@ -439,6 +445,7 @@ function hesk_autoLogin($noredirect = 0)
$ticket['dt'] = hesk_date($ticket['dt'], true); $ticket['dt'] = hesk_date($ticket['dt'], true);
$ticket['lastchange'] = hesk_date($ticket['lastchange'], true); $ticket['lastchange'] = hesk_date($ticket['lastchange'], true);
$ticket = hesk_ticketToPlain($ticket, 1, 0); $ticket = hesk_ticketToPlain($ticket, 1, 0);
mfh_insert_audit_trail_record($ticket['id'], 'TICKET', 'audit_automatically_closed', hesk_date());
$modsForHesk_settings = mfh_getSettings(); $modsForHesk_settings = mfh_getSettings();
hesk_notifyCustomer($modsForHesk_settings, 'ticket_closed'); hesk_notifyCustomer($modsForHesk_settings, 'ticket_closed');
} }
@ -446,7 +453,9 @@ function hesk_autoLogin($noredirect = 0)
} }
// Update ticket statuses and history in database // Update ticket statuses and history in database
hesk_dbQuery("UPDATE `" . $hesk_settings['db_pfix'] . "tickets` SET `status`='3', `closedat`=NOW(), `closedby`='-1', `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') WHERE `status` = '2' AND `lastchange` <= '" . hesk_dbEscape($dt) . "' "); $defaultCloseRs = hesk_dbQuery('SELECT `ID` FROM `' . hesk_dbEscape($hesk_settings['db_pfix']) . 'statuses` WHERE `IsAutocloseOption` = 1');
$defaultCloseStatus = hesk_dbFetchAssoc($defaultCloseRs);
hesk_dbQuery("UPDATE `" . $hesk_settings['db_pfix'] . "tickets` SET `status`= " . $defaultCloseStatus['ID'] . ", `closedat`=NOW(), `closedby`='-1' WHERE `status` = " . $closedStatus['ID'] . " AND `lastchange` <= '" . hesk_dbEscape($dt) . "' ");
} }
/* If session expired while a HESK page is open just continue using it, don't redirect */ /* If session expired while a HESK page is open just continue using it, don't redirect */

@ -2156,3 +2156,31 @@ function mfh_get_hidden_fields_for_language($keys) {
return $output; return $output;
} }
function mfh_insert_audit_trail_record($entity_id, $entity_type, $language_key, $date, $replacement_values = array()) {
global $hesk_settings;
hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "audit_trail` (`entity_id`, `entity_type`,
`language_key`, `date`) VALUES (" . intval($entity_id) . ", '" . hesk_dbEscape($entity_type) . "',
'" . hesk_dbEscape($language_key) . "', '" . hesk_dbEscape($date) . "')");
$audit_id = hesk_dbInsertID();
foreach ($replacement_values as $replacement_index => $replacement_value) {
hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "audit_trail_to_replacement_values`
(`audit_trail_id`, `replacement_index`, `replacement_value`) VALUES (" . intval($audit_id) . ",
" . intval($replacement_index) . ", '" . hesk_dbEscape($replacement_value) . "')");
}
return $audit_id;
}
function mfh_can_customer_change_status($status)
{
global $hesk_settings;
$res = hesk_dbQuery("SELECT `Closable` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "statuses` WHERE `ID` = " . intval($status));
$row = hesk_dbFetchAssoc($res);
return $row['Closable'] == 'yes' || $row['Closable'] == 'conly';
} // END hesk_get_ticket_status()

@ -269,7 +269,6 @@ function hesk_email2ticket($results, $pop3 = 0, $set_category = 1, $set_priority
// Auto assign tickets if aplicable // Auto assign tickets if aplicable
$tmpvar['owner'] = 0; $tmpvar['owner'] = 0;
$tmpvar['history'] = $pop3 ? sprintf($hesklang['thist16'], hesk_date()) : sprintf($hesklang['thist11'], hesk_date());
$tmpvar['openedby'] = $pop3 ? -2 : -1; $tmpvar['openedby'] = $pop3 ? -2 : -1;
$autoassign_owner = hesk_autoAssignTicket($tmpvar['category']); $autoassign_owner = hesk_autoAssignTicket($tmpvar['category']);
@ -278,7 +277,6 @@ function hesk_email2ticket($results, $pop3 = 0, $set_category = 1, $set_priority
if ($autoassign_owner) { if ($autoassign_owner) {
$tmpvar['owner'] = $autoassign_owner['id']; $tmpvar['owner'] = $autoassign_owner['id'];
$tmpvar['history'] .= sprintf($hesklang['thist10'], hesk_date(), $autoassign_owner['name'] . ' (' . $autoassign_owner['user'] . ')');
} }
// Custom fields will be empty as there is no reliable way of detecting them // Custom fields will be empty as there is no reliable way of detecting them
@ -297,6 +295,13 @@ function hesk_email2ticket($results, $pop3 = 0, $set_category = 1, $set_priority
// Insert ticket to database // Insert ticket to database
$ticket = hesk_newTicket($tmpvar); $ticket = hesk_newTicket($tmpvar);
mfh_insert_audit_trail_record($ticket['id'], 'TICKET', ($pop3 ? 'audit_submitted_via_pop' : 'audit_submitted_via_piping'), hesk_date());
if ($autoassign_owner) {
mfh_insert_audit_trail_record($ticket['id'], 'TICKET', 'audit_autoassigned', hesk_date(),
array(0 => $autoassign_owner['name'] . ' (' . $autoassign_owner['user'] . ')'));
}
// Notify the customer // Notify the customer
if ($hesk_settings['notify_new']) { if ($hesk_settings['notify_new']) {
$possible_SPAM = false; $possible_SPAM = false;

@ -115,7 +115,7 @@ function hesk_newTicket($ticket, $isVerified = true)
" . hesk_dbEscape($ticket['screen_resolution_height']) . ", " . hesk_dbEscape($ticket['screen_resolution_height']) . ",
" . hesk_dbEscape($ticket['screen_resolution_width']) . ", " . hesk_dbEscape($ticket['screen_resolution_width']) . ",
{$due_date}, {$due_date},
'" . hesk_dbEscape($ticket['history']) . "' ''
{$custom_what} {$custom_what}
) )
"); ");

@ -1137,9 +1137,9 @@ function execute320Scripts() {
hesk_dbConnect(); hesk_dbConnect();
executeQuery("ALTER TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "categories` executeQuery("ALTER TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "categories`
ADD COLUMN `mfh_description` VARCHAR(255)"); ADD COLUMN `mfh_description` TEXT");
executeQuery("ALTER TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "custom_fields` executeQuery("ALTER TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "custom_fields`
ADD COLUMN `mfh_description` VARCHAR(255)"); ADD COLUMN `mfh_description` TEXT");
// Purge the custom field caches as we're adding a new field // Purge the custom field caches as we're adding a new field
foreach ($hesk_settings['languages'] as $key => $value) { foreach ($hesk_settings['languages'] as $key => $value) {
@ -1147,5 +1147,17 @@ function execute320Scripts() {
hesk_unlink(HESK_PATH . "cache/cf_{$language_hash}.cache.php"); hesk_unlink(HESK_PATH . "cache/cf_{$language_hash}.cache.php");
} }
executeQuery("CREATE TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "audit_trail` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`entity_id` INT NOT NULL,
`entity_type` VARCHAR(50) NOT NULL,
`language_key` VARCHAR(100) NOT NULL,
`date` TIMESTAMP NOT NULL)");
executeQuery("CREATE TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "audit_trail_to_replacement_values` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`audit_trail_id` INT NOT NULL,
`replacement_index` INT NOT NULL,
`replacement_value` TEXT NOT NULL)");
updateVersion('3.2.0'); updateVersion('3.2.0');
} }

@ -194,12 +194,25 @@ function delete_event($id, $hesk_settings) {
} }
function update_ticket_due_date($ticket, $hesk_settings) { function update_ticket_due_date($ticket, $hesk_settings) {
$ticket_id_rs = hesk_dbQuery("SELECT `id` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` WHERE `trackid` = '" . hesk_dbEscape($ticket['trackid']) . "'");
$ticket_id = hesk_dbFetchAssoc($ticket_id_rs);
$due_date = 'NULL'; $due_date = 'NULL';
$language_key = 'audit_due_date_removed';
$audit_array = array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
if ($ticket['due_date'] != NULL) { if ($ticket['due_date'] != NULL) {
$audit_array = array(
0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')',
1 => date('Y-m-d H:i:s', strtotime($ticket['due_date']))
);
$due_date = "'" . date('Y-m-d H:i:s', strtotime($ticket['due_date'])) . "'"; $due_date = "'" . date('Y-m-d H:i:s', strtotime($ticket['due_date'])) . "'";
$language_key = 'audit_due_date_changed';
} }
$sql = "UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `due_date` = {$due_date}, `overdue_email_sent` = '0' $sql = "UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `due_date` = {$due_date}, `overdue_email_sent` = '0'
WHERE `trackid` = '" . hesk_dbEscape($ticket['trackid']) . "'"; WHERE `trackid` = '" . hesk_dbEscape($ticket['trackid']) . "'";
mfh_insert_audit_trail_record($ticket_id['id'], 'TICKET', $language_key, hesk_date(),
$audit_array);
hesk_dbQuery($sql); hesk_dbQuery($sql);
} }

@ -1146,6 +1146,7 @@ $hesklang['maxpost']='You probably tried to submit more data than this server ac
// --> Ticket history log // --> Ticket history log
// Unless otherwise specified, first %s will be replaced with date and second with name/username // Unless otherwise specified, first %s will be replaced with date and second with name/username
// THESE STRINGS ARE DEPRECATED AS OF MODS FOR HESK 3.2.0
$hesklang['thist1']='<li class="smaller">%s | moved to category %s by %s</li>'; // %s = date, new category, user making change $hesklang['thist1']='<li class="smaller">%s | moved to category %s by %s</li>'; // %s = date, new category, user making change
$hesklang['thist2']='<li class="smaller">%s | assigned to %s by %s</li>'; // %s = date, assigned user, user making change $hesklang['thist2']='<li class="smaller">%s | assigned to %s by %s</li>'; // %s = date, assigned user, user making change
$hesklang['thist3']='<li class="smaller">%s | closed by %s</li>'; $hesklang['thist3']='<li class="smaller">%s | closed by %s</li>';
@ -2188,5 +2189,31 @@ $hesklang['permission_group_colon'] = 'Permission Group:';
$hesklang['permission_group'] = 'Permission Group'; $hesklang['permission_group'] = 'Permission Group';
$hesklang['changing_permissions_will_reset_permission_group'] = 'Changing a user\'s categories / features will reset their permission group! Click "Discard Changes" to reset the user\'s categories / features.'; $hesklang['changing_permissions_will_reset_permission_group'] = 'Changing a user\'s categories / features will reset their permission group! Click "Discard Changes" to reset the user\'s categories / features.';
// --> Ticket audit trail
$hesklang['audit_moved_category']='%s moved ticket to category %s'; // %s = new category, user making change, thist1
$hesklang['audit_assigned']='%s assigned ticket to %s'; // %s = assigned user, user making change
$hesklang['audit_assigned_self'] = '%s assigned ticket to themself';
$hesklang['audit_unassigned'] = '%s unassigned ticket';
$hesklang['audit_closed']='%s closed ticket'; // thist3
$hesklang['audit_automatically_closed'] ='Ticket automatically closed';
$hesklang['audit_opened']='%s opened ticket'; // thist4
$hesklang['audit_locked']='%s locked ticket'; // thist5
$hesklang['audit_automatically_locked'] = 'Ticket automatically locked';
$hesklang['audit_unlocked']='%s unlocked ticket'; // thist6
$hesklang['audit_created']='%s created ticket';
$hesklang['audit_priority']='%s changed priority to %s'; // %s = date,new priority, user making change, thist8
$hesklang['audit_status']='%s changed status to %s'; // %s = date, new status, user making change, thist9
$hesklang['audit_autoassigned']='%s automatically assigned to ticket'; //thist10
$hesklang['audit_submitted_via_piping']='Ticket submitted via e-mail piping'; // thist11
$hesklang['audit_attachment_deleted']='%s deleted attachment %s'; // %s = date, deleted attachment, user making change
$hesklang['audit_merged']='%s merged ticket %s with this ticket'; // %s = date, merged ticket ID, user making change, thist13
$hesklang['audit_time_worked']='%s updated time worked to %s'; // %s = date, new time worked, user making change
$hesklang['audit_submitted_by']='%s submitted ticket';
$hesklang['audit_submitted_via_pop']='Ticket submitted via POP3 fetching'; // thist16
$hesklang['audit_due_date_removed'] = '%s removed due date';
$hesklang['audit_due_date_changed'] = '%s changed due date to %s';
$hesklang['audit_linked_ticket'] = '%s linked ticket %s to this ticket';
$hesklang['audit_unlinked_ticket'] = '%s unlinked ticket %s';
// DO NOT CHANGE BELOW // DO NOT CHANGE BELOW
if (!defined('IN_SCRIPT')) die('PHP syntax OK!'); if (!defined('IN_SCRIPT')) die('PHP syntax OK!');

@ -405,13 +405,11 @@ if ($hesk_settings['kb_enable'] && $hesk_settings['kb_recommendanswers'] && isse
// All good now, continue with ticket creation // All good now, continue with ticket creation
$tmpvar['owner'] = 0; $tmpvar['owner'] = 0;
$tmpvar['history'] = sprintf($hesklang['thist15'], hesk_date(), $tmpvar['name']);
// Auto assign tickets if aplicable // Auto assign tickets if aplicable
$autoassign_owner = hesk_autoAssignTicket($tmpvar['category']); $autoassign_owner = hesk_autoAssignTicket($tmpvar['category']);
if ($autoassign_owner) { if ($autoassign_owner) {
$tmpvar['owner'] = $autoassign_owner['id']; $tmpvar['owner'] = $autoassign_owner['id'];
$tmpvar['history'] .= sprintf($hesklang['thist10'], hesk_date(), $autoassign_owner['name'] . ' (' . $autoassign_owner['user'] . ')');
} }
// Insert attachments // Insert attachments
@ -463,6 +461,14 @@ if ($createTicket) {
//-- email has been verified, and a ticket can be created //-- email has been verified, and a ticket can be created
$ticket = hesk_newTicket($tmpvar); $ticket = hesk_newTicket($tmpvar);
mfh_insert_audit_trail_record($ticket['id'], 'TICKET', 'audit_submitted_by', hesk_date(),
array(0 => $tmpvar['name']));
if ($autoassign_owner) {
mfh_insert_audit_trail_record($ticket['id'], 'TICKET', 'audit_autoassigned', hesk_date(),
array(0 => $autoassign_owner['name'] . ' (' . $autoassign_owner['user'] . ')'));
}
// Notify the customer // Notify the customer
if ($hesk_settings['notify_new'] && $email_available) { if ($hesk_settings['notify_new'] && $email_available) {
hesk_notifyCustomer($modsForHesk_settings); hesk_notifyCustomer($modsForHesk_settings);

Loading…
Cancel
Save