Modify calendar UI to properly handle updates on recurring events with attandees (#4318)
Since the Kolab stack doesn't yet fully support invitations for recurring events, the calendar client prevents the user from modifying single recurrence instances if attandees are involved: options to update the "current" or "future" items are disabled and deleting a single event will update the main event and notify all attendees.
This commit is contained in:
parent
7fe08b2814
commit
9d3a665d9c
5 changed files with 80 additions and 24 deletions
|
@ -842,7 +842,7 @@ class calendar extends rcube_plugin
|
||||||
$success = $reload = $got_msg = false;
|
$success = $reload = $got_msg = false;
|
||||||
|
|
||||||
// don't notify if modifying a recurring instance (really?)
|
// don't notify if modifying a recurring instance (really?)
|
||||||
if ($event['_savemode'] && $event['_savemode'] != 'all' && $event['_notify'])
|
if ($event['_savemode'] && in_array($event['_savemode'], array('current','future')) && $event['_notify'] && $action != 'remove')
|
||||||
unset($event['_notify']);
|
unset($event['_notify']);
|
||||||
// force notify if hidden + active
|
// force notify if hidden + active
|
||||||
else if ((int)$this->rc->config->get('calendar_itip_send_option', $this->defaults['calendar_itip_send_option']) === 1)
|
else if ((int)$this->rc->config->get('calendar_itip_send_option', $this->defaults['calendar_itip_send_option']) === 1)
|
||||||
|
@ -866,20 +866,35 @@ class calendar extends rcube_plugin
|
||||||
|
|
||||||
case "edit":
|
case "edit":
|
||||||
$this->write_preprocess($event, $action);
|
$this->write_preprocess($event, $action);
|
||||||
if ($success = $this->driver->edit_event($event))
|
if ($success = $this->driver->edit_event($event)) {
|
||||||
$this->cleanup_event($event);
|
$this->cleanup_event($event);
|
||||||
|
if ($success !== true) {
|
||||||
|
$event['id'] = $success;
|
||||||
|
$old = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
$reload = $success && ($event['recurrence'] || $event['_savemode'] || $event['_fromcalendar']) ? 2 : 1;
|
$reload = $success && ($event['recurrence'] || $event['_savemode'] || $event['_fromcalendar']) ? 2 : 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "resize":
|
case "resize":
|
||||||
$this->write_preprocess($event, $action);
|
$this->write_preprocess($event, $action);
|
||||||
$success = $this->driver->resize_event($event);
|
if ($success = $this->driver->resize_event($event)) {
|
||||||
|
if ($success !== true) {
|
||||||
|
$event['id'] = $success;
|
||||||
|
$old = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
$reload = $event['_savemode'] ? 2 : 1;
|
$reload = $event['_savemode'] ? 2 : 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "move":
|
case "move":
|
||||||
$this->write_preprocess($event, $action);
|
$this->write_preprocess($event, $action);
|
||||||
$success = $this->driver->move_event($event);
|
if ($success = $this->driver->move_event($event)) {
|
||||||
|
if ($success !== true) {
|
||||||
|
$event['id'] = $success;
|
||||||
|
$old = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
$reload = $success && $event['_savemode'] ? 2 : 1;
|
$reload = $success && $event['_savemode'] ? 2 : 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1110,6 +1125,12 @@ class calendar extends rcube_plugin
|
||||||
// make sure we have the complete record
|
// make sure we have the complete record
|
||||||
$event = $action == 'remove' ? $old : $this->driver->get_event($event);
|
$event = $action == 'remove' ? $old : $this->driver->get_event($event);
|
||||||
|
|
||||||
|
// sending notification on a recurrence instance -> re-send the main event
|
||||||
|
if ($event['recurrence_id']) {
|
||||||
|
$event = $this->driver->get_event(array('id' => $event['recurrence_id'], 'cal' => $event['calendar']));
|
||||||
|
$action = 'edit';
|
||||||
|
}
|
||||||
|
|
||||||
// only notify if data really changed (TODO: do diff check on client already)
|
// only notify if data really changed (TODO: do diff check on client already)
|
||||||
if (!$old || $action == 'remove' || self::event_diff($event, $old)) {
|
if (!$old || $action == 'remove' || self::event_diff($event, $old)) {
|
||||||
$sent = $this->notify_attendees($event, $old, $action, $event['_comment']);
|
$sent = $this->notify_attendees($event, $old, $action, $event['_comment']);
|
||||||
|
|
|
@ -742,9 +742,11 @@ function rcube_calendar_ui(settings)
|
||||||
|
|
||||||
// show warning if editing a recurring event
|
// show warning if editing a recurring event
|
||||||
if (event.id && event.recurrence) {
|
if (event.id && event.recurrence) {
|
||||||
var sel = event.thisandfuture ? 'future' : (event.isexception ? 'current' : 'all');
|
var allow_exceptions = !has_attendees(event) || !is_organizer(event),
|
||||||
|
sel = event._savemode || (allow_exceptions && event.thisandfuture ? 'future' : (allow_exceptions && event.isexception ? 'current' : 'all'));
|
||||||
$('#edit-recurring-warning').show();
|
$('#edit-recurring-warning').show();
|
||||||
$('input.edit-recurring-savemode[value="'+sel+'"]').prop('checked', true);
|
$('input.edit-recurring-savemode[value="'+sel+'"]').prop('checked', true);
|
||||||
|
$('input.edit-recurring-savemode[value="current"], input.edit-recurring-savemode[value="future"]').prop('disabled', !allow_exceptions);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
$('#edit-recurring-warning').hide();
|
$('#edit-recurring-warning').hide();
|
||||||
|
@ -764,6 +766,11 @@ function rcube_calendar_ui(settings)
|
||||||
if (event.attendees) {
|
if (event.attendees) {
|
||||||
for (j=0; j < event.attendees.length; j++) {
|
for (j=0; j < event.attendees.length; j++) {
|
||||||
data = event.attendees[j];
|
data = event.attendees[j];
|
||||||
|
// reset attendee status
|
||||||
|
if (event._savemode == 'new' && data.role != 'ORGANIZER') {
|
||||||
|
data.status = 'NEEDS-ACTION';
|
||||||
|
delete data.noreply;
|
||||||
|
}
|
||||||
add_attendee(data, !allow_invitations);
|
add_attendee(data, !allow_invitations);
|
||||||
if (allow_invitations && data.role != 'ORGANIZER' && !data.noreply)
|
if (allow_invitations && data.role != 'ORGANIZER' && !data.noreply)
|
||||||
reply_selected++;
|
reply_selected++;
|
||||||
|
@ -2519,12 +2526,13 @@ function rcube_calendar_ui(settings)
|
||||||
var update_event_confirm = function(action, event, data)
|
var update_event_confirm = function(action, event, data)
|
||||||
{
|
{
|
||||||
if (!data) data = event;
|
if (!data) data = event;
|
||||||
var decline = false, notify = false, html = '', cal = me.calendars[event.calendar];
|
var decline = false, notify = false, html = '', cal = me.calendars[event.calendar],
|
||||||
|
_has_attendees = has_attendees(event), _is_organizer = is_organizer(event);
|
||||||
|
|
||||||
// event has attendees, ask whether to notify them
|
// event has attendees, ask whether to notify them
|
||||||
if (has_attendees(event)) {
|
if (_has_attendees) {
|
||||||
var checked = (settings.itip_notify & 1 ? ' checked="checked"' : '');
|
var checked = (settings.itip_notify & 1 ? ' checked="checked"' : '');
|
||||||
if (is_organizer(event)) {
|
if (_is_organizer) {
|
||||||
notify = true;
|
notify = true;
|
||||||
if (settings.itip_notify & 2) {
|
if (settings.itip_notify & 2) {
|
||||||
html += '<div class="message">' +
|
html += '<div class="message">' +
|
||||||
|
@ -2550,11 +2558,25 @@ function rcube_calendar_ui(settings)
|
||||||
|
|
||||||
// recurring event: user needs to select the savemode
|
// recurring event: user needs to select the savemode
|
||||||
if (event.recurrence) {
|
if (event.recurrence) {
|
||||||
|
var disabled_state = '', message_label = (action == 'remove' ? 'removerecurringeventwarning' : 'changerecurringeventwarning');
|
||||||
|
|
||||||
|
if (_has_attendees) {
|
||||||
|
if (action == 'remove') {
|
||||||
|
if (!_is_organizer) {
|
||||||
|
message_label = 'removerecurringallonly';
|
||||||
|
disabled_state = ' disabled';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (is_organizer(event)) {
|
||||||
|
disabled_state = ' disabled';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
html += '<div class="message"><span class="ui-icon ui-icon-alert"></span>' +
|
html += '<div class="message"><span class="ui-icon ui-icon-alert"></span>' +
|
||||||
rcmail.gettext((action == 'remove' ? 'removerecurringeventwarning' : 'changerecurringeventwarning'), 'calendar') + '</div>' +
|
rcmail.gettext(message_label, 'calendar') + '</div>' +
|
||||||
'<div class="savemode">' +
|
'<div class="savemode">' +
|
||||||
'<a href="#current" class="button">' + rcmail.gettext('currentevent', 'calendar') + '</a>' +
|
'<a href="#current" class="button' + disabled_state + '">' + rcmail.gettext('currentevent', 'calendar') + '</a>' +
|
||||||
'<a href="#future" class="button">' + rcmail.gettext('futurevents', 'calendar') + '</a>' +
|
'<a href="#future" class="button' + disabled_state + '">' + rcmail.gettext('futurevents', 'calendar') + '</a>' +
|
||||||
'<a href="#all" class="button">' + rcmail.gettext('allevents', 'calendar') + '</a>' +
|
'<a href="#all" class="button">' + rcmail.gettext('allevents', 'calendar') + '</a>' +
|
||||||
(action != 'remove' ? '<a href="#new" class="button">' + rcmail.gettext('saveasnew', 'calendar') + '</a>' : '') +
|
(action != 'remove' ? '<a href="#new" class="button">' + rcmail.gettext('saveasnew', 'calendar') + '</a>' : '') +
|
||||||
'</div>';
|
'</div>';
|
||||||
|
@ -2564,14 +2586,24 @@ function rcube_calendar_ui(settings)
|
||||||
if (html) {
|
if (html) {
|
||||||
var $dialog = $('<div>').html(html);
|
var $dialog = $('<div>').html(html);
|
||||||
|
|
||||||
$dialog.find('a.button').button().click(function(e) {
|
$dialog.find('a.button').button().filter(':not(.disabled)').click(function(e) {
|
||||||
data._savemode = String(this.href).replace(/.+#/, '');
|
data._savemode = String(this.href).replace(/.+#/, '');
|
||||||
data._notify = settings.itip_notify;
|
data._notify = settings.itip_notify;
|
||||||
if ($dialog.find('input.confirm-attendees-donotify').length)
|
|
||||||
data._notify = $dialog.find('input.confirm-attendees-donotify').get(0).checked ? 1 : 0;
|
// open event edit dialog when saving as new
|
||||||
if (decline && $dialog.find('input.confirm-attendees-decline:checked').length)
|
if (data._savemode == 'new') {
|
||||||
data.decline = 1;
|
event._savemode = 'new';
|
||||||
update_event(action, data);
|
event_edit_dialog('edit', event);
|
||||||
|
fc.fullCalendar('refetchEvents');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ($dialog.find('input.confirm-attendees-donotify').length)
|
||||||
|
data._notify = $dialog.find('input.confirm-attendees-donotify').get(0).checked ? 1 : 0;
|
||||||
|
if (decline && $dialog.find('input.confirm-attendees-decline:checked').length)
|
||||||
|
data.decline = 1;
|
||||||
|
update_event(action, data);
|
||||||
|
}
|
||||||
|
|
||||||
$dialog.dialog("close");
|
$dialog.dialog("close");
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
|
@ -889,15 +889,16 @@ class kolab_driver extends calendar_driver
|
||||||
// save submitted data as new (non-recurring) event
|
// save submitted data as new (non-recurring) event
|
||||||
$event['recurrence'] = array();
|
$event['recurrence'] = array();
|
||||||
$event['uid'] = $this->cal->generate_uid();
|
$event['uid'] = $this->cal->generate_uid();
|
||||||
unset($event['recurrence_id'], $event['id']);
|
unset($event['recurrence_id'], $event['id'], $event['_savemode'], $event['_fromcalendar'], $event['_identity'], $event['_notify']);
|
||||||
|
|
||||||
// copy attachment data to new event
|
// copy attachment data to new event
|
||||||
foreach ((array)$event['attachments'] as $idx => $attachment) {
|
foreach ((array)$event['attachments'] as $idx => $attachment) {
|
||||||
if (!$attachment['data'])
|
if (!$attachment['content'])
|
||||||
$attachment['data'] = $fromcalendar->get_attachment_body($attachment['id'], $event);
|
$event['attachments'][$idx]['content'] = $this->get_attachment_body($attachment['id'], $master);
|
||||||
}
|
}
|
||||||
|
|
||||||
$success = $storage->insert_event($event);
|
if ($success = $storage->insert_event($event))
|
||||||
|
$success = $event['uid'];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'future':
|
case 'future':
|
||||||
|
@ -907,7 +908,7 @@ class kolab_driver extends calendar_driver
|
||||||
$event['thisandfuture'] = $savemode == 'future';
|
$event['thisandfuture'] = $savemode == 'future';
|
||||||
|
|
||||||
// remove some internal properties which should not be saved
|
// remove some internal properties which should not be saved
|
||||||
unset($event['_savemode'], $event['_fromcalendar'], $event['_identity']);
|
unset($event['_savemode'], $event['_fromcalendar'], $event['_identity'], $event['_notify']);
|
||||||
|
|
||||||
// save properties to a recurrence exception instance
|
// save properties to a recurrence exception instance
|
||||||
if ($old['recurrence_id']) {
|
if ($old['recurrence_id']) {
|
||||||
|
|
|
@ -261,6 +261,7 @@ $labels['changeeventconfirm'] = 'Change event';
|
||||||
$labels['removeeventconfirm'] = 'Delete event';
|
$labels['removeeventconfirm'] = 'Delete event';
|
||||||
$labels['changerecurringeventwarning'] = 'This is a recurring event. Would you like to edit the current event only, this and all future occurences, all occurences or save it as a new event?';
|
$labels['changerecurringeventwarning'] = 'This is a recurring event. Would you like to edit the current event only, this and all future occurences, all occurences or save it as a new event?';
|
||||||
$labels['removerecurringeventwarning'] = 'This is a recurring event. Would you like to delete the current event only, this and all future occurences or all occurences of this event?';
|
$labels['removerecurringeventwarning'] = 'This is a recurring event. Would you like to delete the current event only, this and all future occurences or all occurences of this event?';
|
||||||
|
$labels['removerecurringallonly'] = 'This is a recurring event. As a participant, you can only delete the entire event with all occurences.';
|
||||||
$labels['currentevent'] = 'Current';
|
$labels['currentevent'] = 'Current';
|
||||||
$labels['futurevents'] = 'Future';
|
$labels['futurevents'] = 'Future';
|
||||||
$labels['allevents'] = 'All';
|
$labels['allevents'] = 'All';
|
||||||
|
|
|
@ -1056,6 +1056,7 @@ td.topalign {
|
||||||
.event-update-confirm a.button {
|
.event-update-confirm a.button {
|
||||||
margin: 0 0.5em 0 0.2em;
|
margin: 0 0.5em 0 0.2em;
|
||||||
min-width: 5em;
|
min-width: 5em;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#event-rsvp,
|
#event-rsvp,
|
||||||
|
|
Loading…
Add table
Reference in a new issue