Parallelize search requests + simplify backend API

This commit is contained in:
Thomas Bruederli 2011-07-16 20:03:19 +02:00
parent 299c8bdd68
commit 171408672f
6 changed files with 42 additions and 98 deletions

View file

@ -102,7 +102,6 @@ class calendar extends rcube_plugin
$this->register_action('event', array($this, 'event_action'));
$this->register_action('calendar', array($this, 'calendar_action'));
$this->register_action('load_events', array($this, 'load_events'));
$this->register_action('search_events', array($this, 'search_events'));
$this->register_action('export_events', array($this, 'export_events'));
$this->register_action('upload', array($this, 'attachment_upload'));
$this->register_action('get-attachment', array($this, 'attachment_get'));
@ -510,25 +509,10 @@ class calendar extends rcube_plugin
$events = $this->driver->load_events(
get_input_value('start', RCUBE_INPUT_GET),
get_input_value('end', RCUBE_INPUT_GET),
($query = get_input_value('q', RCUBE_INPUT_GET)),
get_input_value('source', RCUBE_INPUT_GET)
);
echo $this->encode($events);
exit;
}
/**
* Handler for search-requests from client
* This will return pure JSON formatted output for fullcalendar
*/
function search_events()
{
$events = $this->driver->search_events(
get_input_value('start', RCUBE_INPUT_GET),
get_input_value('end', RCUBE_INPUT_GET),
get_input_value('q', RCUBE_INPUT_GET),
get_input_value('source', RCUBE_INPUT_GET)
);
echo $this->encode($events, true);
echo $this->encode($events, !empty($query));
exit;
}
@ -642,6 +626,7 @@ class calendar extends rcube_plugin
* Encode events as JSON
*
* @param array Events as array
* @param boolean Add CSS class names according to calendar and categories
* @return string JSON encoded events
*/
function encode($events, $addcss = false)

View file

