T1523: Support shared calendars in iTip handling

This commit is contained in:
Aleksander Machniak 2016-10-03 14:10:57 +02:00
parent 12cf79adaa
commit 4a9e42a026
6 changed files with 118 additions and 66 deletions

View file

@ -2521,12 +2521,22 @@ class calendar extends rcube_plugin
/**
* Find an event in user calendars
*/
protected function find_event($event)
protected function find_event($event, &$mode)
{
$this->load_driver();
// We search for writeable calendars in personal namespace by default
return $this->driver->get_event($event, calendar_driver::FILTER_WRITEABLE | calendar_driver::FILTER_PERSONAL);
$mode = calendar_driver::FILTER_WRITEABLE | calendar_driver::FILTER_PERSONAL;
$result = $this->driver->get_event($event, $mode);
// ... now check shared folders if not found
if (!$result) {
$result = $this->driver->get_event($event, calendar_driver::FILTER_WRITEABLE | calendar_driver::FILTER_SHARED);
if ($result) {
$mode |= calendar_driver::FILTER_SHARED;
}
}
return $result;
}
/**
@ -2538,14 +2548,18 @@ class calendar extends rcube_plugin
$this->load_driver();
// find local copy of the referenced event
$existing = $this->find_event($data);
$itip = $this->load_itip();
$response = $itip->get_itip_status($data, $existing);
// find local copy of the referenced event (in personal namespace)
$existing = $this->find_event($data, $mode);
$is_shared = $mode & calendar_driver::FILTER_SHARED;
$itip = $this->load_itip();
$response = $itip->get_itip_status($data, $existing);
// get a list of writeable calendars to save new events to
if (!$existing && !$data['nosave'] && $response['action'] == 'rsvp' || $response['action'] == 'import') {
$calendars = $this->driver->list_calendars(calendar_driver::FILTER_WRITEABLE | calendar_driver::FILTER_PERSONAL);
if ((!$existing || $is_shared)
&& !$data['nosave']
&& ($response['action'] == 'rsvp' || $response['action'] == 'import')
) {
$calendars = $this->driver->list_calendars($mode);
$calendar_select = new html_select(array('name' => 'calendar', 'id' => 'itip-saveto', 'is_escaped' => true));
$calendar_select->add('--', '');
$numcals = 0;
@ -2562,7 +2576,7 @@ class calendar extends rcube_plugin
if ($calendar_select) {
$default_calendar = $this->get_default_calendar($data['sensitivity'], $calendars);
$response['select'] = html::span('folder-select', $this->gettext('saveincalendar') . ' ' .
$calendar_select->show($default_calendar['id']));
$calendar_select->show($is_shared ? $existing['calendar'] : $default_calendar['id']));
}
else if ($data['nosave']) {
$response['select'] = html::tag('input', array('type' => 'hidden', 'name' => 'calendar', 'id' => 'itip-saveto', 'value' => ''));
@ -2884,15 +2898,19 @@ class calendar extends rcube_plugin
$event['free_busy'] = 'free';
}
$mode = calendar_driver::FILTER_PERSONAL
| calendar_driver::FILTER_SHARED
| calendar_driver::FILTER_WRITEABLE;
// find writeable calendar to store event
$cal_id = !empty($_REQUEST['_folder']) ? rcube_utils::get_input_value('_folder', rcube_utils::INPUT_POST) : null;
$dontsave = ($_REQUEST['_folder'] === '' && $event['_method'] == 'REQUEST');
$calendars = $this->driver->list_calendars(calendar_driver::FILTER_PERSONAL);
$calendar = $calendars[$cal_id];
$cal_id = rcube_utils::get_input_value('_folder', rcube_utils::INPUT_POST);
$dontsave = $cal_id === '' && $event['_method'] == 'REQUEST';
$calendars = $this->driver->list_calendars($mode);
$calendar = $calendars[$cal_id];
// select default calendar except user explicitly selected 'none'
if (!$calendar && !$dontsave)
$calendar = $this->get_default_calendar($event['sensitivity']);
$calendar = $this->get_default_calendar($event['sensitivity'], $calendars);
$metadata = array(
'uid' => $event['uid'],
@ -2940,9 +2958,16 @@ class calendar extends rcube_plugin
// save to calendar
if ($calendar && $calendar['editable']) {
// check for existing event with the same UID
$existing = $this->find_event($event);
$existing = $this->find_event($event, $mode);
// we'll create a new copy if user decided to change the calendar
if ($existing && $cal_id && $calendar && $calendar['id'] != $existing['calendar']) {
$existing = null;
}
if ($existing) {
$calendar = $calendars[$existing['calendar']];
// forward savemode for correct updates of recurring events
$existing['_savemode'] = $savemode ?: $event['_savemode'];
@ -3015,10 +3040,9 @@ class calendar extends rcube_plugin
$event['id'] = $existing['id'];
$event['calendar'] = $existing['calendar'];
// preserve my participant status for regular updates
if (empty($status)) {
$this->lib->merge_attendees($event, $existing);
}
// merge attendees status
// e.g. preserve my participant status for regular updates
$this->lib->merge_attendees($event, $existing, $status);
// set status=CANCELLED on CANCEL messages
if ($event['_method'] == 'CANCEL')

View file

@ -101,6 +101,7 @@ abstract class calendar_driver
const FILTER_PERSONAL = 8;
const FILTER_PRIVATE = 16;
const FILTER_CONFIDENTIAL = 32;
const FILTER_SHARED = 64;
const BIRTHDAY_CALENDAR_ID = '__bdays__';
// features supported by backend

View file

@ -296,16 +296,15 @@ class kolab_driver extends calendar_driver
'list' => $this->calendars,
'calendars' => $calendars,
'filter' => $filter,
'editable' => ($filter & self::FILTER_WRITEABLE),
'insert' => ($filter & self::FILTER_INSERTABLE),
'active' => ($filter & self::FILTER_ACTIVE),
'personal' => ($filter & self::FILTER_PERSONAL)
));
if ($plugin['abort']) {
return $plugin['calendars'];
}
$personal = $filter & self::FILTER_PERSONAL;
$shared = $filter & self::FILTER_SHARED;
foreach ($this->calendars as $cal) {
if (!$cal->ready) {
continue;
@ -325,9 +324,13 @@ class kolab_driver extends calendar_driver
if (($filter & self::FILTER_CONFIDENTIAL) && $cal->subtype != 'confidential') {
continue;
}
if (($filter & self::FILTER_PERSONAL) && $cal->get_namespace() != 'personal') {
continue;
if ($personal || $shared) {
$ns = $cal->get_namespace();
if (!(($personal && $ns == 'personal') || ($shared && $ns == 'shared'))) {
continue;
}
}
$calendars[$cal->id] = $cal;
}

View file

@ -783,10 +783,19 @@ class kolab_delegation_engine
*/
public function delegator_folder_filter(&$args)
{
$context = $this->delegator_context();
$context = $this->delegator_context();
if (empty($context)) {
return $args;
}
$storage = $this->rc->get_storage();
$other_ns = $storage->get_namespace('other');
$delim = $storage->get_hierarchy_delimiter();
$editable = $filter & calendar_driver::FILTER_WRITEABLE;
$active = $filter & calendar_driver::FILTER_ACTIVE;
$personal = $filter & calendar_driver::FILTER_PERSONAL;
$shared = $filter & calendar_driver::FILTER_SHARED;
$calendars = array();
// code parts derived from kolab_driver::filter_calendars()
@ -794,25 +803,19 @@ class kolab_delegation_engine
if (!$cal->ready) {
continue;
}
if ($args['writeable'] && $cal->readonly) {
if ($editable && !$cal->editable) {
continue;
}
if ($args['active'] && !$cal->storage->is_active()) {
if ($active && !$cal->storage->is_active()) {
continue;
}
if ($args['personal']) {
if ($personal || $shared) {
$ns = $cal->get_namespace();
if (empty($context)) {
if ($ns != 'personal') {
continue;
}
if ($personal && $ns == 'personal') {
continue;
}
else {
if ($ns != 'other') {
continue;
}
else if ($personal && $ns == 'other') {
$found = false;
foreach ($other_ns as $ns) {
$folder = $ns[0] . $context . $delim;
@ -825,6 +828,9 @@ class kolab_delegation_engine
continue;
}
}
else if (!$shared || $ns != 'shared') {
continue;
}
}
$calendars[$cal->id] = $cal;

View file

@ -406,8 +406,8 @@ class libcalendaring_itip
else if (!$existing && !$rsvp) {
$action = 'import';
}
else if ($latest && $status_lc != 'needs-action') {
$action = 'update';
else if ($status_lc != 'needs-action') {
$action = !$latest ? 'update' : '';
}
$html = html::div('rsvp-status ' . $status_lc, $status_text);

View file

@ -1565,43 +1565,61 @@ class libcalendaring extends rcube_plugin
* Merge attendees of the old and new event version
* with keeping current user and his delegatees status
*
* @param array &$new New object data
* @param array $old Old object data
* @param array &$new New object data
* @param array $old Old object data
* @param bool $status New status of the current user
*/
public function merge_attendees(&$new, $old)
public function merge_attendees(&$new, $old, $status = null)
{
$emails = $this->get_user_emails();
$delegates = array();
$attendees = array();
if (empty($status)) {
$emails = $this->get_user_emails();
$delegates = array();
$attendees = array();
// keep attendee status of the current user
foreach ((array) $new['attendees'] as $i => $attendee) {
if (empty($attendee['email'])) {
continue;
// keep attendee status of the current user
foreach ((array) $new['attendees'] as $i => $attendee) {
if (empty($attendee['email'])) {
continue;
}
$attendees[] = $email = strtolower($attendee['email']);
if (in_array($email, $emails)) {
foreach ($old['attendees'] as $_attendee) {
if ($attendee['email'] == $_attendee['email']) {
$new['attendees'][$i] = $_attendee;
if ($_attendee['status'] == 'DELEGATED' && ($email = $_attendee['delegated-to'])) {
$delegates[] = strtolower($email);
}
break;
}
}
}
}
$attendees[] = $email = strtolower($attendee['email']);
if (in_array($email, $emails)) {
foreach ($old['attendees'] as $_attendee) {
if ($attendee['email'] == $_attendee['email']) {
$new['attendees'][$i] = $_attendee;
if ($_attendee['status'] == 'DELEGATED' && ($email = $_attendee['delegated-to'])) {
$delegates[] = strtolower($email);
// make sure delegated attendee is not lost
foreach ($delegates as $delegatee) {
if (!in_array($delegatee, $attendees)) {
foreach ((array) $old['attendees'] as $attendee) {
if ($attendee['email'] && ($email = strtolower($attendee['email'])) && $email == $delegatee) {
$new['attendees'][] = $attendee;
break;
}
break;
}
}
}
}
// make sure delegated attendee is not lost
foreach ($delegates as $delegatee) {
if (!in_array($delegatee, $attendees)) {
foreach ((array) $old['attendees'] as $attendee) {
if ($attendee['email'] && ($email = strtolower($attendee['email'])) && $email == $delegatee) {
$new['attendees'][] = $attendee;
// We also make sure that status of any attendee
// is not overriden by NEEDS-ACTION if it was already set
// which could happen if you work with shared events
foreach ((array) $new['attendees'] as $i => $attendee) {
if ($attendee['email'] && $attendee['status'] == 'NEEDS-ACTION') {
foreach ($old['attendees'] as $_attendee) {
if ($attendee['email'] == $_attendee['email']) {
$new['attendees'][$i]['status'] = $_attendee['status'];
unset($new['attendees'][$i]['rsvp']);
break;
}
}