/** * (non-PHPdoc) * @see Sabre\DAV\Collection::getChild() */ public function getChild($_name) { $eventId = $_name instanceof Tinebase_Record_Interface ? $_name->getId() : $this->_getIdFromName($_name); // check if child exists in calendarQuery cache if ($this->_calendarQueryCache && isset($this->_calendarQueryCache[$eventId])) { $child = $this->_calendarQueryCache[$eventId]; // remove entries from cache / they will not be used anymore unset($this->_calendarQueryCache[$eventId]); if (empty($this->_calendarQueryCache)) { $this->_calendarQueryCache = null; } return $child; } $modelName = $this->_application->name . '_Model_' . $this->_model; if ($_name instanceof $modelName) { $object = $_name; } else { $filterClass = $this->_application->name . '_Model_' . $this->_model . 'Filter'; $filter = new $filterClass(array(array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_container->getId()), array('condition' => 'OR', 'filters' => array(array('field' => 'id', 'operator' => 'equals', 'value' => $eventId), array('field' => 'uid', 'operator' => 'equals', 'value' => $eventId))))); $object = $this->_getController()->search($filter, null, false, false, 'sync')->getFirstRecord(); if ($object == null) { throw new Sabre\DAV\Exception\NotFound('Object not found'); } } $httpRequest = new Sabre\HTTP\Request(); // lie about existence of event of request is a PUT request from an ATTENDEE for an already existing event // to prevent ugly (and not helpful) error messages on the client if (isset($_SERVER['REQUEST_METHOD']) && $httpRequest->getMethod() == 'PUT' && $httpRequest->getHeader('If-None-Match') === '*') { if ($object->organizer != Tinebase_Core::getUser()->contact_id && Calendar_Model_Attender::getOwnAttender($object->attendee) !== null) { throw new Sabre\DAV\Exception\NotFound('Object not found'); } } $objectClass = $this->_application->name . '_Frontend_WebDAV_' . $this->_model; return new $objectClass($this->_container, $object); }
/** * @return void */ public function testEmailsToAttendeeWithGroups() { if (Tinebase_User::getConfiguredBackend() === Tinebase_User::LDAP || Tinebase_User::getConfiguredBackend() === Tinebase_User::ACTIVEDIRECTORY) { $this->markTestSkipped('FIXME: Does not work with LDAP/AD backend'); } $event = $this->_getEvent(); $persistentEvent = Calendar_Controller_Event::getInstance()->create($event); $primaryGroup = Tinebase_Group::getInstance()->getGroupById(Tinebase_Core::getUser()->accountPrimaryGroup); $newAttendees = array(array('userType' => Calendar_Model_Attender::USERTYPE_USER, 'firstName' => $this->_originalTestUser->accountFirstName, 'lastName' => $this->_originalTestUser->accountLastName, 'partStat' => Calendar_Model_Attender::STATUS_TENTATIVE, 'role' => Calendar_Model_Attender::ROLE_REQUIRED, 'email' => $this->_originalTestUser->accountEmailAddress), array('userType' => Calendar_Model_Attender::USERTYPE_GROUP, 'displayName' => $primaryGroup->name, 'partStat' => Calendar_Model_Attender::STATUS_NEEDSACTION, 'role' => Calendar_Model_Attender::ROLE_REQUIRED, 'email' => '*****@*****.**')); Calendar_Model_Attender::emailsToAttendee($persistentEvent, $newAttendees, TRUE); $persistentEvent = Calendar_Controller_Event::getInstance()->update($persistentEvent); $attendees = clone $persistentEvent->attendee; Calendar_Model_Attender::resolveAttendee($attendees); $newAttendees = array(); foreach ($attendees as $attendee) { $newAttendees[] = array('userType' => $attendee->user_type == 'group' ? Calendar_Model_Attender::USERTYPE_GROUP : Calendar_Model_Attender::USERTYPE_USER, 'partStat' => Calendar_Model_Attender::STATUS_TENTATIVE, 'role' => Calendar_Model_Attender::ROLE_REQUIRED, 'email' => $attendee->user_type == 'group' ? $attendee->user_id->getId() : $attendee->user_id->email, 'displayName' => $attendee->user_type == 'group' ? $attendee->user_id->name : $attendee->user_id->n_fileas); } Calendar_Model_Attender::emailsToAttendee($persistentEvent, $newAttendees, TRUE); $persistentEvent = Calendar_Controller_Event::getInstance()->update($persistentEvent); // print_r($persistentEvent->attendee->toArray()); // there must be more than 2 attendees the user, the group + the groupmembers $this->assertGreaterThan(2, count($persistentEvent->attendee)); // current account must not be a groupmember $this->assertFalse(!!Calendar_Model_Attender::getAttendee($persistentEvent->attendee, new Calendar_Model_Attender(array('user_type' => Calendar_Model_Attender::USERTYPE_GROUPMEMBER, 'user_id' => $this->_originalTestUser->contact_id))), 'found user as groupmember'); $this->assertEquals(Calendar_Model_Attender::STATUS_TENTATIVE, Calendar_Model_Attender::getOwnAttender($persistentEvent->attendee)->status); }
/** * (non-PHPdoc) * @see Sabre\DAV\Collection::getChild() */ public function getChild($_name) { try { $event = $_name instanceof Calendar_Model_Event ? $_name : $this->_getController()->get($this->_getIdFromName($_name)); } catch (Tinebase_Exception_NotFound $tenf) { throw new \Sabre\DAV\Exception\NotFound('Object not found'); } $ownAttendee = Calendar_Model_Attender::getOwnAttender($event->attendee); $displayContainer = $ownAttendee->displaycontainer_id instanceof Tinebase_Model_Container ? $ownAttendee->displaycontainer_id : Tinebase_Container::getInstance()->get($ownAttendee->displaycontainer_id); return new Calendar_Frontend_WebDAV_Event($displayContainer, $event); }
/** * (non-PHPdoc) * @see Sabre_DAV_Collection::getChild() */ public function getChild($_name) { $modelName = $this->_application->name . '_Model_' . $this->_model; if ($_name instanceof $modelName) { $object = $_name; } else { $filterClass = $this->_application->name . '_Model_' . $this->_model . 'Filter'; $filter = new $filterClass(array(array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_container->getId()), array('condition' => 'OR', 'filters' => array(array('field' => 'id', 'operator' => 'equals', 'value' => $this->_getIdFromName($_name)), array('field' => 'uid', 'operator' => 'equals', 'value' => $this->_getIdFromName($_name)))))); $object = $this->_getController()->search($filter, null, false, false, 'sync')->getFirstRecord(); if ($object == null) { throw new Sabre_DAV_Exception_FileNotFound('Object not found'); } } $httpRequest = new Sabre_HTTP_Request(); // lie about existance of event of request is a PUT request from an ATTENDEE for an already existing event // to prevent ugly (and not helpful) error messages on the client if (isset($_SERVER['REQUEST_METHOD']) && $httpRequest->getMethod() == 'PUT' && $httpRequest->getHeader('If-None-Match') === '*') { if ($object->organizer != Tinebase_Core::getUser()->contact_id && Calendar_Model_Attender::getOwnAttender($object->attendee) !== null) { throw new Sabre_DAV_Exception_FileNotFound('Object not found'); } } $objectClass = $this->_application->name . '_Frontend_WebDAV_' . $this->_model; return new $objectClass($this->_container, $object); }
/** * process request * * @param Calendar_Model_iMIP $_iMIP * @param string $_status */ protected function _processRequest($_iMIP, $_status) { $existingEvent = $this->getExistingEvent($_iMIP); $ownAttender = Calendar_Model_Attender::getOwnAttender($existingEvent ? $existingEvent->attendee : $_iMIP->getEvent()->attendee); $organizer = $existingEvent ? $existingEvent->resolveOrganizer() : $_iMIP->getEvent()->resolveOrganizer(); // internal organizer: // - event is up to date // - status change could also be done by calendar method // - normal notifications if ($organizer->account_id) { if (!$existingEvent) { // organizer has an account but no event exists, it seems that event was created from a non-caldav client // do not send notifications in this case + create event in context of organizer if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Organizer has an account but no event exists!'); } return; // not clear how to create in the organizers context... $sendNotifications = Calendar_Controller_Event::getInstance()->sendNotifications(FALSE); $existingEvent = Calendar_Controller_MSEventFacade::getInstance()->create($_iMIP->getEvent()); Calendar_Controller_Event::getInstance()->sendNotifications($sendNotifications); } if ($_status && $_status != $ownAttender->status) { $ownAttender->status = $_status; Calendar_Controller_Event::getInstance()->attenderStatusUpdate($existingEvent, $ownAttender, $ownAttender->status_authkey); } } else { $sendNotifications = Calendar_Controller_Event::getInstance()->sendNotifications(false); $event = $_iMIP->getEvent(); if (!$existingEvent) { if (!$event->container_id) { $event->container_id = Tinebase_Core::getPreference('Calendar')->{Calendar_Preference::DEFAULTCALENDAR}; } $event = $_iMIP->event = Calendar_Controller_MSEventFacade::getInstance()->create($event); } else { if ($event->external_seq > $existingEvent->external_seq && !$_status) { // no buttons pressed (just reading/updating) // updates event with .ics $event->id = $existingEvent->id; $event = $_iMIP->event = Calendar_Controller_MSEventFacade::getInstance()->update($event); } else { $event = $_iMIP->event = Calendar_Controller_MSEventFacade::getInstance()->update($existingEvent); } } Calendar_Controller_Event::getInstance()->sendNotifications($sendNotifications); $ownAttender = Calendar_Model_Attender::getOwnAttender($event->attendee); // NOTE: we do the status update in a separate call to trigger the right notifications if ($ownAttender && $_status) { $ownAttender->status = $_status; $a = Calendar_Controller_Event::getInstance()->attenderStatusUpdate($event, $ownAttender, $ownAttender->status_authkey); } } }
/** * add status_authkey for own attender * * @param Calendar_Model_Event $event */ protected function _addStatusAuthkeyForOwnAttender($event) { if (!$event->attendee instanceof Tinebase_Record_RecordSet) { return; } $ownAttender = Calendar_Model_Attender::getOwnAttender($event->attendee); if ($ownAttender) { $currentEvent = $this->_eventController->get($event->id); $currentAttender = Calendar_Model_Attender::getAttendee($currentEvent->attendee, $ownAttender); if ($currentAttender) { $ownAttender->status_authkey = $currentAttender->status_authkey; } else { if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) { Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' currentAttender not found in currentEvent: ' . print_r($currentEvent->toArray(), true)); } } } }
/** * set status of own attender depending on BusyStatus * * @param SimpleXMLElement $xmlData * @param Calendar_Model_Event $event * * @todo move detection of special handling / device type to device library */ protected function _handleBusyStatus($data, $event) { if (!isset($data->busyStatus)) { return; } $ownAttender = Calendar_Model_Attender::getOwnAttender($event->attendee); if ($ownAttender === NULL) { if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' No own attender found.'); } return; } $busyStatus = $data->busyStatus; if (in_array(strtolower($this->_device->devicetype), $this->_devicesWithWrongBusyStatusDefault)) { if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Device uses a bad default setting. BUSY and FREE are mapped to ACCEPTED.'); } $busyStatusMapping = array(Syncroton_Model_Event::BUSY_STATUS_BUSY => Calendar_Model_Attender::STATUS_ACCEPTED, Syncroton_Model_Event::BUSY_STATUS_TENATTIVE => Calendar_Model_Attender::STATUS_TENTATIVE, Syncroton_Model_Event::BUSY_STATUS_FREE => Calendar_Model_Attender::STATUS_ACCEPTED); } else { $busyStatusMapping = $this->_busyStatusMapping; } if (isset($busyStatusMapping[$busyStatus])) { $ownAttender->status = $busyStatusMapping[$busyStatus]; } else { $ownAttender->status = Calendar_Model_Attender::STATUS_NEEDSACTION; } }
/** * NOTE: As noted in {@see testMoveOriginPersonalToShared} we can't delete/decline for organizer in his * personal cal because a move personal->shared would delete/decline the event. * * To support intensional delete/declines we allow the delte/decline only if ther is some * time between this and the last update */ public function testDeleteImplicitDeclineOrganizer() { $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.21) Gecko/20110831 Lightning/1.0b2 Thunderbird/3.1.13'; $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning.ics'); $vcalendar = preg_replace('#DTSTART;TZID=Europe/Berlin:20111004T100000#', 'DTSTART;TZID=Europe/Berlin:' . Tinebase_DateTime::now()->format('Ymd\\THis'), $vcalendar); $vcalendar = preg_replace('#DTEND;TZID=Europe/Berlin:20111004T120000#', 'DTEND;TZID=Europe/Berlin:' . Tinebase_DateTime::now()->addHour(1)->format('Ymd\\THis'), $vcalendar); $id = Tinebase_Record_Abstract::generateUID(); $event = Calendar_Frontend_WebDAV_Event::create($this->objects['initialContainer'], "{$id}.ics", $vcalendar); // move event origin to shared (origin and display where the same) Calendar_Frontend_WebDAV_Event::create($this->objects['sharedContainer'], "{$id}.ics", stream_get_contents($event->get())); // $oldEvent = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], "$id.ics"); // $oldEvent->delete(); // wait some time $cbs = new Calendar_Backend_Sql(); $cbs->updateMultiple(array($id), array('creation_time' => Tinebase_DateTime::now()->subMinute(5), 'last_modified_time' => Tinebase_DateTime::now()->subMinute(3))); $personalEvent = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], "{$id}.ics"); $personalEvent->delete(); $loadedEvent = new Calendar_Frontend_WebDAV_Event($this->objects['sharedContainer'], "{$id}.ics"); $ownAttendee = Calendar_Model_Attender::getOwnAttender($loadedEvent->getRecord()->attendee); $this->assertEquals(Calendar_Model_Attender::STATUS_DECLINED, $ownAttendee->status, 'event must be declined'); }
public function testAlarm() { Tinebase_Alarm::getInstance()->sendPendingAlarms("Tinebase_Event_Async_Minutely"); $event = $this->_getEvent(); $event->dtstart = Tinebase_DateTime::now()->addMinute(15); $event->dtend = clone $event->dtstart; $event->dtend->addMinute(30); $event->attendee = $this->_getAttendee(); $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm', array(new Tinebase_Model_Alarm(array('minutes_before' => 30), TRUE))); $persistentEvent = $this->_eventController->create($event); Calendar_Model_Attender::getOwnAttender($persistentEvent->attendee)->status = Calendar_Model_Attender::STATUS_DECLINED; // hack to get declined attendee $this->_eventController->sendNotifications(FALSE); $updatedEvent = $this->_eventController->update($persistentEvent); $this->_eventController->sendNotifications(TRUE); self::flushMailer(); Tinebase_Alarm::getInstance()->sendPendingAlarms("Tinebase_Event_Async_Minutely"); $this->_assertMail('sclever', 'Alarm'); $this->assertEquals(1, count(self::getMessages())); }
/** * testInternalInvitationRequestProcess */ public function testInternalInvitationRequestProcess() { $iMIP = $this->_getiMIP('REQUEST'); $result = $this->_iMIPFrontendMock->process($iMIP, Calendar_Model_Attender::STATUS_TENTATIVE); $event = $this->_iMIPFrontend->getExistingEvent($iMIP, true); $attender = Calendar_Model_Attender::getOwnAttender($event->attendee); $this->assertEquals(Calendar_Model_Attender::STATUS_TENTATIVE, $attender->status); }
/** * Updates the VCard-formatted object * * @param string $cardData * @return void */ public function put($cardData) { if (get_class($this->_converter) == 'Calendar_Convert_Event_VCalendar_Generic') { if (Tinebase_Core::isLogLevel(Zend_Log::WARN)) { Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . " update by generic client not allowed. See Calendat_Convert_Event_VCalendar_Factory for supported clients."); } throw new Sabre_DAV_Exception_Forbidden('Update denied for unknow client'); } if (is_resource($cardData)) { $cardData = stream_get_contents($cardData); } // 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::getVcal($cardData); foreach ($vobject->children() as $component) { if (isset($component->{'X-TINE20-CONTAINER'})) { $xContainerId = $component->{'X-TINE20-CONTAINER'}; break; } } // keep old record for reference $recordBeforeUpdate = clone $this->getRecord(); $event = $this->_converter->toTine20Model($vobject, $this->getRecord()); // iCal does sends back an old value, because it does not refresh the vcalendar after // update. Therefor we must reapply the value of last_modified_time after the convert $event->last_modified_time = $recordBeforeUpdate->last_modified_time; $currentContainer = Tinebase_Container::getInstance()->getContainerById($this->getRecord()->container_id); $ownAttendee = Calendar_Model_Attender::getOwnAttender($this->getRecord()->attendee); // event 'belongs' current user -> allow container move if ($currentContainer->isPersonalOf(Tinebase_Core::getUser())) { $event->container_id = $this->_container->getId(); } else { if (isset($xContainerId)) { if ($xContainerId == $currentContainer->getId()) { $event->container_id = $this->_container->getId(); } else { // @TODO allow organizer to move original cal when he edits the displaycal event? if ($ownAttendee && $this->_container->type == Tinebase_Model_Container::TYPE_PERSONAL) { $ownAttendee->displaycontainer_id = $this->_container->getId(); } } } else { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " X-TINE20-CONTAINER not present -> restrict container moves"); } if ($ownAttendee && $this->_container->type == Tinebase_Model_Container::TYPE_PERSONAL) { if ($ownAttendee->displaycontainer_id == $currentContainer->getId()) { $event->container_id = $this->_container->getId(); } $ownAttendee->displaycontainer_id = $this->_container->getId(); } } } self::enforceEventParameters($event); // don't allow update of alarms for non organizer if oganizer is Tine 2.0 user if ($event->organizer !== Tinebase_Core::getUser()->contact_id) { $organizerContact = Addressbook_Controller_Contact::getInstance()->get($event->organizer); // reset alarms if organizer is Tine 2.0 user if (!empty($organizerContact->account_id)) { $this->_resetAlarms($event, $recordBeforeUpdate); } } if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " " . print_r($event->toArray(), true)); } try { $this->_event = Calendar_Controller_MSEventFacade::getInstance()->update($event); } catch (Tinebase_Timemachine_Exception_ConcurrencyConflict $ttecc) { throw new Sabre_DAV_Exception_PreconditionFailed('An If-Match header was specified, but none of the specified the ETags matched.', 'If-Match'); } // avoid sending headers during unit tests if (php_sapi_name() != 'cli') { // @todo this belong to DAV_Server, but it currently not supported header('ETag: ' . $this->getETag()); } }
/** * checks event for given grant * * @param string $_grant * @return bool */ public function hasGrant($_grant) { $hasGrant = (isset($this->_properties[$_grant]) || array_key_exists($_grant, $this->_properties)) && (bool) $this->{$_grant}; if ($this->class !== Calendar_Model_Event::CLASS_PUBLIC) { $hasGrant &= $this->{Tinebase_Model_Grants::GRANT_PRIVATE} || Tinebase_Core::getUser()->contact_id == ($this->organizer instanceof Addressbook_Model_Contact ? $this->organizer->getId() : $this->organizer) || Calendar_Model_Attender::getOwnAttender($this->attendee); } return $hasGrant; }
public function testRepairAttendee() { $event = $this->_getEvent(true); $event->attendee = null; $persistentEvent = $this->_controller->create($event); $result = $this->_controller->repairAttendee($persistentEvent->container_id, Tinebase_DateTime::now()->subDay(1), Tinebase_DateTime::now()); $this->assertEquals(1, $result, 'should repair 1 event'); $repairedEvent = $this->_controller->get($persistentEvent->getId()); $this->assertEquals(1, count($repairedEvent->attendee)); $ownAttender = Calendar_Model_Attender::getOwnAttender($repairedEvent->attendee); $this->assertTrue($ownAttender !== null); }
/** * parse VEVENT part of VCALENDAR * * @param Sabre_VObject_Component $_vevent the VEVENT to parse * @param Calendar_Model_Event $_event the Tine 2.0 event to update */ protected function _convertVevent(Sabre_VObject_Component $_vevent, Calendar_Model_Event $_event) { $event = $_event; $newAttendees = array(); // unset supported fields foreach ($this->_supportedFields as $field) { if ($field == 'alarms') { $event->{$field} = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm'); } else { $event->{$field} = null; } } foreach ($_vevent->children() as $property) { switch ($property->name) { case 'CREATED': case 'DTSTAMP': // do nothing break; case 'LAST-MODIFIED': $event->last_modified_time = new Tinebase_DateTime($property->value); break; case 'ATTENDEE': $newAttendees[] = $this->_getAttendee($property); break; case 'CLASS': if (in_array($property->value, array(Calendar_Model_Event::CLASS_PRIVATE, Calendar_Model_Event::CLASS_PUBLIC))) { $event->class = $property->value; } else { $event->class = Calendar_Model_Event::CLASS_PUBLIC; } break; case 'DTEND': if (isset($property['VALUE']) && strtoupper($property['VALUE']) == 'DATE') { // all day event $event->is_all_day_event = true; $dtend = $this->_convertToTinebaseDateTime($property, TRUE); // whole day events ends at 23:59:59 in Tine 2.0 but 00:00 the next day in vcalendar $dtend->subSecond(1); } else { $event->is_all_day_event = false; $dtend = $this->_convertToTinebaseDateTime($property); } $event->dtend = $dtend; break; case 'DTSTART': if (isset($property['VALUE']) && strtoupper($property['VALUE']) == 'DATE') { // all day event $event->is_all_day_event = true; $dtstart = $this->_convertToTinebaseDateTime($property, TRUE); } else { $event->is_all_day_event = false; $dtstart = $this->_convertToTinebaseDateTime($property); } $event->originator_tz = $dtstart->getTimezone()->getName(); $event->dtstart = $dtstart; break; case 'SEQUENCE': $event->seq = $property->value; break; case 'DESCRIPTION': case 'LOCATION': case 'UID': case 'SUMMARY': $key = strtolower($property->name); $event->{$key} = $property->value; break; case 'ORGANIZER': if (preg_match('/mailto:(?P<email>.*)/i', $property->value, $matches)) { // it's not possible to change the organizer by spec if (empty($event->organizer)) { $name = isset($property['CN']) ? $property['CN']->value : $matches['email']; $contact = Calendar_Model_Attender::resolveEmailToContact(array('email' => $matches['email'], 'lastName' => $name)); $event->organizer = $contact->getId(); } // Lightning attaches organizer ATTENDEE properties to ORGANIZER property and does not add an ATTENDEE for the organizer if (isset($property['PARTSTAT'])) { $newAttendees[] = $this->_getAttendee($property); } } break; case 'RECURRENCE-ID': // original start of the event $event->recurid = $this->_convertToTinebaseDateTime($property); // convert recurrence id to utc $event->recurid->setTimezone('UTC'); break; case 'RRULE': $event->rrule = $property->value; // convert date format $event->rrule = preg_replace_callback('/UNTIL=([\\dTZ]+)(?=;?)/', function ($matches) { if (strlen($matches[1]) < 10) { $dtUntil = date_create($matches[1], new DateTimeZone((string) Tinebase_Core::get(Tinebase_Core::USERTIMEZONE))); $dtUntil->setTimezone(new DateTimeZone('UTC')); } else { $dtUntil = date_create($matches[1]); } return 'UNTIL=' . $dtUntil->format(Tinebase_Record_Abstract::ISO8601LONG); }, $event->rrule); // remove additional days from BYMONTHDAY property $event->rrule = preg_replace('/(BYMONTHDAY=)([\\d]+)([,\\d]+)/', '$1$2', $event->rrule); // process exceptions if (isset($_vevent->EXDATE)) { $exdates = new Tinebase_Record_RecordSet('Calendar_Model_Event'); foreach ($_vevent->EXDATE as $exdate) { foreach ($exdate->getDateTimes() as $exception) { if (isset($exdate['VALUE']) && strtoupper($exdate['VALUE']) == 'DATE') { $recurid = new Tinebase_DateTime($exception->format(Tinebase_Record_Abstract::ISO8601LONG), (string) Tinebase_Core::get(Tinebase_Core::USERTIMEZONE)); } else { $recurid = new Tinebase_DateTime($exception->format(Tinebase_Record_Abstract::ISO8601LONG), $exception->getTimezone()); } $recurid->setTimezone(new DateTimeZone('UTC')); $eventException = new Calendar_Model_Event(array('recurid' => $recurid, 'is_deleted' => true)); $exdates->addRecord($eventException); } } $event->exdate = $exdates; } break; case 'TRANSP': if (in_array($property->value, array(Calendar_Model_Event::TRANSP_OPAQUE, Calendar_Model_Event::TRANSP_TRANSP))) { $event->transp = $property->value; } else { $event->transp = Calendar_Model_Event::TRANSP_TRANSP; } break; case 'UID': // it's not possible to change the uid by spec if (!empty($event->uid)) { continue; } $event->uid = $property->value; break; case 'VALARM': foreach ($property as $valarm) { switch (strtoupper($valarm->TRIGGER['VALUE']->value)) { # TRIGGER;VALUE=DATE-TIME:20111031T130000Z case 'DATE-TIME': //@TODO fixme $alarmTime = new Tinebase_DateTime($valarm->TRIGGER->value); $alarmTime->setTimezone('UTC'); $alarm = new Tinebase_Model_Alarm(array('alarm_time' => $alarmTime, 'minutes_before' => 'custom', 'model' => 'Calendar_Model_Event')); $event->alarms->addRecord($alarm); break; # TRIGGER;VALUE=DURATION:-PT1H15M # TRIGGER;VALUE=DURATION:-PT1H15M case 'DURATION': default: $alarmTime = $this->_convertToTinebaseDateTime($_vevent->DTSTART); $alarmTime->setTimezone('UTC'); preg_match('/(?P<invert>[+-]?)(?P<spec>P.*)/', $valarm->TRIGGER->value, $matches); $duration = new DateInterval($matches['spec']); $duration->invert = !!($matches['invert'] === '-'); $alarm = new Tinebase_Model_Alarm(array('alarm_time' => $alarmTime->add($duration), 'minutes_before' => $duration->format('%d') * 60 * 24 + $duration->format('%h') * 60 + $duration->format('%i'), 'model' => 'Calendar_Model_Event')); $event->alarms->addRecord($alarm); break; } } break; case 'CATEGORIES': // @todo handle categories break; case 'X-MOZ-LASTACK': $lastAck = $this->_convertToTinebaseDateTime($property); break; case 'X-MOZ-SNOOZE-TIME': $snoozeTime = $this->_convertToTinebaseDateTime($property); break; default: break; } } // merge old and new attendees Calendar_Model_Attender::emailsToAttendee($event, $newAttendees); if (($ownAttendee = Calendar_Model_Attender::getOwnAttender($event->attendee)) !== null) { if (isset($lastAck)) { $ownAttendee->alarm_ack_time = $lastAck; } if (isset($snoozeTime)) { $ownAttendee->alarm_snooze_time = $snoozeTime; } } if (empty($event->seq)) { $event->seq = 0; } if (empty($event->class)) { $event->class = Calendar_Model_Event::CLASS_PUBLIC; } // convert all datetime fields to UTC $event->setTimezone('UTC'); }
/** * add calendar owner as attendee if not already set * * @param string $calendarId * @param Tinebase_DateTime $from * @param Tinebase_DateTime $until * @param boolean $dry run * * @return number of updated events */ public function repairAttendee($calendarId, $from, $until, $dry = false) { $container = Tinebase_Container::getInstance()->getContainerById($calendarId); if ($container->type !== Tinebase_Model_Container::TYPE_PERSONAL) { throw new Calendar_Exception('Only allowed for personal containers!'); } if ($container->owner_id !== Tinebase_Core::getUser()->getId()) { throw new Calendar_Exception('Only allowed for own containers!'); } $updateCount = 0; while ($from->isEarlier($until)) { $endWeek = $from->getClone()->addWeek(1); if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Repairing period ' . $from . ' - ' . $endWeek); } // TODO we need to detect events with DECLINED/DELETED attendee $events = $this->_getEventsForPeriodAndCalendar($calendarId, $from, $endWeek); $from->addWeek(1); if (count($events) == 0) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' No events found'); } continue; } foreach ($events as $event) { // add attendee if not already set if ($event->isRecurInstance()) { // TODO get base event if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Skip recur instance ' . $event->toShortString()); } continue; } $ownAttender = Calendar_Model_Attender::getOwnAttender($event->attendee); if (!$ownAttender) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Add missing attender to event ' . $event->toShortString()); } $attender = new Calendar_Model_Attender(array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => Tinebase_Core::getUser()->contact_id, 'status' => Calendar_Model_Attender::STATUS_ACCEPTED)); $event->attendee->addRecord($attender); if (!$dry) { $this->update($event); } $updateCount++; } } } return $updateCount; }
/** * convert contact from xml to Calendar_Model_Event * * @todo handle BusyStatus * @param SimpleXMLElement $_data * @return Calendar_Model_Event */ public function toTineModel(SimpleXMLElement $_data, $_entry = null) { if ($_entry instanceof Calendar_Model_Event) { $event = $_entry; } else { $event = new Calendar_Model_Event(array(), true); } $xmlData = $_data->children('uri:Calendar'); $airSyncBase = $_data->children('uri:AirSyncBase'); foreach ($this->_mapping as $fieldName => $value) { switch ($value) { case 'dtend': case 'dtstart': if (isset($xmlData->{$fieldName})) { $event->{$value} = new Tinebase_DateTime((string) $xmlData->{$fieldName}); } else { $event->{$value} = null; } break; default: if (isset($xmlData->{$fieldName})) { $event->{$value} = (string) $xmlData->{$fieldName}; } else { $event->{$value} = null; } break; } } // get body if (version_compare($this->_device->acsversion, '12.0', '>=') === true) { $event->description = isset($airSyncBase->Body) ? (string) $airSyncBase->Body->Data : null; } else { $event->description = isset($xmlData->Body) ? (string) $xmlData->Body : null; } // whole day events ends at 23:59:59 in Tine 2.0 but 00:00 the next day in AS if (isset($xmlData->AllDayEvent) && $xmlData->AllDayEvent == 1) { $event->dtend->subSecond(1); } if (isset($xmlData->Reminder)) { $alarm = clone $event->dtstart; $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm', array(array('alarm_time' => $alarm->subMinute((int) $xmlData->Reminder), 'minutes_before' => (int) $xmlData->Reminder, 'model' => 'Calendar_Model_Event'))); } // decode timezone data if (isset($xmlData->Timezone)) { $timeZoneConverter = ActiveSync_TimezoneConverter::getInstance(Tinebase_Core::getLogger(), Tinebase_Core::get(Tinebase_Core::CACHE)); try { $timezone = $timeZoneConverter->getTimezone((string) $xmlData->Timezone, Tinebase_Core::get(Tinebase_Core::USERTIMEZONE)); $event->originator_tz = $timezone; } catch (ActiveSync_TimezoneNotFoundException $e) { Tinebase_Core::getLogger()->crit(__METHOD__ . '::' . __LINE__ . " timezone data not found " . (string) $xmlData->Timezone); $event->originator_tz = Tinebase_Core::get(Tinebase_Core::USERTIMEZONE); } if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " timezone data " . $event->originator_tz); } } if (!$event->attendee instanceof Tinebase_Record_RecordSet) { $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender'); } if (isset($xmlData->Attendees)) { $newAttendees = array(); foreach ($xmlData->Attendees->Attendee as $attendee) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " attendee email " . $attendee->Email); } if (isset($attendee->AttendeeType) && array_key_exists((int) $attendee->AttendeeType, $this->_attendeeTypeMapping)) { $role = $this->_attendeeTypeMapping[(int) $attendee->AttendeeType]; } else { $role = Calendar_Model_Attender::ROLE_REQUIRED; } // AttendeeStatus send only on repsonse if (preg_match('/(?P<firstName>\\S*) (?P<lastNameName>\\S*)/', (string) $attendee->Name, $matches)) { $firstName = $matches['firstName']; $lastName = $matches['lastNameName']; } else { $firstName = null; $lastName = $attendee->Name; } // @todo handle resources $newAttendees[] = array('userType' => Calendar_Model_Attender::USERTYPE_USER, 'firstName' => $firstName, 'lastName' => $lastName, 'role' => $role, 'email' => (string) $attendee->Email); } Calendar_Model_Attender::emailsToAttendee($event, $newAttendees); } // new event, add current user as participant if ($event->getId() == null) { $selfContactId = Tinebase_Core::getUser()->contact_id; $selfAttender = $event->attendee->filter('user_type', Calendar_Model_Attender::USERTYPE_USER)->filter('user_id', $selfContactId); if (count($selfAttender) == 0) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " added current user as attender for new event "); } $newAttender = new Calendar_Model_Attender(array('user_id' => $selfContactId, 'user_type' => Calendar_Model_Attender::USERTYPE_USER, 'status' => Calendar_Model_Attender::STATUS_ACCEPTED, 'role' => Calendar_Model_Attender::ROLE_REQUIRED)); $event->attendee->addRecord($newAttender); } } if (isset($xmlData->BusyStatus) && ($ownAttendee = Calendar_Model_Attender::getOwnAttender($event->attendee)) !== null) { if (isset($this->_busyStatusMapping[(string) $xmlData->BusyStatus])) { $ownAttendee->status = $this->_busyStatusMapping[(string) $xmlData->BusyStatus]; } else { $ownAttendee->status = Calendar_Model_Attender::STATUS_NEEDSACTION; } } // handle recurrence if (isset($xmlData->Recurrence) && isset($xmlData->Recurrence->Type)) { $rrule = new Calendar_Model_Rrule(); switch ((int) $xmlData->Recurrence->Type) { case self::RECUR_TYPE_DAILY: $rrule->freq = Calendar_Model_Rrule::FREQ_DAILY; break; case self::RECUR_TYPE_WEEKLY: $rrule->freq = Calendar_Model_Rrule::FREQ_WEEKLY; $rrule->byday = $this->_convertBitMaskToDay((int) $xmlData->Recurrence->DayOfWeek); break; case self::RECUR_TYPE_MONTHLY: $rrule->freq = Calendar_Model_Rrule::FREQ_MONTHLY; $rrule->bymonthday = (int) $xmlData->Recurrence->DayOfMonth; break; case self::RECUR_TYPE_MONTHLY_DAYN: $rrule->freq = Calendar_Model_Rrule::FREQ_MONTHLY; $week = (int) $xmlData->Recurrence->WeekOfMonth; $day = (int) $xmlData->Recurrence->DayOfWeek; $byDay = $week == 5 ? -1 : $week; $byDay .= $this->_convertBitMaskToDay($day); $rrule->byday = $byDay; break; case self::RECUR_TYPE_YEARLY: $rrule->freq = Calendar_Model_Rrule::FREQ_YEARLY; $rrule->bymonth = (int) $xmlData->Recurrence->MonthOfYear; $rrule->bymonthday = (int) $xmlData->Recurrence->DayOfMonth; break; case self::RECUR_TYPE_YEARLY_DAYN: $rrule->freq = Calendar_Model_Rrule::FREQ_YEARLY; $rrule->bymonth = (int) $xmlData->Recurrence->MonthOfYear; $week = (int) $xmlData->Recurrence->WeekOfMonth; $day = (int) $xmlData->Recurrence->DayOfWeek; $byDay = $week == 5 ? -1 : $week; $byDay .= $this->_convertBitMaskToDay($day); $rrule->byday = $byDay; break; } $rrule->interval = isset($xmlData->Recurrence->Interval) ? (int) $xmlData->Recurrence->Interval : 1; if (isset($xmlData->Recurrence->Until)) { $rrule->until = new Tinebase_DateTime((string) $xmlData->Recurrence->Until); // until ends at 23:59:59 in Tine 2.0 but at 00:00:00 in Windows CE (local user time) if ($rrule->until->format('s') == '00') { $rrule->until->addHour(23)->addMinute(59)->addSecond(59); } } else { $rrule->until = null; } $event->rrule = $rrule; // handle exceptions from recurrence if (isset($xmlData->Exceptions)) { $exdates = new Tinebase_Record_RecordSet('Calendar_Model_Event'); foreach ($xmlData->Exceptions->Exception as $exception) { $eventException = new Calendar_Model_Event(array('recurid' => new Tinebase_DateTime((string) $exception->ExceptionStartTime))); if ((int) $exception->Deleted === 0) { $eventException->is_deleted = false; $this->toTineModel($exception, $eventException); } else { $eventException->is_deleted = true; } $exdates->addRecord($eventException); } $event->exdate = $exdates; } } else { $event->rrule = null; $event->exdate = null; } if (empty($event->organizer)) { $event->organizer = Tinebase_Core::getUser()->contact_id; } // event should be valid now $event->isValid(); if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " eventData " . print_r($event->toArray(), true)); } return $event; }
/** * convert calendar event to Sabre\VObject\Component * * @param \Sabre\VObject\Component\VCalendar $vcalendar * @param Calendar_Model_Event $_event * @param Calendar_Model_Event $_mainEvent */ protected function _convertCalendarModelEvent(\Sabre\VObject\Component\VCalendar $vcalendar, Calendar_Model_Event $_event, Calendar_Model_Event $_mainEvent = null) { // clone the event and change the timezone $event = clone $_event; $event->setTimezone($event->originator_tz); $lastModifiedDateTime = $_event->last_modified_time ? $_event->last_modified_time : $_event->creation_time; if (!$event->creation_time instanceof Tinebase_DateTime) { throw new Tinebase_Exception_Record_Validation('creation_time needed for conversion to Sabre\\VObject\\Component'); } $vevent = $vcalendar->create('VEVENT', array('CREATED' => $_event->creation_time->getClone()->setTimezone('UTC'), 'LAST-MODIFIED' => $lastModifiedDateTime->getClone()->setTimezone('UTC'), 'DTSTAMP' => Tinebase_DateTime::now(), 'UID' => $event->uid)); $vevent->add('SEQUENCE', $event->hasExternalOrganizer() ? $event->external_seq : $event->seq); if ($event->isRecurException()) { $originalDtStart = $_event->getOriginalDtStart()->setTimezone($_event->originator_tz); $recurrenceId = $vevent->add('RECURRENCE-ID', $originalDtStart); if ($_mainEvent && $_mainEvent->is_all_day_event == true) { $recurrenceId['VALUE'] = 'DATE'; } } // dtstart and dtend $dtstart = $vevent->add('DTSTART', $_event->dtstart->getClone()->setTimezone($event->originator_tz)); if ($event->is_all_day_event == true) { $dtstart['VALUE'] = 'DATE'; // whole day events ends at 23:59:(00|59) in Tine 2.0 but 00:00 the next day in vcalendar $event->dtend->addSecond($event->dtend->get('s') == 59 ? 1 : 0); $event->dtend->addMinute($event->dtend->get('i') == 59 ? 1 : 0); $dtend = $vevent->add('DTEND', $event->dtend); $dtend['VALUE'] = 'DATE'; } else { $dtend = $vevent->add('DTEND', $event->dtend); } // auto status for deleted events if ($event->is_deleted) { $event->status = Calendar_Model_Event::STATUS_CANCELED; } // event organizer if (!empty($event->organizer)) { $organizerContact = $event->resolveOrganizer(); if ($organizerContact instanceof Addressbook_Model_Contact && !empty($organizerContact->email)) { $organizer = $vevent->add('ORGANIZER', 'mailto:' . $organizerContact->email, array('CN' => $organizerContact->n_fileas, 'EMAIL' => $organizerContact->email)); } } $this->_addEventAttendee($vevent, $event); $optionalProperties = array('class', 'status', 'description', 'geo', 'location', 'priority', 'summary', 'transp', 'url'); foreach ($optionalProperties as $property) { if (!empty($event->{$property})) { $vevent->add(strtoupper($property), $event->{$property}); } } $class = $event->class == Calendar_Model_Event::CLASS_PUBLIC ? 'PUBLIC' : 'CONFIDENTIAL'; $vcalendar->add('X-CALENDARSERVER-ACCESS', $class); $vevent->add('X-CALENDARSERVER-ACCESS', $class); // categories if (!isset($event->tags)) { $event->tags = Tinebase_Tags::getInstance()->getTagsOfRecord($event); } if (isset($event->tags) && count($event->tags) > 0) { $vevent->add('CATEGORIES', (array) $event->tags->name); } // repeating event properties if ($event->rrule) { if ($event->is_all_day_event == true) { $vevent->add('RRULE', preg_replace_callback('/UNTIL=([\\d :-]{19})(?=;?)/', function ($matches) { $dtUntil = new Tinebase_DateTime($matches[1]); $dtUntil->setTimezone((string) Tinebase_Core::getUserTimezone()); return 'UNTIL=' . $dtUntil->format('Ymd'); }, $event->rrule)); } else { $vevent->add('RRULE', preg_replace('/(UNTIL=)(\\d{4})-(\\d{2})-(\\d{2}) (\\d{2}):(\\d{2}):(\\d{2})/', '$1$2$3$4T$5$6$7Z', $event->rrule)); } if ($event->exdate instanceof Tinebase_Record_RecordSet) { $event->exdate->addIndices(array('is_deleted')); $deletedEvents = $event->exdate->filter('is_deleted', true); foreach ($deletedEvents as $deletedEvent) { $dateTime = $deletedEvent->getOriginalDtStart(); $exdate = $vevent->add('EXDATE'); if ($event->is_all_day_event == true) { $dateTime->setTimezone($event->originator_tz); $exdate['VALUE'] = 'DATE'; } $exdate->setValue($dateTime); } } } $ownAttendee = Calendar_Model_Attender::getOwnAttender($event->attendee); if ($event->alarms instanceof Tinebase_Record_RecordSet) { $mozLastAck = NULL; $mozSnooze = NULL; foreach ($event->alarms as $alarm) { $valarm = $vcalendar->create('VALARM'); $valarm->add('ACTION', 'DISPLAY'); $valarm->add('DESCRIPTION', $event->summary); if ($dtack = Calendar_Controller_Alarm::getAcknowledgeTime($alarm)) { $valarm->add('ACKNOWLEDGED', $dtack->getClone()->setTimezone('UTC')->format('Ymd\\THis\\Z')); $mozLastAck = $dtack > $mozLastAck ? $dtack : $mozLastAck; } if ($dtsnooze = Calendar_Controller_Alarm::getSnoozeTime($alarm)) { $mozSnooze = $dtsnooze > $mozSnooze ? $dtsnooze : $mozSnooze; } if (is_numeric($alarm->minutes_before)) { if ($event->dtstart == $alarm->alarm_time) { $periodString = 'PT0S'; } else { $interval = $event->dtstart->diff($alarm->alarm_time); $periodString = sprintf('%sP%s%s%s%s', $interval->format('%r'), $interval->format('%d') > 0 ? $interval->format('%dD') : null, $interval->format('%h') > 0 || $interval->format('%i') > 0 ? 'T' : null, $interval->format('%h') > 0 ? $interval->format('%hH') : null, $interval->format('%i') > 0 ? $interval->format('%iM') : null); } # TRIGGER;VALUE=DURATION:-PT1H15M $trigger = $valarm->add('TRIGGER', $periodString); $trigger['VALUE'] = "DURATION"; } else { # TRIGGER;VALUE=DATE-TIME:... $trigger = $valarm->add('TRIGGER', $alarm->alarm_time->getClone()->setTimezone('UTC')->format('Ymd\\THis\\Z')); $trigger['VALUE'] = "DATE-TIME"; } $vevent->add($valarm); } if ($mozLastAck instanceof DateTime) { $vevent->add('X-MOZ-LASTACK', $mozLastAck->getClone()->setTimezone('UTC'), array('VALUE' => 'DATE-TIME')); } if ($mozSnooze instanceof DateTime) { $vevent->add('X-MOZ-SNOOZE-TIME', $mozSnooze->getClone()->setTimezone('UTC'), array('VALUE' => 'DATE-TIME')); } } $baseUrl = Tinebase_Core::getHostname() . "/webdav/Calendar/records/Calendar_Model_Event/{$event->getId()}/"; if ($event->attachments instanceof Tinebase_Record_RecordSet) { foreach ($event->attachments as $attachment) { $filename = rawurlencode($attachment->name); $attach = $vcalendar->createProperty('ATTACH', "{$baseUrl}{$filename}", array('MANAGED-ID' => $attachment->hash, 'FMTTYPE' => $attachment->contenttype, 'SIZE' => $attachment->size, 'FILENAME' => $filename), 'TEXT'); $vevent->add($attach); } } $vcalendar->add($vevent); }
/** * inspect before create/update * * @TODO move stuff from other places here * @param Calendar_Model_Event $_record the record to inspect */ protected function _inspectEvent($_record) { $_record->uid = $_record->uid ? $_record->uid : Tinebase_Record_Abstract::generateUID(); $_record->originator_tz = $_record->originator_tz ? $_record->originator_tz : Tinebase_Core::get(Tinebase_Core::USERTIMEZONE); $_record->organizer = $_record->organizer ? $_record->organizer : Tinebase_Core::getUser()->contact_id; // external organizer (iTIP) if (!$_record->resolveOrganizer()->account_id && count($_record->attendee) > 1) { $ownAttendee = Calendar_Model_Attender::getOwnAttender($_record->attendee); $_record->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', $ownAttendee ? array($ownAttendee) : array()); } $_record->setRruleUntil(); }
public function testMeetingResponseWithNewInstanceId() { $syncrotonFolder = $this->testCreateFolder(); list($serverId, $event) = $this->testCreateEntry($syncrotonFolder); $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), new Tinebase_DateTime(null, null, 'de_DE')); $XMLMeetingResponse = $this->_testXMLMeetingResponse; $thisYear = Tinebase_DateTime::now()->format('Y'); $XMLMeetingResponse = str_replace('2012', $thisYear, $XMLMeetingResponse); $XMLMeetingResponse = str_replace('<CollectionId>17</CollectionId>', '<CollectionId>' . $syncrotonFolder->serverId . '</CollectionId>', $XMLMeetingResponse); $XMLMeetingResponse = str_replace('<RequestId>f0c79775b6b44be446f91187e24566aa1c5d06ab</RequestId>', '<RequestId>' . $serverId . '</RequestId>', $XMLMeetingResponse); $XMLMeetingResponse = str_replace('<InstanceId>20121125T130000Z</InstanceId>', '<InstanceId>20121126T130000Z</InstanceId>', $XMLMeetingResponse); $xml = new SimpleXMLElement($XMLMeetingResponse); $meetingResponse = new Syncroton_Model_MeetingResponse($xml->Request[0]); $eventId = $controller->setAttendeeStatus($meetingResponse); $event = Calendar_Controller_MSEventFacade::getInstance()->get($serverId); $event->exdate->sort('dtstart', 'DESC'); $instance = $event->exdate->filter('is_deleted', 0)->getFirstRecord(); $ownAttendee = Calendar_Model_Attender::getOwnAttender($instance->attendee); $this->assertEquals(Calendar_Model_Attender::STATUS_TENTATIVE, $ownAttendee->status); }
/** * process request * * @param Calendar_Model_iMIP $_iMIP * @param string $_status * @throws Tinebase_Exception_NotImplemented * * @todo handle external organizers * @todo create event in the organizers context */ protected function _processRequest($_iMIP, $_status) { $existingEvent = $_iMIP->getExistingEvent(); $ownAttender = Calendar_Model_Attender::getOwnAttender($existingEvent ? $existingEvent->attendee : $_iMIP->getEvent()->attendee); $organizer = $existingEvent ? $existingEvent->resolveOrganizer() : $_iMIP->getEvent()->resolveOrganizer(); // internal organizer: // - event is up to date // - status change could also be done by calendar method // - normal notifications if ($organizer->account_id) { if (!$existingEvent) { // organizer has an account but no event exists, it seems that event was created from a non-caldav client // do not send notifications in this case + create event in context of organizer return; // not clear how to create in the organizers context... $sendNotifications = Calendar_Controller_Event::getInstance()->sendNotifications(FALSE); $existingEvent = Calendar_Controller_MSEventFacade::getInstance()->create($_iMIP->getEvent()); Calendar_Controller_Event::getInstance()->sendNotifications($sendNotifications); } if ($_status && $_status != $ownAttender->status) { $ownAttender->status = $_status; Calendar_Controller_Event::getInstance()->attenderStatusUpdate($existingEvent, $ownAttender, $ownAttender->status_authkey); } } else { if ($ownAttender && $_status) { $ownAttender->status = $_status; } if (!$existingEvent) { $event = $_iMIP->getEvent(); if (!$event->container_id) { $event->container_id = Tinebase_Core::getPreference('Calendar')->{Calendar_Preference::DEFAULTCALENDAR}; } $_iMIP->event = Calendar_Controller_MSEventFacade::getInstance()->create($event); } else { $_iMIP->event = Calendar_Controller_MSEventFacade::getInstance()->update($existingEvent); } // - send reply to organizer } }