Calendar V3
This commit is contained in:
@@ -20,6 +20,9 @@ class CalDavClient
|
||||
/** @var int HTTP timeout in seconds */
|
||||
protected const TIMEOUT = 30;
|
||||
|
||||
/** @var string Last request error message for diagnostics */
|
||||
protected static string $lastRequestError = '';
|
||||
|
||||
/**
|
||||
* Update the STATUS of a specific event or task on the remote CalDAV server.
|
||||
*
|
||||
@@ -33,7 +36,7 @@ class CalDavClient
|
||||
* @param string $recurrenceId Recurrence ID (empty for non-recurring)
|
||||
* @param string $newStatus New status value (e.g. COMPLETED, TODO)
|
||||
* @param string $dateIso Occurrence date YYYY-MM-DD
|
||||
* @return bool True if the remote update succeeded
|
||||
* @return string Empty string on success, error message on failure
|
||||
*/
|
||||
public static function updateEventStatus(
|
||||
string $caldavUrl,
|
||||
@@ -43,13 +46,17 @@ class CalDavClient
|
||||
string $recurrenceId,
|
||||
string $newStatus,
|
||||
string $dateIso
|
||||
): bool {
|
||||
if ($caldavUrl === '' || $uid === '') return false;
|
||||
): string {
|
||||
if ($caldavUrl === '' || $uid === '') return 'Missing CalDAV URL or UID';
|
||||
|
||||
try {
|
||||
// Find the calendar object href for this UID via REPORT
|
||||
$objectInfo = self::findObjectByUid($caldavUrl, $username, $password, $uid);
|
||||
if ($objectInfo === null) return false;
|
||||
if ($objectInfo === null) {
|
||||
$msg = "CalDAV: Could not find object with UID '$uid' on server";
|
||||
dbglog($msg);
|
||||
return $msg;
|
||||
}
|
||||
|
||||
$objectHref = $objectInfo['href'];
|
||||
$etag = $objectInfo['etag'];
|
||||
@@ -57,19 +64,35 @@ class CalDavClient
|
||||
|
||||
// Parse and update the status
|
||||
$calendar = Reader::read($calendarData, Reader::OPTION_FORGIVING);
|
||||
if (!($calendar instanceof VCalendar)) return false;
|
||||
if (!($calendar instanceof VCalendar)) {
|
||||
$msg = "CalDAV: Failed to parse calendar data for UID '$uid'";
|
||||
dbglog($msg);
|
||||
return $msg;
|
||||
}
|
||||
|
||||
$updated = IcsWriter::applyStatusUpdateToCalendar(
|
||||
$calendar, $uid, $recurrenceId, $newStatus, $dateIso
|
||||
);
|
||||
if (!$updated) return false;
|
||||
if (!$updated) {
|
||||
$msg = "CalDAV: applyStatusUpdateToCalendar failed for UID '$uid'";
|
||||
dbglog($msg);
|
||||
return $msg;
|
||||
}
|
||||
|
||||
$newData = $calendar->serialize();
|
||||
|
||||
// PUT the updated object back with If-Match for conflict detection
|
||||
return self::putCalendarObject($objectHref, $username, $password, $newData, $etag);
|
||||
$putError = self::putCalendarObject($objectHref, $username, $password, $newData, $etag);
|
||||
if ($putError !== '') {
|
||||
dbglog($putError);
|
||||
return $putError;
|
||||
}
|
||||
|
||||
return '';
|
||||
} catch (Throwable $e) {
|
||||
return false;
|
||||
$msg = 'CalDAV: Exception during updateEventStatus: ' . $e->getMessage();
|
||||
dbglog($msg);
|
||||
return $msg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,7 +266,7 @@ class CalDavClient
|
||||
* @param string $password
|
||||
* @param string $data ICS data to write
|
||||
* @param string $etag ETag for If-Match header (empty to skip)
|
||||
* @return bool
|
||||
* @return string Empty string on success, error message on failure
|
||||
*/
|
||||
protected static function putCalendarObject(
|
||||
string $href,
|
||||
@@ -251,7 +274,7 @@ class CalDavClient
|
||||
string $password,
|
||||
string $data,
|
||||
string $etag
|
||||
): bool {
|
||||
): string {
|
||||
$headers = [
|
||||
'Content-Type: text/calendar; charset=utf-8',
|
||||
];
|
||||
@@ -260,9 +283,10 @@ class CalDavClient
|
||||
}
|
||||
|
||||
$response = self::request('PUT', $href, $username, $password, $data, $headers);
|
||||
// PUT returns null body but we check by HTTP status via the request method
|
||||
// A successful PUT returns 2xx
|
||||
return $response !== null;
|
||||
if ($response === null) {
|
||||
return self::$lastRequestError ?: 'CalDAV PUT failed (unknown error)';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,8 +317,6 @@ class CalDavClient
|
||||
|
||||
$etags = $resp->xpath('.//d:getetag');
|
||||
$etag = ($etags && count($etags) > 0) ? trim((string)$etags[0]) : '';
|
||||
// Strip surrounding quotes from etag if present
|
||||
$etag = trim($etag, '"');
|
||||
|
||||
$caldata = $resp->xpath('.//cal:calendar-data');
|
||||
$data = ($caldata && count($caldata) > 0) ? trim((string)$caldata[0]) : '';
|
||||
@@ -392,10 +414,18 @@ class CalDavClient
|
||||
string $body = '',
|
||||
array $headers = []
|
||||
): ?string {
|
||||
if (!function_exists('curl_init')) return null;
|
||||
self::$lastRequestError = '';
|
||||
|
||||
if (!function_exists('curl_init')) {
|
||||
self::$lastRequestError = 'CalDAV: curl extension not available';
|
||||
return null;
|
||||
}
|
||||
|
||||
$ch = curl_init();
|
||||
if ($ch === false) return null;
|
||||
if ($ch === false) {
|
||||
self::$lastRequestError = 'CalDAV: curl_init() failed';
|
||||
return null;
|
||||
}
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||
@@ -423,9 +453,13 @@ class CalDavClient
|
||||
// Capture HTTP status code
|
||||
$responseBody = curl_exec($ch);
|
||||
$httpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curlError = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if (!is_string($responseBody)) return null;
|
||||
if (!is_string($responseBody)) {
|
||||
self::$lastRequestError = "CalDAV $method failed: curl error: $curlError";
|
||||
return null;
|
||||
}
|
||||
|
||||
// Accept 2xx and 207 (multistatus) responses
|
||||
if ($httpCode >= 200 && $httpCode < 300) {
|
||||
@@ -435,6 +469,7 @@ class CalDavClient
|
||||
return $responseBody;
|
||||
}
|
||||
|
||||
self::$lastRequestError = "CalDAV $method failed: HTTP $httpCode";
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user