mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Merge pull request #2435 from PaulWieland/dev
Adding user definable properties to inject node
This commit is contained in:
		| @@ -16,14 +16,12 @@ | ||||
|  | ||||
| <script type="text/html" data-template-name="inject"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-payload"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload"></span></label> | ||||
|         <input type="text" id="node-input-payload" style="width:70%"> | ||||
|         <input type="hidden" id="node-input-payloadType"> | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
|         <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"> | ||||
|     </div> | ||||
|  | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-topic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label> | ||||
|         <input type="text" id="node-input-topic"> | ||||
|      | ||||
|     <div class="form-row node-input-property-container-row"> | ||||
|         <ol id="node-input-property-container"></ol> | ||||
|     </div> | ||||
|  | ||||
|     <div class="form-row" id="node-once"> | ||||
| @@ -114,12 +112,7 @@ | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
|         <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"> | ||||
|     </div> | ||||
|  | ||||
|     <div class="form-tips" data-i18n="[html]inject.tip"></div> | ||||
| </script> | ||||
| <style> | ||||
|     .inject-time-row { | ||||
| @@ -160,36 +153,85 @@ | ||||
|         color:"#a6bbcf", | ||||
|         defaults: { | ||||
|             name: {value:""}, | ||||
|             topic: {value:""}, | ||||
|             payload: {value:"", validate: RED.validators.typedInput("payloadType")}, | ||||
|             payloadType: {value:"date"}, | ||||
|             props:{value:[{p:"payload",v:"",vt:"date"},{p:"topic",v:"",vt:"str"}]}, | ||||
|             repeat: {value:"", validate:function(v) { return ((v === "") || (RED.validators.number(v) && (v >= 0) && (v <= 2147483))) }}, | ||||
|             crontab: {value:""}, | ||||
|             once: {value:false}, | ||||
|             onceDelay: {value:0.1} | ||||
|             onceDelay: {value:0.1}, | ||||
|             /* Legacy */ | ||||
|             topic: {value:""}, | ||||
|             payload: {value:"", validate: RED.validators.typedInput("payloadType")}, | ||||
|             payloadType: {value:"date"}, | ||||
|             /* */ | ||||
|         }, | ||||
|         icon: "inject.svg", | ||||
|         inputs:0, | ||||
|         outputs:1, | ||||
|         outputLabels: function(index) { | ||||
|             var lab = this.payloadType; | ||||
|             if (lab === "json") { | ||||
|                 try { | ||||
|                     lab = typeof JSON.parse(this.payload); | ||||
|                     if (lab === "object") { | ||||
|                         if (Array.isArray(JSON.parse(this.payload))) { lab = "Array"; } | ||||
|             var lab = ''; | ||||
|              | ||||
|             // if only payload and topic - display payload type | ||||
|             // if only one property - show it's type | ||||
|             // if more than one property (other than payload and topic) - show "x properties" where x is the number of properties. | ||||
|  | ||||
|             // this.props will not be an array for legacy inject nodes until they are re-deployed | ||||
|             if (Array.isArray(this.props)) { | ||||
|                 var propertyCount = this.props.length; | ||||
|                 var payloadProperty = this.props.find(p => p.p === 'payload'); | ||||
|                 var topicProperty = this.props.find(p => p.p === 'topic' && p.vt === 'str'); | ||||
|                  | ||||
|                 if (payloadProperty && topicProperty) { | ||||
|                     lab = payloadProperty.vt; | ||||
|                 } else if (propertyCount > 1){ | ||||
|                     lab = propertyCount + " " + this._("inject.label.properties"); | ||||
|                 } else if(propertyCount === 1){ | ||||
|                     lab = this.props[0].vt; | ||||
|                 } | ||||
|                  | ||||
|             } else { | ||||
|  | ||||
|                 lab = this.payloadType; | ||||
|                 if (lab === "json") { | ||||
|                     try { | ||||
|                         lab = typeof JSON.parse(this.payload); | ||||
|                         if (lab === "object") { | ||||
|                             if (Array.isArray(JSON.parse(this.payload))) { lab = "Array"; } | ||||
|                         } | ||||
|                     } catch(e) { | ||||
|                         return this._("inject.label.invalid"); | ||||
|                     } | ||||
|                 } catch(e) { | ||||
|                     return this._("inject.label.invalid"); } | ||||
|                 } | ||||
|             } | ||||
|              | ||||
|             var name = "inject.label."+lab; | ||||
|             var label = this._(name); | ||||
|             if (name !== label) { | ||||
|                 return label; | ||||
|             } | ||||
|             if (name !== label) { lab = label; } | ||||
|  | ||||
|             return lab; | ||||
|         }, | ||||
|         label: function() { | ||||
|         label: function() {         | ||||
|             if (Array.isArray(this.props)) { | ||||
|                 // find the payload & topic | ||||
|                 var payloadProperty = this.props.find(p => p.p === 'payload'); | ||||
|                 var topicProperty = this.props.find(p => p.p === 'topic' && p.vt === 'str'); | ||||
|                  | ||||
|                 // If no payload/topic are found, use the first property instead | ||||
|                 if(this.props[0]){ | ||||
|                     payloadProperty = payloadProperty === undefined ? this.props[0] : payloadProperty; | ||||
|                     topicProperty = topicProperty === undefined ? {v: payloadProperty.p} : topicProperty; // if no topic, use the property name of the payload | ||||
|                 } | ||||
|  | ||||
|                 var payload = payloadProperty === undefined ? "" : payloadProperty.v; | ||||
|                 var payloadType = payloadProperty === undefined ? "str" : payloadProperty.vt; | ||||
|                 var topic = topicProperty === undefined ? "" : topicProperty.v;                 | ||||
|             } else { | ||||
|                 /* Legacy */ | ||||
|                 var payload = this.payload; | ||||
|                 var payloadType = this.payloadType; | ||||
|                 var topic = this.topic; | ||||
|             } | ||||
|              | ||||
|              | ||||
|             var suffix = ""; | ||||
|             // if fire once then add small indication | ||||
|             if (this.once) { | ||||
| @@ -201,27 +243,27 @@ | ||||
|             } | ||||
|             if (this.name) { | ||||
|                 return this.name+suffix; | ||||
|             } else if (this.payloadType === "string" || | ||||
|                     this.payloadType === "str" || | ||||
|                     this.payloadType === "num" || | ||||
|                     this.payloadType === "bool" || | ||||
|                     this.payloadType === "json") { | ||||
|                 if ((this.topic !== "") && ((this.topic.length + this.payload.length) <= 32)) { | ||||
|                     return this.topic + ":" + this.payload+suffix; | ||||
|                 } else if (this.payload.length > 0 && this.payload.length < 24) { | ||||
|                     return this.payload+suffix; | ||||
|             } else if (payloadType === "string" || | ||||
|                     payloadType === "str" || | ||||
|                     payloadType === "num" || | ||||
|                     payloadType === "bool" || | ||||
|                     payloadType === "json") { | ||||
|                 if ((topic !== "") && ((topic.length + payload.length) <= 32)) { | ||||
|                     return topic + ":" + payload+suffix; | ||||
|                 } else if (payload.length > 0 && payload.length < 24) { | ||||
|                     return payload+suffix; | ||||
|                 } else { | ||||
|                     return this._("inject.inject")+suffix; | ||||
|                 } | ||||
|             } else if (this.payloadType === 'date') { | ||||
|                 if ((this.topic !== "") && (this.topic.length <= 16)) { | ||||
|                     return this.topic + ":" + this._("inject.timestamp")+suffix; | ||||
|             } else if (payloadType === 'date' || payloadType === 'bin' || payloadType === 'env') { | ||||
|                 if ((topic !== "") && (topic.length <= 16)) { | ||||
|                     return topic + ":" + this._(`inject.label.${payloadType}`)+suffix; | ||||
|                 } else { | ||||
|                     return this._("inject.timestamp")+suffix; | ||||
|                     return this._(`inject.label.${payloadType}`)+suffix; | ||||
|                 } | ||||
|             } else if (this.payloadType === 'flow' || this.payloadType === 'global') { | ||||
|                 var key = RED.utils.parseContextKey(this.payload); | ||||
|                 return this.payloadType+"."+key.key+suffix; | ||||
|             } else if (payloadType === 'flow' || payloadType === 'global') { | ||||
|                 var key = RED.utils.parseContextKey(payload); | ||||
|                 return payloadType+"."+key.key+suffix; | ||||
|             } else { | ||||
|                 return this._("inject.inject")+suffix; | ||||
|             } | ||||
| @@ -259,6 +301,10 @@ | ||||
|                     $("#node-once").hide(); | ||||
|                     $("#node-input-once").prop('checked', false); | ||||
|                 } | ||||
|                  | ||||
|                 // Scroll down | ||||
|                 var scrollDiv = $("#dialog-form").parent(); | ||||
|                 scrollDiv.scrollTop(scrollDiv.prop('scrollHeight')); | ||||
|             }); | ||||
|  | ||||
|             $("#node-input-once").on("change", function() { | ||||
| @@ -383,8 +429,117 @@ | ||||
|             $("#inject-time-type-select").trigger("change"); | ||||
|             $("#inject-time-interval-time-start").trigger("change"); | ||||
|  | ||||
|             /* */ | ||||
|  | ||||
|             function resizeItem(item) { | ||||
|                 var newWidth = item.width(); | ||||
|  | ||||
|                 item.find('.node-input-prop-property-name').typedInput("width", '155px'); | ||||
|                 item.find('.node-input-prop-property-value').typedInput("width", `${newWidth - 180}px`); | ||||
|             } | ||||
|  | ||||
|             $('#node-input-property-container').css('min-height','150px').css('min-width','450px').editableList({ | ||||
|                 addItem: function(container,i,opt) { | ||||
|                     var prop = opt; | ||||
|                     if (!prop.hasOwnProperty('p')) { | ||||
|                         prop = {p:"",v:"",vt:"str"}; | ||||
|                     } | ||||
|                     container.css({ | ||||
|                         overflow: 'hidden', | ||||
|                         whiteSpace: 'nowrap' | ||||
|                     }); | ||||
|                     var row = $('<div/>').appendTo(container); | ||||
|  | ||||
|                     var propertyName = $('<input/>',{class:"node-input-prop-property-name",type:"text"}) | ||||
|                         .appendTo(row) | ||||
|                         .typedInput({types:['msg']}); | ||||
|  | ||||
|                     $('<div/>',{style: 'display:inline-block; padding:0px 4px 0px 4px;'}) | ||||
|                         .text('=') | ||||
|                         .appendTo(row); | ||||
|  | ||||
|                     var propertyValue = $('<input/>',{class:"node-input-prop-property-value",type:"text"}) | ||||
|                         .appendTo(row) | ||||
|                         .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','date','jsonata','env']}); | ||||
|  | ||||
|                     propertyName.typedInput('value',prop.p); | ||||
|  | ||||
|                     propertyValue.typedInput('value',prop.v); | ||||
|                     propertyValue.typedInput('type',prop.vt); | ||||
|  | ||||
|                     resizeItem(container); | ||||
|                 }, | ||||
|                 resizeItem: resizeItem, | ||||
|                 removable: true, | ||||
|                 sortable: true | ||||
|             }); | ||||
|  | ||||
|             if (!this.props) { | ||||
|                 var payload = { | ||||
|                     p:'payload', | ||||
|                     v: this.payload ? this.payload : '', | ||||
|                     vt:this.payloadType ? this.payloadType : 'date' | ||||
|                 }; | ||||
|                 var topic = { | ||||
|                     p:'topic', | ||||
|                     v: this.topic ? this.topic : '', | ||||
|                     vt:'string' | ||||
|                 } | ||||
|  | ||||
|                 this.props = [payload,topic]; | ||||
|             } | ||||
|  | ||||
|             for (var i=0; i<this.props.length; i++) { | ||||
|                 var prop = this.props[i]; | ||||
|                 $("#node-input-property-container").editableList('addItem',prop); | ||||
|             } | ||||
|              | ||||
|             /* Experimental paste object | ||||
|              * This allows you to copy an object to your clipboard from the debug and then paste it back into an inject node | ||||
|              */ | ||||
|             // $("#dialog-form").on('paste', function(e) { | ||||
|             //   var pasteData = e.originalEvent.clipboardData.getData('text'); | ||||
|             //   try{ | ||||
|             //       var pasteObject = JSON.parse(pasteData); | ||||
|             //   } catch(e){ } | ||||
|             // | ||||
|             //   if(pasteObject){ | ||||
|             //       for(var p in pasteObject){ | ||||
|             //           if(p === '_msgid') continue; | ||||
|             //           var v = pasteObject[p]; | ||||
|             //           var vt = 'json'; | ||||
|             // | ||||
|             //           // Remove existing property before adding to avoid duplicates | ||||
|             //           $(`#node-input-property-container .node-input-prop-property-name[value=${p}]`).closest('.red-ui-editableList-item-content').parent().remove(); | ||||
|             // | ||||
|             //           if(typeof v === 'string'){ | ||||
|             //               vt = 'str'; | ||||
|             //           } else if (typeof v === "boolean") { | ||||
|             //               vt = 'bool'; | ||||
|             //           } else if (!isNaN(v)) { | ||||
|             //               vt = 'num'; | ||||
|             //           } else if(Array.isArray(v) && v.every(e => Number.isInteger(e) && e >= 0 && e <=255)) { // Fuzzy buffer detection | ||||
|             //               vt = 'bin'; | ||||
|             //               v = JSON.stringify(pasteObject[p]); | ||||
|             //           } else { | ||||
|             //               vt = 'json'; | ||||
|             //               v = JSON.stringify(pasteObject[p]); | ||||
|             //           } | ||||
|             // | ||||
|             //           var prop = {p, v, vt}; | ||||
|             //           $("#node-input-property-container").editableList('addItem',prop); | ||||
|             //       } | ||||
|             //   } | ||||
|             // }); | ||||
|  | ||||
|         }, | ||||
|         oneditsave: function() { | ||||
|             /* Cleanup Legacy */ | ||||
|             delete this.payload; | ||||
|             delete this.payloadType | ||||
|             delete this.topic | ||||
|             /* */ | ||||
|              | ||||
|             var repeat = ""; | ||||
|             var crontab = ""; | ||||
|             var type = $("#inject-time-type-select").val(); | ||||
| @@ -474,6 +629,22 @@ | ||||
|  | ||||
|             $("#node-input-repeat").val(repeat); | ||||
|             $("#node-input-crontab").val(crontab); | ||||
|  | ||||
|             /* Gather the injected properties of the msg object */ | ||||
|             var props = $("#node-input-property-container").editableList('items'); | ||||
|             var node = this; | ||||
|             node.props= []; | ||||
|             props.each(function(i) { | ||||
|                 var prop = $(this); | ||||
|                 var p = { | ||||
|                     p:prop.find(".node-input-prop-property-name").typedInput('value') | ||||
|                 }; | ||||
|  | ||||
|                 p.v = prop.find(".node-input-prop-property-value").typedInput('value'); | ||||
|                 p.vt = prop.find(".node-input-prop-property-value").typedInput('type'); | ||||
|  | ||||
|                 node.props.push(p); | ||||
|             }); | ||||
|         }, | ||||
|         button: { | ||||
|             enabled: function() { | ||||
| @@ -483,12 +654,7 @@ | ||||
|                 if (this.changed) { | ||||
|                     return RED.notify(RED._("notification.warning", {message:RED._("notification.warnings.undeployedChanges")}),"warning"); | ||||
|                 } | ||||
|                 var payload = this.payload; | ||||
|                 if ((this.payloadType === 'flow') || | ||||
|                     (this.payloadType === 'global')) { | ||||
|                     var key = RED.utils.parseContextKey(payload); | ||||
|                     payload = this.payloadType+"."+key.key; | ||||
|                 } | ||||
|  | ||||
|                 var label = this._def.label.call(this); | ||||
|                 if (label.length > 30) { | ||||
|                     label = label.substring(0,50)+"..."; | ||||
| @@ -514,6 +680,17 @@ | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         }, | ||||
|         oneditresize: function(size) { | ||||
|             var rows = $("#dialog-form>div:not(.node-input-property-container-row):visible"); | ||||
|             var height = size.height; | ||||
|             for (var i=0; i<rows.length; i++) { | ||||
|                 height -= $(rows[i]).outerHeight(true); | ||||
|             } | ||||
|             var editorRow = $("#dialog-form>div.node-input-property-container-row"); | ||||
|             height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom"))); | ||||
|             height += 16; | ||||
|             $("#node-input-property-container").editableList('height',height); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|   | ||||
| @@ -20,9 +20,23 @@ module.exports = function(RED) { | ||||
|  | ||||
|     function InjectNode(n) { | ||||
|         RED.nodes.createNode(this,n); | ||||
|         this.topic = n.topic; | ||||
|         this.payload = n.payload; | ||||
|         this.payloadType = n.payloadType; | ||||
|          | ||||
|         /* Handle legacy */ | ||||
|         if(!Array.isArray(n.props)){ | ||||
|             n.props = []; | ||||
|             n.props.push({ | ||||
|                 p:'payload', | ||||
|                 v:n.payload, | ||||
|                 vt:n.payloadType | ||||
|             }); | ||||
|             n.props.push({ | ||||
|                 p:'topic', | ||||
|                 v:n.topic, | ||||
|                 vt:'str' | ||||
|             }); | ||||
|         } | ||||
|          | ||||
|         this.props = n.props; | ||||
|         this.repeat = n.repeat; | ||||
|         this.crontab = n.crontab; | ||||
|         this.once = n.once; | ||||
| @@ -62,34 +76,27 @@ module.exports = function(RED) { | ||||
|           node.repeaterSetup(); | ||||
|         } | ||||
|  | ||||
|         this.on("input",function(msg) { | ||||
|             msg.topic = this.topic; | ||||
|             if (this.payloadType !== 'flow' && this.payloadType !== 'global') { | ||||
|                 try { | ||||
|                     if ( (this.payloadType == null && this.payload === "") || this.payloadType === "date") { | ||||
|                         msg.payload = Date.now(); | ||||
|                     } else if (this.payloadType == null) { | ||||
|                         msg.payload = this.payload; | ||||
|                     } else if (this.payloadType === 'none') { | ||||
|                         msg.payload = ""; | ||||
|                     } else { | ||||
|                         msg.payload = RED.util.evaluateNodeProperty(this.payload,this.payloadType,this,msg); | ||||
|                     } | ||||
|                     this.send(msg); | ||||
|                     msg = null; | ||||
|                 } catch(err) { | ||||
|                     this.error(err,msg); | ||||
|                 } | ||||
|             } else { | ||||
|                 RED.util.evaluateNodeProperty(this.payload,this.payloadType,this,msg, function(err,res) { | ||||
|                     if (err) { | ||||
|                         node.error(err,msg); | ||||
|                     } else { | ||||
|                         msg.payload = res; | ||||
|                         node.send(msg); | ||||
|                     } | ||||
|         this.on("input", function(msg) { | ||||
|             var errors = []; | ||||
|  | ||||
|                 }); | ||||
|             this.props.forEach(p => { | ||||
|                 var property = p.p; | ||||
|                 var value = p.v ? p.v : ''; | ||||
|                 var valueType = p.vt ? p.vt : 'str'; | ||||
|  | ||||
|                 if (!property) return; | ||||
|  | ||||
|                 try { | ||||
|                     RED.util.setMessageProperty(msg,property,RED.util.evaluateNodeProperty(value, valueType, this, msg),true); | ||||
|                 } catch (err) { | ||||
|                     errors.push(err); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             if (errors.length) { | ||||
|                 node.error(errors.join('; '), msg); | ||||
|             } else { | ||||
|                 node.send(msg); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|   | ||||
| @@ -37,6 +37,7 @@ | ||||
|         "stopped": "stopped", | ||||
|         "failed": "Inject failed: __error__", | ||||
|         "label": { | ||||
|             "properties": "Properties", | ||||
|             "repeat": "Repeat", | ||||
|             "flow": "flow context", | ||||
|             "global": "global context", | ||||
| @@ -79,7 +80,6 @@ | ||||
|         "on": "on", | ||||
|         "onstart": "Inject once after", | ||||
|         "onceDelay": "seconds, then", | ||||
|         "tip": "<b>Note:</b> \"interval between times\" and \"at a specific time\" will use cron.<br/>\"interval\" should be 596 hours or less.<br/>See info box for details.", | ||||
|         "success": "Successfully injected: __label__", | ||||
|         "errors": { | ||||
|             "failed": "inject failed, see log for details", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user