Move new calendar list widget and folder searching to libkolab for shared use
This commit is contained in:
parent
008c5db5d9
commit
8a47c676d5
6 changed files with 192 additions and 106 deletions
|
@ -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 };
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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')), ' '))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
1
plugins/calendar/lib/js/folderlist.js
Symbolic link
1
plugins/calendar/lib/js/folderlist.js
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../libkolab/js/folderlist.js
|
130
plugins/libkolab/js/folderlist.js
Normal file
130
plugins/libkolab/js/folderlist.js
Normal 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;
|
|
@ -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
|
||||
*
|
||||
|
|
Loading…
Add table
Reference in a new issue