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/email_templates.php
admin/generate_spam_question.php
admin/priority.php
admin/test_connection.php
attachments/index.htm
cache/

@ -189,15 +189,20 @@ if ($hesk_settings['attachments']['use'] && !empty($attachments)) {
// Add reply
$html = $modsForHesk_settings['rich_text_for_tickets'];
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 {
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 */
$revision = '';
/* 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'])) {
$priority = intval(hesk_POST('priority'));
if ($priority < 0 || $priority > 3) {
@ -211,9 +216,17 @@ if (!empty($_POST['set_priority'])) {
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 {
$priority_sql = "";
}
@ -238,8 +251,11 @@ if ($ticket['locked']) {
$newStatus = hesk_dbFetchAssoc($newStatusRs);
if ($newStatus['IsClosed'] && hesk_checkPermission('can_resolve', 0)) {
$revision = sprintf($hesklang['thist3'], hesk_date(), $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
$sql_status = " , `closedat`=NOW(), `closedby`=" . intval($_SESSION['id']) . ", `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') ";
$audit_closed = array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
$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
if ($hesk_settings['custopen'] != 1) {
@ -247,8 +263,8 @@ if ($ticket['locked']) {
}
} else {
// 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'] . ')');
$sql_status = " , `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') ";
$audit_status = array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')',
1 => mfh_getDisplayTextForStatusId($new_status));
}
}
} // -> Submit as Customer reply
@ -259,8 +275,8 @@ elseif ($submit_as_customer) {
$new_status = $customerReplyStatus['ID'];
if ($ticket['status'] != $new_status) {
$revision = sprintf($hesklang['thist9'], hesk_date(), $hesklang['wait_reply'], $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
$sql_status = " , `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') ";
$audit_customer_status = array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')',
1 => mfh_getDisplayTextForStatusId($new_status));
}
} // -> Default: submit as "Replied by staff"
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']))) {
$revision = sprintf($hesklang['thist2'], hesk_date(), $_SESSION['name'] . ' (' . $_SESSION['user'] . ')', $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
$sql .= " , `owner`=" . intval($_SESSION['id']) . ", `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') ";
$audit_assigned_self = array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
$sql .= " , `owner`=" . intval($_SESSION['id']) . " ";
}
$sql .= " $priority_sql ";
@ -306,6 +322,29 @@ unset($sql);
/* 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']) . "'");
//-- 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
// 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();
// Log who submitted ticket
$tmpvar['history'] = sprintf($hesklang['thist7'], hesk_date(), $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
$tmpvar['openedby'] = $_SESSION['id'];
// Owner
$tmpvar['owner'] = 0;
$autoassign_owner = null;
if (hesk_checkPermission('can_assign_others', 0)) {
$tmpvar['owner'] = intval(hesk_POST('owner'));
@ -192,7 +192,6 @@ if (hesk_checkPermission('can_assign_others', 0)) {
$autoassign_owner = hesk_autoAssignTicket($tmpvar['category']);
if ($autoassign_owner) {
$tmpvar['owner'] = intval($autoassign_owner['id']);
$tmpvar['history'] .= sprintf($hesklang['thist10'], hesk_date(), $autoassign_owner['name'] . ' (' . $autoassign_owner['user'] . ')');
} else {
$tmpvar['owner'] = 0;
}
@ -315,6 +314,14 @@ $tmpvar['screen_resolution_width'] = "NULL";
// Insert ticket to database
$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?
if ($notify && $email_available) {
hesk_notifyCustomer($modsForHesk_settings);

@ -97,6 +97,37 @@ if (!$ticket['owner'] && !$can_view_unassigned) {
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 */
if ($ticket['lastreplier']) {
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);
/* 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) . "', `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') WHERE `trackid`='" . hesk_dbEscape($trackingID) . "'");
hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `time_worked`='" . hesk_dbEscape($time_worked) . "' 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 */
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')) . '\'');
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');
}
/* Delete child action */
if (($can_reply || $can_edit) && isset($_GET['deleteChild'])) {
//-- 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']));
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');
} 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) . "'");
/* Update ticket or reply in the database */
$revision = sprintf($hesklang['thist12'], hesk_date(), $att['real_name'], $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
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']) . "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']) . ",',''), `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');
}
@ -1605,7 +1653,7 @@ function print_form()
} // End print_form()
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>
<div class="timeline-item">
@ -1766,7 +1814,44 @@ function mfh_print_message() {
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">';
if (!$hesk_settings['new_top']) {
@ -1775,78 +1860,189 @@ 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>';
}
while ($reply = hesk_dbFetchAssoc($result)) {
$reply['dt'] = hesk_date($reply['dt'], true);
?>
<li>
<?php if ($reply['staffid']): ?>
<i class="fa fa-reply bg-orange" data-toggle="tooltip" title="<?php echo $hesklang['reply_by_staff']; ?>"></i>
<?php else: ?>
<i class="fa fa-share bg-blue" data-toggle="tooltip" title="<?php echo $hesklang['reply_by_customer']; ?>"></i>
<?php endif; ?>
<div class="timeline-item">
<span class="time"><i class="fa fa-clock-o"></i> <?php echo $reply['dt']; ?></span>
<h3 class="timeline-header"><?php echo $reply['name']; ?></h3>
<div class="timeline-body">
<div class="row">
<div class="col-md-3 text-right">
<strong><?php echo $hesklang['message_colon']; ?></strong>
</div>
<div class="col-md-9">
<?php
if ($reply['html']) {
echo hesk_html_entity_decode($reply['message']);
} else {
echo $reply['message'];
} ?>
</div>
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);
?>
<li>
<?php if ($reply['staffid']): ?>
<i class="fa fa-reply bg-orange" data-toggle="tooltip" title="<?php echo $hesklang['reply_by_staff']; ?>"></i>
<?php else: ?>
<i class="fa fa-share bg-blue" data-toggle="tooltip" title="<?php echo $hesklang['reply_by_customer']; ?>"></i>
<?php endif; ?>
<div class="timeline-item">
<span class="time"><i class="fa fa-clock-o"></i> <?php echo $reply['dt']; ?></span>
<h3 class="timeline-header"><?php echo $reply['name']; ?></h3>
<div class="timeline-body">
<div class="row">
<div class="col-md-3 text-right">
<strong><?php echo $hesklang['message_colon']; ?></strong>
</div>
<div class="col-md-9">
<?php
if ($reply['html']) {
echo hesk_html_entity_decode($reply['message']);
} else {
echo $reply['message'];
} ?>
</div>
</div>
<?php
if ($hesk_settings['attachments']['use'] && strlen($reply['attachments'])):
</div>
<?php
if ($hesk_settings['attachments']['use'] && strlen($reply['attachments'])):
?>
<div class="timeline-footer">
<?php mfh_listAttachments($reply['attachments'], $reply['id'], true); ?>
</div>
<?php endif; ?>
<div class="timeline-footer">
<div class="row">
<div class="col-md-6">
<?php
/* Staff rating */
if ($hesk_settings['rating'] && $reply['staffid']) {
if ($reply['rating'] == 1) {
echo '<p class="rate">' . $hesklang['rnh'] . '</p>';
} elseif ($reply['rating'] == 5) {
echo '<p class="rate">' . $hesklang['rh'] . '</p>';
}
}
/* Show "unread reply" message? */
if ($reply['staffid'] && !$reply['read']) {
echo '<p class="rate">' . $hesklang['unread'] . '</p>';
<?php endif; ?>
<div class="timeline-footer">
<div class="row">
<div class="col-md-6">
<?php
/* Staff rating */
if ($hesk_settings['rating'] && $reply['staffid']) {
if ($reply['rating'] == 1) {
echo '<p class="rate">' . $hesklang['rnh'] . '</p>';
} elseif ($reply['rating'] == 5) {
echo '<p class="rate">' . $hesklang['rh'] . '</p>';
}
?>
</div>
<div class="col-md-6 text-right">
<?php echo hesk_getAdminButtonsInTicket(); ?>
</div>
}
/* Show "unread reply" message? */
if ($reply['staffid'] && !$reply['read']) {
echo '<p class="rate">' . $hesklang['unread'] . '</p>';
}
?>
</div>
<div class="col-md-6 text-right">
<?php echo hesk_getAdminButtonsInTicket(); ?>
</div>
</div>
</div>
</li>
<?php
}
</div>
</li>
<?php
}
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>';
function mfh_print_audit_record($record) {
global $hesklang;
return;
$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 {
$font_icon = 'fa-long-arrow-down';
}
} // End hesk_printTicketReplies()
// 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';
//-- 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()

