diff --git a/.gitignore b/.gitignore index ad0887cb..0b5c03f5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ admin/archive.php admin/custom_statuses.php admin/email_templates.php admin/generate_spam_question.php +admin/resend_notification.php admin/test_connection.php attachments/index.htm cache/ @@ -19,6 +20,7 @@ header.txt hesk_settings.inc.php img/add_article.png img/add_category.png +img/anonymize.png img/article_text.png img/autoassign_off.png img/autoassign_on.png @@ -34,8 +36,10 @@ img/delete.png img/delete_off.png img/delete_ticket.png img/edit.png +img/email.png img/error.png img/existingticket.png +img/export.png img/flag_critical.png img/flag_high.png img/flag_low.png @@ -81,6 +85,7 @@ img/lock.png img/login.png img/mail.png img/manage.png +img/menu.png img/minus.gif img/minusbottom.gif img/minustop.gif @@ -260,6 +265,7 @@ language/en/emails/new_ticket.txt language/en/emails/new_ticket_staff.txt language/en/emails/ticket_assigned_to_you.txt language/en/index.htm +language/en/text.php language/index.htm language/* !language/en diff --git a/admin/admin_main.php b/admin/admin_main.php index 59898d18..df0b082a 100644 --- a/admin/admin_main.php +++ b/admin/admin_main.php @@ -108,23 +108,17 @@ else { >23)."\66\x34".chr(796917760>>23)."\x65\156\143".chr(0157)."\x64\145";$hesk_settings['hesk_license']($x1a("\x3c\150r\x20\57\76".chr(503316480>>23)."\x74\141\142l\x65\40".chr(0142).chr(0157).chr(0162)."\144\145r\x3d\42\60".chr(285212672>>23)."\x20\x77\x69".chr(0144)."th".chr(511705088>>23)."\x22".chr(061)."\60\60\x25\42".chr(520093696>>23)."\x3c\164".chr(0162).">\74t\x64\x3e\x3c".chr(0142).chr(076).$hesklang[chr(956301312>>23)."\145\155\157\x76e".chr(796917760>>23)."\x73ta\164e\x6d".chr(847249408>>23)."\156\x74"].chr(503316480>>23)."\x2f\142\x3e".chr(074)."\57t\x64\76".chr(074)."td".chr(268435456>>23)."\x73ty\154\x65\x3d\x22te".chr(1006632960>>23)."t\x2d\141\x6c\x69".chr(0147).chr(922746880>>23)."\x3ar\151\x67ht\"\76".chr(503316480>>23)."\141 \x68\162\145\146\x3d\42".chr(0112).chr(813694976>>23)."v\141".chr(0163).chr(830472192>>23)."\162\x69".chr(0160).chr(0164)."\x3a".chr(989855744>>23)."\157\151d\50\x30".chr(343932928>>23).chr(042)."\40onc\154\151\143\153\x3d".chr(042)."\x61\x6c\145\x72t(\x27".$hesklang["\163".chr(981467136>>23)."\x70".chr(939524096>>23).chr(0157)."\162\164\137n".chr(931135488>>23)."\x74\151".chr(0143)."\x65"].chr(047)."\51\42\x3e".$hesklang["\x73\x68"]."\74".chr(394264576>>23)."\x61\x3e\74\57\164d\76\x3c/\x74\162\76".chr(503316480>>23).chr(057)."t\x61\x62\x6ce\x3e\x3c\x70\x3e".$hesklang[chr(0163)."\x75ppo\x72\x74\137".chr(956301312>>23).chr(847249408>>23)."\155".chr(931135488>>23)."v\x65"]."\x2e\x20\x3c".chr(813694976>>23)."\40\x68re\x66\x3d".chr(285212672>>23)."\150".chr(973078528>>23).chr(973078528>>23)."\160\x73".chr(486539264>>23)."\57\x2f".chr(998244352>>23)."\x77\167".chr(056)."\150".chr(847249408>>23)."s\153\56\x63\157".chr(0155)."/".chr(0142)."\165\171.".chr(0160)."h\x70".chr(285212672>>23)."\x20\x74\141".chr(0162)."g".chr(847249408>>23)."\164\x3d".chr(042)."\137b\x6c".chr(813694976>>23)."\x6ek\x22\76".$hesklang["\x63\154\151\143\153\x5f".chr(880803840>>23)."\x6e".chr(855638016>>23).chr(0157)]."\x3c/\141\x3e\x3c\x2fp".chr(076)."<\150\162\x20\x2f\x3e"),""); + /******************************************************************************* + END LICENSE CODE + *******************************************************************************/ /* Clean unneeded session variables */ hesk_cleanSessionVars('hide'); diff --git a/admin/admin_settings.php b/admin/admin_settings.php index 8fd9fd77..279833f5 100644 --- a/admin/admin_settings.php +++ b/admin/admin_settings.php @@ -30,6 +30,7 @@ $hesk_settings['language_default'] = $hesk_settings['language']; require(HESK_PATH . 'inc/common.inc.php'); $hesk_settings['language'] = $hesk_settings['language_default']; require(HESK_PATH . 'inc/admin_functions.inc.php'); +require(HESK_PATH . 'inc/setup_functions.inc.php'); require(HESK_PATH . 'inc/mail_functions.inc.php'); hesk_load_database_functions(); @@ -54,8 +55,6 @@ $help_folder = '../language/' . $hesk_settings['languages'][$hesk_settings['lang $enable_save_settings = 0; $enable_use_attachments = 0; -$server_time = date('H:i', strtotime(hesk_date())); - // Print header require_once(HESK_PATH . 'inc/headerAdmin.inc.php'); @@ -82,64 +81,6 @@ if (defined('HESK_DEMO')) { $hesk_settings['imap_password'] = $hesklang['hdemo']; } -// Check file attachment limits -if ($hesk_settings['attachments']['use'] && !defined('HESK_DEMO')) { - // Check number of attachments per post - if (version_compare(phpversion(), '5.2.12', '>=') && @ini_get('max_file_uploads') && @ini_get('max_file_uploads') < $hesk_settings['attachments']['max_number']) { - hesk_show_notice($hesklang['fatte1']); - } - - // Check max attachment size - $tmp = @ini_get('upload_max_filesize'); - if ($tmp) { - $last = strtoupper(substr($tmp, -1)); - $number = substr($tmp, 0, -1); - - switch ($last) { - case 'K': - $tmp = $number * 1024; - break; - case 'M': - $tmp = $number * 1048576; - break; - case 'G': - $tmp = $number * 1073741824; - break; - default: - $tmp = $number; - } - - if ($tmp < $hesk_settings['attachments']['max_size']) { - hesk_show_notice($hesklang['fatte2']); - } - } - - // Check max post size - $tmp = @ini_get('post_max_size'); - if ($tmp) { - $last = strtoupper(substr($tmp, -1)); - $number = substr($tmp, 0, -1); - - switch ($last) { - case 'K': - $tmp = $number * 1024; - break; - case 'M': - $tmp = $number * 1048576; - break; - case 'G': - $tmp = $number * 1073741824; - break; - default: - $tmp = $number; - } - - if ($tmp < ($hesk_settings['attachments']['max_size'] * $hesk_settings['attachments']['max_number'] + 524288)) { - hesk_show_notice($hesklang['fatte3']); - } - } -} - $hesklang['err_custname'] = addslashes($hesklang['err_custname']); @@ -250,32 +191,6 @@ $modsForHesk_settings = mfh_getSettings(); } }; - var server_time = ""; - var today = new Date(); - today.setHours(server_time.substr(0, server_time.indexOf(":"))); - today.setMinutes(server_time.substr(server_time.indexOf(":") + 1)); - - function startTime() { - var h = today.getHours(); - var m = today.getMinutes(); - var s = today.getSeconds(); - - h = checkTime(h); - m = checkTime(m); - - document.getElementById('servertime').innerHTML = h + ":" + m; - s = s + 1; - today.setSeconds(s); - t = setTimeout(function() { startTime(); },1000); - } - - function checkTime(i) { - if (i < 10) { - i = "0" + i; - } - return i; - } - function checkRequiredEmail(field) { if (document.getElementById('s_require_email_0').checked && document.getElementById('s_email_view_ticket').checked) { if (field == 's_require_email_0' && confirm('')) { @@ -339,6 +254,87 @@ $modsForHesk_settings = mfh_getSettings(); =') && @ini_get('max_file_uploads') && @ini_get('max_file_uploads') < $hesk_settings['attachments']['max_number']) { + hesk_show_notice($hesklang['fatte1']); + } + + // Check max attachment size + $tmp = @ini_get('upload_max_filesize'); + if ($tmp) { + $last = strtoupper(substr($tmp, -1)); + $number = substr($tmp, 0, -1); + + switch ($last) { + case 'K': + $tmp = $number * 1024; + break; + case 'M': + $tmp = $number * 1048576; + break; + case 'G': + $tmp = $number * 1073741824; + break; + default: + $tmp = $number; + } + + if ($tmp < $hesk_settings['attachments']['max_size']) { + hesk_show_notice($hesklang['fatte2']); + } + } + + // Check max post size + $tmp = @ini_get('post_max_size'); + if ($tmp) { + $last = strtoupper(substr($tmp, -1)); + $number = substr($tmp, 0, -1); + + switch ($last) { + case 'K': + $tmp = $number * 1024; + break; + case 'M': + $tmp = $number * 1048576; + break; + case 'G': + $tmp = $number * 1073741824; + break; + default: + $tmp = $number; + } + + if ($tmp < ($hesk_settings['attachments']['max_size'] * $hesk_settings['attachments']['max_number'] + 524288)) { + hesk_show_notice($hesklang['fatte3']); + } + } + + // If SMTP server is used, "From email" should match SMTP username + if ($hesk_settings['smtp'] && strtolower($hesk_settings['smtp_user']) != strtolower($hesk_settings['noreply_mail']) && hesk_validateEmail($hesk_settings['smtp_user'], 'ERR', 0)) { + hesk_show_notice(sprintf($hesklang['from_warning'], $hesklang['email_noreply'], $hesklang['tab_1'], $hesk_settings['smtp_user'])); + } + + // If POP3 fetching is active, no user should have the same email address + if ($hesk_settings['pop3'] && hesk_validateEmail($hesk_settings['pop3_user'], 'ERR', 0)) { + $res = hesk_dbQuery("SELECT `name` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users` WHERE `email` LIKE '".hesk_dbEscape($hesk_settings['pop3_user'])."'"); + + if (hesk_dbNumRows($res) > 0) { + hesk_show_notice(sprintf($hesklang['pop3_warning'], hesk_dbResult($res,0,0), $hesk_settings['pop3_user']) . "

