Merge branch '357-calendar-planning-phase-two' into '3-3-0'

Resolve "Calendar Planning, Phase Two"

See merge request mike-koch/Mods-for-HESK!86
master
Mike Koch 6 years ago
commit cfabdb950d

@ -282,10 +282,10 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php');
</div>
<div class="col-sm-4">
<select name="reminder-unit" class="form-control">
<option value="0"><?php echo $hesklang['event_min_before_event']; ?></option>
<option value="1"><?php echo $hesklang['event_hours_before_event']; ?></option>
<option value="2"><?php echo $hesklang['event_days_before_event']; ?></option>
<option value="3"><?php echo $hesklang['event_weeks_before_event']; ?></option>
<option value="MINUTE"><?php echo $hesklang['event_min_before_event']; ?></option>
<option value="HOUR"><?php echo $hesklang['event_hours_before_event']; ?></option>
<option value="DAY"><?php echo $hesklang['event_days_before_event']; ?></option>
<option value="WEEK"><?php echo $hesklang['event_weeks_before_event']; ?></option>
</select>
</div>
</div>
@ -334,147 +334,168 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php');
</div>
<form id="edit-form" class="form-horizontal" data-toggle="validator">
<div class="modal-body">
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label for="name" class="col-sm-3 control-label">
<?php echo $hesklang['event_title']; ?>
<i class="fa fa-question-circle settingsquestionmark"
data-toggle="tooltip"
title="<?php echo htmlspecialchars($hesklang['event_title_tooltip']); ?>"></i></label>
<div class="col-sm-9">
<input type="text" name="name" class="form-control"
placeholder="<?php echo htmlspecialchars($hesklang['event_title']); ?>"
data-error="<?php echo htmlspecialchars($hesklang['this_field_is_required']); ?>"
required>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="form-group">
<label for="location" class="col-sm-3 control-label">
<?php echo $hesklang['event_location']; ?>
<i class="fa fa-question-circle settingsquestionmark"
data-toggle="tooltip"
title="<?php echo htmlspecialchars($hesklang['event_location_tooltip']); ?>"></i>
</label>
<div class="col-sm-9">
<input type="text" name="location" class="form-control"
placeholder="<?php echo htmlspecialchars($hesklang['event_location']); ?>">
<div class="help-block with-errors"></div>
</div>
</div>
<div class="form-group">
<label for="category" class="col-sm-3 control-label">
<?php echo $hesklang['category']; ?>
<i class="fa fa-question-circle settingsquestionmark"
data-toggle="tooltip"
title="<?php echo htmlspecialchars($hesklang['event_category_tooltip']); ?>"></i>
</label>
<div class="col-sm-9">
<select name="category" class="form-control"
pattern="[0-9]+"
data-error="<?php echo htmlspecialchars($hesklang['sel_app_cat']); ?>" required>
<?php
if ($hesk_settings['select_cat']) {
echo '<option value="">'.$hesklang['select'].'</option>';
}
foreach ($categories as $category): ?>
<option value="<?php echo $category['id']; ?>" data-background-color="<?php echo htmlspecialchars($category['background_color']); ?>"
data-foreground-color="<?php echo htmlspecialchars($category['foreground_color']); ?>"
data-display-border="<?php echo htmlspecialchars($category['display_border_outline']); ?>">
<?php echo $category['name']; ?>
</option>
<?php endforeach; ?>
</select>
<div class="help-block with-errors"></div>
<ul class="nav nav-tabs" role="tablist" id="edit-modal-tabs">
<li role="presentation" class="active"><a href="#edit-contents" aria-controls="home" role="tab" data-toggle="tab"><?php echo $hesklang['information']; ?></a></li>
<li role="presentation"><a href="#edit-history" aria-controls="profile" role="tab" data-toggle="tab"><?php echo $hesklang['thist']; ?></a></li>
</ul>
<div class="tab-content" id="information-tab">
<div role="tabpanel" class="tab-pane active" id="edit-contents">
<br>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label for="name" class="col-sm-3 control-label">
<?php echo $hesklang['event_title']; ?>
<i class="fa fa-question-circle settingsquestionmark"
data-toggle="tooltip"
title="<?php echo htmlspecialchars($hesklang['event_title_tooltip']); ?>"></i></label>
<div class="col-sm-9">
<input type="text" name="name" class="form-control"
placeholder="<?php echo htmlspecialchars($hesklang['event_title']); ?>"
data-error="<?php echo htmlspecialchars($hesklang['this_field_is_required']); ?>"
required>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="form-group">
<label for="location" class="col-sm-3 control-label">
<?php echo $hesklang['event_location']; ?>
<i class="fa fa-question-circle settingsquestionmark"
data-toggle="tooltip"
title="<?php echo htmlspecialchars($hesklang['event_location_tooltip']); ?>"></i>
</label>
<div class="col-sm-9">
<input type="text" name="location" class="form-control"
placeholder="<?php echo htmlspecialchars($hesklang['event_location']); ?>">
<div class="help-block with-errors"></div>
</div>
</div>
<div class="form-group">
<label for="category" class="col-sm-3 control-label">
<?php echo $hesklang['category']; ?>
<i class="fa fa-question-circle settingsquestionmark"
data-toggle="tooltip"
title="<?php echo htmlspecialchars($hesklang['event_category_tooltip']); ?>"></i>
</label>
<div class="col-sm-9">
<select name="category" class="form-control"
pattern="[0-9]+"
data-error="<?php echo htmlspecialchars($hesklang['sel_app_cat']); ?>" required>
<?php
if ($hesk_settings['select_cat']) {
echo '<option value="">'.$hesklang['select'].'</option>';
}
foreach ($categories as $category): ?>
<option value="<?php echo $category['id']; ?>" data-background-color="<?php echo htmlspecialchars($category['background_color']); ?>"
data-foreground-color="<?php echo htmlspecialchars($category['foreground_color']); ?>"
data-display-border="<?php echo htmlspecialchars($category['display_border_outline']); ?>">
<?php echo $category['name']; ?>
</option>
<?php endforeach; ?>
</select>
<div class="help-block with-errors"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="start-date" class="col-sm-6 control-label">
<?php echo $hesklang['event_start']; ?>
<i class="fa fa-question-circle settingsquestionmark"
data-toggle="tooltip"
title="<?php echo htmlspecialchars($hesklang['event_start_tooltip']); ?>"></i>
</label>
<div class="col-sm-6">
<input type="text" name="start-date" class="form-control datepicker"
placeholder="<?php echo htmlspecialchars($hesklang['event_start_date']); ?>"
data-error="<?php echo htmlspecialchars($hesklang['this_field_is_required']); ?>"
required>
<input type="text" name="start-time" class="form-control clockpicker"
placeholder="<?php echo htmlspecialchars($hesklang['event_start_time']); ?>"
data-placement="left" data-align="top" data-autoclose="true">
<div class="help-block with-errors"></div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="start-date" class="col-sm-6 control-label">
<?php echo $hesklang['event_start']; ?>
<i class="fa fa-question-circle settingsquestionmark"
data-toggle="tooltip"
title="<?php echo htmlspecialchars($hesklang['event_start_tooltip']); ?>"></i>
</label>
<div class="col-sm-6">
<input type="text" name="start-date" class="form-control datepicker"
placeholder="<?php echo htmlspecialchars($hesklang['event_start_date']); ?>"
data-error="<?php echo htmlspecialchars($hesklang['this_field_is_required']); ?>"
required>
<input type="text" name="start-time" class="form-control clockpicker"
placeholder="<?php echo htmlspecialchars($hesklang['event_start_time']); ?>"
data-placement="left" data-align="top" data-autoclose="true">
<div class="help-block with-errors"></div>
<div class="checkbox">
<label>
<input type="checkbox" name="all-day"> <?php echo $hesklang['event_all_day']; ?>
<div class="checkbox">
<label>
<input type="checkbox" name="all-day"> <?php echo $hesklang['event_all_day']; ?>
</label>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="end-date" class="col-sm-6 control-label">
<?php echo $hesklang['event_end']; ?>
<i class="fa fa-question-circle settingsquestionmark"
data-toggle="tooltip"
title="<?php echo htmlspecialchars($hesklang['event_end_tooltip']); ?>"></i>
</label>
<div class="col-sm-6">
<input type="text" name="end-date" class="form-control datepicker"
placeholder="<?php echo htmlspecialchars($hesklang['event_end_date']); ?>"
data-error="<?php echo htmlspecialchars($hesklang['this_field_is_required']); ?>"
required>
<input type="text" name="end-time" class="form-control clockpicker"
data-placement="left" data-align="top" data-autoclose="true"
placeholder="<?php echo htmlspecialchars($hesklang['event_end_time']); ?>">
<div class="help-block with-errors"></div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="end-date" class="col-sm-6 control-label">
<?php echo $hesklang['event_end']; ?>
<i class="fa fa-question-circle settingsquestionmark"
data-toggle="tooltip"
title="<?php echo htmlspecialchars($hesklang['event_end_tooltip']); ?>"></i>
</label>
<div class="col-sm-6">
<input type="text" name="end-date" class="form-control datepicker"
placeholder="<?php echo htmlspecialchars($hesklang['event_end_date']); ?>"
data-error="<?php echo htmlspecialchars($hesklang['this_field_is_required']); ?>"
required>
<input type="text" name="end-time" class="form-control clockpicker"
data-placement="left" data-align="top" data-autoclose="true"
placeholder="<?php echo htmlspecialchars($hesklang['event_end_time']); ?>">
<div class="help-block with-errors"></div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label for="reminder" class="col-sm-3 control-label">
<?php echo $hesklang['event_reminder']; ?>
<i class="fa fa-question-circle settingsquestionmark"
data-toggle="tooltip"
title="<?php echo htmlspecialchars($hesklang['event_reminder_tooltip']); ?>"></i>
</label>
<div class="col-sm-2">
<input type="text" name="reminder-value" class="form-control" placeholder="#">
</div>
<div class="col-sm-4">
<select name="reminder-unit" class="form-control">
<option value="MINUTE"><?php echo $hesklang['event_min_before_event']; ?></option>
<option value="HOUR"><?php echo $hesklang['event_hours_before_event']; ?></option>
<option value="DAY"><?php echo $hesklang['event_days_before_event']; ?></option>
<option value="WEEK"><?php echo $hesklang['event_weeks_before_event']; ?></option>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label for="reminder" class="col-sm-3 control-label">
<?php echo $hesklang['event_reminder']; ?>
<i class="fa fa-question-circle settingsquestionmark"
data-toggle="tooltip"
title="<?php echo htmlspecialchars($hesklang['event_reminder_tooltip']); ?>"></i>
</label>
<div class="col-sm-2">
<input type="text" name="reminder-value" class="form-control" placeholder="#">
</div>
<div class="col-sm-4">
<select name="reminder-unit" class="form-control">
<option value="0"><?php echo $hesklang['event_min_before_event']; ?></option>
<option value="1"><?php echo $hesklang['event_hours_before_event']; ?></option>
<option value="2"><?php echo $hesklang['event_days_before_event']; ?></option>
<option value="3"><?php echo $hesklang['event_weeks_before_event']; ?></option>
</select>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label for="comments" class="col-sm-3 control-label">
<?php echo $hesklang['event_comments']; ?>
<i class="fa fa-question-circle settingsquestionmark"
data-toggle="tooltip"
title="<?php echo htmlspecialchars($hesklang['event_comments_tooltip']); ?>"></i>
</label>
<div class="col-sm-9">
<textarea name="comments" class="form-control" placeholder="<?php echo htmlspecialchars($hesklang['event_comments']); ?>"></textarea>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label for="comments" class="col-sm-3 control-label">
<?php echo $hesklang['event_comments']; ?>
<i class="fa fa-question-circle settingsquestionmark"
data-toggle="tooltip"
title="<?php echo htmlspecialchars($hesklang['event_comments_tooltip']); ?>"></i>
</label>
<div class="col-sm-9">
<textarea name="comments" class="form-control" placeholder="<?php echo htmlspecialchars($hesklang['event_comments']); ?>"></textarea>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="edit-history">
<br>
<table class="table table-striped">
<thead>
<tr>
<th><?php echo $hesklang['date']; ?></th>
<th><?php echo $hesklang['description']; ?></th>
</tr>
</thead>
<tbody id="history-table"></tbody>
</table>
</div>
</div>
</div>
@ -545,22 +566,34 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php');
<strong><?php echo $hesklang['category']; ?></strong>
<span></span>
</div>
<div class="popover-status">
<strong><?php echo $hesklang['status']; ?></strong>
<span></span>
</div>
<div class="popover-priority">
<strong><?php echo $hesklang['priority']; ?></strong>
<span></span>
</div>
</div>
</div>
<?php
echo mfh_get_hidden_fields_for_language(array('error_loading_events',
'error_deleting_event',
'event_deleted',
'event_created',
'error_creating_event',
'event_updated',
'error_updating_event',
'ticket_due_date_updated',
'error_updating_ticket_due_date',
'critical',
'high',
'medium',
'low',
'audit_event_created',
'audit_event_updated'));
?>
<div style="display: none">
<p id="lang_error_loading_events"><?php echo $hesklang['error_loading_events']; ?></p>
<p id="lang_error_deleting_event"><?php echo $hesklang['error_deleting_event']; ?></p>
<p id="lang_event_deleted"><?php echo $hesklang['event_deleted']; ?></p>
<p id="lang_event_created"><?php echo $hesklang['event_created']; ?></p>
<p id="lang_error_creating_event"><?php echo $hesklang['error_creating_event']; ?></p>
<p id="lang_event_updated"><?php echo $hesklang['event_updated']; ?></p>
<p id="lang_error_updating_event"><?php echo $hesklang['error_updating_event']; ?></p>
<p id="lang_ticket_due_date_updated"><?php echo $hesklang['ticket_due_date_updated']; ?></p>
<p id="lang_error_updating_ticket_due_date"><?php echo $hesklang['error_updating_ticket_due_date']; ?></p>
<p id="setting_first_day_of_week"><?php echo $modsForHesk_settings['first_day_of_week']; ?></p>
<p id="setting_default_view">
<?php
@ -573,6 +606,12 @@ require_once(HESK_PATH . 'inc/show_admin_nav.inc.php');
?>
</p>
</div>
<script type="text/html" id="audit-trail-template">
<tr>
<td data-property="date"></td>
<td data-property="description"></td>
</tr>
</script>
<?php
require_once(HESK_PATH . 'inc/footer.inc.php');