@ -29,10 +29,10 @@ function rcube_calendar_ui(settings)
rcube_calendar.call(this, settings);
/*** member vars ***/
this.is_loading = false;
this.selected_event = null;
this.selected_calendar = null;
this.search_request = null;
this.search_source = null;
this.eventcount = [];
this.saving_lock = null;
@ -975,37 +975,32 @@ function rcube_calendar_ui(settings)
rcmail.hide_message(this._search_message);
for (var sid in this.calendars) {
if (this.calendars[sid] && this.calendars[sid].active) {
fc.fullCalendar('removeEventSource', this.calendars[sid]);
if (this.calendars[sid]) {
this.calendars[sid].url = this.calendars[sid].url.replace(/&q=.+/, '') + '&q='+escape(q);
sources.push(sid);
}
}
id += '@'+sources.join(',');
// just refetch events if query didn't change
// ignore if query didn't change
if (this.search_request == id) {
fc.fullCalendar('refetchEvents');
return;
}
// remove old search results
else if (this.search_source) {
fc.fullCalendar('removeEventSource', this.search_source);
}
else {
// remember current view
else if (!this.search_request) {
this.default_view = fc.fullCalendar('getView').name;
}
// replace event source from fullcalendar
this.search_request = id;
this.search_query = q;
this.search_source = {
url: "./?_task=calendar&_action=search_events&q="+escape(q)+'&source='+escape(sources.join(',')),
editable: false
};
// change to list view
fc.fullCalendar('option', 'listSections', 'month');
fc.fullCalendar('addEventSource', this.search_source);
fc.fullCalendar('changeView', 'table');
// refetch events with new url (if not already triggered by changeView)
if (!this.is_loading)
fc.fullCalendar('refetchEvents');
}
else // empty search input equals reset
this.reset_quicksearch();
@ -1023,15 +1018,17 @@ function rcube_calendar_ui(settings)
if (this.search_request) {
// restore original event sources and view mode from fullcalendar
fc.fullCalendar('option', 'listSections', 'smart');
fc.fullCalendar('removeEventSource', this.search_source);
for (var sid in this.calendars) {
if (this.calendars[sid] && this.calendars[sid].active)
fc.fullCalendar('addEventSource', this.calendars[sid]);
if (this.calendars[sid])
this.calendars[sid].url = this.calendars[sid].url.replace(/&q=.+/, '');
}
if (this.default_view)
fc.fullCalendar('changeView', this.default_view);
this.search_request = this.search_source = this.search_query = null;
if (!this.is_loading)
fc.fullCalendar('refetchEvents');
this.search_request = this.search_query = null;
}
};
@ -1078,14 +1075,10 @@ function rcube_calendar_ui(settings)
me.calendars[id].active = false;
settings.hidden_calendars.push(id);
}
// just trigger search again (don't save prefs?)
if (me.search_request) {
me.quicksearch();
}
else { // add/remove event source
fc.fullCalendar(action, me.calendars[id]);
rcmail.save_pref({ name:'hidden_calendars', value:settings.hidden_calendars.join(',') });
}
// add/remove event source
fc.fullCalendar(action, me.calendars[id]);
rcmail.save_pref({ name:'hidden_calendars', value:settings.hidden_calendars.join(',') });
}
}).data('id', id).get(0).checked = active;
@ -1159,6 +1152,7 @@ function rcube_calendar_ui(settings)
selectable: true,
selectHelper: true,
loading: function(isLoading) {
me.is_loading = isLoading;
this._rc_loading = rcmail.set_busy(isLoading, 'loading', this._rc_loading);
// trigger callback
if (!isLoading && me.search_request)

View file

@ -167,21 +167,11 @@ abstract class calendar_driver
*
* @param integer Event's new start (unix timestamp)
* @param integer Event's new end (unix timestamp)
* @param string Search query (optional)
* @param mixed List of calendar IDs to load events from (either as array or comma-separated string)
* @return array A list of event objects (see header of this file for struct of an event)
*/
abstract function load_events($start, $end, $calendars = null);
/**
* Search events using the given query
*
* @param integer Event's new start (unix timestamp)
* @param integer Event's new end (unix timestamp)
* @param string Search query
* @param mixed List of calendar IDs to load events from (either as array or comma-separated string)
* @return array A list of event objects (see header of this file for struct of an event)
*/
abstract function search_events($start, $end, $query, $calendars = null);
abstract function load_events($start, $end, $query = null, $calendars = null);
/**
* Get a list of pending alarms to be displayed to the user

View file

@ -618,16 +618,24 @@ class database_driver extends calendar_driver
*
* @see Driver:load_events()
*/
public function load_events($start, $end, $calendars = null, $sql_add = '')
public function load_events($start, $end, $query = null, $calendars = null)
{
if (empty($calendars))
$calendars = array_keys($this->calendars);
else if (is_string($calendars))
$calendars = explode(',', $calendars);
// only allow to select from calendars of this use
$calendar_ids = array_map(array($this->rc->db, 'quote'), array_intersect($calendars, array_keys($this->calendars)));
// compose (slow) SQL query for searching
// FIXME: improve searching using a dedicated col and normalized values
if ($query) {
foreach (array('title','location','description','categories','attendees') as $col)
$sql_query[] = $this->rc->db->ilike($col, '%'.$query.'%');
$sql_add = 'AND (' . join(' OR ', $sql_query) . ')';
}
$events = array();
if (!empty($calendar_ids)) {
$result = $this->rc->db->query(sprintf(
@ -687,22 +695,6 @@ class database_driver extends calendar_driver
return $event;
}
/**
* Search events
*
* @see Driver:search_events()
*/
public function search_events($start, $end, $query, $calendars = null)
{
// compose (slow) SQL query for searching
// FIXME: improve searching using a dedicated col and normalized values
foreach (array('title','location','description','categories','attendees') as $col) {
$sql_query[] = $this->rc->db->ilike($col, '%'.$query.'%');
}
return $this->load_events($start, $end, $calendars, 'AND (' . join(' OR ', $sql_query) . ')');
}
/**
* Get a list of pending alarms to be displayed to the user
*

View file

@ -485,11 +485,11 @@ class kolab_driver extends calendar_driver
*
* @param integer Event's new start (unix timestamp)
* @param integer Event's new end (unix timestamp)
* @param mixed List of calendar IDs to load events from (either as array or comma-separated string)
* @param string Search query (optional)
* @param mixed List of calendar IDs to load events from (either as array or comma-separated string)
* @return array A list of event records
*/
public function load_events($start, $end, $calendars = null, $search = null)
public function load_events($start, $end, $search = null, $calendars = null)
{
if ($calendars && is_string($calendars))
$calendars = explode(',', $calendars);
@ -505,18 +505,6 @@ class kolab_driver extends calendar_driver
return $events;
}
/**
* Search events using the given query
*
* @see Driver::search_events()
* @return array A list of event records
*/
public function search_events($start, $end, $query, $calendars = null)
{
// delegate request to load_events()
return $this->load_events($start, $end, $calendars, $query);
}
/**
* Get a list of pending alarms to be displayed to the user
*

View file

@ -27,23 +27,18 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
var settings = rcmail.env.calendar_settings;
// create list of event sources AKA calendars
var src, calendars = [], event_sources = [];
var src, event_sources = [];
var add_url = (rcmail.env.search ? '&q='+escape(rcmail.env.search) : '');
for (var id in rcmail.env.calendars) {
source = $.extend({
url: "./?_task=calendar&_action=load_events&source="+escape(id),
url: "./?_task=calendar&_action=load_events&source=" + escape(id) + add_url,
className: 'fc-event-cal-'+id,
id: id
}, rcmail.env.calendars[id]);
event_sources.push(source);
calendars.push(id);
}
// search query is active
if (rcmail.env.search) {
event_sources = [{ url: "./?_task=calendar&_action=search_events&q="+escape(rcmail.env.search)+'&source='+escape(calendars.join(',')) }];
}
var viewdate = new Date();
if (rcmail.env.date)
viewdate.setTime(rcmail.env.date * 1000);