Ask about notifying attendess when moving event; finish import from ical mail attachments

This commit is contained in:
Thomas Bruederli 2011-07-29 17:51:04 +02:00
parent 0f266481dd
commit 593a3d3faa
10 changed files with 347 additions and 100 deletions

View file

@ -120,6 +120,7 @@ class calendar extends rcube_plugin
$this->register_action('freebusy-times', array($this, 'freebusy_times'));
$this->register_action('randomdata', array($this, 'generate_randomdata'));
$this->register_action('print', array($this,'print_view'));
$this->register_action('mailimportevent', array($this, 'mail_import_event'));
// remove undo information...
if ($undo = $_SESSION['calendar_event_undo']) {
@ -137,9 +138,12 @@ class calendar extends rcube_plugin
$this->add_hook('preferences_list', array($this, 'preferences_list'));
$this->add_hook('preferences_save', array($this, 'preferences_save'));
}
else if ($this->rc->task == 'mail' && ($this->rc->action == 'show' || $this->rc->action == 'preview')) {
else if ($this->rc->task == 'mail') {
// hooks to catch event invitations on incoming mails
if ($this->rc->action == 'show' || $this->rc->action == 'preview') {
$this->add_hook('message_load', array($this, 'mail_message_load'));
$this->add_hook('template_object_messagebody', array($this, 'mail_messagebody_html'));
}
}
// add hook to display alarms
@ -583,7 +587,7 @@ class calendar extends rcube_plugin
}
// send out notifications
if ($success && $event['_notify'] && $event['attendees']) {
if ($success && $event['_notify'] && ($event['attendees'] || $old['attendees'])) {
// make sure we have the complete record
$event = $this->driver->get_event($event);
@ -657,10 +661,10 @@ class calendar extends rcube_plugin
$events = $this->driver->load_events($start, $end, null, $calendar_name, 0);
header("Content-Type: text/calendar");
header("Content-Disposition: inline; filename=".$calendar_name);
header("Content-Disposition: inline; filename=".$calendar_name.'.ics');
$this->load_ical();
echo $this->ical->export($events);
$this->ical->export($events, '', true);
exit;
}
@ -841,7 +845,7 @@ class calendar extends rcube_plugin
break;
}
if ($rrule['INTERVAL'] == 1)
if ($rrule['INTERVAL'] <= 1)
$freq = $this->gettext(strtolower($rrule['FREQ']));
if ($rrule['COUNT'])
@ -1526,7 +1530,7 @@ class calendar extends rcube_plugin
}
/**
* Add UI elements to copy event invitations or updates to the calendar
* Add UI element to copy event invitations or updates to the calendar
*/
public function mail_messagebody_html($p)
{
@ -1545,6 +1549,7 @@ class calendar extends rcube_plugin
if (empty($events))
continue;
// TODO: show more iTip options like (accept, deny, etc.)
foreach ($events as $idx => $event) {
// add box below messsage body
$html .= html::p('calendar-invitebox',
@ -1553,7 +1558,7 @@ class calendar extends rcube_plugin
html::tag('input', array(
'type' => 'button',
'class' => 'button',
# 'onclick' => "rcube_calendar.add_event_from_mail('" . JQ($part.':'.$idx) . "', '" . JQ($event['title']) . "')",
'onclick' => "rcube_calendar.add_event_from_mail('" . JQ($part.':'.$idx) . "', '" . JQ($event['title']) . "')",
'value' => $this->gettext('importtocalendar'),
))
);
@ -1569,6 +1574,75 @@ class calendar extends rcube_plugin
return $p;
}
/**
* Handler for POST request to import an event attached to a mail message
*/
public function mail_import_event()
{
$uid = get_input_value('_uid', RCUBE_INPUT_POST);
$mbox = get_input_value('_mbox', RCUBE_INPUT_POST);
$mime_id = get_input_value('_part', RCUBE_INPUT_POST);
// establish imap connection
$this->rc->imap_connect();
$this->rc->imap->set_mailbox($mbox);
if ($uid && $mime_id) {
list($mime_id, $index) = explode(':', $mime_id);
$part = $this->rc->imap->get_message_part($uid, $mime_id);
}
$this->load_ical();
$events = $this->ical->import($part);
$error_msg = $this->gettext('errorimportingevent');
$success = false;
// successfully parsed events?
if (!empty($events) && ($event = $events[$index])) {
// find writeable calendar to store event
$cal_id = $this->rc->config->get('calendar_default_calendar');
$calendars = $this->driver->list_calendars();
$calendar = $calendars[$cal_id] ? $calendars[$calname] : null;
if (!$calendar || $calendar['readonly']) {
foreach ($calendars as $cal) {
if (!$cal['readonly']) {
$calendar = $cal;
break;
}
}
}
if ($calendar && !$calendar['readonly']) {
$event['id'] = $event['uid'];
$event['calendar'] = $calendar['id'];
// check for existing event with the same UID
$existing = $this->driver->get_event($event);
if ($existing) {
if ($event['changed'] >= $existing['changed'])
$success = $this->driver->edit_event($event);
else
$error_msg = $this->gettext('newerversionexists');
}
else if (!$existing) {
$success = $this->driver->new_event($event);
}
}
else
$error_msg = $this->gettext('nowritecalendarfound');
}
if ($success)
$this->rc->output->command('display_message', $this->gettext(array('name' => 'importedsuccessfully', 'vars' => array('calendar' => $calendar['name']))), 'confirmation');
else
$this->rc->output->command('display_message', $error_msg, 'error');
$this->rc->output->send();
}
/**
* Checks if specified message part is a vcalendar data
*

View file

@ -166,6 +166,15 @@ function rcube_calendar(settings)
}
// static methods
rcube_calendar.add_event_from_mail = function(mime_id, title)
{
var lock = rcmail.set_busy(true, 'loading');
rcmail.http_post('calendar/mailimportevent', '_uid='+rcmail.env.uid+'&_mbox='+urlencode(rcmail.env.mailbox)+'&_part='+urlencode(mime_id), lock);
return false;
};
// extend jQuery
(function($){
$.fn.serializeJSON = function(){
@ -183,5 +192,10 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
var cal = new rcube_calendar(rcmail.env.calendar_settings);
rcmail.addEventListener('plugin.display_alarms', function(alarms){ cal.display_alarms(alarms); });
}
rcmail.addEventListener('plugin.ping_url', function(p){
var action = p.action;
p.action = p.event = null;
new Image().src = rcmail.url(action, p);
});
});

View file

@ -140,6 +140,12 @@ function rcube_calendar_ui(settings)
return date.getHours() >= settings['work_start'] && date.getHours() < settings['work_end'];
};
// check if the event has 'real' attendees, excluding the current user
var has_attendees = function(event)
{
return (event.attendees && (event.attendees.length > 1 || event.attendees[0].email != settings.event_owner.email));
};
// create a nice human-readable string for the date/time range
var event_date_text = function(event)
{
@ -465,7 +471,7 @@ function rcube_calendar_ui(settings)
for (var j=0; j < event.attendees.length; j++)
add_attendee(event.attendees[j], true);
if (event.attendees.length > 1 || event.attendees[0].email != settings.event_owner.email) {
if (has_attendees(event)) {
notify.checked = invite.checked = true; // enable notification by default
}
}
@ -1292,43 +1298,86 @@ function rcube_calendar_ui(settings)
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>');
// display confirm dialog when modifying/deleting an event
var update_event_confirm = function(action, event, data)
{
if (!data) data = event;
var html = '';
$dialog.find('a.button').button().click(function(e){
event.savemode = String(this.href).replace(/.+#/, '');
update_event(action, event);
$dialog.dialog("destroy").hide();
return false;
});
// event has attendees, ask whether to notify them
if (has_attendees(event)) {
html += '<div class="message">' +
'<label><input class="confirm-attendees-donotify" type="checkbox" checked="checked" value="1" name="notify" />&nbsp;' +
rcmail.gettext('sendnotifications', 'calendar') +
'</label></div>';
}
$dialog.dialog({
modal: true,
width: 420,
dialogClass: 'warning',
title: rcmail.gettext((action == 'remove' ? 'removerecurringevent' : 'changerecurringevent'), 'calendar'),
buttons: [
{
text: rcmail.gettext('cancel', 'calendar'),
// recurring event: user needs to select the savemode
if (event.recurrence) {
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>';
}
// show dialog
if (html) {
var $dialog = $('<div>').html(html);
$dialog.find('a.button').button().click(function(e){
data.savemode = String(this.href).replace(/.+#/, '');
if ($dialog.find('input.confirm-attendees-donotify').get(0))
data._notify = $dialog.find('input.confirm-attendees-donotify').get(0).checked ? 1 : 0;
update_event(action, data);
$dialog.dialog("destroy").hide();
return false;
});
var buttons = [{
text: rcmail.gettext('cancel', 'calendar'),
click: function() {
$(this).dialog("close");
}
}];
if (!event.recurrence) {
buttons.push({
text: rcmail.gettext((action == 'remove' ? 'remove' : 'save'), 'calendar'),
click: function() {
data._notify = $dialog.find('input.confirm-attendees-donotify').get(0).checked ? 1 : 0;
update_event(action, data);
$(this).dialog("close");
}
}
],
close: function(){
$dialog.dialog("destroy").hide();
fc.fullCalendar('refetchEvents');
});
}
}).show();
$dialog.dialog({
modal: true,
width: 420,
dialogClass: 'warning',
title: rcmail.gettext((action == 'remove' ? 'removeeventconfirm' : 'changeeventconfirm'), 'calendar'),
buttons: buttons,
close: function(){
$dialog.dialog("destroy").hide();
if (!rcmail.busy)
fc.fullCalendar('refetchEvents');
}
}).addClass('event-update-confirm').show();
return false;
}
// show regular confirm box when deleting
else if (action == 'remove') {
if (!confirm(rcmail.gettext('deleteventconfirm', 'calendar')))
return false;
}
// do update
update_event(action, data);
return true;
};
@ -1361,17 +1410,8 @@ function rcube_calendar_ui(settings)
// 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, calendar:event.calendar }, 'remove');
// send remove request to plugin
if (confirm(rcmail.gettext('deleteventconfirm', 'calendar'))) {
update_event('remove', { id:event.id, calendar:event.calendar });
return true;
}
return false;
// show confirm dialog for recurring events, use jquery UI dialog
return update_event_confirm('remove', event, { id:event.id, calendar:event.calendar });
};
// opens a jquery UI dialog with event properties (or empty for creating a new calendar)
@ -1752,10 +1792,7 @@ function rcube_calendar_ui(settings)
end: date2unixtime(event.end),
allday: allDay?1:0
};
if (event.recurrence)
recurring_edit_confirm(data, 'move');
else
update_event('move', data);
update_event_confirm('move', event, data);
},
// callback for event resizing
eventResize: function(event, delta) {
@ -1766,10 +1803,7 @@ function rcube_calendar_ui(settings)
start: date2unixtime(event.start),
end: date2unixtime(event.end)
};
if (event.recurrence)
recurring_edit_confirm(data, 'resize');
else
update_event('resize', data);
update_event_confirm('resize', event, data);
},
viewDisplay: function(view) {
me.eventcount = [];
@ -2046,7 +2080,6 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
rcmail.addEventListener('plugin.reload_calendar', function(p){ $('#calendar').fullCalendar('refetchEvents', cal.calendars[p.source]); });
rcmail.addEventListener('plugin.destroy_source', function(p){ cal.calendar_destroy_source(p.id); });
rcmail.addEventListener('plugin.unlock_saving', function(p){ rcmail.set_busy(false, null, cal.saving_lock); });
rcmail.addEventListener('plugin.ping_url', function(p){ p.event = null; new Image().src = rcmail.url(p.action, p); });
// let's go
var cal = new rcube_calendar_ui(rcmail.env.calendar_settings);

View file

@ -33,6 +33,7 @@
* 'start' => <unixtime>, // Event start date/time as unix timestamp
* 'end' => <unixtime>, // Event end date/time as unix timestamp
* 'allday' => true|false, // Boolean flag if this is an all-day event
* 'changed' => <unixtime>, // Last modification date of event
* 'title' => 'Event title/summary',
* 'location' => 'Location string',
* 'description' => 'Event description',

View file

@ -686,6 +686,7 @@ class database_driver extends calendar_driver
$event['start'] = strtotime($event['start']);
$event['end'] = strtotime($event['end']);
$event['allday'] = intval($event['all_day']);
$event['changed'] = strtotime($event['changed']);
$event['free_busy'] = $free_busy_map[$event['free_busy']];
$event['calendar'] = $event['calendar_id'];
$event['recurrence_id'] = intval($event['recurrence_id']);

View file

@ -561,6 +561,7 @@ class kolab_calendar
'free_busy' => $rec['show-time-as'],
'priority' => isset($priority_map[$rec['priority']]) ? $priority_map[$rec['priority']] : 1,
'sensitivity' => $sensitivity_map[$rec['sensitivity']],
'changed' => $rec['last-modification-date'],
'calendar' => $this->id,
);
}

View file

@ -310,7 +310,7 @@ class kolab_driver extends calendar_driver
$success = $storage->insert_event($event);
if ($success)
$this->rc->output->command('plugin.ping_url', array('action' => 'push-freebusy', 'source' => $storage->id));
$this->rc->output->command('plugin.ping_url', array('action' => 'calendar/push-freebusy', 'source' => $storage->id));
return $success;
}
@ -405,7 +405,7 @@ class kolab_driver extends calendar_driver
}
if ($success)
$this->rc->output->command('plugin.ping_url', array('action' => 'push-freebusy', 'source' => $storage->id));
$this->rc->output->command('plugin.ping_url', array('action' => 'calendar/push-freebusy', 'source' => $storage->id));
return $success;
}
@ -558,7 +558,7 @@ class kolab_driver extends calendar_driver
}
if ($success)
$this->rc->output->command('plugin.ping_url', array('action' => 'push-freebusy', 'source' => $storage->id));
$this->rc->output->command('plugin.ping_url', array('action' => 'calendar/push-freebusy', 'source' => $storage->id));
return $success;
}

View file

@ -78,11 +78,13 @@ class calendar_ical
{
$event = array(
'uid' => $ve->getAttributeDefault('UID'),
'changed' => $ve->getAttributeDefault('DTSTAMP', 0),
'title' => $ve->getAttributeDefault('SUMMARY'),
'description' => $ve->getAttributeDefault('DESCRIPTION'),
'location' => $ve->getAttributeDefault('LOCATION'),
'start' => $ve->getAttribute('DTSTART'),
'end' => $ve->getAttribute('DTEND'),
// set defaults
'free_busy' => 'busy',
'priority' => 1,
);
// check for all-day dates
@ -94,10 +96,96 @@ class calendar_ical
$event['end'] = gmmktime(0, 0, 0, $event['end']['month'], $event['end']['mday'], $event['end']['year']) + $this->cal->gmt_offset - 60;
}
// TODO: complete this
// map other attributes to internal fields
$_attendees = array();
foreach ($ve->getAllAttributes() as $attr) {
switch ($attr['name']) {
case 'ORGANIZER':
$organizer = array(
'name' => $attr['params']['CN'],
'email' => preg_replace('/^mailto:/', '', $attr['value']),
'role' => 'ORGANIZER',
'status' => 'ACCEPTED',
);
if (isset($_attendees[$organizer['email']])) {
$i = $_attendees[$organizer['email']];
$event['attendees'][$i]['role'] = $organizer['role'];
}
break;
case 'ATTENDEE':
$attendee = array(
'name' => $attr['params']['CN'],
'email' => preg_replace('/^mailto:/', '', $attr['value']),
'role' => $attr['params']['ROLE'] ? $attr['params']['ROLE'] : 'REQ-PARTICIPANT',
'status' => $attr['params']['PARTSTAT'],
'rsvp' => $attr['params']['RSVP'] == 'TRUE',
);
if ($organizer && $organizer['email'] == $attendee['email'])
$attendee['role'] = 'ORGANIZER';
// make sure event has an UID
$event['attendees'][] = $attendee;
$_attendees[$attendee['email']] = count($event['attendees']) - 1;
break;
case 'TRANSP':
$event['free_busy'] = $attr['value'] == 'TRANSPARENT' ? 'free' : 'busy';
break;
case 'STATUS':
if ($attr['value'] == 'TENTATIVE')
$event['free_busy'] == 'tentative';
break;
case 'PRIORITY':
if (is_numeric($attr['value'])) {
$event['priority'] = $attr['value'] <= 4 ? 2 /* high */ :
($attr['value'] == 5 ? 1 /* normal */ : 0 /* low */);
}
break;
case 'RRULE':
// parse recurrence rule attributes
foreach (explode(';', $attr['value']) as $par) {
list($k, $v) = explode('=', $par);
$params[$k] = $v;
}
if ($params['UNTIL'])
$params['UNTIL'] = $ve->_parseDateTime($params['UNTIL']);
if (!$params['INTERVAL'])
$params['INTERVAL'] = 1;
$event['recurrence'] = $params;
break;
case 'EXDATE':
break;
case 'DESCRIPTION':
case 'LOCATION':
$event[strtolower($attr['name'])] = $attr['value'];
break;
case 'CLASS':
case 'X-CALENDARSERVER-ACCESS':
$sensitivity_map = array('PUBLIC' => 0, 'PRIVATE' => 1, 'CONFIDENTIAL' => 2);
$event['sensitivity'] = $sensitivity_map[$attr['value']];
break;
case 'X-MICROSOFT-CDO-BUSYSTATUS':
if ($attr['value'] == 'OOF')
$event['free_busy'] == 'outofoffice';
else if (in_array($attr['value'], array('FREE', 'BUSY', 'TENTATIVE')))
$event['free_busy'] = strtolower($attr['value']);
break;
}
}
// add organizer to attendees list if not already present
if ($organizer && !isset($_attendees[$organizer['email']]))
array_unshift($event['attendees'], $organizer);
// make sure the event has an UID
if (!$event['uid'])
$event['uid'] = $this->cal->$this->generate_uid();
@ -108,10 +196,12 @@ class calendar_ical
/**
* Export events to iCalendar format
*
* @param array Events as array
* @param array Events as array
* @param string VCalendar method to advertise
* @param boolean Directly send data to stdout instead of returning
* @return string Events in iCalendar format (http://tools.ietf.org/html/rfc5545)
*/
public function export($events, $method = null)
public function export($events, $method = null, $write = false)
{
if (!empty($this->rc->user->ID)) {
$ical = "BEGIN:VCALENDAR" . self::EOL;
@ -122,56 +212,72 @@ class calendar_ical
if ($method)
$ical .= "METHOD:" . strtoupper($method) . self::EOL;
if ($write) {
echo $ical;
$ical = '';
}
foreach ($events as $event) {
$ical .= "BEGIN:VEVENT" . self::EOL;
$ical .= "UID:" . self::escpape($event['uid']) . self::EOL;
$vevent = "BEGIN:VEVENT" . self::EOL;
$vevent .= "UID:" . self::escpape($event['uid']) . self::EOL;
$vevent .= "DTSTAMP:" . gmdate('Ymd\THis\Z', $event['changed'] ? $event['changed'] : time()) . self::EOL;
// correctly set all-day dates
if ($event['allday']) {
$ical .= "DTSTART;VALUE=DATE:" . gmdate('Ymd', $event['start'] + $this->cal->gmt_offset) . self::EOL;
$ical .= "DTEND;VALUE=DATE:" . gmdate('Ymd', $event['end'] + $this->cal->gmt_offset + 60) . self::EOL; // ends the next day
$vevent .= "DTSTART;VALUE=DATE:" . gmdate('Ymd', $event['start'] + $this->cal->gmt_offset) . self::EOL;
$vevent .= "DTEND;VALUE=DATE:" . gmdate('Ymd', $event['end'] + $this->cal->gmt_offset + 60) . self::EOL; // ends the next day
}
else {
$ical .= "DTSTART:" . gmdate('Ymd\THis\Z', $event['start']) . self::EOL;
$ical .= "DTEND:" . gmdate('Ymd\THis\Z', $event['end']) . self::EOL;
$vevent .= "DTSTART:" . gmdate('Ymd\THis\Z', $event['start']) . self::EOL;
$vevent .= "DTEND:" . gmdate('Ymd\THis\Z', $event['end']) . self::EOL;
}
$ical .= "SUMMARY:" . self::escpape($event['title']) . self::EOL;
$ical .= "DESCRIPTION:" . self::escpape($event['description']) . self::EOL;
$vevent .= "SUMMARY:" . self::escpape($event['title']) . self::EOL;
$vevent .= "DESCRIPTION:" . self::escpape($event['description']) . self::EOL;
if (!empty($event['attendees'])){
$ical .= $this->_get_attendees($event['attendees']);
$vevent .= $this->_get_attendees($event['attendees']);
}
if (!empty($event['location'])) {
$ical .= "LOCATION:" . self::escpape($event['location']) . self::EOL;
$vevent .= "LOCATION:" . self::escpape($event['location']) . self::EOL;
}
if ($event['recurrence']) {
$ical .= "RRULE:" . calendar::to_rrule($event['recurrence']) . self::EOL;
$vevent .= "RRULE:" . calendar::to_rrule($event['recurrence'], self::EOL) . self::EOL;
}
if(!empty($event['categories'])) {
$ical .= "CATEGORIES:" . self::escpape(strtoupper($event['categories'])) . self::EOL;
$vevent .= "CATEGORIES:" . self::escpape(strtoupper($event['categories'])) . self::EOL;
}
if ($event['sensitivity'] > 0) {
$ical .= "X-CALENDARSERVER-ACCESS:CONFIDENTIAL";
$vevent .= "CLASS:" . ($event['sensitivity'] == 2 ? 'CONFIDENTIAL' : 'PRIVATE') . self::EOL;
}
if ($event['alarms']) {
list($trigger, $action) = explode(':', $event['alarms']);
$val = calendar::parse_alaram_value($trigger);
$ical .= "BEGIN:VALARM\n";
if ($val[1]) $ical .= "TRIGGER:" . preg_replace('/^([-+])(.+)/', '\\1PT\\2', $trigger) . self::EOL;
else $ical .= "TRIGGER;VALUE=DATE-TIME:" . gmdate('Ymd\THis\Z', $val[0]) . self::EOL;
if ($action) $ical .= "ACTION:" . self::escpape(strtoupper($action)) . self::EOL;
$ical .= "END:VALARM\n";
$vevent .= "BEGIN:VALARM\n";
if ($val[1]) $vevent .= "TRIGGER:" . preg_replace('/^([-+])(.+)/', '\\1PT\\2', $trigger) . self::EOL;
else $vevent .= "TRIGGER;VALUE=DATE-TIME:" . gmdate('Ymd\THis\Z', $val[0]) . self::EOL;
if ($action) $vevent .= "ACTION:" . self::escpape(strtoupper($action)) . self::EOL;
$vevent .= "END:VALARM\n";
}
$ical .= "TRANSP:" . ($event['free_busy'] == 'free' ? 'TRANSPARENT' : 'OPAQUE') . self::EOL;
$vevent .= "TRANSP:" . ($event['free_busy'] == 'free' ? 'TRANSPARENT' : 'OPAQUE') . self::EOL;
// TODO: export attachments
$ical .= "END:VEVENT" . self::EOL;
$vevent .= "END:VEVENT" . self::EOL;
if ($write)
echo rcube_vcard::rfc2425_fold($vevent);
else
$ical .= $vevent;
}
$ical .= "END:VCALENDAR" . self::EOL;
if ($write) {
echo $ical;
return true;
}
// fold lines to 75 chars
return rcube_vcard::rfc2425_fold($ical);
}

View file

@ -60,6 +60,7 @@ $labels['generated'] = 'generated at';
$labels['selectdate'] = 'Select date';
$labels['printdescriptions'] = 'Print descriptions';
$labels['parentcalendar'] = 'Superior calendar';
$labels['importtocalendar'] = 'Save to my calendar';
// alarm/reminder settings
$labels['alarmemail'] = 'Send Email';
@ -104,7 +105,7 @@ $labels['availtentative'] = 'Tentative';
$labels['availoutofoffice'] = 'Out of Office';
$labels['scheduletime'] = 'Find availability';
$labels['sendinvitations'] = 'Send invitations';
$labels['sendnotifications'] = 'Notify attendees about modifications';
$labels['sendnotifications'] = 'Notify participants about modifications';
$labels['onlyworkinghours'] = 'Find availability within my working hours';
$labels['reqallattendees'] = 'Required/all participants';
$labels['prevslot'] = 'Previous Slot';
@ -133,6 +134,10 @@ $labels['searchnoresults'] = 'No events found in the selected calendars.';
$labels['successremoval'] = 'The event has been deleted successfully.';
$labels['successrestore'] = 'The event has been restored successfully.';
$labels['errornotifying'] = 'Failed to send notifications to event participants';
$labels['errorimportingevent'] = 'Failed to import the event';
$labels['newerversionexists'] = 'A newer version of this event already exists! Aborted.';
$labels['nowritecalendarfound'] = 'No calendar found to save the event';
$labels['importedsuccessfully'] = 'The event was successfully added to \'$calendar\'';
// recurrence form
$labels['repeat'] = 'Repeat';
@ -162,8 +167,8 @@ $labels['fourth'] = 'fourth';
$labels['last'] = 'last';
$labels['dayofmonth'] = 'Day of month';
$labels['changerecurringevent'] = 'Change recurring event';
$labels['removerecurringevent'] = 'Remove recurring event';
$labels['changeeventconfirm'] = 'Change event';
$labels['removeeventconfirm'] = 'Remove 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';

View file

@ -472,14 +472,20 @@ td.topalign {
padding: 0.2em 0;
}
.edit-recurring-warning {
.ui-dialog .event-update-confirm {
padding: 0 0.5em 0.5em 0.5em;
}
.edit-recurring-warning,
.event-update-confirm .message {
margin-top: 0.5em;
padding: 0.8em;
background-color: #F7FDCB;
border: 1px solid #C2D071;
}
.edit-recurring-warning .message {
.edit-recurring-warning .message,
.event-update-confirm .message {
margin-bottom: 0.5em;
}
@ -487,17 +493,23 @@ td.topalign {
padding-left: 20px;
}
.edit-recurring-warning span.ui-icon {
.event-update-confirm .savemode {
padding-left: 30px;
}
.edit-recurring-warning span.ui-icon,
.event-update-confirm span.ui-icon {
float: left;
margin: 0 7px 20px 0;
}
.edit-recurring-warning label {
.edit-recurring-warning label,
.event-update-confirm label {
min-width: 3em;
padding-right: 1em;
}
.edit-recurring-warning a.button {
.event-update-confirm a.button {
margin: 0 0.5em 0 0.2em;
min-width: 5em;
}