2015-12-02 23:38:11 +01:00
|
|
|
/**
|
|
|
|
* @provides phuix-form-control-view
|
|
|
|
* @requires javelin-install
|
|
|
|
* javelin-dom
|
|
|
|
*/
|
|
|
|
|
|
|
|
JX.install('PHUIXFormControl', {
|
|
|
|
|
|
|
|
members: {
|
|
|
|
_node: null,
|
|
|
|
_labelNode: null,
|
|
|
|
_errorNode: null,
|
|
|
|
_inputNode: null,
|
2016-11-09 19:33:05 +01:00
|
|
|
_className: null,
|
2015-12-02 23:38:11 +01:00
|
|
|
_valueSetCallback: null,
|
|
|
|
_valueGetCallback: null,
|
2018-01-10 20:23:55 +01:00
|
|
|
_rawInputNode: null,
|
2015-12-02 23:38:11 +01:00
|
|
|
|
|
|
|
setLabel: function(label) {
|
|
|
|
JX.DOM.setContent(this._getLabelNode(), label);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
setError: function(error) {
|
|
|
|
JX.DOM.setContent(this._getErrorNode(), error);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2016-11-09 19:33:05 +01:00
|
|
|
setClass: function(className) {
|
|
|
|
this._className = className;
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2015-12-02 23:38:11 +01:00
|
|
|
setControl: function(type, spec) {
|
|
|
|
var node = this._getInputNode();
|
|
|
|
|
|
|
|
var input;
|
|
|
|
switch (type) {
|
|
|
|
case 'tokenizer':
|
|
|
|
input = this._newTokenizer(spec);
|
|
|
|
break;
|
2015-12-04 16:56:03 +01:00
|
|
|
case 'select':
|
|
|
|
input = this._newSelect(spec);
|
|
|
|
break;
|
2016-02-09 00:24:52 +01:00
|
|
|
case 'points':
|
|
|
|
input = this._newPoints(spec);
|
|
|
|
break;
|
2016-04-06 14:59:09 +02:00
|
|
|
case 'optgroups':
|
|
|
|
input = this._newOptgroups(spec);
|
|
|
|
break;
|
2016-12-28 19:21:54 +01:00
|
|
|
case 'static':
|
|
|
|
input = this._newStatic(spec);
|
|
|
|
break;
|
When accepting revisions, allow users to accept on behalf of a subset of reviewers
Summary:
Ref T12271. Currenty, when you "Accept" a revision, you always accept it for all reviewers you have authority over.
There are some situations where communication can be more clear if users can accept as only themselves, or for only some packages, etc. T12271 discusses some of these use cases in more depth.
Instead of making "Accept" a blanket action, default it to doing what it does now but let the user uncheck reviewers.
In cases where project/package reviewers aren't in use, this doesn't change anything.
For now, "reject" still acts the old way (reject everything). We could make that use checkboxes too, but I'm not sure there's as much of a use case for it, and I generally want users who are blocking stuff to have more direct accountability in a product sense.
Test Plan:
- Accepted normally.
- Accepted a subset.
- Tried to accept none.
- Tried to accept bogus reviewers.
- Accepted with myself not a reviewer
- Accepted with only one reviewer (just got normal "this will be accepted" text).
{F4251255}
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T12271
Differential Revision: https://secure.phabricator.com/D17533
2017-03-22 17:28:36 +01:00
|
|
|
case 'checkboxes':
|
|
|
|
input = this._newCheckboxes(spec);
|
|
|
|
break;
|
2018-01-10 20:23:55 +01:00
|
|
|
case 'text':
|
|
|
|
input = this._newText(spec);
|
|
|
|
break;
|
Restore bulk edit support for remarkup fields (description, add comment)
Summary:
Depends on D18866. Ref T13025. Fixes T12415. This makes the old "Add Comment" action work, and adds support for a new "Set description to" action (possibly, I could imagine "append description" being useful some day, maybe).
The implementation is just a `<textarea />`, not a whole fancy remarkup box with `[Bold] [Italic] ...` buttons, preview, typeaheads, etc. It would be nice to enrich this eventually but doing the rendering in pure JS is currently very involved.
This requires a little bit of gymnastics to get the transaction populated properly, and adds some extra validation since we need some code there anyway.
Test Plan:
- Changed the description of a task via bulk editor.
- Added a comment to a task via bulk editor.
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T13025, T12415
Differential Revision: https://secure.phabricator.com/D18867
2018-01-11 18:48:33 +01:00
|
|
|
case 'remarkup':
|
|
|
|
input = this._newRemarkup(spec);
|
|
|
|
break;
|
2015-12-02 23:38:11 +01:00
|
|
|
default:
|
|
|
|
// TODO: Default or better error?
|
|
|
|
JX.$E('Bad Input Type');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
JX.DOM.setContent(node, input.node);
|
|
|
|
this._valueGetCallback = input.get;
|
|
|
|
this._valueSetCallback = input.set;
|
2018-01-10 20:23:55 +01:00
|
|
|
this._rawInputNode = input.node;
|
2015-12-02 23:38:11 +01:00
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
setValue: function(value) {
|
|
|
|
this._valueSetCallback(value);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
getValue: function() {
|
|
|
|
return this._valueGetCallback();
|
|
|
|
},
|
|
|
|
|
2018-01-10 20:23:55 +01:00
|
|
|
getRawInputNode: function() {
|
|
|
|
return this._rawInputNode;
|
|
|
|
},
|
|
|
|
|
2015-12-02 23:38:11 +01:00
|
|
|
getNode: function() {
|
|
|
|
if (!this._node) {
|
|
|
|
|
|
|
|
var attrs = {
|
2016-11-09 19:33:05 +01:00
|
|
|
className: 'aphront-form-control ' + this._className + ' grouped'
|
2015-12-02 23:38:11 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
var content = [
|
|
|
|
this._getLabelNode(),
|
|
|
|
this._getErrorNode(),
|
|
|
|
this._getInputNode()
|
|
|
|
];
|
|
|
|
|
|
|
|
this._node = JX.$N('div', attrs, content);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this._node;
|
|
|
|
},
|
|
|
|
|
|
|
|
_getLabelNode: function() {
|
|
|
|
if (!this._labelNode) {
|
|
|
|
var attrs = {
|
|
|
|
className: 'aphront-form-label'
|
|
|
|
};
|
|
|
|
|
|
|
|
this._labelNode = JX.$N('label', attrs);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this._labelNode;
|
|
|
|
},
|
|
|
|
|
|
|
|
_getErrorNode: function() {
|
|
|
|
if (!this._errorNode) {
|
|
|
|
var attrs = {
|
|
|
|
className: 'aphront-form-error'
|
|
|
|
};
|
|
|
|
|
|
|
|
this._errorNode = JX.$N('span', attrs);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this._errorNode;
|
|
|
|
},
|
|
|
|
|
|
|
|
_getInputNode: function() {
|
|
|
|
if (!this._inputNode) {
|
|
|
|
var attrs = {
|
|
|
|
className: 'aphront-form-input'
|
|
|
|
};
|
|
|
|
|
|
|
|
this._inputNode = JX.$N('div', attrs);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this._inputNode;
|
|
|
|
},
|
|
|
|
|
|
|
|
_newTokenizer: function(spec) {
|
|
|
|
var build = JX.Prefab.newTokenizerFromTemplate(
|
|
|
|
spec.markup,
|
|
|
|
spec.config);
|
|
|
|
build.tokenizer.start();
|
|
|
|
|
2015-12-04 23:37:27 +01:00
|
|
|
function get_value() {
|
|
|
|
return JX.keys(build.tokenizer.getTokens());
|
|
|
|
}
|
|
|
|
|
2015-12-04 18:30:53 +01:00
|
|
|
function set_value(map) {
|
2015-12-04 23:37:27 +01:00
|
|
|
var tokens = get_value();
|
|
|
|
for (var ii = 0; ii < tokens.length; ii++) {
|
|
|
|
build.tokenizer.removeToken(tokens[ii]);
|
|
|
|
}
|
2015-12-04 18:30:53 +01:00
|
|
|
for (var k in map) {
|
|
|
|
var v = JX.Prefab.transformDatasourceResults(map[k]);
|
|
|
|
build.tokenizer.addToken(k, v);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
set_value(spec.value || {});
|
|
|
|
|
2015-12-02 23:38:11 +01:00
|
|
|
return {
|
|
|
|
node: build.node,
|
2015-12-04 23:37:27 +01:00
|
|
|
get: get_value,
|
2015-12-04 18:30:53 +01:00
|
|
|
set: set_value
|
2015-12-02 23:38:11 +01:00
|
|
|
};
|
2015-12-04 16:56:03 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
_newSelect: function(spec) {
|
2015-12-04 23:37:27 +01:00
|
|
|
var node = JX.Prefab.renderSelect(
|
|
|
|
spec.options,
|
|
|
|
spec.value,
|
|
|
|
{},
|
|
|
|
spec.order);
|
2015-12-04 16:56:03 +01:00
|
|
|
|
2016-02-09 00:24:52 +01:00
|
|
|
return {
|
|
|
|
node: node,
|
|
|
|
get: function() {
|
|
|
|
return node.value;
|
|
|
|
},
|
|
|
|
set: function(value) {
|
|
|
|
node.value = value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
2016-12-28 19:21:54 +01:00
|
|
|
_newStatic: function(spec) {
|
|
|
|
var node = JX.$N(
|
|
|
|
'div',
|
|
|
|
{
|
|
|
|
className: 'phui-form-static-action'
|
|
|
|
},
|
|
|
|
spec.description || '');
|
|
|
|
|
|
|
|
return {
|
|
|
|
node: node,
|
|
|
|
get: function() {
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
set: function() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
When accepting revisions, allow users to accept on behalf of a subset of reviewers
Summary:
Ref T12271. Currenty, when you "Accept" a revision, you always accept it for all reviewers you have authority over.
There are some situations where communication can be more clear if users can accept as only themselves, or for only some packages, etc. T12271 discusses some of these use cases in more depth.
Instead of making "Accept" a blanket action, default it to doing what it does now but let the user uncheck reviewers.
In cases where project/package reviewers aren't in use, this doesn't change anything.
For now, "reject" still acts the old way (reject everything). We could make that use checkboxes too, but I'm not sure there's as much of a use case for it, and I generally want users who are blocking stuff to have more direct accountability in a product sense.
Test Plan:
- Accepted normally.
- Accepted a subset.
- Tried to accept none.
- Tried to accept bogus reviewers.
- Accepted with myself not a reviewer
- Accepted with only one reviewer (just got normal "this will be accepted" text).
{F4251255}
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T12271
Differential Revision: https://secure.phabricator.com/D17533
2017-03-22 17:28:36 +01:00
|
|
|
_newCheckboxes: function(spec) {
|
|
|
|
var checkboxes = [];
|
|
|
|
var checkbox_list = [];
|
|
|
|
for (var ii = 0; ii < spec.keys.length; ii++) {
|
|
|
|
var key = spec.keys[ii];
|
|
|
|
var checkbox_id = 'checkbox-' + Math.floor(Math.random() * 1000000);
|
|
|
|
|
|
|
|
var checkbox = JX.$N(
|
|
|
|
'input',
|
|
|
|
{
|
|
|
|
type: 'checkbox',
|
|
|
|
value: key,
|
|
|
|
id: checkbox_id
|
|
|
|
});
|
|
|
|
|
|
|
|
checkboxes.push(checkbox);
|
|
|
|
|
|
|
|
var label = JX.$N(
|
|
|
|
'label',
|
|
|
|
{
|
|
|
|
className: 'phuix-form-checkbox-label',
|
|
|
|
htmlFor: checkbox_id
|
|
|
|
},
|
|
|
|
JX.$H(spec.labels[key] || ''));
|
|
|
|
|
|
|
|
var display = JX.$N(
|
|
|
|
'div',
|
|
|
|
{
|
|
|
|
className: 'phuix-form-checkbox-item'
|
|
|
|
},
|
|
|
|
[checkbox, label]);
|
|
|
|
|
|
|
|
checkbox_list.push(display);
|
|
|
|
}
|
|
|
|
|
|
|
|
var node = JX.$N(
|
|
|
|
'div',
|
|
|
|
{
|
|
|
|
className: 'phuix-form-checkbox-action'
|
|
|
|
},
|
|
|
|
checkbox_list);
|
|
|
|
|
|
|
|
var get_value = function() {
|
|
|
|
var list = [];
|
|
|
|
for (var ii = 0; ii < checkboxes.length; ii++) {
|
|
|
|
if (checkboxes[ii].checked) {
|
|
|
|
list.push(checkboxes[ii].value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return list;
|
|
|
|
};
|
|
|
|
|
|
|
|
var set_value = function(value) {
|
|
|
|
value = value || [];
|
|
|
|
|
|
|
|
if (!value.length) {
|
|
|
|
value = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
var map = {};
|
|
|
|
var ii;
|
|
|
|
for (ii = 0; ii < value.length; ii++) {
|
|
|
|
map[value[ii]] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (ii = 0; ii < checkboxes.length; ii++) {
|
|
|
|
if (map.hasOwnProperty(checkboxes[ii].value)) {
|
|
|
|
checkboxes[ii].checked = 'checked';
|
|
|
|
} else {
|
|
|
|
checkboxes[ii].checked = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
set_value(spec.value);
|
|
|
|
|
|
|
|
return {
|
|
|
|
node: node,
|
|
|
|
get: get_value,
|
|
|
|
set: set_value
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
2016-02-09 00:24:52 +01:00
|
|
|
_newPoints: function(spec) {
|
2018-01-31 19:37:38 +01:00
|
|
|
return this._newText(spec);
|
2018-01-10 20:23:55 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
_newText: function(spec) {
|
2016-02-09 00:24:52 +01:00
|
|
|
var attrs = {
|
|
|
|
type: 'text',
|
|
|
|
value: spec.value
|
|
|
|
};
|
|
|
|
|
|
|
|
var node = JX.$N('input', attrs);
|
|
|
|
|
2016-04-06 14:59:09 +02:00
|
|
|
return {
|
|
|
|
node: node,
|
Restore bulk edit support for remarkup fields (description, add comment)
Summary:
Depends on D18866. Ref T13025. Fixes T12415. This makes the old "Add Comment" action work, and adds support for a new "Set description to" action (possibly, I could imagine "append description" being useful some day, maybe).
The implementation is just a `<textarea />`, not a whole fancy remarkup box with `[Bold] [Italic] ...` buttons, preview, typeaheads, etc. It would be nice to enrich this eventually but doing the rendering in pure JS is currently very involved.
This requires a little bit of gymnastics to get the transaction populated properly, and adds some extra validation since we need some code there anyway.
Test Plan:
- Changed the description of a task via bulk editor.
- Added a comment to a task via bulk editor.
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T13025, T12415
Differential Revision: https://secure.phabricator.com/D18867
2018-01-11 18:48:33 +01:00
|
|
|
get: function() {
|
|
|
|
return node.value;
|
|
|
|
},
|
|
|
|
set: function(value) {
|
|
|
|
node.value = value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
_newRemarkup: function(spec) {
|
|
|
|
var attrs = {};
|
|
|
|
|
|
|
|
// We could imagine a world where this renders a full remarkup control
|
|
|
|
// with all the hint buttons and client behaviors, but today much of that
|
|
|
|
// behavior is defined server-side and thus this isn't a world we
|
|
|
|
// currently live in.
|
|
|
|
|
|
|
|
var node = JX.$N('textarea', attrs);
|
|
|
|
node.value = spec.value || '';
|
|
|
|
|
|
|
|
return {
|
|
|
|
node: node,
|
2016-04-06 14:59:09 +02:00
|
|
|
get: function() {
|
|
|
|
return node.value;
|
|
|
|
},
|
|
|
|
set: function(value) {
|
|
|
|
node.value = value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
_newOptgroups: function(spec) {
|
|
|
|
var value = spec.value || null;
|
|
|
|
|
|
|
|
var optgroups = [];
|
|
|
|
for (var ii = 0; ii < spec.groups.length; ii++) {
|
|
|
|
var group = spec.groups[ii];
|
|
|
|
var options = [];
|
|
|
|
for (var jj = 0; jj < group.options.length; jj++) {
|
|
|
|
var option = group.options[jj];
|
|
|
|
options.push(JX.$N('option', {value: option.key}, option.label));
|
|
|
|
|
|
|
|
if (option.selected && (value === null)) {
|
|
|
|
value = option.key;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
optgroups.push(JX.$N('optgroup', {label: group.label}, options));
|
|
|
|
}
|
|
|
|
|
|
|
|
var node = JX.$N('select', {}, optgroups);
|
|
|
|
node.value = value;
|
|
|
|
|
2015-12-04 16:56:03 +01:00
|
|
|
return {
|
|
|
|
node: node,
|
|
|
|
get: function() {
|
|
|
|
return node.value;
|
|
|
|
},
|
|
|
|
set: function(value) {
|
|
|
|
node.value = value;
|
|
|
|
}
|
|
|
|
};
|
2015-12-02 23:38:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|