@ -0,0 +1,22 @@
<?php
namespace BusinessLogic\Calendar;
class AbstractEvent {
public $id;
public $startTime;
public $title;
public $categoryId;
public $categoryName;
public $backgroundColor;
public $foregroundColor;
public $displayBorder;
}

@ -0,0 +1,26 @@
<?php
namespace BusinessLogic\Calendar;
use BusinessLogic\Tickets\AuditTrail;
class CalendarEvent extends AbstractEvent {
public $type = 'CALENDAR';
public $endTime;
/* @var $allDay bool */
public $allDay;
public $location;
public $comments;
public $reminderValue;
public $reminderUnits;
/* @var $auditTrail AuditTrail[] */
public $auditTrail = array();
}

@ -0,0 +1,92 @@
<?php
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,
AuditTrailGateway $auditTrailGateway) {
$this->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);
}
}

@ -0,0 +1,41 @@
<?php
namespace BusinessLogic\Calendar;
class ReminderUnit {
const MINUTE = 0;
const HOUR = 1;
const DAY = 2;
const WEEK = 3;
static function getByValue($value) {
switch ($value) {
case 0:
return 'MINUTE';
case 1:
return 'HOUR';
case 2:
return 'DAY';
case 3:
return 'WEEK';
default:
return 'UNKNOWN';
}
}
static function getByName($name) {
switch ($name) {
case 'MINUTE':
return self::MINUTE;
case 'HOUR':
return self::HOUR;
case 'DAY':
return self::DAY;
case 'WEEK':
return self::WEEK;
default:
return null;
}
}
}

