public function testConvert()
 {
     $emailArrayFromClient = array(array('userType' => 'user', 'firstName' => 'Users', 'lastName' => '(Group)', 'partStat' => 'NEEDS-ACTION', 'role' => 'REQ', 'email' => 'urn:uuid:principals/intelligroups/cc74c2880f8c5c0eaacc57ea95f4d2571fb8a4b1'));
     $event = new Calendar_Model_Event();
     Calendar_Model_Attender::emailsToAttendee($event, $emailArrayFromClient);
     $this->assertEquals('cc74c2880f8c5c0eaacc57ea95f4d2571fb8a4b1', $event->attendee->getFirstRecord()->user_id);
     $this->assertEquals('group', $event->attendee->getFirstRecord()->user_type);
 }
 /**
  * @return void
  */
 public function testEmailsToAttendeeWithGroups()
 {
     $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->_testUser->accountFirstName, 'lastName' => $this->_testUser->accountLastName, 'partStat' => Calendar_Model_Attender::STATUS_TENTATIVE, 'role' => Calendar_Model_Attender::ROLE_REQUIRED, 'email' => $this->_testUser->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);
     //var_dump($persistentEvent->attendee->toArray());
     // there must be more than 2 attendees the user, the group + the groupmembers
     $this->assertGreaterThan(2, count($persistentEvent->attendee));
 }
 /**
  * (non-PHPdoc)
  * @see ActiveSync_Frontend_Abstract::toTineModel()
  */
 public function toTineModel(Syncroton_Model_IEntry $data, $entry = null)
 {
     if ($entry instanceof Calendar_Model_Event) {
         $event = $entry;
     } else {
         $event = new Calendar_Model_Event(array(), true);
     }
     if ($data instanceof Syncroton_Model_Event) {
         $data->copyFieldsFromParent();
     }
     // Update seq to entries seq to prevent concurrent update
     $event->seq = $entry['seq'];
     if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) {
         Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . " Event before mapping: " . print_r($event->toArray(), true));
     }
     foreach ($this->_mapping as $syncrotonProperty => $tine20Property) {
         if (!isset($data->{$syncrotonProperty})) {
             if ($tine20Property === 'description' && $this->_device->devicetype == Syncroton_Model_Device::TYPE_IPHONE) {
                 // @see #8230: added alarm to event on iOS 6.1 -> description removed
                 // this should be removed when Tine 2.0 / Syncroton supports ghosted properties
                 if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) {
                     Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' Unsetting description');
                 }
                 unset($event->{$tine20Property});
             } else {
                 if ($tine20Property === 'attendee' && $entry && $this->_device->devicetype === Syncroton_Model_Device::TYPE_IPHONE && $entry->container_id !== $this->_getDefaultContainerId()) {
                     // keep attendees as the are / they were not sent to the device before
                 } else {
                     if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) {
                         Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' Removing ' . $tine20Property);
                     }
                     $event->{$tine20Property} = null;
                 }
             }
             continue;
         }
         switch ($tine20Property) {
             case 'alarms':
                 // handled after switch statement
                 break;
             case 'attendee':
                 if ($entry && $this->_device->devicetype === Syncroton_Model_Device::TYPE_IPHONE && $entry->container_id !== $this->_getDefaultContainerId()) {
                     // keep attendees as the are / they were not sent to the device before
                     continue;
                 }
                 $newAttendees = array();
                 foreach ($data->{$syncrotonProperty} as $attendee) {
                     if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
                         Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " attendee email " . $attendee->email);
                     }
                     if (isset($attendee->attendeeType) && (isset($this->_attendeeTypeMapping[$attendee->attendeeType]) || array_key_exists($attendee->attendeeType, $this->_attendeeTypeMapping))) {
                         $role = $this->_attendeeTypeMapping[$attendee->attendeeType];
                     } else {
                         $role = Calendar_Model_Attender::ROLE_REQUIRED;
                     }
                     // AttendeeStatus send only on repsonse
                     if (preg_match('/(?P<firstName>\\S*) (?P<lastNameName>\\S*)/', $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' => $attendee->email);
                 }
                 Calendar_Model_Attender::emailsToAttendee($event, $newAttendees);
                 break;
             case 'class':
                 $event->{$tine20Property} = $data->{$syncrotonProperty} == 2 ? Calendar_Model_Event::CLASS_PRIVATE : Calendar_Model_Event::CLASS_PUBLIC;
                 break;
             case 'exdate':
                 // handle exceptions from recurrence
                 $exdates = new Tinebase_Record_RecordSet('Calendar_Model_Event');
                 foreach ($data->{$syncrotonProperty} as $exception) {
                     if ($exception->deleted == 0) {
                         $eventException = $this->toTineModel($exception);
                         $eventException->last_modified_time = new Tinebase_DateTime($this->_syncTimeStamp);
                         $eventException->recurid = new Tinebase_DateTime($exception->exceptionStartTime);
                         $eventException->is_deleted = false;
                     } else {
                         $eventException = new Calendar_Model_Event(array('recurid' => new Tinebase_DateTime($exception->exceptionStartTime), 'is_deleted' => true));
                     }
                     $eventException->seq = $entry['seq'];
                     $exdates->addRecord($eventException);
                 }
                 $event->{$tine20Property} = $exdates;
                 break;
             case 'description':
                 // @todo check $data->$fieldName->Type and convert to/from HTML if needed
                 if ($data->{$syncrotonProperty} instanceof Syncroton_Model_EmailBody) {
                     $event->{$tine20Property} = $data->{$syncrotonProperty}->data;
                 } else {
                     if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) {
                         Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' Removing description.');
                     }
                     $event->{$tine20Property} = null;
                 }
                 break;
             case 'rrule':
                 // handle recurrence
                 if ($data->{$syncrotonProperty} instanceof Syncroton_Model_EventRecurrence && isset($data->{$syncrotonProperty}->type)) {
                     $rrule = new Calendar_Model_Rrule();
                     switch ($data->{$syncrotonProperty}->type) {
                         case Syncroton_Model_EventRecurrence::TYPE_DAILY:
                             $rrule->freq = Calendar_Model_Rrule::FREQ_DAILY;
                             break;
                         case Syncroton_Model_EventRecurrence::TYPE_WEEKLY:
                             $rrule->freq = Calendar_Model_Rrule::FREQ_WEEKLY;
                             $rrule->byday = $this->_convertBitMaskToDay($data->{$syncrotonProperty}->dayOfWeek);
                             break;
                         case Syncroton_Model_EventRecurrence::TYPE_MONTHLY:
                             $rrule->freq = Calendar_Model_Rrule::FREQ_MONTHLY;
                             $rrule->bymonthday = $data->{$syncrotonProperty}->dayOfMonth;
                             break;
                         case Syncroton_Model_EventRecurrence::TYPE_MONTHLY_DAYN:
                             $rrule->freq = Calendar_Model_Rrule::FREQ_MONTHLY;
                             $week = $data->{$syncrotonProperty}->weekOfMonth;
                             $day = $data->{$syncrotonProperty}->dayOfWeek;
                             $byDay = $week == 5 ? -1 : $week;
                             $byDay .= $this->_convertBitMaskToDay($day);
                             $rrule->byday = $byDay;
                             break;
                         case Syncroton_Model_EventRecurrence::TYPE_YEARLY:
                             $rrule->freq = Calendar_Model_Rrule::FREQ_YEARLY;
                             $rrule->bymonth = $data->{$syncrotonProperty}->monthOfYear;
                             $rrule->bymonthday = $data->{$syncrotonProperty}->dayOfMonth;
                             break;
                         case Syncroton_Model_EventRecurrence::TYPE_YEARLY_DAYN:
                             $rrule->freq = Calendar_Model_Rrule::FREQ_YEARLY;
                             $rrule->bymonth = $data->{$syncrotonProperty}->monthOfYear;
                             $week = $data->{$syncrotonProperty}->weekOfMonth;
                             $day = $data->{$syncrotonProperty}->dayOfWeek;
                             $byDay = $week == 5 ? -1 : $week;
                             $byDay .= $this->_convertBitMaskToDay($day);
                             $rrule->byday = $byDay;
                             break;
                     }
                     $rrule->interval = isset($data->{$syncrotonProperty}->interval) ? $data->{$syncrotonProperty}->interval : 1;
                     if (isset($data->{$syncrotonProperty}->occurrences)) {
                         $rrule->count = $data->{$syncrotonProperty}->occurrences;
                         $rrule->until = null;
                     } else {
                         if (isset($data->{$syncrotonProperty}->until)) {
                             $rrule->count = null;
                             $rrule->until = new Tinebase_DateTime($data->{$syncrotonProperty}->until);
                         } else {
                             $rrule->count = null;
                             $rrule->until = null;
                         }
                     }
                     $event->rrule = $rrule;
                 }
                 break;
             default:
                 if ($data->{$syncrotonProperty} instanceof DateTime) {
                     $event->{$tine20Property} = new Tinebase_DateTime($data->{$syncrotonProperty});
                 } else {
                     $event->{$tine20Property} = $data->{$syncrotonProperty};
                 }
                 break;
         }
     }
     // whole day events ends at 23:59:59 in Tine 2.0 but 00:00 the next day in AS
     if (isset($event->is_all_day_event) && $event->is_all_day_event == 1) {
         $event->dtend->subSecond(1);
     }
     // decode timezone data
     if (isset($data->timezone)) {
         $timeZoneConverter = ActiveSync_TimezoneConverter::getInstance(Tinebase_Core::getLogger(), Tinebase_Core::get(Tinebase_Core::CACHE));
         try {
             $timezone = $timeZoneConverter->getTimezone($data->timezone, Tinebase_Core::getUserTimezone());
             $event->originator_tz = $timezone;
         } catch (ActiveSync_TimezoneNotFoundException $e) {
             Tinebase_Core::getLogger()->crit(__METHOD__ . '::' . __LINE__ . " timezone data not found " . $data->timezone);
             $event->originator_tz = Tinebase_Core::getUserTimezone();
         }
         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
             Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " timezone data " . $event->originator_tz);
         }
     }
     $this->_handleAlarms($data, $event);
     $this->_handleBusyStatus($data, $event);
     // 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;
 }
