diff --git a/.gitignore b/.gitignore index f3ca7bf6..fd26d961 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ admin/admin_suggest_articles.php admin/archive.php admin/assign_owner.php -admin/delete_tickets.php admin/generate_spam_question.php admin/move_category.php admin/options.php @@ -149,7 +148,6 @@ img/tag_off.png img/unlock.png img/vertical.jpg img/view.png -inc/admin_functions.inc.php inc/assignment_search.inc.php inc/attachments.inc.php inc/calendar/img/cal.gif @@ -164,7 +162,6 @@ inc/calendar/tcal.js inc/calendar/tcal.php inc/database.inc.php inc/database_mysqli.inc.php -inc/email_functions.inc.php inc/footer.inc.php inc/index.htm inc/mail/email_parser.php diff --git a/admin/change_status.php b/admin/change_status.php index 1b594d74..890e61c2 100644 --- a/admin/change_status.php +++ b/admin/change_status.php @@ -74,6 +74,13 @@ if ( ! isset($status_options[$status])) $locked = 0; +// 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); + $statusRow = hesk_dbFetchAssoc(hesk_dbQuery("SELECT `ID`, `IsClosed` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses` WHERE ID = ".$status)); if ($statusRow['IsClosed']) // Closed { @@ -84,6 +91,10 @@ if ($statusRow['IsClosed']) // Closed { $locked = 1; } + + // Notify customer + require(HESK_PATH . 'inc/email_functions.inc.php'); + hesk_notifyCustomer('ticket_closed'); } elseif ($statusRow['ID'] != 0) //Ticket is still open, but not new { @@ -96,6 +107,16 @@ else // Ticket is marked as "NEW" $revision = sprintf($hesklang['thist4'],hesk_date(),$_SESSION['name'].' ('.$_SESSION['user'].')'); } +//-- Notify staff after ticket re-open? +$currentStatusRS = hesk_dbQuery('SELECT * FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'statuses` WHERE `ID` = '.$ticket['status']); +$currentStatus = hesk_dbFetchAssoc($currentStatusRS); + +if (intval($currentStatus['IsClosed']) == 1 && $statusRow['IsClosed'] == 0 && $ticket['owner'] != $_SESSION['id']) { + $ticket['name'] = $_SESSION['name']; + require(HESK_PATH . 'inc/email_functions.inc.php'); + hesk_notifyAssignedStaff(false, 'ticket_reopen_assigned'); +} + hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` SET `status`='{$status}', `locked`='{$locked}', `history`=CONCAT(`history`,'".hesk_dbEscape($revision)."') WHERE `trackid`='".hesk_dbEscape($trackingID)."' LIMIT 1"); if (hesk_dbAffectedRows() != 1) diff --git a/admin/delete_tickets.php b/admin/delete_tickets.php new file mode 100644 index 00000000..c68b10d6 --- /dev/null +++ b/admin/delete_tickets.php @@ -0,0 +1,292 @@ + diff --git a/inc/admin_functions.inc.php b/inc/admin_functions.inc.php new file mode 100644 index 00000000..662ffec1 --- /dev/null +++ b/inc/admin_functions.inc.php @@ -0,0 +1,694 @@ + 59) + { + $m = floor($s / 60) + $m; + $s = intval($s % 60); + } + + /* Convert minutes to hours if 60 or more minutes */ + if ($m > 59) + { + $h = floor($m / 60) + $h; + $m = intval($m % 60); + } + + /* MySQL accepts max time value of 838:59:59 */ + if ($h > 838) + { + return '838:59:59'; + } + + /* That's it, let's send out formatted time string */ + return str_pad($h, 2, "0", STR_PAD_LEFT) . ':' . str_pad($m, 2, "0", STR_PAD_LEFT) . ':' . str_pad($s, 2, "0", STR_PAD_LEFT); + +} // END hesk_getTime(); + + +function hesk_mergeTickets($merge_these, $merge_into) +{ + global $hesk_settings, $hesklang, $hesk_db_link; + + /* Target ticket must not be in the "merge these" list */ + if ( in_array($merge_into, $merge_these) ) + { + $merge_these = array_diff($merge_these, array( $merge_into ) ); + } + + /* At least 1 ticket needs to be merged with target ticket */ + if ( count($merge_these) < 1 ) + { + $_SESSION['error'] = $hesklang['merr1']; + return false; + } + + /* Make sure target ticket exists */ + $res = hesk_dbQuery("SELECT `id`,`trackid`,`category` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `id`='".intval($merge_into)."' LIMIT 1"); + if (hesk_dbNumRows($res) != 1) + { + $_SESSION['error'] = $hesklang['merr2']; + return false; + } + $ticket = hesk_dbFetchAssoc($res); + + /* Make sure user has access to ticket category */ + if ( ! hesk_okCategory($ticket['category'], 0) ) + { + $_SESSION['error'] = $hesklang['merr3']; + return false; + } + + /* Set some variables for later */ + $merge['attachments'] = ''; + $merge['replies'] = array(); + $merge['notes'] = array(); + $sec_worked = 0; + $history = ''; + $merged = ''; + + /* Get messages, replies, notes and attachments of tickets that will be merged */ + foreach ($merge_these as $this_id) + { + /* Validate ID */ + if ( is_array($this_id) ) + { + continue; + } + $this_id = intval($this_id) or hesk_error($hesklang['id_not_valid']); + + /* Get required ticket information */ + $res = hesk_dbQuery("SELECT `id`,`trackid`,`category`,`name`,`message`,`dt`,`time_worked`,`attachments` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `id`='".intval($this_id)."' LIMIT 1"); + if (hesk_dbNumRows($res) != 1) + { + continue; + } + $row = hesk_dbFetchAssoc($res); + + /* Has this user access to the ticket category? */ + if ( ! hesk_okCategory($row['category'], 0) ) + { + continue; + } + + /* Insert ticket message as a new reply to target ticket */ + hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` (`replyto`,`name`,`message`,`dt`,`attachments`) VALUES ('".intval($ticket['id'])."','".hesk_dbEscape($row['name'])."','".hesk_dbEscape($row['message'])."','".hesk_dbEscape($row['dt'])."','".hesk_dbEscape($row['attachments'])."')"); + + /* Update attachments */ + hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."attachments` SET `ticket_id`='".hesk_dbEscape($ticket['trackid'])."' WHERE `ticket_id`='".hesk_dbEscape($row['trackid'])."'"); + + /* Get old ticket replies and insert them as new replies */ + $res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` WHERE `replyto`='".intval($row['id'])."'"); + while ( $reply = hesk_dbFetchAssoc($res) ) + { + hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` (`replyto`,`name`,`message`,`dt`,`attachments`,`staffid`,`rating`,`read`) VALUES ('".intval($ticket['id'])."','".hesk_dbEscape($reply['name'])."','".hesk_dbEscape($reply['message'])."','".hesk_dbEscape($reply['dt'])."','".hesk_dbEscape($reply['attachments'])."','".intval($reply['staffid'])."','".intval($reply['rating'])."','".intval($reply['read'])."')"); + } + + /* Delete replies to the old ticket */ + hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."replies` WHERE `replyto`='".intval($row['id'])."'"); + + /* Get old ticket notes and insert them as new notes */ + $res = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."notes` WHERE `ticket`='".intval($row['id'])."'"); + while ( $note = hesk_dbFetchAssoc($res) ) + { + hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."notes` (`ticket`,`who`,`dt`,`message`) VALUES ('".intval($ticket['id'])."','".intval($note['who'])."','".hesk_dbEscape($note['dt'])."','".hesk_dbEscape($note['message'])."')"); + } + + /* Delete replies to the old ticket */ + hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."notes` WHERE `ticket`='".intval($row['id'])."'"); + + /* Delete old ticket */ + 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'].')'); + + /* Add old ticket ID to target ticket "merged" field */ + $merged .= '#' . $row['trackid']; + + /* Convert old ticket "time worked" to seconds and add to $sec_worked variable */ + list ($hr, $min, $sec) = explode(':', $row['time_worked']); + $sec_worked += (((int)$hr) * 3600) + (((int)$min) * 60) + ((int)$sec); + } + + /* Convert seconds to HHH:MM:SS */ + $sec_worked = hesk_getTime('0:'.$sec_worked); + + /* Update history (log) and merged IDs of target ticket */ + hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` SET `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)."' LIMIT 1"); + + return true; + +} // END hesk_mergeTickets() + + +function hesk_updateStaffDefaults() +{ + global $hesk_settings, $hesklang; + + // Demo mode + if ( defined('HESK_DEMO') ) + { + return true; + } + // Remove the part that forces saving as default - we don't need it every time + $default_list = str_replace('&def=1','',$_SERVER['QUERY_STRING']); + + // Update database + $res = hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."users` SET `default_list`='".hesk_dbEscape($default_list)."' WHERE `id`='".intval($_SESSION['id'])."'"); + + // Update session values so the changes take effect immediately + $_SESSION['default_list'] = $default_list; + + return true; + +} // END hesk_updateStaffDefaults() + + +function hesk_makeJsString($in) +{ + return addslashes(preg_replace("/\s+/",' ',$in)); +} // END hesk_makeJsString() + + +function hesk_checkNewMail() +{ + global $hesk_settings, $hesklang; + + $res = hesk_dbQuery("SELECT COUNT(*) FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."mail` WHERE `to`='".intval($_SESSION['id'])."' AND `read`='0' AND `deletedby`!='".intval($_SESSION['id'])."' "); + $num = hesk_dbResult($res,0,0); + + return $num; +} // END hesk_checkNewMail() + + +function hesk_dateToString($dt, $returnName=1, $returnTime=0, $returnMonth=0, $from_database=false) +{ + global $hesk_settings, $hesklang; + + $dt = strtotime($dt); + + // Adjust MySQL time if different from PHP time + if ($from_database) + { + if ( ! defined('MYSQL_TIME_DIFF') ) + { + define('MYSQL_TIME_DIFF', time()-hesk_dbTime() ); + } + + 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) ); + + $m = $hesklang['m'.$m]; + $d = $hesklang['d'.$d]; + + if ($returnName) + { + return "$d, $m $n, $y"; + } + + if ($returnTime) + { + return "$d, $m $n, $y $G:$i:$s"; + } + + if ($returnMonth) + { + return "$m $y"; + } + + return "$m $n, $y"; +} // End hesk_dateToString() + + +function hesk_getCategoriesArray($kb = 0) { + global $hesk_settings, $hesklang, $hesk_db_link; + + $categories = array(); + if ($kb) + { + $result = hesk_dbQuery('SELECT `id`, `name` FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'kb_categories` ORDER BY `cat_order` ASC'); + } + else + { + $result = hesk_dbQuery('SELECT `id`, `name` FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'categories` ORDER BY `cat_order` ASC'); + } + + while ($row=hesk_dbFetchAssoc($result)) + { + $categories[$row['id']] = $row['name']; + } + + return $categories; +} // END hesk_getCategoriesArray() + + +function hesk_getHTML($in) +{ + global $hesk_settings, $hesklang; + + $replace_from = array("\t","","$","<%","%>"); + $replace_to = array("","<?","?>","\$","<%","%>"); + + $in = trim($in); + $in = str_replace($replace_from,$replace_to,$in); + $in = preg_replace('/\(.*)\<\/script\>/Uis',"",$in); + $in = preg_replace('/\<\!\-\-(.*)\-\-\>/Uis',"",$in); + + if (HESK_SLASH === true) + { + $in = addslashes($in); + } + $in = str_replace('\"','"',$in); + + return $in; +} // END hesk_getHTML() + + +function hesk_autoLogin($noredirect=0) +{ + global $hesk_settings, $hesklang, $hesk_db_link; + + if (!$hesk_settings['autologin']) + { + return false; + } + + $user = hesk_htmlspecialchars( hesk_COOKIE('hesk_username') ); + $hash = hesk_htmlspecialchars( hesk_COOKIE('hesk_p') ); + define('HESK_USER', $user); + + if (empty($user) || empty($hash)) + { + return false; + } + + /* Login cookies exist, now lets limit brute force attempts */ + hesk_limitBfAttempts(); + + /* Check username */ + $result = hesk_dbQuery('SELECT * FROM `'.$hesk_settings['db_pfix']."users` WHERE `user` = '".hesk_dbEscape($user)."' LIMIT 1"); + if (hesk_dbNumRows($result) != 1) + { + setcookie('hesk_username', ''); + setcookie('hesk_p', ''); + header('Location: index.php?a=login¬ice=1'); + exit(); + } + + $res=hesk_dbFetchAssoc($result); + foreach ($res as $k=>$v) + { + $_SESSION[$k]=$v; + } + + /* Check password */ + if ($hash != hesk_Pass2Hash($_SESSION['pass'] . strtolower($user) . $_SESSION['pass']) ) + { + setcookie('hesk_username', ''); + setcookie('hesk_p', ''); + header('Location: index.php?a=login¬ice=1'); + exit(); + } + + /* Check if default password */ + if ($_SESSION['pass'] == '499d74967b28a841c98bb4baaabaad699ff3c079') + { + hesk_process_messages($hesklang['chdp'],'NOREDIRECT','NOTICE'); + } + + unset($_SESSION['pass']); + + /* Login successful, clean brute force attempts */ + hesk_cleanBfAttempts(); + + /* Regenerate session ID (security) */ + hesk_session_regenerate_id(); + + /* Get allowed categories */ + if (empty($_SESSION['isadmin'])) + { + $_SESSION['categories']=explode(',',$_SESSION['categories']); + } + + /* Renew cookies */ + setcookie('hesk_username', "$user", strtotime('+1 year')); + setcookie('hesk_p', "$hash", strtotime('+1 year')); + + /* Close any old tickets here so Cron jobs aren't necessary */ + if ($hesk_settings['autoclose']) + { + $dt = date('Y-m-d H:i:s',time() - $hesk_settings['autoclose']*86400); + + $waitingForCustomerRS = hesk_dbQuery("SELECT `ID` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses` WHERE `IsDefaultStaffReplyStatus` = 1"); + $waitingForCustomerStatus = hesk_dbFetchAssoc($waitingForCustomerRS); + + $result = hesk_dbQuery("SELECT * FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."tickets` WHERE `status` = ".$waitingForCustomerStatus['ID']." AND `lastchange` <= '".hesk_dbEscape($dt)."' "); + if (hesk_dbNumRows($result) > 0) + { + require(HESK_PATH . 'inc/email_functions.inc.php'); + global $ticket; + while ($ticket = hesk_dbFetchAssoc($result)) { + hesk_notifyCustomer('ticket_closed'); + } + + $revision = sprintf($hesklang['thist3'],hesk_date(),$hesklang['auto']); + + $closedStatusRS = hesk_dbQuery("SELECT `ID` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses` WHERE `IsStaffClosedOption` = 1"); + $closedStatus = hesk_dbFetchAssoc($closedStatusRS); + + $sql = "UPDATE `".$hesk_settings['db_pfix']."tickets` SET `status`=".$closedStatus['ID'].", `history`=CONCAT(`history`,'".hesk_dbEscape($revision)."') WHERE `status` = ".$waitingForCustomerStatus['ID']." AND `lastchange` <= '".hesk_dbEscape($dt)."' "; + hesk_dbQuery($sql); + } + } + + /* If session expired while a HESK page is open just continue using it, don't redirect */ + if ($noredirect) + { + return true; + } + + /* Redirect to the destination page */ + if ( hesk_isREQUEST('goto') && $url=hesk_REQUEST('goto') ) + { + $url = str_replace('&','&',$url); + header('Location: '.$url); + } + else + { + header('Location: admin_main.php'); + } + exit(); +} // END hesk_autoLogin() + + +function hesk_isLoggedIn() +{ + global $hesk_settings; + + $referer = hesk_input($_SERVER['REQUEST_URI']); + $referer = str_replace('&','&',$referer); + + if (empty($_SESSION['id'])) + { + if ($hesk_settings['autologin'] && hesk_autoLogin(1) ) + { + // Users online + if ($hesk_settings['online']) + { + require(HESK_PATH . 'inc/users_online.inc.php'); + hesk_initOnline($_SESSION['id']); + } + + return true; + } + + // Some pages cannot be redirected to + $modify_redirect = array( + 'admin_reply_ticket.php' => 'admin_main.php', + 'admin_settings_save.php' => 'admin_settings.php', + 'delete_tickets.php' => 'admin_main.php', + 'move_category.php' => 'admin_main.php', + 'priority.php' => 'admin_main.php', + ); + + foreach ($modify_redirect as $from => $to) + { + if ( strpos($referer,$from) !== false ) + { + $referer = $to; + } + } + + $url = 'index.php?a=login¬ice=1&goto='.urlencode($referer); + header('Location: '.$url); + exit(); + } + else + { + hesk_session_regenerate_id(); + + // Need to update permissions? + if ( empty($_SESSION['isadmin']) ) + { + $res = hesk_dbQuery("SELECT `isadmin`, `categories`, `heskprivileges` FROM `".$hesk_settings['db_pfix']."users` WHERE `id` = '".intval($_SESSION['id'])."' LIMIT 1"); + if (hesk_dbNumRows($res) == 1) + { + $me = hesk_dbFetchAssoc($res); + foreach ($me as $k => $v) + { + $_SESSION[$k]=$v; + } + + // Get allowed categories + if (empty($_SESSION['isadmin']) ) + { + $_SESSION['categories']=explode(',',$_SESSION['categories']); + } + } + else + { + hesk_session_stop(); + $url = 'index.php?a=login¬ice=1&goto='.urlencode($referer); + header('Location: '.$url); + exit(); + } + } + + // Users online + if ($hesk_settings['online']) + { + require(HESK_PATH . 'inc/users_online.inc.php'); + hesk_initOnline($_SESSION['id']); + } + + return true; + } + +} // END hesk_isLoggedIn() + + +function hesk_Pass2Hash($plaintext) { + $majorsalt = ''; + $len = strlen($plaintext); + for ($i=0;$i<$len;$i++) + { + $majorsalt .= sha1(substr($plaintext,$i,1)); + } + $corehash = sha1($majorsalt); + return $corehash; +} // END hesk_Pass2Hash() + + +function hesk_formatDate($dt) +{ + $dt=hesk_date($dt); + $dt=str_replace(' ','
',$dt); + return $dt; +} // End hesk_formatDate() + + +function hesk_jsString($str) +{ + $str = str_replace( array('\'','
') , array('\\\'','') ,$str); + $from = array("/\r\n|\n|\r/", '/\([^\<]*)\<\/a\>/i', '/\([^\<]*)\<\/a\>/i'); + $to = array("\\r\\n' + \r\n'", "$1", "$1"); + return preg_replace($from,$to,$str); +} // END hesk_jsString() + + +function hesk_myCategories($what='category') +{ + if ( ! empty($_SESSION['isadmin']) ) + { + return '1'; + } + else + { + return " `".hesk_dbEscape($what)."` IN ('" . implode("','", array_map('intval', $_SESSION['categories']) ) . "')"; + } +} // END hesk_myCategories() + + +function hesk_okCategory($cat,$error=1,$user_isadmin=false,$user_cat=false) +{ + global $hesklang; + + /* Checking for current user or someone else? */ + if ($user_isadmin === false) + { + $user_isadmin = $_SESSION['isadmin']; + } + + if ($user_cat === false) + { + $user_cat = $_SESSION['categories']; + } + + /* Is admin? */ + if ($user_isadmin) + { + return true; + } + /* Staff with access? */ + elseif (in_array($cat,$user_cat)) + { + return true; + } + /* No access */ + else + { + if ($error) + { + hesk_error($hesklang['not_authorized_tickets']); + } + else + { + return false; + } + } + +} // END hesk_okCategory() + + +function hesk_checkPermission($feature,$showerror=1) { + global $hesklang; + + + /* Check if this is for managing settings */ + if ($feature == 'can_manage_settings') + { + if ($_SESSION['can_manage_settings']) { + return true; + } else { + if ($showerror) { + hesk_error($hesklang['no_permission'].'

 

