diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index 6cd4f68c..242fcc0b 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -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') diff --git a/plugins/calendar/drivers/calendar_driver.php b/plugins/calendar/drivers/calendar_driver.php index d2703488..dcb7ac99 100644 --- a/plugins/calendar/drivers/calendar_driver.php +++ b/plugins/calendar/drivers/calendar_driver.php @@ -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 diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php index 4c21ab8d..c2810ee9 100644 --- a/plugins/calendar/drivers/kolab/kolab_driver.php +++ b/plugins/calendar/drivers/kolab/kolab_driver.php @@ -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; } diff --git a/plugins/kolab_delegation/kolab_delegation_engine.php b/plugins/kolab_delegation/kolab_delegation_engine.php index 3f63df9b..ca3637fa 100644 --- a/plugins/kolab_delegation/kolab_delegation_engine.php +++ b/plugins/kolab_delegation/kolab_delegation_engine.php @@ -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; diff --git a/plugins/libcalendaring/lib/libcalendaring_itip.php b/plugins/libcalendaring/lib/libcalendaring_itip.php index 95138c76..929b0967 100644 --- a/plugins/libcalendaring/lib/libcalendaring_itip.php +++ b/plugins/libcalendaring/lib/libcalendaring_itip.php @@ -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); diff --git a/plugins/libcalendaring/libcalendaring.php b/plugins/libcalendaring/libcalendaring.php index 1ddf7ac7..1efead86 100644 --- a/plugins/libcalendaring/libcalendaring.php +++ b/plugins/libcalendaring/libcalendaring.php @@ -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; } }