@ -0,0 +1,30 @@
<?php
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;
/* @var $reminderUserId int|null */
public $reminderUserId;
/* @var $includeTickets bool */
public $includeTickets;
/* @var $includeUnassignedTickets bool */
public $includeUnassignedTickets;
/* @var $includeTicketsAssignedToOthers bool */
public $includeTicketsAssignedToOthers;
}

@ -0,0 +1,20 @@
<?php
namespace BusinessLogic\Calendar;
class TicketEvent extends AbstractEvent {
public $type = 'TICKET';
public $trackingId;
public $subject;
public $url;
public $owner;
public $priority;
public $status;
}

@ -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;

@ -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;
}
}

@ -30,4 +30,8 @@ class Helpers extends \BaseClass {
static function boolval($val) {
return $val == true;
}
static function heskHtmlSpecialCharsDecode($in) {
return str_replace(array('&amp;', '&lt;', '&gt;', '&quot;'), array('&', '<', '>', '"'), $in);
}
}

@ -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';
}

@ -5,4 +5,5 @@ namespace BusinessLogic\Tickets;
class AuditTrailEntityType extends \BaseClass {
const TICKET = 'TICKET';
const CALENDAR_EVENT = 'CALENDAR_EVENT';
}

@ -0,0 +1,9 @@
<?php
namespace BusinessLogic\Tickets;
class AuditTrailEvent extends \BaseClass {
const DUE_DATE_REMOVED = 'audit_due_date_removed';
const DUE_DATE_CHANGED = 'audit_due_date_changed';
}

@ -3,6 +3,7 @@
namespace BusinessLogic\Tickets;
use BusinessLogic\DateTimeHelpers;
use BusinessLogic\Exceptions\AccessViolationException;
use BusinessLogic\Exceptions\ApiFriendlyException;
use BusinessLogic\Exceptions\ValidationException;
@ -13,6 +14,7 @@ use BusinessLogic\Tickets\CustomFields\CustomFieldValidator;
use BusinessLogic\ValidationModel;
use BusinessLogic\Validators;
use Core\Constants\CustomField;
use DataAccess\AuditTrail\AuditTrailGateway;
use DataAccess\Tickets\TicketGateway;
class TicketEditor extends \BaseClass {
@ -22,10 +24,15 @@ class TicketEditor extends \BaseClass {
/* @var $userToTicketChecker UserToTicketChecker */
private $userToTicketChecker;
/* @var $auditTrailGateway AuditTrailGateway */
private $auditTrailGateway;
function __construct(TicketGateway $ticketGateway,
UserToTicketChecker $userToTicketChecker) {
UserToTicketChecker $userToTicketChecker,
AuditTrailGateway $auditTrailGateway) {
$this->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);
}
}
}

