Changeset View
Changeset View
Standalone View
Standalone View
plugins/libcalendaring/lib/libcalendaring_itip.php
Show First 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | public function send_itip_message($event, $method, $recipient, $subject, $bodytext, $message = null, $rsvp = true) | ||||
'title' => $event['title'], | 'title' => $event['title'], | ||||
'name' => $this->sender['name'], | 'name' => $this->sender['name'], | ||||
) | ) | ||||
)); | )); | ||||
// compose a list of all event attendees | // compose a list of all event attendees | ||||
$attendees_list = array(); | $attendees_list = array(); | ||||
foreach ((array)$event['attendees'] as $attendee) { | foreach ((array)$event['attendees'] as $attendee) { | ||||
$attendees_list[] = ($attendee['name'] && $attendee['email']) ? | $attendees_list[] = (!empty($attendee['name']) && !empty($attendee['email'])) ? | ||||
$attendee['name'] . ' <' . $attendee['email'] . '>' : | $attendee['name'] . ' <' . $attendee['email'] . '>' : | ||||
($attendee['name'] ? $attendee['name'] : $attendee['email']); | (!empty($attendee['name']) ? $attendee['name'] : $attendee['email']); | ||||
} | } | ||||
$recurrence_info = ''; | $recurrence_info = ''; | ||||
if (!empty($event['recurrence_id'])) { | if (!empty($event['recurrence_id'])) { | ||||
$recurrence_info = "\n\n** " . $this->gettext($event['thisandfuture'] ? 'itipmessagefutureoccurrence' : 'itipmessagesingleoccurrence') . ' **'; | $msg = $this->gettext(!empty($event['thisandfuture']) ? 'itipmessagefutureoccurrence' : 'itipmessagesingleoccurrence'); | ||||
$recurrence_info = "\n\n** $msg **"; | |||||
} | } | ||||
else if (!empty($event['recurrence'])) { | else if (!empty($event['recurrence'])) { | ||||
$recurrence_info = sprintf("\n%s: %s", $this->gettext('recurring'), $this->lib->recurrence_text($event['recurrence'])); | $recurrence_info = sprintf("\n%s: %s", $this->gettext('recurring'), $this->lib->recurrence_text($event['recurrence'])); | ||||
} | } | ||||
$mailbody = $this->gettext(array( | $mailbody = $this->gettext(array( | ||||
'name' => $bodytext, | 'name' => $bodytext, | ||||
'vars' => array( | 'vars' => array( | ||||
'title' => $event['title'], | 'title' => $event['title'], | ||||
'date' => $this->lib->event_date_text($event, true) . $recurrence_info, | 'date' => $this->lib->event_date_text($event, true) . $recurrence_info, | ||||
'attendees' => join(",\n ", $attendees_list), | 'attendees' => join(",\n ", $attendees_list), | ||||
'sender' => $this->sender['name'], | 'sender' => $this->sender['name'], | ||||
'organizer' => $this->sender['name'], | 'organizer' => $this->sender['name'], | ||||
'description' => $event['description'], | 'description' => isset($event['description']) ? $event['description'] : '', | ||||
) | ) | ||||
)); | )); | ||||
// remove redundant empty lines (e.g. when an event description is empty) | // remove redundant empty lines (e.g. when an event description is empty) | ||||
$mailbody = preg_replace('/\n{3,}/', "\n\n", $mailbody); | $mailbody = preg_replace('/\n{3,}/', "\n\n", $mailbody); | ||||
// if (!empty($event['comment'])) { | // if (!empty($event['comment'])) { | ||||
// $mailbody .= "\n\n" . $this->gettext('itipsendercomment') . $event['comment']; | // $mailbody .= "\n\n" . $this->gettext('itipsendercomment') . $event['comment']; | ||||
▲ Show 20 Lines • Show All 87 Lines • ▼ Show 20 Lines | public function compose_itip_message($event, $method, $rsvp = true) | ||||
} | } | ||||
if ($event['recurrence']) { | if ($event['recurrence']) { | ||||
unset($event['recurrence']['EXCEPTIONS']); | unset($event['recurrence']['EXCEPTIONS']); | ||||
} | } | ||||
} | } | ||||
// set RSVP for every attendee | // set RSVP for every attendee | ||||
else if ($method == 'REQUEST') { | else if ($method == 'REQUEST') { | ||||
foreach ($event['attendees'] as $i => $attendee) { | foreach ($event['attendees'] as $i => $attendee) { | ||||
if (($rsvp || !isset($attendee['rsvp'])) && ($attendee['status'] != 'DELEGATED' && $attendee['role'] != 'NON-PARTICIPANT')) { | if ( | ||||
($rsvp || !isset($attendee['rsvp'])) | |||||
&& ( | |||||
(empty($attendee['status']) || $attendee['status'] != 'DELEGATED') | |||||
&& $attendee['role'] != 'NON-PARTICIPANT' | |||||
) | |||||
) { | |||||
$event['attendees'][$i]['rsvp']= (bool)$rsvp; | $event['attendees'][$i]['rsvp']= (bool) $rsvp; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
else if ($method == 'CANCEL') { | else if ($method == 'CANCEL') { | ||||
if ($event['recurrence']) { | if ($event['recurrence']) { | ||||
unset($event['recurrence']['EXCEPTIONS']); | unset($event['recurrence']['EXCEPTIONS']); | ||||
} | } | ||||
} | } | ||||
Show All 32 Lines | public function compose_itip_message($event, $method, $rsvp = true) | ||||
$headers['User-Agent'] = $agent; | $headers['User-Agent'] = $agent; | ||||
} | } | ||||
$message->headers($headers); | $message->headers($headers); | ||||
// attach ics file for this event | // attach ics file for this event | ||||
$ical = libcalendaring::get_ical(); | $ical = libcalendaring::get_ical(); | ||||
$ics = $ical->export(array($event), $method, false, $method == 'REQUEST' && $this->plugin->driver ? array($this->plugin->driver, 'get_attachment_body') : false); | $ics = $ical->export(array($event), $method, false, $method == 'REQUEST' && $this->plugin->driver ? array($this->plugin->driver, 'get_attachment_body') : false); | ||||
$filename = $event['_type'] == 'task' ? 'todo.ics' : 'event.ics'; | $filename = !empty($event['_type']) && $event['_type'] == 'task' ? 'todo.ics' : 'event.ics'; | ||||
$message->addAttachment($ics, 'text/calendar', $filename, false, '8bit', '', RCUBE_CHARSET . "; method=" . $method); | $message->addAttachment($ics, 'text/calendar', $filename, false, '8bit', '', RCUBE_CHARSET . "; method=" . $method); | ||||
return $message; | return $message; | ||||
} | } | ||||
/** | /** | ||||
* Forward the given iTip event as delegation to another person | * Forward the given iTip event as delegation to another person | ||||
* | * | ||||
▲ Show 20 Lines • Show All 211 Lines • ▼ Show 20 Lines | public function get_itip_status($event, $existing = null) | ||||
'action' => $action, | 'action' => $action, | ||||
'rescheduled' => $rescheduled, | 'rescheduled' => $rescheduled, | ||||
'html' => $html, | 'html' => $html, | ||||
); | ); | ||||
} | } | ||||
protected function get_itip_diff($event, $existing) | protected function get_itip_diff($event, $existing) | ||||
{ | { | ||||
if (empty($event) || empty($existing) || empty($event['message_uid'])) { | if (empty($event) || empty($existing) || empty($event['message_uid']) || empty($event['mime_id'])) { | ||||
return; | return; | ||||
} | } | ||||
$itip = $this->lib->mail_get_itip_object($event['mbox'], $event['message_uid'], $event['mime_id'], | $itip = $this->lib->mail_get_itip_object($event['mbox'], $event['message_uid'], $event['mime_id'], | ||||
$event['task'] == 'calendar' ? 'event' : 'task'); | $event['task'] == 'calendar' ? 'event' : 'task'); | ||||
if ($itip) { | if ($itip) { | ||||
// List of properties that could change without SEQUENCE bump | // List of properties that could change without SEQUENCE bump | ||||
Show All 18 Lines | protected function get_itip_diff($event, $existing) | ||||
foreach ((array) $existing['attendees'] as $idx => $attendee) { | foreach ((array) $existing['attendees'] as $idx => $attendee) { | ||||
if ($attendee['email'] && in_array(strtolower($attendee['email']), $emails)) { | if ($attendee['email'] && in_array(strtolower($attendee['email']), $emails)) { | ||||
$status[strtolower($attendee['email'])] = $attendee['status']; | $status[strtolower($attendee['email'])] = $attendee['status']; | ||||
} | } | ||||
if ($attendee['role'] == 'ORGANIZER') { | if ($attendee['role'] == 'ORGANIZER') { | ||||
$attendee['status'] = 'ACCEPTED'; // sometimes is not set for exceptions | $attendee['status'] = 'ACCEPTED'; // sometimes is not set for exceptions | ||||
$existing['attendees'][$idx] = $attendee; | $existing['attendees'][$idx] = $attendee; | ||||
} | } | ||||
$existing_attendees[] = $attendee['email'].$attendee['name']; | $existing_attendees[] = $attendee['email'] . (isset($attendee['name']) ? $attendee['name'] : ''); | ||||
} | } | ||||
foreach ((array) $itip['attendees'] as $idx => $attendee) { | foreach ((array) $itip['attendees'] as $idx => $attendee) { | ||||
if ($attendee['email'] && ($_status = $status[strtolower($attendee['email'])])) { | if (!empty($attendee['email']) && !empty($status[strtolower($attendee['email'])])) { | ||||
$attendee['status'] = $_status; | $attendee['status'] = $status[strtolower($attendee['email'])]; | ||||
$itip['attendees'][$idx] = $attendee; | $itip['attendees'][$idx] = $attendee; | ||||
} | } | ||||
$itip_attendees[] = $attendee['email'].$attendee['name']; | $itip_attendees[] = $attendee['email'] . (isset($attendee['name']) ? $attendee['name'] : ''); | ||||
} | } | ||||
if ($itip_attendees != $existing_attendees) { | if ($itip_attendees != $existing_attendees) { | ||||
$diff['attendees'] = array( | $diff['attendees'] = array( | ||||
'new' => $itip['attendees'], | 'new' => $itip['attendees'], | ||||
'old' => $existing['attendees'] | 'old' => $existing['attendees'] | ||||
); | ); | ||||
} | } | ||||
Show All 17 Lines | class libcalendaring_itip | ||||
} | } | ||||
/** | /** | ||||
* Build inline UI elements for iTip messages | * Build inline UI elements for iTip messages | ||||
*/ | */ | ||||
public function mail_itip_inline_ui($event, $method, $mime_id, $task, $message_date = null, $preview_url = null) | public function mail_itip_inline_ui($event, $method, $mime_id, $task, $message_date = null, $preview_url = null) | ||||
{ | { | ||||
$buttons = array(); | $buttons = array(); | ||||
$dom_id = asciiwords($event['uid'], true); | $dom_id = asciiwords($event['uid'], true); | ||||
$rsvp_status = 'unknown'; | $rsvp_status = 'unknown'; | ||||
$rsvp_buttons = ''; | |||||
// pass some metadata about the event and trigger the asynchronous status check | // pass some metadata about the event and trigger the asynchronous status check | ||||
$changed = is_object($event['changed']) ? $event['changed'] : $message_date; | $changed = is_object($event['changed']) ? $event['changed'] : $message_date; | ||||
$metadata = array( | $metadata = array( | ||||
'uid' => $event['uid'], | 'uid' => $event['uid'], | ||||
'_instance' => $event['_instance'], | '_instance' => isset($event['_instance']) ? $event['_instance'] : null, | ||||
'changed' => $changed ? $changed->format('U') : 0, | 'changed' => $changed ? $changed->format('U') : 0, | ||||
'sequence' => intval($event['sequence']), | 'sequence' => intval($event['sequence']), | ||||
'method' => $method, | 'method' => $method, | ||||
'task' => $task, | 'task' => $task, | ||||
'mime_id' => $mime_id, | 'mime_id' => $mime_id, | ||||
); | ); | ||||
// create buttons to be activated from async request checking existence of this event in local calendars | // create buttons to be activated from async request checking existence of this event in local calendars | ||||
▲ Show 20 Lines • Show All 123 Lines • ▼ Show 20 Lines | public function mail_itip_inline_ui($event, $method, $mime_id, $task, $message_date = null, $preview_url = null) | ||||
$metadata['attendee'] = $attendee['email']; | $metadata['attendee'] = $attendee['email']; | ||||
$metadata['rsvp'] = $attendee['rsvp'] || $attendee['role'] != 'NON-PARTICIPANT'; | $metadata['rsvp'] = $attendee['rsvp'] || $attendee['role'] != 'NON-PARTICIPANT'; | ||||
$rsvp_status = !empty($attendee['status']) ? strtoupper($attendee['status']) : 'NEEDS-ACTION'; | $rsvp_status = !empty($attendee['status']) ? strtoupper($attendee['status']) : 'NEEDS-ACTION'; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
// add itip reply message controls | // add itip reply message controls | ||||
$rsvp_buttons .= html::div('itip-reply-controls', $this->itip_rsvp_options_ui($dom_id, $metadata['nosave'])); | $rsvp_buttons .= html::div('itip-reply-controls', $this->itip_rsvp_options_ui($dom_id, !empty($metadata['nosave']))); | ||||
$buttons[] = html::div(array('id' => 'rsvp-'.$dom_id, 'class' => 'rsvp-buttons', 'style' => 'display:none'), $rsvp_buttons); | $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); | $buttons[] = html::div(array('id' => 'update-'.$dom_id, 'style' => 'display:none'), $update_button); | ||||
// prepare autocompletion for delegation dialog | // prepare autocompletion for delegation dialog | ||||
if (in_array('delegated', $this->rsvp_actions)) { | if (in_array('delegated', $this->rsvp_actions)) { | ||||
$this->rc->autocomplete_init(); | $this->rc->autocomplete_init(); | ||||
} | } | ||||
} | } | ||||
// for CANCEL messages, we can: | // for CANCEL messages, we can: | ||||
else if ($method == 'CANCEL') { | else if ($method == 'CANCEL') { | ||||
$title = $this->gettext('itipcancellation'); | $title = $this->gettext('itipcancellation'); | ||||
$event_prop = array_filter(array( | $event_prop = array_filter(array( | ||||
'uid' => $event['uid'], | 'uid' => $event['uid'], | ||||
'_instance' => $event['_instance'], | '_instance' => isset($event['_instance']) ? $event['_instance'] : null, | ||||
'_savemode' => $event['_savemode'], | '_savemode' => isset($event['_savemode']) ? $event['_savemode'] : null, | ||||
)); | )); | ||||
// 1. remove the event from our calendar | // 1. remove the event from our calendar | ||||
$button_remove = html::tag('input', array( | $button_remove = html::tag('input', array( | ||||
'type' => 'button', | 'type' => 'button', | ||||
'class' => 'button', | 'class' => 'button', | ||||
'onclick' => "rcube_libcalendaring.remove_from_itip(" . rcube_output::json_serialize($event_prop) . ", '$task', '" . rcube::JQ($event['title']) . "')", | 'onclick' => "rcube_libcalendaring.remove_from_itip(" . rcube_output::json_serialize($event_prop) . ", '$task', '" . rcube::JQ($event['title']) . "')", | ||||
'value' => $this->gettext('removefromcalendar'), | 'value' => $this->gettext('removefromcalendar'), | ||||
Show All 9 Lines | public function mail_itip_inline_ui($event, $method, $mime_id, $task, $message_date = null, $preview_url = null) | ||||
$buttons[] = html::div(array('id' => 'rsvp-'.$dom_id, 'style' => 'display:none'), $button_remove . $button_update); | $buttons[] = html::div(array('id' => 'rsvp-'.$dom_id, 'style' => 'display:none'), $button_remove . $button_update); | ||||
$rsvp_status = 'CANCELLED'; | $rsvp_status = 'CANCELLED'; | ||||
$metadata['rsvp'] = true; | $metadata['rsvp'] = true; | ||||
} | } | ||||
// append generic import button | // append generic import button | ||||
if ($import_button) { | if (!empty($import_button)) { | ||||
$buttons[] = html::div(array('id' => 'import-'.$dom_id, 'style' => 'display:none'), $import_button); | $buttons[] = html::div(array('id' => 'import-'.$dom_id, 'style' => 'display:none'), $import_button); | ||||
} | } | ||||
// pass some metadata about the event and trigger the asynchronous status check | // pass some metadata about the event and trigger the asynchronous status check | ||||
$metadata['fallback'] = $rsvp_status; | $metadata['fallback'] = $rsvp_status; | ||||
$metadata['rsvp'] = intval($metadata['rsvp']); | $metadata['rsvp'] = intval($metadata['rsvp']); | ||||
$this->rc->output->add_script("rcube_libcalendaring.fetch_itip_object_status(" . rcube_output::json_serialize($metadata) . ")", 'docready'); | $this->rc->output->add_script("rcube_libcalendaring.fetch_itip_object_status(" . rcube_output::json_serialize($metadata) . ")", 'docready'); | ||||
Show All 12 Lines | class libcalendaring_itip | ||||
/** | /** | ||||
* Render an RSVP UI widget with buttons to respond on iTip invitations | * Render an RSVP UI widget with buttons to respond on iTip invitations | ||||
*/ | */ | ||||
function itip_rsvp_buttons($attrib = array(), $actions = null) | function itip_rsvp_buttons($attrib = array(), $actions = null) | ||||
{ | { | ||||
$attrib += array('type' => 'button'); | $attrib += array('type' => 'button'); | ||||
if (!$actions) | if (!$actions) { | ||||
$actions = $this->rsvp_actions; | $actions = $this->rsvp_actions; | ||||
} | |||||
$buttons = ''; | |||||
foreach ($actions as $method) { | foreach ($actions as $method) { | ||||
$buttons .= html::tag('input', array( | $buttons .= html::tag('input', array( | ||||
'type' => $attrib['type'], | 'type' => $attrib['type'], | ||||
'name' => $attrib['iname'], | 'name' => !empty($attrib['iname']) ? $attrib['iname'] : null, | ||||
'class' => 'button', | 'class' => 'button', | ||||
'rel' => $method, | 'rel' => $method, | ||||
'value' => $this->gettext('itip' . $method), | 'value' => $this->gettext('itip' . $method), | ||||
)); | )); | ||||
} | } | ||||
// add localized texts for the delegation dialog | // add localized texts for the delegation dialog | ||||
if (in_array('delegated', $actions)) { | if (in_array('delegated', $actions)) { | ||||
▲ Show 20 Lines • Show All 85 Lines • ▼ Show 20 Lines | function itip_object_details_table($event, $title) | ||||
if (!empty($event['recurrence_date'])) { | if (!empty($event['recurrence_date'])) { | ||||
$table->add('label', ''); | $table->add('label', ''); | ||||
$table->add('recurrence-id', $this->gettext($event['thisandfuture'] ? 'itipfutureoccurrence' : 'itipsingleoccurrence')); | $table->add('recurrence-id', $this->gettext($event['thisandfuture'] ? 'itipfutureoccurrence' : 'itipsingleoccurrence')); | ||||
} | } | ||||
else if (!empty($event['recurrence'])) { | else if (!empty($event['recurrence'])) { | ||||
$table->add('label', $this->gettext('recurring')); | $table->add('label', $this->gettext('recurring')); | ||||
$table->add('recurrence', $this->lib->recurrence_text($event['recurrence'])); | $table->add('recurrence', $this->lib->recurrence_text($event['recurrence'])); | ||||
} | } | ||||
if ($location = trim($event['location'])) { | if (isset($event['location']) && ($location = trim($event['location']))) { | ||||
$table->add('label', $this->gettext('location')); | $table->add('label', $this->gettext('location')); | ||||
$table->add('location', rcube::Q($location)); | $table->add('location', rcube::Q($location)); | ||||
} | } | ||||
if (($sensitivity = trim($event['sensitivity'])) && !preg_match('/^(x-|public$)/i', $sensitivity)) { | if (($sensitivity = trim($event['sensitivity'])) && !preg_match('/^(x-|public$)/i', $sensitivity)) { | ||||
$table->add('label', $this->gettext('sensitivity')); | $table->add('label', $this->gettext('sensitivity')); | ||||
$table->add('sensitivity', ucfirst($this->gettext($sensitivity)) . '!'); | $table->add('sensitivity', ucfirst($this->gettext($sensitivity)) . '!'); | ||||
} | } | ||||
if ($event['status'] == 'COMPLETED' || $event['status'] == 'CANCELLED') { | if (!empty($event['status']) && ($event['status'] == 'COMPLETED' || $event['status'] == 'CANCELLED')) { | ||||
$table->add('label', $this->gettext('status')); | $table->add('label', $this->gettext('status')); | ||||
$table->add('status', $this->gettext('status-' . strtolower($event['status']))); | $table->add('status', $this->gettext('status-' . strtolower($event['status']))); | ||||
} | } | ||||
if ($comment = trim($event['comment'])) { | if (isset($event['comment']) && ($comment = trim($event['comment']))) { | ||||
$table->add('label', $this->gettext('comment')); | $table->add('label', $this->gettext('comment')); | ||||
$table->add('location', rcube::Q($comment)); | $table->add('location', rcube::Q($comment)); | ||||
} | } | ||||
return $table->show(); | return $table->show(); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 53 Lines • Show Last 20 Lines |