/** * @expectedException \Sabre\VObject\Recur\MaxInstancesExceededException */ function testExceedMaxRecurrences() { $input = <<<ICS BEGIN:VCALENDAR VERSION:2.0 BEGIN:VEVENT UID:foobar DTSTART:20140803T120000Z RRULE:FREQ=WEEKLY SUMMARY:Original END:VEVENT END:VCALENDAR ICS; $temp = Settings::$maxRecurrences; Settings::$maxRecurrences = 4; try { $vcal = Reader::read($input); $vcal->expand(new DateTime('2014-08-01'), new DateTime('2014-09-01')); } finally { Settings::$maxRecurrences = $temp; } }
/** * Returns an array with all instances of a recurring event. If an event was detached it's not part of the series' instances. * * todo: Refactor - combining eluceo/ical and sabre/vobject this way seems very expensive. * @param Event $event * @param \DateTime|null $dateFrom * @param \DateTime|null $dateTo * * @return Event[] * * @throws \Exception */ public function getInstances(Event $event, \DateTime $dateFrom = null, \DateTime $dateTo = null, $findOnlyOne = false) { $eventInstances = array(); // this is necessary for using nthOccurrence, otherwise the maximum recurrence entities will result in an error // todo: Refactor - solve recurrence count for nthOccurrence VObject\Settings::$maxRecurrences = -1; if (!$event->getRecurrenceRule()) { return array($event); } $dateFrom = $dateFrom ? $dateFrom : $event->getDtStart(); if (!$dateFrom) { return $eventInstances; } if (!$dateTo) { //default to one year if no end is set $dateTo = clone $event->getDtStart(); $dateTo->add(new \DateInterval('P10Y')); } if (!$dateFrom || !$dateTo) { throw new \Exception('Trying to get instances of a recurring event without dateFrom and/or dateTo being set.'); } //create the calendar $vCalendar = new Calendar($event->getUniqueId()); if (self::isValidDateTime($event->getDtStart()) && self::isValidDateTime($event->getDtEnd())) { $vCalendar->addComponent($event); } //get edited events: depending on $includeEditedEvents to mark these events as deleted or to replace them by their edited event $editedEventsByTimestamp = array(); $qb = $this->em->createQueryBuilder(); $qb->select('e')->from('Xima\\ICalBundle\\Entity\\Component\\Event', 'e')->where('e.uniqueId = :uniqueId')->andWhere($qb->expr()->isNotNull('e.recurrenceId'))->setParameter('uniqueId', $event->getUniqueId()); $editedEvents = $qb->getQuery()->getResult(); foreach ($editedEvents as $editedEvent) { /* @var $editedEvent \Xima\ICalBundle\Entity\Component\Event */ if (self::isValidDateTime($editedEvent->getDtStart()) && self::isValidDateTime($editedEvent->getDtEnd()) && self::isValidDateTime($editedEvent->getRecurrenceId()->getDatetime())) { $editedEventsByTimestamp[$editedEvent->getDtStart()->getTimestamp()] = $editedEvent; $vCalendar->addComponent($editedEvent); } } //render the calendar and parse it to get all recurrences of the event $vCalendarExpandedData = $vCalendar->render(); $vCalendarExpanded = VObject\Reader::read($vCalendarExpandedData); /* @var $vCalendarExpanded \Sabre\VObject\Component\VCalendar */ $vCalendarExpanded = $vCalendarExpanded->expand($dateFrom, $dateTo); foreach ($vCalendarExpanded->getComponents() as $instanceComp) { /* @var $instanceComp \Sabre\VObject\Component\VEvent */ // It's basically the same event, but with the new calculated dates and times... // @todo: refactor so that dtStart is set when dateFrom gets set and so on... $dtStart = new \DateTime(); $dtStart->setTimestamp($instanceComp->DTSTART->getDateTime()->getTimestamp()); $dtEnd = new \DateTime(); $dtEnd->setTimestamp($instanceComp->DTEND->getDateTime()->getTimestamp()); $eventInstance = clone $event; /* @var $eventInstance Event */ $eventInstance->setDtStart($dtStart); $eventInstance->setDateFrom($dtStart); $eventInstance->setTimeFrom($dtStart); $eventInstance->setDtEnd($dtEnd); $eventInstance->setDateTo($dtEnd); $eventInstance->setTimeTo($dtEnd); if ($findOnlyOne) { return array($eventInstance); } if (isset($editedEventsByTimestamp[$instanceComp->DTSTART->getDateTime()->getTimestamp()])) { // if the instance was detached, it's not part of the series' instances continue; } else { $eventInstances[] = $eventInstance; } } return $eventInstances; }