mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Merge branch 'dev' into button-add-config-node
This commit is contained in:
commit
6e7fa6f921
@ -29,6 +29,15 @@ Nodes
|
|||||||
- Let debug node status msg length be settable via settings (#4402) @dceejay
|
- Let debug node status msg length be settable via settings (#4402) @dceejay
|
||||||
- Feat: Add ability to set headers for WebSocket client (#4436) @marcus-j-davies
|
- Feat: Add ability to set headers for WebSocket client (#4436) @marcus-j-davies
|
||||||
|
|
||||||
|
#### 3.1.8: Maintenance Release
|
||||||
|
|
||||||
|
- Add validation and error handling on subflow instance properties (#4632) @knolleary
|
||||||
|
- Hide import/export context menu if disabled in theme (#4633) @knolleary
|
||||||
|
- Show change indicator on subflow tabs (#4631) @knolleary
|
||||||
|
- Bump dependencies (#4630) @knolleary
|
||||||
|
- Reset workspace index when clearing nodes (#4619) @knolleary
|
||||||
|
- Remove typo in global config (#4613) @kazuhitoyokoi
|
||||||
|
|
||||||
#### 3.1.7: Maintenance Release
|
#### 3.1.7: Maintenance Release
|
||||||
|
|
||||||
- Add Japanese translation for v3.1.6 (#4603) @kazuhitoyokoi
|
- Add Japanese translation for v3.1.6 (#4603) @kazuhitoyokoi
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
"cors": "2.8.5",
|
"cors": "2.8.5",
|
||||||
"cronosjs": "1.7.1",
|
"cronosjs": "1.7.1",
|
||||||
"denque": "2.1.0",
|
"denque": "2.1.0",
|
||||||
"express": "4.18.2",
|
"express": "4.19.2",
|
||||||
"express-session": "1.17.3",
|
"express-session": "1.17.3",
|
||||||
"form-data": "4.0.0",
|
"form-data": "4.0.0",
|
||||||
"fs-extra": "11.1.1",
|
"fs-extra": "11.1.1",
|
||||||
@ -64,7 +64,7 @@
|
|||||||
"mqtt": "4.3.7",
|
"mqtt": "4.3.7",
|
||||||
"multer": "1.4.5-lts.1",
|
"multer": "1.4.5-lts.1",
|
||||||
"mustache": "4.2.0",
|
"mustache": "4.2.0",
|
||||||
"node-red-admin": "^3.1.2",
|
"node-red-admin": "^3.1.3",
|
||||||
"node-watch": "0.7.4",
|
"node-watch": "0.7.4",
|
||||||
"nopt": "5.0.0",
|
"nopt": "5.0.0",
|
||||||
"oauth2orize": "1.11.1",
|
"oauth2orize": "1.11.1",
|
||||||
@ -112,7 +112,7 @@
|
|||||||
"mermaid": "^10.4.0",
|
"mermaid": "^10.4.0",
|
||||||
"minami": "1.2.3",
|
"minami": "1.2.3",
|
||||||
"mocha": "9.2.2",
|
"mocha": "9.2.2",
|
||||||
"node-red-node-test-helper": "^0.3.2",
|
"node-red-node-test-helper": "^0.3.3",
|
||||||
"nodemon": "2.0.20",
|
"nodemon": "2.0.20",
|
||||||
"proxy": "^1.0.2",
|
"proxy": "^1.0.2",
|
||||||
"sass": "1.62.1",
|
"sass": "1.62.1",
|
||||||
|
@ -91,6 +91,7 @@ module.exports = {
|
|||||||
// Plugins
|
// Plugins
|
||||||
adminApp.get("/plugins", needsPermission("plugins.read"), plugins.getAll, apiUtil.errorHandler);
|
adminApp.get("/plugins", needsPermission("plugins.read"), plugins.getAll, apiUtil.errorHandler);
|
||||||
adminApp.get("/plugins/messages", needsPermission("plugins.read"), plugins.getCatalogs, apiUtil.errorHandler);
|
adminApp.get("/plugins/messages", needsPermission("plugins.read"), plugins.getCatalogs, apiUtil.errorHandler);
|
||||||
|
adminApp.get(/^\/plugins\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,needsPermission("plugins.read"),plugins.getConfig,apiUtil.errorHandler);
|
||||||
|
|
||||||
adminApp.get("/diagnostics", needsPermission("diagnostics.read"), diagnostics.getReport, apiUtil.errorHandler);
|
adminApp.get("/diagnostics", needsPermission("diagnostics.read"), diagnostics.getReport, apiUtil.errorHandler);
|
||||||
|
|
||||||
|
@ -40,5 +40,31 @@ module.exports = {
|
|||||||
console.log(err.stack);
|
console.log(err.stack);
|
||||||
apiUtils.rejectHandler(req,res,err);
|
apiUtils.rejectHandler(req,res,err);
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
getConfig: function(req, res) {
|
||||||
|
|
||||||
|
let opts = {
|
||||||
|
user: req.user,
|
||||||
|
module: req.params[0],
|
||||||
|
req: apiUtils.getRequestLogObject(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.get("accept") === "application/json") {
|
||||||
|
runtimeAPI.nodes.getNodeInfo(opts.module).then(function(result) {
|
||||||
|
res.send(result);
|
||||||
|
}).catch(function(err) {
|
||||||
|
apiUtils.rejectHandler(req,res,err);
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
opts.lang = apiUtils.determineLangFromHeaders(req.acceptsLanguages());
|
||||||
|
if (/[^0-9a-z=\-\*]/i.test(opts.lang)) {
|
||||||
|
opts.lang = "en-US";
|
||||||
|
}
|
||||||
|
runtimeAPI.plugins.getPluginConfig(opts).then(function(result) {
|
||||||
|
return res.send(result);
|
||||||
|
}).catch(function(err) {
|
||||||
|
apiUtils.rejectHandler(req,res,err);
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -77,6 +77,53 @@ function CommsConnection(ws, user) {
|
|||||||
log.trace("comms.close "+self.session);
|
log.trace("comms.close "+self.session);
|
||||||
removeActiveConnection(self);
|
removeActiveConnection(self);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleAuthPacket = function(msg) {
|
||||||
|
Tokens.get(msg.auth).then(function(client) {
|
||||||
|
if (client) {
|
||||||
|
Users.get(client.user).then(function(user) {
|
||||||
|
if (user) {
|
||||||
|
self.user = user;
|
||||||
|
log.audit({event: "comms.auth",user:self.user});
|
||||||
|
completeConnection(msg, client.scope,msg.auth,true);
|
||||||
|
} else {
|
||||||
|
log.audit({event: "comms.auth.fail"});
|
||||||
|
completeConnection(msg, null,null,false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Users.tokens(msg.auth).then(function(user) {
|
||||||
|
if (user) {
|
||||||
|
self.user = user;
|
||||||
|
log.audit({event: "comms.auth",user:self.user});
|
||||||
|
completeConnection(msg, user.permissions,msg.auth,true);
|
||||||
|
} else {
|
||||||
|
log.audit({event: "comms.auth.fail"});
|
||||||
|
completeConnection(msg, null,null,false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const completeConnection = function(msg, userScope, session, sendAck) {
|
||||||
|
try {
|
||||||
|
if (!userScope || !Permissions.hasPermission(userScope,"status.read")) {
|
||||||
|
ws.send(JSON.stringify({auth:"fail"}));
|
||||||
|
ws.close();
|
||||||
|
} else {
|
||||||
|
pendingAuth = false;
|
||||||
|
addActiveConnection(self);
|
||||||
|
self.token = msg.auth;
|
||||||
|
if (sendAck) {
|
||||||
|
ws.send(JSON.stringify({auth:"ok"}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
console.log(err.stack);
|
||||||
|
// Just in case the socket closes before we attempt
|
||||||
|
// to send anything.
|
||||||
|
}
|
||||||
|
}
|
||||||
ws.on('message', function(data,flags) {
|
ws.on('message', function(data,flags) {
|
||||||
var msg = null;
|
var msg = null;
|
||||||
try {
|
try {
|
||||||
@ -86,68 +133,34 @@ function CommsConnection(ws, user) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!pendingAuth) {
|
if (!pendingAuth) {
|
||||||
if (msg.subscribe) {
|
if (msg.auth) {
|
||||||
|
handleAuthPacket(msg)
|
||||||
|
} else if (msg.subscribe) {
|
||||||
self.subscribe(msg.subscribe);
|
self.subscribe(msg.subscribe);
|
||||||
// handleRemoteSubscription(ws,msg.subscribe);
|
// handleRemoteSubscription(ws,msg.subscribe);
|
||||||
|
} else if (msg.topic) {
|
||||||
|
runtimeAPI.comms.receive({
|
||||||
|
user: self.user,
|
||||||
|
client: self,
|
||||||
|
topic: msg.topic,
|
||||||
|
data: msg.data
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var completeConnection = function(userScope,session,sendAck) {
|
|
||||||
try {
|
|
||||||
if (!userScope || !Permissions.hasPermission(userScope,"status.read")) {
|
|
||||||
ws.send(JSON.stringify({auth:"fail"}));
|
|
||||||
ws.close();
|
|
||||||
} else {
|
|
||||||
pendingAuth = false;
|
|
||||||
addActiveConnection(self);
|
|
||||||
self.token = msg.auth;
|
|
||||||
if (sendAck) {
|
|
||||||
ws.send(JSON.stringify({auth:"ok"}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch(err) {
|
|
||||||
console.log(err.stack);
|
|
||||||
// Just in case the socket closes before we attempt
|
|
||||||
// to send anything.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (msg.auth) {
|
if (msg.auth) {
|
||||||
Tokens.get(msg.auth).then(function(client) {
|
handleAuthPacket(msg)
|
||||||
if (client) {
|
|
||||||
Users.get(client.user).then(function(user) {
|
|
||||||
if (user) {
|
|
||||||
self.user = user;
|
|
||||||
log.audit({event: "comms.auth",user:self.user});
|
|
||||||
completeConnection(client.scope,msg.auth,true);
|
|
||||||
} else {
|
|
||||||
log.audit({event: "comms.auth.fail"});
|
|
||||||
completeConnection(null,null,false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Users.tokens(msg.auth).then(function(user) {
|
|
||||||
if (user) {
|
|
||||||
self.user = user;
|
|
||||||
log.audit({event: "comms.auth",user:self.user});
|
|
||||||
completeConnection(user.permissions,msg.auth,true);
|
|
||||||
} else {
|
|
||||||
log.audit({event: "comms.auth.fail"});
|
|
||||||
completeConnection(null,null,false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
if (anonymousUser) {
|
if (anonymousUser) {
|
||||||
log.audit({event: "comms.auth",user:anonymousUser});
|
log.audit({event: "comms.auth",user:anonymousUser});
|
||||||
self.user = anonymousUser;
|
self.user = anonymousUser;
|
||||||
completeConnection(anonymousUser.permissions,null,false);
|
completeConnection(msg, anonymousUser.permissions, null, false);
|
||||||
//TODO: duplicated code - pull non-auth message handling out
|
//TODO: duplicated code - pull non-auth message handling out
|
||||||
if (msg.subscribe) {
|
if (msg.subscribe) {
|
||||||
self.subscribe(msg.subscribe);
|
self.subscribe(msg.subscribe);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.audit({event: "comms.auth.fail"});
|
log.audit({event: "comms.auth.fail"});
|
||||||
completeConnection(null,null,false);
|
completeConnection(msg, null,null,false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
"clone": "2.1.2",
|
"clone": "2.1.2",
|
||||||
"cors": "2.8.5",
|
"cors": "2.8.5",
|
||||||
"express-session": "1.17.3",
|
"express-session": "1.17.3",
|
||||||
"express": "4.18.2",
|
"express": "4.19.2",
|
||||||
"memorystore": "1.6.7",
|
"memorystore": "1.6.7",
|
||||||
"mime": "3.0.0",
|
"mime": "3.0.0",
|
||||||
"multer": "1.4.5-lts.1",
|
"multer": "1.4.5-lts.1",
|
||||||
|
@ -590,6 +590,8 @@
|
|||||||
},
|
},
|
||||||
"nodeCount": "__label__ Node",
|
"nodeCount": "__label__ Node",
|
||||||
"nodeCount_plural": "__label__ Nodes",
|
"nodeCount_plural": "__label__ Nodes",
|
||||||
|
"pluginCount": "__count__ Plugin",
|
||||||
|
"pluginCount_plural": "__count__ Plugins",
|
||||||
"moduleCount": "__count__ Modul verfügbar",
|
"moduleCount": "__count__ Modul verfügbar",
|
||||||
"moduleCount_plural": "__count__ Module verfügbar",
|
"moduleCount_plural": "__count__ Module verfügbar",
|
||||||
"inuse": "In Gebrauch",
|
"inuse": "In Gebrauch",
|
||||||
|
@ -614,6 +614,8 @@
|
|||||||
},
|
},
|
||||||
"nodeCount": "__label__ node",
|
"nodeCount": "__label__ node",
|
||||||
"nodeCount_plural": "__label__ nodes",
|
"nodeCount_plural": "__label__ nodes",
|
||||||
|
"pluginCount": "__count__ plugin",
|
||||||
|
"pluginCount_plural": "__count__ plugins",
|
||||||
"moduleCount": "__count__ module available",
|
"moduleCount": "__count__ module available",
|
||||||
"moduleCount_plural": "__count__ modules available",
|
"moduleCount_plural": "__count__ modules available",
|
||||||
"inuse": "in use",
|
"inuse": "in use",
|
||||||
|
@ -924,7 +924,14 @@
|
|||||||
"date": "horodatage",
|
"date": "horodatage",
|
||||||
"jsonata": "expression",
|
"jsonata": "expression",
|
||||||
"env": "variable d'environnement",
|
"env": "variable d'environnement",
|
||||||
"cred": "identifiant"
|
"cred": "identifiant",
|
||||||
|
"conf-types": "noeud de configuration"
|
||||||
|
},
|
||||||
|
"date": {
|
||||||
|
"format": {
|
||||||
|
"timestamp": "millisecondes depuis l'époque",
|
||||||
|
"object": "Objet de date JavaScript"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"editableList": {
|
"editableList": {
|
||||||
|
@ -26,6 +26,15 @@ RED.comms = (function() {
|
|||||||
var reconnectAttempts = 0;
|
var reconnectAttempts = 0;
|
||||||
var active = false;
|
var active = false;
|
||||||
|
|
||||||
|
RED.events.on('login', function(username) {
|
||||||
|
// User has logged in
|
||||||
|
// Need to upgrade the connection to be authenticated
|
||||||
|
if (ws && ws.readyState == 1) {
|
||||||
|
const auth_tokens = RED.settings.get("auth-tokens");
|
||||||
|
ws.send(JSON.stringify({auth:auth_tokens.access_token}))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
function connectWS() {
|
function connectWS() {
|
||||||
active = true;
|
active = true;
|
||||||
var wspath;
|
var wspath;
|
||||||
@ -56,6 +65,7 @@ RED.comms = (function() {
|
|||||||
ws.send(JSON.stringify({subscribe:t}));
|
ws.send(JSON.stringify({subscribe:t}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
emit('connect')
|
||||||
}
|
}
|
||||||
|
|
||||||
ws = new WebSocket(wspath);
|
ws = new WebSocket(wspath);
|
||||||
@ -180,9 +190,53 @@ RED.comms = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function send(topic, msg) {
|
||||||
|
if (ws && ws.readyState == 1) {
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
topic,
|
||||||
|
data: msg
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventHandlers = {};
|
||||||
|
function on(evt,func) {
|
||||||
|
eventHandlers[evt] = eventHandlers[evt]||[];
|
||||||
|
eventHandlers[evt].push(func);
|
||||||
|
}
|
||||||
|
function off(evt,func) {
|
||||||
|
const handler = eventHandlers[evt];
|
||||||
|
if (handler) {
|
||||||
|
for (let i=0;i<handler.length;i++) {
|
||||||
|
if (handler[i] === func) {
|
||||||
|
handler.splice(i,1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function emit() {
|
||||||
|
const evt = arguments[0]
|
||||||
|
const args = Array.prototype.slice.call(arguments,1);
|
||||||
|
if (eventHandlers[evt]) {
|
||||||
|
let cpyHandlers = [...eventHandlers[evt]];
|
||||||
|
for (let i=0;i<cpyHandlers.length;i++) {
|
||||||
|
try {
|
||||||
|
cpyHandlers[i].apply(null, args);
|
||||||
|
} catch(err) {
|
||||||
|
console.warn("RED.comms.emit error: ["+evt+"] "+(err.toString()));
|
||||||
|
console.warn(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
connect: connectWS,
|
connect: connectWS,
|
||||||
subscribe: subscribe,
|
subscribe: subscribe,
|
||||||
unsubscribe:unsubscribe
|
unsubscribe:unsubscribe,
|
||||||
|
on,
|
||||||
|
off,
|
||||||
|
send
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
@ -149,6 +149,8 @@ RED.nodes = (function() {
|
|||||||
},
|
},
|
||||||
removeNodeSet: function(id) {
|
removeNodeSet: function(id) {
|
||||||
var ns = nodeSets[id];
|
var ns = nodeSets[id];
|
||||||
|
if (!ns) { return {} }
|
||||||
|
|
||||||
for (var j=0;j<ns.types.length;j++) {
|
for (var j=0;j<ns.types.length;j++) {
|
||||||
delete typeToId[ns.types[j]];
|
delete typeToId[ns.types[j]];
|
||||||
}
|
}
|
||||||
@ -572,12 +574,16 @@ RED.nodes = (function() {
|
|||||||
* @param {String} z tab id
|
* @param {String} z tab id
|
||||||
*/
|
*/
|
||||||
checkTabState: function (z) {
|
checkTabState: function (z) {
|
||||||
const ws = workspaces[z]
|
const ws = workspaces[z] || subflows[z]
|
||||||
if (ws) {
|
if (ws) {
|
||||||
const contentsChanged = tabDirtyMap[z].size > 0 || tabDeletedNodesMap[z].size > 0
|
const contentsChanged = tabDirtyMap[z].size > 0 || tabDeletedNodesMap[z].size > 0
|
||||||
if (Boolean(ws.contentsChanged) !== contentsChanged) {
|
if (Boolean(ws.contentsChanged) !== contentsChanged) {
|
||||||
ws.contentsChanged = contentsChanged
|
ws.contentsChanged = contentsChanged
|
||||||
RED.events.emit("flows:change", ws);
|
if (ws.type === 'tab') {
|
||||||
|
RED.events.emit("flows:change", ws);
|
||||||
|
} else {
|
||||||
|
RED.events.emit("subflows:change", ws);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1050,7 +1056,22 @@ RED.nodes = (function() {
|
|||||||
RED.nodes.registerType("subflow:"+sf.id, {
|
RED.nodes.registerType("subflow:"+sf.id, {
|
||||||
defaults:{
|
defaults:{
|
||||||
name:{value:""},
|
name:{value:""},
|
||||||
env:{value:[]}
|
env:{value:[], validate: function(value) {
|
||||||
|
const errors = []
|
||||||
|
if (value) {
|
||||||
|
value.forEach(env => {
|
||||||
|
const r = RED.utils.validateTypedProperty(env.value, env.type)
|
||||||
|
if (r !== true) {
|
||||||
|
errors.push(env.name+': '+r)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (errors.length === 0) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
}}
|
||||||
},
|
},
|
||||||
icon: function() { return sf.icon||"subflow.svg" },
|
icon: function() { return sf.icon||"subflow.svg" },
|
||||||
category: sf.category || "subflows",
|
category: sf.category || "subflows",
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
RED.plugins = (function() {
|
RED.plugins = (function() {
|
||||||
var plugins = {};
|
var plugins = {};
|
||||||
var pluginsByType = {};
|
var pluginsByType = {};
|
||||||
|
var moduleList = {};
|
||||||
|
|
||||||
function registerPlugin(id,definition) {
|
function registerPlugin(id,definition) {
|
||||||
plugins[id] = definition;
|
plugins[id] = definition;
|
||||||
@ -38,9 +39,43 @@ RED.plugins = (function() {
|
|||||||
function getPluginsByType(type) {
|
function getPluginsByType(type) {
|
||||||
return pluginsByType[type] || [];
|
return pluginsByType[type] || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setPluginList(list) {
|
||||||
|
for(let i=0;i<list.length;i++) {
|
||||||
|
let p = list[i];
|
||||||
|
addPlugin(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addPlugin(p) {
|
||||||
|
|
||||||
|
moduleList[p.module] = moduleList[p.module] || {
|
||||||
|
name:p.module,
|
||||||
|
version:p.version,
|
||||||
|
local:p.local,
|
||||||
|
sets:{},
|
||||||
|
plugin: true,
|
||||||
|
id: p.id
|
||||||
|
};
|
||||||
|
if (p.pending_version) {
|
||||||
|
moduleList[p.module].pending_version = p.pending_version;
|
||||||
|
}
|
||||||
|
moduleList[p.module].sets[p.name] = p;
|
||||||
|
|
||||||
|
RED.events.emit("registry:plugin-module-added",p.module);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getModule(module) {
|
||||||
|
return moduleList[module];
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
registerPlugin: registerPlugin,
|
registerPlugin: registerPlugin,
|
||||||
getPlugin: getPlugin,
|
getPlugin: getPlugin,
|
||||||
getPluginsByType: getPluginsByType
|
getPluginsByType: getPluginsByType,
|
||||||
|
|
||||||
|
setPluginList: setPluginList,
|
||||||
|
addPlugin: addPlugin,
|
||||||
|
getModule: getModule
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
@ -25,6 +25,7 @@ var RED = (function() {
|
|||||||
cache: false,
|
cache: false,
|
||||||
url: 'plugins',
|
url: 'plugins',
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
|
RED.plugins.setPluginList(data);
|
||||||
loader.reportProgress(RED._("event.loadPlugins"), 13)
|
loader.reportProgress(RED._("event.loadPlugins"), 13)
|
||||||
RED.i18n.loadPluginCatalogs(function() {
|
RED.i18n.loadPluginCatalogs(function() {
|
||||||
loadPlugins(function() {
|
loadPlugins(function() {
|
||||||
@ -534,6 +535,41 @@ var RED = (function() {
|
|||||||
RED.view.redrawStatus(node);
|
RED.view.redrawStatus(node);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
RED.comms.subscribe("notification/plugin/#",function(topic,msg) {
|
||||||
|
if (topic == "notification/plugin/added") {
|
||||||
|
RED.settings.refreshSettings(function(err, data) {
|
||||||
|
let addedPlugins = [];
|
||||||
|
msg.forEach(function(m) {
|
||||||
|
let id = m.id;
|
||||||
|
RED.plugins.addPlugin(m);
|
||||||
|
|
||||||
|
m.plugins.forEach((p) => {
|
||||||
|
addedPlugins.push(p.id);
|
||||||
|
})
|
||||||
|
|
||||||
|
RED.i18n.loadNodeCatalog(id, function() {
|
||||||
|
var lang = localStorage.getItem("editor-language")||RED.i18n.detectLanguage();
|
||||||
|
$.ajax({
|
||||||
|
headers: {
|
||||||
|
"Accept":"text/html",
|
||||||
|
"Accept-Language": lang
|
||||||
|
},
|
||||||
|
cache: false,
|
||||||
|
url: 'plugins/'+id,
|
||||||
|
success: function(data) {
|
||||||
|
appendPluginConfig(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (addedPlugins.length) {
|
||||||
|
let pluginList = "<ul><li>"+addedPlugins.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>";
|
||||||
|
// ToDo: Adapt notification (node -> plugin)
|
||||||
|
RED.notify(RED._("palette.event.nodeAdded", {count:addedPlugins.length})+pluginList,"success");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let pendingNodeRemovedNotifications = []
|
let pendingNodeRemovedNotifications = []
|
||||||
let pendingNodeRemovedTimeout
|
let pendingNodeRemovedTimeout
|
||||||
|
@ -118,10 +118,16 @@ RED.contextMenu = (function () {
|
|||||||
onselect: 'core:split-wire-with-link-nodes',
|
onselect: 'core:split-wire-with-link-nodes',
|
||||||
disabled: !canEdit || !hasLinks
|
disabled: !canEdit || !hasLinks
|
||||||
},
|
},
|
||||||
null,
|
null
|
||||||
{ onselect: 'core:show-import-dialog', label: RED._('common.label.import')},
|
|
||||||
{ onselect: 'core:show-examples-import-dialog', label: RED._('menu.label.importExample') }
|
|
||||||
)
|
)
|
||||||
|
if (RED.settings.theme("menu.menu-item-import-library", true)) {
|
||||||
|
insertOptions.push(
|
||||||
|
{ onselect: 'core:show-import-dialog', label: RED._('common.label.import')},
|
||||||
|
{ onselect: 'core:show-examples-import-dialog', label: RED._('menu.label.importExample') }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (hasSelection && canEdit) {
|
if (hasSelection && canEdit) {
|
||||||
const nodeOptions = []
|
const nodeOptions = []
|
||||||
if (!hasMultipleSelection && !isGroup) {
|
if (!hasMultipleSelection && !isGroup) {
|
||||||
@ -194,8 +200,14 @@ RED.contextMenu = (function () {
|
|||||||
{ onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !canEdit || !RED.view.clipboard() },
|
{ onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !canEdit || !RED.view.clipboard() },
|
||||||
{ onselect: 'core:delete-selection', label: RED._('keyboard.deleteSelected'), disabled: !canEdit || !canDelete },
|
{ onselect: 'core:delete-selection', label: RED._('keyboard.deleteSelected'), disabled: !canEdit || !canDelete },
|
||||||
{ onselect: 'core:delete-selection-and-reconnect', label: RED._('keyboard.deleteReconnect'), disabled: !canEdit || !canDelete },
|
{ onselect: 'core:delete-selection-and-reconnect', label: RED._('keyboard.deleteReconnect'), disabled: !canEdit || !canDelete },
|
||||||
{ onselect: 'core:show-export-dialog', label: RED._("menu.label.export") },
|
)
|
||||||
{ onselect: 'core:select-all-nodes', label: RED._("keyboard.selectAll") },
|
if (RED.settings.theme("menu.menu-item-export-library", true)) {
|
||||||
|
menuItems.push(
|
||||||
|
{ onselect: 'core:show-export-dialog', label: RED._("menu.label.export") }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
menuItems.push(
|
||||||
|
{ onselect: 'core:select-all-nodes', label: RED._("keyboard.selectAll") }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,10 +153,6 @@ RED.envVar = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function init(done) {
|
function init(done) {
|
||||||
if (!RED.user.hasPermission("settings.write")) {
|
|
||||||
RED.notify(RED._("user.errors.settings"),"error");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
RED.userSettings.add({
|
RED.userSettings.add({
|
||||||
id:'envvar',
|
id:'envvar',
|
||||||
title: RED._("env-var.environment"),
|
title: RED._("env-var.environment"),
|
||||||
|
@ -248,86 +248,106 @@ RED.palette.editor = (function() {
|
|||||||
var moduleInfo = nodeEntries[module].info;
|
var moduleInfo = nodeEntries[module].info;
|
||||||
var nodeEntry = nodeEntries[module].elements;
|
var nodeEntry = nodeEntries[module].elements;
|
||||||
if (nodeEntry) {
|
if (nodeEntry) {
|
||||||
var activeTypeCount = 0;
|
if (moduleInfo.plugin) {
|
||||||
var typeCount = 0;
|
nodeEntry.enableButton.hide();
|
||||||
var errorCount = 0;
|
nodeEntry.removeButton.show();
|
||||||
nodeEntry.errorList.empty();
|
|
||||||
nodeEntries[module].totalUseCount = 0;
|
|
||||||
nodeEntries[module].setUseCount = {};
|
|
||||||
|
|
||||||
for (var setName in moduleInfo.sets) {
|
let pluginCount = 0;
|
||||||
if (moduleInfo.sets.hasOwnProperty(setName)) {
|
for (let setName in moduleInfo.sets) {
|
||||||
var inUseCount = 0;
|
if (moduleInfo.sets.hasOwnProperty(setName)) {
|
||||||
var set = moduleInfo.sets[setName];
|
let set = moduleInfo.sets[setName];
|
||||||
var setElements = nodeEntry.sets[setName];
|
if (set.plugins) {
|
||||||
if (set.err) {
|
pluginCount += set.plugins.length;
|
||||||
errorCount++;
|
|
||||||
var errMessage = set.err;
|
|
||||||
if (set.err.message) {
|
|
||||||
errMessage = set.err.message;
|
|
||||||
} else if (set.err.code) {
|
|
||||||
errMessage = set.err.code;
|
|
||||||
}
|
}
|
||||||
$("<li>").text(errMessage).appendTo(nodeEntry.errorList);
|
|
||||||
}
|
}
|
||||||
if (set.enabled) {
|
}
|
||||||
activeTypeCount += set.types.length;
|
|
||||||
}
|
nodeEntry.setCount.text(RED._('palette.editor.pluginCount',{count:pluginCount,label:pluginCount}));
|
||||||
typeCount += set.types.length;
|
|
||||||
for (var i=0;i<moduleInfo.sets[setName].types.length;i++) {
|
} else {
|
||||||
var t = moduleInfo.sets[setName].types[i];
|
var activeTypeCount = 0;
|
||||||
inUseCount += (typesInUse[t]||0);
|
var typeCount = 0;
|
||||||
var swatch = setElements.swatches[t];
|
var errorCount = 0;
|
||||||
|
nodeEntry.errorList.empty();
|
||||||
|
nodeEntries[module].totalUseCount = 0;
|
||||||
|
nodeEntries[module].setUseCount = {};
|
||||||
|
|
||||||
|
for (var setName in moduleInfo.sets) {
|
||||||
|
if (moduleInfo.sets.hasOwnProperty(setName)) {
|
||||||
|
var inUseCount = 0;
|
||||||
|
const set = moduleInfo.sets[setName];
|
||||||
|
const setElements = nodeEntry.sets[setName]
|
||||||
|
|
||||||
|
if (set.err) {
|
||||||
|
errorCount++;
|
||||||
|
var errMessage = set.err;
|
||||||
|
if (set.err.message) {
|
||||||
|
errMessage = set.err.message;
|
||||||
|
} else if (set.err.code) {
|
||||||
|
errMessage = set.err.code;
|
||||||
|
}
|
||||||
|
$("<li>").text(errMessage).appendTo(nodeEntry.errorList);
|
||||||
|
}
|
||||||
if (set.enabled) {
|
if (set.enabled) {
|
||||||
var def = RED.nodes.getType(t);
|
activeTypeCount += set.types.length;
|
||||||
if (def && def.color) {
|
}
|
||||||
swatch.css({background:RED.utils.getNodeColor(t,def)});
|
typeCount += set.types.length;
|
||||||
swatch.css({border: "1px solid "+getContrastingBorder(swatch.css('backgroundColor'))})
|
for (var i=0;i<moduleInfo.sets[setName].types.length;i++) {
|
||||||
|
var t = moduleInfo.sets[setName].types[i];
|
||||||
|
inUseCount += (typesInUse[t]||0);
|
||||||
|
if (setElements && set.enabled) {
|
||||||
|
var def = RED.nodes.getType(t);
|
||||||
|
if (def && def.color) {
|
||||||
|
setElements.swatches[t].css({background:RED.utils.getNodeColor(t,def)});
|
||||||
|
setElements.swatches[t].css({border: "1px solid "+getContrastingBorder(setElements.swatches[t].css('backgroundColor'))})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
nodeEntries[module].setUseCount[setName] = inUseCount;
|
||||||
nodeEntries[module].setUseCount[setName] = inUseCount;
|
nodeEntries[module].totalUseCount += inUseCount;
|
||||||
nodeEntries[module].totalUseCount += inUseCount;
|
|
||||||
|
|
||||||
if (inUseCount > 0) {
|
if (setElements) {
|
||||||
setElements.enableButton.text(RED._('palette.editor.inuse'));
|
if (inUseCount > 0) {
|
||||||
setElements.enableButton.addClass('disabled');
|
setElements.enableButton.text(RED._('palette.editor.inuse'));
|
||||||
} else {
|
setElements.enableButton.addClass('disabled');
|
||||||
setElements.enableButton.removeClass('disabled');
|
} else {
|
||||||
if (set.enabled) {
|
setElements.enableButton.removeClass('disabled');
|
||||||
setElements.enableButton.text(RED._('palette.editor.disable'));
|
if (set.enabled) {
|
||||||
} else {
|
setElements.enableButton.text(RED._('palette.editor.disable'));
|
||||||
setElements.enableButton.text(RED._('palette.editor.enable'));
|
} else {
|
||||||
|
setElements.enableButton.text(RED._('palette.editor.enable'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setElements.setRow.toggleClass("red-ui-palette-module-set-disabled",!set.enabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setElements.setRow.toggleClass("red-ui-palette-module-set-disabled",!set.enabled);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (errorCount === 0) {
|
if (errorCount === 0) {
|
||||||
nodeEntry.errorRow.hide()
|
nodeEntry.errorRow.hide()
|
||||||
} else {
|
|
||||||
nodeEntry.errorRow.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
var nodeCount = (activeTypeCount === typeCount)?typeCount:activeTypeCount+" / "+typeCount;
|
|
||||||
nodeEntry.setCount.text(RED._('palette.editor.nodeCount',{count:typeCount,label:nodeCount}));
|
|
||||||
|
|
||||||
if (nodeEntries[module].totalUseCount > 0) {
|
|
||||||
nodeEntry.enableButton.text(RED._('palette.editor.inuse'));
|
|
||||||
nodeEntry.enableButton.addClass('disabled');
|
|
||||||
nodeEntry.removeButton.hide();
|
|
||||||
} else {
|
|
||||||
nodeEntry.enableButton.removeClass('disabled');
|
|
||||||
if (moduleInfo.local) {
|
|
||||||
nodeEntry.removeButton.css('display', 'inline-block');
|
|
||||||
}
|
|
||||||
if (activeTypeCount === 0) {
|
|
||||||
nodeEntry.enableButton.text(RED._('palette.editor.enableall'));
|
|
||||||
} else {
|
} else {
|
||||||
nodeEntry.enableButton.text(RED._('palette.editor.disableall'));
|
nodeEntry.errorRow.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
var nodeCount = (activeTypeCount === typeCount)?typeCount:activeTypeCount+" / "+typeCount;
|
||||||
|
nodeEntry.setCount.text(RED._('palette.editor.nodeCount',{count:typeCount,label:nodeCount}));
|
||||||
|
|
||||||
|
if (nodeEntries[module].totalUseCount > 0) {
|
||||||
|
nodeEntry.enableButton.text(RED._('palette.editor.inuse'));
|
||||||
|
nodeEntry.enableButton.addClass('disabled');
|
||||||
|
nodeEntry.removeButton.hide();
|
||||||
|
} else {
|
||||||
|
nodeEntry.enableButton.removeClass('disabled');
|
||||||
|
if (moduleInfo.local) {
|
||||||
|
nodeEntry.removeButton.css('display', 'inline-block');
|
||||||
|
}
|
||||||
|
if (activeTypeCount === 0) {
|
||||||
|
nodeEntry.enableButton.text(RED._('palette.editor.enableall'));
|
||||||
|
} else {
|
||||||
|
nodeEntry.enableButton.text(RED._('palette.editor.disableall'));
|
||||||
|
}
|
||||||
|
nodeEntry.container.toggleClass("disabled",(activeTypeCount === 0));
|
||||||
}
|
}
|
||||||
nodeEntry.container.toggleClass("disabled",(activeTypeCount === 0));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (moduleInfo.pending_version) {
|
if (moduleInfo.pending_version) {
|
||||||
@ -678,6 +698,33 @@ RED.palette.editor = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
RED.events.on("registry:plugin-module-added", function(module) {
|
||||||
|
|
||||||
|
if (!nodeEntries.hasOwnProperty(module)) {
|
||||||
|
nodeEntries[module] = {info:RED.plugins.getModule(module)};
|
||||||
|
var index = [module];
|
||||||
|
for (var s in nodeEntries[module].info.sets) {
|
||||||
|
if (nodeEntries[module].info.sets.hasOwnProperty(s)) {
|
||||||
|
index.push(s);
|
||||||
|
index = index.concat(nodeEntries[module].info.sets[s].types)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nodeEntries[module].index = index.join(",").toLowerCase();
|
||||||
|
nodeList.editableList('addItem', nodeEntries[module]);
|
||||||
|
} else {
|
||||||
|
_refreshNodeModule(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i=0;i<filteredList.length;i++) {
|
||||||
|
if (filteredList[i].info.id === module) {
|
||||||
|
var installButton = filteredList[i].elements.installButton;
|
||||||
|
installButton.addClass('disabled');
|
||||||
|
installButton.text(RED._('palette.editor.installed'));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var settingsPane;
|
var settingsPane;
|
||||||
@ -804,6 +851,7 @@ RED.palette.editor = (function() {
|
|||||||
errorRow: errorRow,
|
errorRow: errorRow,
|
||||||
errorList: errorList,
|
errorList: errorList,
|
||||||
setCount: setCount,
|
setCount: setCount,
|
||||||
|
setButton: setButton,
|
||||||
container: container,
|
container: container,
|
||||||
shade: shade,
|
shade: shade,
|
||||||
versionSpan: versionSpan,
|
versionSpan: versionSpan,
|
||||||
@ -814,49 +862,88 @@ RED.palette.editor = (function() {
|
|||||||
if (container.hasClass('expanded')) {
|
if (container.hasClass('expanded')) {
|
||||||
container.removeClass('expanded');
|
container.removeClass('expanded');
|
||||||
contentRow.slideUp();
|
contentRow.slideUp();
|
||||||
|
setTimeout(() => {
|
||||||
|
contentRow.empty()
|
||||||
|
}, 200)
|
||||||
|
object.elements.sets = {}
|
||||||
} else {
|
} else {
|
||||||
container.addClass('expanded');
|
container.addClass('expanded');
|
||||||
|
populateSetList()
|
||||||
contentRow.slideDown();
|
contentRow.slideDown();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const populateSetList = function () {
|
||||||
var setList = Object.keys(entry.sets)
|
var setList = Object.keys(entry.sets)
|
||||||
setList.sort(function(A,B) {
|
setList.sort(function(A,B) {
|
||||||
return A.toLowerCase().localeCompare(B.toLowerCase());
|
return A.toLowerCase().localeCompare(B.toLowerCase());
|
||||||
});
|
});
|
||||||
setList.forEach(function(setName) {
|
setList.forEach(function(setName) {
|
||||||
var set = entry.sets[setName];
|
var set = entry.sets[setName];
|
||||||
var setRow = $('<div>',{class:"red-ui-palette-module-set"}).appendTo(contentRow);
|
var setRow = $('<div>',{class:"red-ui-palette-module-set"}).appendTo(contentRow);
|
||||||
var buttonGroup = $('<div>',{class:"red-ui-palette-module-set-button-group"}).appendTo(setRow);
|
var buttonGroup = $('<div>',{class:"red-ui-palette-module-set-button-group"}).appendTo(setRow);
|
||||||
var typeSwatches = {};
|
var typeSwatches = {};
|
||||||
set.types.forEach(function(t) {
|
let enableButton;
|
||||||
var typeDiv = $('<div>',{class:"red-ui-palette-module-type"}).appendTo(setRow);
|
if (set.types) {
|
||||||
typeSwatches[t] = $('<span>',{class:"red-ui-palette-module-type-swatch"}).appendTo(typeDiv);
|
set.types.forEach(function(t) {
|
||||||
$('<span>',{class:"red-ui-palette-module-type-node"}).text(t).appendTo(typeDiv);
|
var typeDiv = $('<div>',{class:"red-ui-palette-module-type"}).appendTo(setRow);
|
||||||
})
|
typeSwatches[t] = $('<span>',{class:"red-ui-palette-module-type-swatch"}).appendTo(typeDiv);
|
||||||
var enableButton = $('<a href="#" class="red-ui-button red-ui-button-small"></a>').appendTo(buttonGroup);
|
if (set.enabled) {
|
||||||
enableButton.on("click", function(evt) {
|
var def = RED.nodes.getType(t);
|
||||||
evt.preventDefault();
|
if (def && def.color) {
|
||||||
if (object.setUseCount[setName] === 0) {
|
typeSwatches[t].css({background:RED.utils.getNodeColor(t,def)});
|
||||||
var currentSet = RED.nodes.registry.getNodeSet(set.id);
|
typeSwatches[t].css({border: "1px solid "+getContrastingBorder(typeSwatches[t].css('backgroundColor'))})
|
||||||
shade.show();
|
|
||||||
var newState = !currentSet.enabled
|
|
||||||
changeNodeState(set.id,newState,shade,function(xhr){
|
|
||||||
if (xhr) {
|
|
||||||
if (xhr.responseJSON) {
|
|
||||||
RED.notify(RED._('palette.editor.errors.'+(newState?'enable':'disable')+'Failed',{module: id,message:xhr.responseJSON.message}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
$('<span>',{class:"red-ui-palette-module-type-node"}).text(t).appendTo(typeDiv);
|
||||||
}
|
})
|
||||||
})
|
enableButton = $('<a href="#" class="red-ui-button red-ui-button-small"></a>').appendTo(buttonGroup);
|
||||||
|
enableButton.on("click", function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
if (object.setUseCount[setName] === 0) {
|
||||||
|
var currentSet = RED.nodes.registry.getNodeSet(set.id);
|
||||||
|
shade.show();
|
||||||
|
var newState = !currentSet.enabled
|
||||||
|
changeNodeState(set.id,newState,shade,function(xhr){
|
||||||
|
if (xhr) {
|
||||||
|
if (xhr.responseJSON) {
|
||||||
|
RED.notify(RED._('palette.editor.errors.'+(newState?'enable':'disable')+'Failed',{module: id,message:xhr.responseJSON.message}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
object.elements.sets[set.name] = {
|
if (object.setUseCount[setName] > 0) {
|
||||||
setRow: setRow,
|
enableButton.text(RED._('palette.editor.inuse'));
|
||||||
enableButton: enableButton,
|
enableButton.addClass('disabled');
|
||||||
swatches: typeSwatches
|
} else {
|
||||||
};
|
enableButton.removeClass('disabled');
|
||||||
});
|
if (set.enabled) {
|
||||||
|
enableButton.text(RED._('palette.editor.disable'));
|
||||||
|
} else {
|
||||||
|
enableButton.text(RED._('palette.editor.enable'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setRow.toggleClass("red-ui-palette-module-set-disabled",!set.enabled);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
if (set.plugins) {
|
||||||
|
set.plugins.forEach(function(p) {
|
||||||
|
var typeDiv = $('<div>',{class:"red-ui-palette-module-type"}).appendTo(setRow);
|
||||||
|
// typeSwatches[p.id] = $('<span>',{class:"red-ui-palette-module-type-swatch"}).appendTo(typeDiv);
|
||||||
|
$('<span><i class="fa fa-puzzle-piece" aria-hidden="true"></i> </span>',{class:"red-ui-palette-module-type-swatch"}).appendTo(typeDiv);
|
||||||
|
$('<span>',{class:"red-ui-palette-module-type-node"}).text(p.id).appendTo(typeDiv);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
object.elements.sets[set.name] = {
|
||||||
|
setRow: setRow,
|
||||||
|
enableButton: enableButton,
|
||||||
|
swatches: typeSwatches
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
enableButton.on("click", function(evt) {
|
enableButton.on("click", function(evt) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
if (object.totalUseCount === 0) {
|
if (object.totalUseCount === 0) {
|
||||||
@ -1226,7 +1313,55 @@ RED.palette.editor = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}); }
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// dedicated list management for plugins
|
||||||
|
if (entry.plugin) {
|
||||||
|
|
||||||
|
let e = nodeEntries[entry.name];
|
||||||
|
if (e) {
|
||||||
|
nodeList.editableList('removeItem', e);
|
||||||
|
delete nodeEntries[entry.name];
|
||||||
|
}
|
||||||
|
|
||||||
|
// We assume that a plugin that implements onremove
|
||||||
|
// cleans the editor accordingly of its left-overs.
|
||||||
|
let found_onremove = true;
|
||||||
|
|
||||||
|
let keys = Object.keys(entry.sets);
|
||||||
|
keys.forEach((key) => {
|
||||||
|
let set = entry.sets[key];
|
||||||
|
for (let i=0; i<set.plugins?.length; i++) {
|
||||||
|
let plgn = RED.plugins.getPlugin(set.plugins[i].id);
|
||||||
|
if (plgn && plgn.onremove && typeof plgn.onremove === 'function') {
|
||||||
|
plgn.onremove();
|
||||||
|
} else {
|
||||||
|
if (plgn && plgn.onadd && typeof plgn.onadd === 'function') {
|
||||||
|
// if there's no 'onadd', there shouldn't be any left-overs
|
||||||
|
found_onremove = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!found_onremove) {
|
||||||
|
let removeNotify = RED.notify("Removed plugin " + entry.name + ". Please reload the editor to clear left-overs.",{
|
||||||
|
modal: true,
|
||||||
|
fixed: true,
|
||||||
|
type: 'warning',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: "Understood",
|
||||||
|
class:"primary",
|
||||||
|
click: function(e) {
|
||||||
|
removeNotify.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
notification.close();
|
notification.close();
|
||||||
|
@ -35,6 +35,10 @@ RED.palette = (function() {
|
|||||||
var categoryContainers = {};
|
var categoryContainers = {};
|
||||||
var sidebarControls;
|
var sidebarControls;
|
||||||
|
|
||||||
|
let paletteState = { filter: "", collapsed: [] };
|
||||||
|
|
||||||
|
let filterRefreshTimeout
|
||||||
|
|
||||||
function createCategory(originalCategory,rootCategory,category,ns) {
|
function createCategory(originalCategory,rootCategory,category,ns) {
|
||||||
if ($("#red-ui-palette-base-category-"+rootCategory).length === 0) {
|
if ($("#red-ui-palette-base-category-"+rootCategory).length === 0) {
|
||||||
createCategoryContainer(originalCategory,rootCategory, ns+":palette.label."+rootCategory);
|
createCategoryContainer(originalCategory,rootCategory, ns+":palette.label."+rootCategory);
|
||||||
@ -60,20 +64,57 @@ RED.palette = (function() {
|
|||||||
catDiv.data('label',label);
|
catDiv.data('label',label);
|
||||||
categoryContainers[category] = {
|
categoryContainers[category] = {
|
||||||
container: catDiv,
|
container: catDiv,
|
||||||
close: function() {
|
hide: function (instant) {
|
||||||
|
if (instant) {
|
||||||
|
catDiv.hide()
|
||||||
|
} else {
|
||||||
|
catDiv.slideUp()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
show: function () {
|
||||||
|
catDiv.show()
|
||||||
|
},
|
||||||
|
isOpen: function () {
|
||||||
|
return !!catDiv.hasClass("red-ui-palette-open")
|
||||||
|
},
|
||||||
|
getNodeCount: function (visibleOnly) {
|
||||||
|
const nodes = catDiv.find(".red-ui-palette-node")
|
||||||
|
if (visibleOnly) {
|
||||||
|
return nodes.filter(function() { return $(this).css('display') !== 'none'}).length
|
||||||
|
} else {
|
||||||
|
return nodes.length
|
||||||
|
}
|
||||||
|
},
|
||||||
|
close: function(instant, skipSaveState) {
|
||||||
catDiv.removeClass("red-ui-palette-open");
|
catDiv.removeClass("red-ui-palette-open");
|
||||||
catDiv.addClass("red-ui-palette-closed");
|
catDiv.addClass("red-ui-palette-closed");
|
||||||
$("#red-ui-palette-base-category-"+category).slideUp();
|
if (instant) {
|
||||||
|
$("#red-ui-palette-base-category-"+category).hide();
|
||||||
|
} else {
|
||||||
|
$("#red-ui-palette-base-category-"+category).slideUp();
|
||||||
|
}
|
||||||
$("#red-ui-palette-header-"+category+" i").removeClass("expanded");
|
$("#red-ui-palette-header-"+category+" i").removeClass("expanded");
|
||||||
|
if (!skipSaveState) {
|
||||||
|
if (!paletteState.collapsed.includes(category)) {
|
||||||
|
paletteState.collapsed.push(category);
|
||||||
|
savePaletteState();
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
open: function() {
|
open: function(skipSaveState) {
|
||||||
catDiv.addClass("red-ui-palette-open");
|
catDiv.addClass("red-ui-palette-open");
|
||||||
catDiv.removeClass("red-ui-palette-closed");
|
catDiv.removeClass("red-ui-palette-closed");
|
||||||
$("#red-ui-palette-base-category-"+category).slideDown();
|
$("#red-ui-palette-base-category-"+category).slideDown();
|
||||||
$("#red-ui-palette-header-"+category+" i").addClass("expanded");
|
$("#red-ui-palette-header-"+category+" i").addClass("expanded");
|
||||||
|
if (!skipSaveState) {
|
||||||
|
if (paletteState.collapsed.includes(category)) {
|
||||||
|
paletteState.collapsed.splice(paletteState.collapsed.indexOf(category), 1);
|
||||||
|
savePaletteState();
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
toggle: function() {
|
toggle: function() {
|
||||||
if (catDiv.hasClass("red-ui-palette-open")) {
|
if (categoryContainers[category].isOpen()) {
|
||||||
categoryContainers[category].close();
|
categoryContainers[category].close();
|
||||||
} else {
|
} else {
|
||||||
categoryContainers[category].open();
|
categoryContainers[category].open();
|
||||||
@ -415,8 +456,16 @@ RED.palette = (function() {
|
|||||||
|
|
||||||
var categoryNode = $("#red-ui-palette-container-"+rootCategory);
|
var categoryNode = $("#red-ui-palette-container-"+rootCategory);
|
||||||
if (categoryNode.find(".red-ui-palette-node").length === 1) {
|
if (categoryNode.find(".red-ui-palette-node").length === 1) {
|
||||||
categoryContainers[rootCategory].open();
|
if (!paletteState?.collapsed?.includes(rootCategory)) {
|
||||||
|
categoryContainers[rootCategory].open();
|
||||||
|
} else {
|
||||||
|
categoryContainers[rootCategory].close(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
clearTimeout(filterRefreshTimeout)
|
||||||
|
filterRefreshTimeout = setTimeout(() => {
|
||||||
|
refreshFilter()
|
||||||
|
}, 200)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -516,7 +565,8 @@ RED.palette = (function() {
|
|||||||
paletteNode.css("backgroundColor", sf.color);
|
paletteNode.css("backgroundColor", sf.color);
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterChange(val) {
|
function refreshFilter() {
|
||||||
|
const val = $("#red-ui-palette-search input").val()
|
||||||
var re = new RegExp(val.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'),'i');
|
var re = new RegExp(val.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'),'i');
|
||||||
$("#red-ui-palette-container .red-ui-palette-node").each(function(i,el) {
|
$("#red-ui-palette-container .red-ui-palette-node").each(function(i,el) {
|
||||||
var currentLabel = $(el).attr("data-palette-label");
|
var currentLabel = $(el).attr("data-palette-label");
|
||||||
@ -528,16 +578,26 @@ RED.palette = (function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
for (var category in categoryContainers) {
|
for (let category in categoryContainers) {
|
||||||
if (categoryContainers.hasOwnProperty(category)) {
|
if (categoryContainers.hasOwnProperty(category)) {
|
||||||
if (categoryContainers[category].container
|
const categorySection = categoryContainers[category]
|
||||||
.find(".red-ui-palette-node")
|
if (categorySection.getNodeCount(true) === 0) {
|
||||||
.filter(function() { return $(this).css('display') !== 'none'}).length === 0) {
|
categorySection.hide()
|
||||||
categoryContainers[category].close();
|
|
||||||
categoryContainers[category].container.slideUp();
|
|
||||||
} else {
|
} else {
|
||||||
categoryContainers[category].open();
|
categorySection.show()
|
||||||
categoryContainers[category].container.show();
|
if (val) {
|
||||||
|
// There is a filter being applied and it has matched
|
||||||
|
// something in this category - show the contents
|
||||||
|
categorySection.open(true)
|
||||||
|
} else {
|
||||||
|
// No filter. Only show the category if it isn't in lastState
|
||||||
|
if (!paletteState.collapsed.includes(category)) {
|
||||||
|
categorySection.open(true)
|
||||||
|
} else if (categorySection.isOpen()) {
|
||||||
|
// This section should be collapsed but isn't - so make it so
|
||||||
|
categorySection.close(true, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -553,6 +613,9 @@ RED.palette = (function() {
|
|||||||
|
|
||||||
$("#red-ui-palette > .red-ui-palette-spinner").show();
|
$("#red-ui-palette > .red-ui-palette-spinner").show();
|
||||||
|
|
||||||
|
RED.events.on('logout', function () {
|
||||||
|
RED.settings.removeLocal('palette-state')
|
||||||
|
})
|
||||||
|
|
||||||
RED.events.on('registry:node-type-added', function(nodeType) {
|
RED.events.on('registry:node-type-added', function(nodeType) {
|
||||||
var def = RED.nodes.getType(nodeType);
|
var def = RED.nodes.getType(nodeType);
|
||||||
@ -596,14 +659,14 @@ RED.palette = (function() {
|
|||||||
|
|
||||||
RED.events.on("subflows:change",refreshSubflow);
|
RED.events.on("subflows:change",refreshSubflow);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$("#red-ui-palette-search input").searchBox({
|
$("#red-ui-palette-search input").searchBox({
|
||||||
delay: 100,
|
delay: 100,
|
||||||
change: function() {
|
change: function() {
|
||||||
filterChange($(this).val());
|
refreshFilter();
|
||||||
|
paletteState.filter = $(this).val();
|
||||||
|
savePaletteState();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
sidebarControls = $('<div class="red-ui-sidebar-control-left"><i class="fa fa-chevron-left"></i></div>').appendTo($("#red-ui-palette"));
|
sidebarControls = $('<div class="red-ui-sidebar-control-left"><i class="fa fa-chevron-left"></i></div>').appendTo($("#red-ui-palette"));
|
||||||
RED.popover.tooltip(sidebarControls,RED._("keyboard.togglePalette"),"core:toggle-palette");
|
RED.popover.tooltip(sidebarControls,RED._("keyboard.togglePalette"),"core:toggle-palette");
|
||||||
@ -669,7 +732,23 @@ RED.palette = (function() {
|
|||||||
togglePalette(state);
|
togglePalette(state);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
paletteState = JSON.parse(RED.settings.getLocal("palette-state") || '{"filter":"", "collapsed": []}');
|
||||||
|
if (paletteState.filter) {
|
||||||
|
// Apply the category filter
|
||||||
|
$("#red-ui-palette-search input").searchBox("value", paletteState.filter);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Unexpected error loading palette state from localStorage: ", error);
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
// Lazily tidy up any categories that haven't been reloaded
|
||||||
|
paletteState.collapsed = paletteState.collapsed.filter(category => !!categoryContainers[category])
|
||||||
|
savePaletteState()
|
||||||
|
}, 10000)
|
||||||
}
|
}
|
||||||
|
|
||||||
function togglePalette(state) {
|
function togglePalette(state) {
|
||||||
if (!state) {
|
if (!state) {
|
||||||
$("#red-ui-main-container").addClass("red-ui-palette-closed");
|
$("#red-ui-main-container").addClass("red-ui-palette-closed");
|
||||||
@ -689,6 +768,15 @@ RED.palette = (function() {
|
|||||||
})
|
})
|
||||||
return categories;
|
return categories;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function savePaletteState() {
|
||||||
|
try {
|
||||||
|
RED.settings.setLocal("palette-state", JSON.stringify(paletteState));
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Unexpected error saving palette state to localStorage: ", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
init: init,
|
init: init,
|
||||||
add:addNodeType,
|
add:addNodeType,
|
||||||
|
@ -1280,14 +1280,20 @@ RED.subflow = (function() {
|
|||||||
var nodePropValue = nodeProp;
|
var nodePropValue = nodeProp;
|
||||||
if (prop.ui && prop.ui.type === "cred") {
|
if (prop.ui && prop.ui.type === "cred") {
|
||||||
nodePropType = "cred";
|
nodePropType = "cred";
|
||||||
|
} else if (prop.ui && prop.ui.type === "conf-types") {
|
||||||
|
nodePropType = prop.value.type
|
||||||
} else {
|
} else {
|
||||||
switch(typeof nodeProp) {
|
switch(typeof nodeProp) {
|
||||||
case "string": nodePropType = "str"; break;
|
case "string": nodePropType = "str"; break;
|
||||||
case "number": nodePropType = "num"; break;
|
case "number": nodePropType = "num"; break;
|
||||||
case "boolean": nodePropType = "bool"; nodePropValue = nodeProp?"true":"false"; break;
|
case "boolean": nodePropType = "bool"; nodePropValue = nodeProp?"true":"false"; break;
|
||||||
default:
|
default:
|
||||||
nodePropType = nodeProp.type;
|
if (nodeProp) {
|
||||||
nodePropValue = nodeProp.value;
|
nodePropType = nodeProp.type;
|
||||||
|
nodePropValue = nodeProp.value;
|
||||||
|
} else {
|
||||||
|
nodePropType = 'str'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var item = {
|
var item = {
|
||||||
|
@ -158,8 +158,10 @@ RED.sidebar.help = (function() {
|
|||||||
|
|
||||||
function refreshSubflow(sf) {
|
function refreshSubflow(sf) {
|
||||||
var item = treeList.treeList('get',"node-type:subflow:"+sf.id);
|
var item = treeList.treeList('get',"node-type:subflow:"+sf.id);
|
||||||
item.subflowLabel = sf._def.label().toLowerCase();
|
if (item) {
|
||||||
item.treeList.replaceElement(getNodeLabel({_def:sf._def,type:sf._def.label()}));
|
item.subflowLabel = sf._def.label().toLowerCase();
|
||||||
|
item.treeList.replaceElement(getNodeLabel({_def:sf._def,type:sf._def.label()}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideTOC() {
|
function hideTOC() {
|
||||||
|
@ -491,6 +491,11 @@ RED.workspaces = (function() {
|
|||||||
createWorkspaceTabs();
|
createWorkspaceTabs();
|
||||||
RED.events.on("sidebar:resize",workspace_tabs.resize);
|
RED.events.on("sidebar:resize",workspace_tabs.resize);
|
||||||
|
|
||||||
|
RED.events.on("workspace:clear", () => {
|
||||||
|
// Reset the index used to generate new flow names
|
||||||
|
workspaceIndex = 0
|
||||||
|
})
|
||||||
|
|
||||||
RED.actions.add("core:show-next-tab",function() {
|
RED.actions.add("core:show-next-tab",function() {
|
||||||
var oldActive = activeWorkspace;
|
var oldActive = activeWorkspace;
|
||||||
workspace_tabs.nextTab();
|
workspace_tabs.nextTab();
|
||||||
@ -657,6 +662,9 @@ RED.workspaces = (function() {
|
|||||||
RED.events.on("flows:change", (ws) => {
|
RED.events.on("flows:change", (ws) => {
|
||||||
$("#red-ui-tab-"+(ws.id.replace(".","-"))).toggleClass('red-ui-workspace-changed',!!(ws.contentsChanged || ws.changed || ws.added));
|
$("#red-ui-tab-"+(ws.id.replace(".","-"))).toggleClass('red-ui-workspace-changed',!!(ws.contentsChanged || ws.changed || ws.added));
|
||||||
})
|
})
|
||||||
|
RED.events.on("subflows:change", (ws) => {
|
||||||
|
$("#red-ui-tab-"+(ws.id.replace(".","-"))).toggleClass('red-ui-workspace-changed',!!(ws.contentsChanged || ws.changed || ws.added));
|
||||||
|
})
|
||||||
|
|
||||||
hideWorkspace();
|
hideWorkspace();
|
||||||
}
|
}
|
||||||
|
@ -187,6 +187,7 @@ RED.user = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function logout() {
|
function logout() {
|
||||||
|
RED.events.emit('logout')
|
||||||
var tokens = RED.settings.get("auth-tokens");
|
var tokens = RED.settings.get("auth-tokens");
|
||||||
var token = tokens?tokens.access_token:"";
|
var token = tokens?tokens.access_token:"";
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@ -225,6 +226,7 @@ RED.user = (function() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
$('<i class="fa fa-user"></i>').appendTo("#red-ui-header-button-user");
|
||||||
} else {
|
} else {
|
||||||
RED.menu.addItem("red-ui-header-button-user",{
|
RED.menu.addItem("red-ui-header-button-user",{
|
||||||
id:"usermenu-item-username",
|
id:"usermenu-item-username",
|
||||||
@ -237,6 +239,15 @@ RED.user = (function() {
|
|||||||
RED.user.logout();
|
RED.user.logout();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const userMenu = $("#red-ui-header-button-user")
|
||||||
|
userMenu.empty()
|
||||||
|
if (RED.settings.user.image) {
|
||||||
|
$('<span class="user-profile"></span>').css({
|
||||||
|
backgroundImage: "url("+RED.settings.user.image+")",
|
||||||
|
}).appendTo(userMenu);
|
||||||
|
} else {
|
||||||
|
$('<i class="fa fa-user"></i>').appendTo(userMenu);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -247,14 +258,6 @@ RED.user = (function() {
|
|||||||
|
|
||||||
var userMenu = $('<li><a id="red-ui-header-button-user" class="button hide" href="#"></a></li>')
|
var userMenu = $('<li><a id="red-ui-header-button-user" class="button hide" href="#"></a></li>')
|
||||||
.prependTo(".red-ui-header-toolbar");
|
.prependTo(".red-ui-header-toolbar");
|
||||||
if (RED.settings.user.image) {
|
|
||||||
$('<span class="user-profile"></span>').css({
|
|
||||||
backgroundImage: "url("+RED.settings.user.image+")",
|
|
||||||
}).appendTo(userMenu.find("a"));
|
|
||||||
} else {
|
|
||||||
$('<i class="fa fa-user"></i>').appendTo(userMenu.find("a"));
|
|
||||||
}
|
|
||||||
|
|
||||||
RED.menu.init({id:"red-ui-header-button-user",
|
RED.menu.init({id:"red-ui-header-button-user",
|
||||||
options: []
|
options: []
|
||||||
});
|
});
|
||||||
|
@ -63,25 +63,29 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.red-ui-header-toolbar {
|
.red-ui-header-toolbar {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
float: right;
|
float: right;
|
||||||
|
|
||||||
> li {
|
> li {
|
||||||
display: inline-block;
|
display: inline-flex;
|
||||||
|
align-items: stretch;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
|
height: 100%;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
min-width: 20px;
|
min-width: 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 40px;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
padding: 0px 12px;
|
padding: 0px 12px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@ -271,13 +275,13 @@
|
|||||||
color: var(--red-ui-header-menu-heading-color);
|
color: var(--red-ui-header-menu-heading-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
#red-ui-header-button-user .user-profile {
|
.user-profile {
|
||||||
background-position: center center;
|
background-position: center center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 40px;
|
width: 30px;
|
||||||
height: 35px;
|
height: 30px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,8 @@ export default {
|
|||||||
{
|
{
|
||||||
title: {
|
title: {
|
||||||
"en-US": "Timestamp formatting options",
|
"en-US": "Timestamp formatting options",
|
||||||
"ja": "タイムスタンプの形式の項目"
|
"ja": "タイムスタンプの形式の項目",
|
||||||
|
"fr": "Options de formatage de l'horodatage"
|
||||||
},
|
},
|
||||||
image: 'images/nr4-timestamp-formatting.png',
|
image: 'images/nr4-timestamp-formatting.png',
|
||||||
description: {
|
description: {
|
||||||
@ -34,13 +35,21 @@ export default {
|
|||||||
<li>エポックからのミリ秒 - 従来動作と同じになるタイムスタンプの項目</li>
|
<li>エポックからのミリ秒 - 従来動作と同じになるタイムスタンプの項目</li>
|
||||||
<li>ISO 8601 - 多くのシステムで使用されている共通の形式</li>
|
<li>ISO 8601 - 多くのシステムで使用されている共通の形式</li>
|
||||||
<li>JavaScript日付オブジェクト</li>
|
<li>JavaScript日付オブジェクト</li>
|
||||||
|
</ul>`,
|
||||||
|
"fr": `<p>Les noeuds qui vous permettent de définir un horodatage disposent désormais d'options sur le format dans lequel cet horodatage peut être défini.</p>
|
||||||
|
<p>Nous gardons les choses simples en proposant trois options :<p>
|
||||||
|
<ul>
|
||||||
|
<li>Millisecondes depuis l'époque : il s'agit du comportement existant de l'option d'horodatage</li>
|
||||||
|
<li>ISO 8601 : un format commun utilisé par de nombreux systèmes</li>
|
||||||
|
<li>Objet Date JavaScript</li>
|
||||||
</ul>`
|
</ul>`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: {
|
title: {
|
||||||
"en-US": "Auto-complete of flow/global and env types",
|
"en-US": "Auto-complete of flow/global and env types",
|
||||||
"ja": "フロー/グローバル、環境変数の型の自動補完"
|
"ja": "フロー/グローバル、環境変数の型の自動補完",
|
||||||
|
"fr": "Saisie automatique des types de flux/global et env"
|
||||||
},
|
},
|
||||||
image: 'images/nr4-auto-complete.png',
|
image: 'images/nr4-auto-complete.png',
|
||||||
description: {
|
description: {
|
||||||
@ -48,13 +57,17 @@ export default {
|
|||||||
now all include auto-complete suggestions based on the live state of your flows.</p>
|
now all include auto-complete suggestions based on the live state of your flows.</p>
|
||||||
`,
|
`,
|
||||||
"ja": `<p><code>flow</code>/<code>global</code>コンテキストや<code>env</code>の入力を、現在のフローの状態をもとに自動補完で提案するようになりました。</p>
|
"ja": `<p><code>flow</code>/<code>global</code>コンテキストや<code>env</code>の入力を、現在のフローの状態をもとに自動補完で提案するようになりました。</p>
|
||||||
`
|
`,
|
||||||
|
"fr": `<p>Les entrées contextuelles <code>flow</code>/<code>global</code> et l'entrée <code>env</code>
|
||||||
|
incluent désormais des suggestions de saisie semi-automatique basées sur l'état actuel de vos flux.</p>
|
||||||
|
`,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: {
|
title: {
|
||||||
"en-US": "Config node customisation in Subflows",
|
"en-US": "Config node customisation in Subflows",
|
||||||
"ja": "サブフローでの設定ノードのカスタマイズ"
|
"ja": "サブフローでの設定ノードのカスタマイズ",
|
||||||
|
"fr": "Personnalisation du noeud de configuration dans les sous-flux"
|
||||||
},
|
},
|
||||||
image: 'images/nr4-sf-config.png',
|
image: 'images/nr4-sf-config.png',
|
||||||
description: {
|
description: {
|
||||||
@ -65,6 +78,11 @@ export default {
|
|||||||
`,
|
`,
|
||||||
"ja": `<p>サブフローをカスタマイズして、選択した型の異なる設定ノードを各インスタンスが使用できるようになりました。</p>
|
"ja": `<p>サブフローをカスタマイズして、選択した型の異なる設定ノードを各インスタンスが使用できるようになりました。</p>
|
||||||
<p>例えば、MQTTブローカへ接続し、メッセージ受信と後処理を行うサブフローの各インスタンスに異なるブローカを指定することも可能です。</p>
|
<p>例えば、MQTTブローカへ接続し、メッセージ受信と後処理を行うサブフローの各インスタンスに異なるブローカを指定することも可能です。</p>
|
||||||
|
`,
|
||||||
|
"fr": `<p>Les sous-flux peuvent désormais être personnalisés pour permettre à chaque instance d'utiliser un
|
||||||
|
noeud de configuration d'un type sélectionné.</p>
|
||||||
|
<p>Par exemple, chaque instance d'un sous-flux qui se connecte à un courtier MQTT et effectue un post-traitement
|
||||||
|
des messages reçus peut être pointée vers un autre courtier.</p>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -90,6 +108,14 @@ export default {
|
|||||||
<li>WebSocketノードのカスタマイズ可能なヘッダ</li>
|
<li>WebSocketノードのカスタマイズ可能なヘッダ</li>
|
||||||
<li>Splitノードは、メッセージプロパティで操作できるようになりました</li>
|
<li>Splitノードは、メッセージプロパティで操作できるようになりました</li>
|
||||||
<li>他にも沢山あります...</li>
|
<li>他にも沢山あります...</li>
|
||||||
|
</ul>`,
|
||||||
|
"fr": `<p>Les noeuds principaux ont reçu de nombreux correctifs mineurs ainsi que des améliorations. La documentation a été mise à jour.
|
||||||
|
Consultez le journal des modifications dans la barre latérale d'aide pour une liste complète. Ci-dessous, les changements les plus importants :</p>
|
||||||
|
<ul>
|
||||||
|
<li>Un mode CSV entièrement conforme à la norme RFC4180</li>
|
||||||
|
<li>En-têtes personnalisables pour le noeud WebSocket</li>
|
||||||
|
<li>Le noeud Split peut désormais fonctionner sur n'importe quelle propriété de message</li>
|
||||||
|
<li>Et bien plus encore...</li>
|
||||||
</ul>`
|
</ul>`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -378,7 +378,7 @@
|
|||||||
return { id: id, label: RED.nodes.workspace(id).label } //flow id + name
|
return { id: id, label: RED.nodes.workspace(id).label } //flow id + name
|
||||||
} else {
|
} else {
|
||||||
const instanceNode = RED.nodes.node(id)
|
const instanceNode = RED.nodes.node(id)
|
||||||
const pathLabel = (instanceNode.name || RED.nodes.subflow(instanceNode.type.substring(8)).name)
|
const pathLabel = (instanceNode.name || RED.nodes.subflow(instanceNode.type.substring(8))?.name || instanceNode.type)
|
||||||
return { id: id, label: pathLabel }
|
return { id: id, label: pathLabel }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -233,9 +233,12 @@ module.exports = function(RED) {
|
|||||||
// only replace if they match exactly
|
// only replace if they match exactly
|
||||||
RED.util.setMessageProperty(msg,property,value);
|
RED.util.setMessageProperty(msg,property,value);
|
||||||
} else {
|
} else {
|
||||||
// if target is boolean then just replace it
|
current = current.replace(fromRE,value);
|
||||||
if (rule.tot === "bool") { current = value; }
|
if (rule.tot === "bool" && current === ""+value) {
|
||||||
else { current = current.replace(fromRE,value); }
|
// If the target type is boolean, and the replace call has resulted in "true"/"false",
|
||||||
|
// convert to boolean type (which 'value' already is)
|
||||||
|
current = value
|
||||||
|
}
|
||||||
RED.util.setMessageProperty(msg,property,current);
|
RED.util.setMessageProperty(msg,property,current);
|
||||||
}
|
}
|
||||||
} else if ((typeof current === 'number' || current instanceof Number) && fromType === 'num') {
|
} else if ((typeof current === 'number' || current instanceof Number) && fromType === 'num') {
|
||||||
|
3
packages/node_modules/@node-red/nodes/locales/fr/common/91-global-config.html
vendored
Normal file
3
packages/node_modules/@node-red/nodes/locales/fr/common/91-global-config.html
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<script type="text/html" data-help-name="global-config">
|
||||||
|
<p>Un noeud pour contenir la configuration globale des flux.</p>
|
||||||
|
</script>
|
@ -94,6 +94,7 @@
|
|||||||
},
|
},
|
||||||
"catch": {
|
"catch": {
|
||||||
"catch": "catch : tout",
|
"catch": "catch : tout",
|
||||||
|
"catchGroup": "catch: groupe",
|
||||||
"catchNodes": "catch : __number__",
|
"catchNodes": "catch : __number__",
|
||||||
"catchUncaught": "catch : non capturé",
|
"catchUncaught": "catch : non capturé",
|
||||||
"label": {
|
"label": {
|
||||||
@ -109,6 +110,7 @@
|
|||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"status": "statut : tout",
|
"status": "statut : tout",
|
||||||
|
"statusGroup": "statut: groupe",
|
||||||
"statusNodes": "statut : __number__",
|
"statusNodes": "statut : __number__",
|
||||||
"label": {
|
"label": {
|
||||||
"source": "Signaler l'état de",
|
"source": "Signaler l'état de",
|
||||||
@ -250,7 +252,8 @@
|
|||||||
"initialize": "Au démarrage",
|
"initialize": "Au démarrage",
|
||||||
"finalize": "À l'arrêt",
|
"finalize": "À l'arrêt",
|
||||||
"outputs": "Sorties",
|
"outputs": "Sorties",
|
||||||
"modules": "Modules"
|
"modules": "Modules",
|
||||||
|
"timeout": "Délai d'attente"
|
||||||
},
|
},
|
||||||
"text": {
|
"text": {
|
||||||
"initialize": "// Le code ajouté ici sera exécuté une fois\n// à chaque démarrage du noeud.\n",
|
"initialize": "// Le code ajouté ici sera exécuté une fois\n// à chaque démarrage du noeud.\n",
|
||||||
@ -847,7 +850,13 @@
|
|||||||
"newline": "Nouvelle ligne",
|
"newline": "Nouvelle ligne",
|
||||||
"usestrings": "Analyser les valeurs numériques",
|
"usestrings": "Analyser les valeurs numériques",
|
||||||
"include_empty_strings": "Inclure les chaînes vides",
|
"include_empty_strings": "Inclure les chaînes vides",
|
||||||
"include_null_values": "Inclure les valeurs nulles"
|
"include_null_values": "Inclure les valeurs nulles",
|
||||||
|
"spec": "Analyseur"
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"rfc": "RFC4180",
|
||||||
|
"legacy": "Hérité (Legacy)",
|
||||||
|
"legacy_warning": "Le mode hérité sera supprimé dans une prochaine version."
|
||||||
},
|
},
|
||||||
"placeholder": {
|
"placeholder": {
|
||||||
"columns": "noms de colonnes séparés par des virgules"
|
"columns": "noms de colonnes séparés par des virgules"
|
||||||
@ -876,6 +885,7 @@
|
|||||||
"once": "envoyer les en-têtes une fois, jusqu'à msg.reset"
|
"once": "envoyer les en-têtes une fois, jusqu'à msg.reset"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"bad_template": "Colonnes du modèle mal formées.",
|
||||||
"csv_js": "Ce noeud ne gère que les chaînes CSV ou les objets js.",
|
"csv_js": "Ce noeud ne gère que les chaînes CSV ou les objets js.",
|
||||||
"obj_csv": "Aucun modèle de colonnes spécifié pour l'objet -> CSV.",
|
"obj_csv": "Aucun modèle de colonnes spécifié pour l'objet -> CSV.",
|
||||||
"bad_csv": "Données CSV mal formées - sortie probablement corrompue."
|
"bad_csv": "Données CSV mal formées - sortie probablement corrompue."
|
||||||
@ -885,12 +895,14 @@
|
|||||||
"label": {
|
"label": {
|
||||||
"select": "Sélecteur",
|
"select": "Sélecteur",
|
||||||
"output": "Sortie",
|
"output": "Sortie",
|
||||||
"in": "dans"
|
"in": "dans",
|
||||||
|
"prefix": "Nom de la propriété pour le contenu HTML"
|
||||||
},
|
},
|
||||||
"output": {
|
"output": {
|
||||||
"html": "le contenu html des éléments",
|
"html": "le contenu html des éléments",
|
||||||
"text": "uniquement le contenu textuel des éléments",
|
"text": "uniquement le contenu textuel des éléments",
|
||||||
"attr": "un objet de n'importe quel attribut des éléments"
|
"attr": "un objet de n'importe quel attribut des éléments",
|
||||||
|
"compl": "un objet pour tous les attributs de tous les éléments ainsi que du contenu HTML"
|
||||||
},
|
},
|
||||||
"format": {
|
"format": {
|
||||||
"single": "comme un seul message contenant un tableau",
|
"single": "comme un seul message contenant un tableau",
|
||||||
|
@ -30,6 +30,8 @@
|
|||||||
avant d'être envoyé.</p>
|
avant d'être envoyé.</p>
|
||||||
<p>Si <code>msg._session</code> n'est pas présent, la charge utile est
|
<p>Si <code>msg._session</code> n'est pas présent, la charge utile est
|
||||||
envoyé à <b>tous</b> les clients connectés.</p>
|
envoyé à <b>tous</b> les clients connectés.</p>
|
||||||
|
<p>En mode Répondre à, définir <code>msg.reset = true</code> réinitialisera la connexion
|
||||||
|
spécifiée par _session.id ou toutes les connexions si aucun _session.id n'est spécifié.</p>
|
||||||
<p><b>Remarque</b> : Sur certains systèmes, vous aurez peut-être besoin d'un accès root ou administrateur
|
<p><b>Remarque</b> : Sur certains systèmes, vous aurez peut-être besoin d'un accès root ou administrateur
|
||||||
pour accéder aux ports inférieurs à 1024.</p>
|
pour accéder aux ports inférieurs à 1024.</p>
|
||||||
</script>
|
</script>
|
||||||
@ -40,6 +42,8 @@
|
|||||||
caractères renvoyés dans un tampon fixe, correspondant à un caractère spécifié avant de revenir,
|
caractères renvoyés dans un tampon fixe, correspondant à un caractère spécifié avant de revenir,
|
||||||
attendre un délai fixe à partir de la première réponse, puis revenir, s'installer et attender les données, ou envoie puis ferme la connexion
|
attendre un délai fixe à partir de la première réponse, puis revenir, s'installer et attender les données, ou envoie puis ferme la connexion
|
||||||
immédiatement, sans attendre de réponse.</p>
|
immédiatement, sans attendre de réponse.</p>
|
||||||
|
<p>Dans le cas du mode veille (maintien de la connexion), vous pouvez envoyer <code>msg.reset = true</code> ou <code>msg.reset = "host:port"</code> pour forcer une interruption
|
||||||
|
de la connexion et une reconnexion automatique.</p>
|
||||||
<p>La réponse sortira dans <code>msg.payload</code> en tant que tampon, vous pouvez alors utiliser la fonction .toString().</p>
|
<p>La réponse sortira dans <code>msg.payload</code> en tant que tampon, vous pouvez alors utiliser la fonction .toString().</p>
|
||||||
<p>Si vous laissez l'hôte ou le port tcp vide, ils doivent être définis à l'aide des propriétés <code>msg.host</code> et <code>msg.port</code> dans chaque message envoyé au noeud.</ p>
|
<p>Si vous laissez l'hôte ou le port tcp vide, ils doivent être définis à l'aide des propriétés <code>msg.host</code> et <code>msg.port</code> dans chaque message envoyé au noeud.</ p>
|
||||||
</script>
|
</script>
|
@ -36,7 +36,9 @@
|
|||||||
</dl>
|
</dl>
|
||||||
<h3>Détails</h3>
|
<h3>Détails</h3>
|
||||||
<p>Le modèle de colonne peut contenir une liste ordonnée de noms de colonnes. Lors de la conversion de CSV en objet, les noms de colonne
|
<p>Le modèle de colonne peut contenir une liste ordonnée de noms de colonnes. Lors de la conversion de CSV en objet, les noms de colonne
|
||||||
seront utilisés comme noms de propriété. Alternativement, les noms de colonne peuvent être tirés de la première ligne du CSV.</p>
|
seront utilisés comme noms de propriété. Alternativement, les noms de colonne peuvent être tirés de la première ligne du CSV.
|
||||||
|
<p>Lorsque l'analyseur RFC est sélectionné, le modèle de colonne doit être conforme à la norme RFC4180.</p>
|
||||||
|
</p>
|
||||||
<p>Lors de la conversion au format CSV, le modèle de colonnes est utilisé pour identifier les propriétés à extraire de l'objet et dans quel ordre.</p>
|
<p>Lors de la conversion au format CSV, le modèle de colonnes est utilisé pour identifier les propriétés à extraire de l'objet et dans quel ordre.</p>
|
||||||
<p>Si le modèle de colonnes est vide, vous pouvez utiliser une simple liste de propriétés séparées par des virgules fournies dans <code>msg.columns</code> pour
|
<p>Si le modèle de colonnes est vide, vous pouvez utiliser une simple liste de propriétés séparées par des virgules fournies dans <code>msg.columns</code> pour
|
||||||
déterminer quoi extraire et dans quel ordre. Si ni l'un ni l'autre n'est présent, toutes les propriétés de l'objet sont sorties dans l'ordre
|
déterminer quoi extraire et dans quel ordre. Si ni l'un ni l'autre n'est présent, toutes les propriétés de l'objet sont sorties dans l'ordre
|
||||||
|
@ -319,6 +319,7 @@ module.exports = {
|
|||||||
getPluginsByType: plugins.getPluginsByType,
|
getPluginsByType: plugins.getPluginsByType,
|
||||||
getPluginList: plugins.getPluginList,
|
getPluginList: plugins.getPluginList,
|
||||||
getPluginConfigs: plugins.getPluginConfigs,
|
getPluginConfigs: plugins.getPluginConfigs,
|
||||||
|
getPluginConfig: plugins.getPluginConfig,
|
||||||
exportPluginSettings: plugins.exportPluginSettings,
|
exportPluginSettings: plugins.exportPluginSettings,
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@ const child_process = require('child_process');
|
|||||||
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
||||||
let installerEnabled = false;
|
let installerEnabled = false;
|
||||||
|
|
||||||
|
const plugins = require("./plugins");
|
||||||
|
|
||||||
let settings;
|
let settings;
|
||||||
const moduleRe = /^(@[^/@]+?[/])?[^/@]+?$/;
|
const moduleRe = /^(@[^/@]+?[/])?[^/@]+?$/;
|
||||||
const slashRe = process.platform === "win32" ? /\\|[/]/ : /[/]/;
|
const slashRe = process.platform === "win32" ? /\\|[/]/ : /[/]/;
|
||||||
@ -330,10 +332,18 @@ function reportRemovedModules(removedNodes) {
|
|||||||
//comms.publish("node/removed",removedNodes,false);
|
//comms.publish("node/removed",removedNodes,false);
|
||||||
log.info(log._("server.removed-types"));
|
log.info(log._("server.removed-types"));
|
||||||
for (var j=0;j<removedNodes.length;j++) {
|
for (var j=0;j<removedNodes.length;j++) {
|
||||||
for (var i=0;i<removedNodes[j].types.length;i++) {
|
for (var i=0;i<removedNodes[j].types?.length;i++) {
|
||||||
log.info(" - "+(removedNodes[j].module?removedNodes[j].module+":":"")+removedNodes[j].types[i]);
|
log.info(" - "+(removedNodes[j].module?removedNodes[j].module+":":"")+removedNodes[j].types[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.info(log._("server.removed-plugins"));
|
||||||
|
for (let j=0;j<removedNodes.length;j++) {
|
||||||
|
for (var i=0;i<removedNodes[j].plugins?.length;i++) {
|
||||||
|
log.info(" - "+(removedNodes[j].module?removedNodes[j].module+":":"")+removedNodes[j].plugins[i].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return removedNodes;
|
return removedNodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -495,8 +505,12 @@ function uninstallModule(module) {
|
|||||||
} catch(err) {
|
} catch(err) {
|
||||||
return reject(new Error(log._("server.install.uninstall-failed",{name:module})));
|
return reject(new Error(log._("server.install.uninstall-failed",{name:module})));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// need to remove the plugins first,
|
||||||
|
// as registry data necessary to perform this operation
|
||||||
|
var list = plugins.removeModule(module);
|
||||||
|
list = list.concat(registry.removeModule(module));
|
||||||
|
|
||||||
var list = registry.removeModule(module);
|
|
||||||
log.info(log._("server.install.uninstalling",{name:module}));
|
log.info(log._("server.install.uninstalling",{name:module}));
|
||||||
|
|
||||||
let triggerPayload = {
|
let triggerPayload = {
|
||||||
|
@ -39,6 +39,8 @@ function registerPlugin(nodeSetId,id,definition) {
|
|||||||
pluginSettings[id] = definition.settings;
|
pluginSettings[id] = definition.settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reset the cache when a new plugin is incoming!
|
||||||
|
pluginConfigCache = {};
|
||||||
|
|
||||||
if (definition.onadd && typeof definition.onadd === 'function') {
|
if (definition.onadd && typeof definition.onadd === 'function') {
|
||||||
definition.onadd();
|
definition.onadd();
|
||||||
@ -55,29 +57,47 @@ function getPluginsByType(type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getPluginConfigs(lang) {
|
function getPluginConfigs(lang) {
|
||||||
|
// we're not re-using getPluginConfig() here,
|
||||||
|
// to avoid calling registry.getModuleList() multiple times!
|
||||||
|
|
||||||
if (!pluginConfigCache[lang]) {
|
if (!pluginConfigCache[lang]) {
|
||||||
var result = "";
|
var result = "";
|
||||||
var script = "";
|
|
||||||
var moduleConfigs = registry.getModuleList();
|
var moduleConfigs = registry.getModuleList();
|
||||||
for (var module in moduleConfigs) {
|
for (var module in moduleConfigs) {
|
||||||
/* istanbul ignore else */
|
/* istanbul ignore else */
|
||||||
if (moduleConfigs.hasOwnProperty(module)) {
|
if (moduleConfigs.hasOwnProperty(module)) {
|
||||||
var plugins = moduleConfigs[module].plugins;
|
result += generateModulePluginConfig(moduleConfigs[module]);
|
||||||
for (var plugin in plugins) {
|
|
||||||
if (plugins.hasOwnProperty(plugin)) {
|
|
||||||
var config = plugins[plugin];
|
|
||||||
if (config.enabled && !config.err && config.config) {
|
|
||||||
result += "\n<!-- --- [red-plugin:"+config.id+"] --- -->\n";
|
|
||||||
result += config.config;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pluginConfigCache[lang] = result;
|
pluginConfigCache[lang] = result;
|
||||||
}
|
}
|
||||||
return pluginConfigCache[lang];
|
return pluginConfigCache[lang];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getPluginConfig(id, lang) {
|
||||||
|
let result = '';
|
||||||
|
let moduleConfigs = registry.getModuleList();
|
||||||
|
if (moduleConfigs.hasOwnProperty(id)) {
|
||||||
|
result = generateModulePluginConfig(moduleConfigs[id]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateModulePluginConfig(module) {
|
||||||
|
let result = '';
|
||||||
|
const plugins = module.plugins
|
||||||
|
for (let plugin in plugins) {
|
||||||
|
if (plugins.hasOwnProperty(plugin)) {
|
||||||
|
let config = plugins[plugin];
|
||||||
|
if (config.enabled && !config.err && config.config) {
|
||||||
|
result += "\n<!-- --- [red-plugin:"+config.id+"] --- -->\n";
|
||||||
|
result += config.config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
function getPluginList() {
|
function getPluginList() {
|
||||||
var list = [];
|
var list = [];
|
||||||
var moduleConfigs = registry.getModuleList();
|
var moduleConfigs = registry.getModuleList();
|
||||||
@ -142,12 +162,51 @@ function exportPluginSettings(safeSettings) {
|
|||||||
return safeSettings;
|
return safeSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function removeModule(moduleId) {
|
||||||
|
|
||||||
|
// clean the (plugin) registry when a module is removed / uninstalled
|
||||||
|
|
||||||
|
let pluginList = [];
|
||||||
|
let module = registry.getModule(moduleId);
|
||||||
|
let keys = Object.keys(module.plugins ?? {});
|
||||||
|
keys.forEach( key => {
|
||||||
|
let _plugins = module.plugins[key].plugins ?? [];
|
||||||
|
_plugins.forEach( plugin => {
|
||||||
|
let id = plugin.id;
|
||||||
|
|
||||||
|
if (plugin.onremove && typeof plugin.onremove === 'function') {
|
||||||
|
plugin.onremove();
|
||||||
|
}
|
||||||
|
|
||||||
|
delete pluginToId[id];
|
||||||
|
delete plugins[id];
|
||||||
|
delete pluginSettings[id];
|
||||||
|
pluginConfigCache = {};
|
||||||
|
|
||||||
|
let psbtype = pluginsByType[plugin.type] ?? [];
|
||||||
|
for (let i=psbtype.length; i>0; i--) {
|
||||||
|
let pbt = psbtype[i-1];
|
||||||
|
if (pbt.id == id) {
|
||||||
|
psbtype.splice(i-1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
pluginList.push(registry.filterNodeInfo(module.plugins[key]));
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
return pluginList;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
init,
|
init,
|
||||||
registerPlugin,
|
registerPlugin,
|
||||||
getPlugin,
|
getPlugin,
|
||||||
getPluginsByType,
|
getPluginsByType,
|
||||||
getPluginConfigs,
|
getPluginConfigs,
|
||||||
|
getPluginConfig,
|
||||||
getPluginList,
|
getPluginList,
|
||||||
exportPluginSettings
|
exportPluginSettings,
|
||||||
|
removeModule
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,13 @@ function filterNodeInfo(n) {
|
|||||||
r.err = n.err;
|
r.err = n.err;
|
||||||
}
|
}
|
||||||
if (n.hasOwnProperty("plugins")) {
|
if (n.hasOwnProperty("plugins")) {
|
||||||
r.plugins = n.plugins;
|
r.plugins = n.plugins.map(p => {
|
||||||
|
return {
|
||||||
|
id: p.id,
|
||||||
|
type: p.type,
|
||||||
|
module: p.module
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (n.type === "plugin") {
|
if (n.type === "plugin") {
|
||||||
r.editor = !!n.template;
|
r.editor = !!n.template;
|
||||||
@ -386,7 +392,8 @@ function getModuleInfo(module) {
|
|||||||
local: moduleConfigs[module].local,
|
local: moduleConfigs[module].local,
|
||||||
user: moduleConfigs[module].user,
|
user: moduleConfigs[module].user,
|
||||||
path: moduleConfigs[module].path,
|
path: moduleConfigs[module].path,
|
||||||
nodes: []
|
nodes: [],
|
||||||
|
plugins: []
|
||||||
};
|
};
|
||||||
if (moduleConfigs[module].dependencies) {
|
if (moduleConfigs[module].dependencies) {
|
||||||
m.dependencies = moduleConfigs[module].dependencies;
|
m.dependencies = moduleConfigs[module].dependencies;
|
||||||
@ -399,6 +406,14 @@ function getModuleInfo(module) {
|
|||||||
nodeInfo.version = m.version;
|
nodeInfo.version = m.version;
|
||||||
m.nodes.push(nodeInfo);
|
m.nodes.push(nodeInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let plugins = Object.values(moduleConfigs[module].plugins ?? {});
|
||||||
|
plugins.forEach((plugin) => {
|
||||||
|
let nodeInfo = filterNodeInfo(plugin);
|
||||||
|
nodeInfo.version = m.version;
|
||||||
|
m.plugins.push(nodeInfo);
|
||||||
|
});
|
||||||
|
|
||||||
return m;
|
return m;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
|
@ -36,7 +36,7 @@ var connections = [];
|
|||||||
const events = require("@node-red/util").events;
|
const events = require("@node-red/util").events;
|
||||||
|
|
||||||
function handleCommsEvent(event) {
|
function handleCommsEvent(event) {
|
||||||
publish(event.topic,event.data,event.retain);
|
publish(event.topic,event.data,event.retain,event.session,event.excludeSession);
|
||||||
}
|
}
|
||||||
function handleStatusEvent(event) {
|
function handleStatusEvent(event) {
|
||||||
if (!event.status) {
|
if (!event.status) {
|
||||||
@ -74,13 +74,17 @@ function handleEventLog(event) {
|
|||||||
publish("event-log/"+event.id,event.payload||{});
|
publish("event-log/"+event.id,event.payload||{});
|
||||||
}
|
}
|
||||||
|
|
||||||
function publish(topic,data,retain) {
|
function publish(topic, data, retain, session, excludeSession) {
|
||||||
if (retain) {
|
if (retain) {
|
||||||
retained[topic] = data;
|
retained[topic] = data;
|
||||||
} else {
|
} else {
|
||||||
delete retained[topic];
|
delete retained[topic];
|
||||||
}
|
}
|
||||||
connections.forEach(connection => connection.send(topic,data))
|
connections.forEach(connection => {
|
||||||
|
if ((!session || connection.session === session) && (!excludeSession || connection.session !== excludeSession)) {
|
||||||
|
connection.send(topic,data)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -109,6 +113,10 @@ var api = module.exports = {
|
|||||||
*/
|
*/
|
||||||
addConnection: async function(opts) {
|
addConnection: async function(opts) {
|
||||||
connections.push(opts.client);
|
connections.push(opts.client);
|
||||||
|
events.emit('comms:connection-added', {
|
||||||
|
session: opts.client.session,
|
||||||
|
user: opts.client.user
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -126,6 +134,9 @@ var api = module.exports = {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
events.emit('comms:connection-removed', {
|
||||||
|
session: opts.client.session
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -157,5 +168,23 @@ var api = module.exports = {
|
|||||||
* @return {Promise<Object>} - resolves when complete
|
* @return {Promise<Object>} - resolves when complete
|
||||||
* @memberof @node-red/runtime_comms
|
* @memberof @node-red/runtime_comms
|
||||||
*/
|
*/
|
||||||
unsubscribe: async function(opts) {}
|
unsubscribe: async function(opts) {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object} opts
|
||||||
|
* @param {User} opts.user - the user calling the api
|
||||||
|
* @param {CommsConnection} opts.client - the client connection
|
||||||
|
* @param {String} opts.topic - the message topic
|
||||||
|
* @param {String} opts.data - the message data
|
||||||
|
* @return {Promise<Object>} - resolves when complete
|
||||||
|
*/
|
||||||
|
receive: async function (opts) {
|
||||||
|
if (opts.topic) {
|
||||||
|
events.emit('comms:message:' + opts.topic, {
|
||||||
|
session: opts.client.session,
|
||||||
|
user: opts.user,
|
||||||
|
data: opts.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -65,6 +65,25 @@ var api = module.exports = {
|
|||||||
runtime.log.audit({event: "plugins.configs.get"}, opts.req);
|
runtime.log.audit({event: "plugins.configs.get"}, opts.req);
|
||||||
return runtime.plugins.getPluginConfigs(opts.lang);
|
return runtime.plugins.getPluginConfigs(opts.lang);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the editor content for one registered plugin
|
||||||
|
* @param {Object} opts
|
||||||
|
* @param {User} opts.user - the user calling the api
|
||||||
|
* @param {User} opts.user - the user calling the api
|
||||||
|
* @param {Object} opts.req - the request to log (optional)
|
||||||
|
* @return {Promise<NodeInfo>} - the plugin information
|
||||||
|
* @memberof @node-red/runtime_plugins
|
||||||
|
*/
|
||||||
|
getPluginConfig: async function(opts) {
|
||||||
|
if (/[^0-9a-z=\-\*]/i.test(opts.lang)) {
|
||||||
|
throw new Error("Invalid language: "+opts.lang)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
runtime.log.audit({event: "plugins.configs.get"}, opts.req);
|
||||||
|
return runtime.plugins.getPluginConfig(opts.module, opts.lang);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all registered module message catalogs
|
* Gets all registered module message catalogs
|
||||||
* @param {Object} opts
|
* @param {Object} opts
|
||||||
|
@ -106,14 +106,22 @@ async function evaluateEnvProperties(flow, env, credentials) {
|
|||||||
result = { value: result, __clone__: true}
|
result = { value: result, __clone__: true}
|
||||||
}
|
}
|
||||||
evaluatedEnv[name] = result
|
evaluatedEnv[name] = result
|
||||||
|
} else {
|
||||||
|
evaluatedEnv[name] = undefined
|
||||||
|
flow.error(`Error evaluating env property '${name}': ${err.toString()}`)
|
||||||
}
|
}
|
||||||
resolve()
|
resolve()
|
||||||
});
|
});
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
value = redUtil.evaluateNodeProperty(value, type, {_flow: flow}, null, null);
|
try {
|
||||||
if (typeof value === 'object') {
|
value = redUtil.evaluateNodeProperty(value, type, {_flow: flow}, null, null);
|
||||||
value = { value: value, __clone__: true}
|
if (typeof value === 'object') {
|
||||||
|
value = { value: value, __clone__: true}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
value = undefined
|
||||||
|
flow.error(`Error evaluating env property '${name}': ${err.toString()}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
evaluatedEnv[name] = value
|
evaluatedEnv[name] = value
|
||||||
|
@ -173,7 +173,11 @@ function installModule(module,version,url) {
|
|||||||
if (info.pending_version) {
|
if (info.pending_version) {
|
||||||
events.emit("runtime-event",{id:"node/upgraded",retain:false,payload:{module:info.name,version:info.pending_version}});
|
events.emit("runtime-event",{id:"node/upgraded",retain:false,payload:{module:info.name,version:info.pending_version}});
|
||||||
} else {
|
} else {
|
||||||
events.emit("runtime-event",{id:"node/added",retain:false,payload:info.nodes});
|
if (!info.nodes.length && info.plugins.length) {
|
||||||
|
events.emit("runtime-event",{id:"plugin/added",retain:false,payload:info.plugins});
|
||||||
|
} else {
|
||||||
|
events.emit("runtime-event",{id:"node/added",retain:false,payload:info.nodes});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return info;
|
return info;
|
||||||
});
|
});
|
||||||
|
@ -7,5 +7,6 @@ module.exports = {
|
|||||||
getPluginsByType: registry.getPluginsByType,
|
getPluginsByType: registry.getPluginsByType,
|
||||||
getPluginList: registry.getPluginList,
|
getPluginList: registry.getPluginList,
|
||||||
getPluginConfigs: registry.getPluginConfigs,
|
getPluginConfigs: registry.getPluginConfigs,
|
||||||
|
getPluginConfig: registry.getPluginConfig,
|
||||||
exportPluginSettings: registry.exportPluginSettings
|
exportPluginSettings: registry.exportPluginSettings
|
||||||
}
|
}
|
@ -25,6 +25,7 @@
|
|||||||
"removing-modules": "Removing modules from config",
|
"removing-modules": "Removing modules from config",
|
||||||
"added-types": "Added node types:",
|
"added-types": "Added node types:",
|
||||||
"removed-types": "Removed node types:",
|
"removed-types": "Removed node types:",
|
||||||
|
"removed-plugins": "Removed plugins:",
|
||||||
"install": {
|
"install": {
|
||||||
"invalid": "Invalid module name",
|
"invalid": "Invalid module name",
|
||||||
"installing": "Installing module: __name__, version: __version__",
|
"installing": "Installing module: __name__, version: __version__",
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
"@node-red/util": "4.0.0-beta.1",
|
"@node-red/util": "4.0.0-beta.1",
|
||||||
"async-mutex": "0.4.0",
|
"async-mutex": "0.4.0",
|
||||||
"clone": "2.1.2",
|
"clone": "2.1.2",
|
||||||
"express": "4.18.2",
|
"express": "4.19.2",
|
||||||
"fs-extra": "11.1.1",
|
"fs-extra": "11.1.1",
|
||||||
"json-stringify-safe": "5.0.1"
|
"json-stringify-safe": "5.0.1"
|
||||||
}
|
}
|
||||||
|
4
packages/node_modules/node-red/package.json
vendored
4
packages/node_modules/node-red/package.json
vendored
@ -37,9 +37,9 @@
|
|||||||
"@node-red/nodes": "4.0.0-beta.1",
|
"@node-red/nodes": "4.0.0-beta.1",
|
||||||
"basic-auth": "2.0.1",
|
"basic-auth": "2.0.1",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"express": "4.18.2",
|
"express": "4.19.2",
|
||||||
"fs-extra": "11.1.1",
|
"fs-extra": "11.1.1",
|
||||||
"node-red-admin": "^3.1.2",
|
"node-red-admin": "^3.1.3",
|
||||||
"nopt": "5.0.0",
|
"nopt": "5.0.0",
|
||||||
"semver": "7.5.4"
|
"semver": "7.5.4"
|
||||||
},
|
},
|
||||||
|
@ -918,7 +918,7 @@ describe('change Node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('changes the value and type of the message property if a complete match', function(done) {
|
it('changes the value and type of the message property if a complete match - number', function(done) {
|
||||||
var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload", "pt": "msg", "from": "123", "fromt": "str", "to": "456", "tot": "num" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]]},
|
var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload", "pt": "msg", "from": "123", "fromt": "str", "to": "456", "tot": "num" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]]},
|
||||||
{id:"helperNode1", type:"helper", wires:[]}];
|
{id:"helperNode1", type:"helper", wires:[]}];
|
||||||
helper.load(changeNode, flow, function() {
|
helper.load(changeNode, flow, function() {
|
||||||
@ -938,6 +938,25 @@ describe('change Node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('changes the value and type of the message property if a complete match - boolean', function(done) {
|
||||||
|
var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload.a", "pt": "msg", "from": "123", "fromt": "str", "to": "true", "tot": "bool" }, { "t": "change", "p": "payload.b", "pt": "msg", "from": "456", "fromt": "str", "to": "false", "tot": "bool" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]]},
|
||||||
|
{id:"helperNode1", type:"helper", wires:[]}];
|
||||||
|
helper.load(changeNode, flow, function() {
|
||||||
|
var changeNode1 = helper.getNode("changeNode1");
|
||||||
|
var helperNode1 = helper.getNode("helperNode1");
|
||||||
|
helperNode1.on("input", function(msg) {
|
||||||
|
try {
|
||||||
|
msg.payload.a.should.equal(true);
|
||||||
|
msg.payload.b.should.equal(false);
|
||||||
|
done();
|
||||||
|
} catch(err) {
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
changeNode1.receive({payload: { a: "123", b: "456" }});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('changes the value of a multi-level message property', function(done) {
|
it('changes the value of a multi-level message property', function(done) {
|
||||||
var flow = [{"id":"changeNode1","type":"change","action":"change","property":"foo.bar","from":"Hello","to":"Goodbye","reg":false,"name":"changeNode","wires":[["helperNode1"]]},
|
var flow = [{"id":"changeNode1","type":"change","action":"change","property":"foo.bar","from":"Hello","to":"Goodbye","reg":false,"name":"changeNode","wires":[["helperNode1"]]},
|
||||||
{id:"helperNode1", type:"helper", wires:[]}];
|
{id:"helperNode1", type:"helper", wires:[]}];
|
||||||
@ -993,20 +1012,28 @@ describe('change Node', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('changes the value of the message property based on a regex', function(done) {
|
it('changes the value of the message property based on a regex', function(done) {
|
||||||
var flow = [{"id":"changeNode1","type":"change","action":"change","property":"payload","from":"\\d+","to":"NUMBER","reg":true,"name":"changeNode","wires":[["helperNode1"]]},
|
const flow = [
|
||||||
{id:"helperNode1", type:"helper", wires:[]}];
|
{"id":"changeNode1","type":"change",rules:[
|
||||||
|
{ "t": "change", "p": "payload.a", "pt": "msg", "from": "\\d+", "fromt": "re", "to": "NUMBER", "tot": "str" },
|
||||||
|
{ "t": "change", "p": "payload.b", "pt": "msg", "from": "on", "fromt": "re", "to": "true", "tot": "bool" },
|
||||||
|
{ "t": "change", "p": "payload.c", "pt": "msg", "from": "off", "fromt": "re", "to": "false", "tot": "bool" }
|
||||||
|
],"reg":false,"name":"changeNode","wires":[["helperNode1"]]},
|
||||||
|
{id:"helperNode1", type:"helper", wires:[]}
|
||||||
|
];
|
||||||
helper.load(changeNode, flow, function() {
|
helper.load(changeNode, flow, function() {
|
||||||
var changeNode1 = helper.getNode("changeNode1");
|
var changeNode1 = helper.getNode("changeNode1");
|
||||||
var helperNode1 = helper.getNode("helperNode1");
|
var helperNode1 = helper.getNode("helperNode1");
|
||||||
helperNode1.on("input", function(msg) {
|
helperNode1.on("input", function(msg) {
|
||||||
try {
|
try {
|
||||||
msg.payload.should.equal("Replace all numbers NUMBER and NUMBER");
|
msg.payload.a.should.equal("Replace all numbers NUMBER and NUMBER");
|
||||||
|
msg.payload.b.should.equal(true)
|
||||||
|
msg.payload.c.should.equal(false)
|
||||||
done();
|
done();
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
done(err);
|
done(err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
changeNode1.receive({payload:"Replace all numbers 12 and 14"});
|
changeNode1.receive({payload:{ a: "Replace all numbers 12 and 14", b: 'on', c: 'off' } });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -60,6 +60,7 @@ describe('HTTP Request Node', function() {
|
|||||||
function startServer(done) {
|
function startServer(done) {
|
||||||
testPort += 1;
|
testPort += 1;
|
||||||
testServer = stoppable(http.createServer(testApp));
|
testServer = stoppable(http.createServer(testApp));
|
||||||
|
const promises = []
|
||||||
testServer.listen(testPort,function(err) {
|
testServer.listen(testPort,function(err) {
|
||||||
testSslPort += 1;
|
testSslPort += 1;
|
||||||
console.log("ssl port", testSslPort);
|
console.log("ssl port", testSslPort);
|
||||||
@ -81,13 +82,17 @@ describe('HTTP Request Node', function() {
|
|||||||
*/
|
*/
|
||||||
};
|
};
|
||||||
testSslServer = stoppable(https.createServer(sslOptions,testApp));
|
testSslServer = stoppable(https.createServer(sslOptions,testApp));
|
||||||
testSslServer.listen(testSslPort, function(err){
|
console.log('> start testSslServer')
|
||||||
if (err) {
|
promises.push(new Promise((resolve, reject) => {
|
||||||
console.log(err);
|
testSslServer.listen(testSslPort, function(err){
|
||||||
} else {
|
console.log(' done testSslServer')
|
||||||
console.log("started testSslServer");
|
if (err) {
|
||||||
}
|
reject(err)
|
||||||
});
|
} else {
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}))
|
||||||
|
|
||||||
testSslClientPort += 1;
|
testSslClientPort += 1;
|
||||||
var sslClientOptions = {
|
var sslClientOptions = {
|
||||||
@ -97,10 +102,17 @@ describe('HTTP Request Node', function() {
|
|||||||
requestCert: true
|
requestCert: true
|
||||||
};
|
};
|
||||||
testSslClientServer = stoppable(https.createServer(sslClientOptions, testApp));
|
testSslClientServer = stoppable(https.createServer(sslClientOptions, testApp));
|
||||||
testSslClientServer.listen(testSslClientPort, function(err){
|
console.log('> start testSslClientServer')
|
||||||
console.log("ssl-client", err)
|
promises.push(new Promise((resolve, reject) => {
|
||||||
});
|
testSslClientServer.listen(testSslClientPort, function(err){
|
||||||
|
console.log(' done testSslClientServer')
|
||||||
|
if (err) {
|
||||||
|
reject(err)
|
||||||
|
} else {
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}))
|
||||||
testProxyPort += 1;
|
testProxyPort += 1;
|
||||||
testProxyServer = stoppable(httpProxy(http.createServer()))
|
testProxyServer = stoppable(httpProxy(http.createServer()))
|
||||||
|
|
||||||
@ -109,7 +121,17 @@ describe('HTTP Request Node', function() {
|
|||||||
res.setHeader("x-testproxy-header", "foobar")
|
res.setHeader("x-testproxy-header", "foobar")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
testProxyServer.listen(testProxyPort)
|
console.log('> testProxyServer')
|
||||||
|
promises.push(new Promise((resolve, reject) => {
|
||||||
|
testProxyServer.listen(testProxyPort, function(err) {
|
||||||
|
console.log(' done testProxyServer')
|
||||||
|
if (err) {
|
||||||
|
reject(err)
|
||||||
|
} else {
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
|
||||||
testProxyAuthPort += 1
|
testProxyAuthPort += 1
|
||||||
testProxyServerAuth = stoppable(httpProxy(http.createServer()))
|
testProxyServerAuth = stoppable(httpProxy(http.createServer()))
|
||||||
@ -131,9 +153,19 @@ describe('HTTP Request Node', function() {
|
|||||||
res.setHeader("x-testproxy-header", "foobar")
|
res.setHeader("x-testproxy-header", "foobar")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
testProxyServerAuth.listen(testProxyAuthPort)
|
console.log('> testProxyServerAuth')
|
||||||
|
promises.push(new Promise((resolve, reject) => {
|
||||||
|
testProxyServerAuth.listen(testProxyAuthPort, function(err) {
|
||||||
|
console.log(' done testProxyServerAuth')
|
||||||
|
if (err) {
|
||||||
|
reject(err)
|
||||||
|
} else {
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
|
||||||
done(err);
|
Promise.all(promises).then(() => { done() }).catch(done)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -429,7 +461,11 @@ describe('HTTP Request Node', function() {
|
|||||||
if (err) {
|
if (err) {
|
||||||
done(err);
|
done(err);
|
||||||
}
|
}
|
||||||
helper.startServer(done);
|
console.log('> helper.startServer')
|
||||||
|
helper.startServer(function(err) {
|
||||||
|
console.log('> helper started')
|
||||||
|
done(err)
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ var NR_TEST_UTILS = require("nr-test-utils");
|
|||||||
var installer = NR_TEST_UTILS.require("@node-red/registry/lib/installer");
|
var installer = NR_TEST_UTILS.require("@node-red/registry/lib/installer");
|
||||||
var registry = NR_TEST_UTILS.require("@node-red/registry/lib/index");
|
var registry = NR_TEST_UTILS.require("@node-red/registry/lib/index");
|
||||||
var typeRegistry = NR_TEST_UTILS.require("@node-red/registry/lib/registry");
|
var typeRegistry = NR_TEST_UTILS.require("@node-red/registry/lib/registry");
|
||||||
|
let pluginRegistry = NR_TEST_UTILS.require("@node-red/registry/lib/plugins");
|
||||||
const { events, exec, log, hooks } = NR_TEST_UTILS.require("@node-red/util");
|
const { events, exec, log, hooks } = NR_TEST_UTILS.require("@node-red/util");
|
||||||
|
|
||||||
describe('nodes/registry/installer', function() {
|
describe('nodes/registry/installer', function() {
|
||||||
@ -66,6 +67,9 @@ describe('nodes/registry/installer', function() {
|
|||||||
if (typeRegistry.setModulePendingUpdated.restore) {
|
if (typeRegistry.setModulePendingUpdated.restore) {
|
||||||
typeRegistry.setModulePendingUpdated.restore();
|
typeRegistry.setModulePendingUpdated.restore();
|
||||||
}
|
}
|
||||||
|
if (pluginRegistry.removeModule.restore) {
|
||||||
|
pluginRegistry.removeModule.restore();
|
||||||
|
}
|
||||||
if (fs.statSync.restore) {
|
if (fs.statSync.restore) {
|
||||||
fs.statSync.restore();
|
fs.statSync.restore();
|
||||||
}
|
}
|
||||||
@ -502,6 +506,9 @@ describe('nodes/registry/installer', function() {
|
|||||||
var removeModule = sinon.stub(typeRegistry,"removeModule").callsFake(function(md) {
|
var removeModule = sinon.stub(typeRegistry,"removeModule").callsFake(function(md) {
|
||||||
return nodeInfo;
|
return nodeInfo;
|
||||||
});
|
});
|
||||||
|
let removePluginModule = sinon.stub(pluginRegistry,"removeModule").callsFake(function(md) {
|
||||||
|
return [];
|
||||||
|
});
|
||||||
var getModuleInfo = sinon.stub(registry,"getModuleInfo").callsFake(function(md) {
|
var getModuleInfo = sinon.stub(registry,"getModuleInfo").callsFake(function(md) {
|
||||||
return {nodes:[]};
|
return {nodes:[]};
|
||||||
});
|
});
|
||||||
|
@ -115,38 +115,7 @@ test-module-config`)
|
|||||||
|
|
||||||
let pluginList = plugins.getPluginList();
|
let pluginList = plugins.getPluginList();
|
||||||
JSON.stringify(pluginList).should.eql(JSON.stringify(
|
JSON.stringify(pluginList).should.eql(JSON.stringify(
|
||||||
[
|
[{"id":"test-module/test-set","enabled":true,"local":false,"user":false,"plugins":[{"id":"a-plugin","type":"foo","module":"test-module"},{"id":"a-plugin2","type":"bar","module":"test-module"},{"id":"a-plugin3","type":"foo","module":"test-module"}]},{"id":"test-module/test-disabled-set","enabled":false,"local":false,"user":false,"plugins":[]}]
|
||||||
{
|
|
||||||
"id": "test-module/test-set",
|
|
||||||
"enabled": true,
|
|
||||||
"local": false,
|
|
||||||
"user": false,
|
|
||||||
"plugins": [
|
|
||||||
{
|
|
||||||
"type": "foo",
|
|
||||||
"id": "a-plugin",
|
|
||||||
"module": "test-module"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "bar",
|
|
||||||
"id": "a-plugin2",
|
|
||||||
"module": "test-module"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "foo",
|
|
||||||
"id": "a-plugin3",
|
|
||||||
"module": "test-module"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "test-module/test-disabled-set",
|
|
||||||
"enabled": false,
|
|
||||||
"local": false,
|
|
||||||
"user": false,
|
|
||||||
"plugins": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user