Handle editing/moving/resizing/deleting recurring events in UI and database backend

This commit is contained in:
Thomas Bruederli 2011-06-01 18:35:10 +02:00
parent 0bd3160968
commit 4c21f13eaf
9 changed files with 378 additions and 128 deletions

View file

@ -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]') + "{ '&mdash;' " + 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;
});
});

View file

@ -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")

View file

@ -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',
);
?>

View file

@ -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
* );
*/

View file

@ -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)

View file

@ -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
*/

View file

@ -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';
?>

View file

@ -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 {

View file

@ -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" />