From 49f72881f481bed94f4ffbd12369136bc751ffb0 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 12 Oct 2016 22:30:32 +0100 Subject: [PATCH] Disable palette editor if npm not found --- red/api/info.js | 12 ++++- red/api/theme.js | 1 + red/runtime/index.js | 1 - red/runtime/locales/en-US/runtime.json | 4 ++ red/runtime/nodes/index.js | 1 + red/runtime/nodes/registry/index.js | 6 ++- red/runtime/nodes/registry/installer.js | 36 ++++++++++++-- red/runtime/nodes/registry/loader.js | 1 + test/red/api/index_spec.js | 4 +- test/red/api/info_spec.js | 48 +++++++++++++++---- .../red/runtime/nodes/registry/loader_spec.js | 24 +++++----- 11 files changed, 108 insertions(+), 30 deletions(-) diff --git a/red/api/info.js b/red/api/info.js index 462868735..ce7ea07ea 100644 --- a/red/api/info.js +++ b/red/api/info.js @@ -1,5 +1,5 @@ /** - * Copyright 2014, 2015 IBM Corp. + * Copyright 2014, 2016 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,10 +15,12 @@ **/ var theme = require("./theme"); var util = require('util'); +var runtime; var settings; module.exports = { - init: function(runtime) { + init: function(_runtime) { + runtime = _runtime; settings = runtime.settings; }, settings: function(req,res) { @@ -40,6 +42,12 @@ module.exports = { if (settings.flowFilePretty) { safeSettings.flowFilePretty = settings.flowFilePretty; } + if (!runtime.nodes.paletteEditorEnabled()) { + safeSettings.editorTheme = safeSettings.editorTheme || {}; + safeSettings.editorTheme.palette = safeSettings.editorTheme.palette || {}; + safeSettings.editorTheme.palette.editable = false; + } + res.json(safeSettings); } diff --git a/red/api/theme.js b/red/api/theme.js index 56c40fcf9..74c765abb 100644 --- a/red/api/theme.js +++ b/red/api/theme.js @@ -38,6 +38,7 @@ var defaultContext = { var theme = null; var themeContext = clone(defaultContext); var themeSettings = null; +var runtime = null; function serveFile(app,baseUrl,file) { try { diff --git a/red/runtime/index.js b/red/runtime/index.js index 988a3220c..f0ea0fab3 100644 --- a/red/runtime/index.js +++ b/red/runtime/index.js @@ -98,7 +98,6 @@ function start() { } log.info(log._("runtime.version",{component:"Node.js ",version:process.version})); log.info(os.type()+" "+os.release()+" "+os.arch()+" "+os.endianness()); - log.info(log._("server.loading")); return redNodes.load().then(function() { var i; diff --git a/red/runtime/locales/en-US/runtime.json b/red/runtime/locales/en-US/runtime.json index edce362c7..6f223a880 100644 --- a/red/runtime/locales/en-US/runtime.json +++ b/red/runtime/locales/en-US/runtime.json @@ -9,6 +9,10 @@ "server": { "loading": "Loading palette nodes", + "palette-editor": { + "disabled": "Palette editor disabled : user settings", + "npm-not-found": "Palette editor disabled : npm command not found" + }, "errors": "Failed to register __count__ node type", "errors_plural": "Failed to register __count__ node types", "errors-help": "Run with -v for details", diff --git a/red/runtime/nodes/index.js b/red/runtime/nodes/index.js index 55c9c8e79..609d2e345 100644 --- a/red/runtime/nodes/index.js +++ b/red/runtime/nodes/index.js @@ -110,6 +110,7 @@ module.exports = { getNode: flows.get, eachNode: flows.eachNode, + paletteEditorEnabled: registry.paletteEditorEnabled, installModule: registry.installModule, uninstallModule: uninstallModule, diff --git a/red/runtime/nodes/registry/index.js b/red/runtime/nodes/registry/index.js index 0a6b9aac8..f8bea2d0a 100644 --- a/red/runtime/nodes/registry/index.js +++ b/red/runtime/nodes/registry/index.js @@ -34,7 +34,7 @@ function init(runtime) { function load() { registry.load(); - return loader.load(); + return installer.checkPrereq().then(loader.load); } function addModule(module) { @@ -80,5 +80,7 @@ module.exports = { installModule: installer.installModule, uninstallModule: installer.uninstallModule, - cleanModuleList: registry.cleanModuleList + cleanModuleList: registry.cleanModuleList, + + paletteEditorEnabled: installer.paletteEditorEnabled }; diff --git a/red/runtime/nodes/registry/installer.js b/red/runtime/nodes/registry/installer.js index 621660b02..f4fae446e 100644 --- a/red/runtime/nodes/registry/installer.js +++ b/red/runtime/nodes/registry/installer.js @@ -25,6 +25,8 @@ var log = require("../../log"); var events = require("../../events"); var child_process = require('child_process'); +var npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm'; +var paletteEditorEnabled = false; var settings; @@ -86,7 +88,7 @@ function installModule(module) { log.info(log._("server.install.installing",{name: module})); var installDir = settings.userDir || process.env.NODE_RED_HOME || "."; - var child = child_process.execFile('npm',['install','--production',installName], + var child = child_process.execFile(npmCommand,['install','--production',installName], { cwd: installDir }, @@ -160,7 +162,7 @@ function uninstallModule(module) { var list = registry.removeModule(module); log.info(log._("server.install.uninstalling",{name:module})); - var child = child_process.execFile('npm',['remove',module], + var child = child_process.execFile(npmCommand,['remove',module], { cwd: installDir }, @@ -183,9 +185,35 @@ function uninstallModule(module) { }); } +function checkPrereq() { + if (settings.hasOwnProperty('editorTheme') && + settings.editorTheme.hasOwnProperty('palette') && + settings.editorTheme.palette.hasOwnProperty('editable') && + settings.editorTheme.palette.editable === false + ) { + log.info(log._("server.palette-editor.disabled")); + paletteEditorEnabled = false; + return when.resolve(); + } else { + return when.promise(function(resolve) { + child_process.execFile(npmCommand,['-v'],function(err) { + if (err) { + log.info(log._("server.palette-editor.npm-not-found")); + paletteEditorEnabled = false; + } else { + paletteEditorEnabled = true; + } + resolve(); + }); + }) + } +} module.exports = { init: init, - + checkPrereq: checkPrereq, installModule: installModule, - uninstallModule: uninstallModule + uninstallModule: uninstallModule, + paletteEditorEnabled: function() { + return paletteEditorEnabled + } } diff --git a/red/runtime/nodes/registry/loader.js b/red/runtime/nodes/registry/loader.js index d51b427e9..3ad53d269 100644 --- a/red/runtime/nodes/registry/loader.js +++ b/red/runtime/nodes/registry/loader.js @@ -36,6 +36,7 @@ function load(defaultNodesDir,disableNodePathScan) { // We should expose that as an option at some point, although the // performance gains are minimal. //return loadNodeFiles(registry.getModuleList()); + runtime.log.info(runtime.log._("server.loading")); var nodeFiles = localfilesystem.getNodeFiles(defaultNodesDir,disableNodePathScan); return loadNodeFiles(nodeFiles); diff --git a/test/red/api/index_spec.js b/test/red/api/index_spec.js index 9995d91c7..be6f962db 100644 --- a/test/red/api/index_spec.js +++ b/test/red/api/index_spec.js @@ -30,7 +30,9 @@ describe("api index", function() { before(function() { api.init({},{ settings:{httpNodeRoot:true, httpAdminRoot: true,disableEditor:true}, - events: {on:function(){},removeListener: function(){}} + events: {on:function(){},removeListener: function(){}}, + log: {info:function(){},_:function(){}}, + nodes: {paletteEditorEnabled: function(){return true}} }); app = api.adminApp; }); diff --git a/test/red/api/info_spec.js b/test/red/api/info_spec.js index 2d522a576..d64d6e49e 100644 --- a/test/red/api/info_spec.js +++ b/test/red/api/info_spec.js @@ -28,14 +28,6 @@ describe("info api", function() { describe("settings handler", function() { before(function() { sinon.stub(theme,"settings",function() { return { test: 456 };}); - info.init({ - settings: { - foo: 123, - httpNodeRoot: "testHttpNodeRoot", - version: "testVersion", - paletteCategories :["red","blue","green"] - } - }) app = express(); app.get("/settings",info.settings); }); @@ -45,6 +37,17 @@ describe("info api", function() { }); it('returns the filtered settings', function(done) { + info.init({ + settings: { + foo: 123, + httpNodeRoot: "testHttpNodeRoot", + version: "testVersion", + paletteCategories :["red","blue","green"] + }, + nodes: { + paletteEditorEnabled: function() { return true; } + } + }); request(app) .get("/settings") .expect(200) @@ -60,6 +63,35 @@ describe("info api", function() { done(); }); }); + it('overrides palette editable if runtime says it is disabled', function(done) { + info.init({ + settings: { + httpNodeRoot: "testHttpNodeRoot", + version: "testVersion", + paletteCategories :["red","blue","green"] + + }, + nodes: { + paletteEditorEnabled: function() { return false; } + } + }); + request(app) + .get("/settings") + .expect(200) + .end(function(err,res) { + if (err) { + return done(err); + } + res.body.should.have.property("httpNodeRoot","testHttpNodeRoot"); + res.body.should.have.property("version","testVersion"); + res.body.should.have.property("paletteCategories",["red","blue","green"]); + res.body.should.have.property("editorTheme"); + res.body.editorTheme.should.have.property("test",456); + + res.body.editorTheme.should.have.property("palette",{editable:false}); + done(); + }); + }) }); }); diff --git a/test/red/runtime/nodes/registry/loader_spec.js b/test/red/runtime/nodes/registry/loader_spec.js index de3620643..62f64903b 100644 --- a/test/red/runtime/nodes/registry/loader_spec.js +++ b/test/red/runtime/nodes/registry/loader_spec.js @@ -44,7 +44,7 @@ describe("red/nodes/registry/loader",function() { }) describe("#init",function() { it("init",function() { - loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}}}); + loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){},log:{info:function(){},_:function(){}}}}); localfilesystem.init.called.should.be.true(); }); }); @@ -53,7 +53,7 @@ describe("red/nodes/registry/loader",function() { it("load empty set without settings available", function(done) { stubs.push(sinon.stub(localfilesystem,"getNodeFiles", function(){ return {};})); stubs.push(sinon.stub(registry,"saveNodeList", function(){ return {};})); - loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},settings:{available:function(){return false;}}}); + loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},log:{info:function(){},_:function(){}},settings:{available:function(){return false;}}}); loader.load("foo",true).then(function() { localfilesystem.getNodeFiles.called.should.be.true(); localfilesystem.getNodeFiles.lastCall.args[0].should.eql('foo'); @@ -65,7 +65,7 @@ describe("red/nodes/registry/loader",function() { it("load empty set with settings available triggers registery save", function(done) { stubs.push(sinon.stub(localfilesystem,"getNodeFiles", function(){ return {};})); stubs.push(sinon.stub(registry,"saveNodeList", function(){ return {};})); - loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},settings:{available:function(){return true;}}}); + loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); loader.load("foo",true).then(function() { registry.saveNodeList.called.should.be.true(); done(); @@ -96,7 +96,7 @@ describe("red/nodes/registry/loader",function() { stubs.push(sinon.stub(registry,"getNodeInfo", function(){ return null; })); stubs.push(sinon.stub(nodes,"registerType")); - loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},settings:{available:function(){return true;}}}); + loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); loader.load().then(function(result) { registry.addNodeSet.called.should.be.true(); registry.addNodeSet.lastCall.args[0].should.eql("node-red/TestNode1"); @@ -144,7 +144,7 @@ describe("red/nodes/registry/loader",function() { // This module isn't already loaded stubs.push(sinon.stub(registry,"getNodeInfo", function(){ return null; })); stubs.push(sinon.stub(nodes,"registerType")); - loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},settings:{available:function(){return true;}}}); + loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); loader.load().then(function(result) { registry.addNodeSet.called.should.be.true(); registry.addNodeSet.lastCall.args[0].should.eql("node-red/MultipleNodes1"); @@ -197,7 +197,7 @@ describe("red/nodes/registry/loader",function() { stubs.push(sinon.stub(registry,"getNodeInfo", function(){ return null; })); stubs.push(sinon.stub(nodes,"registerType")); - loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},settings:{available:function(){return true;}}}); + loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); loader.load().then(function(result) { registry.addNodeSet.called.should.be.true(); registry.addNodeSet.lastCall.args[0].should.eql("node-red/TestNode2"); @@ -247,7 +247,7 @@ describe("red/nodes/registry/loader",function() { stubs.push(sinon.stub(registry,"getNodeInfo", function(){ return null; })); stubs.push(sinon.stub(nodes,"registerType")); - loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},settings:{available:function(){return true;}}}); + loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); loader.load().then(function(result) { registry.addNodeSet.called.should.be.true(); registry.addNodeSet.lastCall.args[0].should.eql("node-red/TestNode3"); @@ -294,7 +294,7 @@ describe("red/nodes/registry/loader",function() { stubs.push(sinon.stub(registry,"getNodeInfo", function(){ return null; })); stubs.push(sinon.stub(nodes,"registerType")); - loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},settings:{available:function(){return true;}}}); + loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); loader.load().then(function(result) { registry.addNodeSet.called.should.be.true(); registry.addNodeSet.lastCall.args[0].should.eql("node-red/DoesNotExist"); @@ -321,7 +321,7 @@ describe("red/nodes/registry/loader",function() { describe("#addModule",function() { it("throws error if settings unavailable", function() { - loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},settings:{available:function(){return false;}}}); + loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},log:{info:function(){},_:function(){}},settings:{available:function(){return false;}}}); /*jshint immed: false */ (function(){ loader.addModule("test-module"); @@ -330,7 +330,7 @@ describe("red/nodes/registry/loader",function() { it("returns rejected error if module already loaded", function(done) { stubs.push(sinon.stub(registry,"getModuleInfo",function(){return{}})); - loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},settings:{available:function(){return true;}}}); + loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); loader.addModule("test-module").otherwise(function(err) { err.code.should.eql("module_already_loaded"); @@ -342,7 +342,7 @@ describe("red/nodes/registry/loader",function() { stubs.push(sinon.stub(localfilesystem,"getModuleFiles",function() { throw new Error("failure"); })); - loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},settings:{available:function(){return true;}}}); + loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); loader.addModule("test-module").otherwise(function(err) { err.message.should.eql("failure"); done(); @@ -374,7 +374,7 @@ describe("red/nodes/registry/loader",function() { stubs.push(sinon.stub(registry,"saveNodeList", function(){ return "a node list" })); stubs.push(sinon.stub(registry,"addNodeSet", function(){ return })); stubs.push(sinon.stub(nodes,"registerType")); - loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},settings:{available:function(){return true;}}}); + loader.init({nodes:nodes,i18n:{defaultLang:"en-US"},events:{on:function(){},removeListener:function(){}},log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); loader.addModule("TestNodeModule").then(function(result) { result.should.eql("a node list"); registry.addNodeSet.calledOnce.should.be.true();