mirror of
https://github.com/itflow-org/itflow
synced 2026-08-10 01:27:15 +00:00
Fix ticket schedule cancellation never cancelling the calendar event
This commit is contained in:
@@ -3062,7 +3062,7 @@ if (isset($_POST['edit_ticket_schedule'])) {
|
||||
|
||||
|
||||
/// Create iCal event
|
||||
$cal_str = createiCalStr($schedule, $cal_subject, $cal_description, $cal_location);
|
||||
$cal_str = createiCalStr($schedule, $cal_subject, $cal_description, $cal_location, getTicketCalendarUid($ticket_id));
|
||||
|
||||
// Notify the agent of the scheduled work
|
||||
$data[] = [
|
||||
@@ -3183,7 +3183,6 @@ if (isset($_GET['cancel_ticket_schedule'])) {
|
||||
$ticket_number = intval($row['ticket_number']);
|
||||
$ticket_subject = escapeSql($row['ticket_subject']);
|
||||
$ticket_schedule = escapeSql($row['ticket_schedule']);
|
||||
$ticket_cal_str = escapeSql($row['ticket_cal_str']);
|
||||
|
||||
// Don't Enforce Client Access if Ticket doesn't have an assigned client
|
||||
if ($client_id) {
|
||||
@@ -3203,9 +3202,6 @@ if (isset($_GET['cancel_ticket_schedule'])) {
|
||||
$config_ticket_from_name = escapeSql($config_ticket_from_name);
|
||||
$session_company_name = escapeSql($session_company_name);
|
||||
|
||||
//Create iCal event
|
||||
$cal_str = createiCalStrCancel($ticket_cal_str);
|
||||
|
||||
//Send emails
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT client_name, contact_email, contact_name, ticket_client_id, ticket_details, ticket_number,
|
||||
@@ -3229,6 +3225,10 @@ if (isset($_GET['cancel_ticket_schedule'])) {
|
||||
$user_name = escapeSql($row['user_name']);
|
||||
$user_email = escapeSql($row['user_email']);
|
||||
|
||||
//Create the iCal cancellation - same UID and subject as the original invite
|
||||
$cal_subject = $ticket_number . ": " . $client_name . " - " . $ticket_subject;
|
||||
$cal_str = createiCalStrCancel($ticket_schedule, $cal_subject, getTicketCalendarUid($ticket_id));
|
||||
|
||||
// Notify the agent of the cancellation
|
||||
$data[] = [
|
||||
// User Email
|
||||
|
||||
@@ -415,7 +415,16 @@ function addToMailQueue($data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function createiCalStr($datetime, $title, $description, $location) {
|
||||
function getTicketCalendarUid($ticket_id) {
|
||||
// An invite and its later cancellation MUST carry the same UID or the
|
||||
// recipient's calendar client cannot match them up. Derive it from the
|
||||
// ticket so it is stable across both, rather than from the current time.
|
||||
$ticket_id = intval($ticket_id);
|
||||
$host = $_SERVER['SERVER_NAME'] ?? 'itflow';
|
||||
return "ticket-$ticket_id@$host";
|
||||
}
|
||||
|
||||
function createiCalStr($datetime, $title, $description, $location, $uid = null) {
|
||||
require_once "../libs/zapcal/zapcallib.php";
|
||||
|
||||
// Create the iCal object
|
||||
@@ -431,8 +440,11 @@ function createiCalStr($datetime, $title, $description, $location) {
|
||||
// Todo: adjust this for actual duration
|
||||
$event->addNode(new ZCiCalDataNode("DTEND:" . ZCiCal::fromSqlDateTime($datetime)));
|
||||
$event->addNode(new ZCiCalDataNode("DTSTAMP:" . ZCiCal::fromSqlDateTime()));
|
||||
$uid = date('Y-m-d-H-i-s') . "@" . $_SERVER['SERVER_NAME'];
|
||||
if (empty($uid)) {
|
||||
$uid = date('Y-m-d-H-i-s') . "@" . ($_SERVER['SERVER_NAME'] ?? 'itflow');
|
||||
}
|
||||
$event->addNode(new ZCiCalDataNode("UID:" . $uid));
|
||||
$event->addNode(new ZCiCalDataNode("SEQUENCE:0"));
|
||||
$event->addNode(new ZCiCalDataNode("LOCATION:" . $location));
|
||||
$event->addNode(new ZCiCalDataNode("DESCRIPTION:" . $description));
|
||||
// Todo: add organizer details
|
||||
@@ -442,31 +454,27 @@ function createiCalStr($datetime, $title, $description, $location) {
|
||||
return $cal_event->export();
|
||||
}
|
||||
|
||||
function createiCalStrCancel($originaliCalStr) {
|
||||
function createiCalStrCancel($datetime, $title, $uid) {
|
||||
require_once "../libs/zapcal/zapcallib.php";
|
||||
|
||||
// Import the original iCal string
|
||||
$cal_event = new ZCiCal($originaliCalStr);
|
||||
// Build the cancellation fresh. There is no stored copy of the original
|
||||
// invite to reopen - the match is made by UID, not by the body.
|
||||
$cal_event = new ZCiCal();
|
||||
|
||||
// Iterate through the iCalendar object to find VEVENT nodes
|
||||
foreach($cal_event->tree->child as $node) {
|
||||
if($node->getName() == "VEVENT") {
|
||||
// Check if STATUS node exists, update it, or add a new one
|
||||
$statusFound = false;
|
||||
foreach($node->data as $key => $value) {
|
||||
if($key == "STATUS") {
|
||||
$value->setValue("CANCELLED");
|
||||
$statusFound = true;
|
||||
break; // Exit the loop once the STATUS is updated
|
||||
}
|
||||
}
|
||||
// If STATUS node is not found, add a new STATUS node
|
||||
if (!$statusFound) {
|
||||
$node->addNode(new ZCiCalDataNode("STATUS:CANCELLED"));
|
||||
}
|
||||
}
|
||||
// METHOD belongs on the VCALENDAR, not on the VEVENT
|
||||
$cal_event->tree->data['METHOD'] = new ZCiCalDataNode("METHOD:CANCEL");
|
||||
|
||||
$event = new ZCiCalNode("VEVENT", $cal_event->curnode);
|
||||
$event->addNode(new ZCiCalDataNode("UID:" . $uid));
|
||||
$event->addNode(new ZCiCalDataNode("SUMMARY:" . $title));
|
||||
if (!empty($datetime)) {
|
||||
$event->addNode(new ZCiCalDataNode("DTSTART:" . ZCiCal::fromSqlDateTime($datetime)));
|
||||
$event->addNode(new ZCiCalDataNode("DTEND:" . ZCiCal::fromSqlDateTime($datetime)));
|
||||
}
|
||||
$event->addNode(new ZCiCalDataNode("DTSTAMP:" . ZCiCal::fromSqlDateTime()));
|
||||
// Must outrank the invite's SEQUENCE:0 or clients ignore the cancellation
|
||||
$event->addNode(new ZCiCalDataNode("SEQUENCE:1"));
|
||||
$event->addNode(new ZCiCalDataNode("STATUS:CANCELLED"));
|
||||
|
||||
// Return the modified iCal string
|
||||
return $cal_event->export();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user