From eca8ebafc32cf3261f5d91d7ac5ba8f7bfe836c1 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Wed, 23 May 2012 14:41:32 +0200 Subject: [PATCH] Read (old) configuration.dictionary objects with new kolab_storage layer; writing not implemented yet, until libkolabxml supports it --- plugins/kolab_config/kolab_config.php | 81 ++++-- plugins/kolab_config/lib/configuration.php | 76 ------ .../kolab_config/lib/kolab_configuration.php | 241 ------------------ plugins/kolab_config/package.xml | 10 +- .../lib/kolab_format_configuration.php | 121 +++++++++ plugins/libkolab/libkolab.php | 1 + 6 files changed, 192 insertions(+), 338 deletions(-) delete mode 100644 plugins/kolab_config/lib/configuration.php delete mode 100644 plugins/kolab_config/lib/kolab_configuration.php create mode 100644 plugins/libkolab/lib/kolab_format_configuration.php diff --git a/plugins/kolab_config/kolab_config.php b/plugins/kolab_config/kolab_config.php index b785306b..518c0011 100644 --- a/plugins/kolab_config/kolab_config.php +++ b/plugins/kolab_config/kolab_config.php @@ -8,8 +8,9 @@ * * @version @package_version@ * @author Machniak Aleksander + * @author Thomas Bruederli * - * Copyright (C) 2011, Kolab Systems AG + * Copyright (C) 2011-2012, Kolab Systems AG * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -29,8 +30,10 @@ class kolab_config extends rcube_plugin { public $task = 'utils'; - private $config; private $enabled; + private $default; + private $folders; + private $dicts = array(); /** * Required startup method of a Roundcube plugin @@ -58,17 +61,26 @@ class kolab_config extends rcube_plugin */ private function load() { - if ($this->config) + // nothing to be done here + if (isset($this->folders)) return; - return; // CURRENTLY DISABLED until libkolabxml has support for config objects - $this->require_plugin('libkolab'); - $this->config = new kolab_configuration(); + $this->folders = kolab_storage::get_folders('configuration'); + foreach ($this->folders as $i => $folder) { + if ($folder->default) { + $this->default = $folder; + break; + } + } + + // if no folder is set as default, choose the first one + if (!$this->default) + $this->default = $this->folders[0]; // check if configuration folder exist - if (strlen($this->config->dir)) { + if ($this->default && $this->default->name) { $this->enabled = true; } } @@ -89,7 +101,7 @@ class kolab_config extends rcube_plugin } $lang = $args['language']; - $dict = $this->dict; + $dict = $this->read_dictionary($lang, true); $dict['type'] = 'dictionary'; $dict['language'] = $args['language']; @@ -97,16 +109,16 @@ class kolab_config extends rcube_plugin if (empty($dict['e'])) { // Delete the object - $this->config->del($dict); + $this->default->delete($dict); } else { // Update the object - $this->config->set($dict); + // $this->default->save($dict); } $args['abort'] = true; - return $args; + return $args; } /** @@ -125,14 +137,53 @@ class kolab_config extends rcube_plugin } $lang = $args['language']; - $this->dict = $this->config->get('dictionary.'.$lang); + $dict = $this->read_dictionary($lang); - if (!empty($this->dict)) { - $args['dictionary'] = $this->dict['e']; + if (!empty($dict)) { + $args['dictionary'] = (array)$dict['e']; } $args['abort'] = true; - return $args; + return $args; + } + + /** + * Load dictionary config objects from Kolab storage + * + * @param string The language (2 chars) to load + * @param boolean Only load objects from default folder + * @return array Dictionary object as hash array + */ + private function read_dictionary($lang, $default = false) + { + if (isset($this->dicts[$lang])) + return $this->dicts[$lang]; + + $query = array(array('type','=','configuration.dictionary'), array('tags','=',' '.$lang.' ')); + + foreach ($this->folders as $folder) { + // we only want to read from default folder + if ($default && !$folder->default) + continue; + + foreach ((array)$folder->select($query) as $object) { + if ($object['type'] == 'dictionary' && $object['language'] == $lang) { + + if (is_array($this->dicts[$lang])) + $this->dicts[$lang]['e'] = array_merge((array)$this->dicts[$lang]['e'], $object['e']); + else + $this->dicts[$lang] = $object; + + // make sure the default object is cached + if ($folder->default) { + $object['e'] = $this->dicts[$lang]['e']; + $this->dicts[$lang] = $object; + } + } + } + } + + return $this->dicts[$lang]; } } diff --git a/plugins/kolab_config/lib/configuration.php b/plugins/kolab_config/lib/configuration.php deleted file mode 100644 index c80fbd3d..00000000 --- a/plugins/kolab_config/lib/configuration.php +++ /dev/null @@ -1,76 +0,0 @@ - - * - * Copyright (C) 2011, Kolab Systems AG - * - * 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 . - */ -class Horde_Kolab_Format_XML_configuration extends Horde_Kolab_Format_XML { - /** - * Specific data fields for the configuration object - * - * @var Kolab - */ - var $_fields_specific; - - var $_root_version = 2.1; - - /** - * Constructor - */ - function Horde_Kolab_Format_XML_configuration($params = array()) - { - $this->_root_name = 'configuration'; - - // Specific configuration fields, in kolab format specification order - $this->_fields_specific = array( - 'application' => array ( - 'type' => HORDE_KOLAB_XML_TYPE_STRING, - 'value' => HORDE_KOLAB_XML_VALUE_MAYBE_MISSING, - ), - 'type' => array( - 'type' => HORDE_KOLAB_XML_TYPE_STRING, - 'value' => HORDE_KOLAB_XML_VALUE_NOT_EMPTY, - ), - ); - - // Dictionary fields - if (!empty($params['subtype']) && preg_match('/^dictionary.*/', $params['subtype'])) { - $this->_fields_specific = array_merge($this->_fields_specific, array( - 'language' => array ( - 'type' => HORDE_KOLAB_XML_TYPE_STRING, - 'value' => HORDE_KOLAB_XML_VALUE_NOT_EMPTY, - ), - 'e' => array( - 'type' => HORDE_KOLAB_XML_TYPE_MULTIPLE, - 'value' => HORDE_KOLAB_XML_VALUE_NOT_EMPTY, - 'array' => array( - 'type' => HORDE_KOLAB_XML_TYPE_STRING, - 'value' => HORDE_KOLAB_XML_VALUE_NOT_EMPTY, - ), - ), - )); - } - - parent::Horde_Kolab_Format_XML($params); - - unset($this->_fields_basic['body']); - unset($this->_fields_basic['categories']); - unset($this->_fields_basic['sensitivity']); - } -} diff --git a/plugins/kolab_config/lib/kolab_configuration.php b/plugins/kolab_config/lib/kolab_configuration.php deleted file mode 100644 index c936432c..00000000 --- a/plugins/kolab_config/lib/kolab_configuration.php +++ /dev/null @@ -1,241 +0,0 @@ - - * - * Copyright (C) 2011, Kolab Systems AG - * - * 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 . - */ -class kolab_configuration -{ - public $dir; - private $rc; - private $imap_data = array(); - - /** - * Class constructor. Establishes IMAP connection. - */ - public function __construct() - { - $this->rc = rcmail::get_instance(); - - // Connect to IMAP - $this->rc->imap_connect(); - - // Get default configuration directory - // @TODO: handle many config directories, according to KEP:9 - $dirs = $this->rc->imap->list_unsubscribed('', '*', 'configuration'); - $this->dir = array_shift($dirs); - } - - /** - * Returns configuration object - * - * @param string $type Configuration object type - * - * @return array Configuration object - */ - public function get($type) - { - // If config folder exists - if (isset($this->dir[0])) { - // search by object type - // @TODO: configuration folders shouldn't be big, consider - // caching of all objects, then if we extend cache with object type - // column we could simply search in cache. - // @TODO: handle many config directories, according to KEP:9 - $ctype = 'application/x-vnd.kolab.configuration' . ($type ? '.'.$type : ''); - $search = 'HEADER X-Kolab-Type ' . $ctype; - - $this->set_imap_props(); - $this->rc->imap->search($this->dir, $search, 'US-ASCII', 'date'); - - $list = $this->rc->imap->list_headers($this->dir, 1, 'date', 'DESC'); - $this->reset_imap_props(); - - foreach ($list as $idx => $obj) { - // @TODO: maybe we could skip parsing the message structure and - // get second part's body directly - $msg = new rcube_message($obj->uid); - $xml = null; - - // get XML part - foreach ((array)$msg->attachments as $part) { - if ($part->mimetype == 'application/xml') { - if (!$part->body) - $part->body = $msg->get_part_content($part->mime_id); - $xml = $part->body; - break; - } - } - - // that shouldn't happen - if ($xml === null) { - continue; - } - - // Load XML object parser - $handler = Horde_Kolab_Format::factory('XML', 'configuration', array('subtype' => $type)); - if (is_object($handler) && is_a($handler, 'PEAR_Error')) { - return null; - } - - // XML-to-array - $object = $handler->load($xml); - - if (is_object($object) && is_a($object, 'PEAR_Error')) { - return null; - } - - $object['mailbox'] = $this->dir; - $object['msguid'] = $obj->uid; - - return $object; - } - } - } - - /** - * Saves configuration object - * - * @param array $object Configuration object - * - * @return bool True on success, False on failre - */ - public function set($object) - { - $object['uid'] = md5(uniqid(mt_rand(), true)); - $raw_msg = $this->build_message($object); - - if ($raw_msg && isset($this->dir[0])) { - $result = $this->rc->imap->save_message($this->dir, $raw_msg, '', false); - } - - // delete old message - if ($result && !empty($object['msguid'])) { - $this->rc->imap->delete_message($object['msguid'], $object['mailbox']); - } - - return $result; - } - - /** - * Deletes configuration object - * - * @param array $object Configuration object - * - * @return bool True on success, False on failre - */ - public function del($object) - { - if (!empty($object['msguid'])) { - return $this->rc->imap->delete_message($object['msguid'], $object['mailbox']); - } - - return true; - } - - /** - * Sets some rcube_imap settings before searching for config objects - */ - private function set_imap_props() - { - // Save old settings - $this->imap_data = array( - 'skip_deleted' => $skip_deleted, - 'page_size' => $page_size, - 'list_page' => $list_page, - 'threading' => $threading, - ); - - // Configure for configuration folder - $this->rc->imap->skip_deleted = true; - $this->rc->imap->threading = false; - $this->rc->imap->page_size = 100; - $this->rc->imap->list_page = 1; - } - - /** - * Resets rcube_imap settings after searching for config objects - */ - private function reset_imap_props() - { - // Reset to old settings - foreach ($this->imap_data as $key => $val) { - $this->rc->imap->$key = $val; - } - // reset saved search - $this->rc->imap->search_set = null; - } - - /** - * Creates source of the configuration object message - */ - private function build_message($object) - { - $MAIL_MIME = new Mail_mime("\r\n"); - - $name = 'configuration'; - - if (!empty($object['type'])) { - $name .= '.' . $object['type']; - if ($object['type'] == 'dictionary' && !empty($object['language'])) { - $name .= '.' . $object['language']; - } - } - - $handler = Horde_Kolab_Format::factory('XML', 'configuration', - array('subtype' => $object['type'])); - - if (is_object($handler) && is_a($handler, 'PEAR_Error')) { - return false; - } - - $xml = $handler->save($object); - - if (is_object($xml) && is_a($xml, 'PEAR_Error')) { - return false; - } - - if ($ident = $this->rc->user->get_identity()) { - $headers['From'] = $ident['email']; - $headers['To'] = $ident['email']; - } - $headers['Date'] = date('r'); - $headers['X-Kolab-Type'] = 'application/x-vnd.kolab.'.$name; - $headers['Subject'] = $object['uid']; - $headers['Message-ID'] = rcmail_gen_message_id(); - $headers['User-Agent'] = $this->rc->config->get('useragent'); - - $MAIL_MIME->headers($headers); - $MAIL_MIME->setTXTBody('This is a Kolab Groupware object. ' - .'To view this object you will need an email client that understands the Kolab Groupware format. ' - .'For a list of such email clients please visit http://www.kolab.org/kolab2-clients.html'); - - $MAIL_MIME->addAttachment($xml, - 'application/xml', - $name . '.xml', - false, '8bit', 'attachment', RCMAIL_CHARSET, '', '', - $this->rc->config->get('mime_param_folding') ? 'quoted-printable' : null, - $this->rc->config->get('mime_param_folding') == 2 ? 'quoted-printable' : null, - '', RCMAIL_CHARSET - ); - - return $MAIL_MIME->getMessage(); - } - -} diff --git a/plugins/kolab_config/package.xml b/plugins/kolab_config/package.xml index a0a29796..ac293b0e 100644 --- a/plugins/kolab_config/package.xml +++ b/plugins/kolab_config/package.xml @@ -16,10 +16,10 @@ machniak@kolabsys.com yes - 2011-11-01 + 2012-05-23 - 1.0 - 1.0 + 2.0 + 2.0 stable @@ -34,8 +34,6 @@ - - @@ -48,7 +46,7 @@ 1.7.0 - kolab_folders + libkolab http://kolabsys.com diff --git a/plugins/libkolab/lib/kolab_format_configuration.php b/plugins/libkolab/lib/kolab_format_configuration.php new file mode 100644 index 00000000..5ef0fa77 --- /dev/null +++ b/plugins/libkolab/lib/kolab_format_configuration.php @@ -0,0 +1,121 @@ + + * + * Copyright (C) 2012, Kolab Systems AG + * + * 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 . + */ + +class kolab_format_configuration extends kolab_format +{ + public $CTYPE = 'application/x-vnd.kolab.configuration'; + + protected $read_func = 'kolabformat::readConfiguration'; + protected $write_func = 'kolabformat::writeConfiguration'; + + + function __construct($xmldata = null) + { + $this->obj = new Configuration; + $this->xmldata = $xmldata; + } + + /** + * Set properties to the kolabformat object + * + * @param array Object data as hash array + */ + public function set(&$object) + { + $this->init(); + + // set some automatic values if missing +# if (!empty($object['uid'])) +# $this->obj->setUid($object['uid']); + + // TODO: set object propeties + + // cache this data + $this->data = $object; + unset($this->data['_formatobj']); + } + + /** + * + */ + public function is_valid() + { + return $this->data || (is_object($this->obj)/* && $this->obj->isValid()*/); + } + + /** + * Load data from old Kolab2 format + */ + public function fromkolab2($record) + { + $object = array( + 'uid' => $record['uid'], + 'changed' => $record['last-modification-date'], + ); + + $this->data = $object + $record; + } + + /** + * Convert the Configuration object into a hash array data structure + * + * @return array Config object data as hash array + */ + public function to_array() + { + // return cached result + if (!empty($this->data)) + return $this->data; + + $this->init(); + + // read object properties + $object = array( +# 'uid' => $this->obj->uid(), +# 'changed' => $this->obj->lastModified(), + ); + + + // TODO: read object properties + + $this->data = $object; + return $this->data; + } + + /** + * Callback for kolab_storage_cache to get object specific tags to cache + * + * @return array List of tags to save in cache + */ + public function get_tags() + { + $tags = array(); + + if ($this->data['type'] == 'dictionary') + $tags = array($this->data['language']); + + return $tags; + } + +} diff --git a/plugins/libkolab/libkolab.php b/plugins/libkolab/libkolab.php index 5d64dfb4..4ecab46a 100644 --- a/plugins/libkolab/libkolab.php +++ b/plugins/libkolab/libkolab.php @@ -56,6 +56,7 @@ class libkolab extends rcube_plugin include_once 'Horde/Kolab/Format/XML.php'; include_once 'Horde/Kolab/Format/XML/contact.php'; include_once 'Horde/Kolab/Format/XML/event.php'; + include_once 'Horde_Kolab_Format_XML_configuration.php'; String::setDefaultCharset('UTF-8'); }