mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Handle import of unknown nodes that include module meta
This commit is contained in:
		| @@ -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')) { | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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 = { | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user