From bfd98f3767ea89bd2f0c0c3367a64669dc72b438 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 12 Nov 2018 17:04:22 +0000 Subject: [PATCH] Add ability to delete context values from sidebar --- .../@node-red/editor-api/lib/admin/context.js | 15 +++ .../@node-red/editor-api/lib/admin/index.js | 5 + .../editor-client/locales/en-US/editor.json | 3 +- .../editor-client/src/js/ui/common/popover.js | 12 +++ .../editor-client/src/js/ui/tab-context.js | 78 +++++++++++--- .../editor-client/src/js/ui/utils.js | 9 +- .../editor-client/src/sass/debug.scss | 6 ++ .../editor-client/src/sass/popover.scss | 13 +++ .../@node-red/runtime/lib/api/context.js | 100 +++++++++++++++++- 9 files changed, 225 insertions(+), 16 deletions(-) diff --git a/packages/node_modules/@node-red/editor-api/lib/admin/context.js b/packages/node_modules/@node-red/editor-api/lib/admin/context.js index 09b1aa53b..6a2efd82d 100644 --- a/packages/node_modules/@node-red/editor-api/lib/admin/context.js +++ b/packages/node_modules/@node-red/editor-api/lib/admin/context.js @@ -37,5 +37,20 @@ module.exports = { }).catch(function(err) { apiUtils.rejectHandler(req,res,err); }) + }, + + delete: function(req,res) { + var opts = { + user: req.user, + scope: req.params.scope, + id: req.params.id, + key: req.params[0], + store: req.query['store'] + } + runtimeAPI.context.delete(opts).then(function(result) { + res.status(204).end(); + }).catch(function(err) { + apiUtils.rejectHandler(req,res,err); + }) } } diff --git a/packages/node_modules/@node-red/editor-api/lib/admin/index.js b/packages/node_modules/@node-red/editor-api/lib/admin/index.js index 1f77b70b4..32bf010c5 100644 --- a/packages/node_modules/@node-red/editor-api/lib/admin/index.js +++ b/packages/node_modules/@node-red/editor-api/lib/admin/index.js @@ -62,6 +62,11 @@ module.exports = { adminApp.get("/context/:scope(node|flow)/:id",needsPermission("context.read"),context.get,apiUtil.errorHandler); adminApp.get("/context/:scope(node|flow)/:id/*",needsPermission("context.read"),context.get,apiUtil.errorHandler); + // adminApp.delete("/context/:scope(global)",needsPermission("context.write"),context.delete,apiUtil.errorHandler); + adminApp.delete("/context/:scope(global)/*",needsPermission("context.write"),context.delete,apiUtil.errorHandler); + // 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); + return adminApp; } } diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json index 23df7550d..7a7661b1f 100644 --- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json @@ -515,7 +515,8 @@ "empty": "empty", "node": "Node", "flow": "Flow", - "global": "Global" + "global": "Global", + "deleteConfirm": "Are you sure you want to delete this item?" }, "palette": { "name": "Palette management", diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js index 065fcd7f9..5b5057666 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js @@ -131,6 +131,7 @@ RED.popover = (function() { } } var closePopup = function(instant) { + $(document).off('mousedown.modal-popover-close'); if (!active) { if (div) { if (instant) { @@ -171,6 +172,17 @@ RED.popover = (function() { openPopup(); } }); + } else if (trigger === 'modal') { + $(document).on('mousedown.modal-popover-close', function (event) { + var target = event.target; + while (target.nodeName !== 'BODY' && target !== div[0]) { + target = target.parentElement; + } + if (target.nodeName === 'BODY') { + active = false; + closePopup(); + } + }); } else if (autoClose) { setTimeout(function() { active = false; diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js index 65758fbb6..b62ad13c3 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js @@ -237,29 +237,83 @@ RED.sidebar.context = (function() { var propRow = $('').appendTo(container); var obj = $(propRow.children()[0]); obj.text(k); - var tools = $('').appendTo(obj); + var tools = $(''); + var refreshItem = $('').appendTo(tools).click(function(e) { e.preventDefault(); e.stopPropagation(); $.getJSON(baseUrl+"/"+k+"?store="+v.store, function(data) { - $(propRow.children()[1]).empty(); - var payload = data.msg; - var format = data.format; - payload = RED.utils.decodeObject(payload,format); - RED.utils.createObjectElement(payload, { - typeHint: data.format, - sourceId: id+"."+k - }).appendTo(propRow.children()[1]); + if (data.msg !== payload || data.format !== format) { + payload = data.msg; + format = data.format; + tools.detach(); + $(propRow.children()[1]).empty(); + RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), { + typeHint: data.format, + sourceId: id+"."+k, + tools: tools + }).appendTo(propRow.children()[1]); + } }) }); + var deleteItem = $('').appendTo(tools).click(function(e) { + e.preventDefault(); + e.stopPropagation(); + var popover = RED.popover.create({ + trigger: 'modal', + target: propRow, + direction: "left", + content: function() { + var content = $('
'); + $('

').appendTo(content); + var row = $('

').appendTo(content); + var bg = $('').appendTo(row); + $('').appendTo(bg).click(function(e) { + e.preventDefault(); + popover.close(); + }); + bg = $('').appendTo(row); + $('').appendTo(bg).click(function(e) { + e.preventDefault(); + popover.close(); + $.ajax({ + url: baseUrl+"/"+k+"?store="+v.store, + type: "DELETE" + }).done(function(data,textStatus,xhr) { + $.getJSON(baseUrl+"/"+k+"?store="+v.store, function(data) { + if (data.format === 'undefined') { + propRow.remove(); + if (container.children().length === 0) { + $('').appendTo(container).i18n(); + } + } else { + payload = data.msg; + format = data.format; + tools.detach(); + $(propRow.children()[1]).empty(); + RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), { + typeHint: data.format, + sourceId: id+"."+k, + tools: tools + }).appendTo(propRow.children()[1]); + } + }); + }).fail(function(xhr,textStatus,err) { + }) + }); + return content.i18n(); + } + }); + popover.open(); + }); var payload = v.msg; var format = v.format; - payload = RED.utils.decodeObject(payload,format); - RED.utils.createObjectElement(payload, { + RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), { typeHint: v.format, - sourceId: id+"."+k + sourceId: id+"."+k, + tools: tools }).appendTo(propRow.children()[1]); if (contextStores.length > 1) { $("",{class:"sidebar-context-property-storename"}).text(v.store).appendTo($(propRow.children()[0])) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js b/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js index 77faf60ab..43d3cced3 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js @@ -113,7 +113,7 @@ RED.utils = (function() { var pinnedPaths = {}; var formattedPaths = {}; - function addMessageControls(obj,sourceId,key,msg,rootPath,strippedKey) { + function addMessageControls(obj,sourceId,key,msg,rootPath,strippedKey,extraTools) { if (!pinnedPaths.hasOwnProperty(sourceId)) { pinnedPaths[sourceId] = {} } @@ -150,6 +150,10 @@ RED.utils = (function() { }).toggleClass("selected",isPinned); obj.toggleClass("debug-message-row-pinned",isPinned); } + if (extraTools) { + extraTools.addClass("debug-message-tools-other"); + extraTools.appendTo(tools); + } } function checkExpanded(strippedKey,expandPaths,minRange,maxRange) { if (expandPaths && expandPaths.length > 0) { @@ -243,6 +247,7 @@ RED.utils = (function() { var expandPaths = options.expandPaths; var ontoggle = options.ontoggle; var exposeApi = options.exposeApi; + var tools = options.tools; var subElements = {}; var i; @@ -262,7 +267,7 @@ RED.utils = (function() { } header = $('').appendTo(element); if (sourceId) { - addMessageControls(header,sourceId,path,obj,rootPath,strippedKey); + addMessageControls(header,sourceId,path,obj,rootPath,strippedKey,tools); } if (!key) { element.addClass("debug-message-top-level"); diff --git a/packages/node_modules/@node-red/editor-client/src/sass/debug.scss b/packages/node_modules/@node-red/editor-client/src/sass/debug.scss index a3acd18ff..456ef2559 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/debug.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/debug.scss @@ -81,6 +81,9 @@ .debug-message-tools-pin { display: inline-block; } + .debug-message-tools-other { + display: inline-block; + } } } } @@ -134,6 +137,9 @@ .debug-message-tools-copy { display: none; } + .debug-message-tools-other { + display: none; + } } .debug-message-payload { display: block; diff --git a/packages/node_modules/@node-red/editor-client/src/sass/popover.scss b/packages/node_modules/@node-red/editor-client/src/sass/popover.scss index bfc182ec4..55b88452f 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/popover.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/popover.scss @@ -146,3 +146,16 @@ border-radius:3px; padding: 1px 2px; } + +.red-ui-popover .editor-button { + &:not(.primary) { + color: #444 !important; + border-color: rgba(0,0,0,0); + } + &.primary { + border-color: #bbb; + } + &.primary:hover { + border-color: #666 !important; + } +} diff --git a/packages/node_modules/@node-red/runtime/lib/api/context.js b/packages/node_modules/@node-red/runtime/lib/api/context.js index 1ef875356..64eddda93 100644 --- a/packages/node_modules/@node-red/runtime/lib/api/context.js +++ b/packages/node_modules/@node-red/runtime/lib/api/context.js @@ -20,7 +20,6 @@ var runtime; -// TODO: move runtime/util to util/index var util = require("@node-red/util").util; function exportContextStore(scope,ctx, store, result, callback) { @@ -152,5 +151,104 @@ var api = module.exports = { resolve({}); } }) + }, + + /** + * Gets the info of an individual node set + * @param {Object} opts + * @param {User} opts.user - the user calling the api + * @param {String} opts.scope - the scope of the context + * @param {String} opts.id - the id of the context + * @param {String} opts.store - the context store + * @param {String} opts.key - the context key + + * @return {Promise} - the node information + * @memberof RED.nodes + */ + delete: function(opts) { + return new Promise(function(resolve,reject) { + var scope = opts.scope; + var id = opts.id; + var store = opts.store; + var key = opts.key; + + var availableStores = runtime.nodes.listContextStores(); + //{ default: 'default', stores: [ 'default', 'file' ] } + if (store && availableStores.stores.indexOf(store) === -1) { + runtime.log.audit({event: "context.get",scope:scope,id:id,store:store,key:key,error:"not_found"}); + var err = new Error(); + err.code = "not_found"; + err.status = 404; + return reject(err); + } + var ctx; + if (scope === 'global') { + ctx = runtime.nodes.getContext('global'); + } else if (scope === 'flow') { + ctx = runtime.nodes.getContext(id); + } else if (scope === 'node') { + var node = runtime.nodes.getNode(id); + if (node) { + ctx = node.context(); + } + } + if (ctx) { + if (key) { + store = store || availableStores.default; + ctx.set(key,undefined,store,function(err) { + runtime.log.audit({event: "context.delete",scope:scope,id:id,store:store,key:key}); + resolve(); + }); + return; + } else { + // TODO: support deleting whole context + runtime.log.audit({event: "context.get",scope:scope,id:id,store:store,key:key,error:"not_found"}); + var err = new Error(); + err.code = "not_found"; + err.status = 404; + return reject(err); + // var stores; + // if (!store) { + // stores = availableStores.stores; + // } else { + // stores = [store]; + // } + // + // var result = {}; + // var c = stores.length; + // var errorReported = false; + // stores.forEach(function(store) { + // exportContextStore(scope,ctx,store,result,function(err) { + // if (err) { + // // TODO: proper error reporting + // if (!errorReported) { + // errorReported = true; + // runtime.log.audit({event: "context.delete",scope:scope,id:id,store:store,key:key,error:"unexpected_error"}); + // var err = new Error(); + // err.code = "unexpected_error"; + // err.status = 400; + // return reject(err); + // } + // + // return; + // } + // c--; + // if (c === 0) { + // if (!errorReported) { + // runtime.log.audit({event: "context.get",scope:scope,id:id,store:store,key:key}); + // resolve(result); + // } + // } + // }); + // }) + } + } else { + runtime.log.audit({event: "context.delete",scope:scope,id:id,store:store,key:key}); + resolve(); + } + + + }); } + }