mirror of https://github.com/node-red/node-red.git
Merge b8f0e13cd6
into 34bf4b0268
commit
0ba612d01d
|
@ -264,6 +264,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",
|
||||
|
|
|
@ -1495,7 +1495,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 || {};
|
||||
|
@ -1529,7 +1534,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);
|
||||
}
|
||||
}
|
||||
|
@ -1564,19 +1569,23 @@ 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;
|
||||
}
|
||||
|
||||
// Create the Flow JSON for the current configuration
|
||||
// opts.credentials (whether to include (known) credentials) - default: true
|
||||
// opts.dimensions (whether to include node dimensions) - default: false
|
||||
// opts.includeModuleConfig (whether to include modules) - default: false
|
||||
function createCompleteNodeSet(opts) {
|
||||
var nns = [];
|
||||
var i;
|
||||
|
@ -1608,6 +1617,9 @@ RED.nodes = (function() {
|
|||
RED.nodes.eachNode(function(n) {
|
||||
nns.push(convertNode(n, opts));
|
||||
})
|
||||
if (opts?.includeModuleConfig) {
|
||||
updateGlobalConfigModuleList(nns);
|
||||
}
|
||||
return nns;
|
||||
}
|
||||
|
||||
|
@ -1835,6 +1847,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: {} }
|
||||
|
@ -1970,12 +1983,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();
|
||||
|
@ -2403,6 +2458,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") {
|
||||
|
@ -2412,6 +2470,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') && node._def.defaults.hasOwnProperty("inputs")) {
|
||||
|
@ -3098,7 +3160,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) {
|
||||
|
|
|
@ -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,9 +745,9 @@ 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 });
|
||||
nodes = RED.nodes.createCompleteNodeSet({ credentials: false, includeModuleConfig: true });
|
||||
}
|
||||
if (nodes !== null) {
|
||||
if (format === "red-ui-clipboard-dialog-export-fmt-full") {
|
||||
|
@ -848,7 +848,7 @@ RED.clipboard = (function() {
|
|||
children: []
|
||||
};
|
||||
treeSubflows.push(subflows[node.id])
|
||||
} else {
|
||||
} else if (node.type !== 'global-config') {
|
||||
nodes.push(node);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -671,8 +671,20 @@ RED.palette.editor = (function() {
|
|||
// Load catalogues
|
||||
refreshCatalogues()
|
||||
|
||||
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) {
|
||||
|
@ -1030,8 +1042,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);
|
||||
|
|
|
@ -5695,27 +5695,50 @@ 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");
|
||||
if (n.type === "global-config") {
|
||||
importedGlobalConfig = n
|
||||
// Do not import it if one exists
|
||||
// The properties of this one will be merged after import
|
||||
return false
|
||||
}
|
||||
return true
|
||||
});
|
||||
globalConfig = nodesToImport.find(function (n) {
|
||||
return (n.type === "global-config");
|
||||
} else {
|
||||
filteredNodesToImport = nodesToImport.filter(function (n) {
|
||||
if (n.type === "global-config") {
|
||||
importedGlobalConfig = n
|
||||
if (n.env && n.env.length) {
|
||||
// No existing global-config
|
||||
// Contains env and maybe modules, so import it
|
||||
return true
|
||||
}
|
||||
// Contains modules only - do not import it
|
||||
return false
|
||||
}
|
||||
return true
|
||||
});
|
||||
}
|
||||
|
||||
const modules = importedGlobalConfig?.modules || {}
|
||||
// Ensure do not import modules - since it can contain it
|
||||
delete importedGlobalConfig?.modules
|
||||
|
||||
var result = RED.nodes.import(filteredNodesToImport,{
|
||||
generateIds: options.generateIds,
|
||||
addFlow: addNewFlow,
|
||||
importMap: options.importMap,
|
||||
markChanged: true
|
||||
markChanged: true,
|
||||
modules: modules
|
||||
});
|
||||
if (result) {
|
||||
var new_nodes = result.nodes;
|
||||
|
@ -5839,38 +5862,38 @@ RED.view = (function() {
|
|||
}
|
||||
}
|
||||
|
||||
if (globalConfig) {
|
||||
if (importedGlobalConfig && existingGlobalConfig) {
|
||||
// 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 = {
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue