diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js index 4db85937..52baf116 100644 --- a/plugins/calendar/calendar_ui.js +++ b/plugins/calendar/calendar_ui.js @@ -1548,7 +1548,8 @@ function rcube_calendar_ui(settings) id_prefix: 'rcres', id_encode: rcmail.html_identifier_encode, id_decode: rcmail.html_identifier_decode, - selectable: true + selectable: true, + save_state: true }); resources_treelist.addEventListener('select', function(node) { if (resources_data[node.id]) { @@ -2671,72 +2672,6 @@ function rcube_calendar_ui(settings) this.selected_calendar = id; }; - // render the results for calendar list search - var calendar_search_results = function(results) - { - if (results.length) { - // create treelist widget to present the search results - if (!calenders_search_list) { - calenders_search_container = $('
') - .html('

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

') - .insertAfter(rcmail.gui_objects.calendarslist); - - calenders_search_list = new rcube_treelist_widget('', { - id_prefix: 'rcmlical', - selectable: false - }); - - // register click handler on search result's checkboxes to select the given calendar for listing - calenders_search_list.container - .appendTo(calenders_search_container) - .on('click', 'input[type=checkbox]', function(e){ - var li = $(this).closest('li'), - id = li.attr('id').replace(/^rcmlical/, ''), - prop = search_calendars[id], - parent_id = prop.parent || null; - - if (!this.checked) - return; - - // find parent node and insert at the right place - if (parent_id && $('#rcmlical'+parent_id, rcmail.gui_objects.calendarslist).length) { - prop.listname = prop.editname; - li.children().first().find('.calname').html(Q(prop.listname)); - } - - // move this calendar to the calendars_list widget - calendars_list.insert({ - id: id, - classes: [], - html: li.children().first() - }, parent_id, parent_id ? true : false); - - search_calendars[id].active = true; - add_calendar_source(prop); - li.remove(); - - // add css classes related to this calendar to document - if (cal.css) { - $('') - .html(cal.css) - .appendTo('head'); - } - }); - } - - for (var cal, i=0; i < results.length; i++) { - cal = results[i]; - search_calendars[cal.id] = cal; - $('
  • ') - .attr('id', 'rcmlical' + cal.id) - .html(cal.html) - .appendTo(calenders_search_list.container); - } - - calenders_search_container.show(); - } - }; - // register the given calendar to the current view var add_calendar_source = function(cal) { @@ -2798,37 +2733,31 @@ function rcube_calendar_ui(settings) } // initialize treelist widget that controls the calendars list - calendars_list = new rcube_treelist_widget(rcmail.gui_objects.calendarslist, { + var widget_class = window.kolab_folderlist || rcube_treelist_widget; + calendars_list = new widget_class(rcmail.gui_objects.calendarslist, { id_prefix: 'rcmlical', selectable: true, - searchbox: '#calendarlistsearch' + save_state: true, + searchbox: '#calendarlistsearch', + search_action: 'calendar/calendar' }); - calendars_list.addEventListener('select', function(node){ + calendars_list.addEventListener('select', function(node) { me.select_calendar(node.id); rcmail.enable_command('calendar-edit', 'calendar-showurl', true); rcmail.enable_command('calendar-remove', !me.calendars[node.id].readonly); }); - calendars_list.addEventListener('search', function(search){ - // hide search results - if (calenders_search_list) { - calenders_search_container.hide(); - calenders_search_list.reset(); - } - search_calendars = {}; + calendars_list.addEventListener('insert-item', function(p) { + var cal = p.data; + if (cal && cal.id) { + cal.active = true; + add_calendar_source(cal); - // send search request(s) to server - if (search.query && search.execute) { - var sources = [ 'folders' /*, 'users'*/ ]; - var reqid = rcmail.multi_thread_http_request({ - items: sources, - threads: rcmail.env.autocomplete_threads || 1, - action: 'calendar/calendar', - postdata: { action:'search', q:search.query, source:'%s' }, - lock: rcmail.display_message(rcmail.get_label('searching'), 'loading'), - onresponse: calendar_search_results - }); - - listsearch_data = { id:reqid, sources:sources.slice(), num:sources.length }; + // add css classes related to this calendar to document + if (cal.css) { + $('') + .html(cal.css) + .appendTo('head'); + } } }); diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php index 786556f0..9c45eb65 100644 --- a/plugins/calendar/drivers/kolab/kolab_driver.php +++ b/plugins/calendar/drivers/kolab/kolab_driver.php @@ -381,23 +381,10 @@ class kolab_driver extends calendar_driver return array(); $this->calendars = array(); - $imap = $this->rc->get_storage(); // find unsubscribed IMAP folders that have "event" type if ($source == 'folders') { - $folders = array(); - foreach ((array)kolab_storage::list_folders('', '*', 'event', false, $folderdata) as $foldername) { - // FIXME: only consider the last part of the folder path for searching? - $realname = strtolower(rcube_charset::convert($foldername, 'UTF7-IMAP')); - if (strpos($realname, $query) !== false && - !kolab_storage::folder_is_subscribed($foldername, true) && - $imap->folder_namespace($foldername) != 'other' - ) { - $folders[$foldername] = new kolab_storage_folder($foldername, $folderdata[$foldername]); - } - } - - foreach ($folders as $folder) { + foreach ((array)kolab_storage::search_folders('event', $query, array('other')) as $folder) { $calendar = new kolab_calendar($folder->name, $this->cal); $this->calendars[$calendar->id] = $calendar; } diff --git a/plugins/calendar/lib/calendar_ui.php b/plugins/calendar/lib/calendar_ui.php index 69ba9c2d..0f5091af 100644 --- a/plugins/calendar/lib/calendar_ui.php +++ b/plugins/calendar/lib/calendar_ui.php @@ -117,6 +117,12 @@ class calendar_ui $this->cal->include_script('calendar_ui.js'); $this->cal->include_script('lib/js/fullcalendar.js'); $this->rc->output->include_script('treelist.js'); + + // include kolab folderlist widget if available + if (is_readable($this->cal->home . '/lib/js/folderlist.js')) { + $this->cal->include_script('lib/js/folderlist.js'); + } + jqueryui::miniColors(); } @@ -292,9 +298,9 @@ class calendar_ui $content = ''; if (!$attrib['activeonly'] || $prop['active']) { $content = html::div($class, + html::span(array('class' => 'calname', 'title' => $title), $prop['editname'] ? Q($prop['editname']) : $prop['listname']) . ($prop['virtual'] ? '' : html::tag('input', array('type' => 'checkbox', 'name' => '_cal[]', 'value' => $id, 'checked' => $prop['active']), '') . - html::span(array('class' => 'handle', 'style' => "background-color: #" . ($prop['color'] ?: 'f00')), ' ')) . - html::span(array('class' => 'calname', 'title' => $title), $prop['editname'] ? Q($prop['editname']) : $prop['listname']) + html::span(array('class' => 'handle', 'style' => "background-color: #" . ($prop['color'] ?: 'f00')), ' ')) ); } diff --git a/plugins/calendar/lib/js/folderlist.js b/plugins/calendar/lib/js/folderlist.js new file mode 120000 index 00000000..c49706b3 --- /dev/null +++ b/plugins/calendar/lib/js/folderlist.js @@ -0,0 +1 @@ +../../../libkolab/js/folderlist.js \ No newline at end of file diff --git a/plugins/libkolab/js/folderlist.js b/plugins/libkolab/js/folderlist.js new file mode 100644 index 00000000..eed8b509 --- /dev/null +++ b/plugins/libkolab/js/folderlist.js @@ -0,0 +1,130 @@ +/** + * Kolab groupware folders treelist widget + * + * @author Thomas Bruederli + * + * @licstart The following is the entire license notice for the + * JavaScript code in this file. + * + * 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 . + * + * @licend The above is the entire license notice + * for the JavaScript code in this file. + */ + +function kolab_folderlist(node, p) +{ + // extends treelist.js + rcube_treelist_widget.call(this, node, p); + + // private vars + var me = this; + var search_results; + var search_results_widget; + var search_results_container; + var listsearch_request; + + var Q = rcmail.quote_html; + + // render the results for folderlist search + function render_search_results(results) + { + if (results.length) { + // create treelist widget to present the search results + if (!search_results_widget) { + search_results_container = $('
    ') + .html('

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

    ') + .insertAfter(me.container); + + search_results_widget = new rcube_treelist_widget('
      ', { + id_prefix: p.id_prefix, + selectable: false + }); + + // register click handler on search result's checkboxes to select the given item for listing + search_results_widget.container + .appendTo(search_results_container) + .on('click', 'input[type=checkbox]', function(e) { + if (!this.checked) + return; + + var li = $(this).closest('li'), + id = li.attr('id').replace(new RegExp('^'+p.id_prefix), ''), + prop = search_results[id], + parent_id = prop.parent || null; + + // 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)); + } + + // move this result item to the main list widget + me.insert({ + id: id, + classes: [], + html: li.children().first() + }, parent_id, parent_id ? true : false); + + delete prop.html; + me.triggerEvent('insert-item', { id: id, data: prop, item: li }); + li.remove(); + }); + } + + // add results to list + for (var prop, i=0; i < results.length; i++) { + prop = results[i]; + search_results[prop.id] = prop; + $('
    • ') + .attr('id', p.id_prefix + prop.id) + .html(prop.html) + .appendTo(search_results_widget.container); + } + + search_results_container.show(); + } + } + + // do some magic when search is performed on the widget + this.addEventListener('search', function(search) { + // hide search results + if (search_results_widget) { + search_results_container.hide(); + search_results_widget.reset(); + } + search_results = {}; + + // send search request(s) to server + if (search.query && search.execute) { + var sources = [ 'folders' /*, 'users'*/ ]; + var reqid = rcmail.multi_thread_http_request({ + items: sources, + threads: rcmail.env.autocomplete_threads || 1, + action: p.search_action || 'listsearch', + postdata: { action:'search', q:search.query, source:'%s' }, + lock: rcmail.display_message(rcmail.get_label('searching'), 'loading'), + onresponse: render_search_results + }); + + listsearch_request = { id:reqid, sources:sources.slice(), num:sources.length }; + } + }); + +} + +// link prototype from base class +kolab_folderlist.prototype = rcube_treelist_widget.prototype; diff --git a/plugins/libkolab/lib/kolab_storage.php b/plugins/libkolab/lib/kolab_storage.php index 55526fd6..970316e2 100644 --- a/plugins/libkolab/lib/kolab_storage.php +++ b/plugins/libkolab/lib/kolab_storage.php @@ -758,6 +758,39 @@ class kolab_storage } + /** + * Search for shared or otherwise not listed groupware folders the user has access + * + * @param string Folder type of folders to search for + * @param string Search string + * @param array Namespace(s) to exclude results from + * + * @return array List of matching kolab_storage_folder objects + */ + public static function search_folders($type, $query, $exclude_ns = array()) + { + if (!self::setup()) { + return array(); + } + + $folders = array(); + + // find unsubscribed IMAP folders of the given type + foreach ((array)self::list_folders('', '*', $type, false, $folderdata) as $foldername) { + // FIXME: only consider the last part of the folder path for searching? + $realname = strtolower(rcube_charset::convert($foldername, 'UTF7-IMAP')); + if (strpos($realname, $query) !== false && + !self::folder_is_subscribed($foldername, true) && + !in_array(self::$imap->folder_namespace($foldername), (array)$exclude_ns) + ) { + $folders[] = new kolab_storage_folder($foldername, $folderdata[$foldername]); + } + } + + return $folders; + } + + /** * Sort the given list of kolab folders by namespace/name *