1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-10 17:02:41 +01:00
phorge-phorge/webroot/rsrc/js/application/herald/HeraldRuleEditor.js

373 lines
11 KiB
JavaScript
Raw Normal View History

2011-03-23 01:08:08 +01:00
/**
* @requires multirow-row-manager
* javelin-install
* javelin-util
* javelin-dom
* javelin-stratcom
* javelin-json
* phabricator-prefab
2011-03-23 01:08:08 +01:00
* @provides herald-rule-editor
* @javelin
*/
JX.install('HeraldRuleEditor', {
construct : function(config) {
var root = JX.$(config.root);
this._root = root;
JX.DOM.listen(
root,
'click',
'create-condition',
JX.bind(this, this._onnewcondition));
JX.DOM.listen(
root,
'click',
'create-action',
JX.bind(this, this._onnewaction));
JX.DOM.listen(root, 'change', null, JX.bind(this, this._onchange));
JX.DOM.listen(root, 'submit', null, JX.bind(this, this._onsubmit));
var conditionsTable = JX.DOM.find(root, 'table', 'rule-conditions');
var actionsTable = JX.DOM.find(root, 'table', 'rule-actions');
this._conditionsRowManager = new JX.MultirowRowManager(conditionsTable);
this._conditionsRowManager.listen(
'row-removed',
JX.bind(this, function(row_id) {
delete this._config.conditions[row_id];
}));
this._actionsRowManager = new JX.MultirowRowManager(actionsTable);
this._actionsRowManager.listen(
'row-removed',
JX.bind(this, function(row_id) {
delete this._config.actions[row_id];
}));
this._conditionGetters = {};
this._conditionTypes = {};
this._actionGetters = {};
this._actionTypes = {};
this._config = config;
var conditions = this._config.conditions;
this._config.conditions = [];
var actions = this._config.actions;
this._config.actions = [];
this._renderConditions(conditions);
this._renderActions(actions);
},
members : {
_config : null,
_root : null,
_conditionGetters : null,
_conditionTypes : null,
_actionGetters : null,
_actionTypes : null,
_conditionsRowManager : null,
_actionsRowManager : null,
_onnewcondition : function(e) {
this._newCondition();
e.kill();
},
_onnewaction : function(e) {
this._newAction();
e.kill();
},
_onchange : function(e) {
var target = e.getTarget();
var row = e.getNode(JX.MultirowRowManager.getRowSigil());
if (!row) {
// Changing the "when all of / any of these..." dropdown.
return;
}
if (JX.Stratcom.hasSigil(target, 'field-select')) {
this._onfieldchange(row);
} else if (JX.Stratcom.hasSigil(target, 'condition-select')) {
this._onconditionchange(row);
} else if (JX.Stratcom.hasSigil(target, 'action-select')) {
this._onactionchange(row);
}
},
_onsubmit : function() {
2011-03-23 01:08:08 +01:00
var rule = JX.DOM.find(this._root, 'input', 'rule');
var k;
for (k in this._config.conditions) {
this._config.conditions[k][2] = this._getConditionValue(k);
}
for (k in this._config.actions) {
this._config.actions[k][1] = this._getActionTarget(k);
}
2011-07-05 23:17:38 +02:00
rule.value = JX.JSON.stringify({
2011-03-23 01:08:08 +01:00
conditions: this._config.conditions,
actions: this._config.actions
});
},
_getConditionValue : function(id) {
if (this._conditionGetters[id]) {
return this._conditionGetters[id]();
}
return this._config.conditions[id][2];
},
_getActionTarget : function(id) {
if (this._actionGetters[id]) {
return this._actionGetters[id]();
}
return this._config.actions[id][1];
},
_onactionchange : function(r) {
var target = JX.DOM.find(r, 'select', 'action-select');
var row_id = this._actionsRowManager.getRowID(r);
this._config.actions[row_id][0] = target.value;
var target_cell = JX.DOM.find(r, 'td', 'target-cell');
var target_input = this._renderTargetInputForRow(row_id);
JX.DOM.setContent(target_cell, target_input);
},
_onfieldchange : function(r) {
var target = JX.DOM.find(r, 'select', 'field-select');
var row_id = this._actionsRowManager.getRowID(r);
this._config.conditions[row_id][0] = target.value;
var condition_cell = JX.DOM.find(r, 'td', 'condition-cell');
var condition_select = this._renderSelect(
this._selectKeys(
this._config.info.conditions,
this._config.info.conditionMap[target.value]),
this._config.conditions[row_id][1],
'condition-select');
JX.DOM.setContent(condition_cell, condition_select);
this._onconditionchange(r);
var condition_name = this._config.conditions[row_id][1];
if (condition_name == 'unconditionally') {
JX.DOM.hide(condition_select);
}
2011-03-23 01:08:08 +01:00
},
_onconditionchange : function(r) {
var target = JX.DOM.find(r, 'select', 'condition-select');
var row_id = this._conditionsRowManager.getRowID(r);
this._config.conditions[row_id][1] = target.value;
var value_cell = JX.DOM.find(r, 'td', 'value-cell');
var value_input = this._renderValueInputForRow(row_id);
JX.DOM.setContent(value_cell, value_input);
},
_renderTargetInputForRow : function(row_id) {
var action = this._config.actions[row_id];
var type = this._config.info.targets[action[0]];
var input = this._buildInput(type);
var node = input[0];
var get_fn = input[1];
var set_fn = input[2];
if (node) {
JX.Stratcom.addSigil(node, 'action-target');
}
var old_type = this._actionTypes[row_id];
if (old_type == type || !old_type) {
set_fn(this._getActionTarget(row_id));
}
this._actionTypes[row_id] = type;
this._actionGetters[row_id] = get_fn;
return node;
},
_buildInput : function(type) {
var input;
var get_fn;
var set_fn;
switch (type) {
case 'rule':
input = this._renderSelect(this._config.template.rules);
get_fn = function() { return input.value; };
set_fn = function(v) { input.value = v; };
break;
case 'email':
2011-03-23 04:41:02 +01:00
case 'user':
2011-03-23 01:08:08 +01:00
case 'repository':
case 'tag':
case 'package':
Allow Herald to trigger audits for users or projects Summary: Allows you to write a commit rule that triggers an audit by a user (personal rules) or a project (global rules). Mostly this is trying to make auditing more lightweight and accessible in environments where setting up Owners packages doesn't make sense. For instance, Disqus wants a rule like "trigger an audit for everything that didn't have a Differential revision". While not necessarily scalable, this is a perfectly reasonable rule for a small company, but a lot of work to implement with Owners (and you'll get a lot of collateral damage if you don't make every committer a project owner). Instead, they can create a project called 'Unreviewed Commits' and write a rule like: - When: Differential revision does not exist - Action: Trigger an Audit for project: "Unreviewed Commits" Then whoever cares can join that project and they'll see those audits in their queue, and when they approve/raise on commits their actions will affect the project audit. Similarly, if I want to look at all commits that match some other rule (say, XSS) but only want to do it like once a month, I can just set up an audit rule and go through the queue when I feel like it. NOTE: This abuses the 'packagePHID' field to also store user and project PHIDs. Through the magic of handles, this (apparently) works fine for now; I'll do a big schema patch soon but have several other edits I want to make at the same time. Also: - Adds an "active" fiew for /audit/, eventually this will be like the Differential "active" view (stuff that is relevant to you right now). - On commits, highlight triggered audits you are responsible for. Test Plan: Added personal and global audit triggers to Herald, reparsed some commits with --herald, got audits. Browsed all audit interfaces to make sure nothing exploded. Viewed a commit where I was responsible for only some audits. Performed audits and made sure the triggers I am supposed to be responsible for updated properly. Reviewers: btrahan, jungejason Reviewed By: jungejason CC: aran, epriestley Maniphest Tasks: T904 Differential Revision: https://secure.phabricator.com/D1690
2012-02-27 18:36:30 +01:00
case 'project':
case 'userorproject':
case 'buildplan':
case 'taskpriority':
case 'taskstatus':
Allow Herald to "Require legal signatures" for reviews Summary: Ref T3116. Add a Herald action "Require legal signatures" which requires revision authors to accept legal agreements before their revisions can be accepted. - Herald will check which documents the author has signed, and trigger a "you have to sign X, Y, Z" for other documents. - If the author has already signed everything, we don't spam the revision -- basically, this only triggers when signatures are missing. - The UI will show which documents must be signed and warn that the revision can't be accepted until they're completed. - Users aren't allowed to "Accept" the revision until documents are cleared. Fixes T1157. The original install making the request (Hive) no longer uses Phabricator, and this satisfies our requirements. Test Plan: - Added a Herald rule. - Created a revision, saw the rule trigger. - Viewed as author and non-author, saw field UI (generic for non-author, specific for author), transaction UI, and accept-warning UI. - Tried to accept revision. - Signed document, saw UI update. Note that signatures don't currently //push// an update to the revision, but could eventually (like blocking tasks work). - Accepted revision. - Created another revision, saw rules not add the document (since it's already signed, this is the "no spam" case). Reviewers: btrahan, chad Reviewed By: chad Subscribers: asherkin, epriestley Maniphest Tasks: T1157, T3116 Differential Revision: https://secure.phabricator.com/D9771
2014-06-29 16:53:53 +02:00
case 'legaldocuments':
case 'applicationemail':
2011-03-23 01:08:08 +01:00
var tokenizer = this._newTokenizer(type);
input = tokenizer[0];
get_fn = tokenizer[1];
set_fn = tokenizer[2];
break;
case 'none':
input = '';
get_fn = JX.bag;
set_fn = JX.bag;
break;
case 'contentsource':
case 'flagcolor':
Add Herald support for blocking ref changes Summary: Ref T4195. Allows users to write Herald rules which block ref changes. For example, you can write a rule like `alincoln can not create branches`, or `no one can push to the branch "frozen"`. Test Plan: This covers a lot of ground. I created and pushed a bunch of rules, then looked at transcripts, in general. Here are some bits in detail: Here's a hook-based reject message: >>> orbital ~/repos/POEMS $ git push Counting objects: 5, done. Delta compression using up to 8 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 274 bytes, done. Total 3 (delta 2), reused 0 (delta 0) remote: +---------------------------------------------------------------+ remote: | * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * | remote: +---------------------------------------------------------------+ remote: \ remote: \ ^ /^ remote: \ / \ // \ remote: \ |\___/| / \// .\ remote: \ /V V \__ / // | \ \ *----* remote: / / \/_/ // | \ \ \ | remote: @___@` \/_ // | \ \ \/\ \ remote: 0/0/| \/_ // | \ \ \ \ remote: 0/0/0/0/| \/// | \ \ | | remote: 0/0/0/0/0/_|_ / ( // | \ _\ | / remote: 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / / remote: ,-} _ *-.|.-~-. .~ ~ remote: \ \__/ `/\ / ~-. _ .-~ / remote: \____(Oo) *. } { / remote: ( (--) .----~-.\ \-` .~ remote: //__\\ \ DENIED! ///.----..< \ _ -~ remote: // \\ ///-._ _ _ _ _ _ _{^ - - - - ~ remote: remote: remote: This commit was rejected by Herald pre-commit rule H24. remote: Rule: No Branches Called Blarp remote: Reason: "blarp" is a bad branch name remote: To ssh://dweller@localhost/diffusion/POEMS/ ! [remote rejected] blarp -> blarp (pre-receive hook declined) error: failed to push some refs to 'ssh://dweller@localhost/diffusion/POEMS/' Here's a transcript, showing that all the field values populate sensibly: {F90453} Here's a rule: {F90454} Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T4195 Differential Revision: https://secure.phabricator.com/D7782
2013-12-18 00:23:55 +01:00
case 'value-ref-type':
case 'value-ref-change':
input = this._renderSelect(this._config.select[type].options);
get_fn = function() { return input.value; };
set_fn = function(v) { input.value = v; };
Add Herald support for blocking ref changes Summary: Ref T4195. Allows users to write Herald rules which block ref changes. For example, you can write a rule like `alincoln can not create branches`, or `no one can push to the branch "frozen"`. Test Plan: This covers a lot of ground. I created and pushed a bunch of rules, then looked at transcripts, in general. Here are some bits in detail: Here's a hook-based reject message: >>> orbital ~/repos/POEMS $ git push Counting objects: 5, done. Delta compression using up to 8 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 274 bytes, done. Total 3 (delta 2), reused 0 (delta 0) remote: +---------------------------------------------------------------+ remote: | * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * | remote: +---------------------------------------------------------------+ remote: \ remote: \ ^ /^ remote: \ / \ // \ remote: \ |\___/| / \// .\ remote: \ /V V \__ / // | \ \ *----* remote: / / \/_/ // | \ \ \ | remote: @___@` \/_ // | \ \ \/\ \ remote: 0/0/| \/_ // | \ \ \ \ remote: 0/0/0/0/| \/// | \ \ | | remote: 0/0/0/0/0/_|_ / ( // | \ _\ | / remote: 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / / remote: ,-} _ *-.|.-~-. .~ ~ remote: \ \__/ `/\ / ~-. _ .-~ / remote: \____(Oo) *. } { / remote: ( (--) .----~-.\ \-` .~ remote: //__\\ \ DENIED! ///.----..< \ _ -~ remote: // \\ ///-._ _ _ _ _ _ _{^ - - - - ~ remote: remote: remote: This commit was rejected by Herald pre-commit rule H24. remote: Rule: No Branches Called Blarp remote: Reason: "blarp" is a bad branch name remote: To ssh://dweller@localhost/diffusion/POEMS/ ! [remote rejected] blarp -> blarp (pre-receive hook declined) error: failed to push some refs to 'ssh://dweller@localhost/diffusion/POEMS/' Here's a transcript, showing that all the field values populate sensibly: {F90453} Here's a rule: {F90454} Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T4195 Differential Revision: https://secure.phabricator.com/D7782
2013-12-18 00:23:55 +01:00
set_fn(this._config.select[type]['default']);
break;
2011-03-23 01:08:08 +01:00
default:
input = JX.$N('input', {type: 'text'});
2011-03-23 01:08:08 +01:00
get_fn = function() { return input.value; };
set_fn = function(v) { input.value = v; };
break;
}
return [input, get_fn, set_fn];
},
_renderValueInputForRow : function(row_id) {
var cond = this._config.conditions[row_id];
var type = this._config.info.values[cond[0]][cond[1]];
var input = this._buildInput(type);
var node = input[0];
var get_fn = input[1];
var set_fn = input[2];
if (node) {
JX.Stratcom.addSigil(node, 'condition-value');
}
var old_type = this._conditionTypes[row_id];
if (old_type == type || !old_type) {
set_fn(this._getConditionValue(row_id));
}
this._conditionTypes[row_id] = type;
this._conditionGetters[row_id] = get_fn;
return node;
},
_newTokenizer : function(type) {
var tokenizerConfig = {
src : this._config.template.source[type].uri,
placeholder: this._config.template.source[type].placeholder,
browseURI: this._config.template.source[type].browseURI,
icons : this._config.template.icons,
username : this._config.username
};
2011-03-23 01:08:08 +01:00
var build = JX.Prefab.newTokenizerFromTemplate(
this._config.template.markup,
tokenizerConfig);
build.tokenizer.start();
2011-03-23 01:08:08 +01:00
return [
build.node,
2011-03-23 01:08:08 +01:00
function() {
return build.tokenizer.getTokens();
2011-03-23 01:08:08 +01:00
},
function(map) {
for (var k in map) {
build.tokenizer.addToken(k, map[k]);
2011-03-23 01:08:08 +01:00
}
}];
},
_selectKeys : function(map, keys) {
var r = {};
for (var ii = 0; ii < keys.length; ii++) {
r[keys[ii]] = map[keys[ii]];
}
return r;
},
_renderConditions : function(conditions) {
for (var k in conditions) {
this._newCondition(conditions[k]);
}
},
_newCondition : function(data) {
var row = this._conditionsRowManager.addRow([]);
var row_id = this._conditionsRowManager.getRowID(row);
this._config.conditions[row_id] = data || [null, null, ''];
var r = this._conditionsRowManager.updateRow(
row_id,
this._renderCondition(row_id));
this._onfieldchange(r);
},
_renderCondition : function(row_id) {
var field_select = this._renderSelect(
this._config.info.fields,
this._config.conditions[row_id][0],
'field-select');
var field_cell = JX.$N('td', {sigil: 'field-cell'}, field_select);
var condition_cell = JX.$N('td', {sigil: 'condition-cell'});
var value_cell = JX.$N('td', {className : 'value', sigil: 'value-cell'});
return [field_cell, condition_cell, value_cell];
},
_renderActions : function(actions) {
for (var k in actions) {
this._newAction(actions[k]);
delete actions[k];
}
},
_newAction : function(data) {
data = data || [];
var temprow = this._actionsRowManager.addRow([]);
var row_id = this._actionsRowManager.getRowID(temprow);
this._config.actions[row_id] = data;
var r = this._actionsRowManager.updateRow(row_id,
this._renderAction(data));
this._onactionchange(r);
},
_renderAction : function(action) {
var action_select = this._renderSelect(
this._config.info.actions,
action[0],
'action-select');
var action_cell = JX.$N('td', {sigil: 'action-cell'}, action_select);
var target_cell = JX.$N(
'td',
{className : 'target', sigil : 'target-cell'});
return [action_cell, target_cell];
},
_renderSelect : function(map, selected, sigil) {
var attrs = {
sigil : sigil
};
return JX.Prefab.renderSelect(map, selected, attrs);
2011-03-23 01:08:08 +01:00
}
}
2011-03-23 01:19:52 +01:00
});