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 A3DCB1FF144 for ; Tue, 24 Mar 2026 11:57:16 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 5E3B5C147; Tue, 24 Mar 2026 11:57:36 +0100 (CET) Message-ID: <2290f062-22b9-48ae-b43c-9e7bca683497@proxmox.com> Date: Tue, 24 Mar 2026 11:56:54 +0100 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Beta Subject: Re: [PATCH proxmox-backup v5 8/9] ui: add move namespace action To: Hannes Laimer , pbs-devel@lists.proxmox.com References: <20260319161325.206846-1-h.laimer@proxmox.com> <20260319161325.206846-9-h.laimer@proxmox.com> Content-Language: en-US From: Dominik Csapak In-Reply-To: <20260319161325.206846-9-h.laimer@proxmox.com> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1774349768459 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.041 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: 6BXPYNAE32F5SMJA4LZNMTC7XBR4JL2Z X-Message-ID-Hash: 6BXPYNAE32F5SMJA4LZNMTC7XBR4JL2Z 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 Backup Server development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: looks mostly fine preexisting but slightly confusing: the action of a namespace can only be done from that namespace itself, not from one level above. maybe it would be sensible to make that possible as well? otherwise one comment inline On 3/19/26 5:13 PM, Hannes Laimer wrote: > When moving a namespace, exclude the source namespace and its subtree > from the 'New Parent' selector. > > Signed-off-by: Hannes Laimer > --- > www/Makefile | 1 + > www/datastore/Content.js | 27 +++++++++++- > www/form/NamespaceSelector.js | 11 +++++ > www/window/NamespaceMove.js | 79 +++++++++++++++++++++++++++++++++++ > 4 files changed, 117 insertions(+), 1 deletion(-) > create mode 100644 www/window/NamespaceMove.js > > diff --git a/www/Makefile b/www/Makefile > index 7745d9f4..f2011af3 100644 > --- a/www/Makefile > +++ b/www/Makefile > @@ -80,6 +80,7 @@ JSSRC= \ > window/CreateDirectory.js \ > window/DataStoreEdit.js \ > window/NamespaceEdit.js \ > + window/NamespaceMove.js \ > window/MaintenanceOptions.js \ > window/NotesEdit.js \ > window/RemoteEdit.js \ > diff --git a/www/datastore/Content.js b/www/datastore/Content.js > index b56cc4ac..e3e0d0c9 100644 > --- a/www/datastore/Content.js > +++ b/www/datastore/Content.js > @@ -667,6 +667,26 @@ 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(); > @@ -685,6 +705,8 @@ Ext.define('PBS.DataStoreContent', { > let me = this; > if (data.ty === 'group') { > me.moveGroup(data); > + } else if (data.ty === 'ns') { > + me.moveNS(); > } > }, > > @@ -1113,10 +1135,13 @@ Ext.define('PBS.DataStoreContent', { > if (data.ty === 'group') { > return Ext.String.format(gettext("Move group '{0}'"), v); > } > - return ''; > + return Ext.String.format(gettext("Move namespace '{0}'"), v); this tooltip is shown for all entries when hovering, even when he icon is not visible, probably due to it never being disabled i think verify and remove have the same problem... > }, > 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, > diff --git a/www/form/NamespaceSelector.js b/www/form/NamespaceSelector.js > index ddf68254..d349b568 100644 > --- a/www/form/NamespaceSelector.js > +++ b/www/form/NamespaceSelector.js > @@ -90,6 +90,17 @@ Ext.define('PBS.form.NamespaceSelector', { > }, > }); > > + if (me.excludeNs) { > + me.store.addFilter( > + new Ext.util.Filter({ > + filterFn: (rec) => { > + let ns = rec.data.ns; > + return ns !== me.excludeNs && !ns.startsWith(`${me.excludeNs}/`); > + }, > + }), > + ); > + } > + > me.callParent(); > }, > }); > diff --git a/www/window/NamespaceMove.js b/www/window/NamespaceMove.js > new file mode 100644 > index 00000000..415d509d > --- /dev/null > +++ b/www/window/NamespaceMove.js > @@ -0,0 +1,79 @@ > +Ext.define('PBS.window.NamespaceMove', { > + extend: 'Proxmox.window.Edit', > + alias: 'widget.pbsNamespaceMove', > + mixins: ['Proxmox.Mixin.CBind'], > + > + onlineHelp: 'storage-namespaces', > + > + submitText: gettext('Move'), > + isCreate: true, > + showTaskViewer: true, > + > + cbind: { > + url: '/api2/extjs/admin/datastore/{datastore}/namespace', > + title: (get) => Ext.String.format(gettext("Move Namespace '{0}'"), get('namespace')), > + }, > + method: 'PUT', > + > + width: 450, > + fieldDefaults: { > + labelWidth: 120, > + }, > + > + cbindData: function (initialConfig) { > + let ns = initialConfig.namespace ?? ''; > + let parts = ns.split('/'); > + return { nsName: parts[parts.length - 1] }; > + }, > + > + // Returns the new-ns path that was submitted, for use by the caller after success. > + getNewNamespace: function () { > + let me = this; > + let parent = me.down('[name=parent]').getValue() || ''; > + let name = me.down('[name=name]').getValue(); > + return parent ? `${parent}/${name}` : name; > + },> + > + items: { > + xtype: 'inputpanel', > + onGetValues: function (values) { > + let parent = values.parent || ''; > + let newNs = parent ? `${parent}/${values.name}` : values.name; > + return { > + ns: this.up('window').namespace, > + 'new-ns': newNs, > + }; > + }, > + items: [ > + { > + xtype: 'displayfield', > + fieldLabel: gettext('Namespace'), > + cbind: { > + value: '{namespace}', > + }, > + }, > + { > + xtype: 'pbsNamespaceSelector', > + name: 'parent', > + fieldLabel: gettext('New Parent'), > + allowBlank: true, > + cbind: { > + datastore: '{datastore}', > + excludeNs: '{namespace}', > + }, > + }, > + { > + xtype: 'proxmoxtextfield', > + name: 'name', > + fieldLabel: gettext('New Name'), > + allowBlank: false, > + maxLength: 31, > + regex: PBS.Utils.SAFE_ID_RE, > + regexText: gettext("Only alpha numerical, '_' and '-' (if not at start) allowed"), > + cbind: { > + value: '{nsName}', > + }, > + }, > + ], > + }, > +});