From d5a9d225f219b013cdfc512072f2ab57a2d5a74b Mon Sep 17 00:00:00 2001 From: Skylar Ittner Date: Sun, 30 Jul 2017 01:15:27 +0000 Subject: [PATCH 001/248] Fix bootstrap-theme not disabling on Firefox --- inc/header.inc.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/inc/header.inc.php b/inc/header.inc.php index 192e1580..f459df6c 100644 --- a/inc/header.inc.php +++ b/inc/header.inc.php @@ -55,10 +55,10 @@ if (is_dir(HESK_PATH . 'install')) { + > + rel="stylesheet" /> + Date: Sun, 30 Jul 2017 01:21:26 +0000 Subject: [PATCH 002/248] Swap == for != --- inc/header.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/header.inc.php b/inc/header.inc.php index f459df6c..ca27118b 100644 --- a/inc/header.inc.php +++ b/inc/header.inc.php @@ -55,7 +55,7 @@ if (is_dir(HESK_PATH . 'install')) { - + From 68308b02a5dc20de4bfa390f68197fead7aa8328 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Sat, 29 Jul 2017 21:57:29 -0400 Subject: [PATCH 003/248] Some sort of progress on category endpoint --- .../Categories/CategoryHandler.php | 28 ++++++++++++++ .../Categories/CategoryController.php | 15 ++++++++ api/DataAccess/Categories/CategoryGateway.php | 19 ++++++++++ .../Categories/CategoryHandlerTest.php | 37 +++++++++++++++++++ 4 files changed, 99 insertions(+) create mode 100644 api/BusinessLogic/Categories/CategoryHandler.php create mode 100644 api/Tests/BusinessLogic/Categories/CategoryHandlerTest.php diff --git a/api/BusinessLogic/Categories/CategoryHandler.php b/api/BusinessLogic/Categories/CategoryHandler.php new file mode 100644 index 00000000..ca315998 --- /dev/null +++ b/api/BusinessLogic/Categories/CategoryHandler.php @@ -0,0 +1,28 @@ +categoryGateway = $categoryGateway; + } + + /** + * @param $category Category + * @param $heskSettings array + */ + function createCategory($category, $heskSettings) { + $this->categoryGateway->createCategory($category, $heskSettings); + } + + function editCategory($category, $heskSettings) { + + + } +} \ No newline at end of file diff --git a/api/Controllers/Categories/CategoryController.php b/api/Controllers/Categories/CategoryController.php index e376afef..bb729ca7 100644 --- a/api/Controllers/Categories/CategoryController.php +++ b/api/Controllers/Categories/CategoryController.php @@ -4,6 +4,7 @@ namespace Controllers\Categories; use BusinessLogic\Categories\CategoryRetriever; use BusinessLogic\Exceptions\ApiFriendlyException; +use Controllers\JsonRetriever; class CategoryController { function get($id) { @@ -28,4 +29,18 @@ class CategoryController { return $categoryRetriever->getAllCategories($hesk_settings, $userContext); } + + function post() { + //-- TODO: Create Category + $data = JsonRetriever::getJsonData(); + + } + + function put($id) { + //-- TODO: Edit category + } + + function delete($id) { + //-- TODO: Delete category + } } \ No newline at end of file diff --git a/api/DataAccess/Categories/CategoryGateway.php b/api/DataAccess/Categories/CategoryGateway.php index 4fd35c7b..fde6f824 100644 --- a/api/DataAccess/Categories/CategoryGateway.php +++ b/api/DataAccess/Categories/CategoryGateway.php @@ -40,4 +40,23 @@ class CategoryGateway extends CommonDao { return $results; } + + /** + * @param $category Category + * @param $heskSettings array + */ + function createCategory($category, $heskSettings) { + $this->init(); + + $sql = "INSERT INTO `" . hesk_dbEscape($heskSettings['db_pfix']) . "categories` () + VALUES ()"; + + $this->close(); + } + + function updateCategory($category, $heskSettings) { + $this->init(); + + $this->close(); + } } \ No newline at end of file diff --git a/api/Tests/BusinessLogic/Categories/CategoryHandlerTest.php b/api/Tests/BusinessLogic/Categories/CategoryHandlerTest.php new file mode 100644 index 00000000..32b5f99c --- /dev/null +++ b/api/Tests/BusinessLogic/Categories/CategoryHandlerTest.php @@ -0,0 +1,37 @@ +categoryGateway = $this->createMock(CategoryGateway::class); + + $this->categoryHandler = new CategoryHandler($this->categoryGateway); + $this->heskSettings = array(); + } + + function testCreateCallsTheGatewayWithTheCategory() { + //-- Arrange + $category = new Category(); + + //-- Assert + $this->categoryGateway->expects($this->once())->method('createCategory')->with($category, $this->heskSettings); + + //-- Act + $this->categoryHandler->createCategory($category, $this->heskSettings); + } +} From 4db151de66b88cd523148e7c11614c56133797b9 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Sun, 30 Jul 2017 14:52:03 -0400 Subject: [PATCH 004/248] Disable AutoTLS for PHPMailer --- api/BusinessLogic/Emails/BasicEmailSender.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api/BusinessLogic/Emails/BasicEmailSender.php b/api/BusinessLogic/Emails/BasicEmailSender.php index 058ef937..fd6cecfe 100644 --- a/api/BusinessLogic/Emails/BasicEmailSender.php +++ b/api/BusinessLogic/Emails/BasicEmailSender.php @@ -15,6 +15,10 @@ class BasicEmailSender implements EmailSender { if ($heskSettings['smtp']) { $mailer->isSMTP(); $mailer->SMTPAuth = true; + + //-- We'll set this explicitly below if the user has it enabled. + $mailer->SMTPAutoTLS = false; + if ($heskSettings['smtp_ssl']) { $mailer->SMTPSecure = "ssl"; } elseif ($heskSettings['smtp_tls']) { From 84f491bd40e9fc2bd04ee1c18578924a6eb91c54 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Sun, 30 Jul 2017 15:05:53 -0400 Subject: [PATCH 005/248] Don't fail resending email when there is no owner --- api/BusinessLogic/Emails/EmailTemplateParser.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/api/BusinessLogic/Emails/EmailTemplateParser.php b/api/BusinessLogic/Emails/EmailTemplateParser.php index 4fbc3e65..e53a03b2 100644 --- a/api/BusinessLogic/Emails/EmailTemplateParser.php +++ b/api/BusinessLogic/Emails/EmailTemplateParser.php @@ -190,7 +190,9 @@ class EmailTemplateParser { $defaultStatus = $this->statusGateway->getStatusForDefaultAction(DefaultStatusForAction::NEW_TICKET, $heskSettings); $statusName = hesk_msgToPlain($defaultStatus->localizedNames[$language]); $category = hesk_msgToPlain($this->categoryGateway->getAllCategories($heskSettings)[$ticket->categoryId]->name); - $owner = hesk_msgToPlain($this->userGateway->getUserById($ticket->ownerId, $heskSettings)->name); + $owner = $this->userGateway->getUserById($ticket->ownerId, $heskSettings); + + $ownerName = $owner === null ? $hesklang['unas'] : hesk_msgToPlain($owner->name); switch ($ticket->priorityId) { case Priority::CRITICAL: @@ -219,7 +221,7 @@ class EmailTemplateParser { $msg = str_replace('%%SITE_URL%%', $heskSettings['site_url'], $msg); $msg = str_replace('%%CATEGORY%%', $category, $msg); $msg = str_replace('%%PRIORITY%%', $priority, $msg); - $msg = str_replace('%%OWNER%%', $owner, $msg); + $msg = str_replace('%%OWNER%%', $ownerName, $msg); $msg = str_replace('%%STATUS%%', $statusName, $msg); $msg = str_replace('%%EMAIL%%', implode(';',$ticket->email), $msg); $msg = str_replace('%%CREATED%%', $ticket->dateCreated, $msg); From 73b3a01bc0470bf38ef80a552ca13d8f8e202bc6 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Thu, 10 Aug 2017 21:51:59 -0400 Subject: [PATCH 006/248] Version checks are now asynchronous --- admin/admin_settings.php | 299 ++++++------------ .../System/HeskVersionController.php | 56 ++++ api/index.php | 82 +++-- 3 files changed, 218 insertions(+), 219 deletions(-) create mode 100644 api/Controllers/System/HeskVersionController.php diff --git a/admin/admin_settings.php b/admin/admin_settings.php index 6f686d97..fcf24ec4 100644 --- a/admin/admin_settings.php +++ b/admin/admin_settings.php @@ -355,86 +355,121 @@ $modsForHesk_settings = mfh_getSettings(); : - - + - ' . $hesklang['hud'] . ' '; - } elseif ($latest != -1) { - // Is this a beta/dev version? - if (strpos($hesk_settings['hesk_version'], 'beta') || strpos($hesk_settings['hesk_version'], 'dev') || strpos($hesk_settings['hesk_version'], 'RC')) { - echo ' ' . $hesklang['beta'] . ' '; ?> ' . $hesklang['hnw'] . ' '; ?> - - + - + + + + + + + + + - + target="_blank"> + : - - + - ' . $hesklang['beta'] . ' '; ?> ' . $hesklang['mfh_up_to_date'] . ''; - } else { - ?> - - - + - + + + + + + + + - - + + @@ -3935,130 +3970,6 @@ $modsForHesk_settings = mfh_getSettings(); } - function hesk_checkVersion() - { - global $hesk_settings; - - if ($latest = hesk_getLatestVersion()) { - if (strlen($latest) > 12) { - return -1; - } elseif ($latest == $hesk_settings['hesk_version']) { - return true; - } else { - return $latest; - } - } else { - return -1; - } - - } // END hesk_checkVersion() - - - function hesk_getLatestVersion() - { - global $hesk_settings; - - // Do we have a cached version file? - if (file_exists(HESK_PATH . $hesk_settings['cache_dir'] . '/__latest.txt')) { - if (preg_match('/^(\d+)\|([\d.]+)+$/', @file_get_contents(HESK_PATH . $hesk_settings['cache_dir'] . '/__latest.txt'), $matches) && (time() - intval($matches[1])) < 3600) { - return $matches[2]; - } - } - - // No cached file or older than 3600 seconds, try to get an update - $hesk_version_url = 'https://hesk.com/version'; - - // Try using cURL - if (function_exists('curl_init')) { - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $hesk_version_url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 6); - $latest = curl_exec($ch); - curl_close($ch); - return hesk_cacheLatestVersion($latest); - } - - // Try using a simple PHP function instead - if ($latest = @file_get_contents($hesk_version_url)) { - return hesk_cacheLatestVersion($latest); - } - - // Can't check automatically, will need a manual check - return false; - - } // END hesk_getLatestVersion() - - function hesk_cacheLatestVersion($latest) - { - global $hesk_settings; - - @file_put_contents(HESK_PATH . $hesk_settings['cache_dir'] . '/__latest.txt', time() . '|' . $latest); - - return $latest; - - } // END hesk_cacheLatestVersion() - - function hesk_checkMfhVersion($currentVersion) - { - if ($latest = hesk_getMfhLatestVersion()) { - if (strlen($latest) > 12) { - return -1; - } elseif ($latest == $currentVersion) { - return true; - } else { - return $latest; - } - } else { - return -1; - } - } - - function hesk_getMfhLatestVersion() - { - global $hesk_settings; - - // Do we have a cached version file? - if (file_exists(HESK_PATH . $hesk_settings['cache_dir'] . '/__latest-mfh.txt')) { - if (preg_match('/^(\d+)\|([\d.]+)+$/', @file_get_contents(HESK_PATH . $hesk_settings['cache_dir'] . '/__latest-mfh.txt'), $matches) && (time() - intval($matches[1])) < 3600) { - return $matches[2]; - } - } - - // No cached file or older than 3600 seconds, try to get an update - $hesk_version_url = 'http://mods-for-hesk.mkochcs.com/latestversion.php'; - - // Try using cURL - if (function_exists('curl_init')) { - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $hesk_version_url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 6); - $latest = curl_exec($ch); - curl_close($ch); - return hesk_cacheMfhLatestVersion($latest); - } - - // Try using a simple PHP function instead - if ($latest = file_get_contents($hesk_version_url)) { - return hesk_cacheMfhLatestVersion($latest); - } - - // Can't check automatically, will need a manual check - return false; - } - - function hesk_cacheMfhLatestVersion($latest) - { - global $hesk_settings; - - @file_put_contents(HESK_PATH . $hesk_settings['cache_dir'] . '/__latest-mfh.txt', time() . '|' . $latest); - - return $latest; - - } - - function hesk_testLanguage($return_options = 0) { global $hesk_settings, $hesklang, $modsForHesk_settings; diff --git a/api/Controllers/System/HeskVersionController.php b/api/Controllers/System/HeskVersionController.php new file mode 100644 index 00000000..c07122ed --- /dev/null +++ b/api/Controllers/System/HeskVersionController.php @@ -0,0 +1,56 @@ + $matches[2])); + } + + // Try using cURL + if (function_exists('curl_init')) { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 6); + $latest = curl_exec($ch); + curl_close($ch); + + self::cacheLatestVersion($latest, $fileName, $hesk_settings); + + return output(array('version' => $latest)); + } + + // Try using a simple PHP function instead + if ($latest = @file_get_contents($url)) { + self::cacheLatestVersion($latest, $fileName, $hesk_settings); + + return output(array('version' => $latest)); + } + + // Can't check automatically, will need a manual check + return http_response_code(408); + } + + private static function cacheLatestVersion($latest, $fileName, $hesk_settings) { + @file_put_contents(__DIR__ . '/../../../' . $hesk_settings['cache_dir'] . '/' . $fileName, + time() . '|' . $latest); + } +} \ No newline at end of file diff --git a/api/index.php b/api/index.php index 672bbf8b..63879505 100644 --- a/api/index.php +++ b/api/index.php @@ -15,26 +15,37 @@ function handle404() { ), 404); } -function before() { - if (defined('HESK_DEMO') && $_SERVER['REQUEST_METHOD'] !== 'GET') { +function globalBefore() { + if (defined('HESK_DEMO') && $_SERVER['REQUEST_METHOD'] === 'GET') { print_error('Demo Mode', 'Only read-only commands are available in demo mode!', null, 401); die(); } +} +function internalHandler() { + buildUserContextFromSession(); +} + +function authTokenHandler() { + assertApiIsEnabled(); + $token = \BusinessLogic\Helpers::getHeader('X-AUTH-TOKEN'); + buildUserContext($token); +} + +function internalOrAuthHandler() { $internalUse = \BusinessLogic\Helpers::getHeader('X-INTERNAL-CALL'); if ($internalUse === 'true') { - buildUserContextFromSession(); - } elseif (preg_match('/\/v1\/tickets\/.+\/attachments\/\d+/', $_SERVER['PATH_INFO'])) { - //-- TODO Clean this up - return; + internalHandler(); } else { - assertApiIsEnabled(); - $token = \BusinessLogic\Helpers::getHeader('X-AUTH-TOKEN'); - buildUserContext($token); + authTokenHandler(); } } +function publicHandler() { + //-- No-op +} + function assertApiIsEnabled() { global $applicationContext, $hesk_settings; @@ -172,34 +183,55 @@ function fatalErrorShutdownHandler() { } } -Link::before('before'); +Link::before('globalBefore'); Link::all(array( // Categories - '/v1/categories' => [\Controllers\Categories\CategoryController::class . '::printAllCategories'], - '/v1/categories/{i}' => \Controllers\Categories\CategoryController::class, + '/v1/categories' => action(\Controllers\Categories\CategoryController::class . '::printAllCategories'), + '/v1/categories/{i}' => action(\Controllers\Categories\CategoryController::class), // Tickets - '/v1/tickets' => \Controllers\Tickets\CustomerTicketController::class, + '/v1/tickets' => action(\Controllers\Tickets\CustomerTicketController::class), // Tickets - Staff - '/v1/staff/tickets/{i}' => \Controllers\Tickets\StaffTicketController::class, + '/v1/staff/tickets/{i}' => action(\Controllers\Tickets\StaffTicketController::class), // Attachments - '/v1/tickets/{a}/attachments/{i}' => \Controllers\Attachments\PublicAttachmentController::class . '::getRaw', - '/v1/staff/tickets/{i}/attachments' => \Controllers\Attachments\StaffTicketAttachmentsController::class, - '/v1/staff/tickets/{i}/attachments/{i}' => \Controllers\Attachments\StaffTicketAttachmentsController::class, + '/v1/tickets/{a}/attachments/{i}' => action(\Controllers\Attachments\PublicAttachmentController::class . '::getRaw'), + '/v1/staff/tickets/{i}/attachments' => action(\Controllers\Attachments\StaffTicketAttachmentsController::class), + '/v1/staff/tickets/{i}/attachments/{i}' => action(\Controllers\Attachments\StaffTicketAttachmentsController::class), // Statuses - '/v1/statuses' => \Controllers\Statuses\StatusController::class, + '/v1/statuses' => action(\Controllers\Statuses\StatusController::class), // Settings - '/v1/settings' => \Controllers\Settings\SettingsController::class, + '/v1/settings' => action(\Controllers\Settings\SettingsController::class), /* Internal use only routes */ // Resend email response - '/v1-internal/staff/tickets/{i}/resend-email' => \Controllers\Tickets\ResendTicketEmailToCustomerController::class, + '/v1-internal/staff/tickets/{i}/resend-email' => + action(\Controllers\Tickets\ResendTicketEmailToCustomerController::class, SecurityHandler::INTERNAL), // Custom Navigation - '/v1-internal/custom-navigation/all' => \Controllers\Navigation\CustomNavElementController::class . '::getAll', - '/v1-internal/custom-navigation' => \Controllers\Navigation\CustomNavElementController::class, - '/v1-internal/custom-navigation/{i}' => \Controllers\Navigation\CustomNavElementController::class, - '/v1-internal/custom-navigation/{i}/sort/{s}' => \Controllers\Navigation\CustomNavElementController::class . '::sort', + '/v1-internal/custom-navigation/all' => + action(\Controllers\Navigation\CustomNavElementController::class . '::getAll', SecurityHandler::INTERNAL), + '/v1-internal/custom-navigation' => + action(\Controllers\Navigation\CustomNavElementController::class, SecurityHandler::INTERNAL), + '/v1-internal/custom-navigation/{i}' => + action(\Controllers\Navigation\CustomNavElementController::class, SecurityHandler::INTERNAL), + '/v1-internal/custom-navigation/{i}/sort/{s}' => + action(\Controllers\Navigation\CustomNavElementController::class . '::sort', SecurityHandler::INTERNAL), + + '/v1-public/hesk-version' => + action(\Controllers\System\HeskVersionController::class . '::getHeskVersion', SecurityHandler::OPEN), + '/v1-public/mods-for-hesk-version' => + action(\Controllers\System\HeskVersionController::class . '::getModsForHeskVersion', SecurityHandler::OPEN), // Any URL that doesn't match goes to the 404 handler '404' => 'handle404' -)); \ No newline at end of file +)); + +function action($class, $securityHandler = SecurityHandler::AUTH_TOKEN) { + return [$class, $class, $securityHandler]; +} + +class SecurityHandler { + const OPEN = 'publicHandler'; + const INTERNAL = 'internalHandler'; + const AUTH_TOKEN = 'authTokenHandler'; + const INTERNAL_OR_AUTH_TOKEN = 'internalOrAuthHandler'; +} \ No newline at end of file From 65576d5c4333464102d272c70798f850a8846b50 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Sat, 12 Aug 2017 21:41:41 -0400 Subject: [PATCH 007/248] Fix document type on IE intranet pages --- inc/header.inc.php | 1 + inc/headerAdmin.inc.php | 2 ++ 2 files changed, 3 insertions(+) diff --git a/inc/header.inc.php b/inc/header.inc.php index ca27118b..f35f50fa 100644 --- a/inc/header.inc.php +++ b/inc/header.inc.php @@ -43,6 +43,7 @@ if (is_dir(HESK_PATH . 'install')) { $modsForHesk_settings = mfh_getSettings(); } +header('X-UA-Compatible: IE=edge'); ?> diff --git a/inc/headerAdmin.inc.php b/inc/headerAdmin.inc.php index ce2f0f1d..20501baa 100644 --- a/inc/headerAdmin.inc.php +++ b/inc/headerAdmin.inc.php @@ -21,6 +21,8 @@ if (!defined('IN_SCRIPT')) { define('ADMIN_PAGE', true); $modsForHesk_settings = mfh_getSettings(); + +header('X-UA-Compatible: IE=edge'); ?> From c60d2ed8662b9224fd6ee77935afafc7dc3ed289 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Sat, 12 Aug 2017 21:57:41 -0400 Subject: [PATCH 008/248] Update installer --- install/install_functions.inc.php | 2 +- install/mods-for-hesk/ajax/install-database-ajax.php | 2 ++ install/mods-for-hesk/installModsForHesk.php | 1 + install/mods-for-hesk/js/version-scripts.js | 3 +++ install/mods-for-hesk/modsForHesk.php | 1 + install/mods-for-hesk/sql/installSql.php | 7 +++++++ 6 files changed, 15 insertions(+), 1 deletion(-) diff --git a/install/install_functions.inc.php b/install/install_functions.inc.php index 864e6dc8..a498ab67 100644 --- a/install/install_functions.inc.php +++ b/install/install_functions.inc.php @@ -16,7 +16,7 @@ if (!defined('IN_SCRIPT')) {die('Invalid attempt');} // We will be installing this HESK version: define('HESK_NEW_VERSION','2.7.3'); -define('MODS_FOR_HESK_NEW_VERSION','3.1.0'); +define('MODS_FOR_HESK_NEW_VERSION','3.1.1'); define('REQUIRE_PHP_VERSION','5.3.0'); define('REQUIRE_MYSQL_VERSION','5.0.7'); diff --git a/install/mods-for-hesk/ajax/install-database-ajax.php b/install/mods-for-hesk/ajax/install-database-ajax.php index e28169ec..77aed493 100644 --- a/install/mods-for-hesk/ajax/install-database-ajax.php +++ b/install/mods-for-hesk/ajax/install-database-ajax.php @@ -90,6 +90,8 @@ if ($version == 2) { execute307Scripts(); } elseif ($version == 42) { execute310Scripts(); +} elseif ($version == 43) { + execute311Scripts(); } else { $response = 'The version "' . $version . '" was not recognized. Check the value submitted and try again.'; print $response; diff --git a/install/mods-for-hesk/installModsForHesk.php b/install/mods-for-hesk/installModsForHesk.php index cc89aaf4..cae36a82 100644 --- a/install/mods-for-hesk/installModsForHesk.php +++ b/install/mods-for-hesk/installModsForHesk.php @@ -51,6 +51,7 @@ $buildToVersionMap = array( 40 => '3.0.6', 41 => '3.0.7', 42 => '3.1.0', + 43 => '3.1.1', ); function echoInitialVersionRows($version, $build_to_version_map) diff --git a/install/mods-for-hesk/js/version-scripts.js b/install/mods-for-hesk/js/version-scripts.js index 68a83e12..a7a9d1fc 100644 --- a/install/mods-for-hesk/js/version-scripts.js +++ b/install/mods-for-hesk/js/version-scripts.js @@ -122,6 +122,9 @@ function processUpdates(startingVersion) { } else if (startingVersion < 42) { startVersionUpgrade('310'); executeUpdate(42, '310', '3.1.0'); + } else if (startingVersion < 43) { + startVersionUpgrade('311'); + executeUpdate(43, '311', '3.1.1'); } else { installationFinished(); } diff --git a/install/mods-for-hesk/modsForHesk.php b/install/mods-for-hesk/modsForHesk.php index 386a53ff..4496bc49 100644 --- a/install/mods-for-hesk/modsForHesk.php +++ b/install/mods-for-hesk/modsForHesk.php @@ -118,6 +118,7 @@ hesk_dbConnect(); buildCategoryFromJson($data); - $category->id = $id; + $category->id = intval($id); /* @var $categoryHandler CategoryHandler */ $categoryHandler = $applicationContext->get[CategoryHandler::class]; diff --git a/api/index.php b/api/index.php index 25b77ee3..ffaf590d 100644 --- a/api/index.php +++ b/api/index.php @@ -188,8 +188,8 @@ Link::before('globalBefore'); Link::all(array( // Categories '/v1/categories/all' => action(\Controllers\Categories\CategoryController::class . '::printAllCategories', [RequestMethod::GET], SecurityHandler::INTERNAL_OR_AUTH_TOKEN), - '/v1/categories' => action(\Controllers\Categories\CategoryController::class, [RequestMethod::POST]), - '/v1/categories/{i}' => action(\Controllers\Categories\CategoryController::class, [RequestMethod::GET, RequestMethod::PUT, RequestMethod::DELETE]), + '/v1/categories' => action(\Controllers\Categories\CategoryController::class, [RequestMethod::POST], SecurityHandler::INTERNAL_OR_AUTH_TOKEN), + '/v1/categories/{i}' => action(\Controllers\Categories\CategoryController::class, [RequestMethod::GET, RequestMethod::PUT, RequestMethod::DELETE], SecurityHandler::INTERNAL_OR_AUTH_TOKEN), // Tickets '/v1/tickets' => action(\Controllers\Tickets\CustomerTicketController::class), // Tickets - Staff diff --git a/internal-api/js/manage-categories.js b/internal-api/js/manage-categories.js index a7716ad7..077eb423 100644 --- a/internal-api/js/manage-categories.js +++ b/internal-api/js/manage-categories.js @@ -62,9 +62,10 @@ function loadTable() { // Low $priority.text(mfhLang.text('low')).addClass('normal'); } + var linkPattern = $('input[name="show-tickets-path"]').val(); $template.find('a[data-property="number-of-tickets"]') .text(this.numberOfTickets) - .attr('href', '#' + this.numberOfTickets); + .attr('href', linkPattern.replace('{0}', this.id)); var percentText = mfhLang.text('perat'); var percentage = Math.round(this.numberOfTickets / totalNumberOfTickets * 100); $template.find('div.progress').attr('title', percentText.replace('%s', percentage + '%')); @@ -165,6 +166,9 @@ function bindEditModal() { $modal.find('input[name="foreground-color"]') .colorpicker(colorpickerOptions).end().modal('show'); $modal.find('input[name="cat-order"]').val(element.catOrder); + $modal.find('input[name="autoassign"]').val(element.autoAssign); + $modal.find('input[name="type"]').val(element.type); + $modal.find('textarea[name="description"]').val(element.description === null ? '' : element.description); $modal.modal('show'); }); @@ -189,16 +193,16 @@ function bindFormSubmit() { var $modal = $('#category-modal'); var data = { - autoassign: $modal.find('input[name="autoassign"]').val(), + autoassign: $modal.find('input[name="autoassign"]').val() === 'true', backgroundColor: $modal.find('input[name="background-color"]').val(), description: $modal.find('textarea[name="description"]').val(), - displayBorder: $modal.find('input[name="display-border"]:checked').val(), - foregroundColor: $modal.find('input[name="foreground-color"]').val() === '' ? 'AUTO' : $modal.find('input[name="foreground-color"]'), + displayBorder: $modal.find('input[name="display-border"]:checked').val() === '1', + foregroundColor: $modal.find('input[name="foreground-color"]').val() === '' ? 'AUTO' : $modal.find('input[name="foreground-color"]').val(), name: $modal.find('input[name="name"]').val(), - priority: $modal.find('select[name="priority"]').val(), - type: $modal.find('input[name="type"]').val(), - usage: $modal.find('select[name="usage"]').val(), - catOrder: $modal.find('input[name="cat-order"]').val() + priority: parseInt($modal.find('select[name="priority"]').val()), + type: parseInt($modal.find('input[name="type"]').val()), + usage: parseInt($modal.find('select[name="usage"]').val()), + catOrder: parseInt($modal.find('input[name="cat-order"]').val()) }; var url = heskUrl + 'api/index.php/v1/categories/'; @@ -206,16 +210,14 @@ function bindFormSubmit() { var categoryId = $modal.find('input[name="id"]').val(); if (categoryId !== -1) { - url += id; + url += categoryId; method = 'PUT'; } $modal.find('#action-buttons').find('.cancel-button').attr('disabled', 'disabled'); $modal.find('#action-buttons').find('.save-button').attr('disabled', 'disabled'); - console.log('') - - /*$.ajax({ + $.ajax({ method: 'POST', url: url, headers: { @@ -224,7 +226,7 @@ function bindFormSubmit() { }, data: JSON.stringify(data), success: function(data) { - if (id === -1) { + if (categoryId === -1) { mfhAlert.success('CREATED'); } else { mfhAlert.success('SAVED'); @@ -240,6 +242,6 @@ function bindFormSubmit() { $modal.find('#action-buttons').find('.cancel-button').removeAttr('disabled'); $modal.find('#action-buttons').find('.save-button').removeAttr('disabled'); } - });*/ + }); }); } \ No newline at end of file From 24cdb08449d60f5d7076b8aa336f474ca1b4083b Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Wed, 30 Aug 2017 22:03:51 -0400 Subject: [PATCH 029/248] Categories can be created, modify modal --- admin/manage_categories.php | 89 ++++++++++++++++++++-------- internal-api/js/manage-categories.js | 74 ++++++++++++++++++++++- language/en/text.php | 3 + 3 files changed, 140 insertions(+), 26 deletions(-) diff --git a/admin/manage_categories.php b/admin/manage_categories.php index b90caebb..ad504c95 100644 --- a/admin/manage_categories.php +++ b/admin/manage_categories.php @@ -297,17 +297,18 @@ $res = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($hesk_settings['db_pfix'])
- - \ No newline at end of file From 109a4fa25feb962e18d48aeb98ae1b2990582702 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Fri, 20 Oct 2017 13:07:01 -0400 Subject: [PATCH 121/248] Uninstaller works --- install/database-validation.php | 4 ++-- install/js/install-script.js | 10 +++++----- install/js/uninstall-script.js | 16 ++++++++-------- install/uninstall.php | 4 ++-- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/install/database-validation.php b/install/database-validation.php index 4eaf4c8a..e7ae6853 100644 --- a/install/database-validation.php +++ b/install/database-validation.php @@ -31,7 +31,7 @@ hesk_dbConnect(); + var users = [];'; diff --git a/api/Controllers/ServiceMessages/ServiceMessagesController.php b/api/Controllers/ServiceMessages/ServiceMessagesController.php index 94ff9776..faf9f568 100644 --- a/api/Controllers/ServiceMessages/ServiceMessagesController.php +++ b/api/Controllers/ServiceMessages/ServiceMessagesController.php @@ -6,7 +6,6 @@ use BusinessLogic\Security\UserContext; use BusinessLogic\ServiceMessages\ServiceMessage; use BusinessLogic\ServiceMessages\ServiceMessageHandler; use Controllers\JsonRetriever; -use Symfony\Component\EventDispatcher\Tests\Service; class ServiceMessagesController extends \BaseClass { function get() { diff --git a/internal-api/js/service-messages.js b/internal-api/js/service-messages.js index a2c3f950..fc3754af 100644 --- a/internal-api/js/service-messages.js +++ b/internal-api/js/service-messages.js @@ -3,7 +3,6 @@ var serviceMessages = []; $(document).ready(function() { loadTable(); bindEditModal(); - bindModalCancelCallback(); bindFormSubmit(); bindDeleteButton(); bindCreateModal(); @@ -24,7 +23,7 @@ function loadTable() { $tableBody.html(''); if (data.length === 0) { - // TODO "No Service Messages Found" + $tableBody.append('' + mfhLang.text('no_sm') + ''); $('#overlay').hide(); return; } @@ -63,15 +62,10 @@ function loadTable() { } }, error: function(data) { - mfhAlert.errorWithLog(mfhLang.text('error_retrieving_categories'), data.responseJSON); + mfhAlert.errorWithLog(mfhLang.text('error_retrieving_sm'), data.responseJSON); console.error(data); }, complete: function() { - refreshBackgroundVolatileItems(); - $('[data-toggle="popover"]').popover({ - trigger: 'hover', - container: 'body' - }); $('#overlay').hide(); } }); @@ -184,49 +178,19 @@ function bindEditModal() { function bindCreateModal() { $('#create-button').click(function() { - var $modal = $('#category-modal'); - $modal.find('#edit-label').hide(); - $modal.find('#create-label').show(); - - $modal.find('input[name="name"]').val(''); - $modal.find('select[name="priority"]').val(3); // Low priority - $modal.find('select[name="usage"]').val(0); // Tickets and events - $modal.find('input[name="id"]').val(-1); - $modal.find('textarea[name="description"]').val(''); - $modal.find('input[name="cat-order"]').val(''); - $modal.find('input[name="type"][value="0"]').prop('checked', 'checked'); - $modal.find('input[name="autoassign"][value="0"]').prop('checked', 'checked'); - $modal.find('input[name="display-border"][value="0"]') - .prop('checked', 'checked'); - - var colorpickerOptions = { - format: 'hex', - color: '#fff' - }; - $modal.find('input[name="background-color"]') - .colorpicker(colorpickerOptions).end().modal('show'); - $modal.find('input[name="background-color"]').val(''); - $modal.find('input[name="foreground-color"]') - .colorpicker(colorpickerOptions).end().modal('show'); - $modal.find('input[name="foreground-color"]').val(''); + 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(); + setIcon(''); + tinyMCE.get('content').setContent(''); $modal.modal('show'); }); } -function bindModalCancelCallback() { - $('.cancel-callback').click(function() { - var $editCategoryModal = $('#category-modal'); - - $editCategoryModal.find('input[name="background-color"]').val('').colorpicker('destroy').end(); - $editCategoryModal.find('input[name="foreground-color"]').val('').colorpicker('destroy').end(); - $editCategoryModal.find('input[name="display-border"][value="1"]').prop('checked'); - $editCategoryModal.find('input[name="display-border"][value="0"]').prop('checked'); - $editCategoryModal.find('input[name="autoassign"][value="1"]').prop('checked'); - $editCategoryModal.find('input[name="autoassign"][value="0"]').prop('checked'); - }); -} - function bindFormSubmit() { $('form#manage-category').submit(function(e) { e.preventDefault(); diff --git a/language/en/text.php b/language/en/text.php index d543ccd3..833551f5 100644 --- a/language/en/text.php +++ b/language/en/text.php @@ -2215,5 +2215,8 @@ $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['error_retrieving_sm'] = 'An error occurred retrieving service messages!'; + // DO NOT CHANGE BELOW if (!defined('IN_SCRIPT')) die('PHP syntax OK!'); From d897fa2a4bacb07d310d73c4a709f4af0826ebbd Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Mon, 13 Nov 2017 22:13:55 -0500 Subject: [PATCH 162/248] More progress' --- admin/service_messages.php | 10 +-- internal-api/js/service-messages.js | 100 +++++++++++----------------- 2 files changed, 46 insertions(+), 64 deletions(-) diff --git a/admin/service_messages.php b/admin/service_messages.php index 6368b572..8602f3ee 100644 --- a/admin/service_messages.php +++ b/admin/service_messages.php @@ -93,6 +93,7 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php'); + @@ -121,7 +122,7 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php'); - + +
+ +
+
Customer Pages
+
+ Homepage +
+
+ Knowledgebase Home +
+
+ View Knowledgebase Article +
+
+ Submit Ticket +
+
+ View Ticket +
+
+
+
Staff Pages
+
+ Login Page +
+
+ Homepage +
+
+ Knowledgebase Home +
+
+ View Knowledgebase Article +
+
+ Submit Ticket +
+
+ View Ticket +
+
+
diff --git a/install/migrations/v330/UpdateExistingServiceMessagesLocations.php b/install/migrations/v330/UpdateExistingServiceMessagesLocations.php new file mode 100644 index 00000000..899909dc --- /dev/null +++ b/install/migrations/v330/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 index 050c864a..ebe79766 100644 --- a/internal-api/js/service-messages.js +++ b/internal-api/js/service-messages.js @@ -137,7 +137,8 @@ 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(''); + $modal.find('#preview-pane').html('').end() + .find('input[name="location[]"]').prop('checked', false); $modal.find('#edit-label').show(); $modal.find('#create-label').hide(); @@ -150,6 +151,10 @@ function bindEditModal() { .find('input[name="order"]').val(element.order).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 { @@ -171,7 +176,8 @@ function bindCreateModal() { .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('#preview-pane').html('').end() + .find('input[name="location[]"]').prop('checked', false); setIcon(''); if ($('input[name="kb_wysiwyg"]').val() === "1") { @@ -198,13 +204,21 @@ function bindFormSubmit() { 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() + order: $modal.find('input[name="order"]').val(), + locations: locations }; var url = heskUrl + 'api/index.php/v1/service-messages/'; From 3e82065fa4010c92098bbee2967f97c9a9ec8f0f Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Sat, 18 Nov 2017 22:05:11 -0500 Subject: [PATCH 169/248] Working on adding a language option to service messages --- admin/service_messages.php | 330 ++++++++++-------- install/migrations/core.php | 4 +- .../AddLanguageColumnToServiceMessages.php | 17 + .../CreateServiceMessageToLocationTable.php | 2 +- ...UpdateExistingServiceMessagesLocations.php | 2 +- 5 files changed, 213 insertions(+), 142 deletions(-) create mode 100644 install/migrations/v330/ServiceMessagesImprovements/AddLanguageColumnToServiceMessages.php rename install/migrations/v330/{ => ServiceMessagesImprovements}/CreateServiceMessageToLocationTable.php (91%) rename install/migrations/v330/{ => ServiceMessagesImprovements}/UpdateExistingServiceMessagesLocations.php (94%) diff --git a/admin/service_messages.php b/admin/service_messages.php index 93ad7a5c..8d13ea0a 100644 --- a/admin/service_messages.php +++ b/admin/service_messages.php @@ -130,150 +130,202 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php');
+ @@ -131,11 +132,11 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php'); + ', $change_title, $css_color, $text); From 22ecc4804efe51bd3f188d5384579603bbc3a4e6 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Sun, 4 Feb 2018 21:16:18 -0500 Subject: [PATCH 227/248] Improve the validator --- install/database-validation.php | 158 +++++++++++++++++++++----------- install/index.php | 2 +- 2 files changed, 105 insertions(+), 55 deletions(-) diff --git a/install/database-validation.php b/install/database-validation.php index 8f47a9c9..d90aa816 100644 --- a/install/database-validation.php +++ b/install/database-validation.php @@ -21,7 +21,7 @@ if (hesk_dbNumRows($tableSql) > 0) { // They have installed at LEAST to version 1.6.0. Just pull the version number OR migration number $migrationNumberSql = hesk_dbQuery("SELECT `Value` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "settings` WHERE `Key` = 'migrationNumber'"); if ($migrationRow = hesk_dbFetchAssoc($migrationNumberSql)) { - $startingValidationNumber = intval($migrationRow['Value']) + 1; + $startingValidationNumber = intval($migrationRow['Value']); } else { $versionSql = hesk_dbQuery("SELECT `Value` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "settings` WHERE `Key` = 'modsForHeskVersion'"); $versionRow = hesk_dbFetchAssoc($versionSql); @@ -79,12 +79,11 @@ if (hesk_dbNumRows($tableSql) > 0) {
+

Results

@@ -219,56 +226,87 @@ if (hesk_dbNumRows($tableSql) > 0) { $validations[] = run_column_check('categories', 'usage', 112); $validations[] = run_column_check('users', 'notify_overdue_unassigned', 113); $validations[] = run_column_check('users', 'default_calendar_view', 114); + $validations[] = run_setting_check('enable_calendar', 115); + $validations[] = run_setting_check('first_day_of_week', 116); + $validations[] = run_setting_check('default_calendar_view', 117); output_header_row('2.6.2'); - $validations[] = run_column_check('stage_tickets', 'due_date'); - $validations[] = run_column_check('stage_tickets', 'overdue_email_sent'); + $validations[] = run_column_check('stage_tickets', 'due_date', 122); + $validations[] = run_column_check('stage_tickets', 'overdue_email_sent', 123); output_header_row('3.1.0'); - $validations[] = run_column_check('categories', 'background_color'); - $validations[] = run_column_check('categories', 'foreground_color'); - $validations[] = run_column_check('categories', 'display_border_outline'); - $validations[] = run_column_check('logging', 'stack_trace'); - $validations[] = run_table_check('custom_nav_element'); - $validations[] = run_column_check('custom_nav_element', 'id'); - $validations[] = run_column_check('custom_nav_element', 'image_url'); - $validations[] = run_column_check('custom_nav_element', 'font_icon'); - $validations[] = run_column_check('custom_nav_element', 'place'); - $validations[] = run_column_check('custom_nav_element', 'url'); - $validations[] = run_column_check('custom_nav_element', 'sort'); - $validations[] = run_table_check('custom_nav_element_to_text'); - $validations[] = run_column_check('custom_nav_element_to_text', 'id'); - $validations[] = run_column_check('custom_nav_element_to_text', 'nav_element_id'); - $validations[] = run_column_check('custom_nav_element_to_text', 'language'); - $validations[] = run_column_check('custom_nav_element_to_text', 'text'); - $validations[] = run_column_check('custom_nav_element_to_text', 'subtext'); - $validations[] = run_setting_check('admin_navbar_background'); - $validations[] = run_setting_check('admin_navbar_background_hover'); - $validations[] = run_setting_check('admin_navbar_text'); - $validations[] = run_setting_check('admin_navbar_text_hover'); - $validations[] = run_setting_check('admin_navbar_brand_background'); - $validations[] = run_setting_check('admin_navbar_brand_background_hover'); - $validations[] = run_setting_check('admin_navbar_brand_text'); - $validations[] = run_setting_check('admin_navbar_brand_text_hover'); - $validations[] = run_setting_check('admin_sidebar_background'); - $validations[] = run_setting_check('admin_sidebar_background_hover'); - $validations[] = run_setting_check('admin_sidebar_text'); - $validations[] = run_setting_check('admin_sidebar_text_hover'); - $validations[] = run_setting_check('admin_sidebar_font_weight'); - $validations[] = run_setting_check('admin_sidebar_header_background'); - $validations[] = run_setting_check('admin_sidebar_header_text'); + $validations[] = run_column_check('logging', 'stack_trace', 140); + $validations[] = run_column_check('categories', 'background_color', 145); + $validations[] = run_column_check('categories', 'foreground_color', 143); + $validations[] = run_column_check('categories', 'display_border_outline', 144); + $validations[] = run_table_check('custom_nav_element', 141); + $validations[] = run_column_check('custom_nav_element', 'id', 141); + $validations[] = run_column_check('custom_nav_element', 'image_url', 141); + $validations[] = run_column_check('custom_nav_element', 'font_icon', 141); + $validations[] = run_column_check('custom_nav_element', 'place', 141); + $validations[] = run_column_check('custom_nav_element', 'url', 141); + $validations[] = run_column_check('custom_nav_element', 'sort', 141); + $validations[] = run_table_check('custom_nav_element_to_text', 142); + $validations[] = run_column_check('custom_nav_element_to_text', 'id', 142); + $validations[] = run_column_check('custom_nav_element_to_text', 'nav_element_id', 142); + $validations[] = run_column_check('custom_nav_element_to_text', 'language', 142); + $validations[] = run_column_check('custom_nav_element_to_text', 'text', 142); + $validations[] = run_column_check('custom_nav_element_to_text', 'subtext', 142); + $validations[] = run_setting_check('admin_navbar_background', 151); + $validations[] = run_setting_check('admin_navbar_background_hover', 151); + $validations[] = run_setting_check('admin_navbar_text', 151); + $validations[] = run_setting_check('admin_navbar_text_hover', 151); + $validations[] = run_setting_check('admin_navbar_brand_background', 151); + $validations[] = run_setting_check('admin_navbar_brand_background_hover', 151); + $validations[] = run_setting_check('admin_navbar_brand_text', 151); + $validations[] = run_setting_check('admin_navbar_brand_text_hover', 151); + $validations[] = run_setting_check('admin_sidebar_background', 151); + $validations[] = run_setting_check('admin_sidebar_background_hover', 151); + $validations[] = run_setting_check('admin_sidebar_text', 151); + $validations[] = run_setting_check('admin_sidebar_text_hover', 151); + $validations[] = run_setting_check('admin_sidebar_font_weight', 151); + $validations[] = run_setting_check('admin_sidebar_header_background', 151); + $validations[] = run_setting_check('admin_sidebar_header_text', 151); + $validations[] = run_setting_check('login_background_type', 146); + $validations[] = run_setting_check('login_background', 147); + $validations[] = run_setting_check('login_box_header', 148); + $validations[] = run_setting_check('login_box_header_image', 149); + $validations[] = run_setting_check('api_url_rewrite', 150); output_header_row('3.2.0'); - $validations[] = run_table_check('audit_trail'); - $validations[] = run_table_check('audit_trail_to_replacement_values'); - $validations[] = run_column_check('categories', 'mfh_description'); - $validations[] = run_column_check('custom_fields', 'mfh_description'); - $validations[] = run_setting_check('migrationNumber'); + $validations[] = run_table_check('audit_trail', 156); + $validations[] = run_table_check('audit_trail_to_replacement_values', 157); + $validations[] = run_column_check('categories', 'mfh_description', 154); + $validations[] = run_column_check('custom_fields', 'mfh_description', 155); + $validations[] = run_setting_check('migrationNumber', 158); output_header_row('3.3.0'); - $validations[] = run_table_check('mfh_calendar_business_hours'); + $validations[] = run_table_check('mfh_service_message_to_location', 164); + $validations[] = run_column_check('mfh_service_message_to_location', 'service_message_id', 164); + $validations[] = run_column_check('mfh_service_message_to_location', 'location', 164); + $validations[] = run_column_check('service_messages', 'mfh_language', 166); + $validations[] = run_table_check('mfh_calendar_business_hours', 167); + $validations[] = run_setting_check('calendar_show_start_time', 169); + $validations[] = run_setting_check('calendar_show_start_time', 999); - if ($checks) { + $passed = false; + $failed = false; + $skipped = false; + foreach ($validations as $validation) { + if ($validation === 'SKIPPED') { + $skipped = true; + } elseif ($validation === 'FAIL') { + $failed = true; + } else if ($validation === 'PASS') { + $passed = true; + } + } + + if ($passed && !$failed) { echo ""; - } else { + } elseif ($failed) { echo ""; } + + if ($skipped) { + echo ""; + } ?>
diff --git a/api/BusinessLogic/ServiceMessages/ServiceMessage.php b/api/BusinessLogic/ServiceMessages/ServiceMessage.php index 9a5aff94..f40e9b9c 100644 --- a/api/BusinessLogic/ServiceMessages/ServiceMessage.php +++ b/api/BusinessLogic/ServiceMessages/ServiceMessage.php @@ -33,4 +33,7 @@ class ServiceMessage extends \BaseClass { /* @var $locations string[] */ public $locations; + + /* @var $language string */ + public $language; } \ No newline at end of file diff --git a/api/BusinessLogic/ServiceMessages/ServiceMessageHandler.php b/api/BusinessLogic/ServiceMessages/ServiceMessageHandler.php index e852f4cf..2cf31892 100644 --- a/api/BusinessLogic/ServiceMessages/ServiceMessageHandler.php +++ b/api/BusinessLogic/ServiceMessages/ServiceMessageHandler.php @@ -118,6 +118,20 @@ class ServiceMessageHandler extends \BaseClass { $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) { + $validationModel->errorKeys[] = 'LANGUAGE_NOT_INSTALLED'; + } if ($serviceMessage->title === null || trim($serviceMessage->title) === '') { $validationModel->errorKeys[] = 'MISSING_TITLE'; diff --git a/api/Controllers/ServiceMessages/ServiceMessagesController.php b/api/Controllers/ServiceMessages/ServiceMessagesController.php index 481c10a6..bf6c26de 100644 --- a/api/Controllers/ServiceMessages/ServiceMessagesController.php +++ b/api/Controllers/ServiceMessages/ServiceMessagesController.php @@ -76,6 +76,7 @@ class ServiceMessagesController extends \BaseClass { $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'); diff --git a/api/DataAccess/ServiceMessages/ServiceMessagesGateway.php b/api/DataAccess/ServiceMessages/ServiceMessagesGateway.php index 6a58b070..371089a9 100644 --- a/api/DataAccess/ServiceMessages/ServiceMessagesGateway.php +++ b/api/DataAccess/ServiceMessages/ServiceMessagesGateway.php @@ -24,14 +24,15 @@ class ServiceMessagesGateway extends CommonDao { $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`) VALUES ( + 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->icon) . "', + '" . hesk_dbEscape($serviceMessage->language) . "', )"); $serviceMessage->id = hesk_dbInsertID(); @@ -74,6 +75,7 @@ class ServiceMessagesGateway extends CommonDao { $serviceMessage->message = $row['message']; $serviceMessage->style = ServiceMessageStyle::getStyleById($row['style']); $serviceMessage->icon = $row['icon']; + $serviceMessage->language = $row['mfh_language']; $serviceMessage->locations = array(); $locationsRs = hesk_dbQuery("SELECT `location` FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "mfh_service_message_to_location` @@ -102,7 +104,8 @@ class ServiceMessagesGateway extends CommonDao { `style` = '" . intval($style) . "', `type` = " . intval($type) . ", `icon` = '" . hesk_dbEscape($serviceMessage->icon) . "', - `order` = " . intval($serviceMessage->order) . " + `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` diff --git a/internal-api/js/service-messages.js b/internal-api/js/service-messages.js index ebe79766..e75719eb 100644 --- a/internal-api/js/service-messages.js +++ b/internal-api/js/service-messages.js @@ -50,6 +50,9 @@ function loadTable() { } 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); @@ -148,7 +151,8 @@ function bindEditModal() { .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('input[name="order"]').val(element.order).end() + .find('select[name="language"]').val(element.language).end(); setIcon(element.icon); $.each(element.locations, function() { @@ -161,6 +165,11 @@ function bindEditModal() { $('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'); }); @@ -177,7 +186,8 @@ function bindCreateModal() { .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('input[name="location[]"]').prop('checked', false) + .find('select[name="language"]').val('ALL'); setIcon(''); if ($('input[name="kb_wysiwyg"]').val() === "1") { @@ -186,6 +196,11 @@ function bindCreateModal() { $('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'); }); } @@ -218,6 +233,7 @@ function bindFormSubmit() { 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 }; From eeb5ada6108f3551f761268065d399e4811eb0aa Mon Sep 17 00:00:00 2001 From: Jozef Date: Mon, 20 Nov 2017 11:00:25 +0000 Subject: [PATCH 171/248] Fix usage of CC and BCC fields in mail header. --- inc/email_functions.inc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inc/email_functions.inc.php b/inc/email_functions.inc.php index f4e7d3aa..13db7b8c 100644 --- a/inc/email_functions.inc.php +++ b/inc/email_functions.inc.php @@ -512,10 +512,10 @@ function hesk_mail($to, $subject, $message, $htmlMessage, $modsForHesk_settings, $headers .= "MIME-Version: 1.0\n"; $headers .= "From: $hesk_settings[from_header]\n"; if (count($cc) > 0) { - $headers .= "Cc: " . implode(',', $cc); + $headers .= "Cc: " . implode(',', $cc) . "\n"; } if (count($bcc) > 0) { - $headers .= "Bcc: " . implode(',', $bcc); + $headers .= "Bcc: " . implode(',', $bcc) . "\n"; } $headers .= "Reply-To: $hesk_settings[from_header]\n"; $headers .= "Return-Path: $hesk_settings[webmaster_mail]\n"; From eac23907e7f88a9e4300b963d83825b1760237c4 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Mon, 20 Nov 2017 07:46:07 -0500 Subject: [PATCH 172/248] Fix edit/delete reply buttons --- admin/admin_ticket.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/admin/admin_ticket.php b/admin/admin_ticket.php index d17ee27b..0210de9d 100644 --- a/admin/admin_ticket.php +++ b/admin/admin_ticket.php @@ -1561,7 +1561,7 @@ function hesk_getAdminButtons($category_id) function hesk_getAdminButtonsInTicket($reply = 0, $white = 1) { - global $hesk_settings, $hesklang, $ticket, $reply, $trackingID, $can_edit, $can_archive, $can_delete, $isManager; + global $hesk_settings, $hesklang, $ticket, $trackingID, $can_edit, $can_archive, $can_delete, $isManager; $options = $reply ? '' : '
'; @@ -1934,7 +1934,7 @@ function mfh_print_reply($reply) { ?>
- +
From 70435cee6265caf9eb069ff6450b9c1383c133f8 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Mon, 20 Nov 2017 12:36:04 -0500 Subject: [PATCH 173/248] Fix storing dates when format is not MySQL-friendly --- inc/common.inc.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/inc/common.inc.php b/inc/common.inc.php index be8f07eb..29f35fc0 100644 --- a/inc/common.inc.php +++ b/inc/common.inc.php @@ -2160,6 +2160,10 @@ function mfh_get_hidden_fields_for_language($keys) { function mfh_insert_audit_trail_record($entity_id, $entity_type, $language_key, $date, $replacement_values = array()) { global $hesk_settings; + $oldTimeFormat = $hesk_settings['timeformat']; + $hesk_settings['timeformat'] = 'Y-m-d H:i:s'; + $date = hesk_date(); + hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "audit_trail` (`entity_id`, `entity_type`, `language_key`, `date`) VALUES (" . intval($entity_id) . ", '" . hesk_dbEscape($entity_type) . "', '" . hesk_dbEscape($language_key) . "', '" . hesk_dbEscape($date) . "')"); @@ -2172,6 +2176,8 @@ function mfh_insert_audit_trail_record($entity_id, $entity_type, $language_key, " . intval($replacement_index) . ", '" . hesk_dbEscape($replacement_value) . "')"); } + $hesk_settings['timeformat'] = $oldTimeFormat; + return $audit_id; } From f8ab89946d97bf4da91faa497c6b6a38b5d8746e Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Mon, 20 Nov 2017 12:37:48 -0500 Subject: [PATCH 174/248] Update version --- install/migrations/core.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/migrations/core.php b/install/migrations/core.php index 6526344d..5157c658 100644 --- a/install/migrations/core.php +++ b/install/migrations/core.php @@ -213,8 +213,9 @@ function getAllMigrations() { 157 => new \v320\AddAuditTrail\CreateAuditTrailToReplacementValuesTable(), 158 => new \v320\AddMigrationSetting(), 159 => new UpdateMigration('3.2.0', '3.1.1', 159), - //3.2.1 - 3.2.2 + //3.2.1 - 3.2.3 160 => new UpdateMigration('3.2.1', '3.2.0', 160), 161 => new UpdateMigration('3.2.2', '3.2.1', 161), + 162 => new UpdateMigration('3.2.3', '3.2.2', 162), ); } \ No newline at end of file From a7742614df5c5e4ccd056d25ab01134c19ff2826 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Mon, 20 Nov 2017 22:16:46 -0500 Subject: [PATCH 175/248] Update pages for HESK 2.7.4 --- admin/admin_reply_ticket.php | 13 ++++++- admin/admin_settings_save.php | 1 + admin/admin_ticket.php | 3 +- admin/assign_owner.php | 3 +- admin/export.php | 2 +- admin/find_tickets.php | 13 ++++--- admin/index.php | 4 +- admin/manage_email_templates.php | 2 + admin/manage_knowledgebase.php | 8 +++- admin/move_category.php | 6 +-- admin/new_ticket.php | 4 +- admin/profile.php | 2 +- admin/reports.php | 2 +- api/BusinessLogic/Emails/BasicEmailSender.php | 4 ++ .../Emails/EmailTemplateParser.php | 16 ++++++++ api/Core/database.inc.php | 2 +- api/Core/database_mysqli.inc.php | 2 +- cron/core/database.inc.php | 2 +- cron/core/database_mysqli.inc.php | 2 +- inc/admin_functions.inc.php | 6 +-- inc/common.inc.php | 38 ++++++++++++++++++- inc/email_functions.inc.php | 36 +++++++++++++++--- inc/pipe_functions.inc.php | 2 +- inc/posting_functions.inc.php | 5 ++- index.php | 4 +- install/install_functions.inc.php | 2 +- internal-api/core/database.inc.php | 2 +- internal-api/core/database_mysqli.inc.php | 2 +- knowledgebase.php | 4 +- .../en/emails/html/new_reply_by_customer.txt | 2 +- .../en/emails/html/new_reply_by_staff.txt | 5 ++- language/en/emails/html/ticket_closed.txt | 2 +- language/en/emails/ticket_closed.txt | 3 +- language/en/text.php | 3 +- reply_ticket.php | 3 +- submit_ticket.php | 2 +- ticket.php | 4 +- 37 files changed, 161 insertions(+), 55 deletions(-) diff --git a/admin/admin_reply_ticket.php b/admin/admin_reply_ticket.php index 29a0d5c8..834ceb6c 100644 --- a/admin/admin_reply_ticket.php +++ b/admin/admin_reply_ticket.php @@ -294,6 +294,15 @@ $sql .= $submit_as_customer ? "`lastreplier`='0', `replierid`='0' " : "`lastrepl if ($time_worked == '00:00:00') { $sql .= ", `lastchange` = NOW() "; } else { + $parts = explode(':', $ticket['time_worked']); + $seconds = ($parts[0] * 3600) + ($parts[1] * 60) + $parts[2]; + + $parts = explode(':', $time_worked); + $seconds += ($parts[0] * 3600) + ($parts[1] * 60) + $parts[2]; + + require(HESK_PATH . 'inc/reporting_functions.inc.php'); + $ticket['time_worked'] = hesk_SecondsToHHMMSS($seconds); + $sql .= ",`time_worked` = ADDTIME(`time_worked`,'" . hesk_dbEscape($time_worked) . "') "; } @@ -363,7 +372,9 @@ $info = array( 'dt' => hesk_date($ticket['dt'], true), 'lastchange' => hesk_date($ticket['lastchange'], true), 'id' => $ticket['id'], - 'language' => $ticket['language'] + 'language' => $ticket['language'], + 'time_worked' => $ticket['time_worked'], + 'last_reply_by' => ($submit_as_customer ? $ticket['name'] : $_SESSION['name']), ); // 2. Add custom fields to the array diff --git a/admin/admin_settings_save.php b/admin/admin_settings_save.php index 7113cd6f..ee1d8181 100644 --- a/admin/admin_settings_save.php +++ b/admin/admin_settings_save.php @@ -62,6 +62,7 @@ $set['noreply_mail'] = hesk_validateEmail(hesk_POST('s_noreply_mail'), $hesklang $set['noreply_name'] = hesk_input(hesk_POST('s_noreply_name')); $set['noreply_name'] = str_replace(array('\\"', '<', '>'), '', $set['noreply_name']); $set['noreply_name'] = trim(preg_replace('/\s{2,}/', ' ', $set['noreply_name'])); +$set['noreply_name'] = preg_replace("/\n|\r|\t|%0A|%0D|%08|%09/", '', $set['noreply_name']); /* --> Language settings */ $set['can_sel_lang'] = empty($_POST['s_can_sel_lang']) ? 0 : 1; diff --git a/admin/admin_ticket.php b/admin/admin_ticket.php index 0210de9d..978f8112 100644 --- a/admin/admin_ticket.php +++ b/admin/admin_ticket.php @@ -423,13 +423,14 @@ if (isset($_POST['notemsg']) && hesk_token_check('POST')) { 'trackid' => $ticket['trackid'], 'status' => $ticket['status'], 'name' => $_SESSION['name'], - 'lastreplier' => $ticket['lastreplier'], 'subject' => $ticket['subject'], 'message' => stripslashes($msg), 'dt' => hesk_date($ticket['dt'], true), 'lastchange' => hesk_date($ticket['lastchange'], true), 'attachments' => $myattachments, 'id' => $ticket['id'], + 'time_worked' => $ticket['time_worked'], + 'last_reply_by' => $ticket['repliername'], ); // 2. Add custom fields to the array diff --git a/admin/assign_owner.php b/admin/assign_owner.php index e875b2a5..e103e8aa 100755 --- a/admin/assign_owner.php +++ b/admin/assign_owner.php @@ -129,13 +129,14 @@ $info = array( 'trackid' => $ticket['trackid'], 'status' => $ticket['status'], 'name' => $ticket['name'], - 'lastreplier' => $ticket['lastreplier'], 'subject' => $ticket['subject'], 'message' => $ticket['message'], 'attachments' => $ticket['attachments'], 'dt' => hesk_date($ticket['dt'], true), 'lastchange' => hesk_date($ticket['lastchange'], true), 'id' => $ticket['id'], + 'time_worked' => $ticket['time_worked'], + 'last_reply_by' => hesk_getReplierName($ticket), ); // 2. Add custom fields to the array diff --git a/admin/export.php b/admin/export.php index 1e4e543e..c0a366e6 100644 --- a/admin/export.php +++ b/admin/export.php @@ -697,7 +697,7 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php'); () diff --git a/admin/find_tickets.php b/admin/find_tickets.php index c9bcd812..350bb27b 100644 --- a/admin/find_tickets.php +++ b/admin/find_tickets.php @@ -145,21 +145,21 @@ LEFT(`message`, 400) AS `message`, $sql .= " ( `trackid` = '" . hesk_dbEscape($q) . "' OR `merged` LIKE '%#" . hesk_dbEscape($q) . "#%' ) "; break; case 'name': - $sql .= "`name` LIKE '%" . hesk_dbEscape($q) . "%' COLLATE '" . hesk_dbCollate() . "' "; + $sql .= "`name` LIKE '%".hesk_dbEscape( hesk_dbLike($q) )."%' COLLATE '" . hesk_dbCollate() . "' "; break; case 'email': $sql .= "`email` LIKE '%" . hesk_dbEscape($q) . "%' "; break; case 'subject': - $sql .= "`subject` LIKE '%" . hesk_dbEscape($q) . "%' COLLATE '" . hesk_dbCollate() . "' "; + $sql .= "`subject` LIKE '%".hesk_dbEscape( hesk_dbLike($q) )."%' COLLATE '" . hesk_dbCollate() . "' "; break; case 'message': - $sql .= " ( `message` LIKE '%" . hesk_dbEscape($q) . "%' COLLATE '" . hesk_dbCollate() . "' + $sql .= " ( `message` LIKE '%".hesk_dbEscape( hesk_dbLike($q) )."%' COLLATE '" . hesk_dbCollate() . "' OR `id` IN ( SELECT DISTINCT `replyto` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "replies` - WHERE `message` LIKE '%" . hesk_dbEscape($q) . "%' COLLATE '" . hesk_dbCollate() . "' ) + WHERE `message` LIKE '%".hesk_dbEscape( hesk_dbLike($q) )."%' COLLATE '" . hesk_dbCollate() . "' ) ) "; break; @@ -170,7 +170,7 @@ LEFT(`message`, 400) AS `message`, $sql .= "`id` IN ( SELECT DISTINCT `ticket` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "notes` - WHERE `message` LIKE '%" . hesk_dbEscape($q) . "%' COLLATE '" . hesk_dbCollate() . "' ) + WHERE `message` LIKE '%".hesk_dbEscape( hesk_dbLike($q) )."%' COLLATE '" . hesk_dbCollate() . "' ) "; break; default: @@ -220,6 +220,9 @@ LEFT(`message`, 400) AS `message`, $sql_count .= $sql; $sql = $sql_final . $sql; + // Strip extra slashes + $q = stripslashes($q); + /* Prepare variables used in search and forms */ require_once(HESK_PATH . 'inc/prepare_ticket_search.inc.php'); ?> diff --git a/admin/index.php b/admin/index.php index ba7c4b00..aec1e374 100644 --- a/admin/index.php +++ b/admin/index.php @@ -158,7 +158,7 @@ function do_login() exit(); } - $pass_enc = hesk_Pass2Hash($_SESSION['pass'] . strtolower($user) . $_SESSION['pass']); + $pass_enc = hesk_Pass2Hash($_SESSION['pass'].hesk_mb_strtolower($user).$_SESSION['pass']); /* Check if default password */ if ($_SESSION['pass'] == '499d74967b28a841c98bb4baaabaad699ff3c079') { @@ -330,7 +330,7 @@ function print_login() - '; - $_SESSION['new']['email'] = hesk_validateEmail(hesk_POST('email'), 'ERR', 0) or $hesk_error_buffer = '
  • ' . $hesklang['enter_valid_email'] . '
  • '; + $_SESSION['new']['email'] = hesk_validateEmail( hesk_POST('email'), 'ERR', 0) or $hesk_error_buffer .= '
  • ' . $hesklang['enter_valid_email'] . '
  • '; $_SESSION['new']['signature'] = hesk_input(hesk_POST('signature')); /* Signature */ diff --git a/admin/reports.php b/admin/reports.php index 64d3ba00..e9f341a2 100644 --- a/admin/reports.php +++ b/admin/reports.php @@ -249,7 +249,7 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php'); ()
    '; } diff --git a/language/en/emails/html/new_reply_by_customer.txt b/language/en/emails/html/new_reply_by_customer.txt index f0eb563d..79cc0a86 100644 --- a/language/en/emails/html/new_reply_by_customer.txt +++ b/language/en/emails/html/new_reply_by_customer.txt @@ -1,6 +1,6 @@

    Hello,

    A customer has just replied to ticket "%%SUBJECT%%".

    -

    You can manage this ticket here: +

    You can read the reply message and manage this ticket here: %%TRACK_URL%%

    Regards,

    %%SITE_TITLE%%
    diff --git a/language/en/emails/html/new_reply_by_staff.txt b/language/en/emails/html/new_reply_by_staff.txt index 1287b2b8..fcf1093a 100644 --- a/language/en/emails/html/new_reply_by_staff.txt +++ b/language/en/emails/html/new_reply_by_staff.txt @@ -1,8 +1,9 @@

    Dear %%NAME%%,

    -

    Our staff has just replied to your ticket "%%SUBJECT%%".

    -

    You can view the ticket here:

    +

    We have just replied to your ticket "%%SUBJECT%%".

    +

    To read the message, submit a reply and view details, please visit:

    %%TRACK_URL%%

    +

    If the above link is not clickable, try copying and pasting it into the address bar of your web browser.

    Sincerely,

    %%SITE_TITLE%%
    diff --git a/language/en/emails/html/ticket_closed.txt b/language/en/emails/html/ticket_closed.txt index f547ba76..10220a1a 100644 --- a/language/en/emails/html/ticket_closed.txt +++ b/language/en/emails/html/ticket_closed.txt @@ -1,8 +1,8 @@

    Dear %%NAME%%,

    Your support ticket "%%SUBJECT%%" has been updated to a closed/resolved status.

    -

    Ticket tracking ID: %%TRACK_ID%%

    You can view the status of your ticket here: %%TRACK_URL%%

    +

    If the above link is not clickable, try copying and pasting it into the address bar of your web browser.

    Sincerely,

    %%SITE_TITLE%%
    %%SITE_URL%% \ No newline at end of file diff --git a/language/en/emails/ticket_closed.txt b/language/en/emails/ticket_closed.txt index 51cfb97c..33a94afd 100644 --- a/language/en/emails/ticket_closed.txt +++ b/language/en/emails/ticket_closed.txt @@ -2,11 +2,10 @@ Dear %%NAME%%, Your support ticket "%%SUBJECT%%" has been updated to a closed/resolved status. -Ticket tracking ID: %%TRACK_ID%% - You can view the status of your ticket here: %%TRACK_URL%% +If the above link is not clickable, try copying and pasting it into the address bar of your web browser. Sincerely, diff --git a/language/en/text.php b/language/en/text.php index d543ccd3..042a2130 100644 --- a/language/en/text.php +++ b/language/en/text.php @@ -1239,7 +1239,7 @@ $hesklang['cat_pri_ch']='Category priority has been set to:'; $hesklang['err_dbversion']='Too old MySQL version:'; // %s will be replaced with MySQL version $hesklang['signature_max']='Signature (max 1000 chars)'; $hesklang['signature_long']='User signature is too long! Please limit the signature to 1000 chars'; -$hesklang['ip_whois']='IP whois'; +$hesklang['ip_whois']='IP WHOIS'; $hesklang['ednote']='Edit note message'; $hesklang['ednote2']='Note message saved'; $hesklang['perm_deny']='Permission denied'; @@ -1284,7 +1284,6 @@ $hesklang['click_unban']='Click here to unban.'; $hesklang['banip_intro']='Visitors from banned IP addresses will not be able to view or submit tickets and login into the help desk.'; $hesklang['ipperm']='Permanent IP bans:'; $hesklang['iptemp']='Login failure bans:'; -$hesklang['savebanip']='Ban this IP'; $hesklang['no_banips']='No IPs are being banned.'; $hesklang['bananip']='IP address to ban'; $hesklang['banex']='Examples:'; diff --git a/reply_ticket.php b/reply_ticket.php index 499f3d1f..0760ac06 100644 --- a/reply_ticket.php +++ b/reply_ticket.php @@ -206,13 +206,14 @@ $info = array( 'trackid' => $ticket['trackid'], 'status' => $ticket['status'], 'name' => $ticket['name'], - 'lastreplier' => $ticket['name'], 'subject' => $ticket['subject'], 'message' => stripslashes($message), 'attachments' => $myattachments, 'dt' => hesk_date($ticket['dt'], true), 'lastchange' => hesk_date($ticket['lastchange'], true), 'id' => $ticket['id'], + 'time_worked' => $ticket['time_worked'], + 'last_reply_by' => $ticket['name'], ); // 2. Add custom fields to the array diff --git a/submit_ticket.php b/submit_ticket.php index 06090ef6..d11f5225 100644 --- a/submit_ticket.php +++ b/submit_ticket.php @@ -73,7 +73,7 @@ if ($hesk_settings['question_use']) { if (strlen($question) == 0) { $hesk_error_buffer['question'] = $hesklang['q_miss']; - } elseif (strtolower($question) != strtolower($hesk_settings['question_ans'])) { + } elseif (hesk_mb_strtolower($question) != hesk_mb_strtolower($hesk_settings['question_ans'])) { $hesk_error_buffer['question'] = $hesklang['q_wrng']; } else { $_SESSION['c_question'] = $question; diff --git a/ticket.php b/ticket.php index 2d7197b4..cb7ef11b 100644 --- a/ticket.php +++ b/ticket.php @@ -722,8 +722,8 @@ function hesk_printCustomerTicketReplies() echo ' '; } From 3203eed8aa85c1f8466f3c420c7656870ca337df Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Tue, 21 Nov 2017 12:29:54 -0500 Subject: [PATCH 176/248] Use NOW() for reply dates --- admin/admin_reply_ticket.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/admin/admin_reply_ticket.php b/admin/admin_reply_ticket.php index 29a0d5c8..216f0394 100644 --- a/admin/admin_reply_ticket.php +++ b/admin/admin_reply_ticket.php @@ -189,9 +189,9 @@ if ($hesk_settings['attachments']['use'] && !empty($attachments)) { // Add reply $html = $modsForHesk_settings['rich_text_for_tickets']; if ($submit_as_customer) { - hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "replies` (`replyto`,`name`,`message`,`dt`,`attachments`,`html`) VALUES ('" . intval($replyto) . "','" . hesk_dbEscape(addslashes($ticket['name'])) . "','" . hesk_dbEscape($message . "

    {$hesklang['creb']} {$_SESSION['name']}") . "','" . hesk_dbEscape(hesk_date()) . "','" . hesk_dbEscape($myattachments) . "', '" . $html . "')"); + hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "replies` (`replyto`,`name`,`message`,`dt`,`attachments`,`html`) VALUES ('" . intval($replyto) . "','" . hesk_dbEscape(addslashes($ticket['name'])) . "','" . hesk_dbEscape($message . "

    {$hesklang['creb']} {$_SESSION['name']}") . "', NOW(),'" . hesk_dbEscape($myattachments) . "', '" . $html . "')"); } else { - hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "replies` (`replyto`,`name`,`message`,`dt`,`attachments`,`staffid`,`html`) VALUES ('" . intval($replyto) . "','" . hesk_dbEscape(addslashes($_SESSION['name'])) . "','" . hesk_dbEscape($message) . "','" . hesk_dbEscape(hesk_date()) . "','" . hesk_dbEscape($myattachments) . "','" . intval($_SESSION['id']) . "', '" . $html . "')"); + hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "replies` (`replyto`,`name`,`message`,`dt`,`attachments`,`staffid`,`html`) VALUES ('" . intval($replyto) . "','" . hesk_dbEscape(addslashes($_SESSION['name'])) . "','" . hesk_dbEscape($message) . "', NOW(),'" . hesk_dbEscape($myattachments) . "','" . intval($_SESSION['id']) . "', '" . $html . "')"); } /* Track ticket status changes for history */ From 8b263f66a32fef6a3dc7fd2d4bed341edbb52f16 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Tue, 21 Nov 2017 12:58:04 -0500 Subject: [PATCH 177/248] Fix some email sending, updat edit_post maxlengths --- admin/edit_post.php | 4 ++-- api/BusinessLogic/Emails/EmailTemplateParser.php | 7 +------ .../Tickets/ResendTicketEmailToCustomerController.php | 4 ++++ 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/admin/edit_post.php b/admin/edit_post.php index a0bc7c55..8e14c90a 100644 --- a/admin/edit_post.php +++ b/admin/edit_post.php @@ -368,7 +368,7 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php');
    -
    @@ -380,7 +380,7 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php');
    - lastReplier)) { - if (!empty($ticket->lastReplier)) { - $lastReplyBy = $hesklang['staff']; - } else { - $lastReplyUser = $this->userGateway->getUserById($ticket->lastReplier, $heskSettings); - $lastReplyBy = $lastReplyUser !== null ? $lastReplyUser->name : $hesklang['unas']; - } + $lastReplyBy = $ticket->lastReplier; } else { $lastReplyBy = $ticket->name; } diff --git a/api/Controllers/Tickets/ResendTicketEmailToCustomerController.php b/api/Controllers/Tickets/ResendTicketEmailToCustomerController.php index ddab20cc..42ac4c08 100644 --- a/api/Controllers/Tickets/ResendTicketEmailToCustomerController.php +++ b/api/Controllers/Tickets/ResendTicketEmailToCustomerController.php @@ -41,6 +41,10 @@ class ResendTicketEmailToCustomerController extends InternalApiController { $reply = null; $emailTemplate = EmailTemplateRetriever::NEW_TICKET; + + // Use 0 for new tickets + $ticket->lastReplier = 0; + if (isset($_GET['replyId'])) { $replyId = $_GET['replyId']; $emailTemplate = EmailTemplateRetriever::NEW_REPLY_BY_STAFF; From 2390af6393ac55f2316640c4897fc96d977e96bd Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Sat, 25 Nov 2017 21:14:15 -0500 Subject: [PATCH 178/248] Update HESK verison to 2.7.5 --- install/install_functions.inc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/install_functions.inc.php b/install/install_functions.inc.php index e94f1e85..8dcae5c0 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.4'); -define('MODS_FOR_HESK_NEW_VERSION','3.2.2'); +define('HESK_NEW_VERSION','2.7.5'); +define('MODS_FOR_HESK_NEW_VERSION','3.2.4'); define('REQUIRE_PHP_VERSION','5.3.0'); define('REQUIRE_MYSQL_VERSION','5.0.7'); From d092a49babb8ca76935b5fd1c5bc19cf6c082bc1 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Sat, 25 Nov 2017 21:40:27 -0500 Subject: [PATCH 179/248] Fix sorting of ticket events --- admin/admin_ticket.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/admin/admin_ticket.php b/admin/admin_ticket.php index 0210de9d..c98979a2 100644 --- a/admin/admin_ticket.php +++ b/admin/admin_ticket.php @@ -1828,7 +1828,7 @@ function hesk_printTicketReplies() } // Re-sort them so they're in order by date - usort($combined_records, function ($a, $b) { + usort($combined_records, function ($a, $b) use (&$hesk_settings) { $a_date = null; $b_date = null; if ($a['SORT_TYPE'] == 'REPLY') { @@ -1844,12 +1844,14 @@ function hesk_printTicketReplies() } if ($a_date === $b_date && $a['SORT_TYPE'] != $b['SORT_TYPE']) { - if ($a['SORT_TYPE'] != $b['SORT_TYPE']) { - return $a['SORT_TYPE'] == 'REPLY' ? -1 : 1; + if ($hesk_settings['new_top']) { + return $a['SORT_TYPE'] == 'REPLY' ? 1 : -1; } + + return $a['SORT_TYPE'] == 'REPLY' ? -1 : 1; } - return $a_date - $b_date; + return $hesk_settings['new_top'] ? $b_date - $a_date : $a_date - $b_date; }); From 03e7f626843f277ed0bb039b0685a2bf28836c5b Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Sat, 25 Nov 2017 21:42:23 -0500 Subject: [PATCH 180/248] Change verbiage --- language/en/text.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/language/en/text.php b/language/en/text.php index d543ccd3..59e4b7b7 100644 --- a/language/en/text.php +++ b/language/en/text.php @@ -2192,7 +2192,7 @@ $hesklang['changing_permissions_will_reset_permission_group'] = 'Changing a user // --> Ticket audit trail $hesklang['audit_moved_category']='%s moved ticket to category %s'; // %s = new category, user making change, thist1 $hesklang['audit_assigned']='%s assigned ticket to %s'; // %s = assigned user, user making change -$hesklang['audit_assigned_self'] = '%s assigned ticket to themself'; +$hesklang['audit_assigned_self'] = '%s self-assigned ticket'; $hesklang['audit_unassigned'] = '%s unassigned ticket'; $hesklang['audit_closed']='%s closed ticket'; // thist3 $hesklang['audit_automatically_closed'] ='Ticket automatically closed'; From dd99ebdb0e10e43ee0d73bae7723e1008d32f5b3 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Sat, 25 Nov 2017 21:46:19 -0500 Subject: [PATCH 181/248] Fix input-group-addon CSS issue --- admin/manage_categories.php | 2 +- css/mods-for-hesk-new.css | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/admin/manage_categories.php b/admin/manage_categories.php index 5b6cf082..0b123369 100644 --- a/admin/manage_categories.php +++ b/admin/manage_categories.php @@ -374,7 +374,7 @@ echo '';
    -
    - +
    -
    Customer Pages
    +
    - Select All +
    - Deselect All +
    Homepage + name="location[]" value="CUSTOMER_HOME">
    Knowledgebase Home + name="location[]" value="CUSTOMER_KB_HOME">
    View Knowledgebase Article + name="location[]" value="CUSTOMER_VIEW_KB_ARTICLE">
    Submit Ticket + name="location[]" value="CUSTOMER_SUBMIT_TICKET">
    View Ticket + name="location[]" value="CUSTOMER_VIEW_TICKET">
    -
    Staff Pages
    +
    - Select All +
    - Deselect All +
    Login Page + name="location[]" value="STAFF_LOGIN">
    Homepage + name="location[]" value="STAFF_HOME">
    Knowledgebase Home + name="location[]" value="STAFF_KB_HOME">
    View Knowledgebase Article + name="location[]" value="STAFF_VIEW_KB_ARTICLE">
    Submit Ticket + name="location[]" value="STAFF_SUBMIT_TICKET">
    View Ticket + name="location[]" value="STAFF_VIEW_TICKET">
    diff --git a/language/en/text.php b/language/en/text.php index a360ef74..7d62e3f3 100644 --- a/language/en/text.php +++ b/language/en/text.php @@ -2219,6 +2219,15 @@ $hesklang['error_retrieving_sm'] = 'An error occurred retrieving service message $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!'); From 902f3edcf6c2ab3e5febffd41299cf80a2dc3045 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Mon, 27 Nov 2017 12:51:34 -0500 Subject: [PATCH 184/248] Only show locations that are active --- admin/service_messages.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/admin/service_messages.php b/admin/service_messages.php index cb08371a..052e0eb4 100644 --- a/admin/service_messages.php +++ b/admin/service_messages.php @@ -50,11 +50,6 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php'); title="" data-placement="bottom"> -
    - -
    @@ -274,6 +269,7 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php');
    + 0): ?>
    @@ -282,6 +278,7 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php');
    +
    @@ -290,6 +287,7 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php');
    +
    @@ -309,6 +307,7 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php');
    + 0): ?>
    @@ -317,6 +316,7 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php');
    +
    @@ -325,6 +325,7 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php');
    + From 2faac6be1a44e84425ca65e291b153ce9579c707 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Mon, 27 Nov 2017 13:00:35 -0500 Subject: [PATCH 185/248] Fixed type not being stored, working on display locations --- api/DataAccess/ServiceMessages/ServiceMessagesGateway.php | 4 ++-- index.php | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/api/DataAccess/ServiceMessages/ServiceMessagesGateway.php b/api/DataAccess/ServiceMessages/ServiceMessagesGateway.php index 371089a9..d9d8985a 100644 --- a/api/DataAccess/ServiceMessages/ServiceMessagesGateway.php +++ b/api/DataAccess/ServiceMessages/ServiceMessagesGateway.php @@ -32,7 +32,7 @@ class ServiceMessagesGateway extends CommonDao { '{$type}', '{$myOrder}', '" . hesk_dbEscape($serviceMessage->icon) . "', - '" . hesk_dbEscape($serviceMessage->language) . "', + '" . hesk_dbEscape($serviceMessage->language) . "' )"); $serviceMessage->id = hesk_dbInsertID(); @@ -102,7 +102,7 @@ class ServiceMessagesGateway extends CommonDao { SET `title` = '" . hesk_dbEscape($serviceMessage->title) . "', `message` = '" . hesk_dbEscape($serviceMessage->message) . "', `style` = '" . intval($style) . "', - `type` = " . intval($type) . ", + `type` = '{$type}', `icon` = '" . hesk_dbEscape($serviceMessage->icon) . "', `order` = " . intval($serviceMessage->order) . ", `mfh_language` = '" . hesk_dbEscape($serviceMessage->language) . "' diff --git a/index.php b/index.php index e9082980..ad824f67 100644 --- a/index.php +++ b/index.php @@ -1315,7 +1315,12 @@ function print_start() 0) { ?> From 3e8085110ff464b269fe96d9ab0e4efd4c15c98b Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Tue, 28 Nov 2017 13:03:06 -0500 Subject: [PATCH 186/248] Service messages now appear in their proper locations --- admin/admin_main.php | 10 +++++++++- admin/admin_ticket.php | 5 +++++ admin/index.php | 6 ++++++ admin/knowledgebase_private.php | 19 ++++++++++++++++++- admin/manage_knowledgebase.php | 6 ++++++ admin/new_ticket.php | 5 +++++ inc/common.inc.php | 21 +++++++++++++++++++++ index.php | 21 ++++++++++----------- knowledgebase.php | 15 +++++++++++---- ticket.php | 13 +++++++++++++ 10 files changed, 104 insertions(+), 17 deletions(-) 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/inc/common.inc.php b/inc/common.inc.php index b510e92b..b6ce26f9 100644 --- a/inc/common.inc.php +++ b/inc/common.inc.php @@ -192,6 +192,27 @@ function hesk_service_message($sm)
    -

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

    +
    @@ -1315,20 +1321,13 @@ function print_start() 0) - { + $service_messages = mfh_get_service_messages('CUSTOMER_HOME'); + if (count($service_messages) > 0) { ?>
    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/ticket.php b/ticket.php index cb7ef11b..316dac96 100644 --- a/ticket.php +++ b/ticket.php @@ -215,6 +215,13 @@ if (!$show['show']) {
    From f70c3635a9d30b3440414c59b9c1c9854b443b04 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Wed, 29 Nov 2017 21:58:30 -0500 Subject: [PATCH 187/248] Working on moving the calendar API --- api/BusinessLogic/Calendar/AbstractEvent.php | 20 +++ api/BusinessLogic/Calendar/CalendarEvent.php | 21 +++ api/BusinessLogic/Calendar/TicketEvent.php | 18 +++ api/DataAccess/Calendar/CalendarGateway.php | 129 +++++++++++++++++++ 4 files changed, 188 insertions(+) create mode 100644 api/BusinessLogic/Calendar/AbstractEvent.php create mode 100644 api/BusinessLogic/Calendar/CalendarEvent.php create mode 100644 api/BusinessLogic/Calendar/TicketEvent.php create mode 100644 api/DataAccess/Calendar/CalendarGateway.php diff --git a/api/BusinessLogic/Calendar/AbstractEvent.php b/api/BusinessLogic/Calendar/AbstractEvent.php new file mode 100644 index 00000000..5413ef48 --- /dev/null +++ b/api/BusinessLogic/Calendar/AbstractEvent.php @@ -0,0 +1,20 @@ +init(); + + $startTimeSql = "CONVERT_TZ(FROM_UNIXTIME(" . hesk_dbEscape($startTime) . " / 1000), @@session.time_zone, '+00:00')"; + $endTimeSql = "CONVERT_TZ(FROM_UNIXTIME(" . hesk_dbEscape($endTime) . " / 1000), @@session.time_zone, '+00:00')"; + + $sql = "SELECT `events`.*, `categories`.`name` AS `category_name`, `categories`.`background_color` AS `background_color`, + `categories`.`foreground_color` AS `foreground_color`, `categories`.`display_border_outline` AS `display_border` "; + + if ($staff) { + $sql .= ",`reminders`.`amount` AS `reminder_value`, `reminders`.`unit` AS `reminder_unit` "; + } + + $sql .= "FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "calendar_event` AS `events` + INNER JOIN `" . hesk_dbEscape($heskSettings['db_pfix']) . "categories` AS `categories` + ON `events`.`category` = `categories`.`id` "; + + if ($staff) { + $sql .= "LEFT JOIN `" . hesk_dbEscape($heskSettings['db_pfix']) . "calendar_event_reminder` AS `reminders` ON + `reminders`.`user_id` = " . intval($_SESSION['id']) . " AND `reminders`.`event_id` = `events`.`id`"; + } + $sql .= "WHERE NOT (`end` < {$startTimeSql} OR `start` > {$endTimeSql}) 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 + // TODO This should be business logic + if ($staff && !$_SESSION['isadmin'] && !in_array($row['category'], $_SESSION['categories'])) { + continue; + } + + $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) { + $oldTimeSetting = $heskSettings['timeformat']; + $heskSettings['timeformat'] = 'Y-m-d'; + $currentDate = hesk_date(); + $heskSettings['timeformat'] = $oldTimeSetting; + + $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` < '{$currentDate}' THEN 1 ELSE 0 END AS `overdue`, `owner`.`name` AS `owner_name`, `tickets`.`owner` AS `owner_id`, + `tickets`.`priority` AS `priority` + FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "tickets` AS `tickets` + INNER JOIN `" . hesk_dbEscape($heskSettings['db_pfix']) . "categories` AS `categories` + ON `categories`.`id` = `tickets`.`category` + AND `categories`.`usage` <> 2 + LEFT JOIN `" . hesk_dbEscape($heskSettings['db_pfix']) . "users` AS `owner` + ON `tickets`.`owner` = `owner`.`id` + WHERE `due_date` >= CONVERT_TZ(FROM_UNIXTIME(" . hesk_dbEscape($startTime) + . " / 1000), @@session.time_zone, '+00:00') + AND `due_date` <= CONVERT_TZ(FROM_UNIXTIME(" . hesk_dbEscape($endTime) . " / 1000), @@session.time_zone, '+00:00') + AND `status` IN (SELECT `id` FROM `" . hesk_dbEscape($heskSettings['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'] = $heskSettings['hesk_url'] . '/' . $heskSettings['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']; + $event['priority'] = $row['priority']; + + $events[] = $event; + } + } + + $this->close(); + + return $events; + } +} \ No newline at end of file From 0a2e37320aef3a7cd9c4f8670772ba051c249a00 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Fri, 1 Dec 2017 12:56:23 -0500 Subject: [PATCH 188/248] Working on adding controller security --- api/BusinessLogic/Security/UserPrivilege.php | 1 + .../ServiceMessagesController.php | 17 ++++++++++++++++- api/Core/json_error.php | 6 +++++- api/index.php | 2 +- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/api/BusinessLogic/Security/UserPrivilege.php b/api/BusinessLogic/Security/UserPrivilege.php index 353e8e43..8cd1eee4 100644 --- a/api/BusinessLogic/Security/UserPrivilege.php +++ b/api/BusinessLogic/Security/UserPrivilege.php @@ -15,4 +15,5 @@ class UserPrivilege extends \BaseClass { const CAN_EDIT_TICKETS = 'can_edit_tickets'; const CAN_DELETE_TICKETS = 'can_del_tickets'; const CAN_MANAGE_CATEGORIES = 'can_man_cat'; + const CAN_MANAGE_SERVICE_MESSAGES = 'can_service_msg'; } \ No newline at end of file diff --git a/api/Controllers/ServiceMessages/ServiceMessagesController.php b/api/Controllers/ServiceMessages/ServiceMessagesController.php index bf6c26de..b147f123 100644 --- a/api/Controllers/ServiceMessages/ServiceMessagesController.php +++ b/api/Controllers/ServiceMessages/ServiceMessagesController.php @@ -2,15 +2,30 @@ namespace Controllers\ServiceMessages; +use BusinessLogic\Exceptions\ApiFriendlyException; use BusinessLogic\Helpers; use BusinessLogic\Security\UserContext; +use BusinessLogic\Security\UserPrivilege; use BusinessLogic\ServiceMessages\ServiceMessage; use BusinessLogic\ServiceMessages\ServiceMessageHandler; +use Controllers\ControllerWithSecurity; use Controllers\JsonRetriever; class ServiceMessagesController extends \BaseClass { + /** + * @param $userContext UserContext + * @throws ApiFriendlyException + */ + function checkSecurity($userContext) { + if (!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() { - global $applicationContext, $hesk_settings; + global $applicationContext, $hesk_settings, $userContext; + + $this->checkSecurity($userContext); /* @var $handler ServiceMessageHandler */ $handler = $applicationContext->get(ServiceMessageHandler::clazz()); 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/index.php b/api/index.php index d7fa3656..8502e108 100644 --- a/api/index.php +++ b/api/index.php @@ -105,7 +105,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; From 39e61b85c46643aa1ae508e568513391701de1ee Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Mon, 4 Dec 2017 12:56:09 -0500 Subject: [PATCH 189/248] Fixed some issues with the service messages endpoints: --- .../MissingAuthenticationTokenException.php | 4 +- api/BusinessLogic/Security/UserContext.php | 4 ++ .../GetServiceMessagesFilter.php | 12 ++++++ .../ServiceMessages/ServiceMessageHandler.php | 8 ++-- .../ServiceMessagesController.php | 38 +++++++++++++++---- .../ServiceMessagesGateway.php | 31 +++++++++++++-- api/index.php | 13 +++++-- 7 files changed, 90 insertions(+), 20 deletions(-) create mode 100644 api/BusinessLogic/ServiceMessages/GetServiceMessagesFilter.php 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 3055b4ff..6ce0fba0 100644 --- a/api/BusinessLogic/Security/UserContext.php +++ b/api/BusinessLogic/Security/UserContext.php @@ -57,6 +57,10 @@ class UserContext extends \BaseClass { /* @var $active bool */ public $active; + function isAnonymousUser() { + return $this->id === -1; + } + static function buildAnonymousUser() { $userContext = new UserContext(); $userContext->id = -1; 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->createServiceMessage($serviceMessage, $heskSettings); } - function getServiceMessages($heskSettings) { - return $this->serviceMessageGateway->getServiceMessages($heskSettings); + function getServiceMessages($heskSettings, $searchFilter) { + return $this->serviceMessageGateway->getServiceMessages($heskSettings, $searchFilter); } function editServiceMessage($serviceMessage, $heskSettings) { @@ -78,7 +78,7 @@ class ServiceMessageHandler extends \BaseClass { } function sortServiceMessage($id, $direction, $heskSettings) { - $serviceMessages = $this->serviceMessageGateway->getServiceMessages($heskSettings); + $serviceMessages = $this->serviceMessageGateway->getServiceMessages($heskSettings, new GetServiceMessagesFilter()); $serviceMessage = null; foreach ($serviceMessages as $innerServiceMessage) { if (intval($innerServiceMessage->id) === intval($id)) { @@ -129,7 +129,7 @@ class ServiceMessageHandler extends \BaseClass { break; } } - if (!$languageFound) { + if (!$languageFound && !in_array('MISSING_LANGUAGE', $validationModel->errorKeys)) { $validationModel->errorKeys[] = 'LANGUAGE_NOT_INSTALLED'; } diff --git a/api/Controllers/ServiceMessages/ServiceMessagesController.php b/api/Controllers/ServiceMessages/ServiceMessagesController.php index b147f123..48da1a7d 100644 --- a/api/Controllers/ServiceMessages/ServiceMessagesController.php +++ b/api/Controllers/ServiceMessages/ServiceMessagesController.php @@ -6,6 +6,7 @@ use BusinessLogic\Exceptions\ApiFriendlyException; use BusinessLogic\Helpers; use BusinessLogic\Security\UserContext; use BusinessLogic\Security\UserPrivilege; +use BusinessLogic\ServiceMessages\GetServiceMessagesFilter; use BusinessLogic\ServiceMessages\ServiceMessage; use BusinessLogic\ServiceMessages\ServiceMessageHandler; use Controllers\ControllerWithSecurity; @@ -17,25 +18,41 @@ class ServiceMessagesController extends \BaseClass { * @throws ApiFriendlyException */ function checkSecurity($userContext) { - if (!in_array(UserPrivilege::CAN_MANAGE_SERVICE_MESSAGES, $userContext->permissions)) { + 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); + } + } + + 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; - $this->checkSecurity($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)); + 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()); @@ -46,7 +63,9 @@ class ServiceMessagesController extends \BaseClass { } function put($id) { - global $applicationContext, $hesk_settings; + global $applicationContext, $hesk_settings, $userContext; + + $this->checkSecurity($userContext); /* @var $handler ServiceMessageHandler */ $handler = $applicationContext->get(ServiceMessageHandler::clazz()); @@ -60,7 +79,9 @@ class ServiceMessagesController extends \BaseClass { } function delete($id) { - global $applicationContext, $hesk_settings; + global $applicationContext, $hesk_settings, $userContext; + + $this->checkSecurity($userContext); /* @var $handler ServiceMessageHandler */ $handler = $applicationContext->get(ServiceMessageHandler::clazz()); @@ -79,7 +100,7 @@ class ServiceMessagesController extends \BaseClass { $serviceMessage = new ServiceMessage(); if (!$creating) { - $serviceMessage->order = $data['order']; + $serviceMessage->order = Helpers::safeArrayGet($data, 'order'); } if ($creating) { @@ -105,7 +126,10 @@ class ServiceMessagesController extends \BaseClass { } static function sort($id, $direction) { - global $applicationContext, $hesk_settings; + /* @var $userContext UserContext */ + global $applicationContext, $hesk_settings, $userContext; + + self::staticCheckSecurity($userContext); /* @var $handler ServiceMessageHandler */ $handler = $applicationContext->get(ServiceMessageHandler::clazz()); diff --git a/api/DataAccess/ServiceMessages/ServiceMessagesGateway.php b/api/DataAccess/ServiceMessages/ServiceMessagesGateway.php index d9d8985a..e15a593e 100644 --- a/api/DataAccess/ServiceMessages/ServiceMessagesGateway.php +++ b/api/DataAccess/ServiceMessages/ServiceMessagesGateway.php @@ -3,7 +3,9 @@ namespace DataAccess\ServiceMessages; +use BusinessLogic\ServiceMessages\GetServiceMessagesFilter; use BusinessLogic\ServiceMessages\ServiceMessage; +use BusinessLogic\ServiceMessages\ServiceMessageLocation; use BusinessLogic\ServiceMessages\ServiceMessageStyle; use DataAccess\CommonDao; @@ -56,14 +58,29 @@ class ServiceMessagesGateway extends CommonDao { /** * @param $heskSettings + * @param $searchFilter GetServiceMessagesFilter * @return ServiceMessage[] */ - function getServiceMessages($heskSettings) { + function getServiceMessages($heskSettings, $searchFilter) { $this->init(); $serviceMessages = array(); - $rs = hesk_dbQuery("SELECT * FROM `". hesk_dbEscape($heskSettings['db_pfix']) . "service_messages` ORDER BY `order`"); + $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']; @@ -78,8 +95,14 @@ class ServiceMessagesGateway extends CommonDao { $serviceMessage->language = $row['mfh_language']; $serviceMessage->locations = array(); - $locationsRs = hesk_dbQuery("SELECT `location` FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "mfh_service_message_to_location` - WHERE `service_message_id` = " . intval($serviceMessage->id)); + $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']; } diff --git a/api/index.php b/api/index.php index 8502e108..3a67c362 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() { @@ -205,7 +212,7 @@ Link::all(array( // Service Messages '/v1/service-messages' => action(\Controllers\ServiceMessages\ServiceMessagesController::clazz(), array(RequestMethod::GET, RequestMethod::POST), - SecurityHandler::INTERNAL_OR_AUTH_TOKEN), + SecurityHandler::OPEN), '/v1/service-messages/{i}' => action(\Controllers\ServiceMessages\ServiceMessagesController::clazz(), array(RequestMethod::PUT, RequestMethod::DELETE), SecurityHandler::INTERNAL_OR_AUTH_TOKEN), From 8ee6e1a55f91589c81a9eec4076cc425d7d52efd Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Wed, 6 Dec 2017 12:18:17 -0500 Subject: [PATCH 190/248] Can't concatenate an array to a string --- api/BusinessLogic/Emails/BasicEmailSender.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/BusinessLogic/Emails/BasicEmailSender.php b/api/BusinessLogic/Emails/BasicEmailSender.php index 8ab1c37d..d29f34af 100644 --- a/api/BusinessLogic/Emails/BasicEmailSender.php +++ b/api/BusinessLogic/Emails/BasicEmailSender.php @@ -10,7 +10,8 @@ use PHPMailer; class BasicEmailSender extends \BaseClass implements EmailSender { function sendEmail($emailBuilder, $heskSettings, $modsForHeskSettings, $sendAsHtml) { - if (preg_match("/\n|\r|\t|%0A|%0D|%08|%09/", $emailBuilder->to . $emailBuilder->subject)) { + $toEmails = implode(',', $emailBuilder->to); + if (preg_match("/\n|\r|\t|%0A|%0D|%08|%09/", $toEmails . $emailBuilder->subject)) { return false; } From 878b2aa09662a468bad1c9441f9fa17a4400c4e9 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Wed, 6 Dec 2017 12:45:44 -0500 Subject: [PATCH 191/248] Fix headers --- api/autoload.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/api/autoload.php b/api/autoload.php index 0f14ff20..33ac8300 100644 --- a/api/autoload.php +++ b/api/autoload.php @@ -23,4 +23,17 @@ require_once(__DIR__ . '/../inc/custom_fields.inc.php'); // Load the ApplicationContext $builder = new \DI\ContainerBuilder(); -$applicationContext = $builder->build(); \ No newline at end of file +$applicationContext = $builder->build(); + +// Fix for getallheaders() on PHP-FPM and nginx +if (!function_exists('getallheaders')) { + function getallheaders() { + $headers = []; + foreach ($_SERVER as $name => $value) { + if (substr($name, 0, 5) == 'HTTP_') { + $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value; + } + } + return $headers; + } +} \ No newline at end of file From 95342815e2b3a1c8481303eea64c7767260e8c33 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Wed, 6 Dec 2017 12:52:02 -0500 Subject: [PATCH 192/248] Update installer --- install/index.php | 2 +- install/migrations/core.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/install/index.php b/install/index.php index 6ee2fc35..e6199ac3 100644 --- a/install/index.php +++ b/install/index.php @@ -100,7 +100,7 @@ if (HESK_NEW_VERSION != $hesk_settings['hesk_version']) {
    -

    You need to be running HESK 2.7.3 to install Mods for HESK. You currently have HESK .

    +

    You need to be running HESK to install Mods for HESK. You currently have HESK .

    @@ -453,10 +453,10 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php');
    diff --git a/api/BusinessLogic/Calendar/CalendarHandler.php b/api/BusinessLogic/Calendar/CalendarHandler.php index 7f327997..5347eaf1 100644 --- a/api/BusinessLogic/Calendar/CalendarHandler.php +++ b/api/BusinessLogic/Calendar/CalendarHandler.php @@ -3,7 +3,10 @@ namespace BusinessLogic\Calendar; +use BusinessLogic\Exceptions\ApiFriendlyException; +use BusinessLogic\Security\UserContext; use DataAccess\Calendar\CalendarGateway; +use PHPUnit\Runner\Exception; class CalendarHandler extends \BaseClass { private $calendarGateway; @@ -12,11 +15,35 @@ class CalendarHandler extends \BaseClass { $this->calendarGateway = $calendarGateway; } - public function getEventsForStaff($startTime, $endTime, $searchEventsFilter, $heskSettings) { - return $this->calendarGateway->getEventsForStaff($startTime, $endTime, $searchEventsFilter, $heskSettings); + public function getEventsForStaff($searchEventsFilter, $heskSettings) { + return $this->calendarGateway->getEventsForStaff($searchEventsFilter, $heskSettings); } + /** + * @param $calendarEvent CalendarEvent + * @param $userContext UserContext + * @param $heskSettings array + * @return CalendarEvent + * @throws \Exception If more than one event is returned for the given ID + */ public function updateEvent($calendarEvent, $userContext, $heskSettings) { $this->calendarGateway->updateEvent($calendarEvent, $userContext, $heskSettings); + + $eventFilter = new SearchEventsFilter(); + $eventFilter->eventId = $calendarEvent->id; + $eventFilter->reminderUserId = $userContext->id; + + $events = $this->calendarGateway->getEventsForStaff($eventFilter, $heskSettings); + + if (count($events) !== 1) { + throw new \Exception("Expected exactly 1 event, found: " . count($events)); + } + + return $events[0]; + } + + + public function createEvent($calendarEvent, $userContext, $heskSettings) { + return $this->calendarGateway->createEvent($calendarEvent, $userContext, $heskSettings); } } \ No newline at end of file diff --git a/api/BusinessLogic/Calendar/SearchEventsFilter.php b/api/BusinessLogic/Calendar/SearchEventsFilter.php index 1a509f9f..72e31b3d 100644 --- a/api/BusinessLogic/Calendar/SearchEventsFilter.php +++ b/api/BusinessLogic/Calendar/SearchEventsFilter.php @@ -4,6 +4,15 @@ namespace BusinessLogic\Calendar; class SearchEventsFilter { + /* @var $startTime int|null */ + public $startTime; + + /* @var $endTime int|null */ + public $endTime; + + /* @var $id int|null */ + public $eventId; + /* @var $categories int[]|null */ public $categories; diff --git a/api/BusinessLogic/Tickets/TicketEditor.php b/api/BusinessLogic/Tickets/TicketEditor.php index e15b33d9..5b6e2f7d 100644 --- a/api/BusinessLogic/Tickets/TicketEditor.php +++ b/api/BusinessLogic/Tickets/TicketEditor.php @@ -135,4 +135,25 @@ class TicketEditor extends \BaseClass { throw new ValidationException($validationModel); } } + + /** + * @param $id int + * @param $dueDate string + * @param $userContext UserContext + * @param $heskSettings array + * @throws ApiFriendlyException If ticket does not exist or if the user cannot edit the ticket + */ + function updateDueDate($id, $dueDate, $userContext, $heskSettings) { + $ticket = $this->ticketGateway->getTicketById($id, $heskSettings); + + if ($ticket === null) { + throw new ApiFriendlyException("Please enter a valid ticket ID.", "Ticket Not Found!", 400); + } + + if (!$this->userToTicketChecker->isTicketAccessibleToUser($userContext, $ticket, $heskSettings, array(UserPrivilege::CAN_EDIT_TICKETS))) { + throw new ApiFriendlyException("User " . $userContext->id . " does not have permission to edit ticket " . $id, "Access Denied", 403); + } + + // TODO Do it + } } \ No newline at end of file diff --git a/api/Controllers/Calendar/CalendarController.php b/api/Controllers/Calendar/CalendarController.php index 1a180fc1..2e30c0b1 100644 --- a/api/Controllers/Calendar/CalendarController.php +++ b/api/Controllers/Calendar/CalendarController.php @@ -32,18 +32,20 @@ class CalendarController extends \BaseClass { $calendarHandler = $applicationContext->get(CalendarHandler::clazz()); $searchEventsFilter = new SearchEventsFilter(); + $searchEventsFilter->startTime = $startTime; + $searchEventsFilter->endTime = $endTime; $searchEventsFilter->reminderUserId = $userContext->id; $searchEventsFilter->includeTicketsAssignedToOthers = in_array(UserPrivilege::CAN_VIEW_ASSIGNED_TO_OTHER, $userContext->permissions); $searchEventsFilter->includeUnassignedTickets = in_array(UserPrivilege::CAN_VIEW_UNASSIGNED, $userContext->permissions); $searchEventsFilter->includeTickets = true; $searchEventsFilter->categories = $userContext->admin ? null : $userContext->categories; - $events = $calendarHandler->getEventsForStaff($startTime, $endTime, $searchEventsFilter, $hesk_settings); + $events = $calendarHandler->getEventsForStaff($searchEventsFilter, $hesk_settings); return output($events); } - function put($id) { + function post() { /* @var $userContext UserContext */ global $applicationContext, $hesk_settings, $userContext; @@ -53,25 +55,36 @@ class CalendarController extends \BaseClass { /* @var $calendarHandler CalendarHandler */ $calendarHandler = $applicationContext->get(CalendarHandler::clazz()); + } + + function put($id) { + /* @var $userContext UserContext */ + global $applicationContext, $hesk_settings, $userContext; + + $json = JsonRetriever::getJsonData(); + + $event = $this->transformJson($json, $id); + + /* @var $calendarHandler CalendarHandler */ + $calendarHandler = $applicationContext->get(CalendarHandler::clazz()); return output($calendarHandler->updateEvent($event, $userContext, $hesk_settings)); } - private function transformJson($json, $creating = false) { + private function transformJson($json, $id = null) { $event = new CalendarEvent(); - if ($creating) { - $event->id = Helpers::safeArrayGet($json, 'id'); - } - - $event->startTime = date('Y-m-d H:i:s', Helpers::safeArrayGet($json, 'startTime')); - $event->endTime = date('Y-m-d H:i:s', Helpers::safeArrayGet($json, 'endTime')); - $event->allDay = Helpers::safeArrayGet($json, 'allDay') === 'true'; + $event->id = $id; + $event->startTime = date('Y-m-d H:i:s', strtotime(Helpers::safeArrayGet($json, 'startTime'))); + $event->endTime = date('Y-m-d H:i:s', strtotime(Helpers::safeArrayGet($json, 'endTime'))); + $event->allDay = Helpers::safeArrayGet($json, 'allDay'); $event->title = Helpers::safeArrayGet($json, 'title'); $event->location = Helpers::safeArrayGet($json, 'location'); $event->comments = Helpers::safeArrayGet($json, 'comments'); $event->categoryId = Helpers::safeArrayGet($json, 'categoryId'); $event->reminderValue = Helpers::safeArrayGet($json, 'reminderValue'); $event->reminderUnits = ReminderUnit::getByName(Helpers::safeArrayGet($json, 'reminderUnits')); + + return $event; } } \ No newline at end of file diff --git a/api/Controllers/Tickets/StaffTicketController.php b/api/Controllers/Tickets/StaffTicketController.php index 023f5352..ea4761a1 100644 --- a/api/Controllers/Tickets/StaffTicketController.php +++ b/api/Controllers/Tickets/StaffTicketController.php @@ -4,6 +4,7 @@ namespace Controllers\Tickets; use BusinessLogic\Helpers; +use BusinessLogic\Security\UserContext; use BusinessLogic\Tickets\EditTicketModel; use BusinessLogic\Tickets\TicketDeleter; use BusinessLogic\Tickets\TicketEditor; @@ -45,6 +46,16 @@ class StaffTicketController extends \BaseClass { return; } + static function updateDueDate($id) { + /* @var $userContext UserContext */ + global $applicationContext, $userContext, $hesk_settings; + + /* @var $ticketEditor TicketEditor */ + $ticketEditor = $applicationContext->get(TicketEditor::clazz()); + + + } + private function getEditTicketModel($id, $jsonRequest) { $editTicketModel = new EditTicketModel(); $editTicketModel->id = $id; diff --git a/api/DataAccess/Calendar/CalendarGateway.php b/api/DataAccess/Calendar/CalendarGateway.php index 9367dbb7..a644370d 100644 --- a/api/DataAccess/Calendar/CalendarGateway.php +++ b/api/DataAccess/Calendar/CalendarGateway.php @@ -11,23 +11,19 @@ use BusinessLogic\Calendar\TicketEvent; use BusinessLogic\Security\UserContext; use Core\Constants\Priority; use DataAccess\CommonDao; +use DataAccess\Logging\LoggingGateway; class CalendarGateway extends CommonDao { /** - * @param $startTime int - * @param $endTime int * @param $searchEventsFilter SearchEventsFilter * @param $heskSettings array * @return AbstractEvent[] */ - public function getEventsForStaff($startTime, $endTime, $searchEventsFilter, $heskSettings) { + public function getEventsForStaff($searchEventsFilter, $heskSettings) { $this->init(); $events = array(); - $startTimeSql = "CONVERT_TZ(FROM_UNIXTIME(" . hesk_dbEscape($startTime) . " / 1000), @@session.time_zone, '+00:00')"; - $endTimeSql = "CONVERT_TZ(FROM_UNIXTIME(" . hesk_dbEscape($endTime) . " / 1000), @@session.time_zone, '+00:00')"; - // EVENTS $sql = "SELECT `events`.*, `categories`.`name` AS `category_name`, `categories`.`background_color` AS `background_color`, `categories`.`foreground_color` AS `foreground_color`, `categories`.`display_border_outline` AS `display_border`, @@ -38,9 +34,21 @@ class CalendarGateway extends CommonDao { LEFT JOIN `" . hesk_dbEscape($heskSettings['db_pfix']) . "calendar_event_reminder` AS `reminders` ON `reminders`.`user_id` = " . intval($searchEventsFilter->reminderUserId) . " AND `reminders`.`event_id` = `events`.`id` - WHERE NOT (`end` < {$startTimeSql} OR `start` > {$endTimeSql}) + WHERE 1=1"; + + if ($searchEventsFilter->startTime !== null && $searchEventsFilter->endTime !== null) { + $startTimeSql = "CONVERT_TZ(FROM_UNIXTIME(" . hesk_dbEscape($searchEventsFilter->startTime) . " / 1000), @@session.time_zone, '+00:00')"; + $endTimeSql = "CONVERT_TZ(FROM_UNIXTIME(" . hesk_dbEscape($searchEventsFilter->endTime) . " / 1000), @@session.time_zone, '+00:00')"; + + + $sql .= " AND NOT (`end` < {$startTimeSql} OR `start` > {$endTimeSql}) AND `categories`.`usage` <> 1 AND `categories`.`type` = '0'"; + } + + if ($searchEventsFilter->eventId !== null) { + $sql .= " AND `events`.`id` = " . intval($searchEventsFilter->eventId); + } if (!empty($searchEventsFilter->categories)) { $categoriesAsString = implode(',', $searchEventsFilter->categories); @@ -130,6 +138,33 @@ class CalendarGateway extends CommonDao { return $events; } + /** + * @param $event CalendarEvent + * @param $userContext UserContext + * @param $heskSettings array + * @return CalendarEvent + */ + public function createEvent($event, $userContext, $heskSettings) { + $this->init(); + + hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($heskSettings['db_pfix']) . "calendar_event` (`start`, `end`, `all_day`, `name`, + `location`, `comments`, `category`) VALUES ('" . hesk_dbEscape($event->startTime) . "', '" . hesk_dbEscape($event->endTime) . "', + '" . ($event->allDay ? 1 : 0) . "', '" . hesk_dbEscape(addslashes($event->title)) . "', + '" . hesk_dbEscape(addslashes($event->location)) . "', '". hesk_dbEscape(addslashes($event->comments)) . "', " . intval($event->categoryId) . ")"); + + $event->id = hesk_dbInsertID(); + + if ($event->reminderValue !== null) { + hesk_dbQuery("INSERT INTO `" . hesk_dbEscape($heskSettings['db_pfix']) . "calendar_event_reminder` (`user_id`, `event_id`, + `amount`, `unit`) VALUES (" . intval($userContext->id) . ", " . intval($event->id) . ", " . intval($event->reminderValue) . ", + " . intval($event->reminderUnits) . ")"); + } + + $this->close(); + + return $event; + } + /** * @param $event CalendarEvent * @param $userContext UserContext @@ -143,7 +178,7 @@ class CalendarGateway extends CommonDao { . hesk_dbEscape(addslashes($event->title)) . "', `location` = '" . hesk_dbEscape(addslashes($event->location)) . "', `comments` = '" . hesk_dbEscape(addslashes($event->comments)) . "', `category` = " . intval($event->categoryId) . " WHERE `id` = " . intval($event->id); - if ($event->reminderValue != null) { + if ($event->reminderValue !== null) { $delete_sql = "DELETE FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "calendar_event_reminder` WHERE `event_id` = " . intval($event->id) . " AND `user_id` = " . intval($userContext->id); hesk_dbQuery($delete_sql); diff --git a/api/index.php b/api/index.php index d833c236..00bd9af3 100644 --- a/api/index.php +++ b/api/index.php @@ -203,7 +203,7 @@ Link::all(array( // Settings '/v1/settings' => action(\Controllers\Settings\SettingsController::clazz(), RequestMethod::all()), // Calendar - '/v1/calendar/events/staff' => action(\Controllers\Calendar\CalendarController::clazz(), array(RequestMethod::GET), SecurityHandler::INTERNAL_OR_AUTH_TOKEN), + '/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), SecurityHandler::INTERNAL_OR_AUTH_TOKEN), /* Internal use only routes */ diff --git a/js/calendar/mods-for-hesk-calendar.js b/js/calendar/mods-for-hesk-calendar.js index 88197f4f..1ccfe7eb 100644 --- a/js/calendar/mods-for-hesk-calendar.js +++ b/js/calendar/mods-for-hesk-calendar.js @@ -204,7 +204,6 @@ $(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'), @@ -216,8 +215,10 @@ $(document).ready(function() { $.ajax({ method: 'POST', - url: heskPath + 'internal-api/admin/calendar/', - data: data, + url: heskPath + 'api/v1/calendar/events/staff', + data: JSON.stringify(data), + contentType: 'json', + headers: { 'X-Internal-Call': true }, success: function(id) { addToCalendar(id, data, $('#lang_event_created').text()); $('#create-event-modal').modal('hide'); @@ -245,7 +246,6 @@ $(document).ready(function() { } var data = { - id: $form.find('input[name="id"]').val(), title: $form.find('input[name="name"]').val(), location: $form.find('input[name="location"]').val(), startTime: moment(start).format(dateFormat), @@ -257,15 +257,19 @@ $(document).ready(function() { 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() }; $.ajax({ method: 'POST', - url: heskPath + 'internal-api/admin/calendar/', - data: data, + url: heskPath + 'api/v1/calendar/events/staff/' + $form.find('input[name="id"]').val(), + data: JSON.stringify(data), + contentType: 'json', + headers: { + 'X-Internal-Call': true, + 'X-HTTP-Method-Override': 'PUT' + }, success: function() { removeFromCalendar(data.id); addToCalendar(data.id, data, $('#lang_event_updated').text()); @@ -291,7 +295,7 @@ function removeFromCalendar(id) { } function buildEvent(id, dbObject) { - if (dbObject.type == 'TICKET') { + if (dbObject.type === 'TICKET') { return { title: dbObject.title, subject: dbObject.subject, From bd5b3e1322ff03f6cd097f90d64ff3e078c32dc9 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Mon, 1 Jan 2018 21:39:01 -0500 Subject: [PATCH 201/248] Working on moving the update ticket due date endpoint --- api/BusinessLogic/Tickets/AuditTrailEvent.php | 9 +++ api/BusinessLogic/Tickets/TicketEditor.php | 59 +++++++++++++++++-- api/DataAccess/Tickets/TicketGateway.php | 14 +++++ 3 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 api/BusinessLogic/Tickets/AuditTrailEvent.php diff --git a/api/BusinessLogic/Tickets/AuditTrailEvent.php b/api/BusinessLogic/Tickets/AuditTrailEvent.php new file mode 100644 index 00000000..d3063e73 --- /dev/null +++ b/api/BusinessLogic/Tickets/AuditTrailEvent.php @@ -0,0 +1,9 @@ +ticketGateway = $ticketGateway; $this->userToTicketChecker = $userToTicketChecker; + $this->auditTrailGateway = $auditTrailGateway; } @@ -141,19 +148,61 @@ class TicketEditor extends \BaseClass { * @param $dueDate string * @param $userContext UserContext * @param $heskSettings array - * @throws ApiFriendlyException If ticket does not exist or if the user cannot edit the ticket + * @return Ticket The updated ticket */ function updateDueDate($id, $dueDate, $userContext, $heskSettings) { $ticket = $this->ticketGateway->getTicketById($id, $heskSettings); + $this->validateDueDate($ticket, $dueDate, $userContext, $heskSettings); + + $this->ticketGateway->updateTicketDueDate($ticket->id, $dueDate, $heskSettings); + + $event = AuditTrailEvent::DUE_DATE_REMOVED; + $replacementValues = array(0 => $userContext->name . ' (' . $userContext->username . ')'); + if ($dueDate !== null) { + $event = AuditTrailEvent::DUE_DATE_CHANGED; + $replacementValues = array( + 0 => $userContext->name . ' (' . $userContext->username . ')', + 1 => date('Y-m-d H:i:s', strtotime($dueDate)) + ); + } + + $this->auditTrailGateway->insertAuditTrailRecord($ticket->id, + AuditTrailEntityType::TICKET, + $event, + DateTimeHelpers::heskDate($heskSettings), + $replacementValues, + $heskSettings); + + $ticket->dueDate = $dueDate; + + return $ticket; + } + + /** + * @param $ticket Ticket + * @param $dueDate string + * @param $userContext UserContext + * @param $heskSettings array + * @throws ValidationException When validation fails + */ + private function validateDueDate($ticket, $dueDate, $userContext, $heskSettings) { + $validationModel = new ValidationModel(); + if ($ticket === null) { - throw new ApiFriendlyException("Please enter a valid ticket ID.", "Ticket Not Found!", 400); + $validationModel->errorKeys[] = 'TICKET_MUST_EXIST_FOR_ID'; } if (!$this->userToTicketChecker->isTicketAccessibleToUser($userContext, $ticket, $heskSettings, array(UserPrivilege::CAN_EDIT_TICKETS))) { - throw new ApiFriendlyException("User " . $userContext->id . " does not have permission to edit ticket " . $id, "Access Denied", 403); + $validationModel->errorKeys[] = 'TICKET_MUST_BE_ACCESSIBLE_TO_USER'; } - // TODO Do it + if ($dueDate === false) { + $validationModel->errorKeys[] = 'DUE_DATE_MUST_BE_IN_VALID_FORMAT'; + } + + if (count($validationModel->errorKeys) > 0) { + throw new ValidationException($validationModel); + } } } \ No newline at end of file diff --git a/api/DataAccess/Tickets/TicketGateway.php b/api/DataAccess/Tickets/TicketGateway.php index 4c72a9f2..2ea5a84d 100644 --- a/api/DataAccess/Tickets/TicketGateway.php +++ b/api/DataAccess/Tickets/TicketGateway.php @@ -440,4 +440,18 @@ class TicketGateway extends CommonDao { $this->close(); } + + function updateTicketDueDate($id, $dueDate, $heskSettings) { + $this->init(); + + $sqlDueDate = 'NULL'; + if ($dueDate != NULL) { + $sqlDueDate = "'" . date('Y-m-d H:i:s', strtotime($dueDate)) . "'"; + } + + hesk_dbQuery("UPDATE `" . hesk_dbEscape($heskSettings['db_pfix']) . "tickets` SET `due_date` = {$sqlDueDate} + WHERE `id` = " . intval($id)); + + $this->close(); + } } \ No newline at end of file From 0514965040d3199a843bdfd676198319d3359b35 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Tue, 2 Jan 2018 13:05:10 -0500 Subject: [PATCH 202/248] Update due date mostly done... just need to fix the buildEvent function --- api/BusinessLogic/Calendar/AbstractEvent.php | 2 ++ api/BusinessLogic/Calendar/CalendarEvent.php | 2 -- api/BusinessLogic/Helpers.php | 4 ++++ .../Tickets/StaffTicketController.php | 4 ++++ api/DataAccess/Calendar/CalendarGateway.php | 14 ++++++----- api/index.php | 1 + js/calendar/mods-for-hesk-calendar.js | 23 ++++++++++++------- 7 files changed, 34 insertions(+), 16 deletions(-) diff --git a/api/BusinessLogic/Calendar/AbstractEvent.php b/api/BusinessLogic/Calendar/AbstractEvent.php index 5413ef48..27f2a184 100644 --- a/api/BusinessLogic/Calendar/AbstractEvent.php +++ b/api/BusinessLogic/Calendar/AbstractEvent.php @@ -4,6 +4,8 @@ namespace BusinessLogic\Calendar; class AbstractEvent { + public $id; + public $startTime; public $title; diff --git a/api/BusinessLogic/Calendar/CalendarEvent.php b/api/BusinessLogic/Calendar/CalendarEvent.php index 893cbccb..91a45761 100644 --- a/api/BusinessLogic/Calendar/CalendarEvent.php +++ b/api/BusinessLogic/Calendar/CalendarEvent.php @@ -4,8 +4,6 @@ namespace BusinessLogic\Calendar; class CalendarEvent extends AbstractEvent { - public $id; - public $type = 'CALENDAR'; public $endTime; diff --git a/api/BusinessLogic/Helpers.php b/api/BusinessLogic/Helpers.php index f2a49c6d..b841fd27 100644 --- a/api/BusinessLogic/Helpers.php +++ b/api/BusinessLogic/Helpers.php @@ -30,4 +30,8 @@ class Helpers extends \BaseClass { static function boolval($val) { return $val == true; } + + static function heskHtmlSpecialCharsDecode($in) { + return str_replace(array('&', '<', '>', '"'), array('&', '<', '>', '"'), $in); + } } \ No newline at end of file diff --git a/api/Controllers/Tickets/StaffTicketController.php b/api/Controllers/Tickets/StaffTicketController.php index ea4761a1..e049bca5 100644 --- a/api/Controllers/Tickets/StaffTicketController.php +++ b/api/Controllers/Tickets/StaffTicketController.php @@ -53,7 +53,11 @@ class StaffTicketController extends \BaseClass { /* @var $ticketEditor TicketEditor */ $ticketEditor = $applicationContext->get(TicketEditor::clazz()); + $json = JsonRetriever::getJsonData(); + $dueDate = date('Y-m-d H:i:s', strtotime(Helpers::safeArrayGet($json, 'dueDate'))); + + $ticketEditor->updateDueDate($id, $dueDate, $userContext, $hesk_settings); } private function getEditTicketModel($id, $jsonRequest) { diff --git a/api/DataAccess/Calendar/CalendarGateway.php b/api/DataAccess/Calendar/CalendarGateway.php index a644370d..f101f1bd 100644 --- a/api/DataAccess/Calendar/CalendarGateway.php +++ b/api/DataAccess/Calendar/CalendarGateway.php @@ -8,6 +8,7 @@ use BusinessLogic\Calendar\CalendarEvent; use BusinessLogic\Calendar\ReminderUnit; use BusinessLogic\Calendar\SearchEventsFilter; use BusinessLogic\Calendar\TicketEvent; +use BusinessLogic\Helpers; use BusinessLogic\Security\UserContext; use Core\Constants\Priority; use DataAccess\CommonDao; @@ -61,15 +62,15 @@ class CalendarGateway extends CommonDao { $event->id = intval($row['id']); $event->startTime = $row['start']; $event->endTime = $row['end']; - $event->allDay = $row['all_day'] ? true : false; + $event->allDay = Helpers::boolval($row['all_day']); $event->title = $row['name']; $event->location = $row['location']; $event->comments = $row['comments']; $event->categoryId = intval($row['category']); - $event->categoryName = $row['category_name']; + $event->categoryName = Helpers::heskHtmlSpecialCharsDecode($row['category_name']); $event->backgroundColor = $row['background_color']; $event->foregroundColor = $row['foreground_color']; - $event->displayBorder = $row['display_border'] === '1'; + $event->displayBorder = Helpers::boolval($row['display_border']); $event->reminderValue = $row['reminder_value'] === null ? null : floatval($row['reminder_value']); $event->reminderUnits = $row['reminder_unit'] === null ? null : ReminderUnit::getByValue($row['reminder_unit']); @@ -83,7 +84,7 @@ class CalendarGateway extends CommonDao { $currentDate = hesk_date(); $heskSettings['timeformat'] = $oldTimeSetting; - $sql = "SELECT `trackid`, `subject`, `due_date`, `category`, `categories`.`name` AS `category_name`, `categories`.`background_color` AS `background_color`, + $sql = "SELECT `tickets`.`id` AS `id`, `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` < '{$currentDate}' THEN 1 ELSE 0 END AS `overdue`, `owner`.`name` AS `owner_name`, `tickets`.`owner` AS `owner_id`, `tickets`.`priority` AS `priority` @@ -116,16 +117,17 @@ class CalendarGateway extends CommonDao { $rs = hesk_dbQuery($sql); while ($row = hesk_dbFetchAssoc($rs)) { $event = new TicketEvent(); + $event->id = intval($row['id']); $event->trackingId = $row['trackid']; $event->subject = $row['subject']; $event->title = $row['subject']; $event->startTime = $row['due_date']; $event->url = $heskSettings['hesk_url'] . '/' . $heskSettings['admin_dir'] . '/admin_ticket.php?track=' . $event->trackingId; $event->categoryId = intval($row['category']); - $event->categoryName = $row['category_name']; + $event->categoryName = Helpers::heskHtmlSpecialCharsDecode($row['category_name']); $event->backgroundColor = $row['background_color']; $event->foregroundColor = $row['foreground_color']; - $event->displayBorder = $row['display_border'] === '0'; + $event->displayBorder = Helpers::boolval($row['display_border']); $event->owner = $row['owner_name']; $event->priority = Priority::getByValue($row['priority']); diff --git a/api/index.php b/api/index.php index 00bd9af3..3888685c 100644 --- a/api/index.php +++ b/api/index.php @@ -194,6 +194,7 @@ Link::all(array( '/v1/tickets' => action(\Controllers\Tickets\CustomerTicketController::clazz(), RequestMethod::all(), SecurityHandler::OPEN), // Tickets - Staff '/v1/staff/tickets/{i}' => action(\Controllers\Tickets\StaffTicketController::clazz(), RequestMethod::all()), + '/v1/staff/tickets/{i}/due-date' => action(\Controllers\Tickets\StaffTicketController::clazz() . '::updateDueDate', array(RequestMethod::PATCH), SecurityHandler::INTERNAL_OR_AUTH_TOKEN), // Attachments '/v1/tickets/{a}/attachments/{i}' => action(\Controllers\Attachments\PublicAttachmentController::clazz() . '::getRaw', RequestMethod::all()), '/v1/staff/tickets/{i}/attachments' => action(\Controllers\Attachments\StaffTicketAttachmentsController::clazz(), RequestMethod::all()), diff --git a/js/calendar/mods-for-hesk-calendar.js b/js/calendar/mods-for-hesk-calendar.js index 1ccfe7eb..1d6729f4 100644 --- a/js/calendar/mods-for-hesk-calendar.js +++ b/js/calendar/mods-for-hesk-calendar.js @@ -482,15 +482,19 @@ function updateCategoryVisibility() { function respondToDragAndDrop(event, delta, revertFunc) { var heskPath = $('p#hesk-path').text(); + if (event.type === 'TICKET') { + var uri = 'api/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 @@ -519,7 +523,6 @@ function respondToDragAndDrop(event, delta, revertFunc) { end += ' ' + event.end.format('HH:mm:ss'); } var data = { - id: event.id, title: event.title, location: event.location, startTime: start, @@ -533,8 +536,12 @@ function respondToDragAndDrop(event, delta, revertFunc) { }; $.ajax({ method: 'POST', - url: heskPath + 'internal-api/admin/calendar/', - data: data, + url: heskPath + 'api/v1/calendar/events/staff/' + event.id, + data: JSON.stringify(data), + headers: { + 'X-Internal-Call': true, + 'X-HTTP-Method-Override': 'PUT' + }, success: function() { mfhAlert.success(mfhLang.text('event_updated')); }, From c4b79a722c1f7aae466e5aad8c611a0f8099319f Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Wed, 3 Jan 2018 13:04:23 -0500 Subject: [PATCH 203/248] More calendar tweaks --- .../Calendar/CalendarHandler.php | 14 +++++- .../Calendar/CalendarController.php | 2 + api/DataAccess/Calendar/CalendarGateway.php | 2 - js/calendar/mods-for-hesk-calendar.js | 44 +++++++++++-------- 4 files changed, 41 insertions(+), 21 deletions(-) diff --git a/api/BusinessLogic/Calendar/CalendarHandler.php b/api/BusinessLogic/Calendar/CalendarHandler.php index 5347eaf1..766ea537 100644 --- a/api/BusinessLogic/Calendar/CalendarHandler.php +++ b/api/BusinessLogic/Calendar/CalendarHandler.php @@ -44,6 +44,18 @@ class CalendarHandler extends \BaseClass { public function createEvent($calendarEvent, $userContext, $heskSettings) { - return $this->calendarGateway->createEvent($calendarEvent, $userContext, $heskSettings); + $this->calendarGateway->createEvent($calendarEvent, $userContext, $heskSettings); + + $eventFilter = new SearchEventsFilter(); + $eventFilter->eventId = $calendarEvent->id; + $eventFilter->reminderUserId = $userContext->id; + + $events = $this->calendarGateway->getEventsForStaff($eventFilter, $heskSettings); + + if (count($events) !== 1) { + throw new \Exception("Expected exactly 1 event, found: " . count($events)); + } + + return $events[0]; } } \ No newline at end of file diff --git a/api/Controllers/Calendar/CalendarController.php b/api/Controllers/Calendar/CalendarController.php index 2e30c0b1..f7be1d0e 100644 --- a/api/Controllers/Calendar/CalendarController.php +++ b/api/Controllers/Calendar/CalendarController.php @@ -55,6 +55,8 @@ class CalendarController extends \BaseClass { /* @var $calendarHandler CalendarHandler */ $calendarHandler = $applicationContext->get(CalendarHandler::clazz()); + + return output($calendarHandler->createEvent($event, $userContext, $hesk_settings)); } function put($id) { diff --git a/api/DataAccess/Calendar/CalendarGateway.php b/api/DataAccess/Calendar/CalendarGateway.php index f101f1bd..416e7f1e 100644 --- a/api/DataAccess/Calendar/CalendarGateway.php +++ b/api/DataAccess/Calendar/CalendarGateway.php @@ -163,8 +163,6 @@ class CalendarGateway extends CommonDao { } $this->close(); - - return $event; } /** diff --git a/js/calendar/mods-for-hesk-calendar.js b/js/calendar/mods-for-hesk-calendar.js index 1d6729f4..677a5db1 100644 --- a/js/calendar/mods-for-hesk-calendar.js +++ b/js/calendar/mods-for-hesk-calendar.js @@ -52,16 +52,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(); @@ -77,6 +68,13 @@ $(document).ready(function() { .find('.popover-category span').text(event.categoryName).end() .find('.popover-priority span').text(event.priority); } else { + if (event.allDay) { + endDate = event.end.clone(); + endDate.add(-1, 'days'); + } else { + format += ', HH:mm'; + } + if (event.location === '') { $contents.find('.popover-location').hide(); } @@ -196,6 +194,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(), @@ -209,8 +210,8 @@ $(document).ready(function() { 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({ @@ -245,25 +246,29 @@ $(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(), location: $form.find('input[name="location"]').val(), startTime: moment(start).format(dateFormat), 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(), - 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 + 'api/v1/calendar/events/staff/' + $form.find('input[name="id"]').val(), + url: heskPath + 'api/v1/calendar/events/staff/' + data.id, data: JSON.stringify(data), contentType: 'json', headers: { @@ -297,6 +302,7 @@ function removeFromCalendar(id) { function buildEvent(id, dbObject) { if (dbObject.type === 'TICKET') { return { + id: id, title: dbObject.title, subject: dbObject.subject, trackingId: dbObject.trackingId, @@ -384,7 +390,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'); @@ -534,9 +540,11 @@ function respondToDragAndDrop(event, delta, revertFunc) { reminderValue: event.reminderValue, reminderUnits: event.reminderUnits }; + + var url = heskPath + 'api/v1/calendar/events/staff/' + event.id; $.ajax({ method: 'POST', - url: heskPath + 'api/v1/calendar/events/staff/' + event.id, + url: url, data: JSON.stringify(data), headers: { 'X-Internal-Call': true, From 3d73b9a4b25c55481c09efeea982ae4e79484690 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Sun, 7 Jan 2018 22:02:43 -0500 Subject: [PATCH 204/248] All main staff calendar endpoints moved --- .../Calendar/CalendarHandler.php | 6 +++-- .../Calendar/CalendarController.php | 12 ++++++++++ api/DataAccess/Calendar/CalendarGateway.php | 16 +++++++++++++ api/index.php | 2 +- js/calendar/mods-for-hesk-calendar.js | 24 +++++++++---------- 5 files changed, 44 insertions(+), 16 deletions(-) diff --git a/api/BusinessLogic/Calendar/CalendarHandler.php b/api/BusinessLogic/Calendar/CalendarHandler.php index 766ea537..ad5a1775 100644 --- a/api/BusinessLogic/Calendar/CalendarHandler.php +++ b/api/BusinessLogic/Calendar/CalendarHandler.php @@ -3,10 +3,8 @@ namespace BusinessLogic\Calendar; -use BusinessLogic\Exceptions\ApiFriendlyException; use BusinessLogic\Security\UserContext; use DataAccess\Calendar\CalendarGateway; -use PHPUnit\Runner\Exception; class CalendarHandler extends \BaseClass { private $calendarGateway; @@ -58,4 +56,8 @@ class CalendarHandler extends \BaseClass { return $events[0]; } + + public function deleteEvent($id, $userContext, $heskSettings) { + $this->calendarGateway->deleteEvent($id, $userContext, $heskSettings); + } } \ No newline at end of file diff --git a/api/Controllers/Calendar/CalendarController.php b/api/Controllers/Calendar/CalendarController.php index f7be1d0e..d586b310 100644 --- a/api/Controllers/Calendar/CalendarController.php +++ b/api/Controllers/Calendar/CalendarController.php @@ -73,6 +73,18 @@ class CalendarController extends \BaseClass { return output($calendarHandler->updateEvent($event, $userContext, $hesk_settings)); } + function delete($id) { + /* @var $userContext UserContext */ + global $applicationContext, $hesk_settings, $userContext; + + /* @var $calendarHandler CalendarHandler */ + $calendarHandler = $applicationContext->get(CalendarHandler::clazz()); + + $calendarHandler->deleteEvent($id, $userContext, $hesk_settings); + + return http_response_code(204); + } + private function transformJson($json, $id = null) { $event = new CalendarEvent(); diff --git a/api/DataAccess/Calendar/CalendarGateway.php b/api/DataAccess/Calendar/CalendarGateway.php index 416e7f1e..81b7026f 100644 --- a/api/DataAccess/Calendar/CalendarGateway.php +++ b/api/DataAccess/Calendar/CalendarGateway.php @@ -192,4 +192,20 @@ class CalendarGateway extends CommonDao { $this->close(); } + + /** + * @param $id int + * @param $userContext UserContext + * @param $heskSettings array + */ + public function deleteEvent($id, $userContext, $heskSettings) { + $this->init(); + + hesk_dbQuery("DELETE FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "calendar_event_reminder` + WHERE `event_id` = " . intval($id) . " AND `user_id` = " . intval($userContext->id)); + hesk_dbQuery("DELETE FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "calendar_event` + WHERE `id` = " . intval($id)); + + $this->close(); + } } \ No newline at end of file diff --git a/api/index.php b/api/index.php index 3888685c..2277d647 100644 --- a/api/index.php +++ b/api/index.php @@ -205,7 +205,7 @@ Link::all(array( '/v1/settings' => action(\Controllers\Settings\SettingsController::clazz(), RequestMethod::all()), // Calendar '/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), 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), /* Internal use only routes */ // Resend email response diff --git a/js/calendar/mods-for-hesk-calendar.js b/js/calendar/mods-for-hesk-calendar.js index 677a5db1..2561381b 100644 --- a/js/calendar/mods-for-hesk-calendar.js +++ b/js/calendar/mods-for-hesk-calendar.js @@ -16,7 +16,7 @@ $(document).ready(function() { defaultView: $('#setting_default_view').text().trim(), events: function(start, end, timezone, callback) { $.ajax({ - url: heskPath + 'api/v1/calendar/events/staff?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 }, @@ -160,17 +160,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'); }, @@ -216,7 +214,7 @@ $(document).ready(function() { $.ajax({ method: 'POST', - url: heskPath + 'api/v1/calendar/events/staff', + url: heskPath + 'api/index.php/v1/calendar/events/staff', data: JSON.stringify(data), contentType: 'json', headers: { 'X-Internal-Call': true }, @@ -268,7 +266,7 @@ $(document).ready(function() { $.ajax({ method: 'POST', - url: heskPath + 'api/v1/calendar/events/staff/' + data.id, + url: heskPath + 'api/index.php/v1/calendar/events/staff/' + data.id, data: JSON.stringify(data), contentType: 'json', headers: { @@ -490,7 +488,7 @@ function respondToDragAndDrop(event, delta, revertFunc) { var heskPath = $('p#hesk-path').text(); if (event.type === 'TICKET') { - var uri = 'api/v1/staff/tickets/' + event.id + '/due-date'; + var uri = 'api/index.php/v1/staff/tickets/' + event.id + '/due-date'; $.ajax({ method: 'POST', url: heskPath + uri, @@ -541,7 +539,7 @@ function respondToDragAndDrop(event, delta, revertFunc) { reminderUnits: event.reminderUnits }; - var url = heskPath + 'api/v1/calendar/events/staff/' + event.id; + var url = heskPath + 'api/index.php/v1/calendar/events/staff/' + event.id; $.ajax({ method: 'POST', url: url, From 4c2432a35bec4dacc697dc003dae03bf4d1d8b97 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Tue, 9 Jan 2018 12:37:29 -0500 Subject: [PATCH 205/248] Moved all calendar API logic to the api folder --- api/BusinessLogic/Categories/Category.php | 2 +- .../Categories/CategoryHandler.php | 13 ++ .../Calendar/CalendarController.php | 27 ++- api/DataAccess/Calendar/CalendarGateway.php | 3 +- api/index.php | 1 + internal-api/calendar/index.php | 29 --- internal-api/dao/calendar_dao.php | 195 ------------------ .../mods-for-hesk-calendar-admin-readonly.js | 3 +- .../mods-for-hesk-calendar-readonly.js | 2 +- js/calendar/mods-for-hesk-calendar.js | 4 +- 10 files changed, 44 insertions(+), 235 deletions(-) delete mode 100644 internal-api/calendar/index.php delete mode 100644 internal-api/dao/calendar_dao.php diff --git a/api/BusinessLogic/Categories/Category.php b/api/BusinessLogic/Categories/Category.php index 597c3410..f7ea363e 100644 --- a/api/BusinessLogic/Categories/Category.php +++ b/api/BusinessLogic/Categories/Category.php @@ -22,7 +22,7 @@ class Category extends \BaseClass { public $autoAssign; /** - * @var int The type of Categories (1 = Private, 2 = Public) + * @var int The type of Categories (1 = Private, 0 = Public) */ public $type; diff --git a/api/BusinessLogic/Categories/CategoryHandler.php b/api/BusinessLogic/Categories/CategoryHandler.php index 98e5b6d4..49d0b189 100644 --- a/api/BusinessLogic/Categories/CategoryHandler.php +++ b/api/BusinessLogic/Categories/CategoryHandler.php @@ -187,4 +187,17 @@ class CategoryHandler extends \BaseClass { $this->categoryGateway->updateCategory($category, $heskSettings); $this->categoryGateway->resortAllCategories($heskSettings); } + + function getPublicCategories($heskSettings) { + $allCategories = $this->categoryGateway->getAllCategories($heskSettings, $this->modsForHeskSettingsGateway->getAllSettings($heskSettings)); + + $publicCategories = array(); + foreach ($allCategories as $category) { + if ($category->type === 0) { + $publicCategories[] = $category; + } + } + + return $publicCategories; + } } \ No newline at end of file diff --git a/api/Controllers/Calendar/CalendarController.php b/api/Controllers/Calendar/CalendarController.php index d586b310..0953b585 100644 --- a/api/Controllers/Calendar/CalendarController.php +++ b/api/Controllers/Calendar/CalendarController.php @@ -7,12 +7,14 @@ use BusinessLogic\Calendar\CalendarEvent; use BusinessLogic\Calendar\CalendarHandler; use BusinessLogic\Calendar\ReminderUnit; use BusinessLogic\Calendar\SearchEventsFilter; +use BusinessLogic\Categories\CategoryHandler; use BusinessLogic\Exceptions\ValidationException; use BusinessLogic\Helpers; use BusinessLogic\Security\UserContext; use BusinessLogic\Security\UserPrivilege; use BusinessLogic\ValidationModel; use Controllers\JsonRetriever; +use DataAccess\Settings\ModsForHeskSettingsGateway; class CalendarController extends \BaseClass { function get() { @@ -35,10 +37,27 @@ class CalendarController extends \BaseClass { $searchEventsFilter->startTime = $startTime; $searchEventsFilter->endTime = $endTime; $searchEventsFilter->reminderUserId = $userContext->id; - $searchEventsFilter->includeTicketsAssignedToOthers = in_array(UserPrivilege::CAN_VIEW_ASSIGNED_TO_OTHER, $userContext->permissions); - $searchEventsFilter->includeUnassignedTickets = in_array(UserPrivilege::CAN_VIEW_UNASSIGNED, $userContext->permissions); - $searchEventsFilter->includeTickets = true; - $searchEventsFilter->categories = $userContext->admin ? null : $userContext->categories; + + if ($userContext->isAnonymousUser()) { + $searchEventsFilter->includeTicketsAssignedToOthers = false; + $searchEventsFilter->includeUnassignedTickets = false; + $searchEventsFilter->includeTickets = false; + + /* @var $categoryHandler CategoryHandler */ + $categoryHandler = $applicationContext->get(CategoryHandler::clazz()); + + $publicCategories = $categoryHandler->getPublicCategories($hesk_settings); + $ids = array(); + foreach ($publicCategories as $category) { + $ids[] = $category->id; + } + $searchEventsFilter->categories = $ids; + } else { + $searchEventsFilter->includeTicketsAssignedToOthers = in_array(UserPrivilege::CAN_VIEW_ASSIGNED_TO_OTHER, $userContext->permissions); + $searchEventsFilter->includeUnassignedTickets = in_array(UserPrivilege::CAN_VIEW_UNASSIGNED, $userContext->permissions); + $searchEventsFilter->includeTickets = true; + $searchEventsFilter->categories = $userContext->admin ? null : $userContext->categories; + } $events = $calendarHandler->getEventsForStaff($searchEventsFilter, $hesk_settings); diff --git a/api/DataAccess/Calendar/CalendarGateway.php b/api/DataAccess/Calendar/CalendarGateway.php index 81b7026f..ddb263e6 100644 --- a/api/DataAccess/Calendar/CalendarGateway.php +++ b/api/DataAccess/Calendar/CalendarGateway.php @@ -43,8 +43,7 @@ class CalendarGateway extends CommonDao { $sql .= " AND NOT (`end` < {$startTimeSql} OR `start` > {$endTimeSql}) - AND `categories`.`usage` <> 1 - AND `categories`.`type` = '0'"; + AND `categories`.`usage` <> 1"; } if ($searchEventsFilter->eventId !== null) { diff --git a/api/index.php b/api/index.php index 2277d647..7c0574b2 100644 --- a/api/index.php +++ b/api/index.php @@ -204,6 +204,7 @@ Link::all(array( // Settings '/v1/settings' => action(\Controllers\Settings\SettingsController::clazz(), RequestMethod::all()), // Calendar + '/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), 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'); - } - - -} - -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/js/calendar/mods-for-hesk-calendar-admin-readonly.js b/js/calendar/mods-for-hesk-calendar-admin-readonly.js index 619e5d23..7290189e 100644 --- a/js/calendar/mods-for-hesk-calendar-admin-readonly.js +++ b/js/calendar/mods-for-hesk-calendar-admin-readonly.js @@ -16,9 +16,10 @@ $(document).ready(function() { 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() { diff --git a/js/calendar/mods-for-hesk-calendar-readonly.js b/js/calendar/mods-for-hesk-calendar-readonly.js index eff85039..a282c155 100644 --- a/js/calendar/mods-for-hesk-calendar-readonly.js +++ b/js/calendar/mods-for-hesk-calendar-readonly.js @@ -16,7 +16,7 @@ $(document).ready(function() { 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 2561381b..48fac68f 100644 --- a/js/calendar/mods-for-hesk-calendar.js +++ b/js/calendar/mods-for-hesk-calendar.js @@ -218,8 +218,8 @@ $(document).ready(function() { data: JSON.stringify(data), contentType: 'json', headers: { 'X-Internal-Call': true }, - success: function(id) { - addToCalendar(id, data, $('#lang_event_created').text()); + success: function(createdEvent) { + addToCalendar(createdEvent.id, data, $('#lang_event_created').text()); $('#create-event-modal').modal('hide'); updateCategoryVisibility(); }, From 4959ccb815dd6d530c5f027624e10f07d9f4657b Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Thu, 18 Jan 2018 22:02:48 -0500 Subject: [PATCH 206/248] Add php-rrule for recurring event parsing --- api/composer.json | 3 +- api/composer.lock | 151 ++++++++++++++++++++++++++++++---------------- 2 files changed, 102 insertions(+), 52 deletions(-) diff --git a/api/composer.json b/api/composer.json index b91d34b5..c7215460 100644 --- a/api/composer.json +++ b/api/composer.json @@ -17,6 +17,7 @@ "phpmailer/phpmailer": "^5.2", "mailgun/mailgun-php": "1.7.2", "mike-koch/php-di": "4.4.11", - "doctrine/annotations": "1.2" + "doctrine/annotations": "1.2", + "rlanvin/php-rrule": "1.6.0" } } diff --git a/api/composer.lock b/api/composer.lock index 1668fba8..14ef8855 100644 --- a/api/composer.lock +++ b/api/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "cba4f6a9b461d9050bd4095da7c79aea", + "content-hash": "271633d6be53dff4f327e318b49a049f", "packages": [ { "name": "container-interop/container-interop", @@ -691,17 +691,61 @@ "time": "2017-02-14T16:28:37+00:00" }, { - "name": "symfony/event-dispatcher", - "version": "v2.8.28", + "name": "rlanvin/php-rrule", + "version": "v1.6.0", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "7fe089232554357efb8d4af65ce209fc6e5a2186" + "url": "https://github.com/rlanvin/php-rrule.git", + "reference": "c543bda87d2f8b6c490210f1a6e2bf0d79162a4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/7fe089232554357efb8d4af65ce209fc6e5a2186", - "reference": "7fe089232554357efb8d4af65ce209fc6e5a2186", + "url": "https://api.github.com/repos/rlanvin/php-rrule/zipball/c543bda87d2f8b6c490210f1a6e2bf0d79162a4a", + "reference": "c543bda87d2f8b6c490210f1a6e2bf0d79162a4a", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "<6" + }, + "suggest": { + "ext-intl": "Intl extension is needed for humanReadable()" + }, + "type": "library", + "autoload": { + "psr-4": { + "RRule\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Lightweight and fast recurrence rules for PHP (RFC 5545)", + "homepage": "https://github.com/rlanvin/php-rrule", + "keywords": [ + "date", + "ical", + "recurrence", + "recurring", + "rrule" + ], + "time": "2017-10-11T12:23:40+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v2.8.33", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "d64be24fc1eba62f9daace8a8918f797fc8e87cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d64be24fc1eba62f9daace8a8918f797fc8e87cc", + "reference": "d64be24fc1eba62f9daace8a8918f797fc8e87cc", "shasum": "" }, "require": { @@ -748,7 +792,7 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2017-10-01T21:00:16+00:00" + "time": "2018-01-03T07:36:31+00:00" }, { "name": "zendframework/zend-code", @@ -1334,29 +1378,35 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "4.1.1", + "version": "4.2.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "2d3d238c433cf69caeb4842e97a3223a116f94b2" + "reference": "66465776cfc249844bde6d117abff1d22e06c2da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/2d3d238c433cf69caeb4842e97a3223a116f94b2", - "reference": "2d3d238c433cf69caeb4842e97a3223a116f94b2", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/66465776cfc249844bde6d117abff1d22e06c2da", + "reference": "66465776cfc249844bde6d117abff1d22e06c2da", "shasum": "" }, "require": { "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0@dev", + "phpdocumentor/reflection-common": "^1.0.0", "phpdocumentor/type-resolver": "^0.4.0", "webmozart/assert": "^1.0" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^4.4" + "doctrine/instantiator": "~1.0.5", + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^6.4" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + } + }, "autoload": { "psr-4": { "phpDocumentor\\Reflection\\": [ @@ -1375,7 +1425,7 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-08-30T18:51:59+00:00" + "time": "2017-11-27T17:38:31+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -1426,16 +1476,16 @@ }, { "name": "phpspec/prophecy", - "version": "v1.7.2", + "version": "1.7.3", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6" + "reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6", - "reference": "c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf", + "reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf", "shasum": "" }, "require": { @@ -1447,7 +1497,7 @@ }, "require-dev": { "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8 || ^5.6.5" + "phpunit/phpunit": "^4.8.35 || ^5.7" }, "type": "library", "extra": { @@ -1485,20 +1535,20 @@ "spy", "stub" ], - "time": "2017-09-04T11:05:03+00:00" + "time": "2017-11-24T13:59:53+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "5.2.3", + "version": "5.3.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "8e1d2397d8adf59a3f12b2878a3aaa66d1ab189d" + "reference": "661f34d0bd3f1a7225ef491a70a020ad23a057a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/8e1d2397d8adf59a3f12b2878a3aaa66d1ab189d", - "reference": "8e1d2397d8adf59a3f12b2878a3aaa66d1ab189d", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/661f34d0bd3f1a7225ef491a70a020ad23a057a1", + "reference": "661f34d0bd3f1a7225ef491a70a020ad23a057a1", "shasum": "" }, "require": { @@ -1507,14 +1557,13 @@ "php": "^7.0", "phpunit/php-file-iterator": "^1.4.2", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^2.0", + "phpunit/php-token-stream": "^2.0.1", "sebastian/code-unit-reverse-lookup": "^1.0.1", "sebastian/environment": "^3.0", "sebastian/version": "^2.0.1", "theseer/tokenizer": "^1.1" }, "require-dev": { - "ext-xdebug": "^2.5", "phpunit/phpunit": "^6.0" }, "suggest": { @@ -1523,7 +1572,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.2.x-dev" + "dev-master": "5.3.x-dev" } }, "autoload": { @@ -1538,7 +1587,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -1549,20 +1598,20 @@ "testing", "xunit" ], - "time": "2017-11-03T13:47:33+00:00" + "time": "2017-12-06T09:29:45+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "1.4.2", + "version": "1.4.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", - "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", "shasum": "" }, "require": { @@ -1596,7 +1645,7 @@ "filesystem", "iterator" ], - "time": "2016-10-03T07:40:28+00:00" + "time": "2017-11-27T13:52:08+00:00" }, { "name": "phpunit/php-text-template", @@ -1690,16 +1739,16 @@ }, { "name": "phpunit/php-token-stream", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "9a02332089ac48e704c70f6cefed30c224e3c0b0" + "reference": "791198a2c6254db10131eecfe8c06670700904db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/9a02332089ac48e704c70f6cefed30c224e3c0b0", - "reference": "9a02332089ac48e704c70f6cefed30c224e3c0b0", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", + "reference": "791198a2c6254db10131eecfe8c06670700904db", "shasum": "" }, "require": { @@ -1735,7 +1784,7 @@ "keywords": [ "tokenizer" ], - "time": "2017-08-20T05:47:52+00:00" + "time": "2017-11-27T05:48:46+00:00" }, { "name": "phpunit/phpunit", @@ -1974,16 +2023,16 @@ }, { "name": "sebastian/comparator", - "version": "2.1.0", + "version": "2.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "1174d9018191e93cb9d719edec01257fc05f8158" + "reference": "11c07feade1d65453e06df3b3b90171d6d982087" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1174d9018191e93cb9d719edec01257fc05f8158", - "reference": "1174d9018191e93cb9d719edec01257fc05f8158", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/11c07feade1d65453e06df3b3b90171d6d982087", + "reference": "11c07feade1d65453e06df3b3b90171d6d982087", "shasum": "" }, "require": { @@ -2034,7 +2083,7 @@ "compare", "equality" ], - "time": "2017-11-03T07:16:52+00:00" + "time": "2018-01-12T06:34:42+00:00" }, { "name": "sebastian/diff", @@ -2488,16 +2537,16 @@ }, { "name": "symfony/console", - "version": "v2.8.28", + "version": "v2.8.33", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "f81549d2c5fdee8d711c9ab3c7e7362353ea5853" + "reference": "a4bd0f02ea156cf7b5138774a7ba0ab44d8da4fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/f81549d2c5fdee8d711c9ab3c7e7362353ea5853", - "reference": "f81549d2c5fdee8d711c9ab3c7e7362353ea5853", + "url": "https://api.github.com/repos/symfony/console/zipball/a4bd0f02ea156cf7b5138774a7ba0ab44d8da4fe", + "reference": "a4bd0f02ea156cf7b5138774a7ba0ab44d8da4fe", "shasum": "" }, "require": { @@ -2545,7 +2594,7 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2017-10-01T21:00:16+00:00" + "time": "2018-01-03T07:36:31+00:00" }, { "name": "symfony/debug", From 78cb2de9b6767bd005a00b466a948df3802c78aa Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Fri, 19 Jan 2018 22:17:15 -0500 Subject: [PATCH 207/248] Going to use rrule.js instead --- api/BusinessLogic/Calendar/CalendarEvent.php | 2 + .../Calendar/CalendarController.php | 2 + api/composer.json | 3 +- api/composer.lock | 46 +------------------ 4 files changed, 6 insertions(+), 47 deletions(-) diff --git a/api/BusinessLogic/Calendar/CalendarEvent.php b/api/BusinessLogic/Calendar/CalendarEvent.php index 91a45761..9e974b01 100644 --- a/api/BusinessLogic/Calendar/CalendarEvent.php +++ b/api/BusinessLogic/Calendar/CalendarEvent.php @@ -18,4 +18,6 @@ class CalendarEvent extends AbstractEvent { public $reminderValue; public $reminderUnits; + + public $recurringRule; } \ No newline at end of file diff --git a/api/Controllers/Calendar/CalendarController.php b/api/Controllers/Calendar/CalendarController.php index 0953b585..2c84270a 100644 --- a/api/Controllers/Calendar/CalendarController.php +++ b/api/Controllers/Calendar/CalendarController.php @@ -15,6 +15,7 @@ use BusinessLogic\Security\UserPrivilege; use BusinessLogic\ValidationModel; use Controllers\JsonRetriever; use DataAccess\Settings\ModsForHeskSettingsGateway; +use RRule\RRule; class CalendarController extends \BaseClass { function get() { @@ -117,6 +118,7 @@ class CalendarController extends \BaseClass { $event->categoryId = Helpers::safeArrayGet($json, 'categoryId'); $event->reminderValue = Helpers::safeArrayGet($json, 'reminderValue'); $event->reminderUnits = ReminderUnit::getByName(Helpers::safeArrayGet($json, 'reminderUnits')); + $event->recurringRule = Helpers::safeArrayGet($json, 'recurringRule'); return $event; } diff --git a/api/composer.json b/api/composer.json index c7215460..b91d34b5 100644 --- a/api/composer.json +++ b/api/composer.json @@ -17,7 +17,6 @@ "phpmailer/phpmailer": "^5.2", "mailgun/mailgun-php": "1.7.2", "mike-koch/php-di": "4.4.11", - "doctrine/annotations": "1.2", - "rlanvin/php-rrule": "1.6.0" + "doctrine/annotations": "1.2" } } diff --git a/api/composer.lock b/api/composer.lock index 14ef8855..f882508e 100644 --- a/api/composer.lock +++ b/api/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "271633d6be53dff4f327e318b49a049f", + "content-hash": "cba4f6a9b461d9050bd4095da7c79aea", "packages": [ { "name": "container-interop/container-interop", @@ -690,50 +690,6 @@ ], "time": "2017-02-14T16:28:37+00:00" }, - { - "name": "rlanvin/php-rrule", - "version": "v1.6.0", - "source": { - "type": "git", - "url": "https://github.com/rlanvin/php-rrule.git", - "reference": "c543bda87d2f8b6c490210f1a6e2bf0d79162a4a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/rlanvin/php-rrule/zipball/c543bda87d2f8b6c490210f1a6e2bf0d79162a4a", - "reference": "c543bda87d2f8b6c490210f1a6e2bf0d79162a4a", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "<6" - }, - "suggest": { - "ext-intl": "Intl extension is needed for humanReadable()" - }, - "type": "library", - "autoload": { - "psr-4": { - "RRule\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Lightweight and fast recurrence rules for PHP (RFC 5545)", - "homepage": "https://github.com/rlanvin/php-rrule", - "keywords": [ - "date", - "ical", - "recurrence", - "recurring", - "rrule" - ], - "time": "2017-10-11T12:23:40+00:00" - }, { "name": "symfony/event-dispatcher", "version": "v2.8.33", From 770a01a970a2c44ff7be6cec3105b35e13c63c39 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Sun, 21 Jan 2018 22:03:54 -0500 Subject: [PATCH 208/248] Add status to ticket popover on calendar --- admin/calendar.php | 10 +++++++++- api/BusinessLogic/Calendar/CalendarEvent.php | 2 -- api/BusinessLogic/Calendar/TicketEvent.php | 2 ++ api/Controllers/Calendar/CalendarController.php | 3 ++- api/DataAccess/Calendar/CalendarGateway.php | 6 +++++- .../mods-for-hesk-calendar-admin-readonly.js | 16 ++++++++++++---- js/calendar/mods-for-hesk-calendar.js | 15 ++++++++++++--- 7 files changed, 42 insertions(+), 12 deletions(-) diff --git a/admin/calendar.php b/admin/calendar.php index 905cc81d..4fc81a57 100644 --- a/admin/calendar.php +++ b/admin/calendar.php @@ -545,6 +545,10 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php');
    +
    + + +
    @@ -560,7 +564,11 @@ echo mfh_get_hidden_fields_for_language(array('error_loading_events', 'event_updated', 'error_updating_event', 'ticket_due_date_updated', - 'error_updating_ticket_due_date')); + 'error_updating_ticket_due_date', + 'critical', + 'high', + 'medium', + 'low')); ?>

    diff --git a/api/BusinessLogic/Calendar/CalendarEvent.php b/api/BusinessLogic/Calendar/CalendarEvent.php index 9e974b01..91a45761 100644 --- a/api/BusinessLogic/Calendar/CalendarEvent.php +++ b/api/BusinessLogic/Calendar/CalendarEvent.php @@ -18,6 +18,4 @@ class CalendarEvent extends AbstractEvent { public $reminderValue; public $reminderUnits; - - public $recurringRule; } \ No newline at end of file diff --git a/api/BusinessLogic/Calendar/TicketEvent.php b/api/BusinessLogic/Calendar/TicketEvent.php index e2e7102f..95bf621d 100644 --- a/api/BusinessLogic/Calendar/TicketEvent.php +++ b/api/BusinessLogic/Calendar/TicketEvent.php @@ -15,4 +15,6 @@ class TicketEvent extends AbstractEvent { public $owner; public $priority; + + public $status; } \ No newline at end of file diff --git a/api/Controllers/Calendar/CalendarController.php b/api/Controllers/Calendar/CalendarController.php index 2c84270a..4b7ab9cd 100644 --- a/api/Controllers/Calendar/CalendarController.php +++ b/api/Controllers/Calendar/CalendarController.php @@ -5,6 +5,7 @@ namespace Controllers\Calendar; use BusinessLogic\Calendar\CalendarEvent; use BusinessLogic\Calendar\CalendarHandler; +use BusinessLogic\Calendar\RecurringRule; use BusinessLogic\Calendar\ReminderUnit; use BusinessLogic\Calendar\SearchEventsFilter; use BusinessLogic\Categories\CategoryHandler; @@ -16,6 +17,7 @@ use BusinessLogic\ValidationModel; use Controllers\JsonRetriever; use DataAccess\Settings\ModsForHeskSettingsGateway; use RRule\RRule; +use RRule\RSet; class CalendarController extends \BaseClass { function get() { @@ -118,7 +120,6 @@ class CalendarController extends \BaseClass { $event->categoryId = Helpers::safeArrayGet($json, 'categoryId'); $event->reminderValue = Helpers::safeArrayGet($json, 'reminderValue'); $event->reminderUnits = ReminderUnit::getByName(Helpers::safeArrayGet($json, 'reminderUnits')); - $event->recurringRule = Helpers::safeArrayGet($json, 'recurringRule'); return $event; } diff --git a/api/DataAccess/Calendar/CalendarGateway.php b/api/DataAccess/Calendar/CalendarGateway.php index ddb263e6..1693e23d 100644 --- a/api/DataAccess/Calendar/CalendarGateway.php +++ b/api/DataAccess/Calendar/CalendarGateway.php @@ -86,13 +86,16 @@ class CalendarGateway extends CommonDao { $sql = "SELECT `tickets`.`id` AS `id`, `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` < '{$currentDate}' THEN 1 ELSE 0 END AS `overdue`, `owner`.`name` AS `owner_name`, `tickets`.`owner` AS `owner_id`, - `tickets`.`priority` AS `priority` + `tickets`.`priority` AS `priority`, `text_to_status_xref`.`text` AS `status_name` FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "tickets` AS `tickets` INNER JOIN `" . hesk_dbEscape($heskSettings['db_pfix']) . "categories` AS `categories` ON `categories`.`id` = `tickets`.`category` AND `categories`.`usage` <> 2 LEFT JOIN `" . hesk_dbEscape($heskSettings['db_pfix']) . "users` AS `owner` ON `tickets`.`owner` = `owner`.`id` + LEFT JOIN `" . hesk_dbEscape($heskSettings['db_pfix']) . "text_to_status_xref` AS `text_to_status_xref` + ON `tickets`.`status` = `text_to_status_xref`.`status_id` + AND `text_to_status_xref`.`language` = '" . hesk_dbEscape($heskSettings['language']) . "' WHERE `due_date` >= {$startTimeSql} AND `due_date` <= {$endTimeSql} AND `status` IN (SELECT `id` FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "statuses` WHERE `IsClosed` = 0) @@ -129,6 +132,7 @@ class CalendarGateway extends CommonDao { $event->displayBorder = Helpers::boolval($row['display_border']); $event->owner = $row['owner_name']; $event->priority = Priority::getByValue($row['priority']); + $event->status = $row['status_name']; $events[] = $event; } diff --git a/js/calendar/mods-for-hesk-calendar-admin-readonly.js b/js/calendar/mods-for-hesk-calendar-admin-readonly.js index 7290189e..51f2b763 100644 --- a/js/calendar/mods-for-hesk-calendar-admin-readonly.js +++ b/js/calendar/mods-for-hesk-calendar-admin-readonly.js @@ -62,7 +62,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(); @@ -125,7 +126,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, @@ -141,8 +148,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.js b/js/calendar/mods-for-hesk-calendar.js index 48fac68f..7551e2d7 100644 --- a/js/calendar/mods-for-hesk-calendar.js +++ b/js/calendar/mods-for-hesk-calendar.js @@ -66,7 +66,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).end() + .find('.popover-status span').text(event.status).end(); } else { if (event.allDay) { endDate = event.end.clone(); @@ -298,6 +299,13 @@ function removeFromCalendar(id) { } function buildEvent(id, dbObject) { + 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, @@ -315,8 +323,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 }; } From 1a66485d48478f1b228d83920adef0ae107368ab Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Tue, 23 Jan 2018 22:14:25 -0500 Subject: [PATCH 209/248] Working on adding audit trail to events --- .../Calendar/CalendarHandler.php | 35 +++++++++++++++++-- .../Tickets/AuditTrailEntityType.php | 1 + .../Calendar/CalendarController.php | 3 +- api/DataAccess/Calendar/CalendarGateway.php | 32 +++++++++++++++++ language/en/text.php | 4 +++ 5 files changed, 71 insertions(+), 4 deletions(-) diff --git a/api/BusinessLogic/Calendar/CalendarHandler.php b/api/BusinessLogic/Calendar/CalendarHandler.php index ad5a1775..2c4967d1 100644 --- a/api/BusinessLogic/Calendar/CalendarHandler.php +++ b/api/BusinessLogic/Calendar/CalendarHandler.php @@ -3,14 +3,20 @@ namespace BusinessLogic\Calendar; +use BusinessLogic\DateTimeHelpers; use BusinessLogic\Security\UserContext; +use BusinessLogic\Tickets\AuditTrailEntityType; +use DataAccess\AuditTrail\AuditTrailGateway; use DataAccess\Calendar\CalendarGateway; class CalendarHandler extends \BaseClass { private $calendarGateway; + private $auditTrailGateway; - public function __construct(CalendarGateway $calendarGateway) { + public function __construct(CalendarGateway $calendarGateway, + AuditTrailGateway $auditTrailGateway) { $this->calendarGateway = $calendarGateway; + $this->auditTrailGateway = $auditTrailGateway; } public function getEventsForStaff($searchEventsFilter, $heskSettings) { @@ -37,10 +43,25 @@ class CalendarHandler extends \BaseClass { throw new \Exception("Expected exactly 1 event, found: " . count($events)); } - return $events[0]; + $event = $events[0]; + + $this->auditTrailGateway->insertAuditTrailRecord($event->id, + AuditTrailEntityType::CALENDAR_EVENT, + 'audit_event_updated', + DateTimeHelpers::heskDate($heskSettings), + array(0 => $userContext->name . ' (' . $userContext->username . ')'), $heskSettings); + + return $event; } + /** + * @param $calendarEvent CalendarEvent + * @param $userContext UserContext + * @param $heskSettings array + * @return AbstractEvent + * @throws \Exception + */ public function createEvent($calendarEvent, $userContext, $heskSettings) { $this->calendarGateway->createEvent($calendarEvent, $userContext, $heskSettings); @@ -54,7 +75,15 @@ class CalendarHandler extends \BaseClass { throw new \Exception("Expected exactly 1 event, found: " . count($events)); } - return $events[0]; + $event = $events[0]; + + $this->auditTrailGateway->insertAuditTrailRecord($event->id, + AuditTrailEntityType::CALENDAR_EVENT, + 'audit_event_created', + DateTimeHelpers::heskDate($heskSettings), + array(0 => $userContext->name . ' (' . $userContext->username . ')'), $heskSettings); + + return $event; } public function deleteEvent($id, $userContext, $heskSettings) { diff --git a/api/BusinessLogic/Tickets/AuditTrailEntityType.php b/api/BusinessLogic/Tickets/AuditTrailEntityType.php index feea16f6..dafcad42 100644 --- a/api/BusinessLogic/Tickets/AuditTrailEntityType.php +++ b/api/BusinessLogic/Tickets/AuditTrailEntityType.php @@ -5,4 +5,5 @@ namespace BusinessLogic\Tickets; class AuditTrailEntityType extends \BaseClass { const TICKET = 'TICKET'; + const CALENDAR_EVENT = 'CALENDAR_EVENT'; } \ No newline at end of file diff --git a/api/Controllers/Calendar/CalendarController.php b/api/Controllers/Calendar/CalendarController.php index 4b7ab9cd..0440f54a 100644 --- a/api/Controllers/Calendar/CalendarController.php +++ b/api/Controllers/Calendar/CalendarController.php @@ -69,6 +69,7 @@ class CalendarController extends \BaseClass { function post() { /* @var $userContext UserContext */ + /* @var $hesk_settings array */ global $applicationContext, $hesk_settings, $userContext; $json = JsonRetriever::getJsonData(); @@ -78,7 +79,7 @@ class CalendarController extends \BaseClass { /* @var $calendarHandler CalendarHandler */ $calendarHandler = $applicationContext->get(CalendarHandler::clazz()); - return output($calendarHandler->createEvent($event, $userContext, $hesk_settings)); + return output($calendarHandler->createEvent($event, $userContext, $hesk_settings), 201); } function put($id) { diff --git a/api/DataAccess/Calendar/CalendarGateway.php b/api/DataAccess/Calendar/CalendarGateway.php index 1693e23d..705651d0 100644 --- a/api/DataAccess/Calendar/CalendarGateway.php +++ b/api/DataAccess/Calendar/CalendarGateway.php @@ -10,6 +10,8 @@ use BusinessLogic\Calendar\SearchEventsFilter; use BusinessLogic\Calendar\TicketEvent; use BusinessLogic\Helpers; use BusinessLogic\Security\UserContext; +use BusinessLogic\Tickets\AuditTrail; +use BusinessLogic\Tickets\AuditTrailEntityType; use Core\Constants\Priority; use DataAccess\CommonDao; use DataAccess\Logging\LoggingGateway; @@ -73,6 +75,36 @@ class CalendarGateway extends CommonDao { $event->reminderValue = $row['reminder_value'] === null ? null : floatval($row['reminder_value']); $event->reminderUnits = $row['reminder_unit'] === null ? null : ReminderUnit::getByValue($row['reminder_unit']); + $auditTrailSql = "SELECT `at`.`id` AS `id`, `at`.`entity_id`, `at`.`language_key`, `at`.`date`, + `values`.`replacement_index`, `values`.`replacement_values` + FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "audit_trail` AS `at` + INNER JOIN `" . hesk_dbEscape($heskSettings['db_pfix']) . "audit_trail_to_replacement_values` AS `values` + ON `at`.`id` = `values`.`audit_trail_id` + WHERE `entity_id` = " . intval($event->id) . " + AND `entity_type` = '" . AuditTrailEntityType::CALENDAR_EVENT . "'"; + $auditTrailRs = hesk_dbFetchAssoc($auditTrailSql); + $auditTrailEntry = null; + while ($row = hesk_dbFetchAssoc($rs)) { + if ($auditTrailEntry == null || intval($auditTrailEntry['id']) !== intval($row['id'])) { + if ($auditTrailEntry !== null) { + //$audit_records[] = $auditTrailEntry; + } + + $auditTrailEntry = new AuditTrail(); + $auditTrailEntry->id = $row['id']; + $auditTrailEntry->entityId = $row['entity_id']; + $auditTrailEntry->entityType = AuditTrailEntityType::CALENDAR_EVENT; + $auditTrailEntry->languageKey = $row['language_key']; + $auditTrailEntry->date = $row['date']; + $auditTrailEntry->replacementValues = array(); + } + $auditTrailEntry->replacementValues[intval($row['replacement_index'])] = $row['replacement_value']; + } + + if ($auditTrailEntry !== null) { + //$event->auditTrail[] = $audiTrailEntry; + } + $events[] = $event; } diff --git a/language/en/text.php b/language/en/text.php index e55cd0e4..6f5e9838 100644 --- a/language/en/text.php +++ b/language/en/text.php @@ -2214,5 +2214,9 @@ $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'; + // DO NOT CHANGE BELOW if (!defined('IN_SCRIPT')) die('PHP syntax OK!'); From ad63bcac0298a35c182cb87e168ba7f7e6a963b5 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Wed, 24 Jan 2018 13:01:23 -0500 Subject: [PATCH 210/248] Fixed a couple issues with the audit trail changes --- api/BusinessLogic/Calendar/CalendarEvent.php | 5 +++++ api/DataAccess/Calendar/CalendarGateway.php | 17 +++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/api/BusinessLogic/Calendar/CalendarEvent.php b/api/BusinessLogic/Calendar/CalendarEvent.php index 91a45761..967ae3ea 100644 --- a/api/BusinessLogic/Calendar/CalendarEvent.php +++ b/api/BusinessLogic/Calendar/CalendarEvent.php @@ -3,6 +3,8 @@ namespace BusinessLogic\Calendar; +use BusinessLogic\Tickets\AuditTrail; + class CalendarEvent extends AbstractEvent { public $type = 'CALENDAR'; @@ -18,4 +20,7 @@ class CalendarEvent extends AbstractEvent { public $reminderValue; public $reminderUnits; + + /* @var $auditTrail AuditTrail[] */ + public $auditTrail = array(); } \ No newline at end of file diff --git a/api/DataAccess/Calendar/CalendarGateway.php b/api/DataAccess/Calendar/CalendarGateway.php index 705651d0..3561631c 100644 --- a/api/DataAccess/Calendar/CalendarGateway.php +++ b/api/DataAccess/Calendar/CalendarGateway.php @@ -76,23 +76,24 @@ class CalendarGateway extends CommonDao { $event->reminderUnits = $row['reminder_unit'] === null ? null : ReminderUnit::getByValue($row['reminder_unit']); $auditTrailSql = "SELECT `at`.`id` AS `id`, `at`.`entity_id`, `at`.`language_key`, `at`.`date`, - `values`.`replacement_index`, `values`.`replacement_values` + `values`.`replacement_index`, `values`.`replacement_value` FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "audit_trail` AS `at` INNER JOIN `" . hesk_dbEscape($heskSettings['db_pfix']) . "audit_trail_to_replacement_values` AS `values` ON `at`.`id` = `values`.`audit_trail_id` WHERE `entity_id` = " . intval($event->id) . " AND `entity_type` = '" . AuditTrailEntityType::CALENDAR_EVENT . "'"; - $auditTrailRs = hesk_dbFetchAssoc($auditTrailSql); + $auditTrailRs = hesk_dbQuery($auditTrailSql); + /* @var $auditTrailEntry AuditTrail */ $auditTrailEntry = null; - while ($row = hesk_dbFetchAssoc($rs)) { - if ($auditTrailEntry == null || intval($auditTrailEntry['id']) !== intval($row['id'])) { + while ($row = hesk_dbFetchAssoc($auditTrailRs)) { + if ($auditTrailEntry == null || intval($auditTrailEntry->id) !== intval($row['id'])) { if ($auditTrailEntry !== null) { - //$audit_records[] = $auditTrailEntry; + $event->auditTrail[] = $auditTrailEntry; } $auditTrailEntry = new AuditTrail(); - $auditTrailEntry->id = $row['id']; - $auditTrailEntry->entityId = $row['entity_id']; + $auditTrailEntry->id = intval($row['id']); + $auditTrailEntry->entityId = intval($row['entity_id']); $auditTrailEntry->entityType = AuditTrailEntityType::CALENDAR_EVENT; $auditTrailEntry->languageKey = $row['language_key']; $auditTrailEntry->date = $row['date']; @@ -102,7 +103,7 @@ class CalendarGateway extends CommonDao { } if ($auditTrailEntry !== null) { - //$event->auditTrail[] = $audiTrailEntry; + $event->auditTrail[] = $auditTrailEntry; } $events[] = $event; From 401e335e5f976045a2c82e97977d48aad79ae8fe Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Wed, 24 Jan 2018 22:00:10 -0500 Subject: [PATCH 211/248] Adding history to modal --- admin/calendar.php | 299 ++++++++++++++++++++++++--------------------- 1 file changed, 163 insertions(+), 136 deletions(-) diff --git a/admin/calendar.php b/admin/calendar.php index 4fc81a57..b9be4bab 100644 --- a/admin/calendar.php +++ b/admin/calendar.php @@ -334,147 +334,174 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php');
    %s%s
    @@ -283,13 +321,13 @@ function run_setting_check($setting_name, $minimumValidationNumber) { if ($startingValidationNumber < $minimumValidationNumber) { $checks = 'SKIPPED'; } else { - $res = run_check("SELECT 1 FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "settings` WHERE `Key` = '{$setting_name}'"); - $checks = hesk_dbNumRows($res) > 0; + $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, $checks); - return $checks !== false; + return $checks; } function run_table_check($table_name, $minimumValidationNumber) { @@ -320,10 +358,10 @@ function run_column_check($table_name, $column_name, $minimumValidationNumber) { $checks); } - return $checks !== 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')) { @@ -332,14 +370,23 @@ function run_check($sql) { } $hesk_last_query = $sql; - return @mysqli_query($hesk_db_link, $sql) ? 'PASS' : 'FAIL'; + 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) ? 'PASS' : 'FAIL'; + if ($returnString) { + return $res = @mysql_query($sql, $hesk_db_link) ? 'PASS' : 'FAIL'; + } else { + return $res = @mysql_query($sql, $hesk_db_link); + } } } @@ -355,8 +402,11 @@ function output_result($change_title, $status) { break; case 'SKIPPED': $css_color = 'default'; - $text = ' Skipped'; + $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 a669070e..699e5c9b 100644 --- a/install/index.php +++ b/install/index.php @@ -100,7 +100,7 @@ if (HESK_NEW_VERSION != $hesk_settings['hesk_version']) {
    -

    You need to be running HESK 2.7.3 to install Mods for HESK. You currently have HESK .

    +

    You need to be running HESK to install Mods for HESK. You currently have HESK .

    @@ -201,19 +213,33 @@ function createEditModal($template, $features, $categories)
    - + + '; + elseif (strpos($_SESSION['heskprivileges'], $feature) !== false): ?>
    -
    - +
    From 4bd4eec53d2b02eb37b06fa9383d3ea8a9b31ae0 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Tue, 6 Feb 2018 22:04:16 -0500 Subject: [PATCH 229/248] Starting on fixing permission group creation/edit abilities --- admin/manage_permission_groups.php | 77 ++++++++++++++++-------------- language/en/text.php | 1 + 2 files changed, 42 insertions(+), 36 deletions(-) diff --git a/admin/manage_permission_groups.php b/admin/manage_permission_groups.php index 1e397e29..4220ea3d 100644 --- a/admin/manage_permission_groups.php +++ b/admin/manage_permission_groups.php @@ -137,11 +137,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 +155,12 @@ function createEditModal($template, $features, $categories) diff --git a/language/en/text.php b/language/en/text.php index 0de8b91c..a994fc6c 100644 --- a/language/en/text.php +++ b/language/en/text.php @@ -2238,6 +2238,7 @@ $hesklang['show_event_start_time_help'] = 'Always show the start time on event t $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.'; // DO NOT CHANGE BELOW if (!defined('IN_SCRIPT')) die('PHP syntax OK!'); From 48b28fc3cdfdcb7d27ce39c2599a3947d66a5397 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Wed, 7 Feb 2018 22:03:55 -0500 Subject: [PATCH 230/248] Only modify permissions the user has access to --- admin/manage_permission_groups.php | 36 +++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/admin/manage_permission_groups.php b/admin/manage_permission_groups.php index 4220ea3d..9eb410a0 100644 --- a/admin/manage_permission_groups.php +++ b/admin/manage_permission_groups.php @@ -365,7 +365,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'); @@ -380,6 +379,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['features']); + $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) . "' From 35ed664dfdef0ea48600b134a8030c08196a35d4 Mon Sep 17 00:00:00 2001 From: Mike Koch Date: Sun, 11 Feb 2018 22:00:44 -0500 Subject: [PATCH 231/248] Only allow users to modify permissions that they have access to --- admin/manage_permission_groups.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/admin/manage_permission_groups.php b/admin/manage_permission_groups.php index 9eb410a0..9b9449ac 100644 --- a/admin/manage_permission_groups.php +++ b/admin/manage_permission_groups.php @@ -198,7 +198,7 @@ function createEditModal($template, $features, $categories) $disabled = ' disabled'; }?> - +