diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 02a846c56e..a854b0fc37 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -1343,6 +1343,20 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/js/application/phortune/behavior-balanced-payment-form.js', ), + 'javelin-behavior-config-reorder-fields' => + array( + 'uri' => '/res/691c5c8c/rsrc/js/application/config/behavior-reorder-fields.js', + 'type' => 'js', + 'requires' => + array( + 0 => 'javelin-behavior', + 1 => 'javelin-stratcom', + 2 => 'javelin-dom', + 3 => 'javelin-json', + 4 => 'phabricator-draggable-list', + ), + 'disk' => '/rsrc/js/application/config/behavior-reorder-fields.js', + ), 'javelin-behavior-conpherence-menu' => array( 'uri' => '/res/f27205d4/rsrc/js/application/conpherence/behavior-menu.js', diff --git a/src/applications/people/customfield/PhabricatorUserBlurbField.php b/src/applications/people/customfield/PhabricatorUserBlurbField.php index 67973a8b23..4857502653 100644 --- a/src/applications/people/customfield/PhabricatorUserBlurbField.php +++ b/src/applications/people/customfield/PhabricatorUserBlurbField.php @@ -14,7 +14,11 @@ final class PhabricatorUserBlurbField } public function getFieldDescription() { - return pht('Short user summary.'); + return pht('Short blurb about the user.'); + } + + public function canDisableField() { + return true; } protected function didSetObject(PhabricatorCustomFieldInterface $object) { diff --git a/src/infrastructure/customfield/config/PhabricatorCustomFieldConfigOptionType.php b/src/infrastructure/customfield/config/PhabricatorCustomFieldConfigOptionType.php index 86c1195519..a1a1385afb 100644 --- a/src/infrastructure/customfield/config/PhabricatorCustomFieldConfigOptionType.php +++ b/src/infrastructure/customfield/config/PhabricatorCustomFieldConfigOptionType.php @@ -3,4 +3,129 @@ final class PhabricatorCustomFieldConfigOptionType extends PhabricatorConfigOptionType { + public function readRequest( + PhabricatorConfigOption $option, + AphrontRequest $request) { + + $e_value = null; + $errors = array(); + $storage_value = $request->getStr('value'); + + $in_value = json_decode($storage_value, true); + if (!is_array($in_value)) { + $in_value = array(); + } + + // When we submit from JS, we submit a list (since maps are not guaranteed + // to retain order). Convert it into a map for storage (since it's far more + // convenient for us elsewhere). + $storage_value = ipull($in_value, null, 'key'); + $display_value = $storage_value; + + return array($e_value, $errors, $storage_value, $display_value); + } + + public function renderControl( + PhabricatorConfigOption $option, + $display_value, + $e_value) { + + $field_base_class = $option->getCustomData(); + + $field_spec = $display_value; + if (!is_array($field_spec)) { + $field_spec = PhabricatorEnv::getEnvConfig($option->getKey()); + } + + // Get all of the fields (including disabled fields) by querying for them + // with a faux spec where no fields are disabled. + $faux_spec = $field_spec; + foreach ($faux_spec as $key => $spec) { + unset($faux_spec[$key]['disabled']); + } + $fields = PhabricatorCustomField::buildFieldList( + $field_base_class, + $faux_spec); + + $list_id = celerity_generate_unique_node_id(); + $input_id = celerity_generate_unique_node_id(); + + $list = id(new PhabricatorObjectItemListView()) + ->setFlush(true) + ->setID($list_id); + foreach ($fields as $key => $field) { + $item = id(new PhabricatorObjectItemView()) + ->addSigil('field-spec') + ->setMetadata(array('fieldKey' => $key)) + ->setGrippable(true) + ->addAttribute($field->getFieldDescription()) + ->setHeader($field->getFieldName()); + + $is_disabled = !empty($field_spec[$key]['disabled']); + + $disabled_item = clone $item; + $enabled_item = clone $item; + + if ($is_disabled) { + $list->addItem($disabled_item); + } else { + $list->addItem($enabled_item); + } + + $disabled_item->addIcon('none', pht('Disabled')); + $disabled_item->addAction( + id(new PHUIListItemView()) + ->setHref('#') + ->addSigil('field-spec-toggle') + ->setIcon('new')); + + $enabled_item->setBarColor('green'); + + if (!$field->canDisableField()) { + $enabled_item->addAction( + id(new PHUIListItemView()) + ->setIcon('lock-grey')); + $enabled_item->addIcon('none', pht('Permanent Field')); + } else { + $enabled_item->addAction( + id(new PHUIListItemView()) + ->setHref('#') + ->addSigil('field-spec-toggle') + ->setIcon('delete')); + } + + $fields[$key] = array( + 'disabled' => $is_disabled, + 'disabledMarkup' => $disabled_item->render(), + 'enabledMarkup' => $enabled_item->render(), + ); + } + + $input = phutil_tag( + 'input', + array( + 'id' => $input_id, + 'type' => 'hidden', + 'name' => 'value', + 'value' => json_encode($display_value), + )); + + Javelin::initBehavior( + 'config-reorder-fields', + array( + 'listID' => $list_id, + 'inputID' => $input_id, + 'fields' => $fields, + )); + + return id(new AphrontFormMarkupControl()) + ->setLabel(pht('Value')) + ->setError($e_value) + ->setValue( + array( + $list, + $input, + )); + } + } diff --git a/webroot/rsrc/js/application/config/behavior-reorder-fields.js b/webroot/rsrc/js/application/config/behavior-reorder-fields.js new file mode 100644 index 0000000000..deb9da61b7 --- /dev/null +++ b/webroot/rsrc/js/application/config/behavior-reorder-fields.js @@ -0,0 +1,58 @@ +/** + * @provides javelin-behavior-config-reorder-fields + * @requires javelin-behavior + * javelin-stratcom + * javelin-dom + * javelin-json + * phabricator-draggable-list + */ + +JX.behavior('config-reorder-fields', function(config) { + + var fields = config.fields; + var root = JX.$(config.listID); + + var list = new JX.DraggableList('field-spec', root) + .setFindItemsHandler(function() { + return JX.DOM.scry(root, 'li', 'field-spec'); + }); + + list.listen('didDrop', function(node, after) { + write_state_to_form(); + }); + + JX.DOM.listen(root, 'click', 'field-spec-toggle', function(e) { + e.kill(); + + var key = e.getNodeData('field-spec').fieldKey; + fields[key].disabled = !fields[key].disabled; + + JX.DOM.replace( + e.getNode('field-spec'), + JX.$H( + fields[key].disabled ? + fields[key].disabledMarkup : + fields[key].enabledMarkup)); + + write_state_to_form(); + }); + + var write_state_to_form = function() { + var nodes = list.findItems(); + var order = []; + var key; + for (var ii = 0; ii < nodes.length; ii++) { + key = JX.Stratcom.getData(nodes[ii]).fieldKey; + if (key) { + order.push({ + key: key, + disabled: fields[key].disabled + }); + } + } + + JX.$(config.inputID).value = JX.JSON.stringify(order); + }; + +}); +