mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Merge branch 'master' into dev
This commit is contained in:
		
							
								
								
									
										11
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,3 +1,14 @@ | ||||
| ### 1.2.9: Maintenance Release | ||||
|  | ||||
| Editor | ||||
|  | ||||
|  - Sanitize node type names when displaying in notifications | ||||
|  - Sanitize branch name before displaying in notification message | ||||
|  | ||||
| Runtime | ||||
|  | ||||
|  - Handle more valid language codes when validating lang params Fixes #2856 | ||||
|  | ||||
| ### 1.2.8: Maintenance Release | ||||
|  | ||||
| Editor | ||||
|   | ||||
| @@ -103,7 +103,7 @@ | ||||
|         "grunt-simple-nyc": "^3.0.1", | ||||
|         "http-proxy": "1.18.1", | ||||
|         "jsdoc-nr-template": "github:node-red/jsdoc-nr-template", | ||||
|         "marked": "1.2.7", | ||||
|         "marked": "2.0.0", | ||||
|         "minami": "1.2.3", | ||||
|         "mocha": "^5.2.0", | ||||
|         "node-red-node-test-helper": "^0.2.6", | ||||
|   | ||||
| @@ -33,6 +33,9 @@ module.exports = { | ||||
|             }) | ||||
|         } else { | ||||
|             opts.lang = apiUtils.determineLangFromHeaders(req.acceptsLanguages()); | ||||
|             if (/[^0-9a-z=\-\*]/i.test(opts.lang)) { | ||||
|                 opts.lang = "en-US"; | ||||
|             } | ||||
|             runtimeAPI.nodes.getNodeConfigs(opts).then(function(configs) { | ||||
|                 res.send(configs); | ||||
|             }) | ||||
| @@ -92,6 +95,9 @@ module.exports = { | ||||
|             }) | ||||
|         } else { | ||||
|             opts.lang = apiUtils.determineLangFromHeaders(req.acceptsLanguages()); | ||||
|             if (/[^0-9a-z=\-\*]/i.test(opts.lang)) { | ||||
|                 opts.lang = "en-US"; | ||||
|             } | ||||
|             runtimeAPI.nodes.getNodeConfig(opts).then(function(result) { | ||||
|                 return res.send(result); | ||||
|             }).catch(function(err) { | ||||
| @@ -161,6 +167,9 @@ module.exports = { | ||||
|             lang: req.query.lng, | ||||
|             req: apiUtils.getRequestLogObject(req) | ||||
|         } | ||||
|         if (/[^0-9a-z=\-\*]/i.test(opts.lang)) { | ||||
|             opts.lang = "en-US"; | ||||
|         } | ||||
|         runtimeAPI.nodes.getModuleCatalog(opts).then(function(result) { | ||||
|             res.json(result); | ||||
|         }).catch(function(err) { | ||||
| @@ -175,6 +184,9 @@ module.exports = { | ||||
|             lang: req.query.lng, | ||||
|             req: apiUtils.getRequestLogObject(req) | ||||
|         } | ||||
|         if (/[^0-9a-z=\-\*]/i.test(opts.lang)) { | ||||
|             opts.lang = "en-US"; | ||||
|         } | ||||
|         runtimeAPI.nodes.getModuleCatalogs(opts).then(function(result) { | ||||
|             res.json(result); | ||||
|         }).catch(function(err) { | ||||
|   | ||||
| @@ -41,7 +41,7 @@ module.exports = { | ||||
|         var namespace = req.params[0]; | ||||
|         namespace = namespace.replace(/\.json$/,""); | ||||
|         var lang = req.query.lng || i18n.defaultLang; //apiUtil.determineLangFromHeaders(req.acceptsLanguages() || []); | ||||
|         if (/[^a-z\-\*]/i.test(lang)) { | ||||
|         if (/[^0-9a-z=\-\*]/i.test(lang)) { | ||||
|             res.json({}); | ||||
|             return; | ||||
|         } | ||||
|   | ||||
| @@ -298,7 +298,7 @@ var RED = (function() { | ||||
|                             "merge-complete": RED._("notification.project.merge-complete") | ||||
|                         }[msg.action]; | ||||
|                         loader.end() | ||||
|                         RED.notify("<p>"+message+"</p>"); | ||||
|                         RED.notify($("<p>").text(message)); | ||||
|                         RED.sidebar.info.refresh() | ||||
|                     }); | ||||
|                 }); | ||||
| @@ -469,7 +469,7 @@ var RED = (function() { | ||||
|                     }); | ||||
|                 }); | ||||
|                 if (addedTypes.length) { | ||||
|                     typeList = "<ul><li>"+addedTypes.join("</li><li>")+"</li></ul>"; | ||||
|                     typeList = "<ul><li>"+addedTypes.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>"; | ||||
|                     RED.notify(RED._("palette.event.nodeAdded", {count:addedTypes.length})+typeList,"success"); | ||||
|                 } | ||||
|                 loadIconList(); | ||||
| @@ -478,7 +478,7 @@ var RED = (function() { | ||||
|                     m = msg[i]; | ||||
|                     info = RED.nodes.removeNodeSet(m.id); | ||||
|                     if (info.added) { | ||||
|                         typeList = "<ul><li>"+m.types.join("</li><li>")+"</li></ul>"; | ||||
|                         typeList = "<ul><li>"+m.types.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>"; | ||||
|                         RED.notify(RED._("palette.event.nodeRemoved", {count:m.types.length})+typeList,"success"); | ||||
|                     } | ||||
|                 } | ||||
| @@ -488,12 +488,12 @@ var RED = (function() { | ||||
|                     info = RED.nodes.getNodeSet(msg.id); | ||||
|                     if (info.added) { | ||||
|                         RED.nodes.enableNodeSet(msg.id); | ||||
|                         typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>"; | ||||
|                         typeList = "<ul><li>"+msg.types.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>"; | ||||
|                         RED.notify(RED._("palette.event.nodeEnabled", {count:msg.types.length})+typeList,"success"); | ||||
|                     } else { | ||||
|                         $.get('nodes/'+msg.id, function(data) { | ||||
|                             appendNodeConfig(data); | ||||
|                             typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>"; | ||||
|                             typeList = "<ul><li>"+msg.types.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>"; | ||||
|                             RED.notify(RED._("palette.event.nodeAdded", {count:msg.types.length})+typeList,"success"); | ||||
|                         }); | ||||
|                     } | ||||
| @@ -501,7 +501,7 @@ var RED = (function() { | ||||
|             } else if (topic == "notification/node/disabled") { | ||||
|                 if (msg.types) { | ||||
|                     RED.nodes.disableNodeSet(msg.id); | ||||
|                     typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>"; | ||||
|                     typeList = "<ul><li>"+msg.types.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>"; | ||||
|                     RED.notify(RED._("palette.event.nodeDisabled", {count:msg.types.length})+typeList,"success"); | ||||
|                 } | ||||
|             } else if (topic == "notification/node/upgraded") { | ||||
|   | ||||
| @@ -31,15 +31,66 @@ RED.palette.editor = (function() { | ||||
|     var eventTimers = {}; | ||||
|     var activeFilter = ""; | ||||
|  | ||||
|     function semVerCompare(A,B) { | ||||
|         var aParts = A.split(".").map(function(m) { return parseInt(m);}); | ||||
|         var bParts = B.split(".").map(function(m) { return parseInt(m);}); | ||||
|         for (var i=0;i<3;i++) { | ||||
|             var j = aParts[i]-bParts[i]; | ||||
|             if (j<0) { return -1 } | ||||
|             if (j>0) { return 1 } | ||||
|     var semverre = /^(?<major>\d+)(\.(?<minor>\d+))?(\.(?<patch>\d+))?(-(?<pre>[0-9A-Za-z-]+))?(\.(?<build>[0-9A-Za-z-.]+))?$/; | ||||
|     var NUMBERS_ONLY = /^\d+$/; | ||||
|  | ||||
|  | ||||
| 	function SemVerPart ( part ) { | ||||
|         this.number = 0; | ||||
|         this.text = part; | ||||
|         if ( NUMBERS_ONLY.test( toe ) ) | ||||
|         { | ||||
|             this.number = parseInt( part ); | ||||
|             this.type = "N"; | ||||
|         } else | ||||
|         { | ||||
|             this.type = part == undefined || part.length < 1 ? "E" : "T"; | ||||
|         } | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     SemVerPart.prototype.compare = function ( other ) { | ||||
|         const types = this.type + other.type; | ||||
|  | ||||
|         switch ( types ) | ||||
|         { | ||||
|             case "EE": return 0; | ||||
|  | ||||
|             case "NT": | ||||
|             case "TE": | ||||
|             case "EN": return -1; | ||||
|  | ||||
|             case "NN": return this.number - other.number; | ||||
|  | ||||
|             case "TT": return this.text.localeCompare( other.text ); | ||||
|  | ||||
|             case "ET": | ||||
|             case "TN": | ||||
|             case "NE": return 1; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     function SemVer ( ver ) { | ||||
|         const groups = ver.match( semverre ).groups; | ||||
|         this.parts = [ new SemVerPart( groups.major ), new SemVerPart( groups.minor ), new SemVerPart( groups.patch ), new SemVerPart( groups.pre ), new SemVerPart( groups.build ) ]; | ||||
|     } | ||||
|  | ||||
|     SemVer.prototype.compare = function ( other ) { | ||||
|         let result = 0; | ||||
|         for ( let i = 0, n = this.parts.length; result == 0 && i < n; i++ ) | ||||
|         { | ||||
|             result = this.parts[ i ].compare( other.parts[ i ] ); | ||||
|         } | ||||
|  | ||||
|         return result; | ||||
|     }; | ||||
|  | ||||
|     function semVerCompare ( ver1, ver2 ) { | ||||
|         const semver1 = new SemVer( ver1 ); | ||||
|         const semver2 = new SemVer( ver2 ); | ||||
|  | ||||
|         const result = semver1.compare( semver2 ); | ||||
|  | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     function delayCallback(start,callback) { | ||||
|   | ||||
| @@ -1606,6 +1606,11 @@ RED.projects = (function() { | ||||
|                     done(null,data); | ||||
|                 }, | ||||
|                 400: { | ||||
|                     'credentials_load_failed': function(data) { | ||||
|                         dialog.dialog( "close" ); | ||||
|                         RED.events.emit("project:change", {name:name}); | ||||
|                         done(null,data); | ||||
|                     }, | ||||
|                     '*': done | ||||
|                 }, | ||||
|             } | ||||
|   | ||||
| @@ -108,7 +108,7 @@ | ||||
|             toggle: "active", | ||||
|             visible: function() { return this.tosidebar; }, | ||||
|             onclick: function() { | ||||
|                 var label = this.name||"debug"; | ||||
|                 var label = RED.utils.sanitize(this.name||"debug"); | ||||
|                 var node = this; | ||||
|                 activateAjaxCall(node, node.active, function(resp, textStatus, xhr) { | ||||
|                     var historyEvent = { | ||||
|   | ||||
| @@ -48,7 +48,8 @@ module.exports = function(RED) { | ||||
|             else { node.goodtmpl = true; } | ||||
|             return col; | ||||
|         } | ||||
|         node.template = clean(node.template); | ||||
|         var template = clean(node.template); | ||||
|         var notemplate = node.template.length === 1 && node.template[0] === ''; | ||||
|         node.hdrSent = false; | ||||
|  | ||||
|         this.on("input", function(msg, send, done) { | ||||
| @@ -58,19 +59,22 @@ module.exports = function(RED) { | ||||
|             if (msg.hasOwnProperty("payload")) { | ||||
|                 if (typeof msg.payload == "object") { // convert object to CSV string | ||||
|                     try { | ||||
|                         if (!(notemplate && (msg.hasOwnProperty("parts") && msg.parts.hasOwnProperty("index") && msg.parts.index > 0))) { | ||||
|                             template = clean(node.template); | ||||
|                         } | ||||
|                         var ou = ""; | ||||
|                         if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; } | ||||
|                         if (node.hdrout !== "none" && node.hdrSent === false) { | ||||
|                             if ((node.template.length === 1) && (node.template[0] === '')) { | ||||
|                             if ((template.length === 1) && (template[0] === '')) { | ||||
|                                 if (msg.hasOwnProperty("columns")) { | ||||
|                                     node.template = clean(msg.columns || ""); | ||||
|                                     template = clean(msg.columns || ""); | ||||
|                                 } | ||||
|                                 else { | ||||
|                                     node.template = Object.keys(msg.payload[0]); | ||||
|                                     template = Object.keys(msg.payload[0]); | ||||
|                                 } | ||||
|                             } | ||||
|                             // ou += node.template.join(node.sep) + node.ret; | ||||
|                             ou += node.template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v).join(node.sep) + node.ret; | ||||
|                             ou += template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v).join(node.sep) + node.ret; | ||||
|                             if (node.hdrout === "once") { node.hdrSent = true; } | ||||
|                         } | ||||
|                         for (var s = 0; s < msg.payload.length; s++) { | ||||
| @@ -89,10 +93,10 @@ module.exports = function(RED) { | ||||
|                                 ou += msg.payload[s].join(node.sep) + node.ret; | ||||
|                             } | ||||
|                             else { | ||||
|                                 if ((node.template.length === 1) && (node.template[0] === '') && (msg.hasOwnProperty("columns"))) { | ||||
|                                     node.template = clean(msg.columns || "")//.split(",")); | ||||
|                                 if ((template.length === 1) && (template[0] === '') && (msg.hasOwnProperty("columns"))) { | ||||
|                                     template = clean(msg.columns || "")//.split(",")); | ||||
|                                 } | ||||
|                                 if ((node.template.length === 1) && (node.template[0] === '')) { | ||||
|                                 if ((template.length === 1) && (template[0] === '')) { | ||||
|                                     /* istanbul ignore else */ | ||||
|                                     if (tmpwarn === true) { // just warn about missing template once | ||||
|                                         node.warn(RED._("csv.errors.obj_csv")); | ||||
| @@ -118,12 +122,12 @@ module.exports = function(RED) { | ||||
|                                     ou = ou.slice(0,-1) + node.ret; | ||||
|                                 } | ||||
|                                 else { | ||||
|                                     for (var t=0; t < node.template.length; t++) { | ||||
|                                         if (node.template[t] === '') { | ||||
|                                     for (var t=0; t < template.length; t++) { | ||||
|                                         if (template[t] === '') { | ||||
|                                             ou += node.sep; | ||||
|                                         } | ||||
|                                         else { | ||||
|                                             var p = RED.util.ensureString(RED.util.getMessageProperty(msg,"payload["+s+"]['"+node.template[t]+"']")); | ||||
|                                             var p = RED.util.ensureString(RED.util.getMessageProperty(msg,"payload["+s+"]['"+template[t]+"']")); | ||||
|                                             /* istanbul ignore else */ | ||||
|                                             if (p === "undefined") { p = ""; } | ||||
|                                             if (p.indexOf(node.quo) !== -1) { // add double quotes if any quotes | ||||
| @@ -141,7 +145,7 @@ module.exports = function(RED) { | ||||
|                             } | ||||
|                         } | ||||
|                         msg.payload = ou; | ||||
|                         msg.columns = node.template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).join(','); | ||||
|                         msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).join(','); | ||||
|                         if (msg.payload !== '') { send(msg); } | ||||
|                         done(); | ||||
|                     } | ||||
|   | ||||
| @@ -94,7 +94,7 @@ var api = module.exports = { | ||||
|     getNodeConfig: async function(opts) { | ||||
|         var id = opts.id; | ||||
|         var lang = opts.lang; | ||||
|         if (/[^a-z\-\*]/i.test(opts.lang)) { | ||||
|         if (/[^0-9a-z=\-\*]/i.test(opts.lang)) { | ||||
|             reject(new Error("Invalid language: "+opts.lang)); | ||||
|             return | ||||
|         } | ||||
| @@ -120,11 +120,11 @@ var api = module.exports = { | ||||
|     * @memberof @node-red/runtime_nodes | ||||
|     */ | ||||
|     getNodeConfigs: async function(opts) { | ||||
|         if (/[^a-z\-\*]/i.test(opts.lang)) { | ||||
|         runtime.log.audit({event: "nodes.configs.get"}, opts.req); | ||||
|         if (/[^0-9a-z=\-\*]/i.test(opts.lang)) { | ||||
|             reject(new Error("Invalid language: "+opts.lang)); | ||||
|             return | ||||
|         } | ||||
|         runtime.log.audit({event: "nodes.configs.get"}, opts.req); | ||||
|         return runtime.nodes.getNodeConfigs(opts.lang); | ||||
|     }, | ||||
|  | ||||
| @@ -382,7 +382,7 @@ var api = module.exports = { | ||||
|     getModuleCatalogs: async function(opts) { | ||||
|         var namespace = opts.module; | ||||
|         var lang = opts.lang; | ||||
|         if (/[^a-z\-\*]/i.test(lang)) { | ||||
|         if (/[^0-9a-z=\-\*]/i.test(lang)) { | ||||
|             reject(new Error("Invalid language: "+lang)); | ||||
|             return | ||||
|         } | ||||
| @@ -416,7 +416,7 @@ var api = module.exports = { | ||||
|     getModuleCatalog: async function(opts) { | ||||
|         var namespace = opts.module; | ||||
|         var lang = opts.lang; | ||||
|         if (/[^a-z\-\*]/i.test(lang)) { | ||||
|         if (/[^0-9a-z=\-\*]/i.test(lang)) { | ||||
|             reject(new Error("Invalid language: "+lang)); | ||||
|             return | ||||
|         } | ||||
|   | ||||
| @@ -428,6 +428,12 @@ class Flow { | ||||
|             reportingNode = node; | ||||
|         } | ||||
|         if (!muteStatusEvent) { | ||||
|             if (statusMessage.hasOwnProperty("text") && typeof(statusMessage.text !== "string")) { | ||||
|                 try { | ||||
|                     statusMessage.text = statusMessage.text.toString(); | ||||
|                 } | ||||
|                 catch(e) {} | ||||
|             } | ||||
|             events.emit("node-status",{ | ||||
|                 id: node.id, | ||||
|                 status:statusMessage | ||||
|   | ||||
| @@ -715,6 +715,24 @@ describe('CSV node', function() { | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         it('should be able to include column names as first row, and missing properties', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", hdrout:true, ret:"\r\n", wires:[["n2"]] }, | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
|                 n2.on("input", function(msg) { | ||||
|                     try { | ||||
|                         msg.should.have.property('payload', 'col1,col2,col3,col4\r\nH1,H2,H3,H4\r\nA,B,,\r\nA,,C,\r\nA,,,D\r\n'); | ||||
|                         done(); | ||||
|                     } | ||||
|                     catch(e) { done(e); } | ||||
|                 }); | ||||
|                 var testJson = [{"col1":"H1","col2":"H2","col3":"H3","col4":"H4"},{"col1":"A","col2":"B"},{"col1":"A","col3":"C"},{"col1":"A","col4":"D"}]; | ||||
|                 n1.emit("input", {payload:testJson}); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         it('should be able to pass in column names', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", temp:"", hdrout:"once", ret:"\r\n", wires:[["n2"]] }, | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
| @@ -736,9 +754,27 @@ describe('CSV node', function() { | ||||
|                     catch(e) { done(e); } | ||||
|                 }); | ||||
|                 var testJson = [{ d: 1, b: 3, c: 2, a: 4 }]; | ||||
|                 n1.emit("input", {payload:testJson, columns:"a,,b,a"}); | ||||
|                 n1.emit("input", {payload:testJson}); | ||||
|                 n1.emit("input", {payload:testJson}); | ||||
|                 n1.emit("input", {payload:testJson, columns:"a,,b,a", parts:{index:0}}); | ||||
|                 n1.emit("input", {payload:testJson, parts:{index:1}}); | ||||
|                 n1.emit("input", {payload:testJson, parts:{index:2}}); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         it('should be able to pass in column names - with payload as an array', function(done) { | ||||
|             var flow = [ { id:"n1", type:"csv", hdrout:"once", ret:"\r\n", wires:[["n2"]] }, | ||||
|                 {id:"n2", type:"helper"} ]; | ||||
|             helper.load(csvNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
|                 n2.on("input", function(msg) { | ||||
|                     try { | ||||
|                         msg.should.have.property('payload', 'a,,b,a\r\n4,,3,4\r\n4,,3,4\r\n4,,3,4\r\n'); | ||||
|                         done() | ||||
|                     } | ||||
|                     catch(e) { done(e); } | ||||
|                 }); | ||||
|                 var testJson = { d: 1, b: 3, c: 2, a: 4 }; | ||||
|                 n1.emit("input", {payload:[testJson,testJson,testJson], columns:"a,,b,a"}); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user