ui: tape/Restore: allow simple namespace mapping

add a default namespace selector (of the current default store)
and a namespace selector per target datastore (for media-sets with
multiple datastores).

to achieve that we have to change the way we handle the mapping field a bit:
* don't use it as field directly (otherwise the value gets stringified),
  but use the 'getValue' method in 'onGetValues'.
* set the defaultStore there, not only that we have one
  (with this we can now easily show it as emptytext for each store)
* add a reference to the widgets to the record so that we can access
  them in the respective change handler (also clean those references up,
  else we have a cyclic reference between record <-> widget)

in onGetValues, if we have multiple datastores, the mapping grid does
all the work for us, otherwise, we have to create the ns mapping
ourselves there.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
This commit is contained in:
Dominik Csapak 2022-05-13 13:44:30 +02:00
parent f6b09e83cb
commit b70a12e723
2 changed files with 118 additions and 14 deletions

View File

@ -66,6 +66,9 @@ Ext.define('PBS.form.NamespaceSelector', {
} }
me.store.load(); me.store.load();
me.validate(); me.validate();
} else {
me.datastore = undefined;
me.setDisabled(true);
} }
}, },

View File

@ -38,6 +38,8 @@ Ext.define('PBS.TapeManagement.TapeRestoreWindow', {
singleSelectorLabel: get => singleSelectorLabel: get =>
get('singleDatastore') ? gettext('Target Datastore') : gettext('Default Datastore'), get('singleDatastore') ? gettext('Target Datastore') : gettext('Default Datastore'),
singleSelectorEmptyText: get => get('singleDatastore') ? '' : Proxmox.Utils.NoneText, singleSelectorEmptyText: get => get('singleDatastore') ? '' : Proxmox.Utils.NoneText,
singleSelectorLabelNs: get =>
get('singleDatastore') ? gettext('Target Namespace') : gettext('Default Namespace'),
}, },
}, },
@ -371,16 +373,29 @@ Ext.define('PBS.TapeManagement.TapeRestoreWindow', {
xtype: 'inputpanel', xtype: 'inputpanel',
onGetValues: function(values) { onGetValues: function(values) {
let me = this; let me = this;
let controller = me.up('window').getController();
let datastores = []; let datastores = [];
if (values.store.toString() !== "") { if (values.store.toString() !== "") {
datastores.push(values.store); datastores.push(values.store);
delete values.store; delete values.store;
} }
if (values.mapping.toString() !== "") { let defaultNs = values.defaultNs;
datastores.push(values.mapping); delete values.defaultNs;
let [ds_map, ns_map] = me.down('pbsDataStoreMappingField').getValue();
if (ds_map !== '') {
datastores.push(ds_map);
} }
delete values.mapping; if (ns_map.length > 0) {
values.namespaces = ns_map;
}
if (defaultNs && ns_map.length === 0 && controller.datastores.length === 1) {
// we only have one datastore and a default ns
values.namespaces = [`store=${controller.datastores[0]},target=${defaultNs}`];
}
values.store = datastores.join(','); values.store = datastores.join(',');
@ -427,7 +442,22 @@ Ext.define('PBS.TapeManagement.TapeRestoreWindow', {
}, },
listeners: { listeners: {
change: function(field, value) { change: function(field, value) {
this.up('window').lookup('mappingGrid').setNeedStores(!value); this.up('window').lookup('mappingGrid').setDefaultStore(value);
this.up('window').lookup('defaultNs').setDatastore(value);
},
},
},
{
xtype: 'pbsNamespaceSelector',
name: 'defaultNs',
reference: 'defaultNs',
labelWidth: 120,
bind: {
fieldLabel: '{singleSelectorLabelNs}',
},
listeners: {
change: function(field, value) {
this.up('window').lookup('mappingGrid').setDefaultNs(value);
}, },
}, },
}, },
@ -444,9 +474,9 @@ Ext.define('PBS.TapeManagement.TapeRestoreWindow', {
}, },
{ {
xtype: 'pbsDataStoreMappingField', xtype: 'pbsDataStoreMappingField',
name: 'mapping', isFormField: false,
reference: 'mappingGrid', reference: 'mappingGrid',
height: 260, height: 240,
defaultBindProperty: 'value', defaultBindProperty: 'value',
bind: { bind: {
hidden: '{singleDatastore}', hidden: '{singleDatastore}',
@ -473,28 +503,54 @@ Ext.define('PBS.TapeManagement.DataStoreMappingGrid', {
getValue: function() { getValue: function() {
let me = this; let me = this;
let datastores = []; let datastores = [];
let namespaces = [];
let defaultStore = me.getViewModel().get('defaultStore');
let defaultNs = me.getViewModel().get('defaultNs');
me.getStore().each(rec => { me.getStore().each(rec => {
let { source, target } = rec.data; let { source, target, targetns } = rec.data;
if (target && target !== "") { if (target && target !== "") {
datastores.push(`${source}=${target}`); datastores.push(`${source}=${target}`);
} }
if (target || defaultStore) {
let ns = targetns || defaultNs;
if (ns) {
namespaces.push(`store=${source},target=${ns}`);
}
}
}); });
return datastores.join(','); return [datastores.join(','), namespaces];
}, },
viewModel: { viewModel: {
data: { data: {
needStores: false, // this determines if we need at least one valid mapping defaultStore: '',
defaultNs: false,
}, },
formulas: { formulas: {
emptyMeans: get => get('needStores') ? Proxmox.Utils.NoneText : Proxmox.Utils.defaultText, emptyStore: get => get('defaultStore') || Proxmox.Utils.NoneText,
emptyNs: get => get('defaultNs') || gettext('Root'),
}, },
}, },
setNeedStores: function(needStores) { setDefaultStore: function(store) {
let me = this; let me = this;
me.getViewModel().set('needStores', needStores); me.getViewModel().set('defaultStore', store);
me.getStore().each((rec) => {
if (!rec.dswidget) {
return; // not yet attached
}
if (!rec.dswidget.getValue()) {
rec.nswidget.setDatastore(store);
}
});
me.checkChange();
me.validate();
},
setDefaultNs: function(defaultNs) {
let me = this;
me.getViewModel().set('defaultNs', defaultNs);
me.checkChange(); me.checkChange();
me.validate(); me.validate();
}, },
@ -509,7 +565,7 @@ Ext.define('PBS.TapeManagement.DataStoreMappingGrid', {
let me = this; let me = this;
let error = false; let error = false;
if (me.getViewModel().get('needStores')) { if (me.getViewModel().get('defaultStore') !== '') {
error = true; error = true;
me.getStore().each(rec => { me.getStore().each(rec => {
if (rec.data.target) { if (rec.data.target) {
@ -543,6 +599,7 @@ Ext.define('PBS.TapeManagement.DataStoreMappingGrid', {
data.push({ data.push({
source: datastore, source: datastore,
target: '', target: '',
targetNs: '',
}); });
} }
@ -555,6 +612,16 @@ Ext.define('PBS.TapeManagement.DataStoreMappingGrid', {
store: { data: [] }, store: { data: [] },
listeners: {
beforedestroy: function() {
// break cyclic reference
this.getStore()?.each((rec) => {
delete rec.nswidget;
delete rec.dswidget;
});
},
},
columns: [ columns: [
{ {
text: gettext('Source Datastore'), text: gettext('Source Datastore'),
@ -564,6 +631,10 @@ Ext.define('PBS.TapeManagement.DataStoreMappingGrid', {
{ {
text: gettext('Target Datastore'), text: gettext('Target Datastore'),
xtype: 'widgetcolumn', xtype: 'widgetcolumn',
onWidgetAttach: function(col, widget, rec) {
// so that we can access it from the store
rec.dswidget = widget;
},
dataIndex: 'target', dataIndex: 'target',
flex: 1, flex: 1,
widget: { widget: {
@ -571,7 +642,7 @@ Ext.define('PBS.TapeManagement.DataStoreMappingGrid', {
isFormField: false, isFormField: false,
allowBlank: true, allowBlank: true,
bind: { bind: {
emptyText: '{emptyMeans}', emptyText: '{emptyStore}',
}, },
listeners: { listeners: {
change: function(selector, value) { change: function(selector, value) {
@ -581,6 +652,36 @@ Ext.define('PBS.TapeManagement.DataStoreMappingGrid', {
return; return;
} }
rec.set('target', value); rec.set('target', value);
rec.nswidget.setDatastore(value);
me.up('grid').checkChange();
},
},
},
},
{
text: gettext('Target Namespace'),
xtype: 'widgetcolumn',
onWidgetAttach: function(col, widget, rec) {
// so that we can access it from the store
rec.nswidget = widget;
},
dataIndex: 'targetns',
flex: 1,
widget: {
xtype: 'pbsNamespaceSelector',
isFormField: false,
allowBlank: true,
bind: {
emptyText: '{emptyNs}',
},
listeners: {
change: function(selector, value) {
let me = this;
let rec = me.getWidgetRecord();
if (!rec) {
return;
}
rec.set('targetns', value);
me.up('grid').checkChange(); me.up('grid').checkChange();
}, },
}, },