'.$hesklang['click_login'].''); + } else { + return false; + } + } + } + + /* Admins have full access to all features, besides possibly settings */ + if ($_SESSION['isadmin']) + { + return true; + } + + /* Check other staff for permissions */ + if (strpos($_SESSION['heskprivileges'], $feature) === false) + { + if ($showerror) + { + hesk_error($hesklang['no_permission'].'

 

'.$hesklang['click_login'].''); + } + else + { + return false; + } + } + else + { + return true; + } + +} // END hesk_checkPermission() diff --git a/inc/email_functions.inc.php b/inc/email_functions.inc.php new file mode 100644 index 00000000..d8dc60ac --- /dev/null +++ b/inc/email_functions.inc.php @@ -0,0 +1,617 @@ + $myuser['email'], 'language' => $myuser['language']); + continue; + } + + /* Not admin, is he/she allowed this category? */ + $myuser['categories']=explode(',',$myuser['categories']); + if (in_array($ticket['category'],$myuser['categories'])) + { + $admins[] = array('email' => $myuser['email'], 'language' => $myuser['language']); + continue; + } + } + + if (count($admins) > 0) + { + /* Make sure each user gets email in his/her preferred language */ + $current_language = 'NONE'; + $recipients = array(); + + /* Loop through staff */ + foreach ($admins as $admin) + { + /* If admin language is NULL force default HESK language */ + if ( ! $admin['language'] || ! isset($hesk_settings['languages'][$admin['language']]) ) + { + $admin['language'] = HESK_DEFAULT_LANGUAGE; + } + + /* Generate message or add email to the list of recepients */ + if ($admin['language'] == $current_language) + { + /* We already have the message, just add email to the recipients list */ + $recipients[] = $admin['email']; + } + else + { + /* Send email messages in previous languages (if required) */ + if ($current_language != 'NONE') + { + /* Send e-mail to staff */ + hesk_mail(implode(',',$recipients), $subject, $message ); + + /* Reset list of email addresses */ + $recipients = array(); + } + + /* Set new language */ + hesk_setLanguage($admin['language']); + + /* Format staff email subject and message for this language */ + $subject = hesk_getEmailSubject($email_template,$ticket); + $message = hesk_getEmailMessage($email_template,$ticket,$is_ticket); + + /* Add email to the recipients list */ + $recipients[] = $admin['email']; + + /* Remember the last processed language */ + $current_language = $admin['language']; + } + } + + /* Send email messages to the remaining staff */ + hesk_mail(implode(',',$recipients), $subject, $message ); + + /* Reset language to original one */ + hesk_resetLanguage(); + } + + return true; + +} // END hesk_notifyStaff() + + +function hesk_validEmails() +{ + global $hesklang; + + return array( + + /*** Emails sent to CLIENT ***/ + + // --> Send reminder about existing tickets + 'forgot_ticket_id' => $hesklang['forgot_ticket_id'], + + // --> Staff replied to a ticket + 'new_reply_by_staff' => $hesklang['new_reply_by_staff'], + + // --> New ticket submitted + 'new_ticket' => $hesklang['ticket_received'], + + // --> Ticket closed + 'ticket_closed' => $hesklang['ticket_closed'], + + + /*** Emails sent to STAFF ***/ + + // --> Ticket moved to a new category + 'category_moved' => $hesklang['category_moved'], + + // --> Client replied to a ticket + 'new_reply_by_customer' => $hesklang['new_reply_by_customer'], + + // --> New ticket submitted + 'new_ticket_staff' => $hesklang['new_ticket_staff'], + + // --> New ticket assigned to staff + 'ticket_assigned_to_you'=> $hesklang['ticket_assigned_to_you'], + + // --> New private message + 'new_pm' => $hesklang['new_pm'], + + // --> New note by someone to a ticket assigned to you + 'new_note' => $hesklang['new_note'], + + ); +} // END hesk_validEmails() + + +function hesk_mail($to,$subject,$message) +{ + global $hesk_settings, $hesklang; + + // Demo mode + if ( defined('HESK_DEMO') ) + { + return true; + } + + // Encode subject to UTF-8 + $subject = "=?UTF-8?B?" . base64_encode( hesk_html_entity_decode($subject) ) . "?="; + + // Setup "name " for headers + if ($hesk_settings['noreply_name']) + { + $hesk_settings['from_header'] = "=?UTF-8?B?" . base64_encode( hesk_html_entity_decode($hesk_settings['noreply_name']) ) . "?= <" . $hesk_settings['noreply_mail'] . ">"; + } + else + { + $hesk_settings['from_header'] = $hesk_settings['noreply_mail']; + } + + // Uncomment for debugging + # echo "

