mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Merge branch 'master' into dev
This commit is contained in:
		
							
								
								
									
										18
									
								
								.github/scripts/update-node-red-website.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								.github/scripts/update-node-red-website.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| const fs = require("fs"); | ||||
|  | ||||
| const newVersion = require("../../package.json").version; | ||||
|  | ||||
| if (process.env.GITHUB_REF !== "refs/tags/"+newVersion) { | ||||
|     console.log(`GITHUB_REF doesn't match the package.json version: ${process.env.GITHUB_REF} !== ${newVersion}`); | ||||
|     process.exit(0); | ||||
| } | ||||
|  | ||||
| if (!/^\d+\.\d+\.\d+$/.test(newVersion)) { | ||||
|     console.log(`Not updating for a non-stable release - ${newVersion}`); | ||||
|     process.exit(0); | ||||
| } | ||||
|  | ||||
| const path = __dirname+"/../../../node-red.github.io/index.html"; | ||||
| let contents = fs.readFileSync(path, "utf8"); | ||||
| contents = contents.replace(/<span class="node-red-latest-version">v\d+\.\d+\.\d+<\/span>/, `<span class="node-red-latest-version">v${newVersion}<\/span>` ); | ||||
| fs.writeFileSync(path, contents); | ||||
							
								
								
									
										22
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @@ -18,12 +18,16 @@ jobs: | ||||
|         with: | ||||
|             repository: 'node-red/node-red-docker' | ||||
|             path: 'node-red-docker' | ||||
|       - name: Check out node-red.github.io repository | ||||
|         uses: actions/checkout@v2 | ||||
|         with: | ||||
|             repository: 'node-red/node-red.github.io' | ||||
|             path: 'node-red.github.io' | ||||
|       - uses: actions/setup-node@v1 | ||||
|         with: | ||||
|             node-version: '12' | ||||
|       - run: node ./node-red/.github/scripts/update-node-red-docker.js | ||||
|         id: updateFiles | ||||
|       - name: Create Pull Request | ||||
|       - name: Create Docker Pull Request | ||||
|         uses: peter-evans/create-pull-request@v2 | ||||
|         with: | ||||
|           token: ${{ secrets.NR_REPO_TOKEN }} | ||||
| @@ -37,4 +41,18 @@ jobs: | ||||
|  | ||||
|             Once this is merged, you will need to create a new release with the tag `v${{ env.newVersion }}`. | ||||
|  | ||||
|             This PR was auto-generated by a GitHub Action. Any questions, speak to @knolleary | ||||
|       - run: node ./node-red/.github/scripts/update-node-red-website.js | ||||
|       - name: Create Website Pull Request | ||||
|         uses: peter-evans/create-pull-request@v2 | ||||
|         with: | ||||
|           token: ${{ secrets.NR_REPO_TOKEN }} | ||||
|           committer: GitHub <noreply@github.com> | ||||
|           author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> | ||||
|           path: 'node-red.github.io' | ||||
|           commit-message: 'Bump to ${{ env.newVersion }}' | ||||
|           title: '🚀 Update to Node-RED ${{ env.newVersion }} release' | ||||
|           body: | | ||||
|             Updates the Node-RED Website repo for the ${{ env.newVersion }} release. | ||||
|  | ||||
|             This PR was auto-generated by a GitHub Action. Any questions, speak to @knolleary | ||||
|   | ||||
							
								
								
									
										38
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,3 +1,41 @@ | ||||
| ### 1.1.3: Maintenance Release | ||||
|  | ||||
| Editor | ||||
|  - Fix vertical align of fa node icons Fixes #2670 | ||||
|  - Allow lasso selection to be restricted to active group | ||||
|  - Make ctrl-click on nested group more intuitive | ||||
|  - Fix copy/paste of nested groups | ||||
|  - Add Set(iterable) polyfill for IE11 | ||||
|  - Support select-all inside active group | ||||
|  - Improve performance of moving groups | ||||
|  - Add additional check for git auth failure response Fixes #2656 | ||||
|  - german translation, wording (#2660) (#2666) | ||||
|  - Remove filtering of duplicate fa icons | ||||
|  - Show node help when switching node edit dialogs Fixes #2652 | ||||
|  - Ensure group theme picks up theme defaults properly Fixes #2651 | ||||
|  | ||||
| Nodes | ||||
|  - Clarify Switch node isEmpty help | ||||
|  - HTTP In: handle application/cbor as binary | ||||
|  | ||||
| Runtime | ||||
|  - Move runtime settings back to adminApi from editorApi Fixes #2662 | ||||
|  - Update Chinese message for debug node | ||||
|  | ||||
| ### 1.1.2: Maintenance Release | ||||
|  | ||||
| Editor | ||||
|  | ||||
|  - Fix all the touch screen issues Fixes #2647 | ||||
|  - Add RED.view.redrawStatus to avoid full redraw on update | ||||
|  - Ensure node/group xrefs are consistent on import | ||||
|  - Disable keyboard handler when dialogs are open | ||||
|  - Ensure unknown nodes removed from outliner when node registers Fixes #2646 | ||||
|  | ||||
| Runtime | ||||
|  | ||||
|  - Allow Comms websocket auth to be done via token header Fixes #2642 | ||||
|  | ||||
| ### 1.1.1: Maintenance Release | ||||
|  | ||||
| Editor | ||||
|   | ||||
| @@ -21,15 +21,17 @@ var flows = require("./flows"); | ||||
| var flow = require("./flow"); | ||||
| var context = require("./context"); | ||||
| var auth = require("../auth"); | ||||
| var info = require("./settings"); | ||||
|  | ||||
| var apiUtil = require("../util"); | ||||
|  | ||||
| module.exports = { | ||||
|     init: function(runtimeAPI) { | ||||
|     init: function(settings,runtimeAPI) { | ||||
|         flows.init(runtimeAPI); | ||||
|         flow.init(runtimeAPI); | ||||
|         nodes.init(runtimeAPI); | ||||
|         context.init(runtimeAPI); | ||||
|         info.init(settings,runtimeAPI); | ||||
|  | ||||
|         var needsPermission = auth.needsPermission; | ||||
|  | ||||
| @@ -67,6 +69,8 @@ module.exports = { | ||||
|         // adminApp.delete("/context/:scope(node|flow)/:id",needsPermission("context.write"),context.delete,apiUtil.errorHandler); | ||||
|         adminApp.delete("/context/:scope(node|flow)/:id/*",needsPermission("context.write"),context.delete,apiUtil.errorHandler); | ||||
|  | ||||
|         adminApp.get("/settings",needsPermission("settings.read"),info.runtimeSettings,apiUtil.errorHandler); | ||||
|  | ||||
|         return adminApp; | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										72
									
								
								packages/node_modules/@node-red/editor-api/lib/admin/settings.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								packages/node_modules/@node-red/editor-api/lib/admin/settings.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| /** | ||||
|  * Copyright JS Foundation and other contributors, http://js.foundation | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
| var apiUtils = require("../util"); | ||||
| var runtimeAPI; | ||||
| var settings; | ||||
| var theme = require("../editor/theme"); | ||||
| var clone = require("clone"); | ||||
|  | ||||
| var i18n = require("@node-red/util").i18n | ||||
|  | ||||
| function extend(target, source) { | ||||
|     var keys = Object.keys(source); | ||||
|     var i = keys.length; | ||||
|     while(i--) { | ||||
|         var value = source[keys[i]] | ||||
|         var type = typeof value; | ||||
|         if (type === 'string' || type === 'number' || type === 'boolean' || Array.isArray(value)) { | ||||
|             target[keys[i]] = value; | ||||
|         } else if (value === null) { | ||||
|             if (target.hasOwnProperty(keys[i])) { | ||||
|                 delete target[keys[i]]; | ||||
|             } | ||||
|         } else { | ||||
|             // Object | ||||
|             if (target.hasOwnProperty(keys[i])) { | ||||
|                 target[keys[i]] = extend(target[keys[i]],value); | ||||
|             } else { | ||||
|                 target[keys[i]] = value; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return target; | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|     init: function(_settings,_runtimeAPI) { | ||||
|         runtimeAPI = _runtimeAPI; | ||||
|         settings = _settings; | ||||
|     }, | ||||
|     runtimeSettings: function(req,res) { | ||||
|         var opts = { | ||||
|             user: req.user | ||||
|         } | ||||
|         runtimeAPI.settings.getRuntimeSettings(opts).then(function(result) { | ||||
|             if (!settings.disableEditor) { | ||||
|                 result.editorTheme = result.editorTheme||{}; | ||||
|                 var themeSettings = theme.settings(); | ||||
|                 if (themeSettings) { | ||||
|                     // result.editorTheme may already exist with the palette | ||||
|                     // disabled. Need to merge that into the receive settings | ||||
|                     result.editorTheme = extend(clone(themeSettings),result.editorTheme); | ||||
|                 } | ||||
|                 result.editorTheme.languages = i18n.availableLanguages("editor"); | ||||
|             } | ||||
|             res.json(result); | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
| } | ||||
| @@ -123,38 +123,57 @@ AnonymousStrategy.prototype.authenticate = function(req) { | ||||
|     }); | ||||
| } | ||||
|  | ||||
|  | ||||
| function authenticateUserToken(req) { | ||||
|     return new Promise( (resolve,reject) => { | ||||
|         var token = null; | ||||
|         var tokenHeader = Users.tokenHeader(); | ||||
|         if (Users.tokenHeader() === null) { | ||||
|             // No custom user token provided. Fail the request | ||||
|             reject(); | ||||
|             return; | ||||
|         } else if (Users.tokenHeader() === 'authorization') { | ||||
|             if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') { | ||||
|                 token = req.headers.authorization.split(' ')[1]; | ||||
|             } | ||||
|         } else { | ||||
|             token = req.headers[Users.tokenHeader()]; | ||||
|         } | ||||
|         if (token) { | ||||
|             Users.tokens(token).then(function(user) { | ||||
|                 if (user) { | ||||
|                     resolve(user); | ||||
|                 } else { | ||||
|                     reject(); | ||||
|                 } | ||||
|             }); | ||||
|         } else { | ||||
|             reject(); | ||||
|         } | ||||
|     }); | ||||
| } | ||||
|  | ||||
|  | ||||
| function TokensStrategy() { | ||||
|   passport.Strategy.call(this); | ||||
|   this.name = 'tokens'; | ||||
| } | ||||
| util.inherits(TokensStrategy, passport.Strategy); | ||||
| TokensStrategy.prototype.authenticate = function(req) { | ||||
|     var self = this; | ||||
|     var token = null; | ||||
|     if (Users.tokenHeader() === 'authorization') {   | ||||
|         if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') { | ||||
|             token = req.headers.authorization.split(' ')[1]; | ||||
|         } | ||||
|     } else { | ||||
|         token = req.headers[Users.tokenHeader()]; | ||||
|     } | ||||
|     if (token) { | ||||
|         Users.tokens(token).then(function(admin) { | ||||
|             if (admin) { | ||||
|                 self.success(admin,{scope:admin.permissions}); | ||||
|             } else { | ||||
|                 self.fail(401); | ||||
|             } | ||||
|         }); | ||||
|     } else { | ||||
|         self.fail(401); | ||||
|     } | ||||
|     authenticateUserToken(req).then(user => { | ||||
|         this.success(user,{scope:user.permissions}); | ||||
|     }).catch(err => { | ||||
|         this.fail(401); | ||||
|     }); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| module.exports = { | ||||
|     bearerStrategy: bearerStrategy, | ||||
|     clientPasswordStrategy: clientPasswordStrategy, | ||||
|     passwordTokenExchange: passwordTokenExchange, | ||||
|     anonymousStrategy: new AnonymousStrategy(), | ||||
|     tokensStrategy: new TokensStrategy() | ||||
|     tokensStrategy: new TokensStrategy(), | ||||
|     authenticateUserToken: authenticateUserToken | ||||
| } | ||||
|   | ||||
| @@ -61,7 +61,7 @@ var api = { | ||||
|     authenticate: authenticate, | ||||
|     default: getDefaultUser, | ||||
|     tokens: getDefaultUser, | ||||
|     tokenHeader: "authorization" | ||||
|     tokenHeader: null | ||||
| } | ||||
|  | ||||
| function init(config) { | ||||
| @@ -111,6 +111,8 @@ function init(config) { | ||||
|         api.tokens = config.tokens; | ||||
|         if (config.tokenHeader && typeof config.tokenHeader === "string") { | ||||
|             api.tokenHeader = config.tokenHeader.toLowerCase(); | ||||
|         } else { | ||||
|             api.tokenHeader = "authorization"; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -21,6 +21,7 @@ var log = require("@node-red/util").log; // TODO: separate module | ||||
| var Tokens; | ||||
| var Users; | ||||
| var Permissions; | ||||
| var Strategies; | ||||
|  | ||||
| var server; | ||||
| var settings; | ||||
| @@ -44,6 +45,7 @@ function init(_server,_settings,_runtimeAPI) { | ||||
|     Tokens.onSessionExpiry(handleSessionExpiry); | ||||
|     Users = require("../auth/users"); | ||||
|     Permissions = require("../auth/permissions"); | ||||
|     Strategies = require("../auth/strategies"); | ||||
|  | ||||
| } | ||||
| function handleSessionExpiry(session) { | ||||
| @@ -63,17 +65,18 @@ function generateSession(length) { | ||||
|     return token.join(""); | ||||
| } | ||||
|  | ||||
| function CommsConnection(ws) { | ||||
| function CommsConnection(ws, user) { | ||||
|     this.session = generateSession(32); | ||||
|     this.ws = ws; | ||||
|     this.stack = []; | ||||
|     this.user = null; | ||||
|     this.user = user; | ||||
|     this.lastSentTime = 0; | ||||
|     var self = this; | ||||
|  | ||||
|     log.audit({event: "comms.open"}); | ||||
|     log.trace("comms.open "+self.session); | ||||
|     var pendingAuth = (settings.adminAuth != null); | ||||
|     var preAuthed = !!user; | ||||
|     var pendingAuth = !this.user && (settings.adminAuth != null); | ||||
|  | ||||
|     if (!pendingAuth) { | ||||
|         addActiveConnection(self); | ||||
| @@ -199,8 +202,8 @@ function start() { | ||||
|             var commsPath = settings.httpAdminRoot || "/"; | ||||
|             commsPath = (commsPath.slice(0,1) != "/" ? "/":"") + commsPath + (commsPath.slice(-1) == "/" ? "":"/") + "comms"; | ||||
|             wsServer = new ws.Server({ noServer: true }); | ||||
|             wsServer.on('connection',function(ws) { | ||||
|                 var commsConnection = new CommsConnection(ws); | ||||
|             wsServer.on('connection',function(ws, request, user) { | ||||
|                 var commsConnection = new CommsConnection(ws, user); | ||||
|             }); | ||||
|             wsServer.on('error', function(err) { | ||||
|                 log.warn(log._("comms.error-server",{message:err.toString()})); | ||||
| @@ -209,8 +212,26 @@ function start() { | ||||
|             server.on('upgrade', function upgrade(request, socket, head) { | ||||
|                 const pathname = url.parse(request.url).pathname; | ||||
|                 if (pathname === commsPath) { | ||||
|                     if (Users.tokenHeader() !== null && request.headers[Users.tokenHeader()]) { | ||||
|                         // The user has provided custom token handling. For the websocket, | ||||
|                         // the token could be provided in two ways: | ||||
|                         //  - as an http header (only possible with a reverse proxy setup) | ||||
|                         //  - passed over the connected websock in an auth packet | ||||
|                         // If the header is present, verify the token. If not, use the auth | ||||
|                         // packet over the connected socket | ||||
|                         // | ||||
|                         Strategies.authenticateUserToken(request).then(user => { | ||||
|                             wsServer.handleUpgrade(request, socket, head, function done(ws) { | ||||
|                                 wsServer.emit('connection', ws, request, user); | ||||
|                             }); | ||||
|                         }).catch(err => { | ||||
|                             log.audit({event: "comms.auth.fail"}); | ||||
|                             socket.destroy(); | ||||
|                         }) | ||||
|                         return | ||||
|                     } | ||||
|                     wsServer.handleUpgrade(request, socket, head, function done(ws) { | ||||
|                         wsServer.emit('connection', ws, request); | ||||
|                         wsServer.emit('connection', ws, request, null); | ||||
|                     }); | ||||
|                 } | ||||
|                 // Don't destroy the socket as other listeners may want to handle the | ||||
|   | ||||
| @@ -103,7 +103,7 @@ module.exports = { | ||||
|             editorApp.get('/credentials/:type/:id', needsPermission("credentials.read"),credentials.get,apiUtil.errorHandler); | ||||
|  | ||||
|             // Settings | ||||
|             editorApp.get("/settings",needsPermission("settings.read"),info.runtimeSettings,apiUtil.errorHandler); | ||||
|             //  Main /settings route is an admin route - see lib/admin/settings.js | ||||
|             // User Settings | ||||
|             editorApp.get("/settings/user",needsPermission("settings.read"),info.userSettings,apiUtil.errorHandler); | ||||
|             // User Settings | ||||
|   | ||||
| @@ -16,56 +16,12 @@ | ||||
| var apiUtils = require("../util"); | ||||
| var runtimeAPI; | ||||
| var sshkeys = require("./sshkeys"); | ||||
| var theme = require("./theme"); | ||||
| var clone = require("clone"); | ||||
|  | ||||
| var i18n = require("@node-red/util").i18n | ||||
|  | ||||
| function extend(target, source) { | ||||
|     var keys = Object.keys(source); | ||||
|     var i = keys.length; | ||||
|     while(i--) { | ||||
|         var value = source[keys[i]] | ||||
|         var type = typeof value; | ||||
|         if (type === 'string' || type === 'number' || type === 'boolean' || Array.isArray(value)) { | ||||
|             target[keys[i]] = value; | ||||
|         } else if (value === null) { | ||||
|             if (target.hasOwnProperty(keys[i])) { | ||||
|                 delete target[keys[i]]; | ||||
|             } | ||||
|         } else { | ||||
|             // Object | ||||
|             if (target.hasOwnProperty(keys[i])) { | ||||
|                 target[keys[i]] = extend(target[keys[i]],value); | ||||
|             } else { | ||||
|                 target[keys[i]] = value; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return target; | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|     init: function(_runtimeAPI) { | ||||
|         runtimeAPI = _runtimeAPI; | ||||
|         sshkeys.init(runtimeAPI); | ||||
|     }, | ||||
|     runtimeSettings: function(req,res) { | ||||
|         var opts = { | ||||
|             user: req.user | ||||
|         } | ||||
|         runtimeAPI.settings.getRuntimeSettings(opts).then(function(result) { | ||||
|             result.editorTheme = result.editorTheme||{}; | ||||
|             var themeSettings = theme.settings(); | ||||
|             if (themeSettings) { | ||||
|                 // result.editorTheme may already exist with the palette | ||||
|                 // disabled. Need to merge that into the receive settings | ||||
|                 result.editorTheme = extend(clone(themeSettings),result.editorTheme); | ||||
|             } | ||||
|             result.editorTheme.languages = i18n.availableLanguages("editor"); | ||||
|             res.json(result); | ||||
|         }); | ||||
|     }, | ||||
|     userSettings: function(req, res) { | ||||
|         var opts = { | ||||
|             user: req.user | ||||
|   | ||||
| @@ -99,7 +99,7 @@ function init(settings,_server,storage,runtimeAPI) { | ||||
|             adminApp.use(corsHandler); | ||||
|         } | ||||
|  | ||||
|         var adminApiApp = require("./admin").init(runtimeAPI); | ||||
|         var adminApiApp = require("./admin").init(settings, runtimeAPI); | ||||
|         adminApp.use(adminApiApp); | ||||
|     } else { | ||||
|         adminApp = null; | ||||
|   | ||||
| @@ -808,17 +808,7 @@ RED.nodes.fontAwesome = (function() { | ||||
|         "fa-youtube": "\uf167", | ||||
|     }; | ||||
|  | ||||
|     var iconList = []; | ||||
|     var isUsed = {}; | ||||
|     Object.keys(iconMap).forEach(function(icon) { | ||||
|         var unicode = iconMap[icon]; | ||||
|         // skip icons with a same unicode | ||||
|         if (isUsed[unicode] !== true) { | ||||
|             iconList.push(icon); | ||||
|             isUsed[unicode] = true; | ||||
|         } | ||||
|     }); | ||||
|     isUsed = undefined; | ||||
|     var iconList = Object.keys(iconMap); | ||||
|  | ||||
|     return { | ||||
|         getIconUnicode: function(name) { | ||||
|   | ||||
| @@ -998,6 +998,7 @@ RED.nodes = (function() { | ||||
|         var new_nodes = []; | ||||
|         var new_links = []; | ||||
|         var new_groups = []; | ||||
|         var new_group_set = new Set(); | ||||
|         var nid; | ||||
|         var def; | ||||
|         var configNode; | ||||
| @@ -1326,6 +1327,7 @@ RED.nodes = (function() { | ||||
|                         new_nodes.push(node); | ||||
|                     } else if (node.type === "group") { | ||||
|                         new_groups.push(node); | ||||
|                         new_group_set.add(node.id); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| @@ -1433,16 +1435,23 @@ RED.nodes = (function() { | ||||
|         var groupDepthMap = {}; | ||||
|         for (i=0;i<new_groups.length;i++) { | ||||
|             n = new_groups[i]; | ||||
|             if (n.g && node_map[n.g]) { | ||||
|                 n.g = node_map[n.g].id; | ||||
|             } else { | ||||
|  | ||||
|             if (n.g && !new_group_set.has(n.g)) { | ||||
|                 delete n.g; | ||||
|             } | ||||
|             n.nodes = n.nodes.map(function(id) { | ||||
|                 return node_map[id]; | ||||
|             }) | ||||
|             // Just in case the group references a node that doesn't exist for some reason | ||||
|             n.nodes = n.nodes.filter(function(v) { return !!v}); | ||||
|             n.nodes = n.nodes.filter(function(v) { | ||||
|                 if (v) { | ||||
|                     // Repair any nodes that have forgotten they are in this group | ||||
|                     if (v.g !== n.id) { | ||||
|                         v.g = n.id; | ||||
|                     } | ||||
|                 } | ||||
|                 return !!v | ||||
|             }); | ||||
|             if (!n.g) { | ||||
|                 groupDepthMap[n.id] = 0; | ||||
|             } | ||||
| @@ -1663,6 +1672,7 @@ RED.nodes = (function() { | ||||
|                             } | ||||
|                         } | ||||
|                         reimportList.push(convertNode(n)); | ||||
|                         RED.events.emit('nodes:remove',n); | ||||
|                     }); | ||||
|  | ||||
|                     // Remove any links between nodes that are going to be reimported. | ||||
|   | ||||
| @@ -37,5 +37,21 @@ | ||||
|             } | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|         if (new Set([0]).size === 0) { | ||||
|             // IE does not support passing an iterable to Set constructor | ||||
|             var _Set = Set; | ||||
|             /*global Set:true */ | ||||
|             Set = function Set(iterable) { | ||||
|                 var set = new _Set(); | ||||
|                 if (iterable) { | ||||
|                     iterable.forEach(set.add, set); | ||||
|                 } | ||||
|                 return set; | ||||
|             }; | ||||
|             Set.prototype = _Set.prototype; | ||||
|             Set.prototype.constructor = Set; | ||||
|         } | ||||
|  | ||||
|     } | ||||
| })(); | ||||
|   | ||||
| @@ -372,7 +372,7 @@ var RED = (function() { | ||||
|                 node.status = msg; | ||||
|                 node.dirtyStatus = true; | ||||
|                 node.dirty = true; | ||||
|                 RED.view.redraw(); | ||||
|                 RED.view.redrawStatus(node); | ||||
|             } | ||||
|         }); | ||||
|         RED.comms.subscribe("notification/node/#",function(topic,msg) { | ||||
|   | ||||
| @@ -159,7 +159,11 @@ RED.clipboard = (function() { | ||||
|                         } | ||||
|                     } | ||||
|                 ], | ||||
|                 open: function( event, ui ) { | ||||
|                     RED.keyboard.disable(); | ||||
|                 }, | ||||
|                 close: function(e) { | ||||
|                     RED.keyboard.enable(); | ||||
|                     if (popover) { | ||||
|                         popover.close(true); | ||||
|                         currentPopoverError = null; | ||||
|   | ||||
| @@ -1630,6 +1630,7 @@ RED.editor = (function() { | ||||
|             show: function() { | ||||
|                 if (editing_node) { | ||||
|                     RED.sidebar.info.refresh(editing_node); | ||||
|                     RED.sidebar.help.show(editing_node.type, false); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -1836,6 +1837,7 @@ RED.editor = (function() { | ||||
|             show: function() { | ||||
|                 if (editing_config_node) { | ||||
|                     RED.sidebar.info.refresh(editing_config_node); | ||||
|                     RED.sidebar.help.show(type, false); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -105,18 +105,18 @@ RED.group = (function() { | ||||
|                 cellHeight: 16, | ||||
|                 cellMargin: 3, | ||||
|                 none: true, | ||||
|                 opacity: style['stroke-opacity'] || 1.0 | ||||
|                 opacity: style.hasOwnProperty('stroke-opacity')?style['stroke-opacity']:(defaultGroupStyle.hasOwnProperty('stroke-opacity')?defaultGroupStyle['stroke-opacity']:1.0) | ||||
|             }).appendTo("#node-input-row-style-stroke"); | ||||
|             RED.colorPicker.create({ | ||||
|                 id:"node-input-style-fill", | ||||
|                 value: style.fill || "none", | ||||
|                 value: style.fill || defaultGroupStyle.fill ||"none", | ||||
|                 palette: colorPalette, | ||||
|                 cellPerRow: colorCount, | ||||
|                 cellWidth: 16, | ||||
|                 cellHeight: 16, | ||||
|                 cellMargin: 3, | ||||
|                 none: true, | ||||
|                 opacity: style['fill-opacity'] || 1.0 | ||||
|                 opacity: style.hasOwnProperty('fill-opacity')?style['fill-opacity']:(defaultGroupStyle.hasOwnProperty('fill-opacity')?defaultGroupStyle['fill-opacity']:1.0) | ||||
|             }).appendTo("#node-input-row-style-fill"); | ||||
|  | ||||
|             createLayoutPicker({ | ||||
| @@ -162,12 +162,6 @@ RED.group = (function() { | ||||
|                 delete this.style.color; | ||||
|             } | ||||
|  | ||||
|             if (this.style["stroke-opacity"] === "1") { | ||||
|                 delete this.style["stroke-opacity"] | ||||
|             } | ||||
|             if (this.style["fill-opacity"] === "1") { | ||||
|                 delete this.style["fill-opacity"] | ||||
|             } | ||||
|             var node = this; | ||||
|             ['stroke','fill','stroke-opacity','fill-opacity','color','label-position'].forEach(function(prop) { | ||||
|                 if (node.style[prop] === defaultGroupStyle[prop]) { | ||||
|   | ||||
| @@ -17,6 +17,8 @@ RED.keyboard = (function() { | ||||
|  | ||||
|     var isMac = /Mac/i.test(window.navigator.platform); | ||||
|  | ||||
|     var handlersActive = true; | ||||
|  | ||||
|     var handlers = {}; | ||||
|     var partialState; | ||||
|  | ||||
| @@ -225,6 +227,9 @@ RED.keyboard = (function() { | ||||
|         } | ||||
|     } | ||||
|     d3.select(window).on("keydown",function() { | ||||
|         if (!handlersActive) { | ||||
|             return; | ||||
|         } | ||||
|         if (metaKeyCodes[d3.event.keyCode]) { | ||||
|             return; | ||||
|         } | ||||
| @@ -570,6 +575,13 @@ RED.keyboard = (function() { | ||||
|         return pane; | ||||
|     } | ||||
|  | ||||
|     function enable() { | ||||
|         handlersActive = true; | ||||
|     } | ||||
|     function disable() { | ||||
|         handlersActive = false; | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         init: init, | ||||
|         add: addHandler, | ||||
| @@ -579,7 +591,9 @@ RED.keyboard = (function() { | ||||
|         }, | ||||
|         revertToDefault: revertToDefault, | ||||
|         formatKey: formatKey, | ||||
|         validateKey: validateKey | ||||
|         validateKey: validateKey, | ||||
|         disable: disable, | ||||
|         enable: enable | ||||
|     } | ||||
|  | ||||
| })(); | ||||
|   | ||||
| @@ -472,6 +472,8 @@ RED.library = (function() { | ||||
|                 autoOpen: false, | ||||
|                 width: 800, | ||||
|                 resizable: false, | ||||
|                 open: function( event, ui ) { RED.keyboard.disable() }, | ||||
|                 close: function( event, ui ) { RED.keyboard.enable() }, | ||||
|                 classes: { | ||||
|                     "ui-dialog": "red-ui-editor-dialog", | ||||
|                     "ui-dialog-titlebar-close": "hide", | ||||
| @@ -556,9 +558,11 @@ RED.library = (function() { | ||||
|                     } | ||||
|                 ], | ||||
|                 open: function(e) { | ||||
|                     RED.keyboard.disable(); | ||||
|                     $(this).parent().find(".ui-dialog-titlebar-close").hide(); | ||||
|                 }, | ||||
|                 close: function(e) { | ||||
|                     RED.keyboard.enable(); | ||||
|                     if (libraryEditor) { | ||||
|                         libraryEditor.destroy(); | ||||
|                         libraryEditor = null; | ||||
|   | ||||
| @@ -2263,6 +2263,12 @@ RED.projects = (function() { | ||||
|                 autoOpen: false, | ||||
|                 width: 600, | ||||
|                 resizable: false, | ||||
|                 open: function(e) { | ||||
|                     RED.keyboard.disable(); | ||||
|                 }, | ||||
|                 close: function(e) { | ||||
|                     RED.keyboard.enable(); | ||||
|                 }, | ||||
|                 classes: { | ||||
|                     "ui-dialog": "red-ui-editor-dialog", | ||||
|                     "ui-dialog-titlebar-close": "hide", | ||||
|   | ||||
| @@ -261,10 +261,12 @@ RED.sidebar.help = (function() { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     function show(type) { | ||||
|         RED.sidebar.show("help"); | ||||
|     function show(type, bringToFront) { | ||||
|         if (bringToFront !== false) { | ||||
|             RED.sidebar.show("help"); | ||||
|         } | ||||
|         if (type) { | ||||
|             hideTOC(); | ||||
|             // hideTOC(); | ||||
|             showHelp(type); | ||||
|         } | ||||
|         resizeStack(); | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -39,7 +39,11 @@ RED.user = (function() { | ||||
|             closeOnEscape: !!opts.cancelable, | ||||
|             width: 600, | ||||
|             resizable: false, | ||||
|             draggable: false | ||||
|             draggable: false, | ||||
|             close: function( event, ui ) { | ||||
|                 $("#node-dialog-login").dialog('destroy').remove(); | ||||
|                 RED.keyboard.enable() | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         $("#node-dialog-login-fields").empty(); | ||||
| @@ -98,10 +102,10 @@ RED.user = (function() { | ||||
|                             data: body | ||||
|                         }).done(function(data,textStatus,xhr) { | ||||
|                             RED.settings.set("auth-tokens",data); | ||||
|                             $("#node-dialog-login").dialog('destroy').remove(); | ||||
|                             if (opts.updateMenu) { | ||||
|                                 updateUserMenu(); | ||||
|                             } | ||||
|                             $("#node-dialog-login").dialog("close"); | ||||
|                             done(); | ||||
|                         }).fail(function(jqXHR,textStatus,errorThrown) { | ||||
|                             RED.settings.remove("auth-tokens"); | ||||
| @@ -143,7 +147,8 @@ RED.user = (function() { | ||||
|                 } | ||||
|                 if (opts.cancelable) { | ||||
|                     $("#node-dialog-login-cancel").button().on("click", function( event ) { | ||||
|                         $("#node-dialog-login").dialog('destroy').remove(); | ||||
|                         $("#node-dialog-login").dialog('close'); | ||||
|  | ||||
|                     }); | ||||
|                 } | ||||
|  | ||||
| @@ -152,8 +157,7 @@ RED.user = (function() { | ||||
|                 $("#node-dialog-login-image").load(function() { | ||||
|                     dialog.dialog("open"); | ||||
|                 }).attr("src",loginImageSrc); | ||||
|  | ||||
|  | ||||
|                 RED.keyboard.disable(); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|   | ||||
| @@ -597,6 +597,7 @@ div.red-ui-button-small.red-ui-color-picker-opacity-slider-handle { | ||||
|         padding: 4px; | ||||
|         color: $secondary-text-color; | ||||
|         font-size: 0.9em; | ||||
|         line-height: 24px; | ||||
|     } | ||||
|     button { | ||||
|         float: right; | ||||
|   | ||||
| @@ -204,7 +204,7 @@ | ||||
| .red-ui-palette-icon-fa { | ||||
|     color: white; | ||||
|     position: absolute; | ||||
|     top: 7px; | ||||
|     top: calc(50% - 7px); | ||||
|     left: 3px; | ||||
| } | ||||
| .red-ui-palette-node-small { | ||||
|   | ||||
| @@ -46,10 +46,10 @@ module.exports = function(RED) { | ||||
|                     isText = true; | ||||
|                 } else if (parsedType.type !== "application") { | ||||
|                     isText = false; | ||||
|                 } else if (parsedType.subtype !== "octet-stream") { | ||||
|                 } else if ((parsedType.subtype !== "octet-stream") && (parsedType.subtype !== "cbor")) { | ||||
|                     checkUTF = true; | ||||
|                 } else { | ||||
|                     // applicatino/octet-stream | ||||
|                     // application/octet-stream or application/cbor | ||||
|                     isText = false; | ||||
|                 } | ||||
|  | ||||
|   | ||||
| @@ -604,7 +604,7 @@ | ||||
|       "inputrange" : "von einem Eingabebereich", | ||||
|       "resultrange" : "in einen Zielbereich", | ||||
|       "from" : "von", | ||||
|       "to" : "bis", | ||||
|       "to" : "auf", | ||||
|       "roundresult" : "Runde das Ergebnis auf die nächste ganze Zahl?" | ||||
|     }, | ||||
|     "placeholder" : { | ||||
|   | ||||
| @@ -123,7 +123,7 @@ | ||||
|         "none": "None", | ||||
|         "invalid-exp": "无效的JSONata表达式: __error__", | ||||
|         "msgprop": "信息属性", | ||||
|         "msgobj": "完整信息", | ||||
|         "msgobj": "与调试输出相同", | ||||
|         "autostatus": "自动的", | ||||
|         "to": "目标", | ||||
|         "debtab": "调试窗口", | ||||
|   | ||||
| @@ -123,7 +123,7 @@ | ||||
|         "none": "None", | ||||
|         "invalid-exp": "無效的JSONata表達式: __error__", | ||||
|         "msgprop": "資訊屬性", | ||||
|         "msgobj": "完整資訊", | ||||
|         "msgobj": "與調試輸出相同", | ||||
|         "autostatus": "自動的", | ||||
|         "to": "目標", | ||||
|         "debtab": "除錯窗口", | ||||
|   | ||||
| @@ -81,39 +81,41 @@ var api = module.exports = { | ||||
|                     }) | ||||
|                 } | ||||
|  | ||||
|                 safeSettings.context = runtime.nodes.listContextStores(); | ||||
|                 if (!runtime.settings.disableEditor) { | ||||
|                     safeSettings.context = runtime.nodes.listContextStores(); | ||||
|  | ||||
|                 if (util.isArray(runtime.settings.paletteCategories)) { | ||||
|                     safeSettings.paletteCategories = runtime.settings.paletteCategories; | ||||
|                 } | ||||
|                     if (util.isArray(runtime.settings.paletteCategories)) { | ||||
|                         safeSettings.paletteCategories = runtime.settings.paletteCategories; | ||||
|                     } | ||||
|  | ||||
|                 if (runtime.settings.flowFilePretty) { | ||||
|                     safeSettings.flowFilePretty = runtime.settings.flowFilePretty; | ||||
|                 } | ||||
|                     if (runtime.settings.flowFilePretty) { | ||||
|                         safeSettings.flowFilePretty = runtime.settings.flowFilePretty; | ||||
|                     } | ||||
|  | ||||
|                 if (!runtime.nodes.paletteEditorEnabled()) { | ||||
|                     safeSettings.editorTheme = safeSettings.editorTheme || {}; | ||||
|                     safeSettings.editorTheme.palette = safeSettings.editorTheme.palette || {}; | ||||
|                     safeSettings.editorTheme.palette.editable = false; | ||||
|                 } | ||||
|                 if (runtime.storage.projects) { | ||||
|                     var activeProject = runtime.storage.projects.getActiveProject(); | ||||
|                     if (activeProject) { | ||||
|                         safeSettings.project = activeProject; | ||||
|                     } else if (runtime.storage.projects.flowFileExists()) { | ||||
|                         safeSettings.files = { | ||||
|                             flow: runtime.storage.projects.getFlowFilename(), | ||||
|                             credentials: runtime.storage.projects.getCredentialsFilename() | ||||
|                     if (!runtime.nodes.paletteEditorEnabled()) { | ||||
|                         safeSettings.editorTheme = safeSettings.editorTheme || {}; | ||||
|                         safeSettings.editorTheme.palette = safeSettings.editorTheme.palette || {}; | ||||
|                         safeSettings.editorTheme.palette.editable = false; | ||||
|                     } | ||||
|                     if (runtime.storage.projects) { | ||||
|                         var activeProject = runtime.storage.projects.getActiveProject(); | ||||
|                         if (activeProject) { | ||||
|                             safeSettings.project = activeProject; | ||||
|                         } else if (runtime.storage.projects.flowFileExists()) { | ||||
|                             safeSettings.files = { | ||||
|                                 flow: runtime.storage.projects.getFlowFilename(), | ||||
|                                 credentials: runtime.storage.projects.getCredentialsFilename() | ||||
|                             } | ||||
|                         } | ||||
|                         safeSettings.git = { | ||||
|                             globalUser: runtime.storage.projects.getGlobalGitUser() | ||||
|                         } | ||||
|                     } | ||||
|                     safeSettings.git = { | ||||
|                         globalUser: runtime.storage.projects.getGlobalGitUser() | ||||
|                     } | ||||
|  | ||||
|                     safeSettings.flowEncryptionType = runtime.nodes.getCredentialKeyType(); | ||||
|                     runtime.settings.exportNodeSettings(safeSettings); | ||||
|                 } | ||||
|  | ||||
|                 safeSettings.flowEncryptionType = runtime.nodes.getCredentialKeyType(); | ||||
|  | ||||
|                 runtime.settings.exportNodeSettings(safeSettings); | ||||
|  | ||||
|                 resolve(safeSettings); | ||||
|             }catch(err) { | ||||
|   | ||||
| @@ -51,6 +51,8 @@ function runGitCommand(args,cwd,env,emit) { | ||||
|             err.code = "git_auth_failed"; | ||||
|         } else if(/Permission denied \(publickey\)/i.test(stderr)) { | ||||
|             err.code = "git_auth_failed"; | ||||
|         } else if(/Authentication failed/i.test(stderr)) { | ||||
|             err.code = "git_auth_failed"; | ||||
|         } else if (/commit your changes or stash/i.test(stderr)) { | ||||
|             err.code = "git_local_overwrite"; | ||||
|         } else if (/CONFLICT/.test(err.stdout)) { | ||||
|   | ||||
| @@ -102,7 +102,7 @@ describe("api/admin/index", function() { | ||||
|         }); | ||||
|  | ||||
|         before(function() { | ||||
|             app = adminApi.init({}); | ||||
|             app = adminApi.init({},{}); | ||||
|         }); | ||||
|  | ||||
|         beforeEach(function() { | ||||
|   | ||||
							
								
								
									
										93
									
								
								test/unit/@node-red/editor-api/lib/admin/settings_spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								test/unit/@node-red/editor-api/lib/admin/settings_spec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| /** | ||||
|  * Copyright JS Foundation and other contributors, http://js.foundation | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
|  | ||||
| var should = require("should"); | ||||
| var request = require('supertest'); | ||||
| var express = require('express'); | ||||
| var bodyParser = require("body-parser"); | ||||
| var sinon = require('sinon'); | ||||
|  | ||||
| var app; | ||||
|  | ||||
| var NR_TEST_UTILS = require("nr-test-utils"); | ||||
|  | ||||
| var info = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/settings"); | ||||
| var theme = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/theme"); | ||||
|  | ||||
| describe("api/editor/settings", function() { | ||||
|     before(function() { | ||||
|         sinon.stub(theme,"settings",function() { return { existing: 123, test: 456 };}); | ||||
|         app = express(); | ||||
|         app.use(bodyParser.json()); | ||||
|         app.get("/settings",info.runtimeSettings); | ||||
|     }); | ||||
|  | ||||
|     after(function() { | ||||
|         theme.settings.restore(); | ||||
|     }); | ||||
|  | ||||
|     it('returns the runtime settings', function(done) { | ||||
|         info.init({},{ | ||||
|             settings: { | ||||
|                 getRuntimeSettings: function(opts) { | ||||
|                     return Promise.resolve({ | ||||
|                         a:1, | ||||
|                         b:2, | ||||
|                         editorTheme: { existing: 789 } | ||||
|                     }) | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|         request(app) | ||||
|         .get("/settings") | ||||
|         .expect(200) | ||||
|         .end(function(err,res) { | ||||
|             if (err) { | ||||
|                 return done(err); | ||||
|             } | ||||
|             res.body.should.have.property("a",1); | ||||
|             res.body.should.have.property("b",2); | ||||
|             res.body.should.have.property("editorTheme",{existing: 789, test:456}); | ||||
|             done(); | ||||
|         }); | ||||
|     }); | ||||
|     it('returns the runtime settings - disableEditor true', function(done) { | ||||
|         info.init({disableEditor: true},{ | ||||
|             settings: { | ||||
|                 getRuntimeSettings: function(opts) { | ||||
|                     return Promise.resolve({ | ||||
|                         a:1, | ||||
|                         b:2 | ||||
|                     }) | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|         request(app) | ||||
|         .get("/settings") | ||||
|         .expect(200) | ||||
|         .end(function(err,res) { | ||||
|             if (err) { | ||||
|                 return done(err); | ||||
|             } | ||||
|             res.body.should.have.property("a",1); | ||||
|             res.body.should.have.property("b",2); | ||||
|             // no editorTheme if disabledEditor true | ||||
|             res.body.should.not.have.property("editorTheme"); | ||||
|             done(); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
| }); | ||||
| @@ -155,6 +155,10 @@ describe("api/auth/users", function() { | ||||
|        }); | ||||
|  | ||||
|         describe('#get',function() { | ||||
|             it("returns null for tokenHeader", function() { | ||||
|                 should.not.exist(Users.tokenHeader()); | ||||
|             }); | ||||
|  | ||||
|             it('delegates get user',function(done) { | ||||
|                 Users.get('dave').then(function(user) { | ||||
|                     try { | ||||
|   | ||||
| @@ -32,7 +32,6 @@ describe("api/editor/settings", function() { | ||||
|         sinon.stub(theme,"settings",function() { return { existing: 123, test: 456 };}); | ||||
|         app = express(); | ||||
|         app.use(bodyParser.json()); | ||||
|         app.get("/settings",info.runtimeSettings); | ||||
|         app.get("/settings/user",function(req,res,next) {req.user = "fred"; next()}, info.userSettings); | ||||
|         app.post("/settings/user",function(req,res,next) {req.user = "fred"; next()},info.updateUserSettings); | ||||
|     }); | ||||
| @@ -41,31 +40,6 @@ describe("api/editor/settings", function() { | ||||
|         theme.settings.restore(); | ||||
|     }); | ||||
|  | ||||
|     it('returns the runtime settings', function(done) { | ||||
|         info.init({ | ||||
|             settings: { | ||||
|                 getRuntimeSettings: function(opts) { | ||||
|                     return Promise.resolve({ | ||||
|                         a:1, | ||||
|                         b:2, | ||||
|                         editorTheme: { existing: 789 } | ||||
|                     }) | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|         request(app) | ||||
|         .get("/settings") | ||||
|         .expect(200) | ||||
|         .end(function(err,res) { | ||||
|             if (err) { | ||||
|                 return done(err); | ||||
|             } | ||||
|             res.body.should.have.property("a",1); | ||||
|             res.body.should.have.property("b",2); | ||||
|             res.body.should.have.property("editorTheme",{existing: 789, test:456}); | ||||
|             done(); | ||||
|         }); | ||||
|     }); | ||||
|     it('returns the user settings', function(done) { | ||||
|         info.init({ | ||||
|             settings: { | ||||
|   | ||||
| @@ -101,7 +101,58 @@ describe("runtime-api/settings", function() { | ||||
|                 result.user.should.not.have.property("private"); | ||||
|             }) | ||||
|         }); | ||||
|         it("gets the filtered settings when editor disabled ", function() { | ||||
|             settings.init({ | ||||
|                 settings: { | ||||
|                     disableEditor: true, | ||||
|                     foo: 123, | ||||
|                     httpNodeRoot: "testHttpNodeRoot", | ||||
|                     version: "testVersion", | ||||
|                     paletteCategories :["red","blue","green"], | ||||
|                     exportNodeSettings: (obj) => { | ||||
|                         obj.testNodeSetting = "helloWorld"; | ||||
|                     } | ||||
|                 }, | ||||
|                 nodes: { | ||||
|                     listContextStores: () => { return {stores:["file","memory"], default: "file"} }, | ||||
|                     paletteEditorEnabled: () => false, | ||||
|                     getCredentialKeyType: () => "test-key-type" | ||||
|                 }, | ||||
|                 storage: { | ||||
|                     projects: { | ||||
|                         getActiveProject: () => 'test-active-project', | ||||
|                         getFlowFilename:  () => 'test-flow-file', | ||||
|                         getCredentialsFilename:  () => 'test-creds-file', | ||||
|                         getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}} | ||||
|                     } | ||||
|                 } | ||||
|             }) | ||||
|             return settings.getRuntimeSettings({ | ||||
|                 user: { | ||||
|                     username: "nick", | ||||
|                     anonymous: false, | ||||
|                     image: "http://example.com", | ||||
|                     permissions: "*", | ||||
|                     private: "secret" | ||||
|                 } | ||||
|             }).then(result => { | ||||
|                 result.should.have.property("user"); | ||||
|                 result.user.should.have.property("username","nick"); | ||||
|                 result.user.should.have.property("permissions","*"); | ||||
|                 result.user.should.have.property("image","http://example.com"); | ||||
|                 result.user.should.have.property("anonymous",false); | ||||
|                 result.user.should.not.have.property("private"); | ||||
|  | ||||
|                 // Filtered out when disableEditor is true | ||||
|                 result.should.not.have.property("paletteCategories",["red","blue","green"]); | ||||
|                 result.should.not.have.property("testNodeSetting","helloWorld"); | ||||
|                 result.should.not.have.property("foo",123); | ||||
|                 result.should.not.have.property("flowEncryptionType","test-key-type"); | ||||
|                 result.should.not.have.property("project"); | ||||
|                 result.should.not.have.property("git"); | ||||
|  | ||||
|             }) | ||||
|         }); | ||||
|         it('includes project settings if projects available', function() { | ||||
|             settings.init({ | ||||
|                 settings: { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user