-
-
-
+
+
- |
- |
- |
+
+ |
+ |
+ |
+
@@ -96,13 +101,13 @@ while ($row = hesk_dbFetchAssoc($res)) {
|
-
-
|
@@ -137,11 +142,9 @@ function createEditModal($template, $features, $categories)
{
global $hesklang;
- $disabled = 'checked="checked" disabled';
$enabledFeatures = array();
$enabledCategories = array();
- if ($template['heskprivileges'] != 'ALL') {
- $disabled = '';
+ if ($template['heskprivileges'] !== 'ALL') {
$enabledFeatures = explode(',', $template['heskprivileges']);
$enabledCategories = explode(',', $template['categories']);
}
@@ -157,6 +160,12 @@ function createEditModal($template, $features, $categories)
+
+
+
+
+
+
@@ -270,7 +302,9 @@ function buildCreateModal($features, $categories)
@@ -289,7 +323,9 @@ function buildCreateModal($features, $categories)
@@ -334,7 +370,6 @@ function save()
WHERE `id` = " . intval($templateId));
$row = hesk_dbFetchAssoc($res);
-
// Add 'can ban emails' if 'can unban emails' is set (but not added). Same with 'can ban ips'
$catArray = hesk_POST_array('categories');
$featArray = hesk_POST_array('features');
@@ -349,6 +384,41 @@ function save()
$features = implode(',', $featArray);
$name = hesk_POST('name');
+ // Only allow users to add what they are allowed to add
+ // Admins can handle anything
+ if (!$_SESSION['isadmin']) {
+ // Update categories based on user visibility
+ $originalCategories = explode(',', $row['categories']);
+ $newCategories = array();
+ foreach ($originalCategories as $innerCategory) {
+ if (in_array($innerCategory, $catArray) && in_array($innerCategory, $_SESSION['categories'])) {
+ $newCategories[] = $innerCategory;
+ } elseif (!in_array($innerCategory, $catArray) && !in_array($innerCategory, $_SESSION['categories'])) {
+ // The user can't modify this, so keep it in
+ $newCategories[] = $innerCategory;
+ }
+ // If neither, the user removed it.
+ }
+
+ // Update features based on user visibility
+ $originalFeatures = explode(',', $row['heskprivileges']);
+ $newFeatures = array();
+ foreach ($originalFeatures as $innerFeature) {
+ if (in_array($innerFeature, $featArray) && strpos($_SESSION['heskprivileges'], $innerFeature) !== false) {
+ $newFeatures[] = $innerFeature;
+ } elseif (!in_array($innerFeature, $featArray) && strpos($_SESSION['heskprivileges'], $innerFeature) === false) {
+ // The user can't modify this, so keep it in
+ $newFeatures[] = $innerFeature;
+ }
+ // If neither, the user removed it.
+ }
+
+ $categories = implode(',', $newCategories);
+ $features = implode(',', $newFeatures);
+ }
+
+
+
hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "permission_templates`
SET `categories` = '" . hesk_dbEscape($categories) . "', `heskprivileges` = '" . hesk_dbEscape($features) . "',
`name` = '" . hesk_dbEscape($name) . "'
diff --git a/admin/manage_users.php b/admin/manage_users.php
index e96aff40..dee9b844 100644
--- a/admin/manage_users.php
+++ b/admin/manage_users.php
@@ -736,7 +736,7 @@ function hesk_validateUserInfo($pass_required = 1, $redirect_to = './manage_user
}
}
- if (strlen($myuser['signature']) > 1000) {
+ if (hesk_mb_strlen($myuser['signature']) > 1000) {
$hesk_error_buffer .= '' . $hesklang['signature_long'] . '';
}
diff --git a/admin/new_ticket.php b/admin/new_ticket.php
index 9128c017..261077a3 100644
--- a/admin/new_ticket.php
+++ b/admin/new_ticket.php
@@ -157,6 +157,11 @@ $show_quick_help = $show['show'];
/* This will handle error, success and notice messages */
hesk_handle_messages();
+ $service_messages = mfh_get_service_messages('STAFF_SUBMIT_TICKET');
+ foreach ($service_messages as $sm) {
+ hesk_service_message($sm);
+ }
+
if ($show_quick_help): ?>
@@ -220,39 +315,53 @@ hesk_dbConnect();
0;
+ if ($startingValidationNumber < $minimumValidationNumber) {
+ $checks = 'SKIPPED';
+ } else {
+ $res = run_check("SELECT 1 FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "settings` WHERE `Key` = '{$setting_name}'", false);
+ $checks = hesk_dbNumRows($res) > 0 ? 'PASS' : 'FAIL';
+ }
- output_result('
Setting Exists: ' . $setting_name, $all_good);
+ output_result('
Setting Exists: ' . $setting_name, $checks);
- return $all_good !== false;
+ return $checks;
}
-function run_table_check($table_name) {
- return run_column_check($table_name, '1');
+function run_table_check($table_name, $minimumValidationNumber) {
+
+ return run_column_check($table_name, '1', $minimumValidationNumber);
}
-function run_column_check($table_name, $column_name) {
- global $hesk_settings;
+function run_column_check($table_name, $column_name, $minimumValidationNumber) {
+ global $hesk_settings, $startingValidationNumber;
if ($column_name == '1') {
- $all_good = run_check('SELECT ' . $column_name . ' FROM `' . $hesk_settings['db_pfix'] . $table_name . '` LIMIT 1');
+ if ($startingValidationNumber < $minimumValidationNumber) {
+ $checks = 'SKIPPED';
+ } else {
+ $checks = run_check('SELECT ' . $column_name . ' FROM `' . $hesk_settings['db_pfix'] . $table_name . '` LIMIT 1');
+ }
output_result('
Table Exists: ' . $table_name,
- $all_good);
+ $checks);
} else {
- $all_good = run_check('SELECT `' . $column_name . '` FROM `' . $hesk_settings['db_pfix'] . $table_name . '` LIMIT 1');
+ if ($startingValidationNumber < $minimumValidationNumber) {
+ $checks = 'SKIPPED';
+ } else {
+ $checks = run_check('SELECT `' . $column_name . '` FROM `' . $hesk_settings['db_pfix'] . $table_name . '` LIMIT 1');
+ }
+
output_result('
Column Exists: ' . $table_name . '.' . $column_name,
- $all_good);
+ $checks);
}
- return $all_good !== false;
+ return $checks;
}
-function run_check($sql) {
+function run_check($sql, $returnString = true) {
global $hesk_last_query;
global $hesk_db_link;
if (function_exists('mysqli_connect')) {
@@ -261,23 +370,43 @@ function run_check($sql) {
}
$hesk_last_query = $sql;
- return @mysqli_query($hesk_db_link, $sql);
+ if ($returnString) {
+ return @mysqli_query($hesk_db_link, $sql) ? 'PASS' : 'FAIL';
+ } else {
+ return @mysqli_query($hesk_db_link, $sql);
+ }
+
} else {
if (!$hesk_db_link && !hesk_dbConnect()) {
return false;
}
$hesk_last_query = $sql;
- return $res = @mysql_query($sql, $hesk_db_link);
+ if ($returnString) {
+ return $res = @mysql_query($sql, $hesk_db_link) ? 'PASS' : 'FAIL';
+ } else {
+ return $res = @mysql_query($sql, $hesk_db_link);
+ }
}
}
-function output_result($change_title, $success) {
- $css_color = 'success';
- $text = '
Success';
- if (!$success) {
- $css_color = 'danger';
- $text = '
Failure';
+function output_result($change_title, $status) {
+ switch ($status) {
+ case 'PASS':
+ $css_color = 'success';
+ $text = '
Success';
+ break;
+ case 'FAIL':
+ $css_color = 'danger';
+ $text = '
Failure';
+ break;
+ case 'SKIPPED':
+ $css_color = 'default';
+ $text = '
Skipped';
+ break;
+ default:
+ $css_color = 'danger';
+ $text = 'WTF?! ' . $status;
}
$formatted_text = sprintf('
%s | %s |
', $change_title, $css_color, $text);
diff --git a/install/index.php b/install/index.php
index e6199ac3..699e5c9b 100644
--- a/install/index.php
+++ b/install/index.php
@@ -31,9 +31,9 @@ if (hesk_dbNumRows($tableSql) > 0) {
'1.6.0' => 22, '1.6.1' => 23, '1.7.0' => 27, '2.0.0' => 37, '2.0.1' => 38, '2.1.0' => 39, '2.1.1' => 42,
'2.2.0' => 47, '2.2.1' => 48, '2.3.0' => 68, '2.3.1' => 69, '2.3.2' => 70, '2.4.0' => 86, '2.4.1' => 87,
'2.4.2' => 88, '2.5.0' => 98, '2.5.1' => 99, '2.5.2' => 100, '2.5.3' => 101, '2.5.4' => 102, '2.5.5' => 103,
- '2.6.0' => 121, '2.6.1' => 122, '2.6.2' => 125, '2.6.3' => 126, '2.6.4' => 127, '3.0.0' => 132, '3.0.1' => 133,
- '3.0.2' => 135, '3.0.3' => 136, '3.0.4' => 137, '3.0.5' => 138, '3.0.6' => 139, '3.0.7' => 140, '3.1.0' => 153,
- '3.1.1' => 154
+ '2.6.0' => 121, '2.6.1' => 122, '2.6.2' => 125, '2.6.3' => 126, '2.6.4' => 127, '3.0.0 beta 1' => 130,
+ '3.0.0 RC 1' => 131, '3.0.0' => 132, '3.0.1' => 133, '3.0.2' => 135, '3.0.3' => 136, '3.0.4' => 137,
+ '3.0.5' => 138, '3.0.6' => 139, '3.0.7' => 140, '3.1.0' => 153, '3.1.1' => 154
);
$startingMigrationNumber = $migration_map[$versionRow['Value']];
}
diff --git a/install/install_functions.inc.php b/install/install_functions.inc.php
index 8dcae5c0..894bb47e 100644
--- a/install/install_functions.inc.php
+++ b/install/install_functions.inc.php
@@ -15,8 +15,8 @@
if (!defined('IN_SCRIPT')) {die('Invalid attempt');}
// We will be installing this HESK version:
-define('HESK_NEW_VERSION','2.7.5');
-define('MODS_FOR_HESK_NEW_VERSION','3.2.4');
+define('HESK_NEW_VERSION','2.7.6');
+define('MODS_FOR_HESK_NEW_VERSION','3.3.0');
define('REQUIRE_PHP_VERSION','5.3.0');
define('REQUIRE_MYSQL_VERSION','5.0.7');
diff --git a/install/js/install-script.js b/install/js/install-script.js
index 0e6eda7a..04835fc0 100644
--- a/install/js/install-script.js
+++ b/install/js/install-script.js
@@ -104,10 +104,10 @@ function executeMigration(startingMigrationNumber, migrationNumber, latestMigrat
console.log('latestMigrationNumber: ' + latestMigrationNumber);
console.info('---');
if (migrationNumber === latestMigrationNumber || (migrationNumber === startingMigrationNumber && direction === 'down')) {
- updateProgressBar(migrationNumber, latestMigrationNumber, direction === 'down', true);
+ updateProgressBar(migrationNumber - startingMigrationNumber, latestMigrationNumber - startingMigrationNumber, direction === 'down', true);
console.log('%c Success! ', 'color: white; background-color: green; font-size: 2em');
} else {
- updateProgressBar(migrationNumber, latestMigrationNumber, false, false);
+ updateProgressBar(migrationNumber - startingMigrationNumber, latestMigrationNumber - startingMigrationNumber, false, false);
var newMigrationNumber = direction === 'up' ? migrationNumber + 1 : migrationNumber - 1;
executeMigration(startingMigrationNumber, newMigrationNumber, latestMigrationNumber, direction);
}
@@ -121,7 +121,7 @@ function executeMigration(startingMigrationNumber, migrationNumber, latestMigrat
$errorBlock = $('#error-block');
$errorBlock.html($errorBlock.html() + "
An error occurred! (Error Code: " + migrationNumber + ")
" + message).show();
- updateProgressBar(migrationNumber, latestMigrationNumber, true, false);
+ updateProgressBar(migrationNumber - startingMigrationNumber, latestMigrationNumber - startingMigrationNumber, true, false);
if (direction === 'up') {
// Revert!
diff --git a/install/migrations/Pre140/Statuses/AddIntColumnUpDropTableDown.php b/install/migrations/Pre140/Statuses/AddIntColumnUpDropTableDown.php
index b6bf841e..c3053741 100644
--- a/install/migrations/Pre140/Statuses/AddIntColumnUpDropTableDown.php
+++ b/install/migrations/Pre140/Statuses/AddIntColumnUpDropTableDown.php
@@ -7,10 +7,12 @@ use AbstractMigration;
class AddIntColumnUpDropTableDown extends AbstractMigration {
function up($hesk_settings) {
- $this->executeQuery("ALTER TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` ADD COLUMN `status_int` INT NOT NULL DEFAULT 0 AFTER `status`;");
+ // We no longer need to do this thanks to HESK 2.7.0
+ //$this->executeQuery("ALTER TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` ADD COLUMN `status_int` INT NOT NULL DEFAULT 0 AFTER `status`;");
}
function down($hesk_settings) {
- $this->executeQuery("DROP TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "statuses`");
+ // Moved to migration #2 for clarity
+ //$this->executeQuery("DROP TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "statuses`");
}
}
\ No newline at end of file
diff --git a/install/migrations/Pre140/Statuses/CreateStatusesTable.php b/install/migrations/Pre140/Statuses/CreateStatusesTable.php
index 0d7dbef7..8b2321c2 100644
--- a/install/migrations/Pre140/Statuses/CreateStatusesTable.php
+++ b/install/migrations/Pre140/Statuses/CreateStatusesTable.php
@@ -23,6 +23,9 @@ class CreateStatusesTable extends \AbstractMigration {
}
function down($hesk_settings) {
- $this->executeQuery("ALTER TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` ADD COLUMN `status_int` INT NOT NULL AFTER `status`;");
+ // We no longer need to do this thanks to HESK 2.7.0
+ //$this->executeQuery("ALTER TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` ADD COLUMN `status_int` INT NOT NULL AFTER `status`;");
+ // Moved from migration #1
+ $this->executeQuery("DROP TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "statuses`");
}
}
\ No newline at end of file
diff --git a/install/migrations/Pre140/Statuses/DropOldStatusColumn.php b/install/migrations/Pre140/Statuses/DropOldStatusColumn.php
index eb41f6b6..0e6d136a 100644
--- a/install/migrations/Pre140/Statuses/DropOldStatusColumn.php
+++ b/install/migrations/Pre140/Statuses/DropOldStatusColumn.php
@@ -8,14 +8,16 @@ use AbstractMigration;
class DropOldStatusColumn extends AbstractMigration {
function up($hesk_settings) {
- $this->executeQuery("ALTER TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` DROP COLUMN `status`");
+ // We no longer need to do this thanks to HESK 2.7.0
+ //$this->executeQuery("ALTER TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` DROP COLUMN `status`");
}
function down($hesk_settings) {
- $ticketsRS = $this->executeQuery("SELECT `id`, `status_int` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets`;");
- while ($currentResult = hesk_dbFetchAssoc($ticketsRS)) {
-
- $this->executeQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `status_int` = '" . intval($currentResult['status']) . "' WHERE `id` = " . $currentResult['id']);
- }
+ // We no longer need to do this thanks to HESK 2.7.0
+ //$ticketsRS = $this->executeQuery("SELECT `id`, `status_int` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets`;");
+ //while ($currentResult = hesk_dbFetchAssoc($ticketsRS)) {
+ //
+ // $this->executeQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `status_int` = '" . intval($currentResult['status_int']) . "' WHERE `id` = " . $currentResult['id']);
+ //}
}
}
\ No newline at end of file
diff --git a/install/migrations/Pre140/Statuses/MoveStatusesToNewColumn.php b/install/migrations/Pre140/Statuses/MoveStatusesToNewColumn.php
index ce64aced..58037bec 100644
--- a/install/migrations/Pre140/Statuses/MoveStatusesToNewColumn.php
+++ b/install/migrations/Pre140/Statuses/MoveStatusesToNewColumn.php
@@ -7,14 +7,16 @@ use AbstractMigration;
class MoveStatusesToNewColumn extends AbstractMigration {
function up($hesk_settings) {
- $ticketsRS = $this->executeQuery("SELECT `id`, `status` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets`;");
- while ($currentResult = hesk_dbFetchAssoc($ticketsRS)) {
-
- $this->executeQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `status_int` = " . $currentResult['status'] . " WHERE `id` = " . $currentResult['id']);
- }
+ // We no longer need to do this thanks to HESK 2.7.0
+ //$ticketsRS = $this->executeQuery("SELECT `id`, `status` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets`;");
+ //while ($currentResult = hesk_dbFetchAssoc($ticketsRS)) {
+ //
+ // $this->executeQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `status_int` = " . $currentResult['status'] . " WHERE `id` = " . $currentResult['id']);
+ //}
}
function down($hesk_settings) {
- $this->executeQuery("ALTER TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` CHANGE COLUMN `status_int` `status` INT NOT NULL");
+ // We no longer need to do this thanks to HESK 2.7.0
+ //$this->executeQuery("ALTER TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` CHANGE COLUMN `status_int` `status` INT NOT NULL");
}
}
\ No newline at end of file
diff --git a/install/migrations/Pre140/Statuses/RenameTempColumn.php b/install/migrations/Pre140/Statuses/RenameTempColumn.php
index d1eec50f..5695b798 100644
--- a/install/migrations/Pre140/Statuses/RenameTempColumn.php
+++ b/install/migrations/Pre140/Statuses/RenameTempColumn.php
@@ -6,10 +6,12 @@ namespace Pre140\Statuses;
class RenameTempColumn extends \AbstractMigration {
function up($hesk_settings) {
- $this->executeQuery("ALTER TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` CHANGE COLUMN `status_int` `status` INT NOT NULL");
+ // We no longer need to do this thanks to HESK 2.7.0
+ //$this->executeQuery("ALTER TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` CHANGE COLUMN `status_int` `status` INT NOT NULL");
}
function down($hesk_settings) {
- $this->executeQuery("ALTER TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` DROP COLUMN `status`");
+ // We no longer need to do this thanks to HESK 2.7.0
+ //$this->executeQuery("ALTER TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` DROP COLUMN `status`");
}
}
\ No newline at end of file
diff --git a/install/migrations/core.php b/install/migrations/core.php
index 6c7980d8..4a22c4b7 100644
--- a/install/migrations/core.php
+++ b/install/migrations/core.php
@@ -176,8 +176,8 @@ function getAllMigrations() {
//3.0.0
127 => new \v300\MigrateHeskCustomStatuses(),
128 => new \v300\MigrateAutorefreshOption\UpdateFromOldValue(),
- 129 => new \v300\MigrateAutorefreshOption\DropOldColumn(),
- 130 => new \v300\AddColorSchemeSetting(),
+ 129 => new \v300\AddColorSchemeSetting(),
+ 130 => new \v300\MigrateAutorefreshOption\DropOldColumn(),
131 => new LegacyUpdateMigration('3.0.0', '2.6.4'),
//3.0.1
132 => new LegacyUpdateMigration('3.0.1', '3.0.0'),
@@ -219,5 +219,13 @@ function getAllMigrations() {
162 => new UpdateMigration('3.2.3', '3.2.2', 162),
163 => new UpdateMigration('3.2.4', '3.2.3', 163),
164 => new UpdateMigration('3.2.5', '3.2.4', 164),
+ // 3.3.0
+ 165 => new \v330\ServiceMessagesImprovements\CreateServiceMessageToLocationTable(165),
+ 166 => new \v330\ServiceMessagesImprovements\UpdateExistingServiceMessagesLocations(166),
+ 167 => new \v330\ServiceMessagesImprovements\AddLanguageColumnToServiceMessages(167),
+ 168 => new \v330\CalendarImprovements\AddBusinessHoursTable(168),
+ 169 => new \v330\CalendarImprovements\InsertDefaultBusinessHours(169),
+ 170 => new \v330\CalendarImprovements\AddShowStartTimeSetting(170),
+ 171 => new UpdateMigration('3.3.0', '3.2.5', 171),
);
}
\ No newline at end of file
diff --git a/install/migrations/v240/CreateNewStatusNameTable/InsertTextToStatusXrefValues.php b/install/migrations/v240/CreateNewStatusNameTable/InsertTextToStatusXrefValues.php
index b2ef866d..7ec6a0ce 100644
--- a/install/migrations/v240/CreateNewStatusNameTable/InsertTextToStatusXrefValues.php
+++ b/install/migrations/v240/CreateNewStatusNameTable/InsertTextToStatusXrefValues.php
@@ -17,10 +17,26 @@ class InsertTextToStatusXrefValues extends \AbstractMigration {
$oldSetting = $hesk_settings['can_sel_lang'];
$hesk_settings['can_sel_lang'] = 1;
while ($row = hesk_dbFetchAssoc($statusesRs)) {
+ $englishText = '';
foreach ($languages as $language => $languageCode) {
hesk_setLanguage($language);
+
+ if ($language === 'English') {
+ if (key_exists($row['Key'], $hesklang)) {
+ $englishText = $hesklang[$row['Key']];
+ } else {
+ $englishText = $row['Key'];
+ }
+ }
+
+ if (key_exists($row['Key'], $hesklang)) {
+ $textToInsert = $hesklang[$row['Key']];
+ } else {
+ $textToInsert = $englishText;
+ }
+
$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']) . ")";
+ VALUES ('" . hesk_dbEscape($language) . "', '" . hesk_dbEscape($textToInsert) . "', " . intval($row['ID']) . ")";
$this->executeQuery($sql);
}
}
diff --git a/install/migrations/v330/AddHighlightTicketRowsSetting.php b/install/migrations/v330/AddHighlightTicketRowsSetting.php
new file mode 100644
index 00000000..192359a7
--- /dev/null
+++ b/install/migrations/v330/AddHighlightTicketRowsSetting.php
@@ -0,0 +1,16 @@
+executeQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "settings` (`Key`, `Value`)
+ VALUES ('highlight_ticket_rows_based_on_priority', '0')");
+ }
+
+ function innerDown($hesk_settings) {
+ $this->executeQuery("DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "settings`
+ WHERE `Key` = 'highlight_ticket_rows_based_on_priority'");
+ }
+}
\ No newline at end of file
diff --git a/install/migrations/v330/CalendarImprovements/AddBusinessHoursTable.php b/install/migrations/v330/CalendarImprovements/AddBusinessHoursTable.php
new file mode 100644
index 00000000..42483d29
--- /dev/null
+++ b/install/migrations/v330/CalendarImprovements/AddBusinessHoursTable.php
@@ -0,0 +1,15 @@
+executeQuery("CREATE TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "mfh_calendar_business_hours`
+ (`day_of_week` INT NOT NULL, `start_time` VARCHAR(5) NOT NULL, `end_time` VARCHAR(5) NOT NULL)");
+ }
+
+ function innerDown($hesk_settings) {
+ $this->executeQuery("DROP TABLE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "mfh_calendar_business_hours`");
+ }
+}
\ No newline at end of file
diff --git a/install/migrations/v330/CalendarImprovements/AddShowStartTimeSetting.php b/install/migrations/v330/CalendarImprovements/AddShowStartTimeSetting.php
new file mode 100644
index 00000000..89c0cd81
--- /dev/null
+++ b/install/migrations/v330/CalendarImprovements/AddShowStartTimeSetting.php
@@ -0,0 +1,17 @@
+executeQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "settings` (`Key`, `Value`)
+ VALUES ('calendar_show_start_time', 'true')");
+ }
+
+ function innerDown($hesk_settings) {
+ $this->executeQuery("DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "settings`
+ WHERE `Key` = 'calendar_show_start_time'");
+ }
+}
\ No newline at end of file
diff --git a/install/migrations/v330/CalendarImprovements/InsertDefaultBusinessHours.php b/install/migrations/v330/CalendarImprovements/InsertDefaultBusinessHours.php
new file mode 100644
index 00000000..970b59ca
--- /dev/null
+++ b/install/migrations/v330/CalendarImprovements/InsertDefaultBusinessHours.php
@@ -0,0 +1,28 @@
+executeQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "mfh_calendar_business_hours` (`day_of_week`, `start_time`, `end_time`)
+ VALUES (0, '00:00', '23:59')");
+ $this->executeQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "mfh_calendar_business_hours` (`day_of_week`, `start_time`, `end_time`)
+ VALUES (1, '00:00', '23:59')");
+ $this->executeQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "mfh_calendar_business_hours` (`day_of_week`, `start_time`, `end_time`)
+ VALUES (2, '00:00', '23:59')");
+ $this->executeQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "mfh_calendar_business_hours` (`day_of_week`, `start_time`, `end_time`)
+ VALUES (3, '00:00', '23:59')");
+ $this->executeQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "mfh_calendar_business_hours` (`day_of_week`, `start_time`, `end_time`)
+ VALUES (4, '00:00', '23:59')");
+ $this->executeQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "mfh_calendar_business_hours` (`day_of_week`, `start_time`, `end_time`)
+ VALUES (5, '00:00', '23:59')");
+ $this->executeQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "mfh_calendar_business_hours` (`day_of_week`, `start_time`, `end_time`)
+ VALUES (6, '00:00', '23:59')");
+ }
+
+ function innerDown($hesk_settings) {
+ $this->executeQuery("DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "mfh_calendar_business_hours`");
+ }
+}
\ 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/calendar/index.php b/internal-api/calendar/index.php
deleted file mode 100644
index 1ccc6011..00000000
--- a/internal-api/calendar/index.php
+++ /dev/null
@@ -1,29 +0,0 @@
- {$end_time_sql}) AND `categories`.`usage` <> 1";
-
- if (!$staff) {
- $sql .= " AND `categories`.`type` = '0'";
- }
-
- $rs = hesk_dbQuery($sql);
-
- $events = array();
- while ($row = hesk_dbFetchAssoc($rs)) {
- // Skip the event if the user does not have access to it
- if ($staff && !$_SESSION['isadmin'] && !in_array($row['category'], $_SESSION['categories'])) {
- continue;
- }
-
- mfh_log_debug('Calendar', "Creating event with id: {$row['id']}", '');
-
- $event['type'] = 'CALENDAR';
- $event['id'] = intval($row['id']);
- $event['startTime'] = $row['start'];
- $event['endTime'] = $row['end'];
- $event['allDay'] = $row['all_day'] ? true : false;
- $event['title'] = $row['name'];
- $event['location'] = $row['location'];
- $event['comments'] = $row['comments'];
- $event['categoryId'] = $row['category'];
- $event['categoryName'] = $row['category_name'];
- $event['backgroundColor'] = $row['background_color'];
- $event['foregroundColor'] = $row['foreground_color'];
- $event['displayBorder'] = $row['display_border'];
-
- if ($staff) {
- $event['reminderValue'] = $row['reminder_value'];
- $event['reminderUnits'] = $row['reminder_unit'];
- }
-
- $events[] = $event;
- }
-
- if ($staff) {
- $old_time_setting = $hesk_settings['timeformat'];
- $hesk_settings['timeformat'] = 'Y-m-d';
- $current_date = hesk_date();
- $hesk_settings['timeformat'] = $old_time_setting;
-
- $sql = "SELECT `trackid`, `subject`, `due_date`, `category`, `categories`.`name` AS `category_name`, `categories`.`background_color` AS `background_color`,
- `categories`.`foreground_color` AS `foreground_color`, `categories`.`display_border_outline` AS `display_border`,
- CASE WHEN `due_date` < '{$current_date}' THEN 1 ELSE 0 END AS `overdue`, `owner`.`name` AS `owner_name`, `tickets`.`owner` AS `owner_id`,
- `tickets`.`priority` AS `priority`
- FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` AS `tickets`
- INNER JOIN `" . hesk_dbEscape($hesk_settings['db_pfix']) . "categories` AS `categories`
- ON `categories`.`id` = `tickets`.`category`
- AND `categories`.`usage` <> 2
- LEFT JOIN `" . hesk_dbEscape($hesk_settings['db_pfix']) . "users` AS `owner`
- ON `tickets`.`owner` = `owner`.`id`
- WHERE `due_date` >= CONVERT_TZ(FROM_UNIXTIME(" . hesk_dbEscape($start)
- . " / 1000), @@session.time_zone, '+00:00')
- AND `due_date` <= CONVERT_TZ(FROM_UNIXTIME(" . hesk_dbEscape($end) . " / 1000), @@session.time_zone, '+00:00')
- AND `status` IN (SELECT `id` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "statuses` WHERE `IsClosed` = 0) ";
-
- $rs = hesk_dbQuery($sql);
- while ($row = hesk_dbFetchAssoc($rs)) {
- // Skip the ticket if the user does not have access to it
- if (!hesk_checkPermission('can_view_tickets', 0)
- || ($row['owner_id'] && $row['owner_id'] != $_SESSION['id'] && !hesk_checkPermission('can_view_ass_others', 0))
- || (!$row['owner_id'] && !hesk_checkPermission('can_view_unassigned', 0))) {
- continue;
- }
-
- $event['type'] = 'TICKET';
- $event['trackingId'] = $row['trackid'];
- $event['subject'] = $row['subject'];
- $event['title'] = $row['subject'];
- $event['startTime'] = $row['due_date'];
- $event['url'] = $hesk_settings['hesk_url'] . '/' . $hesk_settings['admin_dir'] . '/admin_ticket.php?track=' . $event['trackingId'];
- $event['categoryId'] = $row['category'];
- $event['categoryName'] = $row['category_name'];
- $event['backgroundColor'] = $row['background_color'];
- $event['foregroundColor'] = $row['foreground_color'];
- $event['displayBorder'] = $row['display_border'];
- $event['owner'] = $row['owner_name'];
-
- $priorities = array(
- 0 => $hesklang['critical'],
- 1 => $hesklang['high'],
- 2 => $hesklang['medium'],
- 3 => $hesklang['low']
- );
- $event['priority'] = $priorities[$row['priority']];
-
- $events[] = $event;
- }
- }
-
- return $events;
-}
-
-function create_event($event, $hesk_settings) {
- // Make sure the user can create events in this category
- if (!$_SESSION['isadmin'] && !in_array($event['category'], $_SESSION['categories'])) {
- print_error('Access Denied', 'You cannot create an event in this category');
- }
-
- $event['start'] = date('Y-m-d H:i:s', strtotime($event['start']));
- $event['end'] = date('Y-m-d H:i:s', strtotime($event['end']));
- $event['all_day'] = $event['all_day'] ? 1 : 0;
-
- $sql = "INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "calendar_event` (`start`, `end`, `all_day`,
- `name`, `location`, `comments`, `category`) VALUES (
- '" . hesk_dbEscape($event['start']) . "', '" . hesk_dbEscape($event['end']) . "', '" . hesk_dbEscape($event['all_day']) . "',
- '" . hesk_dbEscape(addslashes($event['title'])) . "', '" . hesk_dbEscape(addslashes($event['location'])) . "', '" . hesk_dbEscape(addslashes($event['comments'])) . "',
- " . intval($event['category']) . ")";
-
- hesk_dbQuery($sql);
- $event_id = hesk_dbInsertID();
-
- if ($event['reminder_amount'] != null) {
- $sql = "INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "calendar_event_reminder` (`user_id`, `event_id`,
- `amount`, `unit`) VALUES (" . intval($event['reminder_user']) . ", " . intval($event_id) . ", " . intval($event['reminder_amount']) . ",
- " . intval($event['reminder_units']) . ")";
-
- hesk_dbQuery($sql);
- }
-
- return $event_id;
-}
-
-function update_event($event, $hesk_settings) {
- // Make sure the user can edit events in this category
- if (!$_SESSION['isadmin'] && !in_array($event['category'], $_SESSION['categories'])) {
- print_error('Access Denied', 'You cannot edit an event in this category');
- }
-
- $event['start'] = date('Y-m-d H:i:s', strtotime($event['start']));
- $event['end'] = date('Y-m-d H:i:s', strtotime($event['end']));
- if ($event['create_ticket_date'] != null) {
- $event['create_ticket_date'] = date('Y-m-d H:i:s', strtotime($event['create_ticket_date']));
- }
- $event['all_day'] = $event['all_day'] ? 1 : 0;
- $event['assign_to'] = $event['assign_to'] != null ? intval($event['assign_to']) : 'NULL';
-
- $sql = "UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "calendar_event` SET `start` = '" . hesk_dbEscape($event['start'])
- . "', `end` = '" . hesk_dbEscape($event['end']) . "', `all_day` = '" . hesk_dbEscape($event['all_day']) . "', `name` = '"
- . hesk_dbEscape(addslashes($event['title'])) . "', `location` = '" . hesk_dbEscape(addslashes($event['location'])) . "', `comments` = '"
- . hesk_dbEscape(addslashes($event['comments'])) . "', `category` = " . intval($event['category']) . " WHERE `id` = " . intval($event['id']);
-
- if ($event['reminder_amount'] != null) {
- $delete_sql = "DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "calendar_event_reminder` WHERE `event_id` = " . intval($event['id'])
- . " AND `user_id` = " . intval($event['reminder_user']);
- hesk_dbQuery($delete_sql);
- $insert_sql = "INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "calendar_event_reminder` (`user_id`, `event_id`,
- `amount`, `unit`) VALUES (" . intval($event['reminder_user']) . ", " . intval($event['id']) . ", " . intval($event['reminder_amount']) . ",
- " . intval($event['reminder_units']) . ")";
- hesk_dbQuery($insert_sql);
- }
-
- hesk_dbQuery($sql);
-}
-
-function delete_event($id, $hesk_settings) {
- // Make sure the user can delete events in this category
- $categoryRs = hesk_dbQuery('SELECT `category` FROM `' . hesk_dbEscape($hesk_settings['db_pfix']) . 'calendar_event` WHERE `id` = ' . intval($id));
- $category = hesk_dbFetchAssoc($categoryRs);
- if (!$_SESSION['isadmin'] && !in_array($category['category'], $_SESSION['categories'])) {
- print_error('Access Denied', 'You cannot delete events in this category');
- }
-
- $sql = "DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "calendar_event` WHERE `id` = " . intval($id);
-
- hesk_dbQuery($sql);
-}
-
-function update_ticket_due_date($ticket, $hesk_settings) {
- $ticket_id_rs = hesk_dbQuery("SELECT `id` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` WHERE `trackid` = '" . hesk_dbEscape($ticket['trackid']) . "'");
- $ticket_id = hesk_dbFetchAssoc($ticket_id_rs);
-
- $due_date = 'NULL';
- $language_key = 'audit_due_date_removed';
- $audit_array = array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
- if ($ticket['due_date'] != NULL) {
- $audit_array = array(
- 0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')',
- 1 => date('Y-m-d H:i:s', strtotime($ticket['due_date']))
- );
- $due_date = "'" . date('Y-m-d H:i:s', strtotime($ticket['due_date'])) . "'";
- $language_key = 'audit_due_date_changed';
- }
- $sql = "UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `due_date` = {$due_date}, `overdue_email_sent` = '0'
- WHERE `trackid` = '" . hesk_dbEscape($ticket['trackid']) . "'";
-
- mfh_insert_audit_trail_record($ticket_id['id'], 'TICKET', $language_key, hesk_date(),
- $audit_array);
-
- hesk_dbQuery($sql);
-}
\ 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/js/calendar/mods-for-hesk-calendar-admin-readonly.js b/js/calendar/mods-for-hesk-calendar-admin-readonly.js
index 619e5d23..43ce1b43 100644
--- a/js/calendar/mods-for-hesk-calendar-admin-readonly.js
+++ b/js/calendar/mods-for-hesk-calendar-admin-readonly.js
@@ -12,13 +12,52 @@ $(document).ready(function() {
eventLimit: true,
timeFormat: 'H:mm',
axisFormat: 'H:mm',
+ displayEventTime: $('#setting_show_start_time').text(),
+ businessHours: [
+ {
+ dow: [0],
+ start: $('#business_hours_0_start').text(),
+ end: $('#business_hours_0_end').text()
+ },
+ {
+ dow: [1],
+ start: $('#business_hours_1_start').text(),
+ end: $('#business_hours_1_end').text()
+ },
+ {
+ dow: [2],
+ start: $('#business_hours_2_start').text(),
+ end: $('#business_hours_2_end').text()
+ },
+ {
+ dow: [3],
+ start: $('#business_hours_3_start').text(),
+ end: $('#business_hours_3_end').text()
+ },
+ {
+ dow: [4],
+ start: $('#business_hours_4_start').text(),
+ end: $('#business_hours_4_end').text()
+ },
+ {
+ dow: [5],
+ start: $('#business_hours_5_start').text(),
+ end: $('#business_hours_5_end').text()
+ },
+ {
+ dow: [6],
+ start: $('#business_hours_6_start').text(),
+ end: $('#business_hours_6_end').text()
+ }
+ ],
firstDay: $('#setting_first_day_of_week').text(),
defaultView: $('#setting_default_view').text().trim(),
events: function(start, end, timezone, callback) {
$.ajax({
- url: heskPath + 'internal-api/admin/calendar/?start=' + start + '&end=' + end,
+ url: heskPath + 'api/index.php/v1/calendar/events/staff?start=' + start + '&end=' + end,
method: 'GET',
dataType: 'json',
+ headers: { 'X-Internal-Call': true },
success: function(data) {
var events = [];
$(data).each(function() {
@@ -61,7 +100,8 @@ $(document).ready(function() {
.find('.popover-owner span').text(event.owner).end()
.find('.popover-subject span').text(event.subject).end()
.find('.popover-category span').text(event.categoryName).end()
- .find('.popover-priority span').text(event.priority);
+ .find('.popover-priority span').text(event.priority)
+ .find('.popover-status span').text(event.status).end();
} else {
if (event.location === '') {
$contents.find('.popover-location').hide();
@@ -124,7 +164,13 @@ $(document).ready(function() {
});
function buildEvent(id, dbObject) {
- if (dbObject.type == 'TICKET') {
+ var priorities = [];
+ priorities['CRITICAL'] = mfhLang.text('critical');
+ priorities['HIGH'] = mfhLang.text('high');
+ priorities['MEDIUM'] = mfhLang.text('medium');
+ priorities['LOW'] = mfhLang.text('low');
+
+ if (dbObject.type === 'TICKET') {
return {
title: dbObject.title,
subject: dbObject.subject,
@@ -140,8 +186,9 @@ function buildEvent(id, dbObject) {
categoryName: dbObject.categoryName,
className: 'category-' + dbObject.categoryId,
owner: dbObject.owner,
- priority: dbObject.priority,
- fontIconMarkup: getIcon(dbObject)
+ priority: priorities[dbObject.priority],
+ fontIconMarkup: getIcon(dbObject),
+ status: dbObject.status
};
}
diff --git a/js/calendar/mods-for-hesk-calendar-readonly.js b/js/calendar/mods-for-hesk-calendar-readonly.js
index eff85039..72774ce5 100644
--- a/js/calendar/mods-for-hesk-calendar-readonly.js
+++ b/js/calendar/mods-for-hesk-calendar-readonly.js
@@ -12,11 +12,49 @@ $(document).ready(function() {
eventLimit: true,
timeFormat: 'H:mm',
axisFormat: 'H:mm',
+ displayEventTime: $('#setting_show_start_time').text(),
+ businessHours: [
+ {
+ dow: [0],
+ start: $('#business_hours_0_start').text(),
+ end: $('#business_hours_0_end').text()
+ },
+ {
+ dow: [1],
+ start: $('#business_hours_1_start').text(),
+ end: $('#business_hours_1_end').text()
+ },
+ {
+ dow: [2],
+ start: $('#business_hours_2_start').text(),
+ end: $('#business_hours_2_end').text()
+ },
+ {
+ dow: [3],
+ start: $('#business_hours_3_start').text(),
+ end: $('#business_hours_3_end').text()
+ },
+ {
+ dow: [4],
+ start: $('#business_hours_4_start').text(),
+ end: $('#business_hours_4_end').text()
+ },
+ {
+ dow: [5],
+ start: $('#business_hours_5_start').text(),
+ end: $('#business_hours_5_end').text()
+ },
+ {
+ dow: [6],
+ start: $('#business_hours_6_start').text(),
+ end: $('#business_hours_6_end').text()
+ }
+ ],
firstDay: $('#setting_first_day_of_week').text(),
defaultView: $('#setting_default_view').text().trim(),
events: function(start, end, timezone, callback) {
$.ajax({
- url: heskPath + 'internal-api/calendar/?start=' + start + '&end=' + end,
+ url: heskPath + 'api/index.php/v1/calendar/events/?start=' + start + '&end=' + end,
method: 'GET',
dataType: 'json',
success: function(data) {
diff --git a/js/calendar/mods-for-hesk-calendar.js b/js/calendar/mods-for-hesk-calendar.js
index 97c4fc7b..08f1b4f4 100644
--- a/js/calendar/mods-for-hesk-calendar.js
+++ b/js/calendar/mods-for-hesk-calendar.js
@@ -12,13 +12,52 @@ $(document).ready(function() {
eventLimit: true,
timeFormat: 'H:mm',
axisFormat: 'H:mm',
+ displayEventTime: $('#setting_show_start_time').text() === 'true',
+ businessHours: [
+ {
+ dow: [0],
+ start: $('#business_hours_0_start').text(),
+ end: $('#business_hours_0_end').text()
+ },
+ {
+ dow: [1],
+ start: $('#business_hours_1_start').text(),
+ end: $('#business_hours_1_end').text()
+ },
+ {
+ dow: [2],
+ start: $('#business_hours_2_start').text(),
+ end: $('#business_hours_2_end').text()
+ },
+ {
+ dow: [3],
+ start: $('#business_hours_3_start').text(),
+ end: $('#business_hours_3_end').text()
+ },
+ {
+ dow: [4],
+ start: $('#business_hours_4_start').text(),
+ end: $('#business_hours_4_end').text()
+ },
+ {
+ dow: [5],
+ start: $('#business_hours_5_start').text(),
+ end: $('#business_hours_5_end').text()
+ },
+ {
+ dow: [6],
+ start: $('#business_hours_6_start').text(),
+ end: $('#business_hours_6_end').text()
+ }
+ ],
firstDay: $('#setting_first_day_of_week').text(),
defaultView: $('#setting_default_view').text().trim(),
events: function(start, end, timezone, callback) {
$.ajax({
- url: heskPath + 'internal-api/admin/calendar/?start=' + start + '&end=' + end,
+ url: heskPath + 'api/index.php/v1/calendar/events/staff?start=' + start + '&end=' + end,
method: 'GET',
dataType: 'json',
+ headers: { 'X-Internal-Call': true },
success: function(data) {
var events = [];
$(data).each(function() {
@@ -51,16 +90,7 @@ $(document).ready(function() {
var $contents = $(contents);
var format = 'dddd, MMMM Do YYYY';
- var endDate = event.end == null ? event.start : event.end;
-
- if (event.allDay) {
- endDate = event.end.clone();
- endDate.add(-1, 'days');
- }
-
- if (!event.allDay && event.type !== 'TICKET') {
- format += ', HH:mm';
- }
+ var endDate = event.end === null ? event.start : event.end;
if (event.type === 'TICKET') {
contents = $('.ticket-popover-template').html();
@@ -74,8 +104,16 @@ $(document).ready(function() {
.find('.popover-owner span').text(event.owner).end()
.find('.popover-subject span').text(event.subject).end()
.find('.popover-category span').text(event.categoryName).end()
- .find('.popover-priority span').text(event.priority);
+ .find('.popover-priority span').text(event.priority).end()
+ .find('.popover-status span').text(event.status).end();
} else {
+ if (event.allDay) {
+ endDate = event.end.clone();
+ endDate.add(-1, 'days');
+ } else {
+ format += ', HH:mm';
+ }
+
if (event.location === '') {
$contents.find('.popover-location').hide();
}
@@ -90,7 +128,7 @@ $(document).ready(function() {
var $eventMarkup = $(this);
var eventTitle = event.title;
- if (event.fontIconMarkup != undefined) {
+ if (event.fontIconMarkup !== undefined) {
eventTitle = event.fontIconMarkup + ' ' + eventTitle;
}
@@ -161,17 +199,15 @@ $(document).ready(function() {
$editForm.find('#delete-button').click(function() {
var id = $editForm.find('input[name="id"]').val();
- var data = {
- id: id,
- action: 'delete'
- };
-
$.ajax({
method: 'POST',
- url: heskPath + 'internal-api/admin/calendar/',
- data: data,
+ url: heskPath + 'api/index.php/v1/calendar/events/staff/' + id,
+ headers: {
+ 'X-Internal-Call': true,
+ 'X-HTTP-Method-Override': 'DELETE'
+ },
success: function() {
- removeFromCalendar(data.id);
+ removeFromCalendar(id);
mfhAlert.success(mfhLang.text('event_deleted'));
$('#edit-event-modal').modal('hide');
},
@@ -195,6 +231,9 @@ $(document).ready(function() {
dateFormat = 'YYYY-MM-DD HH:mm:ss';
}
+ var reminderValue = $createForm.find('input[name="reminder-value"]').val();
+ var reminderUnits = $createForm.find('select[name="reminder-unit"]').val();
+
var data = {
title: $createForm.find('input[name="name"]').val(),
location: $createForm.find('input[name="location"]').val(),
@@ -203,22 +242,23 @@ $(document).ready(function() {
allDay: allDay,
comments: $createForm.find('textarea[name="comments"]').val(),
categoryId: $createForm.find('select[name="category"]').val(),
- action: 'create',
type: 'CALENDAR',
backgroundColor: $createForm.find('select[name="category"] :selected').attr('data-background-color'),
foregroundColor: $createForm.find('select[name="category"] :selected').attr('data-foreground-color'),
displayBorder: $createForm.find('select[name="category"] :selected').attr('data-display-border'),
categoryName: $createForm.find('select[name="category"] :selected').text().trim(),
- reminderValue: $createForm.find('input[name="reminder-value"]').val(),
- reminderUnits: $createForm.find('select[name="reminder-unit"]').val()
+ reminderValue: reminderValue === "" ? null : reminderValue,
+ reminderUnits: reminderValue === "" ? null : reminderUnits
};
$.ajax({
method: 'POST',
- url: heskPath + 'internal-api/admin/calendar/',
- data: data,
- success: function(id) {
- addToCalendar(id, data, $('#lang_event_created').text());
+ url: heskPath + 'api/index.php/v1/calendar/events/staff',
+ data: JSON.stringify(data),
+ contentType: 'json',
+ headers: { 'X-Internal-Call': true },
+ success: function(createdEvent) {
+ addToCalendar(createdEvent.id, data, $('#lang_event_created').text());
$('#create-event-modal').modal('hide');
updateCategoryVisibility();
},
@@ -243,6 +283,9 @@ $(document).ready(function() {
dateFormat = 'YYYY-MM-DD HH:mm:ss';
}
+ var reminderValue = $createForm.find('input[name="reminder-value"]').val();
+ var reminderUnits = $createForm.find('select[name="reminder-unit"]').val();
+
var data = {
id: $form.find('input[name="id"]').val(),
title: $form.find('input[name="name"]').val(),
@@ -251,21 +294,26 @@ $(document).ready(function() {
endTime: moment(end).format(dateFormat),
allDay: allDay,
comments: $form.find('textarea[name="comments"]').val(),
- categoryId: $form.find('select[name="category"]').val(),
+ categoryId: parseInt($form.find('select[name="category"]').val()),
backgroundColor: $form.find('select[name="category"] :selected').attr('data-background-color'),
foregroundColor: $form.find('select[name="category"] :selected').attr('data-foreground-color'),
displayBorder: $form.find('select[name="category"] :selected').attr('data-display-border'),
categoryName: $form.find('select[name="category"] :selected').text().trim(),
- action: 'update',
- reminderValue: $form.find('input[name="reminder-value"]').val(),
- reminderUnits: $form.find('select[name="reminder-unit"]').val()
+ reminderValue: reminderValue === "" ? null : reminderValue,
+ reminderUnits: reminderValue === "" ? null : reminderUnits,
};
$.ajax({
method: 'POST',
- url: heskPath + 'internal-api/admin/calendar/',
- data: data,
- success: function() {
+ url: heskPath + 'api/index.php/v1/calendar/events/staff/' + data.id,
+ data: JSON.stringify(data),
+ contentType: 'json',
+ headers: {
+ 'X-Internal-Call': true,
+ 'X-HTTP-Method-Override': 'PUT'
+ },
+ success: function(updatedEvent) {
+ data.auditTrail = updatedEvent.auditTrail;
removeFromCalendar(data.id);
addToCalendar(data.id, data, $('#lang_event_updated').text());
$('#edit-event-modal').modal('hide');
@@ -290,8 +338,16 @@ function removeFromCalendar(id) {
}
function buildEvent(id, dbObject) {
- if (dbObject.type == 'TICKET') {
+ var priorities = [];
+ priorities['CRITICAL'] = mfhLang.text('critical');
+ priorities['HIGH'] = mfhLang.text('high');
+ priorities['MEDIUM'] = mfhLang.text('medium');
+ priorities['LOW'] = mfhLang.text('low');
+
+
+ if (dbObject.type === 'TICKET') {
return {
+ id: id,
title: dbObject.title,
subject: dbObject.subject,
trackingId: dbObject.trackingId,
@@ -306,8 +362,9 @@ function buildEvent(id, dbObject) {
categoryName: dbObject.categoryName,
className: 'category-' + dbObject.categoryId,
owner: dbObject.owner,
- priority: dbObject.priority,
- fontIconMarkup: getIcon(dbObject)
+ priority: priorities[dbObject.priority],
+ fontIconMarkup: getIcon(dbObject),
+ status: dbObject.status
};
}
@@ -334,7 +391,8 @@ function buildEvent(id, dbObject) {
borderColor: parseInt(dbObject.displayBorder) === 1 ? dbObject.foregroundColor : dbObject.backgroundColor,
reminderValue: dbObject.reminderValue == null ? '' : dbObject.reminderValue,
reminderUnits: dbObject.reminderUnits,
- fontIconMarkup: '
'
+ fontIconMarkup: '
',
+ auditTrail: dbObject.auditTrail
};
}
@@ -379,7 +437,7 @@ function displayCreateModal(date, viewName) {
.find('input[name="location"]').val('').end()
.find('textarea[name="comments"]').val('').end()
.find('select[name="category"]').val($form.find('select[name="category"] option:first-child').val()).end()
- .find('select[name="reminder-unit"]').val(0).end()
+ .find('select[name="reminder-unit"]').val("MINUTE").end()
.find('input[name="reminder-value"]').val('').end();
var $modal = $('#create-event-modal');
@@ -454,6 +512,24 @@ function displayEditModal(date) {
$form.find('select[name="category"] option[value="' + date.categoryId + '"]').prop('selected', true);
+ var $auditTrail = $('#edit-history');
+ if (date.auditTrail.length === 0) {
+ $('.nav-tabs[role="tablist"]').hide();
+ } else {
+ $('.nav-tabs[role="tablist"]').show();
+
+ var $historyTable = $('#history-table');
+ $historyTable.html('');
+ $.each(date.auditTrail, function() {
+ var $template = $($('#audit-trail-template').html());
+ $template.find('[data-property="date"]').text(this.date).end()
+ .find('[data-property="description"]').text(vsprintf(mfhLang.text(this.languageKey), this.replacementValues));
+
+ $historyTable.append($template);
+ });
+ }
+ $('#edit-modal-tabs').find('a:first').tab('show');
+
$('#edit-event-modal').modal('show');
}
@@ -477,15 +553,19 @@ function updateCategoryVisibility() {
function respondToDragAndDrop(event, delta, revertFunc) {
var heskPath = $('p#hesk-path').text();
+
if (event.type === 'TICKET') {
+ var uri = 'api/index.php/v1/staff/tickets/' + event.id + '/due-date';
$.ajax({
method: 'POST',
- url: heskPath + 'internal-api/admin/calendar/',
- data: {
- trackingId: event.trackingId,
- action: 'update-ticket',
- dueDate: event.start.format('YYYY-MM-DD')
+ url: heskPath + uri,
+ headers: {
+ 'X-Internal-Call': true,
+ 'X-HTTP-Method-Override': 'PATCH'
},
+ data: JSON.stringify({
+ dueDate: event.start.format('YYYY-MM-DD')
+ }),
success: function() {
event.fontIconMarkup = getIcon({
startTime: event.start
@@ -526,12 +606,19 @@ function respondToDragAndDrop(event, delta, revertFunc) {
reminderValue: event.reminderValue,
reminderUnits: event.reminderUnits
};
+
+ var url = heskPath + 'api/index.php/v1/calendar/events/staff/' + event.id;
$.ajax({
method: 'POST',
- url: heskPath + 'internal-api/admin/calendar/',
- data: data,
- success: function() {
- mfhAlert.success(mfhLang.text('event_updated'));
+ url: url,
+ data: JSON.stringify(data),
+ headers: {
+ 'X-Internal-Call': true,
+ 'X-HTTP-Method-Override': 'PUT'
+ },
+ success: function(updatedEvent) {
+ removeFromCalendar(updatedEvent.id);
+ addToCalendar(updatedEvent.id, updatedEvent, $('#lang_event_updated').text());
},
error: function() {
mfhAlert.error(mfhLang.text('error_updating_event'));
diff --git a/js/sprintf.min.js b/js/sprintf.min.js
new file mode 100644
index 00000000..11ea3642
--- /dev/null
+++ b/js/sprintf.min.js
@@ -0,0 +1,3 @@
+/*! sprintf-js v1.1.0 | Copyright (c) 2007-present, Alexandru Marasteanu
| BSD-3-Clause */
+!function(e){"use strict";function t(){var e=arguments[0],r=t.cache;return r[e]||(r[e]=t.parse(e)),t.format.call(null,r[e],arguments)}function r(e){return"number"==typeof e?"number":"string"==typeof e?"string":Object.prototype.toString.call(e).slice(8,-1).toLowerCase()}function n(e,t){return t>=0&&t<=7&&i[e]?i[e][t]:Array(t+1).join(e)}var s={not_string:/[^s]/,not_bool:/[^t]/,not_type:/[^T]/,not_primitive:/[^v]/,number:/[diefg]/,numeric_arg:/[bcdiefguxX]/,json:/[j]/,not_json:/[^j]/,text:/^[^\x25]+/,modulo:/^\x25{2}/,placeholder:/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijostTuvxX])/,key:/^([a-z_][a-z_\d]*)/i,key_access:/^\.([a-z_][a-z_\d]*)/i,index_access:/^\[(\d+)\]/,sign:/^[\+\-]/};t.format=function(e,a){var i,o,l,c,p,f,u,g=1,_=e.length,d="",b=[],h=!0,x="";for(o=0;o<_;o++)if("string"===(d=r(e[o])))b[b.length]=e[o];else if("array"===d){if(c=e[o],c[2])for(i=a[g],l=0;l=0),c[8]){case"b":i=parseInt(i,10).toString(2);break;case"c":i=String.fromCharCode(parseInt(i,10));break;case"d":case"i":i=parseInt(i,10);break;case"j":i=JSON.stringify(i,null,c[6]?parseInt(c[6]):0);break;case"e":i=c[7]?parseFloat(i).toExponential(c[7]):parseFloat(i).toExponential();break;case"f":i=c[7]?parseFloat(i).toFixed(c[7]):parseFloat(i);break;case"g":i=c[7]?parseFloat(i).toPrecision(c[7]):parseFloat(i);break;case"o":i=i.toString(8);break;case"s":i=String(i),i=c[7]?i.substring(0,c[7]):i;break;case"t":i=String(!!i),i=c[7]?i.substring(0,c[7]):i;break;case"T":i=r(i),i=c[7]?i.substring(0,c[7]):i;break;case"u":i=parseInt(i,10)>>>0;break;case"v":i=i.valueOf(),i=c[7]?i.substring(0,c[7]):i;break;case"x":i=parseInt(i,10).toString(16);break;case"X":i=parseInt(i,10).toString(16).toUpperCase()}s.json.test(c[8])?b[b.length]=i:(!s.number.test(c[8])||h&&!c[3]?x="":(x=h?"+":"-",i=i.toString().replace(s.sign,"")),f=c[4]?"0"===c[4]?"0":c[4].charAt(1):" ",u=c[6]-(x+i).length,p=c[6]&&u>0?n(f,u):"",b[b.length]=c[5]?x+i+p:"0"===f?x+p+i:p+x+i)}return b.join("")},t.cache=Object.create(null),t.parse=function(e){for(var t=e,r=[],n=[],a=0;t;){if(null!==(r=s.text.exec(t)))n[n.length]=r[0];else if(null!==(r=s.modulo.exec(t)))n[n.length]="%";else{if(null===(r=s.placeholder.exec(t)))throw new SyntaxError("[sprintf] unexpected placeholder");if(r[2]){a|=1;var i=[],o=r[2],l=[];if(null===(l=s.key.exec(o)))throw new SyntaxError("[sprintf] failed to parse named argument key");for(i[i.length]=l[1];""!==(o=o.substring(l[0].length));)if(null!==(l=s.key_access.exec(o)))i[i.length]=l[1];else{if(null===(l=s.index_access.exec(o)))throw new SyntaxError("[sprintf] failed to parse named argument key");i[i.length]=l[1]}r[2]=i}else a|=2;if(3===a)throw new Error("[sprintf] mixing positional and named placeholders is not (yet) supported");n[n.length]=r}t=t.substring(r[0].length)}return n};var a=function(e,r,n){return n=(r||[]).slice(0),n.splice(0,0,e),t.apply(null,n)},i={0:["","0","00","000","0000","00000","000000","0000000"]," ":[""," "," "," "," "," "," "," "],_:["","_","__","___","____","_____","______","_______"]};"undefined"!=typeof exports&&(exports.sprintf=t,exports.vsprintf=a),void 0!==e&&(e.sprintf=t,e.vsprintf=a,"function"==typeof define&&define.amd&&define(function(){return{sprintf:t,vsprintf:a}}))}("undefined"==typeof window?this:window);
+//# sourceMappingURL=sprintf.min.js.map
diff --git a/knowledgebase.php b/knowledgebase.php
index fdb2a3a4..6e5a4f9c 100644
--- a/knowledgebase.php
+++ b/knowledgebase.php
@@ -173,7 +173,7 @@ if (!$show['show']) {
define('HESK_NO_ROBOTS', 1);
/* Print header */
- $hesk_settings['tmp_title'] = $hesklang['sr'] . ': ' . substr(hesk_htmlspecialchars(stripslashes($query)), 0, 20);
+ $hesk_settings['tmp_title'] = $hesklang['sr'] . ': ' . hesk_mb_substr(hesk_htmlspecialchars(stripslashes($query)),0,20);
require_once(HESK_PATH . 'inc/header.inc.php');
hesk_kb_header($hesk_settings['kb_link']);
@@ -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 e55cd0e4..23fdbd79 100644
--- a/language/en/text.php
+++ b/language/en/text.php
@@ -2214,5 +2214,34 @@ $hesklang['audit_due_date_changed'] = '%s changed due date to %s';
$hesklang['audit_linked_ticket'] = '%s linked ticket %s to this ticket';
$hesklang['audit_unlinked_ticket'] = '%s unlinked ticket %s';
+// 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';
+$hesklang['business_hours'] = 'Business Hours';
+$hesklang['business_hours_help'] = 'Set business hours for the calendar. There is no functional change by setting this,
+but times outside of the defined business hours will have a darker gray background for increased visibility.';
+$hesklang['show_event_start_time'] = 'Show event start time in title';
+$hesklang['show_event_start_time_help'] = 'Always show the start time on event titles (unless the event is an all-day event).';
+$hesklang['highlight_ticket_rows_based_on_priority'] = 'Highlight ticket rows based on priority';
+$hesklang['highlight_ticket_rows_based_on_priority_help'] = 'If enabled, each ticket on the tickets page will be highlighted based on priority. If disabled, only * Critical * and High priority tickets will be highlighted.';
+$hesklang['highlight_ticket_rows_based_on_priority_descr'] = 'Highlight all ticket rows based on priority';
+$hesklang['protected_group'] = 'This is a protected group; you cannot change accessible categories / features.';
+$hesklang['emails_to_receive'] = 'Emails to receive';
+$hesklang['emails_sent_to_staff'] = 'Emails sent to staff';
+$hesklang['emails_sent_to_customer'] = 'Emails sent to customer';
+
// DO NOT CHANGE BELOW
if (!defined('IN_SCRIPT')) die('PHP syntax OK!');
diff --git a/suggest_articles.php b/suggest_articles.php
index 13002665..07b84d66 100644
--- a/suggest_articles.php
+++ b/suggest_articles.php
@@ -59,8 +59,8 @@ if (hesk_isREQUEST('p')) {
}
$txt = strip_tags($article['content']);
- if (strlen($txt) > $hesk_settings['kb_substrart']) {
- $txt = substr($txt, 0, $hesk_settings['kb_substrart']) . '...';
+ if (hesk_mb_strlen($txt) > $hesk_settings['kb_substrart']) {
+ $txt = hesk_mb_substr($txt, 0, $hesk_settings['kb_substrart']).'...';
}
echo '
diff --git a/ticket.php b/ticket.php
index cb7ef11b..1c7e6109 100644
--- a/ticket.php
+++ b/ticket.php
@@ -65,7 +65,7 @@ $is_form = hesk_SESSION('t_form');
$trackingID = hesk_cleanID('', hesk_SESSION('t_track'));
/* Email required to view ticket? */
-$my_email = hesk_getCustomerEmail(1, 't_email');
+$my_email = hesk_getCustomerEmail(1, 't_email', 1);
/* Remember email address? */
$do_remember = strlen($do_remember) || strlen(hesk_SESSION('t_remember')) ? ' checked="checked" ' : '';
@@ -215,6 +215,13 @@ if (!$show['show']) {