Merge pull request #4797 from GogoVega/config-node-history

Add config node to `RED.history` and handle `changed` prop
This commit is contained in:
Nick O'Leary 2024-12-03 14:48:23 +00:00 committed by GitHub
commit 93102837dd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -808,6 +808,17 @@ RED.editor = (function() {
} }
} }
const oldCreds = {};
if (editing_node._def.credentials) {
for (const prop in editing_node._def.credentials) {
if (Object.prototype.hasOwnProperty.call(editing_node._def.credentials, prop)) {
if (prop in editing_node.credentials) {
oldCreds[prop] = editing_node.credentials[prop];
}
}
}
}
try { try {
const rc = editing_node._def.oneditsave.call(editing_node); const rc = editing_node._def.oneditsave.call(editing_node);
if (rc === true) { if (rc === true) {
@ -839,6 +850,21 @@ RED.editor = (function() {
} }
} }
} }
if (editing_node._def.credentials) {
for (const prop in editing_node._def.credentials) {
if (Object.prototype.hasOwnProperty.call(editing_node._def.credentials, prop)) {
if (oldCreds[prop] !== editing_node.credentials[prop]) {
if (editing_node.credentials[prop] === '__PWRD__') {
continue;
}
editState.changes.credentials = editState.changes.credentials || {};
editState.changes.credentials[prop] = oldCreds[prop];
editState.changed = true;
}
}
}
}
} }
} }
@ -1481,134 +1507,181 @@ RED.editor = (function() {
}, },
{ {
id: "node-config-dialog-ok", id: "node-config-dialog-ok",
text: adding?RED._("editor.configAdd"):RED._("editor.configUpdate"), text: adding ? RED._("editor.configAdd") : RED._("editor.configUpdate"),
class: "primary", class: "primary",
click: function() { click: function() {
var editState = { // TODO: Already defined
const configProperty = name;
const configType = type;
const configTypeDef = RED.nodes.getType(configType);
const wasChanged = editing_config_node.changed;
const editState = {
changes: {}, changes: {},
changed: false, changed: false,
outputMap: null outputMap: null
}; };
var configProperty = name;
var configId = editing_config_node.id; // Call `oneditsave` and search for changes
var configType = type; handleEditSave(editing_config_node, editState);
var configAdding = adding;
var configTypeDef = RED.nodes.getType(configType);
var d;
var input;
if (configTypeDef.oneditsave) { // Search for changes in the edit box (panes)
try { activeEditPanes.forEach(function (pane) {
configTypeDef.oneditsave.call(editing_config_node);
} catch(err) {
console.warn("oneditsave",editing_config_node.id,editing_config_node.type,err.toString());
}
}
for (d in configTypeDef.defaults) {
if (configTypeDef.defaults.hasOwnProperty(d)) {
var newValue;
input = $("#node-config-input-"+d);
if (input.attr('type') === "checkbox") {
newValue = input.prop('checked');
} else if ("format" in configTypeDef.defaults[d] && configTypeDef.defaults[d].format !== "" && input[0].nodeName === "DIV") {
newValue = input.text();
} else {
newValue = input.val();
}
if (newValue != null && newValue !== editing_config_node[d]) {
if (editing_config_node._def.defaults[d].type) {
if (newValue == "_ADD_") {
newValue = "";
}
// Change to a related config node
var configNode = RED.nodes.node(editing_config_node[d]);
if (configNode) {
var users = configNode.users;
users.splice(users.indexOf(editing_config_node),1);
RED.events.emit("nodes:change",configNode);
}
configNode = RED.nodes.node(newValue);
if (configNode) {
configNode.users.push(editing_config_node);
RED.events.emit("nodes:change",configNode);
}
}
editing_config_node[d] = newValue;
}
}
}
activeEditPanes.forEach(function(pane) {
if (pane.apply) { if (pane.apply) {
pane.apply.call(pane, editState); pane.apply.call(pane, editState);
} }
}) });
editing_config_node.label = configTypeDef.label; // TODO: Why?
editing_config_node.label = configTypeDef.label
var scope = $("#red-ui-editor-config-scope").val();
editing_config_node.z = scope;
// Check if disabled has changed
if ($("#node-config-input-node-disabled").prop('checked')) { if ($("#node-config-input-node-disabled").prop('checked')) {
if (editing_config_node.d !== true) { if (editing_config_node.d !== true) {
editState.changes.d = editing_config_node.d;
editState.changed = true;
editing_config_node.d = true; editing_config_node.d = true;
} }
} else { } else {
if (editing_config_node.d === true) { if (editing_config_node.d === true) {
editState.changes.d = editing_config_node.d;
editState.changed = true;
delete editing_config_node.d; delete editing_config_node.d;
} }
} }
// NOTE: must be undefined if no scope used
const scope = $("#red-ui-editor-config-scope").val() || undefined;
// Check if the scope has changed
if (editing_config_node.z !== scope) {
editState.changes.z = editing_config_node.z;
editState.changed = true;
editing_config_node.z = scope;
}
// Search for nodes that use this config node that are no longer
// in scope, so must be removed
const historyEvents = [];
if (scope) { if (scope) {
// Search for nodes that use this one that are no longer const newUsers = editing_config_node.users.filter(function (node) {
// in scope, so must be removed let keepNode = false;
editing_config_node.users = editing_config_node.users.filter(function(n) { let nodeModified = null;
var keep = true;
for (var d in n._def.defaults) { for (const d in node._def.defaults) {
if (n._def.defaults.hasOwnProperty(d)) { if (node._def.defaults.hasOwnProperty(d)) {
if (n._def.defaults[d].type === editing_config_node.type && if (node._def.defaults[d].type === editing_config_node.type) {
n[d] === editing_config_node.id && if (node[d] === editing_config_node.id) {
n.z !== scope) { if (node.z === editing_config_node.z) {
keep = false; // The node is kept only if at least one property uses
// Remove the reference to this node // this config node in the correct scope.
// and revalidate keepNode = true;
n[d] = null; } else {
n.dirty = true; if (!nodeModified) {
n.changed = true; nodeModified = {
validateNode(n); t: "edit",
node: node,
changes: { [d]: node[d] },
changed: node.changed,
dirty: node.dirty
};
} else {
nodeModified.changes[d] = node[d];
}
// Remove the reference to the config node
node[d] = "";
}
}
} }
} }
} }
return keep;
});
}
if (configAdding) { // Add the node modified to the history
RED.nodes.add(editing_config_node); if (nodeModified) {
} historyEvents.push(nodeModified);
validateNode(editing_config_node);
var validatedNodes = {};
validatedNodes[editing_config_node.id] = true;
var userStack = editing_config_node.users.slice();
while(userStack.length > 0) {
var user = userStack.pop();
if (!validatedNodes[user.id]) {
validatedNodes[user.id] = true;
if (user.users) {
userStack = userStack.concat(user.users);
} }
validateNode(user);
// Mark as changed and revalidate this node
if (!keepNode) {
node.changed = true;
node.dirty = true;
validateNode(node);
RED.events.emit("nodes:change", node);
}
return keepNode;
});
// Check if users are changed
if (editing_config_node.users.length !== newUsers.length) {
editState.changes.users = editing_config_node.users;
editState.changed = true;
editing_config_node.users = newUsers;
} }
} }
RED.nodes.dirty(true);
RED.view.redraw(true); if (editState.changed) {
if (!configAdding) { // Set the congig node as changed
RED.events.emit("editor:save",editing_config_node); editing_config_node.changed = true;
RED.events.emit("nodes:change",editing_config_node);
} }
// Now, validate the config node
validateNode(editing_config_node);
// And validate nodes using this config node too
const validatedNodes = new Set();
const userStack = editing_config_node.users.slice();
validatedNodes.add(editing_config_node.id);
while (userStack.length) {
const node = userStack.pop();
if (!validatedNodes.has(node.id)) {
validatedNodes.add(node.id);
if (node.users) {
userStack.push(...node.users);
}
validateNode(node);
}
}
let historyEvent = {
t: "edit",
node: editing_config_node,
changes: editState.changes,
changed: wasChanged,
dirty: RED.nodes.dirty()
};
if (historyEvents.length) {
// Need a multi events
historyEvent = {
t: "multi",
events: [historyEvent].concat(historyEvents),
dirty: historyEvent.dirty
};
}
if (!adding) {
// This event is triggered when the edit box is saved,
// regardless of whether there are any modifications.
RED.events.emit("editor:save", editing_config_node);
}
if (editState.changed) {
if (adding) {
RED.history.push({ t: "add", nodes: [editing_config_node.id], dirty: RED.nodes.dirty() });
// Add the new config node and trigger the `nodes:add` event
RED.nodes.add(editing_config_node);
} else {
RED.history.push(historyEvent);
RED.events.emit("nodes:change", editing_config_node);
}
RED.nodes.dirty(true);
RED.view.redraw(true);
}
RED.tray.close(function() { RED.tray.close(function() {
var filter = null; var filter = null;
// when editing a config via subflow edit panel, the `configProperty` will not // when editing a config via subflow edit panel, the `configProperty` will not