TO: $to
SUBJECT: $subject
MSG: $message

"; + # return true; + + // Use PHP's mail function + if ( ! $hesk_settings['smtp']) + { + // Set additional headers + $headers = "From: $hesk_settings[from_header]\n"; + $headers.= "Reply-To: $hesk_settings[from_header]\n"; + $headers.= "Return-Path: $hesk_settings[webmaster_mail]\n"; + $headers.= "Date: " . date(DATE_RFC2822) . "\n"; + $headers.= "Content-Type: text/plain; charset=" . $hesklang['ENCODING']; + + // Send using PHP mail() function + ob_start(); + mail($to,$subject,$message,$headers); + $tmp = trim(ob_get_contents()); + ob_end_clean(); + + return (strlen($tmp)) ? $tmp : true; + } + + // Use a SMTP server directly instead + $smtp = new smtp_class; + $smtp->host_name = $hesk_settings['smtp_host_name']; + $smtp->host_port = $hesk_settings['smtp_host_port']; + $smtp->timeout = $hesk_settings['smtp_timeout']; + $smtp->ssl = $hesk_settings['smtp_ssl']; + $smtp->start_tls = $hesk_settings['smtp_tls']; + $smtp->user = $hesk_settings['smtp_user']; + $smtp->password = hesk_htmlspecialchars_decode($hesk_settings['smtp_password']); + $smtp->debug = 1; + + // Start output buffering so that any errors don't break headers + ob_start(); + + // Send the e-mail using SMTP + $to_arr = explode(',',$to); + if ( ! $smtp->SendMessage($hesk_settings['noreply_mail'], $to_arr, array( + "From: $hesk_settings[from_header]", + "To: $to", + "Reply-To: $hesk_settings[from_header]", + "Return-Path: $hesk_settings[webmaster_mail]", + "Subject: " . $subject, + "Date: " . date(DATE_RFC2822), + "Content-Type: text/plain; charset=" . $hesklang['ENCODING'] + ), $message)) + { + // Suppress errors unless we are in debug mode + if ($hesk_settings['debug_mode']) + { + $error = $hesklang['cnsm'] . ' ' . $to . '

