3 final class AphrontFormDateControl
extends AphrontFormControl
{
11 private $continueOnInvalidDate = false;
12 private $isTimeDisabled;
16 public function setAllowNull($allow_null) {
17 $this->allowNull
= $allow_null;
21 public function setIsTimeDisabled($is_disabled) {
22 $this->isTimeDisabled
= $is_disabled;
26 public function setIsDisabled($is_datepicker_disabled) {
27 $this->isDisabled
= $is_datepicker_disabled;
31 public function setEndDateID($value) {
32 $this->endDateID
= $value;
36 const TIME_START_OF_DAY
= 'start-of-day';
37 const TIME_END_OF_DAY
= 'end-of-day';
38 const TIME_START_OF_BUSINESS
= 'start-of-business';
39 const TIME_END_OF_BUSINESS
= 'end-of-business';
41 public function setInitialTime($time) {
42 $this->initialTime
= $time;
46 public function readValueFromRequest(AphrontRequest
$request) {
47 $date = $request->getStr($this->getDateInputName());
48 $time = $request->getStr($this->getTimeInputName());
49 $enabled = $request->getBool($this->getCheckboxInputName());
51 if ($this->allowNull
&& !$enabled) {
52 $this->setError(null);
53 $this->setValue(null);
57 $err = $this->getError();
60 $this->valueDate
= $date;
61 $this->valueTime
= $time;
64 $err = pht('Invalid');
66 $zone = $this->getTimezone();
69 $datetime = new DateTime("{$date} {$time}", $zone);
70 $value = $datetime->format('U');
71 } catch (Exception
$ex) {
76 $this->setValue($value);
79 $this->setValue(null);
82 $value = $this->getInitialValue();
84 $this->setValue($value);
86 $this->setValue(null);
90 $this->setError($err);
92 return $this->getValue();
95 protected function getCustomControlClass() {
96 return 'aphront-form-control-date';
99 public function setValue($epoch) {
100 if ($epoch instanceof AphrontFormDateControlValue
) {
101 $this->continueOnInvalidDate
= true;
102 $this->valueDate
= $epoch->getValueDate();
103 $this->valueTime
= $epoch->getValueTime();
104 $this->allowNull
= $epoch->getOptional();
105 $this->isDisabled
= $epoch->isDisabled();
107 return parent
::setValue($epoch->getEpoch());
110 $result = parent
::setValue($epoch);
112 if ($epoch === null) {
116 $readable = $this->formatTime($epoch, 'Y!m!d!'.$this->getTimeFormat());
117 $readable = explode('!', $readable, 4);
119 $year = $readable[0];
120 $month = $readable[1];
123 $this->valueDate
= $month.'/'.$day.'/'.$year;
124 $this->valueTime
= $readable[3];
129 private function getDateInputValue() {
130 $date_format = $this->getDateFormat();
131 $timezone = $this->getTimezone();
134 $datetime = new DateTime($this->valueDate
, $timezone);
135 } catch (Exception
$ex) {
136 return $this->valueDate
;
139 return $datetime->format($date_format);
142 private function getTimeFormat() {
143 $viewer = $this->getViewer();
144 $time_key = PhabricatorTimeFormatSetting
::SETTINGKEY
;
145 return $viewer->getUserSetting($time_key);
148 private function getDateFormat() {
149 $viewer = $this->getViewer();
150 $date_key = PhabricatorDateFormatSetting
::SETTINGKEY
;
151 return $viewer->getUserSetting($date_key);
154 private function getTimeInputValue() {
155 return $this->valueTime
;
158 private function formatTime($epoch, $fmt) {
159 return phabricator_format_local_time(
165 private function getDateInputName() {
166 return $this->getName().'_d';
169 private function getTimeInputName() {
170 return $this->getName().'_t';
173 private function getCheckboxInputName() {
174 return $this->getName().'_e';
177 protected function renderInput() {
180 if ($this->getValue() === null && !$this->continueOnInvalidDate
) {
181 $this->setValue($this->getInitialValue());
182 if ($this->allowNull
) {
183 $disabled = 'disabled';
187 if ($this->isDisabled
) {
188 $disabled = 'disabled';
192 if ($this->allowNull
) {
193 $checkbox = javelin_tag(
196 'type' => 'checkbox',
197 'name' => $this->getCheckboxInputName(),
198 'sigil' => 'calendar-enable',
199 'class' => 'aphront-form-date-enabled-input',
201 'checked' => ($disabled === null ?
'checked' : null),
205 $date_sel = javelin_tag(
208 'autocomplete' => 'off',
209 'name' => $this->getDateInputName(),
210 'sigil' => 'date-input',
211 'value' => $this->getDateInputValue(),
213 'class' => 'aphront-form-date-input',
217 $date_div = javelin_tag(
220 'class' => 'aphront-form-date-input-container',
224 $cicon = id(new PHUIIconView())
225 ->setIcon('fa-calendar');
227 $cal_icon = javelin_tag(
231 'class' => 'calendar-button',
232 'sigil' => 'calendar-button',
236 $values = $this->getTimeTypeaheadValues();
238 $time_id = celerity_generate_unique_node_id();
239 Javelin
::initBehavior('time-typeahead', array(
240 'startTimeID' => $time_id,
241 'endTimeID' => $this->endDateID
,
242 'timeValues' => $values,
243 'format' => $this->getTimeFormat(),
246 $time_sel = javelin_tag(
249 'autocomplete' => 'off',
250 'name' => $this->getTimeInputName(),
251 'sigil' => 'time-input',
252 'value' => $this->getTimeInputValue(),
254 'class' => 'aphront-form-time-input',
258 $time_div = javelin_tag(
262 'class' => 'aphront-form-time-input-container',
266 $viewer = $this->getViewer();
267 $week_key = PhabricatorWeekStartDaySetting
::SETTINGKEY
;
268 $week_start = $viewer->getUserSetting($week_key);
270 Javelin
::initBehavior('fancy-datepicker', array(
271 'format' => $this->getDateFormat(),
272 'weekStart' => $week_start,
276 $classes[] = 'aphront-form-date-container';
278 $classes[] = 'datepicker-disabled';
280 if ($this->isTimeDisabled
) {
281 $classes[] = 'no-time';
287 'class' => implode(' ', $classes),
288 'sigil' => 'phabricator-date-control',
290 'disabled' => (bool)$disabled,
292 'id' => $this->getID(),
302 private function getTimezone() {
307 $viewer = $this->getViewer();
309 $user_zone = $viewer->getTimezoneIdentifier();
310 $this->zone
= new DateTimeZone($user_zone);
314 private function getInitialValue() {
315 $zone = $this->getTimezone();
317 // TODO: We could eventually allow these to be customized per install or
318 // per user or both, but let's wait and see.
319 switch ($this->initialTime
) {
320 case self
::TIME_START_OF_DAY
:
324 case self
::TIME_START_OF_BUSINESS
:
327 case self
::TIME_END_OF_BUSINESS
:
330 case self
::TIME_END_OF_DAY
:
335 $today = $this->formatTime(time(), 'Y-m-d');
337 $date = new DateTime("{$today} {$time}", $zone);
338 $value = $date->format('U');
339 } catch (Exception
$ex) {
346 private function getTimeTypeaheadValues() {
347 $time_format = $this->getTimeFormat();
350 if ($time_format == 'g:i A') {
351 $am_pm_list = array('AM', 'PM');
353 foreach ($am_pm_list as $am_pm) {
354 for ($hour = 0; $hour < 12; $hour++
) {
355 $actual_hour = ($hour == 0) ?
12 : $hour;
356 $times[] = $actual_hour.':00 '.$am_pm;
357 $times[] = $actual_hour.':30 '.$am_pm;
360 } else if ($time_format == 'H:i') {
361 for ($hour = 0; $hour < 24; $hour++
) {
362 $written_hour = ($hour > 9) ?
$hour : '0'.$hour;
363 $times[] = $written_hour.':00';
364 $times[] = $written_hour.':30';
368 foreach ($times as $key => $time) {
369 $times[$key] = array($key, $time);