Calendar: Fix some cases where a changed participant status would not remove the event when it should

This commit is contained in:
nathangray 2020-03-12 09:18:30 -06:00
parent cd93c942fa
commit cdbfcb391c
3 changed files with 221 additions and 36 deletions

View File

@ -11,10 +11,10 @@
*/ */
use EGroupware\Api; use EGroupware\Api;
use EGroupware\Api\Framework;
use EGroupware\Api\Egw;
use EGroupware\Api\Acl; use EGroupware\Api\Acl;
use EGroupware\Api\Egw;
use EGroupware\Api\Etemplate; use EGroupware\Api\Etemplate;
use EGroupware\Api\Framework;
/** /**
* Shared base-class of all calendar UserInterface classes * Shared base-class of all calendar UserInterface classes
@ -667,7 +667,12 @@ class calendar_ui
if(!$event || !$filter_match) if(!$event || !$filter_match)
{ {
// Sending null will trigger a removal // Sending null will trigger a removal
$response->generic('data', array('uid' => 'calendar::'.$event_id, 'data' => null)); $uid = 'calendar::' . $event_id;
if ($recurrence_date)
{
$uid .= ':' . $recurrence_date->getTimestamp();
}
$response->generic('data', array('uid' => $uid, 'data' => null));
return false; return false;
} }

View File

@ -175,16 +175,16 @@ var et2_calendar_event = /** @class */ (function (_super) {
this._values_check(value); this._values_check(value);
} }
// Check for changing days in the grid view // Check for changing days in the grid view
if (!this._sameday_check(value)) { if (!this._sameday_check(value) || !this._status_check(value, app.calendar.getState().status_filter, parent.options.owner)) {
// May need to update parent to remove out-of-view events // May need to update parent to remove out-of-view events
parent.removeChild(this); parent.removeChild(this);
if (event === null && parent && parent.instanceOf(et2_widget_daycol_1.et2_calendar_daycol)) { if (event === null && parent && parent.instanceOf(et2_widget_daycol_1.et2_calendar_daycol)) {
parent._out_of_view(); parent._out_of_view();
} }
// This should now cease to exist, as new events have been created // This should now cease to exist, as new events have been created
this.destroy(); this.destroy();
return; return;
} }
// Copy to avoid changes, which may cause nm problems // Copy to avoid changes, which may cause nm problems
this.options.value = jQuery.extend({}, value); this.options.value = jQuery.extend({}, value);
if (this.getParent().options.date) { if (this.getParent().options.date) {
@ -662,26 +662,107 @@ var et2_calendar_event = /** @class */ (function (_super) {
if (new_cache_id != old_cache_id) { if (new_cache_id != old_cache_id) {
var old_daywise = egw.dataGetUIDdata(old_cache_id); var old_daywise = egw.dataGetUIDdata(old_cache_id);
old_daywise = old_daywise && old_daywise.data ? old_daywise.data : []; old_daywise = old_daywise && old_daywise.data ? old_daywise.data : [];
old_daywise.splice(old_daywise.indexOf(this.options.value.row_id), 1); old_daywise.splice(old_daywise.indexOf(this.options.value.row_id), 1);
egw.dataStoreUID(old_cache_id, old_daywise); egw.dataStoreUID(old_cache_id, old_daywise);
if (new_daywise.indexOf(event.row_id) < 0) { if (new_daywise.indexOf(event.row_id) < 0) {
new_daywise.push(event.row_id); new_daywise.push(event.row_id);
} }
if (egw.dataHasUID(new_cache_id)) { if (egw.dataHasUID(new_cache_id)) {
egw.dataStoreUID(new_cache_id, new_daywise); egw.dataStoreUID(new_cache_id, new_daywise);
} }
} }
return false; return false;
}; };
et2_calendar_event.prototype.attachToDOM = function () { /**
var result = _super.prototype.attachToDOM.call(this); * Check that the event passes the given status filter.
// Remove the binding for the click handler, unless there's something * Status filter is set in the sidebox and used when fetching several events, but if user changes their status
// custom here. * for an event, it may no longer match and have to be removed.
if (!this.onclick) { *
jQuery(this.node).off("click"); * @param event
} * @param filter
return result; * @private
}; */
et2_calendar_event.prototype._status_check = function (event, filter, owner) {
if (!owner || !event) {
return false;
}
// If we're doing a bunch, just one passing is enough
if (typeof owner !== "string") {
var pass = false;
for (var j = 0; j < owner.length && pass == false; j++) {
pass = pass || this._status_check(event, filter, owner[j]);
}
return pass;
}
// Show also events just owned by selected user
if (filter == 'owner') {
return owner == event.owner;
}
// Get the relevant participant
var participant = event.participants[owner];
// If filter says don't look in groups, skip it all
if (!participant && filter === 'no-enum-groups') {
return false;
}
// Couldn't find the current owner in the participant list, check groups & resources
if (!participant) {
var options = null;
if (app.calendar && app.calendar.sidebox_et2 && app.calendar.sidebox_et2.getWidgetById('owner')) {
options = app.calendar.sidebox_et2.getWidgetById('owner').taglist.getSelection();
}
if ((isNaN(parseInt(owner)) || parseInt(owner) < 0) && options && typeof options.find == "function") {
var resource = options.find(function (element) {
return element.id == owner;
}) || {};
if (resource && resource.resources) {
var matching_participant = resource.resources.filter(function (id) {
return typeof event.participants[id] != "undefined";
});
return this._status_check(event, filter, matching_participant);
}
}
}
var status = et2_calendar_event.split_status(participant);
switch (filter) {
default:
case 'all':
return true;
case 'default': // Show all status, but rejected
return status !== 'R';
case 'accepted': //Show only accepted events
return status === 'A';
case 'unknown': // Show only invitations, not yet accepted or rejected
return status === 'U';
case 'tentative': // Show only tentative accepted events
return status === 'T';
case 'delegated': // Show only delegated events
return status === 'D';
case 'rejected': // Show only rejected events
return status === 'R';
// Handled above
//case 'owner': // Show also events just owned by selected user
case 'hideprivate': // Show all events, as if they were private
// handled server-side
return true;
case 'showonlypublic': // Show only events flagged as public, -not checked as private
return event.public == '1';
// Handled above
// case 'no-enum-groups': // Do not include events of group members
case 'not-unknown': // Show all status, but unknown
return status !== 'U';
case 'deleted': // Show events that have been deleted
return event.deleted;
}
};
et2_calendar_event.prototype.attachToDOM = function () {
var result = _super.prototype.attachToDOM.call(this);
// Remove the binding for the click handler, unless there's something
// custom here.
if (!this.onclick) {
jQuery(this.node).off("click");
}
return result;
};
/** /**
* Click handler calling custom handler set via onclick attribute to this.onclick. * Click handler calling custom handler set via onclick attribute to this.onclick.
* All other handling is done by the timegrid widget. * All other handling is done by the timegrid widget.

View File

@ -218,7 +218,7 @@ export class et2_calendar_event extends et2_valueWidget implements et2_IDetached
} }
// Check for changing days in the grid view // Check for changing days in the grid view
if(!this._sameday_check(value)) if(!this._sameday_check(value) || !this._status_check(value, app.calendar.getState().status_filter, parent.options.owner))
{ {
// May need to update parent to remove out-of-view events // May need to update parent to remove out-of-view events
parent.removeChild(this); parent.removeChild(this);
@ -862,20 +862,119 @@ export class et2_calendar_event extends et2_valueWidget implements et2_IDetached
} }
if(egw.dataHasUID(new_cache_id)) if(egw.dataHasUID(new_cache_id))
{ {
egw.dataStoreUID(new_cache_id,new_daywise); egw.dataStoreUID(new_cache_id, new_daywise);
} }
} }
return false; return false;
} }
/**
* Check that the event passes the given status filter.
* Status filter is set in the sidebox and used when fetching several events, but if user changes their status
* for an event, it may no longer match and have to be removed.
*
* @param event
* @param filter
* @private
*/
_status_check(event, filter: string, owner: string | string[]): boolean
{
if(!owner || !event)
{
return false;
}
// If we're doing a bunch, just one passing is enough
if(typeof owner !== "string")
{
let pass = false;
for (let j = 0; j < owner.length && pass == false; j++)
{
pass = pass || this._status_check(event, filter, owner[j]);
}
return pass;
}
// Show also events just owned by selected user
if(filter == 'owner')
{
return owner == event.owner;
}
// Get the relevant participant
let participant = event.participants[owner];
// If filter says don't look in groups, skip it all
if(!participant && filter === 'no-enum-groups')
{
return false;
}
// Couldn't find the current owner in the participant list, check groups & resources
if(!participant)
{
let options: any = null;
if(app.calendar && app.calendar.sidebox_et2 && app.calendar.sidebox_et2.getWidgetById('owner'))
{
options = app.calendar.sidebox_et2.getWidgetById('owner').taglist.getSelection();
}
if((isNaN(parseInt(owner)) || parseInt(owner) < 0) && options && typeof options.find == "function")
{
let resource = options.find(function (element)
{
return element.id == owner;
}) || {};
if(resource && resource.resources)
{
let matching_participant = resource.resources.filter(id => typeof event.participants[id] != "undefined");
return this._status_check(event, filter, matching_participant);
}
}
}
let status = et2_calendar_event.split_status(participant);
switch (filter)
{
default:
case 'all':
return true;
case 'default': // Show all status, but rejected
return status !== 'R';
case 'accepted': //Show only accepted events
return status === 'A'
case 'unknown': // Show only invitations, not yet accepted or rejected
return status === 'U';
case 'tentative': // Show only tentative accepted events
return status === 'T';
case 'delegated': // Show only delegated events
return status === 'D';
case 'rejected': // Show only rejected events
return status === 'R';
// Handled above
//case 'owner': // Show also events just owned by selected user
case 'hideprivate': // Show all events, as if they were private
// handled server-side
return true;
case 'showonlypublic': // Show only events flagged as public, -not checked as private
return event.public == '1';
// Handled above
// case 'no-enum-groups': // Do not include events of group members
case 'not-unknown': // Show all status, but unknown
return status !== 'U';
case 'deleted': // Show events that have been deleted
return event.deleted;
}
}
attachToDOM() attachToDOM()
{ {
let result = super.attachToDOM(); let result = super.attachToDOM();
// Remove the binding for the click handler, unless there's something // Remove the binding for the click handler, unless there's something
// custom here. // custom here.
if (!this.onclick) if(!this.onclick)
{ {
jQuery(this.node).off("click"); jQuery(this.node).off("click");
} }