T1523: Support shared calendars in iTip handling
This commit is contained in:
parent
12cf79adaa
commit
4a9e42a026
6 changed files with 118 additions and 66 deletions
|
@ -2521,12 +2521,22 @@ class calendar extends rcube_plugin
|
||||||
/**
|
/**
|
||||||
* Find an event in user calendars
|
* Find an event in user calendars
|
||||||
*/
|
*/
|
||||||
protected function find_event($event)
|
protected function find_event($event, &$mode)
|
||||||
{
|
{
|
||||||
$this->load_driver();
|
$this->load_driver();
|
||||||
|
|
||||||
// We search for writeable calendars in personal namespace by default
|
// 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();
|
$this->load_driver();
|
||||||
|
|
||||||
// find local copy of the referenced event
|
// find local copy of the referenced event (in personal namespace)
|
||||||
$existing = $this->find_event($data);
|
$existing = $this->find_event($data, $mode);
|
||||||
$itip = $this->load_itip();
|
$is_shared = $mode & calendar_driver::FILTER_SHARED;
|
||||||
$response = $itip->get_itip_status($data, $existing);
|
$itip = $this->load_itip();
|
||||||
|
$response = $itip->get_itip_status($data, $existing);
|
||||||
|
|
||||||
// get a list of writeable calendars to save new events to
|
// get a list of writeable calendars to save new events to
|
||||||
if (!$existing && !$data['nosave'] && $response['action'] == 'rsvp' || $response['action'] == 'import') {
|
if ((!$existing || $is_shared)
|
||||||
$calendars = $this->driver->list_calendars(calendar_driver::FILTER_WRITEABLE | calendar_driver::FILTER_PERSONAL);
|
&& !$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 = new html_select(array('name' => 'calendar', 'id' => 'itip-saveto', 'is_escaped' => true));
|
||||||
$calendar_select->add('--', '');
|
$calendar_select->add('--', '');
|
||||||
$numcals = 0;
|
$numcals = 0;
|
||||||
|
@ -2562,7 +2576,7 @@ class calendar extends rcube_plugin
|
||||||
if ($calendar_select) {
|
if ($calendar_select) {
|
||||||
$default_calendar = $this->get_default_calendar($data['sensitivity'], $calendars);
|
$default_calendar = $this->get_default_calendar($data['sensitivity'], $calendars);
|
||||||
$response['select'] = html::span('folder-select', $this->gettext('saveincalendar') . ' ' .
|
$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']) {
|
else if ($data['nosave']) {
|
||||||
$response['select'] = html::tag('input', array('type' => 'hidden', 'name' => 'calendar', 'id' => 'itip-saveto', 'value' => ''));
|
$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';
|
$event['free_busy'] = 'free';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$mode = calendar_driver::FILTER_PERSONAL
|
||||||
|
| calendar_driver::FILTER_SHARED
|
||||||
|
| calendar_driver::FILTER_WRITEABLE;
|
||||||
|
|
||||||
// find writeable calendar to store event
|
// find writeable calendar to store event
|
||||||
$cal_id = !empty($_REQUEST['_folder']) ? rcube_utils::get_input_value('_folder', rcube_utils::INPUT_POST) : null;
|
$cal_id = rcube_utils::get_input_value('_folder', rcube_utils::INPUT_POST);
|
||||||
$dontsave = ($_REQUEST['_folder'] === '' && $event['_method'] == 'REQUEST');
|
$dontsave = $cal_id === '' && $event['_method'] == 'REQUEST';
|
||||||
$calendars = $this->driver->list_calendars(calendar_driver::FILTER_PERSONAL);
|
$calendars = $this->driver->list_calendars($mode);
|
||||||
$calendar = $calendars[$cal_id];
|
$calendar = $calendars[$cal_id];
|
||||||
|
|
||||||
// select default calendar except user explicitly selected 'none'
|
// select default calendar except user explicitly selected 'none'
|
||||||
if (!$calendar && !$dontsave)
|
if (!$calendar && !$dontsave)
|
||||||
$calendar = $this->get_default_calendar($event['sensitivity']);
|
$calendar = $this->get_default_calendar($event['sensitivity'], $calendars);
|
||||||
|
|
||||||
$metadata = array(
|
$metadata = array(
|
||||||
'uid' => $event['uid'],
|
'uid' => $event['uid'],
|
||||||
|
@ -2940,9 +2958,16 @@ class calendar extends rcube_plugin
|
||||||
// save to calendar
|
// save to calendar
|
||||||
if ($calendar && $calendar['editable']) {
|
if ($calendar && $calendar['editable']) {
|
||||||
// check for existing event with the same UID
|
// 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) {
|
if ($existing) {
|
||||||
|
$calendar = $calendars[$existing['calendar']];
|
||||||
|
|
||||||
// forward savemode for correct updates of recurring events
|
// forward savemode for correct updates of recurring events
|
||||||
$existing['_savemode'] = $savemode ?: $event['_savemode'];
|
$existing['_savemode'] = $savemode ?: $event['_savemode'];
|
||||||
|
|
||||||
|
@ -3015,10 +3040,9 @@ class calendar extends rcube_plugin
|
||||||
$event['id'] = $existing['id'];
|
$event['id'] = $existing['id'];
|
||||||
$event['calendar'] = $existing['calendar'];
|
$event['calendar'] = $existing['calendar'];
|
||||||
|
|
||||||
// preserve my participant status for regular updates
|
// merge attendees status
|
||||||
if (empty($status)) {
|
// e.g. preserve my participant status for regular updates
|
||||||
$this->lib->merge_attendees($event, $existing);
|
$this->lib->merge_attendees($event, $existing, $status);
|
||||||
}
|
|
||||||
|
|
||||||
// set status=CANCELLED on CANCEL messages
|
// set status=CANCELLED on CANCEL messages
|
||||||
if ($event['_method'] == 'CANCEL')
|
if ($event['_method'] == 'CANCEL')
|
||||||
|
|
|
@ -101,6 +101,7 @@ abstract class calendar_driver
|
||||||
const FILTER_PERSONAL = 8;
|
const FILTER_PERSONAL = 8;
|
||||||
const FILTER_PRIVATE = 16;
|
const FILTER_PRIVATE = 16;
|
||||||
const FILTER_CONFIDENTIAL = 32;
|
const FILTER_CONFIDENTIAL = 32;
|
||||||
|
const FILTER_SHARED = 64;
|
||||||
const BIRTHDAY_CALENDAR_ID = '__bdays__';
|
const BIRTHDAY_CALENDAR_ID = '__bdays__';
|
||||||
|
|
||||||
// features supported by backend
|
// features supported by backend
|
||||||
|
|
|
@ -296,16 +296,15 @@ class kolab_driver extends calendar_driver
|
||||||
'list' => $this->calendars,
|
'list' => $this->calendars,
|
||||||
'calendars' => $calendars,
|
'calendars' => $calendars,
|
||||||
'filter' => $filter,
|
'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']) {
|
if ($plugin['abort']) {
|
||||||
return $plugin['calendars'];
|
return $plugin['calendars'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$personal = $filter & self::FILTER_PERSONAL;
|
||||||
|
$shared = $filter & self::FILTER_SHARED;
|
||||||
|
|
||||||
foreach ($this->calendars as $cal) {
|
foreach ($this->calendars as $cal) {
|
||||||
if (!$cal->ready) {
|
if (!$cal->ready) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -325,9 +324,13 @@ class kolab_driver extends calendar_driver
|
||||||
if (($filter & self::FILTER_CONFIDENTIAL) && $cal->subtype != 'confidential') {
|
if (($filter & self::FILTER_CONFIDENTIAL) && $cal->subtype != 'confidential') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (($filter & self::FILTER_PERSONAL) && $cal->get_namespace() != 'personal') {
|
if ($personal || $shared) {
|
||||||
continue;
|
$ns = $cal->get_namespace();
|
||||||
|
if (!(($personal && $ns == 'personal') || ($shared && $ns == 'shared'))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$calendars[$cal->id] = $cal;
|
$calendars[$cal->id] = $cal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -783,10 +783,19 @@ class kolab_delegation_engine
|
||||||
*/
|
*/
|
||||||
public function delegator_folder_filter(&$args)
|
public function delegator_folder_filter(&$args)
|
||||||
{
|
{
|
||||||
$context = $this->delegator_context();
|
$context = $this->delegator_context();
|
||||||
|
|
||||||
|
if (empty($context)) {
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
$storage = $this->rc->get_storage();
|
$storage = $this->rc->get_storage();
|
||||||
$other_ns = $storage->get_namespace('other');
|
$other_ns = $storage->get_namespace('other');
|
||||||
$delim = $storage->get_hierarchy_delimiter();
|
$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();
|
$calendars = array();
|
||||||
|
|
||||||
// code parts derived from kolab_driver::filter_calendars()
|
// code parts derived from kolab_driver::filter_calendars()
|
||||||
|
@ -794,25 +803,19 @@ class kolab_delegation_engine
|
||||||
if (!$cal->ready) {
|
if (!$cal->ready) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ($args['writeable'] && $cal->readonly) {
|
if ($editable && !$cal->editable) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ($args['active'] && !$cal->storage->is_active()) {
|
if ($active && !$cal->storage->is_active()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ($args['personal']) {
|
if ($personal || $shared) {
|
||||||
$ns = $cal->get_namespace();
|
$ns = $cal->get_namespace();
|
||||||
|
|
||||||
if (empty($context)) {
|
if ($personal && $ns == 'personal') {
|
||||||
if ($ns != 'personal') {
|
continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else if ($personal && $ns == 'other') {
|
||||||
if ($ns != 'other') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$found = false;
|
$found = false;
|
||||||
foreach ($other_ns as $ns) {
|
foreach ($other_ns as $ns) {
|
||||||
$folder = $ns[0] . $context . $delim;
|
$folder = $ns[0] . $context . $delim;
|
||||||
|
@ -825,6 +828,9 @@ class kolab_delegation_engine
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (!$shared || $ns != 'shared') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$calendars[$cal->id] = $cal;
|
$calendars[$cal->id] = $cal;
|
||||||
|
|
|
@ -406,8 +406,8 @@ class libcalendaring_itip
|
||||||
else if (!$existing && !$rsvp) {
|
else if (!$existing && !$rsvp) {
|
||||||
$action = 'import';
|
$action = 'import';
|
||||||
}
|
}
|
||||||
else if ($latest && $status_lc != 'needs-action') {
|
else if ($status_lc != 'needs-action') {
|
||||||
$action = 'update';
|
$action = !$latest ? 'update' : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$html = html::div('rsvp-status ' . $status_lc, $status_text);
|
$html = html::div('rsvp-status ' . $status_lc, $status_text);
|
||||||
|
|
|
@ -1565,43 +1565,61 @@ class libcalendaring extends rcube_plugin
|
||||||
* Merge attendees of the old and new event version
|
* Merge attendees of the old and new event version
|
||||||
* with keeping current user and his delegatees status
|
* with keeping current user and his delegatees status
|
||||||
*
|
*
|
||||||
* @param array &$new New object data
|
* @param array &$new New object data
|
||||||
* @param array $old Old 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();
|
if (empty($status)) {
|
||||||
$delegates = array();
|
$emails = $this->get_user_emails();
|
||||||
$attendees = array();
|
$delegates = array();
|
||||||
|
$attendees = array();
|
||||||
|
|
||||||
// keep attendee status of the current user
|
// keep attendee status of the current user
|
||||||
foreach ((array) $new['attendees'] as $i => $attendee) {
|
foreach ((array) $new['attendees'] as $i => $attendee) {
|
||||||
if (empty($attendee['email'])) {
|
if (empty($attendee['email'])) {
|
||||||
continue;
|
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']);
|
// make sure delegated attendee is not lost
|
||||||
|
foreach ($delegates as $delegatee) {
|
||||||
if (in_array($email, $emails)) {
|
if (!in_array($delegatee, $attendees)) {
|
||||||
foreach ($old['attendees'] as $_attendee) {
|
foreach ((array) $old['attendees'] as $attendee) {
|
||||||
if ($attendee['email'] == $_attendee['email']) {
|
if ($attendee['email'] && ($email = strtolower($attendee['email'])) && $email == $delegatee) {
|
||||||
$new['attendees'][$i] = $_attendee;
|
$new['attendees'][] = $attendee;
|
||||||
if ($_attendee['status'] == 'DELEGATED' && ($email = $_attendee['delegated-to'])) {
|
break;
|
||||||
$delegates[] = strtolower($email);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure delegated attendee is not lost
|
// We also make sure that status of any attendee
|
||||||
foreach ($delegates as $delegatee) {
|
// is not overriden by NEEDS-ACTION if it was already set
|
||||||
if (!in_array($delegatee, $attendees)) {
|
// which could happen if you work with shared events
|
||||||
foreach ((array) $old['attendees'] as $attendee) {
|
foreach ((array) $new['attendees'] as $i => $attendee) {
|
||||||
if ($attendee['email'] && ($email = strtolower($attendee['email'])) && $email == $delegatee) {
|
if ($attendee['email'] && $attendee['status'] == 'NEEDS-ACTION') {
|
||||||
$new['attendees'][] = $attendee;
|
foreach ($old['attendees'] as $_attendee) {
|
||||||
|
if ($attendee['email'] == $_attendee['email']) {
|
||||||
|
$new['attendees'][$i]['status'] = $_attendee['status'];
|
||||||
|
unset($new['attendees'][$i]['rsvp']);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue