From 61f6535be817f9a7b0c1266f11e5cce01fb55c1c Mon Sep 17 00:00:00 2001 From: HirokiUchikawa Date: Wed, 23 May 2018 16:54:03 +0900 Subject: [PATCH 01/34] Add test case for preventing following redirect --- test/nodes/core/io/21-httprequest_spec.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/nodes/core/io/21-httprequest_spec.js b/test/nodes/core/io/21-httprequest_spec.js index 43c42c8a4..4a1b513f0 100644 --- a/test/nodes/core/io/21-httprequest_spec.js +++ b/test/nodes/core/io/21-httprequest_spec.js @@ -796,6 +796,25 @@ describe('HTTP Request Node', function() { }); }); + it('should prevent following redirect when msg.followRedirects is false', function(done) { + var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/redirectToText')}, + {id:"n2", type:"helper"}]; + helper.load(httpRequestNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + try { + msg.should.have.property('statusCode',302); + msg.should.have.property('responseUrl', getTestURL('/redirectToText')); + done(); + } catch(err) { + done(err); + } + }); + n1.receive({payload:"foo",followRedirects:false}); + }); + }); + it('shuold output an error when request timeout occurred', function(done) { var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/timeout')}, {id:"n2", type:"helper"}]; From 9fd5d1db56a6b25a5342b55cd893a5f180e07909 Mon Sep 17 00:00:00 2001 From: HirokiUchikawa Date: Wed, 23 May 2018 17:16:20 +0900 Subject: [PATCH 02/34] Move to request module --- nodes/core/io/21-httprequest.js | 146 ++++++++-------------- package.json | 2 +- test/nodes/core/io/21-httprequest_spec.js | 8 +- 3 files changed, 63 insertions(+), 93 deletions(-) diff --git a/nodes/core/io/21-httprequest.js b/nodes/core/io/21-httprequest.js index b492fb9dc..4ddb1d3b9 100644 --- a/nodes/core/io/21-httprequest.js +++ b/nodes/core/io/21-httprequest.js @@ -16,9 +16,7 @@ module.exports = function(RED) { "use strict"; - var http = require("follow-redirects").http; - var https = require("follow-redirects").https; - var urllib = require("url"); + var request = require("request"); var mustache = require("mustache"); var querystring = require("querystring"); var cookie = require("cookie"); @@ -78,9 +76,13 @@ module.exports = function(RED) { if (msg.method && n.method && (n.method === "use")) { method = msg.method.toUpperCase(); // use the msg parameter } - var opts = urllib.parse(url); + var opts = {}; + opts.url = url; + opts.timeout = node.reqTimeout; opts.method = method; opts.headers = {}; + opts.encoding = null; // Force NodeJs to return a Buffer (instead of a string) + opts.maxRedirects = 21; var ctSet = "Content-Type"; // set default camel case var clSet = "Content-Length"; if (msg.headers) { @@ -109,7 +111,7 @@ module.exports = function(RED) { } } if (msg.hasOwnProperty('followRedirects')) { - opts.followRedirects = msg.followRedirects; + opts.followRedirect = msg.followRedirects; } if (msg.cookies) { var cookies = []; @@ -134,7 +136,10 @@ module.exports = function(RED) { } } if (this.credentials && this.credentials.user) { - opts.auth = this.credentials.user+":"+(this.credentials.password||""); + opts.auth = { + user: this.credentials.user, + pass: this.credentials.password||"" + }; } var payload = null; @@ -160,6 +165,7 @@ module.exports = function(RED) { opts.headers[clSet] = Buffer.byteLength(payload); } } + opts.body = payload; } // revert to user supplied Capitalisation if needed. if (opts.headers.hasOwnProperty('content-type') && (ctSet !== 'content-type')) { @@ -170,7 +176,6 @@ module.exports = function(RED) { opts.headers[clSet] = opts.headers['content-length']; delete opts.headers['content-length']; } - var urltotest = url; var noproxy; if (noprox) { for (var i in noprox) { @@ -180,22 +185,11 @@ module.exports = function(RED) { if (prox && !noproxy) { var match = prox.match(/^(http:\/\/)?(.+)?:([0-9]+)?/i); if (match) { - //opts.protocol = "http:"; - //opts.host = opts.hostname = match[2]; - //opts.port = (match[3] != null ? match[3] : 80); - opts.headers['Host'] = opts.host; - var heads = opts.headers; - var path = opts.pathname = opts.href; - opts = urllib.parse(prox); - opts.path = opts.pathname = path; - opts.headers = heads; - opts.method = method; - urltotest = match[0]; - if (opts.auth) { - opts.headers['Proxy-Authorization'] = "Basic "+new Buffer(opts.auth).toString('Base64') - } + opts.proxy = prox; + } else { + node.warn("Bad proxy url: "+ prox); + opts.proxy = null; } - else { node.warn("Bad proxy url: "+process.env.http_proxy); } } if (tlsNode) { tlsNode.addTLSOptions(opts); @@ -204,42 +198,37 @@ module.exports = function(RED) { opts.rejectUnauthorized = msg.rejectUnauthorized; } } - var req = ((/^https/.test(urltotest))?https:http).request(opts,function(res) { - // Force NodeJs to return a Buffer (instead of a string) - // See https://github.com/nodejs/node/issues/6038 - res.setEncoding(null); - delete res._readableState.decoder; - - msg.statusCode = res.statusCode; - msg.headers = res.headers; - msg.responseUrl = res.responseUrl; - msg.payload = []; - - if (msg.headers.hasOwnProperty('set-cookie')) { - msg.responseCookies = {}; - msg.headers['set-cookie'].forEach(function(c) { - var parsedCookie = cookie.parse(c); - var eq_idx = c.indexOf('='); - var key = c.substr(0, eq_idx).trim() - parsedCookie.value = parsedCookie[key]; - delete parsedCookie[key]; - msg.responseCookies[key] = parsedCookie; - - }) - - } - msg.headers['x-node-red-request-node'] = hashSum(msg.headers); - // msg.url = url; // revert when warning above finally removed - res.on('data',function(chunk) { - if (!Buffer.isBuffer(chunk)) { - // if the 'setEncoding(null)' fix above stops working in - // a new Node.js release, throw a noisy error so we know - // about it. - throw new Error("HTTP Request data chunk not a Buffer"); + request(opts, function(err, res, body) { + if(err){ + if(err.code === 'ETIMEDOUT' || err.code === 'ESOCKETTIMEDOUT') { + node.error(RED._("common.notification.errors.no-response"), msg); + node.status({fill:"red", shape:"ring", text:"common.notification.errors.no-response"}); + }else{ + node.error(err,msg); + node.status({fill:"red", shape:"ring", text:err.code}); } - msg.payload.push(chunk); - }); - res.on('end',function() { + msg.payload = err.toString() + " : " + url; + msg.statusCode = err.code; + node.send(msg); + }else{ + msg.statusCode = res.statusCode; + msg.headers = res.headers; + msg.responseUrl = res.request.uri.href; + msg.payload = body; + + if (msg.headers.hasOwnProperty('set-cookie')) { + msg.responseCookies = {}; + msg.headers['set-cookie'].forEach(function(c) { + var parsedCookie = cookie.parse(c); + var eq_idx = c.indexOf('='); + var key = c.substr(0, eq_idx).trim() + parsedCookie.value = parsedCookie[key]; + delete parsedCookie[key]; + msg.responseCookies[key] = parsedCookie; + }); + } + msg.headers['x-node-red-request-node'] = hashSum(msg.headers); + // msg.url = url; // revert when warning above finally removed if (node.metric()) { // Calculate request time var diff = process.hrtime(preRequestTimestamp); @@ -251,44 +240,19 @@ module.exports = function(RED) { } } - // Check that msg.payload is an array - if the req error - // handler has been called, it will have been set to a string - // and the error already handled - so no further action should - // be taken. #1344 - if (Array.isArray(msg.payload)) { - // Convert the payload to the required return type - msg.payload = Buffer.concat(msg.payload); // bin - if (node.ret !== "bin") { - msg.payload = msg.payload.toString('utf8'); // txt + // Convert the payload to the required return type + if (node.ret !== "bin") { + msg.payload = msg.payload.toString('utf8'); // txt - if (node.ret === "obj") { - try { msg.payload = JSON.parse(msg.payload); } // obj - catch(e) { node.warn(RED._("httpin.errors.json-error")); } - } + if (node.ret === "obj") { + try { msg.payload = JSON.parse(msg.payload); } // obj + catch(e) { node.warn(RED._("httpin.errors.json-error")); } } - node.status({}); - node.send(msg); } - }); + node.status({}); + node.send(msg); + } }); - req.setTimeout(node.reqTimeout, function() { - node.error(RED._("common.notification.errors.no-response"),msg); - setTimeout(function() { - node.status({fill:"red",shape:"ring",text:"common.notification.errors.no-response"}); - },10); - req.abort(); - }); - req.on('error',function(err) { - node.error(err,msg); - msg.payload = err.toString() + " : " + url; - msg.statusCode = err.code; - node.status({fill:"red",shape:"ring",text:err.code}); - node.send(msg); - }); - if (payload) { - req.write(payload); - } - req.end(); }); this.on("close",function() { diff --git a/package.json b/package.json index f90789e43..eb7f30214 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,6 @@ "cron": "1.3.0", "express": "4.16.3", "express-session": "1.15.6", - "follow-redirects": "1.4.1", "fs-extra": "5.0.0", "fs.notify": "0.0.4", "hash-sum": "1.0.2", @@ -69,6 +68,7 @@ "passport-http-bearer": "1.0.1", "passport-oauth2-client-password": "0.1.2", "raw-body": "2.3.3", + "request": "2.87.0", "semver": "5.5.0", "sentiment": "2.1.0", "uglify-js": "3.3.25", diff --git a/test/nodes/core/io/21-httprequest_spec.js b/test/nodes/core/io/21-httprequest_spec.js index 4a1b513f0..b35c55d66 100644 --- a/test/nodes/core/io/21-httprequest_spec.js +++ b/test/nodes/core/io/21-httprequest_spec.js @@ -825,7 +825,13 @@ describe('HTTP Request Node', function() { var n2 = helper.getNode("n2"); n2.on("input", function(msg) { try { - msg.should.have.property('statusCode','ECONNRESET'); + msg.should.have.property('statusCode','ESOCKETTIMEDOUT'); + var logEvents = helper.log().args.filter(function(evt) { + return evt[0].type == 'http request'; + }); + logEvents.should.have.length(1); + var tstmp = logEvents[0][0].timestamp; + logEvents[0][0].should.eql({level:helper.log().ERROR, id:'n1',type:'http request',msg:'common.notification.errors.no-response', timestamp:tstmp}); done(); } catch(err) { done(err); From fcbea2629c57250f8abfd31d5f693235b77611dd Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 23 May 2018 22:41:39 +0100 Subject: [PATCH 03/34] Support flow.disabled and .info in /flow API --- red/runtime/nodes/flows/index.js | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/red/runtime/nodes/flows/index.js b/red/runtime/nodes/flows/index.js index d1ba541f5..1b2a87ac0 100644 --- a/red/runtime/nodes/flows/index.js +++ b/red/runtime/nodes/flows/index.js @@ -477,11 +477,19 @@ function addFlow(flow) { } flow.id = redUtil.generateId(); - var nodes = [{ + var tabNode = { type:'tab', label:flow.label, id:flow.id - }]; + } + if (flow.hasOwnProperty('info')) { + tabNode.info = flow.info; + } + if (flow.hasOwnProperty('disabled')) { + tabNode.disabled = flow.disabled; + } + + var nodes = [tabNode]; for (i=0;i Date: Thu, 24 May 2018 12:06:39 +0900 Subject: [PATCH 04/34] fix test failure of icon scan on windows --- test/red/runtime/nodes/registry/registry_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/red/runtime/nodes/registry/registry_spec.js b/test/red/runtime/nodes/registry/registry_spec.js index c5b9f6b5a..3a4a9fc21 100644 --- a/test/red/runtime/nodes/registry/registry_spec.js +++ b/test/red/runtime/nodes/registry/registry_spec.js @@ -563,7 +563,7 @@ describe("red/nodes/registry/registry",function() { } },icons: [{path:testIcon,icons:['test_icon.png']}]}); var iconPath = typeRegistry.getNodeIconPath('test-module','test_icon.png'); - iconPath.should.eql(testIcon+"/test_icon.png"); + iconPath.should.eql(path.resolve(testIcon+"/test_icon.png")); }); it('returns the debug icon when getting an unknown module', function() { From 10395ef254c37e3dd312b6e3ae8bfbad19334b33 Mon Sep 17 00:00:00 2001 From: Edward Vielmetti Date: Thu, 24 May 2018 04:48:05 -0400 Subject: [PATCH 05/34] typo fix *hierarchy (#1735) --- nodes/core/io/10-mqtt.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nodes/core/io/10-mqtt.html b/nodes/core/io/10-mqtt.html index 722a7a7ae..afe710472 100644 --- a/nodes/core/io/10-mqtt.html +++ b/nodes/core/io/10-mqtt.html @@ -41,7 +41,7 @@
payload string | buffer
a string unless detected as a binary buffer.
topic string
-
the MQTT topic, uses / as a heirarchy separator.
+
the MQTT topic, uses / as a hierarchy separator.
qos number
0, fire and forget - 1, at least once - 2, once and once only.
retain boolean
From 3df3d6f5162ea7598f2721354133a5d763ead1fb Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Thu, 24 May 2018 10:02:51 +0100 Subject: [PATCH 06/34] add debug to trigger test to help work out fails --- test/nodes/core/core/89-trigger_spec.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/nodes/core/core/89-trigger_spec.js b/test/nodes/core/core/89-trigger_spec.js index fb1b1df9a..d07ab2a9b 100644 --- a/test/nodes/core/core/89-trigger_spec.js +++ b/test/nodes/core/core/89-trigger_spec.js @@ -428,13 +428,14 @@ describe('trigger node', function() { n2.on("input", function(msg) { try { if (c === 0) { + console.log(c,Date.now() - ss,msg); msg.should.have.a.property("payload", "Hello"); c += 1; } else { + console.log(c,Date.now() - ss,msg); msg.should.have.a.property("payload", "World"); - //console.log(Date.now() - ss); - (Date.now() - ss).should.be.greaterThan(140); + (Date.now() - ss).should.be.greaterThan(150); done(); } } @@ -444,7 +445,7 @@ describe('trigger node', function() { n1.emit("input", {payload:"Hello"}); setTimeout( function() { n1.emit("input", {payload:"Error"}); - },20); + },30); setTimeout( function() { n1.emit("input", {payload:"World"}); },150); From 0ef16989cddbe5858ebbf2e89a6a492fc714fbfb Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 24 May 2018 20:25:26 +0100 Subject: [PATCH 07/34] Do not trim wires if node declares outputs in defaults but misses value Fixes #1737 --- editor/js/nodes.js | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/editor/js/nodes.js b/editor/js/nodes.js index 0a2fe7268..433e400a2 100644 --- a/editor/js/nodes.js +++ b/editor/js/nodes.js @@ -1033,15 +1033,31 @@ RED.nodes = (function() { node.type = "unknown"; } if (node._def.category != "config") { - node.inputs = n.inputs||node._def.inputs; - node.outputs = n.outputs||node._def.outputs; - // If 'wires' is longer than outputs, clip wires + if (n.hasOwnProperty('inputs')) { + node.inputs = n.inputs; + node._config.inputs = JSON.stringify(n.inputs); + } else { + node.inputs = node._def.inputs; + } + if (n.hasOwnProperty('outputs')) { + node.outputs = n.outputs; + node._config.outputs = JSON.stringify(n.outputs); + } else { + node.outputs = node._def.outputs; + } if (node.hasOwnProperty('wires') && node.wires.length > node.outputs) { - console.log("Warning: node.wires longer than node.outputs - trimming wires:",node.id," wires:",node.wires.length," outputs:",node.outputs); - node.wires = node.wires.slice(0,node.outputs); + if (!node._def.defaults.hasOwnProperty("outputs") || !isNaN(parseInt(n.outputs))) { + // If 'wires' is longer than outputs, clip wires + console.log("Warning: node.wires longer than node.outputs - trimming wires:",node.id," wires:",node.wires.length," outputs:",node.outputs); + node.wires = node.wires.slice(0,node.outputs); + } else { + // The node declares outputs in its defaults, but has not got a valid value + // Defer to the length of the wires array + node.outputs = node.wires.length; + } } for (d in node._def.defaults) { - if (node._def.defaults.hasOwnProperty(d)) { + if (node._def.defaults.hasOwnProperty(d) && d !== 'inputs' && d !== 'outputs') { node[d] = n[d]; node._config[d] = JSON.stringify(n[d]); } From 40f41678944912145f5d32e0f214b8398ee9d48b Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Thu, 24 May 2018 21:39:46 +0100 Subject: [PATCH 08/34] let TCP in node report remote ip and port when in single packet mode --- nodes/core/io/31-tcpin.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/nodes/core/io/31-tcpin.js b/nodes/core/io/31-tcpin.js index 5853d4f42..c3bc2ab8c 100644 --- a/nodes/core/io/31-tcpin.js +++ b/nodes/core/io/31-tcpin.js @@ -130,6 +130,8 @@ module.exports = function(RED) { socket.setKeepAlive(true,120000); if (socketTimeout !== null) { socket.setTimeout(socketTimeout); } var id = (1+Math.random()*4294967295).toString(16); + var fromi; + var fromp; connectionPool[id] = socket; count++; node.status({text:RED._("tcpin.status.connections",{count:count})}); @@ -155,18 +157,21 @@ module.exports = function(RED) { msg._session = {type:"tcp",id:id}; node.send(msg); } - } else { + } + else { if ((typeof data) === "string") { buffer = buffer+data; } else { buffer = Buffer.concat([buffer,data],buffer.length+data.length); } + fromi = socket.remoteAddress; + fromp = socket.remotePort; } }); socket.on('end', function() { if (!node.stream || (node.datatype === "utf8" && node.newline !== "")) { if (buffer.length > 0) { - var msg = {topic:node.topic, payload:buffer, ip:socket.remoteAddress, port:socket.remotePort}; + var msg = {topic:node.topic, payload:buffer, ip:fromi, port:fromp}; msg._session = {type:"tcp",id:id}; node.send(msg); } From 7f89a4a26f2079fe771ecc28587187f93c0f078d Mon Sep 17 00:00:00 2001 From: KatsuyaHoshii Date: Fri, 25 May 2018 11:48:33 +0900 Subject: [PATCH 09/34] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 328a85ef7..05edbdbb3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ matrix: include: - node_js: "10" script: - - istanbul cover ./node_modules/.bin/grunt --report lcovonly && istanbul report text && ( cat coverage/lcov.info | $(npm get prefix)/bin/coveralls || true ) && rm -rf coverage + - ./node_modules/.bin/grunt coverage && istanbul report text && ( cat coverage/lcov.info | $(npm get prefix)/bin/coveralls || true ) && rm -rf coverage before_script: - npm install -g istanbul coveralls - node_js: "8" From 252df81f5963a76ea11619496a9aa38a1ebb0978 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 25 May 2018 10:50:58 +0100 Subject: [PATCH 10/34] Pass Date into the Function node sandbox to fix instanceof tests --- nodes/core/core/80-function.js | 1 + test/nodes/core/core/80-function_spec.js | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/nodes/core/core/80-function.js b/nodes/core/core/80-function.js index 8aa95e4fc..0a7448ccd 100644 --- a/nodes/core/core/80-function.js +++ b/nodes/core/core/80-function.js @@ -80,6 +80,7 @@ module.exports = function(RED) { console:console, util:util, Buffer:Buffer, + Date: Date, RED: { util: RED.util }, diff --git a/test/nodes/core/core/80-function_spec.js b/test/nodes/core/core/80-function_spec.js index b96588b5b..f19e933dc 100644 --- a/test/nodes/core/core/80-function_spec.js +++ b/test/nodes/core/core/80-function_spec.js @@ -508,6 +508,22 @@ describe('function node', function() { }); }); + + it('should use the same Date object from outside the sandbox', function(done) { + var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=global.get('typeTest')(new Date());return msg;"}, + {id:"n2", type:"helper"}]; + helper.load(functionNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n1.context().global.set("typeTest",function(d) { return d instanceof Date }); + n2.on("input", function(msg) { + msg.should.have.property('payload', true); + done(); + }); + n1.receive({payload:"foo",topic: "bar"}); + }); + }); + describe('Logger', function () { it('should log an Info Message', function (done) { var flow = [{id: "n1", type: "function", wires: [["n2"]], func: "node.log('test');"}]; From 5069f2844c86ff5061f6fde20b82455801cdeaee Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 25 May 2018 10:51:29 +0100 Subject: [PATCH 11/34] Bump jsonata version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f90789e43..0f8b7fbd3 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "is-utf8": "0.2.1", "js-yaml": "3.11.0", "json-stringify-safe": "5.0.1", - "jsonata": "1.5.3", + "jsonata": "1.5.4", "media-typer": "0.3.0", "memorystore": "1.6.0", "mqtt": "2.18.0", From bca020bc4dbf357614c2f59e1c6752b1cd435982 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 25 May 2018 11:36:17 +0100 Subject: [PATCH 12/34] Tidy up default grunt task and fixup test break due to reorder Fixes #1738 --- .travis.yml | 2 +- Gruntfile.js | 4 ++-- red/api/auth/users.js | 4 ++++ test/red/api/auth/users_spec.js | 8 +++++--- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 05edbdbb3..054edefba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ matrix: include: - node_js: "10" script: - - ./node_modules/.bin/grunt coverage && istanbul report text && ( cat coverage/lcov.info | $(npm get prefix)/bin/coveralls || true ) && rm -rf coverage + - ./node_modules/.bin/grunt && istanbul report text && ( cat coverage/lcov.info | $(npm get prefix)/bin/coveralls || true ) && rm -rf coverage before_script: - npm install -g istanbul coveralls - node_js: "8" diff --git a/Gruntfile.js b/Gruntfile.js index 2be2d23c0..3738e60b4 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -55,7 +55,7 @@ module.exports = function(grunt) { reportFormats: ['lcov','html'], print: 'both' }, - all: { src: ['test/**/*_spec.js'] }, + all: { src: ["test/_spec.js","test/red/**/*_spec.js","test/nodes/**/*_spec.js"] }, core: { src: ["test/_spec.js","test/red/**/*_spec.js"]}, nodes: { src: ["test/nodes/**/*_spec.js"]} }, @@ -474,7 +474,7 @@ module.exports = function(grunt) { grunt.registerTask('default', 'Builds editor content then runs code style checks and unit tests on all components', - ['build','test-core','test-editor','test-nodes']); + ['build','jshint:editor','mocha_istanbul:all']); grunt.registerTask('test-core', 'Runs code style check and unit tests on core runtime code', diff --git a/red/api/auth/users.js b/red/api/auth/users.js index 92c4c9ce9..24a762958 100644 --- a/red/api/auth/users.js +++ b/red/api/auth/users.js @@ -86,6 +86,10 @@ function init(config) { } else { api.authenticate = authenticate; } + } else { + api.get = get; + api.authenticate = authenticate; + api.default = api.default; } if (config.default) { if (typeof config.default === "function") { diff --git a/test/red/api/auth/users_spec.js b/test/red/api/auth/users_spec.js index 23c34001d..e7b70f7b1 100644 --- a/test/red/api/auth/users_spec.js +++ b/test/red/api/auth/users_spec.js @@ -194,8 +194,7 @@ describe("api/auth/users", function() { it('should fail to return user fred',function(done) { Users.get("fred").then(function(userf) { try { - userf.should.not.have.a.property("username","fred"); - userf.should.not.have.a.property("permissions","*"); + should.not.exist(userf); done(); } catch(err) { done(err); @@ -212,9 +211,12 @@ describe("api/auth/users", function() { default: function() { return("Done"); } }); }); + after(function() { + Users.init({}); + }); describe('#default',function() { it('handles api.default being a function',function(done) { - Users.should.have.property('default').which.is.a.Function; + Users.should.have.property('default').which.is.a.Function(); (Users.default()).should.equal("Done"); done(); }); From 8cb2e5140762c1a53abc946f326670867c7a24ca Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 25 May 2018 11:40:14 +0100 Subject: [PATCH 13/34] Relax twitter node version ready for major version bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0f8b7fbd3..820f6e426 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "node-red-node-email": "0.1.*", "node-red-node-feedparser": "0.1.*", "node-red-node-rbe": "0.2.*", - "node-red-node-twitter": "0.1.*", + "node-red-node-twitter": "*", "nopt": "4.0.1", "oauth2orize": "1.11.0", "on-headers": "1.0.1", From 787709371361f3055aac1a7d3b11ca9aad58dcc2 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 25 May 2018 13:23:18 +0100 Subject: [PATCH 14/34] Bump 0.18.7 --- CHANGELOG.md | 18 ++++++++++++++++++ package.json | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d6cccb65..749226d20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +#### 0.18.7: Maintenance Release + + +Editor Fixes + + - - Do not trim wires if node declares outputs in defaults but misses value Fixes #1737 + +Node Fixes + + - Relax twitter node version ready for major version bump + - Pass Date into the Function node sandbox to fix instanceof tests + - let TCP in node report remote ip and port when in single packet mode + - typo fix in node help (#1735) + +Other Fixes + - Tidy up default grunt task and fixup test break due to reorder Fixes #1738 + - Bump jsonata version + #### 0.18.6: Maintenance Release Editor Fixes diff --git a/package.json b/package.json index 820f6e426..ec0f63a34 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red", - "version": "0.18.6", + "version": "0.18.7", "description": "A visual tool for wiring the Internet of Things", "homepage": "http://nodered.org", "license": "Apache-2.0", From 472bbdb59f59108b7fa8624f1a8a2110d213530c Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 25 May 2018 13:27:58 +0100 Subject: [PATCH 15/34] Fix typo in CHANGELOG --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 749226d20..2a73106c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,8 @@ #### 0.18.7: Maintenance Release - Editor Fixes - - - Do not trim wires if node declares outputs in defaults but misses value Fixes #1737 + - Do not trim wires if node declares outputs in defaults but misses value Fixes #1737 Node Fixes From a5c00b5c81fd5c3c7f8877c602d4b2dd49458aeb Mon Sep 17 00:00:00 2001 From: YumaMatsuura <38545050+YumaMatsuura@users.noreply.github.com> Date: Fri, 25 May 2018 21:55:03 +0900 Subject: [PATCH 16/34] add translate-user-settings (#1740) --- editor/js/ui/projects/projectUserSettings.js | 44 ++++++++++---------- red/api/editor/locales/en-US/editor.json | 25 ++++++++++- red/api/editor/locales/ja/editor.json | 25 ++++++++++- 3 files changed, 70 insertions(+), 24 deletions(-) diff --git a/editor/js/ui/projects/projectUserSettings.js b/editor/js/ui/projects/projectUserSettings.js index 4c3840461..a343a07cc 100644 --- a/editor/js/ui/projects/projectUserSettings.js +++ b/editor/js/ui/projects/projectUserSettings.js @@ -24,18 +24,18 @@ RED.projects.userSettings = (function() { var currentGitSettings = RED.settings.get('git') || {}; currentGitSettings.user = currentGitSettings.user || {}; - var title = $('

').text("Committer Details").appendTo(pane); + var title = $('

').text(RED._("editor:sidebar.project.userSettings.committerDetail")).appendTo(pane); var gitconfigContainer = $('').appendTo(pane); - $('
').appendTo(gitconfigContainer).text("Leave blank to use system default"); + $('
').appendTo(gitconfigContainer).text(RED._("editor:sidebar.project.userSettings.committer-tip")); var row = $('').appendTo(gitconfigContainer); - $('').text('Username').appendTo(row); + $('').text(RED._("editor:sidebar.project.userSettings.userName")).appendTo(row); gitUsernameInput = $('').appendTo(row); gitUsernameInput.val(currentGitSettings.user.name||""); row = $('').appendTo(gitconfigContainer); - $('').text('Email').appendTo(row); + $('').text(RED._("editor:sidebar.project.userSettings.email")).appendTo(row); gitEmailInput = $('').appendTo(row); gitEmailInput.val(currentGitSettings.user.email||""); } @@ -44,10 +44,10 @@ RED.projects.userSettings = (function() { function createSSHKeySection(pane) { var container = $('').appendTo(pane); var popover; - var title = $('

').text("SSH Keys").appendTo(container); - var subtitle = $('
').appendTo(container).text("Allows you to create secure connections to remote git repositories."); + var title = $('

').text(RED._("editor:sidebar.project.userSettings.sshKeys")).appendTo(container); + var subtitle = $('
').appendTo(container).text(RED._("editor:sidebar.project.userSettings.sshKeys-tip")); - var addKeyButton = $('') + var addKeyButton = $('') .appendTo(subtitle) .click(function(evt) { addKeyButton.attr('disabled',true); @@ -72,9 +72,9 @@ RED.projects.userSettings = (function() { var validPassphrase = passphrase.length === 0 || passphrase.length >= 8; passphraseInput.toggleClass('input-error',!validPassphrase); if (!validPassphrase) { - passphraseInputSubLabel.text("Passphrase too short"); + passphraseInputSubLabel.text(RED._("editor:sidebar.project.userSettings.passphrase-short")); } else if (passphrase.length === 0) { - passphraseInputSubLabel.text("Optional"); + passphraseInputSubLabel.text(RED._("editor:sidebar.project.userSettings.optional")); } else { passphraseInputSubLabel.text(""); } @@ -91,11 +91,11 @@ RED.projects.userSettings = (function() { var row = $('').appendTo(container); var addKeyDialog = $('
').hide().appendTo(row); - $('
').text('Add SSH Key').appendTo(addKeyDialog); + $('
').text(RED._("editor:sidebar.project.userSettings.add-sshKey")).appendTo(addKeyDialog); var addKeyDialogBody = $('
').appendTo(addKeyDialog); row = $('').appendTo(addKeyDialogBody); - $('
').appendTo(row).text("Generate a new public/private key pair"); + $('
').appendTo(row).text(RED._("editor:sidebar.project.userSettings.add-sshKey-tip")); // var bg = $('
',{class:"button-group", style:"text-align: center"}).appendTo(row); // var addLocalButton = $('').appendTo(bg); // var uploadButton = $('').appendTo(bg); @@ -125,19 +125,19 @@ RED.projects.userSettings = (function() { row = $('').appendTo(addKeyDialogBody); - $('').text('Name').appendTo(row); + $('').text(RED._("editor:sidebar.project.userSettings.name")).appendTo(row); var keyNameInputChanged = false; var keyNameInput = $('').appendTo(row).on("change keyup paste",function() { keyNameInputChanged = true; validateForm(); }); - $('').appendTo(row).find("small"); + $('').appendTo(row).find("small"); var generateKeyPane = $('
').appendTo(addKeyDialogBody); row = $('').appendTo(generateKeyPane); - $('').text('Passphrase').appendTo(row); + $('').text(RED._("editor:sidebar.project.userSettings.passphrase")).appendTo(row); var passphraseInput = $('').appendTo(row).on("change keyup paste",validateForm); - var passphraseInputSubLabel = $('').appendTo(row).find("small"); + var passphraseInputSubLabel = $('').appendTo(row).find("small"); // var addLocalKeyPane = $('
').hide().appendTo(addKeyDialogBody); // row = $('').appendTo(addLocalKeyPane); @@ -179,13 +179,13 @@ RED.projects.userSettings = (function() { } } var formButtons = $('').appendTo(addKeyDialog); - $('') + $('') .appendTo(formButtons) .click(function(evt) { evt.preventDefault(); hideEditForm(); }); - var saveButton = $('') + var saveButton = $('') .appendTo(formButtons) .click(function(evt) { evt.preventDefault(); @@ -264,7 +264,7 @@ RED.projects.userSettings = (function() { utils.sendRequest(options); var formButtons = $('').appendTo(row); - $('') + $('') .appendTo(formButtons) .click(function(evt) { try { @@ -289,7 +289,7 @@ RED.projects.userSettings = (function() { var container = $('
').appendTo(row); if (entry.empty) { container.addClass('red-ui-search-empty'); - container.text("No SSH keys"); + container.text(RED._("editor:sidebar.project.userSettings.no-sshKeys")); return; } var topRow = $('
').appendTo(container); @@ -313,7 +313,7 @@ RED.projects.userSettings = (function() { .click(function(e) { e.stopPropagation(); var spinner = utils.addSpinnerOverlay(row).addClass('projects-dialog-spinner-contain'); - var notification = RED.notify("Are you sure you want to delete the SSH key '"+entry.name+"'? This cannot be undone.", { + var notification = RED.notify(RED._("editor:sidebar.project.userSettings.deleteConfirm", {name:entry.name}), { type: 'warning', modal: true, fixed: true, @@ -326,7 +326,7 @@ RED.projects.userSettings = (function() { } }, { - text: "Delete key", + text: RED._("editor:sidebar.project.userSettings.delete"), click: function() { notification.close(); var url = "settings/user/keys/"+entry.name; @@ -400,7 +400,7 @@ RED.projects.userSettings = (function() { utils = _utils; RED.userSettings.add({ id:'gitconfig', - title: "Git config", // TODO: nls + title: RED._("editor:sidebar.project.userSettings.git-config"), // TODO: nls get: createSettingsPane, close: function() { var currentGitSettings = RED.settings.get('git') || {}; diff --git a/red/api/editor/locales/en-US/editor.json b/red/api/editor/locales/en-US/editor.json index d827ba092..dfcef21a6 100644 --- a/red/api/editor/locales/en-US/editor.json +++ b/red/api/editor/locales/en-US/editor.json @@ -434,7 +434,30 @@ "dependencies": "Dependencies", "settings": "Settings", "editDescription": "Edit project description", - "editDependencies": "Edit project dependencies" + "editDependencies": "Edit project dependencies", + "userSettings": { + "committerDetail": "Committer Details", + "committer-tip": "Leave blank to use system default", + "userName": "Username", + "email": "Email", + "sshKeys": "SSH Keys", + "sshKeys-tip": "Allows you to create secure connections to remote git repositories.", + "add": "add key", + "add-sshKey": "Add SSH Key", + "add-sshKey-tip": "Generate a new public/private key pair", + "name": "Name", + "nameRule": "Must contain only A-Z 0-9 _ -", + "passphrase": "Passphrase", + "passphrase-short": "Passphrase too short", + "optional": "Optional", + "cancel": "Cancel", + "generate": "Generate key", + "no-sshKeys": "No SSH keys", + "copy-publicKey": "Copy public key to clipboard", + "delete": "Delete key", + "git-config": "Git config", + "deleteConfirm": "Are you sure you want to delete the SSH key __name__? This cannot be undone." + } } }, "typedInput": { diff --git a/red/api/editor/locales/ja/editor.json b/red/api/editor/locales/ja/editor.json index 68e4fc517..999dc3422 100644 --- a/red/api/editor/locales/ja/editor.json +++ b/red/api/editor/locales/ja/editor.json @@ -424,7 +424,30 @@ "dependencies": "依存関係", "settings": "設定", "editDescription": "プロジェクトの詳細を編集", - "editDependencies": "プロジェクトの依存関係を編集" + "editDependencies": "プロジェクトの依存関係を編集", + "userSettings": { + "committerDetail": "コミッター詳細", + "committer-tip": "システムのデフォルトを使用する場合、空白のままにしてください", + "userName": "ユーザ名", + "email": "メールアドレス", + "sshKeys": "SSH キー", + "sshKeys-tip": "gitリポジトリへのセキュアな接続を作成できます。", + "add": "キーを追加", + "add-sshKey": "SSHキーを追加", + "add-sshKey-tip": "新しい公開鍵/秘密鍵ペアを生成します", + "name": "名前", + "nameRule": "A-Z 0-9 _ - のみを含む", + "passphrase": "パスフレーズ", + "passphrase-short": "パスフレーズが短すぎます", + "optional": "任意", + "cancel": "中止", + "generate": "キーを生成", + "no-sshKeys": "SSHキーがありません", + "copy-publicKey": "公開鍵をクリップボードにコピー", + "delete": "キーを削除", + "git-config": "Git設定", + "deleteConfirm": "SSHキー __name__ を削除してもよいですか? 削除したSSHキーを元に戻すことはできません。" + } } }, "typedInput": { From c157960846578a50998b2e18bad8da023b5cf471 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 24 May 2018 16:47:51 +0100 Subject: [PATCH 17/34] Change debug sidebar icon --- nodes/core/core/58-debug.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nodes/core/core/58-debug.html b/nodes/core/core/58-debug.html index 95b55d7f5..dc011ebda 100644 --- a/nodes/core/core/58-debug.html +++ b/nodes/core/core/58-debug.html @@ -156,7 +156,7 @@ toolbar: uiComponents.footer, enableOnEdit: true, pinned: true, - iconClass: "fa fa-wrench" + iconClass: "fa fa-list-alt" }); RED.actions.add("core:show-debug-tab",function() { RED.sidebar.show('debug'); }); From 392ed706fdd4a33333ec35a5a7b7ae49b0130c98 Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Sat, 26 May 2018 14:21:30 +0900 Subject: [PATCH 18/34] add i18n support for projects interface and Japanese message catalogue --- editor/js/ui/projects/projects.js | 319 +++++++++++------------ red/api/editor/locales/en-US/editor.json | 168 +++++++++++- red/api/editor/locales/ja/editor.json | 168 +++++++++++- 3 files changed, 492 insertions(+), 163 deletions(-) diff --git a/editor/js/ui/projects/projects.js b/editor/js/ui/projects/projects.js index d7c2787d8..71a19d661 100644 --- a/editor/js/ui/projects/projects.js +++ b/editor/js/ui/projects/projects.js @@ -22,18 +22,18 @@ RED.projects = (function() { function reportUnexpectedError(error) { var notification; if (error.error === 'git_missing_user') { - notification = RED.notify("

You Git client is not configured with a username/email.

",{ + notification = RED.notify("

"+RED._("projects.errors.no-username-email")+"

",{ fixed: true, type:'error', buttons: [ { - text: "Cancel", + text: RED._("common.label.cancel"), click: function() { notification.close(); } }, { - text: "Configure Git client", + text: RED._("projects.config-git"), click: function() { RED.userSettings.show('gitconfig'); notification.close(); @@ -43,13 +43,13 @@ RED.projects = (function() { }) } else { console.log(error); - notification = RED.notify("

An unexpected error occurred:

"+error.message+"

code: "+error.error+"",{ + notification = RED.notify("

"+RED._("projects.errors.unexpected")+":

"+error.message+"

"+RED._("projects.errors.code")+": "+error.error+"",{ fixed: true, modal: true, type: 'error', buttons: [ { - text: "Close", + text: RED._("common.label.close"), click: function() { notification.close(); } @@ -75,14 +75,14 @@ RED.projects = (function() { migrateProjectHeader.appendTo(container); var body = $('
').appendTo(container); - $('

').text("Hello! We have introduced 'projects' to Node-RED.").appendTo(body); - $('

').text("This is a new way for you to manage your flow files and includes version control of your flows.").appendTo(body); - $('

').text("To get started you can create your first project or clone an existing project from a git repository.").appendTo(body); - $('

').text("If you are not sure, you can skip this for now. You will still be able to create your first project from the 'Projects' menu at any time.").appendTo(body); + $('

').text(RED._("projects.welcome.hello")).appendTo(body); + $('

').text(RED._("projects.welcome.desc0")).appendTo(body); + $('

').text(RED._("projects.welcome.desc1")).appendTo(body); + $('

').text(RED._("projects.welcome.desc2")).appendTo(body); var row = $('

').appendTo(body); - var createAsEmpty = $('').appendTo(row); - var createAsClone = $('').appendTo(row); + var createAsEmpty = $('').appendTo(row); + var createAsClone = $('').appendTo(row); createAsEmpty.click(function(e) { e.preventDefault(); @@ -105,7 +105,7 @@ RED.projects = (function() { buttons: [ { // id: "clipboard-dialog-cancel", - text: "Not right now", + text: RED._("projects.welcome.not-right-now"), click: function() { createProjectOptions = {}; $( this ).dialog( "close" ); @@ -139,23 +139,23 @@ RED.projects = (function() { migrateProjectHeader.appendTo(container); var body = $('
').appendTo(container); - $('

').text("Setup your version control client").appendTo(body); - $('

').text("Node-RED uses the open source tool Git for version control. It tracks changes to your project files and lets you push them to remote repositories.").appendTo(body); - $('

').text("When you commit a set of changes, Git records who made the changes with a username and email address. The Username can be anything you want - it does not need to be your real name.").appendTo(body); + $('

').text(RED._("projects.git-config.setup")).appendTo(body); + $('

').text(RED._("projects.git-config.desc0")).appendTo(body); + $('

').text(RED._("projects.git-config.desc1")).appendTo(body); if (isGlobalConfig) { - $('

').text("Your Git client is already configured with the details below.").appendTo(body); + $('

').text(RED._("projects.git-config.desc2")).appendTo(body); } - $('

').text("You can change these settings later under the 'Git config' tab of the settings dialog.").appendTo(body); + $('

').text(RED._("projects.git-config.desc3")).appendTo(body); var row = $('

').appendTo(body); - $('').appendTo(row); + $('').appendTo(row); gitUsernameInput = $('').val((existingGitSettings&&existingGitSettings.name)||"").appendTo(row); // $('
').text("This does not need to be your real name").appendTo(row); gitUsernameInput.on("change keyup paste",validateForm); row = $('
').appendTo(body); - $('').appendTo(row); + $('').appendTo(row); gitEmailInput = $('').val((existingGitSettings&&existingGitSettings.email)||"").appendTo(row); gitEmailInput.on("change keyup paste",validateForm); // $('
').text("Something something email").appendTo(row); @@ -168,14 +168,14 @@ RED.projects = (function() { buttons: [ { // id: "clipboard-dialog-cancel", - text: "Back", + text: RED._("common.label.back"), click: function() { show('welcome'); } }, { id: "projects-dialog-git-config", - text: "Next", // TODO: nls + text: RED._("common.label.next"), class: "primary", click: function() { var currentGitSettings = RED.settings.get('git') || {}; @@ -216,10 +216,10 @@ RED.projects = (function() { migrateProjectHeader.appendTo(container); var body = $('
').appendTo(container); - $('

').text("Create your project").appendTo(body); - $('

').text("A project is maintained as a Git repository. It makes it much easier to share your flows with others and to collaborate on them.").appendTo(body); - $('

').text("You can create multiple projects and quickly switch between them from the editor.").appendTo(body); - $('

').text("To begin, your project needs a name and an optional description.").appendTo(body); + $('

').text(RED._("projects.project-details.create")).appendTo(body); + $('

').text(RED._("projects.project-details.desc0")).appendTo(body); + $('

').text(RED._("projects.project-details.desc1")).appendTo(body); + $('

').text(RED._("projects.project-details.desc2")).appendTo(body); var validateForm = function() { var projectName = projectNameInput.val(); @@ -236,14 +236,14 @@ RED.projects = (function() { projectNameValid = false; valid = false; if (projectList[projectName]) { - projectNameSublabel.text("Project already exists"); + projectNameSublabel.text(RED._("projects.project-details.already-exists")); } else { - projectNameSublabel.text("Must contain only A-Z 0-9 _ -"); + projectNameSublabel.text(RED._("projects.project-details.must-contain")); } } else { projectNameInput.removeClass("input-error"); $('').appendTo(projectNameStatus); - projectNameSublabel.text("Must contain only A-Z 0-9 _ -"); + projectNameSublabel.text(RED._("projects.project-details.must-contain")); projectNameValid = true; } projectNameLastChecked = projectName; @@ -253,7 +253,7 @@ RED.projects = (function() { } var row = $('

').appendTo(body); - $('').appendTo(row); + $('').appendTo(row); var subrow = $('
').appendTo(row); projectNameInput = $('').val(createProjectOptions.name||"").appendTo(subrow); @@ -283,13 +283,13 @@ RED.projects = (function() { checkProjectName = null; },300) }); - projectNameSublabel = $('').appendTo(row).find("small"); + projectNameSublabel = $('').appendTo(row).find("small"); // Empty Project row = $('
').appendTo(body); - $('').appendTo(row); + $('').appendTo(row); projectSummaryInput = $('').val(createProjectOptions.summary||"").appendTo(row); - $('').appendTo(row); + $('').appendTo(row); setTimeout(function() { projectNameInput.focus(); @@ -300,7 +300,7 @@ RED.projects = (function() { buttons: function(options) { return [ { - text: "Back", + text: RED._("common.label.back"), click: function() { show('git-config'); } @@ -308,7 +308,7 @@ RED.projects = (function() { { id: "projects-dialog-create-name", disabled: true, - text: "Next", // TODO: nls + text: RED._("common.label.next"), class: "primary disabled", click: function() { createProjectOptions.name = projectNameInput.val(); @@ -344,8 +344,8 @@ RED.projects = (function() { var container = $('
'); migrateProjectHeader.appendTo(container); var body = $('
').appendTo(container); - $('

').text("Clone a project").appendTo(body); - $('

').text("If you already have a git repository containing a project, you can clone it to get started.").appendTo(body); + $('

').text(RED._("projects.clone-project.clone")).appendTo(body); + $('

').text(RED._("projects.clone-project.desc0")).appendTo(body); var projectList = null; var pendingFormValidation = false; @@ -376,14 +376,14 @@ RED.projects = (function() { projectNameValid = false; valid = false; if (projectList[projectName]) { - projectNameSublabel.text("Project already exists"); + projectNameSublabel.text(RED._("projects.clone-project.already-exists")); } else { - projectNameSublabel.text("Must contain only A-Z 0-9 _ -"); + projectNameSublabel.text(RED._("projects.clone-project.must-contain")); } } else { projectNameInput.removeClass("input-error"); $('').appendTo(projectNameStatus); - projectNameSublabel.text("Must contain only A-Z 0-9 _ -"); + projectNameSublabel.text(RED._("projects.clone-project.must-contain")); projectNameValid = true; } projectNameLastChecked = projectName; @@ -395,7 +395,7 @@ RED.projects = (function() { // var validRepo = /^(?:file|git|ssh|https?|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?[\w\.@:\/~_-]+(?:\/?|\#[\d\w\.\-_]+?)$/.test(repo); var validRepo = repo.length > 0 && !/\s/.test(repo); if (/^https?:\/\/[^/]+@/i.test(repo)) { - $("#projects-dialog-screen-create-project-repo-label small").text("Do not include the username/password in the url"); + $("#projects-dialog-screen-create-project-repo-label small").text(RED._("projects.clone-project.no-info-in-url")); validRepo = false; } if (!validRepo) { @@ -426,7 +426,7 @@ RED.projects = (function() { var row; row = $('

').appendTo(body); - $('').appendTo(row); + $('').appendTo(row); var subrow = $('
').appendTo(row); projectNameInput = $('').appendTo(subrow); @@ -456,19 +456,19 @@ RED.projects = (function() { checkProjectName = null; },300) }); - projectNameSublabel = $('').appendTo(row).find("small"); + projectNameSublabel = $('').appendTo(row).find("small"); row = $('
').appendTo(body); - $('').appendTo(row); + $('').appendTo(row); projectRepoInput = $('').appendTo(row); - $('').appendTo(row); + $('').appendTo(row); var projectRepoChanged = false; var lastProjectRepo = ""; projectRepoInput.on("change keyup paste",function() { projectRepoChanged = true; var repo = $(this).val(); if (lastProjectRepo !== repo) { - $("#projects-dialog-screen-create-project-repo-label small").text("https://, ssh:// or file://"); + $("#projects-dialog-screen-create-project-repo-label small").text(RED._("projects.clone-project.protocols")); } lastProjectRepo = repo; @@ -486,24 +486,24 @@ RED.projects = (function() { var cloneAuthRows = $('
').appendTo(body); row = $('
').hide().appendTo(cloneAuthRows); - $('
Authentication failed
').appendTo(row); + $('
'+RED._("projects.clone-project.auth-failed")+'
').appendTo(row); // Repo credentials - username/password ---------------- row = $('
').hide().appendTo(cloneAuthRows); var subrow = $('
').appendTo(row); - $('').appendTo(subrow); + $('').appendTo(subrow); projectRepoUserInput = $('').appendTo(subrow); subrow = $('
').appendTo(row); - $('').appendTo(subrow); + $('').appendTo(subrow); projectRepoPasswordInput = $('').appendTo(subrow); // ----------------------------------------------------- // Repo credentials - key/passphrase ------------------- row = $('
').hide().appendTo(cloneAuthRows); subrow = $('
').appendTo(row); - $('').appendTo(subrow); + $('').appendTo(subrow); projectRepoSSHKeySelect = $("').appendTo(subrow); subrow = $('
').appendTo(cloneAuthRows); var sshwarningRow = $('
').hide().appendTo(subrow); - $('
Before you can clone a repository over ssh you must add an SSH key to access it.
').appendTo(sshwarningRow); + $('
'+RED._("projects.clone-project.ssh-key-desc")+'
').appendTo(sshwarningRow); subrow = $('
').appendTo(sshwarningRow); - $('').appendTo(subrow).click(function(e) { + $('').appendTo(subrow).click(function(e) { e.preventDefault(); $('#projects-dialog-cancel').click(); RED.userSettings.show('gitconfig'); @@ -543,7 +543,7 @@ RED.projects = (function() { // Secret - clone row = $('
').appendTo(body); - $('').appendTo(row); + $('').appendTo(row); projectSecretInput = $('').appendTo(row); @@ -553,7 +553,7 @@ RED.projects = (function() { buttons: function(options) { return [ { - text: "Back", + text: RED._("common.label.back"), click: function() { show('git-config'); } @@ -561,7 +561,7 @@ RED.projects = (function() { { id: "projects-dialog-clone-project", disabled: true, - text: "Clone project", // TODO: nls + text: RED._("common.label.clone"), class: "primary disabled", click: function() { var projectType = $(".projects-dialog-screen-create-type.selected").data('type'); @@ -585,7 +585,7 @@ RED.projects = (function() { }; } else { - console.log("Error! Can't get selected SSH key path."); + console.log(RED._("projects.clone-project.cant-get-ssh-key")); return; } } @@ -602,7 +602,7 @@ RED.projects = (function() { } $(".projects-dialog-screen-create-row-auth-error").hide(); - $("#projects-dialog-screen-create-project-repo-label small").text("https://, ssh:// or file://"); + $("#projects-dialog-screen-create-project-repo-label small").text(RED._("projects.clone-project.protocols")); projectRepoUserInput.removeClass("input-error"); projectRepoPasswordInput.removeClass("input-error"); @@ -622,22 +622,22 @@ RED.projects = (function() { }, 400: { 'project_exists': function(error) { - console.log("already exists"); + console.log(RED._("projects.clone-project.already-exists")); }, 'git_error': function(error) { - console.log("git error",error); + console.log(RED._("projects.clone-project.git-error"),error); }, 'git_connection_failed': function(error) { projectRepoInput.addClass("input-error"); - $("#projects-dialog-screen-create-project-repo-label small").text("Connection failed"); + $("#projects-dialog-screen-create-project-repo-label small").text(RED._("projects.clone-project.connection-failed")); }, 'git_not_a_repository': function(error) { projectRepoInput.addClass("input-error"); - $("#projects-dialog-screen-create-project-repo-label small").text("Not a git repository"); + $("#projects-dialog-screen-create-project-repo-label small").text(RED._("projects.clone-project.not-git-repo")); }, 'git_repository_not_found': function(error) { projectRepoInput.addClass("input-error"); - $("#projects-dialog-screen-create-project-repo-label small").text("Repository not found"); + $("#projects-dialog-screen-create-project-repo-label small").text(RED._("projects.clone-project.repo-not-found")); }, 'git_auth_failed': function(error) { $(".projects-dialog-screen-create-row-auth-error").show(); @@ -689,11 +689,11 @@ RED.projects = (function() { migrateProjectHeader.appendTo(container); var body = $('
').appendTo(container); - $('

').text("Create your project files").appendTo(body); - $('

').text("A project contains your flow files, a README file and a package.json file.").appendTo(body); - $('

').text("It can contain any other files you want to maintain in the Git repository.").appendTo(body); + $('

').text(RED._("projects.default-files.create")).appendTo(body); + $('

').text(RED._("projects.default-files.desc0")).appendTo(body); + $('

').text(RED._("projects.default-files.desc1")).appendTo(body); if (!options.existingProject && RED.settings.files) { - $('

').text("Your existing flow and credential files will be copied into the project.").appendTo(body); + $('

').text(RED._("projects.default-files.desc2")).appendTo(body); } var validateForm = function() { @@ -724,7 +724,7 @@ RED.projects = (function() { $("#projects-dialog-create-default-files").prop('disabled',!valid).toggleClass('disabled ui-button-disabled ui-state-disabled',!valid); } var row = $('

').appendTo(body); - $('').appendTo(row); + $('').appendTo(row); var subrow = $('
').appendTo(row); var defaultFlowFile = (createProjectOptions.files &&createProjectOptions.files.flow) || (RED.settings.files && RED.settings.files.flow)||"flow.json"; projectFlowFileInput = $('').val(defaultFlowFile) @@ -735,7 +735,7 @@ RED.projects = (function() { var defaultCredentialsFile = (createProjectOptions.files &&createProjectOptions.files.credentials) || (RED.settings.files && RED.settings.files.credentials)||"flow_cred.json"; row = $('
').appendTo(body); - $('').appendTo(row); + $('').appendTo(row); subrow = $('
').appendTo(row); projectCredentialFileInput = $('
').text(defaultCredentialsFile) .appendTo(subrow); @@ -752,7 +752,7 @@ RED.projects = (function() { return [ { // id: "clipboard-dialog-cancel", - text: options.existingProject?"Cancel":"Back", + text: RED._(options.existingProject ? "common.label.cancel": "common.label.back"), click: function() { if (options.existingProject) { $(this).dialog('close'); @@ -763,7 +763,7 @@ RED.projects = (function() { }, { id: "projects-dialog-create-default-files", - text: "Next", // TODO: nls + text: RED._("common.label.next"), class: "primary", click: function() { createProjectOptions.files = { @@ -789,22 +789,22 @@ RED.projects = (function() { migrateProjectHeader.appendTo(container); var body = $('
').appendTo(container); - $('

').text("Setup encryption of your credentials file").appendTo(body); + $('

').text(RED._("projects.encryption-config.setup")).appendTo(body); if (options.existingProject) { - $('

').text("Your flow credentials file can be encrypted to keep its contents secure.").appendTo(body); - $('

').text("If you want to store these credentials in a public Git repository, you must encrypt them by providing a secret key phrase.").appendTo(body); + $('

').text(RED._("projects.encryption-config.desc0")).appendTo(body); + $('

').text(RED._("projects.encryption-config.desc1")).appendTo(body); } else { if (RED.settings.flowEncryptionType === 'disabled') { - $('

').text("Your flow credentials file is not currently encrypted.").appendTo(body); - $('

').text("That means its contents, such as passwords and access tokens, can be read by anyone with access to the file.").appendTo(body); - $('

').text("If you want to store these credentials in a public Git repository, you must encrypt them by providing a secret key phrase.").appendTo(body); + $('

').text(RED._("projects.encryption-config.desc2")).appendTo(body); + $('

').text(RED._("projects.encryption-config.desc3")).appendTo(body); + $('

').text(RED._("projects.encryption-config.desc4")).appendTo(body); } else { if (RED.settings.flowEncryptionType === 'user') { - $('

').text("Your flow credentials file is currently encrypted using the credentialSecret property from your settings file as the key.").appendTo(body); + $('

').text(RED._("projects.encryption-config.desc5")).appendTo(body); } else if (RED.settings.flowEncryptionType === 'system') { - $('

').text("Your flow credentials file is currently encrypted using a system-generated key. You should provide a new secret key for this project.").appendTo(body); + $('

').text(RED._("projects.encryption-config.desc6")).appendTo(body); } - $('

').text("The key will be stored separately from your project files. You will need to provide the key to use this project in another instance of Node-RED.").appendTo(body); + $('

').text(RED._("projects.encryption-config.desc7")).appendTo(body); } } @@ -832,16 +832,16 @@ RED.projects = (function() { var row = $('

').appendTo(body); - $('').appendTo(row); + $('').appendTo(row); var credentialsBox = $('
').appendTo(row); var credentialsRightBox = $('
').appendTo(credentialsBox); var credentialsLeftBox = $('
').appendTo(credentialsBox); var credentialsEnabledBox = $('
').appendTo(credentialsLeftBox); - $('').appendTo(credentialsEnabledBox); + $('').appendTo(credentialsEnabledBox); var credentialsDisabledBox = $('
').appendTo(credentialsLeftBox); - $('').appendTo(credentialsDisabledBox); + $('').appendTo(credentialsDisabledBox); credentialsLeftBox.find("input[name=projects-encryption-type]").click(function(e) { var val = $(this).val(); @@ -876,15 +876,15 @@ RED.projects = (function() { }) row = $('
').appendTo(credentialsRightBox); - $('').appendTo(row); + $('').appendTo(row); row = $('
').appendTo(credentialsRightBox); - $('').appendTo(row); + $('').appendTo(row); row = $('
').appendTo(credentialsRightBox); emptyProjectCredentialInput = $('').appendTo(row); emptyProjectCredentialInput.on("change keyup paste", validateForm); row = $('
').hide().appendTo(credentialsRightBox); - $('
The credentials file will not be encrypted and its contents easily read
').appendTo(row); + $('
'+RED._("projects.encryption-config.desc8")+'
').appendTo(row); credentialsRightBox.find("input[name=projects-encryption-key]").click(function() { var val = $(this).val(); @@ -911,14 +911,14 @@ RED.projects = (function() { return [ { // id: "clipboard-dialog-cancel", - text: "Back", + text: RED._("common.label.back"), click: function() { show('default-files',options); } }, { id: "projects-dialog-create-encryption", - text: options.existingProject?"Create project files":"Create project", // TODO: nls + text: RED._(options.existingProject?"projects.encryption-config.create-project-files":"projects.encryption-config.create-project"), class: "primary disabled", disabled: true, click: function() { @@ -966,10 +966,10 @@ RED.projects = (function() { }, 400: { 'project_exists': function(error) { - console.log("already exists"); + console.log(RED._("projects.encryption-config.already-exists")); }, 'git_error': function(error) { - console.log("git error",error); + console.log(RED._("projects.encryption-config.git-error"),error); }, 'git_connection_failed': function(error) { projectRepoInput.addClass("input-error"); @@ -978,7 +978,7 @@ RED.projects = (function() { projectRepoUserInput.addClass("input-error"); projectRepoPasswordInput.addClass("input-error"); // getRepoAuthDetails(req); - console.log("git auth error",error); + console.log(RED._("projects.encryption-config.git-auth-error"),error); }, '*': function(error) { reportUnexpectedError(error); @@ -1004,19 +1004,16 @@ RED.projects = (function() { migrateProjectHeader.appendTo(container); var body = $('
').appendTo(container); - $('

').text("You have successfully created your first project!").appendTo(body); - $('

').text("You can now continue to use Node-RED just as you always have.").appendTo(body); - $('

').text("The 'info' tab in the sidebar shows you what your current active project is. "+ - "The button next to the name can be used to access the project settings view.").appendTo(body); - $('

').text("The 'history' tab in the sidebar can be used to view files that have changed "+ - "in your project and to commit them. It shows you a complete history of your commits and "+ - "allows you to push your changes to a remote repository.").appendTo(body); + $('

').text(RED._("projects.create-success.success")).appendTo(body); + $('

').text(RED._("projects.create-success.desc0")).appendTo(body); + $('

').text(RED._("projects.create-success.desc1")).appendTo(body); + $('

').text(RED._("projects.create-success.desc2")).appendTo(body); return container; }, buttons: [ { - text: "Done", + text: RED._("common.label.done"), click: function() { $( this ).dialog( "close" ); } @@ -1043,7 +1040,7 @@ RED.projects = (function() { var selectedProject; return { - title: "Projects", // TODO: NLS + title: RED._("projects.create.projects"), content: function(options) { var projectList = null; selectedProject = null; @@ -1077,14 +1074,14 @@ RED.projects = (function() { projectNameValid = false; valid = false; if (projectList[projectName]) { - projectNameSublabel.text("Project already exists"); + projectNameSublabel.text(RED._("projects.create.already-exists")); } else { - projectNameSublabel.text("Must contain only A-Z 0-9 _ -"); + projectNameSublabel.text(RED._("projects.create.must-contain")); } } else { projectNameInput.removeClass("input-error"); $('').appendTo(projectNameStatus); - projectNameSublabel.text("Must contain only A-Z 0-9 _ -"); + projectNameSublabel.text(RED._("projects.create.must-contain")); projectNameValid = true; } projectNameLastChecked = projectName; @@ -1102,7 +1099,7 @@ RED.projects = (function() { // var validRepo = /^(?:file|git|ssh|https?|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?[\w\.@:\/~_-]+(?:\/?|\#[\d\w\.\-_]+?)$/.test(repo); var validRepo = repo.length > 0 && !/\s/.test(repo); if (/^https?:\/\/[^/]+@/i.test(repo)) { - $("#projects-dialog-screen-create-project-repo-label small").text("Do not include the username/password in the url"); + $("#projects-dialog-screen-create-project-repo-label small").text(RED._("projects.create.no-info-in-url")); validRepo = false; } if (!validRepo) { @@ -1159,10 +1156,10 @@ RED.projects = (function() { row = $('

').appendTo(container); - var openProject = $('').appendTo(row); - var createAsEmpty = $('').appendTo(row); + var openProject = $('').appendTo(row); + var createAsEmpty = $('').appendTo(row); // var createAsCopy = $('').appendTo(row); - var createAsClone = $('').appendTo(row); + var createAsClone = $('').appendTo(row); // var createAsClone = $('').appendTo(row); row.find(".projects-dialog-screen-create-type").click(function(evt) { evt.preventDefault(); @@ -1173,9 +1170,9 @@ RED.projects = (function() { validateForm(); projectNameInput.focus(); switch ($(this).data('type')) { - case "open": $("#projects-dialog-create").text("Open project"); break; - case "empty": $("#projects-dialog-create").text("Create project"); break; - case "clone": $("#projects-dialog-create").text("Clone project"); break; + case "open": $("#projects-dialog-create").text(RED._("projects.create.open")); break; + case "empty": $("#projects-dialog-create").text(RED._("projects.create.create")); break; + case "clone": $("#projects-dialog-create").text(RED._("projects.create.clone")); break; } }) @@ -1201,7 +1198,7 @@ RED.projects = (function() { }).appendTo(row); row = $('
').appendTo(container); - $('').appendTo(row); + $('').appendTo(row); var subrow = $('
').appendTo(row); projectNameInput = $('').appendTo(subrow); @@ -1231,16 +1228,16 @@ RED.projects = (function() { checkProjectName = null; },300) }); - projectNameSublabel = $('').appendTo(row).find("small"); + projectNameSublabel = $('').appendTo(row).find("small"); // Empty Project row = $('
').appendTo(container); - $('').appendTo(row); + $('').appendTo(row); projectSummaryInput = $('').appendTo(row); - $('').appendTo(row); + $('').appendTo(row); row = $('
').appendTo(container); - $('').appendTo(row); + $('').appendTo(row); subrow = $('
').appendTo(row); projectFlowFileInput = $('').val("flow.json") .on("change keyup paste",validateForm) @@ -1249,16 +1246,16 @@ RED.projects = (function() { $('').appendTo(row); row = $('
').appendTo(container); - $('').appendTo(row); + $('').appendTo(row); var credentialsBox = $('
').appendTo(row); var credentialsRightBox = $('
').appendTo(credentialsBox); var credentialsLeftBox = $('
').appendTo(credentialsBox); var credentialsEnabledBox = $('
').appendTo(credentialsLeftBox); - $('').appendTo(credentialsEnabledBox); + $('').appendTo(credentialsEnabledBox); var credentialsDisabledBox = $('
').appendTo(credentialsLeftBox); - $('').appendTo(credentialsDisabledBox); + $('').appendTo(credentialsDisabledBox); credentialsLeftBox.find("input[name=projects-encryption-type]").click(function(e) { var val = $(this).val(); @@ -1292,15 +1289,15 @@ RED.projects = (function() { }) row = $('
').appendTo(credentialsRightBox); - $('').appendTo(row); + $('').appendTo(row); // row = $('
').appendTo(credentialsRightBox); emptyProjectCredentialInput = $('').appendTo(row); emptyProjectCredentialInput.on("change keyup paste", validateForm); - $('').appendTo(row); + $('').appendTo(row); row = $('
').hide().appendTo(credentialsRightBox); - $('
The credentials file will not be encrypted and its contents easily read
').appendTo(row); + $('
'+RED._("projects.create.desc1")+'
').appendTo(row); credentialsRightBox.find("input[name=projects-encryption-key]").click(function() { var val = $(this).val(); @@ -1313,9 +1310,9 @@ RED.projects = (function() { // Clone Project row = $('
').appendTo(container); - $('').appendTo(row); + $('').appendTo(row); projectRepoInput = $('').appendTo(row); - $('').appendTo(row); + $('').appendTo(row); var projectRepoChanged = false; var lastProjectRepo = ""; @@ -1323,7 +1320,7 @@ RED.projects = (function() { projectRepoChanged = true; var repo = $(this).val(); if (lastProjectRepo !== repo) { - $("#projects-dialog-screen-create-project-repo-label small").text("https://, ssh:// or file://"); + $("#projects-dialog-screen-create-project-repo-label small").text(RED._("projects.create.protocols")); } lastProjectRepo = repo; @@ -1342,24 +1339,24 @@ RED.projects = (function() { var cloneAuthRows = $('
').hide().appendTo(container); row = $('
').hide().appendTo(cloneAuthRows); - $('
Authentication failed
').appendTo(row); + $('
'+RED._("projects.create.auth-failed")+'
').appendTo(row); // Repo credentials - username/password ---------------- row = $('
').hide().appendTo(cloneAuthRows); var subrow = $('
').appendTo(row); - $('').appendTo(subrow); + $('').appendTo(subrow); projectRepoUserInput = $('').appendTo(subrow); subrow = $('
').appendTo(row); - $('').appendTo(subrow); + $('').appendTo(subrow); projectRepoPasswordInput = $('').appendTo(subrow); // ----------------------------------------------------- // Repo credentials - key/passphrase ------------------- row = $('
').hide().appendTo(cloneAuthRows); subrow = $('
').appendTo(row); - $('').appendTo(subrow); + $('').appendTo(subrow); projectRepoSSHKeySelect = $("').appendTo(subrow); subrow = $('
').appendTo(cloneAuthRows); var sshwarningRow = $('
').hide().appendTo(subrow); - $('
Before you can clone a repository over ssh you must add an SSH key to access it.
').appendTo(sshwarningRow); + $('
'+RED._("projects.create.desc2")+'
').appendTo(sshwarningRow); subrow = $('
').appendTo(sshwarningRow); - $('').appendTo(subrow).click(function(e) { + $('').appendTo(subrow).click(function(e) { e.preventDefault(); $('#projects-dialog-cancel').click(); RED.userSettings.show('gitconfig'); @@ -1399,7 +1396,7 @@ RED.projects = (function() { // Secret - clone row = $('
').appendTo(container); - $('').appendTo(row); + $('').appendTo(row); projectSecretInput = $('').appendTo(row); @@ -1421,9 +1418,9 @@ RED.projects = (function() { buttons: function(options) { var initialLabel; switch (options.screen||"empty") { - case "open": initialLabel = "Open project"; break; - case "empty": initialLabel = "Create project"; break; - case "clone": initialLabel = "Clone project"; break; + case "open": initialLabel = RED._("projects.create.open"); break; + case "empty": initialLabel = RED._("projects.create.create"); break; + case "clone": initialLabel = RED._("projects.create.clone"); break; } return [ { @@ -1477,7 +1474,7 @@ RED.projects = (function() { }; } else { - console.log("Error! Can't get selected SSH key path."); + console.log(RED._("projects.create.cant-get-ssh-key-path")); return; } } @@ -1497,14 +1494,14 @@ RED.projects = (function() { dialog.dialog( "close" ); if (err) { if (err.error !== 'credentials_load_failed') { - console.log("unexpected_error",err) + console.log(RED._("projects.create.unexpected_error"),err) } } }) } $(".projects-dialog-screen-create-row-auth-error").hide(); - $("#projects-dialog-screen-create-project-repo-label small").text("https://, ssh:// or file://"); + $("#projects-dialog-screen-create-project-repo-label small").text(RED._("projects.create.protocols")); projectRepoUserInput.removeClass("input-error"); projectRepoPasswordInput.removeClass("input-error"); @@ -1524,22 +1521,22 @@ RED.projects = (function() { }, 400: { 'project_exists': function(error) { - console.log("already exists"); + console.log(RED._("projects.create.already-exists-2")); }, 'git_error': function(error) { - console.log("git error",error); + console.log(RED._("projects.create.git-error"),error); }, 'git_connection_failed': function(error) { projectRepoInput.addClass("input-error"); - $("#projects-dialog-screen-create-project-repo-label small").text("Connection failed"); + $("#projects-dialog-screen-create-project-repo-label small").text(RED._("projects.create.con-failed")); }, 'git_not_a_repository': function(error) { projectRepoInput.addClass("input-error"); - $("#projects-dialog-screen-create-project-repo-label small").text("Not a git repository"); + $("#projects-dialog-screen-create-project-repo-label small").text(RED._("projects.create.not-git")); }, 'git_repository_not_found': function(error) { projectRepoInput.addClass("input-error"); - $("#projects-dialog-screen-create-project-repo-label small").text("Repository not found"); + $("#projects-dialog-screen-create-project-repo-label small").text(RED._("projects.create.no-resource")); }, 'git_auth_failed': function(error) { $(".projects-dialog-screen-create-row-auth-error").show(); @@ -1619,15 +1616,15 @@ RED.projects = (function() { whitespace: "nowrap", width:"1000px" }).click(function(evt) { evt.stopPropagation(); }).appendTo(row); - $('').css({"lineHeight":"40px"}).text("Are you sure you want to delete this project?").appendTo(cover); - $('') + $('').css({"lineHeight":"40px"}).text(RED._("projects.delete.confirm")).appendTo(cover); + $('') .appendTo(cover) .click(function(e) { e.stopPropagation(); cover.remove(); done(true); }); - $('') + $('') .appendTo(cover) .click(function(e) { e.stopPropagation(); @@ -1681,7 +1678,7 @@ RED.projects = (function() { var filterTerm = ""; var searchDiv = $("
",{class:"red-ui-search-container"}).appendTo(container); - var searchInput = $('').appendTo(searchDiv).searchBox({ + var searchInput = $('').appendTo(searchDiv).searchBox({ //data-i18n="[placeholder]menu.label.searchInput" delay: 200, change: function() { @@ -1790,7 +1787,7 @@ RED.projects = (function() { $('').text(entry.name).appendTo(header); if (activeProject && activeProject.name === entry.name) { header.addClass("projects-list-entry-current"); - $('current').appendTo(header); + $(''+RED._("projects.create-project-list.current")+'').appendTo(header); if (options.canSelectActive === false) { // active project cannot be selected; so skip the rest return @@ -1852,7 +1849,7 @@ RED.projects = (function() { function requireCleanWorkspace(done) { if (RED.nodes.dirty()) { - var message = '

You have undeployed changes that will be lost.

Do you want to continue?

'; + var message = RED._("projects.require-clean.confirm"); var cleanNotification = RED.notify(message,{ type:"info", fixed: true, @@ -1867,7 +1864,7 @@ RED.projects = (function() { done(true); } },{ - text: 'Continue', + text: RED._("common.label.cont"), click: function() { cleanNotification.close(); done(false); @@ -1945,14 +1942,14 @@ RED.projects = (function() { var url = activeProject.git.remotes[xhr.responseJSON.remote||options.remote||'origin'].fetch; var message = $('
'+ - '
Authentication required for repository:
'+ + '
'+RED._("projects.send-req.auth-req")+':
'+ '
'+url+'
'+ '
'); var isSSH = false; if (/^https?:\/\//.test(url)) { - $('
'+ - '
').appendTo(message); + $('
'+ + '
').appendTo(message); } else if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(url)) { isSSH = true; var row = $('
').appendTo(message); @@ -1969,7 +1966,7 @@ RED.projects = (function() { } }); row = $('
').appendTo(message); - $('').appendTo(row); + $('').appendTo(row); $('').appendTo(row); } @@ -1999,7 +1996,7 @@ RED.projects = (function() { } var done = function(err) { if (err) { - console.log("Failed to update auth"); + console.log(RED._("projects.send-req.update-failed")); console.log(err); } else { sendRequest(options,body); @@ -2039,7 +2036,7 @@ RED.projects = (function() { return; } } - console.log("Unhandled error response:"); + console.log(RED._("projects.send-req.unhandled")+":"); console.log(xhr); console.log(textStatus); console.log(err); @@ -2073,7 +2070,7 @@ RED.projects = (function() { branchFilterCreateItem.addClass("input-error"); branchFilterCreateItem.find("i").addClass("fa-warning").removeClass("fa-code-fork"); } - branchFilterCreateItem.find("span").text("Invalid branch: "+branchPrefix+branchFilterTerm); + branchFilterCreateItem.find("span").text(RED._("projects.create-branch-list.invalid")+": "+branchPrefix+branchFilterTerm); } else { if (branchFilterCreateItem.hasClass("input-error")) { branchFilterCreateItem.removeClass("input-error"); @@ -2093,14 +2090,14 @@ RED.projects = (function() { if (!entry.hasOwnProperty('commit')) { branchFilterCreateItem = container; $('').appendTo(container); - $('').text("Create branch:").appendTo(container); + $('').text(RED._("projects.create-branch-list.create")+":").appendTo(container); $('
').text(entry.name).appendTo(container); } else { $('').appendTo(container); $('').text(entry.name).appendTo(container); if (entry.current) { container.addClass("selected"); - $('').text(options.currentLabel||"current").appendTo(container); + $('').text(options.currentLabel||RED._("projects.create-branch-list.current")).appendTo(container); } } container.click(function(evt) { @@ -2240,9 +2237,9 @@ RED.projects = (function() { function createDefaultFileSet() { if (!activeProject) { - throw new Error("Cannot create default file set without an active project"); + throw new Error(RED._("projects.create-default-file-set.no-active")); } else if (!activeProject.empty) { - throw new Error("Cannot create default file set on a non-empty project"); + throw new Error(RED._("projects.create-default-file-set.no-empty")); } if (!RED.user.hasPermission("projects.write")) { RED.notify(RED._("user.errors.notAuthorized"),"error"); @@ -2269,7 +2266,7 @@ RED.projects = (function() { 200: function(data) { }, 400: { 'git_error': function(error) { - console.log("git error",error); + console.log(RED._("projects.create-default-file-set.git-error"),error); }, 'missing_flow_file': function(error) { // This is a natural next error - but let the runtime event diff --git a/red/api/editor/locales/en-US/editor.json b/red/api/editor/locales/en-US/editor.json index dfcef21a6..0b7880667 100644 --- a/red/api/editor/locales/en-US/editor.json +++ b/red/api/editor/locales/en-US/editor.json @@ -10,7 +10,11 @@ "load": "Load", "save": "Save", "import": "Import", - "export": "Export" + "export": "Export", + "back": "Back", + "next": "Next", + "clone": "Clone project", + "cont": "Continue" } }, "workspace": { @@ -509,5 +513,167 @@ "modeString": "Handle as UTF-8 String", "modeArray": "Handle as JSON array", "modeDesc":"

Buffer editor

The Buffer type is stored as a JSON array of byte values. The editor will attempt to parse the entered value as a JSON array. If it is not valid JSON, it will be treated as a UTF-8 String and converted to an array of the individual character code points.

For example, a value of Hello World will be converted to the JSON array:

[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]

" + }, + "projects": { + "config-git": "Configure Git client", + "welcome": { + "hello": "Hello! We have introduced 'projects' to Node-RED.", + "desc0": "This is a new way for you to manage your flow files and includes version control of your flows.", + "desc1": "To get started you can create your first project or clone an existing project from a git repository.", + "desc2": "If you are not sure, you can skip this for now. You will still be able to create your first project from the 'Projects' menu at any time.", + "create": "Create Project", + "clone": "Clone Repository", + "not-right-now": "Not right now" + }, + "git-config": { + "setup": "Setup your version control client", + "desc0": "Node-RED uses the open source tool Git for version control. It tracks changes to your project files and lets you push them to remote repositories.", + "desc1": "When you commit a set of changes, Git records who made the changes with a username and email address. The Username can be anything you want - it does not need to be your real name.", + "desc2": "Your Git client is already configured with the details below.", + "desc3": "You can change these settings later under the 'Git config' tab of the settings dialog.", + "username": "Username", + "email": "Email" + }, + "project-details": { + "create": "Create your project", + "desc0": "A project is maintained as a Git repository. It makes it much easier to share your flows with others and to collaborate on them.", + "desc1": "You can create multiple projects and quickly switch between them from the editor.", + "desc2": "To begin, your project needs a name and an optional description.", + "already-exists": "Project already exists", + "must-contain": "Must contain only A-Z 0-9 _ -", + "project-name": "Project name", + "desc": "Description", + "opt": "Optional" + }, + "clone-project": { + "clone": "Clone a project", + "desc0": "If you already have a git repository containing a project, you can clone it to get started.", + "already-exists": "Project already exists", + "must-contain": "Must contain only A-Z 0-9 _ -", + "project-name": "Project name", + "no-info-in-url": "Do not include the username/password in the url", + "git-url": "Git repository URL", + "protocols": "https://, ssh:// or file://", + "auth-failed": "Authentication failed", + "username": "Username", + "passwd": "Password", + "ssh-key": "SSH Key", + "passphrase": "Passphrase", + "ssh-key-desc": "Before you can clone a repository over ssh you must add an SSH key to access it.", + "ssh-key-add": "Add an ssh key", + "credential-key": "Credentials encryption key", + "cant-get-ssh-key": "Error! Can't get selected SSH key path.", + "already-exists": "already exists", + "git-error": "git error", + "connection-failed": "Connection failed", + "not-git-repo": "Not a git repository", + "repo-not-found": "Repository not found" + }, + "default-files": { + "create": "Create your project files", + "desc0": "A project contains your flow files, a README file and a package.json file.", + "desc1": "It can contain any other files you want to maintain in the Git repository.", + "desc2": "Your existing flow and credential files will be copied into the project.", + "flow-file": "Flow file", + "credentials-file": "Credentials file" + }, + "encryption-config": { + "setup": "Setup encryption of your credentials file", + "desc0": "Your flow credentials file can be encrypted to keep its contents secure.", + "desc1": "If you want to store these credentials in a public Git repository, you must encrypt them by providing a secret key phrase.", + "desc2": "Your flow credentials file is not currently encrypted.", + "desc3": "That means its contents, such as passwords and access tokens, can be read by anyone with access to the file.", + "desc4": "If you want to store these credentials in a public Git repository, you must encrypt them by providing a secret key phrase.", + "desc5": "Your flow credentials file is currently encrypted using the credentialSecret property from your settings file as the key.", + "desc6": "Your flow credentials file is currently encrypted using a system-generated key. You should provide a new secret key for this project.", + "desc7": "The key will be stored separately from your project files. You will need to provide the key to use this project in another instance of Node-RED.", + "credentials": "Credentials", + "enable": "Enable encryption", + "disable": "Disable encryption", + "disabled": "disabled", + "copy": "Copy over existing key", + "use-custom": "Use custom key", + "desc8": "The credentials file will not be encrypted and its contents easily read", + "create-project-files": "Create project files", + "create-project": "Create project", + "already-exists": "already exists", + "git-error": "git error", + "git-auth-error": "git auth error" + }, + "create-success": { + "success": "You have successfully created your first project!", + "desc0": "You can now continue to use Node-RED just as you always have.", + "desc1": "The 'info' tab in the sidebar shows you what your current active project is. The button next to the name can be used to access the project settings view.", + "desc2": "The 'history' tab in the sidebar can be used to view files that have changed in your project and to commit them. It shows you a complete history of your commits and allows you to push your changes to a remote repository." + }, + "create": { + "projects": "Projects", + "already-exists": "Project already exists", + "must-contain": "Must contain only A-Z 0-9 _ -", + "no-info-in-url": "Do not include the username/password in the url", + "open": "Open Project", + "create": "Create Project", + "clone": "Clone Repository", + "project-name": "Project name", + "desc": "Description", + "opt": "Optional", + "flow-file": "Flow file", + "credentials": "Credentials", + "enable-encryption": "Enable encryption", + "disable-encryption": "Disable encryption", + "encryption-key": "Encryption key", + "desc0": "A phrase to secure your credentials with", + "desc1": "The credentials file will not be encrypted and its contents easily read", + "git-url": "Git repository URL", + "protocols": "https://, ssh:// or file://", + "auth-failed": "Authentication failed", + "username": "Username", + "password": "Password", + "ssh-key": "SSH Key", + "passphrase": "Passphrase", + "desc2": "Before you can clone a repository over ssh you must add an SSH key to access it.", + "add-ssh-key": "Add an ssh key", + "credentials-encryption-key": "Credentials encryption key", + "already-exists-2": "already exists", + "git-error": "git error", + "con-failed": "Connection failed", + "not-git": "Not a git repository", + "no-resource": "Repository not found", + "cant-get-ssh-key-path": "Error! Can't get selected SSH key path.", + "unexpected_error": "unexpected_error" + }, + "delete": { + "confirm": "Are you sure you want to delete this project?" + }, + "create-project-list": { + "search": "search your projects", + "current": "current" + }, + "require-clean": { + "confirm": "

You have undeployed changes that will be lost.

Do you want to continue?

" + }, + "send-req": { + "auth-req": "Authentication required for repository", + "username": "Username", + "password": "Password", + "passphrase": "Passphrase", + "update-failed": "Failed to update auth", + "unhandled": "Unhandled error response" + }, + "create-branch-list": { + "invalid": "Invalid branch", + "create": "Create branch", + "current": "current" + }, + "create-default-file-set": { + "no-active": "Cannot create default file set without an active project", + "no-empty": "Cannot create default file set on a non-empty project", + "git-error": "git error" + }, + "errors" : { + "no-username-email": "Your Git client is not configured with a username/email.", + "unexpected": "An unexpected error occurred", + "code": "code" + } } } diff --git a/red/api/editor/locales/ja/editor.json b/red/api/editor/locales/ja/editor.json index 999dc3422..46f970341 100644 --- a/red/api/editor/locales/ja/editor.json +++ b/red/api/editor/locales/ja/editor.json @@ -10,7 +10,11 @@ "load": "読み込み", "save": "保存", "import": "読み込み", - "export": "書き出し" + "export": "書き出し", + "back": "戻る", + "next": "進む", + "clone": "プロジェクトをクローン", + "cont": "続ける" } }, "workspace": { @@ -499,5 +503,167 @@ "modeString": "UTF-8文字列として処理", "modeArray": "JSON配列として処理", "modeDesc": "

バッファエディタ

バッファ型は、バイト値から成るJSON配列として格納されます。このエディタは、入力値をJSON配列として構文解析します。もし不正なJSON配列の場合、UTF-8文字列として扱い、各文字コード番号から成る配列へ変換します。

例えば、 Hello World という値を、以下のJSON配列へ変換します。

[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]

" + }, + "projects": { + "config-git": "Gitクライアントの設定", + "welcome": { + "hello": "こんにちは! Node-REDで「プロジェクト」機能が利用できるようになりました。", + "desc0": "フローファイルの管理方法が刷新され、バージョン管理も可能です。", + "desc1": "まず最初にプロジェクトを作成するか、既存のGitリポジトリからプロジェクトをクローンしてください。", + "desc2": "とりあえずこの処理をスキップしてもかまいません。「プロジェクト」メニューから、いつでもプロジェクトの作成を開始できます。", + "create": "プロジェクトの作成", + "clone": "プロジェクトのクローン", + "not-right-now": "後にする" + }, + "git-config": { + "setup": "バージョン管理クライアントの設定", + "desc0": "Node-REDはオープンソースツールのGitを使ってバージョン管理を行います。Gitによりプロジェクトファイルに対する変化を記録し、外部リポジトリに保存することができます。", + "desc1": "変更をコミットする際、変更を行った人物の情報としてユーザ名とEmailアドレスをGitが記憶します。ユーザ名は本名でなくても構いません。好きな名前を使ってください。", + "desc2": "Gitクライアントの現在の設定は以下の通りです。", + "desc3": "設定ダイアログの「Git設定」タブから別途変更することもできます。", + "username": "ユーザ名", + "email": "Email" + }, + "project-details": { + "create": "プロジェクトの作成", + "desc0": "プロジェクトはGitリポジトリとして管理します。Gitリポジトリを使ってフローの共有やコラボレーションが簡単にできます。", + "desc1": "複数のプロジェクトを作成し、エディタから即座に変更できます。", + "desc2": "まず、プロジェクト名と説明(任意)を指定してください。", + "already-exists": "既に存在するプロジェクトです", + "must-contain": "A-Z 0-9 _ - のみ指定可能", + "project-name": "プロジェクト名", + "desc": "説明", + "opt": "任意" + }, + "clone-project": { + "clone": "プロジェクトをクローン", + "desc0": "プロジェクトを含んだGitリポジトリを作成済みの場合、クローンを作成することができます。", + "already-exists": "既に存在するプロジェクトです", + "must-contain": "A-Z 0-9 _ - のみ指定可能", + "project-name": "プロジェクト名", + "no-info-in-url": "URLにユーザ名/パスワードを含めないようにしてください", + "git-url": "GitリポジトリのURL", + "protocols": "https://, ssh:// もしくは file://", + "auth-failed": "認証に失敗しました", + "username": "ユーザ名", + "passwd": "パスワード", + "ssh-key": "SSHキー", + "passphrase": "パスフレーズ", + "ssh-key-desc": "SSHでリポジトリをクローンする前にSSHキーを追加してください。", + "ssh-key-add": "SSHキーの追加", + "credential-key": "認証情報の暗号化キー", + "cant-get-ssh-key": "エラー! 選択したSSHキーのパスを取得できません。", + "already-exists": "既に存在します", + "git-error": "Gitエラー", + "connection-failed": "接続に失敗しました", + "not-git-repo": "Gitリポジトリではありません", + "repo-not-found": "リポジトリが見つかりません" + }, + "default-files": { + "create": "プロジェクト関連ファアイルの作成", + "desc0": "プロジェクトはフローファイル、README、package.jsonを含みます。", + "desc1": "その他、Gitリポジトリで管理したいファイルを含めても構いません。", + "desc2": "既存のフローと認証情報ファイルをプロジェクトにコピーします。", + "flow-file": "フローファイル", + "credentials-file": "認証情報ファイル" + }, + "encryption-config": { + "setup": "認証情報ファイルの暗号化設定", + "desc0": "フロー認証情報ファイルを暗号化して内容の安全性を担保できます。", + "desc1": "認証情報を公開Gitリポジトリに保存する際には、秘密キーフレーズによって暗号化します。", + "desc2": "認証情報ファイルは暗号化されていません。", + "desc3": "パスワードやアクセストークンといった認証情報を他人が参照できます。", + "desc4": "認証情報を公開Gitリポジトリに保存する際には、秘密キーフレーズによって暗号化します。", + "desc5": "フロー認証情報ファイルはsettingsファイルのcredentialSecretプロパティで暗号化されています。", + "desc6": "フロー認証情報ファイルはシステムが生成したキーによって暗号化されています。このプロジェクト用に新しい秘密キーを指定してください。", + "desc7": "キーはプロジェクトファイルとば別に保存されます。他のNode-REDでこのプロジェクトを利用するには、このプロジェクトのキーが必要です。", + "credentials": "認証情報", + "enable": "暗号化を有効にする", + "disable": "暗号化を無効にする", + "disabled": "無効", + "copy": "既存のキーをコピー", + "use-custom": "カスタムキーを使用", + "desc8": "認証情報ファイルが暗号化されないため、簡単に読み出すことができます。", + "create-project-files": "プロジェクト関連ファイル作成", + "create-project": "プロジェクト作成", + "already-exists": "既に存在", + "git-error": "Gitエラー", + "git-auth-error": "Git認証エラー" + }, + "create-success": { + "success": "最初のプロジェクトの作成が成功しました!", + "desc0": "以降は、これまでと同様にNode-REDを利用できます。", + "desc1": "サイドバーの「情報」タブに現在選択されたプロジェクトを表示します。プロジェクト名の隣のボタンでプロジェクト設定画面を呼び出すことができます。", + "desc2": "サイドバーの「履歴」タブで変更が加えられたプロジェクト内のファイルを確認しコミットできます。このサイドバーでは、全てのコミット履歴を確認し、変更を外部リポジトリにプッシュすることが可能です。" + }, + "create": { + "projects": "プロジェクト", + "already-exists": "プロジェクトは既に存在します", + "must-contain": "A-Z 0-9 _ - のみ指定可能", + "no-info-in-url": "URLにユーザ名/パスワードを含めないようにしてください", + "open": "プロジェクトを開く", + "create": "プロジェクトを作成", + "clone": "プロジェクトをクローン", + "project-name": "プロジェクト名", + "desc": "説明", + "opt": "任意", + "flow-file": "フローファイル", + "credentials": "認証情報", + "enable-encryption": "暗号化を有効にする", + "disable-encryption": "暗号化を無効にする", + "encryption-key": "暗号化キー", + "desc0": "認証情報をセキュアにするためのフレーズ", + "desc1": "認証情報ファイルが暗号化されないため、簡単に読み出すことができます", + "git-url": "GitリポジトリのURL", + "protocols": "https://, ssh:// もしくは file://", + "auth-failed": "認証に失敗しました", + "username": "ユーザ名", + "password": "パスワード", + "ssh-key": "SSHキー", + "passphrase": "パスフレーズ", + "desc2": "SSHでリポジトリをクローンする前にSSHキーを追加してください。", + "add-ssh-key": "SSHキーの追加", + "credentials-encryption-key": "認証情報の暗号化キー", + "already-exists-2": "既に存在します", + "git-error": "Gitエラー", + "con-failed": "接続に失敗しました", + "not-git": "Gitリポジトリではありません", + "no-resource": "リポジトリが見つかりません", + "cant-get-ssh-key-path": "エラー! 選択したSSHキーのパスを取得できません。", + "unexpected_error": "予期しないエラー" + }, + "delete": { + "confirm": "プロジェクトを削除しても良いですか?" + }, + "create-project-list": { + "search": "プロジェクトを検索", + "current": "有効" + }, + "require-clean": { + "confirm": "

デプロイされていない変更は失われます。

続けますか?

" + }, + "send-req": { + "auth-req": "リポジトリ対する認証が必要です", + "username": "ユーザ名", + "password": "パスワード", + "passphrase": "パスフレーズ", + "update-failed": "認証の更新に失敗しました", + "unhandled": "エラー応答が処理されませんでした" + }, + "create-branch-list": { + "invalid": "不正なブランチ", + "create": "ブランチの作成", + "current": "有効" + }, + "create-default-file-set": { + "no-active": "有効なプロジェクトが無い場合、デフォルトのファイル群を作成できません。", + "no-empty": "デフォルトのファイル群を空でないプロジェクトに作成することはできません。", + "git-error": "Gitエラー" + }, + "errors" : { + "no-username-email": "Gitクライアントのユーザ名/emailが設定されていません。", + "unexpected": "予期しないエラーが発生しました", + "code": "コード" + } } } From 865853da1901b307766432449443acff8cfebd57 Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Sat, 26 May 2018 20:08:39 +0900 Subject: [PATCH 19/34] add some i18n support for main editor interface and Japanese message catalogue --- editor/js/main.js | 40 ++++++++++++------------ red/api/editor/locales/en-US/editor.json | 24 +++++++++++++- red/api/editor/locales/ja/editor.json | 27 ++++++++++++++-- 3 files changed, 68 insertions(+), 23 deletions(-) diff --git a/editor/js/main.js b/editor/js/main.js index e65b8f30a..80e0a692f 100644 --- a/editor/js/main.js +++ b/editor/js/main.js @@ -153,13 +153,13 @@ loadFlows(function() { var project = RED.projects.getActiveProject(); var message = { - "change-branch":"Change to local branch '"+project.git.branches.local+"'", - "merge-abort":"Git merge aborted", - "loaded":"Project '"+msg.project+"' loaded", - "updated":"Project '"+msg.project+"' updated", - "pull":"Project '"+msg.project+"' reloaded", - "revert": "Project '"+msg.project+"' reloaded", - "merge-complete":"Git merge completed" + "change-branch": RED._("notification.project.change-branch", {project: project.git.branches.local}), + "merge-abort": RED._("notification.project.merge-abort"), + "loaded": RED._("notification.project.loaded", {project: msg.project}), + "updated": RED._("notification.project.updated", {project: msg.project}), + "pull": RED._("notification.project.pull", {project: msg.project}), + "revert": RED._("notification.project.revert", {project: msg.project}), + "merge-complete": RED._("notification.project.merge-complete") }[msg.action]; RED.notify("

"+message+"

"); RED.sidebar.info.refresh() @@ -183,7 +183,7 @@ if (!!RED.projects.getActiveProject()) { options.buttons = [ { - text: "Manage project dependencies", + text: RED._("notification.label.manage-project-dep"), click: function() { persistentNotifications[notificationId].hideNotification(); RED.projects.settings.show('deps'); @@ -194,7 +194,7 @@ } else { options.buttons = [ { - text: "Close", + text: RED._("common.label.close"), click: function() { persistentNotifications[notificationId].hideNotification(); } @@ -207,7 +207,7 @@ if (RED.user.hasPermission("projects.write")) { options.buttons = [ { - text: "Setup credentials", + text: RED._("notification.label.setup-cred"), click: function() { persistentNotifications[notificationId].hideNotification(); RED.projects.showCredentialsPrompt(); @@ -218,7 +218,7 @@ } else { options.buttons = [ { - text: "Close", + text: RED._("common.label.close"), click: function() { persistentNotifications[notificationId].hideNotification(); } @@ -229,7 +229,7 @@ if (RED.user.hasPermission("projects.write")) { options.buttons = [ { - text: "Setup project files", + text: RED._("notification.label.setup-project"), click: function() { persistentNotifications[notificationId].hideNotification(); RED.projects.showFilesPrompt(); @@ -241,7 +241,7 @@ if (RED.user.hasPermission("projects.write")) { options.buttons = [ { - text: "Create default package file", + text: RED._("notification.label.create-default-package"), click: function() { persistentNotifications[notificationId].hideNotification(); RED.projects.createDefaultPackageFile(); @@ -253,13 +253,13 @@ if (RED.user.hasPermission("projects.write")) { options.buttons = [ { - text: "No thanks", + text: RED._("notification.label.no-thanks"), click: function() { persistentNotifications[notificationId].hideNotification(); } }, { - text: "Create default project files", + text: RED._("notification.label.create-default-project"), click: function() { persistentNotifications[notificationId].hideNotification(); RED.projects.createDefaultFileSet(); @@ -273,7 +273,7 @@ if (RED.user.hasPermission("projects.write")) { options.buttons = [ { - text: "Show merge conflicts", + text: RED._("notification.label.show-merge-conflicts"), click: function() { persistentNotifications[notificationId].hideNotification(); RED.sidebar.versionControl.showLocalChanges(); @@ -382,10 +382,10 @@ function loadEditor() { var menuOptions = []; if (RED.settings.theme("projects.enabled",false)) { - menuOptions.push({id:"menu-item-projects-menu",label:"Projects",options:[ - {id:"menu-item-projects-new",label:"New",disabled:false,onselect:"core:new-project"}, - {id:"menu-item-projects-open",label:"Open",disabled:false,onselect:"core:open-project"}, - {id:"menu-item-projects-settings",label:"Project Settings",disabled:false,onselect:"core:show-project-settings"} + menuOptions.push({id:"menu-item-projects-menu",label:RED._("menu.label.projects"),options:[ + {id:"menu-item-projects-new",label:RED._("menu.label.projects-new"),disabled:false,onselect:"core:new-project"}, + {id:"menu-item-projects-open",label:RED._("menu.label.projects-open"),disabled:false,onselect:"core:open-project"}, + {id:"menu-item-projects-settings",label:RED._("menu.label.projects-settings"),disabled:false,onselect:"core:show-project-settings"} ]}); } diff --git a/red/api/editor/locales/en-US/editor.json b/red/api/editor/locales/en-US/editor.json index dfcef21a6..ed7b7c2dc 100644 --- a/red/api/editor/locales/en-US/editor.json +++ b/red/api/editor/locales/en-US/editor.json @@ -67,7 +67,11 @@ "editPalette":"Manage palette", "other": "Other", "showTips": "Show tips", - "help": "Node-RED website" + "help": "Node-RED website", + "projects": "Projects", + "projects-new": "New", + "projects-open": "Open", + "projects-settings": "Project Settings" } }, "user": { @@ -108,6 +112,24 @@ "cannotAddCircularReference": "Cannot add subflow - circular reference detected", "unsupportedVersion": "

Using an unsupported version of Node.js

You should upgrade to the latest Node.js LTS release

", "failedToAppendNode": "

Failed to load '__module__'

__error__

" + }, + "project": { + "change-branch": "Change to local branch '__project__'", + "merge-abort": "Git merge aborted", + "loaded": "Project '__project__' loaded", + "updated": "Project '__project__' updated", + "pull": "Project '__project__' reloaded", + "revert": "Project '__project__' reloaded", + "merge-complete": "Git merge completed" + }, + "label": { + "manage-project-dep": "Manage project dependencies", + "setup-cred": "Setup credentials", + "setup-project": "Setup project files", + "create-default-package": "Create default package file", + "no-thanks": "No thanks", + "create-default-project": "Create default project files", + "show-merge-conflicts": "Show merge conflicts" } }, "clipboard": { diff --git a/red/api/editor/locales/ja/editor.json b/red/api/editor/locales/ja/editor.json index 999dc3422..ac17f9a75 100644 --- a/red/api/editor/locales/ja/editor.json +++ b/red/api/editor/locales/ja/editor.json @@ -67,7 +67,11 @@ "editPalette": "パレットの管理", "other": "その他", "showTips": "ヒントを表示", - "help": "Node-REDウェブサイト" + "help": "Node-REDウェブサイト", + "projects": "プロジェクト", + "projects-new": "新規", + "projects-open": "開く", + "projects-settings": "設定" } }, "user": { @@ -102,7 +106,26 @@ "lostConnectionTry": "すぐに接続", "cannotAddSubflowToItself": "サブフロー自身を追加できません", "cannotAddCircularReference": "循環参照を検出したため、サブフローを追加できません", - "unsupportedVersion": "サポートされていないバージョンのNode.jsを使用しています。
最新のNode.js LTSに更新してください。" + "unsupportedVersion": "サポートされていないバージョンのNode.jsを使用しています。
最新のNode.js LTSに更新してください。", + "failedToAppendNode": "

'__module__'がロードできませんでした。

__error__

" + }, + "project": { + "change-branch": "ローカルブランチ'__project__'に変更しました", + "merge-abort": "Gitマージを中止しました", + "loaded": "プロジェクト'__project__'をロードしました", + "updated": "プロジェクト'__project__'を更新しました", + "pull": "プロジェクト'__project__'を再ロードしました", + "revert": "プロジェクト'__project__'を再ロードしました", + "merge-complete": "Gitマージが完了しました" + }, + "label": { + "manage-project-dep": "プロジェクトの依存関係の管理", + "setup-cred": "認証情報の設定", + "setup-project": "プロジェクトファイルの設定", + "create-default-package": "デフォルトパッケージファイルの作成", + "no-thanks": "不要", + "create-default-project": "デフォルトプロジェクトファイルの作成", + "show-merge-conflicts": "マージ競合を表示" } }, "clipboard": { From 0ad54cc2d1270c6074643fe09933c6bfc4259642 Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Sun, 27 May 2018 01:05:50 +0900 Subject: [PATCH 20/34] allow i18n translation in runtime --- red/runtime/i18n.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/red/runtime/i18n.js b/red/runtime/i18n.js index fc3599c87..336a2c9a6 100644 --- a/red/runtime/i18n.js +++ b/red/runtime/i18n.js @@ -81,16 +81,32 @@ var MessageFileLoader = { } +function current_locale() { + var env = process.env; + for (var name of ['LC_ALL', 'LC_MESSAGES', 'LANG']) { + if (name in env) { + var val = env[name]; + return val.substring(0, 2); + } + } + return undefined; +} + function init() { return when.promise(function(resolve,reject) { i18n.backend(MessageFileLoader); - i18n.init({ + var opt = { ns: { namespaces: [], defaultNs: "runtime" }, fallbackLng: [defaultLang] - },function() { + }; + var lang = current_locale(); + if (lang) { + opt.lng = lang; + } + i18n.init(opt ,function() { resolve(); }); }); From 4565342b057e7d9d354969b8148eee245f4cfed3 Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Sun, 27 May 2018 01:38:54 +0900 Subject: [PATCH 21/34] add i18n support for project default files generation --- red/runtime/locales/en-US/runtime.json | 4 +++- red/runtime/locales/ja/runtime.json | 10 ++++++++++ .../storage/localfilesystem/projects/Project.js | 4 ++-- .../localfilesystem/projects/defaultFileSet.js | 11 ++++++----- 4 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 red/runtime/locales/ja/runtime.json diff --git a/red/runtime/locales/en-US/runtime.json b/red/runtime/locales/en-US/runtime.json index 2b04a26b7..6e9f057ae 100644 --- a/red/runtime/locales/en-US/runtime.json +++ b/red/runtime/locales/en-US/runtime.json @@ -150,7 +150,9 @@ "disabled": "Projects disabled : editorTheme.projects.enabled=false", "disabledNoFlag": "Projects disabled : set editorTheme.projects.enabled=true to enable", "git-not-found": "Projects disabled : git command not found", - "git-version-old": "Projects disabled : git __version__ not supported. Requires 2.x" + "git-version-old": "Projects disabled : git __version__ not supported. Requires 2.x", + "summary": "A Node-RED Project", + "readme": "### About\n\nThis is your project's README.md file. It helps users understand what your\nproject does, how to use it and anything else they may need to know." } } } diff --git a/red/runtime/locales/ja/runtime.json b/red/runtime/locales/ja/runtime.json new file mode 100644 index 000000000..87e3fe0cb --- /dev/null +++ b/red/runtime/locales/ja/runtime.json @@ -0,0 +1,10 @@ +{ + "storage": { + "localfilesystem": { + "projects": { + "summary": "Node-REDプロジェクト", + "readme": "### 説明\nこれはプロジェクトのREADME.mdファイルです。このファイルには、\nプロジェクトの説明、利用方法、その他の情報を記載します。" + } + } + } +} diff --git a/red/runtime/storage/localfilesystem/projects/Project.js b/red/runtime/storage/localfilesystem/projects/Project.js index 8ba85f768..989774d6c 100644 --- a/red/runtime/storage/localfilesystem/projects/Project.js +++ b/red/runtime/storage/localfilesystem/projects/Project.js @@ -162,7 +162,7 @@ Project.prototype.initialise = function(user,data) { if (defaultFileSet.hasOwnProperty(file)) { var path = fspath.join(project.path,file); if (!fs.existsSync(path)) { - promises.push(util.writeFile(path,defaultFileSet[file](project))); + promises.push(util.writeFile(path,defaultFileSet[file](project, runtime))); } } @@ -850,7 +850,7 @@ function createDefaultProject(user, project) { } for (var file in defaultFileSet) { if (defaultFileSet.hasOwnProperty(file)) { - promises.push(util.writeFile(fspath.join(projectPath,file),defaultFileSet[file](project))); + promises.push(util.writeFile(fspath.join(projectPath,file),defaultFileSet[file](project, runtime))); } } diff --git a/red/runtime/storage/localfilesystem/projects/defaultFileSet.js b/red/runtime/storage/localfilesystem/projects/defaultFileSet.js index 12368ec75..a7e825207 100644 --- a/red/runtime/storage/localfilesystem/projects/defaultFileSet.js +++ b/red/runtime/storage/localfilesystem/projects/defaultFileSet.js @@ -15,10 +15,11 @@ **/ module.exports = { - "package.json": function(project) { + "package.json": function(project, runtime) { + var i18n = runtime.i18n; var package = { "name": project.name, - "description": project.summary||"A Node-RED Project", + "description": project.summary||i18n._("storage.localfilesystem.projects.summary"), "version": "0.0.1", "dependencies": {}, "node-red": { @@ -34,13 +35,13 @@ module.exports = { } return JSON.stringify(package,"",4); }, - "README.md": function(project) { + "README.md": function(project, runtime) { + var i18n = runtime.i18n; var content = project.name+"\n"+("=".repeat(project.name.length))+"\n\n"; if (project.summary) { content += project.summary+"\n\n"; } - content += "### About\n\nThis is your project's README.md file. It helps users understand what your\nproject does, how to use it and anything else they may need to know."; - + content += i18n._("storage.localfilesystem.projects.readme"); return content; }, ".gitignore": function() { return "*.backup" ;} From a84b2ab5bbbfba50a92e50ef5af0fbfdc6f42e62 Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Sun, 27 May 2018 22:30:05 +0900 Subject: [PATCH 22/34] update defaultFileSet test for i18n support --- .../localfilesystem/projects/defaultFileSet_spec.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/test/red/runtime/storage/localfilesystem/projects/defaultFileSet_spec.js b/test/red/runtime/storage/localfilesystem/projects/defaultFileSet_spec.js index 130e5c15b..71ec8adc1 100644 --- a/test/red/runtime/storage/localfilesystem/projects/defaultFileSet_spec.js +++ b/test/red/runtime/storage/localfilesystem/projects/defaultFileSet_spec.js @@ -19,6 +19,13 @@ var should = require("should"); var defaultFileSet = require("../../../../../../red/runtime/storage/localfilesystem/projects/defaultFileSet"); describe('storage/localfilesystem/projects/defaultFileSet', function() { + var runtime = { + i18n: { + "_": function(name) { + return name; + } + } + }; it('generates package.json for a project', function() { var generated = defaultFileSet["package.json"]({ name: "A TEST NAME", @@ -27,7 +34,7 @@ describe('storage/localfilesystem/projects/defaultFileSet', function() { flow: "MY FLOW FILE", credentials: "MY CREDENTIALS FILE" } - }); + }, runtime); var parsed = JSON.parse(generated); parsed.should.have.property('name',"A TEST NAME"); @@ -42,7 +49,7 @@ describe('storage/localfilesystem/projects/defaultFileSet', function() { var generated = defaultFileSet["README.md"]({ name: "A TEST NAME", summary: "A TEST SUMMARY" - }); + }, runtime); generated.should.match(/A TEST NAME/); generated.should.match(/A TEST SUMMARY/); }); @@ -50,7 +57,7 @@ describe('storage/localfilesystem/projects/defaultFileSet', function() { var generated = defaultFileSet[".gitignore"]({ name: "A TEST NAME", summary: "A TEST SUMMARY" - }); + }, runtime); generated.length.should.be.greaterThan(0); }); }); From b2cca10e8ba5f42cacd41eae93fafb9bcb66a5d3 Mon Sep 17 00:00:00 2001 From: Kazuki-Nakanishi Date: Mon, 28 May 2018 17:01:53 +0900 Subject: [PATCH 23/34] Add i18n support for version control of project --- editor/js/ui/projects/tab-versionControl.js | 131 ++++++++++---------- red/api/editor/locales/en-US/editor.json | 69 ++++++++++- red/api/editor/locales/ja/editor.json | 68 ++++++++++ 3 files changed, 203 insertions(+), 65 deletions(-) diff --git a/editor/js/ui/projects/tab-versionControl.js b/editor/js/ui/projects/tab-versionControl.js index 4560a56e5..c5db3f251 100644 --- a/editor/js/ui/projects/tab-versionControl.js +++ b/editor/js/ui/projects/tab-versionControl.js @@ -52,11 +52,11 @@ RED.sidebar.versionControl = (function() { 200: function(data) { var title; if (state === 'unstaged') { - title = 'Unstaged changes : '+entry.file + title = RED._("sidebar.project.versionControl.unstagedChanges")+' : '+entry.file } else if (state === 'staged') { - title = 'Staged changes : '+entry.file + title = RED._("sidebar.project.versionControl.stagedChanges")+' : '+entry.file } else { - title = 'Resolve conflicts : '+entry.file + title = RED._("sidebar.project.versionControl.resolveConflicts")+' : '+entry.file } var options = { diff: data.diff, @@ -65,18 +65,18 @@ RED.sidebar.versionControl = (function() { project: activeProject } if (state == 'unstaged') { - options.oldRevTitle = entry.indexStatus === " "?"HEAD":"Staged"; - options.newRevTitle = "Unstaged"; + options.oldRevTitle = entry.indexStatus === " "?RED._("sidebar.project.versionControl.head"):RED._("sidebar.project.versionControl.staged"); + options.newRevTitle = RED._("sidebar.project.versionControl.unstaged"); options.oldRev = entry.indexStatus === " "?"@":":0"; options.newRev = "_"; } else if (state === 'staged') { - options.oldRevTitle = "HEAD"; - options.newRevTitle = "Staged"; + options.oldRevTitle = RED._("sidebar.project.versionControl.head"); + options.newRevTitle = RED._("sidebar.project.versionControl.staged"); options.oldRev = "@"; options.newRev = ":0"; } else { - options.oldRevTitle = "Local"; - options.newRevTitle = "Remote"; + options.oldRevTitle = RED._("sidebar.project.versionControl.local"); + options.newRevTitle = RED._("sidebar.project.versionControl.remote"); options.commonRev = ":1"; options.oldRev = ":2"; options.newRev = ":3"; @@ -156,7 +156,7 @@ RED.sidebar.versionControl = (function() { evt.preventDefault(); var spinner = utils.addSpinnerOverlay(container).addClass('projects-dialog-spinner-contain'); - var notification = RED.notify("Are you sure you want to revert the changes to '"+entry.file+"'? This cannot be undone.", { + var notification = RED.notify(RED._("sidebar.project.versionControl.revert",{file:entry.file}), { type: "warning", modal: true, fixed: true, @@ -168,7 +168,7 @@ RED.sidebar.versionControl = (function() { notification.close(); } },{ - text: 'Revert changes', + text: RED._("sidebar.project.versionControl.revertChanges"), click: function() { notification.close(); var activeProject = RED.projects.getActiveProject(); @@ -281,6 +281,8 @@ RED.sidebar.versionControl = (function() { entry["update"+((state==='unstaged')?"Unstaged":"Staged")](entry, status); } var utils; + var emptyStagedItem; + var emptyMergedItem; function init(_utils) { utils = _utils; @@ -312,7 +314,7 @@ RED.sidebar.versionControl = (function() { }); localChanges = sections.add({ - title: "Local Changes", + title: RED._("sidebar.project.versionControl.localChanges"), collapsible: true }); localChanges.expand(); @@ -326,10 +328,12 @@ RED.sidebar.versionControl = (function() { refresh(true); }) + emptyStagedItem = { label: RED._("sidebar.project.versionControl.none") }; + emptyMergedItem = { label: RED._("sidebar.project.versionControl.conflictResolve") }; var unstagedContent = $('').appendTo(localChanges.content); - var header = $('').appendTo(unstagedContent); - stageAllButton = $('') + var header = $('').appendTo(unstagedContent); + stageAllButton = $('') .appendTo(header) .click(function(evt) { evt.preventDefault(); @@ -359,9 +363,9 @@ RED.sidebar.versionControl = (function() { unmergedContent = $('').appendTo(localChanges.content); - header = $('').appendTo(unmergedContent); + header = $('').appendTo(unmergedContent); bg = $('
').appendTo(header); - var abortMergeButton = $('') + var abortMergeButton = $('') .appendTo(bg) .click(function(evt) { evt.preventDefault(); @@ -399,7 +403,7 @@ RED.sidebar.versionControl = (function() { addItem: function(row,index,entry) { if (entry === emptyMergedItem) { entry.button = { - label: 'commit', + label: RED._("sidebar.project.versionControl.commit"), click: function(evt) { evt.preventDefault(); evt.stopPropagation(); @@ -423,7 +427,7 @@ RED.sidebar.versionControl = (function() { var stagedContent = $('').appendTo(localChanges.content); - header = $('').appendTo(stagedContent); + header = $('').appendTo(stagedContent); bg = $('
').appendTo(header); var showCommitBox = function() { @@ -446,14 +450,14 @@ RED.sidebar.versionControl = (function() { abortMergeButton.attr("disabled",true); commitMessage.focus(); } - commitButton = $('') + commitButton = $('') .appendTo(bg) .click(function(evt) { evt.preventDefault(); evt.stopPropagation(); showCommitBox(); }); - unstageAllButton = $('') + unstageAllButton = $('') .appendTo(bg) .click(function(evt) { evt.preventDefault(); @@ -480,14 +484,14 @@ RED.sidebar.versionControl = (function() { commitBox = $('').hide().appendTo(localChanges.content); - var commitMessage = $('') + var commitMessage = $('') .appendTo(commitBox) .on("change keyup paste",function() { submitCommitButton.attr('disabled',$(this).val().trim()===""); }); var commitToolbar = $('