From cad93b3650579d305dd74622c15f77ea83a58e60 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Sun, 27 Oct 2024 13:24:43 +0100 Subject: [PATCH 1/9] Add function docs --- .../@node-red/editor-client/src/js/nodes.js | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 131d40d18..e0c6f290b 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -1494,7 +1494,14 @@ RED.nodes = (function() { } /** * Converts the current node selection to an exportable JSON Object - **/ + * @param {Array} set the node selection to export + * @param {Object} options + * @param {Record} [options.exportedIds] + * @param {Record} [options.exportedSubflows] + * @param {Record} [options.exportedConfigNodes] + * @param {boolean} [options.includeModuleConfig] + * @returns {Array} + */ function createExportableNodeSet(set, { exportedIds, exportedSubflows, @@ -1582,10 +1589,14 @@ RED.nodes = (function() { return nns; } - // Create the Flow JSON for the current configuration - // opts.credentials (whether to include (known) credentials) - default: true - // opts.dimensions (whether to include node dimensions) - default: false - // opts.includeModuleConfig (whether to include modules) - default: false + /** + * Converts the current configuration to an exportable JSON Object + * @param {object} opts + * @param {boolean} [opts.credentials] whether to include (known) credentials. Default `true`. + * @param {boolean} [opts.dimensions] whether to include node dimensions. Default `false`. + * @param {boolean} [opts.includeModuleConfig] whether to include modules. Default `false`. + * @returns {Array} + */ function createCompleteNodeSet(opts) { var nns = []; var i; @@ -3164,6 +3175,12 @@ RED.nodes = (function() { } } } + + /** + * Gets the module list for the given nodes + * @param {Array} nodes the nodes to search in + * @returns {Record} an object with {[moduleName]: moduleVersion} + */ function getModuleListForNodes(nodes) { const modules = {} nodes.forEach(n => { From 42af21c1a4f3846b8ba7cf20224ea512eb88a16d Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Sun, 27 Oct 2024 13:26:47 +0100 Subject: [PATCH 2/9] Improve `getModuleListForNodes` + fix typo --- .../@node-red/editor-client/src/js/nodes.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index e0c6f290b..91da27b99 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -3183,19 +3183,25 @@ RED.nodes = (function() { */ function getModuleListForNodes(nodes) { const modules = {} - nodes.forEach(n => { - const nodeSet = RED.nodes.registry.getNodeSetForType(n.type) - if (nodeSet) { - modules[nodeSet.module] = nodeSet.version + const typeSet = new Set() + nodes.forEach((n) => { + if (!typeSet.has(n.type)) { + typeSet.add(n.type) + const nodeSet = RED.nodes.registry.getNodeSetForType(n.type) + if (nodeSet) { + modules[nodeSet.module] = nodeSet.version + nodeSet.types.forEach((t) => typeSet.add(t)) + } } }) return modules } + function updateGlobalConfigModuleList(nodes) { const modules = getModuleListForNodes(nodes) delete modules['node-red'] const hasModules = (Object.keys(modules).length > 0) - let globalConfigNode = nodes.find(n => n.type === 'global-config') + let globalConfigNode = nodes.find((n) => n.type === 'global-config') if (!globalConfigNode && hasModules) { globalConfigNode = { id: RED.nodes.id(), @@ -3204,7 +3210,7 @@ RED.nodes = (function() { modules } nodes.push(globalConfigNode) - } else if (globalConfigNode) { + } else if (hasModules) { globalConfigNode.modules = modules } } From a81d6f3a89656e066437ae651ca83be7e7902b46 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Sun, 27 Oct 2024 16:30:33 +0100 Subject: [PATCH 3/9] Allow `core:manage-palette` action to auto install modules --- .../editor-client/locales/en-US/editor.json | 6 +- .../@node-red/editor-client/src/js/nodes.js | 18 ++++- .../editor-client/src/js/ui/palette-editor.js | 80 ++++++++++++++++++- 3 files changed, 99 insertions(+), 5 deletions(-) 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 a59dcacf4..f5ecbf9ee 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 @@ -265,7 +265,7 @@ "download": "Download", "importUnrecognised": "Imported unrecognised type:", "importUnrecognised_plural": "Imported unrecognised types:", - "importWithModuleInfo": "Required dependencies missing", + "importWithModuleInfo": "Required modules missing", "importWithModuleInfoDesc": "These nodes are not currently installed in your palette and are required for the imported flow:", "importDuplicate": "Imported duplicate node:", "importDuplicate_plural": "Imported duplicate nodes:", @@ -626,6 +626,7 @@ "yearsMonthsV": "__y__ years, __count__ month ago", "yearsMonthsV_plural": "__y__ years, __count__ months ago" }, + "manageModules": "Manage modules", "nodeCount": "__label__ node", "nodeCount_plural": "__label__ nodes", "pluginCount": "__count__ plugin", @@ -643,7 +644,9 @@ "update": "update to __version__", "updated": "updated", "install": "install", + "installEverything": "Install everything", "installed": "installed", + "installing": "Module installation in progress: __module__", "conflict": "conflict", "conflictTip": "

This module cannot be installed as it includes a
node type that has already been installed

Conflicts with __module__

", "loading": "Loading catalogues...", @@ -653,6 +656,7 @@ "sortRelevance": "relevance", "sortAZ": "a-z", "sortRecent": "recent", + "successfulInstall": "Successfully installed modules", "more": "+ __count__ more", "upload": "Upload module tgz file", "refresh": "Refresh module list", diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 91da27b99..e6ea5bf0c 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -2011,15 +2011,27 @@ RED.nodes = (function() { // Provide option to install missing modules notificationOptions.buttons = [ { - text: "Manage dependencies", - class:"primary", + text: RED._("palette.editor.manageModules"), + class: "primary", click: function(e) { unknownNotification.close(); RED.actions.invoke('core:manage-palette', { view: 'install', filter: '"' + missingModules.join('", "') + '"' - }) + }); + } + }, + { + text: RED._("palette.editor.installEverything"), + class: "pull-left", + click: function(e) { + unknownNotification.close(); + + RED.actions.invoke('core:manage-palette', { + autoInstall: true, + modules: options.modules + }); } } ] diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js index e73a3a9b1..2105a6a7f 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js @@ -700,6 +700,9 @@ RED.palette.editor = (function() { refreshCatalogues() RED.actions.add("core:manage-palette", function(opts) { + if (opts && opts.autoInstall && opts.modules) { + autoInstallModules(opts.modules); + } else { RED.userSettings.show('palette'); if (opts) { if (opts.view) { @@ -713,7 +716,8 @@ RED.palette.editor = (function() { } } } - }); + } + }); RED.events.on('registry:module-updated', function(ns) { refreshNodeModule(ns.module); @@ -1593,6 +1597,80 @@ RED.palette.editor = (function() { }) } + function autoInstallModules(modules) { + if (RED.settings.get('externalModules.palette.allowInstall', true) === false) { + console.error(new Error('Palette not editable')); + return; + } + + let notification; + const notificationOptions = { + fixed: true, + buttons: [ + { + text: RED._("common.label.close"), + click: function () { + notification.close(); + } + }, { + text: RED._("eventLog.view"), + click: function () { + notification.close(); + RED.actions.invoke("core:show-event-log"); + } + } + ] + }; + + const moduleArray = Object.entries(modules); + const installModule = function (module) { + const [moduleName, moduleVersion] = module; + + const msg = RED.notify(RED._("palette.editor.installing", { module: moduleName })); + + if (!notification) { + notification = RED.notify(msg, notificationOptions); + } else { + notification.update(msg); + } + + // TODO: add spinner to a div below the title + //const spinner = RED.utils.addSpinnerOverlay(container, true); + + RED.eventLog.startEvent(RED._("palette.editor.confirm.button.install") + " : " + moduleName + " " + moduleVersion); + + installNodeModule(moduleName, moduleVersion, undefined, function(xhr, textStatus, err) { + //spinner.close(); + if (err && xhr.status === 504) { + notification.update(RED._("palette.editor.errors.installTimeout"), { + modal: true, + fixed: true, + buttons: notificationOptions.buttons + }); + } else if (xhr) { + if (xhr.responseJSON) { + notification.update(RED._("palette.editor.errors.installFailed", { module: moduleName, message:xhr.responseJSON.message }), { + type: "error", + modal: true, + fixed: true, + buttons: notificationOptions.buttons + }); + } + } else { + if (moduleArray.length) { + installModule(moduleArray.shift()); + } else { + notification.update(RED._("palette.editor.successfulInstall"), { ...notificationOptions, type: "success", timeout: 1000 }); + } + } + }); + }; + + if (moduleArray.length) { + installModule(moduleArray.shift()); + } + } + const updateStatusWidget = $(''); let updateAvailable = []; From a4c8f578024c6d52ad8a9019edb538b99edf16ac Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Sun, 27 Oct 2024 16:33:28 +0100 Subject: [PATCH 4/9] Add note + nls to the "manage modules" button --- .../node_modules/@node-red/nodes/core/common/98-unknown.html | 2 +- .../node_modules/@node-red/nodes/locales/en-US/messages.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/common/98-unknown.html b/packages/node_modules/@node-red/nodes/core/common/98-unknown.html index 282ad3415..64c9e8e4e 100644 --- a/packages/node_modules/@node-red/nodes/core/common/98-unknown.html +++ b/packages/node_modules/@node-red/nodes/core/common/98-unknown.html @@ -3,7 +3,7 @@

- +

diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json index d26f0f56b..ecc261416 100644 --- a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json @@ -406,7 +406,8 @@ "label": { "unknown": "unknown" }, - "tip": "

This node is a type unknown to your installation of Node-RED.

If you deploy with the node in this state, it's configuration will be preserved, but the flow will not start until the missing type is installed.

See the Info side bar for more help

" + "manageModules": "Manage modules", + "tip": "

This node is a type unknown to your installation of Node-RED.

If you deploy with the node in this state, it's configuration will be preserved, but the flow will not start until the missing type is installed.

See the Info side bar for more help

You can also click the button below to open the palette and install the missing modules.

" }, "mqtt": { "label": { From 222ab71f21793ed841a25744797676ed4ad0b770 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Sun, 27 Oct 2024 23:08:19 +0100 Subject: [PATCH 5/9] Add spinner + fix typos --- .../editor-client/src/js/ui/palette-editor.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js index 2105a6a7f..70be11146 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js @@ -1626,21 +1626,18 @@ RED.palette.editor = (function() { const installModule = function (module) { const [moduleName, moduleVersion] = module; - const msg = RED.notify(RED._("palette.editor.installing", { module: moduleName })); + const spinner = '
'; + const msg = "

" + RED._("palette.editor.installing", { module: moduleName }) + "

" + spinner; if (!notification) { notification = RED.notify(msg, notificationOptions); } else { - notification.update(msg); + notification.update(msg, notificationOptions); } - // TODO: add spinner to a div below the title - //const spinner = RED.utils.addSpinnerOverlay(container, true); - RED.eventLog.startEvent(RED._("palette.editor.confirm.button.install") + " : " + moduleName + " " + moduleVersion); installNodeModule(moduleName, moduleVersion, undefined, function(xhr, textStatus, err) { - //spinner.close(); if (err && xhr.status === 504) { notification.update(RED._("palette.editor.errors.installTimeout"), { modal: true, @@ -1660,7 +1657,7 @@ RED.palette.editor = (function() { if (moduleArray.length) { installModule(moduleArray.shift()); } else { - notification.update(RED._("palette.editor.successfulInstall"), { ...notificationOptions, type: "success", timeout: 1000 }); + notification.update(RED._("palette.editor.successfulInstall"), { ...notificationOptions, type: "success", timeout: 10000 }); } } }); From 75e37dc75f4716c8eabd6d742624c3b827b598f8 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Mon, 28 Oct 2024 10:58:53 +0100 Subject: [PATCH 6/9] Replace `installEverything` by `InstallAll` --- .../@node-red/editor-client/locales/en-US/editor.json | 2 +- packages/node_modules/@node-red/editor-client/src/js/nodes.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 f5ecbf9ee..a8bf70a19 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 @@ -644,7 +644,7 @@ "update": "update to __version__", "updated": "updated", "install": "install", - "installEverything": "Install everything", + "installAll": "Install all", "installed": "installed", "installing": "Module installation in progress: __module__", "conflict": "conflict", diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index e6ea5bf0c..b9d5874ca 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -2023,7 +2023,7 @@ RED.nodes = (function() { } }, { - text: RED._("palette.editor.installEverything"), + text: RED._("palette.editor.installAll"), class: "pull-left", click: function(e) { unknownNotification.close(); From 570dd1f5a4d9f181bcf7ee7bf10211701201a843 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Mon, 28 Apr 2025 21:45:57 +0200 Subject: [PATCH 7/9] Only install missing modules --- .../node_modules/@node-red/editor-client/src/js/nodes.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index b9d5874ca..20af0bd11 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -2030,7 +2030,10 @@ RED.nodes = (function() { RED.actions.invoke('core:manage-palette', { autoInstall: true, - modules: options.modules + modules: missingModules.reduce((modules, moduleName) => { + modules[moduleName] = options.modules[moduleName]; + return modules; + }, {}), }); } } From f14c50c20c7daf63cce02b80a66c4a2089323993 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Mon, 28 Apr 2025 21:47:35 +0200 Subject: [PATCH 8/9] Don't call `eventLog` anymore because it's handled in the queue --- .../@node-red/editor-client/src/js/ui/palette-editor.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js index 70be11146..2f60ff03f 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js @@ -1635,8 +1635,6 @@ RED.palette.editor = (function() { notification.update(msg, notificationOptions); } - RED.eventLog.startEvent(RED._("palette.editor.confirm.button.install") + " : " + moduleName + " " + moduleVersion); - installNodeModule(moduleName, moduleVersion, undefined, function(xhr, textStatus, err) { if (err && xhr.status === 504) { notification.update(RED._("palette.editor.errors.installTimeout"), { From 843313185ac08f28e3884746e841aab3367c0081 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 6 Jun 2025 10:15:53 +0100 Subject: [PATCH 9/9] Apply suggestions from code review --- .../@node-red/editor-client/src/js/ui/palette-editor.js | 2 +- .../node_modules/@node-red/nodes/locales/en-US/messages.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js index 91ad65fe2..64d526dc9 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js @@ -1694,7 +1694,7 @@ RED.palette.editor = (function() { function autoInstallModules(modules) { if (RED.settings.get('externalModules.palette.allowInstall', true) === false) { - console.error(new Error('Palette not editable')); + console.error('Palette not editable'); return; } diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json index 1af06be11..7cde427f4 100644 --- a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json @@ -407,7 +407,7 @@ "unknown": "unknown" }, "manageModules": "Manage modules", - "tip": "

This node is a type unknown to your installation of Node-RED.

If you deploy with the node in this state, it's configuration will be preserved, but the flow will not start until the missing type is installed.

See the Info side bar for more help

You can also click the button below to open the palette and install the missing modules.

" + "tip": "

This node is a type unknown to your installation of Node-RED.

If you deploy with the node in this state, it's configuration will be preserved, but the flow will not start until the missing type is installed.

See the Info side bar for more help

" }, "mqtt": { "label": {