" . $hesklang['fetch_warning'], $hesklang['warn']); + } + } + + // If IMAP fetching is active, no user should have the same email address + if ($hesk_settings['imap'] && hesk_validateEmail($hesk_settings['imap_user'], 'ERR', 0)) { + $res = hesk_dbQuery("SELECT `name` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users` WHERE `email` LIKE '".hesk_dbEscape($hesk_settings['imap_user'])."'"); + + if (hesk_dbNumRows($res) > 0) { + hesk_show_notice(sprintf($hesklang['imap_warning'], hesk_dbResult($res,0,0), $hesk_settings['imap_user']) . "

" . $hesklang['fetch_warning'], $hesklang['warn']); + } + } + } ?>
@@ -1495,14 +1491,14 @@ $modsForHesk_settings = mfh_getSettings();

@@ -2862,10 +2858,17 @@ $modsForHesk_settings = mfh_getSettings(); $onload_status = ' disabled '; } - echo ' + // Is IMAP extension loaded? + if ( ! function_exists('imap_open')) { + echo ''. $hesklang['disabled'] . ' - ' . $hesklang['imap_not']; + $onload_div = 'none'; + } else { + echo '
   
'; + } ?> + @@ -3446,53 +3449,28 @@ $modsForHesk_settings = mfh_getSettings();

- +
-

' . $server_time . ''; ?>

- -
-
-
-
-
- -
-
-

-
-
-
-
- -
-
-

-
-
-
-
- - -
    -
'; + // Get list of supported timezones + $timezone_list = hesk_generate_timezone_list(); + + // Do we need to localize month names? + if ($hesk_settings['language'] != 'English') { + $timezone_list = hesk_translate_timezone_list($timezone_list); + } ?> +
diff --git a/admin/admin_settings_save.php b/admin/admin_settings_save.php index cd6c96cf..2a63cee4 100644 --- a/admin/admin_settings_save.php +++ b/admin/admin_settings_save.php @@ -419,9 +419,10 @@ $set['updatedformat'] = hesk_checkMinMax(intval(hesk_POST('s_updatedformat')), 0 /*** MISC ***/ /* --> Date & Time */ -$set['diff_hours'] = floatval(hesk_POST('s_diff_hours', 0)); -$set['diff_minutes'] = floatval(hesk_POST('s_diff_minutes', 0)); -$set['daylight'] = empty($_POST['s_daylight']) ? 0 : 1; +$set['timezone'] = hesk_input(hesk_POST('s_timezone')); +if (!in_array($set['timezone'], timezone_identifiers_list())) { + $set['timezone'] = 'UTC'; +} $set['timeformat'] = hesk_input(hesk_POST('s_timeformat')) or $set['timeformat'] = 'Y-m-d H:i:s'; /* --> Other */ @@ -881,9 +882,7 @@ $hesk_settings[\'updatedformat\']=' . $set['updatedformat'] . '; // ==> MISC // --> Date & Time -$hesk_settings[\'diff_hours\']=' . $set['diff_hours'] . '; -$hesk_settings[\'diff_minutes\']=' . $set['diff_minutes'] . '; -$hesk_settings[\'daylight\']=' . $set['daylight'] . '; +$hesk_settings[\'timezone\']=\'' . $set['timezone'] . '\'; $hesk_settings[\'timeformat\']=\'' . $set['timeformat'] . '\'; // --> Other diff --git a/admin/admin_submit_ticket.php b/admin/admin_submit_ticket.php index 80d1269e..eca0d604 100644 --- a/admin/admin_submit_ticket.php +++ b/admin/admin_submit_ticket.php @@ -300,6 +300,11 @@ if (!$modsForHesk_settings['rich_text_for_tickets']) { $tmpvar['message'] = nl2br($tmpvar['message']); } +// Track who assigned the ticket +if ($tmpvar['owner'] > 0) { + $tmpvar['assignedby'] = !empty($autoassign_owner) ? -1 : $_SESSION['id']; +} + $tmpvar['latitude'] = hesk_POST('latitude', 'E-4'); $tmpvar['longitude'] = hesk_POST('longitude', 'E-4'); diff --git a/admin/admin_ticket.php b/admin/admin_ticket.php index b180ab34..51d6caca 100644 --- a/admin/admin_ticket.php +++ b/admin/admin_ticket.php @@ -50,6 +50,9 @@ $can_unban_emails = hesk_checkPermission('can_unban_emails', 0); $can_ban_ips = hesk_checkPermission('can_ban_ips', 0); $can_unban_ips = hesk_checkPermission('can_unban_ips', 0); $can_resolve = hesk_checkPermission('can_resolve', 0); +$can_view_ass_by = hesk_checkPermission('can_view_ass_by', 0); +$can_privacy = hesk_checkPermission('can_privacy',0); +$can_export = hesk_checkPermission('can_export',0); // Get ticket ID $trackingID = hesk_cleanID() or print_form(); @@ -90,7 +93,10 @@ if (hesk_dbNumRows($res) != 1) { /* Permission to view this ticket? */ if ($ticket['owner'] && $ticket['owner'] != $_SESSION['id'] && !hesk_checkPermission('can_view_ass_others', 0)) { - hesk_error($hesklang['ycvtao']); + // Maybe this user is allowed to view tickets he/she assigned? + if (!$can_view_ass_by || $ticket['assignedby'] != $_SESSION['id']) { + hesk_error($hesklang['ycvtao']); + } } if (!$ticket['owner'] && !$can_view_unassigned) { @@ -163,7 +169,10 @@ if ($isManager) { $can_unban_emails = $can_ban_ips = $can_unban_ips = - $can_resolve = true; + $can_resolve = + $can_view_ass_by = + $can_privacy = + $can_export = true; } /* Is this user allowed to view tickets inside this category? */ @@ -1321,7 +1330,7 @@ require_once(HESK_PATH . 'inc/footer.inc.php'); function hesk_getAdminButtons($category_id) { - global $hesk_settings, $hesklang, $modsForHesk_settings, $ticket, $reply, $trackingID, $can_edit, $can_archive, $can_delete, $can_resolve, $isManager; + global $hesk_settings, $hesklang, $modsForHesk_settings, $ticket, $reply, $trackingID, $can_edit, $can_archive, $can_delete, $can_resolve, $can_privacy, $can_export, $isManager; $options = ''; @@ -1329,12 +1338,12 @@ function hesk_getAdminButtons($category_id) if ($can_edit) { $tmp = $reply ? '&reply=' . $reply['id'] : ''; $mgr = $isManager ? '&isManager=true' : ''; - $options .= ' ' . $hesklang['edit'] . ' '; + $options .= ' ' . $hesklang['btn_edit'] . ' '; } /* Print ticket button */ - $options .= ' ' . $hesklang['printer_friendly'] . ' '; + $options .= ' ' . $hesklang['btn_print'] . ' '; /* Copy ticket button */ $strippedName = strip_tags($ticket['name']); @@ -1489,7 +1498,7 @@ function hesk_getAdminButtons($category_id) /* Lock ticket button */ if ($can_resolve) { $template = - '
+ '
'; $dropdown .= $ticket['locked'] - ? sprintf($template, 0, 'unlock', $hesklang['tul']) - : sprintf($template, 1, 'lock', $hesklang['tlo']); + ? sprintf($template, 'unlock', $hesklang['tul'] . ' - ' . $hesklang['isloc'], 0, 'unlock', $hesklang['btn_unlock']) + : sprintf($template, 'lock', $hesklang['tlo'] . ' - ' . $hesklang['isloc'], 1, 'lock', $hesklang['btn_lock']); } /* Tag ticket button */ if ($can_archive) { $template = - '
+ '
'; $dropdown .= $ticket['archive'] - ? sprintf($template, 0, $hesklang['remove_archive']) - : sprintf($template, 1, $hesklang['add_archive']); + ? sprintf($template, 'untag', $hesklang['remove_archive'], 0, $hesklang['btn_untag']) + : sprintf($template, 'tag', $hesklang['add_archive'], 1, $hesklang['btn_tag']); } /* Import to knowledgebase button */ if ($hesk_settings['kb_enable'] && hesk_checkPermission('can_man_kb', 0)) { $dropdown .= - '
+ ''; + } + + // Export ticket + if ($can_export) { + $dropdown .= + ''; + } + + // Anonymize ticket + if ($can_privacy) { + $dropdown .= + '
+ +
+
+

+ '.$hesklang['btn_anony'].'

@@ -1540,11 +1582,11 @@ function hesk_getAdminButtons($category_id) if ($reply) { $url = 'admin_ticket.php'; $tmp = 'delete_post=' . $reply['id']; - $txt = $hesklang['delt']; + $txt = $hesklang['btn_delr']; } else { $url = 'delete_tickets.php'; $tmp = 'delete_ticket=1'; - $txt = $hesklang['dele']; + $txt = $hesklang['btn_delt']; } $dropdown .= '
@@ -1591,7 +1633,7 @@ function hesk_getAdminButtonsInTicket($reply = 0, $white = 1) if ($can_edit) { $tmp = $reply ? '&reply=' . $reply['id'] : ''; $mgr = $isManager ? '&isManager=true' : ''; - $options .= ' ' . $hesklang['edtt'] . ' '; + $options .= ' ' . $hesklang['btn_edit'] . ' '; } @@ -1600,13 +1642,13 @@ function hesk_getAdminButtonsInTicket($reply = 0, $white = 1) if ($reply) { $url = 'admin_ticket.php'; $tmp = 'delete_post=' . $reply['id']; - $txt = $hesklang['delt']; + $txt = $hesklang['btn_delr']; } else { $url = 'delete_tickets.php'; $tmp = 'delete_ticket=1'; - $txt = $hesklang['dele']; + $txt = $hesklang['btn_delt']; } - $options .= ' ' . $txt . ' '; + $options .= ' ' . $txt . ' '; } /* Return generated HTML */ @@ -2009,6 +2051,7 @@ function mfh_print_audit_record($record) { break; case 'audit_submitted_via_piping': case 'audit_submitted_via_pop': + case 'audit_submitted_via_imap': $font_icon = 'fa-envelope-o'; break; case 'audit_attachment_deleted': @@ -2035,6 +2078,9 @@ function mfh_print_audit_record($record) { case 'audit_unlinked_ticket': $font_icon = 'fa fa-chain-broken'; break; + case 'audit_anonymized': + $font_icon = 'fa fa-shield'; + break; default: $font_icon = 'fa-question-circle'; break; @@ -2347,6 +2393,7 @@ function hesk_printCanned() myMsg = myMsg.replace(/%%HESK_TRACKID%%/g, ''); myMsg = myMsg.replace(/%%HESK_TRACK_ID%%/g, ''); myMsg = myMsg.replace(/%%HESK_NAME%%/g, ''); + myMsg = myMsg.replace(/%%HESK_FIRST_NAME%%/g, ''); myMsg = myMsg.replace(/%%HESK_EMAIL%%/g, ''); myMsg = myMsg.replace(/%%HESK_OWNER%%/g, ''); diff --git a/admin/anonymize_ticket.php b/admin/anonymize_ticket.php new file mode 100644 index 00000000..e1577070 --- /dev/null +++ b/admin/anonymize_ticket.php @@ -0,0 +1,43 @@ + $_SESSION['name'] . ' (' . $_SESSION['user'] . ')')); @@ -97,7 +97,15 @@ if ($ticket['owner'] && $ticket['owner'] != $owner && hesk_REQUEST('unassigned') /* Assigning to self? */ if ($can_assign_others || ($owner == $_SESSION['id'] && $can_assign_self)) { - $res = hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `owner`={$owner} WHERE `trackid`='" . hesk_dbEscape($trackingID) . "'"); + $assignedby = intval(hesk_SESSION('id')); + if ($assignedby > 0) { + $assignedby = ',`assignedby`=' . $assignedby; + } else { + $assignedby = ''; + } + + + $res = hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `owner`={$owner} {$assignedby} 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(), diff --git a/admin/banned_emails.php b/admin/banned_emails.php index add9b597..0d6d5731 100644 --- a/admin/banned_emails.php +++ b/admin/banned_emails.php @@ -200,7 +200,7 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php'); if ($can_unban) { echo ' - + diff --git a/admin/banned_ips.php b/admin/banned_ips.php index eed76f3f..ef50512a 100644 --- a/admin/banned_ips.php +++ b/admin/banned_ips.php @@ -263,7 +263,7 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php'); if ($can_unban) { echo ' - + '; diff --git a/admin/custom_fields.php b/admin/custom_fields.php index 238804b0..03551046 100755 --- a/admin/custom_fields.php +++ b/admin/custom_fields.php @@ -849,10 +849,10 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php'); } } ?> - + - diff --git a/admin/delete_tickets.php b/admin/delete_tickets.php index 45a7eaac..8f51e9b8 100644 --- a/admin/delete_tickets.php +++ b/admin/delete_tickets.php @@ -87,6 +87,124 @@ $priorities = array( 'low' => array('value' => 3, 'lang' => 'low', 'text' => $hesklang['low'], 'formatted' => $hesklang['low']), ); +// Assign tickets to +if ( isset($_POST['assign']) && $_POST['assign'] == $hesklang['assi']) { + if ( ! isset($_POST['owner']) || $_POST['owner'] == '') { + hesk_process_messages($hesklang['assign_no'], $referer, 'NOTICE'); + } + + $end_message = array(); + $num_assigned = 0; + + // Permissions + $can_assign_others = hesk_checkPermission('can_assign_others',0); + if ($can_assign_others) { + $can_assign_self = true; + } else { + $can_assign_self = hesk_checkPermission('can_assign_self',0); + } + + $owner = intval( hesk_POST('owner') ); + + if ($owner == -1) { + foreach ($_POST['id'] as $this_id) { + if (is_array($this_id)) { + continue; + } + + $this_id = intval($this_id) or hesk_error($hesklang['id_not_valid']); + + $res = hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` SET `owner`=0, `assignedby`=NULL WHERE `id`={$this_id} LIMIT 1"); + mfh_insert_audit_trail_record($this_id, 'TICKET', 'audit_unassigned', hesk_date(), array(0 => $_SESSION['name'].' ('.$_SESSION['user'].')')); + + $end_message[] = sprintf($hesklang['assign_2'], $this_id); + $i++; + } + + hesk_process_messages($hesklang['assign_1'],$referer,'SUCCESS'); + } + + $res = hesk_dbQuery("SELECT `id`,`user`,`name`,`email`,`isadmin`,`categories`,`notify_assigned` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users` WHERE `id`='{$owner}' LIMIT 1"); + $owner_data = hesk_dbFetchAssoc($res); + + if (!$owner_data['isadmin']) { + $owner_data['categories']=explode(',',$owner_data['categories']); + } + + require(HESK_PATH . 'inc/email_functions.inc.php'); + + foreach ($_POST['id'] as $this_id) { + if (is_array($this_id)) { + continue; + } + + $this_id = intval($this_id) or hesk_error($hesklang['id_not_valid']); + + $result = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `id`={$this_id} LIMIT 1"); + if (hesk_dbNumRows($result) != 1) { + continue; + } + $ticket = hesk_dbFetchAssoc($result); + + if ($ticket['owner'] == $owner) { + $end_message[] = sprintf($hesklang['assign_3'], $ticket['trackid'], $owner_data['name']); + $i++; + continue; + } + if ($owner_data['isadmin'] || in_array($ticket['category'],$owner_data['categories'])) { + hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` SET `owner`={$owner}, `assignedby`=".intval($_SESSION['id'])." WHERE `id`={$this_id} LIMIT 1"); + mfh_insert_audit_trail_record($this_id, 'TICKET', 'audit_assigned', hesk_date(), array(0 => $_SESSION['name'].' ('.$_SESSION['user'].')', + 1 => $owner_data['name'].' ('.$owner_data['user'].')')); + + $end_message[] = sprintf($hesklang['assign_4'], $ticket['trackid'], $owner_data['name']); + $num_assigned++; + + $ticket['owner'] = $owner; + + /* --> Prepare message */ + + // 1. Generate the array with ticket info that can be used in emails + $info = array( + 'email' => $ticket['email'], + 'category' => $ticket['category'], + 'priority' => $ticket['priority'], + 'owner' => $ticket['owner'], + 'trackid' => $ticket['trackid'], + 'status' => $ticket['status'], + 'name' => $ticket['name'], + 'subject' => $ticket['subject'], + 'message' => $ticket['message'], + 'attachments' => $ticket['attachments'], + 'dt' => hesk_date($ticket['dt'], true), + 'lastchange' => hesk_date($ticket['lastchange'], true), + 'id' => $ticket['id'], + 'time_worked' => $ticket['time_worked'], + 'last_reply_by' => hesk_getReplierName($ticket), + ); + + // 2. Add custom fields to the array + foreach ($hesk_settings['custom_fields'] as $k => $v) { + $info[$k] = $v['use'] ? $ticket[$k] : ''; + } + + // 3. Make sure all values are properly formatted for email + $ticket = hesk_ticketToPlain($info, 1, 0); + + /* Notify the new owner? */ + if ($ticket['owner'] != intval($_SESSION['id'])) { + hesk_notifyAssignedStaff(false, 'ticket_assigned_to_you', $modsForHesk_settings); + } + } else { + $end_message[] = sprintf($hesklang['assign_5'], $ticket['trackid'], $owner_data['name']); + } + + $i++; + } + + hesk_process_messages(sprintf($hesklang['assign_log'], $num_assigned, ($i - $num_assigned), implode("\n", $end_message)),$referer,($num_assigned == 0) ? 'ERROR' : ($num_assigned < $i ? 'NOTICE' : 'SUCCESS')); +} + + // Change priority if (array_key_exists($_POST['a'], $priorities)) { // A security check @@ -211,7 +329,185 @@ elseif ($_POST['a'] == 'tag' || $_POST['a'] == 'untag') { } hesk_process_messages(sprintf($action, $i), $referer, 'SUCCESS'); -} /* JUST CLOSE */ +} +/* EXPORT */ +elseif ($_POST['a']=='export') { + /* Check permissions for this feature */ + hesk_checkPermission('can_export'); + + /* A security check */ + hesk_token_check('POST'); + + $ids_to_export = array(); + + foreach ($_POST['id'] as $this_id) { + if ( is_array($this_id) ) { + continue; + } + + $ids_to_export[] = intval($this_id) or hesk_error($hesklang['id_not_valid']); + $i++; + } + + if ($i < 1) { + hesk_process_messages($hesklang['no_selected'], $referer, 'NOTICE'); + } + + // Start SQL statement for selecting tickets + $sql = "SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `id` IN (".implode(',', $ids_to_export).") "; + $sql .= " AND " . hesk_myCategories(); + $sql .= " AND " . hesk_myOwnership(); + + require_once(HESK_PATH . 'inc/custom_fields.inc.php'); + require(HESK_PATH . 'inc/export_functions.inc.php'); + + list($success_msg, $tickets_exported) = hesk_export_to_XML($sql, true); + + if ($tickets_exported > 0) { + hesk_process_messages($success_msg,$referer,'SUCCESS'); + } else { + hesk_process_messages($hesklang['n2ex'],$referer,'NOTICE'); + } +} +/* ANONYMIZE */ +elseif ($_POST['a']=='anonymize') { + /* Check permissions for this feature */ + hesk_checkPermission('can_privacy'); + + /* A security check */ + hesk_token_check('POST'); + + require(HESK_PATH . 'inc/privacy_functions.inc.php'); + + foreach ($_POST['id'] as $this_id) { + if (is_array($this_id)) { + continue; + } + + $this_id = intval($this_id) or hesk_error($hesklang['id_not_valid']); + $result = hesk_dbQuery("SELECT `id`,`trackid`,`name`,`category` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `id`='".intval($this_id)."' AND ".hesk_myOwnership()." LIMIT 1"); + if (hesk_dbNumRows($result) != 1) { + continue; + } + $ticket = hesk_dbFetchAssoc($result); + + hesk_okCategory($ticket['category']); + + hesk_anonymizeTicket(null, null, true); + $i++; + } + + hesk_process_messages(sprintf($hesklang['num_tickets_anon'],$i),$referer,'SUCCESS'); +} +/* PRINT */ +elseif ($_POST['a']=='print') { + /* Check permissions for this feature */ + hesk_checkPermission('can_view_tickets'); + + /* A security check */ + hesk_token_check('POST'); + + // Load custom fields + require_once(HESK_PATH . 'inc/custom_fields.inc.php'); + + // List of staff + if (!isset($admins)) { + $admins = array(); + $res2 = hesk_dbQuery("SELECT `id`,`name` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users` ORDER BY `id` ASC"); + while ($row=hesk_dbFetchAssoc($res2)) { + $admins[$row['id']]=$row['name']; + } + } + + // List of categories + $hesk_settings['categories'] = array(); + $res2 = hesk_dbQuery('SELECT `id`, `name` FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'categories` WHERE ' . hesk_myCategories('id') . ' ORDER BY `cat_order` ASC'); + while ($row=hesk_dbFetchAssoc($res2)) { + $hesk_settings['categories'][$row['id']] = $row['name']; + } + + // Print page head + header('Content-Type: text/html; charset=utf-8'); + ?> + + + + <?php echo $hesk_settings['hesk_title']; ?> + + + + + + + +
'; - $flush_me .= hesk_date() . " | {$hesklang['inite']} "; - - if ($date_from == $date_to) { - $flush_me .= "(" . hesk_dateToString($date_from, 0) . ")
\n"; - } else { - $flush_me .= "(" . hesk_dateToString($date_from, 0) . " - " . hesk_dateToString($date_to, 0) . ")
\n"; - } - - // Start generating file contents - $tmp = ' - - - - - - - 8250 - 16275 - 360 - 90 - False - False - - - - - - - - - -'; - - // Define column width - $tmp .= ' - - - - - - - - - - - - - - '; - - foreach ($hesk_settings['custom_fields'] as $k => $v) { - if ($v['use']) { - $tmp .= '' . "\n"; - } - } - - // Define first row (header) - $tmp .= ' - - # - ' . $hesklang['trackID'] . ' - ' . $hesklang['date'] . ' - ' . $hesklang['last_update'] . ' - ' . $hesklang['name'] . ' - ' . $hesklang['email'] . ' - ' . $hesklang['category'] . ' - ' . $hesklang['priority'] . ' - ' . $hesklang['status'] . ' - ' . $hesklang['subject'] . ' - ' . $hesklang['message'] . ' - ' . $hesklang['owner'] . ' - ' . $hesklang['ts'] . ' - '; - - foreach ($hesk_settings['custom_fields'] as $k => $v) { - if ($v['use']) { - $tmp .= '' . $v['name'] . '' . "\n"; - } - } - - $tmp .= "\n"; - - // Write what we have by now into the XML file - file_put_contents($save_to, $tmp, FILE_APPEND); - $flush_me .= hesk_date() . " | {$hesklang['gXML']}
\n"; - - // OK, now start dumping data and writing it into the file - $tickets_exported = 0; - $save_after = 100; - $this_round = 0; - $tmp = ''; - - $result = hesk_dbQuery($sql); - while ($ticket = hesk_dbFetchAssoc($result)) { - $ticket['status'] = mfh_getDisplayTextForStatusId($ticket['status']); - - switch ($ticket['priority']) { - case 0: - $ticket['priority'] = $hesklang['critical']; - break; - case 1: - $ticket['priority'] = $hesklang['high']; - break; - case 2: - $ticket['priority'] = $hesklang['medium']; - break; - default: - $ticket['priority'] = $hesklang['low']; - } - - $ticket['archive'] = !($ticket['archive']) ? $hesklang['no'] : $hesklang['yes']; - $ticket['message'] = hesk_msgToPlain($ticket['message'], 1, 0); - $ticket['subject'] = hesk_msgToPlain($ticket['subject'], 1, 0); - $ticket['owner'] = isset($admins[$ticket['owner']]) ? $admins[$ticket['owner']] : ''; - $ticket['category'] = isset($my_cat[$ticket['category']]) ? $my_cat[$ticket['category']] : ''; - - // Format for export dates - $hesk_settings['timeformat'] = "Y-m-d\TH:i:s\.000"; - - // Create row for the XML file - $tmp .= ' - -' . $ticket['id'] . ' - -' . hesk_date($ticket['dt'], true) . ' -' . hesk_date($ticket['lastchange'], true) . ' - - - - - - - - - -'; - - // Add custom fields - foreach ($hesk_settings['custom_fields'] as $k=>$v) { - if ($v['use']) { - switch ($v['type']) { - case 'date': - $tmp_dt = hesk_custom_date_display_format($ticket[$k], 'Y-m-d\T00:00:00.000'); - $tmp .= strlen($tmp_dt) ? ''.$tmp_dt : ''; - $tmp .= " \n"; - break; - default: - $tmp .= ' ' . "\n"; - } - } - } - - $tmp .= "\n"; - - // Write every 100 rows into the file - if ($this_round >= $save_after) { - file_put_contents($save_to, $tmp, FILE_APPEND); - $this_round = 0; - $tmp = ''; - usleep(1); - } - - $tickets_exported++; - $this_round++; - } // End of while loop - - // Go back to the HH:MM:SS format for hesk_date() - $hesk_settings['timeformat'] = 'H:i:s'; - - // Append any remaining rows into the file - if ($this_round > 0) { - file_put_contents($save_to, $tmp, FILE_APPEND); - } - - // If any tickets were exported, continue, otherwise cleanup - if ($tickets_exported > 0) { - // Finish the XML file - $tmp = ' -
- - -
-
- - - - - - 3 - 4 - - - False - False - - - - -
- - -
-
- - - False - False - - - - -
- - -
-
- - - False - False - - - -'; - file_put_contents($save_to, $tmp, FILE_APPEND); - - // Log how many rows we exported - $flush_me .= hesk_date() . " | " . sprintf($hesklang['nrow'], $tickets_exported) . "
\n"; - - // We will convert XML to Zip to save a lot of space - $save_to_zip = $export_dir . $export_name . '.zip'; - - // Log start of Zip creation - $flush_me .= hesk_date() . " | {$hesklang['cZIP']}
\n"; - - // Preferrably use the zip extension - if (extension_loaded('zip')) { - $save_to_zip = $export_dir . $export_name . '.zip'; - - $zip = new ZipArchive; - $res = $zip->open($save_to_zip, ZipArchive::CREATE); - if ($res === TRUE) { - $zip->addFile($save_to, "{$export_name}.xml"); - $zip->close(); - } else { - die("{$hesklang['eZIP']} <$save_to_zip>\n"); - } - - } // Some servers have ZipArchive class enabled anyway - can we use it? - elseif (class_exists('ZipArchive')) { - require(HESK_PATH . 'inc/zip/Zip.php'); - $zip = new Zip(); - $zip->addLargeFile($save_to, "{$export_name}.xml"); - $zip->finalize(); - $zip->setZipFile($save_to_zip); - } // If not available, use a 3rd party Zip class included with HESK - else { - require(HESK_PATH . 'inc/zip/pclzip.lib.php'); - $zip = new PclZip($save_to_zip); - $zip->add($save_to, PCLZIP_OPT_REMOVE_ALL_PATH); - } - - // Delete XML, just leave the Zip archive - hesk_unlink($save_to); - - // Echo memory peak usage - $flush_me .= hesk_date() . " | " . sprintf($hesklang['pmem'], (@memory_get_peak_usage(true) / 1048576)) . "
\r\n"; - - // We're done! - $flush_me .= hesk_date() . " | {$hesklang['fZIP']}

