Skip to content
This repository has been archived by the owner on Dec 27, 2023. It is now read-only.

Commit

Permalink
fix(Calendar CalDAV) create/put event refactor re move / displayconta…
Browse files Browse the repository at this point in the history
…iner

Change-Id: Ib162226375b286c4fff5dabdbfd585e462f5c1d5
Reviewed-on: http://gerrit.tine20.com/customers/18671
Tested-by: Jenkins CI (http://ci.tine20.com/) <tine20-jenkins@metaways.de>
Reviewed-by: Paul Mehrer <p.mehrer@metaways.de>
  • Loading branch information
paulmhh committed Dec 16, 2020
1 parent 6878e0a commit 596c10c
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 58 deletions.
7 changes: 7 additions & 0 deletions tests/tine20/Calendar/Frontend/WebDAV/EventTest.php
Expand Up @@ -18,6 +18,8 @@ class Calendar_Frontend_WebDAV_EventTest extends Calendar_TestCase
*/
protected $objects = array();

protected $_ifMatchHeader;

/**
* Sets up the fixture.
* This method is called before a test is executed.
Expand Down Expand Up @@ -53,13 +55,17 @@ public function setUp()
$_SERVER['HTTP_USER_AGENT'] = 'CalendarStore/5.0 (1127); iCal/5.0 (1535); Mac OS X/10.7.1 (11B26)';

$_SERVER['REQUEST_URI'] = 'lars';

$this->_ifMatchHeader = new Zend\Http\Header\IfMatch('shalala');
Tinebase_Core::getRequest()->getHeaders()->addHeader($this->_ifMatchHeader);
}

public function tearDown()
{
parent::tearDown();
Tinebase_Core::getPreference('Calendar')->resetAppPrefsCache();
Tinebase_Core::set(Tinebase_Core::PREFERENCES, null);
Tinebase_Core::getRequest()->getHeaders()->removeHeader($this->_ifMatchHeader);
}

/**
Expand Down Expand Up @@ -1012,6 +1018,7 @@ public function testMoveOriginEvent()
// move event (origin container)
Calendar_Frontend_WebDAV_Event::create($this->objects['initialContainer'], "$id.ics", stream_get_contents($event->get()));
$oldEvent = new Calendar_Frontend_WebDAV_Event($this->objects['sharedContainer'], "$id.ics");

$oldEvent->delete();

$loadedEvent = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], "$id.ics");
Expand Down
21 changes: 11 additions & 10 deletions tine20/Calendar/Controller/Event.php
Expand Up @@ -2762,15 +2762,15 @@ protected function _createAttender(Calendar_Model_Attender $attender, Calendar_M

// attach to display calendar if attender has/is a useraccount
if ($userAccountId) {
if ($calendar->type == Tinebase_Model_Container::TYPE_PERSONAL && Tinebase_Container::getInstance()->hasGrant($userAccountId, $calendar, Tinebase_Model_Grants::GRANT_ADMIN)) {
if ($calendar->type === Tinebase_Model_Container::TYPE_PERSONAL && Tinebase_Container::getInstance()->hasGrant($userAccountId, $calendar, Tinebase_Model_Grants::GRANT_ADMIN)) {
// if attender has admin grant to (is owner of) personal physical container, this phys. cal also gets displ. cal
$attender->displaycontainer_id = $calendar->getId();
} else if ($attender->displaycontainer_id && $userAccountId == Tinebase_Core::getUser()->getId() && Tinebase_Container::getInstance()->hasGrant($userAccountId, $attender->displaycontainer_id, Tinebase_Model_Grants::GRANT_ADMIN)) {
// allow user to set his own displ. cal
$attender->displaycontainer_id = $attender->displaycontainer_id;
} else {
$displayCalId = self::getDefaultDisplayContainerId($userAccountId);
$attender->displaycontainer_id = $displayCalId;

// allow user to set his own *personal* displ. cal
// otherwise set default display container
} elseif (!$attender->displaycontainer_id || $userAccountId !== Tinebase_Core::getUser()->getId() || !Tinebase_Container::getInstance()->hasGrant($userAccountId, $attender->displaycontainer_id, Tinebase_Model_Grants::GRANT_ADMIN) ||
Tinebase_Container::getInstance()->get($attender->displaycontainer_id)->type !== Tinebase_Model_Container::TYPE_PERSONAL) {
$attender->displaycontainer_id = self::getDefaultDisplayContainerId($userAccountId);
}

} else if ($attender->user_type === Calendar_Model_Attender::USERTYPE_RESOURCE) {
Expand Down Expand Up @@ -2884,11 +2884,12 @@ protected function _updateAttender($attender, $currentAttender, $event, $isResch

// update display calendar if attender has/is a useraccount
if ($userAccountId) {
if ($calendar->type == Tinebase_Model_Container::TYPE_PERSONAL && Tinebase_Container::getInstance()->hasGrant($userAccountId, $calendar, Tinebase_Model_Grants::GRANT_ADMIN)) {
if ($calendar->type === Tinebase_Model_Container::TYPE_PERSONAL && Tinebase_Container::getInstance()->hasGrant($userAccountId, $calendar, Tinebase_Model_Grants::GRANT_ADMIN)) {
// if attender has admin grant to personal physical container, this phys. cal also gets displ. cal
$attender->displaycontainer_id = $calendar->getId();
} else if ($userAccountId == Tinebase_Core::getUser()->getId() && Tinebase_Container::getInstance()->hasGrant($userAccountId, $attender->displaycontainer_id, Tinebase_Model_Grants::GRANT_ADMIN)) {
// allow user to set his own displ. cal
} else if ($userAccountId === Tinebase_Core::getUser()->getId() && Tinebase_Container::getInstance()->hasGrant($userAccountId, $attender->displaycontainer_id, Tinebase_Model_Grants::GRANT_ADMIN) &&
Tinebase_Container::getInstance()->get($attender->displaycontainer_id)->type === Tinebase_Model_Container::TYPE_PERSONAL) {
// allow user to set his own *personal* displ. cal
$attender->displaycontainer_id = $attender->displaycontainer_id;
} else {
$attender->displaycontainer_id = $currentAttender->displaycontainer_id;
Expand Down
130 changes: 83 additions & 47 deletions tine20/Calendar/Frontend/WebDAV/Event.php
Expand Up @@ -234,27 +234,12 @@ public static function create(Tinebase_Model_Container $container, $name, $vobje
Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' update existing event');

$vevent = new self($container, $existingEvent);
/** @var Calendar_Model_Event $existingEvent */
$existingEvent = clone $existingEvent;
$existingEvent->alarms = $event->alarms;
$existingEvent->transp = $event->transp;
if (null === ($contactId = $container->getOwner())) {
$contactId = Tinebase_Core::getUser()->contact_id;
} else {
$contactId = Tinebase_User::getInstance()->getUserById($contactId)->contact_id;
}
if (null !== ($attender = $event->attendee->find('user_id', $contactId))) {
if (null !== ($oldAttender = $existingEvent->attendee->find('user_id', $contactId))) {
$existingEvent->attendee->removeRecord($oldAttender);
$attender->setId($oldAttender->getId());
}
$existingEvent->attendee->addRecord($attender);
}
$event = static::_allowOnlyAttendeeProperties($existingEvent, $event, $container);

$calCtrl = Calendar_Controller_Event::getInstance();
$oldCalenderAcl = $calCtrl->doContainerACLChecks();
try {
if ($existingEvent->hasExternalOrganizer()) {
if ($event->hasExternalOrganizer()) {
$calCtrl->doContainerACLChecks(false);
}
$vobject = Calendar_Convert_Event_VCalendar_Abstract::getVObject($vobjectData);
Expand All @@ -268,10 +253,14 @@ public static function create(Tinebase_Model_Container $container, $name, $vobje
break;
}
}
$vcalendar = $converter->fromTine20Model($existingEvent);
$vcalendar = $converter->fromTine20Model($event);
if (null !== $xTine20Container) {
static::_addXPropsToVEvent($vcalendar, $xTine20Container);
}
// set a dummy if-match header
if (!Tinebase_Core::getRequest()->getHeaders()->has('If-Match')) {
Tinebase_Core::getRequest()->getHeaders()->addHeader(new Zend\Http\Header\IfMatch('dummy'));
}
$vevent->put($vcalendar->serialize());
} finally {
$calCtrl->doContainerACLChecks($oldCalenderAcl);
Expand All @@ -281,6 +270,28 @@ public static function create(Tinebase_Model_Container $container, $name, $vobje
return $vevent;
}

protected static function _allowOnlyAttendeeProperties(Calendar_Model_Event $_origEvent, Calendar_Model_Event $_event, Tinebase_Model_Container $_container)
{
$event = clone $_origEvent;
$event->alarms = $_event->alarms;
$event->transp = $_event->transp;
if (null === ($contactId = $_container->getOwner())) {
$contactId = Tinebase_Core::getUser()->contact_id;
} else {
$contactId = Tinebase_User::getInstance()->getUserById($contactId)->contact_id;
}
/** @var Calendar_Model_Attender $attender */
if (null !== ($attender = $_event->attendee->find('user_id', $contactId))) {
if (null !== ($oldAttender = $event->attendee->find('user_id', $contactId))) {
$event->attendee->removeRecord($oldAttender);
$attender->setId($oldAttender->getId());
}
$event->attendee->addRecord($attender);
}

return $event;
}

/**
* @param Calendar_Convert_Event_VCalendar_Abstract $converter
* @throws \Sabre\DAV\Exception\Forbidden
Expand Down Expand Up @@ -508,52 +519,77 @@ public function put($cardData, $retry = true)
}
// Converting to UTF-8, if needed
$cardData = Sabre\DAV\StringUtil::ensureUTF8($cardData);

#Sabre_CalDAV_ICalendarUtil::validateICalendarObject($cardData, array('VEVENT', 'VFREEBUSY'));

$vobject = Calendar_Convert_Event_VCalendar_Abstract::getVObject($cardData);
$xTine20Container = null;
foreach ($vobject->children() as $component) {
if (isset($component->{'X-TINE20-CONTAINER'})) {
$xTine20Container = $component->{'X-TINE20-CONTAINER'};
$xTine20Container = $component->{'X-TINE20-CONTAINER'}->getValue();
break;
}
}
// concurrency management is based on etag in CalDAV
$event = $this->_getConverter()->toTine20Model($vobject, $this->getRecord(), array(

// clone, otherwise $this->_event and $event would be the same object
$event = $this->_getConverter()->toTine20Model($vobject, clone $this->getRecord(), array(
Calendar_Convert_Event_VCalendar_Abstract::OPTION_USE_SERVER_MODLOG => true,
));

if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG))
Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " " . print_r($event->toArray(), true));


$currentEvent = $this->getRecord();
if (null !== $xTine20Container && $currentEvent->container_id !== $xTine20Container &&
$currentEvent->attendee->find('displaycontainer_id', $xTine20Container) === null) {
$xTine20Container = null;
}
$inDisplayContainer = $currentEvent->attendee->find('displaycontainer_id', $this->_container->getId()) !== null;
$currentOwnAttendee = Calendar_Model_Attender::getOwnAttender($currentEvent->attendee);
$xTine20ContainerAttendee = $xTine20Container ?
$currentEvent->attendee->find('displaycontainer_id', $xTine20Container) : null;
$currentContainer = Tinebase_Container::getInstance()->getContainerById($currentEvent->container_id);

// client sends CalDAV event -> handle a container move
if (isset($xTine20Container)) {
if ($xTine20Container->getValue() == $currentContainer->getId()) {
$currentContainer->resolveGrantsAndPath();

// no If-Match header -> no moves / displaycontainer changes
if (!Tinebase_Core::getRequest()->getHeaders()->has('If-Match')) {
$event = static::_allowOnlyAttendeeProperties($currentEvent, $event, $this->_container);

// no xTineContainer
} elseif (null === $xTine20Container) {
// target container is not a current displaycontainer and we have admin on origin container or are organizer
// move to target container
if (!$inDisplayContainer && ($currentContainer->account_grants->{Tinebase_Model_Grants::GRANT_ADMIN} ||
$currentEvent->organizer === Tinebase_Core::getUser()->contact_id ||
$currentEvent->organizer === Calendar_Controller_MSEventFacade::getInstance()->getCalendarUser()->user_id)) {
$event->container_id = $this->_container->getId();
// target container is not a current displaycontainer and is not the origin container ( and we are not organizer or admin on origin)
// set as new displaycontainer for OUR attendee (not currentCalendarUsers attendee!)
} elseif (!$inDisplayContainer && $currentOwnAttendee && $event->container_id !== $this->_container->getId()) {
Calendar_Controller_MSEventFacade::getInstance()->setDisplaycontainer($event, $this->_container->getId(),
$currentOwnAttendee);
} // else do nothing

} else { // we have a xTineContainer
// the xTineContainer is the origin container => do the move
if ($event->container_id === $xTine20Container) {
// grant check?
/* ($currentContainer->account_grants->{Tinebase_Model_Grants::GRANT_ADMIN} ||
$currentEvent->organizer === Calendar_Controller_MSEventFacade::getInstance()->getCalendarUser()->user_id)) {
*/
$event->container_id = $this->_container->getId();
} else {
// @TODO allow organizer to move original cal when he edits the displaycal event?
if ($this->_container->type == Tinebase_Model_Container::TYPE_PERSONAL) {
Calendar_Controller_MSEventFacade::getInstance()->setDisplaycontainer($event, $this->_container->getId());
}
}
}

