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
*