@ -0,0 +1,127 @@
<?php
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;
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;
use RRule\RRule;
use RRule\RSet;
class CalendarController extends \BaseClass {
function get() {
/* @var $userContext UserContext */
global $applicationContext, $hesk_settings, $userContext;
if (!isset($_GET['start']) || !isset($_GET['end'])) {
$validationModel = new ValidationModel();
$validationModel->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;
}
}

@ -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;

@ -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';
}
}
}

@ -0,0 +1,246 @@
<?php
namespace DataAccess\Calendar;
use BusinessLogic\Calendar\AbstractEvent;
use BusinessLogic\Calendar\CalendarEvent;
use BusinessLogic\Calendar\ReminderUnit;
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;
class CalendarGateway extends CommonDao {
/**
* @param $searchEventsFilter SearchEventsFilter
* @param $heskSettings array
* @return AbstractEvent[]
*/
public function getEventsForStaff($searchEventsFilter, $heskSettings) {
$this->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();
}
}

@ -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();
}
}

101
api/composer.lock generated

@ -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",

@ -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),

@ -111,8 +111,12 @@ require_once(HESK_PATH . 'inc/header.inc.php');
</div>
</div>
</div>
<?php
echo mfh_get_hidden_fields_for_language(array(
'error_loading_events'
));
?>
<div style="display: none">
<p id="lang_error_loading_events"><?php echo $hesklang['error_loading_events']; ?></p>
<p id="setting_default_view"><?php echo $modsForHesk_settings['default_calendar_view']; ?></p>
<p id="setting_first_day_of_week"><?php echo $modsForHesk_settings['first_day_of_week']; ?></p>
</div>

