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); $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 * 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 * @param type $event
*/ */

View File

@ -1013,14 +1013,7 @@ class calendar_uiforms extends calendar_ui
$response = egw_json_response::get(); $response = egw_json_response::get();
if($response && !$content['id'] && $event['id']) if($response && !$content['id'] && $event['id'])
{ {
// Directly update stored data. $this->update_client($event['id']);
// 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);
} }
if (in_array($button,array('cancel','save','delete','delete_exceptions','delete_keep_exceptions')) && $noerror) 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; $message = false;
$conflicts=$this->bo->update($event,false, true, false, true, $message); $conflicts=$this->bo->update($event,false, true, false, true, $message);
$this->update_client($event['id'],$d);
$response = egw_json_response::get(); $response = egw_json_response::get();
if(!is_array($conflicts) && $conflicts) if(!is_array($conflicts) && $conflicts)
{ {
if(is_int($conflicts)) if(is_int($conflicts))
{ {
$event['id'] = $conflicts; $event['id'] = $conflicts;
} $response->call('egw.refresh', '','calendar',$event['id'],'add');
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');
} }
} }
else if ($conflicts) else if ($conflicts)
@ -2879,6 +2862,7 @@ class calendar_uiforms extends calendar_ui
{ {
$response->call('egw.message', implode('<br />', $message)); $response->call('egw.message', implode('<br />', $message));
} }
if($event['id'] != $eventId ) $this->update_client($_eventId);
if ($status_reset_to_unknown) if ($status_reset_to_unknown)
{ {
foreach((array)$event['participants'] as $uid => $status) 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 // Directly update stored data. If event is still visible, it will
// be notified & update itself. // be notified & update itself.
$this->to_client($event); $this->update_client($_eventId);
egw_json_response::get()->call('egw.dataStoreUID','calendar::'.$event['id'].($date?':'.$date:''),$event);
} }
/** /**
@ -2940,8 +2923,8 @@ class calendar_uiforms extends calendar_ui
*/ */
public function ajax_delete($eventId) public function ajax_delete($eventId)
{ {
list($eventId, $date) = explode(':',$eventId); list($id, $date) = explode(':',$eventId);
$event=$this->bo->read($eventId); $event=$this->bo->read($id);
$response = egw_json_response::get(); $response = egw_json_response::get();
if ($this->bo->delete($event['id'], (int)$date)) if ($this->bo->delete($event['id'], (int)$date))
@ -2958,7 +2941,7 @@ class calendar_uiforms extends calendar_ui
} }
else 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') if(event && event.data && event.data.date || _type === 'delete')
{ {
// Intelligent refresh without reloading everything // 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') 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 // Updates are handled by events themselves through egw.data
else if (_type !== 'update') else if (_type !== 'update')
{ {
this._update_events(this.state, ['calendar::'+_id]); this._update_events(this.state, ids);
} }
return false; 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 // Get the top level element - timegrid or so
var objectManager = this.getParent().getParent()._actionObject || var objectManager = this.getParent().getParent()._actionObject ||
egw_getAppObjectManager(true).getObjectById(this._parent._parent._parent.id) || egw_getAppObjectManager(true); 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) { if (this._actionObject == null) {
// Add a new container to the object manager which will hold the widget // Add a new container to the object manager which will hold the widget
// objects // objects
this._actionObject = objectManager.insertObject(false, new egwActionObject( this._actionObject = objectManager.insertObject(false, new egwActionObject(
'calendar::'+this.options.value.id, objectManager, new et2_event_action_object_impl(this,this.getDOMNode()), 'calendar::'+this.options.value.row_id, objectManager, new et2_event_action_object_impl(this,this.getDOMNode()),
this._actionManager || objectManager.manager.getActionById(this.options.value.id) || objectManager.manager this._actionManager || objectManager.manager.getActionById(this.options.value.row_id) || objectManager.manager
)); ));
} }
else else