diff --git a/.gitignore b/.gitignore index 39da1146..7b12609a 100644 --- a/.gitignore +++ b/.gitignore @@ -269,7 +269,6 @@ attachments img/ban.png img/banned.png img/ico_tools.png -inc/recaptcha/recaptchalib_v2.php ip_whois.php language/en/emails/reset_password.txt language/en/help_files/ticket_list.html diff --git a/admin/admin_main.php b/admin/admin_main.php index bad21451..111ed08a 100644 --- a/admin/admin_main.php +++ b/admin/admin_main.php @@ -40,8 +40,10 @@ if (is_dir(HESK_PATH . 'install')) {die('Please delete the install folder /* Get all the required files and functions */ require(HESK_PATH . 'hesk_settings.inc.php'); +require(HESK_PATH . 'modsForHesk_settings.inc.php'); require(HESK_PATH . 'inc/common.inc.php'); require(HESK_PATH . 'inc/admin_functions.inc.php'); +require(HESK_PATH . 'inc/status_functions.inc.php'); hesk_load_database_functions(); hesk_session_start(); diff --git a/admin/admin_settings.php b/admin/admin_settings.php index 68a1df02..5e578079 100644 --- a/admin/admin_settings.php +++ b/admin/admin_settings.php @@ -860,7 +860,7 @@ if ( defined('HESK_DEMO') ) ?> -
+
+
+ +
+
+
+ '; + ?> +
+
diff --git a/admin/admin_settings_save.php b/admin/admin_settings_save.php index e749b178..1f2bd635 100644 --- a/admin/admin_settings_save.php +++ b/admin/admin_settings_save.php @@ -517,6 +517,7 @@ $set['show_number_merged'] = empty($_POST['show_number_merged']) ? 0 : 1; $set['request_location'] = empty($_POST['request_location']) ? 0 : 1; $set['category_order_column'] = empty($_POST['category_order_column']) ? 'cat_order' : 'name'; $set['rich_text_for_tickets'] = empty($_POST['rich_text_for_tickets']) ? 0 : 1; +$set['statuses_order_column'] = empty($_POST['statuses_order_column']) ? 'sort' : 'name'; if ($set['customer-email-verification-required']) { @@ -588,7 +589,10 @@ $modsForHesk_settings[\'request_location\'] = '.$set['request_location'].'; $modsForHesk_settings[\'category_order_column\'] = \''.$set['category_order_column'].'\'; //-- Setting for using rich-text editor for tickets. 0 = Disable, 1 = Enable -$modsForHesk_settings[\'rich_text_for_tickets\'] = '.$set['rich_text_for_tickets'].';'; +$modsForHesk_settings[\'rich_text_for_tickets\'] = '.$set['rich_text_for_tickets'].'; + +//-- Column to sort statuses by. Can be either \'sort\' or \'name\' +$modsForHesk_settings[\'statuses_order_column\'] = \''.$set['statuses_order_column'].'\';'; // Write the file if ( ! file_put_contents(HESK_PATH . 'modsForHesk_settings.inc.php', $modsForHesk_file_content) ) diff --git a/admin/admin_ticket.php b/admin/admin_ticket.php index ffeafc61..d6ebe0b4 100644 --- a/admin/admin_ticket.php +++ b/admin/admin_ticket.php @@ -41,6 +41,7 @@ require(HESK_PATH . 'hesk_settings.inc.php'); require(HESK_PATH . 'modsForHesk_settings.inc.php'); require(HESK_PATH . 'inc/common.inc.php'); require(HESK_PATH . 'inc/admin_functions.inc.php'); +require(HESK_PATH . 'inc/status_functions.inc.php'); hesk_load_database_functions(); hesk_session_start(); @@ -678,9 +679,9 @@ if($ticket['email'] != '') { $recentTicketsWithStatuses = array(); foreach ($recentTickets as $recentTicket) { $newRecentTicket = $recentTicket; - $thisTicketStatusRS = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "statuses` WHERE `ID` = " . intval($recentTicket['status'])); + $thisTicketStatusRS = hesk_dbQuery("SELECT `ID`, `TextColor` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "statuses` WHERE `ID` = " . intval($recentTicket['status'])); $theStatusRow = hesk_dbFetchAssoc($thisTicketStatusRS); - $newRecentTicket['statusText'] = $hesklang[$theStatusRow['Key']]; + $newRecentTicket['statusText'] = mfh_getDisplayTextForStatusId($theStatusRow['ID']); $newRecentTicket['statusColor'] = $theStatusRow['TextColor']; array_push($recentTicketsWithStatuses, $newRecentTicket); } @@ -976,11 +977,11 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php'); fetch_assoc()) + while ($statusRow = hesk_dbFetchAssoc($statusRs)) { if ($statusRow['IsStaffReopenedStatus'] == 1) { @@ -1080,11 +1081,11 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php'); echo '

'.$hesklang['status'].'

'; $status_options = array(); - $results = hesk_dbQuery("SELECT `ID`, `Key` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses`"); - while ($row = $results->fetch_assoc()) + $results = mfh_getAllStatuses(); + foreach ($results as $row) { $selected = $ticket['status'] == $row['ID'] ? 'selected' : ''; - $status_options[$row['ID']] = ''; + $status_options[$row['ID']] = ''; } echo ' @@ -2024,11 +2025,6 @@ function hesk_printReplyForm() { } } - $statusSql = 'SELECT `ID` FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'statuses` WHERE `IsStaffClosedOption` = 1'; - $statusRow = hesk_dbQuery($statusSql)->fetch_assoc(); - $staffClosedOptionStatus = array(); - $staffClosedOptionStatus['ID'] = $statusRow['ID']; - ?>
diff --git a/admin/manage_statuses.php b/admin/manage_statuses.php index b69aee79..d7d50f5a 100644 --- a/admin/manage_statuses.php +++ b/admin/manage_statuses.php @@ -5,8 +5,10 @@ define('HESK_PATH','../'); /* Get all the required files and functions */ require(HESK_PATH . 'hesk_settings.inc.php'); +require(HESK_PATH . 'modsForHesk_settings.inc.php'); require(HESK_PATH . 'inc/common.inc.php'); require(HESK_PATH . 'inc/admin_functions.inc.php'); +require(HESK_PATH . 'inc/status_functions.inc.php'); hesk_load_database_functions(); hesk_session_start(); @@ -18,11 +20,14 @@ hesk_checkPermission('can_man_ticket_statuses'); define('WYSIWYG',1); // Are we performing an action? -if (isset($_POST['action'])) { - if ($_POST['action'] == 'save') { - save(); - } +if (isset($_REQUEST['a'])) { + if ($_POST['a'] == 'create') { createStatus(); } + elseif ($_POST['a'] == 'update') { updateStatus(); } + elseif ($_GET['a'] == 'delete') { deleteStatus(); } + elseif ($_GET['a'] == 'sort') { moveStatus(); } } + + /* Print header */ require_once(HESK_PATH . 'inc/headerAdmin.inc.php'); @@ -77,214 +82,228 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php'); /* This will handle error, success and notice messages */ hesk_handle_messages(); - ?> -
-
-
-
+
+
+

+ + + + +

+
- - - - - - + + + + fetch_assoc()) - { - $checkedEcho = ($row['IsClosed'] == 1) ? 'checked="checked"' : ''; - $isDisabled = false; - if ($row['IsNewTicketStatus'] || $row['IsClosedByClient'] || $row['IsCustomerReplyStatus'] || - $row['IsStaffClosedOption'] || $row['IsStaffReopenedStatus'] || $row['IsDefaultStaffReplyStatus'] || - $row['LockedTicketStatus'] || $row['IsAutocloseOption']) - { - $isDisabled = true; - } - - $yesSelected = $customersOnlySelected = $staffOnlySelected = $noSelected = ''; - if ($row['Closable'] == 'yes') { $yesSelected = 'selected'; } - elseif ($row['Closable'] == 'conly') { $customersOnlySelected = 'selected'; } - elseif ($row['Closable'] == 'sonly') { $staffOnlySelected = 'selected'; } - else { $noSelected = 'selected'; } - - echo ''; - echo ''; //Name - echo ''; // Language File Key - echo ''; // Text Color - echo ''; - echo ''; // Resolved Status? - echo ''; //Delete status? - echo ''; - } - - //Print out an additional blank space for adding a status - echo ''; - echo ''; - echo ''; // Language File Key - echo ''; // Text Color - echo ''; - echo ''; // Resolved Status? - echo ''; //Empty placeholder where the delete row is. - echo ''; + $j = 1; + foreach ($statuses as $key => $row): ?> + + + + + + +
'.$hesklang[$row['Key']].' - - '; - if ($isDisabled) - { - echo ''; - } else - { - echo ''; - } - echo '
'.$hesklang['addNew'].' - -
+ + + + + '; + } + ?> + + + + + + + + +
-
-
-
- -
- +
+
+

-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
@@ -298,140 +317,399 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php');
+ + + '; + } else { + echo ' '; + } + + if ($index !== $numberOfStatuses) { + // Display move down + echo ' + '; + } else { + echo ''; + } + +} + +function buildCreateModal() { + global $hesklang, $hesk_settings; + + $languages = array(); + foreach ($hesk_settings['languages'] as $key => $value) { + $languages[$key] = $hesk_settings['languages'][$key]['folder']; + } +?> + + $value) { + $languages[$key] = $hesk_settings['languages'][$key]['folder']; + } + ?> + + '; +} + +function createStatus() { + global $hesklang, $hesk_settings; + + hesk_dbConnect(); + + // Create the new status record + $isClosed = hesk_POST('closed'); + $closable = hesk_POST('closable'); + $textColor = hesk_POST('text-color'); + $insert = "INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses` (`Key`, `TextColor`, `IsClosed`, `Closable`) + VALUES ('STORED IN XREF TABLE', '".hesk_dbEscape($textColor)."', ".intval($isClosed).", '".hesk_dbEscape($closable)."')"; + hesk_dbQuery($insert); + + $newStatusId = hesk_dbInsertID(); + + // For each language, create a value in the xref table + foreach (hesk_POST_array('name') as $language => $translation) { + hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."text_to_status_xref` (`language`, `text`, `status_id`) + VALUES ('".hesk_dbEscape($language)."', '".hesk_dbEscape($translation)."', ".intval($newStatusId).")"); + } + + hesk_process_messages($hesklang['new_status_created'],'manage_statuses.php','SUCCESS'); +} + +function updateStatus() { + global $hesklang, $hesk_settings; + + $statusId = hesk_POST('status-id'); + $isClosed = hesk_POST('closed'); + $closable = hesk_POST('closable'); + $textColor = hesk_POST('text-color'); + $update = "UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses` + SET `TextColor` = '".hesk_dbEscape($textColor)."', + `IsClosed` = ".intval($isClosed).", + `Closable` = '".hesk_dbEscape($closable)."' + WHERE `ID` = ".intval($statusId); + hesk_dbQuery($update); + + // For each language, delete the xref record and insert the new ones + hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."text_to_status_xref` WHERE `status_id` = ".intval($statusId)); + foreach (hesk_POST_array('name') as $language => $translation) { + hesk_dbQuery("INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."text_to_status_xref` (`language`, `text`, `status_id`) + VALUES ('".hesk_dbEscape($language)."', '".hesk_dbEscape($translation)."', ".intval($statusId).")"); + } + + hesk_process_messages($hesklang['ticket_status_updated'],'manage_statuses.php','SUCCESS'); +} + +function deleteStatus() { + global $hesklang, $hesk_settings; + + $statusId = hesk_GET('id'); + + hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."text_to_status_xref` WHERE `status_id` = ".intval($statusId)); + hesk_dbQuery("DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses` WHERE `ID` = ".intval($statusId)); + + hesk_process_messages($hesklang['ticket_status_deleted'],'manage_statuses.php','SUCCESS'); +} + +function moveStatus() { + global $hesk_settings, $hesklang; + + $statusId = intval(hesk_GET('id')); + $statusMove = intval(hesk_GET('move')); + + hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses` SET `sort` = `sort`+".intval($statusMove)." + WHERE `ID` = '".intval($statusId)."' LIMIT 1"); + + /* Update all category fields with new order */ + $res = hesk_dbQuery("SELECT `ID` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses` ORDER BY `sort` ASC"); + + $i = 10; + while ($myStatus = hesk_dbFetchAssoc($res)) + { + hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses` SET `sort`=".intval($i)." + WHERE `ID`='".intval($myStatus['ID'])."' LIMIT 1"); + $i += 10; + } + + hesk_process_messages($hesklang['status_sort_updated'],'manage_statuses.php','SUCCESS'); +} + function save() { global $hesklang, $hesk_settings; - //-- Before we do anything, make sure the statuses are valid. - $rows = hesk_dbQuery('SELECT * FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'statuses`'); - while ($row = $rows->fetch_assoc()) - { - if (!isset($_POST['s'.$row['ID'].'_delete'])) - { - validateStatus($_POST['s'.$row['ID'].'_key'], $_POST['s'.$row['ID'].'_textColor']); - } - } - - //-- Validate the new one if at least one of the fields are used / checked - if ($_POST['sN_key'] != null || $_POST['sN_textColor'] != null || isset($_POST['sN_isClosed'])) - { - validateStatus($_POST['sN_key'], $_POST['sN_textColor']); - } - - hesk_dbConnect(); - $wasStatusDeleted = false; - //-- Get all the status IDs - $statusesSql = 'SELECT * FROM `'.$hesk_settings['db_pfix'].'statuses`'; - $results = hesk_dbQuery($statusesSql); - while ($row = $results->fetch_assoc()) - { - //-- If the status is marked for deletion, delete it and skip everything below. - if (isset($_POST['s'.$row['ID'].'_delete'])) - { - $delete = "DELETE FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses` WHERE `ID` = ?"; - $stmt = hesk_dbConnect()->prepare($delete); - $stmt->bind_param('i', $row['ID']); - $stmt->execute(); - $wasStatusDeleted = true; - } else - { - //-- Update the information in the database with what is on the page - $query = "UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses` SET `Key` = ?, `TextColor` = ?, `IsClosed` = ?, `Closable` = ? WHERE `ID` = ?"; - $stmt = hesk_dbConnect()->prepare($query); - $isStatusClosed = (isset($_POST['s'.$row['ID'].'_isClosed']) ? 1 : 0); - $stmt->bind_param('sssisi', $_POST['s'.$row['ID'].'_key'], $_POST['s'.$row['ID'].'_textColor'], $isStatusClosed, $_POST['s'.$row['ID'].'_closable'], $row['ID']); - $stmt->execute(); - } - } - - //-- If any statuses were deleted, re-index them before adding a new one - if ($wasStatusDeleted) { - //-- First drop and re-add the ID column - hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses` DROP COLUMN `ID`"); - hesk_dbQuery("ALTER TABLE `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses` ADD `ID` INT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST"); - - //-- Since statuses should be zero-based, but are now one-based, subtract one from each ID - hesk_dbQuery("UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses` SET `ID` = `ID`-1"); - } - - //-- Insert the addition if there is anything to add - if ($_POST['sN_key'] != null && $_POST['sN_textColor'] != null) - { - //-- The next ID is equal to the number of rows, since the IDs are zero-indexed. - $nextValue = hesk_dbQuery('SELECT * FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'statuses`')->num_rows; - $isClosed = isset($_POST['sN_isClosed']) ? 1 : 0; - $insert = "INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses` (`ID`, `Key`, `TextColor`, `IsClosed`, `Closable`) - VALUES (".$nextValue.", '".hesk_dbEscape($_POST['sN_key'])."', '".hesk_dbEscape($_POST['sN_textColor'])."', ".$isClosed.", '".hesk_dbEscape($_POST['sN_closable'])."')"; - hesk_dbQuery($insert); - } - //-- Update default status for actions $defaultQuery = "UPDATE `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses` SET "; - hesk_dbConnect()->query($defaultQuery . "`IsNewTicketStatus` = 0"); - $updateQuery = $defaultQuery . "`IsNewTicketStatus` = 1 WHERE `ID` = ?"; - $stmt = hesk_dbConnect()->prepare($updateQuery); - $stmt->bind_param('i', $_POST['newTicket']); - $stmt->execute(); + hesk_dbQuery($defaultQuery . "`IsNewTicketStatus` = 0"); + $updateQuery = $defaultQuery . "`IsNewTicketStatus` = 1 WHERE `ID` = ".intval($_POST['newTicket']); + hesk_dbQuery($updateQuery); + hesk_dbQuery($defaultQuery . "`IsClosedByClient` = 0"); + $updateQuery = $defaultQuery . "`IsClosedByClient` = 1 WHERE `ID` = ".intval($_POST['closedByClient']); + hesk_dbQuery($updateQuery); - hesk_dbConnect()->query($defaultQuery . "`IsClosedByClient` = 0"); - $updateQuery = $defaultQuery . "`IsClosedByClient` = 1 WHERE `ID` = ?"; - $stmt = hesk_dbConnect()->prepare($updateQuery); - $stmt->bind_param('i', $_POST['closedByClient']); - $stmt->execute(); + hesk_dbQuery($defaultQuery . "`IsCustomerReplyStatus` = 0"); + $updateQuery = $defaultQuery . "`IsCustomerReplyStatus` = 1 WHERE `ID` = ".intval($_POST['replyFromClient']); + hesk_dbQuery($updateQuery); - hesk_dbConnect()->query($defaultQuery . "`IsCustomerReplyStatus` = 0"); - $updateQuery = $defaultQuery . "`IsCustomerReplyStatus` = 1 WHERE `ID` = ?"; - $stmt = hesk_dbConnect()->prepare($updateQuery); - $stmt->bind_param('i', $_POST['replyFromClient']); - $stmt->execute(); + hesk_dbQuery($defaultQuery . "`IsStaffClosedOption` = 0"); + $updateQuery = $defaultQuery . "`IsStaffClosedOption` = 1 WHERE `ID` = ".intval($_POST['staffClosedOption']); + hesk_dbQuery($updateQuery); - hesk_dbConnect()->query($defaultQuery . "`IsStaffClosedOption` = 0"); - $updateQuery = $defaultQuery . "`IsStaffClosedOption` = 1 WHERE `ID` = ?"; - $stmt = hesk_dbConnect()->prepare($updateQuery); - $stmt->bind_param('i', $_POST['staffClosedOption']); - $stmt->execute(); + hesk_dbQuery($defaultQuery . "`IsStaffReopenedStatus` = 0"); + $updateQuery = $defaultQuery . "`IsStaffReopenedStatus` = 1 WHERE `ID` = ".intval($_POST['staffReopenedStatus']); + hesk_dbQuery($updateQuery); - hesk_dbConnect()->query($defaultQuery . "`IsStaffReopenedStatus` = 0"); - $updateQuery = $defaultQuery . "`IsStaffReopenedStatus` = 1 WHERE `ID` = ?"; - $stmt = hesk_dbConnect()->prepare($updateQuery); - $stmt->bind_param('i', $_POST['staffReopenedStatus']); - $stmt->execute(); + hesk_dbQuery($defaultQuery . "`IsDefaultStaffReplyStatus` = 0"); + $updateQuery = $defaultQuery . "`IsDefaultStaffReplyStatus` = 1 WHERE `ID` = ".intval($_POST['defaultStaffReplyStatus']); + hesk_dbQuery($updateQuery); - hesk_dbConnect()->query($defaultQuery . "`IsDefaultStaffReplyStatus` = 0"); - $updateQuery = $defaultQuery . "`IsDefaultStaffReplyStatus` = 1 WHERE `ID` = ?"; - $stmt = hesk_dbConnect()->prepare($updateQuery); - $stmt->bind_param('i', $_POST['defaultStaffReplyStatus']); - $stmt->execute(); + hesk_dbQuery($defaultQuery . "`LockedTicketStatus` = 0"); + $updateQuery = $defaultQuery . "`LockedTicketStatus` = 1 WHERE `ID` = ".intval($_POST['lockedTicketStatus']); + hesk_dbQuery($updateQuery); - hesk_dbConnect()->query($defaultQuery . "`LockedTicketStatus` = 0"); - $updateQuery = $defaultQuery . "`LockedTicketStatus` = 1 WHERE `ID` = ?"; - $stmt = hesk_dbConnect()->prepare($updateQuery); - $stmt->bind_param('i', $_POST['lockedTicketStatus']); - $stmt->execute(); - - hesk_dbConnect()->query($defaultQuery . "`IsAutocloseOption` = 0"); - $updateQuery = $defaultQuery . "`IsAutocloseOption` = 1 WHERE `ID` = ?"; - $stmt = hesk_dbConnect()->prepare($updateQuery); - $stmt->bind_param('i', $_POST['autocloseTicketOption']); - $stmt->execute(); + hesk_dbQuery($defaultQuery . "`IsAutocloseOption` = 0"); + $updateQuery = $defaultQuery . "`IsAutocloseOption` = 1 WHERE `ID` = ".intval($_POST['autocloseTicketOption']); + hesk_dbQuery($updateQuery); hesk_process_messages($hesklang['statuses_saved'],'manage_statuses.php','SUCCESS'); -} - -function validateStatus($key, $textColor) -{ - global $hesklang; - - //-- Validation logic - if ($key == '') - { - hesk_process_messages($hesklang['key_required'], 'manage_statuses.php'); - } elseif ($textColor == '') - { - hesk_process_messages($hesklang['textColorRequired'], 'manage_statuses.php'); - } } \ No newline at end of file diff --git a/admin/show_tickets.php b/admin/show_tickets.php index 3bd835d2..dd140dc8 100644 --- a/admin/show_tickets.php +++ b/admin/show_tickets.php @@ -37,8 +37,10 @@ define('HESK_PATH','../'); /* Get all the required files and functions */ require(HESK_PATH . 'hesk_settings.inc.php'); +require(HESK_PATH . 'modsForHesk_settings.inc.php'); require(HESK_PATH . 'inc/common.inc.php'); require(HESK_PATH . 'inc/admin_functions.inc.php'); +require(HESK_PATH . 'inc/status_functions.inc.php'); hesk_load_database_functions(); hesk_session_start(); diff --git a/inc/common.inc.php b/inc/common.inc.php index dd7afddc..5a6a7074 100644 --- a/inc/common.inc.php +++ b/inc/common.inc.php @@ -1947,4 +1947,29 @@ function hesk_getFeatureArray() { 'can_man_settings', /* User can manage helpdesk settings */ 'can_change_notification_settings', /* User can change notification settings */ ); +} + +function mfh_doesStatusHaveXrefRecord($statusId, $language) { + global $hesk_settings; + + $rs = hesk_dbQuery("SELECT 1 FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."text_to_status_xref` + WHERE `language` = '".hesk_dbEscape($language)."' AND `status_id` = ".intval($statusId)); + return hesk_dbNumRows($rs) > 0; +} + +function mfh_getDisplayTextForStatusId($statusId) { + global $hesklang, $hesk_settings; + + $statusRs = hesk_dbQuery("SELECT `text`, `Key`, `language` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses` AS `statuses` + LEFT JOIN `".hesk_dbEscape($hesk_settings['db_pfix'])."text_to_status_xref` ON `status_id` = `statuses`.`ID` + WHERE `statuses`.`ID` = ".intval($statusId)); + + $statusRec = hesk_dbFetchAssoc($statusRs); + if ($statusRec['language'] == $hesk_settings['language'] && $statusRec['text'] != NULL) { + // We found a record. Use the text field + return $statusRec['text']; + } else { + // Fallback to the language key + return $hesklang[$statusRec['Key']]; + } } \ No newline at end of file diff --git a/inc/print_tickets.inc.php b/inc/print_tickets.inc.php index de70e6ae..fc77e679 100644 --- a/inc/print_tickets.inc.php +++ b/inc/print_tickets.inc.php @@ -112,23 +112,21 @@ $fid = 1; require(HESK_PATH . 'inc/assignment_search.inc.php'); // --> TICKET STATUS -$statusSql = "SELECT `ID` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses`"; +$statuses = mfh_getAllStatuses(); $totalStatuses = 0; $possible_status = array(); -$results = hesk_dbQuery($statusSql); -while ($row = $results->fetch_assoc()) +foreach ($statuses as $row) { - array_push($possible_status, $row['ID']); + $possible_status[$row['ID']] = $row['ID']; $totalStatuses++; } $status = $possible_status; - // Process statuses unless overridden with "s_all" variable if ( ! hesk_GET('s_all') ) { foreach ($status as $k => $v) { - if (empty($_GET['s' . $k])) + if (empty($_GET['s' . $v])) { unset($status[$k]); } @@ -145,10 +143,12 @@ if ( $tmp < $totalStatuses ) if ($tmp == 0) { $status = $possible_status; - $resolvedSql = "SELECT `ID` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses` WHERE `IsClosed` = 1"; - $resolvedRS = hesk_dbQuery($resolvedSql); - while ($row = $resolvedRS->fetch_assoc()) + foreach ($statuses as $row) { + if ($row['IsClosed'] == 0) { + continue; + } + unset($status[$row['ID']]); } } diff --git a/inc/recaptcha/recaptchalib_v2.php b/inc/recaptcha/recaptchalib_v2.php new file mode 100755 index 00000000..9b035ada --- /dev/null +++ b/inc/recaptcha/recaptchalib_v2.php @@ -0,0 +1,161 @@ +" . self::$_signupUrl . ""); + } + $this->_secret=$secret; + } + + /** + * Encodes the given data into a query string format. + * + * @param array $data array of string elements to be encoded. + * + * @return string - encoded request. + */ + private function _encodeQS($data) + { + $req = ""; + foreach ($data as $key => $value) { + $req .= $key . '=' . urlencode(stripslashes($value)) . '&'; + } + + // Cut the last '&' + $req=substr($req, 0, strlen($req)-1); + return $req; + } + + /** + * Submits an HTTP GET to a reCAPTCHA server. + * + * @param string $path url path to recaptcha server. + * @param array $data array of parameters to be sent. + * + * @return array response + */ + private function _submitHTTPGet($path, $data) + { + $req = $this->_encodeQS($data); + // Try using cURL first. If that fails, fallback to file_get_contents + if (function_exists('curl_init')) { + $handle = curl_init($path); + $queryString = http_build_query($data, '', '&'); + $options = array( + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => $queryString, + CURLOPT_HTTPHEADER => array( + 'Content-Type: application/x-www-form-urlencoded' + ), + CURLINFO_HEADER_OUT => false, + CURLOPT_HEADER => false, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_SSL_VERIFYPEER => true + ); + curl_setopt_array($handle, $options); + $response = curl_exec($handle); + curl_close($handle); + return $response; + } + + $response = file_get_contents($path . $req); + return $response; + } + + /** + * Calls the reCAPTCHA siteverify API to verify whether the user passes + * CAPTCHA test. + * + * @param string $remoteIp IP address of end user. + * @param string $response response string from recaptcha verification. + * + * @return ReCaptchaResponse + */ + public function verifyResponse($remoteIp, $response) + { + // Discard empty solution submissions + if ($response == null || strlen($response) == 0) { + $recaptchaResponse = new ReCaptchaResponse(); + $recaptchaResponse->success = false; + $recaptchaResponse->errorCodes = 'missing-input'; + return $recaptchaResponse; + } + + $getResponse = $this->_submitHttpGet( + self::$_siteVerifyUrl, + array ( + 'secret' => $this->_secret, + 'remoteip' => $remoteIp, + 'v' => self::$_version, + 'response' => $response + ) + ); + $answers = json_decode($getResponse, true); + $recaptchaResponse = new ReCaptchaResponse(); + + if (trim($answers ['success']) == true) { + $recaptchaResponse->success = true; + } else { + $recaptchaResponse->success = false; + $recaptchaResponse->errorCodes = $answers [error-codes]; + } + + return $recaptchaResponse; + } +} + +?> diff --git a/inc/show_search_form.inc.php b/inc/show_search_form.inc.php index 413e3a71..e0b35307 100644 --- a/inc/show_search_form.inc.php +++ b/inc/show_search_form.inc.php @@ -37,11 +37,15 @@ if (!defined('IN_SCRIPT')) {die('Invalid attempt');} if ( ! isset($status) ) { $status = array(); + $allStatuses = mfh_getAllStatuses(); //-- We don't want to check statuses that are considered "closed" - $statusRS = hesk_dbQuery('SELECT `ID`, `Key` FROM `'.hesk_dbEscape($hesk_settings['db_pfix']).'statuses` WHERE `IsClosed` = 0'); - while ($row = $statusRS->fetch_assoc()) + foreach ($allStatuses as $row) { - $status[$row['ID']] = $row['Key']; + if ($status['IsClosed'] == 1) { + continue; + } + + $status[$row['ID']] = mfh_getDisplayTextForStatusId($row['ID']); } } @@ -131,8 +135,8 @@ $more2 = empty($_GET['more2']) ? 0 : 1; fetch_assoc()) + $statuses = mfh_getAllStatuses(); + foreach ($statuses as $row) { if ($rowCounter > 3) { @@ -149,7 +153,7 @@ $more2 = empty($_GET['more2']) ? 0 : 1; } echo ''; + echo '/> '.mfh_getDisplayTextForStatusId($row['ID']).''; $rowCounter++; } diff --git a/inc/status_functions.inc.php b/inc/status_functions.inc.php new file mode 100644 index 00000000..ce451b42 --- /dev/null +++ b/inc/status_functions.inc.php @@ -0,0 +1,55 @@ + 0) // Print ticket status if ( hesk_show_column('status') ) { - $statusName = hesk_dbFetchAssoc(hesk_dbQuery("SELECT `Key`, `TextColor` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses` WHERE ID = ".$ticket['status'])); - $ticket['status']=''.$hesklang[$statusName['Key']].''; + $statusRS = hesk_dbQuery("SELECT `ID`, `TextColor` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses` WHERE ID = ".$ticket['status']); + $statusName = hesk_dbFetchAssoc($statusRS); + $ticket['status']=''.mfh_getDisplayTextForStatusId($statusName['ID']).''; echo ''.$ticket['status'].' '; } diff --git a/install/mods-for-hesk/ajax/task-ajax.php b/install/mods-for-hesk/ajax/task-ajax.php index b5e3d503..4d855688 100644 --- a/install/mods-for-hesk/ajax/task-ajax.php +++ b/install/mods-for-hesk/ajax/task-ajax.php @@ -23,6 +23,8 @@ if ($task == 'ip-email-bans') { print json_encode($jsonToSend); } elseif ($task == 'migrate-bans') { migrateBans($_POST['user']); +} elseif ($task == 'initialize-statuses') { + initializeXrefTable(); } else { $response = 'The task "'.$task.'" was not recognized. Check your spelling and try again.'; print $response; diff --git a/install/mods-for-hesk/installModsForHesk.php b/install/mods-for-hesk/installModsForHesk.php index b41431db..5e576af3 100644 --- a/install/mods-for-hesk/installModsForHesk.php +++ b/install/mods-for-hesk/installModsForHesk.php @@ -120,6 +120,10 @@ function printRow($version) { Migrate IP / Email Bans Waiting... + + Initialize Statuses + Waiting... + diff --git a/install/mods-for-hesk/js/version-scripts.js b/install/mods-for-hesk/js/version-scripts.js index bee321ec..f55b3614 100644 --- a/install/mods-for-hesk/js/version-scripts.js +++ b/install/mods-for-hesk/js/version-scripts.js @@ -62,7 +62,9 @@ function executeUpdate(version, cssclass, formattedVersion) { success: function(data) { markUpdateAsSuccess(cssclass, formattedVersion); if (version == 200) { - migrateIpEmailBans('banmigrate', cssclass); + migrateIpEmailBans('banmigrate', 'banmigrate'); + } else if (version == 240) { + initializeStatuses('initialize-statuses', 'initialize-statuses'); } else { processUpdates(version); } @@ -83,7 +85,6 @@ function migrateIpEmailBans(version, cssclass) { data: { task: 'ip-email-bans' }, success: function(data) { var parsedData = $.parseJSON(data); - console.info(parsedData); if (parsedData.status == 'ATTENTION') { appendToInstallConsole('WARNINGYour response is needed. Please check above.'); markUpdateAsAttention(version); @@ -94,11 +95,33 @@ function migrateIpEmailBans(version, cssclass) { }, error: function(data) { appendToInstallConsole('ERROR' + data.responseText + ''); - markUpdateAsFailure(cssclass); + markUpdateAsFailure(version); } }); } +function initializeStatuses(version, cssclass) { + startVersionUpgrade(version); + appendToInstallConsole('INFOInitializing Statuses'); + $.ajax({ + type: 'POST', + url: 'ajax/task-ajax.php', + data: { task: 'initialize-statuses' }, + success: function(data) { + markUpdateAsSuccess(cssclass, 'Initializing Statuses'); + statusesInitialized(); + }, + error: function(data) { + appendToInstallConsole('ERROR' + data.responseText + ''); + markUpdateAsFailure(version); + } + }); +} + +function statusesInitialized() { + processUpdates(240); +} + function runMigration() { // Get user ID that is selected diff --git a/install/mods-for-hesk/sql/installSql.php b/install/mods-for-hesk/sql/installSql.php index d02f8338..10e7de06 100644 --- a/install/mods-for-hesk/sql/installSql.php +++ b/install/mods-for-hesk/sql/installSql.php @@ -1,5 +1,8 @@ $value) { + $languages[$key] = $hesk_settings['languages'][$key]['folder']; + } + + $statusesRs = executeQuery("SELECT `ID`, `Key` FROM `".hesk_dbEscape($hesk_settings['db_pfix'])."statuses`"); + while ($row = hesk_dbFetchAssoc($statusesRs)) { + foreach ($languages as $language => $languageCode) { + $sql = "INSERT INTO `".hesk_dbEscape($hesk_settings['db_pfix'])."text_to_status_xref` (`language`, `text`, `status_id`) + VALUES ('".hesk_dbEscape($language)."', '".hesk_dbEscape($hesklang[$row['Key']])."', ".intval($row['ID']).")"; + executeQuery($sql); + } + } } function execute240FileUpdate() { diff --git a/language/en/text.php b/language/en/text.php index 7b6dfe57..986e8560 100644 --- a/language/en/text.php +++ b/language/en/text.php @@ -34,6 +34,23 @@ $hesklang['view_ticket_form'] = 'View ticket form'; $hesklang['knowledgebase'] = 'Knowledgebase section'; $hesklang['allow_rich_text_for_tickets'] = 'Allow rich text for tickets'; $hesklang['allow_rich_text_for_tickets_help'] = 'Allow staff and customers to use rich text formatting when writing ticket messages / replies.'; +$hesklang['click_to_edit_name'] = 'Click to edit name'; +$hesklang['new_status'] = 'New Status'; +$hesklang['create_new_status_title'] = 'Create New Status'; +$hesklang['status_name_title'] = 'Status Name'; +$hesklang['properties'] = 'Properties'; +$hesklang['closable'] = 'Closable'; // Same as $hesklang['closable_question'], but without punctuation +$hesklang['new_status_created'] = 'New status successfully created'; +$hesklang['editing_status_x'] = 'Editing status %s'; // 1st %s: text color, 2nd %s: status name +$hesklang['status_not_in_database'] = 'The status text for this language was not found in the database, so a suggested translation has been filled for you. + Please click "Save Changes" to save this translation to the database and to remove this warning.'; +$hesklang['ticket_status_updated'] = 'Ticket status successfully updated!'; +$hesklang['ticket_status_deleted'] = 'Ticket status deleted!'; +$hesklang['confirm_delete_status_question'] = 'Delete status?'; +$hesklang['confirm_delete_status'] = 'Are you sure you want to delete this status? This cannot be undone!'; +$hesklang['status_sort_updated'] = 'Ticket status sort updated!'; +$hesklang['status_sort'] = 'Status Sorting'; +$hesklang['status_sort_help'] = 'Determines if statuses shown on the manage statuses page and all dropdowns are sorted by the user-defined order (default), or sorted alphabetically.'; // ADDED OR MODIFIED IN Mods for HESK 2.3.0 $hesklang['sm_icon'] = 'Icon'; diff --git a/modsForHesk_settings.inc.php b/modsForHesk_settings.inc.php index a1dfcd09..602adf10 100644 --- a/modsForHesk_settings.inc.php +++ b/modsForHesk_settings.inc.php @@ -52,4 +52,7 @@ $modsForHesk_settings['request_location'] = 0; $modsForHesk_settings['category_order_column'] = 'cat_order'; //-- Setting for using rich-text editor for tickets. 0 = Disable, 1 = Enable -$modsForHesk_settings['rich_text_for_tickets'] = 1; \ No newline at end of file +$modsForHesk_settings['rich_text_for_tickets'] = 1; + +//-- Column to sort statuses by. Can be either 'sort' or 'name' +$modsForHesk_settings['statuses_order_column'] = 'sort'; \ No newline at end of file diff --git a/ticket.php b/ticket.php index f08648fa..ff9412c4 100644 --- a/ticket.php +++ b/ticket.php @@ -316,8 +316,7 @@ if (!$show['show']) { $repliesColumnWidth = 3; } echo '

'.$hesklang['status'].'

'; - $ticketStatusKey = $status['Key']; - echo '

'.$hesklang[$ticketStatusKey].'

'; + echo '

'.mfh_getDisplayTextForStatusId($status['ID']).'

'; echo '
'; echo '

'.$hesklang['last_replier'].'

'.$ticket['repliername'].'

';