Merge branch 'master' of ssh://git.kolab.org/git/roundcubemail-plugins-kolab
This commit is contained in:
commit
d425e9812d
16 changed files with 1570 additions and 228 deletions
|
@ -1,29 +1,175 @@
|
|||
/**
|
||||
* libkolab database schema
|
||||
*
|
||||
* @version @package_version@
|
||||
* @version 1.0
|
||||
* @author Thomas Bruederli
|
||||
* @licence GNU AGPL
|
||||
**/
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `kolab_folders`;
|
||||
|
||||
CREATE TABLE `kolab_folders` (
|
||||
`folder_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`resource` VARCHAR(255) NOT NULL,
|
||||
`type` VARCHAR(32) NOT NULL,
|
||||
`synclock` INT(10) NOT NULL DEFAULT '0',
|
||||
`ctag` VARCHAR(40) DEFAULT NULL,
|
||||
PRIMARY KEY(`folder_id`),
|
||||
INDEX `resource_type` (`resource`, `type`)
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
DROP TABLE IF EXISTS `kolab_cache`;
|
||||
|
||||
CREATE TABLE `kolab_cache` (
|
||||
`resource` VARCHAR(255) CHARACTER SET ascii NOT NULL,
|
||||
`type` VARCHAR(32) CHARACTER SET ascii NOT NULL,
|
||||
DROP TABLE IF EXISTS `kolab_cache_contact`;
|
||||
|
||||
CREATE TABLE `kolab_cache_contact` (
|
||||
`folder_id` BIGINT UNSIGNED NOT NULL,
|
||||
`msguid` BIGINT UNSIGNED NOT NULL,
|
||||
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
|
||||
`created` DATETIME DEFAULT NULL,
|
||||
`changed` DATETIME DEFAULT NULL,
|
||||
`data` TEXT NOT NULL,
|
||||
`xml` TEXT NOT NULL,
|
||||
`tags` VARCHAR(255) NOT NULL,
|
||||
`words` TEXT NOT NULL,
|
||||
`type` VARCHAR(32) CHARACTER SET ascii NOT NULL,
|
||||
CONSTRAINT `fk_kolab_cache_contact_folder` FOREIGN KEY (`folder_id`)
|
||||
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
PRIMARY KEY(`folder_id`,`msguid`),
|
||||
INDEX `contact_type` (`folder_id`,`type`)
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
DROP TABLE IF EXISTS `kolab_cache_event`;
|
||||
|
||||
CREATE TABLE `kolab_cache_event` (
|
||||
`folder_id` BIGINT UNSIGNED NOT NULL,
|
||||
`msguid` BIGINT UNSIGNED NOT NULL,
|
||||
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
|
||||
`created` DATETIME DEFAULT NULL,
|
||||
`changed` DATETIME DEFAULT NULL,
|
||||
`data` TEXT NOT NULL,
|
||||
`xml` TEXT NOT NULL,
|
||||
`tags` VARCHAR(255) NOT NULL,
|
||||
`words` TEXT NOT NULL,
|
||||
`dtstart` DATETIME,
|
||||
`dtend` DATETIME,
|
||||
CONSTRAINT `fk_kolab_cache_event_folder` FOREIGN KEY (`folder_id`)
|
||||
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
PRIMARY KEY(`folder_id`,`msguid`)
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
DROP TABLE IF EXISTS `kolab_cache_task`;
|
||||
|
||||
CREATE TABLE `kolab_cache_task` (
|
||||
`folder_id` BIGINT UNSIGNED NOT NULL,
|
||||
`msguid` BIGINT UNSIGNED NOT NULL,
|
||||
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
|
||||
`created` DATETIME DEFAULT NULL,
|
||||
`changed` DATETIME DEFAULT NULL,
|
||||
`data` TEXT NOT NULL,
|
||||
`xml` TEXT NOT NULL,
|
||||
`tags` VARCHAR(255) NOT NULL,
|
||||
`words` TEXT NOT NULL,
|
||||
`dtstart` DATETIME,
|
||||
`dtend` DATETIME,
|
||||
CONSTRAINT `fk_kolab_cache_task_folder` FOREIGN KEY (`folder_id`)
|
||||
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
PRIMARY KEY(`folder_id`,`msguid`)
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
DROP TABLE IF EXISTS `kolab_cache_journal`;
|
||||
|
||||
CREATE TABLE `kolab_cache_journal` (
|
||||
`folder_id` BIGINT UNSIGNED NOT NULL,
|
||||
`msguid` BIGINT UNSIGNED NOT NULL,
|
||||
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
|
||||
`created` DATETIME DEFAULT NULL,
|
||||
`changed` DATETIME DEFAULT NULL,
|
||||
`data` TEXT NOT NULL,
|
||||
`xml` TEXT NOT NULL,
|
||||
`tags` VARCHAR(255) NOT NULL,
|
||||
`words` TEXT NOT NULL,
|
||||
`dtstart` DATETIME,
|
||||
`dtend` DATETIME,
|
||||
CONSTRAINT `fk_kolab_cache_journal_folder` FOREIGN KEY (`folder_id`)
|
||||
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
PRIMARY KEY(`folder_id`,`msguid`)
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
DROP TABLE IF EXISTS `kolab_cache_note`;
|
||||
|
||||
CREATE TABLE `kolab_cache_note` (
|
||||
`folder_id` BIGINT UNSIGNED NOT NULL,
|
||||
`msguid` BIGINT UNSIGNED NOT NULL,
|
||||
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
|
||||
`created` DATETIME DEFAULT NULL,
|
||||
`changed` DATETIME DEFAULT NULL,
|
||||
`data` TEXT NOT NULL,
|
||||
`xml` TEXT NOT NULL,
|
||||
`tags` VARCHAR(255) NOT NULL,
|
||||
`words` TEXT NOT NULL,
|
||||
CONSTRAINT `fk_kolab_cache_note_folder` FOREIGN KEY (`folder_id`)
|
||||
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
PRIMARY KEY(`folder_id`,`msguid`)
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
DROP TABLE IF EXISTS `kolab_cache_file`;
|
||||
|
||||
CREATE TABLE `kolab_cache_file` (
|
||||
`folder_id` BIGINT UNSIGNED NOT NULL,
|
||||
`msguid` BIGINT UNSIGNED NOT NULL,
|
||||
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
|
||||
`created` DATETIME DEFAULT NULL,
|
||||
`changed` DATETIME DEFAULT NULL,
|
||||
`data` TEXT NOT NULL,
|
||||
`xml` TEXT NOT NULL,
|
||||
`tags` VARCHAR(255) NOT NULL,
|
||||
`words` TEXT NOT NULL,
|
||||
`filename` varchar(255) DEFAULT NULL,
|
||||
PRIMARY KEY(`resource`,`type`,`msguid`),
|
||||
INDEX `resource_filename` (`resource`, `filename`)
|
||||
CONSTRAINT `fk_kolab_cache_file_folder` FOREIGN KEY (`folder_id`)
|
||||
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
PRIMARY KEY(`folder_id`,`msguid`),
|
||||
INDEX `folder_filename` (`folder_id`, `filename`)
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
INSERT INTO `system` (`name`, `value`) VALUES ('libkolab-version', '2013041900');
|
||||
DROP TABLE IF EXISTS `kolab_cache_configuration`;
|
||||
|
||||
CREATE TABLE `kolab_cache_configuration` (
|
||||
`folder_id` BIGINT UNSIGNED NOT NULL,
|
||||
`msguid` BIGINT UNSIGNED NOT NULL,
|
||||
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
|
||||
`created` DATETIME DEFAULT NULL,
|
||||
`changed` DATETIME DEFAULT NULL,
|
||||
`data` TEXT NOT NULL,
|
||||
`xml` TEXT NOT NULL,
|
||||
`tags` VARCHAR(255) NOT NULL,
|
||||
`words` TEXT NOT NULL,
|
||||
`type` VARCHAR(32) CHARACTER SET ascii NOT NULL,
|
||||
CONSTRAINT `fk_kolab_cache_configuration_folder` FOREIGN KEY (`folder_id`)
|
||||
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
PRIMARY KEY(`folder_id`,`msguid`),
|
||||
INDEX `configuration_type` (`folder_id`,`type`)
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
DROP TABLE IF EXISTS `kolab_cache_freebusy`;
|
||||
|
||||
CREATE TABLE `kolab_cache_freebusy` (
|
||||
`folder_id` BIGINT UNSIGNED NOT NULL,
|
||||
`msguid` BIGINT UNSIGNED NOT NULL,
|
||||
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
|
||||
`created` DATETIME DEFAULT NULL,
|
||||
`changed` DATETIME DEFAULT NULL,
|
||||
`data` TEXT NOT NULL,
|
||||
`xml` TEXT NOT NULL,
|
||||
`tags` VARCHAR(255) NOT NULL,
|
||||
`words` TEXT NOT NULL,
|
||||
`dtstart` DATETIME,
|
||||
`dtend` DATETIME,
|
||||
CONSTRAINT `fk_kolab_cache_freebusy_folder` FOREIGN KEY (`folder_id`)
|
||||
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
PRIMARY KEY(`folder_id`,`msguid`)
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
|
||||
INSERT INTO `system` (`name`, `value`) VALUES ('libkolab-version', '2013100400');
|
||||
|
|
174
plugins/libkolab/SQL/mysql/2013100400.sql
Normal file
174
plugins/libkolab/SQL/mysql/2013100400.sql
Normal file
|
@ -0,0 +1,174 @@
|
|||
CREATE TABLE `kolab_folders` (
|
||||
`folder_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`resource` VARCHAR(255) NOT NULL,
|
||||
`type` VARCHAR(32) NOT NULL,
|
||||
`synclock` INT(10) NOT NULL DEFAULT '0',
|
||||
`ctag` VARCHAR(40) DEFAULT NULL,
|
||||
PRIMARY KEY(`folder_id`),
|
||||
INDEX `resource_type` (`resource`, `type`)
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
CREATE TABLE `kolab_cache_contact` (
|
||||
`folder_id` BIGINT UNSIGNED NOT NULL,
|
||||
`msguid` BIGINT UNSIGNED NOT NULL,
|
||||
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
|
||||
`created` DATETIME DEFAULT NULL,
|
||||
`changed` DATETIME DEFAULT NULL,
|
||||
`data` TEXT NOT NULL,
|
||||
`xml` TEXT NOT NULL,
|
||||
`tags` VARCHAR(255) NOT NULL,
|
||||
`words` TEXT NOT NULL,
|
||||
`type` VARCHAR(32) CHARACTER SET ascii NOT NULL,
|
||||
CONSTRAINT `fk_kolab_cache_contact_folder` FOREIGN KEY (`folder_id`)
|
||||
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
PRIMARY KEY(`folder_id`,`msguid`),
|
||||
INDEX `contact_type` (`folder_id`,`type`)
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
CREATE TABLE `kolab_cache_event` (
|
||||
`folder_id` BIGINT UNSIGNED NOT NULL,
|
||||
`msguid` BIGINT UNSIGNED NOT NULL,
|
||||
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
|
||||
`created` DATETIME DEFAULT NULL,
|
||||
`changed` DATETIME DEFAULT NULL,
|
||||
`data` TEXT NOT NULL,
|
||||
`xml` TEXT NOT NULL,
|
||||
`tags` VARCHAR(255) NOT NULL,
|
||||
`words` TEXT NOT NULL,
|
||||
`dtstart` DATETIME,
|
||||
`dtend` DATETIME,
|
||||
CONSTRAINT `fk_kolab_cache_event_folder` FOREIGN KEY (`folder_id`)
|
||||
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
PRIMARY KEY(`folder_id`,`msguid`)
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
CREATE TABLE `kolab_cache_task` (
|
||||
`folder_id` BIGINT UNSIGNED NOT NULL,
|
||||
`msguid` BIGINT UNSIGNED NOT NULL,
|
||||
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
|
||||
`created` DATETIME DEFAULT NULL,
|
||||
`changed` DATETIME DEFAULT NULL,
|
||||
`data` TEXT NOT NULL,
|
||||
`xml` TEXT NOT NULL,
|
||||
`tags` VARCHAR(255) NOT NULL,
|
||||
`words` TEXT NOT NULL,
|
||||
`dtstart` DATETIME,
|
||||
`dtend` DATETIME,
|
||||
CONSTRAINT `fk_kolab_cache_task_folder` FOREIGN KEY (`folder_id`)
|
||||
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
PRIMARY KEY(`folder_id`,`msguid`)
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
CREATE TABLE `kolab_cache_journal` (
|
||||
`folder_id` BIGINT UNSIGNED NOT NULL,
|
||||
`msguid` BIGINT UNSIGNED NOT NULL,
|
||||
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
|
||||
`created` DATETIME DEFAULT NULL,
|
||||
`changed` DATETIME DEFAULT NULL,
|
||||
`data` TEXT NOT NULL,
|
||||
`xml` TEXT NOT NULL,
|
||||
`tags` VARCHAR(255) NOT NULL,
|
||||
`words` TEXT NOT NULL,
|
||||
`dtstart` DATETIME,
|
||||
`dtend` DATETIME,
|
||||
CONSTRAINT `fk_kolab_cache_journal_folder` FOREIGN KEY (`folder_id`)
|
||||
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
PRIMARY KEY(`folder_id`,`msguid`)
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
CREATE TABLE `kolab_cache_note` (
|
||||
`folder_id` BIGINT UNSIGNED NOT NULL,
|
||||
`msguid` BIGINT UNSIGNED NOT NULL,
|
||||
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
|
||||
`created` DATETIME DEFAULT NULL,
|
||||
`changed` DATETIME DEFAULT NULL,
|
||||
`data` TEXT NOT NULL,
|
||||
`xml` TEXT NOT NULL,
|
||||
`tags` VARCHAR(255) NOT NULL,
|
||||
`words` TEXT NOT NULL,
|
||||
CONSTRAINT `fk_kolab_cache_note_folder` FOREIGN KEY (`folder_id`)
|
||||
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
PRIMARY KEY(`folder_id`,`msguid`)
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
CREATE TABLE `kolab_cache_file` (
|
||||
`folder_id` BIGINT UNSIGNED NOT NULL,
|
||||
`msguid` BIGINT UNSIGNED NOT NULL,
|
||||
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
|
||||
`created` DATETIME DEFAULT NULL,
|
||||
`changed` DATETIME DEFAULT NULL,
|
||||
`data` TEXT NOT NULL,
|
||||
`xml` TEXT NOT NULL,
|
||||
`tags` VARCHAR(255) NOT NULL,
|
||||
`words` TEXT NOT NULL,
|
||||
`filename` varchar(255) DEFAULT NULL,
|
||||
CONSTRAINT `fk_kolab_cache_file_folder` FOREIGN KEY (`folder_id`)
|
||||
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
PRIMARY KEY(`folder_id`,`msguid`),
|
||||
INDEX `folder_filename` (`folder_id`, `filename`)
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
CREATE TABLE `kolab_cache_configuration` (
|
||||
`folder_id` BIGINT UNSIGNED NOT NULL,
|
||||
`msguid` BIGINT UNSIGNED NOT NULL,
|
||||
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
|
||||
`created` DATETIME DEFAULT NULL,
|
||||
`changed` DATETIME DEFAULT NULL,
|
||||
`data` TEXT NOT NULL,
|
||||
`xml` TEXT NOT NULL,
|
||||
`tags` VARCHAR(255) NOT NULL,
|
||||
`words` TEXT NOT NULL,
|
||||
`type` VARCHAR(32) CHARACTER SET ascii NOT NULL,
|
||||
CONSTRAINT `fk_kolab_cache_configuration_folder` FOREIGN KEY (`folder_id`)
|
||||
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
PRIMARY KEY(`folder_id`,`msguid`),
|
||||
INDEX `configuration_type` (`folder_id`,`type`)
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
CREATE TABLE `kolab_cache_freebusy` (
|
||||
`folder_id` BIGINT UNSIGNED NOT NULL,
|
||||
`msguid` BIGINT UNSIGNED NOT NULL,
|
||||
`uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
|
||||
`created` DATETIME DEFAULT NULL,
|
||||
`changed` DATETIME DEFAULT NULL,
|
||||
`data` TEXT NOT NULL,
|
||||
`xml` TEXT NOT NULL,
|
||||
`tags` VARCHAR(255) NOT NULL,
|
||||
`words` TEXT NOT NULL,
|
||||
`dtstart` DATETIME,
|
||||
`dtend` DATETIME,
|
||||
CONSTRAINT `fk_kolab_cache_freebusy_folder` FOREIGN KEY (`folder_id`)
|
||||
REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
PRIMARY KEY(`folder_id`,`msguid`)
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
|
||||
-- Migrate data from old kolab_cache table
|
||||
|
||||
INSERT INTO kolab_folders (resource, type)
|
||||
SELECT DISTINCT resource, type
|
||||
FROM kolab_cache WHERE type IN ('event','contact','task','file');
|
||||
|
||||
INSERT INTO kolab_cache_event (folder_id, msguid, uid, created, changed, data, xml, tags, words, dtstart, dtend)
|
||||
SELECT kolab_folders.folder_id, msguid, uid, created, changed, data, xml, tags, words, dtstart, dtend
|
||||
FROM kolab_cache LEFT JOIN kolab_folders ON (kolab_folders.resource = kolab_cache.resource)
|
||||
WHERE kolab_cache.type = 'event' AND kolab_folders.folder_id IS NOT NULL;
|
||||
|
||||
INSERT INTO kolab_cache_task (folder_id, msguid, uid, created, changed, data, xml, tags, words, dtstart, dtend)
|
||||
SELECT kolab_folders.folder_id, msguid, uid, created, changed, data, xml, tags, words, dtstart, dtend
|
||||
FROM kolab_cache LEFT JOIN kolab_folders ON (kolab_folders.resource = kolab_cache.resource)
|
||||
WHERE kolab_cache.type = 'task' AND kolab_folders.folder_id IS NOT NULL;
|
||||
|
||||
INSERT INTO kolab_cache_contact (folder_id, msguid, uid, created, changed, data, xml, tags, words, type)
|
||||
SELECT kolab_folders.folder_id, msguid, uid, created, changed, data, xml, tags, words, kolab_cache.type
|
||||
FROM kolab_cache LEFT JOIN kolab_folders ON (kolab_folders.resource = kolab_cache.resource)
|
||||
WHERE kolab_cache.type IN ('contact','distribution-list') AND kolab_folders.folder_id IS NOT NULL;
|
||||
|
||||
INSERT INTO kolab_cache_file (folder_id, msguid, uid, created, changed, data, xml, tags, words, filename)
|
||||
SELECT kolab_folders.folder_id, msguid, uid, created, changed, data, xml, tags, words, filename
|
||||
FROM kolab_cache LEFT JOIN kolab_folders ON (kolab_folders.resource = kolab_cache.resource)
|
||||
WHERE kolab_cache.type = 'file' AND kolab_folders.folder_id IS NOT NULL;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `kolab_cache`;
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
/**
|
||||
* Kolab storage cache modification script
|
||||
*
|
||||
* @version 3.0
|
||||
* @version 3.1
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com>
|
||||
|
@ -56,7 +56,14 @@ $opts = get_opt(array(
|
|||
$opts['username'] = !empty($opts[1]) ? $opts[1] : $opts['user'];
|
||||
$action = $opts[0];
|
||||
|
||||
$rcmail = rcube::get_instance();
|
||||
$rcmail = rcube::get_instance(rcube::INIT_WITH_DB | rcube::INIT_WITH_PLUGINS);
|
||||
|
||||
|
||||
// connect to database
|
||||
$db = $rcmail->get_dbh();
|
||||
$db->db_connect('w');
|
||||
if (!$db->is_connected() || $db->is_error())
|
||||
die("No DB connection\n");
|
||||
|
||||
|
||||
/*
|
||||
|
@ -68,32 +75,42 @@ switch (strtolower($action)) {
|
|||
* Clear/expunge all cache records
|
||||
*/
|
||||
case 'expunge':
|
||||
$folder_types = $opts['type'] ? explode(',', $opts['type']) : array('contact','configuration','event','file','journal','note','task');
|
||||
$folder_types_db = array_map(array($db, 'quote'), $folder_types);
|
||||
$expire = strtotime(!empty($opts[2]) ? $opts[2] : 'now - 10 days');
|
||||
$sql_add = " AND created <= '" . date('Y-m-d 00:00:00', $expire) . "'";
|
||||
if ($opts['limit']) {
|
||||
$sql_add .= ' LIMIT ' . intval($opts['limit']);
|
||||
$sql_where = "type IN (" . join(',', $folder_types_db) . ")";
|
||||
|
||||
if ($opts['username']) {
|
||||
$sql_where .= ' AND resource LIKE ?';
|
||||
}
|
||||
|
||||
case 'clear':
|
||||
// connect to database
|
||||
$db = $rcmail->get_dbh();
|
||||
$db->db_connect('w');
|
||||
if (!$db->is_connected() || $db->is_error())
|
||||
die("No DB connection\n");
|
||||
$sql_query = "DELETE FROM %s WHERE folder_id IN (SELECT folder_id FROM kolab_folders WHERE $sql_where) AND created <= " . $db->quote(date('Y-m-d 00:00:00', $expire));
|
||||
if ($opts['limit']) {
|
||||
$sql_query = ' LIMIT ' . intval($opts['limit']);
|
||||
}
|
||||
foreach ($folder_types as $type) {
|
||||
$table_name = 'kolab_cache_' . $type;
|
||||
$db->query(sprintf($sql_query, $table_name), resource_prefix($opts).'%');
|
||||
echo $db->affected_rows() . " records deleted from '$table_name'\n";
|
||||
}
|
||||
|
||||
$folder_types = $opts['type'] ? explode(',', $opts['type']) : array('contact','distribution-list','event','task','configuration','file');
|
||||
$db->query("UPDATE kolab_folders SET ctag='' WHERE $sql_where", resource_prefix($opts).'%');
|
||||
break;
|
||||
|
||||
case 'clear':
|
||||
$folder_types = $opts['type'] ? explode(',', $opts['type']) : array('contact','configuration','event','file','journal','note','task');
|
||||
$folder_types_db = array_map(array($db, 'quote'), $folder_types);
|
||||
|
||||
if ($opts['all']) {
|
||||
$sql_query = "DELETE FROM kolab_cache WHERE type IN (" . join(',', $folder_types_db) . ")";
|
||||
$sql_query = "DELETE FROM kolab_folders WHERE 1";
|
||||
}
|
||||
else if ($opts['username']) {
|
||||
$sql_query = "DELETE FROM kolab_cache WHERE type IN (" . join(',', $folder_types_db) . ") AND resource LIKE ?";
|
||||
$sql_query = "DELETE FROM kolab_folders WHERE type IN (" . join(',', $folder_types_db) . ") AND resource LIKE ?";
|
||||
}
|
||||
|
||||
if ($sql_query) {
|
||||
$db->query($sql_query . $sql_add, resource_prefix($opts).'%');
|
||||
echo $db->affected_rows() . " records deleted from 'kolab_cache'\n";
|
||||
echo $db->affected_rows() . " records deleted from 'kolab_folders'\n";
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -106,7 +123,7 @@ case 'prewarm':
|
|||
$rcmail->plugins->load_plugin('libkolab');
|
||||
|
||||
if (authenticate($opts)) {
|
||||
$folder_types = $opts['type'] ? explode(',', $opts['type']) : array('contact','event','task','configuration','file');
|
||||
$folder_types = $opts['type'] ? explode(',', $opts['type']) : array('contact','configuration','event','file','task');
|
||||
foreach ($folder_types as $type) {
|
||||
// sync every folder of the given type
|
||||
foreach (kolab_storage::get_folders($type) as $folder) {
|
||||
|
@ -140,7 +157,7 @@ default:
|
|||
*/
|
||||
function resource_prefix($opts)
|
||||
{
|
||||
return 'imap://' . urlencode($opts['username']) . '@' . $opts['host'] . '/';
|
||||
return 'imap://' . str_replace('%', '\\%', urlencode($opts['username'])) . '@' . $opts['host'] . '/';
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -24,3 +24,9 @@ $rcmail_config['kolab_custom_display_names'] = false;
|
|||
// See http://pear.php.net/manual/en/package.http.http-request2.config.php
|
||||
// for list of supported configuration options (array keys)
|
||||
$rcmail_config['kolab_http_request'] = array();
|
||||
|
||||
// When kolab_cache is enabled Roundcube's messages cache will be redundant
|
||||
// when working on kolab folders. Here we can:
|
||||
// 2 - bypass messages/indexes cache completely
|
||||
// 1 - bypass only messages, but use index cache
|
||||
$rcmail_config['kolab_messages_cache_bypass'] = 0;
|
||||
|
|
|
@ -158,7 +158,7 @@ class kolab_storage
|
|||
* This will search all folders storing objects of the given type.
|
||||
*
|
||||
* @param string Object UID
|
||||
* @param string Object type (contact,distribution-list,event,task,note)
|
||||
* @param string Object type (contact,event,task,journal,file,note,configuration)
|
||||
* @return array The Kolab object represented as hash array or false if not found
|
||||
*/
|
||||
public static function get_object($uid, $type)
|
||||
|
@ -599,7 +599,7 @@ class kolab_storage
|
|||
*
|
||||
* @param string Optional root folder
|
||||
* @param string Optional name pattern
|
||||
* @param string Data type to list folders for (contact,distribution-list,event,task,note,mail)
|
||||
* @param string Data type to list folders for (contact,event,task,journal,file,note,mail,configuration)
|
||||
* @param boolean Enable to return subscribed folders only (null to use configured subscription mode)
|
||||
* @param array Will be filled with folder-types data
|
||||
*
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* @version @package_version@
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com>
|
||||
* Copyright (C) 2012-2013, Kolab Systems AG <contact@kolabsys.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
|
@ -24,24 +24,44 @@
|
|||
|
||||
class kolab_storage_cache
|
||||
{
|
||||
private $db;
|
||||
private $imap;
|
||||
private $folder;
|
||||
private $uid2msg;
|
||||
private $objects;
|
||||
private $index = array();
|
||||
private $resource_uri;
|
||||
private $enabled = true;
|
||||
private $synched = false;
|
||||
private $synclock = false;
|
||||
private $ready = false;
|
||||
private $max_sql_packet;
|
||||
private $max_sync_lock_time = 600;
|
||||
private $binary_items = array(
|
||||
'photo' => '|<photo><uri>[^;]+;base64,([^<]+)</uri></photo>|i',
|
||||
'pgppublickey' => '|<key><uri>date:application/pgp-keys;base64,([^<]+)</uri></photo>|i',
|
||||
'pkcs7publickey' => '|<key><uri>date:application/pkcs7-mime;base64,([^<]+)</uri></photo>|i',
|
||||
);
|
||||
protected $db;
|
||||
protected $imap;
|
||||
protected $folder;
|
||||
protected $uid2msg;
|
||||
protected $objects;
|
||||
protected $index = array();
|
||||
protected $metadata = array();
|
||||
protected $folder_id;
|
||||
protected $resource_uri;
|
||||
protected $enabled = true;
|
||||
protected $synched = false;
|
||||
protected $synclock = false;
|
||||
protected $ready = false;
|
||||
protected $cache_table;
|
||||
protected $folders_table;
|
||||
protected $max_sql_packet;
|
||||
protected $max_sync_lock_time = 600;
|
||||
protected $binary_items = array();
|
||||
protected $extra_cols = array();
|
||||
|
||||
|
||||
/**
|
||||
* Factory constructor
|
||||
*/
|
||||
public static function factory(kolab_storage_folder $storage_folder)
|
||||
{
|
||||
$subclass = 'kolab_storage_cache_' . $storage_folder->type;
|
||||
if (class_exists($subclass)) {
|
||||
return new $subclass($storage_folder);
|
||||
}
|
||||
else {
|
||||
rcube::raise_error(array(
|
||||
'code' => 900,
|
||||
'type' => 'php',
|
||||
'message' => "No kolab_storage_cache class found for folder of type " . $storage_folder->type
|
||||
), true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -55,6 +75,8 @@ class kolab_storage_cache
|
|||
$this->enabled = $rcmail->config->get('kolab_cache', false);
|
||||
|
||||
if ($this->enabled) {
|
||||
// always read folder cache and lock state from DB master
|
||||
$this->db->set_table_dsn('kolab_folders', 'w');
|
||||
// remove sync-lock on script termination
|
||||
$rcmail->add_shutdown_function(array($this, '_sync_unlock'));
|
||||
}
|
||||
|
@ -80,9 +102,19 @@ class kolab_storage_cache
|
|||
|
||||
// compose fully qualified ressource uri for this instance
|
||||
$this->resource_uri = $this->folder->get_resource_uri();
|
||||
$this->folders_table = $this->db->table_name('kolab_folders');
|
||||
$this->cache_table = $this->db->table_name('kolab_cache_' . $this->folder->type);
|
||||
$this->ready = $this->enabled;
|
||||
$this->folder_id = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this cache supports query by type
|
||||
*/
|
||||
public function has_type_col()
|
||||
{
|
||||
return in_array('type', $this->extra_cols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronize local cache data with remote
|
||||
|
@ -96,52 +128,66 @@ class kolab_storage_cache
|
|||
// increase time limit
|
||||
@set_time_limit($this->max_sync_lock_time);
|
||||
|
||||
// lock synchronization for this folder or wait if locked
|
||||
$this->_sync_lock();
|
||||
// read cached folder metadata
|
||||
$this->_read_folder_data();
|
||||
|
||||
// synchronize IMAP mailbox cache
|
||||
$this->imap->folder_sync($this->folder->name);
|
||||
// check cache status hash first ($this->metadata is set in _read_folder_data())
|
||||
if ($this->metadata['ctag'] != $this->folder->get_ctag()) {
|
||||
|
||||
// compare IMAP index with object cache index
|
||||
$imap_index = $this->imap->index($this->folder->name, null, null, true, true);
|
||||
$this->index = $imap_index->get();
|
||||
// lock synchronization for this folder or wait if locked
|
||||
$this->_sync_lock();
|
||||
|
||||
// determine objects to fetch or to invalidate
|
||||
if ($this->ready) {
|
||||
// read cache index
|
||||
$sql_result = $this->db->query(
|
||||
"SELECT msguid, uid FROM kolab_cache WHERE resource=? AND type<>?",
|
||||
$this->resource_uri,
|
||||
'lock'
|
||||
);
|
||||
// disable messages cache if configured to do so
|
||||
$this->bypass(true);
|
||||
|
||||
$old_index = array();
|
||||
while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
|
||||
$old_index[] = $sql_arr['msguid'];
|
||||
$this->uid2msg[$sql_arr['uid']] = $sql_arr['msguid'];
|
||||
}
|
||||
// synchronize IMAP mailbox cache
|
||||
$this->imap->folder_sync($this->folder->name);
|
||||
|
||||
// fetch new objects from imap
|
||||
foreach (array_diff($this->index, $old_index) as $msguid) {
|
||||
if ($object = $this->folder->read_object($msguid, '*')) {
|
||||
$this->_extended_insert($msguid, $object);
|
||||
}
|
||||
}
|
||||
$this->_extended_insert(0, null);
|
||||
// compare IMAP index with object cache index
|
||||
$imap_index = $this->imap->index($this->folder->name);
|
||||
$this->index = $imap_index->get();
|
||||
|
||||
// delete invalid entries from local DB
|
||||
$del_index = array_diff($old_index, $this->index);
|
||||
if (!empty($del_index)) {
|
||||
$quoted_ids = join(',', array_map(array($this->db, 'quote'), $del_index));
|
||||
$this->db->query(
|
||||
"DELETE FROM kolab_cache WHERE resource=? AND msguid IN ($quoted_ids)",
|
||||
$this->resource_uri
|
||||
// determine objects to fetch or to invalidate
|
||||
if ($this->ready) {
|
||||
// read cache index
|
||||
$sql_result = $this->db->query(
|
||||
"SELECT msguid, uid FROM $this->cache_table WHERE folder_id=?",
|
||||
$this->folder_id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// remove lock
|
||||
$this->_sync_unlock();
|
||||
$old_index = array();
|
||||
while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
|
||||
$old_index[] = $sql_arr['msguid'];
|
||||
$this->uid2msg[$sql_arr['uid']] = $sql_arr['msguid'];
|
||||
}
|
||||
|
||||
// fetch new objects from imap
|
||||
foreach (array_diff($this->index, $old_index) as $msguid) {
|
||||
if ($object = $this->folder->read_object($msguid, '*')) {
|
||||
$this->_extended_insert($msguid, $object);
|
||||
}
|
||||
}
|
||||
$this->_extended_insert(0, null);
|
||||
|
||||
// delete invalid entries from local DB
|
||||
$del_index = array_diff($old_index, $this->index);
|
||||
if (!empty($del_index)) {
|
||||
$quoted_ids = join(',', array_map(array($this->db, 'quote'), $del_index));
|
||||
$this->db->query(
|
||||
"DELETE FROM $this->cache_table WHERE folder_id=? AND msguid IN ($quoted_ids)",
|
||||
$this->folder_id
|
||||
);
|
||||
}
|
||||
|
||||
// update ctag value (will be written to database in _sync_unlock())
|
||||
$this->metadata['ctag'] = $this->folder->get_ctag();
|
||||
}
|
||||
|
||||
$this->bypass(false);
|
||||
|
||||
// remove lock
|
||||
$this->_sync_unlock();
|
||||
}
|
||||
|
||||
$this->synched = time();
|
||||
}
|
||||
|
@ -165,11 +211,12 @@ class kolab_storage_cache
|
|||
// load object if not in memory
|
||||
if (!isset($this->objects[$msguid])) {
|
||||
if ($this->ready) {
|
||||
$this->_read_folder_data();
|
||||
|
||||
$sql_result = $this->db->query(
|
||||
"SELECT * FROM kolab_cache ".
|
||||
"WHERE resource=? AND type=? AND msguid=?",
|
||||
$this->resource_uri,
|
||||
$type ?: $this->folder->type,
|
||||
"SELECT * FROM $this->cache_table ".
|
||||
"WHERE folder_id=? AND msguid=?",
|
||||
$this->folder_id,
|
||||
$msguid
|
||||
);
|
||||
|
||||
|
@ -210,8 +257,9 @@ class kolab_storage_cache
|
|||
|
||||
// remove old entry
|
||||
if ($this->ready) {
|
||||
$this->db->query("DELETE FROM kolab_cache WHERE resource=? AND msguid=? AND type<>?",
|
||||
$this->resource_uri, $msguid, 'lock');
|
||||
$this->_read_folder_data();
|
||||
$this->db->query("DELETE FROM $this->cache_table WHERE folder_id=? AND msguid=?",
|
||||
$this->folder_id, $msguid);
|
||||
}
|
||||
|
||||
if ($object) {
|
||||
|
@ -235,27 +283,33 @@ class kolab_storage_cache
|
|||
{
|
||||
// write to cache
|
||||
if ($this->ready) {
|
||||
$sql_data = $this->_serialize($object);
|
||||
$objtype = $object['_type'] ? $object['_type'] : $this->folder->type;
|
||||
$this->_read_folder_data();
|
||||
|
||||
$result = $this->db->query(
|
||||
"INSERT INTO kolab_cache ".
|
||||
" (resource, type, msguid, uid, created, changed, data, xml, dtstart, dtend, tags, words, filename)".
|
||||
" VALUES (?, ?, ?, ?, " . $this->db->now() . ", ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
$this->resource_uri,
|
||||
$objtype,
|
||||
$sql_data = $this->_serialize($object);
|
||||
|
||||
$extra_cols = $this->extra_cols ? ', ' . join(', ', $this->extra_cols) : '';
|
||||
$extra_fields = $this->extra_cols ? str_repeat(', ?', count($this->extra_cols)) : '';
|
||||
|
||||
$args = array(
|
||||
"INSERT INTO $this->cache_table ".
|
||||
" (folder_id, msguid, uid, created, changed, data, xml, tags, words $extra_cols)".
|
||||
" VALUES (?, ?, ?, " . $this->db->now() . ", ?, ?, ?, ?, ? $extra_fields)",
|
||||
$this->folder_id,
|
||||
$msguid,
|
||||
$object['uid'],
|
||||
$sql_data['changed'],
|
||||
$sql_data['data'],
|
||||
$sql_data['xml'],
|
||||
$sql_data['dtstart'],
|
||||
$sql_data['dtend'],
|
||||
$sql_data['tags'],
|
||||
$sql_data['words'],
|
||||
$sql_data['filename']
|
||||
);
|
||||
|
||||
foreach ($this->extra_cols as $col) {
|
||||
$args[] = $sql_data[$col];
|
||||
}
|
||||
|
||||
$result = call_user_func_array(array($this->db, 'query'), $args);
|
||||
|
||||
if (!$this->db->affected_rows($result)) {
|
||||
rcube::raise_error(array(
|
||||
'code' => 900, 'type' => 'php',
|
||||
|
@ -283,14 +337,15 @@ class kolab_storage_cache
|
|||
|
||||
// resolve new message UID in target folder
|
||||
if ($new_msguid = $target->cache->uid2msguid($uid)) {
|
||||
$this->_read_folder_data();
|
||||
|
||||
$this->db->query(
|
||||
"UPDATE kolab_cache SET resource=?, msguid=? ".
|
||||
"WHERE resource=? AND msguid=? AND type<>?",
|
||||
$target->get_resource_uri(),
|
||||
"UPDATE $this->cache_table SET folder_id=?, msguid=? ".
|
||||
"WHERE folder_id=? AND msguid=?",
|
||||
$target->folder_id,
|
||||
$new_msguid,
|
||||
$this->resource_uri,
|
||||
$msguid,
|
||||
'lock'
|
||||
$this->folder_id,
|
||||
$msguid
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
@ -307,11 +362,11 @@ class kolab_storage_cache
|
|||
*/
|
||||
public function purge($type = null)
|
||||
{
|
||||
$this->_read_folder_data();
|
||||
|
||||
$result = $this->db->query(
|
||||
"DELETE FROM kolab_cache WHERE resource=?".
|
||||
($type ? ' AND type=?' : ''),
|
||||
$this->resource_uri,
|
||||
$type
|
||||
"DELETE FROM $this->cache_table WHERE folder_id=?".
|
||||
$this->folder_id
|
||||
);
|
||||
return $this->db->affected_rows($result);
|
||||
}
|
||||
|
@ -327,7 +382,7 @@ class kolab_storage_cache
|
|||
|
||||
// resolve new message UID in target folder
|
||||
$this->db->query(
|
||||
"UPDATE kolab_cache SET resource=? ".
|
||||
"UPDATE $this->folders_table SET resource=? ".
|
||||
"WHERE resource=?",
|
||||
$target->get_resource_uri(),
|
||||
$this->resource_uri
|
||||
|
@ -348,10 +403,12 @@ class kolab_storage_cache
|
|||
|
||||
// read from local cache DB (assume it to be synchronized)
|
||||
if ($this->ready) {
|
||||
$this->_read_folder_data();
|
||||
|
||||
$sql_result = $this->db->query(
|
||||
"SELECT " . ($uids ? 'msguid, uid' : '*') . " FROM kolab_cache ".
|
||||
"WHERE resource=? " . $this->_sql_where($query),
|
||||
$this->resource_uri
|
||||
"SELECT " . ($uids ? 'msguid, uid' : '*') . " FROM $this->cache_table ".
|
||||
"WHERE folder_id=? " . $this->_sql_where($query),
|
||||
$this->folder_id
|
||||
);
|
||||
|
||||
while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
|
||||
|
@ -408,10 +465,12 @@ class kolab_storage_cache
|
|||
|
||||
// cache is in sync, we can count records in local DB
|
||||
if ($this->synched) {
|
||||
$this->_read_folder_data();
|
||||
|
||||
$sql_result = $this->db->query(
|
||||
"SELECT COUNT(*) AS numrows FROM kolab_cache ".
|
||||
"WHERE resource=? " . $this->_sql_where($query),
|
||||
$this->resource_uri
|
||||
"SELECT COUNT(*) AS numrows FROM $this->cache_table ".
|
||||
"WHERE folder_id=? " . $this->_sql_where($query),
|
||||
$this->folder_id
|
||||
);
|
||||
|
||||
$sql_arr = $this->db->fetch_assoc($sql_result);
|
||||
|
@ -432,10 +491,10 @@ class kolab_storage_cache
|
|||
/**
|
||||
* Helper method to compose a valid SQL query from pseudo filter triplets
|
||||
*/
|
||||
private function _sql_where($query)
|
||||
protected function _sql_where($query)
|
||||
{
|
||||
$sql_where = '';
|
||||
foreach ($query as $param) {
|
||||
foreach ((array) $query as $param) {
|
||||
if (is_array($param[0])) {
|
||||
$subq = array();
|
||||
foreach ($param[0] as $q) {
|
||||
|
@ -477,7 +536,7 @@ class kolab_storage_cache
|
|||
* Helper method to convert the given pseudo-query triplets into
|
||||
* an associative filter array with 'equals' values only
|
||||
*/
|
||||
private function _query2assoc($query)
|
||||
protected function _query2assoc($query)
|
||||
{
|
||||
// extract object type from query parameter
|
||||
$filter = array();
|
||||
|
@ -496,7 +555,7 @@ class kolab_storage_cache
|
|||
* @param string IMAP folder to read from
|
||||
* @return array List of parsed Kolab objects
|
||||
*/
|
||||
private function _fetch($index, $type = null, $folder = null)
|
||||
protected function _fetch($index, $type = null, $folder = null)
|
||||
{
|
||||
$results = array();
|
||||
foreach ((array)$index as $msguid) {
|
||||
|
@ -518,13 +577,19 @@ class kolab_storage_cache
|
|||
* @param string IMAP folder to read from
|
||||
* @return array List of parsed Kolab objects
|
||||
*/
|
||||
private function _fetch_uids($index, $type = null)
|
||||
protected function _fetch_uids($index, $type = null)
|
||||
{
|
||||
if (!$type)
|
||||
$type = $this->folder->type;
|
||||
|
||||
$this->bypass(true);
|
||||
|
||||
$results = array();
|
||||
foreach ((array)$this->imap->fetch_headers($this->folder->name, $index, false) as $msguid => $headers) {
|
||||
$headers = $this->imap->fetch_headers($this->folder->name, $index, false);
|
||||
|
||||
$this->bypass(false);
|
||||
|
||||
foreach ((array)$headers as $msguid => $headers) {
|
||||
$object_type = kolab_format::mime2object_type($headers->others['x-kolab-type']);
|
||||
|
||||
// check object type header and abort on mismatch
|
||||
|
@ -543,35 +608,9 @@ class kolab_storage_cache
|
|||
/**
|
||||
* Helper method to convert the given Kolab object into a dataset to be written to cache
|
||||
*/
|
||||
private function _serialize($object)
|
||||
protected function _serialize($object)
|
||||
{
|
||||
$sql_data = array('changed' => null, 'dtstart' => null, 'dtend' => null, 'xml' => '', 'tags' => '', 'words' => '');
|
||||
$objtype = $object['_type'] ? $object['_type'] : $this->folder->type;
|
||||
|
||||
// set type specific values
|
||||
if ($objtype == 'event') {
|
||||
// database runs in server's timezone so using date() is what we want
|
||||
$sql_data['dtstart'] = date('Y-m-d H:i:s', is_object($object['start']) ? $object['start']->format('U') : $object['start']);
|
||||
$sql_data['dtend'] = date('Y-m-d H:i:s', is_object($object['end']) ? $object['end']->format('U') : $object['end']);
|
||||
|
||||
// extend date range for recurring events
|
||||
if ($object['recurrence'] && $object['_formatobj']) {
|
||||
$recurrence = new kolab_date_recurrence($object['_formatobj']);
|
||||
$sql_data['dtend'] = date('Y-m-d 23:59:59', $recurrence->end() ?: strtotime('now +1 year'));
|
||||
}
|
||||
}
|
||||
else if ($objtype == 'task') {
|
||||
if ($object['start'])
|
||||
$sql_data['dtstart'] = date('Y-m-d H:i:s', is_object($object['start']) ? $object['start']->format('U') : $object['start']);
|
||||
if ($object['due'])
|
||||
$sql_data['dtend'] = date('Y-m-d H:i:s', is_object($object['due']) ? $object['due']->format('U') : $object['due']);
|
||||
}
|
||||
else if ($objtype == 'file') {
|
||||
if (!empty($object['_attachments'])) {
|
||||
reset($object['_attachments']);
|
||||
$sql_data['filename'] = $object['_attachments'][key($object['_attachments'])]['name'];
|
||||
}
|
||||
}
|
||||
$sql_data = array('changed' => null, 'xml' => '', 'tags' => '', 'words' => '');
|
||||
|
||||
if ($object['changed']) {
|
||||
$sql_data['changed'] = date('Y-m-d H:i:s', is_object($object['changed']) ? $object['changed']->format('U') : $object['changed']);
|
||||
|
@ -613,7 +652,7 @@ class kolab_storage_cache
|
|||
/**
|
||||
* Helper method to turn stored cache data into a valid storage object
|
||||
*/
|
||||
private function _unserialize($sql_arr)
|
||||
protected function _unserialize($sql_arr)
|
||||
{
|
||||
$object = unserialize($sql_arr['data']);
|
||||
|
||||
|
@ -625,11 +664,11 @@ class kolab_storage_cache
|
|||
}
|
||||
|
||||
// add meta data
|
||||
$object['_type'] = $sql_arr['type'];
|
||||
$object['_type'] = $sql_arr['type'] ?: $this->folder->type;
|
||||
$object['_msguid'] = $sql_arr['msguid'];
|
||||
$object['_mailbox'] = $this->folder->name;
|
||||
$object['_size'] = strlen($sql_arr['xml']);
|
||||
$object['_formatobj'] = kolab_format::factory($sql_arr['type'], 3.0, $sql_arr['xml']);
|
||||
$object['_formatobj'] = kolab_format::factory($object['_type'], 3.0, $sql_arr['xml']);
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
@ -640,37 +679,35 @@ class kolab_storage_cache
|
|||
* @param int Message UID. Set 0 to commit buffered inserts
|
||||
* @param array Kolab object to cache
|
||||
*/
|
||||
private function _extended_insert($msguid, $object)
|
||||
protected function _extended_insert($msguid, $object)
|
||||
{
|
||||
static $buffer = '';
|
||||
|
||||
$line = '';
|
||||
if ($object) {
|
||||
$sql_data = $this->_serialize($object);
|
||||
$objtype = $object['_type'] ? $object['_type'] : $this->folder->type;
|
||||
|
||||
$values = array(
|
||||
$this->db->quote($this->resource_uri),
|
||||
$this->db->quote($objtype),
|
||||
$this->db->quote($this->folder_id),
|
||||
$this->db->quote($msguid),
|
||||
$this->db->quote($object['uid']),
|
||||
$this->db->now(),
|
||||
$this->db->quote($sql_data['changed']),
|
||||
$this->db->quote($sql_data['data']),
|
||||
$this->db->quote($sql_data['xml']),
|
||||
$this->db->quote($sql_data['dtstart']),
|
||||
$this->db->quote($sql_data['dtend']),
|
||||
$this->db->quote($sql_data['tags']),
|
||||
$this->db->quote($sql_data['words']),
|
||||
$this->db->quote($sql_data['filename']),
|
||||
);
|
||||
foreach ($this->extra_cols as $col) {
|
||||
$values[] = $this->db->quote($sql_data[$col]);
|
||||
}
|
||||
$line = '(' . join(',', $values) . ')';
|
||||
}
|
||||
|
||||
if ($buffer && (!$msguid || (strlen($buffer) + strlen($line) > $this->max_sql_packet()))) {
|
||||
$extra_cols = $this->extra_cols ? ', ' . join(', ', $this->extra_cols) : '';
|
||||
$result = $this->db->query(
|
||||
"INSERT INTO kolab_cache ".
|
||||
" (resource, type, msguid, uid, created, changed, data, xml, dtstart, dtend, tags, words, filename)".
|
||||
"INSERT INTO $this->cache_table ".
|
||||
" (folder_id, msguid, uid, created, changed, data, xml, tags, words $extra_cols)".
|
||||
" VALUES $buffer"
|
||||
);
|
||||
if (!$this->db->affected_rows($result)) {
|
||||
|
@ -689,7 +726,7 @@ class kolab_storage_cache
|
|||
/**
|
||||
* Returns max_allowed_packet from mysql config
|
||||
*/
|
||||
private function max_sql_packet()
|
||||
protected function max_sql_packet()
|
||||
{
|
||||
if (!$this->max_sql_packet) {
|
||||
// mysql limit or max 4 MB
|
||||
|
@ -700,17 +737,37 @@ class kolab_storage_cache
|
|||
return $this->max_sql_packet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read this folder's ID and cache metadata
|
||||
*/
|
||||
protected function _read_folder_data()
|
||||
{
|
||||
// already done
|
||||
if (!empty($this->folder_id))
|
||||
return;
|
||||
|
||||
$sql_arr = $this->db->fetch_assoc($this->db->query("SELECT folder_id, synclock, ctag FROM $this->folders_table WHERE resource=?", $this->resource_uri));
|
||||
if ($sql_arr) {
|
||||
$this->metadata = $sql_arr;
|
||||
$this->folder_id = $sql_arr['folder_id'];
|
||||
}
|
||||
else {
|
||||
$this->db->query("INSERT INTO $this->folders_table (resource, type) VALUES (?, ?)", $this->resource_uri, $this->folder->type);
|
||||
$this->folder_id = $this->db->insert_id('kolab_folders');
|
||||
$this->metadata = array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check lock record for this folder and wait if locked or set lock
|
||||
*/
|
||||
private function _sync_lock()
|
||||
protected function _sync_lock()
|
||||
{
|
||||
if (!$this->ready)
|
||||
return;
|
||||
|
||||
$sql_query = "SELECT msguid AS locked, ".$this->db->unixtimestamp('created')." AS created FROM kolab_cache ".
|
||||
"WHERE resource=? AND type=?";
|
||||
$sql_arr = $this->db->fetch_assoc($this->db->query($sql_query, $this->resource_uri, 'lock'));
|
||||
$this->_read_folder_data();
|
||||
$sql_query = "SELECT synclock, ctag FROM $this->folders_table WHERE folder_id=?";
|
||||
|
||||
// abort if database is not set-up
|
||||
if ($this->db->is_error()) {
|
||||
|
@ -721,28 +778,13 @@ class kolab_storage_cache
|
|||
$this->synclock = true;
|
||||
|
||||
// wait if locked (expire locks after 10 minutes)
|
||||
while ($sql_arr && intval($sql_arr['locked']) > 0 && $sql_arr['created'] + $this->max_sync_lock_time > time()) {
|
||||
while ($this->metadata && intval($this->metadata['synclock']) > 0 && $this->metadata['synclock'] + $this->max_sync_lock_time > time()) {
|
||||
usleep(500000);
|
||||
$sql_arr = $this->db->fetch_assoc($this->db->query($sql_query, $this->resource_uri, 'lock'));
|
||||
$this->metadata = $this->db->fetch_assoc($this->db->query($sql_query, $this->folder_id));
|
||||
}
|
||||
|
||||
// create lock record if not exists
|
||||
if (!$sql_arr) {
|
||||
$this->db->query(
|
||||
"INSERT INTO kolab_cache (resource, type, msguid, created, uid, data, xml, tags, words)".
|
||||
" VALUES (?, ?, 1, " . $this->db->now() . ", '', '', '', '', '')",
|
||||
$this->resource_uri,
|
||||
'lock'
|
||||
);
|
||||
}
|
||||
else {
|
||||
$this->db->query(
|
||||
"UPDATE kolab_cache SET msguid = 1, created = " . $this->db->now() .
|
||||
" WHERE resource = ? AND type = ?",
|
||||
$this->resource_uri,
|
||||
'lock'
|
||||
);
|
||||
}
|
||||
// set lock
|
||||
$this->db->query("UPDATE $this->folders_table SET synclock = ? WHERE folder_id = ?", time(), $this->folder_id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -754,9 +796,9 @@ class kolab_storage_cache
|
|||
return;
|
||||
|
||||
$this->db->query(
|
||||
"UPDATE kolab_cache SET msguid = 0 WHERE resource = ? AND type = ?",
|
||||
$this->resource_uri,
|
||||
'lock'
|
||||
"UPDATE $this->folders_table SET synclock = 0, ctag = ? WHERE folder_id = ?",
|
||||
$this->metadata['ctag'],
|
||||
$this->folder_id
|
||||
);
|
||||
|
||||
$this->synclock = false;
|
||||
|
@ -782,4 +824,72 @@ class kolab_storage_cache
|
|||
return $this->uid2msg[$uid];
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for protected member variables
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
if ($name == 'folder_id') {
|
||||
$this->_read_folder_data();
|
||||
}
|
||||
|
||||
return $this->$name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bypass Roundcube messages cache.
|
||||
* Roundcube cache duplicates information already stored in kolab_cache.
|
||||
*
|
||||
* @param bool $disable True disables, False enables messages cache
|
||||
*/
|
||||
public function bypass($disable = false)
|
||||
{
|
||||
// if kolab cache is disabled do nothing
|
||||
if (!$this->enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
static $messages_cache, $cache_bypass;
|
||||
|
||||
if ($messages_cache === null) {
|
||||
$rcmail = rcube::get_instance();
|
||||
$messages_cache = (bool) $rcmail->config->get('messages_cache');
|
||||
$cache_bypass = (int) $rcmail->config->get('kolab_messages_cache_bypass');
|
||||
}
|
||||
|
||||
if ($messages_cache) {
|
||||
// handle recurrent (multilevel) bypass() calls
|
||||
if ($disable) {
|
||||
$this->cache_bypassed += 1;
|
||||
if ($this->cache_bypassed > 1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->cache_bypassed -= 1;
|
||||
if ($this->cache_bypassed > 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch ($cache_bypass) {
|
||||
case 2:
|
||||
// Disable messages cache completely
|
||||
$this->imap->set_messages_caching(!$disable);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// We'll disable messages cache, but keep index cache.
|
||||
// Default mode is both (MODE_INDEX | MODE_MESSAGE)
|
||||
$mode = rcube_imap_cache::MODE_INDEX;
|
||||
|
||||
if (!$disable) {
|
||||
$mode |= rcube_imap_cache::MODE_MESSAGE;
|
||||
}
|
||||
|
||||
$this->imap->set_messages_caching(true, $mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
40
plugins/libkolab/lib/kolab_storage_cache_configuration.php
Normal file
40
plugins/libkolab/lib/kolab_storage_cache_configuration.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Kolab storage cache class for configuration objects
|
||||
*
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2013, Kolab Systems AG <contact@kolabsys.com>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
class kolab_storage_cache_configuration extends kolab_storage_cache
|
||||
{
|
||||
protected $extra_cols = array('type');
|
||||
|
||||
/**
|
||||
* Helper method to convert the given Kolab object into a dataset to be written to cache
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
protected function _serialize($object)
|
||||
{
|
||||
$sql_data = parent::_serialize($object);
|
||||
$sql_data['type'] = $object['type'];
|
||||
|
||||
return $sql_data;
|
||||
}
|
||||
}
|
45
plugins/libkolab/lib/kolab_storage_cache_contact.php
Normal file
45
plugins/libkolab/lib/kolab_storage_cache_contact.php
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Kolab storage cache class for contact objects
|
||||
*
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2013, Kolab Systems AG <contact@kolabsys.com>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
class kolab_storage_cache_contact extends kolab_storage_cache
|
||||
{
|
||||
protected $extra_cols = array('type');
|
||||
protected $binary_items = array(
|
||||
'photo' => '|<photo><uri>[^;]+;base64,([^<]+)</uri></photo>|i',
|
||||
'pgppublickey' => '|<key><uri>date:application/pgp-keys;base64,([^<]+)</uri></key>|i',
|
||||
'pkcs7publickey' => '|<key><uri>date:application/pkcs7-mime;base64,([^<]+)</uri></key>|i',
|
||||
);
|
||||
|
||||
/**
|
||||
* Helper method to convert the given Kolab object into a dataset to be written to cache
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
protected function _serialize($object)
|
||||
{
|
||||
$sql_data = parent::_serialize($object);
|
||||
$sql_data['type'] = $object['_type'];
|
||||
|
||||
return $sql_data;
|
||||
}
|
||||
}
|
49
plugins/libkolab/lib/kolab_storage_cache_event.php
Normal file
49
plugins/libkolab/lib/kolab_storage_cache_event.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Kolab storage cache class for calendar event objects
|
||||
*
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2013, Kolab Systems AG <contact@kolabsys.com>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
class kolab_storage_cache_event extends kolab_storage_cache
|
||||
{
|
||||
protected $extra_cols = array('dtstart','dtend');
|
||||
|
||||
/**
|
||||
* Helper method to convert the given Kolab object into a dataset to be written to cache
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
protected function _serialize($object)
|
||||
{
|
||||
$sql_data = parent::_serialize($object);
|
||||
|
||||
// database runs in server's timezone so using date() is what we want
|
||||
$sql_data['dtstart'] = date('Y-m-d H:i:s', is_object($object['start']) ? $object['start']->format('U') : $object['start']);
|
||||
$sql_data['dtend'] = date('Y-m-d H:i:s', is_object($object['end']) ? $object['end']->format('U') : $object['end']);
|
||||
|
||||
// extend date range for recurring events
|
||||
if ($object['recurrence'] && $object['_formatobj']) {
|
||||
$recurrence = new kolab_date_recurrence($object['_formatobj']);
|
||||
$sql_data['dtend'] = date('Y-m-d 23:59:59', $recurrence->end() ?: strtotime('now +1 year'));
|
||||
}
|
||||
|
||||
return $sql_data;
|
||||
}
|
||||
}
|
44
plugins/libkolab/lib/kolab_storage_cache_file.php
Normal file
44
plugins/libkolab/lib/kolab_storage_cache_file.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Kolab storage cache class for file objects
|
||||
*
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2013, Kolab Systems AG <contact@kolabsys.com>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
class kolab_storage_cache_file extends kolab_storage_cache
|
||||
{
|
||||
protected $extra_cols = array('filename');
|
||||
|
||||
/**
|
||||
* Helper method to convert the given Kolab object into a dataset to be written to cache
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
protected function _serialize($object)
|
||||
{
|
||||
$sql_data = parent::_serialize($object);
|
||||
|
||||
if (!empty($object['_attachments'])) {
|
||||
reset($object['_attachments']);
|
||||
$sql_data['filename'] = $object['_attachments'][key($object['_attachments'])]['name'];
|
||||
}
|
||||
|
||||
return $sql_data;
|
||||
}
|
||||
}
|
27
plugins/libkolab/lib/kolab_storage_cache_freebusy.php
Normal file
27
plugins/libkolab/lib/kolab_storage_cache_freebusy.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Kolab storage cache class for freebusy objects
|
||||
*
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2013, Kolab Systems AG <contact@kolabsys.com>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
class kolab_storage_cache_freebusy extends kolab_storage_cache
|
||||
{
|
||||
protected $extra_cols = array('dtstart','dtend');
|
||||
}
|
28
plugins/libkolab/lib/kolab_storage_cache_journal.php
Normal file
28
plugins/libkolab/lib/kolab_storage_cache_journal.php
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Kolab storage cache class for journal objects
|
||||
*
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2013, Kolab Systems AG <contact@kolabsys.com>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
class kolab_storage_cache_journal extends kolab_storage_cache
|
||||
{
|
||||
protected $extra_cols = array('dtstart','dtend');
|
||||
|
||||
}
|
561
plugins/libkolab/lib/kolab_storage_cache_mongodb.php
Normal file
561
plugins/libkolab/lib/kolab_storage_cache_mongodb.php
Normal file
|
@ -0,0 +1,561 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Kolab storage cache class providing a local caching layer for Kolab groupware objects.
|
||||
*
|
||||
* @version @package_version@
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
class kolab_storage_cache_mongodb
|
||||
{
|
||||
private $db;
|
||||
private $imap;
|
||||
private $folder;
|
||||
private $uid2msg;
|
||||
private $objects;
|
||||
private $index = array();
|
||||
private $resource_uri;
|
||||
private $enabled = true;
|
||||
private $synched = false;
|
||||
private $synclock = false;
|
||||
private $ready = false;
|
||||
private $max_sql_packet = 1046576; // 1 MB - 2000 bytes
|
||||
private $binary_cols = array('photo','pgppublickey','pkcs7publickey');
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public function __construct(kolab_storage_folder $storage_folder = null)
|
||||
{
|
||||
$rcmail = rcube::get_instance();
|
||||
$mongo = new Mongo();
|
||||
$this->db = $mongo->kolab_cache;
|
||||
$this->imap = $rcmail->get_storage();
|
||||
$this->enabled = $rcmail->config->get('kolab_cache', false);
|
||||
|
||||
if ($this->enabled) {
|
||||
// remove sync-lock on script termination
|
||||
$rcmail->add_shutdown_function(array($this, '_sync_unlock'));
|
||||
}
|
||||
|
||||
if ($storage_folder)
|
||||
$this->set_folder($storage_folder);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Connect cache with a storage folder
|
||||
*
|
||||
* @param kolab_storage_folder The storage folder instance to connect with
|
||||
*/
|
||||
public function set_folder(kolab_storage_folder $storage_folder)
|
||||
{
|
||||
$this->folder = $storage_folder;
|
||||
|
||||
if (empty($this->folder->name)) {
|
||||
$this->ready = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// compose fully qualified ressource uri for this instance
|
||||
$this->resource_uri = $this->folder->get_resource_uri();
|
||||
$this->ready = $this->enabled;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Synchronize local cache data with remote
|
||||
*/
|
||||
public function synchronize()
|
||||
{
|
||||
// only sync once per request cycle
|
||||
if ($this->synched)
|
||||
return;
|
||||
|
||||
// increase time limit
|
||||
@set_time_limit(500);
|
||||
|
||||
// lock synchronization for this folder or wait if locked
|
||||
$this->_sync_lock();
|
||||
|
||||
// synchronize IMAP mailbox cache
|
||||
$this->imap->folder_sync($this->folder->name);
|
||||
|
||||
// compare IMAP index with object cache index
|
||||
$imap_index = $this->imap->index($this->folder->name);
|
||||
$this->index = $imap_index->get();
|
||||
|
||||
// determine objects to fetch or to invalidate
|
||||
if ($this->ready) {
|
||||
// read cache index
|
||||
$old_index = array();
|
||||
$cursor = $this->db->cache->find(array('resource' => $this->resource_uri), array('msguid' => 1, 'uid' => 1));
|
||||
foreach ($cursor as $doc) {
|
||||
$old_index[] = $doc['msguid'];
|
||||
$this->uid2msg[$doc['uid']] = $doc['msguid'];
|
||||
}
|
||||
|
||||
// fetch new objects from imap
|
||||
foreach (array_diff($this->index, $old_index) as $msguid) {
|
||||
if ($object = $this->folder->read_object($msguid, '*')) {
|
||||
try {
|
||||
$this->db->cache->insert($this->_serialize($object, $msguid));
|
||||
}
|
||||
catch (Exception $e) {
|
||||
rcmail::raise_error(array(
|
||||
'code' => 900, 'type' => 'php',
|
||||
'message' => "Failed to write to mongodb cache: " . $e->getMessage(),
|
||||
), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// delete invalid entries from local DB
|
||||
$del_index = array_diff($old_index, $this->index);
|
||||
if (!empty($del_index)) {
|
||||
$this->db->cache->remove(array('resource' => $this->resource_uri, 'msguid' => array('$in' => $del_index)));
|
||||
}
|
||||
}
|
||||
|
||||
// remove lock
|
||||
$this->_sync_unlock();
|
||||
|
||||
$this->synched = time();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read a single entry from cache or from IMAP directly
|
||||
*
|
||||
* @param string Related IMAP message UID
|
||||
* @param string Object type to read
|
||||
* @param string IMAP folder name the entry relates to
|
||||
* @param array Hash array with object properties or null if not found
|
||||
*/
|
||||
public function get($msguid, $type = null, $foldername = null)
|
||||
{
|
||||
// delegate to another cache instance
|
||||
if ($foldername && $foldername != $this->folder->name) {
|
||||
return kolab_storage::get_folder($foldername)->cache->get($msguid, $object);
|
||||
}
|
||||
|
||||
// load object if not in memory
|
||||
if (!isset($this->objects[$msguid])) {
|
||||
if ($this->ready && ($doc = $this->db->cache->findOne(array('resource' => $this->resource_uri, 'msguid' => $msguid))))
|
||||
$this->objects[$msguid] = $this->_unserialize($doc);
|
||||
|
||||
// fetch from IMAP if not present in cache
|
||||
if (empty($this->objects[$msguid])) {
|
||||
$result = $this->_fetch(array($msguid), $type, $foldername);
|
||||
$this->objects[$msguid] = $result[0];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->objects[$msguid];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Insert/Update a cache entry
|
||||
*
|
||||
* @param string Related IMAP message UID
|
||||
* @param mixed Hash array with object properties to save or false to delete the cache entry
|
||||
* @param string IMAP folder name the entry relates to
|
||||
*/
|
||||
public function set($msguid, $object, $foldername = null)
|
||||
{
|
||||
// delegate to another cache instance
|
||||
if ($foldername && $foldername != $this->folder->name) {
|
||||
kolab_storage::get_folder($foldername)->cache->set($msguid, $object);
|
||||
return;
|
||||
}
|
||||
|
||||
// write to cache
|
||||
if ($this->ready) {
|
||||
// remove old entry
|
||||
$this->db->cache->remove(array('resource' => $this->resource_uri, 'msguid' => $msguid));
|
||||
|
||||
// write new object data if not false (wich means deleted)
|
||||
if ($object) {
|
||||
try {
|
||||
$this->db->cache->insert($this->_serialize($object, $msguid));
|
||||
}
|
||||
catch (Exception $e) {
|
||||
rcmail::raise_error(array(
|
||||
'code' => 900, 'type' => 'php',
|
||||
'message' => "Failed to write to mongodb cache: " . $e->getMessage(),
|
||||
), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// keep a copy in memory for fast access
|
||||
$this->objects[$msguid] = $object;
|
||||
|
||||
if ($object)
|
||||
$this->uid2msg[$object['uid']] = $msguid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move an existing cache entry to a new resource
|
||||
*
|
||||
* @param string Entry's IMAP message UID
|
||||
* @param string Entry's Object UID
|
||||
* @param string Target IMAP folder to move it to
|
||||
*/
|
||||
public function move($msguid, $objuid, $target_folder)
|
||||
{
|
||||
$target = kolab_storage::get_folder($target_folder);
|
||||
|
||||
// resolve new message UID in target folder
|
||||
if ($new_msguid = $target->cache->uid2msguid($objuid)) {
|
||||
/*
|
||||
$this->db->query(
|
||||
"UPDATE kolab_cache SET resource=?, msguid=? ".
|
||||
"WHERE resource=? AND msguid=? AND type<>?",
|
||||
$target->get_resource_uri(),
|
||||
$new_msguid,
|
||||
$this->resource_uri,
|
||||
$msguid,
|
||||
'lock'
|
||||
);
|
||||
*/
|
||||
}
|
||||
else {
|
||||
// just clear cache entry
|
||||
$this->set($msguid, false);
|
||||
}
|
||||
|
||||
unset($this->uid2msg[$uid]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove all objects from local cache
|
||||
*/
|
||||
public function purge($type = null)
|
||||
{
|
||||
return $this->db->cache->remove(array(), array('safe' => true));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Select Kolab objects filtered by the given query
|
||||
*
|
||||
* @param array Pseudo-SQL query as list of filter parameter triplets
|
||||
* triplet: array('<colname>', '<comparator>', '<value>')
|
||||
* @return array List of Kolab data objects (each represented as hash array)
|
||||
*/
|
||||
public function select($query = array())
|
||||
{
|
||||
$result = array();
|
||||
|
||||
// read from local cache DB (assume it to be synchronized)
|
||||
if ($this->ready) {
|
||||
$cursor = $this->db->cache->find(array('resource' => $this->resource_uri) + $this->_mongo_filter($query));
|
||||
foreach ($cursor as $doc) {
|
||||
if ($object = $this->_unserialize($doc))
|
||||
$result[] = $object;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// extract object type from query parameter
|
||||
$filter = $this->_query2assoc($query);
|
||||
|
||||
// use 'list' for folder's default objects
|
||||
if ($filter['type'] == $this->type) {
|
||||
$index = $this->index;
|
||||
}
|
||||
else { // search by object type
|
||||
$search = 'UNDELETED HEADER X-Kolab-Type ' . kolab_storage_folder::KTYPE_PREFIX . $filter['type'];
|
||||
$index = $this->imap->search_once($this->folder->name, $search)->get();
|
||||
}
|
||||
|
||||
// fetch all messages in $index from IMAP
|
||||
$result = $this->_fetch($index, $filter['type']);
|
||||
|
||||
// TODO: post-filter result according to query
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get number of objects mathing the given query
|
||||
*
|
||||
* @param array $query Pseudo-SQL query as list of filter parameter triplets
|
||||
* @return integer The number of objects of the given type
|
||||
*/
|
||||
public function count($query = array())
|
||||
{
|
||||
$count = 0;
|
||||
|
||||
// cache is in sync, we can count records in local DB
|
||||
if ($this->synched) {
|
||||
$cursor = $this->db->cache->find(array('resource' => $this->resource_uri) + $this->_mongo_filter($query));
|
||||
$count = $cursor->valid() ? $cursor->count() : 0;
|
||||
}
|
||||
else {
|
||||
// search IMAP by object type
|
||||
$filter = $this->_query2assoc($query);
|
||||
$ctype = kolab_storage_folder::KTYPE_PREFIX . $filter['type'];
|
||||
$index = $this->imap->search_once($this->folder->name, 'UNDELETED HEADER X-Kolab-Type ' . $ctype);
|
||||
$count = $index->count();
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to convert the pseudo-SQL query into a valid mongodb filter
|
||||
*/
|
||||
private function _mongo_filter($query)
|
||||
{
|
||||
$filters = array();
|
||||
foreach ($query as $param) {
|
||||
$filter = array();
|
||||
if ($param[1] == '=' && is_array($param[2])) {
|
||||
$filter[$param[0]] = array('$in' => $param[2]);
|
||||
$filters[] = $filter;
|
||||
}
|
||||
else if ($param[1] == '=') {
|
||||
$filters[] = array($param[0] => $param[2]);
|
||||
}
|
||||
else if ($param[1] == 'LIKE' || $param[1] == '~') {
|
||||
$filter[$param[0]] = array('$regex' => preg_quote($param[2]), '$options' => 'i');
|
||||
$filters[] = $filter;
|
||||
}
|
||||
else if ($param[1] == '!~' || $param[1] == '!LIKE') {
|
||||
$filter[$param[0]] = array('$not' => '/' . preg_quote($param[2]) . '/i');
|
||||
$filters[] = $filter;
|
||||
}
|
||||
else {
|
||||
$op = '';
|
||||
switch ($param[1]) {
|
||||
case '>': $op = '$gt'; break;
|
||||
case '>=': $op = '$gte'; break;
|
||||
case '<': $op = '$lt'; break;
|
||||
case '<=': $op = '$lte'; break;
|
||||
case '!=':
|
||||
case '<>': $op = '$gte'; break;
|
||||
}
|
||||
if ($op) {
|
||||
$filter[$param[0]] = array($op => $param[2]);
|
||||
$filters[] = $filter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array('$and' => $filters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to convert the given pseudo-query triplets into
|
||||
* an associative filter array with 'equals' values only
|
||||
*/
|
||||
private function _query2assoc($query)
|
||||
{
|
||||
// extract object type from query parameter
|
||||
$filter = array();
|
||||
foreach ($query as $param) {
|
||||
if ($param[1] == '=')
|
||||
$filter[$param[0]] = $param[2];
|
||||
}
|
||||
return $filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch messages from IMAP
|
||||
*
|
||||
* @param array List of message UIDs to fetch
|
||||
* @return array List of parsed Kolab objects
|
||||
*/
|
||||
private function _fetch($index, $type = null, $folder = null)
|
||||
{
|
||||
$results = array();
|
||||
foreach ((array)$index as $msguid) {
|
||||
if ($object = $this->folder->read_object($msguid, $type, $folder)) {
|
||||
$results[] = $object;
|
||||
$this->set($msguid, $object);
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper method to convert the given Kolab object into a dataset to be written to cache
|
||||
*/
|
||||
private function _serialize($object, $msguid)
|
||||
{
|
||||
$bincols = array_flip($this->binary_cols);
|
||||
$doc = array(
|
||||
'resource' => $this->resource_uri,
|
||||
'type' => $object['_type'] ? $object['_type'] : $this->folder->type,
|
||||
'msguid' => $msguid,
|
||||
'uid' => $object['uid'],
|
||||
'xml' => '',
|
||||
'tags' => array(),
|
||||
'words' => array(),
|
||||
'objcols' => array(),
|
||||
);
|
||||
|
||||
// set type specific values
|
||||
if ($this->folder->type == 'event') {
|
||||
// database runs in server's timezone so using date() is what we want
|
||||
$doc['dtstart'] = date('Y-m-d H:i:s', is_object($object['start']) ? $object['start']->format('U') : $object['start']);
|
||||
$doc['dtend'] = date('Y-m-d H:i:s', is_object($object['end']) ? $object['end']->format('U') : $object['end']);
|
||||
|
||||
// extend date range for recurring events
|
||||
if ($object['recurrence']) {
|
||||
$doc['dtend'] = date('Y-m-d H:i:s', $object['recurrence']['UNTIL'] ?: strtotime('now + 2 years'));
|
||||
}
|
||||
}
|
||||
|
||||
if ($object['_formatobj']) {
|
||||
$doc['xml'] = preg_replace('!(</?[a-z0-9:-]+>)[\n\r\t\s]+!ms', '$1', (string)$object['_formatobj']->write());
|
||||
$doc['tags'] = $object['_formatobj']->get_tags();
|
||||
$doc['words'] = $object['_formatobj']->get_words();
|
||||
}
|
||||
|
||||
// extract object data
|
||||
$data = array();
|
||||
foreach ($object as $key => $val) {
|
||||
if ($val === "" || $val === null) {
|
||||
// skip empty properties
|
||||
continue;
|
||||
}
|
||||
if (isset($bincols[$key])) {
|
||||
$data[$key] = base64_encode($val);
|
||||
}
|
||||
else if (is_object($val)) {
|
||||
if (is_a($val, 'DateTime')) {
|
||||
$data[$key] = array('_class' => 'DateTime', 'date' => $val->format('Y-m-d H:i:s'), 'timezone' => $val->getTimezone()->getName());
|
||||
$doc['objcols'][] = $key;
|
||||
}
|
||||
}
|
||||
else if ($key[0] != '_') {
|
||||
$data[$key] = $val;
|
||||
}
|
||||
else if ($key == '_attachments') {
|
||||
foreach ($val as $k => $att) {
|
||||
unset($att['content'], $att['path']);
|
||||
if ($att['id'])
|
||||
$data[$key][$k] = $att;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$doc['data'] = $data;
|
||||
return $doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to turn stored cache data into a valid storage object
|
||||
*/
|
||||
private function _unserialize($doc)
|
||||
{
|
||||
$object = $doc['data'];
|
||||
|
||||
// decode binary properties
|
||||
foreach ($this->binary_cols as $key) {
|
||||
if (!empty($object[$key]))
|
||||
$object[$key] = base64_decode($object[$key]);
|
||||
}
|
||||
|
||||
// restore serialized objects
|
||||
foreach ((array)$doc['objcols'] as $key) {
|
||||
switch ($object[$key]['_class']) {
|
||||
case 'DateTime':
|
||||
$val = new DateTime($object[$key]['date'], new DateTimeZone($object[$key]['timezone']));
|
||||
$object[$key] = $val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// add meta data
|
||||
$object['_type'] = $doc['type'];
|
||||
$object['_msguid'] = $doc['msguid'];
|
||||
$object['_mailbox'] = $this->folder->name;
|
||||
$object['_formatobj'] = kolab_format::factory($doc['type'], $doc['xml']);
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check lock record for this folder and wait if locked or set lock
|
||||
*/
|
||||
private function _sync_lock()
|
||||
{
|
||||
if (!$this->ready)
|
||||
return;
|
||||
|
||||
$this->synclock = true;
|
||||
$lock = $this->db->locks->findOne(array('resource' => $this->resource_uri));
|
||||
|
||||
// create lock record if not exists
|
||||
if (!$lock) {
|
||||
$this->db->locks->insert(array('resource' => $this->resource_uri, 'created' => time()));
|
||||
}
|
||||
// wait if locked (expire locks after 10 minutes)
|
||||
else if ((time() - $lock['created']) < 600) {
|
||||
usleep(500000);
|
||||
return $this->_sync_lock();
|
||||
}
|
||||
// set lock
|
||||
else {
|
||||
$lock['created'] = time();
|
||||
$this->db->locks->update(array('_id' => $lock['_id']), $lock, array('safe' => true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove lock for this folder
|
||||
*/
|
||||
public function _sync_unlock()
|
||||
{
|
||||
if (!$this->ready || !$this->synclock)
|
||||
return;
|
||||
|
||||
$this->db->locks->remove(array('resource' => $this->resource_uri));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve an object UID into an IMAP message UID
|
||||
*
|
||||
* @param string Kolab object UID
|
||||
* @param boolean Include deleted objects
|
||||
* @return int The resolved IMAP message UID
|
||||
*/
|
||||
public function uid2msguid($uid, $deleted = false)
|
||||
{
|
||||
if (!isset($this->uid2msg[$uid])) {
|
||||
// use IMAP SEARCH to get the right message
|
||||
$index = $this->imap->search_once($this->folder->name, ($deleted ? '' : 'UNDELETED ') . 'HEADER SUBJECT ' . $uid);
|
||||
$results = $index->get();
|
||||
$this->uid2msg[$uid] = $results[0];
|
||||
}
|
||||
|
||||
return $this->uid2msg[$uid];
|
||||
}
|
||||
|
||||
}
|
27
plugins/libkolab/lib/kolab_storage_cache_note.php
Normal file
27
plugins/libkolab/lib/kolab_storage_cache_note.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Kolab storage cache class for note objects
|
||||
*
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2013, Kolab Systems AG <contact@kolabsys.com>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
class kolab_storage_cache_note extends kolab_storage_cache
|
||||
{
|
||||
|
||||
}
|
44
plugins/libkolab/lib/kolab_storage_cache_task.php
Normal file
44
plugins/libkolab/lib/kolab_storage_cache_task.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Kolab storage cache class for task objects
|
||||
*
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2013, Kolab Systems AG <contact@kolabsys.com>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
class kolab_storage_cache_task extends kolab_storage_cache
|
||||
{
|
||||
protected $extra_cols = array('dtstart','dtend');
|
||||
|
||||
/**
|
||||
* Helper method to convert the given Kolab object into a dataset to be written to cache
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
protected function _serialize($object)
|
||||
{
|
||||
$sql_data = parent::_serialize($object) + array('dtstart' => null, 'dtend' => null);
|
||||
|
||||
if ($object['start'])
|
||||
$sql_data['dtstart'] = date('Y-m-d H:i:s', is_object($object['start']) ? $object['start']->format('U') : $object['start']);
|
||||
if ($object['due'])
|
||||
$sql_data['dtend'] = date('Y-m-d H:i:s', is_object($object['due']) ? $object['due']->format('U') : $object['due']);
|
||||
|
||||
return $sql_data;
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
* @author Aleksander Machniak <machniak@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com>
|
||||
* Copyright (C) 2012-2013, Kolab Systems AG <contact@kolabsys.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
|
@ -64,7 +64,6 @@ class kolab_storage_folder
|
|||
{
|
||||
$this->imap = rcube::get_instance()->get_storage();
|
||||
$this->imap->set_options(array('skip_deleted' => true));
|
||||
$this->cache = new kolab_storage_cache($this);
|
||||
$this->set_folder($name, $type);
|
||||
}
|
||||
|
||||
|
@ -79,11 +78,16 @@ class kolab_storage_folder
|
|||
{
|
||||
$this->type_annotation = $ftype ? $ftype : kolab_storage::folder_type($name);
|
||||
|
||||
$oldtype = $this->type;
|
||||
list($this->type, $suffix) = explode('.', $this->type_annotation);
|
||||
$this->default = $suffix == 'default';
|
||||
$this->name = $name;
|
||||
$this->resource_uri = null;
|
||||
|
||||
// get a new cache instance of folder type changed
|
||||
if (!$this->cache || $type != $oldtype)
|
||||
$this->cache = kolab_storage_cache::factory($this);
|
||||
|
||||
$this->imap->set_folder($this->name);
|
||||
$this->cache->set_folder($this);
|
||||
}
|
||||
|
@ -296,6 +300,15 @@ class kolab_storage_folder
|
|||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose a folder Etag identifier
|
||||
*/
|
||||
public function get_ctag()
|
||||
{
|
||||
$fdata = $this->get_imap_data();
|
||||
return sprintf('%d-%d-%d', $fdata['UIDVALIDITY'], $fdata['HIGHESTMODSEQ'], $fdata['UIDNEXT']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check activation status of this folder
|
||||
*
|
||||
|
@ -340,7 +353,6 @@ class kolab_storage_folder
|
|||
return $subscribed ? kolab_storage::folder_subscribe($this->name) : kolab_storage::folder_unsubscribe($this->name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get number of objects stored in this folder
|
||||
*
|
||||
|
@ -349,19 +361,12 @@ class kolab_storage_folder
|
|||
* @return integer The number of objects of the given type
|
||||
* @see self::select()
|
||||
*/
|
||||
public function count($type_or_query = null)
|
||||
public function count($query = null)
|
||||
{
|
||||
if (!$type_or_query)
|
||||
$query = array(array('type','=',$this->type));
|
||||
else if (is_string($type_or_query))
|
||||
$query = array(array('type','=',$type_or_query));
|
||||
else
|
||||
$query = $this->_prepare_query((array)$type_or_query);
|
||||
|
||||
// synchronize cache first
|
||||
$this->cache->synchronize();
|
||||
|
||||
return $this->cache->count($query);
|
||||
return $this->cache->count($this->_prepare_query($query));
|
||||
}
|
||||
|
||||
|
||||
|
@ -379,7 +384,7 @@ class kolab_storage_folder
|
|||
$this->cache->synchronize();
|
||||
|
||||
// fetch objects from cache
|
||||
return $this->cache->select(array(array('type','=',$type)));
|
||||
return $this->cache->select($this->_prepare_query($type));
|
||||
}
|
||||
|
||||
|
||||
|
@ -425,10 +430,15 @@ class kolab_storage_folder
|
|||
*/
|
||||
private function _prepare_query($query)
|
||||
{
|
||||
$type = null;
|
||||
foreach ($query as $i => $param) {
|
||||
if ($param[0] == 'type') {
|
||||
$type = $param[2];
|
||||
// string equals type query
|
||||
// FIXME: should not be called this way!
|
||||
if (is_string($query)) {
|
||||
return $this->cache->has_type_col() && !empty($query) ? array(array('type','=',$query)) : array();
|
||||
}
|
||||
|
||||
foreach ((array)$query as $i => $param) {
|
||||
if ($param[0] == 'type' && !$this->cache->has_type_col()) {
|
||||
unset($query[$i]);
|
||||
}
|
||||
else if (($param[0] == 'dtstart' || $param[0] == 'dtend' || $param[0] == 'changed')) {
|
||||
if (is_object($param[2]) && is_a($param[2], 'DateTime'))
|
||||
|
@ -438,10 +448,6 @@ class kolab_storage_folder
|
|||
}
|
||||
}
|
||||
|
||||
// add type selector if not in $query
|
||||
if (!$type)
|
||||
$query[] = array('type','=',$this->type);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
@ -502,6 +508,7 @@ class kolab_storage_folder
|
|||
* @param string The IMAP message UID to fetch
|
||||
* @param string The object type expected (use wildcard '*' to accept all types)
|
||||
* @param string The folder name where the message is stored
|
||||
*
|
||||
* @return mixed Hash array representing the Kolab object, a kolab_format instance or false if not found
|
||||
*/
|
||||
public function read_object($msguid, $type = null, $folder = null)
|
||||
|
@ -511,31 +518,31 @@ class kolab_storage_folder
|
|||
|
||||
$this->imap->set_folder($folder);
|
||||
|
||||
$headers = $this->imap->get_message_headers($msguid);
|
||||
$message = null;
|
||||
$this->cache->bypass(true);
|
||||
$message = new rcube_message($msguid);
|
||||
$this->cache->bypass(false);
|
||||
|
||||
// Message doesn't exist?
|
||||
if (empty($headers)) {
|
||||
if (empty($message->headers)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// extract the X-Kolab-Type header from the XML attachment part if missing
|
||||
if (empty($headers->others['x-kolab-type'])) {
|
||||
$message = new rcube_message($msguid);
|
||||
if (empty($message->headers->others['x-kolab-type'])) {
|
||||
foreach ((array)$message->attachments as $part) {
|
||||
if (strpos($part->mimetype, kolab_format::KTYPE_PREFIX) === 0) {
|
||||
$headers->others['x-kolab-type'] = $part->mimetype;
|
||||
$message->headers->others['x-kolab-type'] = $part->mimetype;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// fix buggy messages stating the X-Kolab-Type header twice
|
||||
else if (is_array($headers->others['x-kolab-type'])) {
|
||||
$headers->others['x-kolab-type'] = reset($headers->others['x-kolab-type']);
|
||||
else if (is_array($message->headers->others['x-kolab-type'])) {
|
||||
$message->headers->others['x-kolab-type'] = reset($message->headers->others['x-kolab-type']);
|
||||
}
|
||||
|
||||
// no object type header found: abort
|
||||
if (empty($headers->others['x-kolab-type'])) {
|
||||
if (empty($message->headers->others['x-kolab-type'])) {
|
||||
rcube::raise_error(array(
|
||||
'code' => 600,
|
||||
'type' => 'php',
|
||||
|
@ -546,14 +553,13 @@ class kolab_storage_folder
|
|||
return false;
|
||||
}
|
||||
|
||||
$object_type = kolab_format::mime2object_type($headers->others['x-kolab-type']);
|
||||
$content_type = kolab_format::KTYPE_PREFIX . $object_type;
|
||||
$object_type = kolab_format::mime2object_type($message->headers->others['x-kolab-type']);
|
||||
$content_type = kolab_format::KTYPE_PREFIX . $object_type;
|
||||
|
||||
// check object type header and abort on mismatch
|
||||
if ($type != '*' && $object_type != $type)
|
||||
return false;
|
||||
|
||||
if (!$message) $message = new rcube_message($msguid);
|
||||
$attachments = array();
|
||||
|
||||
// get XML part
|
||||
|
@ -595,7 +601,7 @@ class kolab_storage_folder
|
|||
}
|
||||
|
||||
// check kolab format version
|
||||
$format_version = $headers->others['x-kolab-mime-version'];
|
||||
$format_version = $message->headers->others['x-kolab-mime-version'];
|
||||
if (empty($format_version)) {
|
||||
list($xmltype, $subtype) = explode('.', $object_type);
|
||||
$xmlhead = substr($xml, 0, 512);
|
||||
|
@ -749,7 +755,9 @@ class kolab_storage_folder
|
|||
|
||||
// delete old message
|
||||
if ($result && !empty($object['_msguid']) && !empty($object['_mailbox'])) {
|
||||
$this->cache->bypass(true);
|
||||
$this->imap->delete_message($object['_msguid'], $object['_mailbox']);
|
||||
$this->cache->bypass(false);
|
||||
$this->cache->set($object['_msguid'], false, $object['_mailbox']);
|
||||
}
|
||||
|
||||
|
@ -844,6 +852,8 @@ class kolab_storage_folder
|
|||
$msguid = is_array($object) ? $object['_msguid'] : $this->cache->uid2msguid($object);
|
||||
$success = false;
|
||||
|
||||
$this->cache->bypass(true);
|
||||
|
||||
if ($msguid && $expunge) {
|
||||
$success = $this->imap->delete_message($msguid, $this->name);
|
||||
}
|
||||
|
@ -851,6 +861,8 @@ class kolab_storage_folder
|
|||
$success = $this->imap->set_flag($msguid, 'DELETED', $this->name);
|
||||
}
|
||||
|
||||
$this->cache->bypass(false);
|
||||
|
||||
if ($success) {
|
||||
$this->cache->set($msguid, false);
|
||||
}
|
||||
|
@ -865,7 +877,11 @@ class kolab_storage_folder
|
|||
public function delete_all()
|
||||
{
|
||||
$this->cache->purge();
|
||||
return $this->imap->clear_folder($this->name);
|
||||
$this->cache->bypass(true);
|
||||
$result = $this->imap->clear_folder($this->name);
|
||||
$this->cache->bypass(false);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
|
@ -878,7 +894,11 @@ class kolab_storage_folder
|
|||
public function undelete($uid)
|
||||
{
|
||||
if ($msguid = $this->cache->uid2msguid($uid, true)) {
|
||||
if ($this->imap->set_flag($msguid, 'UNDELETED', $this->name)) {
|
||||
$this->cache->bypass(true);
|
||||
$result = $this->imap->set_flag($msguid, 'UNDELETED', $this->name);
|
||||
$this->cache->bypass(false);
|
||||
|
||||
if ($result) {
|
||||
return $msguid;
|
||||
}
|
||||
}
|
||||
|
@ -897,7 +917,11 @@ class kolab_storage_folder
|
|||
public function move($uid, $target_folder)
|
||||
{
|
||||
if ($msguid = $this->cache->uid2msguid($uid)) {
|
||||
if ($this->imap->move_message($msguid, $target_folder, $this->name)) {
|
||||
$this->cache->bypass(true);
|
||||
$result = $this->imap->move_message($msguid, $target_folder, $this->name);
|
||||
$this->cache->bypass(false);
|
||||
|
||||
if ($result) {
|
||||
$this->cache->move($msguid, $uid, $target_folder);
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue