Compare commits

..

48 Commits

Author SHA1 Message Date
Nick O'Leary
1c3644e338 Fix various issues with debug pop-out window 2023-11-30 11:23:35 +00:00
Nick O'Leary
2dfabb523b Merge pull request #4457 from node-red/4456-fix-group-in-subflow-lookup
Ensure subflow instances keep track of their groups
2023-11-29 16:41:52 +00:00
Nick O'Leary
3e2d20e536 Merge pull request #4455 from GogoVega/4429-fix-validateNodeProperty
Fix `validateNodeProperty` without validator provided
2023-11-29 16:19:41 +00:00
Nick O'Leary
ee7ee083b0 Merge pull request #4440 from node-red/4429-add-typed-validators
Add validators to any fields using msg-typed Input
2023-11-29 16:18:53 +00:00
Nick O'Leary
fb54c05d9f Ensure subflow instances keep track of their groups 2023-11-29 16:12:12 +00:00
Nick O'Leary
21f807aa66 Merge pull request #4453 from node-red/4413-debounce-uninstall-notifications
Debounce node-removed notifications
2023-11-29 16:11:18 +00:00
Nick O'Leary
6b088bda12 Merge pull request #4452 from node-red/4443-add-modules-install-audit-event
Add modules.install audit event when external module installed
2023-11-29 16:11:04 +00:00
Nick O'Leary
a32ee869ae Merge pull request #4448 from bonanitech/first-commit-has-no-parents
Don't try to load the parents of the first commit
2023-11-29 16:10:42 +00:00
GogoVega
a2d7772958 Fix validateNodeProperty if no validator provided 2023-11-28 20:13:25 +01:00
Stephen McLaughlin
6ec052be18 Merge pull request #4454 from node-red/4380-mqtt-undefined-value
Guard against node.broker being undefined
2023-11-27 17:48:02 +00:00
Nick O'Leary
9c71d52d69 Check node.broker is a string before trying to use it
Fixes #4380
2023-11-27 17:27:32 +00:00
Nick O'Leary
171c146ec5 Debounce node-removed notifications
Fixes #4413
2023-11-27 17:14:15 +00:00
Nick O'Leary
bc6afa2164 Add modules.install audit event when external module installed 2023-11-27 16:58:14 +00:00
Stephen McLaughlin
3c036257ef Merge pull request #4451 from node-red/4446-allow-import-of-subpath-modules
Allow import of modules with subpath in specifier
2023-11-27 16:57:32 +00:00
Nick O'Leary
6633730bf1 Allow import of modules with subpath in specifier
Fixes #4446
2023-11-27 16:44:56 +00:00
Mauricio Bonani
2964a4da5e Don't try to load the parents of the first commit 2023-11-24 16:22:29 -05:00
Stephen McLaughlin
a55554193b Merge pull request #4441 from node-red/4274-allow-theme-to-set-mermaid-theme
Allow a theme to specifiy which theme mermaid should use
2023-11-20 21:43:03 +00:00
Nick O'Leary
d2a8338d4a Rename mermaidOptions to mermaid for contrib themes 2023-11-20 17:50:59 +00:00
Nick O'Leary
e945deeab6 Allow mermaid theme to be set via editorTheme and custom themes
Fixes #4274
2023-11-20 17:47:39 +00:00
Nick O'Leary
722fe02933 Add validators to any fields using msg-typed Input
Fixes #4429
2023-11-20 17:17:52 +00:00
Nick O'Leary
3dec609459 Merge pull request #4438 from node-red/update-deps-2
Update node-red-admin version
2023-11-20 10:00:44 +01:00
Nick O'Leary
e73b9f646d Update nr package to match 2023-11-17 10:44:34 +00:00
Nick O'Leary
3cea6400d1 Update node-red-admin version 2023-11-17 10:42:06 +00:00
Nick O'Leary
c6a8eee73d Merge pull request #4416 from node-red/mqtt-check-topic-length
check topic length > 0 before publish
2023-11-07 17:46:33 +00:00
Nick O'Leary
74d431ea36 Merge pull request #4427 from node-red/4394-update-page-title-on-flow-nav
Update browser title with flow name if set
2023-11-07 17:40:36 +00:00
Nick O'Leary
409a559a13 Merge pull request #4411 from node-red/4376-handle-falsy-env-vars
Handle false-like env vars properly
2023-11-07 17:40:18 +00:00
Nick O'Leary
6829535350 Merge pull request #4409 from node-red/4381-avoid-multiple-settings-saves
Only save settings once during node load process
2023-11-07 17:40:03 +00:00
Nick O'Leary
6488111f79 Merge pull request #4423 from node-red/4415-handle-excluded-core-nodes
Ensure typeSearch handles undefined node definitions
2023-11-07 17:39:46 +00:00
Nick O'Leary
923339c1d8 Merge pull request #4426 from node-red/4044-group-bb-import
Ensure group w/h are imported if present
2023-11-07 17:39:30 +00:00
Nick O'Leary
3f4d96f4cd Merge pull request #4425 from node-red/4418-fix-empty-status-background
Hide node status background when there is no status to show
2023-11-07 17:26:00 +00:00
Nick O'Leary
c52985d245 Update browser title with flow name if set
Fixes #4394
2023-11-07 17:24:04 +00:00
Nick O'Leary
88e6c71aa0 Ensure group w/h are imported if present
Fixes #4044
2023-11-07 17:09:22 +00:00
Nick O'Leary
4d08e297c4 Hide node status background when there is no status to show
Fixes #4418
2023-11-07 16:34:36 +00:00
Nick O'Leary
ad2b30691f Ensure typeSearch handles undefined node definitions
Fixes #4415
2023-11-03 11:28:04 +01:00
Dave Conway-Jones
369bad01b8 check topic length > 0 before publish
to close #4414
2023-11-03 08:55:38 +00:00
Nick O'Leary
b6ecc6d9ea Handle false-like env vars properly 2023-11-02 00:40:55 +01:00
Nick O'Leary
0117df0960 Only save settings once during node load process 2023-11-01 16:18:03 +01:00
Nick O'Leary
6ac905f264 Merge pull request #4407 from node-red/4392-add-close-button-to-restart-notification
Add a close button to the restart-required notification
2023-11-01 15:20:48 +01:00
Nick O'Leary
e5307f6604 Add a close button to the restart-required notification
Fixes #4392
2023-11-01 14:39:51 +01:00
Nick O'Leary
8d9b6dd859 Merge pull request #4405 from node-red/4396-fix-global-config-cred-lookup
Ensure global-config nodes lookup cred values properly
2023-11-01 14:20:56 +01:00
Nick O'Leary
01821ead0f Merge pull request #4371 from ralphwetzel/master
Extend typedInput "num" type validity check to NaN, binary, octal & hex
2023-11-01 14:20:31 +01:00
Nick O'Leary
1451fb9e2f Merge pull request #4399 from kazuhitoyokoi/master-fixrepeat4inject
Fix unintended new line in node name
2023-11-01 14:17:21 +01:00
Nick O'Leary
245751bb23 Merge pull request #4382 from hazymat/master
Ctrl-Enter does not close tray (Monaco) #4377
2023-11-01 14:15:08 +01:00
Nick O'Leary
f07519c705 Merge pull request #4393 from node-red/Fix-buffer-viewer-to-handle-0b-style-
fix buffer viewer to handle 0b style binary
2023-11-01 14:14:20 +01:00
Kazuhito Yokoi
fae3a5c26a Fix unintended new line in node name 2023-10-29 14:58:03 +09:00
Dave Conway-Jones
1a52c0adfc fix buffer viewer to handle 0b style binary 2023-10-24 23:15:34 +01:00
Mat Smith
44c0bbc61e Ctrl-Enter does not close tray (Monaco) #4377 2023-10-12 18:53:34 +01:00
ralphwetzel
5ac50fae3a Extend typedInput "num" type validity check to NaN, binary, octal & hex 2023-09-26 20:00:48 +02:00
36 changed files with 317 additions and 188 deletions

View File

@@ -64,7 +64,7 @@
"mqtt": "4.3.7",
"multer": "1.4.5-lts.1",
"mustache": "4.2.0",
"node-red-admin": "^3.1.0",
"node-red-admin": "^3.1.1",
"node-watch": "0.7.4",
"nopt": "5.0.0",
"oauth2orize": "1.11.1",

View File

@@ -339,6 +339,8 @@ module.exports = {
}
theme.codeEditor = theme.codeEditor || {}
theme.codeEditor.options = Object.assign({}, themePlugin.monacoOptions, theme.codeEditor.options);
theme.mermaid = Object.assign({}, themePlugin.mermaid, theme.mermaid)
}
activeThemeInitialised = true;
}

View File

@@ -2198,6 +2198,12 @@ RED.nodes = (function() {
}
node._config.x = node.x;
node._config.y = node.y;
if (n.hasOwnProperty('w')) {
node.w = n.w
}
if (n.hasOwnProperty('h')) {
node.h = n.h
}
} else if (n.type.substring(0,7) === "subflow") {
var parentId = n.type.split(":")[1];
var subflow = subflow_denylist[parentId]||subflow_map[parentId]||getSubflow(parentId);

View File

@@ -498,6 +498,15 @@ var RED = (function() {
]
}
}
} else if (notificationId === 'restart-required') {
options.buttons = [
{
text: RED._("common.label.close"),
click: function() {
persistentNotifications[notificationId].hideNotification();
}
}
]
}
if (!persistentNotifications.hasOwnProperty(notificationId)) {
persistentNotifications[notificationId] = RED.notify(text,options);
@@ -525,6 +534,10 @@ var RED = (function() {
RED.view.redrawStatus(node);
}
});
let pendingNodeRemovedNotifications = []
let pendingNodeRemovedTimeout
RED.comms.subscribe("notification/node/#",function(topic,msg) {
var i,m;
var typeList;
@@ -562,8 +575,15 @@ var RED = (function() {
m = msg[i];
info = RED.nodes.removeNodeSet(m.id);
if (info.added) {
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");
pendingNodeRemovedNotifications = pendingNodeRemovedNotifications.concat(m.types.map(RED.utils.sanitize))
if (pendingNodeRemovedTimeout) {
clearTimeout(pendingNodeRemovedTimeout)
}
pendingNodeRemovedTimeout = setTimeout(function () {
typeList = "<ul><li>"+pendingNodeRemovedNotifications.join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeRemoved", {count:pendingNodeRemovedNotifications.length})+typeList,"success");
pendingNodeRemovedNotifications = []
}, 200)
}
}
loadIconList();

View File

@@ -182,7 +182,9 @@
valueLabel: contextLabel
},
str: {value:"str",label:"string",icon:"red/images/typedInput/az.svg"},
num: {value:"num",label:"number",icon:"red/images/typedInput/09.svg",validate:/^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/},
num: {value:"num",label:"number",icon:"red/images/typedInput/09.svg",validate: function(v) {
return (true === RED.utils.validateTypedProperty(v, "num"));
} },
bool: {value:"bool",label:"boolean",icon:"red/images/typedInput/bool.svg",options:["true","false"]},
json: {
value:"json",

View File

@@ -182,6 +182,17 @@ RED.editor = (function() {
error: err.message
});
}
} else if (valid) {
// If the validator is not provided in node property => Check if the input has a validator
if ("category" in node._def) {
const isConfig = node._def.category === "config";
const prefix = isConfig ? "node-config-input" : "node-input";
const input = $("#"+prefix+"-"+property);
const isTypedInput = input.length > 0 && input.next(".red-ui-typedInput-container").length > 0;
if (isTypedInput) {
valid = input.typedInput("validate");
}
}
}
if (valid && definition[property].type && RED.nodes.getType(definition[property].type) && !("validate" in definition[property])) {
if (!value || value == "_ADD_") {

View File

@@ -121,7 +121,7 @@
var i=0,l=bufferBinValue.length;
var c = 0;
for(i=0;i<l;i++) {
var d = parseInt(bufferBinValue[i]);
var d = parseInt(Number(bufferBinValue[i]));
if (!isString && (isNaN(d) || d < 0 || d > 255)) {
valid = false;
break;

View File

@@ -966,12 +966,10 @@ RED.editor.codeEditor.monaco = (function() {
//Unbind ctrl-Enter (default action is to insert a newline in editor) This permits the shortcut to close the tray.
try {
ed._standaloneKeybindingService.addDynamicKeybinding(
'-editor.action.insertLineAfter', // command ID prefixed by '-'
null, // keybinding
() => {} // need to pass an empty handler
);
} catch (error) { }
monaco.editor.addKeybindingRule({keybinding: 0, command: "-editor.action.insertLineAfter"});
} catch (error) {
console.warn(error)
}
ed.nodered = {
refreshModuleLibs: refreshModuleLibs //expose this for function node externalModules refresh

View File

@@ -15,7 +15,8 @@ RED.editor.mermaid = (function () {
'vendor/mermaid/mermaid.min.js',
function (data, stat, jqxhr) {
mermaid.initialize({
startOnLoad: false
startOnLoad: false,
theme: RED.settings.get('mermaid', {}).theme
})
loaded = true
while(pendingEvals.length > 0) {

View File

@@ -647,9 +647,9 @@ RED.sidebar.versionControl = (function() {
$.getJSON("projects/"+activeProject.name+"/commits/"+entry.sha,function(result) {
result.project = activeProject;
result.parents = entry.parents;
result.oldRev = entry.sha+"~1";
result.oldRev = entry.parents[0].length !== 0 ? entry.sha+"~1" : entry.sha;
result.newRev = entry.sha;
result.oldRevTitle = RED._("sidebar.project.versionControl.commitCapital")+" "+entry.sha.substring(0,7)+"~1";
result.oldRevTitle = entry.parents[0].length !== 0 ? RED._("sidebar.project.versionControl.commitCapital")+" "+entry.sha.substring(0,7)+"~1" : " ";
result.newRevTitle = RED._("sidebar.project.versionControl.commitCapital")+" "+entry.sha.substring(0,7);
result.date = humanizeSinceDate(parseInt(entry.date));
RED.diff.showCommitDiff(result);

View File

@@ -323,7 +323,7 @@ RED.typeSearch = (function() {
}
}
function applyFilter(filter,type,def) {
return !filter ||
return !def || !filter ||
(
(!filter.spliceMultiple) &&
(!filter.type || type === filter.type) &&

View File

@@ -921,7 +921,7 @@ RED.utils = (function() {
error = RED._("validator.errors.invalid-prop")
}
} else if (propertyType === 'num') {
if (!/^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/.test(propertyValue)) {
if (!/^NaN$|^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$|^[+-]?(0b|0B)[01]+$|^[+-]?(0o|0O)[0-7]+$|^[+-]?(0x|0X)[0-9a-fA-F]+$/.test(propertyValue)) {
error = RED._("validator.errors.invalid-num")
}
} else if (propertyType === 'jsonata') {

View File

@@ -4187,7 +4187,7 @@ RED.view = (function() {
nodeEl.__statusGroup__.style.display = "none";
} else {
nodeEl.__statusGroup__.style.display = "inline";
let backgroundWidth = 12
let backgroundWidth = 15
var fill = status_colours[d.status.fill]; // Only allow our colours for now
if (d.status.shape == null && fill == null) {
backgroundWidth = 0
@@ -4207,7 +4207,11 @@ RED.view = (function() {
nodeEl.__statusLabel__.textContent = "";
}
const textSize = nodeEl.__statusLabel__.getBBox()
nodeEl.__statusBackground__.setAttribute('width', backgroundWidth + textSize.width + 6)
backgroundWidth += textSize.width
if (backgroundWidth > 0 && textSize.width > 0) {
backgroundWidth += 6
}
nodeEl.__statusBackground__.setAttribute('width', backgroundWidth)
}
delete d.dirtyStatus;
}
@@ -4619,8 +4623,8 @@ RED.view = (function() {
statusBackground.setAttribute("y",-1);
statusBackground.setAttribute("width",200);
statusBackground.setAttribute("height",13);
statusBackground.setAttribute("rx",1);
statusBackground.setAttribute("ry",1);
statusBackground.setAttribute("rx",2);
statusBackground.setAttribute("ry",2);
statusEl.appendChild(statusBackground);
node[0][0].__statusBackground__ = statusBackground;

View File

@@ -17,6 +17,8 @@
RED.workspaces = (function() {
const documentTitle = document.title;
var activeWorkspace = 0;
var workspaceIndex = 0;
@@ -339,12 +341,18 @@ RED.workspaces = (function() {
$("#red-ui-workspace-chart").show();
activeWorkspace = tab.id;
window.location.hash = 'flow/'+tab.id;
if (tab.label) {
document.title = `${documentTitle} : ${tab.label}`
} else {
document.title = documentTitle
}
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled", !!tab.disabled);
$("#red-ui-workspace").toggleClass("red-ui-workspace-locked", !!tab.locked);
} else {
$("#red-ui-workspace-chart").hide();
activeWorkspace = 0;
window.location.hash = '';
document.title = documentTitle
}
event.workspace = activeWorkspace;
RED.events.emit("workspace:change",event);

View File

@@ -40,9 +40,22 @@ RED.validators = {
return opt ? RED._("validator.errors.invalid-regexp") : false;
};
},
typedInput: function(ptypeName,isConfig,mopt) {
typedInput: function(ptypeName, isConfig, mopt) {
let options = ptypeName
if (typeof ptypeName === 'string' ) {
options = {}
options.typeField = ptypeName
options.isConfig = isConfig
options.allowBlank = false
}
return function(v, opt) {
var ptype = $("#node-"+(isConfig?"config-":"")+"input-"+ptypeName).val() || this[ptypeName];
let ptype = options.type
if (!ptype && options.typeField) {
ptype = $("#node-"+(options.isConfig?"config-":"")+"input-"+options.typeField).val() || this[options.typeField];
}
if (options.allowBlank && v === '') {
return true
}
const result = RED.utils.validateTypedProperty(v, ptype, opt)
if (result === true || opt) {
// Valid, or opt provided - return result as-is

View File

@@ -320,7 +320,7 @@
}
// but replace with repeat one if set to repeat
if ((this.repeat && this.repeat != 0) || this.crontab) {
suffix = " ↻";
suffix = "\t↻";
}
if (this.name) {
return this.name+suffix;

View File

@@ -109,9 +109,8 @@ module.exports = function(RED) {
}
const p = props.shift()
const property = p.p;
const value = p.v ? p.v : '';
const valueType = p.vt ? p.vt : 'str';
const value = p.v !== undefined ? p.v : '';
const valueType = p.vt !== undefined ? p.vt : 'str';
if (property) {
if (valueType === "jsonata") {
if (p.v) {

View File

@@ -86,7 +86,7 @@
},
label: function() {
var suffix = "";
if (this.console === true || this.console === "true") { suffix = " ⇲"; }
if (this.console === true || this.console === "true") { suffix = "\t⇲"; }
if (this.targetType === "jsonata") {
return (this.name || "JSONata") + suffix;
}
@@ -195,6 +195,119 @@
node.dirty = true;
});
RED.view.redraw();
},
requestDebugNodeList: function(filteredNodes) {
var workspaceOrder = RED.nodes.getWorkspaceOrder();
var workspaceOrderMap = {};
workspaceOrder.forEach(function(ws,i) {
workspaceOrderMap[ws] = i;
});
var candidateNodes = [];
var candidateSFs = [];
var subflows = {};
RED.nodes.eachNode(function (n) {
var nt = n.type;
if (nt === "debug") {
if (n.z in workspaceOrderMap) {
candidateNodes.push(n);
}
else {
var sf = RED.nodes.subflow(n.z);
if (sf) {
subflows[sf.id] = {
debug: true,
subflows: {}
};
}
}
}
else if(nt.substring(0, 8) === "subflow:") {
if (n.z in workspaceOrderMap) {
candidateSFs.push(n);
}
else {
var psf = RED.nodes.subflow(n.z);
if (psf) {
var sid = nt.substring(8);
var item = subflows[psf.id];
if (!item) {
item = {
debug: undefined,
subflows: {}
};
subflows[psf.id] = item;
}
item.subflows[sid] = true;
}
}
}
});
candidateSFs.forEach(function (sf) {
var sid = sf.type.substring(8);
if (containsDebug(sid, subflows)) {
candidateNodes.push(sf);
}
});
candidateNodes.sort(function(A,B) {
var wsA = workspaceOrderMap[A.z];
var wsB = workspaceOrderMap[B.z];
if (wsA !== wsB) {
return wsA-wsB;
}
var labelA = RED.utils.getNodeLabel(A,A.id);
var labelB = RED.utils.getNodeLabel(B,B.id);
return labelA.localeCompare(labelB);
});
var currentWs = null;
var data = [];
var currentFlow;
var currentSelectedCount = 0;
candidateNodes.forEach(function(node) {
if (currentWs !== node.z) {
if (currentFlow && currentFlow.checkbox) {
currentFlow.selected = currentSelectedCount === currentFlow.children.length
}
currentSelectedCount = 0;
currentWs = node.z;
var parent = RED.nodes.workspace(currentWs) || RED.nodes.subflow(currentWs);
currentFlow = {
label: RED.utils.getNodeLabel(parent, currentWs),
}
if (!parent.disabled) {
currentFlow.children = [];
currentFlow.checkbox = true;
} else {
currentFlow.class = "disabled"
}
data.push(currentFlow);
}
if (currentFlow.children) {
if (!filteredNodes[node.id]) {
currentSelectedCount++;
}
currentFlow.children.push({
label: RED.utils.getNodeLabel(node,node.id),
node: {
id: node.id
},
checkbox: true,
selected: !filteredNodes[node.id]
});
}
});
if (currentFlow && currentFlow.checkbox) {
currentFlow.selected = currentSelectedCount === currentFlow.children.length
}
if (subWindow) {
try {
subWindow.postMessage({event:"refreshDebugNodeList", nodes:data},"*");
} catch(err) {
console.log(err);
}
}
RED.debug.refreshDebugNodeList(data)
}
};
@@ -427,6 +540,8 @@
options.messageSourceClick(msg.id,msg._alias,msg.path);
} else if (msg.event === "clear") {
options.clear();
} else if (msg.event === "requestDebugNodeList") {
options.requestDebugNodeList(msg.filteredNodes)
}
};
window.addEventListener('message',this.handleWindowMessage);

View File

@@ -167,19 +167,13 @@ RED.debug = (function() {
var menu = RED.popover.menu({
options: options,
onselect: function(item) {
if (item.value !== filterType) {
filterType = item.value;
$('#red-ui-sidebar-debug-filter span').text(RED._('node-red:debug.sidebar.'+filterType));
refreshMessageList();
RED.settings.set("debug.filter",filterType)
}
setFilterType(item.value)
if (filterType === 'filterSelected') {
refreshDebugNodeList();
config.requestDebugNodeList(filteredNodes);
filterDialog.slideDown(200);
filterDialogShown = true;
debugNodeTreeList.focus();
}
}
});
menu.show({
@@ -276,109 +270,7 @@ RED.debug = (function() {
}
function refreshDebugNodeList() {
var workspaceOrder = RED.nodes.getWorkspaceOrder();
var workspaceOrderMap = {};
workspaceOrder.forEach(function(ws,i) {
workspaceOrderMap[ws] = i;
});
var candidateNodes = [];
var candidateSFs = [];
var subflows = {};
RED.nodes.eachNode(function (n) {
var nt = n.type;
if (nt === "debug") {
if (n.z in workspaceOrderMap) {
candidateNodes.push(n);
}
else {
var sf = RED.nodes.subflow(n.z);
if (sf) {
subflows[sf.id] = {
debug: true,
subflows: {}
};
}
}
}
else if(nt.substring(0, 8) === "subflow:") {
if (n.z in workspaceOrderMap) {
candidateSFs.push(n);
}
else {
var psf = RED.nodes.subflow(n.z);
if (psf) {
var sid = nt.substring(8);
var item = subflows[psf.id];
if (!item) {
item = {
debug: undefined,
subflows: {}
};
subflows[psf.id] = item;
}
item.subflows[sid] = true;
}
}
}
});
candidateSFs.forEach(function (sf) {
var sid = sf.type.substring(8);
if (containsDebug(sid, subflows)) {
candidateNodes.push(sf);
}
});
candidateNodes.sort(function(A,B) {
var wsA = workspaceOrderMap[A.z];
var wsB = workspaceOrderMap[B.z];
if (wsA !== wsB) {
return wsA-wsB;
}
var labelA = RED.utils.getNodeLabel(A,A.id);
var labelB = RED.utils.getNodeLabel(B,B.id);
return labelA.localeCompare(labelB);
});
var currentWs = null;
var data = [];
var currentFlow;
var currentSelectedCount = 0;
candidateNodes.forEach(function(node) {
if (currentWs !== node.z) {
if (currentFlow && currentFlow.checkbox) {
currentFlow.selected = currentSelectedCount === currentFlow.children.length
}
currentSelectedCount = 0;
currentWs = node.z;
var parent = RED.nodes.workspace(currentWs) || RED.nodes.subflow(currentWs);
currentFlow = {
label: RED.utils.getNodeLabel(parent, currentWs),
}
if (!parent.disabled) {
currentFlow.children = [];
currentFlow.checkbox = true;
} else {
currentFlow.class = "disabled"
}
data.push(currentFlow);
}
if (currentFlow.children) {
if (!filteredNodes[node.id]) {
currentSelectedCount++;
}
currentFlow.children.push({
label: RED.utils.getNodeLabel(node,node.id),
node: node,
checkbox: true,
selected: !filteredNodes[node.id]
});
}
});
if (currentFlow && currentFlow.checkbox) {
currentFlow.selected = currentSelectedCount === currentFlow.children.length
}
function refreshDebugNodeList(data) {
debugNodeTreeList.treeList("data", data);
}
@@ -401,7 +293,7 @@ RED.debug = (function() {
},200);
}
function _refreshMessageList(_activeWorkspace) {
if (_activeWorkspace) {
if (typeof _activeWorkspace === 'string') {
activeWorkspace = _activeWorkspace.replace(/\./g,"_");
}
if (filterType === "filterAll") {
@@ -479,12 +371,12 @@ RED.debug = (function() {
filteredNodes[n.id] = true;
});
delete filteredNodes[sourceId];
$("#red-ui-sidebar-debug-filterSelected").trigger("click");
RED.settings.set('debug.filteredNodes',Object.keys(filteredNodes))
setFilterType('filterSelected')
refreshMessageList();
}},
{id:"red-ui-debug-msg-menu-item-clear-filter",label:RED._("node-red:debug.messageMenu.clearFilter"),onselect:function(){
$("#red-ui-sidebar-debug-filterAll").trigger("click");
clearFilterSettings()
refreshMessageList();
}}
);
@@ -713,9 +605,17 @@ RED.debug = (function() {
if (!!clearFilter) {
clearFilterSettings();
}
refreshDebugNodeList();
config.requestDebugNodeList(filteredNodes);
}
function setFilterType(type) {
if (type !== filterType) {
filterType = type;
$('#red-ui-sidebar-debug-filter span').text(RED._('node-red:debug.sidebar.'+filterType));
refreshMessageList();
RED.settings.set("debug.filter",filterType)
}
}
function clearFilterSettings() {
filteredNodes = {};
filterType = 'filterAll';
@@ -728,6 +628,7 @@ RED.debug = (function() {
init: init,
refreshMessageList:refreshMessageList,
handleDebugMessage: handleDebugMessage,
clearMessageList: clearMessageList
clearMessageList: clearMessageList,
refreshDebugNodeList: refreshDebugNodeList
}
})();

View File

@@ -12,6 +12,9 @@ $(function() {
},
clear: function() {
window.opener.postMessage({event:"clear"},'*');
},
requestDebugNodeList: function(filteredNodes) {
window.opener.postMessage({event: 'requestDebugNodeList', filteredNodes},'*')
}
}
@@ -26,6 +29,8 @@ $(function() {
RED.debug.refreshMessageList(evt.data.activeWorkspace);
} else if (evt.data.event === "projectChange") {
RED.debug.clearMessageList(true);
} else if (evt.data.event === "refreshDebugNodeList") {
RED.debug.refreshDebugNodeList(evt.data.nodes)
}
},false);
} catch(err) {

View File

@@ -57,7 +57,12 @@
action: {value:"scale"},
round: {value:false},
property: {value:"payload",required:true,
label:RED._("node-red:common.label.property")},
label:RED._("node-red:common.label.property"),
validate: RED.validators.typedInput({ type: 'msg' })
},
// RED.validators.typedInput("propertyType", false)},
name: {value:""}
},
inputs: 1,

View File

@@ -56,7 +56,7 @@
color:"darksalmon",
defaults: {
command: {value:""},
addpay: {value:""},
addpay: {value:"", validate: RED.validators.typedInput({ type: 'msg', allowBlank: true })},
append: {value:""},
useSpawn: {value:"false"},
timer: {value:""},

View File

@@ -56,9 +56,11 @@
inout: {value:"out"},
septopics: {value:true},
property: {value:"payload", required:true,
label:RED._("node-red:rbe.label.property")},
label:RED._("node-red:rbe.label.property"),
validate: RED.validators.typedInput({ type: 'msg' })},
topi: {value:"topic", required:true,
label:RED._("node-red:rbe.label.topic")}
label:RED._("node-red:rbe.label.topic"),
validate: RED.validators.typedInput({ type: 'msg' })}
},
inputs:1,
outputs:1,

View File

@@ -104,6 +104,7 @@ module.exports = function(RED) {
* @returns `true` if it is a valid topic
*/
function isValidPublishTopic(topic) {
if (topic.length === 0) return false;
return !/[\+#\b\f\n\r\t\v\0]/.test(topic);
}
@@ -219,8 +220,8 @@ module.exports = function(RED) {
*/
function subscriptionHandler(node, datatype ,topic, payload, packet) {
const msg = {topic:topic, payload:null, qos:packet.qos, retain:packet.retain};
const v5 = (node && node.brokerConn)
? node.brokerConn.v5()
const v5 = (node && node.brokerConn)
? node.brokerConn.v5()
: Object.prototype.hasOwnProperty.call(packet, "properties");
if(v5 && packet.properties) {
setStrProp(packet.properties, msg, "responseTopic");
@@ -451,7 +452,7 @@ module.exports = function(RED) {
/**
* Perform the disconnect action
* @param {MQTTInNode|MQTTOutNode} node
* @param {MQTTInNode|MQTTOutNode} node
* @param {Function} done
*/
function handleDisconnectAction(node, done) {
@@ -611,7 +612,7 @@ module.exports = function(RED) {
node.brokerurl = node.url;
} else {
// if the broker is ws:// or wss:// or tcp://
if (node.broker.indexOf("://") > -1) {
if ((typeof node.broker === 'string') && node.broker.indexOf("://") > -1) {
node.brokerurl = node.broker;
// Only for ws or wss, check if proxy env var for additional configuration
if (node.brokerurl.indexOf("wss://") > -1 || node.brokerurl.indexOf("ws://") > -1) {
@@ -865,7 +866,7 @@ module.exports = function(RED) {
* Call end and wait for the client to end (or timeout)
* @param {mqtt.MqttClient} client The broker client
* @param {number} ms The time to wait for the client to end
* @returns
* @returns
*/
let waitEnd = (client, ms) => {
return new Promise( (resolve, reject) => {
@@ -905,7 +906,7 @@ module.exports = function(RED) {
node.subid = 1;
//typedef for subscription object:
/**
/**
* @typedef {Object} Subscription
* @property {String} topic - topic to subscribe to
* @property {Object} [options] - options object
@@ -933,7 +934,7 @@ module.exports = function(RED) {
const ref = _ref || 0;
let options
let qos = 1 // default to QoS 1 (AWS and several other brokers don't support QoS 2)
// if options is an object, then clone it
if (typeof _options == "object") {
options = RED.util.cloneMessage(_options || {})
@@ -947,7 +948,7 @@ module.exports = function(RED) {
if (typeof qos === "number" && qos >= 0 && qos <= 2) {
options.qos = qos;
}
subscription.topic = _topic;
subscription.qos = qos;
subscription.options = RED.util.cloneMessage(options);
@@ -957,16 +958,16 @@ module.exports = function(RED) {
}
/**
* If topic is a subscription object, then use that, otherwise look up the topic in
* If topic is a subscription object, then use that, otherwise look up the topic in
* the subscriptions object. If the topic is not found, then create a new subscription
* object and add it to the subscriptions object.
* @param {Subscription|String} topic
* @param {*} options
* @param {*} callback
* @param {*} ref
* @param {Subscription|String} topic
* @param {*} options
* @param {*} callback
* @param {*} ref
*/
node.subscribe = function (topic, options, callback, ref) {
/** @type {Subscription} */
/** @type {Subscription} */
let subscription
let doCompare = false
let changesFound = false
@@ -1004,7 +1005,7 @@ module.exports = function(RED) {
_brokerConn.unsubscribe(sub.topic, sub.ref, true)
}
})
// if subscription is found (or sent in as a parameter), then check for changes.
// if there are any changes requested, tidy up the old subscription
if (subscription) {
@@ -1091,7 +1092,7 @@ module.exports = function(RED) {
delete sub[ref]
}
}
// if instructed to remove the actual MQTT client subscription
// if instructed to remove the actual MQTT client subscription
if (unsub) {
// if there are no more subscriptions for the topic, then remove the topic
if (Object.keys(sub).length === 0) {

View File

@@ -41,8 +41,8 @@
color:"#DEBD5C",
defaults: {
name: {value:""},
property: {value:"payload"},
outproperty: {value:"payload"},
property: {value:"payload", validate: RED.validators.typedInput({ type: 'msg' }) },
outproperty: {value:"payload", validate: RED.validators.typedInput({ type: 'msg' }) },
tag: {value:""},
ret: {value:"html"},
as: {value:"single"}

View File

@@ -32,6 +32,7 @@
defaults: {
name: {value:""},
property: {value:"payload",required:true,
validate: RED.validators.typedInput({ type: 'msg' }),
label:RED._("node-red:json.label.property")},
action: {value:""},
pretty: {value:false}

View File

@@ -27,7 +27,8 @@
defaults: {
name: {value:""},
property: {value:"payload",required:true,
label:RED._("node-red:common.label.property")},
label:RED._("node-red:common.label.property"),
validate: RED.validators.typedInput({ type: 'msg' })},
attr: {value:""},
chr: {value:""}
},

View File

@@ -16,6 +16,7 @@
color:"#DEBD5C",
defaults: {
property: {value:"payload",required:true,
validate: RED.validators.typedInput({ type: 'msg' }),
label:RED._("node-red:common.label.property")},
name: {value:""}
},

View File

@@ -57,7 +57,7 @@
arraySplt: {value:1},
arraySpltType: {value:"len"},
stream: {value:false},
addname: {value:""}
addname: {value:"", validate: RED.validators.typedInput({ type: 'msg', allowBlank: true })}
},
inputs:1,
outputs:1,
@@ -208,7 +208,22 @@
validate:RED.validators.typedInput("propertyType", false)
},
propertyType: { value:"msg"},
key: {value:"topic"},
key: {value:"topic", validate: (function () {
const typeValidator = RED.validators.typedInput({ type: 'msg' })
return function(v, opt) {
const joinMode = $("#node-input-mode").val() || this.mode
if (joinMode !== 'custom') {
return true
}
const buildType = $("#node-input-build").val() || this.build
if (buildType !== 'object') {
return true
} else {
return typeValidator(v, opt)
}
}
})()
},
joiner: { value:"\\n"},
joinerType: { value:"str"},
accumulate: { value:"false" },

View File

@@ -198,7 +198,7 @@
category: 'storage',
defaults: {
name: {value:""},
filename: {value:""},
filename: {value:"", validate: RED.validators.typedInput({ typeField: 'filenameType' })},
filenameType: {value:"str"},
appendNewline: {value:true},
createDir: {value:false},
@@ -297,7 +297,7 @@
category: 'storage',
defaults: {
name: {value:""},
filename: {value:""},
filename: {value:"", validate: RED.validators.typedInput({ typeField: 'filenameType' }) },
filenameType: {value:"str"},
format: {value:"utf8"},
chunk: {value:false},

View File

@@ -98,7 +98,7 @@ function requireModule(module) {
const parsedModule = parseModuleName(module);
if (BUILTIN_MODULES.indexOf(parsedModule.module) !== -1) {
return require(parsedModule.module);
return require(parsedModule.module + parsedModule.subpath);
}
if (!knownExternalModules[parsedModule.module]) {
const e = new Error("Module not allowed");
@@ -131,7 +131,7 @@ function importModule(module) {
const parsedModule = parseModuleName(module);
if (BUILTIN_MODULES.indexOf(parsedModule.module) !== -1) {
return import(parsedModule.module);
return import(parsedModule.module + parsedModule.subpath);
}
if (!knownExternalModules[parsedModule.module]) {
const e = new Error("Module not allowed");
@@ -152,12 +152,13 @@ function importModule(module) {
}
function parseModuleName(module) {
var match = /((?:@[^/]+\/)?[^/@]+)(?:@([\s\S]+))?/.exec(module);
var match = /((?:@[^/]+\/)?[^/@]+)(\/[^/@]+)?(?:@([\s\S]+))?/.exec(module);
if (match) {
return {
spec: module,
module: match[1],
version: match[2],
subpath: match[2] || '',
version: match[3],
builtin: BUILTIN_MODULES.indexOf(match[1]) !== -1,
known: !!knownExternalModules[match[1]]
}
@@ -283,6 +284,7 @@ async function installModule(moduleDetails) {
const runtimeInstalledModules = settings.get("modules") || {};
runtimeInstalledModules[moduleDetails.module] = moduleDetails;
settings.set("modules",runtimeInstalledModules)
log.audit({event: "modules.install",module:moduleDetails.module, version:moduleDetails.version});
}).catch(result => {
var output = result.stderr || result.toString();
var e;

View File

@@ -143,6 +143,12 @@ function loadModuleFiles(modules) {
return loadNodeSetList(pluginList);
}).then(function() {
return loadNodeSetList(nodeList);
}).then(function () {
if (settings.available()) {
return registry.saveNodeList();
} else {
return
}
})
}
@@ -436,7 +442,7 @@ async function loadPlugin(plugin) {
return plugin;
}
}
let invocation = 0
function loadNodeSetList(nodes) {
var promises = [];
nodes.forEach(function(node) {
@@ -451,13 +457,7 @@ function loadNodeSetList(nodes) {
}
});
return Promise.all(promises).then(function() {
if (settings.available()) {
return registry.saveNodeList();
} else {
return;
}
});
return Promise.all(promises)
}
function addModule(module) {

View File

@@ -99,6 +99,9 @@ var api = module.exports = {
safeSettings.markdownEditor = runtime.settings.editorTheme.markdownEditor || {};
safeSettings.markdownEditor.mermaid = safeSettings.markdownEditor.mermaid || { enabled: true };
}
if (runtime.settings.editorTheme.mermaid) {
safeSettings.mermaid = runtime.settings.editorTheme.mermaid
}
}
safeSettings.libraries = runtime.library.getLibraries();
if (util.isArray(runtime.settings.paletteCategories)) {

View File

@@ -73,9 +73,20 @@ class Subflow extends Flow {
id: subflowInstance.id,
configs: {},
nodes: {},
groups: {},
subflows: {}
}
if (subflowDef.groups) {
// Clone all of the subflow group definitions and give them new IDs
for (i in subflowDef.groups) {
if (subflowDef.groups.hasOwnProperty(i)) {
node = createNodeInSubflow(subflowInstance.id,subflowDef.groups[i]);
node_map[node._alias] = node;
subflowInternalFlowConfig.groups[node.id] = node;
}
}
}
if (subflowDef.configs) {
// Clone all of the subflow config node definitions and give them new IDs
for (i in subflowDef.configs) {

View File

@@ -57,18 +57,20 @@ var EnvVarPropertyRE = /^\${(\S+)}$/;
function mapEnvVarProperties(obj,prop,flow,config) {
var v = obj[prop];
const v = obj[prop];
if (Buffer.isBuffer(v)) {
return;
} else if (Array.isArray(v)) {
for (var i=0;i<v.length;i++) {
for (let i=0;i<v.length;i++) {
mapEnvVarProperties(v,i,flow,config);
}
} else if (typeof obj[prop] === 'string') {
if (obj[prop][0] === "$" && (EnvVarPropertyRE_old.test(v) || EnvVarPropertyRE.test(v)) ) {
var envVar = v.substring(2,v.length-1);
var r = redUtil.getSetting(config, envVar, flow);
obj[prop] = r ? r : obj[prop];
const envVar = v.substring(2,v.length-1);
const r = redUtil.getSetting(config, envVar, flow);
if (r !== undefined && r !== '') {
obj[prop] = r
}
}
} else {
for (var p in v) {

View File

@@ -39,7 +39,7 @@
"bcryptjs": "2.4.3",
"express": "4.18.2",
"fs-extra": "11.1.1",
"node-red-admin": "^3.1.0",
"node-red-admin": "^3.1.1",
"nopt": "5.0.0",
"semver": "7.5.4"
},