/** * resolves rrule of given event(s) * * @param Tinebase_Record_RecordSet|Calendar_Model_Event $_events */ public static function resolveRrule($_events) { $events = $_events instanceof Tinebase_Record_RecordSet ? $_events : array($_events); foreach ($events as $event) { if ($event->rrule) { $event->rrule = Calendar_Model_Rrule::getRruleFromString($event->rrule); } } }
/** * appends sql to given select statement * * @param Zend_Db_Select $_select * @param Tinebase_Backend_Sql_Abstract $_backend * * @todo allow multiple role/status filters? */ public function appendFilterSql($_select, $_backend) { if (!is_array($this->_foreignIds)) { $this->_getFilterData(); $events = $this->_controller->search($this->_filterGroup); Calendar_Model_Rrule::mergeAndRemoveNonMatchingRecurrences($events, $this->_filterGroup); $this->_getForeignIds($events); } // this is supposed to run in ContactFilter context $contactIdFilter = new Addressbook_Model_ContactIdFilter('id', 'in', $this->_foreignIds); $contactIdFilter->appendFilterSql($_select, $_backend); }
public function testExportRecurSet() { $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $from = clone $this->_testEvent->dtstart; $until = clone $this->_testEvent->dtend; $until->addDay(2); $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($this->_testEvent, $exceptions, $from, $until); $this->_testEvent->exdate = array($recurSet->dtstart[0]); $eventSet = new Tinebase_Record_RecordSet('Calendar_Model_Event', array($this->_testEvent, $recurSet[1])); $exporter = new Calendar_Export_Ical(); $ics = $exporter->eventToIcal($eventSet); $this->assertEquals(2, preg_match_all('/BEGIN:VEVENT\\r\\n/', $ics, $matches), 'There should be exactly 2 VEVENT compontents'); }
/** * export records */ protected function _exportRecords() { // to support rrule & sorting we can't do pagination in calendar exports $records = $this->_controller->search($this->_filter, NULL, $this->_getRelations, false, 'export'); Calendar_Model_Rrule::mergeAndRemoveNonMatchingRecurrences($records, $this->_filter); $this->_sortRecords($records); $this->_resolveRecords($records); foreach ($records as $idx => $record) { $this->processRecord($record, $idx); } $result = array(); $this->_onAfterExportRecords($result); }
public function testAdoptAlarmDSTBoundary() { $event = $this->_getEvent(); $event->rrule = 'FREQ=DAILY;INTERVAL=1'; $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm', array(new Tinebase_Model_Alarm(array('minutes_before' => 30), TRUE))); $persistentEvent = $this->_eventController->create($event); // prepare alarm for last non DST instance $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $from = new Tinebase_DateTime('2012-03-24 00:00:00'); $until = new Tinebase_DateTime('2012-03-24 23:59:59'); $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($persistentEvent, $exceptions, $from, $until); $alarm = $persistentEvent->alarms->getFirstRecord(); $alarm->setOption('recurid', $recurSet[0]->recurid); Tinebase_Alarm::getInstance()->update($alarm); $loadedBaseEvent = $this->_eventController->get($persistentEvent->getId()); $alarm = $loadedBaseEvent->alarms->getFirstRecord(); $this->assertEquals('2012-03-24', substr($alarm->getOption('recurid'), -19, -9), 'precondition failed'); // adopt alarm $this->_eventController->adoptAlarmTime($loadedBaseEvent, $alarm, 'instance'); $this->assertEquals('2012-03-25', substr($alarm->getOption('recurid'), -19, -9), 'alarm adoption failed'); }
/** * testImportRruleNormalize * * @see 0009856: ics import: recurring events one day earlier */ public function testImportRruleNormalize() { $importer = new Calendar_Import_Ical(array('container_id' => $this->_getTestCalendar()->getId())); $importer->importFile(dirname(__FILE__) . '/files/ni-zsk.ics'); // fetch first of may in 2014 $from = new Tinebase_DateTime('2014-04-23 22:00:00'); $until = new Tinebase_DateTime('2014-05-23 22:00:00'); $events = Calendar_Controller_Event::getInstance()->search(new Calendar_Model_EventFilter(array(array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()), array('field' => 'period', 'operator' => 'within', 'value' => array('from' => $from->toString(), 'until' => $until->toString())))), NULL); Calendar_Model_Rrule::mergeRecurrenceSet($events, $from, $until); $firstOfMay2014 = $events[1]; $this->assertEquals('2014-04-30 22:00:00', $firstOfMay2014->dtstart); }
/** * sets rrule until helper field * * @return void */ public function setRruleUntil() { if (empty($this->rrule)) { $this->rrule_until = NULL; } else { $rrule = $this->rrule; if (!$this->rrule instanceof Calendar_Model_Rrule) { $rrule = new Calendar_Model_Rrule(array()); $rrule->setFromString($this->rrule); $this->rrule = $rrule; } if (isset($rrule->count)) { $this->rrule_until = NULL; $exdates = $this->exdate; $this->exdate = NULL; $lastOccurrence = Calendar_Model_Rrule::computeNextOccurrence($this, new Tinebase_Record_RecordSet('Calendar_Model_Event'), $this->dtend, $rrule->count - 1); $this->rrule_until = $lastOccurrence->dtend; $this->exdate = $exdates; } else { // set until to end of day in organizers timezone. // NOTE: this is in contrast to the iCal spec which says until should be the // dtstart of the last occurence. But as the client with the name of the // spec sets it to the end of the day, we do it also. if ($rrule->until instanceof Tinebase_DateTime && !$this->is_all_day_event) { $rrule->until->setTimezone($this->originator_tz); // NOTE: subSecond cause some clients send 00:00:00 for midnight $rrule->until->subSecond(1)->setTime(23, 59, 59); $rrule->until->setTimezone('UTC'); } $this->rrule_until = $rrule->until; } } if ($this->rrule_until && $this->rrule_until->getTimeStamp() - $this->dtstart->getTimeStamp() < -1) { throw new Tinebase_Exception_Record_Validation('rrule until must not be before dtstart'); } }
/** * converts Tinebase_Record_RecordSet to external format * * @param Tinebase_Record_RecordSet $_records * @param Tinebase_Model_Filter_FilterGroup $_filter * @param Tinebase_Model_Pagination $_pagination * * @return mixed */ public function fromTine20RecordSet(Tinebase_Record_RecordSet $_records = NULL, $_filter = NULL, $_pagination = NULL) { if (count($_records) == 0) { return array(); } Tinebase_Notes::getInstance()->getMultipleNotesOfRecords($_records); Tinebase_Tags::getInstance()->getMultipleTagsOfRecords($_records); if (Tinebase_Core::isFilesystemAvailable()) { Tinebase_FileSystem_RecordAttachments::getInstance()->getMultipleAttachmentsOfRecords($_records); } Calendar_Model_Attender::resolveAttendee($_records->attendee, TRUE, $_records); Calendar_Convert_Event_Json::resolveRrule($_records); Calendar_Controller_Event::getInstance()->getAlarms($_records); Calendar_Convert_Event_Json::resolveGrantsOfExternalOrganizers($_records); Calendar_Model_Rrule::mergeAndRemoveNonMatchingRecurrences($_records, $_filter); $_records->sortByPagination($_pagination); Tinebase_Frontend_Json_Abstract::resolveContainersAndTags($_records, array('container_id')); $_records->setTimezone(Tinebase_Core::getUserTimezone()); $_records->convertDates = true; $eventsData = $_records->toArray(); foreach ($eventsData as $idx => $eventData) { if (!(isset($eventData[Tinebase_Model_Grants::GRANT_READ]) || array_key_exists(Tinebase_Model_Grants::GRANT_READ, $eventData)) || !$eventData[Tinebase_Model_Grants::GRANT_READ]) { $eventsData[$idx] = array_intersect_key($eventsData[$idx], array_flip(array('id', 'dtstart', 'dtend', 'transp', 'is_all_day_event'))); } } return $eventsData; }
/** * (non-PHPdoc) * @see ActiveSync_Frontend_Abstract::toSyncrotonModel() * @todo handle BusyStatus */ public function toSyncrotonModel($entry, array $options = array()) { if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) { Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . " calendar data " . print_r($entry->toArray(), true)); } $syncrotonEvent = new Syncroton_Model_Event(); foreach ($this->_mapping as $syncrotonProperty => $tine20Property) { if (empty($entry->{$tine20Property}) && $entry->{$tine20Property} != '0' || count($entry->{$tine20Property}) === 0) { continue; } switch ($tine20Property) { case 'alarms': $entry->{$tine20Property}->sort('alarm_time'); $alarm = $entry->alarms->getFirstRecord(); if ($alarm instanceof Tinebase_Model_Alarm) { // NOTE: option minutes_before is always calculated by Calendar_Controller_Event::_inspectAlarmSet $minutesBefore = (int) $alarm->getOption('minutes_before'); // avoid negative alarms which may break phones if ($minutesBefore >= 0) { $syncrotonEvent->{$syncrotonProperty} = $minutesBefore; } } break; case 'attendee': if ($this->_device->devicetype === Syncroton_Model_Device::TYPE_IPHONE && $entry->container_id !== $this->_getDefaultContainerId()) { continue; } // fill attendee cache Calendar_Model_Attender::resolveAttendee($entry->{$tine20Property}, FALSE); $attendees = array(); foreach ($entry->{$tine20Property} as $attenderObject) { $attendee = new Syncroton_Model_EventAttendee(); $attendee->name = $attenderObject->getName(); $attendee->email = $attenderObject->getEmail(); $acsType = array_search($attenderObject->role, $this->_attendeeTypeMapping); $attendee->attendeeType = $acsType ? $acsType : Syncroton_Model_EventAttendee::ATTENDEE_TYPE_REQUIRED; $acsStatus = array_search($attenderObject->status, $this->_attendeeStatusMapping); $attendee->attendeeStatus = $acsStatus ? $acsStatus : Syncroton_Model_EventAttendee::ATTENDEE_STATUS_UNKNOWN; $attendees[] = $attendee; } $syncrotonEvent->{$syncrotonProperty} = $attendees; // set own status if (($ownAttendee = Calendar_Model_Attender::getOwnAttender($entry->attendee)) !== null && ($busyType = array_search($ownAttendee->status, $this->_busyStatusMapping)) !== false) { $syncrotonEvent->busyStatus = $busyType; } break; case 'class': $syncrotonEvent->{$syncrotonProperty} = $entry->{$tine20Property} == Calendar_Model_Event::CLASS_PRIVATE ? 2 : 0; break; case 'description': $syncrotonEvent->{$syncrotonProperty} = new Syncroton_Model_EmailBody(array('type' => Syncroton_Model_EmailBody::TYPE_PLAINTEXT, 'data' => $entry->{$tine20Property})); break; case 'dtend': if ($entry->{$tine20Property} instanceof DateTime) { if ($entry->is_all_day_event == true) { // whole day events ends at 23:59:59 in Tine 2.0 but 00:00 the next day in AS $dtend = clone $entry->{$tine20Property}; $dtend->addSecond($dtend->get('s') == 59 ? 1 : 0); $dtend->addMinute($dtend->get('i') == 59 ? 1 : 0); $syncrotonEvent->{$syncrotonProperty} = $dtend; } else { $syncrotonEvent->{$syncrotonProperty} = $entry->{$tine20Property}; } } break; case 'dtstart': if ($entry->{$tine20Property} instanceof DateTime) { $syncrotonEvent->{$syncrotonProperty} = $entry->{$tine20Property}; } break; case 'exdate': // handle exceptions of repeating events if ($entry->{$tine20Property} instanceof Tinebase_Record_RecordSet && $entry->{$tine20Property}->count() > 0) { $exceptions = array(); foreach ($entry->exdate as $exdate) { $exception = new Syncroton_Model_EventException(); // send the Deleted element only, when needed // HTC devices ignore the value(0 or 1) of the Deleted element if ((int) $exdate->is_deleted === 1) { $exception->deleted = 1; } $exception->exceptionStartTime = $exdate->getOriginalDtStart(); if ((int) $exdate->is_deleted === 0) { $exceptionSyncrotonEvent = $this->toSyncrotonModel($exdate); foreach ($exception->getProperties() as $property) { if (isset($exceptionSyncrotonEvent->{$property})) { $exception->{$property} = $exceptionSyncrotonEvent->{$property}; } } unset($exceptionSyncrotonEvent); } $exceptions[] = $exception; } $syncrotonEvent->{$syncrotonProperty} = $exceptions; } break; case 'rrule': if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " calendar rrule " . $entry->{$tine20Property}); } $rrule = Calendar_Model_Rrule::getRruleFromString($entry->{$tine20Property}); $recurrence = new Syncroton_Model_EventRecurrence(); // required fields switch ($rrule->freq) { case Calendar_Model_Rrule::FREQ_DAILY: $recurrence->type = Syncroton_Model_EventRecurrence::TYPE_DAILY; break; case Calendar_Model_Rrule::FREQ_WEEKLY: $recurrence->type = Syncroton_Model_EventRecurrence::TYPE_WEEKLY; $recurrence->dayOfWeek = $this->_convertDayToBitMask($rrule->byday); break; case Calendar_Model_Rrule::FREQ_MONTHLY: if (!empty($rrule->bymonthday)) { $recurrence->type = Syncroton_Model_EventRecurrence::TYPE_MONTHLY; $recurrence->dayOfMonth = $rrule->bymonthday; } else { $weekOfMonth = (int) substr($rrule->byday, 0, -2); $weekOfMonth = $weekOfMonth == -1 ? 5 : $weekOfMonth; $dayOfWeek = substr($rrule->byday, -2); $recurrence->type = Syncroton_Model_EventRecurrence::TYPE_MONTHLY_DAYN; $recurrence->weekOfMonth = $weekOfMonth; $recurrence->dayOfWeek = $this->_convertDayToBitMask($dayOfWeek); } break; case Calendar_Model_Rrule::FREQ_YEARLY: if (!empty($rrule->bymonthday)) { $recurrence->type = Syncroton_Model_EventRecurrence::TYPE_YEARLY; $recurrence->dayOfMonth = $rrule->bymonthday; $recurrence->monthOfYear = $rrule->bymonth; } else { $weekOfMonth = (int) substr($rrule->byday, 0, -2); $weekOfMonth = $weekOfMonth == -1 ? 5 : $weekOfMonth; $dayOfWeek = substr($rrule->byday, -2); $recurrence->type = Syncroton_Model_EventRecurrence::TYPE_YEARLY_DAYN; $recurrence->weekOfMonth = $weekOfMonth; $recurrence->dayOfWeek = $this->_convertDayToBitMask($dayOfWeek); $recurrence->monthOfYear = $rrule->bymonth; } break; } // required field $recurrence->interval = $rrule->interval ? $rrule->interval : 1; if ($rrule->count) { $recurrence->occurrences = $rrule->count; } else { if ($rrule->until instanceof DateTime) { $recurrence->until = $rrule->until; } } $syncrotonEvent->{$syncrotonProperty} = $recurrence; break; case 'tags': $syncrotonEvent->{$syncrotonProperty} = $entry->{$tine20Property}->name; break; default: $syncrotonEvent->{$syncrotonProperty} = $entry->{$tine20Property}; break; } } $timeZoneConverter = ActiveSync_TimezoneConverter::getInstance(Tinebase_Core::getLogger(), Tinebase_Core::get(Tinebase_Core::CACHE)); $syncrotonEvent->timezone = $timeZoneConverter->encodeTimezone(Tinebase_Core::getUserTimezone()); $syncrotonEvent->meetingStatus = 1; $syncrotonEvent->dtStamp = $entry->creation_time; $syncrotonEvent->uID = $entry->uid; $this->_addOrganizer($syncrotonEvent, $entry); return $syncrotonEvent; }
/** * returns feast days as array containing Tinebase_DateTime objects * if the period exceeds the contracts' period(s), the contracts' period(s) will be used * * @param HumanResources_Model_Contract|Tinebase_Record_RecordSet $contracts * @param Tinebase_DateTime $firstDate * @param Tinebase_DateTime $lastDate * @return array */ public function getFeastDays($contracts, Tinebase_DateTime $firstDate, Tinebase_DateTime $lastDate) { $contracts = $this->_convertToRecordSet($contracts); $dates = array(); foreach ($contracts as $contract) { $fd = $this->_getFirstDate($contract, $firstDate); $ld = $this->_getLastDate($contract, $lastDate); // on calendar search we have to do this to get the right interval: $fd->subSecond(1); $ld->addSecond(1); $periodFilter = new Calendar_Model_PeriodFilter(array('field' => 'period', 'operator' => 'within', 'value' => array('from' => $fd, 'until' => $ld))); $events = Calendar_Controller_Event::getInstance()->search(new Calendar_Model_EventFilter(array(array('field' => 'container_id', 'operator' => 'equals', 'value' => $contract->feast_calendar_id)))); Calendar_Model_Rrule::mergeRecurrenceSet($events, $fd, $ld); $events->setTimezone(Tinebase_Core::getUserTimezone()); foreach ($events as $event) { if (!$event->isInPeriod($periodFilter)) { continue; } if ($event->is_all_day_event) { $days = round(($event->dtend->getTimestamp() - $event->dtstart->getTimestamp()) / 86400); $i = 0; while ($i < $days) { $dateOfEvent = clone $event->dtstart; $dateOfEvent->addDay($i); $dates[] = $dateOfEvent; $i++; } } else { $dates[] = $event->dtstart; } } } return array_unique($dates); }
/** * testRecuringAlarmWithThisAndFutureSplit * * @see 0008386: alarm is sent for recur series that is already over */ public function testRecuringAlarmWithThisAndFutureSplit() { $this->markTestSkipped('@see 0009816: fix failing testRecuringAlarmWithThisAndFutureSplit test'); $event = $this->_getEvent(); // lets flush mailer so next flushing ist faster! Tinebase_Alarm::getInstance()->sendPendingAlarms("Tinebase_Event_Async_Minutely"); self::flushMailer(); // make sure next occurence contains now $event->dtstart = Tinebase_DateTime::now()->subMonth(1)->addDay(1)->subHour(2); $event->dtend = clone $event->dtstart; $event->dtend->addMinute(60); $event->rrule = 'FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=' . $event->dtstart->format('d'); $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm', array(new Tinebase_Model_Alarm(array('minutes_before' => 2880), TRUE))); $persistentEvent = $this->_eventController->create($event); // make sure, next alarm is for next month's event Tinebase_Alarm::getInstance()->sendPendingAlarms("Tinebase_Event_Async_Minutely"); self::flushMailer(); // split THISANDFUTURE, alarm of old series should be set to SUCCESS because it no longer should be sent $from = $event->dtstart; $until = $event->dtend->addMonth(2); $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($persistentEvent, $exceptions, $from, $until); $recurEvent = count($recurSet) > 1 ? $recurSet[1] : $recurSet[0]; // next month $recurEvent->summary = 'split series'; $newPersistentEvent = $this->_eventController->createRecurException($recurEvent, FALSE, TRUE); // check alarms $oldSeriesAlarm = Tinebase_Alarm::getInstance()->getAlarmsOfRecord('Calendar_Model_Event', $persistentEvent->getId())->getFirstRecord(); $this->assertEquals(Tinebase_Model_Alarm::STATUS_SUCCESS, $oldSeriesAlarm->sent_status, 'no pending alarm should exist for old series: ' . print_r($oldSeriesAlarm->toArray(), TRUE)); }
protected function _getEventsForPeriodAndCalendar($calendarId, $from, $until) { $filter = new Calendar_Model_EventFilter(array(array('field' => 'period', 'operator' => 'within', 'value' => array("from" => $from, "until" => $until)), array('field' => 'container_id', 'operator' => 'equals', 'value' => $calendarId))); $events = Calendar_Controller_Event::getInstance()->search($filter); Calendar_Model_Rrule::mergeAndRemoveNonMatchingRecurrences($events, $filter); return $events; }
/** * append event data to xml element * * @todo handle BusyStatus * @todo handle TimeZone data * * @param DOMElement $_domParrent the parrent xml node * @param string $_folderId the local folder id * @param boolean $_withBody retrieve body of entry */ public function appendXML(DOMElement $_domParrent, $_collectionData, $_serverId) { $data = $_serverId instanceof Tinebase_Record_Abstract ? $_serverId : $this->_contentController->get($_serverId); if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) { Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . " calendar data " . print_r($data->toArray(), true)); } // add calendar namespace $_domParrent->ownerDocument->documentElement->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:Calendar', 'uri:Calendar'); foreach ($this->_mapping as $key => $value) { if (!empty($data->{$value}) || $data->{$value} == '0') { $nodeContent = null; switch ($value) { case 'dtend': if ($data->{$value} instanceof DateTime) { if ($data->is_all_day_event == true) { // whole day events ends at 23:59:59 in Tine 2.0 but 00:00 the next day in AS $dtend = clone $data->dtend; $dtend->addSecond($dtend->get('s') == 59 ? 1 : 0); $dtend->addMinute($dtend->get('i') == 59 ? 1 : 0); $nodeContent = $dtend->format('Ymd\\THis') . 'Z'; } else { $nodeContent = $data->dtend->format('Ymd\\THis') . 'Z'; } } break; case 'dtstart': if ($data->{$value} instanceof DateTime) { $nodeContent = $data->{$value}->format('Ymd\\THis') . 'Z'; } break; default: $nodeContent = $data->{$value}; break; } // skip empty elements if ($nodeContent === null || $nodeContent == '') { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . " Value for {$key} is empty. Skip element."); continue; } // strip off any non printable control characters if (!ctype_print($nodeContent)) { $nodeContent = $this->removeControlChars($nodeContent); } $node = $_domParrent->ownerDocument->createElementNS('uri:Calendar', $key); $node->appendChild($_domParrent->ownerDocument->createTextNode($nodeContent)); $_domParrent->appendChild($node); } } if (!empty($data->description)) { if (version_compare($this->_device->acsversion, '12.0', '>=') === true) { $body = $_domParrent->appendChild(new DOMElement('Body', null, 'uri:AirSyncBase')); $body->appendChild(new DOMElement('Type', 1, 'uri:AirSyncBase')); // create a new DOMElement ... $dataTag = new DOMElement('Data', null, 'uri:AirSyncBase'); // ... append it to parent node aka append it to the document ... $body->appendChild($dataTag); // ... and now add the content (DomText takes care of special chars) $dataTag->appendChild(new DOMText($data->description)); } else { // create a new DOMElement ... $node = new DOMElement('Body', null, 'uri:Calendar'); // ... append it to parent node aka append it to the document ... $_domParrent->appendChild($node); // ... and now add the content (DomText takes care of special chars) $node->appendChild(new DOMText($data->description)); } } if (!empty($data->alarms)) { $alarm = $data->alarms->getFirstRecord(); if ($alarm instanceof Tinebase_Model_Alarm) { // NOTE: option minutes_before is always calculated by Calendar_Controller_Event::_inspectAlarmSet $minutesBefore = (int) $alarm->getOption('minutes_before'); if ($minutesBefore >= 0) { $_domParrent->appendChild(new DOMElement('Reminder', $minutesBefore, 'uri:Calendar')); } } } if (!empty($data->rrule)) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " calendar rrule " . $data->rrule); } $rrule = Calendar_Model_Rrule::getRruleFromString($data->rrule); $recurrence = $_domParrent->appendChild(new DOMElement('Recurrence', null, 'uri:Calendar')); // required fields switch ($rrule->freq) { case Calendar_Model_Rrule::FREQ_DAILY: $recurrence->appendChild(new DOMElement('Type', self::RECUR_TYPE_DAILY, 'uri:Calendar')); break; case Calendar_Model_Rrule::FREQ_WEEKLY: $recurrence->appendChild(new DOMElement('Type', self::RECUR_TYPE_WEEKLY, 'uri:Calendar')); $recurrence->appendChild(new DOMElement('DayOfWeek', $this->_convertDayToBitMask($rrule->byday), 'uri:Calendar')); break; case Calendar_Model_Rrule::FREQ_MONTHLY: if (!empty($rrule->bymonthday)) { $recurrence->appendChild(new DOMElement('Type', self::RECUR_TYPE_MONTHLY, 'uri:Calendar')); $recurrence->appendChild(new DOMElement('DayOfMonth', $rrule->bymonthday, 'uri:Calendar')); } else { $weekOfMonth = (int) substr($rrule->byday, 0, -2); $weekOfMonth = $weekOfMonth == -1 ? 5 : $weekOfMonth; $dayOfWeek = substr($rrule->byday, -2); $recurrence->appendChild(new DOMElement('Type', self::RECUR_TYPE_MONTHLY_DAYN, 'uri:Calendar')); $recurrence->appendChild(new DOMElement('WeekOfMonth', $weekOfMonth, 'uri:Calendar')); $recurrence->appendChild(new DOMElement('DayOfWeek', $this->_convertDayToBitMask($dayOfWeek), 'uri:Calendar')); } break; case Calendar_Model_Rrule::FREQ_YEARLY: if (!empty($rrule->bymonthday)) { $recurrence->appendChild(new DOMElement('Type', self::RECUR_TYPE_YEARLY, 'uri:Calendar')); $recurrence->appendChild(new DOMElement('DayOfMonth', $rrule->bymonthday, 'uri:Calendar')); $recurrence->appendChild(new DOMElement('MonthOfYear', $rrule->bymonth, 'uri:Calendar')); } else { $weekOfMonth = (int) substr($rrule->byday, 0, -2); $weekOfMonth = $weekOfMonth == -1 ? 5 : $weekOfMonth; $dayOfWeek = substr($rrule->byday, -2); $recurrence->appendChild(new DOMElement('Type', self::RECUR_TYPE_YEARLY_DAYN, 'uri:Calendar')); $recurrence->appendChild(new DOMElement('WeekOfMonth', $weekOfMonth, 'uri:Calendar')); $recurrence->appendChild(new DOMElement('DayOfWeek', $this->_convertDayToBitMask($dayOfWeek), 'uri:Calendar')); $recurrence->appendChild(new DOMElement('MonthOfYear', $rrule->bymonth, 'uri:Calendar')); } break; } // required field $recurrence->appendChild(new DOMElement('Interval', $rrule->interval, 'uri:Calendar')); if ($rrule->until instanceof DateTime) { $recurrence->appendChild(new DOMElement('Until', $rrule->until->format('Ymd\\THis') . 'Z', 'uri:Calendar')); } // handle exceptions of repeating events if ($data->exdate instanceof Tinebase_Record_RecordSet && $data->exdate->count() > 0) { $exceptionsTag = $_domParrent->appendChild(new DOMElement('Exceptions', null, 'uri:Calendar')); foreach ($data->exdate as $exception) { $exceptionTag = $exceptionsTag->appendChild(new DOMElement('Exception', null, 'uri:Calendar')); $exceptionTag->appendChild(new DOMElement('Deleted', (int) $exception->is_deleted, 'uri:Calendar')); $exceptionTag->appendChild(new DOMElement('ExceptionStartTime', $exception->getOriginalDtStart()->format('Ymd\\THis') . 'Z', 'uri:Calendar')); if ((int) $exception->is_deleted === 0) { $this->appendXML($exceptionTag, $_collectionData, $exception); } } } } if (count($data->attendee) > 0) { // fill attendee cache Calendar_Model_Attender::resolveAttendee($data->attendee, FALSE); $attendees = $_domParrent->ownerDocument->createElementNS('uri:Calendar', 'Attendees'); foreach ($data->attendee as $attenderObject) { $attendee = $attendees->appendChild(new DOMElement('Attendee', null, 'uri:Calendar')); $attendee->appendChild(new DOMElement('Name', $attenderObject->getName(), 'uri:Calendar')); $attendee->appendChild(new DOMElement('Email', $attenderObject->getEmail(), 'uri:Calendar')); if (version_compare($this->_device->acsversion, '12.0', '>=') === true) { $acsType = array_search($attenderObject->role, $this->_attendeeTypeMapping); $attendee->appendChild(new DOMElement('AttendeeType', $acsType ? $acsType : self::ATTENDEE_TYPE_REQUIRED, 'uri:Calendar')); $acsStatus = array_search($attenderObject->status, $this->_attendeeStatusMapping); $attendee->appendChild(new DOMElement('AttendeeStatus', $acsStatus ? $acsStatus : self::ATTENDEE_STATUS_UNKNOWN, 'uri:Calendar')); } } if ($attendees->hasChildNodes()) { $_domParrent->appendChild($attendees); } // set own status if (($ownAttendee = Calendar_Model_Attender::getOwnAttender($data->attendee)) !== null && ($busyType = array_search($ownAttendee->status, $this->_busyStatusMapping)) !== false) { $_domParrent->appendChild(new DOMElement('BusyStatus', $busyType, 'uri:Calendar')); } } $timeZoneConverter = ActiveSync_TimezoneConverter::getInstance(Tinebase_Core::getLogger(), Tinebase_Core::get(Tinebase_Core::CACHE)); $_domParrent->appendChild(new DOMElement('Timezone', $timeZoneConverter->encodeTimezone(Tinebase_Core::get(Tinebase_Core::USERTIMEZONE)), 'uri:Calendar')); $_domParrent->appendChild(new DOMElement('MeetingStatus', 1, 'uri:Calendar')); $_domParrent->appendChild(new DOMElement('Sensitivity', 0, 'uri:Calendar')); $_domParrent->appendChild(new DOMElement('DtStamp', $data->creation_time->format('Ymd\\THis') . 'Z', 'uri:Calendar')); $_domParrent->appendChild(new DOMElement('UID', $data->uid, 'uri:Calendar')); if (!empty($data->organizer)) { try { $contact = Addressbook_Controller_Contact::getInstance()->get($data->organizer); $_domParrent->appendChild(new DOMElement('OrganizerName', $contact->n_fileas, 'uri:Calendar')); $_domParrent->appendChild(new DOMElement('OrganizerEmail', $contact->email, 'uri:Calendar')); } catch (Tinebase_Exception_AccessDenied $e) { // set the current account as organizer // if organizer is not set, you can not edit the event on the Motorola Milestone $_domParrent->appendChild(new DOMElement('OrganizerName', Tinebase_Core::getUser()->accountFullName, 'uri:Calendar')); if (isset(Tinebase_Core::getUser()->accountEmailAddress)) { $_domParrent->appendChild(new DOMElement('OrganizerEmail', Tinebase_Core::getUser()->accountEmailAddress, 'uri:Calendar')); } } } else { // set the current account as organizer // if organizer is not set, you can not edit the event on the Motorola Milestone $_domParrent->appendChild(new DOMElement('OrganizerName', Tinebase_Core::getUser()->accountFullName, 'uri:Calendar')); if (isset(Tinebase_Core::getUser()->accountEmailAddress)) { $_domParrent->appendChild(new DOMElement('OrganizerEmail', Tinebase_Core::getUser()->accountEmailAddress, 'uri:Calendar')); } } if (isset($data->tags) && count($data->tags) > 0) { $categories = $_domParrent->appendChild(new DOMElement('Categories', null, 'uri:Calendar')); foreach ($data->tags as $tag) { $categories->appendChild(new DOMElement('Category', $tag, 'uri:Calendar')); } } }
/** * test xml generation for IPhone * * FIXME fix this test! -> seems to fail depending on the current time / date */ public function testAppendXml_dailyEvent() { $imp = new DOMImplementation(); $dtd = $imp->createDocumentType('AirSync', "-//AIRSYNC//DTD AirSync//EN", "http://www.microsoft.com/"); $testDom = $imp->createDocument('uri:AirSync', 'Sync', $dtd); $testDom->formatOutput = true; $testDom->encoding = 'utf-8'; $testDom->documentElement->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:Calendar', 'uri:Calendar'); $collections = $testDom->documentElement->appendChild($testDom->createElementNS('uri:AirSync', 'Collections')); $collection = $collections->appendChild($testDom->createElementNS('uri:AirSync', 'Collection')); $commands = $collection->appendChild($testDom->createElementNS('uri:AirSync', 'Commands')); $add = $commands->appendChild($testDom->createElementNS('uri:AirSync', 'Add')); $appData = $add->appendChild($testDom->createElementNS('uri:AirSync', 'ApplicationData')); $controller = new ActiveSync_Controller_Calendar($this->objects['deviceIPhone'], new Tinebase_DateTime()); $controller->appendXML($appData, null, $this->objects['eventDaily']->getId(), array()); // namespace === uri:Calendar $this->assertEquals(ActiveSync_Controller_Calendar::RECUR_TYPE_DAILY, @$testDom->getElementsByTagNameNS('uri:Calendar', 'Type')->item(0)->nodeValue, $testDom->saveXML()); $this->assertEquals(4, @$testDom->getElementsByTagNameNS('uri:Calendar', 'Exception')->length, $testDom->saveXML()); $this->assertEquals(4, @$testDom->getElementsByTagNameNS('uri:Calendar', 'ExceptionStartTime')->length, $testDom->saveXML()); $this->assertEquals(3, @$testDom->getElementsByTagNameNS('uri:Calendar', 'Subject')->length, $testDom->saveXML()); $endTime = $this->objects['eventDaily']->dtend->format("Ymd\\THis") . 'Z'; $this->assertEquals($endTime, @$testDom->getElementsByTagNameNS('uri:Calendar', 'EndTime')->item(0)->nodeValue, $testDom->saveXML()); $untilTime = Calendar_Model_Rrule::getRruleFromString($this->objects['eventDaily']->rrule)->until->format("Ymd\\THis") . 'Z'; $this->assertEquals($untilTime, @$testDom->getElementsByTagNameNS('uri:Calendar', 'Until')->item(0)->nodeValue, $testDom->saveXML()); }
/** * inspect update of one record * * @param Tinebase_Record_Interface $_record the update record * @param Tinebase_Record_Interface $_oldRecord the current persistent record * @return void */ protected function _inspectBeforeUpdate($_record, $_oldRecord) { // if dtstart of an event changes, we update the originator_tz, alarm times if (!$_oldRecord->dtstart->equals($_record->dtstart)) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' dtstart changed -> adopting organizer_tz'); } $_record->originator_tz = Tinebase_Core::get(Tinebase_Core::USERTIMEZONE); // update exdates and recurids if dtsart of an recurevent changes if (!empty($_record->rrule)) { $diff = $_oldRecord->dtstart->diff($_record->dtstart); // update exceptions $exceptions = $this->getRecurExceptions($_record); if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' dtstart of a series changed -> adopting ' . count($exceptions) . ' recurid(s)'); } $exdates = array(); foreach ($exceptions as $exception) { $exception->recurid = new Tinebase_DateTime(substr($exception->recurid, -19)); Calendar_Model_Rrule::addUTCDateDstFix($exception->recurid, $diff, $_record->originator_tz); $exdates[] = $exception->recurid; $exception->setRecurId(); $this->_backend->update($exception); } $_record->exdate = $exdates; } } // delete recur exceptions if update is not longer a recur series if (!empty($_oldRecord->rrule) && empty($_record->rrule)) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' deleteing recur exceptions as event is no longer a recur series'); } $this->_backend->delete($this->getRecurExceptions($_record)); } // touch base event of a recur series if an persisten exception changes if ($_record->recurid) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' touch base event of a persisten exception'); } $baseEvent = $this->getRecurBaseEvent($_record); $this->_touch($baseEvent, TRUE); } }
public function testCreateRecurExceptionWithEditGrantOnly() { $this->markTestIncomplete('temporarily disabled until fixed'); // set testuser to have editgrant for sclever Tinebase_Container::getInstance()->setGrants($this->_getPersonasDefaultCals('sclever'), new Tinebase_Record_RecordSet('Tinebase_Model_Grants', array(array('account_id' => $this->_getPersona('sclever')->getId(), 'account_type' => 'user', Tinebase_Model_Grants::GRANT_READ => true, Tinebase_Model_Grants::GRANT_ADD => true, Tinebase_Model_Grants::GRANT_EDIT => true, Tinebase_Model_Grants::GRANT_DELETE => true, Tinebase_Model_Grants::GRANT_PRIVATE => true, Tinebase_Model_Grants::GRANT_ADMIN => true, Tinebase_Model_Grants::GRANT_FREEBUSY => true), array('account_id' => Tinebase_Core::getUser()->getId(), 'account_type' => 'user', Tinebase_Model_Grants::GRANT_READ => false, Tinebase_Model_Grants::GRANT_ADD => false, Tinebase_Model_Grants::GRANT_EDIT => true, Tinebase_Model_Grants::GRANT_DELETE => false, Tinebase_Model_Grants::GRANT_PRIVATE => false, Tinebase_Model_Grants::GRANT_ADMIN => false, Tinebase_Model_Grants::GRANT_FREEBUSY => false))), TRUE); $persistentEvent = $this->_createEventInPersonasCalendar('sclever', 'sclever'); $persistentEvent->rrule = 'FREQ=DAILY;INTERVAL=1'; $updatedEvent = $this->_uit->update($persistentEvent); $events = $this->_uit->search(new Calendar_Model_EventFilter(array(array('field' => 'id', 'operator' => 'equals', 'value' => $persistentEvent->getId()))), NULL, FALSE, FALSE); $this->assertEquals(1, count($events), 'failed to search fb event'); Calendar_Model_Rrule::mergeRecurrenceSet($events, $updatedEvent->dtstart, $updatedEvent->dtstart->getClone()->addDay(7)); $this->assertEquals(8, count($events), 'failed to merge recurrence set'); $events[3]->summary = 'exception'; $exception = $this->_uit->createRecurException($events[3]); $this->assertEquals('exception', $exception->summary); }
/** * returns multiple records prepared for json transport * * @param Tinebase_Record_RecordSet $_records Tinebase_Record_Abstract * @param Tinebase_Model_Filter_FilterGroup $_filter * @param Tinebase_Model_Pagination $_pagination needed for sorting * @return array data * * @todo perhaps we need to resolveContainerTagsUsers() before mergeAndRemoveNonMatchingRecurrences(), but i'm not sure about that * @todo use Calendar_Convert_Event_Json */ protected function _multipleRecordsToJson(Tinebase_Record_RecordSet $_records, $_filter = NULL, $_pagination = NULL) { if ($_records->getRecordClassName() == 'Calendar_Model_Event') { if (is_null($_filter)) { throw new Tinebase_Exception_InvalidArgument('Required argument $_filter is missing'); } Tinebase_Notes::getInstance()->getMultipleNotesOfRecords($_records); Calendar_Model_Attender::resolveAttendee($_records->attendee, TRUE, $_records); Calendar_Convert_Event_Json::resolveOrganizer($_records); Calendar_Convert_Event_Json::resolveRrule($_records); Calendar_Controller_Event::getInstance()->getAlarms($_records); Calendar_Model_Rrule::mergeAndRemoveNonMatchingRecurrences($_records, $_filter); $_records->sortByPagination($_pagination); $eventsData = parent::_multipleRecordsToJson($_records); foreach ($eventsData as $eventData) { if (!array_key_exists(Tinebase_Model_Grants::GRANT_READ, $eventData) || !$eventData[Tinebase_Model_Grants::GRANT_READ]) { $eventData['notes'] = array(); $eventData['tags'] = array(); } } return $eventsData; } return parent::_multipleRecordsToJson($_records); }
public function testExdateContainerMoveUpdateException() { $this->markTestSkipped('exdate container move not yet forbidden'); $event = $this->_getDailyEvent(new Tinebase_DateTime('2014-02-03 09:00:00')); $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $from = new Tinebase_DateTime('2014-02-01 00:00:00'); $until = new Tinebase_DateTime('2014-02-29 23:59:59'); $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($event, $exceptions, $from, $until); $recurSet[2]->summary = 'exdate'; $updatedPersistentEvent = $this->_controller->createRecurException($recurSet[2]); $this->setExpectedException('Calendar_Exception_ExdateContainer'); $updatedPersistentEvent->container_id = $this->_getTestContainer('Calendar')->getId(); $this->_controller->update($updatedPersistentEvent); }
/** * testUpdatePreserveAlarmProperties * * @see #7430: Calendar sends too much alarms for recurring events */ public function testUpdatePreserveAlarmProperties() { $alarm30 = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm', array(array('minutes_before' => 30)), TRUE); $event = $this->_getEvent(); $event->dtstart = Tinebase_DateTime::now()->subDay(1)->addMinute(15); $event->dtend = clone $event->dtstart; $event->dtend->addHour(2); $event->rrule = 'FREQ=DAILY;INTERVAL=1;COUNT=3'; $event->alarms = clone $alarm30; $event->organizer = Tinebase_Core::getUser()->contact_id; $event = Calendar_Controller_Event::getInstance()->create($event); $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($event, $exceptions, $event->dtstart, Tinebase_DateTime::now()->addDay(1)); $exceptionEvent = Calendar_Controller_Event::getInstance()->createRecurException($recurSet->getFirstRecord()); Tinebase_Alarm::getInstance()->sendPendingAlarms("Tinebase_Event_Async_Minutely"); Calendar_Controller_EventNotificationsTests::flushMailer(); $event = $this->_uit->get($event->getId()); $persistentAlarm = $event->exdate[0]->alarms->getFirstRecord(); $event->alarms = $event->alarms = clone $alarm30; Calendar_Controller_Alarm::setAcknowledgeTime($event->alarms, Tinebase_DateTime::now()); foreach ($event->exdate as $exdate) { $exdate->alarms = clone $alarm30; } $updatedEvent = $this->_uit->update($event); $updatedAlarm = $updatedEvent->exdate[0]->alarms->getFirstRecord(); $this->assertNotNull($persistentAlarm); $diff = $persistentAlarm->diff($updatedAlarm); $this->assertTrue($diff->isEmpty(), 'no diff'); $this->assertTrue(Calendar_Controller_Alarm::getAcknowledgeTime($updatedEvent->alarms->getFirstRecord()) instanceof Tinebase_DateTime, 'ack time missing'); }
/** * sets rrule until helper field * * @return void */ public function setRruleUntil() { if (empty($this->rrule)) { $this->rrule_until = NULL; } else { $rrule = $this->rrule; if (!$this->rrule instanceof Calendar_Model_Rrule) { $rrule = new Calendar_Model_Rrule(array()); $rrule->setFromString($this->rrule); } if (isset($rrule->count)) { $this->rrule_until = NULL; $exdates = $this->exdate; $this->exdate = NULL; $lastOccurrence = Calendar_Model_Rrule::computeNextOccurrence($this, new Tinebase_Record_RecordSet('Calendar_Model_Event'), $this->dtend, $rrule->count - 1); $this->rrule_until = $lastOccurrence->dtend; $this->exdate = $exdates; } else { $this->rrule_until = $rrule->until; } } }
/** * testAcceptInvitationForRecurringEventException * * @see 0009022: can not accept invitation to recurring event exception * @see 0009510: is it allowed to have no main vevent in ics? */ public function testAcceptInvitationForRecurringEventException() { Tinebase_Container::getInstance()->setGrants($this->objects['initialContainer'], new Tinebase_Record_RecordSet('Tinebase_Model_Grants', array($this->_getAllCalendarGrants(), array('account_id' => 0, 'account_type' => 'anyone', Tinebase_Model_Grants::GRANT_READ => true, Tinebase_Model_Grants::GRANT_ADD => false, Tinebase_Model_Grants::GRANT_EDIT => false, Tinebase_Model_Grants::GRANT_DELETE => false, Tinebase_Model_Grants::GRANT_FREEBUSY => true, Tinebase_Model_Grants::GRANT_ADMIN => false))), true); $persistentEvent = Calendar_Controller_Event::getInstance()->create(new Calendar_Model_Event(array('container_id' => $this->objects['initialContainer']->getId(), 'rrule' => 'FREQ=WEEKLY;BYDAY=WE', 'dtstart' => new Tinebase_DateTime('20131016T120000'), 'dtend' => new Tinebase_DateTime('20131016T130000'), 'summary' => 'Meeting', 'attendee' => new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(array('user_id' => Tinebase_Core::getUser()->contact_id, 'user_type' => Calendar_Model_Attender::USERTYPE_USER, 'role' => Calendar_Model_Attender::ROLE_REQUIRED, 'status_authkey' => 'e4546f26cb37f69baf59135e7bd379bf94bba429'))), 'uid' => '3ef8b44333aea7c01aa5a9308e2cb014807c60b3'))); // add pwulf as attender to create exception $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $exception = Calendar_Model_Rrule::computeNextOccurrence($persistentEvent, $exceptions, new Tinebase_DateTime('20131017T140000')); $exception->attendee->addRecord(new Calendar_Model_Attender(array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('pwulf')->getId()))); $persistentException = Calendar_Controller_Event::getInstance()->createRecurException($exception); $persistentEvent = Calendar_Controller_Event::getInstance()->get($persistentEvent->getId()); // pwulf tries to accept invitation Tinebase_Core::set(Tinebase_Core::USER, Tinebase_User::getInstance()->getFullUserByLoginName('pwulf')); $_SERVER['HTTP_USER_AGENT'] = 'Mac OS X/10.8.5 (12F45) CalendarAgent/57'; // this ics only has an exdate vevent $vcalendarStream = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/accept_exdate_invite.ics'); $event = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], $persistentEvent); $event->put($vcalendarStream); $exdateWebDAVEvent = $event->getRecord()->exdate[0]; $this->_assertCurrentUserAttender($exdateWebDAVEvent); $exdateCalEvent = Calendar_Controller_Event::getInstance()->get($persistentException->getId()); $this->_assertCurrentUserAttender($exdateCalEvent); }
/** * print alarm acknowledgement report (when, ip, client, user, ...) * * @param Zend_Console_Getopt $_opts */ public function alarmAckReport(Zend_Console_Getopt $_opts) { $until = Tinebase_DateTime::now(); $from = Tinebase_DateTime::now()->subDay(1); // get all events for today for current user $filter = new Calendar_Model_EventFilter(array(array('field' => 'attender', 'operator' => 'in', 'value' => array(array('user_type' => 'user', 'user_id' => Tinebase_Core::getUser()->contact_id))), array('field' => 'attender_status', 'operator' => 'not', 'value' => Calendar_Model_Attender::STATUS_DECLINED), array('field' => 'period', 'operator' => 'within', 'value' => array("from" => $from, "until" => $until)))); $events = Calendar_Controller_Event::getInstance()->search($filter); Calendar_Model_Rrule::mergeAndRemoveNonMatchingRecurrences($events, $filter); Calendar_Controller_Event::getInstance()->getAlarms($events); echo "Reporting alarms for events of user " . Tinebase_Core::getUser()->accountLoginName . " (All times in UTC) from " . $from->toString() . ' until ' . $until->toString() . "...\n\n"; // loop alarms and print alarm ack info foreach ($events as $event) { if (count($event->alarms) > 0) { $this->_printShortEvent($event); foreach ($event->alarms as $alarm) { echo " Alarm " . $alarm->alarm_time . "\n"; $ackTime = Calendar_Controller_Alarm::getAcknowledgeTime($alarm); if (empty($ackTime)) { echo " not acknowledged!"; } else { if (is_array($ackTime)) { $ackTime = array_pop($ackTime); } echo " acknowledged " . $ackTime->toString() . "\n IP -> " . $alarm->getOption(Tinebase_Model_Alarm::OPTION_ACK_IP) . "\n Client ->" . $alarm->getOption(Tinebase_Model_Alarm::OPTION_ACK_CLIENT) . "\n"; } echo "\n"; } echo "\n"; } } }
public function testGetRecurExceptions() { $persitentException = $this->testCreateRecurException(); $baseEvent = $this->_controller->getRecurBaseEvent($persitentException); $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $nextOccurance = Calendar_Model_Rrule::computeNextOccurrence($baseEvent, $exceptions, $baseEvent->dtstart); $this->_controller->createRecurException($nextOccurance, TRUE); $exceptions = $this->_controller->getRecurExceptions($persitentException, TRUE); $dtstarts = $exceptions->dtstart; $this->assertTrue(in_array($nextOccurance->dtstart, $dtstarts), 'deleted instance missing'); $this->assertTrue(in_array($persitentException->dtstart, $dtstarts), 'exception instance missing'); }
/** * testSetAlarmOfRecurSeriesException */ public function testSetAlarmOfRecurSeriesException() { $event = $this->_getEvent(); $event->rrule = 'FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;INTERVAL=1'; $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm', array(new Tinebase_Model_Alarm(array('minutes_before' => 30), TRUE))); $persistentEvent = $this->_controller->create($event); $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $exception = Calendar_Model_Rrule::computeNextOccurrence($persistentEvent, $exceptions, new Tinebase_DateTime()); $exception->dtstart->subHour(6); $exception->dtend->subHour(6); $persistentException = $this->_controller->createRecurException($exception); $baseEvent = $this->_controller->getRecurBaseEvent($persistentException); $this->_controller->getAlarms($baseEvent); $exceptions = $this->_controller->getRecurExceptions($persistentException); $nextOccurance = Calendar_Model_Rrule::computeNextOccurrence($baseEvent, $exceptions, Tinebase_DateTime::now()); $nextAlarmEventStart = new Tinebase_DateTime(substr($baseEvent->alarms->getFirstRecord()->getOption('recurid'), -19)); $this->assertTrue($nextOccurance->dtstart->equals($nextAlarmEventStart), 'next alarm got not adjusted'); $alarmTime = clone $persistentException->dtstart; $alarmTime->subMinute(30); $this->assertTrue($alarmTime->equals($persistentException->alarms->getFirstRecord()->alarm_time), 'alarmtime of persistent exception is not correnct/set'); }
/** * update to 8.3 * - normalize all rrules */ public function update_2() { // find all events with rrule $eventIds = $this->_db->query("SELECT " . $this->_db->quoteIdentifier('id') . " FROM " . $this->_db->quoteIdentifier(SQL_TABLE_PREFIX . "cal_events") . " WHERE " . $this->_db->quoteIdentifier("rrule") . " IS NOT NULL")->fetchAll(Zend_Db::FETCH_ASSOC); // NOTE: we need a generic sql BE to circumvent calendar specific acl issues $eventBE = new Tinebase_Backend_Sql(array('modelName' => 'Calendar_Model_Event', 'tableName' => 'cal_events', 'modlogActive' => false)); foreach ($eventIds as $eventId) { $event = $eventBE->get($eventId['id']); $oldRruleString = (string) $event->rrule; $rrule = Calendar_Model_Rrule::getRruleFromString($oldRruleString); $rrule->normalize($event); if ($oldRruleString != (string) $rrule) { $event->rrule = (string) $rrule; try { $eventBE->update($event); } catch (Tinebase_Exception_Record_Validation $terv) { Tinebase_Exception::log($terv, null, $event->toArray()); } catch (Tinebase_Exception_UnexpectedValue $teuv) { Tinebase_Exception::log($teuv, null, $event->toArray()); } } } $this->setApplicationVersion('Calendar', '8.3'); }
/** * @see #5806: thisandfuture range updates with count part fail */ public function testCreateRecurExceptionAllFollowingWithCount() { $from = new Tinebase_DateTime('2012-02-20 00:00:00'); $until = new Tinebase_DateTime('2012-02-26 23:59:59'); $event = new Calendar_Model_Event(array('uid' => Tinebase_Record_Abstract::generateUID(), 'summary' => 'Abendessen', 'dtstart' => '2012-02-21 14:00:00', 'dtend' => '2012-02-21 15:30:00', 'originator_tz' => 'Europe/Berlin', 'rrule' => 'FREQ=DAILY;COUNT=5', 'container_id' => $this->_testCalendar->getId())); $persistentEvent = $this->_controller->create($event); // create exception $weekviewEvents = $this->_controller->search(new Calendar_Model_EventFilter(array(array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_testCalendar->getId())))); Calendar_Model_Rrule::mergeRecurrenceSet($weekviewEvents, $from, $until); $weekviewEvents[2]->dtstart->subHour(5); $weekviewEvents[2]->dtend->subHour(5); $this->_controller->createRecurException($weekviewEvents[2], FALSE, TRUE); // load events $weekviewEvents = $this->_controller->search(new Calendar_Model_EventFilter(array(array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_testCalendar->getId())))); Calendar_Model_Rrule::mergeRecurrenceSet($weekviewEvents, $from, $until); $weekviewEvents->sort('dtstart', 'ASC'); $this->assertEquals(2, count($weekviewEvents->filter('uid', $weekviewEvents[0]->uid)), 'shorten failed'); $this->assertEquals(5, count($weekviewEvents), 'wrong total count'); }
public function testGetTranslatedRule() { $locale = new Zend_Locale('en'); $translation = Tinebase_Translation::getTranslation('Calendar', $locale); $asserts = array('FREQ=DAILY;INTERVAL=1' => 'Daily', 'FREQ=DAILY;INTERVAL=3' => 'Every 3rd day', 'FREQ=WEEKLY;INTERVAL=3;WKST=SU;BYDAY=TU,WE,TH' => 'Every 3rd week on Tuesday, Wednesday and Thursday', 'FREQ=MONTHLY;INTERVAL=2;BYDAY=-1FR' => 'Every 2nd month on the last Friday', 'FREQ=MONTHLY;INTERVAL=1;BYDAY=2TH' => 'Monthly every second Thursday', 'FREQ=MONTHLY;INTERVAL=3;BYMONTHDAY=24' => 'Every 3rd month on the 24th', 'FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=5' => 'Monthly on the 5th', 'FREQ=YEARLY;INTERVAL=1;BYMONTH=7;BYMONTHDAY=10' => 'Yearly on the 10th of July'); foreach ($asserts as $rruleString => $expected) { $translated = Calendar_Model_Rrule::getRruleFromString($rruleString); $this->assertEquals($translated, $translated); } }
/** * converts egw rrule into tine/iCal rrule * * @param array $_egwEventData * @return Calendar_Model_Rrule */ protected function _convertRrule($_egwEventData) { $egwRrule = $_egwEventData['rrule']; $rrule = new Calendar_Model_Rrule(array()); if (!(isset($this->_rruleFreqMap[$egwRrule['recur_type']]) || array_key_exists($egwRrule['recur_type'], $this->_rruleFreqMap))) { throw new Exception('unsupported rrule freq'); } $rrule->freq = $this->_rruleFreqMap[$egwRrule['recur_type']]; $rrule->interval = $egwRrule['recur_interval']; $rrule->until = $this->convertDate($egwRrule['recur_enddate']); // weekly/monthly by wday if ($egwRrule['recur_type'] == 2 || $egwRrule['recur_type'] == 4) { $wdays = array(); foreach ($this->_rruleWdayMap as $egwBit => $iCalString) { if ($egwRrule['recur_data'] & $egwBit) { $wdays[] = $iCalString; } } $rrule->byday = implode(',', $wdays); } // monthly byday/yearly bymonthday if ($egwRrule['recur_type'] == 3 || $egwRrule['recur_type'] == 5) { $dtstart = $this->convertDate($_egwEventData['cal_start']); $dateArray = Calendar_Model_Rrule::date2array($dtstart); $rrule->bymonthday = $dateArray['day']; if ($egwRrule['recur_type'] == 5) { $rrule->bymonth = $dateArray['month']; } } return $rrule; }
/** * Computes the Recurrence set of the given event leaving out $_event->exdate and $_exceptions * * @todo respect rrule_until! * * @param Calendar_Model_Event $_event * @param Tinebase_Record_RecordSet $_exceptions * @param Tinebase_DateTime $_from * @param Tinebase_DateTime $_until * @return Tinebase_Record_RecordSet * @throws Tinebase_Exception_UnexpectedValue */ public static function computeRecurrenceSet($_event, $_exceptions, $_from, $_until) { if (!$_event->dtstart instanceof Tinebase_DateTime) { throw new Tinebase_Exception_UnexpectedValue('Event needs DateTime dtstart: ' . print_r($_event->toArray(), TRUE)); } $rrule = new Calendar_Model_Rrule(NULL, TRUE); $rrule->setFromString($_event->rrule); $exceptionRecurIds = self::getExceptionsRecurIds($_event, $_exceptions); $recurSet = new Tinebase_Record_RecordSet('Calendar_Model_Event'); switch ($rrule->freq) { case self::FREQ_DAILY: self::_computeRecurDaily($_event, $rrule, $exceptionRecurIds, $_from, $_until, $recurSet); break; case self::FREQ_WEEKLY: // default BYDAY clause if (!$rrule->byday) { $rrule->byday = array_search($_event->dtstart->format('w'), self::$WEEKDAY_DIGIT_MAP); } if (!$rrule->wkst) { $rrule->wkst = self::getWeekStart(); } $weekDays = array_keys(self::$WEEKDAY_DIGIT_MAP); array_splice($weekDays, 0, 0, array_splice($weekDays, array_search($rrule->wkst, $weekDays))); $dailyrrule = clone $rrule; $dailyrrule->freq = self::FREQ_DAILY; $dailyrrule->interval = 7 * $rrule->interval; $eventLength = $_event->dtstart->diff($_event->dtend); foreach (explode(',', $rrule->byday) as $recurWeekDay) { // NOTE: in weekly computation, each wdays base event is a recur instance itself $baseEvent = clone $_event; // NOTE: skipping must be done in organizer_tz $baseEvent->dtstart->setTimezone($_event->originator_tz); $direction = array_search($recurWeekDay, $weekDays) >= array_search(array_search($baseEvent->dtstart->format('w'), self::$WEEKDAY_DIGIT_MAP), $weekDays) ? +1 : -1; self::skipWday($baseEvent->dtstart, $recurWeekDay, $direction, TRUE); $baseEvent->dtstart->setTimezone('UTC'); $baseEvent->dtend = clone $baseEvent->dtstart; $baseEvent->dtend->add($eventLength); self::_computeRecurDaily($baseEvent, $dailyrrule, $exceptionRecurIds, $_from, $_until, $recurSet); // check if base event (recur instance) needs to be added to the set if ($baseEvent->dtstart > $_event->dtstart && $baseEvent->dtstart >= $_from && $baseEvent->dtstart < $_until) { if (!in_array($baseEvent->setRecurId($baseEvent->getId()), $exceptionRecurIds)) { self::addRecurrence($baseEvent, $recurSet); } } } break; case self::FREQ_MONTHLY: if ($rrule->byday) { self::_computeRecurMonthlyByDay($_event, $rrule, $exceptionRecurIds, $_from, $_until, $recurSet); } else { self::_computeRecurMonthlyByMonthDay($_event, $rrule, $exceptionRecurIds, $_from, $_until, $recurSet); } break; case self::FREQ_YEARLY: $yearlyrrule = clone $rrule; $yearlyrrule->freq = self::FREQ_MONTHLY; $yearlyrrule->interval = 12; $baseEvent = clone $_event; $originatorsDtstart = clone $baseEvent->dtstart; $originatorsDtstart->setTimezone($_event->originator_tz); // @TODO respect BYMONTH if ($rrule->bymonth && $rrule->bymonth != $originatorsDtstart->format('n')) { // adopt $diff = (12 + $rrule->bymonth - $originatorsDtstart->format('n')) % 12; // NOTE: skipping must be done in organizer_tz $baseEvent->dtstart->setTimezone($_event->originator_tz); $baseEvent->dtend->setTimezone($_event->originator_tz); $baseEvent->dtstart->addMonth($diff); $baseEvent->dtend->addMonth($diff); $baseEvent->dtstart->setTimezone('UTC'); $baseEvent->dtend->setTimezone('UTC'); // check if base event (recur instance) needs to be added to the set if ($baseEvent->dtstart->isLater($_from) && $baseEvent->dtstart->isEarlier($_until)) { if (!in_array($baseEvent->setRecurId($baseEvent->getId()), $exceptionRecurIds)) { self::addRecurrence($baseEvent, $recurSet); } } } if ($rrule->byday) { self::_computeRecurMonthlyByDay($baseEvent, $yearlyrrule, $exceptionRecurIds, $_from, $_until, $recurSet); } else { self::_computeRecurMonthlyByMonthDay($baseEvent, $yearlyrrule, $exceptionRecurIds, $_from, $_until, $recurSet); } break; } return $recurSet; }
public function testAlarmAdjust() { $event = new Calendar_Model_Event(array('uid' => Tinebase_Record_Abstract::generateUID(), 'summary' => 'custom alarm', 'dtstart' => '2011-12-30 10:30:00', 'dtend' => '2011-12-30 12:30:00', 'rrule' => 'FREQ=YEARLY', 'originator_tz' => 'Europe/Berlin', 'alarms' => array(array('alarm_time' => '2011-12-20 10:30:00', 'minutes_before' => 'custom', 'options' => Zend_Json::encode(array('minutes_before' => 14400, 'recurid' => NULL, 'custom' => TRUE)))), Tinebase_Model_Grants::GRANT_EDIT => true)); $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $from = new Tinebase_DateTime('2012-01-01 00:00:00'); $until = new Tinebase_DateTime('2012-12-31 23:59:59'); $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($event, $exceptions, $from, $until); $this->assertEquals(1, count($recurSet), 'recur set failed'); $this->assertEquals('2012-12-20 10:30:00', (string) $recurSet->getFirstRecord()->alarms->getFirstRecord()->alarm_time); }