From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id B46E61FF137 for ; Tue, 17 Mar 2026 12:48:16 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 06C95BBCD; Tue, 17 Mar 2026 12:48:29 +0100 (CET) Message-ID: Date: Tue, 17 Mar 2026 12:48:24 +0100 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH proxmox-backup v4 6/7] ui: add move group action To: Christian Ebner , pbs-devel@lists.proxmox.com References: <20260311151315.133637-1-h.laimer@proxmox.com> <20260311151315.133637-7-h.laimer@proxmox.com> <720f67da-1791-4b7b-8fb9-d567dd734d60@proxmox.com> Content-Language: en-US From: Hannes Laimer In-Reply-To: <720f67da-1791-4b7b-8fb9-d567dd734d60@proxmox.com> Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1773748063792 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.079 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: 3BX3HDVL5VZJP753YBC5MLCK4YV66YBM X-Message-ID-Hash: 3BX3HDVL5VZJP753YBC5MLCK4YV66YBM X-MailFrom: h.laimer@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 Backup Server development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: On 2026-03-17 12:43, Christian Ebner wrote: > One comment inline. > > On 3/11/26 4:13 PM, Hannes Laimer wrote: >> Signed-off-by: Hannes Laimer >> --- >>   www/Makefile             |  1 + >>   www/datastore/Content.js | 61 ++++++++++++++++++++++++++++++++++++++++ >>   www/window/GroupMove.js  | 56 ++++++++++++++++++++++++++++++++++++ >>   3 files changed, 118 insertions(+) >>   create mode 100644 www/window/GroupMove.js >> >> diff --git a/www/Makefile b/www/Makefile >> index 9ebf0445..db775b64 100644 >> --- a/www/Makefile >> +++ b/www/Makefile >> @@ -77,6 +77,7 @@ JSSRC=                            \ >>       window/ACLEdit.js                \ >>       window/BackupGroupChangeOwner.js        \ >>       window/CreateDirectory.js            \ >> +    window/GroupMove.js                \ >>       window/DataStoreEdit.js                \ >>       window/NamespaceEdit.js                \ >>       window/MaintenanceOptions.js            \ >> diff --git a/www/datastore/Content.js b/www/datastore/Content.js >> index a2aa1949..ccd48616 100644 >> --- a/www/datastore/Content.js >> +++ b/www/datastore/Content.js >> @@ -635,6 +635,50 @@ Ext.define('PBS.DataStoreContent', { >>               }); >>           }, >>   +        moveNS: function () { >> +            let me = this; >> +            let view = me.getView(); >> +            if (!view.namespace || view.namespace === '') { >> +                return; >> +            } >> +            let win = Ext.create('PBS.window.NamespaceMove', { >> +                datastore: view.datastore, >> +                namespace: view.namespace, >> +                taskDone: (success) => { >> +                    if (success) { >> +                        let newNs = win.getNewNamespace(); >> +                        view.down('pbsNamespaceSelector').store?.load(); >> +                        me.nsChange(null, newNs); >> +                    } >> +                }, >> +            }); >> +            win.show(); >> +        }, >> + >> +        moveGroup: function (data) { >> +            let me = this; >> +            let view = me.getView(); >> +            let group = `${data.backup_type}/${data.backup_id}`; >> +            Ext.create('PBS.window.GroupMove', { >> +                datastore: view.datastore, >> +                namespace: view.namespace, >> +                backupType: data.backup_type, >> +                backupId: data.backup_id, >> +                group, >> +                taskDone: () => me.reload(), >> +            }).show(); >> +        }, >> + >> +        onMove: function (table, rI, cI, item, e, { data }) { >> +            let me = this; >> +            if (data.ty === 'group') { >> +                me.moveGroup(data); >> +            } else if (data.ty === 'ns') { >> +                me.moveNS(); >> +            } >> +        }, >> + >> + >>           forgetGroup: function (data) { >>               let me = this; >>               let view = me.getView(); >> @@ -1080,6 +1124,23 @@ Ext.define('PBS.DataStoreContent', { >>                       }, >>                       isActionDisabled: (v, r, c, i, rec) => >> rec.data.ty !== 'dir', >>                   }, >> +                { >> +                    handler: 'onMove', >> +                    getTip: (v, m, { data }) => { >> +                        if (data.ty === 'group') { >> +                            return Ext.String.format(gettext("Move >> group '{0}'"), v); >> +                        } >> +                        return Ext.String.format(gettext("Move >> namespace '{0}'"), v); >> +                    }, >> +                    getClass: (v, m, { data }) => { >> +                        if (data.ty === 'group') { return 'fa fa- >> arrows'; } >> +                        if (data.ty === 'ns' && !data.isRootNS && >> data.ns === undefined) { >> +                            return 'fa fa-arrows'; >> +                        } >> +                        return 'pmx-hidden'; >> +                    }, >> +                    isActionDisabled: (v, r, c, i, { data }) => false, >> +                }, > > comment: placing this right before the prune icon is a bit dangerous > (although a prune will ask for confirmation). > > Since the protected icons is not shown for groups and namespaces, it > should rather be placed before that, right after the verification icon. > makes sense, will change in v5 > Further, what I missed is having this also in the context menu opened on > right click of the group/namespace. never right-clicked a group, didn't know we have a context menu there :) will also add it there, thanks! > >>                   { >>                       handler: 'onForget', >>                       getTip: (v, m, { data }) => { >> diff --git a/www/window/GroupMove.js b/www/window/GroupMove.js >> new file mode 100644 >> index 00000000..f663c606 >> --- /dev/null >> +++ b/www/window/GroupMove.js >> @@ -0,0 +1,56 @@ >> +Ext.define('PBS.window.GroupMove', { >> +    extend: 'Proxmox.window.Edit', >> +    alias: 'widget.pbsGroupMove', >> +    mixins: ['Proxmox.Mixin.CBind'], >> + >> +    onlineHelp: 'storage-namespaces', >> + >> +    isCreate: true, >> +    submitText: gettext('Move'), >> +    showTaskViewer: true, >> + >> +    cbind: { >> +        url: '/api2/extjs/admin/datastore/{datastore}/groups', >> +        title: (get) => Ext.String.format(gettext("Move Backup Group >> '{0}'"), get('group')), >> +    }, >> +    method: 'PUT', >> + >> +    width: 400, >> +    fieldDefaults: { >> +        labelWidth: 120, >> +    }, >> + >> +    items: { >> +        xtype: 'inputpanel', >> +        onGetValues: function (values) { >> +            let win = this.up('window'); >> +            let result = { >> +                'backup-type': win.backupType, >> +                'backup-id': win.backupId, >> +                'new-ns': values['new-ns'] || '', >> +            }; >> +            if (win.namespace && win.namespace !== '') { >> +                result.ns = win.namespace; >> +            } >> +            return result; >> +        }, >> +        items: [ >> +            { >> +                xtype: 'displayfield', >> +                fieldLabel: gettext('Group'), >> +                cbind: { >> +                    value: '{group}', >> +                }, >> +            }, >> +            { >> +                xtype: 'pbsNamespaceSelector', >> +                name: 'new-ns', >> +                fieldLabel: gettext('Target Namespace'), >> +                allowBlank: true, >> +                cbind: { >> +                    datastore: '{datastore}', >> +                }, >> +            }, >> +        ], >> +    }, >> +}); >