@ -52,8 +52,9 @@ $owner = intval(hesk_REQUEST('owner'));
/* If ID is -1 the ticket will be unassigned */
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 , `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') WHERE `trackid`='" . hesk_dbEscape($trackingID) . "'");
$res = hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `owner`=0 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');
} elseif ($owner < 1) {
@ -96,8 +97,17 @@ if ($ticket['owner'] && $ticket['owner'] != $owner && hesk_REQUEST('unassigned')
/* Assigning to 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} , `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') WHERE `trackid`='" . hesk_dbEscape($trackingID) . "'");
$res = hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `owner`={$owner} 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)) {
$_SERVER['PHP_SELF'] = 'admin_main.php';

@ -37,6 +37,10 @@ hesk_token_check();
/* Ticket ID */
$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 */
$statusSql = "SELECT `ID` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "statuses`";
$status_options = array();
@ -54,6 +58,11 @@ if (!isset($status_options[$status])) {
$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));
if ($statusRow['IsClosed']) // Closed
{
@ -62,10 +71,14 @@ if ($statusRow['IsClosed']) // Closed
}
$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) {
$locked = 1;
$audit_locked = array();
}
// Notify customer of closed ticket?
@ -91,21 +104,43 @@ if ($statusRow['IsClosed']) // Closed
} elseif ($statusRow['IsNewTicketStatus'] == 0) //Ticket is still open, but not new
{
$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
$closedby_sql = ' , `closedat`=NULL, `closedby`=NULL ';
} else // Ticket is marked as "NEW"
{
$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
$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) {
hesk_error("$hesklang[int_error]: $hesklang[trackID_not_found].");

@ -81,10 +81,10 @@ $i = 0;
// Possible priorities
$priorities = array(
'critical' => array('value' => 0, 'text' => $hesklang['critical'], 'formatted' => '<font class="critical">' . $hesklang['critical'] . '</font>'),
'high' => array('value' => 1, 'text' => $hesklang['high'], 'formatted' => '<font class="important">' . $hesklang['high'] . '</font>'),
'medium' => array('value' => 2, 'text' => $hesklang['medium'], 'formatted' => '<font class="medium">' . $hesklang['medium'] . '</font>'),
'low' => array('value' => 3, 'text' => $hesklang['low'], 'formatted' => $hesklang['low']),
'critical' => array('value' => 0, 'lang' => 'critical', 'text' => $hesklang['critical'], 'formatted' => '<font class="critical">' . $hesklang['critical'] . '</font>'),
'high' => array('value' => 1, 'lang' => 'high', 'text' => $hesklang['high'], 'formatted' => '<font class="important">' . $hesklang['high'] . '</font>'),
'medium' => array('value' => 2, 'lang' => 'medium', 'text' => $hesklang['medium'], 'formatted' => '<font class="medium">' . $hesklang['medium'] . '</font>'),
'low' => array('value' => 3, 'lang' => 'low', 'text' => $hesklang['low'], 'formatted' => $hesklang['low']),
);
// Change priority
@ -113,8 +113,10 @@ if (array_key_exists($_POST['a'], $priorities)) {
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']}', `history`=CONCAT(`history`,'" . hesk_dbEscape($revision) . "') WHERE `id`={$this_id}");
hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `priority`='{$priority['value']}' 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++;
}
@ -133,8 +135,6 @@ elseif ($_POST['a'] == 'delete') {
require(HESK_PATH . 'inc/email_functions.inc.php');
}
$revision = sprintf($hesklang['thist3'], hesk_date(), $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
foreach ($_POST['id'] as $this_id) {
if (is_array($this_id)) {
continue;
@ -222,8 +222,6 @@ else {
hesk_token_check('POST');
require(HESK_PATH . 'inc/email_functions.inc.php');
$revision = sprintf($hesklang['thist3'], hesk_date(), $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
foreach ($_POST['id'] as $this_id) {
if (is_array($this_id)) {
continue;
@ -239,7 +237,11 @@ else {
$closedStatusRS = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "statuses` WHERE `IsStaffClosedOption` = 1");
$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++;
// Notify customer of closed ticket?
@ -284,6 +286,14 @@ function hesk_fullyDeleteTicket()
/* Delete ticket notes */
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 */
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 */
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);
@ -210,22 +209,25 @@ function do_login()
$closedStatus = hesk_dbFetchAssoc($closedStatusRs);
// Are we allowed to close tickets in this status?
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) . "' ");
if (hesk_dbNumRows($result) > 0) {
global $ticket;
// Load required functions?
if (!function_exists('hesk_notifyCustomer')) {
require(HESK_PATH . 'inc/email_functions.inc.php');
}
while ($ticket = hesk_dbFetchAssoc($result)) {
$ticket['dt'] = hesk_date($ticket['dt'], true);
$ticket['lastchange'] = hesk_date($ticket['lastchange'], true);
$ticket = hesk_ticketToPlain($ticket, 1, 0);
$result = hesk_dbQuery("SELECT * FROM `" . $hesk_settings['db_pfix'] . "tickets` WHERE `status` = " . $closedStatus['ID'] . " AND `lastchange` <= '" . hesk_dbEscape($dt) . "' ");
if (hesk_dbNumRows($result) > 0) {
global $ticket;
// Load required functions?
if (!function_exists('hesk_notifyCustomer')) {
require(HESK_PATH . 'inc/email_functions.inc.php');
}
while ($ticket = hesk_dbFetchAssoc($result)) {
$ticket['dt'] = hesk_date($ticket['dt'], true);
$ticket['lastchange'] = hesk_date($ticket['lastchange'], true);
$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');
}
}
@ -234,7 +236,7 @@ function do_login()
// 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');
$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 */
$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 */
if (empty($_GET['locked'])) {
$status = 0;
$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 ';
} else {
$status = 1;
$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']) . ' ';
// Notify customer of closed ticket?
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');
$ticketIsOpen = true;
while ($row = hesk_dbFetchAssoc($closedStatusRS)) {
@ -82,7 +86,17 @@ $statusRs = hesk_dbQuery($statusSql);
$statusRow = hesk_dbFetchAssoc($statusRs);
$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 */
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);
/* 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 */
$need_to_reassign = 0;
@ -92,18 +90,30 @@ if ($ticket['owner']) {
}
/* Reassign automatically if possible */
$autoassign_owner = null;
if ($need_to_reassign || !$ticket['owner']) {
$need_to_reassign = 1;
$autoassign_owner = hesk_autoAssignTicket($category);
if ($autoassign_owner) {
$ticket['owner'] = $autoassign_owner['id'];
$history .= sprintf($hesklang['thist10'], hesk_date(), $autoassign_owner['name'] . ' (' . $autoassign_owner['user'] . ')');
} else {
$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;

@ -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('%%OWNER%%', $ownerName, $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('%%UPDATED%%', $ticket->lastChanged, $msg);
$msg = str_replace('%%ID%%', $ticket->id, $msg);

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

@ -2,11 +2,13 @@
namespace BusinessLogic\Tickets;
use BusinessLogic\DateTimeHelpers;
use BusinessLogic\Emails\Addressees;
use BusinessLogic\Emails\EmailSenderHelper;
use BusinessLogic\Emails\EmailTemplateRetriever;
use BusinessLogic\Exceptions\ValidationException;
use BusinessLogic\Statuses\DefaultStatusForAction;
use DataAccess\AuditTrail\AuditTrailGateway;
use DataAccess\Security\UserGateway;
use DataAccess\Settings\ModsForHeskSettingsGateway;
use DataAccess\Statuses\StatusGateway;
@ -56,6 +58,9 @@ class TicketCreator extends \BaseClass {
/* @var $modsForHeskSettingsGateway ModsForHeskSettingsGateway */
private $modsForHeskSettingsGateway;
/* @var $auditTrailGateway AuditTrailGateway */
private $auditTrailGateway;
function __construct(NewTicketValidator $newTicketValidator,
TrackingIdGenerator $trackingIdGenerator,
Autoassigner $autoassigner,
@ -64,7 +69,8 @@ class TicketCreator extends \BaseClass {
VerifiedEmailChecker $verifiedEmailChecker,
EmailSenderHelper $emailSenderHelper,
UserGateway $userGateway,
ModsForHeskSettingsGateway $modsForHeskSettingsGateway) {
ModsForHeskSettingsGateway $modsForHeskSettingsGateway,
AuditTrailGateway $auditTrailGateway) {
$this->newTicketValidator = $newTicketValidator;
$this->trackingIdGenerator = $trackingIdGenerator;
$this->autoassigner = $autoassigner;
@ -74,6 +80,7 @@ class TicketCreator extends \BaseClass {
$this->emailSenderHelper = $emailSenderHelper;
$this->userGateway = $userGateway;
$this->modsForHeskSettingsGateway = $modsForHeskSettingsGateway;
$this->auditTrailGateway = $auditTrailGateway;
}
/**
@ -113,7 +120,7 @@ class TicketCreator extends \BaseClass {
// Transform one-to-one properties
$ticket->name = $ticketRequest->name;
$ticket->email = $ticketRequest->email;
$ticket->email = $this->getAddressees($ticketRequest->email);
$ticket->priorityId = $ticketRequest->priority;
$ticket->categoryId = $ticketRequest->category;
$ticket->subject = $ticketRequest->subject;
@ -147,8 +154,13 @@ class TicketCreator extends \BaseClass {
$ticket->timeWorked = '00:00:00';
$ticket->lastReplier = 0;
$this->auditTrailGateway->insertAuditTrailRecord($ticket->id, AuditTrailEntityType::TICKET,
'audit_created', DateTimeHelpers::heskDate($heskSettings), array(
0 => $ticket->name
), $heskSettings);
$addressees = new Addressees();
$addressees->to = $this->getAddressees($ticket->email);
$addressees->to = $ticket->email;
if ($ticketRequest->sendEmailToCustomer && $emailVerified) {
$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\Tickets\Attachment;
use BusinessLogic\Tickets\AuditTrail;
use BusinessLogic\Tickets\AuditTrailEntityType;
use BusinessLogic\Tickets\Ticket;
use BusinessLogic\Tickets\TicketGatewayGeneratedFields;
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");
$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();
@ -92,7 +129,7 @@ class TicketGateway extends CommonDao {
function getTicketByTrackingId($trackingId, $heskSettings) {
$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) {
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));
$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();
@ -170,6 +240,8 @@ class TicketGateway extends CommonDao {
$ipAddress = $ticket->ipAddress !== null
&& $ticket->ipAddress !== '' ? $ticket->ipAddress : '';
$emailAddresses = implode(';', $ticket->email);
$tableName = $isEmailVerified ? 'tickets' : 'stage_tickets';
$sql = "INSERT INTO `" . hesk_dbEscape($heskSettings['db_pfix']) . $tableName ."`
@ -205,7 +277,7 @@ class TicketGateway extends CommonDao {
(
'" . hesk_dbEscape($ticket->trackingId) . "',
'" . hesk_dbEscape($ticket->name) . "',
'" . hesk_dbEscape($ticket->email) . "',
'" . hesk_dbEscape($emailAddresses) . "',
'" . intval($ticket->categoryId) . "',
'" . intval($ticket->priorityId) . "',
'" . hesk_dbEscape($ticket->subject) . "',

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

28
api/composer.lock generated

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

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

@ -52,7 +52,7 @@ if ($status == 3) // Closed
}
$status = $closedStatus;
$action = $hesklang['closed'];
$revision = sprintf($hesklang['thist3'], hesk_date(), $hesklang['customer']);
$revision_key = 'audit_closed';
if ($hesk_settings['custopen'] != 1) {
$locked = 1;
@ -73,7 +73,7 @@ if ($status == 3) // Closed
$status = $statusRow['ID'];
$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
$_SESSION['force_form_top'] = true;
@ -94,12 +94,9 @@ hesk_verifyEmailMatch($trackingID);
$_SESSION['t_track'] = $trackingID;
$_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?
$ticket = hesk_dbFetchAssoc( hesk_dbQuery( "SELECT `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'])) {
$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 (!mfh_can_customer_change_status($ticket['status'])) {
hesk_process_messages($hesklang['scno'],'ticket.php');
}
@ -121,7 +118,10 @@ if ($oldStatus == 2) {
// 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*
if (hesk_dbAffectedRows() != 1) {

@ -350,4 +350,9 @@ div.ticket-info {
border-color: #3c8dbc;
box-shadow: none;
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']) . "'");
/* 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 */
$merged .= '#' . $row['trackid'];
@ -234,7 +237,7 @@ function hesk_mergeTickets($merge_these, $merge_into)
}
/* 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;
@ -419,14 +422,17 @@ function hesk_autoLogin($noredirect = 0)
hesk_setcookie('hesk_p', "$hash", strtotime('+1 year'));
/* Close any old tickets here so Cron jobs aren't necessary */
if ($hesk_settings['autoclose']) {
$revision = sprintf($hesklang['thist3'], hesk_date(), $hesklang['auto']);
$closedStatusRs = hesk_dbQuery('SELECT `ID`, `Closable` FROM `' . hesk_dbEscape($hesk_settings['db_pfix']) . 'statuses` WHERE `IsDefaultStaffReplyStatus` = 1');
$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);
// 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` = '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) {
global $ticket;
@ -439,6 +445,7 @@ function hesk_autoLogin($noredirect = 0)
$ticket['dt'] = hesk_date($ticket['dt'], true);
$ticket['lastchange'] = hesk_date($ticket['lastchange'], true);
$ticket = hesk_ticketToPlain($ticket, 1, 0);
mfh_insert_audit_trail_record($ticket['id'], 'TICKET', 'audit_automatically_closed', hesk_date());
$modsForHesk_settings = mfh_getSettings();
hesk_notifyCustomer($modsForHesk_settings, 'ticket_closed');
}
@ -446,7 +453,9 @@ function hesk_autoLogin($noredirect = 0)
}
// 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 */

@ -2155,4 +2155,32 @@ function mfh_get_hidden_fields_for_language($keys) {
$output .= '</div>';
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
$tmpvar['owner'] = 0;
$tmpvar['history'] = $pop3 ? sprintf($hesklang['thist16'], hesk_date()) : sprintf($hesklang['thist11'], hesk_date());
$tmpvar['openedby'] = $pop3 ? -2 : -1;
$autoassign_owner = hesk_autoAssignTicket($tmpvar['category']);
@ -278,7 +277,6 @@ function hesk_email2ticket($results, $pop3 = 0, $set_category = 1, $set_priority
if ($autoassign_owner) {
$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
@ -297,6 +295,13 @@ function hesk_email2ticket($results, $pop3 = 0, $set_category = 1, $set_priority
// Insert ticket to database
$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
if ($hesk_settings['notify_new']) {
$possible_SPAM = false;

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

@ -1137,9 +1137,9 @@ function execute320Scripts() {
hesk_dbConnect();
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`
ADD COLUMN `mfh_description` VARCHAR(255)");
ADD COLUMN `mfh_description` TEXT");
// Purge the custom field caches as we're adding a new field
foreach ($hesk_settings['languages'] as $key => $value) {
@ -1147,5 +1147,17 @@ function execute320Scripts() {
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');
}

@ -194,12 +194,25 @@ function delete_event($id, $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';
$language_key = 'audit_due_date_removed';
$audit_array = array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
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'])) . "'";
$language_key = 'audit_due_date_changed';
}
$sql = "UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `due_date` = {$due_date}, `overdue_email_sent` = '0'
WHERE `trackid` = '" . hesk_dbEscape($ticket['trackid']) . "'";
mfh_insert_audit_trail_record($ticket_id['id'], 'TICKET', $language_key, hesk_date(),
$audit_array);
hesk_dbQuery($sql);
}

@ -1146,6 +1146,7 @@ $hesklang['maxpost']='You probably tried to submit more data than this server ac
// --> Ticket history log
// 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['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>';
@ -2188,5 +2189,31 @@ $hesklang['permission_group_colon'] = '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.';
// --> 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
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
$tmpvar['owner'] = 0;
$tmpvar['history'] = sprintf($hesklang['thist15'], hesk_date(), $tmpvar['name']);
// Auto assign tickets if aplicable
$autoassign_owner = hesk_autoAssignTicket($tmpvar['category']);
if ($autoassign_owner) {
$tmpvar['owner'] = $autoassign_owner['id'];
$tmpvar['history'] .= sprintf($hesklang['thist10'], hesk_date(), $autoassign_owner['name'] . ' (' . $autoassign_owner['user'] . ')');
}
// Insert attachments
@ -463,6 +461,14 @@ if ($createTicket) {
//-- email has been verified, and a ticket can be created
$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
if ($hesk_settings['notify_new'] && $email_available) {
hesk_notifyCustomer($modsForHesk_settings);

Loading…
Cancel
Save