Move new calendar list widget and folder searching to libkolab for shared use

This commit is contained in:
Thomas Bruederli 2014-05-13 19:14:08 +02:00
parent 008c5db5d9
commit 8a47c676d5
6 changed files with 192 additions and 106 deletions

View file

@ -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 = $('<div class="searchresults"></div>')
.html('<h2 class="boxtitle">' + rcmail.gettext('calsearchresults','calendar') + '</h2>')
.insertAfter(rcmail.gui_objects.calendarslist);
calenders_search_list = new rcube_treelist_widget('<ul class="treelist listing"></ul>', {
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) {
$('<style type="text/css"></style>')
.html(cal.css)
.appendTo('head');
}
});
}
for (var cal, i=0; i < results.length; i++) {
cal = results[i];
search_calendars[cal.id] = cal;
$('<li>')
.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();
calendars_list.addEventListener('insert-item', function(p) {
var cal = p.data;
if (cal && cal.id) {
cal.active = true;
add_calendar_source(cal);
// add css classes related to this calendar to document
if (cal.css) {
$('<style type="text/css"></style>')
.html(cal.css)
.appendTo('head');
}
search_calendars = {};
// 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 };
}
});

View file

@ -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;
}

View file

@ -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')), '&nbsp;')) .
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')), '&nbsp;'))
);
}

View file

@ -0,0 +1 @@
../../../libkolab/js/folderlist.js

View file

@ -0,0 +1,130 @@
/**
* Kolab groupware folders treelist widget
*
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* @licstart The following is the entire license notice for the
* JavaScript code in this file.
*
* Copyright (C) 2014, Kolab Systems AG <contact@kolabsys.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* @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 = $('<div class="searchresults"></div>')
.html('<h2 class="boxtitle">' + rcmail.gettext('calsearchresults','calendar') + '</h2>')
.insertAfter(me.container);
search_results_widget = new rcube_treelist_widget('<ul class="treelist listing"></ul>', {
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;
$('<li>')
.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;

View file

@ -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
*