Beispiel #4
0
 /**
  * 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');
 }
Beispiel #5
0
 /**
  * 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;
 }
 public function testEmailsToAttendeeWithMissingMail()
 {
     $contact = new Addressbook_Model_Contact(array('org_name' => 'unittestorg', 'email' => '', 'email_home' => ''));
     $persistentContact = Addressbook_Controller_Contact::getInstance()->create($contact, FALSE);
     $event = $this->_getEvent();
     $event->attendee->addRecord(new Calendar_Model_Attender(array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $persistentContact->getId(), 'role' => Calendar_Model_Attender::ROLE_REQUIRED)));
     $persistentEvent = Calendar_Controller_Event::getInstance()->create($event);
     $clientAttendee = array();
     foreach ($persistentEvent->attendee as $attendee) {
         $clientAttendee[] = array('userType' => Calendar_Model_Attender::USERTYPE_USER, 'email' => $attendee->getEmail(), 'role' => $attendee->role);
     }
     Calendar_Model_Attender::emailsToAttendee($persistentEvent, $clientAttendee, TRUE);
     $userIds = $persistentEvent->attendee->user_id;
     foreach ($userIds as $idx => $id) {
         if ($id instanceof Tinebase_Record_Abstract) {
             $userIds[$idx] = $id->getId();
         }
     }
     $this->assertEquals(3, count($userIds));
     $this->assertTrue(in_array($this->_testUserContact->getId(), $userIds), 'testaccount missing');
     $this->assertTrue(in_array($this->_personasContacts['sclever']->getId(), $userIds), 'sclever missing');
     $this->assertTrue(in_array($persistentContact->getId(), $userIds), 'unittestorg missing');
 }
 /**
  * parse VEVENT part of VCALENDAR
  * 
  * @param  \Sabre\VObject\Component\VEvent  $vevent  the VEVENT to parse
  * @param  Calendar_Model_Event             $event   the Tine 2.0 event to update
  * @param  array                            $options
  */
 protected function _convertVevent(\Sabre\VObject\Component\VEvent $vevent, Calendar_Model_Event $event, $options)
 {
     if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) {
         Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' vevent ' . $vevent->serialize());
     }
     $newAttendees = array();
     $attachments = new Tinebase_Record_RecordSet('Tinebase_Model_Tree_Node');
     $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm');
     $skipFieldsIfOnlyBasicData = array('ATTENDEE', 'UID', 'ORGANIZER', 'VALARM', 'ATTACH', 'CATEGORIES');
     foreach ($vevent->children() as $property) {
         if (isset($this->_options['onlyBasicData']) && $this->_options['onlyBasicData'] && in_array((string) $property->name, $skipFieldsIfOnlyBasicData)) {
             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
                 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Skipping ' . $property->name . ' (using option onlyBasicData)');
             }
             continue;
         }
         switch ($property->name) {
             case 'CREATED':
             case 'DTSTAMP':
                 if (!isset($options[self::OPTION_USE_SERVER_MODLOG]) || $options[self::OPTION_USE_SERVER_MODLOG] !== true) {
                     $event->{$property->name == 'CREATED' ? 'creation_time' : 'last_modified_time'} = $this->_convertToTinebaseDateTime($property);
                 }
                 break;
             case 'LAST-MODIFIED':
                 $event->last_modified_time = new Tinebase_DateTime($property->getValue());
                 break;
             case 'ATTENDEE':
                 $newAttendee = $this->_getAttendee($property);
                 if ($newAttendee) {
                     $newAttendees[] = $newAttendee;
                 }
                 break;
             case 'CLASS':
                 if (in_array($property->getValue(), array(Calendar_Model_Event::CLASS_PRIVATE, Calendar_Model_Event::CLASS_PUBLIC))) {
                     $event->class = $property->getValue();
                 } else {
                     $event->class = Calendar_Model_Event::CLASS_PUBLIC;
                 }
                 break;
             case 'STATUS':
                 if (in_array($property->getValue(), array(Calendar_Model_Event::STATUS_CONFIRMED, Calendar_Model_Event::STATUS_TENTATIVE, Calendar_Model_Event::STATUS_CANCELED))) {
                     $event->status = $property->getValue();
                 } else {
                     if ($property->getValue() == 'CANCELLED') {
                         $event->status = Calendar_Model_Event::STATUS_CANCELED;
                     } else {
                         $event->status = Calendar_Model_Event::STATUS_CONFIRMED;
                     }
                 }
                 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':
                 if (!isset($options[self::OPTION_USE_SERVER_MODLOG]) || $options[self::OPTION_USE_SERVER_MODLOG] !== true) {
                     $event->seq = $property->getValue();
                 }
                 // iMIP only
                 $event->external_seq = $property->getValue();
                 break;
             case 'DESCRIPTION':
             case 'LOCATION':
             case 'SUMMARY':
                 $key = strtolower($property->name);
                 $value = $property->getValue();
                 $event->{$key} = $value;
                 break;
             case 'ORGANIZER':
                 $email = null;
                 if (!empty($property['EMAIL'])) {
                     $email = $property['EMAIL'];
                 } elseif (preg_match('/mailto:(?P<email>.*)/i', $property->getValue(), $matches)) {
                     $email = $matches['email'];
                 }
                 if ($email !== null) {
                     // it's not possible to change the organizer by spec
                     if (empty($event->organizer)) {
                         $name = isset($property['CN']) ? $property['CN']->getValue() : $email;
                         $contact = Calendar_Model_Attender::resolveEmailToContact(array('email' => $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':
                 $rruleString = $property->getValue();
                 // convert date format
                 $rruleString = preg_replace_callback('/UNTIL=([\\dTZ]+)(?=;?)/', function ($matches) {
                     $dtUntil = Calendar_Convert_Event_VCalendar_Abstract::getUTCDateFromStringInUsertime($matches[1]);
                     return 'UNTIL=' . $dtUntil->format(Tinebase_Record_Abstract::ISO8601LONG);
                 }, $rruleString);
                 // remove additional days from BYMONTHDAY property (BYMONTHDAY=11,15 => BYMONTHDAY=11)
                 $rruleString = preg_replace('/(BYMONTHDAY=)([\\d]+),([,\\d]+)/', '$1$2', $rruleString);
                 $event->rrule = $rruleString;
                 // 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::getUserTimezone());
                             } 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->getValue(), array(Calendar_Model_Event::TRANSP_OPAQUE, Calendar_Model_Event::TRANSP_TRANSP))) {
                     $event->transp = $property->getValue();
                 } 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->getValue();
                 break;
             case 'VALARM':
                 $this->_parseAlarm($event, $property, $vevent);
                 break;
             case 'CATEGORIES':
                 $tags = Tinebase_Model_Tag::resolveTagNameToTag($property->getParts(), 'Calendar');
                 if (!isset($event->tags)) {
                     $event->tags = $tags;
                 } else {
                     $event->tags->merge($tags);
                 }
                 break;
             case 'ATTACH':
                 $name = (string) $property['FILENAME'];
                 $managedId = (string) $property['MANAGED-ID'];
                 $value = (string) $property['VALUE'];
                 $attachment = NULL;
                 $readFromURL = false;
                 $url = '';
                 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
                     Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' attachment found: ' . $name . ' ' . $managedId);
                 }
                 if ($managedId) {
                     $attachment = $event->attachments instanceof Tinebase_Record_RecordSet ? $event->attachments->filter('hash', $property['MANAGED-ID'])->getFirstRecord() : NULL;
                     // NOTE: we might miss a attachment here for the following reasons
                     //       1. client reuses a managed id (we are server):
                     //          We havn't observerd this yet. iCal client reuse manged id's
                     //          from base events in exceptions but this is covered as we
                     //          initialize new exceptions with base event attachments
                     //
                     //          When a client reuses a managed id it's not clear yet if
                     //          this managed id needs to be in the same series/calendar/server
                     //
                     //          As we use the object hash the managed id might be used in the
                     //          same files with different names. We need to evaluate the name
                     //          (if attached) in this case as well.
                     //
                     //       2. server send his managed id (we are client)
                     //          * we need to download the attachment (here?)
                     //          * we need to have a mapping externalid / internalid (where?)
                     if (!$attachment) {
                         $readFromURL = true;
                         $url = $property->getValue();
                     } else {
                         $attachments->addRecord($attachment);
                     }
                 } elseif ('URI' === $value) {
                     /*
                     * ATTACH;VALUE=URI:https://server.com/calendars/__uids__/0AA0
                      3A3B-F7B6-459A-AB3E-4726E53637D0/dropbox/4971F93F-8657-412B-841A-A0FD913
                      9CD61.dropbox/Canada.png
                     */
                     $url = $property->getValue();
                     $urlParts = parse_url($url);
                     $host = $urlParts['host'];
                     $name = pathinfo($urlParts['path'], PATHINFO_BASENAME);
                     // iCal 10.7 places URI before uploading
                     if (parse_url(Tinebase_Core::getHostname(), PHP_URL_HOST) != $host) {
                         $readFromURL = true;
                     }
                 } else {
                     // @TODO: implement (check if add / update / update is needed)
                     if (Tinebase_Core::isLogLevel(Zend_Log::WARN)) {
                         Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' attachment found that could not be imported due to missing managed id');
                     }
                 }
                 if ($readFromURL) {
                     if (preg_match('#^(https?://)(.*)$#', str_replace(array("\n", "\r"), '', $url), $matches)) {
                         // we are client and found an external hosted attachment that we need to import
                         $userCredentialCache = Tinebase_Core::getUserCredentialCache();
                         $url = $matches[1] . $userCredentialCache->username . ':' . $userCredentialCache->password . '@' . $matches[2];
                         $attachmentInfo = $matches[1] . $matches[2] . ' ' . $name . ' ' . $managedId;
                         if (Tinebase_Helper::urlExists($url)) {
                             if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) {
                                 Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Downloading attachment: ' . $attachmentInfo);
                             }
                             $stream = fopen($url, 'r');
                             $attachment = new Tinebase_Model_Tree_Node(array('name' => rawurldecode($name), 'type' => Tinebase_Model_Tree_Node::TYPE_FILE, 'contenttype' => (string) $property['FMTTYPE'], 'tempFile' => $stream), true);
                             $attachments->addRecord($attachment);
                         } else {
                             if (Tinebase_Core::isLogLevel(Zend_Log::WARN)) {
                                 Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Url not found (got 404): ' . $attachmentInfo . ' - Skipping attachment');
                             }
                         }
                     } else {
                         if (Tinebase_Core::isLogLevel(Zend_Log::WARN)) {
                             Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Attachment found with malformed url: ' . $url);
                         }
                     }
                 }
                 break;
             case 'X-MOZ-LASTACK':
                 $lastAck = $this->_convertToTinebaseDateTime($property);
                 break;
             case 'X-MOZ-SNOOZE-TIME':
                 $snoozeTime = $this->_convertToTinebaseDateTime($property);
                 break;
             default:
                 // thunderbird saves snooze time for recurring event occurrences in properties with names like this -
                 // we just assume that the event/recur series has only one snooze time
                 if (preg_match('/^X-MOZ-SNOOZE-TIME-[0-9]+$/', $property->name)) {
                     $snoozeTime = $this->_convertToTinebaseDateTime($property);
                     if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
                         Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Found snooze time for recur occurrence: ' . $snoozeTime->toString());
                     }
                 }
                 break;
         }
     }
     // NOTE: X-CALENDARSERVER-ACCESS overwrites CLASS
     if (isset($vevent->{'X-CALENDARSERVER-ACCESS'})) {
         $event->class = $vevent->{'X-CALENDARSERVER-ACCESS'} == 'PUBLIC' ? Calendar_Model_Event::CLASS_PUBLIC : Calendar_Model_Event::CLASS_PRIVATE;
     }
     if (isset($lastAck)) {
         Calendar_Controller_Alarm::setAcknowledgeTime($event->alarms, $lastAck);
     }
     if (isset($snoozeTime)) {
         Calendar_Controller_Alarm::setSnoozeTime($event->alarms, $snoozeTime);
     }
     // merge old and new attendee
     Calendar_Model_Attender::emailsToAttendee($event, $newAttendees);
     if (empty($event->seq)) {
         $event->seq = 1;
     }
     if (empty($event->class)) {
         $event->class = Calendar_Model_Event::CLASS_PUBLIC;
     }
     $this->_manageAttachmentsFromClient($event, $attachments);
     if (empty($event->dtend)) {
         if (empty($event->dtstart)) {
             throw new Tinebase_Exception_UnexpectedValue("Got event without dtstart and dtend");
         }
         // TODO find out duration (see TRIGGER DURATION)
         //             if (isset($vevent->DURATION)) {
         //             }
         if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) {
             Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' Got event without dtend. Assuming 30 minutes duration');
         }
         $event->dtend = clone $event->dtstart;
         $event->dtend->addMinute(30);
     }
     $this->_manageAttachmentsFromClient($event, $attachments);
     // convert all datetime fields to UTC
     $event->setTimezone('UTC');
 }