1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-18 21:02:41 +01:00

Drive calendar event queries through the RRULE engine

Summary: Ref T10747. This drives event queries through RRULE, too.

Test Plan: Created recurring events, saw them appear correctly on the calendar.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T10747

Differential Revision: https://secure.phabricator.com/D16668
This commit is contained in:
epriestley 2016-10-03 07:40:01 -07:00
parent 5dfb672a80
commit 20f7de91ce
2 changed files with 105 additions and 64 deletions

View file

@ -165,70 +165,54 @@ final class PhabricatorCalendarEventQuery
// discard anything outside of the time window.
$events = $this->getEventsInRange($events);
$enforced_end = null;
$generate_from = $this->rangeBegin;
$generate_until = $this->rangeEnd;
foreach ($parents as $key => $event) {
$sequence_start = 0;
$sequence_end = null;
$start = null;
$duration = $event->getDuration();
$frequency = $event->getFrequencyUnit();
$modify_key = '+1 '.$frequency;
$start_date = $this->getRecurrenceWindowStart(
$event,
$generate_from - $duration);
if (($this->rangeBegin !== null) &&
($this->rangeBegin > $event->getStartDateTimeEpoch())) {
$max_date = $this->rangeBegin - $duration;
$date = $event->getStartDateTimeEpoch();
$datetime = PhabricatorTime::getDateTimeFromEpoch($date, $viewer);
$end_date = $this->getRecurrenceWindowEnd(
$event,
$generate_until);
while ($date < $max_date) {
// TODO: optimize this to not loop through all off-screen events
$sequence_start++;
$datetime = PhabricatorTime::getDateTimeFromEpoch($date, $viewer);
$date = $datetime->modify($modify_key)->format('U');
}
$limit = $this->getRecurrenceLimit($event, $raw_limit);
$start = $this->rangeBegin;
$set = $event->newRecurrenceSet();
$recurrences = $set->getEventsBetween(
null,
$end_date,
$limit + 1);
// We're generating events from the beginning and then filtering them
// here (instead of only generating events starting at the start date)
// because we need to know the proper sequence indexes to generate ghost
// events. This may change after RDATE support.
if ($start_date) {
$start_epoch = $start_date->getEpoch();
} else {
$start = $event->getStartDateTimeEpoch() - $duration;
$start_epoch = null;
}
$date = $start;
$datetime = PhabricatorTime::getDateTimeFromEpoch($date, $viewer);
// Select the minimum end time we need to generate events until.
$end_times = array();
if ($this->rangeEnd) {
$end_times[] = $this->rangeEnd;
foreach ($recurrences as $sequence_index => $sequence_datetime) {
if (!$sequence_index) {
// This is the parent event, which we already have.
continue;
}
if ($event->getUntilDateTimeEpoch()) {
$end_times[] = $event->getUntilDateTimeEpoch();
if ($start_epoch) {
if ($sequence_datetime->getEpoch() < $start_epoch) {
continue;
}
}
if ($enforced_end) {
$end_times[] = $enforced_end;
}
if ($end_times) {
$end = min($end_times);
$sequence_end = $sequence_start;
while ($date < $end) {
$sequence_end++;
$datetime->modify($modify_key);
$date = $datetime->format('U');
if ($sequence_end > $raw_limit + $sequence_start) {
break;
}
}
} else {
$sequence_end = $raw_limit + $sequence_start;
}
$sequence_start = max(1, $sequence_start);
for ($index = $sequence_start; $index < $sequence_end; $index++) {
$events[] = $event->newGhost($viewer, $index);
$events[] = $event->newGhost(
$viewer,
$sequence_index,
$sequence_datetime);
}
// NOTE: We're slicing results every time because this makes it cheaper
@ -240,7 +224,7 @@ final class PhabricatorCalendarEventQuery
if (count($events) > $raw_limit) {
$events = msort($events, 'getStartDateTimeEpoch');
$events = array_slice($events, 0, $raw_limit, true);
$enforced_end = last($events)->getStartDateTimeEpoch();
$generate_until = last($events)->getEndDateTimeEpoch();
}
}
}
@ -525,4 +509,44 @@ final class PhabricatorCalendarEventQuery
return $events;
}
private function getRecurrenceWindowStart(
PhabricatorCalendarEvent $event,
$generate_from) {
if (!$generate_from) {
return null;
}
return PhutilCalendarAbsoluteDateTime::newFromEpoch($generate_from);
}
private function getRecurrenceWindowEnd(
PhabricatorCalendarEvent $event,
$generate_until) {
$end_epochs = array();
if ($generate_until) {
$end_epochs[] = $generate_until;
}
$until_epoch = $event->getUntilDateTimeEpoch();
if ($until_epoch) {
$end_epochs[] = $until_epoch;
}
if (!$end_epochs) {
return null;
}
return PhutilCalendarAbsoluteDateTime::newFromEpoch(min($end_epochs));
}
private function getRecurrenceLimit(
PhabricatorCalendarEvent $event,
$raw_limit) {
return $raw_limit;
}
}

View file

@ -103,7 +103,10 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
->applyViewerTimezone($actor);
}
private function newChild(PhabricatorUser $actor, $sequence) {
private function newChild(
PhabricatorUser $actor,
$sequence,
PhutilCalendarDateTime $start = null) {
if (!$this->isParentEvent()) {
throw new Exception(
pht(
@ -124,7 +127,7 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
->setDateFrom(0)
->setDateTo(0);
return $child->copyFromParent($actor);
return $child->copyFromParent($actor, $start);
}
protected function readField($field) {
@ -156,7 +159,10 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
}
public function copyFromParent(PhabricatorUser $actor) {
public function copyFromParent(
PhabricatorUser $actor,
PhutilCalendarDateTime $start = null) {
if (!$this->isChildEvent()) {
throw new Exception(
pht(
@ -176,11 +182,18 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
->setDescription($parent->getDescription());
$sequence = $this->getSequenceIndex();
if ($start) {
$start_datetime = $start;
} else {
$start_datetime = $parent->newSequenceIndexDateTime($sequence);
if (!$start_datetime) {
throw new Exception(
"Sequence {$sequence} does not exist for event!");
pht(
'Sequence "%s" is not valid for event!',
$sequence));
}
}
$duration = $parent->newDuration();
@ -225,8 +238,12 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
return $stub;
}
public function newGhost(PhabricatorUser $actor, $sequence) {
$ghost = $this->newChild($actor, $sequence);
public function newGhost(
PhabricatorUser $actor,
$sequence,
PhutilCalendarDateTime $start = null) {
$ghost = $this->newChild($actor, $sequence, $start);
$ghost
->setIsGhostEvent(true)