diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -382,7 +382,7 @@ 'rsrc/js/application/aphlict/behavior-desktop-notifications-control.js' => 'edd1ba66', 'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18', 'rsrc/js/application/calendar/behavior-day-view.js' => '4b3c4443', - 'rsrc/js/application/calendar/behavior-event-all-day.js' => '937bb700', + 'rsrc/js/application/calendar/behavior-event-all-day.js' => 'b41537c9', 'rsrc/js/application/calendar/behavior-month-view.js' => 'fe33e256', 'rsrc/js/application/calendar/behavior-recurring-edit.js' => '5f1c4d5f', 'rsrc/js/application/config/behavior-reorder-fields.js' => 'b6993408', @@ -651,7 +651,7 @@ 'javelin-behavior-editengine-reorder-configs' => 'd7a74243', 'javelin-behavior-editengine-reorder-fields' => 'b59e1e96', 'javelin-behavior-error-log' => '6882e80a', - 'javelin-behavior-event-all-day' => '937bb700', + 'javelin-behavior-event-all-day' => 'b41537c9', 'javelin-behavior-fancy-datepicker' => '568931f3', 'javelin-behavior-global-drag-and-drop' => '960f6a39', 'javelin-behavior-herald-rule-editor' => '7ebaeed3', diff --git a/src/aphront/httpparametertype/AphrontBoolHTTPParameterType.php b/src/aphront/httpparametertype/AphrontBoolHTTPParameterType.php --- a/src/aphront/httpparametertype/AphrontBoolHTTPParameterType.php +++ b/src/aphront/httpparametertype/AphrontBoolHTTPParameterType.php @@ -3,8 +3,21 @@ final class AphrontBoolHTTPParameterType extends AphrontHTTPParameterType { + protected function getParameterExists(AphrontRequest $request, $key) { + if ($request->getExists($key)) { + return true; + } + + $checkbox_key = $this->getCheckboxKey($key); + if ($request->getExists($checkbox_key)) { + return true; + } + + return false; + } + protected function getParameterValue(AphrontRequest $request, $key) { - return $request->getBool($key); + return (bool)$request->getBool($key); } protected function getParameterTypeName() { @@ -26,4 +39,8 @@ ); } + public function getCheckboxKey($key) { + return "{$key}.exists"; + } + } diff --git a/src/applications/calendar/editor/PhabricatorCalendarEventEditEngine.php b/src/applications/calendar/editor/PhabricatorCalendarEventEditEngine.php --- a/src/applications/calendar/editor/PhabricatorCalendarEventEditEngine.php +++ b/src/applications/calendar/editor/PhabricatorCalendarEventEditEngine.php @@ -90,8 +90,8 @@ ->setValue($object->getName()), id(new PhabricatorBoolEditField()) ->setKey('isAllDay') - ->setLabel(pht('All Day')) ->setOptions(pht('Normal Event'), pht('All Day Event')) + ->setAsCheckbox(true) ->setTransactionType( PhabricatorCalendarEventAllDayTransaction::TRANSACTIONTYPE) ->setDescription(pht('Marks this as an all day event.')) diff --git a/src/applications/calendar/xaction/PhabricatorCalendarEventAllDayTransaction.php b/src/applications/calendar/xaction/PhabricatorCalendarEventAllDayTransaction.php --- a/src/applications/calendar/xaction/PhabricatorCalendarEventAllDayTransaction.php +++ b/src/applications/calendar/xaction/PhabricatorCalendarEventAllDayTransaction.php @@ -39,7 +39,7 @@ public function getTitle() { if ($this->getNewValue()) { return pht( - '%s changed this as an all day event.', + '%s changed this to an all day event.', $this->renderAuthor()); } else { return pht( diff --git a/src/applications/transactions/editfield/PhabricatorBoolEditField.php b/src/applications/transactions/editfield/PhabricatorBoolEditField.php --- a/src/applications/transactions/editfield/PhabricatorBoolEditField.php +++ b/src/applications/transactions/editfield/PhabricatorBoolEditField.php @@ -4,6 +4,7 @@ extends PhabricatorEditField { private $options; + private $asCheckbox; public function setOptions($off_label, $on_label) { $this->options = array( @@ -17,6 +18,15 @@ return $this->options; } + public function setAsCheckbox($as_checkbox) { + $this->asCheckbox = $as_checkbox; + return $this; + } + + public function getAsCheckbox() { + return $this->asCheckbox; + } + protected function newControl() { $options = $this->getOptions(); @@ -27,8 +37,22 @@ ); } - return id(new AphrontFormSelectControl()) - ->setOptions($options); + if ($this->getAsCheckbox()) { + $key = $this->getKey(); + $value = $this->getValueForControl(); + $checkbox_key = $this->newHTTPParameterType() + ->getCheckboxKey($key); + $id = $this->getControlID(); + + $control = id(new AphrontFormCheckboxControl()) + ->setCheckboxKey($checkbox_key) + ->addCheckbox($key, '1', $options['1'], $value, $id); + } else { + $control = id(new AphrontFormSelectControl()) + ->setOptions($options); + } + + return $control; } protected function newHTTPParameterType() { diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -553,8 +553,16 @@ return true; } - if (!is_array($old) && !strlen($old)) { - return true; + if (!is_array($old)) { + if (!strlen($old)) { + return true; + } + + // The integer 0 is also uninteresting by default; this is often + // an "off" flag for something like "All Day Event". + if ($old === 0) { + return true; + } } break; diff --git a/src/view/form/control/AphrontFormCheckboxControl.php b/src/view/form/control/AphrontFormCheckboxControl.php --- a/src/view/form/control/AphrontFormCheckboxControl.php +++ b/src/view/form/control/AphrontFormCheckboxControl.php @@ -3,6 +3,16 @@ final class AphrontFormCheckboxControl extends AphrontFormControl { private $boxes = array(); + private $checkboxKey; + + public function setCheckboxKey($checkbox_key) { + $this->checkboxKey = $checkbox_key; + return $this; + } + + public function getCheckboxKey() { + return $this->checkboxKey; + } public function addCheckbox( $name, @@ -52,6 +62,23 @@ phutil_tag('th', array(), $label), )); } + + // When a user submits a form with a checkbox unchecked, the browser + // doesn't submit anything to the server. This hidden key lets the server + // know that the checkboxes were present on the client, the user just did + // not select any of them. + + $checkbox_key = $this->getCheckboxKey(); + if ($checkbox_key) { + $rows[] = phutil_tag( + 'input', + array( + 'type' => 'hidden', + 'name' => $checkbox_key, + 'value' => 1, + )); + } + return phutil_tag( 'table', array('class' => 'aphront-form-control-checkbox-layout'), diff --git a/webroot/rsrc/js/application/calendar/behavior-event-all-day.js b/webroot/rsrc/js/application/calendar/behavior-event-all-day.js --- a/webroot/rsrc/js/application/calendar/behavior-event-all-day.js +++ b/webroot/rsrc/js/application/calendar/behavior-event-all-day.js @@ -6,7 +6,7 @@ var all_day = JX.$(config.allDayID); JX.DOM.listen(all_day, 'change', null, function() { - var is_all_day = !!parseInt(all_day.value, 10); + var is_all_day = !!all_day.checked; for (var ii = 0; ii < config.controlIDs.length; ii++) { var control = JX.$(config.controlIDs[ii]);