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:
parent
5a33a6743e
commit
25bd14a9b1
2 changed files with 62 additions and 18 deletions
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in a new issue