From 56abf7e5f36f8092ec2cc8db0f14af9aa033bc08 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Mon, 10 Dec 2012 12:17:41 +0100 Subject: [PATCH] Use client-side subscriptions for folders activation state (calendar, tasklist) Add kolab_use_subscriptions option to limit folders visibility in Kolab plugins (#1314) --- .../calendar/drivers/kolab/kolab_driver.php | 7 +- plugins/kolab_delegation/kolab_delegation.php | 12 +- plugins/libkolab/README | 18 +- plugins/libkolab/config.inc.php.dist | 22 +- plugins/libkolab/lib/kolab_storage.php | 197 ++++++++++++++++-- plugins/libkolab/lib/kolab_storage_folder.php | 54 ++--- plugins/libkolab/libkolab.php | 2 - .../drivers/kolab/tasklist_kolab_driver.php | 6 +- 8 files changed, 246 insertions(+), 72 deletions(-) diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php index c9cc7e79..4787445a 100644 --- a/plugins/calendar/drivers/kolab/kolab_driver.php +++ b/plugins/calendar/drivers/kolab/kolab_driver.php @@ -127,7 +127,7 @@ class kolab_driver extends calendar_driver 'showalarms' => $cal->alarms, 'class_name' => $cal->get_namespace(), 'default' => $cal->storage->default, - 'active' => $cal->storage->is_subscribed(), + 'active' => $cal->storage->is_active(), ); } @@ -154,7 +154,7 @@ class kolab_driver extends calendar_driver if ($writeable && $cal->readonly) { continue; } - if ($active && !$cal->storage->is_subscribed()) { + if ($active && !$cal->storage->is_active()) { continue; } if ($personal && $cal->get_namespace() != 'personal') { @@ -177,7 +177,6 @@ class kolab_driver extends calendar_driver public function create_calendar($prop) { $prop['type'] = 'event'; - $prop['subscribed'] = $prop['active'] ? kolab_storage::SERVERSIDE_SUBSCRIPTION : null; $folder = kolab_storage::folder_update($prop); if ($folder === false) { @@ -249,7 +248,7 @@ class kolab_driver extends calendar_driver public function subscribe_calendar($prop) { if ($prop['id'] && ($cal = $this->calendars[$prop['id']])) { - return $cal->storage->subscribe($prop['active'], kolab_storage::SERVERSIDE_SUBSCRIPTION); + return $cal->storage->activate($prop['active']); } return false; diff --git a/plugins/kolab_delegation/kolab_delegation.php b/plugins/kolab_delegation/kolab_delegation.php index 2667167a..f6709e3c 100644 --- a/plugins/kolab_delegation/kolab_delegation.php +++ b/plugins/kolab_delegation/kolab_delegation.php @@ -92,6 +92,7 @@ class kolab_delegation extends rcube_plugin $delegators = $engine->list_delegators(); $other_ns = $storage->get_namespace('other'); $folders = $storage->list_folders(); + $use_subs = $this->rc->config->get('kolab_use_subscriptions'); $identities = $this->rc->user->list_identities(); $emails = array(); $uids = array(); @@ -139,7 +140,16 @@ class kolab_delegation extends rcube_plugin $prefix = $ns[0] . $delegator['imap_uid']; // subscribe delegator's folder if ($folder === $prefix || strpos($folder, $prefix . substr($ns[0], -1)) === 0) { - $storage->subscribe($folder); + // Event/Task folders need client-side activation + $type = kolab_storage::folder_type($folder); + if (preg_match('/^(event|task)/i', $type)) { + kolab_storage::folder_activate($folder); + } + // Subscribe to mail folders and (if system is configured + // to display only subscribed folders) to other + if ($use_subs || preg_match('/^mail/i', $type)) { + $storage->subscribe($folder); + } } } } diff --git a/plugins/libkolab/README b/plugins/libkolab/README index 54bf4ae1..76312204 100644 --- a/plugins/libkolab/README +++ b/plugins/libkolab/README @@ -28,19 +28,5 @@ To do so, execute the SQL commands in SQL/.sql CONFIGURATION ------------- -The following options can be configured in Roundcube's main config file -or a local config file (config.inc.php) located in the plugin folder. - -// Enable caching of Kolab objects in local database -$rcmail_config['kolab_cache'] = true; - -// Specify format version to write Kolab objects (must be a string value!) -$rcmail_config['kolab_format_version'] = '3.0'; - -// Optional override of the URL to read and trigger Free/Busy information of Kolab users -// Defaults to https:///freebusy -$rcmail_config['kolab_freebusy_server'] = 'https:///'; - -// Set this option to disable SSL certificate checks when triggering Free/Busy (enabled by default) -$rcmail_config['kolab_ssl_verify_peer'] = false; - +Rename config.inc.php.dist to config.inc.php in the plugin folder. +For available configuration options see config.inc.php.dist file. diff --git a/plugins/libkolab/config.inc.php.dist b/plugins/libkolab/config.inc.php.dist index 6c7d05a1..01e13344 100644 --- a/plugins/libkolab/config.inc.php.dist +++ b/plugins/libkolab/config.inc.php.dist @@ -1,10 +1,22 @@ /freebusy +$rcmail_config['kolab_freebusy_server'] = 'https:///'; + +// Set this option to disable SSL certificate checks when triggering Free/Busy (enabled by default) +$rcmail_config['kolab_ssl_verify_peer'] = false; + +// Enables listing of only subscribed folders. This e.g. will limit +// folders in calendar view or available addressbooks +$rcmail_config['kolab_use_subscriptions'] = false; ?> diff --git a/plugins/libkolab/lib/kolab_storage.php b/plugins/libkolab/lib/kolab_storage.php index 6151b9a7..ebbadf2e 100644 --- a/plugins/libkolab/lib/kolab_storage.php +++ b/plugins/libkolab/lib/kolab_storage.php @@ -5,6 +5,7 @@ * * @version @package_version@ * @author Thomas Bruederli + * @author Aleksander Machniak * * Copyright (C) 2012, Kolab Systems AG * @@ -28,13 +29,13 @@ class kolab_storage const CTYPE_KEY_PRIVATE = '/private/vendor/kolab/folder-type'; const COLOR_KEY_SHARED = '/shared/vendor/kolab/color'; const COLOR_KEY_PRIVATE = '/private/vendor/kolab/color'; - const SERVERSIDE_SUBSCRIPTION = 0; - const CLIENTSIDE_SUBSCRIPTION = 1; public static $version = '3.0'; public static $last_error; private static $ready = false; + private static $subscriptions; + private static $states; private static $config; private static $cache; private static $imap; @@ -92,7 +93,7 @@ class kolab_storage $folders = $folderdata = array(); if (self::setup()) { - foreach ((array)self::list_folders('', '*', $type, false, $folderdata) as $foldername) { + foreach ((array)self::list_folders('', '*', $type, null, $folderdata) as $foldername) { $folders[$foldername] = new kolab_storage_folder($foldername, $folderdata[$foldername]); } } @@ -192,13 +193,14 @@ class kolab_storage /** * Creates IMAP folder * - * @param string $name Folder name (UTF7-IMAP) - * @param string $type Folder type - * @param bool $subscribed Sets folder subscription + * @param string $name Folder name (UTF7-IMAP) + * @param string $type Folder type + * @param bool $subscribed Sets folder subscription + * @param bool $active Sets folder state (client-side subscription) * * @return bool True on success, false on failure */ - public static function folder_create($name, $type = null, $subscribed = false) + public static function folder_create($name, $type = null, $subscribed = false, $active = false) { self::setup(); @@ -211,6 +213,10 @@ class kolab_storage if (!$saved) { self::$imap->delete_folder($name); } + // activate folder + else if ($active) { + self::set_state($name, true); + } } } @@ -222,6 +228,7 @@ class kolab_storage return false; } + /** * Renames IMAP folder * @@ -247,10 +254,12 @@ class kolab_storage * Does additional checks for permissions and folder name restrictions * * @param array Hash array with folder properties and metadata - * - name: Folder name - * - oldname: Old folder name when changed - * - parent: Parent folder to create the new one in - * - type: Folder type to create + * - name: Folder name + * - oldname: Old folder name when changed + * - parent: Parent folder to create the new one in + * - type: Folder type to create + * - subscribed: Subscribed flag (IMAP subscription) + * - active: Activation flag (client-side subscription) * @return mixed New folder name or False on failure */ public static function folder_update(&$prop) @@ -319,7 +328,7 @@ class kolab_storage } // create new folder else { - $result = self::folder_create($folder, $prop['type'], $prop['subscribed'] === self::SERVERSIDE_SUBSCRIPTION); + $result = self::folder_create($folder, $prop['type'], $prop['subscribed'], $prop['active']); } // save color in METADATA @@ -532,17 +541,22 @@ 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 Enable to return subscribed folders only + * @param boolean Enable to return subscribed folders only (null to use configured subscription mode) * @param array Will be filled with folder-types data * * @return array List of folders */ - public static function list_folders($root = '', $mbox = '*', $filter = null, $subscribed = false, &$folderdata = array()) + public static function list_folders($root = '', $mbox = '*', $filter = null, $subscribed = null, &$folderdata = array()) { if (!self::setup()) { return null; } + // use IMAP subscriptions + if ($subscribed === null && self::$config->get('kolab_use_subscriptions')) { + $subscribed = true; + } + if (!$filter) { // Get ALL folders list, standard way if ($subscribed) { @@ -566,7 +580,7 @@ class kolab_storage $regexp = '/^' . preg_quote($filter, '/') . '(\..+)?$/'; // In some conditions we can skip LIST command (?) - if ($subscribed == false && $filter != 'mail' && $prefix == '*') { + if (!$subscribed && $filter != 'mail' && $prefix == '*') { foreach ($folderdata as $folder => $type) { if (!preg_match($regexp, $type)) { unset($folderdata[$folder]); @@ -644,6 +658,7 @@ class kolab_storage return 'mail'; } + /** * Sets folder content-type. * @@ -665,4 +680,156 @@ class kolab_storage return $success; } + + + /** + * Check subscription status of this folder + * + * @param string $folder Folder name + * + * @return boolean True if subscribed, false if not + */ + public static function folder_is_subscribed($folder) + { + if (self::$subscriptions === null) { + self::setup(); + self::$subscriptions = self::$imap->list_folders_subscribed(); + } + + return in_array($folder, self::$subscriptions); + } + + + /** + * Change subscription status of this folder + * + * @param string $folder Folder name + * + * @return True on success, false on error + */ + public static function folder_subscribe($folder) + { + self::setup(); + + if (self::$imap->subscribe($folder)) { + self::$subscriptions === null; + return true; + } + + return false; + } + + + /** + * Change subscription status of this folder + * + * @param string $folder Folder name + * + * @return True on success, false on error + */ + public static function folder_unsubscribe($folder) + { + self::setup(); + + if (self::$imap->unsubscribe($folder)) { + self::$subscriptions === null; + return true; + } + + return false; + } + + + /** + * Check activation status of this folder + * + * @param string $folder Folder name + * + * @return boolean True if active, false if not + */ + public static function folder_is_active($folder) + { + $active_folders = self::get_states(); + + return in_array($folder, $active_folders); + } + + + /** + * Change activation status of this folder + * + * @param string $folder Folder name + * + * @return True on success, false on error + */ + public static function folder_activate($folder) + { + return self::set_state($folder, true); + } + + + /** + * Change activation status of this folder + * + * @param string $folder Folder name + * + * @return True on success, false on error + */ + public static function folder_deactivate($folder) + { + return self::set_state($folder, false); + } + + + /** + * Return list of active folders + */ + private static function get_states() + { + if (self::$states !== null) { + return self::$states; + } + + $rcube = rcube::get_instance(); + $folders = $rcube->config->get('kolab_active_folders'); + + if ($folders !== null) { + self::$states = !empty($folders) ? explode('**', $folders) : array(); + } + // for backward-compatibility copy server-side subscriptions to activation states + else { + self::setup(); + if (self::$subscriptions === null) { + self::$subscriptions = self::$imap->list_folders_subscribed(); + } + self::$states = self::$subscriptions; + $folders = implode(self::$states, '**'); + $rcube->user->save_prefs(array('kolab_active_folders' => $folders)); + } + + return self::$states; + } + + + /** + * Update list of active folders + */ + private static function set_state($folder, $state) + { + self::get_states(); + + // update in-memory list + $idx = array_search($folder, self::$states); + if ($state && $idx === false) { + self::$states[] = $folder; + } + else if (!$state && $idx !== false) { + unset(self::$states[$idx]); + } + + // update user preferences + $folders = implode(self::$states, '**'); + $rcube = rcube::get_instance(); + return $rcube->user->save_prefs(array('kolab_active_folders' => $folders)); + } } diff --git a/plugins/libkolab/lib/kolab_storage_folder.php b/plugins/libkolab/lib/kolab_storage_folder.php index 363745e9..27517cff 100644 --- a/plugins/libkolab/lib/kolab_storage_folder.php +++ b/plugins/libkolab/lib/kolab_storage_folder.php @@ -5,6 +5,7 @@ * * @version @package_version@ * @author Thomas Bruederli + * @author Aleksander Machniak * * Copyright (C) 2012, Kolab Systems AG * @@ -235,47 +236,48 @@ class kolab_storage_folder return $this->resource_uri; } + /** + * Check activation status of this folder + * + * @return boolean True if enabled, false if not + */ + public function is_active() + { + return kolab_storage::folder_is_active($this->name); + } + + /** + * Change activation status of this folder + * + * @param boolean The desired subscription status: true = active, false = not active + * + * @return True on success, false on error + */ + public function activate($active) + { + return $active ? kolab_storage::folder_activate($this->name) : kolab_storage::folder_deactivate($this->name); + } + /** * Check subscription status of this folder * - * @param string Subscription type (kolab_storage::SERVERSIDE_SUBSCRIPTION or kolab_storage::CLIENTSIDE_SUBSCRIPTION) * @return boolean True if subscribed, false if not */ - public function is_subscribed($type = 0) + public function is_subscribed() { - static $subscribed; // local cache - - if ($type == kolab_storage::SERVERSIDE_SUBSCRIPTION) { - if (!$subscribed) - $subscribed = $this->imap->list_folders_subscribed(); - - return in_array($this->name, $subscribed); - } - else if (kolab_storage::CLIENTSIDE_SUBSCRIPTION) { - // TODO: implement this - return true; - } - - return false; + return kolab_storage::folder_is_subscribed($this->name); } /** * Change subscription status of this folder * * @param boolean The desired subscription status: true = subscribed, false = not subscribed - * @param string Subscription type (kolab_storage::SERVERSIDE_SUBSCRIPTION or kolab_storage::CLIENTSIDE_SUBSCRIPTION) + * * @return True on success, false on error */ - public function subscribe($subscribed, $type = 0) + public function subscribe($subscribed) { - if ($type == kolab_storage::SERVERSIDE_SUBSCRIPTION) { - return $subscribed ? $this->imap->subscribe($this->name) : $this->imap->unsubscribe($this->name); - } - else { - // TODO: implement this - } - - return false; + return $subscribed ? kolab_storage::folder_subscribe($this->name) : kolab_storage::folder_unsubscribe($this->name); } diff --git a/plugins/libkolab/libkolab.php b/plugins/libkolab/libkolab.php index 2d03733c..b5ff968a 100644 --- a/plugins/libkolab/libkolab.php +++ b/plugins/libkolab/libkolab.php @@ -59,6 +59,4 @@ class libkolab extends rcube_plugin $p['fetch_headers'] = trim($p['fetch_headers'] .' X-KOLAB-TYPE X-KOLAB-MIME-VERSION'); return $p; } - - } diff --git a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php index 3409e9cf..a713311a 100644 --- a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php +++ b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php @@ -119,7 +119,7 @@ class tasklist_kolab_driver extends tasklist_driver 'color' => $folder->get_color('0000CC'), 'showalarms' => isset($prefs[$list_id]['showalarms']) ? $prefs[$list_id]['showalarms'] : $alarms, 'editable' => !$readonly, - 'active' => $folder->is_subscribed(kolab_storage::SERVERSIDE_SUBSCRIPTION), + 'active' => $folder->is_active(), 'parentfolder' => $path_imap, 'default' => $folder->default, 'class_name' => trim($folder->get_namespace() . ($folder->default ? ' default' : '')), @@ -155,7 +155,7 @@ class tasklist_kolab_driver extends tasklist_driver public function create_list($prop) { $prop['type'] = 'task' . ($prop['default'] ? '.default' : ''); - $prop['subscribed'] = kolab_storage::SERVERSIDE_SUBSCRIPTION; // subscribe to folder by default + $prop['active'] = true; // activate folder by default $folder = kolab_storage::folder_update($prop); if ($folder === false) { @@ -229,7 +229,7 @@ class tasklist_kolab_driver extends tasklist_driver public function subscribe_list($prop) { if ($prop['id'] && ($folder = $this->folders[$prop['id']])) { - return $folder->subscribe($prop['active'], kolab_storage::SERVERSIDE_SUBSCRIPTION); + return $folder->activate($prop['active']); } return false; }