Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up some JS, remove use of YAHOO.lang #152

Merged
merged 9 commits into from
Oct 22, 2024
171 changes: 79 additions & 92 deletions src/main/resources/hudson/security/table.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,47 @@
/* global Behaviour, dialog, FormChecker, findElementsBySelector, findAncestor */

function matrixAuthEscapeHtml(html) {
return html.replace(/'/g, "&apos;").replace(/"/g, "&quot;").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

/*
* This handles the addition of new users/groups to the list.
*/
Behaviour.specify(".matrix-auth-add-button", 'GlobalMatrixAuthorizationStrategy', 0, function(e) {
e.onclick = function (e) {
var dataReference = e.target;
var master = document.getElementById(dataReference.getAttribute('data-table-id'));
var table = master.parentNode;
var type = dataReference.getAttribute('data-type');
var typeLabel = dataReference.getAttribute('data-type-label');
const dataReference = e.target;
const master = document.getElementById(dataReference.getAttribute('data-table-id'));
const table = master.parentNode;
const type = dataReference.getAttribute('data-type');
const typeLabel = dataReference.getAttribute('data-type-label');

var name = dialog.prompt(dataReference.getAttribute('data-message-title'), {
dialog.prompt(dataReference.getAttribute('data-message-title'), {
message: dataReference.getAttribute('data-message-prompt')
}).then ((name) => {
if(findElementsBySelector(table,"TR").find(function(n){return n.getAttribute("name")=='['+type+':'+name+']';})!=null) {
if (findElementsBySelector(table, "TR").find(function(n){
return n.getAttribute("name") === '[' + type + ':' + name + ']';
}) != null) {
dialog.alert(dataReference.getAttribute('data-message-error'));
return;
}

if(document.importNode!=null)
copy = document.importNode(master,true);
else
copy = master.cloneNode(true); // for IE
const copy = document.importNode(master, true);
copy.removeAttribute("id");
copy.removeAttribute("style");
copy.firstChild.innerHTML = YAHOO.lang.escapeHTML(name); // TODO consider setting innerText
copy.setAttribute("name",'['+type+':'+name+']');
copy.firstChild.innerHTML = matrixAuthEscapeHtml(name); // TODO consider setting innerText
copy.setAttribute("name", '[' + type + ':' + name + ']');

for(var child = copy.firstChild; child !== null; child = child.nextSibling) {
for (let child = copy.firstChild; child !== null; child = child.nextSibling) {
if (child.hasAttribute('data-permission-id')) {
child.setAttribute("data-tooltip-enabled", child.getAttribute("data-tooltip-enabled").replace("__SID__", name).replace("__TYPE__", typeLabel));
child.setAttribute("data-tooltip-disabled", child.getAttribute("data-tooltip-disabled").replace("__SID__", name).replace("__TYPE__", typeLabel));
}
}

var tooltipAttributeName = getTooltipAttributeName();
const tooltipAttributeName = 'data-html-tooltip';

findElementsBySelector(copy, ".stop a").forEach(function(item) {
// TODO Clean this up, `title` should be long obsolete.
let oldTitle = item.getAttribute("title");
if (oldTitle !== null) {
item.setAttribute("title", oldTitle.replace("__SID__", name).replace("__TYPE__", typeLabel));
Expand All @@ -44,15 +50,16 @@ Behaviour.specify(".matrix-auth-add-button", 'GlobalMatrixAuthorizationStrategy'
});

findElementsBySelector(copy, "input[type=checkbox]").forEach(function(item) {
const tooltip = item.getAttribute(tooltipAttributeName);
const tooltip = item.nextSibling.getAttribute(tooltipAttributeName);
if (tooltip) {
item.setAttribute(tooltipAttributeName, tooltip.replace("__SID__", name).replace("__TYPE__", typeLabel));
item.nextSibling.setAttribute(tooltipAttributeName, tooltip.replace("__SID__", name).replace("__TYPE__", typeLabel));
} else {
item.setAttribute("title", item.getAttribute("title").replace("__SID__", name).replace("__TYPE__", typeLabel));
// TODO Clean this up, `title` should be long obsolete.
item.nextSibling.setAttribute("title", item.getAttribute("title").replace("__SID__", name).replace("__TYPE__", typeLabel));
}
});
table.appendChild(copy);
Behaviour.applySubtree(findAncestor(table,"TABLE"),true);
Behaviour.applySubtree(findAncestor(table, "TABLE"), true);
}, () => {});
}
});
Expand All @@ -64,23 +71,23 @@ Behaviour.specify(".global-matrix-authorization-strategy-table TD.stop A.remove"
e.onclick = function() {
// Run ambiguity warning removal code: If all ambiguous rows are deleted, the warning needs to go as well
// Order of operations: Find table ancestor, remove row, iterate over leftover rows
var table = findAncestor(this,"TABLE");
const table = findAncestor(this, "TABLE");

var tr = findAncestor(this,"TR");
const tr = findAncestor(this, "TR");
tr.parentNode.removeChild(tr);

var tableRows = table.getElementsByTagName('tr');
const tableRows = table.getElementsByTagName('tr');

var hasAmbiguousRows = false;
let hasAmbiguousRows = false;

for (var i = 0; i < tableRows.length; i++) {
for (let i = 0; i < tableRows.length; i++) {
if (tableRows[i].getAttribute('name') !== null && tableRows[i].getAttribute('name').startsWith('[EITHER')) {
hasAmbiguousRows = true;
}
}
if (!hasAmbiguousRows) {
var alertElements = document.getElementsByClassName("alert");
for (var i = 0; i < alertElements.length; i++) {
const alertElements = document.getElementsByClassName("alert");
for (let i = 0; i < alertElements.length; i++) {
if (alertElements[i].hasAttribute('data-table-id') && alertElements[i].getAttribute('data-table-id') === table.getAttribute('data-table-id')) {
alertElements[i].style.display = 'none'; // TODO animate this?
}
Expand All @@ -89,58 +96,59 @@ Behaviour.specify(".global-matrix-authorization-strategy-table TD.stop A.remove"

return false;
}
e = null; // avoid memory leak
});

/*
* Behavior for 'Select all' element that exists for each row of permissions checkboxes
*/
Behaviour.specify(".global-matrix-authorization-strategy-table TD.stop A.selectall", 'GlobalMatrixAuthorizationStrategy', 0, function(e) {
e.onclick = function() {
var tr = findAncestor(this,"TR");
var inputs = tr.getElementsByTagName("INPUT");
for(var i=0; i < inputs.length; i++){
if(inputs[i].type == "checkbox") inputs[i].checked = true;
const tr = findAncestor(this, "TR");
const inputs = tr.getElementsByTagName("INPUT");
for (let i=0; i < inputs.length; i++) {
if (inputs[i].type === "checkbox") {
inputs[i].checked = true;
}
}
Behaviour.applySubtree(findAncestor(this,"TABLE"),true);
Behaviour.applySubtree(findAncestor(this, "TABLE"), true);
return false;
};
e = null; // avoid memory leak
});

/*
* Behavior for 'Unselect all' element that exists for each row of permissions checkboxes
*/
Behaviour.specify(".global-matrix-authorization-strategy-table TD.stop A.unselectall", 'GlobalMatrixAuthorizationStrategy', 0, function(e) {
e.onclick = function() {
var tr = findAncestor(this,"TR");
var inputs = tr.getElementsByTagName("INPUT");
for(var i=0; i < inputs.length; i++){
if(inputs[i].type == "checkbox") inputs[i].checked = false;
const tr = findAncestor(this, "TR");
const inputs = tr.getElementsByTagName("INPUT");
for (let i = 0; i < inputs.length; i++) {
if (inputs[i].type === "checkbox") {
inputs[i].checked = false;
}
}
Behaviour.applySubtree(findAncestor(this,"TABLE"),true);
Behaviour.applySubtree(findAncestor(this, "TABLE"), true);
return false;
};
e = null; // avoid memory leak
});

/*
* Behavior for 'Migrate to user' element that exists for each ambiguous row
*/
Behaviour.specify(".global-matrix-authorization-strategy-table TD.stop A.migrate", 'GlobalMatrixAuthorizationStrategy', 0, function(e) {
e.onclick = function() {
var tr = findAncestor(this,"TR");
var name = tr.getAttribute('name');
const tr = findAncestor(this, "TR");
const name = tr.getAttribute('name');

var newName = name.replace('[EITHER:', '[USER:'); // migrate_user behavior
let newName = name.replace('[EITHER:', '[USER:'); // migrate_user behavior
if (this.classList.contains('migrate_group')) {
newName = name.replace('[EITHER:', '[GROUP:');
}

var table = findAncestor(this,"TABLE");
var tableRows = table.getElementsByTagName('tr');
var newNameElement = null;
for (var i = 0; i < tableRows.length; i++) {
const table = findAncestor(this, "TABLE");
const tableRows = table.getElementsByTagName('tr');
let newNameElement = null;
for (let i = 0; i < tableRows.length; i++) {
if (tableRows[i].getAttribute('name') === newName) {
newNameElement = tableRows[i];
break;
Expand All @@ -156,19 +164,19 @@ Behaviour.specify(".global-matrix-authorization-strategy-table TD.stop A.migrate
tr.removeAttribute('data-checked');

// remove migration buttons from updated row
var buttonContainer = findAncestor(this, "DIV");
var migrateButtons = buttonContainer.getElementsByClassName('migrate');
for (var i = migrateButtons.length - 1; i >= 0; i--) {
const buttonContainer = findAncestor(this, "DIV");
const migrateButtons = buttonContainer.getElementsByClassName('migrate');
for (let i = migrateButtons.length - 1; i >= 0; i--) {
buttonContainer.removeChild(migrateButtons[i]);
}
} else {
// there's already a row for the migrated name (unusual but OK), so merge them

// migrate permissions from this row
var ambiguousPermissionInputs = tr.getElementsByTagName("INPUT");
var unambiguousPermissionInputs = newNameElement.getElementsByTagName("INPUT");
for (var i = 0; i < ambiguousPermissionInputs.length; i++){
if(ambiguousPermissionInputs[i].type == "checkbox") {
const ambiguousPermissionInputs = tr.getElementsByTagName("INPUT");
const unambiguousPermissionInputs = newNameElement.getElementsByTagName("INPUT");
for (let i = 0; i < ambiguousPermissionInputs.length; i++){
if (ambiguousPermissionInputs[i].type === "checkbox") {
unambiguousPermissionInputs[i].checked |= ambiguousPermissionInputs[i].checked;
}
newNameElement.classList.add('highlight-entry');
Expand All @@ -179,16 +187,16 @@ Behaviour.specify(".global-matrix-authorization-strategy-table TD.stop A.migrate
}
Behaviour.applySubtree(table, true);

var hasAmbiguousRows = false;
let hasAmbiguousRows = false;

for (var i = 0; i < tableRows.length; i++) {
for (let i = 0; i < tableRows.length; i++) {
if (tableRows[i].getAttribute('name') !== null && tableRows[i].getAttribute('name').startsWith('[EITHER')) {
hasAmbiguousRows = true;
}
}
if (!hasAmbiguousRows) {
var alertElements = document.getElementsByClassName("alert");
for (var i = 0; i < alertElements.length; i++) {
let alertElements = document.getElementsByClassName("alert");
for (let i = 0; i < alertElements.length; i++) {
if (alertElements[i].hasAttribute('data-table-id') && alertElements[i].getAttribute('data-table-id') === table.getAttribute('data-table-id')) {
alertElements[i].style.display = 'none'; // TODO animate this?
}
Expand All @@ -197,64 +205,43 @@ Behaviour.specify(".global-matrix-authorization-strategy-table TD.stop A.migrate

return false;
};
e = null; // avoid memory leak
});

/*
* Determine which attribute to set tooltips in. Changed in Jenkins 2.379 with Tippy and data-html-tooltip support.
*/
function getTooltipAttributeName() {
let coreVersion = document.body.getAttribute('data-version');
if (coreVersion === null) {
return 'tooltip'
}
// TODO remove after minimum version is 2.379 or higher
let tippySupported = coreVersion >= '2.379';
return tippySupported ? 'data-html-tooltip' : 'tooltip';
}

/*
* Whenever permission assignments change, this ensures that implied permissions get their checkboxes disabled.
*/
Behaviour.specify(".global-matrix-authorization-strategy-table td input", 'GlobalMatrixAuthorizationStrategy', 0, function(e) {
var table = findAncestor(e, "TABLE");
const table = findAncestor(e, "TABLE");
if (table.classList.contains('read-only')) {
// if this is a read-only UI (ExtendedRead / SystemRead), do not enable checkboxes
return;
}

var tooltipAttributeName = getTooltipAttributeName();
const tooltipAttributeName = 'data-html-tooltip';

var impliedByString = findAncestor(e, "TD").getAttribute('data-implied-by-list');
var impliedByList = impliedByString.split(" ");
var tr = findAncestor(e,"TR");
const impliedByString = findAncestor(e, "TD").getAttribute('data-implied-by-list');
const impliedByList = impliedByString.split(" ");
const tr = findAncestor(e, "TR");
e.disabled = false;
let tooltip = YAHOO.lang.escapeHTML(findAncestor(e, "TD").getAttribute('data-tooltip-enabled'));
e.setAttribute(tooltipAttributeName, tooltip); // before 2.335 -- TODO remove once baseline is new enough
daniel-beck marked this conversation as resolved.
Show resolved Hide resolved
e.nextSibling.setAttribute(tooltipAttributeName, tooltip); // 2.335+
let tooltip = matrixAuthEscapeHtml(findAncestor(e, "TD").getAttribute('data-tooltip-enabled'));
e.nextSibling.setAttribute(tooltipAttributeName, tooltip);

for (var i = 0; i < impliedByList.length; i++) {
var permissionId = impliedByList[i];
var reference = tr.querySelector("td[data-permission-id='" + permissionId + "'] input");
for (let i = 0; i < impliedByList.length; i++) {
let permissionId = impliedByList[i];
let reference = tr.querySelector("td[data-permission-id='" + permissionId + "'] input");
if (reference !== null) {
if (reference.checked) {
e.disabled = true;
let tooltip = YAHOO.lang.escapeHTML(findAncestor(e, "TD").getAttribute('data-tooltip-disabled'));
e.setAttribute(tooltipAttributeName, tooltip); // before 2.335 -- TODO remove once baseline is new enough
e.nextSibling.setAttribute(tooltipAttributeName, tooltip); // 2.335+
let tooltip = matrixAuthEscapeHtml(findAncestor(e, "TD").getAttribute('data-tooltip-disabled'));
e.nextSibling.setAttribute(tooltipAttributeName, tooltip);
}
}
}

if (window.registerTooltips) {
window.registerTooltips(e.nextSibling.parentElement);
}

e.onchange = function() {
Behaviour.applySubtree(findAncestor(this,"TABLE"),true);
Behaviour.applySubtree(findAncestor(this, "TABLE"), true);
return true;
};
e = null; // avoid memory leak
});

/*
Expand All @@ -265,7 +252,7 @@ Behaviour.specify(".global-matrix-authorization-strategy-table TR.permission-row
return;
}
if (!e.hasAttribute('data-checked')) {
FormChecker.delayedCheck(e.getAttribute('data-descriptor-url') + "/checkName?value="+encodeURIComponent(e.getAttribute("name")),"GET",e.firstChild);
FormChecker.delayedCheck(e.getAttribute('data-descriptor-url') + "/checkName?value=" + encodeURIComponent(e.getAttribute("name")), "GET", e.firstChild);
e.setAttribute('data-checked', 'true');
}
});