Add posibility to remove pending/declined invitation events (Bifrost#T203431)

This commit is contained in:
Aleksander Machniak 2019-04-29 10:39:08 +00:00
parent ff7f8f76d3
commit e648167c38
6 changed files with 145 additions and 90 deletions

View file

@ -979,8 +979,7 @@ class calendar extends rcube_plugin
case "remove":
// remove previous deletes
$undo_time = $this->driver->undelete ? $this->rc->config->get('undo_timeout', 0) : 0;
$this->rc->session->remove('calendar_event_undo');
// search for event if only UID is given
if (!isset($event['calendar']) && $event['uid']) {
if (!($event = $this->driver->get_event($event, calendar_driver::FILTER_WRITEABLE))) {
@ -989,11 +988,12 @@ class calendar extends rcube_plugin
$undo_time = 0;
}
// Note: the driver is responsible for setting $_SESSION['calendar_event_undo']
// containing 'ts' and 'data' elements
$success = $this->driver->remove_event($event, $undo_time < 1);
$reload = (!$success || $event['_savemode']) ? 2 : 1;
if ($undo_time > 0 && $success) {
$_SESSION['calendar_event_undo'] = array('ts' => time(), 'data' => $event);
// display message with Undo link.
$msg = html::span(null, $this->gettext('successremoval'))
. ' ' . html::a(array('onclick' => sprintf("%s.http_request('event', 'action=undo', %s.display_message('', 'loading'))",
@ -1047,16 +1047,14 @@ class calendar extends rcube_plugin
case "undo":
// Restore deleted event
$event = $_SESSION['calendar_event_undo']['data'];
if ($event)
if ($event = $_SESSION['calendar_event_undo']['data'])
$success = $this->driver->restore_event($event);
if ($success) {
$this->rc->session->remove('calendar_event_undo');
$this->rc->output->show_message('calendar.successrestore', 'confirmation');
$got_msg = true;
$reload = 2;
$reload = 2;
}
break;

View file

@ -527,7 +527,13 @@ function rcube_calendar_ui(settings)
}
}
var buttons = [];
var buttons = [], is_removable_event = function(event, calendar) {
// for invitation calendars check permissions of the original folder
if (event._folder_id)
calendar = me.calendars[event._folder_id];
return calendar && me.has_permission(calendar, 'td');
};
if (!temp && calendar.editable && event.editable !== false) {
buttons.push({
text: rcmail.gettext('edit', 'calendar'),
@ -537,7 +543,8 @@ function rcube_calendar_ui(settings)
}
});
}
if (!temp && me.has_permission(calendar, 'td') && event.editable !== false) {
if (!temp && is_removable_event(event, calendar) && event.editable !== false) {
buttons.push({
text: rcmail.gettext('delete', 'calendar'),
'class': 'delete',
@ -565,7 +572,7 @@ function rcube_calendar_ui(settings)
open: function() {
$dialog.attr('aria-hidden', 'false');
setTimeout(function(){
$dialog.parent().find('button:not(.ui-dialog-titlebar-close)').first().focus();
$dialog.parent().find('button:not(.ui-dialog-titlebar-close,.delete)').first().focus();
}, 5);
},
beforeClose: function(e) {
@ -2587,6 +2594,7 @@ function rcube_calendar_ui(settings)
if (!data) data = event;
var decline = false, notify = false, html = '', cal = me.calendars[event.calendar],
_is_invitation = String(event.calendar).match(/^--invitation--(declined|pending)/) && RegExp.$1,
_has_attendees = me.has_attendees(event),
_is_attendee = _has_attendees && me.is_attendee(event),
_is_organizer = me.is_organizer(event);
@ -2595,19 +2603,19 @@ function rcube_calendar_ui(settings)
if (_has_attendees) {
var checked = (settings.itip_notify & 1 ? ' checked="checked"' : '');
if (action == 'remove' && cal.group != 'shared' && !_is_organizer && _is_attendee) {
if (action == 'remove' && cal.group != 'shared' && !_is_organizer && _is_attendee && _is_invitation != 'declined') {
decline = true;
checked = event.status != 'CANCELLED' ? checked : '';
html += '<div class="message dialog-message ui alert boxwarning">' +
'<label><input class="confirm-attendees-decline" type="checkbox"' + checked + ' value="1" name="decline" />&nbsp;' +
rcmail.gettext('itipdeclineevent', 'calendar') +
'<label><input class="confirm-attendees-decline pretty-checkbox" type="checkbox"' + checked + ' value="1" name="decline" />&nbsp;' +
rcmail.gettext('itipdeclineevent', 'calendar') +
'</label></div>';
}
else if (_is_organizer) {
notify = true;
if (settings.itip_notify & 2) {
html += '<div class="message dialog-message ui alert boxwarning">' +
'<label><input class="confirm-attendees-donotify" type="checkbox"' + checked + ' value="1" name="notify" />&nbsp;' +
'<label><input class="confirm-attendees-donotify pretty-checkbox" type="checkbox"' + checked + ' value="1" name="notify" />&nbsp;' +
rcmail.gettext((action == 'remove' ? 'sendcancellation' : 'sendnotifications'), 'calendar') +
'</label></div>';
}
@ -2615,7 +2623,7 @@ function rcube_calendar_ui(settings)
data._notify = settings.itip_notify;
}
}
else if (cal.group != 'shared') {
else if (cal.group != 'shared' && !_is_invitation) {
html += '<div class="message dialog-message ui alert boxwarning">' + $('#edit-localchanges-warning').html() + '</div>';
data._notify = 0;
}
@ -3217,7 +3225,10 @@ function rcube_calendar_ui(settings)
function update_view(view, event, source) {
var existing = view.fullCalendar('clientEvents', event._id);
if (existing.length) {
delete existing[0].temp;
delete existing[0].editable;
$.extend(existing[0], event);
view.fullCalendar('updateEvent', existing[0]);
// remove old recurrence instances
if (event.recurrence && !event.recurrence_id)
@ -3254,16 +3265,13 @@ function rcube_calendar_ui(settings)
// add/update single event object
else if (source && p.update) {
var event = p.update;
event.temp = false;
event.editable = 0;
// update main view
event.editable = source.editable;
update_view(fc, event, source);
// update the currently displayed event dialog
if ($('#eventshow').is(':visible') && me.selected_event && me.selected_event.id == event.id)
event_show_dialog(event)
event_show_dialog(event);
}
// refetch all calendars
else if (p.refetch) {

View file

@ -406,10 +406,12 @@ class kolab_calendar extends kolab_storage_folder_api
}
/**
* Get number of events in the given calendar
*
* @param integer Date range start (unix timestamp)
* @param integer Date range end (unix timestamp)
* @param array Additional query to filter events
*
* @return integer Count
*/
public function count_events($start, $end = null, $filter_query = null)
@ -432,7 +434,7 @@ class kolab_calendar extends kolab_storage_folder_api
// query Kolab storage
$query[] = array('dtend', '>=', $start);
if ($end)
$query[] = array('dtstart', '<=', $end);
@ -455,7 +457,7 @@ class kolab_calendar extends kolab_storage_folder_api
* Create a new event record
*
* @see calendar_driver::new_event()
*
*
* @return mixed The created record ID on success, False on error
*/
public function insert_event($event)
@ -495,9 +497,9 @@ class kolab_calendar extends kolab_storage_folder_api
* Update a specific event record
*
* @see calendar_driver::new_event()
*
* @return boolean True on success, False on error
*/
public function update_event($event, $exception_id = null)
{
$updated = false;
@ -541,6 +543,7 @@ class kolab_calendar extends kolab_storage_folder_api
* Delete an event record
*
* @see calendar_driver::remove_event()
*
* @return boolean True on success, False on error
*/
public function delete_event($event, $force = true)
@ -549,9 +552,8 @@ class kolab_calendar extends kolab_storage_folder_api
if (!$deleted) {
rcube::raise_error(array(
'code' => 600, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => sprintf("Error deleting event object '%s' from Kolab server", $event['id'])),
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
'message' => sprintf("Error deleting event object '%s' from Kolab server", $event['id'])),
true, false);
}
@ -562,18 +564,21 @@ class kolab_calendar extends kolab_storage_folder_api
* Restore deleted event record
*
* @see calendar_driver::undelete_event()
*
* @return boolean True on success, False on error
*/
public function restore_event($event)
{
if ($this->storage->undelete($event['id'])) {
// Make sure this is not an instance identifier
$uid = preg_replace('/-\d{8}(T\d{6})?$/', '', $event['id']);
if ($this->storage->undelete($uid)) {
return true;
}
else {
rcube::raise_error(array(
'code' => 600, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Error undeleting the event object $event[id] from the Kolab server"),
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
'message' => sprintf("Error undeleting the event object '%s' from the Kolab server", $event['id'])),
true, false);
}
@ -613,7 +618,7 @@ class kolab_calendar extends kolab_storage_folder_api
{
$object = $event['_formatobj'];
if (!$object) {
$rec = $this->storage->get_object($event['id']);
$rec = $this->storage->get_object($event['uid'] ?: $event['id']);
$object = $rec['_formatobj'];
}

View file

@ -779,21 +779,26 @@ class kolab_driver extends calendar_driver
*/
public function remove_event($event, $force = true)
{
$ret = true;
$success = false;
$ret = true;
$success = false;
$savemode = $event['_savemode'];
$decline = $event['_decline'];
if (!$force) {
unset($event['attendees']);
$this->rc->session->remove('calendar_event_undo');
$this->rc->session->remove('calendar_restore_event_data');
$sess_data = $event;
}
if (($storage = $this->get_calendar($event['calendar'])) && ($event = $storage->get_event($event['id']))) {
$event['_savemode'] = $savemode;
$savemode = 'all';
$master = $event;
$this->rc->session->remove('calendar_restore_event_data');
$master = $event;
// read master if deleting a recurring event
if ($event['recurrence'] || $event['recurrence_id'] || $event['isexception']) {
$master = $storage->get_event($event['uid']);
$master = $storage->get_event($event['uid']);
$savemode = $event['_savemode'] ?: ($event['_instance'] || $event['isexception'] ? 'current' : 'all');
// force 'current' mode for single occurrences stored as exception
@ -821,23 +826,8 @@ class kolab_driver extends calendar_driver
case 'current':
$_SESSION['calendar_restore_event_data'] = $master;
// removing the first instance => just move to next occurence
if ($master['recurrence'] && $event['_instance'] == libcalendaring::recurrence_instance_identifier($master)) {
$recurring = reset($storage->get_recurring_events($event, $event['start'], null, $event['id'] . '-1', 1));
// no future instances found: delete the master event (bug #1677)
if (!$recurring['start']) {
$success = $storage->delete_event($master, $force);
break;
}
$master['start'] = $recurring['start'];
$master['end'] = $recurring['end'];
if ($master['recurrence']['COUNT'])
$master['recurrence']['COUNT']--;
}
// remove the matching RDATE entry
else if ($master['recurrence']['RDATE']) {
if ($master['recurrence']['RDATE']) {
foreach ($master['recurrence']['RDATE'] as $j => $rdate) {
if ($rdate->format('Ymd') == $event['start']->format('Ymd')) {
unset($master['recurrence']['RDATE'][$j]);
@ -845,9 +835,10 @@ class kolab_driver extends calendar_driver
}
}
}
else { // add exception to master event
$master['recurrence']['EXDATE'][] = $event['start'];
}
// add exception to master event
$master['recurrence']['EXDATE'][] = $event['start'];
$success = $storage->update_event($master);
break;
@ -855,7 +846,7 @@ class kolab_driver extends calendar_driver
$master['_instance'] = libcalendaring::recurrence_instance_identifier($master);
if ($master['_instance'] != $event['_instance']) {
$_SESSION['calendar_restore_event_data'] = $master;
// set until-date on master event
$master['recurrence']['UNTIL'] = clone $event['start'];
$master['recurrence']['UNTIL']->sub(new DateInterval('P1D'));
@ -885,10 +876,10 @@ class kolab_driver extends calendar_driver
if (!empty($event['recurrence_date']) && empty($master['recurrence']) && !empty($master['exceptions'])) {
// make the first exception the new master
$newmaster = array_shift($master['exceptions']);
$newmaster['exceptions'] = $master['exceptions'];
$newmaster['exceptions'] = $master['exceptions'];
$newmaster['_attachments'] = $master['_attachments'];
$newmaster['_mailbox'] = $master['_mailbox'];
$newmaster['_msguid'] = $master['_msguid'];
$newmaster['_mailbox'] = $master['_mailbox'];
$newmaster['_msguid'] = $master['_msguid'];
$success = $storage->update_event($newmaster);
}
@ -905,8 +896,18 @@ class kolab_driver extends calendar_driver
}
}
if ($success && !$force) {
if ($master['_folder_id'])
$sess_data['_folder_id'] = $master['_folder_id'];
$_SESSION['calendar_event_undo'] = array('ts' => time(), 'data' => $sess_data);
}
if ($success && $this->freebusy_trigger)
$this->rc->output->command('plugin.ping_url', array('action' => 'calendar/push-freebusy', 'source' => $storage->id));
$this->rc->output->command('plugin.ping_url', array(
'action' => 'calendar/push-freebusy',
// _folder_id may be set by invitations calendar
'source' => $master['_folder_id'] ?: $storage->id,
));
return $success ? $ret : false;
}
@ -915,20 +916,26 @@ class kolab_driver extends calendar_driver
* Restore a single deleted event
*
* @param array Hash array with event properties:
* id: Event identifier
* id: Event identifier
* calendar: Event calendar
*
* @return boolean True on success, False on error
*/
public function restore_event($event)
{
if ($storage = $this->get_calendar($event['calendar'])) {
if (!empty($_SESSION['calendar_restore_event_data']))
$success = $storage->update_event($_SESSION['calendar_restore_event_data']);
$success = $storage->update_event($event = $_SESSION['calendar_restore_event_data']);
else
$success = $storage->restore_event($event);
if ($success && $this->freebusy_trigger)
$this->rc->output->command('plugin.ping_url', array('action' => 'calendar/push-freebusy', 'source' => $storage->id));
$this->rc->output->command('plugin.ping_url', array(
'action' => 'calendar/push-freebusy',
// _folder_id may be set by invitations calendar
'source' => $event['_folder_id'] ?: $storage->id,
));
return $success;
}

View file

@ -176,16 +176,31 @@ class kolab_invitation_calendar
$event = $this->cal->driver->get_event($id, calendar_driver::FILTER_WRITEABLE);
if (is_array($event)) {
// add pointer to original calendar folder
$event['_folder_id'] = $event['calendar'];
$event = $this->_mod_event($event);
$event = $this->_mod_event($event, $event['calendar']);
}
return $event;
}
/**
* Create instances of a recurring event
*
* @see kolab_calendar::get_recurring_events()
*/
public function get_recurring_events($event, $start, $end = null, $event_id = null, $limit = null)
{
// forward call to the actual storage folder
if ($event['_folder_id']) {
$cal = $this->cal->driver->get_calendar($event['_folder_id']);
if ($cal && $cal->ready) {
return $cal->get_recurring_events($event, $start, $end, $event_id, $limit);
}
}
}
/**
* Get attachment body
*
* @see calendar_driver::get_attachment_body()
*/
public function get_attachment_body($id, $event)
@ -212,11 +227,12 @@ class kolab_invitation_calendar
}
/**
* @param integer Event's new start (unix timestamp)
* @param integer Event's new end (unix timestamp)
* @param string Search query (optional)
* @param boolean Include virtual events (optional)
* @param array Additional parameters to query storage
* @param integer Event's new start (unix timestamp)
* @param integer Event's new end (unix timestamp)
* @param string Search query (optional)
* @param boolean Include virtual events (optional)
* @param array Additional parameters to query storage
*
* @return array A list of event records
*/
public function list_events($start, $end, $search = null, $virtual = 1, $query = array())
@ -251,7 +267,7 @@ class kolab_invitation_calendar
}
if ($match) {
$events[$event['id'] ?: $event['uid']] = $this->_mod_event($event);
$events[$event['id'] ?: $event['uid']] = $this->_mod_event($event, $cal->id);
}
}
@ -263,12 +279,15 @@ class kolab_invitation_calendar
}
/**
* Get number of events in the given calendar
*
* @param integer Date range start (unix timestamp)
* @param integer Date range end (unix timestamp)
* @param array Additional query to filter events
*
* @param integer Date range start (unix timestamp)
* @param integer Date range end (unix timestamp)
* @return integer Count
*/
public function count_events($start, $end = null)
public function count_events($start, $end = null, $filter = null)
{
// get email addresses of the current user
$user_emails = $this->cal->get_user_emails();
@ -309,7 +328,7 @@ class kolab_invitation_calendar
/**
* Helper method to modify some event properties
*/
private function _mod_event($event)
private function _mod_event($event, $calendar_id = null)
{
// set classes according to PARTSTAT
$event = kolab_driver::add_partstat_class($event, $this->partstats);
@ -318,15 +337,18 @@ class kolab_invitation_calendar
$event['calendar'] = $this->id;
}
// add pointer to original calendar folder
if ($calendar_id) {
$event['_folder_id'] = $calendar_id;
}
return $event;
}
/**
* Create a new event record
*
* @see calendar_driver::new_event()
*
* @return mixed The created record ID on success, False on error
* @see kolab_calendar::insert_event()
*/
public function insert_event($event)
{
@ -336,8 +358,7 @@ class kolab_invitation_calendar
/**
* Update a specific event record
*
* @see calendar_driver::new_event()
* @return boolean True on success, False on error
* @see kolab_calendar::update_event()
*/
public function update_event($event, $exception_id = null)
{
@ -355,22 +376,36 @@ class kolab_invitation_calendar
/**
* Delete an event record
*
* @see calendar_driver::remove_event()
* @return boolean True on success, False on error
* @see kolab_calendar::delete_event()
*/
public function delete_event($event, $force = true)
{
// forward call to the actual storage folder
if ($event['_folder_id']) {
$cal = $this->cal->driver->get_calendar($event['_folder_id']);
if ($cal && $cal->ready) {
return $cal->delete_event($event, $force);
}
}
return false;
}
/**
* Restore deleted event record
*
* @see calendar_driver::undelete_event()
* @return boolean True on success, False on error
* @see kolab_calendar::restore_event()
*/
public function restore_event($event)
{
// forward call to the actual storage folder
if ($event['_folder_id']) {
$cal = $this->cal->driver->get_calendar($event['_folder_id']);
if ($cal && $cal->ready) {
return $cal->restore_event($event);
}
}
return false;
}
}

View file

@ -260,10 +260,12 @@ class kolab_user_calendar extends kolab_calendar
}
/**
* Get number of events in the given calendar
*
* @param integer Date range start (unix timestamp)
* @param integer Date range end (unix timestamp)
* @param array Additional query to filter events
*
* @param integer Date range start (unix timestamp)
* @param integer Date range end (unix timestamp)
* @param array Additional query to filter events
* @return integer Count
*/
public function count_events($start, $end = null, $filter_query = null)