* Calendar: Push updates

This commit is contained in:
nathangray 2020-07-14 13:39:45 -06:00
parent 2c7b7052bc
commit dc6a8e0977
6 changed files with 201 additions and 8 deletions

View File

@ -118,6 +118,7 @@ class calendar_boupdate extends calendar_bo
*/ */
function update(&$event,$ignore_conflicts=false,$touch_modified=true,$ignore_acl=false,$updateTS=true,&$messages=null, $skip_notification=false) function update(&$event,$ignore_conflicts=false,$touch_modified=true,$ignore_acl=false,$updateTS=true,&$messages=null, $skip_notification=false)
{ {
$update_type = 'edit';
unset($updateTS); // ignored, as updating timestamps is required for sync! unset($updateTS); // ignored, as updating timestamps is required for sync!
//error_log(__METHOD__."(".array2string($event).",$ignore_conflicts,$touch_modified,$ignore_acl)"); //error_log(__METHOD__."(".array2string($event).",$ignore_conflicts,$touch_modified,$ignore_acl)");
if (!is_array($messages)) $messages = $messages ? (array)$messages : array(); if (!is_array($messages)) $messages = $messages ? (array)$messages : array();
@ -150,6 +151,7 @@ class calendar_boupdate extends calendar_bo
$status = calendar_so::combine_status($event['owner'] == $this->user ? 'A' : 'U', 1, 'CHAIR'); $status = calendar_so::combine_status($event['owner'] == $this->user ? 'A' : 'U', 1, 'CHAIR');
$event['participants'] = array($event['owner'] => $status); $event['participants'] = array($event['owner'] => $status);
} }
$update_type = 'add';
} }
// check if user has the permission to update / create the event // check if user has the permission to update / create the event
@ -273,6 +275,7 @@ class calendar_boupdate extends calendar_bo
{ {
// Restored, bring back links // Restored, bring back links
Link::restore('calendar', $cal_id); Link::restore('calendar', $cal_id);
$update_type = 'add';
} }
if ($this->log_file) if ($this->log_file)
{ {
@ -291,8 +294,8 @@ class calendar_boupdate extends calendar_bo
} }
} }
// notify the link-class about the update, as other apps may be subscribt to it // notify the link-class about the update, as other apps may be subscribed to it
Link::notify_update('calendar',$cal_id,$event); Link::notify_update('calendar',$cal_id,$event,$update_type);
return $cal_id; return $cal_id;
} }
@ -1777,6 +1780,9 @@ class calendar_boupdate extends calendar_bo
{ {
$this->set_status($old_event, $userid, $status, $recur_date, true, false,$skip_notification); $this->set_status($old_event, $userid, $status, $recur_date, true, false,$skip_notification);
} }
// notify the link-class about the update, as other apps may be subscribed to it
Link::notify_update('calendar',$new_event['id'],$new_event,"update");
} }
/** /**

View File

@ -68,6 +68,7 @@ class calendar_hooks
'merge' => true, 'merge' => true,
'entry' => 'Event', 'entry' => 'Event',
'entries' => 'Events', 'entries' => 'Events',
'push_data' => ['id','owner','participants','start','end']
); );
} }

View File

@ -617,6 +617,22 @@ class calendar_ui
$sidebox->exec('calendar.calendar_ui.sidebox_etemplate', $cont, $sel_options, $readonlys); $sidebox->exec('calendar.calendar_ui.sidebox_etemplate', $cont, $sel_options, $readonlys);
} }
/**
* Get the data for the given event IDs in a format suitable for the client.
*
* Used to get new data when Push tells us. Push doesn't have the full event data,
* just the minimum, so the client needs to ask for it.
*
* @param string[] $event_ids
*/
public function ajax_get($event_ids)
{
foreach($event_ids as $id)
{
$this->update_client($id);
}
}
/** /**
* Send updated event information to the client via ajax * Send updated event information to the client via ajax
* *

View File

@ -3200,9 +3200,8 @@ class calendar_uiforms extends calendar_ui
} }
} }
// Directly update stored data. If event is still visible, it will // notify the link-class about the update, as other apps may be subscribed to it
// be notified & update itself. Link::notify_update('calendar',$event['id'],$event,"update");
$this->update_client($eventId,$d);
} }
/** /**

View File

@ -119,7 +119,8 @@ var CalendarApp = /** @class */ (function (_super) {
egw.preference('defaultcalendar', 'calendar') || 'day', egw.preference('defaultcalendar', 'calendar') || 'day',
owner: egw.user('account_id'), owner: egw.user('account_id'),
keywords: '', keywords: '',
last: undefined last: undefined,
first: undefined
}; };
// If you are in one of these views and select a date in the sidebox, the view // If you are in one of these views and select a date in the sidebox, the view
// will change as needed to show the date. Other views will only change the // will change as needed to show the date. Other views will only change the
@ -513,6 +514,81 @@ var CalendarApp = /** @class */ (function (_super) {
return undefined; return undefined;
} }
}; };
/**
* Handle a push notification about entry changes from the websocket
*
* @param pushData
* @param {string} pushData.app application name
* @param {(string|number)} pushData.id id of entry to refresh or null
* @param {string} pushData.type either 'update', 'edit', 'delete', 'add' or null
* - update: request just modified data from given rows. Sorting is not considered,
* so if the sort field is changed, the row will not be moved.
* - edit: rows changed, but sorting may be affected. Requires full reload.
* - delete: just delete the given rows clientside (no server interaction neccessary)
* - add: ask server for data, add in intelligently
* @param {object|null} pushData.acl Extra data for determining relevance. eg: owner or responsible to decide if update is necessary
* @param {number} pushData.account_id User that caused the notification
*/
CalendarApp.prototype.push = function (pushData) {
// Calendar cares about calendar & infolog
if (pushData.app !== this.appname && pushData.app !== 'infolog')
return;
// pushData does not contain everything, just the minimum. See calendar_hooks::search_link().
var event = pushData.acl || {};
if (pushData.type === 'delete') {
return _super.prototype.push.call(this, pushData);
}
switch (pushData.app) {
case "calendar":
return this.push_calendar(pushData);
case "infolog":
return this.push_infolog(pushData);
}
};
/**
* Handle a push about infolog
*
* @param pushData
*/
CalendarApp.prototype.push_infolog = function (pushData) {
// This isn't the most intelligent, but it refreshes
this.observer("", pushData.app, pushData.id, pushData.type, "", null);
};
/**
* Handle a push from calendar
*
* @param pushData
*/
CalendarApp.prototype.push_calendar = function (pushData) {
// check visibility - grants is ID => permission of people we're allowed to see
var owners = [];
if (typeof this._grants === 'undefined') {
this._grants = egw.grants(this.appname);
}
// Filter what's allowed down to those we care about
var filtered = Object.keys(this._grants).filter(function (account) { return app.calendar.state.owner.indexOf(account) >= 0; });
// Check if we're interested in displaying by owner / participant
var owner_check = et2_widget_event_1.et2_calendar_event.owner_check(event, { options: { owner: filtered } });
if (!owner_check) {
// The owner is not in the list of what we're allowed / care about
return;
}
// Check if we're interested by date?
if (event.end <= new Date(this.state.first).valueOf() / 1000 || event.start > new Date(this.state.last).valueOf() / 1000) {
// The event is outside our current view
return;
}
// Ask for the real data
egw.json("calendar.calendar_ui.ajax_get", [[pushData.id]], function (data) {
var unknown = (typeof egw.dataGetUIDdata(data.uid) === "undefined");
// Store it, which will call all registered listeners
egw.dataStoreUID(data.uid, data.data);
// Any existing events were updated. Run this to catch new events or events moved into view
if (unknown) {
this._update_events(this.state, [data.uid]);
}
}.bind(this)).sendRequest(true);
};
/** /**
* Link hander for jDots template to just reload our iframe, instead of reloading whole admin app * Link hander for jDots template to just reload our iframe, instead of reloading whole admin app
* *

View File

@ -80,7 +80,8 @@ class CalendarApp extends EgwApp
egw.preference('defaultcalendar','calendar') || 'day', egw.preference('defaultcalendar','calendar') || 'day',
owner: egw.user('account_id'), owner: egw.user('account_id'),
keywords: '', keywords: '',
last: undefined last: undefined,
first: undefined
}; };
/** /**
@ -126,6 +127,8 @@ class CalendarApp extends EgwApp
// Data for quick add dialog // Data for quick add dialog
private quick_add: any; private quick_add: any;
//
private _grants : any;
/** /**
* Constructor * Constructor
* *
@ -438,6 +441,98 @@ class CalendarApp extends EgwApp
return undefined; return undefined;
} }
} }
/**
* Handle a push notification about entry changes from the websocket
*
* @param pushData
* @param {string} pushData.app application name
* @param {(string|number)} pushData.id id of entry to refresh or null
* @param {string} pushData.type either 'update', 'edit', 'delete', 'add' or null
* - update: request just modified data from given rows. Sorting is not considered,
* so if the sort field is changed, the row will not be moved.
* - edit: rows changed, but sorting may be affected. Requires full reload.
* - delete: just delete the given rows clientside (no server interaction neccessary)
* - add: ask server for data, add in intelligently
* @param {object|null} pushData.acl Extra data for determining relevance. eg: owner or responsible to decide if update is necessary
* @param {number} pushData.account_id User that caused the notification
*/
push(pushData)
{
// Calendar cares about calendar & infolog
if(pushData.app !== this.appname && pushData.app !== 'infolog') return;
// pushData does not contain everything, just the minimum. See calendar_hooks::search_link().
let event = pushData.acl || {};
if(pushData.type === 'delete')
{
return super.push(pushData);
}
switch (pushData.app)
{
case "calendar":
return this.push_calendar(pushData);
case "infolog":
return this.push_infolog(pushData);
}
}
/**
* Handle a push about infolog
*
* @param pushData
*/
private push_infolog(pushData)
{
// This isn't the most intelligent, but it refreshes
this.observer("", pushData.app, pushData.id, pushData.type,"",null);
}
/**
* Handle a push from calendar
*
* @param pushData
*/
private push_calendar(pushData)
{
// check visibility - grants is ID => permission of people we're allowed to see
let owners = [];
if(typeof this._grants === 'undefined')
{
this._grants = egw.grants(this.appname);
}
// Filter what's allowed down to those we care about
let filtered = Object.keys(this._grants).filter(account => app.calendar.state.owner.indexOf(account) >= 0);
// Check if we're interested in displaying by owner / participant
let owner_check = et2_calendar_event.owner_check(event, {options: {owner: filtered}});
if(!owner_check)
{
// The owner is not in the list of what we're allowed / care about
return;
}
// Check if we're interested by date?
if(event.end <= new Date(this.state.first).valueOf() /1000 || event.start > new Date(this.state.last).valueOf()/1000)
{
// The event is outside our current view
return;
}
// Ask for the real data
egw.json("calendar.calendar_ui.ajax_get", [[pushData.id]], function(data) {
let unknown = (typeof egw.dataGetUIDdata(data.uid) === "undefined");
// Store it, which will call all registered listeners
egw.dataStoreUID(data.uid, data.data);
// Any existing events were updated. Run this to catch new events or events moved into view
if(unknown)
{
this._update_events(this.state, [data.uid]);
}
}.bind(this)).sendRequest(true);
}
/** /**
* Link hander for jDots template to just reload our iframe, instead of reloading whole admin app * Link hander for jDots template to just reload our iframe, instead of reloading whole admin app