Generate file attachment transactions for explicit Remarkup attachments on common...
[phabricator.git] / src / view / form / control / AphrontFormDateControl.php
blob833c0a7c894872bc2b57b058f046f45ded0e4c5b
1 <?php
3 final class AphrontFormDateControl extends AphrontFormControl {
5 private $initialTime;
6 private $zone;
8 private $valueDate;
9 private $valueTime;
10 private $allowNull;
11 private $continueOnInvalidDate = false;
12 private $isTimeDisabled;
13 private $isDisabled;
14 private $endDateID;
16 public function setAllowNull($allow_null) {
17 $this->allowNull = $allow_null;
18 return $this;
21 public function setIsTimeDisabled($is_disabled) {
22 $this->isTimeDisabled = $is_disabled;
23 return $this;
26 public function setIsDisabled($is_datepicker_disabled) {
27 $this->isDisabled = $is_datepicker_disabled;
28 return $this;
31 public function setEndDateID($value) {
32 $this->endDateID = $value;
33 return $this;
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;
43 return $this;
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);
54 return;
57 $err = $this->getError();
59 if ($date || $time) {
60 $this->valueDate = $date;
61 $this->valueTime = $time;
63 // Assume invalid.
64 $err = pht('Invalid');
66 $zone = $this->getTimezone();
68 try {
69 $datetime = new DateTime("{$date} {$time}", $zone);
70 $value = $datetime->format('U');
71 } catch (Exception $ex) {
72 $value = null;
75 if ($value) {
76 $this->setValue($value);
77 $err = null;
78 } else {
79 $this->setValue(null);
81 } else {
82 $value = $this->getInitialValue();
83 if ($value) {
84 $this->setValue($value);
85 } else {
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) {
113 return $result;
116 $readable = $this->formatTime($epoch, 'Y!m!d!'.$this->getTimeFormat());
117 $readable = explode('!', $readable, 4);
119 $year = $readable[0];
120 $month = $readable[1];
121 $day = $readable[2];
123 $this->valueDate = $month.'/'.$day.'/'.$year;
124 $this->valueTime = $readable[3];
126 return $result;
129 private function getDateInputValue() {
130 $date_format = $this->getDateFormat();
131 $timezone = $this->getTimezone();
133 try {
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(
160 $epoch,
161 $this->getViewer(),
162 $fmt);
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() {
179 $disabled = null;
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';
191 $checkbox = null;
192 if ($this->allowNull) {
193 $checkbox = javelin_tag(
194 'input',
195 array(
196 'type' => 'checkbox',
197 'name' => $this->getCheckboxInputName(),
198 'sigil' => 'calendar-enable',
199 'class' => 'aphront-form-date-enabled-input',
200 'value' => 1,
201 'checked' => ($disabled === null ? 'checked' : null),
205 $date_sel = javelin_tag(
206 'input',
207 array(
208 'autocomplete' => 'off',
209 'name' => $this->getDateInputName(),
210 'sigil' => 'date-input',
211 'value' => $this->getDateInputValue(),
212 'type' => 'text',
213 'class' => 'aphront-form-date-input',
215 '');
217 $date_div = javelin_tag(
218 'div',
219 array(
220 'class' => 'aphront-form-date-input-container',
222 $date_sel);
224 $cicon = id(new PHUIIconView())
225 ->setIcon('fa-calendar');
227 $cal_icon = javelin_tag(
228 'a',
229 array(
230 'href' => '#',
231 'class' => 'calendar-button',
232 'sigil' => 'calendar-button',
234 $cicon);
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(
247 'input',
248 array(
249 'autocomplete' => 'off',
250 'name' => $this->getTimeInputName(),
251 'sigil' => 'time-input',
252 'value' => $this->getTimeInputValue(),
253 'type' => 'text',
254 'class' => 'aphront-form-time-input',
256 '');
258 $time_div = javelin_tag(
259 'div',
260 array(
261 'id' => $time_id,
262 'class' => 'aphront-form-time-input-container',
264 $time_sel);
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,
275 $classes = array();
276 $classes[] = 'aphront-form-date-container';
277 if ($disabled) {
278 $classes[] = 'datepicker-disabled';
280 if ($this->isTimeDisabled) {
281 $classes[] = 'no-time';
284 return javelin_tag(
285 'div',
286 array(
287 'class' => implode(' ', $classes),
288 'sigil' => 'phabricator-date-control',
289 'meta' => array(
290 'disabled' => (bool)$disabled,
292 'id' => $this->getID(),
294 array(
295 $checkbox,
296 $date_div,
297 $cal_icon,
298 $time_div,
302 private function getTimezone() {
303 if ($this->zone) {
304 return $this->zone;
307 $viewer = $this->getViewer();
309 $user_zone = $viewer->getTimezoneIdentifier();
310 $this->zone = new DateTimeZone($user_zone);
311 return $this->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:
321 default:
322 $time = '12:00 AM';
323 break;
324 case self::TIME_START_OF_BUSINESS:
325 $time = '9:00 AM';
326 break;
327 case self::TIME_END_OF_BUSINESS:
328 $time = '5:00 PM';
329 break;
330 case self::TIME_END_OF_DAY:
331 $time = '11:59 PM';
332 break;
335 $today = $this->formatTime(time(), 'Y-m-d');
336 try {
337 $date = new DateTime("{$today} {$time}", $zone);
338 $value = $date->format('U');
339 } catch (Exception $ex) {
340 $value = null;
343 return $value;
346 private function getTimeTypeaheadValues() {
347 $time_format = $this->getTimeFormat();
348 $times = array();
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);
372 return $times;