diff --git a/admin/admin_main.php b/admin/admin_main.php index 0d6dcefc..59898d18 100644 --- a/admin/admin_main.php +++ b/admin/admin_main.php @@ -54,7 +54,15 @@ else { ?>
- +

diff --git a/admin/admin_ticket.php b/admin/admin_ticket.php index 150b54e6..36804051 100644 --- a/admin/admin_ticket.php +++ b/admin/admin_ticket.php @@ -939,6 +939,11 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php'); /* This will handle error, success and notice messages */ hesk_handle_messages(); + $service_messages = mfh_get_service_messages('STAFF_VIEW_TICKET'); + foreach ($service_messages as $sm) { + hesk_service_message($sm); + } + // Prepare special custom fields foreach ($hesk_settings['custom_fields'] as $k=>$v) { if ($v['use'] && hesk_is_custom_field_in_category($k, $ticket['category']) ) { diff --git a/admin/index.php b/admin/index.php index aec1e374..f623d4a0 100644 --- a/admin/index.php +++ b/admin/index.php @@ -278,6 +278,12 @@ function print_login()

'; } // END hesk_kb_header() @@ -214,7 +216,15 @@ function hesk_show_kb_article($artid) ?>
- + '; + $service_messages = mfh_get_service_messages('STAFF_VIEW_KB_ARTICLE'); + foreach ($service_messages as $sm) { + hesk_service_message($sm); + } + echo '
'; + ?>
@@ -397,6 +407,13 @@ function hesk_show_kb_category($catid, $is_search = 0) { { /* Print header */ hesk_kb_header($hesk_settings['kb_link'], $catid); + + echo '
'; + $service_messages = mfh_get_service_messages('STAFF_KB_HOME'); + foreach ($service_messages as $sm) { + hesk_service_message($sm); + } + echo '
'; } ?>
diff --git a/admin/manage_knowledgebase.php b/admin/manage_knowledgebase.php index b126a326..03e4ba6f 100644 --- a/admin/manage_knowledgebase.php +++ b/admin/manage_knowledgebase.php @@ -270,6 +270,12 @@ if (!isset($_SESSION['hide']['treemenu']))
diff --git a/admin/service_messages.php b/admin/service_messages.php index 580d0012..052e0eb4 100644 --- a/admin/service_messages.php +++ b/admin/service_messages.php @@ -15,6 +15,7 @@ define('IN_SCRIPT', 1); define('HESK_PATH', '../'); define('PAGE_TITLE', 'ADMIN_SERVICE_MESSAGES'); define('MFH_PAGE_LAYOUT', 'TOP_ONLY'); +define('EXTRA_JS', ''); /* Get all the required files and functions */ require(HESK_PATH . 'hesk_settings.inc.php'); @@ -31,26 +32,8 @@ hesk_isLoggedIn(); hesk_checkPermission('can_service_msg'); // Define required constants -define('LOAD_TABS', 1); define('WYSIWYG', 1); -// What should we do? -if ($action = hesk_REQUEST('a')) { - if ($action == 'edit_sm') { - edit_sm(); - } elseif (defined('HESK_DEMO')) { - hesk_process_messages($hesklang['ddemo'], 'service_messages.php', 'NOTICE'); - } elseif ($action == 'new_sm') { - new_sm(); - } elseif ($action == 'save_sm') { - save_sm(); - } elseif ($action == 'order_sm') { - order_sm(); - } elseif ($action == 'remove_sm') { - remove_sm(); - } -} - /* Print header */ require_once(HESK_PATH . 'inc/headerAdmin.inc.php'); @@ -60,652 +43,401 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php');
+
+

+ + +

+
-
+
+
- var users = [];'; +$usersRs = hesk_dbQuery("SELECT `id`, `name` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "users` WHERE `active` = '1'"); +$users = array(); +while ($row = hesk_dbFetchAssoc($usersRs)) { + $users[] = $row; + echo "users[" . $row['id'] . "] = { + id: ".$row['id'].", + name: '".$row['name']."' + }\n"; +} +echo " +var languages = [];\n"; +foreach ($hesk_settings['languages'] as $key => $value) { + echo "languages[" . json_encode($value['folder']) . "] = " . json_encode($key) . ";\n"; +} +echo ''; +?> + + + -hesk_cleanSessionVars(array('new_sm', 'preview_sm', 'edit_sm')); + 4 || $style < 0) { - $style = 0; - } - - $type = empty($_POST['type']) ? 0 : 1; - $icon = hesk_POST('icon'); - $title = hesk_input(hesk_POST('title')) or $hesk_error_buffer[] = $hesklang['sm_e_title']; - $message = hesk_getHTML(hesk_POST('message')); - - // Clean the HTML code - require(HESK_PATH . 'inc/htmlpurifier/HeskHTMLPurifier.php'); - $purifier = new HeskHTMLPurifier($hesk_settings['cache_dir']); - $message = $purifier->heskPurify($message); - - // Any errors? - if (count($hesk_error_buffer)) { - $_SESSION['edit_sm'] = true; - - $_SESSION['new_sm'] = array( - 'id' => $id, - 'style' => $style, - 'type' => $type, - 'title' => $title, - 'icon' => $icon, - 'message' => hesk_input(hesk_POST('message')), - ); - - $tmp = ''; - foreach ($hesk_error_buffer as $error) { - $tmp .= "
  • $error
  • \n"; - } - $hesk_error_buffer = $tmp; - - $hesk_error_buffer = $hesklang['rfm'] . '

      ' . $hesk_error_buffer . '
    '; - hesk_process_messages($hesk_error_buffer, 'service_messages.php'); - } - - // Just preview the message? - if (isset($_POST['sm_preview'])) { - $_SESSION['preview_sm'] = true; - $_SESSION['edit_sm'] = true; - - $_SESSION['new_sm'] = array( - 'id' => $id, - 'style' => $style, - 'type' => $type, - 'title' => $title, - 'message' => $message, - 'icon' => $icon, - ); - - header('Location: service_messages.php'); - exit; - } - - // Update the service message in the database - hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "service_messages` SET - `author` = '" . intval($_SESSION['id']) . "', - `title` = '" . hesk_dbEscape($title) . "', - `message` = '" . hesk_dbEscape($message) . "', - `style` = '{$style}', - `type` = '{$type}', - `icon` = '{$icon}' - WHERE `id`={$id}"); - - $_SESSION['smord'] = $id; - hesk_process_messages($hesklang['sm_mdf'], 'service_messages.php', 'SUCCESS'); - -} // End save_sm() - - -function edit_sm() -{ - global $hesk_settings, $hesklang; - - // Get service messageID - $id = intval(hesk_GET('id')) or hesk_error($hesklang['sm_e_id']); - - // Get details from the database - $res = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "service_messages` WHERE `id`={$id} LIMIT 1"); - if (hesk_dbNumRows($res) != 1) { - hesk_error($hesklang['sm_not_found']); - } - $sm = hesk_dbFetchAssoc($res); - - $_SESSION['new_sm'] = $sm; - $_SESSION['edit_sm'] = true; - -} // End edit_sm() - - -function order_sm() -{ - global $hesk_settings, $hesklang; - - // A security check - hesk_token_check(); - - // Get ID and move parameters - $id = intval(hesk_GET('id')) or hesk_error($hesklang['sm_e_id']); - $move = intval(hesk_GET('move')); - $_SESSION['smord'] = $id; - - // Update article details - hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "service_messages` SET `order`=`order`+" . intval($move) . " WHERE `id`={$id}"); - - // Update order of all service messages - update_sm_order(); - - // Finish - header('Location: service_messages.php'); - exit(); - -} // End order_sm() - - -function update_sm_order() -{ - global $hesk_settings, $hesklang; - - // Get list of current service messages - $res = hesk_dbQuery("SELECT `id` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "service_messages` ORDER BY `order` ASC"); - - // Update database - $i = 10; - while ($sm = hesk_dbFetchAssoc($res)) { - hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "service_messages` SET `order`=" . intval($i) . " WHERE `id`='" . intval($sm['id']) . "'"); - $i += 10; - } - - return true; - -} // END update_sm_order() - - -function remove_sm() -{ - global $hesk_settings, $hesklang; - - // A security check - hesk_token_check(); - - // Get ID - $id = intval(hesk_GET('id')) or hesk_error($hesklang['sm_e_id']); - - // Delete the service message - hesk_dbQuery("DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "service_messages` WHERE `id`={$id}"); - - // Were we successful? - if (hesk_dbAffectedRows() == 1) { - hesk_process_messages($hesklang['sm_deleted'], './service_messages.php', 'SUCCESS'); - } else { - hesk_process_messages($hesklang['sm_not_found'], './service_messages.php'); - } - -} // End remove_sm() - - -function new_sm() -{ - global $hesk_settings, $hesklang, $listBox; - global $hesk_error_buffer; - - // A security check - # hesk_token_check('POST'); - - $hesk_error_buffer = array(); - - $style = intval(hesk_POST('style', 0)); - if ($style > 4 || $style < 0) { - $style = 0; - } - - $type = empty($_POST['type']) ? 0 : 1; - $icon = hesk_POST('icon'); - $title = hesk_input(hesk_POST('title')) or $hesk_error_buffer[] = $hesklang['sm_e_title']; - $message = hesk_getHTML(hesk_POST('message')); - - // Clean the HTML code - require(HESK_PATH . 'inc/htmlpurifier/HeskHTMLPurifier.php'); - $purifier = new HeskHTMLPurifier($hesk_settings['cache_dir']); - $message = $purifier->heskPurify($message); - - // Any errors? - if (count($hesk_error_buffer)) { - $_SESSION['new_sm'] = array( - 'style' => $style, - 'type' => $type, - 'title' => $title, - 'icon' => $icon, - 'message' => hesk_input(hesk_POST('message')), - ); - - $tmp = ''; - foreach ($hesk_error_buffer as $error) { - $tmp .= "
  • $error
  • \n"; - } - $hesk_error_buffer = $tmp; - - $hesk_error_buffer = $hesklang['rfm'] . '

      ' . $hesk_error_buffer . '
    '; - hesk_process_messages($hesk_error_buffer, 'service_messages.php'); - } - - // Just preview the message? - if (isset($_POST['sm_preview'])) { - $_SESSION['preview_sm'] = true; - - $_SESSION['new_sm'] = array( - 'style' => $style, - 'type' => $type, - 'title' => $title, - 'icon' => $icon, - 'message' => $message, - ); - - header('Location: service_messages.php'); - exit; - } - - // Get the latest service message order - $res = hesk_dbQuery("SELECT `order` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "service_messages` ORDER BY `order` DESC LIMIT 1"); - $row = hesk_dbFetchRow($res); - $my_order = intval($row[0]) + 10; - - // Insert service message into database - hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "service_messages` (`author`,`title`,`message`,`style`,`type`,`order`, `icon`) VALUES ( - '" . intval($_SESSION['id']) . "', - '" . hesk_dbEscape($title) . "', - '" . hesk_dbEscape($message) . "', - '{$style}', - '{$type}', - '{$my_order}', - '{$icon}' - )"); - - $_SESSION['smord'] = hesk_dbInsertID(); - hesk_process_messages($hesklang['sm_added'], 'service_messages.php', 'SUCCESS'); - -} // End new_sm() - ?> diff --git a/api/BusinessLogic/Exceptions/MissingAuthenticationTokenException.php b/api/BusinessLogic/Exceptions/MissingAuthenticationTokenException.php index 596839ff..267a82c0 100644 --- a/api/BusinessLogic/Exceptions/MissingAuthenticationTokenException.php +++ b/api/BusinessLogic/Exceptions/MissingAuthenticationTokenException.php @@ -4,8 +4,8 @@ namespace BusinessLogic\Exceptions; class MissingAuthenticationTokenException extends ApiFriendlyException { function __construct() { - parent::__construct("An 'X-Auth-Token' is required for all requests", + parent::__construct("An 'X-Auth-Token' is required for this request", 'Security Exception', - 400); + 401); } } \ No newline at end of file diff --git a/api/BusinessLogic/Security/UserContext.php b/api/BusinessLogic/Security/UserContext.php index 53002a8e..6ce0fba0 100644 --- a/api/BusinessLogic/Security/UserContext.php +++ b/api/BusinessLogic/Security/UserContext.php @@ -58,7 +58,7 @@ class UserContext extends \BaseClass { public $active; function isAnonymousUser() { - return $this->username === "API - ANONYMOUS USER"; + return $this->id === -1; } static function buildAnonymousUser() { diff --git a/api/BusinessLogic/Security/UserPrivilege.php b/api/BusinessLogic/Security/UserPrivilege.php index 0d68418f..801e1937 100644 --- a/api/BusinessLogic/Security/UserPrivilege.php +++ b/api/BusinessLogic/Security/UserPrivilege.php @@ -17,4 +17,5 @@ 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_MANAGE_SERVICE_MESSAGES = 'can_service_msg'; } \ No newline at end of file diff --git a/api/BusinessLogic/ServiceMessages/GetServiceMessagesFilter.php b/api/BusinessLogic/ServiceMessages/GetServiceMessagesFilter.php new file mode 100644 index 00000000..a8f381d6 --- /dev/null +++ b/api/BusinessLogic/ServiceMessages/GetServiceMessagesFilter.php @@ -0,0 +1,12 @@ +serviceMessageGateway = $serviceMessagesGateway; + } + + function createServiceMessage($serviceMessage, $heskSettings) { + $this->validate($serviceMessage, $heskSettings); + + if ($serviceMessage->icon === null) { + switch ($serviceMessage->style) { + case ServiceMessageStyle::NONE: + $serviceMessage->icon = ''; + break; + case ServiceMessageStyle::INFO: + $serviceMessage->icon = 'fa fa-comment'; + break; + case ServiceMessageStyle::NOTICE: + $serviceMessage->icon = 'fa fa-exclamation-triangle'; + break; + case ServiceMessageStyle::ERROR: + $serviceMessage->icon = 'fa fa-times-circle'; + break; + case ServiceMessageStyle::SUCCESS: + $serviceMessage->icon = 'fa fa-check-circle'; + break; + } + } + + return $this->serviceMessageGateway->createServiceMessage($serviceMessage, $heskSettings); + } + + function getServiceMessages($heskSettings, $searchFilter) { + return $this->serviceMessageGateway->getServiceMessages($heskSettings, $searchFilter); + } + + function editServiceMessage($serviceMessage, $heskSettings) { + $this->validate($serviceMessage, $heskSettings, false); + + if ($serviceMessage->icon === null) { + switch ($serviceMessage->style) { + case ServiceMessageStyle::NONE: + $serviceMessage->icon = ''; + break; + case ServiceMessageStyle::INFO: + $serviceMessage->icon = 'fa fa-comment'; + break; + case ServiceMessageStyle::NOTICE: + $serviceMessage->icon = 'fa fa-exclamation-triangle'; + break; + case ServiceMessageStyle::ERROR: + $serviceMessage->icon = 'fa fa-times-circle'; + break; + case ServiceMessageStyle::SUCCESS: + $serviceMessage->icon = 'fa fa-check-circle'; + break; + } + } + + return $this->serviceMessageGateway->updateServiceMessage($serviceMessage, $heskSettings); + } + + function deleteServiceMessage($id, $heskSettings) { + $this->serviceMessageGateway->deleteServiceMessage($id, $heskSettings); + } + + function sortServiceMessage($id, $direction, $heskSettings) { + $serviceMessages = $this->serviceMessageGateway->getServiceMessages($heskSettings, new GetServiceMessagesFilter()); + $serviceMessage = null; + foreach ($serviceMessages as $innerServiceMessage) { + if (intval($innerServiceMessage->id) === intval($id)) { + $serviceMessage = $innerServiceMessage; + break; + } + } + + if ($serviceMessage === null) { + throw new \BaseException("Could not find service message with ID {$id}!"); + } + + if ($direction === Direction::UP) { + $serviceMessage->order -= 15; + } else { + $serviceMessage->order += 15; + } + + $this->serviceMessageGateway->updateServiceMessage($serviceMessage, $heskSettings); + $this->serviceMessageGateway->resortAllServiceMessages($heskSettings); + } + + /** + * @param $serviceMessage ServiceMessage + * @param bool $isNew + * @throws ValidationException + */ + private function validate($serviceMessage, $heskSettings, $isNew = true) { + $validationModel = new ValidationModel(); + if ($isNew && $serviceMessage->createdBy < 1) { + $validationModel->errorKeys[] = 'MISSING_CREATOR'; + } + + if ($serviceMessage->message === null || trim($serviceMessage->message) === '') { + $validationModel->errorKeys[] = 'MISSING_MESSAGE'; + } else { + $htmlPurifier = new \HeskHTMLPurifier($heskSettings['cache_dir']); + $serviceMessage->message = $htmlPurifier->heskPurify($serviceMessage->message); + } + if ($serviceMessage->language === null || trim($serviceMessage->language) === '') { + $validationModel->errorKeys[] = 'MISSING_LANGUAGE'; + } + + $languageFound = false; + foreach ($heskSettings['languages'] as $key => $value) { + if ($value['folder'] === $serviceMessage->language || $serviceMessage->language === 'ALL') { + $languageFound = true; + break; + } + } + if (!$languageFound && !in_array('MISSING_LANGUAGE', $validationModel->errorKeys)) { + $validationModel->errorKeys[] = 'LANGUAGE_NOT_INSTALLED'; + } + + if ($serviceMessage->title === null || trim($serviceMessage->title) === '') { + $validationModel->errorKeys[] = 'MISSING_TITLE'; + } + if ($serviceMessage->style === null || trim($serviceMessage->style) === '') { + $validationModel->errorKeys[] = 'MISSING_STYLE'; + } + try { + ServiceMessageStyle::getIdForStyle($serviceMessage->style); + } catch (\Exception $e) { + $validationModel->errorKeys[] = 'INVALID_STYLE'; + } + if ($serviceMessage->locations === null || count($serviceMessage->locations) === 0) { + $validationModel->errorKeys[] = 'MISSING_LOCATIONS'; + } else { + $locations = ServiceMessageLocation::getAll(); + foreach ($serviceMessage->locations as $location) { + if (!in_array($location, $locations)) { + $validationModel->errorKeys[] = 'INVALID_LOCATION'; + break; + } + } + } + + if (count($validationModel->errorKeys) > 0) { + // Validation failed + throw new ValidationException($validationModel); + } + } +} \ No newline at end of file diff --git a/api/BusinessLogic/ServiceMessages/ServiceMessageLocation.php b/api/BusinessLogic/ServiceMessages/ServiceMessageLocation.php new file mode 100644 index 00000000..7f918bc7 --- /dev/null +++ b/api/BusinessLogic/ServiceMessages/ServiceMessageLocation.php @@ -0,0 +1,34 @@ + self::NONE, + 1 => self::SUCCESS, + 2 => self::INFO, + 3 => self::NOTICE, + 4 => self::ERROR + ); + + if (!isset($styles[$id])) { + throw new \Exception("Style {$id} is not a valid service message style."); + } + + return $styles[$id]; + } + + static function getIdForStyle($style) { + $styles = array( + self::NONE => 0, + self::SUCCESS => 1, + self::INFO => 2, + self::NOTICE => 3, + self::ERROR => 4 + ); + + if (!isset($styles[$style])) { + throw new \Exception("Style {$style} is not a valid service message style."); + } + + return $styles[$style]; + } +} \ No newline at end of file diff --git a/api/Controllers/ServiceMessages/ServiceMessagesController.php b/api/Controllers/ServiceMessages/ServiceMessagesController.php new file mode 100644 index 00000000..48da1a7d --- /dev/null +++ b/api/Controllers/ServiceMessages/ServiceMessagesController.php @@ -0,0 +1,139 @@ +admin && !in_array(UserPrivilege::CAN_MANAGE_SERVICE_MESSAGES, $userContext->permissions)) { + throw new ApiFriendlyException("User does not have permission to access the following URI: " . $_SERVER['REQUEST_URI'], "Access Forbidden", 403); + } + } + + static function staticCheckSecurity($userContext) { + if (!$userContext->admin && !in_array(UserPrivilege::CAN_MANAGE_SERVICE_MESSAGES, $userContext->permissions)) { + throw new ApiFriendlyException("User does not have permission to access the following URI: " . $_SERVER['REQUEST_URI'], "Access Forbidden", 403); + } + } + + function get() { + /* @var $userContext UserContext */ + /* @var $hesk_settings array */ + global $applicationContext, $hesk_settings, $userContext; + + $searchFilter = new GetServiceMessagesFilter(); + if ($userContext->isAnonymousUser()) { + $searchFilter->includeDrafts = false; + $searchFilter->includeStaffServiceMessages = false; + } elseif (!$userContext->admin && !in_array(UserPrivilege::CAN_MANAGE_SERVICE_MESSAGES, $userContext->permissions)) { + $searchFilter->includeDrafts = false; + } + + /* @var $handler ServiceMessageHandler */ + $handler = $applicationContext->get(ServiceMessageHandler::clazz()); + + return output($handler->getServiceMessages($hesk_settings, $searchFilter)); + } + + function post() { + global $applicationContext, $userContext, $hesk_settings; + + $this->checkSecurity($userContext); + + /* @var $handler ServiceMessageHandler */ + $handler = $applicationContext->get(ServiceMessageHandler::clazz()); + + $data = JsonRetriever::getJsonData(); + $element = $handler->createServiceMessage($this->buildElementModel($data, $userContext), $hesk_settings); + + return output($element, 201); + } + + function put($id) { + global $applicationContext, $hesk_settings, $userContext; + + $this->checkSecurity($userContext); + + /* @var $handler ServiceMessageHandler */ + $handler = $applicationContext->get(ServiceMessageHandler::clazz()); + + $data = JsonRetriever::getJsonData(); + $serviceMessage = $this->buildElementModel($data, null, false); + $serviceMessage->id = $id; + $element = $handler->editServiceMessage($serviceMessage, $hesk_settings); + + return output($element); + } + + function delete($id) { + global $applicationContext, $hesk_settings, $userContext; + + $this->checkSecurity($userContext); + + /* @var $handler ServiceMessageHandler */ + $handler = $applicationContext->get(ServiceMessageHandler::clazz()); + + $handler->deleteServiceMessage($id, $hesk_settings); + + return http_response_code(204); + } + + /** + * @param $data array + * @param $userContext UserContext + * @return ServiceMessage + */ + private function buildElementModel($data, $userContext, $creating = true) { + $serviceMessage = new ServiceMessage(); + + if (!$creating) { + $serviceMessage->order = Helpers::safeArrayGet($data, 'order'); + } + + if ($creating) { + $serviceMessage->createdBy = $userContext->id; + } + + $serviceMessage->title = Helpers::safeArrayGet($data, 'title'); + $serviceMessage->icon = Helpers::safeArrayGet($data, 'icon'); + $serviceMessage->message = Helpers::safeArrayGet($data, 'message'); + $serviceMessage->published = Helpers::safeArrayGet($data, 'published'); + $serviceMessage->style = Helpers::safeArrayGet($data, 'style'); + $serviceMessage->language = Helpers::safeArrayGet($data, 'language'); + + $jsonLocations = Helpers::safeArrayGet($data, 'locations'); + + if ($jsonLocations !== null && !empty($jsonLocations)) { + foreach ($jsonLocations as $key => $value) { + $serviceMessage->locations[] = $value; + } + } + + return $serviceMessage; + } + + static function sort($id, $direction) { + /* @var $userContext UserContext */ + global $applicationContext, $hesk_settings, $userContext; + + self::staticCheckSecurity($userContext); + + /* @var $handler ServiceMessageHandler */ + $handler = $applicationContext->get(ServiceMessageHandler::clazz()); + + $handler->sortServiceMessage(intval($id), $direction, $hesk_settings); + } +} \ No newline at end of file diff --git a/api/Core/json_error.php b/api/Core/json_error.php index 1ff438c0..c9ea1b61 100644 --- a/api/Core/json_error.php +++ b/api/Core/json_error.php @@ -7,7 +7,11 @@ function print_error($title, $message, $logId = null, $response_code = 500) { $error['type'] = 'ERROR'; $error['title'] = $title; $error['message'] = $message; - $error['logId'] = $logId; + + if ($logId !== null) { + $error['logId'] = $logId; + } + print output($error, $response_code); return; diff --git a/api/DataAccess/ServiceMessages/ServiceMessagesGateway.php b/api/DataAccess/ServiceMessages/ServiceMessagesGateway.php new file mode 100644 index 00000000..e15a593e --- /dev/null +++ b/api/DataAccess/ServiceMessages/ServiceMessagesGateway.php @@ -0,0 +1,182 @@ +init(); + + // Get the latest service message order + $res = hesk_dbQuery("SELECT `order` FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "service_messages` ORDER BY `order` DESC LIMIT 1"); + $row = hesk_dbFetchRow($res); + $myOrder = intval($row[0]) + 10; + + $style = ServiceMessageStyle::getIdForStyle($serviceMessage->style); + $type = $serviceMessage->published ? 0 : 1; + + // Insert service message into database + hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($heskSettings['db_pfix']) . "service_messages` (`author`,`title`,`message`,`style`,`type`,`order`, `icon`, `mfh_language`) VALUES ( + '" . intval($serviceMessage->createdBy) . "', + '" . hesk_dbEscape($serviceMessage->title) . "', + '" . hesk_dbEscape($serviceMessage->message) . "', + '" . hesk_dbEscape($style) . "', + '{$type}', + '{$myOrder}', + '" . hesk_dbEscape($serviceMessage->icon) . "', + '" . hesk_dbEscape($serviceMessage->language) . "' + )"); + + $serviceMessage->id = hesk_dbInsertID(); + + foreach ($serviceMessage->locations as $location) { + hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($heskSettings['db_pfix']) . "mfh_service_message_to_location` + (`service_message_id`, `location`) VALUES (" . intval($serviceMessage->id) . ", '" . hesk_dbEscape($location) . "')"); + } + + // Get the autogenerated fields + $rs = hesk_dbQuery("SELECT `dt`, `order` FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "service_messages` + WHERE `id` = " . intval($serviceMessage->id)); + $row = hesk_dbFetchAssoc($rs); + $serviceMessage->dateCreated = $row['dt']; + $serviceMessage->order = intval($row['order']); + + $this->close(); + + return $serviceMessage; + } + + /** + * @param $heskSettings + * @param $searchFilter GetServiceMessagesFilter + * @return ServiceMessage[] + */ + function getServiceMessages($heskSettings, $searchFilter) { + $this->init(); + + $serviceMessages = array(); + + $sql = "SELECT DISTINCT `service_messages`.* FROM `". hesk_dbEscape($heskSettings['db_pfix']) . "service_messages` AS `service_messages` "; + + if (!$searchFilter->includeStaffServiceMessages) { + $sql .= "INNER JOIN `" . hesk_dbEscape($heskSettings['db_pfix']) . "mfh_service_message_to_location` AS `location` + ON `location`.`service_message_id` = `service_messages`.`id` AND `location`.`location` LIKE 'CUSTOMER%' "; + } + + if (!$searchFilter->includeDrafts) { + $sql .= "WHERE `type` = '0' "; + } + + $sql .= "ORDER BY `order`"; + + + $rs = hesk_dbQuery($sql); + while ($row = hesk_dbFetchAssoc($rs)) { + $serviceMessage = new ServiceMessage(); + $serviceMessage->id = $row['id']; + $serviceMessage->published = intval($row['type']) !== 1; + $serviceMessage->createdBy = intval($row['author']); + $serviceMessage->order = intval($row['order']); + $serviceMessage->dateCreated = $row['dt']; + $serviceMessage->title = $row['title']; + $serviceMessage->message = $row['message']; + $serviceMessage->style = ServiceMessageStyle::getStyleById($row['style']); + $serviceMessage->icon = $row['icon']; + $serviceMessage->language = $row['mfh_language']; + $serviceMessage->locations = array(); + + $locationSql = "SELECT `location` FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "mfh_service_message_to_location` + WHERE `service_message_id` = " . intval($serviceMessage->id); + + if (!$searchFilter->includeStaffServiceMessages) { + $locationSql .= " AND `location` LIKE 'CUSTOMER%'"; + } + + $locationsRs = hesk_dbQuery($locationSql); + while ($innerRow = hesk_dbFetchAssoc($locationsRs)) { + $serviceMessage->locations[] = $innerRow['location']; + } + + $serviceMessages[] = $serviceMessage; + } + + $this->close(); + + return $serviceMessages; + } + + function updateServiceMessage($serviceMessage, $heskSettings) { + $this->init(); + + $style = ServiceMessageStyle::getIdForStyle($serviceMessage->style); + $type = $serviceMessage->published ? 0 : 1; + + hesk_dbQuery("UPDATE `" . hesk_dbEscape($heskSettings['db_pfix']) . "service_messages` + SET `title` = '" . hesk_dbEscape($serviceMessage->title) . "', + `message` = '" . hesk_dbEscape($serviceMessage->message) . "', + `style` = '" . intval($style) . "', + `type` = '{$type}', + `icon` = '" . hesk_dbEscape($serviceMessage->icon) . "', + `order` = " . intval($serviceMessage->order) . ", + `mfh_language` = '" . hesk_dbEscape($serviceMessage->language) . "' + WHERE `id` = " . intval($serviceMessage->id)); + + hesk_dbQuery("DELETE FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "mfh_service_message_to_location` + WHERE `service_message_id` = " . intval($serviceMessage->id)); + foreach ($serviceMessage->locations as $location) { + hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($heskSettings['db_pfix']) . "mfh_service_message_to_location` + (`service_message_id`, `location`) VALUES (" . intval($serviceMessage->id) . ", '" . hesk_dbEscape($location) . "')"); + } + + $otherFieldsRs = hesk_dbQuery("SELECT `dt`, `author`, `order` FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "service_messages` + WHERE `id` = " . intval($serviceMessage->id)); + $otherFields = hesk_dbFetchAssoc($otherFieldsRs); + + $serviceMessage->createdBy = intval($otherFields['author']); + $serviceMessage->dateCreated = $otherFields['dt']; + + $this->close(); + + return $serviceMessage; + } + + function deleteServiceMessage($id, $heskSettings) { + $this->init(); + + hesk_dbQuery("DELETE FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "mfh_service_message_to_location` + WHERE `service_message_id` = " . intval($id)); + + hesk_dbQuery("DELETE FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "service_messages` + WHERE `id` = " . intval($id)); + + $this->close(); + } + + function resortAllServiceMessages($heskSettings) { + $this->init(); + + $rs = hesk_dbQuery("SELECT `id` FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "service_messages` + ORDER BY `order` ASC"); + + $sortValue = 10; + while ($row = hesk_dbFetchAssoc($rs)) { + hesk_dbQuery("UPDATE `" . hesk_dbEscape($heskSettings['db_pfix']) . "service_messages` + SET `order` = " . intval($sortValue) . " + WHERE `id` = " . intval($row['id'])); + + $sortValue += 10; + } + + $this->close(); + } +} \ No newline at end of file diff --git a/api/autoload.php b/api/autoload.php index 0f14ff20..cf548485 100644 --- a/api/autoload.php +++ b/api/autoload.php @@ -12,6 +12,7 @@ require_once(__DIR__ . '/Core/output.php'); require_once(__DIR__ . '/../hesk_settings.inc.php'); require_once(__DIR__ . '/http_response_code.php'); require_once(__DIR__ . '/../inc/admin_functions.inc.php'); +require_once(__DIR__ . '/../inc/htmlpurifier/HeskHTMLPurifier.php'); hesk_load_api_database_functions(); diff --git a/api/index.php b/api/index.php index 7c0574b2..ebdb36c7 100644 --- a/api/index.php +++ b/api/index.php @@ -45,8 +45,15 @@ function internalOrAuthHandler() { function publicHandler() { global $userContext; - //-- Create an "anonymous" UserContext - $userContext = \BusinessLogic\Security\UserContext::buildAnonymousUser(); + // Check if we passed in a X-Auth-Token or X-Internal-Call header. Those take priority + if (\BusinessLogic\Helpers::getHeader('X-INTERNAL-CALL') === 'true') { + internalHandler(); + } elseif (\BusinessLogic\Helpers::getHeader('X-AUTH-TOKEN') !== null) { + authTokenHandler(); + } else { + //-- Create an "anonymous" UserContext + $userContext = \BusinessLogic\Security\UserContext::buildAnonymousUser(); + } } function assertApiIsEnabled() { @@ -105,7 +112,7 @@ function exceptionHandler($exception) { /* @var $castedException \BusinessLogic\Exceptions\ApiFriendlyException */ $castedException = $exception; - print_error($castedException->title, $castedException->getMessage(), $castedException->httpResponseCode); + print_error($castedException->title, $castedException->getMessage(), null, $castedException->httpResponseCode); } elseif (exceptionIsOfType($exception, \Core\Exceptions\SQLException::clazz())) { /* @var $castedException \Core\Exceptions\SQLException */ $castedException = $exception; @@ -207,6 +214,16 @@ Link::all(array( '/v1/calendar/events' => action(\Controllers\Calendar\CalendarController::clazz(), array(RequestMethod::GET), SecurityHandler::OPEN), '/v1/calendar/events/staff' => action(\Controllers\Calendar\CalendarController::clazz(), array(RequestMethod::GET, RequestMethod::POST), SecurityHandler::INTERNAL_OR_AUTH_TOKEN), '/v1/calendar/events/staff/{i}' => action(\Controllers\Calendar\CalendarController::clazz(), array(RequestMethod::PUT, RequestMethod::DELETE), SecurityHandler::INTERNAL_OR_AUTH_TOKEN), + // Service Messages + '/v1/service-messages' => action(\Controllers\ServiceMessages\ServiceMessagesController::clazz(), + array(RequestMethod::GET, RequestMethod::POST), + SecurityHandler::OPEN), + '/v1/service-messages/{i}' => action(\Controllers\ServiceMessages\ServiceMessagesController::clazz(), + array(RequestMethod::PUT, RequestMethod::DELETE), + SecurityHandler::INTERNAL_OR_AUTH_TOKEN), + '/v1-internal/service-messages/{i}/sort/{s}' => action(\Controllers\ServiceMessages\ServiceMessagesController::clazz() . '::sort', + array(RequestMethod::POST), + SecurityHandler::INTERNAL), /* Internal use only routes */ // Resend email response diff --git a/inc/common.inc.php b/inc/common.inc.php index 92fe0aa4..b6ce26f9 100644 --- a/inc/common.inc.php +++ b/inc/common.inc.php @@ -185,12 +185,34 @@ function hesk_service_message($sm) ?>
    '; ?> - +
    +

    -

    @@ -322,6 +321,13 @@ function print_add_ticket()

    +
    @@ -1315,15 +1321,13 @@ function print_start() 0) - { + $service_messages = mfh_get_service_messages('CUSTOMER_HOME'); + if (count($service_messages) > 0) { ?>
    diff --git a/install/migrations/core.php b/install/migrations/core.php index f52740e7..798f7fa9 100644 --- a/install/migrations/core.php +++ b/install/migrations/core.php @@ -218,5 +218,9 @@ function getAllMigrations() { 161 => new UpdateMigration('3.2.2', '3.2.1', 161), 162 => new UpdateMigration('3.2.3', '3.2.2', 162), 163 => new UpdateMigration('3.2.4', '3.2.3', 163), + // 3.3.0 + 164 => new \v330\ServiceMessagesImprovements\CreateServiceMessageToLocationTable(164), + 165 => new \v330\ServiceMessagesImprovements\UpdateExistingServiceMessagesLocations(165), + 166 => new \v330\ServiceMessagesImprovements\AddLanguageColumnToServiceMessages(166), ); } \ No newline at end of file diff --git a/install/migrations/v330/ServiceMessagesImprovements/AddLanguageColumnToServiceMessages.php b/install/migrations/v330/ServiceMessagesImprovements/AddLanguageColumnToServiceMessages.php new file mode 100644 index 00000000..21481b99 --- /dev/null +++ b/install/migrations/v330/ServiceMessagesImprovements/AddLanguageColumnToServiceMessages.php @@ -0,0 +1,17 @@ +executeQuery("ALTER TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "service_messages` + ADD COLUMN `mfh_language` VARCHAR(255) NOT NULL DEFAULT 'ALL'"); + } + + function innerDown($hesk_settings) { + $this->executeQuery("ALTER TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "service_messages` + DROP COLUMN `mfh_language`"); + } +} \ No newline at end of file diff --git a/install/migrations/v330/ServiceMessagesImprovements/CreateServiceMessageToLocationTable.php b/install/migrations/v330/ServiceMessagesImprovements/CreateServiceMessageToLocationTable.php new file mode 100644 index 00000000..0fe7d1ec --- /dev/null +++ b/install/migrations/v330/ServiceMessagesImprovements/CreateServiceMessageToLocationTable.php @@ -0,0 +1,16 @@ +executeQuery("CREATE TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "mfh_service_message_to_location` + (`service_message_id` INT NOT NULL, `location` VARCHAR(100) NOT NULL)"); + } + + function innerDown($hesk_settings) { + $this->executeQuery("DROP TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "mfh_service_message_to_location`"); + } +} \ No newline at end of file diff --git a/install/migrations/v330/ServiceMessagesImprovements/UpdateExistingServiceMessagesLocations.php b/install/migrations/v330/ServiceMessagesImprovements/UpdateExistingServiceMessagesLocations.php new file mode 100644 index 00000000..b9da7cc4 --- /dev/null +++ b/install/migrations/v330/ServiceMessagesImprovements/UpdateExistingServiceMessagesLocations.php @@ -0,0 +1,19 @@ +executeQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "mfh_service_message_to_location` (`service_message_id`, `location`) + SELECT `id`, '" . hesk_dbEscape(ServiceMessageLocation::CUSTOMER_HOME) . "' FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "service_messages`"); + } + + function innerDown($hesk_settings) { + $this->executeQuery("DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "mfh_service_message_to_location` + WHERE `service_message_id` IN (SELECT `id` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "service_messages`)"); + } +} \ No newline at end of file diff --git a/internal-api/js/service-messages.js b/internal-api/js/service-messages.js new file mode 100644 index 00000000..e75719eb --- /dev/null +++ b/internal-api/js/service-messages.js @@ -0,0 +1,361 @@ +var serviceMessages = []; + +var g_styles = []; +g_styles["ERROR"] = 4; +g_styles["NOTICE"] = 3; +g_styles["INFO"] = 2; +g_styles["SUCCESS"] = 1; +g_styles["NONE"] = 0; + +$(document).ready(function() { + loadTable(); + bindEditModal(); + bindFormSubmit(); + bindDeleteButton(); + bindCreateModal(); + bindSortButtons(); + bindPreview(); +}); + + +function loadTable() { + $('#overlay').show(); + var heskUrl = $('p#hesk-path').text(); + var $tableBody = $('#table-body'); + + $.ajax({ + method: 'GET', + url: heskUrl + 'api/index.php/v1/service-messages', + headers: { 'X-Internal-Call': true }, + success: function(data) { + $tableBody.html(''); + + if (data.length === 0) { + $tableBody.append('' + mfhLang.text('no_sm') + ''); + $('#overlay').hide(); + return; + } + + var first = true; + var lastElement = null; + $.each(data, function() { + var $template = $($('#service-message-template').html()); + + $template.find('[data-property="id"]').attr('data-value', this.id); + $template.find('span[data-property="title"]').html( + getFormattedTitle(this.icon, this.title, this.style)); + $template.find('span[data-property="author"]').text(users[this.createdBy].name); + if (this.published) { + $template.find('span[data-property="type"]').text(mfhLang.text('sm_published')); + } else { + $template.find('span[data-property="type"]').text(mfhLang.text('sm_draft')); + } + $template.find('[data-property="language"]').text(this.language === 'ALL' ? + mfhLang.text('all') : + languages[this.language]); + + $tableBody.append($template); + + serviceMessages[this.id] = this; + + lastElement = this; + + if (first) { + $template.find('[data-direction="up"]').css('visibility', 'hidden'); + first = false; + } + }); + + if (lastElement) { + //-- Hide the down arrow on the last element + $('[data-value="' + lastElement.id + '"]').parent().parent() + .find('[data-direction="down"]').css('visibility', 'hidden'); + } + }, + error: function(data) { + mfhAlert.errorWithLog(mfhLang.text('error_retrieving_sm'), data.responseJSON); + console.error(data); + }, + complete: function() { + $('#overlay').hide(); + } + }); +} + +function getFormattedTitle(icon, title, style) { + var $template = $($('#service-message-title-template').html()); + + var alertClass = 'none'; + switch (style) { + case 'ERROR': + alertClass = 'alert alert-danger'; + break; + case 'NOTICE': + alertClass = 'alert alert-warning'; + break; + case 'INFO': + alertClass = 'alert alert-info'; + break; + case 'SUCCESS': + alertClass = 'alert alert-success'; + break; + } + $template.addClass(alertClass) + .find('[data-property="icon"]').addClass(icon).end() + .find('[data-property="title"]').text(title); + + return $template; +} + +function getServiceMessagePreview(icon, title, message, style) { + var $template = $('#service-message-preview-template').html(); + + var alertClass = 'none'; + switch (style) { + case 'ERROR': + alertClass = 'alert alert-danger'; + break; + case 'NOTICE': + alertClass = 'alert alert-warning'; + break; + case 'INFO': + alertClass = 'alert alert-info'; + break; + case 'SUCCESS': + alertClass = 'alert alert-success'; + break; + } + $template = $template.replace('none', alertClass) + .replace('{{TITLE}}', title) + .replace('{{MESSAGE}}', message); + $template = $($template); + if (icon !== '') { + $template.find('i.fa').removeClass('fa').addClass(icon); + } + + return $template; +} + +function bindEditModal() { + $(document).on('click', '[data-action="edit"]', function() { + var element = serviceMessages[$(this).parent().parent().find('[data-property="id"]').data('value')]; + var $modal = $('#service-message-modal'); + $modal.find('#preview-pane').html('').end() + .find('input[name="location[]"]').prop('checked', false); + + $modal.find('#edit-label').show(); + $modal.find('#create-label').hide(); + + $modal.find('input[name="style"][value="' + (g_styles[element.style]) + '"]').prop('checked', 'checked').end() + .find('input[name="type"][value="' + (element.published ? 0 : 1) + '"]') + .prop('checked', 'checked').end() + .find('input[name="title"]').val(element.title).end() + .find('input[name="id"]').val(element.id).end() + .find('input[name="order"]').val(element.order).end() + .find('select[name="language"]').val(element.language).end(); + setIcon(element.icon); + + $.each(element.locations, function() { + $modal.find('input[name="location[]"][value="' + this + '"]').prop('checked', 'checked'); + }); + + if ($('input[name="kb_wysiwyg"]').val() === "1") { + tinyMCE.get('content').setContent(element.message); + } else { + $('textarea[name="message"]').val(element.message); + } + + $('.tab-pane#sm-contents').addClass('active'); + $('.tab-pane#properties').removeClass('active'); + $('.nav-tabs > li').removeClass('active'); + $('.nav-tabs > li:first').addClass('active'); + + + $modal.modal('show'); + }); +} + +function bindCreateModal() { + $('#create-button').click(function() { + var $modal = $('#service-message-modal'); + $modal.find('#edit-label').hide().end() + .find('#create-label').show().end() + .find('input[name="style"][value="0"]').prop('checked', 'checked').end() // "None" style + .find('input[name="type"][value="0"]').prop('checked', 'checked').end() // Published + .find('input[name="title"]').val('').end() + .find('input[name="id"]').val(-1).end() + .find('input[name="order"]').val('').end() + .find('#preview-pane').html('').end() + .find('input[name="location[]"]').prop('checked', false) + .find('select[name="language"]').val('ALL'); + setIcon(''); + + if ($('input[name="kb_wysiwyg"]').val() === "1") { + tinyMCE.get('content').setContent(''); + } else { + $('textarea[name="message"]').val(''); + } + + $('.tab-pane#sm-contents').addClass('active'); + $('.tab-pane#properties').removeClass('active'); + $('.nav-tabs > li').removeClass('active'); + $('.nav-tabs > li:first').addClass('active'); + + $modal.modal('show'); + }); +} + +function bindFormSubmit() { + $('form#service-message').submit(function(e) { + e.preventDefault(); + var heskUrl = $('p#hesk-path').text(); + + var $modal = $('#service-message-modal'); + + var styles = []; + styles[0] = "NONE"; + styles[1] = "SUCCESS"; + styles[2] = "INFO"; + styles[3] = "NOTICE"; + styles[4] = "ERROR"; + + var domLocations = $modal.find('input[name="location[]"]:checked'); + + var locations = []; + $.each(domLocations, function() { + locations.push($(this).val()); + }); + + var data = { + icon: $modal.find('input[name="icon"]').val(), + title: $modal.find('input[name="title"]').val(), + message: getMessage(), + published: $modal.find('input[name="type"]:checked').val() === "0", + style: styles[$modal.find('input[name="style"]:checked').val()], + order: $modal.find('input[name="order"]').val(), + language: $modal.find('select[name="language"]').val(), + locations: locations + }; + + var url = heskUrl + 'api/index.php/v1/service-messages/'; + var method = 'POST'; + + var serviceMessageId = parseInt($modal.find('input[name="id"]').val()); + if (serviceMessageId !== -1) { + url += serviceMessageId; + method = 'PUT'; + } + + $modal.find('#action-buttons').find('.cancel-button').attr('disabled', 'disabled'); + $modal.find('#action-buttons').find('.save-button').attr('disabled', 'disabled'); + + $.ajax({ + method: 'POST', + url: url, + headers: { + 'X-Internal-Call': true, + 'X-HTTP-Method-Override': method + }, + data: JSON.stringify(data), + success: function(data) { + if (serviceMessageId === -1) { + mfhAlert.success(mfhLang.text('sm_added')); + } else { + mfhAlert.success(mfhLang.text('sm_mdf')); + } + $modal.modal('hide'); + loadTable(); + }, + error: function(data) { + mfhAlert.errorWithLog(mfhLang.text('error_saving_updating_sm'), data.responseJSON); + console.error(data); + }, + complete: function(data) { + $modal.find('#action-buttons').find('.cancel-button').removeAttr('disabled'); + $modal.find('#action-buttons').find('.save-button').removeAttr('disabled'); + } + }); + }); +} + +function bindDeleteButton() { + $(document).on('click', '[data-action="delete"]', function() { + $('#overlay').show(); + + var heskUrl = $('p#hesk-path').text(); + var element = serviceMessages[$(this).parent().parent().find('[data-property="id"]').data('value')]; + + $.ajax({ + method: 'POST', + url: heskUrl + 'api/index.php/v1/service-messages/' + element.id, + headers: { + 'X-Internal-Call': true, + 'X-HTTP-Method-Override': 'DELETE' + }, + success: function() { + mfhAlert.success(mfhLang.text('sm_deleted')); + loadTable(); + }, + error: function(data) { + $('#overlay').hide(); + mfhAlert.errorWithLog(mfhLang.text('error_deleting_sm'), data.responseJSON); + console.error(data); + } + }); + }); +} + +function bindSortButtons() { + $(document).on('click', '[data-action="sort"]', function() { + $('#overlay').show(); + var heskUrl = $('p#hesk-path').text(); + var direction = $(this).data('direction'); + var element = serviceMessages[$(this).parent().parent().parent().find('[data-property="id"]').data('value')]; + + $.ajax({ + method: 'POST', + url: heskUrl + 'api/index.php/v1-internal/service-messages/' + element.id + '/sort/' + direction, + headers: { 'X-Internal-Call': true }, + success: function() { + loadTable(); + }, + error: function(data) { + mfhAlert.errorWithLog(mfhLang.text('error_sorting_categories'), data.responseJSON); + console.error(data); + $('#overlay').hide(); + } + }) + }); +} + +function bindPreview() { + $('.preview-button').click(function() { + var styles = []; + styles[0] = "NONE"; + styles[1] = "SUCCESS"; + styles[2] = "INFO"; + styles[3] = "NOTICE"; + styles[4] = "ERROR"; + + var $modal = $('#service-message-modal'); + var data = { + icon: $modal.find('input[name="icon"]').val(), + title: $modal.find('input[name="title"]').val(), + message: getMessage(), + published: $modal.find('input[name="type"]:checked').val() === "0", + style: styles[$modal.find('input[name="style"]:checked').val()], + order: $modal.find('input[name="order"]').val() + }; + + var preview = getServiceMessagePreview(data.icon, data.title, data.message, data.style); + $('#preview-pane').html(preview); + }); +} + +function getMessage() { + if ($('input[name="kb_wysiwyg"]').val() === "1") { + return tinyMCE.get('content').getContent(); + } + + return $('textarea[name="message"]').val(); +} \ No newline at end of file diff --git a/knowledgebase.php b/knowledgebase.php index fdb2a3a4..22e86586 100644 --- a/knowledgebase.php +++ b/knowledgebase.php @@ -254,6 +254,13 @@ if (!$show['show']) { require_once(HESK_PATH . 'inc/header.inc.php'); hesk_kb_header($hesk_settings['kb_link']); + + // Service messages + $service_messages = mfh_get_service_messages('CUSTOMER_VIEW_KB_ARTICLE'); + foreach ($service_messages as $sm) { + hesk_service_message($sm); + } + // Update views by 1 - exclude known bots and reloads because of ratings if (!isset($_GET['rated']) && !hesk_detect_bots()) { hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "kb_articles` SET `views`=`views`+1 WHERE `id`={$artid}"); @@ -406,11 +413,11 @@ if (!$show['show']) { hesk_kb_header($hesk_settings['kb_link']); } - // If we are in "Knowledgebase only" mode show system messages - if ($catid == 1 && hesk_check_kb_only(false)) { + // Display service messages on the default category + if ($catid == 1) { // Service messages - $res = hesk_dbQuery('SELECT `title`, `message`, `style` FROM `' . hesk_dbEscape($hesk_settings['db_pfix']) . "service_messages` WHERE `type`='0' ORDER BY `order` ASC"); - while ($sm = hesk_dbFetchAssoc($res)) { + $service_messages = mfh_get_service_messages('CUSTOMER_KB_HOME'); + foreach ($service_messages as $sm) { hesk_service_message($sm); } } diff --git a/language/en/text.php b/language/en/text.php index 6f5e9838..51c2470a 100644 --- a/language/en/text.php +++ b/language/en/text.php @@ -2217,6 +2217,19 @@ $hesklang['audit_unlinked_ticket'] = '%s unlinked ticket %s'; // Added or modified in Mods for HESK 3.3.0 $hesklang['audit_event_created'] = '%s created event'; $hesklang['audit_event_updated'] = '%s updated event'; +$hesklang['error_retrieving_sm'] = 'An error occurred retrieving service messages!'; +$hesklang['error_saving_updating_sm'] = 'An error occurred creating / saving the service message!'; +$hesklang['error_deleting_sm'] = 'An error occurred when trying to delete the service message.'; +$hesklang['error_sorting_sm'] = 'An error occurred sorting service messages!'; +$hesklang['sm_location'] = 'Location'; // Location for service messages +$hesklang['sm_customer_pages'] = 'Customer Pages'; +$hesklang['sm_staff_pages'] = 'Staff Pages'; +$hesklang['sm_homepage'] = 'Homepage'; +$hesklang['sm_kb_home'] = 'Knowledgebase Home'; +$hesklang['sm_view_kb_article'] = 'View Knowledgebase Article'; +$hesklang['sm_submit_ticket'] = 'Submit Ticket'; +$hesklang['sm_view_ticket'] = 'View Ticket'; +$hesklang['sm_login_page'] = 'Login Page'; // DO NOT CHANGE BELOW if (!defined('IN_SCRIPT')) die('PHP syntax OK!'); diff --git a/ticket.php b/ticket.php index cb7ef11b..316dac96 100644 --- a/ticket.php +++ b/ticket.php @@ -215,6 +215,13 @@ if (!$show['show']) {