Handle editing/moving/resizing/deleting recurring events in UI and database backend
This commit is contained in:
parent
0bd3160968
commit
4c21f13eaf
9 changed files with 378 additions and 128 deletions
|
@ -52,7 +52,8 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
|||
var ignore_click = false;
|
||||
|
||||
// create a nice human-readable string for the date/time range
|
||||
var event_date_text = function(event) {
|
||||
var event_date_text = function(event)
|
||||
{
|
||||
var fromto, duration = event.end.getTime() / 1000 - event.start.getTime() / 1000;
|
||||
if (event.allDay)
|
||||
fromto = $.fullCalendar.formatDate(event.start, settings['date_format']) + (duration > 86400 ? ' — ' + $.fullCalendar.formatDate(event.end, settings['date_format']) : '');
|
||||
|
@ -67,7 +68,8 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
|||
};
|
||||
|
||||
// event details dialog (show only)
|
||||
var event_show_dialog = function(event) {
|
||||
var event_show_dialog = function(event)
|
||||
{
|
||||
var $dialog = $("#eventshow");
|
||||
var calendar = event.calendar && me.calendars[event.calendar] ? me.calendars[event.calendar] : { editable:false };
|
||||
|
||||
|
@ -142,7 +144,8 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
|||
};
|
||||
|
||||
// bring up the event dialog (jquery-ui popup)
|
||||
var event_edit_dialog = function(action, event) {
|
||||
var event_edit_dialog = function(action, event)
|
||||
{
|
||||
// close show dialog first
|
||||
$("#eventshow").dialog('close');
|
||||
|
||||
|
@ -225,16 +228,10 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
|||
var wdays = event.recurrence.BYDAY.split(',');
|
||||
$('input.edit-recurrence-weekly-byday').val(wdays);
|
||||
}
|
||||
else if (event.start) {
|
||||
$('input.edit-recurrence-weekly-byday').val([weekdays[event.start.getDay()]]);
|
||||
}
|
||||
if (event.recurrence && event.recurrence.BYMONTHDAY) {
|
||||
$('input.edit-recurrence-monthly-bymonthday').val(String(event.recurrence.BYMONTHDAY).split(','));
|
||||
$('input.edit-recurrence-monthly-mode').val(['BYMONTHDAY']);
|
||||
}
|
||||
else if (event.start) {
|
||||
$('input.edit-recurrence-monthly-bymonthday').val([event.start.getDate()]);
|
||||
}
|
||||
if (event.recurrence && event.recurrence.BYDAY && (event.recurrence.FREQ == 'MONTHLY' || event.recurrence.FREQ == 'YEARLY')) {
|
||||
var byday, section = event.recurrence.FREQ.toLowerCase();
|
||||
if ((byday = String(event.recurrence.BYDAY).match(/(-?[1-4])([A-Z]+)/))) {
|
||||
|
@ -253,6 +250,14 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
|||
$('input.edit-recurrence-yearly-bymonth').val([String(event.start.getMonth()+1)]);
|
||||
}
|
||||
|
||||
// show warning if editing a recurring event
|
||||
if (event.id && event.recurrence) {
|
||||
$('#edit-recurring-warning').show();
|
||||
$('input.edit-recurring-savemode[value="all"]').prop('checked', true);
|
||||
}
|
||||
else
|
||||
$('#edit-recurring-warning').hide();
|
||||
|
||||
// buttons
|
||||
var buttons = {};
|
||||
|
||||
|
@ -260,6 +265,12 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
|||
var start = me.parse_datetime(starttime.val(), startdate.val());
|
||||
var end = me.parse_datetime(endtime.val(), enddate.val());
|
||||
|
||||
// basic input validatetion
|
||||
if (start.getTime() > end.getTime()) {
|
||||
alert(rcmail.gettext('invalideventdates', 'calendar'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// post data to server
|
||||
var data = {
|
||||
start: start.getTime()/1000,
|
||||
|
@ -304,13 +315,15 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
|||
if (freq == 'WEEKLY') {
|
||||
var byday = [];
|
||||
$('input.edit-recurrence-weekly-byday:checked').each(function(){ byday.push(this.value); });
|
||||
data.recurrence.BYDAY = byday.join(',');
|
||||
if (byday.length)
|
||||
data.recurrence.BYDAY = byday.join(',');
|
||||
}
|
||||
else if (freq == 'MONTHLY') {
|
||||
var mode = $('input.edit-recurrence-monthly-mode:checked').val(), bymonday = [];
|
||||
if (mode == 'BYMONTHDAY') {
|
||||
$('input.edit-recurrence-monthly-bymonthday:checked').each(function(){ bymonday.push(this.value); });
|
||||
data.recurrence.BYMONTHDAY = bymonday.join(',');
|
||||
$('input.edit-recurrence-monthly-bymonthday:checked').each(function(){ bymonday.push(this.value); });
|
||||
if (bymonday.length)
|
||||
data.recurrence.BYMONTHDAY = bymonday.join(',');
|
||||
}
|
||||
else
|
||||
data.recurrence.BYDAY = $('#edit-recurrence-monthly-prefix').val() + $('#edit-recurrence-monthly-byday').val();
|
||||
|
@ -318,14 +331,18 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
|||
else if (freq == 'YEARLY') {
|
||||
var byday, bymonth = [];
|
||||
$('input.edit-recurrence-yearly-bymonth:checked').each(function(){ bymonth.push(this.value); });
|
||||
data.recurrence.BYMONTH = bymonth.join(',');
|
||||
if (bymonth.length)
|
||||
data.recurrence.BYMONTH = bymonth.join(',');
|
||||
if ((byday = $('#edit-recurrence-yearly-byday').val()))
|
||||
data.recurrence.BYDAY = $('#edit-recurrence-yearly-prefix').val() + byday;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.id)
|
||||
if (event.id) {
|
||||
data.id = event.id;
|
||||
if (event.recurrence)
|
||||
data.savemode = $('input.edit-recurring-savemode:checked').val();
|
||||
}
|
||||
else
|
||||
data.calendar = calendars.val();
|
||||
|
||||
|
@ -368,7 +385,8 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
|||
};
|
||||
|
||||
// mouse-click handler to check if the show dialog is still open and prevent default action
|
||||
var dialog_check = function(e) {
|
||||
var dialog_check = function(e)
|
||||
{
|
||||
var showd = $("#eventshow");
|
||||
if (showd.is(':visible') && !$(e.target).closest('.ui-dialog').length) {
|
||||
showd.dialog('close');
|
||||
|
@ -382,6 +400,47 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
|||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// display confirm dialog when modifying/deleting a recurring event where the user needs to select the savemode
|
||||
var recurring_edit_confirm = function(event, action) {
|
||||
var $dialog = $('<div>').addClass('edit-recurring-warning');
|
||||
$dialog.html('<div class="message"><span class="ui-icon ui-icon-alert"></span>' +
|
||||
rcmail.gettext((action == 'remove' ? 'removerecurringeventwarning' : 'changerecurringeventwarning'), 'calendar') + '</div>' +
|
||||
'<div class="savemode">' +
|
||||
'<a href="#current" class="button">' + rcmail.gettext('currentevent', 'calendar') + '</a>' +
|
||||
'<a href="#future" class="button">' + rcmail.gettext('futurevents', 'calendar') + '</a>' +
|
||||
'<a href="#all" class="button">' + rcmail.gettext('allevents', 'calendar') + '</a>' +
|
||||
(action != 'remove' ? '<a href="#new" class="button">' + rcmail.gettext('saveasnew', 'calendar') + '</a>' : '') +
|
||||
'</div>');
|
||||
|
||||
$dialog.find('a.button').button().click(function(e){
|
||||
event.savemode = String(this.href).replace(/.+#/, '');
|
||||
rcmail.http_post('plugin.event', { action:action, e:event });
|
||||
$dialog.dialog("destroy").hide();
|
||||
return false;
|
||||
});
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 420,
|
||||
dialogClass: 'warning',
|
||||
title: rcmail.gettext((action == 'remove' ? 'removerecurringevent' : 'changerecurringevent'), 'calendar'),
|
||||
buttons: [
|
||||
{
|
||||
text: rcmail.gettext('cancel', 'calendar'),
|
||||
click: function() {
|
||||
$(this).dialog("close");
|
||||
}
|
||||
}
|
||||
],
|
||||
close: function(){
|
||||
$dialog.dialog("destroy").hide();
|
||||
$('#calendar').fullCalendar('refetchEvents');
|
||||
}
|
||||
}).show();
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// general datepicker settings
|
||||
this.datepicker_settings = {
|
||||
|
@ -423,6 +482,10 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
|||
|
||||
// delete the given event after showing a confirmation dialog
|
||||
this.delete_event = function(event) {
|
||||
// show extended confirm dialog for recurring events, use jquery UI dialog
|
||||
if (event.recurrence)
|
||||
return recurring_edit_confirm({ id:event.id }, 'remove');
|
||||
|
||||
// send remove request to plugin
|
||||
if (confirm(rcmail.gettext('deleteventconfirm', 'calendar'))) {
|
||||
rcmail.http_post('plugin.event', { action:'remove', e:{ id:event.id } });
|
||||
|
@ -475,7 +538,7 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
|||
resizable: true,
|
||||
closeOnEscape: false,
|
||||
dialogClass: 'alarm',
|
||||
title: rcmail.gettext('alarmtitle', 'calendar'),
|
||||
title: '<span class="ui-icon ui-icon-alert" style="float:left; margin:0 4px 0 0"></span>' + rcmail.gettext('alarmtitle', 'calendar'),
|
||||
buttons: buttons,
|
||||
close: function() {
|
||||
$('#alarm-snooze-dropdown').hide();
|
||||
|
@ -564,7 +627,7 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
|||
}).data('id', id);
|
||||
}
|
||||
|
||||
if (!cal.readonly) {
|
||||
if (!cal.readonly && !this.selected_calendar) {
|
||||
this.selected_calendar = id;
|
||||
rcmail.enable_command('plugin.addevent', true);
|
||||
}
|
||||
|
@ -594,6 +657,11 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
|||
week: 'ddd ' + settings['date_short'], // Mon 9/7
|
||||
day: 'dddd ' + settings['date_short'] // Monday 9/7
|
||||
},
|
||||
titleFormat: {
|
||||
month: 'MMMM yyyy',
|
||||
week: settings['date_long'].replace(/ yyyy/, '[ yyyy]') + "{ '—' " + settings['date_long'] + "}",
|
||||
day: 'dddd ' + settings['date_long']
|
||||
},
|
||||
defaultView: settings['default_view'],
|
||||
allDayText: rcmail.gettext('all-day', 'calendar'),
|
||||
buttonText: {
|
||||
|
@ -658,17 +726,23 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
|||
end: event.end.getTime()/1000,
|
||||
allday: allDay?1:0
|
||||
};
|
||||
rcmail.http_post('plugin.event', { action:'move', e:data });
|
||||
if (event.recurrence)
|
||||
recurring_edit_confirm(data, 'move');
|
||||
else
|
||||
rcmail.http_post('plugin.event', { action:'move', e:data });
|
||||
},
|
||||
// callback for event resizing
|
||||
eventResize : function(event, delta) {
|
||||
// send resize request to server
|
||||
var data = {
|
||||
id: event.id,
|
||||
id: event.id,
|
||||
start: event.start.getTime()/1000,
|
||||
end: event.end.getTime()/1000,
|
||||
end: event.end.getTime()/1000,
|
||||
};
|
||||
rcmail.http_post('plugin.event', { action:'resize', e:data });
|
||||
if (event.recurrence)
|
||||
recurring_edit_confirm(data, 'resize');
|
||||
else
|
||||
rcmail.http_post('plugin.event', { action:'resize', e:data });
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -843,10 +917,4 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
|||
});
|
||||
$('#edit-recurrence-enddate').datepicker(cal.datepicker_settings).click(function(){ $("#edit-recurrence-repeat-until").prop('checked', true) });
|
||||
|
||||
// avoid unselecting all weekdays, monthdays and months
|
||||
$('input.edit-recurrence-weekly-byday, input.edit-recurrence-monthly-bymonthday, input.edit-recurrence-yearly-bymonth').click(function(){
|
||||
if (!$('input.'+this.className+':checked').length)
|
||||
this.checked = true;
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -135,6 +135,7 @@ class calendar extends rcube_plugin
|
|||
$this->register_handler('plugin.alarm_select', array($this->ui, 'alarm_select'));
|
||||
$this->register_handler('plugin.snooze_select', array($this->ui, 'snooze_select'));
|
||||
$this->register_handler('plugin.recurrence_form', array($this->ui, 'recurrence_form'));
|
||||
$this->register_handler('plugin.edit_recurring_warning', array($this->ui, 'recurring_event_warning'));
|
||||
|
||||
$this->rc->output->set_env('calendar_settings', $this->load_settings());
|
||||
$this->rc->output->add_label('low','normal','high');
|
||||
|
@ -344,7 +345,7 @@ class calendar extends rcube_plugin
|
|||
switch ($action) {
|
||||
case "new":
|
||||
// create UID for new event
|
||||
$events['uid'] = strtoupper(md5(time() . uniqid(rand())) . '-' . substr(md5($this->rc->user->get_username()), 0, 16));
|
||||
$event['uid'] = $this->generate_uid();
|
||||
$success = $this->driver->new_event($event);
|
||||
$reload = true;
|
||||
break;
|
||||
|
@ -370,12 +371,13 @@ class calendar extends rcube_plugin
|
|||
break;
|
||||
}
|
||||
|
||||
if (!$success) {
|
||||
if ($success)
|
||||
$this->rc->output->show_message('successfullysaved', 'confirmation');
|
||||
else
|
||||
$this->rc->output->show_message('calendar.errorsaving', 'error');
|
||||
}
|
||||
else if ($reload) {
|
||||
|
||||
if ($success && $reload)
|
||||
$this->rc->output->command('plugin.reload_calendar', array());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -433,6 +435,7 @@ class calendar extends rcube_plugin
|
|||
$settings['default_view'] = (string)$this->rc->config->get('calendar_default_view', "agendaWeek");
|
||||
$settings['date_format'] = (string)$this->rc->config->get('calendar_date_format', "yyyy/MM/dd");
|
||||
$settings['date_short'] = (string)$this->rc->config->get('calendar_date_short', "M/d");
|
||||
$settings['date_long'] = (string)$this->rc->config->get('calendar_date_long', "M d yyyy");
|
||||
$settings['time_format'] = (string)$this->rc->config->get('calendar_time_format', "HH:mm");
|
||||
$settings['timeslots'] = (int)$this->rc->config->get('calendar_timeslots', 2);
|
||||
$settings['first_day'] = (int)$this->rc->config->get('calendar_first_day', 1);
|
||||
|
@ -516,10 +519,6 @@ class calendar extends rcube_plugin
|
|||
if ($event['recurrence'])
|
||||
$event['recurrence_text'] = $this->_recurrence_text($event['recurrence']);
|
||||
|
||||
// TEMPORARY: recurring instances are immutable
|
||||
if ($event['recurrence_id'])
|
||||
$event['editable'] = false;
|
||||
|
||||
$json[] = array(
|
||||
'start' => date('c', $event['start']), // ISO 8601 date (added in PHP 5)
|
||||
'end' => date('c', $event['end']), // ISO 8601 date (added in PHP 5)
|
||||
|
@ -619,6 +618,14 @@ class calendar extends rcube_plugin
|
|||
return rtrim($freq . $details . ', ' . $until);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unique identifier for an event
|
||||
*/
|
||||
public function generate_uid()
|
||||
{
|
||||
return strtoupper(md5(time() . uniqid(rand())) . '-' . substr(md5($this->rc->user->get_username()), 0, 16));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to convert alarm trigger strings
|
||||
* into two-field values (e.g. "-45M" => 45, "-M")
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/*
|
||||
+-------------------------------------------------------------------------+
|
||||
| Configuration for the Calendar plugin |
|
||||
| Version 0.3 alpha |
|
||||
| Version 0.3 beta |
|
||||
| |
|
||||
| This program is free software; you can redistribute it and/or modify |
|
||||
| it under the terms of the GNU General Public License version 2 |
|
||||
|
@ -37,6 +37,9 @@ $rcmail_config['calendar_time_format'] = "HH:mm";
|
|||
// short date format (used for column titles)
|
||||
$rcmail_config['calendar_date_short'] = 'M-d';
|
||||
|
||||
// long date format (used for calendar title)
|
||||
$rcmail_config['calendar_date_long'] = 'MMM d yyyy';
|
||||
|
||||
// timeslots per hour (1, 2, 3, 4, 6)
|
||||
$rcmail_config['calendar_timeslots'] = 2;
|
||||
|
||||
|
@ -47,8 +50,11 @@ $rcmail_config['calendar_first_day'] = 1;
|
|||
$rcmail_config['calendar_first_hour'] = 6;
|
||||
|
||||
// event categories
|
||||
$rcmail_config['calendar_categories'] = array('Personal' => 'c0c0c0',
|
||||
'Work' => 'ff0000',
|
||||
'Family' => '00ff00',
|
||||
'Holiday' => 'ff6600');
|
||||
$rcmail_config['calendar_categories'] = array(
|
||||
'Personal' => 'c0c0c0',
|
||||
'Work' => 'ff0000',
|
||||
'Family' => '00ff00',
|
||||
'Holiday' => 'ff6600',
|
||||
);
|
||||
|
||||
?>
|
|
@ -50,6 +50,7 @@
|
|||
* 'priority' => 1|0|2, // Event priority (0=low, 1=normal, 2=high)
|
||||
* 'sensitivity' => 0|1|2, // Event sensitivity (0=public, 1=private, 2=confidential)
|
||||
* 'alarms' => '-15M:DISPLAY', // Reminder settings inspired by valarm definition (e.g. display alert 15 minutes before event)
|
||||
* 'savemode' => 'all|future|current|new', // How changes on recurring event should be handled
|
||||
* );
|
||||
*/
|
||||
|
||||
|
|
|
@ -152,7 +152,7 @@ class database_driver extends calendar_driver
|
|||
),
|
||||
$event['calendar'],
|
||||
strval($event['uid']),
|
||||
intval($event['allday']),
|
||||
intval($event['all_day']),
|
||||
$event['recurrence'],
|
||||
strval($event['title']),
|
||||
strval($event['description']),
|
||||
|
@ -178,37 +178,88 @@ class database_driver extends calendar_driver
|
|||
* Update an event entry with the given data
|
||||
*
|
||||
* @param array Hash array with event properties
|
||||
* @see Driver:new_event()
|
||||
* @see Driver:edit_event()
|
||||
*/
|
||||
public function edit_event($event)
|
||||
{
|
||||
if (!empty($this->calendars)) {
|
||||
$event = $this->_save_preprocess($event);
|
||||
$query = $this->rc->db->query(sprintf(
|
||||
"UPDATE " . $this->db_events . "
|
||||
SET changed=%s, start=%s, end=%s, all_day=?, recurrence=?, title=?, description=?, location=?, categories=?, free_busy=?, priority=?, sensitivity=?, alarms=?, notifyat=?
|
||||
WHERE event_id=?
|
||||
AND calendar_id IN (" . $this->calendar_ids . ")",
|
||||
$this->rc->db->now(),
|
||||
$this->rc->db->fromunixtime($event['start']),
|
||||
$this->rc->db->fromunixtime($event['end'])
|
||||
),
|
||||
intval($event['allday']),
|
||||
$event['recurrence'],
|
||||
strval($event['title']),
|
||||
strval($event['description']),
|
||||
strval($event['location']),
|
||||
strval($event['categories']),
|
||||
intval($event['free_busy']),
|
||||
intval($event['sensitivity']),
|
||||
intval($event['priority']),
|
||||
$event['alarms'],
|
||||
$event['notifyat'],
|
||||
$event['id']
|
||||
);
|
||||
$update_master = false;
|
||||
$update_recurring = true;
|
||||
$old = $this->get_event($event['id']);
|
||||
|
||||
if ($success = $this->rc->db->affected_rows($query))
|
||||
$this->_update_recurring($event);
|
||||
// modify a recurring event, check submitted savemode to do the right things
|
||||
if ($old['recurrence'] || $old['recurrence_id']) {
|
||||
$master = $old['recurrence_id'] ? $this->get_event($old['recurrence_id']) : $old;
|
||||
|
||||
switch ($event['savemode']) {
|
||||
case 'new':
|
||||
$event['uid'] = $this->cal->generate_uid();
|
||||
return $this->new_event($event);
|
||||
|
||||
case 'current':
|
||||
// add exception to master event
|
||||
$master['recurrence']['EXDATE'][] = $old['start'];
|
||||
$update_master = true;
|
||||
|
||||
// just update this occurence (decouple from master)
|
||||
$update_recurring = false;
|
||||
$event['recurrence_id'] = 0;
|
||||
$event['recurrence'] = array();
|
||||
break;
|
||||
|
||||
case 'future':
|
||||
if ($master['id'] != $event['id']) {
|
||||
// set until-date on master event, then save this instance as new recurring event
|
||||
$master['recurrence']['UNTIL'] = $event['start'] - 86400;
|
||||
unset($master['recurrence']['COUNT']);
|
||||
$update_master = true;
|
||||
|
||||
// if recurrence COUNT, update value to the correct number of future occurences
|
||||
if ($event['recurrence']['COUNT']) {
|
||||
$sqlresult = $this->rc->db->query(sprintf(
|
||||
"SELECT event_id FROM " . $this->db_events . "
|
||||
WHERE calendar_id IN (%s)
|
||||
AND start >= %s
|
||||
AND recurrence_id=?",
|
||||
$this->calendar_ids,
|
||||
$this->rc->db->fromunixtime($event['start'])
|
||||
),
|
||||
$master['id']);
|
||||
if ($count = $this->rc->db->num_rows($sqlresult))
|
||||
$event['recurrence']['COUNT'] = $count;
|
||||
}
|
||||
|
||||
$update_recurring = true;
|
||||
$event['recurrence_id'] = 0;
|
||||
break;
|
||||
}
|
||||
// else: 'future' == 'all' if modifying the master event
|
||||
|
||||
default: // 'all' is default
|
||||
$event['id'] = $master['id'];
|
||||
$event['recurrence_id'] = 0;
|
||||
|
||||
// use start date from master but try to be smart on time or duration changes
|
||||
$old_start_date = date('Y-m-d', $old['start']);
|
||||
$old_start_time = date('H:i:s', $old['start']);
|
||||
$old_duration = $old['end'] - $old['start'];
|
||||
|
||||
$new_start_date = date('Y-m-d', $event['start']);
|
||||
$new_start_time = date('H:i:s', $event['start']);
|
||||
$new_duration = $event['end'] - $event['start'];
|
||||
|
||||
// shifted or resized
|
||||
if ($old_start_date == $new_start_date || $old_duration == $new_duration) {
|
||||
$event['start'] = $master['start'] + ($event['start'] - $old['start']);
|
||||
$event['end'] = $event['start'] + $new_duration;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$success = $this->_update_event($event, $update_recurring);
|
||||
if ($success && $update_master)
|
||||
$this->_update_event($master, true);
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
@ -226,14 +277,16 @@ class database_driver extends calendar_driver
|
|||
$event['_exdates'] = (array)$event['recurrence']['EXDATE'];
|
||||
$event['recurrence'] = rtrim($rrule, ';');
|
||||
$event['free_busy'] = intval($this->free_busy_map[strtolower($event['free_busy'])]);
|
||||
$event['allday'] = $event['allday'] ? 1 : 0;
|
||||
|
||||
if (isset($event['allday']))
|
||||
$event['all_day'] = $event['allday'] ? 1 : 0;
|
||||
|
||||
// compute absolute time to notify the user
|
||||
$event['notifyat'] = $this->_get_notification($event);
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute absolute time to notify the user
|
||||
*/
|
||||
|
@ -267,6 +320,43 @@ class database_driver extends calendar_driver
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the given event record to database
|
||||
*
|
||||
* @param array Event data, already passed through self::_save_preprocess()
|
||||
* @param boolean True if recurring events instances should be updated, too
|
||||
*/
|
||||
private function _update_event($event, $update_recurring = true)
|
||||
{
|
||||
$event = $this->_save_preprocess($event);
|
||||
|
||||
$sql_set = array();
|
||||
$set_cols = array('all_day', 'recurrence', 'recurrence_id', 'title', 'description', 'location', 'categories', 'free_busy', 'priority', 'sensitivity', 'alarms', 'notifyat');
|
||||
foreach ($set_cols as $col) {
|
||||
if (isset($event[$col]))
|
||||
$sql_set[] = $this->rc->db->quote_identifier($col) . '=' . $this->rc->db->quote($event[$col]);
|
||||
}
|
||||
|
||||
$query = $this->rc->db->query(sprintf(
|
||||
"UPDATE " . $this->db_events . "
|
||||
SET changed=%s, start=%s, end=%s %s
|
||||
WHERE event_id=?
|
||||
AND calendar_id IN (" . $this->calendar_ids . ")",
|
||||
$this->rc->db->now(),
|
||||
$this->rc->db->fromunixtime($event['start']),
|
||||
$this->rc->db->fromunixtime($event['end']),
|
||||
($sql_set ? ', ' . join(', ', $sql_set) : '')
|
||||
),
|
||||
$event['id']
|
||||
);
|
||||
|
||||
$success = $this->rc->db->affected_rows($query);
|
||||
if ($success && $update_recurring)
|
||||
$this->_update_recurring($event);
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert "fake" entries for recurring occurences of this event
|
||||
*/
|
||||
|
@ -320,7 +410,6 @@ class database_driver extends calendar_driver
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -331,29 +420,8 @@ class database_driver extends calendar_driver
|
|||
*/
|
||||
public function move_event($event)
|
||||
{
|
||||
if (!empty($this->calendars)) {
|
||||
$event = $this->_save_preprocess($event + (array)$this->get_event($event['id']));
|
||||
$query = $this->rc->db->query(sprintf(
|
||||
"UPDATE " . $this->db_events . "
|
||||
SET changed=%s, start=%s, end=%s, all_day=?, notifyat=?
|
||||
WHERE event_id=?
|
||||
AND calendar_id IN (" . $this->calendar_ids . ")",
|
||||
$this->rc->db->now(),
|
||||
$this->rc->db->fromunixtime($event['start']),
|
||||
$this->rc->db->fromunixtime($event['end'])
|
||||
),
|
||||
$event['allday'] ? 1 : 0,
|
||||
$event['notifyat'],
|
||||
$event['id']
|
||||
);
|
||||
|
||||
if ($success = $this->rc->db->affected_rows($query))
|
||||
$this->_update_recurring($event);
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
return false;
|
||||
// let edit_event() do all the magic
|
||||
return $this->edit_event($event + (array)$this->get_event($event['id']));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -364,28 +432,8 @@ class database_driver extends calendar_driver
|
|||
*/
|
||||
public function resize_event($event)
|
||||
{
|
||||
if (!empty($this->calendars)) {
|
||||
$event = $this->_save_preprocess($event + (array)$this->get_event($event['id']));
|
||||
$query = $this->rc->db->query(sprintf(
|
||||
"UPDATE " . $this->db_events . "
|
||||
SET changed=%s, start=%s, end=%s, notifyat=?
|
||||
WHERE event_id=?
|
||||
AND calendar_id IN (" . $this->calendar_ids . ")",
|
||||
$this->rc->db->now(),
|
||||
$this->rc->db->fromunixtime($event['start']),
|
||||
$this->rc->db->fromunixtime($event['end'])
|
||||
),
|
||||
$event['notifyat'],
|
||||
$event['id']
|
||||
);
|
||||
|
||||
if ($success = $this->rc->db->affected_rows($query))
|
||||
$this->_update_recurring($event);
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
return false;
|
||||
// let edit_event() do all the magic
|
||||
return $this->edit_event($event + (array)$this->get_event($event['id']));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -397,14 +445,67 @@ class database_driver extends calendar_driver
|
|||
public function remove_event($event)
|
||||
{
|
||||
if (!empty($this->calendars)) {
|
||||
$query = $this->rc->db->query(
|
||||
"DELETE FROM " . $this->db_events . "
|
||||
WHERE (event_id=? OR recurrence_id=?)
|
||||
AND calendar_id IN (" . $this->calendar_ids . ")",
|
||||
$event['id'],
|
||||
$event['id']
|
||||
);
|
||||
return $this->rc->db->affected_rows($query);
|
||||
$event += (array)$this->get_event($event['id']);
|
||||
$master = $event;
|
||||
$update_master = false;
|
||||
$savemode = 'all';
|
||||
|
||||
// read master if deleting a recurring event
|
||||
if ($event['recurrence'] || $event['recurrence_id']) {
|
||||
$master = $event['recurrence_id'] ? $this->get_event($old['recurrence_id']) : $event;
|
||||
$savemode = $event['savemode'];
|
||||
}
|
||||
|
||||
switch ($savemode) {
|
||||
case 'current':
|
||||
// add exception to master event
|
||||
$master['recurrence']['EXDATE'][] = $event['start'];
|
||||
$update_master = true;
|
||||
|
||||
// just delete this single occurence
|
||||
$query = $this->rc->db->query(
|
||||
"DELETE FROM " . $this->db_events . "
|
||||
WHERE calendar_id IN (" . $this->calendar_ids . ")
|
||||
AND event_id=?",
|
||||
$event['id']
|
||||
);
|
||||
break;
|
||||
|
||||
case 'future':
|
||||
if ($master['id'] != $event['id']) {
|
||||
// set until-date on master event
|
||||
$master['recurrence']['UNTIL'] = $event['start'] - 86400;
|
||||
unset($master['recurrence']['COUNT']);
|
||||
$update_master = true;
|
||||
|
||||
// delete this and all future instances
|
||||
$query = $this->rc->db->query(
|
||||
"DELETE FROM " . $this->db_events . "
|
||||
WHERE calendar_id IN (" . $this->calendar_ids . ")
|
||||
AND start >= " . $this->rc->db->fromunixtime($old['start']) . "
|
||||
AND recurrence_id=?",
|
||||
$master['id']
|
||||
);
|
||||
break;
|
||||
}
|
||||
// else: future == all if modifying the master event
|
||||
|
||||
default: // 'all' is default
|
||||
$query = $this->rc->db->query(
|
||||
"DELETE FROM " . $this->db_events . "
|
||||
WHERE (event_id=? OR recurrence_id=?)
|
||||
AND calendar_id IN (" . $this->calendar_ids . ")",
|
||||
$master['id'],
|
||||
$master['id']
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
$success = $this->rc->db->affected_rows($query);
|
||||
if ($success && $update_master)
|
||||
$this->_update_event($master, true);
|
||||
console($savemode, $master['id'], $success);
|
||||
return $success;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -417,6 +518,11 @@ class database_driver extends calendar_driver
|
|||
*/
|
||||
public function get_event($id)
|
||||
{
|
||||
static $cache = array();
|
||||
|
||||
if ($cache[$id])
|
||||
return $cache[$id];
|
||||
|
||||
$result = $this->rc->db->query(sprintf(
|
||||
"SELECT * FROM " . $this->db_events . "
|
||||
WHERE calendar_id IN (%s)
|
||||
|
@ -425,8 +531,10 @@ class database_driver extends calendar_driver
|
|||
),
|
||||
$id);
|
||||
|
||||
if ($result && ($event = $this->rc->db->fetch_assoc($result)))
|
||||
return $this->_read_postprocess($event);
|
||||
if ($result && ($event = $this->rc->db->fetch_assoc($result))) {
|
||||
$cache[$id] = $this->_read_postprocess($event);
|
||||
return $cache[$id];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -493,7 +601,7 @@ class database_driver extends calendar_driver
|
|||
}
|
||||
}
|
||||
|
||||
unset($event['event_id'], $event['calendar_id']);
|
||||
unset($event['event_id'], $event['calendar_id'], $event['notifyat']);
|
||||
return $event;
|
||||
}
|
||||
|
||||
|
@ -528,7 +636,7 @@ class database_driver extends calendar_driver
|
|||
$result = $this->rc->db->query(sprintf(
|
||||
"SELECT * FROM " . $this->db_events . "
|
||||
WHERE calendar_id IN (%s)
|
||||
AND notifyat <= %s AND end <= %s",
|
||||
AND notifyat <= %s AND end > %s",
|
||||
join(',', $calendar_ids),
|
||||
$this->rc->db->fromunixtime($time),
|
||||
$this->rc->db->fromunixtime($time)
|
||||
|
|
|
@ -277,6 +277,22 @@ class calendar_ui
|
|||
return html::tag('ul', $attrib, join("\n", $items));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the form for recurrence settings
|
||||
*/
|
||||
function recurring_event_warning($attrib = array())
|
||||
{
|
||||
$attrib['id'] = 'edit-recurring-warning';
|
||||
|
||||
$radio = new html_radiobutton(array('name' => 'savemode', 'class' => 'edit-recurring-savemode'));
|
||||
$form = html::label(null, $radio->show('', array('value' => 'current')) . $this->calendar->gettext('currentevent')) . ' ' .
|
||||
html::label(null, $radio->show('', array('value' => 'future')) . $this->calendar->gettext('futurevents')) . ' ' .
|
||||
html::label(null, $radio->show('all', array('value' => 'all')) . $this->calendar->gettext('allevents')) . ' ' .
|
||||
html::label(null, $radio->show('', array('value' => 'new')) . $this->calendar->gettext('saveasnew'));
|
||||
|
||||
return html::div($attrib, html::div('message', html::span('ui-icon ui-icon-alert', '') . $this->calendar->gettext('changerecurringeventwarning')) . html::div('savemode', $form));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the form for recurrence settings
|
||||
*/
|
||||
|
|
|
@ -89,6 +89,7 @@ $labels['tabattachments'] = 'Attachments';
|
|||
$labels['deleteventconfirm'] = "Do you really want to delete this event?";
|
||||
$labels['errorsaving'] = "Failed to save changes";
|
||||
$labels['operationfailed'] = "The requested operation failed";
|
||||
$labels['invalideventdates'] = "Invalid dates entered! Please check your input.";
|
||||
|
||||
// recurrence form
|
||||
$labels['repeat'] = 'Repeat';
|
||||
|
@ -117,4 +118,13 @@ $labels['fourth'] = 'fourth';
|
|||
$labels['last'] = 'last';
|
||||
$labels['dayofmonth'] = 'Day of month';
|
||||
|
||||
$labels['changerecurringevent'] = 'Change recurring event';
|
||||
$labels['removerecurringevent'] = 'Remove recurring 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 remove the current event only, this and all future occurences or all occurences of this event?';
|
||||
$labels['currentevent'] = 'Current';
|
||||
$labels['futurevents'] = 'Future';
|
||||
$labels['allevents'] = 'All';
|
||||
$labels['saveasnew'] = 'Save as new';
|
||||
|
||||
?>
|
|
@ -240,6 +240,7 @@ pre {
|
|||
}
|
||||
|
||||
#eventedit {
|
||||
position: relative;
|
||||
padding: 0.5em 0.1em;
|
||||
}
|
||||
|
||||
|
@ -321,6 +322,36 @@ td.topalign {
|
|||
padding: 0.2em 0;
|
||||
}
|
||||
|
||||
.edit-recurring-warning {
|
||||
margin-top: 0.5em;
|
||||
padding: 0.8em;
|
||||
background-color: #F7FDCB;
|
||||
border: 1px solid #C2D071;
|
||||
}
|
||||
|
||||
.edit-recurring-warning .message {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.edit-recurring-warning .savemode {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.edit-recurring-warning span.ui-icon {
|
||||
float: left;
|
||||
margin: 0 7px 20px 0;
|
||||
}
|
||||
|
||||
.edit-recurring-warning label {
|
||||
min-width: 3em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.edit-recurring-warning a.button {
|
||||
margin: 0 0.5em 0 0.2em;
|
||||
min-width: 5em;
|
||||
}
|
||||
|
||||
span.edit-alarm-set {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
@ -400,6 +431,7 @@ a.alarm-action-snooze:after {
|
|||
height: 160px;
|
||||
}
|
||||
|
||||
|
||||
/* fullcalendar style overrides */
|
||||
|
||||
.fc-event-title {
|
||||
|
|
|
@ -160,6 +160,8 @@
|
|||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<roundcube:object name="plugin.edit_recurring_warning" class="edit-recurring-warning" style="display:none" />
|
||||
</div>
|
||||
<div id="alarm-snooze-dropdown" class="popupmenu">
|
||||
<roundcube:object name="plugin.snooze_select" type="ul" />
|
||||
|
|
Loading…
Add table
Reference in a new issue