Smarter updates for recurring events, to avoid refreshing more than needed.

This commit is contained in:
Nathan Gray 2016-01-20 20:58:14 +00:00
parent c4cfb8a8c0
commit 72991fc007
4 changed files with 74 additions and 31 deletions

View File

@ -673,10 +673,62 @@ class calendar_ui
$sidebox->exec('calendar.calendar_ui.sidebox_etemplate', $content, $sel_options, $readonlys);
}
/**
* Send updated event information to the client via ajax
*
* This allows to pass only changed information for a single (recurring) event
* and update the UI without a refreshing any more than needed. If adding,
* a notification via egw_framework::refresh_opener() is still needed but
* edits, updates and deletes will be automatic.
* If the event is recurring, we send the next month's worth of recurrences
* for lack of a better way to determine how much to send.
*
* @param int $event_id
* @param egw_time $recurrence_date
*/
public function update_client($event_id, egw_time $recurrence_date = null)
{
// Directly update stored data.
// Make sure we have the whole event
$event = $this->bo->read($event_id, $recurrence_date);
$response = egw_json_response::get();
if(!$event)
{
// Sending null will trigger a removal
$response->call('egw.dataStoreUID','calendar::'.$event_id,null);
return false;
}
if(!$event['recur_type'] || $recurrence_date)
{
$this->to_client($event);
$response->call('egw.dataStoreUID','calendar::'.$event['row_id'],$event);
}
// If it's recurring, try to send the next month or so
else if($event['recur_type'] )
{
$this_month = new egw_time('next month');
$rrule = calendar_rrule::event2rrule($event, true);
$rrule->rewind();
do
{
$occurrence = $rrule->current();
$converted = $this->bo->read($event['id'], $occurrence);
$this->to_client($converted);
$response->call('egw.dataStoreUID','calendar::'.$converted['row_id'],$converted);
$rrule->next();
}
while ($rrule->valid() && $occurrence <= $this_month );
}
}
/**
* Prepare an array of event information for sending to the client
*
* This involves changing timestamps into strings with timezone
* This involves changing timestamps into strings with timezone so javascript
* does not change them, and making sure we have everything the client needs
* for proper display.
*
* @param type $event
*/

View File

@ -1013,14 +1013,7 @@ class calendar_uiforms extends calendar_ui
$response = egw_json_response::get();
if($response && !$content['id'] && $event['id'])
{
// Directly update stored data.
// Make sure we have the whole event, not just form data
$event = $this->bo->read($event['id']);
// Copy, so as to not change things for subsequent processing
$converted = $event;
$this->to_client($converted);
$response->call('egw.dataStoreUID','calendar::'.$converted['row_id'],$converted);
$this->update_client($event['id']);
}
if (in_array($button,array('cancel','save','delete','delete_exceptions','delete_keep_exceptions')) && $noerror)
{
@ -2843,24 +2836,14 @@ class calendar_uiforms extends calendar_ui
$message = false;
$conflicts=$this->bo->update($event,false, true, false, true, $message);
$this->update_client($event['id'],$d);
$response = egw_json_response::get();
if(!is_array($conflicts) && $conflicts)
{
if(is_int($conflicts))
{
$event['id'] = $conflicts;
}
if(!$event['recur_type'] && !$old_event['recur_type'])
{
// Directly update stored data. If event is still visible, it will
// be notified & update itself.
$this->to_client($event);
error_log(__METHOD__ . ':' . __LINE__ . ' updated calendar::'.$converted['id']);
$response->call('egw.dataStoreUID','calendar::'.$event['id'].($date?':'.$date:''),$event);
}
else
{
$response->call('egw.refresh', '','calendar',$event['id'],'edit');
$response->call('egw.refresh', '','calendar',$event['id'],'add');
}
}
else if ($conflicts)
@ -2879,6 +2862,7 @@ class calendar_uiforms extends calendar_ui
{
$response->call('egw.message', implode('<br />', $message));
}
if($event['id'] != $eventId ) $this->update_client($_eventId);
if ($status_reset_to_unknown)
{
foreach((array)$event['participants'] as $uid => $status)
@ -2931,8 +2915,7 @@ class calendar_uiforms extends calendar_ui
// Directly update stored data. If event is still visible, it will
// be notified & update itself.
$this->to_client($event);
egw_json_response::get()->call('egw.dataStoreUID','calendar::'.$event['id'].($date?':'.$date:''),$event);
$this->update_client($_eventId);
}
/**
@ -2940,8 +2923,8 @@ class calendar_uiforms extends calendar_ui
*/
public function ajax_delete($eventId)
{
list($eventId, $date) = explode(':',$eventId);
$event=$this->bo->read($eventId);
list($id, $date) = explode(':',$eventId);
$event=$this->bo->read($id);
$response = egw_json_response::get();
if ($this->bo->delete($event['id'], (int)$date))
@ -2958,7 +2941,7 @@ class calendar_uiforms extends calendar_ui
}
else
{
$response->apply('egw.message', lang('Error'),'error');
$response->apply('egw.message', Array(lang('Error')),'error');
}
}

View File

@ -274,14 +274,22 @@ app.classes.calendar = AppJS.extend(
if(event && event.data && event.data.date || _type === 'delete')
{
// Intelligent refresh without reloading everything
var recurrences = Object.keys(egw.dataSearchUIDs(new RegExp('^calendar::'+_id+':')))
var ids = event && event.data.recur_type && typeof _id === 'string' && _id.indexOf(':') < 0 || recurrences.length ?
recurrences :
['calendar::'+_id];
if(_type === 'delete')
{
egw.dataStoreUID('calendar::'+_id, null);
for(var i in ids)
{
egw.dataStoreUID(ids[i], null);
}
}
// Updates are handled by events themselves through egw.data
else if (_type !== 'update')
{
this._update_events(this.state, ['calendar::'+_id]);
this._update_events(this.state, ids);
}
return false;
}

View File

@ -796,15 +796,15 @@ var et2_calendar_event = et2_valueWidget.extend([et2_IDetachedDOM],
// Get the top level element - timegrid or so
var objectManager = this.getParent().getParent()._actionObject ||
egw_getAppObjectManager(true).getObjectById(this._parent._parent._parent.id) || egw_getAppObjectManager(true);
this._actionObject = objectManager.getObjectById('calendar::'+this.options.value.id);
this._actionObject = objectManager.getObjectById('calendar::'+this.options.value.row_id);
}
if (this._actionObject == null) {
// Add a new container to the object manager which will hold the widget
// objects
this._actionObject = objectManager.insertObject(false, new egwActionObject(
'calendar::'+this.options.value.id, objectManager, new et2_event_action_object_impl(this,this.getDOMNode()),
this._actionManager || objectManager.manager.getActionById(this.options.value.id) || objectManager.manager
'calendar::'+this.options.value.row_id, objectManager, new et2_event_action_object_impl(this,this.getDOMNode()),
this._actionManager || objectManager.manager.getActionById(this.options.value.row_id) || objectManager.manager
));
}
else