From 989fbe70cd566da4c5757527aa77022036af6274 Mon Sep 17 00:00:00 2001 From: Max Kostikov Date: Mon, 24 Feb 2020 10:02:09 +0100 Subject: Implement DAV calendars sync with clones --- Zotlabs/Lib/Libsync.php | 5 +- Zotlabs/Module/Cdav.php | 156 ++++++++++++++++++++++++++++++++++++++---------- include/cdav.php | 4 +- include/import.php | 71 ++++++++++++++++++++++ include/zot.php | 3 + 5 files changed, 203 insertions(+), 36 deletions(-) diff --git a/Zotlabs/Lib/Libsync.php b/Zotlabs/Lib/Libsync.php index d1756cf47..c39720735 100644 --- a/Zotlabs/Lib/Libsync.php +++ b/Zotlabs/Lib/Libsync.php @@ -246,7 +246,10 @@ class Libsync { sync_apps($channel,$arr['app']); if(array_key_exists('addressbook',$arr) && $arr['addressbook']) - sync_addressbook($channel,$arr['addressbook']); + sync_addressbook($channel,$arr['addressbook']); + + if(array_key_exists('calendar',$arr) && $arr['calendar']) + sync_calendar($channel,$arr['calendar']); if(array_key_exists('chatroom',$arr) && $arr['chatroom']) sync_chatrooms($channel,$arr['chatroom']); diff --git a/Zotlabs/Module/Cdav.php b/Zotlabs/Module/Cdav.php index 593a78a53..ac73b8a5b 100644 --- a/Zotlabs/Module/Cdav.php +++ b/Zotlabs/Module/Cdav.php @@ -169,24 +169,34 @@ class Cdav extends Controller { logger("debug: method: " . $httpmethod, LOGGER_DEBUG); logger("debug: uri: " . $httpuri, LOGGER_DEBUG); - // currently we process CardDAV requests only if(strpos($httpuri, 'cdav/addressbooks')) { + $sync = 'addressbook'; + $cdavtable = 'addressbooks'; + } + elseif(strpos($httpuri, 'cdav/calendars')) { + $sync = 'calendar'; + $cdavtable = 'calendarinstances'; + } + else + $sync = false; + + if($sync) { $uri = basename($httpuri); $httpbody = file_get_contents('php://input'); logger("debug: body: " . $httpbody, LOGGER_DEBUG); - if($id = get_cdav_id($principalUri, explode("/", $httpuri)[4], 'addressbooks')) { + if($x = get_cdav_id($principalUri, explode("/", $httpuri)[4], $cdavtable)) { - $cdavdata = $this->get_cdav_data($id, 'addressbooks'); + $cdavdata = $this->get_cdav_data($x['id'], $cdavtable); $etag = (isset($_SERVER['HTTP_IF_MATCH']) ? $_SERVER['HTTP_IF_MATCH'] : false); // delete if($httpmethod === 'DELETE' && $cdavdata['etag'] == $etag) build_sync_packet($channel['channel_id'], [ - 'addressbook' => [ + $sync => [ 'action' => 'delete_card', 'uri' => $cdavdata['uri'], 'carduri' => $uri @@ -197,7 +207,7 @@ class Cdav extends Controller { // update if($cdavdata['etag'] !== $etag) build_sync_packet($channel['channel_id'], [ - 'addressbook' => [ + $sync => [ 'action' => 'update_card', 'uri' => $cdavdata['uri'], 'carduri' => $uri, @@ -208,7 +218,7 @@ class Cdav extends Controller { else { // new build_sync_packet($channel['channel_id'], [ - 'addressbook' => [ + $sync => [ 'action' => 'import', 'uri' => $cdavdata['uri'], 'ids' => [ $uri ], @@ -326,6 +336,14 @@ class Cdav extends Controller { // set new calendar to be visible set_pconfig(local_channel(), 'cdav_calendar' , $id[0], 1); + + build_sync_packet($channel['channel_id'], [ + 'calendar' => [ + 'action' => 'create', + 'uri' => $calendarUri, + 'properties' => $properties + ] + ]); } //create new calendar object via ajax request @@ -336,6 +354,8 @@ class Cdav extends Controller { if(!cdav_perms($id[0],$calendars,true)) return; + $cdavdata = $this->get_cdav_data($id[0], 'calendarinstances'); + $timezone = ((x($_POST,'timezone_select')) ? escape_tags(trim($_POST['timezone_select'])) : ''); $tz = (($timezone) ? $timezone : date_default_timezone_get()); @@ -391,9 +411,17 @@ class Cdav extends Controller { $vcalendar->VEVENT->DTSTART['TZID'] = $tz; $calendarData = $vcalendar->serialize(); - $caldavBackend->createCalendarObject($id, $objectUri, $calendarData); + build_sync_packet($channel['channel_id'], [ + 'calendar' => [ + 'action' => 'import', + 'uri' => $cdavdata['uri'], + 'ids' => [ $objectUri ], + 'card' => $calendarData + ] + ]); + killme(); } @@ -405,17 +433,24 @@ class Cdav extends Controller { if(! cdav_perms($id[0],$calendars)) return; + $cdavdata = $this->get_cdav_data($id[0], 'calendarinstances'); + $mutations = [ '{DAV:}displayname' => $_REQUEST['{DAV:}displayname'], '{http://apple.com/ns/ical/}calendar-color' => $_REQUEST['color'] ]; $patch = new \Sabre\DAV\PropPatch($mutations); - $caldavBackend->updateCalendar($id, $patch); - $patch->commit(); + build_sync_packet($channel['channel_id'], [ + 'calendar' => [ + 'action' => 'edit', + 'uri' => $cdavdata['uri'], + 'mutations' => $mutations, + ] + ]); } //edit calendar object via ajax request @@ -423,9 +458,11 @@ class Cdav extends Controller { $id = explode(':', $_REQUEST['target']); - if(!cdav_perms($id[0],$calendars,true)) + if(! cdav_perms($id[0],$calendars,true)) return; + $cdavdata = $this->get_cdav_data($id[0], 'calendarinstances'); + $timezone = ((x($_POST,'timezone_select')) ? escape_tags(trim($_POST['timezone_select'])) : ''); $tz = (($timezone) ? $timezone : date_default_timezone_get()); @@ -471,9 +508,17 @@ class Cdav extends Controller { $vcalendar->VEVENT->LOCATION = $location; $calendarData = $vcalendar->serialize(); - $caldavBackend->updateCalendarObject($id, $uri, $calendarData); + build_sync_packet($channel['channel_id'], [ + 'calendar' => [ + 'action' => 'update_card', + 'uri' => $cdavdata['uri'], + 'carduri' => $uri, + 'card' => $calendarData + ] + ]); + killme(); } @@ -482,13 +527,23 @@ class Cdav extends Controller { $id = explode(':', $_REQUEST['target']); - if(!cdav_perms($id[0],$calendars,true)) + if(! cdav_perms($id[0],$calendars,true)) return; + $cdavdata = $this->get_cdav_data($id[0], 'calendarinstances'); + $uri = $_REQUEST['uri']; $caldavBackend->deleteCalendarObject($id, $uri); + build_sync_packet($channel['channel_id'], [ + 'calendar' => [ + 'action' => 'delete_card', + 'uri' => $cdavdata['uri'], + 'carduri' => $uri + ] + ]); + killme(); } @@ -497,9 +552,11 @@ class Cdav extends Controller { $id = [$_REQUEST['id'][0], $_REQUEST['id'][1]]; - if(!cdav_perms($id[0],$calendars,true)) + if(! cdav_perms($id[0],$calendars,true)) return; + $cdavdata = $this->get_cdav_data($id[0], 'calendarinstances'); + $timezone = ((x($_POST,'timezone_select')) ? escape_tags(trim($_POST['timezone_select'])) : ''); $tz = (($timezone) ? $timezone : date_default_timezone_get()); @@ -535,9 +592,17 @@ class Cdav extends Controller { unset($vcalendar->VEVENT->DTEND); $calendarData = $vcalendar->serialize(); - $caldavBackend->updateCalendarObject($id, $uri, $calendarData); + build_sync_packet($channel['channel_id'], [ + 'calendar' => [ + 'action' => 'update_card', + 'uri' => $cdavdata['uri'], + 'carduri' => $uri, + 'card' => $calendarData + ] + ]); + killme(); } @@ -602,11 +667,11 @@ class Cdav extends Controller { $id = $_REQUEST['id']; - $cdavdata = $this->get_cdav_data($id, 'addressbooks'); - if(! cdav_perms($id,$addressbooks)) return; + $cdavdata = $this->get_cdav_data($id, 'addressbooks'); + $mutations = [ '{DAV:}displayname' => $_REQUEST['{DAV:}displayname'] ]; @@ -626,6 +691,7 @@ class Cdav extends Controller { //create addressbook card if($_REQUEST['create'] && $_REQUEST['target'] && $_REQUEST['fn']) { + $id = $_REQUEST['target']; $cdavdata = $this->get_cdav_data($id, 'addressbooks'); @@ -656,7 +722,6 @@ class Cdav extends Controller { process_cdav_card($fields, $vcard); $cardData = $vcard->serialize(); - $carddavBackend->createCard($id, $uri, $cardData); build_sync_packet($channel['channel_id'], [ @@ -674,11 +739,11 @@ class Cdav extends Controller { $id = $_REQUEST['target']; - $cdavdata = $this->get_cdav_data($id, 'addressbooks'); - - if(!cdav_perms($id,$addressbooks)) + if(! cdav_perms($id,$addressbooks)) return; + $cdavdata = $this->get_cdav_data($id, 'addressbooks'); + $uri = $_REQUEST['uri']; $object = $carddavBackend->getCard($id, $uri); @@ -714,11 +779,11 @@ class Cdav extends Controller { $id = $_REQUEST['target']; - $cdavdata = $this->get_cdav_data($id, 'addressbooks'); - - if(!cdav_perms($id,$addressbooks)) + if(! cdav_perms($id,$addressbooks)) return; + $cdavdata = $this->get_cdav_data($id, 'addressbooks'); + $uri = $_REQUEST['uri']; $carddavBackend->deleteCard($id, $uri); @@ -758,9 +823,12 @@ class Cdav extends Controller { $ext = 'ics'; $table = 'calendarobjects'; $column = 'calendarid'; + $sync = 'calendar'; $objects = new \Sabre\VObject\Splitter\ICalendar($carddata); $profile = \Sabre\VObject\Node::PROFILE_CALDAV; $backend = new \Sabre\CalDAV\Backend\PDO($pdo); + + $cdavdata = $this->get_cdav_data($id, 'calendarinstances'); } if($_REQUEST['a_upload']) { @@ -768,6 +836,7 @@ class Cdav extends Controller { $ext = 'vcf'; $table = 'cards'; $column = 'addressbookid'; + $sync = 'addressbook'; $objects = new \Sabre\VObject\Splitter\VCard($carddata); $profile = \Sabre\VObject\Node::PROFILE_CARDDAV; $backend = new \Sabre\CardDAV\Backend\PDO($pdo); @@ -778,15 +847,14 @@ class Cdav extends Controller { $ids = []; import_cdav_card($id, $ext, $table, $column, $objects, $profile, $backend, $ids, true); - if(isset($cdavdata)) - build_sync_packet($channel['channel_id'], [ - 'addressbook' => [ - 'action' => 'import', - 'uri' => $cdavdata['uri'], - 'ids' => $ids, - 'card' => $carddata - ] - ]); + build_sync_packet($channel['channel_id'], [ + $sync => [ + 'action' => 'import', + 'uri' => $cdavdata['uri'], + 'ids' => $ids, + 'card' => $carddata + ] + ]); } @unlink($src); } @@ -1095,7 +1163,18 @@ class Cdav extends Controller { if(! cdav_perms($id,$calendars)) killme(); - set_pconfig(local_channel(), 'cdav_calendar' , argv(3), argv(4)); + $cdavdata = $this->get_cdav_data($id, 'calendarinstances'); + + set_pconfig(local_channel(), 'cdav_calendar', $id, argv(4)); + + build_sync_packet(local_channel(), [ + 'calendar' => [ + 'action' => 'switch', + 'uri' => $cdavdata['uri'], + 'switch' => intval(argv(4)) + ] + ]); + killme(); } @@ -1106,7 +1185,18 @@ class Cdav extends Controller { if(! cdav_perms($id[0],$calendars)) killme(); + // get metadata before we delete it + $cdavdata = $this->get_cdav_data($id[0], 'calendarinstances'); + $caldavBackend->deleteCalendar($id); + + build_sync_packet($channel['channel_id'], [ + 'calendar' => [ + 'action' => 'drop', + 'uri' => $cdavdata['uri'] + ] + ]); + killme(); } diff --git a/include/cdav.php b/include/cdav.php index d72caa442..cdf7775db 100644 --- a/include/cdav.php +++ b/include/cdav.php @@ -175,12 +175,12 @@ function import_cdav_card($id, $ext, $table, $column, $objects, $profile, $backe function get_cdav_id($principaluri, $uri, $table) { - $r = q("SELECT id FROM $table WHERE principaluri = '%s' AND uri = '%s' LIMIT 1", + $r = q("SELECT * FROM $table WHERE principaluri = '%s' AND uri = '%s' LIMIT 1", dbesc($principaluri), dbesc($uri) ); if(! $r) return false; - return $r[0]['id']; + return $r[0]; } diff --git a/include/import.php b/include/import.php index 0519052d8..bfe71963f 100644 --- a/include/import.php +++ b/include/import.php @@ -1503,6 +1503,7 @@ function sync_files($channel, $files) { } } + /** * @brief Synchronize addressbooks. * @@ -1570,6 +1571,76 @@ function sync_addressbook($channel, $data) { } +/** + * @brief Synchronize calendars. + * + * @param array $channel + * @param array $data + */ +function sync_calendar($channel, $data) { + + if(! \Zotlabs\Lib\Apps::system_app_installed($channel['channel_id'], 'Calendar')) + return; + + logger("debug: " . print_r($data,true), LOGGER_DEBUG); + + require_once('include/cdav.php'); + + $principalUri = 'principals/' . $channel['channel_address']; + + if($data['action'] !== 'create') { + $x = get_cdav_id($principalUri, $data['uri'], 'calendarinstances'); + if(! $x) + return; + $id = [ $x['id'], $x['calendarid'] ]; + } + + $pdo = \DBA::$dba->db; + + $caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo); + $calendars = $caldavBackend->getCalendarsForUser($principalUri); + + switch($data['action']) { + + case 'create': + $id = $caldavBackend->createCalendar($principalUri, $data['uri'], $data['properties']); + set_pconfig($channel['channel_id'], 'cdav_calendar', $id[0], 1); + break; + + case 'drop': + $caldavBackend->deleteCalendar($id); + break; + + case 'edit': + $patch = new \Sabre\DAV\PropPatch($data['mutations']); + $caldavBackend->updateCalendar($id, $patch); + $patch->commit(); + break; + + case 'delete_card': + $caldavBackend->deleteCalendarObject($id, $data['carduri']); + break; + + case 'update_card': + $caldavBackend->updateCalendarObject($id, $data['carduri'], $data['card']); + break; + + case 'switch': + set_pconfig($channel['channel_id'], 'cdav_calendar', $id[0], $data['switch']); + break; + + case 'import': + $objects = new \Sabre\VObject\Splitter\ICalendar($data['card']); + $profile = \Sabre\VObject\Node::PROFILE_CALDAV; + import_cdav_card($id, 'ics', 'calendarobjects', 'calendarid', $objects, $profile, $caldavBackend, $data['ids']); + break; + + default: + break; + } +} + + /** * @brief Rename a key in an array. * diff --git a/include/zot.php b/include/zot.php index c02dab162..5d5ac8424 100644 --- a/include/zot.php +++ b/include/zot.php @@ -3614,6 +3614,9 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { if(array_key_exists('addressbook',$arr) && $arr['addressbook']) sync_addressbook($channel,$arr['addressbook']); + if(array_key_exists('calendar',$arr) && $arr['calendar']) + sync_calendar($channel,$arr['calendar']); + if(array_key_exists('chatroom',$arr) && $arr['chatroom']) sync_chatrooms($channel,$arr['chatroom']); -- cgit v1.2.3