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;
|
var ignore_click = false;
|
||||||
|
|
||||||
// create a nice human-readable string for the date/time range
|
// 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;
|
var fromto, duration = event.end.getTime() / 1000 - event.start.getTime() / 1000;
|
||||||
if (event.allDay)
|
if (event.allDay)
|
||||||
fromto = $.fullCalendar.formatDate(event.start, settings['date_format']) + (duration > 86400 ? ' — ' + $.fullCalendar.formatDate(event.end, settings['date_format']) : '');
|
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)
|
// event details dialog (show only)
|
||||||
var event_show_dialog = function(event) {
|
var event_show_dialog = function(event)
|
||||||
|
{
|
||||||
var $dialog = $("#eventshow");
|
var $dialog = $("#eventshow");
|
||||||
var calendar = event.calendar && me.calendars[event.calendar] ? me.calendars[event.calendar] : { editable:false };
|
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)
|
// 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
|
// close show dialog first
|
||||||
$("#eventshow").dialog('close');
|
$("#eventshow").dialog('close');
|
||||||
|
|
||||||
|
@ -225,16 +228,10 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
||||||
var wdays = event.recurrence.BYDAY.split(',');
|
var wdays = event.recurrence.BYDAY.split(',');
|
||||||
$('input.edit-recurrence-weekly-byday').val(wdays);
|
$('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) {
|
if (event.recurrence && event.recurrence.BYMONTHDAY) {
|
||||||
$('input.edit-recurrence-monthly-bymonthday').val(String(event.recurrence.BYMONTHDAY).split(','));
|
$('input.edit-recurrence-monthly-bymonthday').val(String(event.recurrence.BYMONTHDAY).split(','));
|
||||||
$('input.edit-recurrence-monthly-mode').val(['BYMONTHDAY']);
|
$('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')) {
|
if (event.recurrence && event.recurrence.BYDAY && (event.recurrence.FREQ == 'MONTHLY' || event.recurrence.FREQ == 'YEARLY')) {
|
||||||
var byday, section = event.recurrence.FREQ.toLowerCase();
|
var byday, section = event.recurrence.FREQ.toLowerCase();
|
||||||
if ((byday = String(event.recurrence.BYDAY).match(/(-?[1-4])([A-Z]+)/))) {
|
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)]);
|
$('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
|
// buttons
|
||||||
var buttons = {};
|
var buttons = {};
|
||||||
|
|
||||||
|
@ -260,6 +265,12 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
||||||
var start = me.parse_datetime(starttime.val(), startdate.val());
|
var start = me.parse_datetime(starttime.val(), startdate.val());
|
||||||
var end = me.parse_datetime(endtime.val(), enddate.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
|
// post data to server
|
||||||
var data = {
|
var data = {
|
||||||
start: start.getTime()/1000,
|
start: start.getTime()/1000,
|
||||||
|
@ -304,12 +315,14 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
||||||
if (freq == 'WEEKLY') {
|
if (freq == 'WEEKLY') {
|
||||||
var byday = [];
|
var byday = [];
|
||||||
$('input.edit-recurrence-weekly-byday:checked').each(function(){ byday.push(this.value); });
|
$('input.edit-recurrence-weekly-byday:checked').each(function(){ byday.push(this.value); });
|
||||||
|
if (byday.length)
|
||||||
data.recurrence.BYDAY = byday.join(',');
|
data.recurrence.BYDAY = byday.join(',');
|
||||||
}
|
}
|
||||||
else if (freq == 'MONTHLY') {
|
else if (freq == 'MONTHLY') {
|
||||||
var mode = $('input.edit-recurrence-monthly-mode:checked').val(), bymonday = [];
|
var mode = $('input.edit-recurrence-monthly-mode:checked').val(), bymonday = [];
|
||||||
if (mode == 'BYMONTHDAY') {
|
if (mode == 'BYMONTHDAY') {
|
||||||
$('input.edit-recurrence-monthly-bymonthday:checked').each(function(){ bymonday.push(this.value); });
|
$('input.edit-recurrence-monthly-bymonthday:checked').each(function(){ bymonday.push(this.value); });
|
||||||
|
if (bymonday.length)
|
||||||
data.recurrence.BYMONTHDAY = bymonday.join(',');
|
data.recurrence.BYMONTHDAY = bymonday.join(',');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -318,14 +331,18 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
||||||
else if (freq == 'YEARLY') {
|
else if (freq == 'YEARLY') {
|
||||||
var byday, bymonth = [];
|
var byday, bymonth = [];
|
||||||
$('input.edit-recurrence-yearly-bymonth:checked').each(function(){ bymonth.push(this.value); });
|
$('input.edit-recurrence-yearly-bymonth:checked').each(function(){ bymonth.push(this.value); });
|
||||||
|
if (bymonth.length)
|
||||||
data.recurrence.BYMONTH = bymonth.join(',');
|
data.recurrence.BYMONTH = bymonth.join(',');
|
||||||
if ((byday = $('#edit-recurrence-yearly-byday').val()))
|
if ((byday = $('#edit-recurrence-yearly-byday').val()))
|
||||||
data.recurrence.BYDAY = $('#edit-recurrence-yearly-prefix').val() + byday;
|
data.recurrence.BYDAY = $('#edit-recurrence-yearly-prefix').val() + byday;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.id)
|
if (event.id) {
|
||||||
data.id = event.id;
|
data.id = event.id;
|
||||||
|
if (event.recurrence)
|
||||||
|
data.savemode = $('input.edit-recurring-savemode:checked').val();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
data.calendar = calendars.val();
|
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
|
// 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");
|
var showd = $("#eventshow");
|
||||||
if (showd.is(':visible') && !$(e.target).closest('.ui-dialog').length) {
|
if (showd.is(':visible') && !$(e.target).closest('.ui-dialog').length) {
|
||||||
showd.dialog('close');
|
showd.dialog('close');
|
||||||
|
@ -383,6 +401,47 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
||||||
return true;
|
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
|
// general datepicker settings
|
||||||
this.datepicker_settings = {
|
this.datepicker_settings = {
|
||||||
// translate from fullcalendar format to datepicker format
|
// translate from fullcalendar format to datepicker format
|
||||||
|
@ -423,6 +482,10 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
||||||
|
|
||||||
// delete the given event after showing a confirmation dialog
|
// delete the given event after showing a confirmation dialog
|
||||||
this.delete_event = function(event) {
|
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
|
// send remove request to plugin
|
||||||
if (confirm(rcmail.gettext('deleteventconfirm', 'calendar'))) {
|
if (confirm(rcmail.gettext('deleteventconfirm', 'calendar'))) {
|
||||||
rcmail.http_post('plugin.event', { action:'remove', e:{ id:event.id } });
|
rcmail.http_post('plugin.event', { action:'remove', e:{ id:event.id } });
|
||||||
|
@ -475,7 +538,7 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
||||||
resizable: true,
|
resizable: true,
|
||||||
closeOnEscape: false,
|
closeOnEscape: false,
|
||||||
dialogClass: 'alarm',
|
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,
|
buttons: buttons,
|
||||||
close: function() {
|
close: function() {
|
||||||
$('#alarm-snooze-dropdown').hide();
|
$('#alarm-snooze-dropdown').hide();
|
||||||
|
@ -564,7 +627,7 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
||||||
}).data('id', id);
|
}).data('id', id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cal.readonly) {
|
if (!cal.readonly && !this.selected_calendar) {
|
||||||
this.selected_calendar = id;
|
this.selected_calendar = id;
|
||||||
rcmail.enable_command('plugin.addevent', true);
|
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
|
week: 'ddd ' + settings['date_short'], // Mon 9/7
|
||||||
day: 'dddd ' + settings['date_short'] // Monday 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'],
|
defaultView: settings['default_view'],
|
||||||
allDayText: rcmail.gettext('all-day', 'calendar'),
|
allDayText: rcmail.gettext('all-day', 'calendar'),
|
||||||
buttonText: {
|
buttonText: {
|
||||||
|
@ -658,6 +726,9 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
||||||
end: event.end.getTime()/1000,
|
end: event.end.getTime()/1000,
|
||||||
allday: allDay?1:0
|
allday: allDay?1:0
|
||||||
};
|
};
|
||||||
|
if (event.recurrence)
|
||||||
|
recurring_edit_confirm(data, 'move');
|
||||||
|
else
|
||||||
rcmail.http_post('plugin.event', { action:'move', e:data });
|
rcmail.http_post('plugin.event', { action:'move', e:data });
|
||||||
},
|
},
|
||||||
// callback for event resizing
|
// callback for event resizing
|
||||||
|
@ -668,6 +739,9 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
||||||
start: event.start.getTime()/1000,
|
start: event.start.getTime()/1000,
|
||||||
end: event.end.getTime()/1000,
|
end: event.end.getTime()/1000,
|
||||||
};
|
};
|
||||||
|
if (event.recurrence)
|
||||||
|
recurring_edit_confirm(data, 'resize');
|
||||||
|
else
|
||||||
rcmail.http_post('plugin.event', { action:'resize', e:data });
|
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) });
|
$('#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.alarm_select', array($this->ui, 'alarm_select'));
|
||||||
$this->register_handler('plugin.snooze_select', array($this->ui, 'snooze_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.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->set_env('calendar_settings', $this->load_settings());
|
||||||
$this->rc->output->add_label('low','normal','high');
|
$this->rc->output->add_label('low','normal','high');
|
||||||
|
@ -344,7 +345,7 @@ class calendar extends rcube_plugin
|
||||||
switch ($action) {
|
switch ($action) {
|
||||||
case "new":
|
case "new":
|
||||||
// create UID for new event
|
// 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);
|
$success = $this->driver->new_event($event);
|
||||||
$reload = true;
|
$reload = true;
|
||||||
break;
|
break;
|
||||||
|
@ -370,13 +371,14 @@ class calendar extends rcube_plugin
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$success) {
|
if ($success)
|
||||||
|
$this->rc->output->show_message('successfullysaved', 'confirmation');
|
||||||
|
else
|
||||||
$this->rc->output->show_message('calendar.errorsaving', 'error');
|
$this->rc->output->show_message('calendar.errorsaving', 'error');
|
||||||
}
|
|
||||||
else if ($reload) {
|
if ($success && $reload)
|
||||||
$this->rc->output->command('plugin.reload_calendar', array());
|
$this->rc->output->command('plugin.reload_calendar', array());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for load-requests from fullcalendar
|
* Handler for load-requests from fullcalendar
|
||||||
|
@ -433,6 +435,7 @@ class calendar extends rcube_plugin
|
||||||
$settings['default_view'] = (string)$this->rc->config->get('calendar_default_view', "agendaWeek");
|
$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_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_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['time_format'] = (string)$this->rc->config->get('calendar_time_format', "HH:mm");
|
||||||
$settings['timeslots'] = (int)$this->rc->config->get('calendar_timeslots', 2);
|
$settings['timeslots'] = (int)$this->rc->config->get('calendar_timeslots', 2);
|
||||||
$settings['first_day'] = (int)$this->rc->config->get('calendar_first_day', 1);
|
$settings['first_day'] = (int)$this->rc->config->get('calendar_first_day', 1);
|
||||||
|
@ -516,10 +519,6 @@ class calendar extends rcube_plugin
|
||||||
if ($event['recurrence'])
|
if ($event['recurrence'])
|
||||||
$event['recurrence_text'] = $this->_recurrence_text($event['recurrence']);
|
$event['recurrence_text'] = $this->_recurrence_text($event['recurrence']);
|
||||||
|
|
||||||
// TEMPORARY: recurring instances are immutable
|
|
||||||
if ($event['recurrence_id'])
|
|
||||||
$event['editable'] = false;
|
|
||||||
|
|
||||||
$json[] = array(
|
$json[] = array(
|
||||||
'start' => date('c', $event['start']), // ISO 8601 date (added in PHP 5)
|
'start' => date('c', $event['start']), // ISO 8601 date (added in PHP 5)
|
||||||
'end' => date('c', $event['end']), // 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);
|
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
|
* Helper function to convert alarm trigger strings
|
||||||
* into two-field values (e.g. "-45M" => 45, "-M")
|
* into two-field values (e.g. "-45M" => 45, "-M")
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/*
|
/*
|
||||||
+-------------------------------------------------------------------------+
|
+-------------------------------------------------------------------------+
|
||||||
| Configuration for the Calendar plugin |
|
| 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 |
|
| This program is free software; you can redistribute it and/or modify |
|
||||||
| it under the terms of the GNU General Public License version 2 |
|
| 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)
|
// short date format (used for column titles)
|
||||||
$rcmail_config['calendar_date_short'] = 'M-d';
|
$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)
|
// timeslots per hour (1, 2, 3, 4, 6)
|
||||||
$rcmail_config['calendar_timeslots'] = 2;
|
$rcmail_config['calendar_timeslots'] = 2;
|
||||||
|
|
||||||
|
@ -47,8 +50,11 @@ $rcmail_config['calendar_first_day'] = 1;
|
||||||
$rcmail_config['calendar_first_hour'] = 6;
|
$rcmail_config['calendar_first_hour'] = 6;
|
||||||
|
|
||||||
// event categories
|
// event categories
|
||||||
$rcmail_config['calendar_categories'] = array('Personal' => 'c0c0c0',
|
$rcmail_config['calendar_categories'] = array(
|
||||||
|
'Personal' => 'c0c0c0',
|
||||||
'Work' => 'ff0000',
|
'Work' => 'ff0000',
|
||||||
'Family' => '00ff00',
|
'Family' => '00ff00',
|
||||||
'Holiday' => 'ff6600');
|
'Holiday' => 'ff6600',
|
||||||
|
);
|
||||||
|
|
||||||
?>
|
?>
|
|
@ -50,6 +50,7 @@
|
||||||
* 'priority' => 1|0|2, // Event priority (0=low, 1=normal, 2=high)
|
* 'priority' => 1|0|2, // Event priority (0=low, 1=normal, 2=high)
|
||||||
* 'sensitivity' => 0|1|2, // Event sensitivity (0=public, 1=private, 2=confidential)
|
* '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)
|
* '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'],
|
$event['calendar'],
|
||||||
strval($event['uid']),
|
strval($event['uid']),
|
||||||
intval($event['allday']),
|
intval($event['all_day']),
|
||||||
$event['recurrence'],
|
$event['recurrence'],
|
||||||
strval($event['title']),
|
strval($event['title']),
|
||||||
strval($event['description']),
|
strval($event['description']),
|
||||||
|
@ -178,37 +178,88 @@ class database_driver extends calendar_driver
|
||||||
* Update an event entry with the given data
|
* Update an event entry with the given data
|
||||||
*
|
*
|
||||||
* @param array Hash array with event properties
|
* @param array Hash array with event properties
|
||||||
* @see Driver:new_event()
|
* @see Driver:edit_event()
|
||||||
*/
|
*/
|
||||||
public function edit_event($event)
|
public function edit_event($event)
|
||||||
{
|
{
|
||||||
if (!empty($this->calendars)) {
|
if (!empty($this->calendars)) {
|
||||||
$event = $this->_save_preprocess($event);
|
$update_master = false;
|
||||||
$query = $this->rc->db->query(sprintf(
|
$update_recurring = true;
|
||||||
"UPDATE " . $this->db_events . "
|
$old = $this->get_event($event['id']);
|
||||||
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']
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($success = $this->rc->db->affected_rows($query))
|
// modify a recurring event, check submitted savemode to do the right things
|
||||||
$this->_update_recurring($event);
|
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;
|
return $success;
|
||||||
}
|
}
|
||||||
|
@ -226,7 +277,9 @@ class database_driver extends calendar_driver
|
||||||
$event['_exdates'] = (array)$event['recurrence']['EXDATE'];
|
$event['_exdates'] = (array)$event['recurrence']['EXDATE'];
|
||||||
$event['recurrence'] = rtrim($rrule, ';');
|
$event['recurrence'] = rtrim($rrule, ';');
|
||||||
$event['free_busy'] = intval($this->free_busy_map[strtolower($event['free_busy'])]);
|
$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
|
// compute absolute time to notify the user
|
||||||
$event['notifyat'] = $this->_get_notification($event);
|
$event['notifyat'] = $this->_get_notification($event);
|
||||||
|
@ -267,6 +320,43 @@ class database_driver extends calendar_driver
|
||||||
return null;
|
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
|
* Insert "fake" entries for recurring occurences of this event
|
||||||
*/
|
*/
|
||||||
|
@ -320,7 +410,6 @@ class database_driver extends calendar_driver
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -331,29 +420,8 @@ class database_driver extends calendar_driver
|
||||||
*/
|
*/
|
||||||
public function move_event($event)
|
public function move_event($event)
|
||||||
{
|
{
|
||||||
if (!empty($this->calendars)) {
|
// let edit_event() do all the magic
|
||||||
$event = $this->_save_preprocess($event + (array)$this->get_event($event['id']));
|
return $this->edit_event($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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -364,28 +432,8 @@ class database_driver extends calendar_driver
|
||||||
*/
|
*/
|
||||||
public function resize_event($event)
|
public function resize_event($event)
|
||||||
{
|
{
|
||||||
if (!empty($this->calendars)) {
|
// let edit_event() do all the magic
|
||||||
$event = $this->_save_preprocess($event + (array)$this->get_event($event['id']));
|
return $this->edit_event($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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -397,14 +445,67 @@ class database_driver extends calendar_driver
|
||||||
public function remove_event($event)
|
public function remove_event($event)
|
||||||
{
|
{
|
||||||
if (!empty($this->calendars)) {
|
if (!empty($this->calendars)) {
|
||||||
|
$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(
|
$query = $this->rc->db->query(
|
||||||
"DELETE FROM " . $this->db_events . "
|
"DELETE FROM " . $this->db_events . "
|
||||||
WHERE (event_id=? OR recurrence_id=?)
|
WHERE (event_id=? OR recurrence_id=?)
|
||||||
AND calendar_id IN (" . $this->calendar_ids . ")",
|
AND calendar_id IN (" . $this->calendar_ids . ")",
|
||||||
$event['id'],
|
$master['id'],
|
||||||
$event['id']
|
$master['id']
|
||||||
);
|
);
|
||||||
return $this->rc->db->affected_rows($query);
|
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;
|
return false;
|
||||||
|
@ -417,6 +518,11 @@ class database_driver extends calendar_driver
|
||||||
*/
|
*/
|
||||||
public function get_event($id)
|
public function get_event($id)
|
||||||
{
|
{
|
||||||
|
static $cache = array();
|
||||||
|
|
||||||
|
if ($cache[$id])
|
||||||
|
return $cache[$id];
|
||||||
|
|
||||||
$result = $this->rc->db->query(sprintf(
|
$result = $this->rc->db->query(sprintf(
|
||||||
"SELECT * FROM " . $this->db_events . "
|
"SELECT * FROM " . $this->db_events . "
|
||||||
WHERE calendar_id IN (%s)
|
WHERE calendar_id IN (%s)
|
||||||
|
@ -425,8 +531,10 @@ class database_driver extends calendar_driver
|
||||||
),
|
),
|
||||||
$id);
|
$id);
|
||||||
|
|
||||||
if ($result && ($event = $this->rc->db->fetch_assoc($result)))
|
if ($result && ($event = $this->rc->db->fetch_assoc($result))) {
|
||||||
return $this->_read_postprocess($event);
|
$cache[$id] = $this->_read_postprocess($event);
|
||||||
|
return $cache[$id];
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
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;
|
return $event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -528,7 +636,7 @@ class database_driver extends calendar_driver
|
||||||
$result = $this->rc->db->query(sprintf(
|
$result = $this->rc->db->query(sprintf(
|
||||||
"SELECT * FROM " . $this->db_events . "
|
"SELECT * FROM " . $this->db_events . "
|
||||||
WHERE calendar_id IN (%s)
|
WHERE calendar_id IN (%s)
|
||||||
AND notifyat <= %s AND end <= %s",
|
AND notifyat <= %s AND end > %s",
|
||||||
join(',', $calendar_ids),
|
join(',', $calendar_ids),
|
||||||
$this->rc->db->fromunixtime($time),
|
$this->rc->db->fromunixtime($time),
|
||||||
$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));
|
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
|
* Generate the form for recurrence settings
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -89,6 +89,7 @@ $labels['tabattachments'] = 'Attachments';
|
||||||
$labels['deleteventconfirm'] = "Do you really want to delete this event?";
|
$labels['deleteventconfirm'] = "Do you really want to delete this event?";
|
||||||
$labels['errorsaving'] = "Failed to save changes";
|
$labels['errorsaving'] = "Failed to save changes";
|
||||||
$labels['operationfailed'] = "The requested operation failed";
|
$labels['operationfailed'] = "The requested operation failed";
|
||||||
|
$labels['invalideventdates'] = "Invalid dates entered! Please check your input.";
|
||||||
|
|
||||||
// recurrence form
|
// recurrence form
|
||||||
$labels['repeat'] = 'Repeat';
|
$labels['repeat'] = 'Repeat';
|
||||||
|
@ -117,4 +118,13 @@ $labels['fourth'] = 'fourth';
|
||||||
$labels['last'] = 'last';
|
$labels['last'] = 'last';
|
||||||
$labels['dayofmonth'] = 'Day of month';
|
$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 {
|
#eventedit {
|
||||||
|
position: relative;
|
||||||
padding: 0.5em 0.1em;
|
padding: 0.5em 0.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,6 +322,36 @@ td.topalign {
|
||||||
padding: 0.2em 0;
|
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 {
|
span.edit-alarm-set {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
@ -400,6 +431,7 @@ a.alarm-action-snooze:after {
|
||||||
height: 160px;
|
height: 160px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* fullcalendar style overrides */
|
/* fullcalendar style overrides */
|
||||||
|
|
||||||
.fc-event-title {
|
.fc-event-title {
|
||||||
|
|
|
@ -160,6 +160,8 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<roundcube:object name="plugin.edit_recurring_warning" class="edit-recurring-warning" style="display:none" />
|
||||||
</div>
|
</div>
|
||||||
<div id="alarm-snooze-dropdown" class="popupmenu">
|
<div id="alarm-snooze-dropdown" class="popupmenu">
|
||||||
<roundcube:object name="plugin.snooze_select" type="ul" />
|
<roundcube:object name="plugin.snooze_select" type="ul" />
|
||||||
|
|
Loading…
Add table
Reference in a new issue