diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php b/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php --- a/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php +++ b/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php @@ -17,11 +17,13 @@ $viewer = $request->getViewer(); $user_phid = $viewer->getPHID(); $error_name = true; + $error_recurrence_end_date = true; $error_start_date = true; $error_end_date = true; $validation_exception = null; $is_recurring_id = celerity_generate_unique_node_id(); + $recurrence_end_date_id = celerity_generate_unique_node_id(); $frequency_id = celerity_generate_unique_node_id(); $all_day_id = celerity_generate_unique_node_id(); $start_date_id = celerity_generate_unique_node_id(); @@ -65,6 +67,8 @@ list($start_value, $end_value) = $this->getDefaultTimeValues($viewer); } + $recurrence_end_date_value = clone $end_value; + $recurrence_end_date_value->setOptional(true); $submit_label = pht('Create'); $page_title = pht('Create Event'); @@ -138,6 +142,8 @@ $start_value = AphrontFormDateControlValue::newFromEpoch( $viewer, $event->getDateFrom()); + $recurrence_end_date_value = id(clone $end_value) + ->setOptional(true); $submit_label = pht('Update'); $page_title = pht('Update Event'); @@ -179,6 +185,10 @@ $end_value = AphrontFormDateControlValue::newFromRequest( $request, 'end'); + $recurrence_end_date_value = AphrontFormDateControlValue::newFromRequest( + $request, + 'recurrenceEndDate'); + $recurrence_end_date_value->setOptional(true); $description = $request->getStr('description'); $subscribers = $request->getArr('subscribers'); $edit_policy = $request->getStr('editPolicy'); @@ -213,6 +223,11 @@ ->setTransactionType( PhabricatorCalendarEventTransaction::TYPE_FREQUENCY) ->setNewValue(array('rule' => $frequency)); + + $xactions[] = id(new PhabricatorCalendarEventTransaction()) + ->setTransactionType( + PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE) + ->setNewValue($recurrence_end_date_value); } $xactions[] = id(new PhabricatorCalendarEventTransaction()) @@ -290,6 +305,8 @@ PhabricatorCalendarEventTransaction::TYPE_START_DATE); $error_end_date = $ex->getShortMessage( PhabricatorCalendarEventTransaction::TYPE_END_DATE); + $error_recurrence_end_date = $ex->getShortMessage( + PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE); $event->setViewPolicy($view_policy); $event->setEditPolicy($edit_policy); @@ -297,6 +314,7 @@ } $is_recurring_checkbox = null; + $recurrence_end_date_control = null; $recurrence_frequency_select = null; $name = id(new AphrontFormTextControl()) @@ -309,6 +327,7 @@ Javelin::initBehavior('recurring-edit', array( 'isRecurring' => $is_recurring_id, 'frequency' => $frequency_id, + 'recurrenceEndDate' => $recurrence_end_date_id, )); $is_recurring_checkbox = id(new AphrontFormCheckboxControl()) @@ -319,6 +338,17 @@ $is_recurring, $is_recurring_id); + $recurrence_end_date_control = id(new AphrontFormDateControl()) + ->setUser($viewer) + ->setName('recurrenceEndDate') + ->setLabel(pht('Recurrence End Date')) + ->setError($error_recurrence_end_date) + ->setValue($recurrence_end_date_value) + ->setID($recurrence_end_date_id) + ->setIsTimeDisabled(true) + ->setAllowNull(true) + ->setIsDisabled(!$is_recurring); + $recurrence_frequency_select = id(new AphrontFormSelectControl()) ->setName('frequency') ->setOptions(array( @@ -421,6 +451,9 @@ if ($is_recurring_checkbox) { $form->appendChild($is_recurring_checkbox); } + if ($recurrence_end_date_control) { + $form->appendChild($recurrence_end_date_control); + } if ($recurrence_frequency_select) { $form->appendControl($recurrence_frequency_select); } 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 @@ -135,7 +135,7 @@ $event, PhabricatorPolicyCapability::CAN_EDIT); - if ($event->getIsRecurring() && $event->getIsGhostEvent()) { + if ($event->getIsRecurring() && $event->getInstanceOfEventPHID()) { $index = $event->getSequenceIndex(); $actions->addAction( @@ -231,6 +231,13 @@ $properties->addProperty( pht('Recurs'), ucwords(idx($event->getRecurrenceFrequency(), 'rule'))); + + if ($event->getRecurrenceEndDate()) { + $properties->addProperty( + pht('Recurrence Ends'), + phabricator_datetime($event->getRecurrenceEndDate(), $viewer)); + } + if ($event->getInstanceOfEventPHID()) { $properties->addProperty( pht('Recurrence of Event'), diff --git a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php --- a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php +++ b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php @@ -90,7 +90,6 @@ switch ($xaction->getTransactionType()) { case PhabricatorCalendarEventTransaction::TYPE_RECURRING: case PhabricatorCalendarEventTransaction::TYPE_FREQUENCY: - case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE: case PhabricatorCalendarEventTransaction::TYPE_INSTANCE_OF_EVENT: case PhabricatorCalendarEventTransaction::TYPE_SEQUENCE_INDEX: case PhabricatorCalendarEventTransaction::TYPE_NAME: @@ -101,6 +100,7 @@ return $xaction->getNewValue(); case PhabricatorCalendarEventTransaction::TYPE_ALL_DAY: return (int)$xaction->getNewValue(); + case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE: case PhabricatorCalendarEventTransaction::TYPE_START_DATE: case PhabricatorCalendarEventTransaction::TYPE_END_DATE: return $xaction->getNewValue()->getEpoch(); @@ -118,8 +118,6 @@ return $object->setIsRecurring($xaction->getNewValue()); case PhabricatorCalendarEventTransaction::TYPE_FREQUENCY: return $object->setRecurrenceFrequency($xaction->getNewValue()); - case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE: - return $object->setRecurrenceEndDate($xaction->getNewValue()); case PhabricatorCalendarEventTransaction::TYPE_INSTANCE_OF_EVENT: return $object->setInstanceOfEventPHID($xaction->getNewValue()); case PhabricatorCalendarEventTransaction::TYPE_SEQUENCE_INDEX: @@ -133,6 +131,9 @@ case PhabricatorCalendarEventTransaction::TYPE_END_DATE: $object->setDateTo($xaction->getNewValue()); return; + case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE: + $object->setRecurrenceEndDate($xaction->getNewValue()); + return; case PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION: $object->setDescription($xaction->getNewValue()); return; @@ -313,6 +314,7 @@ $errors[] = $error; } break; + case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE: case PhabricatorCalendarEventTransaction::TYPE_START_DATE: case PhabricatorCalendarEventTransaction::TYPE_END_DATE: foreach ($xactions as $xaction) { diff --git a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php --- a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php @@ -110,8 +110,9 @@ foreach ($events as $event) { $sequence_start = 0; - $instance_count = null; + $sequence_end = null; $duration = $event->getDateTo() - $event->getDateFrom(); + $end = null; if ($event->getIsRecurring() && !$event->getInstanceOfEventPHID()) { $frequency = $event->getFrequencyUnit(); @@ -135,28 +136,31 @@ } $date = $start; - $start_datetime = PhabricatorTime::getDateTimeFromEpoch( - $start, - $viewer); + $datetime = PhabricatorTime::getDateTimeFromEpoch($date, $viewer); - if ($this->rangeEnd) { + if (($this->rangeEnd && $event->getRecurrenceEndDate()) && + $this->rangeEnd < $event->getRecurrenceEndDate()) { $end = $this->rangeEnd; - $instance_count = $sequence_start; + } else if ($event->getRecurrenceEndDate()) { + $end = $event->getRecurrenceEndDate(); + } else if ($this->rangeEnd) { + $end = $this->rangeEnd; + } + if ($end) { + $sequence_end = $sequence_start; while ($date < $end) { - $instance_count++; - $datetime = PhabricatorTime::getDateTimeFromEpoch($date, $viewer); + $sequence_end++; $datetime->modify($modify_key); $date = $datetime->format('U'); } } else { - $instance_count = $this->getRawResultLimit(); + $sequence_end = $this->getRawResultLimit() + $sequence_start; } $sequence_start = max(1, $sequence_start); - $max_sequence = $sequence_start + $instance_count; - for ($index = $sequence_start; $index < $max_sequence; $index++) { + for ($index = $sequence_start; $index < $sequence_end; $index++) { $instance_sequence_pairs[] = array($event->getPHID(), $index); $events[] = $event->generateNthGhost($index, $viewer); diff --git a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php --- a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php @@ -373,7 +373,6 @@ $event->setDescription(pht('%s (%s)', $name_text, $status_text)); $event->setName($status_text); $event->setURI($status->getURI()); - // $event->setEventID($status->getID()); $event->setViewerIsInvited($viewer_is_invited); $month_view->addEvent($event); } diff --git a/src/view/form/control/AphrontFormDateControl.php b/src/view/form/control/AphrontFormDateControl.php --- a/src/view/form/control/AphrontFormDateControl.php +++ b/src/view/form/control/AphrontFormDateControl.php @@ -23,6 +23,11 @@ return $this; } + public function setIsDisabled($is_datepicker_disabled) { + $this->isDisabled = $is_datepicker_disabled; + return $this; + } + public function setEndDateID($value) { $this->endDateID = $value; return $this; diff --git a/webroot/rsrc/js/application/calendar/behavior-recurring-edit.js b/webroot/rsrc/js/application/calendar/behavior-recurring-edit.js --- a/webroot/rsrc/js/application/calendar/behavior-recurring-edit.js +++ b/webroot/rsrc/js/application/calendar/behavior-recurring-edit.js @@ -5,10 +5,17 @@ JX.behavior('recurring-edit', function(config) { var checkbox = JX.$(config.isRecurring); + JX.DOM.listen(checkbox, 'change', null, function() { var frequency = JX.$(config.frequency); + var end_date = JX.$(config.recurrenceEndDate); frequency.disabled = checkbox.checked ? false : true; + end_date.disabled = checkbox.checked ? false : true; + + if (end_date.disabled) { + JX.DOM.alterClass(end_date, 'datepicker-disabled', !checkbox.checked); + } }); });