diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php --- a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php +++ b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php @@ -278,7 +278,14 @@ $parent = $event->getParentEvent(); } - $next_uri = $parent->getURI().'/'.($sequence + 1); + if ($parent->isValidSequenceIndex($viewer, $sequence + 1)) { + $next_uri = $parent->getURI().'/'.($sequence + 1); + $has_next = true; + } else { + $next_uri = null; + $has_next = false; + } + if ($sequence) { if ($sequence > 1) { $previous_uri = $parent->getURI().'/'.($sequence - 1); @@ -302,6 +309,7 @@ ->setTag('a') ->setIcon('fa-chevron-right') ->setHref($next_uri) + ->setDisabled(!$has_next) ->setText(pht('Next')); $header @@ -450,9 +458,11 @@ 'it.')); } - $instance = $event->newStub($viewer, $sequence); + if (!$event->isValidSequenceIndex($viewer, $sequence)) { + return null; + } - return $instance; + return $event->newStub($viewer, $sequence); } private function buildSubheaderView(PhabricatorCalendarEvent $event) { diff --git a/src/applications/calendar/storage/PhabricatorCalendarEvent.php b/src/applications/calendar/storage/PhabricatorCalendarEvent.php --- a/src/applications/calendar/storage/PhabricatorCalendarEvent.php +++ b/src/applications/calendar/storage/PhabricatorCalendarEvent.php @@ -137,7 +137,8 @@ ); // Read these fields from the parent event instead of this event. For - // example, we want any changes to the parent event's name to + // example, we want any changes to the parent event's name to apply to + // the child. if (isset($inherit[$field])) { if ($this->getIsStub()) { // TODO: This should be unconditional, but the execution order of @@ -171,33 +172,66 @@ ->setName($parent->getName()) ->setDescription($parent->getDescription()); - $frequency = $parent->getFrequencyUnit(); - $modify_key = '+'.$this->getSequenceIndex().' '.$frequency; + $sequence = $this->getSequenceIndex(); + $duration = $this->getDuration(); + $epochs = $parent->getSequenceIndexEpochs($actor, $sequence, $duration); + + $this + ->setDateFrom($epochs['dateFrom']) + ->setDateTo($epochs['dateTo']) + ->setAllDayDateFrom($epochs['allDayDateFrom']) + ->setAllDayDateTo($epochs['allDayDateTo']); + + return $this; + } - $date = $parent->getDateFrom(); - $date_time = PhabricatorTime::getDateTimeFromEpoch($date, $actor); + public function isValidSequenceIndex(PhabricatorUser $viewer, $sequence) { + try { + $this->getSequenceIndexEpochs($viewer, $sequence, $this->getDuration()); + return true; + } catch (Exception $ex) { + return false; + } + } + + private function getSequenceIndexEpochs( + PhabricatorUser $viewer, + $sequence, + $duration) { + + $frequency = $this->getFrequencyUnit(); + $modify_key = '+'.$sequence.' '.$frequency; + + $date = $this->getDateFrom(); + $date_time = PhabricatorTime::getDateTimeFromEpoch($date, $viewer); $date_time->modify($modify_key); $date = $date_time->format('U'); - $duration = $this->getDuration(); + $end_date = $this->getRecurrenceEndDate(); + if ($end_date && $date > $end_date) { + throw new Exception( + pht( + 'Sequence "%s" is invalid for this event: it would occur after '. + 'the event stops repeating.', + $sequence)); + } $utc = new DateTimeZone('UTC'); - $allday_from = $parent->getAllDayDateFrom(); + $allday_from = $this->getAllDayDateFrom(); $allday_date = new DateTime('@'.$allday_from, $utc); $allday_date->setTimeZone($utc); $allday_date->modify($modify_key); $allday_min = $allday_date->format('U'); - $allday_duration = ($parent->getAllDayDateTo() - $allday_from); + $allday_duration = ($this->getAllDayDateTo() - $allday_from); - $this - ->setDateFrom($date) - ->setDateTo($date + $duration) - ->setAllDayDateFrom($allday_min) - ->setAllDayDateTo($allday_min + $allday_duration); - - return $this; + return array( + 'dateFrom' => $date, + 'dateTo' => $date + $duration, + 'allDayDateFrom' => $allday_min, + 'allDayDateTo' => $allday_min + $allday_duration, + ); } public function newStub(PhabricatorUser $actor, $sequence) {