diff options
author | Aleksander Machniak (Kolab Systems) <machniak@kolabsys.com> | 2011-10-13 07:38:10 (GMT) |
---|---|---|
committer | Aleksander Machniak (Kolab Systems) <machniak@kolabsys.com> | 2011-10-13 07:38:10 (GMT) |
commit | 4c2b83859ec63ae8c5a9708410beb57f3aa50847 (patch) | |
tree | ce9ba79e1f768a34e4382a05664d944e009a186e /plugins | |
parent | 7bfb9079d5a18daa3f9a26f3c15de901b10adf18 (diff) | |
download | roundcubemail-plugins-kolab-4c2b83859ec63ae8c5a9708410beb57f3aa50847.tar.gz |
Plugin that implements KEP 16: Storage of dictionary entries.
Created simple classes for basic XML configuration objects and configuration folder.
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/kolab_config/kolab_config.php | 135 | ||||
-rw-r--r-- | plugins/kolab_config/lib/configuration.php | 63 | ||||
-rw-r--r-- | plugins/kolab_config/lib/kolab_configuration.php | 232 |
3 files changed, 430 insertions, 0 deletions
diff --git a/plugins/kolab_config/kolab_config.php b/plugins/kolab_config/kolab_config.php new file mode 100644 index 0000000..03aa386 --- /dev/null +++ b/plugins/kolab_config/kolab_config.php @@ -0,0 +1,135 @@ +<?php + +/** + * Kolab configuration storage. + * + * Plugin to use Kolab server as a configuration storage. Provides an API to handle + * configuration according to http://wiki.kolab.org/KEP:9. + * + * 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 General Public License version 2 + * as published by the Free Software Foundation. + * + * @author Machniak Aleksander <machniak@kolabsys.com> + * + */ +class kolab_config extends rcube_plugin +{ + public $task = 'utils'; + + private $config; + private $enabled; + + /** + * Required startup method of a Roundcube plugin + */ + public function init() + { + $rcmail = rcmail::get_instance(); + + // Register spellchecker dictionary handlers + if (strtolower($rcmail->config->get('spellcheck_dictionary')) != 'shared') { + $this->add_hook('spell_dictionary_save', array($this, 'dictionary_save')); + $this->add_hook('spell_dictionary_get', array($this, 'dictionary_get')); + } +/* + // Register addressbook saved searches handlers + $this->add_hook('saved_search_create', array($this, 'saved_search_create')); + $this->add_hook('saved_search_delete', array($this, 'saved_search_delete')); + $this->add_hook('saved_search_list', array($this, 'saved_search_list')); + $this->add_hook('saved_search_get', array($this, 'saved_search_get')); +*/ + } + + /** + * Initializes config object and dependencies + */ + private function load() + { + if ($this->config) + return; + + $this->require_plugin('kolab_folders'); + + // load dependencies + require_once 'Horde/Util.php'; + require_once 'Horde/Kolab/Format.php'; + require_once 'Horde/Kolab/Format/XML.php'; + require_once $this->home . '/lib/configuration.php'; + require_once $this->home . '/lib/kolab_configuration.php'; + + String::setDefaultCharset('UTF-8'); + + $this->config = new kolab_configuration(); + + // check if configuration folder exist + if (strlen($this->config->dir)) { + $this->enabled = true; + } + } + + /** + * Saves spellcheck dictionary. + * + * @param array $args Hook arguments + * + * @return array Hook arguments + */ + public function dictionary_save($args) + { + $this->load(); + + if (!$this->enabled) { + return $args; + } + + $lang = $args['language']; + $dict = $this->dict; + + $dict['type'] = 'dictionary'; + $dict['language'] = $args['language']; + $dict['e'] = $args['dictionary']; + + if (empty($dict['e'])) { + // Delete the object + $this->config->del($dict); + } + else { + // Update the object + $this->config->set($dict); + } + + $args['abort'] = true; + + return $args; + } + + /** + * Returns spellcheck dictionary. + * + * @param array $args Hook arguments + * + * @return array Hook arguments + */ + public function dictionary_get($args) + { + $this->load(); + + if (!$this->enabled) { + return $args; + } + + $lang = $args['language']; + $this->dict = $this->config->get('dictionary.'.$lang); + + if (!empty($this->dict)) { + $args['dictionary'] = $this->dict['e']; + } + + $args['abort'] = true; + + return $args; + } +} diff --git a/plugins/kolab_config/lib/configuration.php b/plugins/kolab_config/lib/configuration.php new file mode 100644 index 0000000..e8c127f --- /dev/null +++ b/plugins/kolab_config/lib/configuration.php @@ -0,0 +1,63 @@ +<?php + +/** + * Kolab XML handler for configuration (KEP:9). + * + * Copyright (C) 2011 Kolab Systems AG + * + * @author Aleksander Machniak <machniak@kolabsys.com> + */ +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 new file mode 100644 index 0000000..9fc4b7b --- /dev/null +++ b/plugins/kolab_config/lib/kolab_configuration.php @@ -0,0 +1,232 @@ +<?php + +/** + * Kolab configuration storage handler. + * + * 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 General Public License version 2 + * as published by the Free Software Foundation. + * + * @author Machniak Aleksander <machniak@kolabsys.com> + */ +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(); + } + +} |