Commit 8c59f06d authored by Daniel Morlock's avatar Daniel Morlock

Ongoing work on CalDAV calendar to use dedicated database tables (WIP).

parent 45e050d0
......@@ -2558,6 +2558,7 @@ class calendar extends rcube_plugin
$day_end = new Datetime(gmdate('Y-m-d 23:59', $data['date']), $this->lib->timezone);
// get events on that day from the user's personal calendars
// FIXME: $this->driver is deprecated
$calendars = $this->driver->list_calendars(false, true);
$events = $this->driver->load_events($day_start->format('U'), $day_end->format('U'), null, array_keys($calendars));
usort($events, function($a, $b) { return $a['start'] > $b['start'] ? 1 : -1; });
......
......@@ -20,13 +20,71 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
CREATE TABLE IF NOT EXISTS `caldav_props` (
`obj_id` int(11) NOT NULL,
`obj_type` enum('vcal','vevent','vtodo','') NOT NULL,
`url` varchar(255) NOT NULL,
`tag` varchar(255) DEFAULT NULL,
`username` varchar(255) DEFAULT NULL,
`pass` varchar(1024) DEFAULT NULL,
`last_change` TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY `obj_id` (`obj_id`,`obj_type`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `caldav_calendars` (
`calendar_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
`name` varchar(255) NOT NULL,
`color` varchar(8) NOT NULL,
`showalarms` tinyint(1) NOT NULL DEFAULT '1',
`caldav_url` varchar(255) NOT NULL,
`caldav_tag` varchar(255) DEFAULT NULL,
`caldav_user` varchar(255) DEFAULT NULL,
`caldav_pass` varchar(1024) DEFAULT NULL,
`caldav_last_change` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY(`calendar_id`),
INDEX `caldav_user_name_idx` (`user_id`, `name`),
CONSTRAINT `fk_caldav_calendars_user_id` FOREIGN KEY (`user_id`)
REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
CREATE TABLE IF NOT EXISTS `caldav_events` (
`event_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`calendar_id` int(11) UNSIGNED NOT NULL DEFAULT '0',
`recurrence_id` int(11) UNSIGNED NOT NULL DEFAULT '0',
`uid` varchar(255) NOT NULL DEFAULT '',
`created` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`sequence` int(1) UNSIGNED NOT NULL DEFAULT '0',
`start` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`end` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`recurrence` varchar(255) DEFAULT NULL,
`title` varchar(255) NOT NULL,
`description` text NOT NULL,
`location` varchar(255) NOT NULL DEFAULT '',
`categories` varchar(255) NOT NULL DEFAULT '',
`url` varchar(255) NOT NULL DEFAULT '',
`all_day` tinyint(1) NOT NULL DEFAULT '0',
`free_busy` tinyint(1) NOT NULL DEFAULT '0',
`priority` tinyint(1) NOT NULL DEFAULT '0',
`sensitivity` tinyint(1) NOT NULL DEFAULT '0',
`status` varchar(32) NOT NULL DEFAULT '',
`alarms` varchar(255) DEFAULT NULL,
`attendees` text DEFAULT NULL,
`notifyat` datetime DEFAULT NULL,
`caldav_url` varchar(255) NOT NULL,
`caldav_tag` varchar(255) DEFAULT NULL,
`caldav_last_change` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY(`event_id`),
INDEX `caldav_uid_idx` (`uid`),
INDEX `caldav_recurrence_idx` (`recurrence_id`),
INDEX `caldav_calendar_notify_idx` (`calendar_id`,`notifyat`),
CONSTRAINT `fk_caldav_events_calendar_id` FOREIGN KEY (`calendar_id`)
REFERENCES `calendars`(`calendar_id`) ON DELETE CASCADE ON UPDATE CASCADE
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
CREATE TABLE IF NOT EXISTS `caldav_attachments` (
`attachment_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`event_id` int(11) UNSIGNED NOT NULL DEFAULT '0',
`filename` varchar(255) NOT NULL DEFAULT '',
`mimetype` varchar(255) NOT NULL DEFAULT '',
`size` int(11) NOT NULL DEFAULT '0',
`data` longtext NOT NULL,
PRIMARY KEY(`attachment_id`),
CONSTRAINT `fk_caldav_attachments_event_id` FOREIGN KEY (`event_id`)
REFERENCES `events`(`event_id`) ON DELETE CASCADE ON UPDATE CASCADE
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
REPLACE INTO `system` (`name`, `value`) VALUES ('calendar-caldav-version', '2015032500');
\ No newline at end of file
ALTER TABLE `caldav_props` change `user` `username` varchar(255);
/**
* CalDAV Client
*
* @version @package_version@
* @author Daniel Morlock <daniel.morlock@awesome-it.de>
*
* Copyright (C) 2013, Awesome IT GbR <info@awesome-it.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
ALTER TABLE `caldav_props` change `user` `username` varchar(255);
ALTER TABLE `events` ADD `status` VARCHAR(32) NOT NULL DEFAULT '' AFTER `sensitivity`;
\ No newline at end of file
/**
* CalDAV Client
*
* @version @package_version@
* @author Daniel Morlock <daniel.morlock@awesome-it.de>
*
* Copyright (C) 2013, Awesome IT GbR <info@awesome-it.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Create new tables */
CREATE TABLE IF NOT EXISTS `caldav_calendars` (
`calendar_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
`name` varchar(255) NOT NULL,
`color` varchar(8) NOT NULL,
`showalarms` tinyint(1) NOT NULL DEFAULT '1',
`caldav_url` varchar(255) NOT NULL,
`caldav_tag` varchar(255) DEFAULT NULL,
`caldav_user` varchar(255) DEFAULT NULL,
`caldav_pass` varchar(1024) DEFAULT NULL,
`caldav_last_change` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY(`calendar_id`),
INDEX `caldav_user_name_idx` (`user_id`, `name`),
CONSTRAINT `fk_caldav_calendars_user_id` FOREIGN KEY (`user_id`)
REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
CREATE TABLE IF NOT EXISTS `caldav_events` (
`event_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`calendar_id` int(11) UNSIGNED NOT NULL DEFAULT '0',
`recurrence_id` int(11) UNSIGNED NOT NULL DEFAULT '0',
`uid` varchar(255) NOT NULL DEFAULT '',
`created` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`sequence` int(1) UNSIGNED NOT NULL DEFAULT '0',
`start` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`end` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`recurrence` varchar(255) DEFAULT NULL,
`title` varchar(255) NOT NULL,
`description` text NOT NULL,
`location` varchar(255) NOT NULL DEFAULT '',
`categories` varchar(255) NOT NULL DEFAULT '',
`url` varchar(255) NOT NULL DEFAULT '',
`all_day` tinyint(1) NOT NULL DEFAULT '0',
`free_busy` tinyint(1) NOT NULL DEFAULT '0',
`priority` tinyint(1) NOT NULL DEFAULT '0',
`sensitivity` tinyint(1) NOT NULL DEFAULT '0',
`status` varchar(32) NOT NULL DEFAULT '',
`alarms` varchar(255) DEFAULT NULL,
`attendees` text DEFAULT NULL,
`notifyat` datetime DEFAULT NULL,
`caldav_url` varchar(255) NOT NULL,
`caldav_tag` varchar(255) DEFAULT NULL,
`caldav_last_change` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY(`event_id`),
INDEX `caldav_uid_idx` (`uid`),
INDEX `caldav_recurrence_idx` (`recurrence_id`),
INDEX `caldav_calendar_notify_idx` (`calendar_id`,`notifyat`),
CONSTRAINT `fk_caldav_events_calendar_id` FOREIGN KEY (`calendar_id`)
REFERENCES `calendars`(`calendar_id`) ON DELETE CASCADE ON UPDATE CASCADE
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
CREATE TABLE IF NOT EXISTS `caldav_attachments` (
`attachment_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`event_id` int(11) UNSIGNED NOT NULL DEFAULT '0',
`filename` varchar(255) NOT NULL DEFAULT '',
`mimetype` varchar(255) NOT NULL DEFAULT '',
`size` int(11) NOT NULL DEFAULT '0',
`data` longtext NOT NULL,
PRIMARY KEY(`attachment_id`),
CONSTRAINT `fk_caldav_attachments_event_id` FOREIGN KEY (`event_id`)
REFERENCES `events`(`event_id`) ON DELETE CASCADE ON UPDATE CASCADE
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
/* Migrate Data */
INSERT INTO caldav_calendars SELECT calendar_id, user_id, `name`, color, showalarms, url as caldav_url,
tag as caldav_tag, username as caldav_user, pass as caldav_pass,
last_change as caldav_last_change
FROM calendars cal, caldav_props dav
WHERE dav.obj_id = cal.calendar_id
AND dav.obj_type = 'vcal';
INSERT INTO caldav_events SELECT e.*, dav.url as caldav_url, dav.tag as caldav_tag, dav.last_change as caldav_last_change
FROM `events` e, caldav_props dav
WHERE dav.obj_id = e.event_id
AND dav.obj_type = 'vevent';
INSERT INTO caldav_attachments SELECT * FROM attachments a
WHERE a.event_id IN (
SELECT obj_id FROM caldav_props dav
WHERE dav.obj_type = 'vevent'
);
/* Drop deprecated data */
DELETE FROM `events` WHERE event_id IN (
SELECT obj_id FROM caldav_props dav
WHERE dav.obj_type = 'vevent'
);
DELETE FROM calendars WHERE calendar_id IN (
SELECT obj_id FROM caldav_props dav
WHERE dav.obj_type = 'vcal'
);
DELETE FROM attachments WHERE event_id IN (
SELECT obj_id FROM caldav_props dav
WHERE dav.obj_type = 'vevent'
);
DROP TABLE caldav_props;
......@@ -38,22 +38,20 @@ class caldav_sync
/**
* Default constructor for calendar synchronization adapter.
*
* @param int Calendar id.
* @param array Hash array with caldav properties:
* url: Caldav calendar URL.
* user: Caldav http basic auth user.
* pass: Password für caldav user.
* ctag: Caldav ctag for calendar.
* @param array Hash array with caldav properties at least the following:
* id: Calendar ID
* caldav_url: Caldav calendar URL.
* caldav_user: Caldav http basic auth user.
* caldav_pass: Password für caldav user.
* caldav_tag: Caldav ctag for calendar.
*/
public function __construct($cal_id, $props)
public function __construct($cal)
{
$this->cal_id = $cal_id;
$this->url = $props["url"];
$this->ctag = isset($props["tag"]) ? $props["tag"] : null;
$this->username = isset($props["username"]) ? $props["username"] : null;
$this->pass = isset($props["pass"]) ? $props["pass"] : null;
$this->cal_id = $cal["id"];
$this->url = $cal["caldav_url"];
$this->ctag = isset($cal["caldav_tag"]) ? $cal["caldav_tag"] : null;
$this->username = isset($cal["caldav_user"]) ? $cal["caldav_user"] : null;
$this->pass = isset($cal["caldav_pass"]) ? $cal["caldav_pass"] : null;
$this->caldav = new caldav_client($this->url, $this->username, $this->pass);
}
......@@ -75,7 +73,7 @@ class caldav_sync
* indicates that there are changes that must be synched. Returns false
* if the calendar is up to date, no sync necesarry.
*/
public function is_synced($force = false)
public function is_synced()
{
$is_synced = $this->ctag == $this->caldav->get_ctag() && $this->ctag;
caldav_driver::debug_log("Ctag indicates that calendar \"$this->cal_id\" ".($is_synced ? "is synced." : "needs update!"));
......@@ -86,8 +84,7 @@ class caldav_sync
/**
* Synchronizes given events with caldav server and returns updates.
*
* @param array List of local events.
* @param array List of caldav properties for each event.
* @param array List of hash arrays with event properties, must include "caldav_url" and "tag".
* @return array Tuple containing the following lists:
*
* Caldav properties for events to be created or to be updated with the keys:
......@@ -98,7 +95,7 @@ class caldav_sync
*
* A list of event ids that are in sync.
*/
public function get_updates($events, $caldav_props)
public function get_updates($events)
{
$ctag = $this->caldav->get_ctag();
......@@ -107,7 +104,7 @@ class caldav_sync
$this->ctag = $ctag;
$etags = $this->caldav->get_etags();
list($updates, $synced_event_ids) = $this->_get_event_updates($events, $caldav_props, $etags);
list($updates, $synced_event_ids) = $this->_get_event_updates($events, $etags);
return array($this->_get_event_data($updates), $synced_event_ids);
}
else
......@@ -121,8 +118,7 @@ class caldav_sync
/**
* Determines sync status and requried updates for the given events using given list of etags.
*
* @param array List of local events.
* @param array List of caldav properties for each event.
* @param array List of hash arrays with event properties, must include "caldav_url" and "caldav_tag".
* @param array List of current remote etags.
* @return array Tuple containing the following lists:
*
......@@ -133,7 +129,7 @@ class caldav_sync
*
* A list of event ids that are in sync.
*/
private function _get_event_updates($events, $caldav_props, $etags)
private function _get_event_updates($events, $etags)
{
$updates = array();
$in_sync = array();
......@@ -143,25 +139,25 @@ class caldav_sync
$url = $etag["url"];
$etag = $etag["etag"];
$event_found = false;
for($i = 0; $i < sizeof($events); $i ++)
foreach($events as $event)
{
if ($caldav_props[$i]["url"] == $url)
if ($event["caldav_url"] == $url)
{
$event_found = true;
if ($caldav_props[$i]["tag"] != $etag)
if ($event["caldav_tag"] != $etag)
{
caldav_driver::debug_log("Event ".$events[$i]["uid"]." needs update.");
caldav_driver::debug_log("Event ".$event["uid"]." needs update.");
array_push($updates, array(
"local_event" => $events[$i],
"local_event" => $event,
"etag" => $etag,
"url" => $url
));
}
else
{
array_push($in_sync, $events[$i]["id"]);
array_push($in_sync, $event["id"]);
}
}
}
......@@ -211,48 +207,47 @@ class caldav_sync
}
/**
* Creates the given event on the caldav server.
* Creates the given event on the CalDAV server.
*
* @param array Hash array with event properties.
* @return Caldav properties with created URL on success, false on error.
* @return Event with updated "caldav_url" and "caldav_tag" attributes, false on error.
*/
public function create_event($event)
{
$props = array(
"url" => parse_url($this->url, PHP_URL_PATH)."/".$event["uid"].".ics",
"tag" => null
"caldav_url" => parse_url($this->url, PHP_URL_PATH)."/".$event["uid"].".ics",
"caldav_tag" => null
);
caldav_driver::debug_log("Push new event to url ".$props["url"]);
$result = $this->caldav->put_event($props["url"], $event);
caldav_driver::debug_log("Push new event to url ".$props["caldav_url"]);
$result = $this->caldav->put_event($props["caldav_url"], $event);
if($result == false || $result < 0) return false;
return $props;
return array_merge($event, $props);
}
/**
* Updates the given event on the caldav server.
* Updates the given event on the CalDAV server.
*
* @param array Hash array with event properties to update.
* @param array Hash array with caldav properties "url" and "tag" for the event.
* @param array Hash array with event properties to update, must include "uid", "caldav_url" and "caldav_tag".
* @return True on success, false on error, -1 if the given event/etag is not up to date.
*/
public function update_event($event, $props)
public function update_event($event)
{
caldav_driver::debug_log("Updating event uid \"".$event["uid"]."\".");
return $this->caldav->put_event($props["url"], $event, $props["tag"]);
return $this->caldav->put_event($event["caldav_url"], $event, $event["caldav_tag"]);
}
/**
* Removes the given event from the caldav server.
*
* @param array Hash array with caldav properties "url" and "tag" for the event.
* @param array Hash array with events properties, must include "caldav_url".
* @return True on success, false on error.
*/
public function remove_event($props)
public function remove_event($event)
{
caldav_driver::debug_log("Removing event url \"".$props["url"]."\".");
return $this->caldav->remove_event($props["url"]);
caldav_driver::debug_log("Removing event uid \"".$event["uid"]."\".");
return $this->caldav->remove_event($event["caldav_url"]);
}
};
?>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment