Merge 1fd4fbc2991c6b612fff2df0f77622a2f21b2202 into 99b3b6530411665b0f4b76e3d0c3d69f973f4d6c

This commit is contained in:
Nick O'Leary 2024-11-08 11:18:08 -05:00 committed by GitHub
commit 7f73c1fb5d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 197 additions and 47 deletions

View File

@ -265,6 +265,8 @@
"download": "Download",
"importUnrecognised": "Imported unrecognised type:",
"importUnrecognised_plural": "Imported unrecognised types:",
"importWithModuleInfo": "Required dependencies missing",
"importWithModuleInfoDesc": "These nodes are not currently installed in your palette and are required for the imported flow:",
"importDuplicate": "Imported duplicate node:",
"importDuplicate_plural": "Imported duplicate nodes:",
"nodesExported": "Nodes exported to clipboard",

View File

@ -1474,7 +1474,12 @@ RED.nodes = (function() {
/**
* Converts the current node selection to an exportable JSON Object
**/
function createExportableNodeSet(set, exportedIds, exportedSubflows, exportedConfigNodes) {
function createExportableNodeSet(set, {
exportedIds,
exportedSubflows,
exportedConfigNodes,
includeModuleConfig = false
} = {}) {
var nns = [];
exportedIds = exportedIds || {};
@ -1508,7 +1513,7 @@ RED.nodes = (function() {
subflowSet = subflowSet.concat(RED.nodes.junctions(subflowId))
subflowSet = subflowSet.concat(RED.nodes.groups(subflowId))
var exportableSubflow = createExportableNodeSet(subflowSet, exportedIds, exportedSubflows, exportedConfigNodes);
var exportableSubflow = createExportableNodeSet(subflowSet, { exportedIds, exportedSubflows, exportedConfigNodes });
nns = exportableSubflow.concat(nns);
}
}
@ -1543,13 +1548,16 @@ RED.nodes = (function() {
}
nns.push(convertedNode);
if (node.type === "group") {
nns = nns.concat(createExportableNodeSet(node.nodes, exportedIds, exportedSubflows, exportedConfigNodes));
nns = nns.concat(createExportableNodeSet(node.nodes, { exportedIds, exportedSubflows, exportedConfigNodes }));
}
} else {
var convertedSubflow = convertSubflow(node, { credentials: false });
nns.push(convertedSubflow);
}
}
if (includeModuleConfig) {
updateGlobalConfigModuleList(nns)
}
return nns;
}
@ -1587,6 +1595,7 @@ RED.nodes = (function() {
RED.nodes.eachNode(function(n) {
nns.push(convertNode(n, opts));
})
updateGlobalConfigModuleList(nns)
return nns;
}
@ -1802,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: {} }
@ -1937,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();
@ -2310,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") {
@ -2319,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')) {
@ -2931,7 +2990,33 @@ RED.nodes = (function() {
}
}
}
function getModuleListForNodes(nodes) {
const modules = {}
nodes.forEach(n => {
const nodeSet = RED.nodes.registry.getNodeSetForType(n.type)
if (nodeSet) {
modules[nodeSet.module] = nodeSet.version
}
})
return modules
}
function updateGlobalConfigModuleList(nodes) {
const modules = getModuleListForNodes(nodes)
delete modules['node-red']
const hasModules = (Object.keys(modules).length > 0)
let globalConfigNode = nodes.find(n => n.type === 'global-config')
if (!globalConfigNode && hasModules) {
globalConfigNode = {
id: RED.nodes.id(),
type: 'global-config',
env: [],
modules
}
nodes.push(globalConfigNode)
} else if (globalConfigNode) {
globalConfigNode.modules = modules
}
}
return {
init: function() {
RED.events.on("registry:node-type-added",function(type) {

View File

@ -730,7 +730,7 @@ RED.clipboard = (function() {
nodes = RED.view.selection().nodes||[];
}
// Don't include the subflow meta-port nodes in the exported selection
nodes = RED.nodes.createExportableNodeSet(nodes.filter(function(n) { return n.type !== 'subflow'}));
nodes = RED.nodes.createExportableNodeSet(nodes.filter(function(n) { return n.type !== 'subflow'}), { includeModuleConfig: true });
} else if (type === 'flow') {
var activeWorkspace = RED.workspaces.active();
nodes = RED.nodes.groups(activeWorkspace);
@ -745,7 +745,7 @@ RED.clipboard = (function() {
});
var parentNode = RED.nodes.workspace(activeWorkspace)||RED.nodes.subflow(activeWorkspace);
nodes.unshift(parentNode);
nodes = RED.nodes.createExportableNodeSet(nodes);
nodes = RED.nodes.createExportableNodeSet(nodes, { includeModuleConfig: true });
} else if (type === 'full') {
nodes = RED.nodes.createCompleteNodeSet({ credentials: false });
}
@ -848,7 +848,7 @@ RED.clipboard = (function() {
children: []
};
treeSubflows.push(subflows[node.id])
} else {
} else if (node.type !== 'global-config') {
nodes.push(node);
}
});

View File

@ -33,8 +33,7 @@ RED.envVar = (function() {
id: RED.nodes.id(),
type: "global-config",
env: [],
name: "global-config",
label: "",
modules: {},
hasUsers: false,
users: [],
credentials: cred,

View File

@ -666,8 +666,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) {
@ -1023,8 +1035,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

@ -11,12 +11,13 @@
RED.nodes.registerType('global-config',{
category: 'config',
defaults: {
name: { value: "" },
env: { value: [] },
modules: { value: {} }
},
credentials: {
map: { type: "map" }
},
label: 'global-config',
oneditprepare: function() {
$('#node-input-edit-env-var').on('click', function(evt) {
RED.actions.invoke('core:show-user-settings', 'envvar')

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>