diff --git a/admin/admin_settings.php b/admin/admin_settings.php index 7d3f1d22..0203eb6a 100644 --- a/admin/admin_settings.php +++ b/admin/admin_settings.php @@ -552,6 +552,14 @@ if (defined('HESK_DEMO')) {
  • +
  • + + + + + +
  • @@ -1970,6 +1978,85 @@ if (defined('HESK_DEMO')) { + +
    +
    +
    +
    + +
    +

    +

    +
    '; + ?> +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    + +
    '); + /* Get all the required files and functions */ require(HESK_PATH . 'hesk_settings.inc.php'); require(HESK_PATH . 'inc/common.inc.php'); @@ -550,7 +552,7 @@ require_once(HESK_PATH . 'inc/headerAdmin.inc.php'); /* List of categories */ $orderBy = $modsForHesk_settings['category_order_column']; -$result = hesk_dbQuery("SELECT `id`,`name` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "categories` ORDER BY `" . $orderBy . "` ASC"); +$result = hesk_dbQuery("SELECT `id`,`name` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "categories` WHERE `usage` <> 2 ORDER BY `" . $orderBy . "` ASC"); $categories_options = ''; while ($row = hesk_dbFetchAssoc($result)) { $selected = ''; @@ -657,15 +659,47 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php'); ?>
  • -
    +
  • -
    +
  • -
    +
    +
    + + +
    + +
    + +
  • +
  • +
  • + +
    +

    +

    +

    +
    1 ORDER BY `cat_order`"); +$categories = []; +while ($row = hesk_dbFetchAssoc($rs)) { + if (!$_SESSION['isadmin'] && !in_array($row['id'], $_SESSION['categories'])) { + continue; + } + + $row['css_style'] = $row['color'] == null ? 'color: black; border: solid 1px #000' : 'background: ' . $row['color']; + $categories[] = $row; +} + +/* Print header */ +require_once(HESK_PATH . 'inc/headerAdmin.inc.php'); + +/* Print main manage users page */ +require_once(HESK_PATH . 'inc/show_admin_nav.inc.php'); +?> + +
    +
    +
    +
    +

    +
    +
    +
      + +
    • +
      + +
      + + + +
    • + +
    +
    +
    +
    +
    +
    +
    +

    + + + + + +

    +
    +
    +
    +
    +
    +
    +
    + + +
    + +
    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    + 'month', + 1 => 'agendaWeek', + 2 => 'agendaDay', + ); + echo $view_array[$_SESSION['default_calendar_view']]; + ?> +

    +
    +
    - -
    -
    - +
    +
    + +
    +
    -

    - -

    - : ()

    + ()

    - data-error="" - required> + data-error="" + required>
    @@ -166,7 +156,30 @@ while ($mycat = hesk_dbFetchAssoc($res)) {
    - + +
    + +
    +
    +
    + +
    + +
    +
    +
    +
    -
    -
    -

    - -
    -
    - - -
    - -
    -
    -
    - - -
    - - data-error="" - required> -
    -
    -
    -
    - - - -
    -
    -
    -
    -
    -

    - -
    -
    - - -
    - -
    -
    -
    - - -
    - -
    -
    -
    -
    - - - -
    -
    -
    -
    - -
    - +
    + +
    +
    + -

    - -
    - - - - - - - - - - - - +
    + + + + + + + + + + - $tmp = $i ? 'White' : 'Blue'; - $style = 'class="option' . $tmp . 'OFF" onmouseover="this.className=\'option' . $tmp . 'ON\'" onmouseout="this.className=\'option' . $tmp . 'OFF\'"'; - $i = $i ? 0 : 1; + '; - } else { - $remove_code = ' '; - } - - /* Is category private or public? */ - if ($mycat['type']) { - $type_code = ''; - } else { - $type_code = ''; - } - - /* Is auto assign enabled? */ - if ($hesk_settings['autoassign']) { - if ($mycat['autoassign']) { - $autoassign_code = ''; - } else { - $autoassign_code = ''; + $res = hesk_dbQuery('SELECT COUNT(*) AS `cnt`, `category` FROM `' . hesk_dbEscape($hesk_settings['db_pfix']) . 'tickets` GROUP BY `category`'); + while ($tmp = hesk_dbFetchAssoc($res)) { + $tickets_all[$tmp['category']] = $tmp['cnt']; + $tickets_total += $tmp['cnt']; } - } else { - $autoassign_code = ''; - } - echo ' - - - + /* Get list of categories */ + $res = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "categories` ORDER BY `" . $orderBy . "` ASC"); + $usersRes = hesk_dbQuery("SELECT * FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "users` WHERE `isadmin` = '0' ORDER BY `name` ASC"); + $users = array(); + while ($userRow = hesk_dbFetchAssoc($usersRes)) { + array_push($users, $userRow); + } + + $i = 1; + $j = 0; + $num = hesk_dbNumRows($res); + + $usage = array( + 0 => ' + ', + 1 => '', + 2 => ' ' + ); + + while ($mycat = hesk_dbFetchAssoc($res)) { + $j++; + + if (isset($_SESSION['selcat2']) && $mycat['id'] == $_SESSION['selcat2']) { + $color = 'admin_green'; + unset($_SESSION['selcat2']); + } else { + $color = $i ? 'admin_white' : 'admin_gray'; + } + + $tmp = $i ? 'White' : 'Blue'; + $style = ''; + if ($mycat['color'] == null) { + $style .= 'color: black; border: solid 1px #000'; + } else { + $style .= 'background: ' . $mycat['color']; + } + $i = $i ? 0 : 1; + + /* Number of tickets and graph width */ + $all = isset($tickets_all[$mycat['id']]) ? $tickets_all[$mycat['id']] : 0; + $width_all = 0; + if ($tickets_total && $all) { + $width_all = round(($all / $tickets_total) * 100); + } + + /* Deleting category with ID 1 (default category) is not allowed */ + if ($mycat['id'] == 1) { + $remove_code = ' '; + } else { + $remove_code = ' '; + } + + /* Is category private or public? */ + if ($mycat['type']) { + $type_code = ''; + } else { + $type_code = ''; + } + + /* Is auto assign enabled? */ + if ($hesk_settings['autoassign']) { + if ($mycat['autoassign']) { + $autoassign_code = ''; + } else { + $autoassign_code = ''; + } + } else { + $autoassign_code = ''; + } + + echo ' + + + - + + + } + } + echo ''; + echo $remove_code . ' '; - } // End while + } // End while - ?> -
    ' . $mycat['id'] . '' . $mycat['name'] . '
    ' . $mycat['id'] . '' . $mycat['name'] . ' ' . $priorities[$mycat['priority']]['formatted'] . ' ' . $all . ' -
    -
    - 40% Complete (success) +
    +
    + 40% Complete (success) +
    -
    ' . output_user_dropdown($mycat['id'], $mycat['manager'], $users) . '' . $usage[$mycat['usage']] . '' . get_manager($mycat['manager'], $users) . ' ' . $autoassign_code . ' ' . $type_code . ' '; - if ($orderBy != 'name' && $num > 1) { - if ($j == 1) { - echo ' '; - } elseif ($j == $num) { - echo ' '; - } else { - echo ' + if ($orderBy != 'name' && $num > 1) { + if ($j == 1) { + echo '  '; + } elseif ($j == $num) { + echo ' '; + } else { + echo '   '; - } - } - - echo $remove_code . '
    + ?> + +
    +
    + + + ' . stripslashes($catname) . '', $_SERVER['PHP_SELF'], 'SUCCESS'); + hesk_process_messages(sprintf($hesklang['category_updated'], stripslashes($catname)), $_SERVER['PHP_SELF'], 'SUCCESS'); } // End rename_cat() @@ -708,59 +777,42 @@ function toggle_type() } // End toggle_type() -function output_user_dropdown($catId, $selectId, $userArray) +function output_user_dropdown($userArray) { global $hesklang; if (!hesk_checkPermission('can_set_manager', 0)) { foreach ($userArray as $user) { if ($user['id'] == $selectId) { - return '

    ' . $user['name'] . '

    '; + return '

    ' . $user['name'] . '

    '; } } - return '

    ' . $hesklang['no_manager'] . '

    '; + return '

    ' . $hesklang['no_manager'] . '

    '; } else { - $dropdownMarkup = ' '; foreach ($userArray as $user) { - $select = $selectId == $user['id'] ? 'selected' : ''; - $dropdownMarkup .= ''; + $dropdownMarkup .= ''; } $dropdownMarkup .= ''; - return '
    - - - ' . $dropdownMarkup . ' -
    '; + return $dropdownMarkup; } } -function change_manager() -{ - global $hesklang, $hesk_settings; +function get_manager($user_id, $user_array) { + global $hesklang; - $catid = hesk_POST('catid'); - $newManagerId = hesk_POST('managerid'); - - hesk_dbQuery('UPDATE `' . hesk_dbEscape($hesk_settings['db_pfix']) . 'categories` SET `manager` = ' . intval($newManagerId) . ' WHERE `id` = ' . intval($catid)); - if (hesk_dbAffectedRows() != 1) { - hesk_process_messages($hesklang['int_error'] . ': ' . $hesklang['cat_not_found'], './manage_categories.php'); - } - if ($newManagerId == 0) { - // There is no new manager. - return; - } - // Add the category to the user's categories list, if not already present - $currentCatRs = hesk_dbQuery('SELECT `categories` FROM `' . hesk_dbEscape($hesk_settings['db_pfix']) . 'users` WHERE `id` = ' . intval($newManagerId)); - $currentCategories = hesk_dbFetchAssoc($currentCatRs); - $categories = explode(',', $currentCategories['categories']); - if (!in_array($catid, $categories)) { - hesk_dbQuery('UPDATE `' . hesk_dbEscape($hesk_settings['db_pfix']) . 'users` SET `categories` = \'' . $currentCategories['categories'] . ',' . $catid . '\' WHERE `id` = ' . intval($newManagerId)); + if ($user_id == 0) { + return $hesklang['no_manager']; } - hesk_process_messages($hesklang['manager_updated'], './manage_categories.php', 'SUCCESS'); + foreach ($user_array as $user) { + if ($user['id'] == $user_id) { + return $user['name']; + } + } } ?> diff --git a/admin/manage_users.php b/admin/manage_users.php index 4e39212b..f228cc34 100644 --- a/admin/manage_users.php +++ b/admin/manage_users.php @@ -49,6 +49,13 @@ hesk_checkPermission('can_man_users'); /* Possible user features */ $hesk_settings['features'] = hesk_getFeatureArray(); +$modsForHesk_settings = mfh_getSettings(); +$calendar_view_array = array( + 'month' => 0, + 'agendaWeek' => 1, + 'agendaDay' => 2, +); +$default_view = $calendar_view_array[$modsForHesk_settings['default_calendar_view']]; /* Set default values */ $default_userdata = array( @@ -78,6 +85,7 @@ $default_userdata = array( 'notify_customer_new' => 1, 'notify_customer_reply' => 1, 'show_suggested' => 1, + 'default_calendar_view' => $default_view, // Notifications 'notify_new_unassigned' => 1, @@ -88,9 +96,9 @@ $default_userdata = array( 'notify_note' => 1, 'notify_pm' => 1, 'notify_note_unassigned' => 1, + 'notify_overdue_unassigned' => 0, ); -$modsForHesk_settings = mfh_getSettings(); /* A list of all categories */ $orderBy = $modsForHesk_settings['category_order_column']; $hesk_settings['categories'] = array(); @@ -515,8 +523,10 @@ function new_user() `notify_pm`, `notify_note`, `notify_note_unassigned`, + `notify_overdue_unassigned`, `autorefresh`, - `permission_template`) VALUES ( + `permission_template`, + `default_calendar_view`) VALUES ( '" . hesk_dbEscape($myuser['user']) . "', '" . hesk_dbEscape($myuser['pass']) . "', '" . intval($myuser['isadmin']) . "', @@ -539,8 +549,10 @@ function new_user() '" . ($myuser['notify_pm']) . "', '" . ($myuser['notify_note']) . "', '" . ($myuser['notify_note_unassigned']) . "', + '" . ($myuser['notify_overdue_unassigned']) . "', " . intval($myuser['autorefresh']) . ", - " . intval($myuser['template']) . ")"); + " . intval($myuser['template']) . ", + " . intval($myuser['default_calendar_view']) . ")"); $_SESSION['seluser'] = hesk_dbInsertID(); @@ -581,6 +593,7 @@ function update_user() $myuser['notify_pm'] = 0; $myuser['notify_note'] = 0; $myuser['notify_note_unassigned'] = 0; + $myuser['notify_overdue_unassigned'] = 0; } /* Check for duplicate usernames */ @@ -662,8 +675,10 @@ function update_user() `notify_pm`='" . ($myuser['notify_pm']) . "', `notify_note`='" . ($myuser['notify_note']) . "', `notify_note_unassigned`='" . ($myuser['notify_note_unassigned']) . "', + `notify_overdue_unassigned`='" . ($myuser['notify_overdue_unassigned']) . "', `autorefresh`=" . intval($myuser['autorefresh']) . ", - `permission_template`=" . intval($myuser['template']) . " + `permission_template`=" . intval($myuser['template']) . ", + `default_calendar_view`=" . intval($myuser['default_calendar_view']) . " WHERE `id`='" . intval($myuser['id']) . "' LIMIT 1"); // If they are now inactive, remove any manager rights @@ -764,6 +779,7 @@ function hesk_validateUserInfo($pass_required = 1, $redirect_to = './manage_user $myuser['notify_customer_new'] = isset($_POST['notify_customer_new']) ? 1 : 0; $myuser['notify_customer_reply'] = isset($_POST['notify_customer_reply']) ? 1 : 0; $myuser['show_suggested'] = isset($_POST['show_suggested']) ? 1 : 0; + $myuser['default_calendar_view'] = hesk_POST('default-calendar-view', 0); /* Notifications */ $myuser['notify_new_unassigned'] = empty($_POST['notify_new_unassigned']) ? 0 : 1; @@ -774,6 +790,7 @@ function hesk_validateUserInfo($pass_required = 1, $redirect_to = './manage_user $myuser['notify_note'] = empty($_POST['notify_note']) ? 0 : 1; $myuser['notify_pm'] = empty($_POST['notify_pm']) ? 0 : 1; $myuser['notify_note_unassigned'] = empty($_POST['notify_note_unassigned']) ? 0 : 1; + $myuser['notify_overdue_unassigned'] = empty($_POST['notify_overdue_unassigned']) ? 0 : 1; /* Save entered info in session so we don't loose it in case of errors */ $_SESSION['userdata'] = $myuser; @@ -890,7 +907,7 @@ function toggle_active() hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "categories` SET `manager` = 0 WHERE `manager` = " . intval($myuser)); $notificationSql = ", `autoassign` = 0, `notify_new_unassigned` = 0, `notify_new_my` = 0, `notify_reply_unassigned` = 0, - `notify_reply_my` = 0, `notify_assigned` = 0, `notify_pm` = 0, `notify_note` = 0, `notify_note_unassigned` = 0"; + `notify_reply_my` = 0, `notify_assigned` = 0, `notify_pm` = 0, `notify_note` = 0, `notify_note_unassigned` = 0, `notify_overdue_unassigned` = 0"; } hesk_dbQuery("UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "users` SET `active` = '" . $active . "'" . $notificationSql . " WHERE `id` = '" . intval($myuser) . "'"); diff --git a/admin/new_ticket.php b/admin/new_ticket.php index d3351af9..4d990d4a 100644 --- a/admin/new_ticket.php +++ b/admin/new_ticket.php @@ -263,7 +263,7 @@ if (!$show['show']) { // List categories $orderByColumn = $modsForHesk_settings['category_order_column']; - $result = hesk_dbQuery('SELECT * FROM `' . hesk_dbEscape($hesk_settings['db_pfix']) . 'categories` ORDER BY `' . $orderByColumn . '` ASC'); + $result = hesk_dbQuery('SELECT * FROM `' . hesk_dbEscape($hesk_settings['db_pfix']) . 'categories` WHERE `usage` <> 2 ORDER BY `' . $orderByColumn . '` ASC'); while ($row = hesk_dbFetchAssoc($result)) { if (isset($_SESSION['as_category']) && $_SESSION['as_category'] == $row['id']) { $selected = ' selected="selected"'; @@ -730,6 +730,15 @@ elseif (hesk_checkPermission('can_man_ticket_tpl', 0)) { } ?> +
    + +
    + + +
    +
    + '; } else { diff --git a/admin/profile.php b/admin/profile.php index 93637154..03841dfc 100644 --- a/admin/profile.php +++ b/admin/profile.php @@ -230,18 +230,25 @@ function update_profile() /* Auto-start ticket timer */ $_SESSION['new']['autostart'] = isset($_POST['autostart']) ? 1 : 0; + /* Default calendar view */ + $_SESSION['new']['default_calendar_view'] = hesk_POST('default-calendar-view', 0); + /* Update auto-refresh time */ $_SESSION['new']['autorefresh'] = isset($_POST['autorefresh']) ? $_POST['autorefresh'] : 0; /* Notifications */ - $_SESSION['new']['notify_new_unassigned'] = empty($_POST['notify_new_unassigned']) || !$can_view_unassigned ? 0 : 1; - $_SESSION['new']['notify_new_my'] = empty($_POST['notify_new_my']) ? 0 : 1; - $_SESSION['new']['notify_reply_unassigned'] = empty($_POST['notify_reply_unassigned']) || !$can_view_unassigned ? 0 : 1; - $_SESSION['new']['notify_reply_my'] = empty($_POST['notify_reply_my']) ? 0 : 1; - $_SESSION['new']['notify_assigned'] = empty($_POST['notify_assigned']) ? 0 : 1; - $_SESSION['new']['notify_note'] = empty($_POST['notify_note']) ? 0 : 1; - $_SESSION['new']['notify_note_unassigned'] = empty($_POST['notify_note_unassigned']) ? 0 : 1; - $_SESSION['new']['notify_pm'] = empty($_POST['notify_pm']) ? 0 : 1; + if (!(!$_SESSION[$session_array]['isadmin'] && isset($_SESSION[$session_array]['heskprivileges']) + && strpos($_SESSION[$session_array]['heskprivileges'], 'can_change_notification_settings') === false)) { + $_SESSION['new']['notify_new_unassigned'] = empty($_POST['notify_new_unassigned']) || !$can_view_unassigned ? 0 : 1; + $_SESSION['new']['notify_new_my'] = empty($_POST['notify_new_my']) ? 0 : 1; + $_SESSION['new']['notify_reply_unassigned'] = empty($_POST['notify_reply_unassigned']) || !$can_view_unassigned ? 0 : 1; + $_SESSION['new']['notify_reply_my'] = empty($_POST['notify_reply_my']) ? 0 : 1; + $_SESSION['new']['notify_assigned'] = empty($_POST['notify_assigned']) ? 0 : 1; + $_SESSION['new']['notify_note'] = empty($_POST['notify_note']) ? 0 : 1; + $_SESSION['new']['notify_note_unassigned'] = empty($_POST['notify_note_unassigned']) ? 0 : 1; + $_SESSION['new']['notify_pm'] = empty($_POST['notify_pm']) ? 0 : 1; + $_SESSION['new']['notify_overdue_unassigned'] = empty($_POST['notify_overdue_unassigned']) ? 0 : 1; + } /* Any errors? */ if (strlen($hesk_error_buffer)) { @@ -272,7 +279,9 @@ function update_profile() `notify_note_unassigned`='" . intval($_SESSION['new']['notify_note_unassigned']) . "', `notify_customer_new`='" . $_SESSION['new']['notify_customer_new'] . "', `notify_customer_reply`='" . $_SESSION['new']['notify_customer_reply'] . "', - `show_suggested`='" . $_SESSION['new']['show_suggested'] . "' + `notify_overdue_unassigned`='" . $_SESSION['new']['notify_overdue_unassigned'] . "', + `show_suggested`='" . $_SESSION['new']['show_suggested'] . "', + `default_calendar_view`=" . intval($_SESSION['new']['default_calendar_view']) . " WHERE `id`='" . intval($_SESSION['id']) . "' LIMIT 1" ); diff --git a/calendar.php b/calendar.php new file mode 100644 index 00000000..71dd1046 --- /dev/null +++ b/calendar.php @@ -0,0 +1,127 @@ + 1 AND `type` = '0' ORDER BY '" . $orderBy . "'"; +$categoryRs = hesk_dbQuery($categorySql); +while ($row = hesk_dbFetchAssoc($categoryRs)) +{ + $row['css_style'] = $row['color'] == null ? 'color: black; border: solid 1px #000' : 'background: ' . $row['color']; + $categories[] = $row; +} + +require_once(HESK_PATH . 'inc/header.inc.php'); +?> + +
    +
    +
    +
    +

    +
    +
    +
      + +
    • +
      + +
      + + + +
    • + +
    +
    +
    +
    +
    +
    +
    +

    + +

    +
    +
    +
    +
    +
    +
    +
    + +
    +

    +

    +

    +
    \ No newline at end of file diff --git a/cron/calendar_reminders.php b/cron/calendar_reminders.php new file mode 100644 index 00000000..1cd8d710 --- /dev/null +++ b/cron/calendar_reminders.php @@ -0,0 +1,182 @@ +#!/usr/bin/php -q +>>>>>> DEMO MODE IS ENABLED. CRON JOBS CANNOT BE EXECUTED WHILE IN DEMO MODE! <<<<<<<'; + die(); +} + +if (hesk_check_maintenance(false)) { + // If Debug mode is ON show "Maintenance mode" message + $message = $hesk_settings['debug_mode'] ? $hesklang['mm1'] : ''; + $message .= "\n"; + die($message); +} + +hesk_load_cron_database_functions(); +hesk_dbConnect(); + +$modsForHesk_settings = mfh_getSettings(); +$skip_events = $modsForHesk_settings['enable_calendar'] == 0; + +if ($hesk_settings['debug_mode']) { + echo "Starting Calendar Reminders...\n"; +} + +// Get all reminders that have a reminder date that is now or earlier, and an email has not been sent for the event yet. +/* + * Reminder units: + * 0 - minutes + * 1 - hours + * 2 - days + * 3 - weeks + */ +$case_statement = "CASE + WHEN `unit` = '0' THEN DATE_SUB(`event`.`start`, INTERVAL `reminder`.`amount` MINUTE) + WHEN `unit` = '1' THEN DATE_SUB(`event`.`start`, INTERVAL `reminder`.`amount` HOUR) + WHEN `unit` = '2' THEN DATE_SUB(`event`.`start`, INTERVAL `reminder`.`amount` DAY) + WHEN `unit` = '3' THEN DATE_SUB(`event`.`start`, INTERVAL `reminder`.`amount` WEEK) + END"; +$sql = "SELECT `reminder`.`id` AS `reminder_id`, `reminder`.`user_id` AS `user_id`, `reminder`.`event_id` AS `event_id`, + `event`.`name` AS `event_name`, `event`.`location` AS `event_location`, `event`.`comments` AS `event_comments`, + `category`.`name` AS `event_category`, `event`.`start` AS `event_start`, `event`.`end` AS `event_end`, + `event`.`all_day` AS `event_all_day`, `user`.`language` AS `user_language`, `user`.`email` AS `user_email`, + " . $case_statement . " AS `reminder_date`, 'EVENT' AS `type` + FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "calendar_event_reminder` AS `reminder` + INNER JOIN `" . hesk_dbEscape($hesk_settings['db_pfix']) . "calendar_event` AS `event` + ON `reminder`.`event_id` = `event`.`id` + INNER JOIN `" . hesk_dbEscape($hesk_settings['db_pfix']) . "categories` AS `category` + ON `event`.`category` = `category`.`id` + INNER JOIN `" . hesk_dbEscape($hesk_settings['db_pfix']) . "users` AS `user` + ON `reminder`.`user_id` = `user`.`id` + WHERE (" . $case_statement . ") <= NOW() + AND `email_sent` = '0'"; + +$rs = hesk_dbQuery($sql); +$reminders_to_flag = []; +$tickets_to_flag = []; + +$included_email_functions = false; +if (hesk_dbNumRows($rs) > 0 && !$skip_events) { + require(HESK_PATH . 'inc/email_functions.inc.php'); + $included_email_functions = true; +} + +$successful_emails = 0; +$failed_emails = 0; +while ($row = hesk_dbFetchAssoc($rs) && !$skip_events) { + if (mfh_sendCalendarReminder($row, $modsForHesk_settings)) { + $reminders_to_flag[] = $row['reminder_id']; + $successful_emails++; + + if ($hesk_settings['debug_mode']) { + $debug_msg = "Sent e-mail reminder for event: {$row['event_name']} to {$row['user_email']}\n"; + echo $debug_msg; + mfh_log_debug($LOCATION, $debug_msg, 'CRON'); + } + } else { + $failed_emails++; + + $warning_text = "Failed to send reminder e-mail for event: {$row['event_name']} to {$row['user_email']}. This will be re-sent next time reminders are processed.\n"; + mfh_log_warning($LOCATION, $warning_text, 'CRON'); + echo $warning_text; + } +} + +if (count($reminders_to_flag) > 0) { + foreach ($reminders_to_flag as $reminder_id) { + $sql = "UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "calendar_event_reminder` SET `email_sent` = '1' WHERE `id` = " . intval($reminder_id); + hesk_dbQuery($sql); + } +} + + +if ($hesk_settings['debug_mode']) { + $debug_msg = "Finished Calendar Reminders. {$successful_emails} reminder e-mails sent. {$failed_emails} emails failed to send.\n"; + echo $debug_msg; + mfh_log_debug($LOCATION, $debug_msg, 'CRON'); +} + +// Overdue tickets +if ($hesk_settings['debug_mode']) { + echo "Starting Overdue Tickets...\n"; +} + +$sql = "SELECT `ticket`.`id` AS `id`, `ticket`.`trackid` AS `trackid`, `ticket`.`name` AS `name`, `ticket`.`subject` AS `subject`, + `ticket`.`message` AS `message`, `ticket`.`category` AS `category`, `ticket`.`priority` AS `priority`, + `ticket`.`owner` AS `owner`, `ticket`.`status` AS `status`, `ticket`.`email` AS `email`, `ticket`.`dt` AS `dt`, + `ticket`.`lastchange` AS `lastchange`, `ticket`.`due_date` AS `due_date`, `user`.`language` AS `user_language`, `user`.`email` AS `user_email`, + `ticket`.`custom1` AS `custom1`, `ticket`.`custom2` AS `custom2`, `ticket`.`custom3` AS `custom3`, `ticket`.`custom4` AS `custom4`, + `ticket`.`custom5` AS `custom5`, `ticket`.`custom6` AS `custom6`, `ticket`.`custom7` AS `custom7`, `ticket`.`custom8` AS `custom8`, + `ticket`.`custom9` AS `custom9`, `ticket`.`custom10` AS `custom10`, `ticket`.`custom11` AS `custom11`, `ticket`.`custom12` AS `custom12`, + `ticket`.`custom13` AS `custom13`, `ticket`.`custom14` AS `custom14`, `ticket`.`custom15` AS `custom15`, `ticket`.`custom16` AS `custom16`, + `ticket`.`custom17` AS `custom17`, `ticket`.`custom18` AS `custom19`, `ticket`.`custom19` AS `custom19`, `ticket`.`custom20` AS `custom20`, + `ticket`.`html` AS `html` + FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` AS `ticket` + LEFT JOIN `" . hesk_dbEscape($hesk_settings['db_pfix']) . "users` AS `user` + ON `ticket`.`owner` = `user`.`id` + WHERE `due_date` IS NOT NULL + AND `due_date` <= NOW() + AND `overdue_email_sent` = '0'"; + +$successful_emails = 0; +$failed_emails = 0; +$rs = hesk_dbQuery($sql); + +if (hesk_dbNumRows($rs) > 0 && !$included_email_functions) { + require(HESK_PATH . 'inc/email_functions.inc.php'); + $included_email_functions = true; +} + +$user_rs = hesk_dbQuery("SELECT `id`, `isadmin`, `categories`, `email`, + CASE WHEN `heskprivileges` LIKE '%can_view_unassigned%' THEN 1 ELSE 0 END AS `can_view_unassigned` + FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "users` WHERE `notify_overdue_unassigned` = '1' + AND (`heskprivileges` LIKE '%can_view_tickets%' OR `isadmin` = '1')"); + +$users = []; +while ($row = hesk_dbFetchAssoc($user_rs)) { + $users[] = $row; +} + +$tickets_to_flag = []; +while ($row = hesk_dbFetchAssoc($rs)) { + if (mfh_sendOverdueTicketReminder($row, $users, $modsForHesk_settings)) { + $tickets_to_flag[] = $row['id']; + $successful_emails++; + + if ($hesk_settings['debug_mode']) { + $debug_msg = "Sent overdue e-mail for ticket: {$row['trackid']} to user id: {$row['owner']}\n"; + mfh_log_debug($LOCATION, $debug_msg, 'CRON'); + echo $debug_msg; + } + } else { + $failed_emails++; + + $warning_text = "Failed to send overdue reminder for ticket: {$row['trackid']} to user id: {$row['owner']}. This will be re-sent next time overdue tickets are processed.\n";\ + mfh_log_warning($LOCATION, $warning_text, 'CRON'); + echo $warning_text; + } +} + +if (count($tickets_to_flag) > 0) { + foreach ($tickets_to_flag as $ticket_id) { + $sql = "UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `overdue_email_sent` = '1' WHERE `id` = " . intval($ticket_id); + hesk_dbQuery($sql); + } +} + +if ($hesk_settings['debug_mode']) { + $debug_msg = "Finished Overdue Tickets. {$successful_emails} e-mails sent. {$failed_emails} emails failed to send.\n"; + echo $debug_msg; + mfh_log_debug($LOCATION, $debug_msg, 'CRON'); +} \ No newline at end of file diff --git a/cron/core/database.inc.php b/cron/core/database.inc.php new file mode 100755 index 00000000..52a81271 --- /dev/null +++ b/cron/core/database.inc.php @@ -0,0 +1,248 @@ +EXPLAIN $query

    \n"; + + if ($res = @mysqli_query($hesk_db_link, $query)) + { + return $res; + } + elseif ($hesk_settings['debug_mode']) + { + $message = 'Error executing SQL: ' . $query . '; ' . $hesklang['mysql_said'] . ': ' . mysqli_error($hesk_db_link); + } + else + { + $message = $hesklang['contact_webmaster'] . $hesk_settings['webmaster_email']; + } + mfh_log_error($_SERVER['HTTP_REFERER'], $message, $_SESSION['id']); + echo $hesklang['cant_sql'] . ': ' . $message; + die(); +} // END hesk_dbQuery() + + +function hesk_dbFetchAssoc($res) +{ + + return @mysqli_fetch_assoc($res); + +} // END hesk_FetchAssoc() + + +function hesk_dbFetchRow($res) +{ + + return @mysqli_fetch_row($res); + +} // END hesk_FetchRow() + + +function hesk_dbResult($res, $row = 0, $column = 0) +{ + $i=0; + $res->data_seek(0); + + while ($tmp = @mysqli_fetch_array($res, MYSQLI_NUM)) + { + if ($i==$row) + { + return $tmp[$column]; + } + $i++; + } + + return ''; + +} // END hesk_dbResult() + + +function hesk_dbInsertID() +{ + global $hesk_db_link; + + if ($lastid = @mysqli_insert_id($hesk_db_link)) + { + return $lastid; + } + +} // END hesk_dbInsertID() + + +function hesk_dbFreeResult($res) +{ + + return @mysqli_free_result($res); + +} // END hesk_dbFreeResult() + + +function hesk_dbNumRows($res) +{ + + return @mysqli_num_rows($res); + +} // END hesk_dbNumRows() + + +function hesk_dbAffectedRows() +{ + global $hesk_db_link; + + return @mysqli_affected_rows($hesk_db_link); + +} // END hesk_dbAffectedRows() diff --git a/css/bootstrap-clockpicker.min.css b/css/bootstrap-clockpicker.min.css new file mode 100755 index 00000000..cf150784 --- /dev/null +++ b/css/bootstrap-clockpicker.min.css @@ -0,0 +1,5 @@ +/*! + * ClockPicker v0.0.7 for Bootstrap (http://weareoutman.github.io/clockpicker/) + * Copyright 2014 Wang Shenwei. + * Licensed under MIT (https://github.com/weareoutman/clockpicker/blob/gh-pages/LICENSE) + */.clockpicker .input-group-addon{cursor:pointer}.clockpicker-moving{cursor:move}.clockpicker-align-left.popover>.arrow{left:25px}.clockpicker-align-top.popover>.arrow{top:17px}.clockpicker-align-right.popover>.arrow{left:auto;right:25px}.clockpicker-align-bottom.popover>.arrow{top:auto;bottom:6px}.clockpicker-popover .popover-title{background-color:#fff;color:#999;font-size:24px;font-weight:700;line-height:30px;text-align:center}.clockpicker-popover .popover-title span{cursor:pointer}.clockpicker-popover .popover-content{background-color:#f8f8f8;padding:12px}.popover-content:last-child{border-bottom-left-radius:5px;border-bottom-right-radius:5px}.clockpicker-plate{background-color:#fff;border:1px solid #ccc;border-radius:50%;width:200px;height:200px;overflow:visible;position:relative;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.clockpicker-canvas,.clockpicker-dial{width:200px;height:200px;position:absolute;left:-1px;top:-1px}.clockpicker-minutes{visibility:hidden}.clockpicker-tick{border-radius:50%;color:#666;line-height:26px;text-align:center;width:26px;height:26px;position:absolute;cursor:pointer}.clockpicker-tick.active,.clockpicker-tick:hover{background-color:#c0e5f7;background-color:rgba(0,149,221,.25)}.clockpicker-button{background-image:none;background-color:#fff;border-width:1px 0 0;border-top-left-radius:0;border-top-right-radius:0;margin:0;padding:10px 0}.clockpicker-button:hover{background-image:none;background-color:#ebebeb}.clockpicker-button:focus{outline:0!important}.clockpicker-dial{-webkit-transition:-webkit-transform 350ms,opacity 350ms;-moz-transition:-moz-transform 350ms,opacity 350ms;-ms-transition:-ms-transform 350ms,opacity 350ms;-o-transition:-o-transform 350ms,opacity 350ms;transition:transform 350ms,opacity 350ms}.clockpicker-dial-out{opacity:0}.clockpicker-hours.clockpicker-dial-out{-webkit-transform:scale(1.2,1.2);-moz-transform:scale(1.2,1.2);-ms-transform:scale(1.2,1.2);-o-transform:scale(1.2,1.2);transform:scale(1.2,1.2)}.clockpicker-minutes.clockpicker-dial-out{-webkit-transform:scale(.8,.8);-moz-transform:scale(.8,.8);-ms-transform:scale(.8,.8);-o-transform:scale(.8,.8);transform:scale(.8,.8)}.clockpicker-canvas{-webkit-transition:opacity 175ms;-moz-transition:opacity 175ms;-ms-transition:opacity 175ms;-o-transition:opacity 175ms;transition:opacity 175ms}.clockpicker-canvas-out{opacity:.25}.clockpicker-canvas-bearing,.clockpicker-canvas-fg{stroke:none;fill:#0095dd}.clockpicker-canvas-bg{stroke:none;fill:#c0e5f7}.clockpicker-canvas-bg-trans{fill:rgba(0,149,221,.25)}.clockpicker-canvas line{stroke:#0095dd;stroke-width:1;stroke-linecap:round}.clockpicker-button.am-button{margin:1px;padding:5px;border:1px solid rgba(0,0,0,.2);border-radius:4px}.clockpicker-button.pm-button{margin:1px 1px 1px 136px;padding:5px;border:1px solid rgba(0,0,0,.2);border-radius:4px} \ No newline at end of file diff --git a/css/fullcalendar.min.css b/css/fullcalendar.min.css new file mode 100755 index 00000000..d88fd5ad --- /dev/null +++ b/css/fullcalendar.min.css @@ -0,0 +1,5 @@ +/*! + * FullCalendar v2.6.0 Stylesheet + * Docs & License: http://fullcalendar.io/ + * (c) 2015 Adam Shaw + */.fc{direction:ltr;text-align:left}.fc-rtl{text-align:right}body .fc{font-size:1em}.fc-unthemed .fc-divider,.fc-unthemed .fc-popover,.fc-unthemed .fc-row,.fc-unthemed tbody,.fc-unthemed td,.fc-unthemed th,.fc-unthemed thead{border-color:#ddd}.fc-unthemed .fc-popover{background-color:#fff}.fc-unthemed .fc-divider,.fc-unthemed .fc-popover .fc-header{background:#eee}.fc-unthemed .fc-popover .fc-header .fc-close{color:#666}.fc-unthemed .fc-today{background:#fcf8e3}.fc-highlight{background:#bce8f1;opacity:.3;filter:alpha(opacity=30)}.fc-bgevent{background:#8fdf82;opacity:.3;filter:alpha(opacity=30)}.fc-nonbusiness{background:#d7d7d7}.fc-icon{display:inline-block;width:1em;height:1em;line-height:1em;font-size:1em;text-align:center;overflow:hidden;font-family:"Courier New",Courier,monospace;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.fc-icon:after{position:relative;margin:0 -1em}.fc-icon-left-single-arrow:after{content:"\02039";font-weight:700;font-size:200%;top:-7%;left:3%}.fc-icon-right-single-arrow:after{content:"\0203A";font-weight:700;font-size:200%;top:-7%;left:-3%}.fc-icon-left-double-arrow:after{content:"\000AB";font-size:160%;top:-7%}.fc-icon-right-double-arrow:after{content:"\000BB";font-size:160%;top:-7%}.fc-icon-left-triangle:after{content:"\25C4";font-size:125%;top:3%;left:-2%}.fc-icon-right-triangle:after{content:"\25BA";font-size:125%;top:3%;left:2%}.fc-icon-down-triangle:after{content:"\25BC";font-size:125%;top:2%}.fc-icon-x:after{content:"\000D7";font-size:200%;top:6%}.fc button{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;height:2.1em;padding:0 .6em;font-size:1em;white-space:nowrap;cursor:pointer}.fc button::-moz-focus-inner{margin:0;padding:0}.fc-state-default{border:1px solid}.fc-state-default.fc-corner-left{border-top-left-radius:4px;border-bottom-left-radius:4px}.fc-state-default.fc-corner-right{border-top-right-radius:4px;border-bottom-right-radius:4px}.fc button .fc-icon{position:relative;top:-.05em;margin:0 .2em;vertical-align:middle}.fc-state-default{background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05)}.fc-state-active,.fc-state-disabled,.fc-state-down,.fc-state-hover{color:#333;background-color:#e6e6e6}.fc-state-hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.fc-state-active,.fc-state-down{background-color:#ccc;background-image:none;box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.fc-state-disabled{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);box-shadow:none}.fc-button-group{display:inline-block}.fc .fc-button-group>*{float:left;margin:0 0 0 -1px}.fc .fc-button-group>:first-child{margin-left:0}.fc-popover{position:absolute;box-shadow:0 2px 6px rgba(0,0,0,.15)}.fc-popover .fc-header{padding:2px 4px}.fc-popover .fc-header .fc-title{margin:0 2px}.fc-popover .fc-header .fc-close{cursor:pointer}.fc-ltr .fc-popover .fc-header .fc-title,.fc-rtl .fc-popover .fc-header .fc-close{float:left}.fc-ltr .fc-popover .fc-header .fc-close,.fc-rtl .fc-popover .fc-header .fc-title{float:right}.fc-unthemed .fc-popover{border-width:1px;border-style:solid}.fc-unthemed .fc-popover .fc-header .fc-close{font-size:.9em;margin-top:2px}.fc-popover>.ui-widget-header+.ui-widget-content{border-top:0}.fc-divider{border-style:solid;border-width:1px}hr.fc-divider{height:0;margin:0;padding:0 0 2px;border-width:1px 0}.fc-clear{clear:both}.fc-bg,.fc-bgevent-skeleton,.fc-helper-skeleton,.fc-highlight-skeleton{position:absolute;top:0;left:0;right:0}.fc-bg{bottom:0}.fc-bg table{height:100%}.fc table{width:100%;table-layout:fixed;border-collapse:collapse;border-spacing:0;font-size:1em}.fc th{text-align:center}.fc td,.fc th{border-style:solid;border-width:1px;padding:0;vertical-align:top}.fc td.fc-today{border-style:double}.fc .fc-row{border-style:solid;border-width:0}.fc-row table{border-left:0 hidden transparent;border-right:0 hidden transparent;border-bottom:0 hidden transparent}.fc-row:first-child table{border-top:0 hidden transparent}.fc-row{position:relative}.fc-row .fc-bg{z-index:1}.fc-row .fc-bgevent-skeleton,.fc-row .fc-highlight-skeleton{bottom:0}.fc-row .fc-bgevent-skeleton table,.fc-row .fc-highlight-skeleton table{height:100%}.fc-row .fc-bgevent-skeleton td,.fc-row .fc-highlight-skeleton td{border-color:transparent}.fc-row .fc-bgevent-skeleton{z-index:2}.fc-row .fc-highlight-skeleton{z-index:3}.fc-row .fc-content-skeleton{position:relative;z-index:4;padding-bottom:2px}.fc-row .fc-helper-skeleton{z-index:5}.fc-row .fc-content-skeleton td,.fc-row .fc-helper-skeleton td{background:0 0;border-color:transparent;border-bottom:0}.fc-row .fc-content-skeleton tbody td,.fc-row .fc-helper-skeleton tbody td{border-top:0}.fc-scroller{overflow-y:scroll;overflow-x:hidden}.fc-scroller>*{position:relative;width:100%;overflow:hidden}.fc-event{position:relative;display:block;font-size:.85em;line-height:1.3;border-radius:3px;border:1px solid #3a87ad;background-color:#3a87ad;font-weight:400}.fc-event,.fc-event:hover,.ui-widget .fc-event{color:#fff;text-decoration:none}.fc-event.fc-draggable,.fc-event[href]{cursor:pointer}.fc-not-allowed,.fc-not-allowed .fc-event{cursor:not-allowed}.fc-event .fc-bg{z-index:1;background:#fff;opacity:.25;filter:alpha(opacity=25)}.fc-event .fc-content{position:relative;z-index:2}.fc-event .fc-resizer{position:absolute;z-index:3}.fc-ltr .fc-h-event.fc-not-start,.fc-rtl .fc-h-event.fc-not-end{margin-left:0;border-left-width:0;padding-left:1px;border-top-left-radius:0;border-bottom-left-radius:0}.fc-ltr .fc-h-event.fc-not-end,.fc-rtl .fc-h-event.fc-not-start{margin-right:0;border-right-width:0;padding-right:1px;border-top-right-radius:0;border-bottom-right-radius:0}.fc-h-event .fc-resizer{top:-1px;bottom:-1px;left:-1px;right:-1px;width:5px}.fc-ltr .fc-h-event .fc-start-resizer,.fc-ltr .fc-h-event .fc-start-resizer:after,.fc-ltr .fc-h-event .fc-start-resizer:before,.fc-rtl .fc-h-event .fc-end-resizer,.fc-rtl .fc-h-event .fc-end-resizer:after,.fc-rtl .fc-h-event .fc-end-resizer:before{right:auto;cursor:w-resize}.fc-ltr .fc-h-event .fc-end-resizer,.fc-ltr .fc-h-event .fc-end-resizer:after,.fc-ltr .fc-h-event .fc-end-resizer:before,.fc-rtl .fc-h-event .fc-start-resizer,.fc-rtl .fc-h-event .fc-start-resizer:after,.fc-rtl .fc-h-event .fc-start-resizer:before{left:auto;cursor:e-resize}.fc-day-grid-event{margin:1px 2px 0;padding:0 1px}.fc-day-grid-event .fc-content{white-space:nowrap;overflow:hidden}.fc-day-grid-event .fc-time{font-weight:700}.fc-day-grid-event .fc-resizer{left:-3px;right:-3px;width:7px}a.fc-more{margin:1px 3px;font-size:.85em;cursor:pointer;text-decoration:none}a.fc-more:hover{text-decoration:underline}.fc-limited{display:none}.fc-day-grid .fc-row{z-index:1}.fc-more-popover{z-index:2;width:220px}.fc-more-popover .fc-event-container{padding:10px}.fc-now-indicator{position:absolute;border:0 solid red}.fc-toolbar{text-align:center;margin-bottom:1em}.fc-toolbar .fc-left{float:left}.fc-toolbar .fc-right{float:right}.fc-toolbar .fc-center{display:inline-block}.fc .fc-toolbar>*>*{float:left;margin-left:.75em}.fc .fc-toolbar>*>:first-child{margin-left:0}.fc-toolbar h2{margin:0}.fc-toolbar button{position:relative}.fc-toolbar .fc-state-hover,.fc-toolbar .ui-state-hover{z-index:2}.fc-toolbar .fc-state-down{z-index:3}.fc-toolbar .fc-state-active,.fc-toolbar .ui-state-active{z-index:4}.fc-toolbar button:focus{z-index:5}.fc-view-container *,.fc-view-container :after,.fc-view-container :before{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.fc-view,.fc-view>table{position:relative;z-index:1}.fc-basicDay-view .fc-content-skeleton,.fc-basicWeek-view .fc-content-skeleton{padding-top:1px;padding-bottom:1em}.fc-basic-view .fc-body .fc-row{min-height:4em}.fc-row.fc-rigid{overflow:hidden}.fc-row.fc-rigid .fc-content-skeleton{position:absolute;top:0;left:0;right:0}.fc-basic-view .fc-day-number,.fc-basic-view .fc-week-number{padding:0 2px}.fc-basic-view td.fc-day-number,.fc-basic-view td.fc-week-number span{padding-top:2px;padding-bottom:2px}.fc-basic-view .fc-week-number{text-align:center}.fc-basic-view .fc-week-number span{display:inline-block;min-width:1.25em}.fc-ltr .fc-basic-view .fc-day-number{text-align:right}.fc-rtl .fc-basic-view .fc-day-number{text-align:left}.fc-day-number.fc-other-month{opacity:.3;filter:alpha(opacity=30)}.fc-agenda-view .fc-day-grid{position:relative;z-index:2}.fc-agenda-view .fc-day-grid .fc-row{min-height:3em}.fc-agenda-view .fc-day-grid .fc-row .fc-content-skeleton{padding-top:1px;padding-bottom:1em}.fc .fc-axis{vertical-align:middle;padding:0 4px;white-space:nowrap}.fc-ltr .fc-axis{text-align:right}.fc-rtl .fc-axis{text-align:left}.ui-widget td.fc-axis{font-weight:400}.fc-time-grid,.fc-time-grid-container{position:relative;z-index:1}.fc-time-grid{min-height:100%}.fc-time-grid table{border:0 hidden transparent}.fc-time-grid>.fc-bg{z-index:1}.fc-time-grid .fc-slats,.fc-time-grid>hr{position:relative;z-index:2}.fc-time-grid .fc-content-col{position:relative}.fc-time-grid .fc-content-skeleton{position:absolute;z-index:3;top:0;left:0;right:0}.fc-time-grid .fc-business-container{position:relative;z-index:1}.fc-time-grid .fc-bgevent-container{position:relative;z-index:2}.fc-time-grid .fc-highlight-container{z-index:3}.fc-time-grid .fc-event-container{position:relative;z-index:4}.fc-time-grid .fc-now-indicator-line{z-index:5}.fc-time-grid .fc-helper-container{position:relative;z-index:6}.fc-time-grid .fc-slats td{height:1.5em;border-bottom:0}.fc-time-grid .fc-slats .fc-minor td{border-top-style:dotted}.fc-time-grid .fc-slats .ui-widget-content{background:0 0}.fc-time-grid .fc-highlight-container{position:relative}.fc-time-grid .fc-highlight{position:absolute;left:0;right:0}.fc-ltr .fc-time-grid .fc-event-container{margin:0 2.5% 0 2px}.fc-rtl .fc-time-grid .fc-event-container{margin:0 2px 0 2.5%}.fc-time-grid .fc-bgevent,.fc-time-grid .fc-event{position:absolute;z-index:1}.fc-time-grid .fc-bgevent{left:0;right:0}.fc-v-event.fc-not-start{border-top-width:0;padding-top:1px;border-top-left-radius:0;border-top-right-radius:0}.fc-v-event.fc-not-end{border-bottom-width:0;padding-bottom:1px;border-bottom-left-radius:0;border-bottom-right-radius:0}.fc-time-grid-event{overflow:hidden}.fc-time-grid-event .fc-time,.fc-time-grid-event .fc-title{padding:0 1px}.fc-time-grid-event .fc-time{font-size:.85em;white-space:nowrap}.fc-time-grid-event.fc-short .fc-content{white-space:nowrap}.fc-time-grid-event.fc-short .fc-time,.fc-time-grid-event.fc-short .fc-title{display:inline-block;vertical-align:top}.fc-time-grid-event.fc-short .fc-time span{display:none}.fc-time-grid-event.fc-short .fc-time:before{content:attr(data-start)}.fc-time-grid-event.fc-short .fc-time:after{content:"\000A0-\000A0"}.fc-time-grid-event.fc-short .fc-title{font-size:.85em;padding:0}.fc-time-grid-event .fc-resizer{left:0;right:0;bottom:0;height:8px;overflow:hidden;line-height:8px;font-size:11px;font-family:monospace;text-align:center;cursor:s-resize}.fc-time-grid-event .fc-resizer:after{content:"="}.fc-time-grid .fc-now-indicator-line{border-top-width:1px;left:0;right:0}.fc-time-grid .fc-now-indicator-arrow{margin-top:-5px}.fc-ltr .fc-time-grid .fc-now-indicator-arrow{left:0;border-width:5px 0 5px 6px;border-top-color:transparent;border-bottom-color:transparent}.fc-rtl .fc-time-grid .fc-now-indicator-arrow{right:0;border-width:5px 6px 5px 0;border-top-color:transparent;border-bottom-color:transparent} \ No newline at end of file diff --git a/css/jquery.jgrowl.min.css b/css/jquery.jgrowl.min.css new file mode 100755 index 00000000..a12f907e --- /dev/null +++ b/css/jquery.jgrowl.min.css @@ -0,0 +1 @@ +.jGrowl{z-index:9999;color:#fff;font-size:12px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;position:fixed}.jGrowl.top-left{left:0;top:0}.jGrowl.top-right{right:0;top:0}.jGrowl.bottom-left{left:0;bottom:0}.jGrowl.bottom-right{right:0;bottom:0}.jGrowl.center{top:0;width:50%;left:25%}.jGrowl.center .jGrowl-closer,.jGrowl.center .jGrowl-notification{margin-left:auto;margin-right:auto}.jGrowl-notification{background-color:#000;opacity:.9;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=(0.9*100));-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=(0.9*100));zoom:1;width:250px;padding:10px;margin:10px;text-align:left;display:none;border-radius:5px;min-height:40px}.jGrowl-notification .ui-state-highlight,.jGrowl-notification .ui-widget-content .ui-state-highlight,.jGrowl-notification .ui-widget-header .ui-state-highlight{border:1px solid #000;background:#000;color:#fff}.jGrowl-notification .jGrowl-header{font-weight:700;font-size:.85em}.jGrowl-notification .jGrowl-close{background-color:transparent;color:inherit;border:none;z-index:99;float:right;font-weight:700;font-size:1em;cursor:pointer}.jGrowl-closer{background-color:#000;opacity:.9;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=(0.9*100));-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=(0.9*100));zoom:1;width:250px;padding:10px;margin:10px;display:none;border-radius:5px;padding-top:4px;padding-bottom:4px;cursor:pointer;font-size:.9em;font-weight:700;text-align:center}.jGrowl-closer .ui-state-highlight,.jGrowl-closer .ui-widget-content .ui-state-highlight,.jGrowl-closer .ui-widget-header .ui-state-highlight{border:1px solid #000;background:#000;color:#fff}@media print{.jGrowl{display:none}} \ No newline at end of file diff --git a/css/mods-for-hesk.css b/css/mods-for-hesk.css index 1ec09169..64c16af4 100644 --- a/css/mods-for-hesk.css +++ b/css/mods-for-hesk.css @@ -295,6 +295,10 @@ div.setupButtons { color: blue; } +.black { + color: black; +} + .pad-down-20 { padding-top: 20px; } @@ -315,4 +319,13 @@ div.setupButtons { .no-bottom-border { border-bottom: none; +} + +.category-label { + font-weight: normal; + font-size: 1em; +} + +.fc-content { + text-overflow: ellipsis; } \ No newline at end of file diff --git a/inc/common.inc.php b/inc/common.inc.php index 58ef87ba..cbb0f230 100644 --- a/inc/common.inc.php +++ b/inc/common.inc.php @@ -179,6 +179,16 @@ function hesk_load_internal_api_database_functions() } } // END hesk_load_database_functions() +function hesk_load_cron_database_functions() +{ + if (function_exists('mysqli_connect')) { + require(HESK_PATH . 'cron/core/database_mysqli.inc.php'); + } // Default to MySQL + else { + require(HESK_PATH . 'cron/core/database.inc.php'); + } +} // END hesk_load_cron_database_functions() + function hesk_unlink($file, $older_than = 0) { return (is_file($file) && (!$older_than || (time() - filectime($file)) > $older_than) && @unlink($file)) ? true : false; @@ -1762,6 +1772,7 @@ function hesk_getFeatureArray() 'can_man_settings', /* User can manage helpdesk settings */ 'can_change_notification_settings', /* User can change notification settings */ 'can_view_logs', /* User can view the message logs */ + 'can_man_calendar', /* User can manage calendar events */ ); } @@ -1820,10 +1831,16 @@ function mfh_log($location, $message, $severity, $user) { $sql = "INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "logging` (`username`, `message`, `severity`, `location`, `timestamp`) VALUES ('" . hesk_dbEscape($user) . "', '" . hesk_dbEscape($message) . "', " . intval($severity) . ", '" . hesk_dbEscape($location) . "', NOW())"; + + hesk_dbQuery($sql); } function mfh_log_debug($location, $message, $user) { - mfh_log($location, $message, 0, $user); + global $hesk_settings; + + if ($hesk_settings['debug_mode']) { + mfh_log($location, $message, 0, $user); + } } function mfh_log_info($location, $message, $user) { diff --git a/inc/email_functions.inc.php b/inc/email_functions.inc.php index 044472ef..a48828eb 100644 --- a/inc/email_functions.inc.php +++ b/inc/email_functions.inc.php @@ -255,6 +255,108 @@ function hesk_notifyStaff($email_template, $sql_where, $modsForHesk_settings, $i } // END hesk_notifyStaff() +function mfh_sendCalendarReminder($reminder_data, $modsForHesk_settings) { + global $hesk_settings, $hesklang; + + if (defined('HESK_DEMO')) { + return true; + } + + hesk_setLanguage($reminder_data['user_language']); + + $valid_emails = hesk_validEmails(); + $subject = NULL; + if (!isset($valid_emails['calendar_reminder'])) { + hesk_error($hesklang['inve']); + } else { + $subject = $valid_emails['calendar_reminder']; + } + + // Format email subject and message + $subject = str_replace('%%TITLE%%', $reminder_data['event_name'], $subject); + $message = hesk_getEmailMessage('calendar_reminder', NULL, $modsForHesk_settings, 1, 0, 1); + $message = mfh_processCalendarTemplate($message, $reminder_data); + $htmlMessage = hesk_getHtmlMessage('calendar_reminder', NULL, $modsForHesk_settings, 1, 0, 1); + $htmlMessage = mfh_processCalendarTemplate($htmlMessage, $reminder_data); + + hesk_mail($reminder_data['user_email'], $subject, $message, $htmlMessage, $modsForHesk_settings); + + return true; +} + +function mfh_processCalendarTemplate($message, $reminder_data) { + global $hesk_settings; + + if ($reminder_data['event_all_day'] == '1') { + $format = 'Y-m-d'; + } else { + $format = $hesk_settings['timeformat']; + } + + $start_date = strtotime($reminder_data['event_start']); + $formatted_start_date = date($format, $start_date); + $formatted_end_date = ''; + + if ($reminder_data['event_start'] != $reminder_data['event_end']) { + $end_date = strtotime($reminder_data['event_end']); + $formatted_end_date = ' - ' . date($format, $end_date); + } + + // Process replaced fields + $message = str_replace('%%TITLE%%', $reminder_data['event_name'], $message); + $message = str_replace('%%LOCATION%%', $reminder_data['event_location'], $message); + $message = str_replace('%%CATEGORY%%', $reminder_data['event_category'], $message); + $message = str_replace('%%WHEN%%', $formatted_start_date . $formatted_end_date, $message); + $message = str_replace('%%COMMENTS%%', $reminder_data['event_comments'], $message); + + return $message; +} + + +function mfh_sendOverdueTicketReminder($ticket, $users, $modsForHesk_settings) { + global $hesk_settings, $hesklang; + + if (defined('HESK_DEMO')) { + return true; + } + + hesk_setLanguage($ticket['user_language']); + + $valid_emails = hesk_validEmails(); + $subject = NULL; + if (!isset($valid_emails['overdue_ticket'])) { + hesk_error($hesklang['inve']); + } else { + $subject = $valid_emails['overdue_ticket']; + } + + // Format email subject and message + $subject = str_replace('%%TITLE%%', $ticket['subject'], $subject); + $subject = str_replace('%%TRACKID%%', $ticket['trackid'], $subject); + $message = hesk_getEmailMessage('overdue_ticket', NULL, $modsForHesk_settings, 1, 0, 1); + $message = hesk_processMessage($message, $ticket, 1, 1, 0, $modsForHesk_settings); + $htmlMessage = hesk_getHtmlMessage('overdue_ticket', NULL, $modsForHesk_settings, 1, 0, 1); + $htmlMessage = hesk_processMessage($htmlMessage, $ticket, 1, 1, 0, $modsForHesk_settings, 1); + + $emails = []; + if ($ticket['user_email'] != NULL) { + $emails[] = $ticket['user_email']; + } + foreach ($users as $user) { + $categories = explode(',', $user['categories']); + if ($user['email'] != $ticket['user_email'] + && ($user['isadmin'] || in_array($ticket['category'], $categories))) { + $emails[] = $user['email']; + } + } + + foreach ($emails as $email) { + hesk_mail($email, $subject, $message, $htmlMessage, $modsForHesk_settings); + } + + return true; +} + function hesk_validEmails() { @@ -303,6 +405,12 @@ function hesk_validEmails() // --> Staff password reset email 'reset_password' => $hesklang['reset_password'], + // --> Calendar reminder + 'calendar_reminder' => $hesklang['calendar_reminder'], + + // --> Overdue Ticket reminder + 'overdue_ticket' => $hesklang['overdue_ticket'], + ); } // END hesk_validEmails() diff --git a/inc/header.inc.php b/inc/header.inc.php index 406dad64..f9478981 100644 --- a/inc/header.inc.php +++ b/inc/header.inc.php @@ -101,6 +101,9 @@ if (is_dir(HESK_PATH . 'install')) { + + + @@ -109,6 +112,7 @@ if (is_dir(HESK_PATH . 'install')) { src="js/modsForHesk-javascript.js"> + @@ -116,6 +120,7 @@ if (is_dir(HESK_PATH . 'install')) { +