Handle import of unknown nodes that include module meta

This commit is contained in:
Nick O'Leary 2024-07-18 16:23:02 +01:00
parent 3f8a5301fa
commit be9add2a95
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
4 changed files with 150 additions and 37 deletions

View File

@ -1811,6 +1811,7 @@ RED.nodes = (function() {
* - id:import - import as-is
* - id:copy - import with new id
* - id:replace - import over the top of existing
* - modules: map of module:version - hints for unknown nodes
*/
function importNodes(newNodesObj,options) { // createNewIds,createMissingWorkspace) {
const defOpts = { generateIds: false, addFlow: false, markChanged: false, reimport: false, importMap: {} }
@ -1946,12 +1947,54 @@ RED.nodes = (function() {
}
if (!isInitialLoad && unknownTypes.length > 0) {
var typeList = $("<ul>");
unknownTypes.forEach(function(t) {
$("<li>").text(t).appendTo(typeList);
})
typeList = typeList[0].outerHTML;
RED.notify("<p>"+RED._("clipboard.importUnrecognised",{count:unknownTypes.length})+"</p>"+typeList,"error",false,10000);
const notificationOptions = {
type: "error",
fixed: false,
timeout: 10000,
}
let unknownNotification
if (options.modules) {
notificationOptions.fixed = true
delete notificationOptions.timeout
// We have module hint list from imported global-config
// Provide option to install missing modules
notificationOptions.buttons = [
{
text: "Manage dependencies",
class:"primary",
click: function(e) {
unknownNotification.close();
RED.actions.invoke('core:manage-palette', {
view: 'install',
filter: '"' + Object.keys(options.modules).join('", "') + '"'
})
}
}
]
let moduleList = $("<ul>");
Object.keys(options.modules).forEach(function(t) {
$("<li>").text(t).appendTo(moduleList);
})
moduleList = moduleList[0].outerHTML;
unknownNotification = RED.notify(
"<p>"+RED._("clipboard.importWithModuleInfo")+"</p>"+
"<p>"+RED._("clipboard.importWithModuleInfoDesc")+"</p>"+
moduleList,
notificationOptions
);
} else {
var typeList = $("<ul>");
unknownTypes.forEach(function(t) {
$("<li>").text(t).appendTo(typeList);
})
typeList = typeList[0].outerHTML;
unknownNotification = RED.notify(
"<p>"+RED._("clipboard.importUnrecognised",{count:unknownTypes.length})+"</p>"+typeList,
notificationOptions
);
}
}
var activeWorkspace = RED.workspaces.active();
@ -2319,6 +2362,9 @@ RED.nodes = (function() {
delete node.z;
}
}
const unknownTypeDef = RED.nodes.getType('unknown')
node._def.oneditprepare = unknownTypeDef.oneditprepare
var orig = {};
for (var p in n) {
if (n.hasOwnProperty(p) && p!="x" && p!="y" && p!="z" && p!="id" && p!="wires") {
@ -2328,6 +2374,10 @@ RED.nodes = (function() {
node._orig = orig;
node.name = n.type;
node.type = "unknown";
if (options.modules) {
// We have a module hint list. Attach to the unknown node so we can reference it later
node.modules = Object.keys(options.modules)
}
}
if (node._def.category != "config") {
if (n.hasOwnProperty('inputs')) {

View File

@ -625,8 +625,20 @@ RED.palette.editor = (function() {
}
})
RED.actions.add("core:manage-palette",function() {
RED.actions.add("core:manage-palette", function(opts) {
RED.userSettings.show('palette');
if (opts) {
if (opts.view) {
editorTabs.activateTab(opts.view);
}
if (opts.filter) {
if (opts.view === 'install') {
searchInput.searchBox('value', opts.filter)
} else if (opts.view === 'nodes') {
filterInput.searchBox('value', opts.filter)
}
}
}
});
RED.events.on('registry:module-updated', function(ns) {
@ -982,8 +994,35 @@ RED.palette.editor = (function() {
change: function() {
var searchTerm = $(this).val().trim().toLowerCase();
if (searchTerm.length > 0 || loadedList.length < 20) {
const searchTerms = []
searchTerm.split(',').forEach(term => {
term = term.trim()
if (term) {
const isExact = term[0] === '"' && term[term.length-1] === '"'
searchTerms.push({
exact: isExact,
term: isExact ? term.substring(1,term.length-1) : term
})
}
})
filteredList = loadedList.filter(function(m) {
return (m.index.indexOf(searchTerm) > -1);
for (let i = 0; i < searchTerms.length; i++) {
const location = m.index.indexOf(searchTerms[i].term)
if (
(
searchTerms[i].exact &&
(
location === 0 && (
m.index.length === searchTerms[i].term.length ||
m.index[searchTerms[i].term.length] === ','
)
)
) ||
(!searchTerms[i].exact && location > -1)) {
return true
}
}
return false
}).map(function(f) { return {info:f}});
refreshFilteredItems();
searchInput.searchBox('count',filteredList.length+" / "+loadedList.length);

View File

@ -5664,27 +5664,29 @@ RED.view = (function() {
activeSubflowChanged = activeSubflow.changed;
}
var filteredNodesToImport = nodesToImport;
var globalConfig = null;
var gconf = null;
var importedGlobalConfig = null;
var existingGlobalConfig = null;
RED.nodes.eachConfig(function (conf) {
if (conf.type === "global-config") {
gconf = conf;
existingGlobalConfig = conf;
}
});
if (gconf) {
if (existingGlobalConfig) {
filteredNodesToImport = nodesToImport.filter(function (n) {
return (n.type !== "global-config");
});
globalConfig = nodesToImport.find(function (n) {
return (n.type === "global-config");
if (n.type === "global-config") {
importedGlobalConfig = n
return false
}
return true
});
}
var result = RED.nodes.import(filteredNodesToImport,{
generateIds: options.generateIds,
addFlow: addNewFlow,
importMap: options.importMap,
markChanged: true
markChanged: true,
modules: importedGlobalConfig ? (importedGlobalConfig.modules || {}) : {}
});
if (result) {
var new_nodes = result.nodes;
@ -5808,38 +5810,38 @@ RED.view = (function() {
}
}
if (globalConfig) {
if (importedGlobalConfig) {
// merge global env to existing global-config
var env0 = gconf.env || [];
var env1 = globalConfig.env || []
var newEnv = Array.from(env0);
var existingEnv = existingGlobalConfig.env || [];
var importedEnv = importedGlobalConfig.env || []
var newEnv = Array.from(existingEnv);
var changed = false;
env1.forEach(function (item1) {
var index = newEnv.findIndex(function (item0) {
return (item0.name === item1.name);
importedEnv.forEach(function (importedItem) {
var index = newEnv.findIndex(function (existingItem) {
return (existingItem.name === importedItem.name);
});
if (index >= 0) {
var item0 = newEnv[index];
if ((item0.type !== item1.type) ||
(item0.value !== item1.value)) {
newEnv[index] = item1;
const existingItem = newEnv[index];
if ((existingItem.type !== importedItem.type) ||
(existingItem.value !== importedItem.value)) {
newEnv[index] = importedItem;
changed = true;
}
}
else {
newEnv.push(item1);
newEnv.push(importedItem);
changed = true;
}
});
if(changed) {
gconf.env = newEnv;
if (changed) {
existingGlobalConfig.env = newEnv;
var replaceEvent = {
t: "edit",
node: gconf,
node: existingGlobalConfig,
changed: true,
changes: {
env: env0
env: existingEnv
}
};
historyEvent = {

View File

@ -1,14 +1,22 @@
<script type="text/html" data-template-name="unknown">
<div class="form-tips"><span data-i18n="[html]unknown.tip"></span></div>
<div class="form-tips">
<span data-i18n="[html]unknown.tip"></span>
<p id="unknown-module-known">
<button id="unknown-manage-dependencies" class="red-ui-button">Manage dependencies</button>
</p>
</div>
</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('unknown',{
RED.nodes.registerType('unknown', {
category: 'unknown',
color:"#fff0f0",
color:"#fff000",
defaults: {
name: {value:""}
name: {value:""},
modules: { value: [] }
},
inputs:1,
outputs:1,
@ -18,6 +26,20 @@
},
labelStyle: function() {
return "node_label_unknown";
},
oneditprepare: function () {
const node = this
if (this.modules && this.modules.length > 0) {
$('#unknown-manage-dependencies').on('click', function () {
RED.actions.invoke('core:cancel-edit-tray')
RED.actions.invoke('core:manage-palette', {
view: 'install',
filter: '"' + node.modules.join('", "') + '"'
})
})
} else {
$('#unknown-module-known').hide()
}
}
});
</script>