core/source/overwrites/caldav_backend.php

441 lines
8.4 KiB
PHP
Raw Permalink Normal View History

2025-09-09 12:07:53 +02:00
<?php
2025-09-16 12:05:35 +02:00
namespace davina\overwrites;
2025-09-09 12:07:53 +02:00
require_once('vendor/autoload.php');
2025-09-16 12:48:45 +02:00
require_once(DIR_LOGIC . '/helpers/call.php');
2025-09-21 13:27:37 +02:00
require_once(DIR_LOGIC . '/helpers/list.php');
2025-09-16 12:48:45 +02:00
require_once(DIR_LOGIC . '/helpers/ics.php');
require_once(DIR_LOGIC . '/model.php');
require_once(DIR_LOGIC . '/conf.php');
2025-09-09 12:07:53 +02:00
/**
*/
class class_caldav_backend
extends \Sabre\CalDAV\Backend\AbstractBackend
implements \Sabre\CalDAV\Backend\BackendInterface
{
/**
2025-09-21 18:49:03 +02:00
* @var array {map<string, \davina\model\struct_realm>}
2025-09-09 12:07:53 +02:00
*/
2025-09-21 18:49:03 +02:00
private array $realms;
2025-09-09 12:07:53 +02:00
/**
*/
2025-09-21 18:49:03 +02:00
private ?string $selected_realm_name;
2025-09-09 12:07:53 +02:00
/**
*/
2025-09-21 13:27:37 +02:00
public function __construct(
2025-09-21 18:49:03 +02:00
array $realms
2025-09-21 13:27:37 +02:00
)
2025-09-09 12:07:53 +02:00
{
2025-09-21 13:27:37 +02:00
// parent::__construct();
2025-09-21 18:49:03 +02:00
$this->realms = $realms;
$this->selected_realm_name = null;
2025-09-09 12:07:53 +02:00
}
2025-09-09 19:43:23 +02:00
/**
* @todo outsource
*/
private function event_to_vevent(
2025-09-16 12:05:35 +02:00
\davina\model\struct_event $event
) : \davina\helpers\ics\struct_vevent
2025-09-09 19:43:23 +02:00
{
2025-09-16 12:05:35 +02:00
$vevent = new \davina\helpers\ics\struct_vevent();
2025-09-09 19:43:23 +02:00
{
2025-09-09 23:17:19 +02:00
$vevent->uid = $event->id;
2025-09-16 12:05:35 +02:00
$vevent->dtstamp = \davina\helpers\ics\datetime_from_unix_timestamp($event->begin);
$vevent->dtstart = new \davina\helpers\ics\struct_dt(
2025-09-09 19:43:23 +02:00
'',
2025-09-16 12:05:35 +02:00
\davina\helpers\ics\datetime_from_unix_timestamp($event->begin)
2025-09-09 19:43:23 +02:00
);
$vevent->dtend = (
2025-09-09 23:17:19 +02:00
($event->end === null)
2025-09-09 19:43:23 +02:00
?
null
:
2025-09-16 12:05:35 +02:00
new \davina\helpers\ics\struct_dt(
2025-09-09 19:43:23 +02:00
'',
2025-09-16 12:05:35 +02:00
\davina\helpers\ics\datetime_from_unix_timestamp($event->end)
2025-09-09 19:43:23 +02:00
)
);
2025-09-09 23:17:19 +02:00
$vevent->summary = $event->title;
$vevent->location = $event->location;
$vevent->description = $event->description;
2025-09-16 12:05:35 +02:00
$vevent->class = \davina\helpers\ics\enum_class::public_;
2025-09-09 23:17:19 +02:00
$vevent->categories = $event->tags;
2025-09-09 19:43:23 +02:00
}
return $vevent;
}
2025-09-09 12:07:53 +02:00
/**
2025-09-09 19:43:23 +02:00
* @todo outsource
*/
private function events_to_vcalendar(
array $events
2025-09-16 12:05:35 +02:00
) : \davina\helpers\ics\struct_vcalendar
2025-09-09 19:43:23 +02:00
{
2025-09-16 12:05:35 +02:00
$vcalendar = new \davina\helpers\ics\struct_vcalendar();
2025-09-09 19:43:23 +02:00
{
$vcalendar->version = '2.0';
/**
* @todo conf
*/
2025-09-16 12:05:35 +02:00
$vcalendar->prodid = 'davina';
2025-09-09 19:43:23 +02:00
$vcalendar->method = 'PUBLISH';
$vcalendar->events = \array_map(
fn($event) => $this->event_to_vevent($event),
$events
);
}
return $vcalendar;
}
2025-09-21 13:27:37 +02:00
/**
*/
private function encode_tag(
2025-09-21 18:49:03 +02:00
?string $tag
) : ?string
2025-09-21 13:27:37 +02:00
{
2025-09-21 18:49:03 +02:00
if ($tag === null)
{
return null;
}
else
{
return \davina\helpers\call\convey(
$tag,
[
fn($x) => \strtolower($x),
fn($x) => \preg_replace('/ä/', 'ae', $x),
fn($x) => \preg_replace('/ö/', 'oe', $x),
fn($x) => \preg_replace('/ü/', 'ue', $x),
fn($x) => \preg_replace('/ß/', 'sz', $x),
fn($x) => \preg_replace('/\-/', '', $x),
fn($x) => \preg_replace('/ /', '-', $x),
fn($x) => \preg_replace('/[^a-zA-Z0-9_\-]/s', '_', $x),
]
);
}
2025-09-21 13:27:37 +02:00
}
/**
* @param array $parameters {
* record<
2025-09-21 18:49:03 +02:00
* realm_name:string,
* calendar_name:(null|string),
2025-09-21 13:27:37 +02:00
* >
* }
*/
private function encode_calendar_identifier(
array $parameters
) : string
{
2025-09-21 18:49:03 +02:00
$str = $parameters['realm_name'];
if ($parameters['calendar_name'] === null)
{
// do nothing
}
else
{
$str .= \sprintf(
'-%s',
$parameters['calendar_name']
);
}
return $str;
2025-09-21 13:27:37 +02:00
}
/**
* @return array {
* record<
2025-09-21 18:49:03 +02:00
* realm_name:string,
* calendar_name:(null|string),
2025-09-21 13:27:37 +02:00
* >
* }
*/
private function decode_calendar_identifier(
string $str
) : array
{
$parts = \explode('-', $str, 2);
return [
2025-09-21 18:49:03 +02:00
'realm_name' => $parts[0],
'calendar_name' => (
(count($parts) < 2)
?
null
:
$parts[1]
),
2025-09-21 13:27:37 +02:00
];
}
/**
*/
private function get_source(
2025-09-21 18:49:03 +02:00
string $realm_name
2025-09-21 13:27:37 +02:00
) : \davina\sources\interface_source
{
if (
2025-09-21 18:49:03 +02:00
($this->selected_realm_name === null)
2025-09-21 13:27:37 +02:00
||
2025-09-21 18:49:03 +02:00
($this->selected_realm_name === $realm_name)
2025-09-21 13:27:37 +02:00
)
{
2025-09-21 18:49:03 +02:00
$this->selected_realm_name = $realm_name;
if (! \array_key_exists($realm_name, $this->realms))
2025-09-21 13:27:37 +02:00
{
2025-09-21 18:49:03 +02:00
throw (new \Exception(\sprintf('no such realm: %s', $realm_name)));
2025-09-21 13:27:37 +02:00
}
else
{
2025-09-21 18:49:03 +02:00
return $this->realms[$realm_name]->source;
2025-09-21 13:27:37 +02:00
}
}
else
{
throw (new \Exception('may not change source'));
}
}
2025-09-09 19:43:23 +02:00
/**
* [implementation]
2025-09-09 12:07:53 +02:00
*/
public function getCalendarsForUser(
$principalUri
)
{
2025-09-21 13:27:37 +02:00
// $source = $this->source;
2025-09-21 18:49:03 +02:00
// $realm_name = \explode('/', $principalUri)[1];
$realm_name = \davina\get_parameters()['realm_name'];
$source = $this->get_source($realm_name);
$calendar = $source->read();
2025-09-09 12:07:53 +02:00
$tags = [];
2025-09-09 23:17:19 +02:00
foreach ($calendar->events as $event)
2025-09-09 12:07:53 +02:00
{
2025-09-09 23:17:19 +02:00
foreach ($event->tags as $tag)
2025-09-09 12:07:53 +02:00
{
$tags[$tag] = null;
}
}
2025-09-21 18:49:03 +02:00
return \davina\helpers\call\convey(
$tags,
[
fn($x) => (
(\count($x) <= 0)
?
[null]
:
\array_keys($x)
),
fn($x) => \davina\helpers\list_\map(
$x,
fn($tag) => [
'id' => $this->encode_calendar_identifier(
[
'realm_name' => $realm_name,
'calendar_name' => $this->encode_tag($tag),
]
),
// 'uri' => $this->encode_tag($tag),
'uri' => $this->encode_calendar_identifier(
[
'realm_name' => $realm_name,
'calendar_name' => $this->encode_tag($tag),
]
),
'principaluri' => $principalUri,
'{DAV:}displayname' => ($tag ?? $realm_name),
\sprintf(
'{%s}supported-calendar-component-set',
\Sabre\CalDAV\Plugin::NS_CALDAV
) => new \Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet(['VEVENT']),
2025-09-21 13:27:37 +02:00
]
),
2025-09-09 12:07:53 +02:00
],
);
}
/**
2025-09-09 19:43:23 +02:00
* [implementation]
2025-09-09 12:07:53 +02:00
*/
public function createCalendar(
$principalUri,
$calendarUri,
array $properties
)
{
throw (new \Exception('not implemented: createCalendar'));
}
/**
2025-09-09 19:43:23 +02:00
* [implementation]
2025-09-09 12:07:53 +02:00
*/
public function deleteCalendar(
$calendarId
)
{
throw (new \Exception('not implemented: deleteCalendar'));
}
/**
2025-09-09 19:43:23 +02:00
* [implementation]
2025-09-09 12:07:53 +02:00
*/
public function getCalendarObjects(
$calendarId
)
{
2025-09-21 13:27:37 +02:00
$calendar_identifier = $this->decode_calendar_identifier($calendarId);
2025-09-21 18:49:03 +02:00
// $realm_name = $calendar_identifier['realm_name'];
$realm_name = \davina\get_parameters()['realm_name'];
$source = $this->get_source($realm_name);
2025-09-21 13:27:37 +02:00
$tag = $calendar_identifier['calendar_name'];
2025-09-21 18:49:03 +02:00
$calendar = $source->read();
2025-09-21 13:27:37 +02:00
2025-09-21 18:49:03 +02:00
return \davina\helpers\call\convey(
$calendar->events,
[
fn($x) => \davina\helpers\list_\filter(
$x,
fn($event) => (
(\count($event->tags) <= 0)
||
\in_array(
$tag,
\davina\helpers\list_\map(
$event->tags,
fn($x) => $this->encode_tag($x)
)
2025-09-09 12:07:53 +02:00
)
)
2025-09-21 18:49:03 +02:00
),
fn($x) => \davina\helpers\list_\map(
$x,
fn($event) => [
'calendarid' => $calendarId,
'id' => $event->id,
// 'uri' => \sprintf('%s.ics', $entry['id']),
'uri' => $event->id,
'lastmodified' => \time(),
// 'etag' => null,
// 'size' => null,
'component' => 'vevent',
'{DAV:}displayname' => $event->title,
],
),
]
2025-09-09 12:07:53 +02:00
);
}
/**
2025-09-09 19:43:23 +02:00
* [implementation]
2025-09-09 12:07:53 +02:00
*/
public function getCalendarObject(
$calendarId,
$objectUri
)
{
$id = $objectUri;
2025-09-21 13:27:37 +02:00
$calendar_identifier = $this->decode_calendar_identifier($calendarId);
2025-09-21 18:49:03 +02:00
// $realm_name = $calendar_identifier['realm_name'];
$realm_name = \davina\get_parameters()['realm_name'];
$source = $this->get_source($realm_name);
2025-09-21 13:27:37 +02:00
$tag = $calendar_identifier['calendar_name'];
2025-09-21 18:49:03 +02:00
$calendar = $source->read();
2025-09-21 13:27:37 +02:00
2025-09-21 18:49:03 +02:00
$events = \davina\helpers\list_\filter(
$calendar->events,
fn($event) => ($event->id === $id)
2025-09-09 12:07:53 +02:00
);
2025-09-10 15:03:44 +02:00
if (\count($events) < 1)
2025-09-09 23:17:19 +02:00
{
throw (new \Exception(\sprintf('not found: %s', $objectUri)));
}
2025-09-10 15:03:44 +02:00
else if (\count($events) > 1)
2025-09-09 12:07:53 +02:00
{
2025-09-09 23:17:19 +02:00
throw (new \Exception(\sprintf('ambiguous: %s', $objectUri)));
2025-09-09 12:07:53 +02:00
}
else
{
2025-09-10 15:03:44 +02:00
$vcalendar = $this->events_to_vcalendar($events);
2025-09-16 12:05:35 +02:00
$ics = \davina\helpers\ics\vcalendar_encode($vcalendar);
2025-09-09 12:07:53 +02:00
return [
'calendardata' => $ics,
'uri' => $objectUri,
/**
* @todo
*/
'lastmodified' => \time(),
/**
* @todo
*/
// 'etag' => '""',
/**
* @todo
*/
// 'size' => 1,
'component' => 'vcalendar',
];
}
}
/**
2025-09-09 19:43:23 +02:00
* [implementation]
2025-09-09 12:07:53 +02:00
*/
public function createCalendarObject(
$calendarId,
$objectUri,
$calendarData
)
{
throw (new \Exception('not implemented: createCalendarObject'));
}
/**
2025-09-09 19:43:23 +02:00
* [implementation]
2025-09-09 12:07:53 +02:00
*/
public function updateCalendarObject(
$calendarId,
$objectUri,
$calendarData
)
{
throw (new \Exception('not implemented: updateCalendarObject'));
}
/**
2025-09-09 19:43:23 +02:00
* [implementation]
2025-09-09 12:07:53 +02:00
*/
public function deleteCalendarObject(
$calendarId,
$objectUri
)
{
throw (new \Exception('not implemented: deleteCalendarObject'));
}
}
?>