// event was created by current user -> allow container move
else if ($currentEvent->created_by == Tinebase_Core::getUser()->getId()) {
$event->container_id = $this->_container->getId();
}
// WE (not the currentCalendarUser) are an attendee and xTineContainer was our displaycontainer
// => change our displaycontainer
} elseif ($currentOwnAttendee && $currentOwnAttendee->displaycontainer_id === $xTine20Container) {
if ($xTine20Container !== $this->_container->getId()) {
Calendar_Controller_MSEventFacade::getInstance()->setDisplaycontainer($event,
$this->_container->getId(), $currentOwnAttendee);
}

// client sends event from iMIP invitation -> only allow displaycontainer move
else {
if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG))
Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " X-TINE20-CONTAINER not present -> restrict container moves");
if ($this->_container->type == Tinebase_Model_Container::TYPE_PERSONAL) {
Calendar_Controller_MSEventFacade::getInstance()->setDisplaycontainer($event, $this->_container->getId());
// problem here is:
// secretary has admin on boss calendar, boss forwards imip to secretary with secretary being an attendee too
// now what?!
} elseif ($xTine20ContainerAttendee && $xTine20Container !== $this->_container->getId()) {
Calendar_Controller_MSEventFacade::getInstance()->setDisplaycontainer($event,
$this->_container->getId(), $xTine20ContainerAttendee);
}
}

Expand Down
3 changes: 2 additions & 1 deletion tine20/Calendar/Model/Attender.php
Expand Up @@ -3,7 +3,7 @@
* @package Calendar
* @license http://www.gnu.org/licenses/agpl.html AGPL Version 3
* @author Cornelius Weiss <c.weiss@metaways.de>
* @copyright Copyright (c) 2009-2018 Metaways Infosystems GmbH (http://www.metaways.de)
* @copyright Copyright (c) 2009-2020 Metaways Infosystems GmbH (http://www.metaways.de)
*/

/**
Expand All @@ -17,6 +17,7 @@
* @property string status
* @property string status_authkey
* @property string user_type
* @property string displaycontainer_id
*/
class Calendar_Model_Attender extends Tinebase_Record_Abstract
{
Expand Down

0 comments on commit 596c10c

Please sign in to comment.