This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ namespace davina\overwrites; require_once('vendor/autoload.php'); require_once(DIR_LOGIC . '/helpers/call.php'); require_once(DIR_LOGIC . '/helpers/list.php'); require_once(DIR_LOGIC . '/helpers/ics.php'); require_once(DIR_LOGIC . '/model.php'); require_once(DIR_LOGIC . '/conf.php'); /** */ class class_caldav_backend extends \Sabre\CalDAV\Backend\AbstractBackend implements \Sabre\CalDAV\Backend\BackendInterface { /** * @var array {map} */ private array $realms; /** */ private ?string $selected_realm_name; /** */ public function __construct( array $realms ) { // parent::__construct(); $this->realms = $realms; $this->selected_realm_name = null; } /** * @todo outsource */ private function event_to_vevent( \davina\model\struct_event $event ) : \davina\helpers\ics\struct_vevent { $vevent = new \davina\helpers\ics\struct_vevent(); { $vevent->uid = $event->id; $vevent->dtstamp = \davina\helpers\ics\datetime_from_unix_timestamp($event->begin); $vevent->dtstart = new \davina\helpers\ics\struct_dt( '', \davina\helpers\ics\datetime_from_unix_timestamp($event->begin) ); $vevent->dtend = ( ($event->end === null) ? null : new \davina\helpers\ics\struct_dt( '', \davina\helpers\ics\datetime_from_unix_timestamp($event->end) ) ); $vevent->summary = $event->title; $vevent->location = $event->location; $vevent->description = $event->description; $vevent->class = \davina\helpers\ics\enum_class::public_; $vevent->categories = $event->tags; } return $vevent; } /** * @todo outsource */ private function events_to_vcalendar( array $events ) : \davina\helpers\ics\struct_vcalendar { $vcalendar = new \davina\helpers\ics\struct_vcalendar(); { $vcalendar->version = '2.0'; /** * @todo conf */ $vcalendar->prodid = 'davina'; $vcalendar->method = 'PUBLISH'; $vcalendar->events = \array_map( fn($event) => $this->event_to_vevent($event), $events ); } return $vcalendar; } /** */ private function encode_tag( ?string $tag ) : ?string { 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), ] ); } } /** * @param array $parameters { * record< * realm_name:string, * calendar_name:(null|string), * > * } */ private function encode_calendar_identifier( array $parameters ) : string { $str = $parameters['realm_name']; if ($parameters['calendar_name'] === null) { // do nothing } else { $str .= \sprintf( '-%s', $parameters['calendar_name'] ); } return $str; } /** * @return array { * record< * realm_name:string, * calendar_name:(null|string), * > * } */ private function decode_calendar_identifier( string $str ) : array { $parts = \explode('-', $str, 2); return [ 'realm_name' => $parts[0], 'calendar_name' => ( (count($parts) < 2) ? null : $parts[1] ), ]; } /** */ private function get_source( string $realm_name ) : \davina\sources\interface_source { if ( ($this->selected_realm_name === null) || ($this->selected_realm_name === $realm_name) ) { $this->selected_realm_name = $realm_name; if (! \array_key_exists($realm_name, $this->realms)) { throw (new \Exception(\sprintf('no such realm: %s', $realm_name))); } else { return $this->realms[$realm_name]->source; } } else { throw (new \Exception('may not change source')); } } /** * [implementation] */ public function getCalendarsForUser( $principalUri ) { // $source = $this->source; // $realm_name = \explode('/', $principalUri)[1]; $realm_name = \davina\get_parameters()['realm_name']; $source = $this->get_source($realm_name); $calendar = $source->read(); $tags = []; foreach ($calendar->events as $event) { foreach ($event->tags as $tag) { $tags[$tag] = null; } } 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']), ] ), ], ); } /** * [implementation] */ public function createCalendar( $principalUri, $calendarUri, array $properties ) { throw (new \Exception('not implemented: createCalendar')); } /** * [implementation] */ public function deleteCalendar( $calendarId ) { throw (new \Exception('not implemented: deleteCalendar')); } /** * [implementation] */ public function getCalendarObjects( $calendarId ) { $calendar_identifier = $this->decode_calendar_identifier($calendarId); // $realm_name = $calendar_identifier['realm_name']; $realm_name = \davina\get_parameters()['realm_name']; $source = $this->get_source($realm_name); $tag = $calendar_identifier['calendar_name']; $calendar = $source->read(); 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) ) ) ) ), 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, ], ), ] ); } /** * [implementation] */ public function getCalendarObject( $calendarId, $objectUri ) { $id = $objectUri; $calendar_identifier = $this->decode_calendar_identifier($calendarId); // $realm_name = $calendar_identifier['realm_name']; $realm_name = \davina\get_parameters()['realm_name']; $source = $this->get_source($realm_name); $tag = $calendar_identifier['calendar_name']; $calendar = $source->read(); $events = \davina\helpers\list_\filter( $calendar->events, fn($event) => ($event->id === $id) ); if (\count($events) < 1) { throw (new \Exception(\sprintf('not found: %s', $objectUri))); } else if (\count($events) > 1) { throw (new \Exception(\sprintf('ambiguous: %s', $objectUri))); } else { $vcalendar = $this->events_to_vcalendar($events); $ics = \davina\helpers\ics\vcalendar_encode($vcalendar); return [ 'calendardata' => $ics, 'uri' => $objectUri, /** * @todo */ 'lastmodified' => \time(), /** * @todo */ // 'etag' => '""', /** * @todo */ // 'size' => 1, 'component' => 'vcalendar', ]; } } /** * [implementation] */ public function createCalendarObject( $calendarId, $objectUri, $calendarData ) { throw (new \Exception('not implemented: createCalendarObject')); } /** * [implementation] */ public function updateCalendarObject( $calendarId, $objectUri, $calendarData ) { throw (new \Exception('not implemented: updateCalendarObject')); } /** * [implementation] */ public function deleteCalendarObject( $calendarId, $objectUri ) { throw (new \Exception('not implemented: deleteCalendarObject')); } } ?>