@ -78,6 +78,7 @@ header('X-UA-Compatible: IE=edge');
<script type="text/javascript" src="<?php echo HESK_PATH; ?>internal-api/js/lang.js?v=<?php echo MODS_FOR_HESK_BUILD; ?>"></script>
<script type="text/javascript" src="<?php echo HESK_PATH; ?>js/clipboard.min.js?v=<?php echo MODS_FOR_HESK_BUILD; ?>"></script>
<script type="text/javascript" src="<?php echo HESK_PATH; ?>js/bootstrap-select.js?v=<?php echo MODS_FOR_HESK_BUILD; ?>"></script>
<script type="text/javascript" src="<?php echo HESK_PATH; ?>js/sprintf.min.js?v=<?php echo MODS_FOR_HESK_BUILD; ?>"></script>
<?php
if (defined('EXTRA_JS')) {
echo EXTRA_JS;

@ -1,29 +0,0 @@
<?php
define('IN_SCRIPT', 1);
define('HESK_PATH', '../../');
define('INTERNAL_API_PATH', '../');
require_once(HESK_PATH . 'hesk_settings.inc.php');
require_once(HESK_PATH . 'inc/common.inc.php');
require_once(HESK_PATH . 'inc/attachments.inc.php');
require_once(HESK_PATH . 'inc/posting_functions.inc.php');
require_once(INTERNAL_API_PATH . 'core/output.php');
require_once(INTERNAL_API_PATH . 'dao/calendar_dao.php');
require_once(INTERNAL_API_PATH . 'core/cors.php');
hesk_session_start();
hesk_load_internal_api_database_functions();
hesk_dbConnect();
$modsForHesk_settings = mfh_getSettings();
// Routing
$request_method = $_SERVER['REQUEST_METHOD'];
if ($request_method === 'GET') {
$start = hesk_GET('start');
$end = hesk_GET('end');
$events = get_events($start, $end, $hesk_settings, false);
return output($events);
}
return http_response_code(400);

@ -1,218 +0,0 @@
<?php
function get_events($start, $end, $hesk_settings, $staff = true) {
global $hesk_settings, $hesklang;
$start_time_sql = "CONVERT_TZ(FROM_UNIXTIME(" . hesk_dbEscape($start) . " / 1000), @@session.time_zone, '+00:00')";
$end_time_sql = "CONVERT_TZ(FROM_UNIXTIME(" . hesk_dbEscape($end) . " / 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($hesk_settings['db_pfix']) . "calendar_event` AS `events`
INNER JOIN `" . hesk_dbEscape($hesk_settings['db_pfix']) . "categories` AS `categories`
ON `events`.`category` = `categories`.`id` ";
if ($staff) {
$sql .= "LEFT JOIN `" . hesk_dbEscape($hesk_settings['db_pfix']) . "calendar_event_reminder` AS `reminders` ON
`reminders`.`user_id` = " . intval($_SESSION['id']) . " AND `reminders`.`event_id` = `events`.`id`";
}
$sql .= "WHERE NOT (`end` < {$start_time_sql} OR `start` > {$end_time_sql}) AND `categories`.`usage` <> 1";
if (!$staff) {
$sql .= " AND `categories`.`type` = '0'";
}
$rs = hesk_dbQuery($sql);
$events = array();
while ($row = hesk_dbFetchAssoc($rs)) {
// Skip the event if the user does not have access to it
if ($staff && !$_SESSION['isadmin'] && !in_array($row['category'], $_SESSION['categories'])) {
continue;
}
mfh_log_debug('Calendar', "Creating event with id: {$row['id']}", '');
$event['type'] = 'CALENDAR';
$event['id'] = intval($row['id']);
$event['startTime'] = $row['start'];
$event['endTime'] = $row['end'];
$event['allDay'] = $row['all_day'] ? true : false;
$event['title'] = $row['name'];
$event['location'] = $row['location'];
$event['comments'] = $row['comments'];
$event['categoryId'] = $row['category'];
$event['categoryName'] = $row['category_name'];
$event['backgroundColor'] = $row['background_color'];
$event['foregroundColor'] = $row['foreground_color'];
$event['displayBorder'] = $row['display_border'];
if ($staff) {
$event['reminderValue'] = $row['reminder_value'];
$event['reminderUnits'] = $row['reminder_unit'];
}
$events[] = $event;
}
if ($staff) {
$old_time_setting = $hesk_settings['timeformat'];
$hesk_settings['timeformat'] = 'Y-m-d';
$current_date = hesk_date();
$hesk_settings['timeformat'] = $old_time_setting;
$sql = "SELECT `trackid`, `subject`, `due_date`, `category`, `categories`.`name` AS `category_name`, `categories`.`background_color` AS `background_color`,
`categories`.`foreground_color` AS `foreground_color`, `categories`.`display_border_outline` AS `display_border`,
CASE WHEN `due_date` < '{$current_date}' THEN 1 ELSE 0 END AS `overdue`, `owner`.`name` AS `owner_name`, `tickets`.`owner` AS `owner_id`,
`tickets`.`priority` AS `priority`
FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` AS `tickets`
INNER JOIN `" . hesk_dbEscape($hesk_settings['db_pfix']) . "categories` AS `categories`
ON `categories`.`id` = `tickets`.`category`
AND `categories`.`usage` <> 2
LEFT JOIN `" . hesk_dbEscape($hesk_settings['db_pfix']) . "users` AS `owner`
ON `tickets`.`owner` = `owner`.`id`
WHERE `due_date` >= CONVERT_TZ(FROM_UNIXTIME(" . hesk_dbEscape($start)
. " / 1000), @@session.time_zone, '+00:00')
AND `due_date` <= CONVERT_TZ(FROM_UNIXTIME(" . hesk_dbEscape($end) . " / 1000), @@session.time_zone, '+00:00')
AND `status` IN (SELECT `id` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "statuses` WHERE `IsClosed` = 0) ";
$rs = hesk_dbQuery($sql);
while ($row = hesk_dbFetchAssoc($rs)) {
// Skip the ticket if the user does not have access to it
if (!hesk_checkPermission('can_view_tickets', 0)
|| ($row['owner_id'] && $row['owner_id'] != $_SESSION['id'] && !hesk_checkPermission('can_view_ass_others', 0))
|| (!$row['owner_id'] && !hesk_checkPermission('can_view_unassigned', 0))) {
continue;
}
$event['type'] = 'TICKET';
$event['trackingId'] = $row['trackid'];
$event['subject'] = $row['subject'];
$event['title'] = $row['subject'];
$event['startTime'] = $row['due_date'];
$event['url'] = $hesk_settings['hesk_url'] . '/' . $hesk_settings['admin_dir'] . '/admin_ticket.php?track=' . $event['trackingId'];
$event['categoryId'] = $row['category'];
$event['categoryName'] = $row['category_name'];
$event['backgroundColor'] = $row['background_color'];
$event['foregroundColor'] = $row['foreground_color'];
$event['displayBorder'] = $row['display_border'];
$event['owner'] = $row['owner_name'];
$priorities = array(
0 => $hesklang['critical'],
1 => $hesklang['high'],
2 => $hesklang['medium'],
3 => $hesklang['low']
);
$event['priority'] = $priorities[$row['priority']];
$events[] = $event;
}
}
return $events;
}
function create_event($event, $hesk_settings) {
// Make sure the user can create events in this category
if (!$_SESSION['isadmin'] && !in_array($event['category'], $_SESSION['categories'])) {
print_error('Access Denied', 'You cannot create an event in this category');
}
$event['start'] = date('Y-m-d H:i:s', strtotime($event['start']));
$event['end'] = date('Y-m-d H:i:s', strtotime($event['end']));
$event['all_day'] = $event['all_day'] ? 1 : 0;
$sql = "INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "calendar_event` (`start`, `end`, `all_day`,
`name`, `location`, `comments`, `category`) VALUES (
'" . hesk_dbEscape($event['start']) . "', '" . hesk_dbEscape($event['end']) . "', '" . hesk_dbEscape($event['all_day']) . "',
'" . hesk_dbEscape(addslashes($event['title'])) . "', '" . hesk_dbEscape(addslashes($event['location'])) . "', '" . hesk_dbEscape(addslashes($event['comments'])) . "',
" . intval($event['category']) . ")";
hesk_dbQuery($sql);
$event_id = hesk_dbInsertID();
if ($event['reminder_amount'] != null) {
$sql = "INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "calendar_event_reminder` (`user_id`, `event_id`,
`amount`, `unit`) VALUES (" . intval($event['reminder_user']) . ", " . intval($event_id) . ", " . intval($event['reminder_amount']) . ",
" . intval($event['reminder_units']) . ")";
hesk_dbQuery($sql);
}
return $event_id;
}
function update_event($event, $hesk_settings) {
// Make sure the user can edit events in this category
if (!$_SESSION['isadmin'] && !in_array($event['category'], $_SESSION['categories'])) {
print_error('Access Denied', 'You cannot edit an event in this category');
}
$event['start'] = date('Y-m-d H:i:s', strtotime($event['start']));
$event['end'] = date('Y-m-d H:i:s', strtotime($event['end']));
if ($event['create_ticket_date'] != null) {
$event['create_ticket_date'] = date('Y-m-d H:i:s', strtotime($event['create_ticket_date']));
}
$event['all_day'] = $event['all_day'] ? 1 : 0;
$event['assign_to'] = $event['assign_to'] != null ? intval($event['assign_to']) : 'NULL';
$sql = "UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "calendar_event` SET `start` = '" . hesk_dbEscape($event['start'])
. "', `end` = '" . hesk_dbEscape($event['end']) . "', `all_day` = '" . hesk_dbEscape($event['all_day']) . "', `name` = '"
. hesk_dbEscape(addslashes($event['title'])) . "', `location` = '" . hesk_dbEscape(addslashes($event['location'])) . "', `comments` = '"
. hesk_dbEscape(addslashes($event['comments'])) . "', `category` = " . intval($event['category']) . " WHERE `id` = " . intval($event['id']);
if ($event['reminder_amount'] != null) {
$delete_sql = "DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "calendar_event_reminder` WHERE `event_id` = " . intval($event['id'])
. " AND `user_id` = " . intval($event['reminder_user']);
hesk_dbQuery($delete_sql);
$insert_sql = "INSERT INTO `" . hesk_dbEscape($hesk_settings['db_pfix']) . "calendar_event_reminder` (`user_id`, `event_id`,
`amount`, `unit`) VALUES (" . intval($event['reminder_user']) . ", " . intval($event['id']) . ", " . intval($event['reminder_amount']) . ",
" . intval($event['reminder_units']) . ")";
hesk_dbQuery($insert_sql);
}
hesk_dbQuery($sql);
}
function delete_event($id, $hesk_settings) {
// Make sure the user can delete events in this category
$categoryRs = hesk_dbQuery('SELECT `category` FROM `' . hesk_dbEscape($hesk_settings['db_pfix']) . 'calendar_event` WHERE `id` = ' . intval($id));
$category = hesk_dbFetchAssoc($categoryRs);
if (!$_SESSION['isadmin'] && !in_array($category['category'], $_SESSION['categories'])) {
print_error('Access Denied', 'You cannot delete events in this category');
}
$sql = "DELETE FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "calendar_event` WHERE `id` = " . intval($id);
hesk_dbQuery($sql);
}
function update_ticket_due_date($ticket, $hesk_settings) {
$ticket_id_rs = hesk_dbQuery("SELECT `id` FROM `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` WHERE `trackid` = '" . hesk_dbEscape($ticket['trackid']) . "'");
$ticket_id = hesk_dbFetchAssoc($ticket_id_rs);
$due_date = 'NULL';
$language_key = 'audit_due_date_removed';
$audit_array = array(0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')');
if ($ticket['due_date'] != NULL) {
$audit_array = array(
0 => $_SESSION['name'] . ' (' . $_SESSION['user'] . ')',
1 => date('Y-m-d H:i:s', strtotime($ticket['due_date']))
);
$due_date = "'" . date('Y-m-d H:i:s', strtotime($ticket['due_date'])) . "'";
$language_key = 'audit_due_date_changed';
}
$sql = "UPDATE `" . hesk_dbEscape($hesk_settings['db_pfix']) . "tickets` SET `due_date` = {$due_date}, `overdue_email_sent` = '0'
WHERE `trackid` = '" . hesk_dbEscape($ticket['trackid']) . "'";
mfh_insert_audit_trail_record($ticket_id['id'], 'TICKET', $language_key, hesk_date(),
$audit_array);
hesk_dbQuery($sql);
}

@ -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() {
@ -61,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();
@ -124,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,
@ -140,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
};
}

@ -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) {

@ -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() {
@ -51,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();
@ -74,8 +66,16 @@ $(document).ready(function() {
.find('.popover-owner span').text(event.owner).end()
.find('.popover-subject span').text(event.subject).end()
.find('.popover-category span').text(event.categoryName).end()
.find('.popover-priority span').text(event.priority);
.find('.popover-priority span').text(event.priority).end()
.find('.popover-status span').text(event.status).end();
} else {
if (event.allDay) {
endDate = event.end.clone();
endDate.add(-1, 'days');
} else {
format += ', HH:mm';
}
if (event.location === '') {
$contents.find('.popover-location').hide();
}
@ -90,7 +90,7 @@ $(document).ready(function() {
var $eventMarkup = $(this);
var eventTitle = event.title;
if (event.fontIconMarkup != undefined) {
if (event.fontIconMarkup !== undefined) {
eventTitle = event.fontIconMarkup + '&nbsp;' + eventTitle;
}
@ -161,17 +161,15 @@ $(document).ready(function() {
$editForm.find('#delete-button').click(function() {
var id = $editForm.find('input[name="id"]').val();
var data = {
id: id,
action: 'delete'
};
$.ajax({
method: 'POST',
url: heskPath + 'internal-api/admin/calendar/',
data: data,
url: heskPath + 'api/index.php/v1/calendar/events/staff/' + id,
headers: {
'X-Internal-Call': true,
'X-HTTP-Method-Override': 'DELETE'
},
success: function() {
removeFromCalendar(data.id);
removeFromCalendar(id);
mfhAlert.success(mfhLang.text('event_deleted'));
$('#edit-event-modal').modal('hide');
},
@ -195,6 +193,9 @@ $(document).ready(function() {
dateFormat = 'YYYY-MM-DD HH:mm:ss';
}
var reminderValue = $createForm.find('input[name="reminder-value"]').val();
var reminderUnits = $createForm.find('select[name="reminder-unit"]').val();
var data = {
title: $createForm.find('input[name="name"]').val(),
location: $createForm.find('input[name="location"]').val(),
@ -203,22 +204,23 @@ $(document).ready(function() {
allDay: allDay,
comments: $createForm.find('textarea[name="comments"]').val(),
categoryId: $createForm.find('select[name="category"]').val(),
action: 'create',
type: 'CALENDAR',
backgroundColor: $createForm.find('select[name="category"] :selected').attr('data-background-color'),
foregroundColor: $createForm.find('select[name="category"] :selected').attr('data-foreground-color'),
displayBorder: $createForm.find('select[name="category"] :selected').attr('data-display-border'),
categoryName: $createForm.find('select[name="category"] :selected').text().trim(),
reminderValue: $createForm.find('input[name="reminder-value"]').val(),
reminderUnits: $createForm.find('select[name="reminder-unit"]').val()
reminderValue: reminderValue === "" ? null : reminderValue,
reminderUnits: reminderValue === "" ? null : reminderUnits
};
$.ajax({
method: 'POST',
url: heskPath + 'internal-api/admin/calendar/',
data: data,
success: function(id) {
addToCalendar(id, data, $('#lang_event_created').text());
url: heskPath + 'api/index.php/v1/calendar/events/staff',
data: JSON.stringify(data),
contentType: 'json',
headers: { 'X-Internal-Call': true },
success: function(createdEvent) {
addToCalendar(createdEvent.id, data, $('#lang_event_created').text());
$('#create-event-modal').modal('hide');
updateCategoryVisibility();
},
@ -243,6 +245,9 @@ $(document).ready(function() {
dateFormat = 'YYYY-MM-DD HH:mm:ss';
}
var reminderValue = $createForm.find('input[name="reminder-value"]').val();
var reminderUnits = $createForm.find('select[name="reminder-unit"]').val();
var data = {
id: $form.find('input[name="id"]').val(),
title: $form.find('input[name="name"]').val(),
@ -251,21 +256,26 @@ $(document).ready(function() {
endTime: moment(end).format(dateFormat),
allDay: allDay,
comments: $form.find('textarea[name="comments"]').val(),
categoryId: $form.find('select[name="category"]').val(),
categoryId: parseInt($form.find('select[name="category"]').val()),
backgroundColor: $form.find('select[name="category"] :selected').attr('data-background-color'),
foregroundColor: $form.find('select[name="category"] :selected').attr('data-foreground-color'),
displayBorder: $form.find('select[name="category"] :selected').attr('data-display-border'),
categoryName: $form.find('select[name="category"] :selected').text().trim(),
action: 'update',
reminderValue: $form.find('input[name="reminder-value"]').val(),
reminderUnits: $form.find('select[name="reminder-unit"]').val()
reminderValue: reminderValue === "" ? null : reminderValue,
reminderUnits: reminderValue === "" ? null : reminderUnits,
};
$.ajax({
method: 'POST',
url: heskPath + 'internal-api/admin/calendar/',
data: data,
success: function() {
url: heskPath + 'api/index.php/v1/calendar/events/staff/' + data.id,
data: JSON.stringify(data),
contentType: 'json',
headers: {
'X-Internal-Call': true,
'X-HTTP-Method-Override': 'PUT'
},
success: function(updatedEvent) {
data.auditTrail = updatedEvent.auditTrail;
removeFromCalendar(data.id);
addToCalendar(data.id, data, $('#lang_event_updated').text());
$('#edit-event-modal').modal('hide');
@ -290,8 +300,16 @@ function removeFromCalendar(id) {
}
function buildEvent(id, dbObject) {
if (dbObject.type == 'TICKET') {
var priorities = [];
priorities['CRITICAL'] = mfhLang.text('critical');
priorities['HIGH'] = mfhLang.text('high');
priorities['MEDIUM'] = mfhLang.text('medium');
priorities['LOW'] = mfhLang.text('low');
if (dbObject.type === 'TICKET') {
return {
id: id,
title: dbObject.title,
subject: dbObject.subject,
trackingId: dbObject.trackingId,
@ -306,8 +324,9 @@ function buildEvent(id, dbObject) {
categoryName: dbObject.categoryName,
className: 'category-' + dbObject.categoryId,
owner: dbObject.owner,
priority: dbObject.priority,
fontIconMarkup: getIcon(dbObject)
priority: priorities[dbObject.priority],
fontIconMarkup: getIcon(dbObject),
status: dbObject.status
};
}
@ -334,7 +353,8 @@ function buildEvent(id, dbObject) {
borderColor: parseInt(dbObject.displayBorder) === 1 ? dbObject.foregroundColor : dbObject.backgroundColor,
reminderValue: dbObject.reminderValue == null ? '' : dbObject.reminderValue,
reminderUnits: dbObject.reminderUnits,
fontIconMarkup: '<i class="fa fa-calendar"></i>'
fontIconMarkup: '<i class="fa fa-calendar"></i>',
auditTrail: dbObject.auditTrail
};
}
@ -379,7 +399,7 @@ function displayCreateModal(date, viewName) {
.find('input[name="location"]').val('').end()
.find('textarea[name="comments"]').val('').end()
.find('select[name="category"]').val($form.find('select[name="category"] option:first-child').val()).end()
.find('select[name="reminder-unit"]').val(0).end()
.find('select[name="reminder-unit"]').val("MINUTE").end()
.find('input[name="reminder-value"]').val('').end();
var $modal = $('#create-event-modal');
@ -454,6 +474,24 @@ function displayEditModal(date) {
$form.find('select[name="category"] option[value="' + date.categoryId + '"]').prop('selected', true);
var $auditTrail = $('#edit-history');
if (date.auditTrail.length === 0) {
$('.nav-tabs[role="tablist"]').hide();
} else {
$('.nav-tabs[role="tablist"]').show();
var $historyTable = $('#history-table');
$historyTable.html('');
$.each(date.auditTrail, function() {
var $template = $($('#audit-trail-template').html());
$template.find('[data-property="date"]').text(this.date).end()
.find('[data-property="description"]').text(vsprintf(mfhLang.text(this.languageKey), this.replacementValues));
$historyTable.append($template);
});
}
$('#edit-modal-tabs').find('a:first').tab('show');
$('#edit-event-modal').modal('show');
}
@ -477,15 +515,19 @@ function updateCategoryVisibility() {
function respondToDragAndDrop(event, delta, revertFunc) {
var heskPath = $('p#hesk-path').text();
if (event.type === 'TICKET') {
var uri = 'api/index.php/v1/staff/tickets/' + event.id + '/due-date';
$.ajax({
method: 'POST',
url: heskPath + 'internal-api/admin/calendar/',
data: {
trackingId: event.trackingId,
action: 'update-ticket',
dueDate: event.start.format('YYYY-MM-DD')
url: heskPath + uri,
headers: {
'X-Internal-Call': true,
'X-HTTP-Method-Override': 'PATCH'
},
data: JSON.stringify({
dueDate: event.start.format('YYYY-MM-DD')
}),
success: function() {
event.fontIconMarkup = getIcon({
startTime: event.start
@ -526,12 +568,19 @@ function respondToDragAndDrop(event, delta, revertFunc) {
reminderValue: event.reminderValue,
reminderUnits: event.reminderUnits
};
var url = heskPath + 'api/index.php/v1/calendar/events/staff/' + event.id;
$.ajax({
method: 'POST',
url: heskPath + 'internal-api/admin/calendar/',
data: data,
success: function() {
mfhAlert.success(mfhLang.text('event_updated'));
url: url,
data: JSON.stringify(data),
headers: {
'X-Internal-Call': true,
'X-HTTP-Method-Override': 'PUT'
},
success: function(updatedEvent) {
removeFromCalendar(updatedEvent.id);
addToCalendar(updatedEvent.id, updatedEvent, $('#lang_event_updated').text());
},
error: function() {
mfhAlert.error(mfhLang.text('error_updating_event'));

3
js/sprintf.min.js vendored

@ -0,0 +1,3 @@
/*! sprintf-js v1.1.0 | Copyright (c) 2007-present, Alexandru Marasteanu <hello@alexei.ro> | BSD-3-Clause */
!function(e){"use strict";function t(){var e=arguments[0],r=t.cache;return r[e]||(r[e]=t.parse(e)),t.format.call(null,r[e],arguments)}function r(e){return"number"==typeof e?"number":"string"==typeof e?"string":Object.prototype.toString.call(e).slice(8,-1).toLowerCase()}function n(e,t){return t>=0&&t<=7&&i[e]?i[e][t]:Array(t+1).join(e)}var s={not_string:/[^s]/,not_bool:/[^t]/,not_type:/[^T]/,not_primitive:/[^v]/,number:/[diefg]/,numeric_arg:/[bcdiefguxX]/,json:/[j]/,not_json:/[^j]/,text:/^[^\x25]+/,modulo:/^\x25{2}/,placeholder:/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijostTuvxX])/,key:/^([a-z_][a-z_\d]*)/i,key_access:/^\.([a-z_][a-z_\d]*)/i,index_access:/^\[(\d+)\]/,sign:/^[\+\-]/};t.format=function(e,a){var i,o,l,c,p,f,u,g=1,_=e.length,d="",b=[],h=!0,x="";for(o=0;o<_;o++)if("string"===(d=r(e[o])))b[b.length]=e[o];else if("array"===d){if(c=e[o],c[2])for(i=a[g],l=0;l<c[2].length;l++){if(!i.hasOwnProperty(c[2][l]))throw new Error(t('[sprintf] property "%s" does not exist',c[2][l]));i=i[c[2][l]]}else i=c[1]?a[c[1]]:a[g++];if(s.not_type.test(c[8])&&s.not_primitive.test(c[8])&&"function"==r(i)&&(i=i()),s.numeric_arg.test(c[8])&&"number"!=r(i)&&isNaN(i))throw new TypeError(t("[sprintf] expecting number but found %s",r(i)));switch(s.number.test(c[8])&&(h=i>=0),c[8]){case"b":i=parseInt(i,10).toString(2);break;case"c":i=String.fromCharCode(parseInt(i,10));break;case"d":case"i":i=parseInt(i,10);break;case"j":i=JSON.stringify(i,null,c[6]?parseInt(c[6]):0);break;case"e":i=c[7]?parseFloat(i).toExponential(c[7]):parseFloat(i).toExponential();break;case"f":i=c[7]?parseFloat(i).toFixed(c[7]):parseFloat(i);break;case"g":i=c[7]?parseFloat(i).toPrecision(c[7]):parseFloat(i);break;case"o":i=i.toString(8);break;case"s":i=String(i),i=c[7]?i.substring(0,c[7]):i;break;case"t":i=String(!!i),i=c[7]?i.substring(0,c[7]):i;break;case"T":i=r(i),i=c[7]?i.substring(0,c[7]):i;break;case"u":i=parseInt(i,10)>>>0;break;case"v":i=i.valueOf(),i=c[7]?i.substring(0,c[7]):i;break;case"x":i=parseInt(i,10).toString(16);break;case"X":i=parseInt(i,10).toString(16).toUpperCase()}s.json.test(c[8])?b[b.length]=i:(!s.number.test(c[8])||h&&!c[3]?x="":(x=h?"+":"-",i=i.toString().replace(s.sign,"")),f=c[4]?"0"===c[4]?"0":c[4].charAt(1):" ",u=c[6]-(x+i).length,p=c[6]&&u>0?n(f,u):"",b[b.length]=c[5]?x+i+p:"0"===f?x+p+i:p+x+i)}return b.join("")},t.cache=Object.create(null),t.parse=function(e){for(var t=e,r=[],n=[],a=0;t;){if(null!==(r=s.text.exec(t)))n[n.length]=r[0];else if(null!==(r=s.modulo.exec(t)))n[n.length]="%";else{if(null===(r=s.placeholder.exec(t)))throw new SyntaxError("[sprintf] unexpected placeholder");if(r[2]){a|=1;var i=[],o=r[2],l=[];if(null===(l=s.key.exec(o)))throw new SyntaxError("[sprintf] failed to parse named argument key");for(i[i.length]=l[1];""!==(o=o.substring(l[0].length));)if(null!==(l=s.key_access.exec(o)))i[i.length]=l[1];else{if(null===(l=s.index_access.exec(o)))throw new SyntaxError("[sprintf] failed to parse named argument key");i[i.length]=l[1]}r[2]=i}else a|=2;if(3===a)throw new Error("[sprintf] mixing positional and named placeholders is not (yet) supported");n[n.length]=r}t=t.substring(r[0].length)}return n};var a=function(e,r,n){return n=(r||[]).slice(0),n.splice(0,0,e),t.apply(null,n)},i={0:["","0","00","000","0000","00000","000000","0000000"]," ":[""," "," "," "," "," "," "," "],_:["","_","__","___","____","_____","______","_______"]};"undefined"!=typeof exports&&(exports.sprintf=t,exports.vsprintf=a),void 0!==e&&(e.sprintf=t,e.vsprintf=a,"function"==typeof define&&define.amd&&define(function(){return{sprintf:t,vsprintf:a}}))}("undefined"==typeof window?this:window);
//# sourceMappingURL=sprintf.min.js.map

@ -2215,6 +2215,8 @@ $hesklang['audit_linked_ticket'] = '%s linked ticket %s to this ticket';
$hesklang['audit_unlinked_ticket'] = '%s unlinked ticket %s';
// Added or modified in Mods for HESK 3.3.0
$hesklang['audit_event_created'] = '%s created event';
$hesklang['audit_event_updated'] = '%s updated event';
$hesklang['error_retrieving_sm'] = 'An error occurred retrieving service messages!';
$hesklang['error_saving_updating_sm'] = 'An error occurred creating / saving the service message!';
$hesklang['error_deleting_sm'] = 'An error occurred when trying to delete the service message.';

Loading…
Cancel
Save