/**
  * handle alarms / Reminder
  * 
  * @param SimpleXMLElement $xmlData
  * @param Calendar_Model_Event $event
  */
 protected function _handleAlarms($data, $event)
 {
     // NOTE: existing alarms are already filtered for CU by MSEF
     $event->alarms = $event->alarms instanceof Tinebase_Record_RecordSet ? $event->alarms : new Tinebase_Record_RecordSet('Tinebase_Model_Alarm');
     $event->alarms->sort('alarm_time');
     $currentAlarm = $event->alarms->getFirstRecord();
     $alarm = NULL;
     if (isset($data->reminder)) {
         $dtstart = clone $event->dtstart;
         $alarm = new Tinebase_Model_Alarm(array('alarm_time' => $dtstart->subMinute($data->reminder), 'minutes_before' => in_array($data->reminder, array(0, 5, 15, 30, 60, 120, 720, 1440, 2880)) ? $data->reminder : 'custom', 'model' => 'Calendar_Model_Event'));
         $alarmUpdate = Calendar_Controller_Alarm::getMatchingAlarm($event->alarms, $alarm);
         if (!$alarmUpdate) {
             // alarm not existing -> add it
             $event->alarms->addRecord($alarm);
             if ($currentAlarm) {
                 // ActiveSync supports one alarm only -> current got deleted
                 $event->alarms->removeRecord($currentAlarm);
             }
         }
     } else {
         if ($currentAlarm) {
             // current alarm got removed
             $event->alarms->removeRecord($currentAlarm);
         }
     }
 }
 /**
  * converts an iTIP event to a tine20 event
  * 
  * @param Calendar_Model_Event $_event
  * @param Calendar_Model_Event $_currentEvent (not iTIP!)
  */
 protected function _fromiTIP($_event, $_currentEvent)
 {
     if (!$_event->rrule) {
         $_event->exdate = NULL;
     }
     if ($_event->exdate instanceof Tinebase_Record_RecordSet) {
         try {
             $currExdates = $this->_eventController->getRecurExceptions($_event, TRUE);
             $this->getAlarms($currExdates);
             $currClientExdates = $this->_eventController->getRecurExceptions($_event, TRUE, $this->getEventFilter());
             $this->getAlarms($currClientExdates);
         } catch (Tinebase_Exception_NotFound $e) {
             $currExdates = NULL;
             $currClientExdates = NULL;
         }
         foreach ($_event->exdate as $idx => $exdate) {
             try {
                 $this->_prepareException($_event, $exdate);
             } catch (Exception $e) {
             }
             $currExdate = $currExdates instanceof Tinebase_Record_RecordSet ? $currExdates->filter('recurid', $exdate->recurid)->getFirstRecord() : NULL;
             if ($exdate->is_deleted) {
                 // reset implicit filter fallouts and mark as don't touch (seq = -1)
                 $currClientExdate = $currClientExdates instanceof Tinebase_Record_RecordSet ? $currClientExdates->filter('recurid', $exdate->recurid)->getFirstRecord() : NULL;
                 if ($currClientExdate && $currClientExdate->is_deleted) {
                     $_event->exdate[$idx] = $currExdate;
                     $currExdate->seq = -1;
                     continue;
                 }
             }
             $this->_fromiTIP($exdate, $currExdate ? $currExdate : clone $_currentEvent);
         }
     }
     // assert organizer
     $_event->organizer = $_event->organizer ?: ($_currentEvent->organizer ?: $this->_calendarUser->user_id);
     $this->_addAttendeeWithoutEmail($_event, $_currentEvent);
     $CUAttendee = Calendar_Model_Attender::getAttendee($_event->attendee, $this->_calendarUser);
     $currentCUAttendee = Calendar_Model_Attender::getAttendee($_currentEvent->attendee, $this->_calendarUser);
     $isOrganizer = $_event->isOrganizer($this->_calendarUser);
     // remove perspective
     if ($CUAttendee && !$isOrganizer) {
         $CUAttendee->transp = $_event->transp;
         $_event->transp = $_currentEvent->transp ? $_currentEvent->transp : $_event->transp;
     }
     // apply changes to original alarms
     $_currentEvent->alarms = $_currentEvent->alarms instanceof Tinebase_Record_RecordSet ? $_currentEvent->alarms : new Tinebase_Record_RecordSet('Tinebase_Model_Alarm');
     $_event->alarms = $_event->alarms instanceof Tinebase_Record_RecordSet ? $_event->alarms : new Tinebase_Record_RecordSet('Tinebase_Model_Alarm');
     foreach ($_currentEvent->alarms as $currentAlarm) {
         if (Calendar_Model_Attender::isAlarmForAttendee($this->_calendarUser, $currentAlarm)) {
             $alarmUpdate = Calendar_Controller_Alarm::getMatchingAlarm($_event->alarms, $currentAlarm);
             if ($alarmUpdate) {
                 // we could map the alarm => save ack & snooze options
                 if ($dtAck = Calendar_Controller_Alarm::getAcknowledgeTime($alarmUpdate)) {
                     Calendar_Controller_Alarm::setAcknowledgeTime($currentAlarm, $dtAck, $this->getCalendarUser()->user_id);
                 }
                 if ($dtSnooze = Calendar_Controller_Alarm::getSnoozeTime($alarmUpdate)) {
                     Calendar_Controller_Alarm::setSnoozeTime($currentAlarm, $dtSnooze, $this->getCalendarUser()->user_id);
                 }
                 $_event->alarms->removeRecord($alarmUpdate);
             } else {
                 // alarm is to be skiped/deleted
                 if (!$currentAlarm->getOption('attendee')) {
                     Calendar_Controller_Alarm::skipAlarm($currentAlarm, $this->_calendarUser);
                 } else {
                     $_currentEvent->alarms->removeRecord($currentAlarm);
                 }
             }
         }
     }
     if (!$isOrganizer) {
         $_event->alarms->setOption('attendee', Calendar_Controller_Alarm::attendeeToOption($this->_calendarUser));
     }
     $_event->alarms->merge($_currentEvent->alarms);
     // assert organizer for personal calendars to be calendar owner
     if ($this->_currentEventFacadeContainer && $this->_currentEventFacadeContainer->getId() == $_event->container_id && $this->_currentEventFacadeContainer->type == Tinebase_Model_Container::TYPE_PERSONAL && !$_event->hasExternalOrganizer()) {
         $_event->organizer = $this->_calendarUser->user_id;
     }
     // in MS world only cal_user can do status updates
     if ($CUAttendee) {
         $CUAttendee->status_authkey = $currentCUAttendee ? $currentCUAttendee->status_authkey : NULL;
     }
 }
 /**
  * testAlarmAckInRecurException
  * 
  * @see 0009396: alarm_ack_time and alarm_snooze_time are not updated
  */
 public function testAlarmAckInRecurException()
 {
     $event = $this->testCreate();
     // save event as sclever to ack sclevers alarm
     Tinebase_Core::set(Tinebase_Core::USER, $this->_personas['sclever']);
     $exdateAlarms = $event->exdate->getFirstRecord()->alarms;
     $ackTime = Tinebase_DateTime::now();
     $scleverAlarm = new Tinebase_Model_Alarm(array('model' => 'Calendar_Model_Event', 'alarm_time' => $ackTime, 'minutes_before' => 90));
     $ackAlarm = Calendar_Controller_Alarm::getMatchingAlarm($exdateAlarms, $scleverAlarm);
     Calendar_Controller_Alarm::setAcknowledgeTime($ackAlarm, $ackTime);
     $updatedEvent = $this->_uit->update($event);
     $this->assertEquals(3, count($updatedEvent->alarms));
     $updatedAlarm = Calendar_Controller_Alarm::getMatchingAlarm($updatedEvent->exdate->getFirstRecord()->alarms, $scleverAlarm);
     $this->assertEquals($ackTime, Calendar_Controller_Alarm::getAcknowledgeTime($updatedAlarm));
     // check alarm ack client + ip
     $accessLog = Tinebase_Core::get(Tinebase_Core::USERACCESSLOG);
     $this->assertEquals($accessLog->ip, $updatedAlarm->getOption(Tinebase_Model_Alarm::OPTION_ACK_IP), 'ip not found in options: ' . print_r($updatedAlarm->toArray(), true));
     $expectedClient = 'type: ' . $accessLog->clienttype . '|useragent: ' . $_SERVER['HTTP_USER_AGENT'];
     $this->assertEquals($expectedClient, $updatedAlarm->getOption(Tinebase_Model_Alarm::OPTION_ACK_CLIENT), 'clienttype not found in options: ' . print_r($updatedAlarm->toArray(), true));
 }