diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js index 52baf116..a1a60b1b 100644 --- a/plugins/calendar/calendar_ui.js +++ b/plugins/calendar/calendar_ui.js @@ -2739,7 +2739,9 @@ function rcube_calendar_ui(settings) selectable: true, save_state: true, searchbox: '#calendarlistsearch', - search_action: 'calendar/calendar' + search_action: 'calendar/calendar', + search_sources: [ 'folders', 'users' ], + search_title: rcmail.gettext('calsearchresults','calendar') }); calendars_list.addEventListener('select', function(node) { me.select_calendar(node.id); diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php index 5feed234..97f6a96f 100644 --- a/plugins/calendar/drivers/kolab/kolab_calendar.php +++ b/plugins/calendar/drivers/kolab/kolab_calendar.php @@ -7,7 +7,7 @@ * @author Thomas Bruederli * @author Aleksander Machniak * - * Copyright (C) 2012, Kolab Systems AG + * Copyright (C) 2012-2014, 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 @@ -35,11 +35,30 @@ class kolab_calendar public $storage; public $name; - private $cal; - private $events = array(); - private $imap_folder = 'INBOX/Calendar'; - private $search_fields = array('title', 'description', 'location', 'attendees'); + protected $cal; + protected $events = array(); + protected $imap_folder = 'INBOX/Calendar'; + protected $search_fields = array('title', 'description', 'location', 'attendees'); + /** + * Factory method to instantiate a kolab_calendar object + * + * @param string Calendar ID (encoded IMAP folder name) + * @param object calendar plugin object + * @return object kolab_calendar instance + */ + public static function factory($id, $calendar) + { + $imap = $calendar->rc->get_storage(); + $imap_folder = kolab_storage::id_decode($id); + $info = $imap->folder_info($imap_folder, true); + if (empty($info) || $info['noselect'] || kolab_storage::folder_type($imap_folder) != 'event') { + return new kolab_user_calendar($imap_folder, $calendar); + } + else { + return new kolab_calendar($imap_folder, $calendar); + } + } /** * Default constructor @@ -177,14 +196,6 @@ class kolab_calendar return false; } - /** - * Return the corresponding kolab_storage_folder instance - */ - public function get_folder() - { - return $this->storage; - } - /** * Getter for a single event object diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php index 9c45eb65..12e4258b 100644 --- a/plugins/calendar/drivers/kolab/kolab_driver.php +++ b/plugins/calendar/drivers/kolab/kolab_driver.php @@ -24,6 +24,7 @@ */ require_once(dirname(__FILE__) . '/kolab_calendar.php'); +require_once(dirname(__FILE__) . '/kolab_user_calendar.php'); class kolab_driver extends calendar_driver { @@ -78,14 +79,20 @@ class kolab_driver extends calendar_driver return $this->calendars; // get all folders that have "event" type, sorted by namespace/name - $folders = kolab_storage::sort_folders(kolab_storage::get_folders('event')); + $folders = kolab_storage::sort_folders(kolab_storage::get_folders('event') + kolab_storage::get_user_folders(true)); $this->calendars = array(); foreach ($folders as $folder) { - $calendar = new kolab_calendar($folder->name, $this->cal); - $this->calendars[$calendar->id] = $calendar; - if (!$calendar->readonly) - $this->has_writeable = true; + if ($folder instanceof kolab_storage_user_folder) + $calendar = new kolab_user_calendar($folder->name, $this->cal); + else + $calendar = new kolab_calendar($folder->name, $this->cal); + + if ($calendar->ready) { + $this->calendars[$calendar->id] = $calendar; + if (!$calendar->readonly) + $this->has_writeable = true; + } } return $this->calendars; @@ -114,18 +121,32 @@ class kolab_driver extends calendar_driver $calendars = $names = array(); // include virtual folders for a full folder tree - if (!$active && !$personal && !$this->rc->output->ajax_call && in_array($this->rc->action, array('index',''))) + if (!is_null($tree)) $folders = kolab_storage::folder_hierarchy($folders, $tree); foreach ($folders as $id => $cal) { $fullname = $cal->get_name(); - $listname = kolab_storage::folder_displayname($fullname, $names); + $listname = $cal->get_foldername(); $imap_path = explode('/', $cal->name); $topname = array_pop($imap_path); $parent_id = kolab_storage::folder_id(join('/', $imap_path), true); - // special handling for virtual folders - if ($cal->virtual) { + // special handling for user or virtual folders + if ($cal instanceof kolab_storage_user_folder) { + $calendars[$cal->id] = array( + 'id' => $cal->id, + 'name' => kolab_storage::object_name($fullname), + 'listname' => $listname, + 'editname' => $cal->get_foldername(), + 'color' => $cal->get_color(), + 'active' => $cal->is_active(), + 'owner' => $cal->get_owner(), + 'virtual' => false, + 'readonly' => true, + 'class_name' => 'user', + ); + } + else if ($cal->virtual) { $calendars[$cal->id] = array( 'id' => $cal->id, 'name' => $fullname, @@ -230,8 +251,8 @@ class kolab_driver extends calendar_driver { // create calendar object if necesary if (!$this->calendars[$id] && $id !== self::BIRTHDAY_CALENDAR_ID) { - $foldername = kolab_storage::id_decode($id); - $calendar = new kolab_calendar($foldername, $this->cal); + $calendar = kolab_calendar::factory($id, $this->cal); + console($id, $calendar->id, $calendar->ready); if ($calendar->ready) $this->calendars[$calendar->id] = $calendar; } @@ -389,8 +410,20 @@ class kolab_driver extends calendar_driver $this->calendars[$calendar->id] = $calendar; } } + // find other user's virtual calendars else if ($source == 'users') { - // TODO: implement this + foreach (kolab_storage::search_users($query, 0) as $user) { + $calendar = new kolab_user_calendar($user, $this->cal); + $this->calendars[$calendar->id] = $calendar; + + // search for calendar folders shared by this user + foreach (kolab_storage::list_user_folders($user, 'event', false) as $foldername) { + if (1 || !kolab_storage::folder_is_subscribed($foldername, true)) { + $cal = new kolab_calendar($foldername, $this->cal); + $this->calendars[$cal->id] = $cal; + } + } + } } // don't list the birthday calendar diff --git a/plugins/calendar/drivers/kolab/kolab_user_calendar.php b/plugins/calendar/drivers/kolab/kolab_user_calendar.php new file mode 100644 index 00000000..a5795a4f --- /dev/null +++ b/plugins/calendar/drivers/kolab/kolab_user_calendar.php @@ -0,0 +1,219 @@ + + * + * Copyright (C) 2014, 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_user_calendar extends kolab_calendar +{ + public $id = 'unknown'; + public $ready = false; + public $readonly = true; + public $attachments = false; + public $name; + + protected $userdata = array(); + + + /** + * Default constructor + */ + public function __construct($user_or_folder, $calendar) + { + $this->cal = $calendar; + + // full user record is provided + if (is_array($user_or_folder)) { + $this->userdata = $user_or_folder; + $this->storage = new kolab_storage_user_folder($this->userdata['kolabtargetfolder'], '', $this->userdata); + } + else { // get user record from LDAP + $this->storage = new kolab_storage_user_folder($user_or_folder); + $this->userdata = $this->storage->ldaprec; + } + + $this->ready = !empty($this->userdata['kolabtargetfolder']); + + if ($this->ready) { + // ID is derrived from the user's kolabtargetfolder attribute + $this->id = kolab_storage::folder_id($this->userdata['kolabtargetfolder'], true); + $this->imap_folder = $this->userdata['kolabtargetfolder']; + $this->name = $this->storage->get_name(); + + // user-specific alarms settings win + $prefs = $this->cal->rc->config->get('kolab_calendars', array()); + if (isset($prefs[$this->id]['showalarms'])) + $this->alarms = $prefs[$this->id]['showalarms']; + } + } + + + /** + * Getter for a nice and human readable name for this calendar + * + * @return string Name of this calendar + */ + public function get_name() + { + return $this->userdata['name'] ?: $this->userdata['mail']; + } + + + /** + * Getter for the IMAP folder owner + * + * @return string Name of the folder owner + */ + public function get_owner() + { + return $this->userdata['mail']; + } + + + /** + * Getter for the name of the namespace to which the IMAP folder belongs + * + * @return string Name of the namespace (personal, other, shared) + */ + public function get_namespace() + { + return 'user'; + } + + + /** + * Getter for the top-end calendar folder name (not the entire path) + * + * @return string Name of this calendar + */ + public function get_foldername() + { + return $this->get_name(); + } + + /** + * Return color to display this calendar + */ + public function get_color() + { + // calendar color is stored in local user prefs + $prefs = $this->cal->rc->config->get('kolab_calendars', array()); + + if (!empty($prefs[$this->id]) && !empty($prefs[$this->id]['color'])) + return $prefs[$this->id]['color']; + + return 'cc0000'; + } + + /** + * Compose an URL for CalDAV access to this calendar (if configured) + */ + public function get_caldav_url() + { + return false; + } + + /** + * Getter for a single event object + */ + public function get_event($id) + { + // TODO: implement this + return $this->events[$id]; + } + + + /** + * @param integer Event's new start (unix timestamp) + * @param integer Event's new end (unix timestamp) + * @param string Search query (optional) + * @param boolean Include virtual events (optional) + * @param array Additional parameters to query storage + * @return array A list of event records + */ + public function list_events($start, $end, $search = null, $virtual = 1, $query = array()) + { + // TODO: implement this + console('kolab_user_calendar::list_events()'); + return array(); + } + + + /** + * Create a new event record + * + * @see calendar_driver::new_event() + * + * @return mixed The created record ID on success, False on error + */ + public function insert_event($event) + { + return false; + } + + /** + * Update a specific event record + * + * @see calendar_driver::new_event() + * @return boolean True on success, False on error + */ + + public function update_event($event, $exception_id = null) + { + return false; + } + + /** + * Delete an event record + * + * @see calendar_driver::remove_event() + * @return boolean True on success, False on error + */ + public function delete_event($event, $force = true) + { + return false; + } + + /** + * Restore deleted event record + * + * @see calendar_driver::undelete_event() + * @return boolean True on success, False on error + */ + public function restore_event($event) + { + return false; + } + + + /** + * Convert from Kolab_Format to internal representation + */ + private function _to_rcube_event($record) + { + $record['id'] = $record['uid']; + $record['calendar'] = $this->id; + + // TODO: implement this + + return $record; + } + +} diff --git a/plugins/calendar/lib/calendar_ui.php b/plugins/calendar/lib/calendar_ui.php index 0f5091af..a3ed443d 100644 --- a/plugins/calendar/lib/calendar_ui.php +++ b/plugins/calendar/lib/calendar_ui.php @@ -205,6 +205,7 @@ class calendar_ui { $html = ''; $jsenv = array(); + $tree = true; $calendars = $this->cal->driver->list_calendars(false, false, $tree); // walk folder tree @@ -273,19 +274,22 @@ class calendar_ui */ public function calendar_list_item($id, $prop, &$jsenv) { - unset($prop['user_id']); - $prop['alarms'] = $this->cal->driver->alarms; - $prop['attendees'] = $this->cal->driver->attendees; - $prop['freebusy'] = $this->cal->driver->freebusy; - $prop['attachments'] = $this->cal->driver->attachments; - $prop['undelete'] = $this->cal->driver->undelete; - $prop['feedurl'] = $this->cal->get_url(array('_cal' => $this->cal->ical_feed_hash($id) . '.ics', 'action' => 'feed')); + // enrich calendar properties with settings from the driver + if (!$prop['virtual']) { + unset($prop['user_id']); + $prop['alarms'] = $this->cal->driver->alarms; + $prop['attendees'] = $this->cal->driver->attendees; + $prop['freebusy'] = $this->cal->driver->freebusy; + $prop['attachments'] = $this->cal->driver->attachments; + $prop['undelete'] = $this->cal->driver->undelete; + $prop['feedurl'] = $this->cal->get_url(array('_cal' => $this->cal->ical_feed_hash($id) . '.ics', 'action' => 'feed')); - if (!$prop['virtual']) $jsenv[$id] = $prop; + } $class = 'calendar cal-' . asciiwords($id, true); - $title = $prop['name'] != $prop['listname'] ? html_entity_decode($prop['name'], ENT_COMPAT, RCMAIL_CHARSET) : ''; + $title = $prop['name'] != $prop['listname'] || strlen($prop['name']) > 25 ? + html_entity_decode($prop['name'], ENT_COMPAT, RCMAIL_CHARSET) : ''; $is_collapsed = false; // TODO: determine this somehow? if ($prop['virtual']) diff --git a/plugins/calendar/skins/larry/calendar.css b/plugins/calendar/skins/larry/calendar.css index 01a4c3d6..a44899b5 100644 --- a/plugins/calendar/skins/larry/calendar.css +++ b/plugins/calendar/skins/larry/calendar.css @@ -269,12 +269,12 @@ pre { #calendars .treelist div.readonly span.calname { background-position: right -20px; } -/* -#calendars .treelist div.other span.calname { + +#calendars .treelist div.user span.calname { background-position: right -38px; } - -#calendars .treelist div.other.readonly span.calname { +/* +#calendars .treelist div.user.readonly span.calname { background-position: right -56px; } diff --git a/plugins/kolab_auth/kolab_auth_ldap.php b/plugins/kolab_auth/kolab_auth_ldap.php index d529b73e..7044ebf2 100644 --- a/plugins/kolab_auth/kolab_auth_ldap.php +++ b/plugins/kolab_auth/kolab_auth_ldap.php @@ -213,7 +213,6 @@ class kolab_auth_ldap extends rcube_ldap_generic * 0 - partial (*abc*), * 1 - strict (=), * 2 - prefix (abc*) - * @param boolean $select True if results are requested, False if count only * @param array $required List of fields that cannot be empty * @param int $limit Number of records * diff --git a/plugins/libkolab/js/folderlist.js b/plugins/libkolab/js/folderlist.js index eed8b509..e2119a84 100644 --- a/plugins/libkolab/js/folderlist.js +++ b/plugins/libkolab/js/folderlist.js @@ -46,7 +46,7 @@ function kolab_folderlist(node, p) // create treelist widget to present the search results if (!search_results_widget) { search_results_container = $('
') - .html('

' + rcmail.gettext('calsearchresults','calendar') + '

') + .html(p.search_title ? '

' + p.search_title + '

' : '') .insertAfter(me.container); search_results_widget = new rcube_treelist_widget('
    ', { @@ -63,36 +63,64 @@ function kolab_folderlist(node, p) var li = $(this).closest('li'), id = li.attr('id').replace(new RegExp('^'+p.id_prefix), ''), + node = search_results_widget.get_node(id), prop = search_results[id], - parent_id = prop.parent || null; + parent_id = prop.parent || null, + has_children = node.children && node.children.length, + dom_node = has_children ? li.children().first().clone(true, true) : li.children().first(); // find parent node and insert at the right place if (parent_id && $('#' + p.id_prefix + parent_id, me.container).length) { prop.listname = prop.editname; - li.children().first().children('span,a').first().html(Q(prop.listname)); + dom_node.children('span,a').first().html(Q(prop.listname)); } - // move this result item to the main list widget - me.insert({ - id: id, - classes: [], - html: li.children().first() - }, parent_id, parent_id ? true : false); + // TODO: copy parent tree too + + // replace virtual node with a real one + if (me.get_node(id)) { + $(me.get_item(id, true)).children().first() + .replaceWith(dom_node) + .removeClass('virtual'); + } + else { + // move this result item to the main list widget + me.insert({ + id: id, + classes: [], + virtual: prop.virtual, + html: dom_node, + }, parent_id, parent_id ? true : false); + } delete prop.html; me.triggerEvent('insert-item', { id: id, data: prop, item: li }); - li.remove(); + + if (has_children) { + li.find('input[type=checkbox]').first().prop('disabled', true).get(0).checked = true; + } + else { + li.remove(); + } }); } // add results to list - for (var prop, i=0; i < results.length; i++) { + for (var prop, item, i=0; i < results.length; i++) { prop = results[i]; + item = $(prop.html); search_results[prop.id] = prop; - $('
  • ') - .attr('id', p.id_prefix + prop.id) - .html(prop.html) - .appendTo(search_results_widget.container); + search_results_widget.insert({ + id: prop.id, + classes: prop.class_name ? String(prop.class_name).split(' ') : [], + html: item, + collapsed: true + }, prop.parent); + + // disable checkbox if item already exists in main list + if (me.get_node(prop.id) && !me.get_node(prop.id).virtual) { + item.find('input[type=checkbox]').first().prop('disabled', true).get(0).checked = true; + } } search_results_container.show(); @@ -110,7 +138,7 @@ function kolab_folderlist(node, p) // send search request(s) to server if (search.query && search.execute) { - var sources = [ 'folders' /*, 'users'*/ ]; + var sources = p.search_sources || [ 'folders' ]; var reqid = rcmail.multi_thread_http_request({ items: sources, threads: rcmail.env.autocomplete_threads || 1, diff --git a/plugins/libkolab/lib/kolab_storage.php b/plugins/libkolab/lib/kolab_storage.php index 970316e2..872ce29c 100644 --- a/plugins/libkolab/lib/kolab_storage.php +++ b/plugins/libkolab/lib/kolab_storage.php @@ -44,6 +44,7 @@ class kolab_storage private static $states; private static $config; private static $imap; + private static $ldap; // Default folder names private static $default_folders = array( @@ -101,6 +102,40 @@ class kolab_storage return self::$ready; } + /** + * Initializes LDAP object to resolve Kolab users + */ + public static function ldap() + { + if (self::$ldap) { + return self::$ldap; + } + + $rcmail = rcube::get_instance(); + $config = $rcmail->config->get('kolab_users_directory', $rcmail->config->get('kolab_auth_addressbook')); + + if (!is_array($config)) { + $ldap_config = (array)$rcmail->config->get('ldap_public'); + $config = $ldap_config[$config]; + } + + if (empty($config)) { + return null; + } + + // overwrite filter option + if ($filter = $rcmail->config->get('kolab_users_filter')) { + $rcmail->config->set('kolab_auth_filter', $filter); + } + + // re-use the LDAP wrapper class from kolab_auth plugin + require_once rtrim(RCUBE_PLUGINS_DIR, '/') . '/kolab_auth/kolab_auth_ldap.php'; + + self::$ldap = new kolab_auth_ldap($config); + + return self::$ldap; + } + /** * Get a list of storage folders for the given data type @@ -490,7 +525,7 @@ class kolab_storage $folder = substr($folder, $pos+1); } else { - $prefix = $folder; + $prefix = '('.$folder.')'; $folder = ''; } @@ -811,7 +846,7 @@ class kolab_storage // $folders is a result of get_folders() we can assume folders were already sorted foreach (array_keys($nsnames) as $ns) { - // asort($nsnames[$ns], SORT_LOCALE_STRING); + asort($nsnames[$ns], SORT_LOCALE_STRING); foreach (array_keys($nsnames[$ns]) as $utf7name) { $out[] = $folders[$utf7name]; } @@ -829,13 +864,18 @@ class kolab_storage * * @return array Flat folders list */ - public static function folder_hierarchy($folders, &$tree) + public static function folder_hierarchy($folders, &$tree = null) { $_folders = array(); - $delim = rcube::get_instance()->get_storage()->get_hierarchy_delimiter(); - $tree = new virtual_kolab_storage_folder('', '', ''); // create tree root + $delim = self::$imap->get_hierarchy_delimiter(); + $other_ns = self::$imap->get_namespace('other'); + $tree = new kolab_storage_virtual_folder('', '', ''); // create tree root $refs = array('' => $tree); + if (is_array($other_ns)) { + $other_ns = rtrim($other_ns[0][0], '/'); + } + foreach ($folders as $idx => $folder) { $path = explode($delim, $folder->name); array_pop($path); @@ -852,9 +892,15 @@ class kolab_storage while (count($path) >= $depth && ($parent = join($delim, $path))) { array_pop($path); - $name = kolab_storage::object_name($parent, $folder->get_namespace()); + $parent_parent = join($delim, $path); if (!$refs[$parent]) { - $refs[$parent] = new virtual_kolab_storage_folder($parent, $name, $folder->get_namespace(), join($delim, $path)); + if ($parent_parent == $other_ns) { + $refs[$parent] = new kolab_storage_user_folder($parent, $parent_parent); + } + else { + $name = kolab_storage::object_name($parent, $folder->get_namespace()); + $refs[$parent] = new kolab_storage_virtual_folder($parent, $name, $folder->get_namespace(), $parent_parent); + } $parents[] = $refs[$parent]; } } @@ -1023,14 +1069,22 @@ class kolab_storage * Change subscription status of this folder * * @param string $folder Folder name + * @param boolean $temp Only remove temporary subscription * * @return True on success, false on error */ - public static function folder_unsubscribe($folder) + public static function folder_unsubscribe($folder, $temp = false) { self::setup(); - if (self::$imap->unsubscribe($folder)) { + // temporary/session subscription + if ($temp) { + if (is_array($_SESSION['kolab_subscribed_folders']) && ($i = array_search($folder, $_SESSION['kolab_subscribed_folders'])) !== false) { + unset($_SESSION['kolab_subscribed_folders'][$i]); + } + return true; + } + else if (self::$imap->unsubscribe($folder)) { self::$subscriptions === null; return true; } @@ -1078,10 +1132,8 @@ class kolab_storage */ public static function folder_deactivate($folder) { - // remove from temp subscriptions - if (is_array($_SESSION['kolab_subscribed_folders']) && ($i = array_search($folder, $_SESSION['kolab_subscribed_folders'])) !== false) { - unset($_SESSION['kolab_subscribed_folders'][$i]); - } + // remove from temp subscriptions, really? + self::folder_unsubscribe($folder, true); return self::set_state($folder, false); } @@ -1241,6 +1293,106 @@ class kolab_storage } } + + /** + * + * @param mixed $query Search value (or array of field => value pairs) + * @param int $mode Matching mode: 0 - partial (*abc*), 1 - strict (=), 2 - prefix (abc*) + * @param array $required List of fields that shall ot be empty + * @param int $limit Number of records + * + * @return array List or false on error + */ + public static function search_users($query, $mode = 1, $required = array(), $limit = 0) + { + // requires a working LDAP setup + if (!self::ldap()) { + return array(); + } + + // FIXME: make search attributes configurable + $results = self::$ldap->search(array('cn','mail','alias'), $query, $mode, $required, $limit); + + // resolve to IMAP folder name + $other_ns = self::$imap->get_namespace('other'); + $user_attrib = rcube::get_instance()->config->get('kolab_auth_login', 'mail'); + + array_walk($results, function(&$user, $dn) use ($other_ns, $user_attrib) { + list($localpart, $domain) = explode('@', $user[$user_attrib]); + $root = $other_ns[0][0]; + $user['kolabtargetfolder'] = $root . $localpart; + }); + + return $results; + } + + + /** + * Returns a list of IMAP folders shared by the given user + * + * @param array User entry from LDAP + * @param string Data type to list folders for (contact,event,task,journal,file,note,mail,configuration) + * @param boolean 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_user_folders($user, $type, $subscribed = null, &$folderdata = array()) + { + $folders = array(); + + // use localpart of user attribute as root for folder listing + $user_attrib = rcube::get_instance()->config->get('kolab_auth_login', 'mail'); + if (!empty($user[$user_attrib])) { + list($mbox) = explode('@', $user[$user_attrib]); + + $other_ns = self::$imap->get_namespace('other'); + if (is_array($other_ns)) { + $other_ns = $other_ns[0][0]; + } + + $folders = self::list_folders($other_ns . $mbox, '*', $type, $subscribed, $folderdata); + } + + return $folders; + } + + + /** + * Get a list of (virtual) top-level folders from the other users namespace + * + * @param boolean Enable to return subscribed folders only (null to use configured subscription mode) + * + * @return array List of Kolab_Folder objects (folder names in UTF7-IMAP) + */ + public static function get_user_folders($subscribed) + { + $folders = $folderdata = array(); + + if (self::setup()) { + $delimiter = self::$imap->get_hierarchy_delimiter(); + $other_ns = self::$imap->get_namespace('other'); + if (is_array($other_ns)) { + $other_ns = rtrim($other_ns[0][0], $delimiter); + $other_depth = count(explode($delimiter, $other_ns)); + } + + foreach ((array)self::list_folders($other_ns, '*', '', $subscribed) as $foldername) { + $path = explode($delimiter, $foldername); + $depth = count($path) - $other_depth; + array_pop($path); + + // only list top-level folders of the 'other' namespace + if ($depth == 1) { + $folders[$foldername] = new kolab_storage_user_folder($foldername, $other_ns); + } + } + } + + return $folders; + } + + /** * Handler for user_delete plugin hooks * @@ -1255,43 +1407,3 @@ class kolab_storage } -/** - * Helper class that represents a virtual IMAP folder - * with a subset of the kolab_storage_folder API. - */ -class virtual_kolab_storage_folder -{ - public $id; - public $name; - public $namespace; - public $parent = ''; - public $children = array(); - public $virtual = true; - protected $displayname; - - public function __construct($name, $dispname, $ns, $parent = '') - { - $this->id = kolab_storage::folder_id($name); - $this->name = $name; - $this->namespace = $ns; - $this->parent = $parent; - $this->displayname = $dispname; - } - - public function get_namespace() - { - return $this->namespace; - } - - public function get_name() - { - // this is already kolab_storage::object_name() result - return $this->displayname; - } - - public function get_foldername() - { - $parts = explode('/', $this->name); - return rcube_charset::convert(end($parts), 'UTF7-IMAP'); - } -} diff --git a/plugins/libkolab/lib/kolab_storage_user_folder.php b/plugins/libkolab/lib/kolab_storage_user_folder.php new file mode 100644 index 00000000..55e38a0d --- /dev/null +++ b/plugins/libkolab/lib/kolab_storage_user_folder.php @@ -0,0 +1,123 @@ + + * + * Copyright (C) 2014, 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_storage_user_folder extends kolab_storage_virtual_folder +{ + protected static $ldapcache = array(); + + public $ldaprec; + + /** + * Default constructor + */ + public function __construct($name, $parent = '', $ldaprec = null) + { + parent::__construct($name, $name, 'other', $parent); + + if (!empty($ldaprec)) { + self::$ldapcache[$name] = $this->ldaprec = $ldaprec; + } + // use value cached in memory for repeated lookups + else if (array_key_exists($name, self::$ldapcache)) { + $this->ldaprec = self::$ldapcache[$name]; + } + // lookup user in LDAP and set $this->ldaprec + else if ($ldap = kolab_storage::ldap()) { + // get domain from current user + list(,$domain) = explode('@', rcube::get_instance()->get_user_name()); + $this->ldaprec = $ldap->get_user_record(parent::get_foldername($this->name) . '@' . $domain, $_SESSION['imap_host']); + if (!empty($this->ldaprec)) { + $this->ldaprec['kolabtargetfolder'] = $name; + } + self::$ldapcache[$name] = $this->ldaprec; + } + } + + /** + * Getter for the top-end folder name to be displayed + * + * @return string Name of this folder + */ + public function get_foldername() + { + return $this->ldaprec ? ($this->ldaprec['displayname'] ?: $this->ldaprec['name']) : + parent::get_foldername(); + } + + /** + * Returns the owner of the folder. + * + * @return string The owner of this folder. + */ + public function get_owner() + { + return $this->ldaprec['mail']; + } + + /** + * 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 + * + * @return boolean True if subscribed, false if not + */ + public function is_subscribed() + { + return kolab_storage::folder_is_subscribed($this->name, true); + } + + /** + * Change subscription status of this folder + * + * @param boolean The desired subscription status: true = subscribed, false = not subscribed + * + * @return True on success, false on error + */ + public function subscribe($subscribed) + { + return $subscribed ? + kolab_storage::folder_subscribe($this->name, true) : + kolab_storage::folder_unsubscribe($this->name, true); + } + +} \ No newline at end of file diff --git a/plugins/libkolab/lib/kolab_storage_virtual_folder.php b/plugins/libkolab/lib/kolab_storage_virtual_folder.php new file mode 100644 index 00000000..61d0fe03 --- /dev/null +++ b/plugins/libkolab/lib/kolab_storage_virtual_folder.php @@ -0,0 +1,86 @@ + + * + * Copyright (C) 2014, 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_storage_virtual_folder +{ + public $id; + public $name; + public $namespace; + public $parent = ''; + public $children = array(); + public $virtual = true; + + protected $displayname; + + public function __construct($name, $dispname, $ns, $parent = '') + { + $this->id = kolab_storage::folder_id($name); + $this->name = $name; + $this->namespace = $ns; + $this->parent = $parent; + $this->displayname = $dispname; + } + + /** + * Getter for the name of the namespace to which the IMAP folder belongs + * + * @return string Name of the namespace (personal, other, shared) + */ + public function get_namespace() + { + return $this->namespace; + } + + /** + * Get the display name value of this folder + * + * @return string Folder name + */ + public function get_name() + { + // this is already kolab_storage::object_name() result + return $this->displayname; + } + + /** + * Getter for the top-end folder name (not the entire path) + * + * @return string Name of this folder + */ + public function get_foldername() + { + $parts = explode('/', $this->name); + return rcube_charset::convert(end($parts), 'UTF7-IMAP'); + } + + /** + * Get the color value stored in metadata + * + * @param string Default color value to return if not set + * @return mixed Color value from IMAP metadata or $default is not set + */ + public function get_color($default = null) + { + return $default; + } +} \ No newline at end of file