diff --git a/plugins/calendar/calendar.js b/plugins/calendar/calendar.js
index 7b1070a2..8324ea32 100644
--- a/plugins/calendar/calendar.js
+++ b/plugins/calendar/calendar.js
@@ -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 = $('
').addClass('edit-recurring-warning');
+ $dialog.html('
' +
+ rcmail.gettext((action == 'remove' ? 'removerecurringeventwarning' : 'changerecurringeventwarning'), 'calendar') + '
' +
+ '
');
+
+ $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: '
' + 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;
- });
-
});
diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index 037e89e9..c973d171 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -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")
diff --git a/plugins/calendar/config.inc.php.dist b/plugins/calendar/config.inc.php.dist
index 4b8cb0b1..8abc924a 100644
--- a/plugins/calendar/config.inc.php.dist
+++ b/plugins/calendar/config.inc.php.dist
@@ -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',
+);
+
?>
\ No newline at end of file
diff --git a/plugins/calendar/drivers/calendar_driver.php b/plugins/calendar/drivers/calendar_driver.php
index 044a361b..be50e300 100644
--- a/plugins/calendar/drivers/calendar_driver.php
+++ b/plugins/calendar/drivers/calendar_driver.php
@@ -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
* );
*/
diff --git a/plugins/calendar/drivers/database/database_driver.php b/plugins/calendar/drivers/database/database_driver.php
index 0378b35a..e86da7d2 100644
--- a/plugins/calendar/drivers/database/database_driver.php
+++ b/plugins/calendar/drivers/database/database_driver.php
@@ -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)
diff --git a/plugins/calendar/lib/calendar_ui.php b/plugins/calendar/lib/calendar_ui.php
index 461a92ba..8ccccc48 100644
--- a/plugins/calendar/lib/calendar_ui.php
+++ b/plugins/calendar/lib/calendar_ui.php
@@ -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
*/
diff --git a/plugins/calendar/localization/en_US.inc b/plugins/calendar/localization/en_US.inc
index ec8947e8..8872f798 100644
--- a/plugins/calendar/localization/en_US.inc
+++ b/plugins/calendar/localization/en_US.inc
@@ -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';
+
?>
\ No newline at end of file
diff --git a/plugins/calendar/skins/default/calendar.css b/plugins/calendar/skins/default/calendar.css
index a9607578..4400c3a1 100644
--- a/plugins/calendar/skins/default/calendar.css
+++ b/plugins/calendar/skins/default/calendar.css
@@ -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 {
diff --git a/plugins/calendar/skins/default/templates/calendar.html b/plugins/calendar/skins/default/templates/calendar.html
index b4efed38..1d20e522 100644
--- a/plugins/calendar/skins/default/templates/calendar.html
+++ b/plugins/calendar/skins/default/templates/calendar.html
@@ -160,6 +160,8 @@
+
+