From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id 1FA331FF0B0 for ; Tue, 27 Oct 2026 15:31:22 +0100 (CET) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 50F3E2142D; Tue, 27 Oct 2026 15:31:19 +0100 (CET) From: Nicolas Frey To: pve-devel@lists.proxmox.com Subject: [PATCH pve-manager 3/3] ui: resource tree: allow selecting multiple guests for bulk actions Date: Tue, 27 Oct 2026 15:30:50 +0100 Message-ID: <20261027143050.493707-4-n.frey@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20261027143050.493707-1-n.frey@proxmox.com> References: <20261027143050.493707-1-n.frey@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 2 DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) KAM_LAZY_DOMAIN_SECURITY 1 Sending domain does not have any anti-forgery methods RDNS_NONE 1.274 Delivered to internal network by a host with no rDNS SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_NONE 0.001 SPF: sender does not publish an SPF Record Message-ID-Hash: GR3BA3LOWY5E54BHGMUWMUMOWZGACYXU X-Message-ID-Hash: GR3BA3LOWY5E54BHGMUWMUMOWZGACYXU X-MailFrom: nfrey@miso.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: by switching the selmModel mode to 'MULTI' and restricting those selects to guest resources only. now opens `PVE.dc.GuestSelectionCmdMenu` as the context menu on right click, instead of the one for that resource. Signed-off-by: Nicolas Frey --- www/manager6/Utils.js | 17 ++++++++++++++++- www/manager6/Workspace.js | 6 +++++- www/manager6/tree/ResourceTree.js | 10 +++++++++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/www/manager6/Utils.js b/www/manager6/Utils.js index 8b99371d..1efce1a4 100644 --- a/www/manager6/Utils.js +++ b/www/manager6/Utils.js @@ -1615,7 +1615,22 @@ Ext.define('PVE.Utils', { let menu; let type = record.data.type; - if (record.data.template) { + let selModel = v.getSelectionModel(); + let selection = selModel.isSelected(record) ? selModel.getSelection() : []; + let vmids = + selection + .filter( + (rec) => + (rec.data.type === 'qemu' || rec.data.type === 'lxc') && + !rec.data.template, + ) + .map((rec) => rec.data.vmid); + + if (selection.length > 1 && vmids.length) { + menu = Ext.create('PVE.dc.GuestSelectionCmdMenu', { + vmids, + }); + } else if (record.data.template) { if (type === 'qemu' || type === 'lxc') { menu = Ext.create('PVE.menu.TemplateMenu', { pveSelNode: record, diff --git a/www/manager6/Workspace.js b/www/manager6/Workspace.js index 189d3373..f1b0c90a 100644 --- a/www/manager6/Workspace.js +++ b/www/manager6/Workspace.js @@ -232,9 +232,13 @@ Ext.define('PVE.StdWorkspace', { flex: 1, selModel: { selType: 'treemodel', + mode: 'MULTI', + allowDeselect: true, listeners: { selectionchange: function (sm, selected) { - if (selected.length <= 0) { + // when selecting multiple guests, another selection (in multi-select) + // should not switch the view + if (selected.length !== 1) { return; } let treeNode = selected[0]; diff --git a/www/manager6/tree/ResourceTree.js b/www/manager6/tree/ResourceTree.js index e2acc431..971c1140 100644 --- a/www/manager6/tree/ResourceTree.js +++ b/www/manager6/tree/ResourceTree.js @@ -547,7 +547,15 @@ Ext.define('PVE.tree.ResourceTree', { beforeselect: function (tree, record, index, eopts) { let allow = me.allowSelection; me.allowSelection = true; - return allow; + if (!allow) { + return false; + } + // only guests can be part of a selection of multiple entries, as they're the + // only resources with bulk actions + let isGuest = (rec) => + (rec.data.type === 'qemu' || rec.data.type === 'lxc') && !rec.data.template; + let others = tree.getSelection().filter((rec) => rec !== record); + return !others.length || (isGuest(record) && others.every(isGuest)); }, itemdblclick: PVE.Utils.openTreeConsole, afterrender: function () { -- 2.47.3