diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index cb5b5a3a..722e64b9 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -2155,9 +2155,10 @@ class calendar extends rcube_plugin $response = $itip->get_itip_status($data, $existing); // get a list of writeable calendars to save new events to - if (!$existing && $response['action'] == 'rsvp' || $response['action'] == 'import') { + if (!$existing && !$data['nosave'] && $response['action'] == 'rsvp' || $response['action'] == 'import') { $calendars = $this->driver->list_calendars(false, true); $calendar_select = new html_select(array('name' => 'calendar', 'id' => 'itip-saveto', 'is_escaped' => true)); + $calendar_select->add('--', ''); $numcals = 0; foreach ($calendars as $calendar) { if (!$calendar['readonly']) { @@ -2174,6 +2175,9 @@ class calendar extends rcube_plugin $response['select'] = html::span('folder-select', $this->gettext('saveincalendar') . ' ' . $calendar_select->show($this->rc->config->get('calendar_default_calendar', $default_calendar['id']))); } + else if ($data['nosave']) { + $response['select'] = html::tag('input', array('type' => 'hidden', 'name' => 'calendar', 'id' => 'itip-saveto', 'value' => '')); + } $this->rc->output->command('plugin.update_itip_object_status', $response); } @@ -2390,8 +2394,13 @@ class calendar extends rcube_plugin if ($event = $this->lib->mail_get_itip_object($mbox, $uid, $mime_id, 'event')) { // find writeable calendar to store event $cal_id = !empty($_REQUEST['_folder']) ? get_input_value('_folder', RCUBE_INPUT_POST) : null; + $dontsave = ($_REQUEST['_folder'] === '' && $event['_method'] == 'REQUEST'); $calendars = $this->driver->list_calendars(false, true); - $calendar = $calendars[$cal_id] ?: $this->get_default_calendar(true); + $calendar = $calendars[$cal_id]; + + // select default calendar except user explicitly selected 'none' + if (!$calendar && !$dontsave) + $calendar = $this->get_default_calendar(true); $metadata = array( 'uid' => $event['uid'], @@ -2525,7 +2534,7 @@ class calendar extends rcube_plugin else if ($status == 'declined') $error_msg = null; } - else if ($status == 'declined') + else if ($status == 'declined' || $dontsave) $error_msg = null; else $error_msg = $this->gettext('nowritecalendarfound'); @@ -2534,14 +2543,18 @@ class calendar extends rcube_plugin if ($success) { $message = $event['_method'] == 'REPLY' ? 'attendeupdateesuccess' : ($deleted ? 'successremoval' : ($existing ? 'updatedsuccessfully' : 'importedsuccessfully')); $this->rc->output->command('display_message', $this->gettext(array('name' => $message, 'vars' => array('calendar' => $calendar['name']))), 'confirmation'); + } + if ($success || $dontsave) { + $metadata['nosave'] = $dontsave; $metadata['rsvp'] = intval($metadata['rsvp']); $metadata['after_action'] = $this->rc->config->get('calendar_itip_after_action', $this->defaults['calendar_itip_after_action']); $this->rc->output->command('plugin.itip_message_processed', $metadata); $error_msg = null; } - else if ($error_msg) + else if ($error_msg) { $this->rc->output->command('display_message', $error_msg, 'error'); + } // send iTip reply if ($event['_method'] == 'REQUEST' && $organizer && !$noreply && !in_array(strtolower($organizer['email']), $emails) && !$error_msg) { diff --git a/plugins/libcalendaring/lib/libcalendaring_itip.php b/plugins/libcalendaring/lib/libcalendaring_itip.php index 748aa439..0b21ed8c 100644 --- a/plugins/libcalendaring/lib/libcalendaring_itip.php +++ b/plugins/libcalendaring/lib/libcalendaring_itip.php @@ -434,6 +434,12 @@ class libcalendaring_itip $title = $event['sequence'] > 0 ? $this->gettext('itipupdate') : $this->gettext('itipinvitation'); $metadata['rsvp'] = true; + // check for X-KOLAB-INVITATIONTYPE property and only show accept/decline buttons + if (self::get_custom_property($event, 'X-KOLAB-INVITATIONTYPE') == 'CONFIRMATION') { + $this->rsvp_actions = array('accepted','declined'); + $metadata['nosave'] = true; + } + // 1. display RSVP buttons (if the user was invited) foreach ($this->rsvp_actions as $method) { $rsvp_buttons .= html::tag('input', array( @@ -471,7 +477,7 @@ class libcalendaring_itip } // add itip reply message controls - $rsvp_buttons .= html::div('itip-reply-controls', $this->itip_rsvp_options_ui($dom_id)); + $rsvp_buttons .= html::div('itip-reply-controls', $this->itip_rsvp_options_ui($dom_id, $metadata['nosave'])); $buttons[] = html::div(array('id' => 'rsvp-'.$dom_id, 'class' => 'rsvp-buttons', 'style' => 'display:none'), $rsvp_buttons); $buttons[] = html::div(array('id' => 'update-'.$dom_id, 'style' => 'display:none'), $update_button); @@ -507,7 +513,6 @@ class libcalendaring_itip $buttons[] = html::div(array('id' => 'import-'.$dom_id, 'style' => 'display:none'), $import_button); } - // TODO: add field for COMMENT on iTip replies // TODO: add option/checkbox to delete this message after update // pass some metadata about the event and trigger the asynchronous status check @@ -556,11 +561,11 @@ class libcalendaring_itip /** * Render UI elements to control iTip reply message sending */ - public function itip_rsvp_options_ui($dom_id) + public function itip_rsvp_options_ui($dom_id, $disable = false) { // add checkbox to suppress itip reply message $rsvp_additions = html::label(array('class' => 'noreply-toggle'), - html::tag('input', array('type' => 'checkbox', 'id' => 'noreply-'.$dom_id, 'value' => 1)) + html::tag('input', array('type' => 'checkbox', 'id' => 'noreply-'.$dom_id, 'value' => 1, 'disabled' => $disable)) . ' ' . $this->gettext('itipsuppressreply') ); @@ -626,4 +631,22 @@ class libcalendaring_itip return false; } + /** + * Utility function to get the value of a custom property + */ + public static function get_custom_property($event, $name) + { + $ret = false; + + if (is_array($event['x-custom'])) { + array_walk($event['x-custom'], function($prop, $i) use ($name, &$ret) { + if (strcasecmp($prop[0], $name) === 0) { + $ret = $prop[1]; + } + }); + } + + return $ret; + } + }