Merge pull request #2691 from node-red/recover-nodes

Recover nodes with invalid z property
This commit is contained in:
Nick O'Leary 2020-09-16 11:56:29 +01:00 committed by GitHub
commit e619b9bf7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 101 additions and 19 deletions

View File

@ -213,6 +213,9 @@
"groupCopied_plural": "__count__ groups copied", "groupCopied_plural": "__count__ groups copied",
"groupStyleCopied": "Group style copied", "groupStyleCopied": "Group style copied",
"invalidFlow": "Invalid flow: __message__", "invalidFlow": "Invalid flow: __message__",
"recoveredNodes": "Recovered Nodes",
"recoveredNodesInfo": "The nodes on this flow were missing a valid flow id when they were imported. They have been added to this flow so you can either restore or delete them.",
"recoveredNodesNotification": "<p>Imported nodes without a valid flow id</p><p>They have been added to a new flow called '__flowName__'.</p>",
"export": { "export": {
"selected":"selected nodes", "selected":"selected nodes",
"current":"current flow", "current":"current flow",

View File

@ -897,12 +897,23 @@ RED.nodes = (function() {
} }
return true; return true;
} }
/**
function importNodes(newNodesObj,createNewIds,createMissingWorkspace) { * Options:
* - generateIds - whether to replace all node ids
* - addFlow - whether to import nodes to a new tab
*/
function importNodes(newNodesObj,options) { // createNewIds,createMissingWorkspace) {
options = options || {
generateIds: false,
addFlow: false
}
var createNewIds = options.generateIds;
var createMissingWorkspace = options.addFlow;
var i; var i;
var n; var n;
var newNodes; var newNodes;
var nodeZmap = {}; var nodeZmap = {};
var recoveryWorkspace;
if (typeof newNodesObj === "string") { if (typeof newNodesObj === "string") {
if (newNodesObj === "") { if (newNodesObj === "") {
return; return;
@ -956,6 +967,23 @@ RED.nodes = (function() {
if (n.z) { if (n.z) {
nodeZmap[n.z] = nodeZmap[n.z] || []; nodeZmap[n.z] = nodeZmap[n.z] || [];
nodeZmap[n.z].push(n); nodeZmap[n.z].push(n);
} else if (n.z === 0) {
// Hit the rare issue where node z values get set to 0.
// Repair the flow - but we really need to track that down.
if (!recoveryWorkspace) {
recoveryWorkspace = {
id: RED.nodes.id(),
type: "tab",
disabled: false,
label: RED._("clipboard.recoveredNodes"),
info: RED._("clipboard.recoveredNodesInfo")
}
addWorkspace(recoveryWorkspace);
RED.workspaces.add(recoveryWorkspace);
nodeZmap[recoveryWorkspace.id] = [];
}
n.z = recoveryWorkspace.id;
nodeZmap[recoveryWorkspace.id].push(n);
} }
} }
@ -1005,6 +1033,10 @@ RED.nodes = (function() {
var missingWorkspace = null; var missingWorkspace = null;
var d; var d;
if (recoveryWorkspace) {
new_workspaces.push(recoveryWorkspace);
}
// Find all tabs and subflow templates // Find all tabs and subflow templates
for (i=0;i<newNodes.length;i++) { for (i=0;i<newNodes.length;i++) {
n = newNodes[i]; n = newNodes[i];
@ -1492,6 +1524,18 @@ RED.nodes = (function() {
} }
RED.workspaces.refresh(); RED.workspaces.refresh();
if (recoveryWorkspace) {
var notification = RED.notify(RED._("clipboard.recoveredNodesNotification",{flowName:RED._("clipboard.recoveredNodes")}),{
type:"warning",
fixed:true,
buttons: [
{text: RED._("common.label.close"), click: function() { notification.close() }}
]
});
}
return [new_nodes,new_links,new_groups,new_workspaces,new_subflows,missingWorkspace]; return [new_nodes,new_links,new_groups,new_workspaces,new_subflows,missingWorkspace];
} }
@ -1688,7 +1732,7 @@ RED.nodes = (function() {
// Force the redraw to be synchronous so the view updates // Force the redraw to be synchronous so the view updates
// *now* and removes the unknown node // *now* and removes the unknown node
RED.view.redraw(true, true); RED.view.redraw(true, true);
var result = importNodes(reimportList,false); var result = importNodes(reimportList,{generateIds:false});
var newNodeMap = {}; var newNodeMap = {};
result[0].forEach(function(n) { result[0].forEach(function(n) {
newNodeMap[n.id] = n; newNodeMap[n.id] = n;

View File

@ -141,7 +141,7 @@ RED.clipboard = (function() {
click: function() { click: function() {
var addNewFlow = ($("#red-ui-clipboard-dialog-import-opt > a.selected").attr('id') === 'red-ui-clipboard-dialog-import-opt-new'); var addNewFlow = ($("#red-ui-clipboard-dialog-import-opt > a.selected").attr('id') === 'red-ui-clipboard-dialog-import-opt-new');
if (activeTab === "red-ui-clipboard-dialog-import-tab-clipboard") { if (activeTab === "red-ui-clipboard-dialog-import-tab-clipboard") {
RED.view.importNodes($("#red-ui-clipboard-dialog-import-text").val(),addNewFlow); importNodes($("#red-ui-clipboard-dialog-import-text").val(),addNewFlow);
} else { } else {
var selectedPath; var selectedPath;
if (activeTab === "red-ui-clipboard-dialog-import-tab-library") { if (activeTab === "red-ui-clipboard-dialog-import-tab-library") {
@ -151,7 +151,7 @@ RED.clipboard = (function() {
} }
if (selectedPath.path) { if (selectedPath.path) {
$.get('library/'+selectedPath.library+'/'+selectedPath.type+'/'+selectedPath.path, function(data) { $.get('library/'+selectedPath.library+'/'+selectedPath.type+'/'+selectedPath.path, function(data) {
RED.view.importNodes(data,addNewFlow); importNodes(data,addNewFlow);
}); });
} }
} }
@ -359,7 +359,7 @@ RED.clipboard = (function() {
} }
} }
function importNodes(mode) { function showImportNodes(mode) {
if (disabled) { if (disabled) {
return; return;
} }
@ -494,7 +494,7 @@ RED.clipboard = (function() {
}); });
} }
function exportNodes(mode) { function showExportNodes(mode) {
if (disabled) { if (disabled) {
return; return;
} }
@ -749,19 +749,39 @@ RED.clipboard = (function() {
} }
return result; return result;
} }
function importNodes(nodesStr,addFlow) {
var newNodes;
try {
nodesStr = nodesStr.trim();
if (nodesStr.length === 0) {
return;
}
newNodes = JSON.parse(nodesStr);
} catch(err) {
var e = new Error(RED._("clipboard.invalidFlow",{message:err.message}));
e.code = "NODE_RED";
throw e;
}
RED.view.importNodes(newNodes,{addFlow: addFlow});
}
return { return {
init: function() { init: function() {
setupDialogs(); setupDialogs();
$('<input type="text" id="red-ui-clipboard-hidden" tabIndex="-1">').appendTo("#red-ui-editor"); $('<input type="text" id="red-ui-clipboard-hidden" tabIndex="-1">').appendTo("#red-ui-editor");
RED.actions.add("core:show-export-dialog",exportNodes); RED.actions.add("core:show-export-dialog",showExportNodes);
RED.actions.add("core:show-import-dialog",importNodes); RED.actions.add("core:show-import-dialog",showImportNodes);
RED.actions.add("core:show-library-export-dialog",function() { exportNodes('library') }); RED.actions.add("core:show-library-export-dialog",function() { showExportNodes('library') });
RED.actions.add("core:show-library-import-dialog",function() { importNodes('library') }); RED.actions.add("core:show-library-import-dialog",function() { showImportNodes('library') });
RED.actions.add("core:show-examples-import-dialog",function() { importNodes('examples') }); RED.actions.add("core:show-examples-import-dialog",function() { showImportNodes('examples') });
RED.events.on("editor:open",function() { disabled = true; }); RED.events.on("editor:open",function() { disabled = true; });
RED.events.on("editor:close",function() { disabled = false; }); RED.events.on("editor:close",function() { disabled = false; });
@ -795,7 +815,7 @@ RED.clipboard = (function() {
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) { if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
var data = event.originalEvent.dataTransfer.getData("text/plain"); var data = event.originalEvent.dataTransfer.getData("text/plain");
data = data.substring(data.indexOf('['),data.lastIndexOf(']')+1); data = data.substring(data.indexOf('['),data.lastIndexOf(']')+1);
RED.view.importNodes(data); importNodes(data);
} else if ($.inArray("Files",event.originalEvent.dataTransfer.types) != -1) { } else if ($.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
var files = event.originalEvent.dataTransfer.files; var files = event.originalEvent.dataTransfer.files;
if (files.length === 1) { if (files.length === 1) {
@ -803,7 +823,7 @@ RED.clipboard = (function() {
var reader = new FileReader(); var reader = new FileReader();
reader.onload = (function(theFile) { reader.onload = (function(theFile) {
return function(e) { return function(e) {
RED.view.importNodes(e.target.result); importNodes(e.target.result);
}; };
})(file); })(file);
reader.readAsText(file); reader.readAsText(file);
@ -814,8 +834,8 @@ RED.clipboard = (function() {
}); });
}, },
import: importNodes, import: showImportNodes,
export: exportNodes, export: showExportNodes,
copyText: copyText copyText: copyText
} }
})(); })();

View File

@ -3479,7 +3479,7 @@ RED.view = (function() {
options.push({name:"delete",disabled:(movingSet.length()===0 && selected_link === null),onselect:function() {deleteSelection();}}); options.push({name:"delete",disabled:(movingSet.length()===0 && selected_link === null),onselect:function() {deleteSelection();}});
options.push({name:"cut",disabled:(movingSet.length()===0),onselect:function() {copySelection();deleteSelection();}}); options.push({name:"cut",disabled:(movingSet.length()===0),onselect:function() {copySelection();deleteSelection();}});
options.push({name:"copy",disabled:(movingSet.length()===0),onselect:function() {copySelection();}}); options.push({name:"copy",disabled:(movingSet.length()===0),onselect:function() {copySelection();}});
options.push({name:"paste",disabled:(clipboard.length===0),onselect:function() {importNodes(clipboard,false,true);}}); options.push({name:"paste",disabled:(clipboard.length===0),onselect:function() {importNodes(clipboard,{touchImport:true});}});
options.push({name:"edit",disabled:(movingSet.length() != 1),onselect:function() { RED.editor.edit(mdn);}}); options.push({name:"edit",disabled:(movingSet.length() != 1),onselect:function() { RED.editor.edit(mdn);}});
options.push({name:"select",onselect:function() {selectAll();}}); options.push({name:"select",onselect:function() {selectAll();}});
options.push({name:"undo",disabled:(RED.history.depth() === 0),onselect:function() {RED.history.pop();}}); options.push({name:"undo",disabled:(RED.history.depth() === 0),onselect:function() {RED.history.pop();}});
@ -4630,11 +4630,26 @@ RED.view = (function() {
/** /**
* Imports a new collection of nodes from a JSON String. * Imports a new collection of nodes from a JSON String.
*
* - all get new IDs assigned * - all get new IDs assigned
* - all "selected" * - all "selected"
* - attached to mouse for placing - "IMPORT_DRAGGING" * - attached to mouse for placing - "IMPORT_DRAGGING"
* @param {String/Array} newNodesStr nodes to import
* @param {Object} options options object
*
* Options:
* - addFlow - whether to import nodes to a new tab
* - touchImport - whether this is a touch import. If not, imported nodes are
* attachedto mouse for placing - "IMPORT_DRAGGING" state
*/ */
function importNodes(newNodesStr,addNewFlow,touchImport) { function importNodes(newNodesStr,options) {
options = options || {
addFlow: false,
touchImport: false
}
var addNewFlow = options.addFlow
var touchImport = options.touchImport;
if (mouse_mode === RED.state.SELECTING_NODE) { if (mouse_mode === RED.state.SELECTING_NODE) {
return; return;
} }
@ -4643,7 +4658,7 @@ RED.view = (function() {
if (activeSubflow) { if (activeSubflow) {
activeSubflowChanged = activeSubflow.changed; activeSubflowChanged = activeSubflow.changed;
} }
var result = RED.nodes.import(newNodesStr,true,addNewFlow); var result = RED.nodes.import(newNodesStr,{generateIds:true, addFlow: addNewFlow});
if (result) { if (result) {
var new_nodes = result[0]; var new_nodes = result[0];
var new_links = result[1]; var new_links = result[1];