' . + $hesklang['error'] . ': ' . htmlspecialchars($smtp->error). '

' . + ''; + ob_end_clean(); + hesk_error($error); + } + else + { + $_SESSION['HESK_2ND_NOTICE'] = true; + $_SESSION['HESK_2ND_MESSAGE'] = $hesklang['esf'] . ' ' . $hesklang['contact_webmsater'] . ' ' . $hesk_settings['webmaster_mail'] . ''; + } + } + + ob_end_clean(); + + return true; + +} // END hesk_mail() + + +function hesk_getEmailSubject($eml_file, $ticket='', $is_ticket=1, $strip=0) +{ + global $hesk_settings, $hesklang; + + // Demo mode + if ( defined('HESK_DEMO') ) + { + return ''; + } + + /* Get list of valid emails */ + $valid_emails = hesk_validEmails(); + + /* Verify this is a valid email include */ + if ( ! isset($valid_emails[$eml_file])) + { + hesk_error($hesklang['inve']); + } + else + { + $msg = $valid_emails[$eml_file]; + } + + /* If not a ticket-related email return subject as is */ + if ( ! $ticket ) + { + return $msg; + } + + /* Strip slashes from the subject only if it's a new ticket */ + if ($strip) + { + $ticket['subject'] = stripslashes($ticket['subject']); + } + + /* Not a ticket, but has some info in the $ticket array */ + if ( ! $is_ticket) + { + return str_replace('%%SUBJECT%%', $ticket['subject'], $msg); + } + + /* Set category title */ + $ticket['category'] = hesk_msgToPlain(hesk_getCategoryName($ticket['category']), 1); + + /* Get priority */ + 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']; + } + + /* Set status */ + switch ($ticket['status']) + { + case 1: + $ticket['status'] = $hesklang['wait_reply']; + break; + case 2: + $ticket['status'] = $hesklang['replied']; + break; + case 3: + $ticket['status'] = $hesklang['closed']; + break; + case 4: + $ticket['status'] = $hesklang['in_progress']; + break; + case 5: + $ticket['status'] = $hesklang['on_hold']; + break; + default: + $ticket['status'] = $hesklang['open']; + } + + /* Replace all special tags */ + $msg = str_replace('%%SUBJECT%%', $ticket['subject'], $msg); + $msg = str_replace('%%TRACK_ID%%', $ticket['trackid'], $msg); + $msg = str_replace('%%CATEGORY%%', $ticket['category'], $msg); + $msg = str_replace('%%PRIORITY%%', $ticket['priority'], $msg); + $msg = str_replace('%%STATUS%%', $ticket['status'], $msg); + + return $msg; + +} // hesk_getEmailSubject() + + +function hesk_getEmailMessage($eml_file, $ticket, $is_admin=0, $is_ticket=1, $just_message=0) +{ + global $hesk_settings, $hesklang; + + // Demo mode + if ( defined('HESK_DEMO') ) + { + return ''; + } + + /* Get list of valid emails */ + $valid_emails = hesk_validEmails(); + + /* Verify this is a valid email include */ + if ( ! isset($valid_emails[$eml_file])) + { + hesk_error($hesklang['inve']); + } + + /* Get email template */ + $eml_file = 'language/' . $hesk_settings['languages'][$hesk_settings['language']]['folder'] . '/emails/' . $eml_file . '.txt'; + + if (file_exists(HESK_PATH . $eml_file)) + { + $msg = file_get_contents(HESK_PATH . $eml_file); + } + else + { + hesk_error($hesklang['emfm'].': '.$eml_file); + } + + /* Return just the message without any processing? */ + if ($just_message) + { + return $msg; + } + + // Convert any entities in site title to plain text + $hesk_settings['site_title'] = hesk_msgToPlain($hesk_settings['site_title'], 1); + + /* If it's not a ticket-related mail (like "a new PM") just process quickly */ + if ( ! $is_ticket) + { + $trackingURL = $hesk_settings['hesk_url'] . '/' . $hesk_settings['admin_dir'] . '/mail.php?a=read&id=' . intval($ticket['id']); + + $msg = str_replace('%%NAME%%', $ticket['name'] ,$msg); + $msg = str_replace('%%SUBJECT%%', $ticket['subject'] ,$msg); + $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); + + if ( isset($ticket['message']) ) + { + return str_replace('%%MESSAGE%%', $ticket['message'], $msg); + } + else + { + return $msg; + } + } + + // Is email required to view ticket (for customers only)? + $hesk_settings['e_param'] = $hesk_settings['email_view_ticket'] ? '&e=' . rawurlencode($ticket['email']) : ''; + + /* Generate the ticket URLs */ + $trackingURL = $hesk_settings['hesk_url']; + $trackingURL.= $is_admin ? '/' . $hesk_settings['admin_dir'] . '/admin_ticket.php' : '/ticket.php'; + $trackingURL.= '?track='.$ticket['trackid'].($is_admin ? '' : $hesk_settings['e_param']).'&Refresh='.rand(10000,99999); + + /* Set category title */ + $ticket['category'] = hesk_msgToPlain(hesk_getCategoryName($ticket['category']), 1); + + /* Set priority title */ + 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']; + } + + /* Get owner name */ + $ticket['owner'] = hesk_msgToPlain( hesk_getOwnerName($ticket['owner']), 1); + + /* Set status */ + switch ($ticket['status']) + { + case 1: + $ticket['status'] = $hesklang['wait_reply']; + break; + case 2: + $ticket['status'] = $hesklang['replied']; + break; + case 3: + $ticket['status'] = $hesklang['closed']; + break; + case 4: + $ticket['status'] = $hesklang['in_progress']; + break; + case 5: + $ticket['status'] = $hesklang['on_hold']; + break; + default: + $ticket['status'] = $hesklang['open']; + } + + /* Replace all special tags */ + $msg = str_replace('%%NAME%%', $ticket['name'] ,$msg); + $msg = str_replace('%%SUBJECT%%', $ticket['subject'] ,$msg); + $msg = str_replace('%%TRACK_ID%%', $ticket['trackid'] ,$msg); + $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('%%CATEGORY%%', $ticket['category'] ,$msg); + $msg = str_replace('%%PRIORITY%%', $ticket['priority'] ,$msg); + $msg = str_replace('%%OWNER%%', $ticket['owner'] ,$msg); + $msg = str_replace('%%STATUS%%', $ticket['status'] ,$msg); + $msg = str_replace('%%EMAIL%%', $ticket['email'] ,$msg); + $msg = str_replace('%%CREATED%%', $ticket['dt'] ,$msg); + $msg = str_replace('%%UPDATED%%', $ticket['lastchange'] ,$msg); + + /* All custom fields */ + foreach ($hesk_settings['custom_fields'] as $k=>$v) + { + if ($v['use']) + { + if ($v['type'] == 'checkbox') + { + $ticket[$k] = str_replace("
","\n",$ticket[$k]); + } + + $msg = str_replace('%%'.strtoupper($k).'%%',stripslashes($ticket[$k]),$msg); + } + else + { + $msg = str_replace('%%'.strtoupper($k).'%%','',$msg); + } + } + + // Is message tag in email template? + if (strpos($msg, '%%MESSAGE%%') !== false) + { + // Replace message + $msg = str_replace('%%MESSAGE%%',$ticket['message'],$msg); + + // Add direct links to any attachments at the bottom of the email message + if ($hesk_settings['attachments']['use'] && isset($ticket['attachments']) && strlen($ticket['attachments']) ) + { + $msg .= "\n\n\n" . $hesklang['fatt']; + + $att = explode(',', substr($ticket['attachments'], 0, -1)); + foreach ($att as $myatt) + { + list($att_id, $att_name) = explode('#', $myatt); + $msg .= "\n\n" . $att_name . "\n" . $hesk_settings['hesk_url'] . '/download_attachment.php?att_id='.$att_id.'&track='.$ticket['trackid'].$hesk_settings['e_param']; + } + } + + // For customer notifications: if we allow email piping/pop 3 fetching and + // stripping quoted replies add an "reply above this line" tag + if ( ! $is_admin && ($hesk_settings['email_piping'] || $hesk_settings['pop3']) && $hesk_settings['strip_quoted']) + { + $msg = $hesklang['EMAIL_HR'] . "\n\n" . $msg; + } + } + + return $msg; + +} // END hesk_getEmailMessage diff --git a/language/en/emails/ticket_closed.txt b/language/en/emails/ticket_closed.txt new file mode 100644 index 00000000..51cfb97c --- /dev/null +++ b/language/en/emails/ticket_closed.txt @@ -0,0 +1,14 @@ +Dear %%NAME%%, + +Your support ticket "%%SUBJECT%%" has been updated to a closed/resolved status. + +Ticket tracking ID: %%TRACK_ID%% + +You can view the status of your ticket here: +%%TRACK_URL%% + + +Sincerely, + +%%SITE_TITLE%% +%%SITE_URL%% \ No newline at end of file diff --git a/language/en/text.php b/language/en/text.php index 23eb6d45..14e9c087 100644 --- a/language/en/text.php +++ b/language/en/text.php @@ -21,6 +21,11 @@ $hesklang['_COLLATE']='utf8_unicode_ci'; // This is the email break line that will be used in email piping $hesklang['EMAIL_HR']='------ Reply above this line ------'; +// ADDED OR MODIFIED IN Mods For Hesk 1.6.0 +$hesklang['ticket_closed'] = '[#%%TRACK_ID%%] Ticket closed/resolved'; +$hesklang['ticket_reopen'] = '[#%%TRACK_ID%%] Ticket reopened'; +$hesklang['ticket_reopen_assigned'] = '[#%%TRACK_ID%%] Assigned ticket reopened'; + // ADDED OR MODIFIED IN NuMods 1.5.0 $hesklang['ticket_auto_refresh'] = 'Ticket Table Auto-Refresh:'; $hesklang['display_rtl'] = 'Display site right-to-left';