-
-
-
+
-
-
-
-
-
-
@@ -545,22 +566,34 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php');
+
+
+
+
+
+
calendarGateway = $calendarGateway;
+ $this->auditTrailGateway = $auditTrailGateway;
+ }
+
+ 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);
+
+ $this->auditTrailGateway->insertAuditTrailRecord($calendarEvent->id,
+ AuditTrailEntityType::CALENDAR_EVENT,
+ 'audit_event_updated',
+ DateTimeHelpers::heskDate($heskSettings),
+ array(0 => $userContext->name . ' (' . $userContext->username . ')'), $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));
+ }
+
+ $event = $events[0];
+
+ 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);
+
+ $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));
+ }
+
+ $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) {
+ $this->calendarGateway->deleteEvent($id, $userContext, $heskSettings);
+ }
+}
\ No newline at end of file
diff --git a/api/BusinessLogic/Calendar/ReminderUnit.php b/api/BusinessLogic/Calendar/ReminderUnit.php
new file mode 100644
index 00000000..e45b2169
--- /dev/null
+++ b/api/BusinessLogic/Calendar/ReminderUnit.php
@@ -0,0 +1,41 @@
+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/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/BusinessLogic/Security/UserPrivilege.php b/api/BusinessLogic/Security/UserPrivilege.php
index 8cd1eee4..801e1937 100644
--- a/api/BusinessLogic/Security/UserPrivilege.php
+++ b/api/BusinessLogic/Security/UserPrivilege.php
@@ -15,5 +15,7 @@ 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_VIEW_ASSIGNED_TO_OTHER = 'can_view_ass_others';
+ const CAN_VIEW_UNASSIGNED = 'can_view_unassigned';
const CAN_MANAGE_SERVICE_MESSAGES = 'can_service_msg';
}
\ No newline at end of file
diff --git a/api/BusinessLogic/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/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;
}
@@ -135,4 +142,67 @@ class TicketEditor extends \BaseClass {
throw new ValidationException($validationModel);
}
}
+
+ /**
+ * @param $id int
+ * @param $dueDate string
+ * @param $userContext UserContext
+ * @param $heskSettings array
+ * @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) {
+ $validationModel->errorKeys[] = 'TICKET_MUST_EXIST_FOR_ID';
+ }
+
+ if (!$this->userToTicketChecker->isTicketAccessibleToUser($userContext, $ticket, $heskSettings, array(UserPrivilege::CAN_EDIT_TICKETS))) {
+ $validationModel->errorKeys[] = 'TICKET_MUST_BE_ACCESSIBLE_TO_USER';
+ }
+
+ 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/Controllers/Calendar/CalendarController.php b/api/Controllers/Calendar/CalendarController.php
new file mode 100644
index 00000000..0440f54a
--- /dev/null
+++ b/api/Controllers/Calendar/CalendarController.php
@@ -0,0 +1,127 @@
+errorKeys = array('START_AND_END_TIMES_REQUIRED');
+ throw new ValidationException($validationModel);
+ }
+
+ $startTime = $_GET['start'];
+ $endTime = $_GET['end'];
+
+ /* @var $calendarHandler CalendarHandler */
+ $calendarHandler = $applicationContext->get(CalendarHandler::clazz());
+
+ $searchEventsFilter = new SearchEventsFilter();
+ $searchEventsFilter->startTime = $startTime;
+ $searchEventsFilter->endTime = $endTime;
+ $searchEventsFilter->reminderUserId = $userContext->id;
+
+ 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);
+
+ return output($events);
+ }
+
+ function post() {
+ /* @var $userContext UserContext */
+ /* @var $hesk_settings array */
+ global $applicationContext, $hesk_settings, $userContext;
+
+ $json = JsonRetriever::getJsonData();
+
+ $event = $this->transformJson($json);
+
+ /* @var $calendarHandler CalendarHandler */
+ $calendarHandler = $applicationContext->get(CalendarHandler::clazz());
+
+ return output($calendarHandler->createEvent($event, $userContext, $hesk_settings), 201);
+ }
+
+ 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));
+ }
+
+ 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();
+
+ $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..e049bca5 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,20 @@ 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());
+
+ $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) {
$editTicketModel = new EditTicketModel();
$editTicketModel->id = $id;
diff --git a/api/Core/Constants/Priority.php b/api/Core/Constants/Priority.php
index 39b86313..de361ab3 100644
--- a/api/Core/Constants/Priority.php
+++ b/api/Core/Constants/Priority.php
@@ -8,4 +8,19 @@ class Priority extends \BaseClass {
const HIGH = 1;
const MEDIUM = 2;
const LOW = 3;
+
+ static function getByValue($value) {
+ switch ($value) {
+ case self::CRITICAL:
+ return 'CRITICAL';
+ case self::HIGH:
+ return 'HIGH';
+ case self::MEDIUM:
+ return 'MEDIUM';
+ case self::LOW:
+ return 'LOW';
+ default:
+ return 'UNKNOWN';
+ }
+ }
}
\ No newline at end of file
diff --git a/api/DataAccess/Calendar/CalendarGateway.php b/api/DataAccess/Calendar/CalendarGateway.php
new file mode 100644
index 00000000..39c73c44
--- /dev/null
+++ b/api/DataAccess/Calendar/CalendarGateway.php
@@ -0,0 +1,246 @@
+init();
+
+ $events = array();
+
+ // 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`,
+ `reminders`.`amount` AS `reminder_value`, `reminders`.`unit` AS `reminder_unit`
+ 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`
+ 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 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";
+ }
+
+ if ($searchEventsFilter->eventId !== null) {
+ $sql .= " AND `events`.`id` = " . intval($searchEventsFilter->eventId);
+ }
+
+ if (!empty($searchEventsFilter->categories)) {
+ $categoriesAsString = implode(',', $searchEventsFilter->categories);
+ $sql .= " AND `events`.`category` IN (" . $categoriesAsString . ")";
+ }
+
+ $rs = hesk_dbQuery($sql);
+ while ($row = hesk_dbFetchAssoc($rs)) {
+ $event = new CalendarEvent();
+ $event->id = intval($row['id']);
+ $event->startTime = $row['start'];
+ $event->endTime = $row['end'];
+ $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 = Helpers::heskHtmlSpecialCharsDecode($row['category_name']);
+ $event->backgroundColor = $row['background_color'];
+ $event->foregroundColor = $row['foreground_color'];
+ $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']);
+
+ $auditTrailSql = "SELECT `at`.`id` AS `id`, `at`.`entity_id`, `at`.`language_key`, `at`.`date`,
+ `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_dbQuery($auditTrailSql);
+ /* @var $auditTrailEntry AuditTrail */
+ $auditTrailEntry = null;
+ while ($row = hesk_dbFetchAssoc($auditTrailRs)) {
+ if ($auditTrailEntry == null || intval($auditTrailEntry->id) !== intval($row['id'])) {
+ if ($auditTrailEntry !== null) {
+ $event->auditTrail[] = $auditTrailEntry;
+ }
+
+ $auditTrailEntry = new AuditTrail();
+ $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'];
+ $auditTrailEntry->replacementValues = array();
+ }
+ $auditTrailEntry->replacementValues[intval($row['replacement_index'])] = $row['replacement_value'];
+ }
+
+ if ($auditTrailEntry !== null) {
+ $event->auditTrail[] = $auditTrailEntry;
+ }
+
+ $events[] = $event;
+ }
+
+ // TICKETS
+ if ($searchEventsFilter->includeTickets) {
+ $oldTimeSetting = $heskSettings['timeformat'];
+ $heskSettings['timeformat'] = 'Y-m-d';
+ $currentDate = hesk_date();
+ $heskSettings['timeformat'] = $oldTimeSetting;
+
+ $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`, `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)
+ AND (`owner` = " . $searchEventsFilter->reminderUserId;
+
+ if ($searchEventsFilter->includeUnassignedTickets) {
+ $sql .= " OR `owner` = 0 ";
+ }
+
+ if ($searchEventsFilter->includeTicketsAssignedToOthers) {
+ $sql .= " OR `owner` NOT IN (0, " . $searchEventsFilter->reminderUserId . ") ";
+ }
+
+ $sql .= ")";
+
+ if (!empty($searchEventsFilter->categories)) {
+ $categoriesAsString = implode(',', $searchEventsFilter->categories);
+ $sql .= " AND `tickets`.`category` IN (" . $categoriesAsString . ")";
+ }
+
+ $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 = Helpers::heskHtmlSpecialCharsDecode($row['category_name']);
+ $event->backgroundColor = $row['background_color'];
+ $event->foregroundColor = $row['foreground_color'];
+ $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;
+ }
+ }
+
+ $this->close();
+
+ 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();
+ }
+
+ /**
+ * @param $event CalendarEvent
+ * @param $userContext UserContext
+ * @param $heskSettings array
+ */
+ public function updateEvent($event, $userContext, $heskSettings) {
+ $this->init();
+
+ $sql = "UPDATE `" . hesk_dbEscape($heskSettings['db_pfix']) . "calendar_event` SET `start` = '" . hesk_dbEscape($event->startTime)
+ . "', `end` = '" . hesk_dbEscape($event->endTime) . "', `all_day` = '" . ($event->allDay ? 1 : 0) . "', `name` = '"
+ . 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) {
+ $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);
+ $insert_sql = "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) . ")";
+ hesk_dbQuery($insert_sql);
+ }
+
+ hesk_dbQuery($sql);
+
+ $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/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
diff --git a/api/composer.lock b/api/composer.lock
index 1668fba8..f882508e 100644
--- a/api/composer.lock
+++ b/api/composer.lock
@@ -692,16 +692,16 @@
},
{
"name": "symfony/event-dispatcher",
- "version": "v2.8.28",
+ "version": "v2.8.33",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "7fe089232554357efb8d4af65ce209fc6e5a2186"
+ "reference": "d64be24fc1eba62f9daace8a8918f797fc8e87cc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/7fe089232554357efb8d4af65ce209fc6e5a2186",
- "reference": "7fe089232554357efb8d4af65ce209fc6e5a2186",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d64be24fc1eba62f9daace8a8918f797fc8e87cc",
+ "reference": "d64be24fc1eba62f9daace8a8918f797fc8e87cc",
"shasum": ""
},
"require": {
@@ -748,7 +748,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 +1334,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 +1381,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 +1432,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 +1453,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 +1491,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 +1513,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 +1528,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "5.2.x-dev"
+ "dev-master": "5.3.x-dev"
}
},
"autoload": {
@@ -1538,7 +1543,7 @@
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sb@sebastian-bergmann.de",
+ "email": "sebastian@phpunit.de",
"role": "lead"
}
],
@@ -1549,20 +1554,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 +1601,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 +1695,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 +1740,7 @@
"keywords": [
"tokenizer"
],
- "time": "2017-08-20T05:47:52+00:00"
+ "time": "2017-11-27T05:48:46+00:00"
},
{
"name": "phpunit/phpunit",
@@ -1974,16 +1979,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 +2039,7 @@
"compare",
"equality"
],
- "time": "2017-11-03T07:16:52+00:00"
+ "time": "2018-01-12T06:34:42+00:00"
},
{
"name": "sebastian/diff",
@@ -2488,16 +2493,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 +2550,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",
diff --git a/api/index.php b/api/index.php
index 3a67c362..ebdb36c7 100644
--- a/api/index.php
+++ b/api/index.php
@@ -201,6 +201,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()),
@@ -209,6 +210,10 @@ Link::all(array(
'/v1/statuses' => action(\Controllers\Statuses\StatusController::clazz(), RequestMethod::all()),
// 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),
// Service Messages
'/v1/service-messages' => action(\Controllers\ServiceMessages\ServiceMessagesController::clazz(),
array(RequestMethod::GET, RequestMethod::POST),
diff --git a/calendar.php b/calendar.php
index bb6a9ee0..2dcde8d3 100644
--- a/calendar.php
+++ b/calendar.php
@@ -111,8 +111,12 @@ require_once(HESK_PATH . 'inc/header.inc.php');