From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id E30E71FF14C for ; Fri, 15 May 2026 10:54:42 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 7F6E61155F; Fri, 15 May 2026 10:54:07 +0200 (CEST) From: Dominik Csapak To: pve-devel@lists.proxmox.com Subject: [PATCH manager v3 06/13] ui: button: add config remove button Date: Fri, 15 May 2026 10:44:24 +0200 Message-ID: <20260515085349.1123127-7-d.csapak@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260515085349.1123127-1-d.csapak@proxmox.com> References: <20260515085349.1123127-1-d.csapak@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.050 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: OXA5ODPCUMFFQHAPCEK7GZTLUZIIIEGN X-Message-ID-Hash: OXA5ODPCUMFFQHAPCEK7GZTLUZIIIEGN X-MailFrom: d.csapak@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: this is a special remove button that has two main features: * removes properties from configs via PUT/POST instead of DELETE * can show an alternative text if set Signed-off-by: Dominik Csapak --- www/manager6/Makefile | 1 + www/manager6/button/ConfigRemove.js | 74 +++++++++++++++++++++++++++++ www/manager6/qemu/HardwareView.js | 66 +++---------------------- 3 files changed, 81 insertions(+), 60 deletions(-) create mode 100644 www/manager6/button/ConfigRemove.js diff --git a/www/manager6/Makefile b/www/manager6/Makefile index c2c1dffb..e4964b32 100644 --- a/www/manager6/Makefile +++ b/www/manager6/Makefile @@ -13,6 +13,7 @@ JSSRC= \ button/ConsoleButton.js \ button/Revert.js \ button/Split.js \ + button/ConfigRemove.js \ controller/StorageEdit.js \ data/PermPathStore.js \ data/ResourceStore.js \ diff --git a/www/manager6/button/ConfigRemove.js b/www/manager6/button/ConfigRemove.js new file mode 100644 index 00000000..ab195dfd --- /dev/null +++ b/www/manager6/button/ConfigRemove.js @@ -0,0 +1,74 @@ +Ext.define('PVE.button.ConfigRemove', { + extend: 'Proxmox.button.Button', + alias: 'widget.pveConfigRemoveButton', + + text: gettext('Remove'), + defaultText: gettext('Remove'), + altText: gettext('Detach'), + disabled: true, + dangerous: true, + RESTMethod: 'PUT', + + // overwrite for key rendering + renderKey: Ext.htmlEncode, + // overwrite for extra message + rows: {}, + + reloadCallback: Ext.identityFn, + + confirmMsg: function (rec) { + let me = this; + let warn = gettext('Are you sure you want to remove entry {0}'); + if (this.text === this.altText) { + warn = gettext('Are you sure you want to detach entry {0}'); + } + let rendered = me.renderKey(rec.data.key, {}, rec); + let msg = Ext.String.format(warn, `'${rendered}'`); + + if (me.rows[rec.data.key].del_extra_msg) { + msg += '
' + me.rows[rec.data.key].del_extra_msg; + } + return msg; + }, + handler: function (btn, e, rec) { + let me = this; + let params = { delete: rec.data.key }; + if (btn.RESTMethod === 'POST') { + params.background_delay = 5; + } + Proxmox.Utils.API2Request({ + url: '/api2/extjs/' + me.baseurl, + waitMsgTarget: me, + method: btn.RESTMethod, + params: params, + callback: () => me.reloadCallback(), + failure: (response) => Ext.Msg.alert('Error', response.htmlStatus), + success: function (response, options) { + if (btn.RESTMethod === 'POST' && response.result.data !== null) { + Ext.create('Proxmox.window.TaskProgress', { + autoShow: true, + upid: response.result.data, + listeners: { + destroy: () => me.reloadCallback(), + }, + }); + } + }, + }); + }, + listeners: { + render: function (btn) { + // hack: calculate the max button width on first display to prevent the whole + // toolbar to move when we switch between the "Remove" and "Detach" labels + var def = btn.getSize().width; + + btn.setText(btn.altText); + var alt = btn.getSize().width; + + btn.setText(btn.defaultText); + + var optimal = alt > def ? alt : def; + btn.setSize({ width: optimal }); + }, + }, +}); diff --git a/www/manager6/qemu/HardwareView.js b/www/manager6/qemu/HardwareView.js index 471e3a8f..58f86779 100644 --- a/www/manager6/qemu/HardwareView.js +++ b/www/manager6/qemu/HardwareView.js @@ -608,67 +608,13 @@ Ext.define('PVE.qemu.HardwareView', { }, }); - let remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - defaultText: gettext('Remove'), - altText: gettext('Detach'), - selModel: sm, - disabled: true, - dangerous: true, - RESTMethod: 'PUT', - confirmMsg: function (rec) { - let warn = gettext('Are you sure you want to remove entry {0}'); - if (this.text === this.altText) { - warn = gettext('Are you sure you want to detach entry {0}'); - } - let rendered = me.renderKey(rec.data.key, {}, rec); - let msg = Ext.String.format(warn, `'${rendered}'`); - - if (rows[rec.data.key].del_extra_msg) { - msg += '
' + rows[rec.data.key].del_extra_msg; - } - return msg; - }, - handler: function (btn, e, rec) { - let params = { delete: rec.data.key }; - if (btn.RESTMethod === 'POST') { - params.background_delay = 5; - } - Proxmox.Utils.API2Request({ - url: '/api2/extjs/' + baseurl, - waitMsgTarget: me, - method: btn.RESTMethod, - params: params, - callback: () => me.reload(), - failure: (response) => Ext.Msg.alert('Error', response.htmlStatus), - success: function (response, options) { - if (btn.RESTMethod === 'POST' && response.result.data !== null) { - Ext.create('Proxmox.window.TaskProgress', { - autoShow: true, - upid: response.result.data, - listeners: { - destroy: () => me.reload(), - }, - }); - } - }, - }); - }, - listeners: { - render: function (btn) { - // hack: calculate the max button width on first display to prevent the whole - // toolbar to move when we switch between the "Remove" and "Detach" labels - var def = btn.getSize().width; - - btn.setText(btn.altText); - var alt = btn.getSize().width; - - btn.setText(btn.defaultText); - - var optimal = alt > def ? alt : def; - btn.setSize({ width: optimal }); - }, + let remove_btn = new PVE.button.ConfigRemove({ + baseurl, + rows, + renderKey: function () { + return me.renderKey(...arguments); }, + reloadCallback: () => me.reload(), }); let revert_btn = new PVE.button.PendingRevert({ -- 2.47.3