1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-30 10:42:41 +01:00

Respect "End Date" for recurring Calendar events in detail UI

Summary:
Fixes T11396. Currently, you can keep clicking "Next >" forever to generate infinite instances of an event, even if it has a set end date.

Likewise, you can visit `/E123/999999` or whatever to stub out the 999999th instance of an event.

Instead:

  - Before creating a new stub, make sure it happens before any end date.
  - 404 stubs if we can't create them.
  - Disable the "Next >" button if it isn't valid.

Test Plan:
  - Visited `/E123/9999` for an event with a recurrence end date, got 404.
  - Clicked "Next >" on an event with an end date, got new events until I hit the end date.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T11396

Differential Revision: https://secure.phabricator.com/D16517
This commit is contained in:
epriestley 2016-09-08 09:12:38 -07:00
parent 5a33a6743e
commit 25bd14a9b1
2 changed files with 62 additions and 18 deletions

View file

@ -278,7 +278,14 @@ final class PhabricatorCalendarEventViewController
$parent = $event->getParentEvent(); $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) {
if ($sequence > 1) { if ($sequence > 1) {
$previous_uri = $parent->getURI().'/'.($sequence - 1); $previous_uri = $parent->getURI().'/'.($sequence - 1);
@ -302,6 +309,7 @@ final class PhabricatorCalendarEventViewController
->setTag('a') ->setTag('a')
->setIcon('fa-chevron-right') ->setIcon('fa-chevron-right')
->setHref($next_uri) ->setHref($next_uri)
->setDisabled(!$has_next)
->setText(pht('Next')); ->setText(pht('Next'));
$header $header
@ -450,9 +458,11 @@ final class PhabricatorCalendarEventViewController
'it.')); '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) { private function buildSubheaderView(PhabricatorCalendarEvent $event) {

View file

@ -137,7 +137,8 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
); );
// Read these fields from the parent event instead of this event. For // 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 (isset($inherit[$field])) {
if ($this->getIsStub()) { if ($this->getIsStub()) {
// TODO: This should be unconditional, but the execution order of // TODO: This should be unconditional, but the execution order of
@ -171,33 +172,66 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
->setName($parent->getName()) ->setName($parent->getName())
->setDescription($parent->getDescription()); ->setDescription($parent->getDescription());
$frequency = $parent->getFrequencyUnit(); $sequence = $this->getSequenceIndex();
$modify_key = '+'.$this->getSequenceIndex().' '.$frequency; $duration = $this->getDuration();
$epochs = $parent->getSequenceIndexEpochs($actor, $sequence, $duration);
$date = $parent->getDateFrom(); $this
$date_time = PhabricatorTime::getDateTimeFromEpoch($date, $actor); ->setDateFrom($epochs['dateFrom'])
->setDateTo($epochs['dateTo'])
->setAllDayDateFrom($epochs['allDayDateFrom'])
->setAllDayDateTo($epochs['allDayDateTo']);
return $this;
}
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_time->modify($modify_key);
$date = $date_time->format('U'); $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'); $utc = new DateTimeZone('UTC');
$allday_from = $parent->getAllDayDateFrom(); $allday_from = $this->getAllDayDateFrom();
$allday_date = new DateTime('@'.$allday_from, $utc); $allday_date = new DateTime('@'.$allday_from, $utc);
$allday_date->setTimeZone($utc); $allday_date->setTimeZone($utc);
$allday_date->modify($modify_key); $allday_date->modify($modify_key);
$allday_min = $allday_date->format('U'); $allday_min = $allday_date->format('U');
$allday_duration = ($parent->getAllDayDateTo() - $allday_from); $allday_duration = ($this->getAllDayDateTo() - $allday_from);
$this return array(
->setDateFrom($date) 'dateFrom' => $date,
->setDateTo($date + $duration) 'dateTo' => $date + $duration,
->setAllDayDateFrom($allday_min) 'allDayDateFrom' => $allday_min,
->setAllDayDateTo($allday_min + $allday_duration); 'allDayDateTo' => $allday_min + $allday_duration,
);
return $this;
} }
public function newStub(PhabricatorUser $actor, $sequence) { public function newStub(PhabricatorUser $actor, $sequence) {