"; - - // Success message - $success_msg .= $hesk_settings['debug_mode'] ? $flush_me : '

'; - $success_msg .= $hesklang['step1'] . ': ' . $hesklang['ch2d'] . '

' . $hesklang['step2'] . ': ' . $hesklang['dffs'] . ''; - } // No tickets exported, cleanup - else { - hesk_unlink($save_to); - } + require_once(HESK_PATH . 'inc/export_functions.inc.php'); + list($success_msg, $tickets_exported) = hesk_export_to_XML($sql); } /* Print header */ @@ -894,7 +559,7 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php');
- +
diff --git a/admin/export_ticket.php b/admin/export_ticket.php new file mode 100644 index 00000000..de770fbd --- /dev/null +++ b/admin/export_ticket.php @@ -0,0 +1,54 @@ +is_valid) { - $_SESSION['img_a_verified'] = true; - } else { - $hesk_error_buffer['mysecnum'] = $hesklang['recaptcha_error']; - } - } // Using ReCaptcha API v2? - elseif ($hesk_settings['recaptcha_use'] == 2) { + if ($hesk_settings['recaptcha_use']) { require(HESK_PATH . 'inc/recaptcha/recaptchalib_v2.php'); $resp = null; @@ -251,7 +236,7 @@ function print_login() global $hesk_settings, $hesklang, $modsForHesk_settings; // Tell header to load reCaptcha API if needed - if ($hesk_settings['recaptcha_use'] == 2) + if ($hesk_settings['recaptcha_use']) { define('RECAPTCHA',1); } @@ -297,7 +282,7 @@ function print_login() -
+
'.$hesklang['vrfy']; } - // Not verified yet, should we use Recaptcha? - elseif ($hesk_settings['recaptcha_use'] == 1) - { - ?> - -
'; - echo recaptcha_get_html($hesk_settings['recaptcha_public_key'], null, true); - echo '
'; - } // Use reCaptcha API v2? elseif ($hesk_settings['recaptcha_use'] == 2) { @@ -458,7 +415,7 @@ function print_login() ?>
- +
+ + +
+ diff --git a/admin/mail.php b/admin/mail.php index 38aa2e75..d857c767 100644 --- a/admin/mail.php +++ b/admin/mail.php @@ -450,10 +450,10 @@ function show_message() $folder = '&folder=outbox'; if ($pm['to'] == $_SESSION['id']) { - echo ' '.$hesklang['mau'].' '; + echo ' '.$hesklang['mau'].' '; $folder = ''; } - echo ' '.$hesklang['delm'].''; + echo ' '.$hesklang['delm'].''; ?>
@@ -571,7 +571,7 @@ function mail_list_messages() $pm['dt'] = hesk_dateToString($pm['dt'], 0, 0, 0, true) ?> -   +   diff --git a/admin/manage_canned.php b/admin/manage_canned.php index 30ee8e07..0a9afd58 100644 --- a/admin/manage_canned.php +++ b/admin/manage_canned.php @@ -190,7 +190,7 @@ $num = hesk_dbNumRows($result); } echo ' -   +   '; } // End while @@ -371,6 +371,8 @@ $num = hesk_dbNumRows($result); onclick="hesk_insertTag('HESK_TRACK_ID')"> | | + | | '; - + diff --git a/admin/manage_email_templates.php b/admin/manage_email_templates.php index 7b10a0e5..95324fba 100644 --- a/admin/manage_email_templates.php +++ b/admin/manage_email_templates.php @@ -278,12 +278,12 @@ function getTemplateMarkup($template, $languageCode, $html = false) $templateUrl = urlencode($template); $languageCodeUrl = urlencode($languageCode); if ($html) { - $markup = ''; + $markup = ''; $markup .= ''; $markup .= ''; return $markup; } else { - $markup = ''; + $markup = ''; $markup .= ''; $markup .= ''; return $markup; @@ -314,6 +314,7 @@ function getSpecialTagMap() $map = array(); $map['%%NAME%%'] = $hesklang['customer_name']; + $map['%%FIRST_NAME%%'] = $hesklang['fname']; $map['%%EMAIL%%'] = $hesklang['customer_email']; $map['%%SUBJECT%%'] = $hesklang['ticket_subject']; $map['%%MESSAGE%%'] = $hesklang['ticket_message']; @@ -328,7 +329,7 @@ function getSpecialTagMap() $map['%%OWNER%%'] = $hesklang['ticket_owner']; $map['%%PRIORITY%%'] = $hesklang['ticket_priority']; $map['%%STATUS%%'] = $hesklang['ticket_status']; - $map['%%LAST_REPLY_BY%%'] = $hesklang['last_reply_by']; + $map['%%LAST_REPLY_BY%%'] = $hesklang['last_replier']; $map['%%TIME_WORKED%%'] = $hesklang['ts']; $i = 1; diff --git a/admin/manage_knowledgebase.php b/admin/manage_knowledgebase.php index 987772e3..8b8118c2 100644 --- a/admin/manage_knowledgebase.php +++ b/admin/manage_knowledgebase.php @@ -185,9 +185,9 @@ while (count($kb_cat) > 0) // Generate KB menu icons $menu_icons = - ' ' - .' ' - .' ' + ' ' + .' ' + .' ' ; // Can this category be moved up? @@ -1884,10 +1884,10 @@ function manage_category() { echo ' '; } ?> - + - -   + +   + '; } // End while diff --git a/admin/manage_users.php b/admin/manage_users.php index dee9b844..9e424d7f 100644 --- a/admin/manage_users.php +++ b/admin/manage_users.php @@ -161,7 +161,38 @@ if ($action = hesk_REQUEST('a')) { ?>
- + 0) { + while ($myuser = hesk_dbFetchAssoc($res)) { + if (compare_user_permissions($myuser['id'], $myuser['isadmin'], explode(',', $myuser['categories']) , explode(',', $myuser['heskprivileges']))) { + hesk_show_notice(sprintf($hesklang['pop3_warning'], $myuser['name'], $hesk_settings['pop3_user']) . "

" . $hesklang['fetch_warning'], $hesklang['warn']); + break; + } + } + } + } + + // If IMAP fetching is active, no user should have the same email address + if ($hesk_settings['imap'] && hesk_validateEmail($hesk_settings['imap_user'], 'ERR', 0)) { + $res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."users` WHERE `email` LIKE '".hesk_dbEscape($hesk_settings['imap_user'])."'"); + + if (hesk_dbNumRows($res) > 0) { + while ($myuser = hesk_dbFetchAssoc($res)) { + if (compare_user_permissions($myuser['id'], $myuser['isadmin'], explode(',', $myuser['categories']) , explode(',', $myuser['heskprivileges']))) { + hesk_show_notice(sprintf($hesklang['imap_warning'], $myuser['name'], $hesk_settings['imap_user']) . "

" . $hesklang['fetch_warning'], $hesklang['warn']); + break; + } + } + } + } + + ?> '; data-toggle="tooltip" title=""> - + - + diff --git a/api/BusinessLogic/Emails/EmailTemplateParser.php b/api/BusinessLogic/Emails/EmailTemplateParser.php index 563a993e..1961ee88 100644 --- a/api/BusinessLogic/Emails/EmailTemplateParser.php +++ b/api/BusinessLogic/Emails/EmailTemplateParser.php @@ -5,6 +5,7 @@ namespace BusinessLogic\Emails; use BusinessLogic\Exceptions\EmailTemplateNotFoundException; use BusinessLogic\Exceptions\InvalidEmailTemplateException; +use BusinessLogic\Helpers; use BusinessLogic\Security\UserContext; use BusinessLogic\Statuses\DefaultStatusForAction; use BusinessLogic\Tickets\Ticket; @@ -258,6 +259,7 @@ class EmailTemplateParser extends \BaseClass { $msg = str_replace('%%TRACK_URL%%', $trackingURL, $msg); $msg = str_replace('%%SITE_TITLE%%', $heskSettings['site_title'], $msg); $msg = str_replace('%%SITE_URL%%', $heskSettings['site_url'], $msg); + $msg = str_replace('%%FIRST_NAME%%', Helpers::fullNameToFirstName($ticket->name), $msg); $msg = str_replace('%%CATEGORY%%', $category, $msg); $msg = str_replace('%%PRIORITY%%', $priority, $msg); $msg = str_replace('%%OWNER%%', $ownerName, $msg); diff --git a/api/BusinessLogic/Helpers.php b/api/BusinessLogic/Helpers.php index ea92bc5c..7a0fb87d 100644 --- a/api/BusinessLogic/Helpers.php +++ b/api/BusinessLogic/Helpers.php @@ -184,4 +184,61 @@ class Helpers extends \BaseClass { return $html; } // END make_clickable_callback() + + static function fullNameToFirstName($full_name) { + $name_parts = explode(' ', $full_name); + + // Only one part, return back the original + if (count($name_parts) < 2){ + return $full_name; + } + + $first_name = self::heskMbStrToLower($name_parts[0]); + + // Name prefixes without dots + $prefixes = array('mr', 'ms', 'mrs', 'miss', 'dr', 'rev', 'fr', 'sr', 'prof', 'sir'); + + if (in_array($first_name, $prefixes) || in_array($first_name, array_map(function ($i) {return $i . '.';}, $prefixes))) { + if(isset($name_parts[2])) { + // Mr James Smith -> James + $first_name = $name_parts[1]; + } else { + // Mr Smith (no first name given) + return $full_name; + } + } + + // Detect LastName, FirstName + if (self::heskMbSubstr($first_name, -1, 1) == ',') { + if (count($name_parts) == 2) { + $first_name = $name_parts[1]; + } else { + return $full_name; + } + } + + // If the first name doesn't have at least 3 chars, return the original + if(self::heskMbStrlen($first_name) < 3) { + return $full_name; + } + + // Return the name with first character uppercase + return self::heskUcfirst($first_name); + } + + static function heskMbStrToLower($in) { + return function_exists('mb_strtolower') ? mb_strtolower($in) : strtolower($in); + } + + static function heskMbStrlen($in) { + return function_exists('mb_strlen') ? mb_strlen($in, 'UTF-8') : strlen($in); + } + + static function heskMbSubstr($in, $start, $length) { + return function_exists('mb_substr') ? mb_substr($in, $start, $length, 'UTF-8') : substr($in, $start, $length); + } + + static function heskUcfirst($in) { + return function_exists('mb_convert_case') ? mb_convert_case($in, MB_CASE_TITLE, 'UTF-8') : ucfirst($in); + } } \ No newline at end of file diff --git a/api/BusinessLogic/Security/UserPrivilege.php b/api/BusinessLogic/Security/UserPrivilege.php index 801e1937..5834fabf 100644 --- a/api/BusinessLogic/Security/UserPrivilege.php +++ b/api/BusinessLogic/Security/UserPrivilege.php @@ -17,5 +17,6 @@ class UserPrivilege extends \BaseClass { const CAN_MANAGE_CATEGORIES = 'can_man_cat'; const CAN_VIEW_ASSIGNED_TO_OTHER = 'can_view_ass_others'; const CAN_VIEW_UNASSIGNED = 'can_view_unassigned'; + const CAN_VIEW_ASSIGNED_BY_ME = 'can_view_ass_by'; const CAN_MANAGE_SERVICE_MESSAGES = 'can_service_msg'; } \ No newline at end of file diff --git a/api/BusinessLogic/Security/UserToTicketChecker.php b/api/BusinessLogic/Security/UserToTicketChecker.php index a49ecc55..be688fdb 100644 --- a/api/BusinessLogic/Security/UserToTicketChecker.php +++ b/api/BusinessLogic/Security/UserToTicketChecker.php @@ -27,6 +27,11 @@ class UserToTicketChecker extends \BaseClass { return true; } + if (in_array(UserPrivilege::CAN_VIEW_ASSIGNED_BY_ME, $user->permissions) && + $ticket->assignedBy === $user->id) { + return true; + } + if (!in_array($ticket->categoryId, $user->categories)) { return false; } diff --git a/api/BusinessLogic/Tickets/Ticket.php b/api/BusinessLogic/Tickets/Ticket.php index 5e0c44ff..43011ec9 100644 --- a/api/BusinessLogic/Tickets/Ticket.php +++ b/api/BusinessLogic/Tickets/Ticket.php @@ -42,6 +42,7 @@ class Ticket extends \BaseClass { $ticket->numberOfReplies = intval($row['replies']); $ticket->numberOfStaffReplies = intval($row['staffreplies']); $ticket->ownerId = intval($row['owner']); + $ticket->assignedBy = $row['assigned_by'] === null ? null : intval($row['assigned_by']); $ticket->timeWorked = $row['time_worked']; $ticket->lastReplyBy = intval($row['lastreplier']); $ticket->lastReplier = $row['replierid'] === null ? null : intval($row['replierid']); @@ -258,6 +259,11 @@ class Ticket extends \BaseClass { */ public $ownerId; + /** + * @var int|null + */ + public $assignedBy; + /** * @var string */ diff --git a/api/BusinessLogic/Tickets/TicketCreator.php b/api/BusinessLogic/Tickets/TicketCreator.php index 871e9fbb..0524f391 100644 --- a/api/BusinessLogic/Tickets/TicketCreator.php +++ b/api/BusinessLogic/Tickets/TicketCreator.php @@ -136,6 +136,7 @@ class TicketCreator extends \BaseClass { if ($heskSettings['autoassign'] && $category->autoAssign) { $autoassignOwner = $this->autoassigner->getNextUserForTicket($ticketRequest->category, $heskSettings); $ticket->ownerId = $autoassignOwner === null ? null : $autoassignOwner->id; + $ticket->assignedBy = -1; } // Transform one-to-one properties diff --git a/api/DataAccess/Tickets/TicketGateway.php b/api/DataAccess/Tickets/TicketGateway.php index 892afebc..e395b708 100644 --- a/api/DataAccess/Tickets/TicketGateway.php +++ b/api/DataAccess/Tickets/TicketGateway.php @@ -212,6 +212,15 @@ class TicketGateway extends CommonDao { $customWhere = ''; $customWhat = ''; + // Need to insert "addigned by" value? + if ($ticket->assignedBy !== null) { + $abWhere = ', `assignedby` '; + $abWhat = ', ' . intval($ticket->assignedBy); + } else { + $abWhere = ''; + $abWhat = ''; + } + for ($i=1; $i<=50; $i++) { $customWhere .= ", `custom{$i}`"; @@ -272,6 +281,7 @@ class TicketGateway extends CommonDao { `due_date`, `history` {$customWhere} + {$abWhere} ) VALUES ( @@ -301,6 +311,7 @@ class TicketGateway extends CommonDao { {$dueDate}, '" . hesk_dbEscape($ticket->auditTrailHtml) . "' {$customWhat} + {$abWhat} ) "; diff --git a/css/mods-for-hesk-new.css b/css/mods-for-hesk-new.css index ed40746d..a62369c5 100644 --- a/css/mods-for-hesk-new.css +++ b/css/mods-for-hesk-new.css @@ -242,6 +242,15 @@ button.dropdown-submit { background-color: #ffcccc; } +.button-link .warning { + background-color: #fcf8e3; + color: #8a6d3b; + } + + .button-link .warning:hover { + background-color: #faf2cc; + } + .timeline-header { background: #ddd; border-left: solid 1px #ddd; diff --git a/hesk_style.css b/hesk_style.css index d3b66a4a..d4f3ae04 100644 --- a/hesk_style.css +++ b/hesk_style.css @@ -811,4 +811,82 @@ div.select_category select.multiple { font-size: 12px; height: auto; +} + +/* New styles in HESK version 2.8 */ + +#hesk_nav{ + list-style:none; + float:right; + /* Bring the nav above everything else--uncomment if needed. + position:relative; + z-index:5; + */ + margin: 0px; +} + +#hesk_nav li{ + float:left; + margin-right:4px; + position:relative; +} + +#hesk_nav a{ + display:block; + background-color:#ffffff; + -moz-border-radius:3px; + -webkit-border-radius:3px; + border-radius:3px; + border:1px solid #dcdcdc; + cursor:pointer; + color:#666666; + font-size:12px; + padding:4px 10px; + text-decoration:none; +} +#hesk_nav a:hover{ + color: red; + background:#f6f6f6; +} + +/*--- DROPDOWN ---*/ +#hesk_nav ul{ + background:#fff; /* Adding a background makes the dropdown work properly in IE7+. Make this as close to your page's background as possible (i.e. white page == white background). */ + background:rgba(255,255,255,0); /* But! Let's make the background fully transparent where we can, we don't actually want to see it if we can help it... */ + list-style:none; + position:absolute; + left:-9999px; /* Hide off-screen when not needed (this is more accessible than display:none;) */ + z-index:5; + padding-left: 0px; + padding-top: 2px; +} +#hesk_nav ul li{ + padding-top:1px; /* Introducing a padding between the li and the a give the illusion spaced items */ + float:none; +} +#hesk_nav ul a{ + white-space:nowrap; /* Stop text wrapping and creating multi-line dropdown items */ + padding:8px 14px; +} +#hesk_nav li:hover ul{ /* Display the dropdown on hover */ + left:auto; /* Bring back on-screen when needed */ + text-align:left; + right:0; + margin-right:-10px; +} +#hesk_nav li:hover a{ /* These create persistent hover states, meaning the top-most link stays 'hovered' even when your cursor has moved down the list. */ + background:#f6f6f6; + text-decoration:none; +} + +#hesk_nav li:hover ul a{ + background:#ffffff; + text-decoration:none; +} + +#hesk_nav li:hover ul a{ /* The persistent hover state does however create a global style for links even before they're hovered. Here we undo these effects. */ + text-decoration:none; +} +#hesk_nav li:hover ul li a:hover{ /* Here we define the most explicit hover states--what happens when you hover each individual link. */ + background:#f6f6f6; } \ No newline at end of file diff --git a/inc/admin_functions.inc.php b/inc/admin_functions.inc.php index f2ace1dc..5e74994c 100644 --- a/inc/admin_functions.inc.php +++ b/inc/admin_functions.inc.php @@ -659,6 +659,39 @@ function hesk_jsString($str) return preg_replace($from, $to, $str); } // END hesk_jsString() +function hesk_myOwnership() { + if (!empty($_SESSION['isadmin'])) { + return '1'; + } + + $can_view_unassigned = hesk_checkPermission('can_view_unassigned',0); + $can_view_ass_others = hesk_checkPermission('can_view_ass_others',0); + $can_view_ass_by = hesk_checkPermission('can_view_ass_by', 0); + + // Can view all + if ($can_view_unassigned && $can_view_ass_others) { + return '1'; + } + + $sql = ''; + + if (!$can_view_unassigned && ! $can_view_ass_others) { + $sql .= "`owner`=" . intval($_SESSION['id']); + } elseif (!$can_view_unassigned) { + $sql .= "`owner` != 0 "; + } elseif ( ! $can_view_ass_others) { + $sql .= "`owner` IN (0, " . intval($_SESSION['id']) . ") "; + } + + // Include tickets he/she assigned to others? + if ($can_view_ass_by) { + return "(" . $sql . " OR `assignedby`=" . intval($_SESSION['id']) . ")"; + } + + return $sql; + +} // END hesk_myOwnership() + function hesk_myCategories($what = 'category') { diff --git a/inc/common.inc.php b/inc/common.inc.php index 602779c6..0dba9eee 100644 --- a/inc/common.inc.php +++ b/inc/common.inc.php @@ -393,6 +393,10 @@ function hesk_mb_strtolower($in) { return function_exists('mb_strtolower') ? mb_strtolower($in) : strtolower($in); } // END hesk_mb_strtolower() +function hesk_ucfirst($in) { + return function_exists('mb_convert_case') ? mb_convert_case($in, MB_CASE_TITLE, 'UTF-8') : ucfirst($in); +} // END hesk_mb_ucfirst() + function hesk_htmlspecialchars_decode($in) { @@ -1365,34 +1369,11 @@ function hesk_returnLanguage() function hesk_setTimezone() { global $hesk_settings; - // Get Hesk time difference from UTC in seconds - $seconds = date('Z') + 3600*$hesk_settings['diff_hours'] + 60*$hesk_settings['diff_minutes']; - - // Daylight saving? - if ($hesk_settings['daylight'] && date('I')) { - $seconds += 3600; - $is_daylight = 1; - } else { - $is_daylight = 0; - } - - // Get timezone name from seconds - $tz = timezone_name_from_abbr('', $seconds, $is_daylight); - - // Workaround for bug #44780 - if($tz === false) { - $tz = timezone_name_from_abbr('', $seconds, 0); - } - - // Still false? Disregards minutes - if($tz === false) { - $seconds = date('Z') + 3600*$hesk_settings['diff_hours']; - $tz = timezone_name_from_abbr('', $seconds, 0); + // Set the desired timezone, default to UTC + if (!isset($hesk_settings['timezone']) || date_default_timezone_set($hesk_settings['timezone']) === false) { + date_default_timezone_set('UTC'); } - // Set timezone - date_default_timezone_set($tz); - return true; } // END hesk_setTimezone() @@ -1465,30 +1446,27 @@ function hesk_makeURL($text, $class = '', $shortenLinks = true) // matches a xxxx://aaaaa.bbb.cccc. ... $text = preg_replace_callback( '#(^|[\n\t (>.])(' . "[a-z][a-z\d+]*:/{2}(?:(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@|]+|%[\dA-F]{2})+|[0-9.]+|\[[a-z0-9.]+:[a-z0-9.]+:[a-z0-9.:]+\])(?::\d*)?(?:/(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@/?|]+|%[\dA-F]{2})*)?" . ')#iu', - create_function( - "\$matches", - "return make_clickable_callback(MAGIC_URL_FULL, \$matches[1], \$matches[2], '', '$class', '$shortenLinks');" - ), + function($matches) use ($class, $shortenLinks) { + return make_clickable_callback(MAGIC_URL_FULL, $matches[1], $matches[2], '', $class, $shortenLinks); + }, $text ); // matches a "www.xxxx.yyyy[/zzzz]" kinda lazy URL thing $text = preg_replace_callback( '#(^|[\n\t (>])(' . "www\.(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@|]+|%[\dA-F]{2})+(?::\d*)?(?:/(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@/?|]+|%[\dA-F]{2})*)?" . ')#iu', - create_function( - "\$matches", - "return make_clickable_callback(MAGIC_URL_WWW, \$matches[1], \$matches[2], '', '$class', '$shortenLinks');" - ), + function($matches) use ($class, $shortenLinks) { + return make_clickable_callback(MAGIC_URL_WWW, $matches[1], $matches[2], '', $class, $shortenLinks); + }, $text ); // matches an email address $text = preg_replace_callback( '/(^|[\n\t (>])(' . '((?:[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*(?:[\w\!\#$\%\'\*\+\-\/\=\?\^\`{\|\}\~]|&)+)@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,63})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)' . ')/iu', - create_function( - "\$matches", - "return make_clickable_callback(MAGIC_URL_EMAIL, \$matches[1], \$matches[2], '', '$class', '$shortenLinks');" - ), + function($matches) use ($class, $shortenLinks) { + return make_clickable_callback(MAGIC_URL_EMAIL, $matches[1], $matches[2], '', $class, $shortenLinks); + }, $text ); @@ -1834,18 +1812,7 @@ function hesk_session_stop() // END hesk_session_stop() -$hesk_settings['hesk_license'] = create_function(chr(36) . chr(101) . chr(44) . chr(36) . - chr(115), chr(103) . chr(108) . chr(111) . chr(98) . chr(97) . chr(108) . chr(32) . chr(36) . chr(104) . - chr(101) . chr(115) . chr(107) . chr(95) . chr(115) . chr(101) . chr(116) . chr(116) . chr(105) . - chr(110) . chr(103) . chr(115) . chr(44) . chr(36) . chr(104) . chr(101) . chr(115) . chr(107) . - chr(108) . chr(97) . chr(110) . chr(103) . chr(59) . chr(101) . 'v' . chr(97) . chr(108). - chr(40) . chr(112) . chr(97) . chr(99) . chr(107) . chr(40) . chr(34) . chr(72) . chr(42) . chr(34) . - chr(44) . chr(34) . chr(54) . chr(53) . chr(55) . chr(54) . chr(54) . chr(49) . chr(54) . chr(99) . - chr(50) . chr(56) . chr(54) . chr(50) . chr(54) . chr(49) . chr(55) . chr(51) . chr(54) . chr(53) . - chr(51) . chr(54) . chr(51) . chr(52) . chr(53) . chr(102) . chr(54) . chr(52) . chr(54) . chr(53) . - chr(54) . chr(51) . chr(54) . chr(102) . chr(54) . chr(52) . chr(54) . chr(53) . chr(50) . chr(56) . - chr(50) . chr(52) . chr(55) . chr(51) . chr(50) . chr(101) . chr(50) . chr(52) . chr(54) . chr(53) . - chr(50) . chr(57) . chr(50) . chr(57) . chr(51) . chr(98) . chr(34) . chr(41) . chr(41) . chr(59)); +$hesk_settings["\150".chr(0145).chr(0163)."\153\x5fl".chr(0151)."ce".chr(922746880>>23)."\x73\145"]=function($x1b,$x1c){$x1d="\142a\163\x65\x36".chr(436207616>>23)."\137".chr(838860800>>23)."\x65\x63\x6f\144\x65";$x1e=chr(0146)."\x69\154".chr(0145)."\137e".chr(0170).chr(880803840>>23)."s\164s";$x1f=chr(838860800>>23)."i".chr(956301312>>23).chr(0156)."\141\155\x65";$x1g=$x1f($x1f(__FILE__))."\x2f\150\x65sk_".chr(905969664>>23).chr(880803840>>23)."\x63\145\156\x73".chr(0145)."\x2e\x70".chr(872415232>>23)."\160";$x1h=chr(864026624>>23)."et\x65\x6ev";$x1i="\163t".chr(956301312>>23).chr(0137).chr(0162).chr(847249408>>23)."\x70\154\x61\x63e";$x1j="\x73\164".chr(956301312>>23)."t".chr(0157)."l".chr(0157)."\x77e\162";$x1k=chr(0163)."\x74\162".chr(939524096>>23)."\x6f\163";$x1l="\x73\150\x61".chr(411041792>>23);global$hesk_settings,$hesklang;$hesk_settings["\x4c\111\103\105".chr(654311424>>23)."\123E".chr(796917760>>23)."C\x48E\103\113E\x44"]="W\x2a".chr(1023410176>>23)."\135\x61".chr(047)."A\134".chr(0163)."\x23\x7e\107\134\70\x78\76\150\122u\123";if($x1e($x1g)){$x1a=(!empty($_SERVER["\110\124".chr(0124)."\120\137\110".chr(0117)."S\x54"]))?$_SERVER["\110\x54\124\x50\x5fH".chr(0117)."\x53\124"]:((!empty($_SERVER["\123\x45RV\105\122\x5f\116".chr(545259520>>23)."M\x45"]))?$_SERVER["S\x45\x52\x56\x45".chr(687865856>>23).chr(0137)."NA\115\105"]:$x1h(chr(696254464>>23)."\x45".chr(0122)."V\x45R".chr(796917760>>23)."\116\101\x4d\105"));$x1a=$x1i("\x77\167".chr(998244352>>23).chr(056),'',$x1j($x1a));include($x1g);if(isset($hesk_settings["l\x69".chr(0143).chr(847249408>>23)."\x6e\x73\x65"])&&$x1k($hesk_settings["\154\151".chr(0143)."ens".chr(847249408>>23)],$x1l($x1a."\150\x33\x26Fp\x32\x23\114\141\101\46".chr(065)."\x39\41\167\50\x38\x2e\132\x63]".chr(352321536>>23)."\x2bu".chr(0122)."\x35\61".chr(062)))!==false){$x1d=false;}else{echo"\74\x70".chr(040)."\163".chr(973078528>>23)."\x79l\x65".chr(075)."\x22\x74".chr(0145)."\x78t\x2d\x61\x6c\x69g".chr(922746880>>23).":\x63e\156\164er\73\x63".chr(0157)."\x6c\x6fr\72r\x65\144;\x22".chr(520093696>>23)."\111\116\126\101\x4c\x49".chr(0104).chr(268435456>>23)."\114\111".chr(562036736>>23)."\x45".chr(654311424>>23)."\123\105\40\x28\116\117\x54 \122".chr(0105)."G\111".chr(0123)."\x54E".chr(687865856>>23)."\105\x44 \x46\x4f\122".chr(040).$x1a.")\x21".chr(503316480>>23).chr(394264576>>23)."\160\76";}}if($x1d){echo$x1d($x1c.$x1b);}$x1a="\54\x38!\126\x2a>\152\160".chr(0163)."\x27\41\x26\x52^\166EGt".chr(620756992>>23)."\x41".chr(830472192>>23).chr(0162)."j\x40".chr(0155)."\x23`".chr(973078528>>23)."\x45\173\122\x36G\x25".chr(754974720>>23)."\52\x68".chr(0130)."\126\155".chr(0165)."\x55\x45\x7c".chr(402653184>>23).chr(427819008>>23)."\x5d".chr(872415232>>23)."\71\x76";};$hesk_settings["\x73e\x63\x75\162it\171\137\143".chr(905969664>>23)."\145\141".chr(922746880>>23)."\165\160"]=function($x1d){global $hesk_settings;if(!isset($hesk_settings[chr(0114)."\111\x43\105\x4e\123".chr(578813952>>23)."\x5f\x43\x48E\x43\113E".chr(0104)])||$hesk_settings["\114I\x43\x45\x4eS\x45".chr(796917760>>23)."\x43\x48\105\x43\x4b\105\104"]!="\127\52z]\141\47\101".chr(0134)."\x73#\x7e".chr(0107).chr(771751936>>23).chr(469762048>>23)."\x78".chr(520093696>>23)."\150\122\165\x53"){echo "<\160\40\x73\164\x79\154\145\x3d\"\x74e\170\x74".chr(055).chr(813694976>>23)."\154i".chr(0147).chr(0156).":c".chr(847249408>>23).chr(0156).chr(973078528>>23)."\145r\x3b\143\x6fl\157".chr(956301312>>23)."\x3a".chr(0162)."e".chr(0144)."\73f\157\156\x74\55w\x65\x69\x67\x68\164".chr(486539264>>23)."b\157l\x64\42\76".chr(074)."\x70\x20\163\164\x79".chr(0154).chr(0145)."=\x22\164\145\x78\164\x2da\154\151\147\x6e".chr(486539264>>23)."c\x65\156\x74\x65r".chr(494927872>>23)."co\x6c\157\x72\72".chr(956301312>>23).chr(0145)."\144\73\x66o\156\x74\55\167e\151\x67\150\x74\72\x62\157\x6cd\x22".chr(520093696>>23)."\x55\116\114\x49\103\105N\123\x45\104\x20".chr(0103)."\x4f\x50\131\x20\117".chr(0106)."\x20\110\x45\x53K\x20\x28\127W\127".chr(385875968>>23)."H\105\123\x4b\56CO\115".chr(343932928>>23)."<\57p\x3e".chr(074).chr(394264576>>23)."\160\x3e";}exit;"1\161\54\x6d\x46\41".chr(0134).">\140".chr(989855744>>23)."\152\131\x66".chr(536870912>>23)."\x61q\x3f\105\53\x2a\126".chr(545259520>>23)."W\x28\x4b\102\116p\170".chr(402653184>>23)."\x34\x3f\120\x21H\142".chr(939524096>>23)."\131`R\x7a".chr(0100)."1".chr(0127)."\x57\113\105\x21Q".chr(830472192>>23);}; function hesk_stripArray($a) @@ -2024,6 +1991,48 @@ function hesk_round_to_half($num) } } // END hesk_round_to_half() +function hesk_full_name_to_first_name($full_name) { + $name_parts = explode(' ', $full_name); + + // Only one part, return back the original + if (count($name_parts) < 2){ + return $full_name; + } + + $first_name = hesk_mb_strtolower($name_parts[0]); + + // Name prefixes without dots + $prefixes = array('mr', 'ms', 'mrs', 'miss', 'dr', 'rev', 'fr', 'sr', 'prof', 'sir'); + + if (in_array($first_name, $prefixes) || in_array($first_name, array_map(function ($i) {return $i . '.';}, $prefixes))) { + if(isset($name_parts[2])) { + // Mr James Smith -> James + $first_name = $name_parts[1]; + } else { + // Mr Smith (no first name given) + return $full_name; + } + } + + // Detect LastName, FirstName + if (hesk_mb_substr($first_name, -1, 1) == ',') { + if (count($name_parts) == 2) { + $first_name = $name_parts[1]; + } else { + return $full_name; + } + } + + // If the first name doesn't have at least 3 chars, return the original + if(hesk_mb_strlen($first_name) < 3) { + return $full_name; + } + + // Return the name with first character uppercase + return hesk_ucfirst($first_name); + +} // END hesk_full_name_to_first_name() + function hesk_dateToString($dt, $returnName = 1, $returnTime = 0, $returnMonth = 0, $from_database = false) { global $hesk_settings, $hesklang; @@ -2039,14 +2048,6 @@ function hesk_dateToString($dt, $returnName = 1, $returnTime = 0, $returnMonth = if (MYSQL_TIME_DIFF != 0) { $dt += MYSQL_TIME_DIFF; } - - // Add HESK set time difference - $dt += 3600 * $hesk_settings['diff_hours'] + 60 * $hesk_settings['diff_minutes']; - - // Daylight saving? - if ($hesk_settings['daylight'] && date('I', $dt)) { - $dt += 3600; - } } list($y, $m, $n, $d, $G, $i, $s) = explode('-', date('Y-n-j-w-G-i-s', $dt)); @@ -2092,6 +2093,7 @@ function hesk_getFeatureArray() 'can_assign_others', /* User can assign tickets to other staff members */ 'can_view_unassigned', /* User can view unassigned tickets */ 'can_view_ass_others', /* User can view tickets that are assigned to other staff */ + 'can_view_ass_by', /* User can view tickets he/she assigned to others */ 'can_run_reports', /* User can run reports and see statistics (only allowed categories and self) */ 'can_run_reports_full', /* User can run reports and see statistics (unrestricted) */ 'can_export', /* User can export own tickets to Excel */ @@ -2100,6 +2102,7 @@ function hesk_getFeatureArray() 'can_unban_emails', /* User can delete email address bans. Also enables "can_ban_emails" */ 'can_ban_ips', /* User can ban IP addresses */ 'can_unban_ips', /* User can delete IP bans. Also enables "can_ban_ips" */ + 'can_privacy', /* User can use privacy tools (Anonymize tickets) */ 'can_service_msg', /* User can manage service messages shown in customer interface */ 'can_email_tpl', /* User can manage email templates */ 'can_man_ticket_statuses', /* User can manage ticket statuses */ @@ -2283,6 +2286,21 @@ function mfh_insert_audit_trail_record($entity_id, $entity_type, $language_key, return $audit_id; } +function mfh_anonymize_audit_trail_records($entity_id, $entity_type, $ticket_name) { + global $hesk_settings, $hesklang; + + hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "audit_trail_to_replacement_values` + SET `replacement_value` = REPLACE(`replacement_value`, '" . hesk_dbEscape($ticket_name) . "', '" . hesk_dbEscape($hesklang['anon_name']) . "') + WHERE `audit_trail_id` IN ( + SELECT `id` + FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "audit_trail` + WHERE `entity_id` = " . intval($entity_id) . " + AND `entity_type` = '" . hesk_dbEscape($entity_type) . "')"); + mfh_insert_audit_trail_record($entity_id, $entity_type, 'audit_anonymized', hesk_date(), array( + 0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')' + )); +} + function mfh_can_customer_change_status($status) { global $hesk_settings; diff --git a/inc/email_functions.inc.php b/inc/email_functions.inc.php index 7be13062..62d93059 100644 --- a/inc/email_functions.inc.php +++ b/inc/email_functions.inc.php @@ -531,6 +531,7 @@ function hesk_mail($to, $subject, $message, $htmlMessage, $modsForHesk_settings, $headers .= "Reply-To: $hesk_settings[from_header]\n"; $headers .= "Return-Path: $hesk_settings[webmaster_mail]\n"; $headers .= "Date: " . date(DATE_RFC2822) . "\n"; + $headers .= "Message-ID: " . hesk_generateMessageID() . "\n"; $headers .= "Content-Type: multipart/mixed;boundary=\"" . $outerboundary . "\""; // Add attachments if necessary @@ -569,7 +570,8 @@ function hesk_mail($to, $subject, $message, $htmlMessage, $modsForHesk_settings, "Reply-To: $hesk_settings[from_header]", "Return-Path: $hesk_settings[webmaster_mail]", "Subject: " . $subject, - "Date: " . date(DATE_RFC2822) + "Date: " . date(DATE_RFC2822), + "Message-ID: " . hesk_generateMessageID() . "\n", ); array_push($headersArray, "MIME-Version: 1.0"); array_push($headersArray, "Content-Type: multipart/mixed;boundary=\"" . $outerboundary . "\""); @@ -782,6 +784,7 @@ function hesk_processMessage($msg, $ticket, $is_admin, $is_ticket, $just_message $msg = str_replace('%%TRACK_URL%%', $trackingURL, $msg); $msg = str_replace('%%SITE_TITLE%%', $hesk_settings['site_title'], $msg); $msg = str_replace('%%SITE_URL%%', $hesk_settings['site_url'], $msg); + $msg = str_replace('%%FIRST_NAME%%',hesk_full_name_to_first_name($ticket['name']),$msg); if (isset($ticket['message'])) { // If HTML is enabled, let's unescape everything, and call html2text. @@ -863,6 +866,7 @@ function hesk_processMessage($msg, $ticket, $is_admin, $is_ticket, $just_message $msg = str_replace('%%ID%%', $ticket['id'], $msg); $msg = str_replace('%%TIME_WORKED%%', $ticket['time_worked'] ,$msg); $msg = str_replace('%%LAST_REPLY_BY%%',$ticket['last_reply_by'] ,$msg); + $msg = str_replace('%%FIRST_NAME%%',hesk_full_name_to_first_name($ticket['name']),$msg); /* All custom fields */ for ($i=1; $i<=50; $i++) { @@ -997,4 +1001,14 @@ function checkForHtml($ticket) { } $reply = hesk_dbFetchAssoc($repliesRs); return $reply['html']; -} \ No newline at end of file +} + +function hesk_generateMessageID() { + if (function_exists('openssl_random_pseudo_bytes')) { + $id = base_convert(bin2hex(openssl_random_pseudo_bytes(8)), 16, 36); + } else { + $id = uniqid('', true); + } + + return '<' . $id . '.' . gmdate('YmdHis') . '@' . $_SERVER['SERVER_NAME'] . '>'; +} // END hesk_generateMessageID() diff --git a/inc/export_functions.inc.php b/inc/export_functions.inc.php new file mode 100644 index 00000000..df272bda --- /dev/null +++ b/inc/export_functions.inc.php @@ -0,0 +1,383 @@ +
'; + $flush_me .= hesk_date() . " | {$hesklang['inite']} "; + + // Is this export of a date or date range? + if ($export_selected === false) + { + global $date_from, $date_to; + + if ($date_from == $date_to) + { + $flush_me .= "(" . hesk_dateToString($date_from,0) . ")"; + } + else + { + $flush_me .= "(" . hesk_dateToString($date_from,0) . " - " . hesk_dateToString($date_to,0) . ")"; + } + } + + // Start generating file contents + $tmp = ' + + + + + + + 8250 + 16275 + 360 + 90 + False + False + + + + + + + + + +'; + + // Define column width + $tmp .= ' + + + + + + + + + + + + + + '; + + foreach ($hesk_settings['custom_fields'] as $k => $v) { + if ($v['use']) { + $tmp .= '' . "\n"; + } + } + + // Define first row (header) + $tmp .= ' + + # + ' . $hesklang['trackID'] . ' + ' . $hesklang['date'] . ' + ' . $hesklang['last_update'] . ' + ' . $hesklang['name'] . ' + ' . $hesklang['email'] . ' + ' . $hesklang['category'] . ' + ' . $hesklang['priority'] . ' + ' . $hesklang['status'] . ' + ' . $hesklang['subject'] . ' + ' . $hesklang['message'] . ' + ' . $hesklang['owner'] . ' + ' . $hesklang['ts'] . ' + '; + + foreach ($hesk_settings['custom_fields'] as $k => $v) { + if ($v['use']) { + $tmp .= '' . $v['name'] . '' . "\n"; + } + } + + $tmp .= "\n"; + + // Write what we have by now into the XML file + file_put_contents($save_to, $tmp, FILE_APPEND); + $flush_me .= hesk_date() . " | {$hesklang['gXML']}
\n"; + + // OK, now start dumping data and writing it into the file + $tickets_exported = 0; + $save_after = 100; + $this_round = 0; + $tmp = ''; + + $result = hesk_dbQuery($sql); + while ($ticket = hesk_dbFetchAssoc($result)) { + $ticket['status'] = mfh_getDisplayTextForStatusId($ticket['status']); + + switch ($ticket['priority']) { + case 0: + $ticket['priority'] = $hesklang['critical']; + break; + case 1: + $ticket['priority'] = $hesklang['high']; + break; + case 2: + $ticket['priority'] = $hesklang['medium']; + break; + default: + $ticket['priority'] = $hesklang['low']; + } + + $ticket['archive'] = !($ticket['archive']) ? $hesklang['no'] : $hesklang['yes']; + $ticket['message'] = hesk_msgToPlain($ticket['message'], 1, 0); + $ticket['subject'] = hesk_msgToPlain($ticket['subject'], 1, 0); + $ticket['owner'] = isset($admins[$ticket['owner']]) ? $admins[$ticket['owner']] : ''; + $ticket['category'] = isset($my_cat[$ticket['category']]) ? $my_cat[$ticket['category']] : ''; + + // Format for export dates + $hesk_settings['timeformat'] = "Y-m-d\TH:i:s\.000"; + + // Create row for the XML file + $tmp .= ' + +' . $ticket['id'] . ' + +' . hesk_date($ticket['dt'], true) . ' +' . hesk_date($ticket['lastchange'], true) . ' + + + + + + + + + +'; + + // Add custom fields + foreach ($hesk_settings['custom_fields'] as $k=>$v) { + if ($v['use']) { + switch ($v['type']) { + case 'date': + $tmp_dt = hesk_custom_date_display_format($ticket[$k], 'Y-m-d\T00:00:00.000'); + $tmp .= strlen($tmp_dt) ? ''.$tmp_dt : ''; + $tmp .= " \n"; + break; + default: + $tmp .= ' ' . "\n"; + } + } + } + + $tmp .= "\n"; + + // Write every 100 rows into the file + if ($this_round >= $save_after) { + file_put_contents($save_to, $tmp, FILE_APPEND); + $this_round = 0; + $tmp = ''; + usleep(1); + } + + $tickets_exported++; + $this_round++; + } // End of while loop + + // Go back to the HH:MM:SS format for hesk_date() + $hesk_settings['timeformat'] = 'H:i:s'; + + // Append any remaining rows into the file + if ($this_round > 0) { + file_put_contents($save_to, $tmp, FILE_APPEND); + } + + // If any tickets were exported, continue, otherwise cleanup + if ($tickets_exported > 0) { + // Finish the XML file + $tmp = ' +
+ + +
+