a4388ffc36
we have 2 modi in that window: * backup has multiple datastores * backup has single datastore In the first case we show a 'mapping' grid so that the user can only restore a part. Here a user sees all source Datastores and can select a target for each one. In the second case we only have a single 'Datastore' selector, but we do not show the source. Because of this, the naming is slightly ambiguous (is it the 'Source' or the 'Target' ?), so rename it to 'Target Datastore'. Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
286 lines
6.0 KiB
JavaScript
286 lines
6.0 KiB
JavaScript
Ext.define('PBS.TapeManagement.TapeRestoreWindow', {
|
|
extend: 'Proxmox.window.Edit',
|
|
alias: 'widget.pbsTapeRestoreWindow',
|
|
mixins: ['Proxmox.Mixin.CBind'],
|
|
|
|
width: 800,
|
|
title: gettext('Restore Media Set'),
|
|
url: '/api2/extjs/tape/restore',
|
|
method: 'POST',
|
|
showTaskViewer: true,
|
|
isCreate: true,
|
|
|
|
defaults: {
|
|
labelWidth: 120,
|
|
},
|
|
|
|
referenceHolder: true,
|
|
|
|
items: [
|
|
{
|
|
xtype: 'inputpanel',
|
|
|
|
onGetValues: function(values) {
|
|
let me = this;
|
|
let datastores = [];
|
|
if (values.store.toString() !== "") {
|
|
datastores.push(values.store);
|
|
delete values.store;
|
|
}
|
|
|
|
if (values.mapping.toString() !== "") {
|
|
datastores.push(values.mapping);
|
|
}
|
|
delete values.mapping;
|
|
|
|
values.store = datastores.join(',');
|
|
|
|
return values;
|
|
},
|
|
|
|
column1: [
|
|
{
|
|
xtype: 'displayfield',
|
|
fieldLabel: gettext('Media Set'),
|
|
cbind: {
|
|
value: '{mediaset}',
|
|
},
|
|
},
|
|
{
|
|
xtype: 'displayfield',
|
|
fieldLabel: gettext('Media Set UUID'),
|
|
name: 'media-set',
|
|
submitValue: true,
|
|
cbind: {
|
|
value: '{uuid}',
|
|
},
|
|
},
|
|
{
|
|
xtype: 'pbsDriveSelector',
|
|
fieldLabel: gettext('Drive'),
|
|
name: 'drive',
|
|
},
|
|
],
|
|
|
|
column2: [
|
|
{
|
|
xtype: 'pbsUserSelector',
|
|
name: 'notify-user',
|
|
fieldLabel: gettext('Notify User'),
|
|
emptyText: gettext('Current User'),
|
|
value: null,
|
|
allowBlank: true,
|
|
skipEmptyText: true,
|
|
renderer: Ext.String.htmlEncode,
|
|
},
|
|
{
|
|
xtype: 'pbsUserSelector',
|
|
name: 'owner',
|
|
fieldLabel: gettext('Owner'),
|
|
emptyText: gettext('Current User'),
|
|
value: null,
|
|
allowBlank: true,
|
|
skipEmptyText: true,
|
|
renderer: Ext.String.htmlEncode,
|
|
},
|
|
{
|
|
xtype: 'pbsDataStoreSelector',
|
|
fieldLabel: gettext('Target Datastore'),
|
|
reference: 'defaultDatastore',
|
|
name: 'store',
|
|
listeners: {
|
|
change: function(field, value) {
|
|
let me = this;
|
|
let grid = me.up('window').lookup('mappingGrid');
|
|
grid.setNeedStores(!value);
|
|
},
|
|
},
|
|
},
|
|
],
|
|
|
|
columnB: [
|
|
{
|
|
fieldLabel: gettext('Datastore Mapping'),
|
|
labelWidth: 200,
|
|
hidden: true,
|
|
reference: 'mappingLabel',
|
|
xtype: 'displayfield',
|
|
},
|
|
{
|
|
xtype: 'pbsDataStoreMappingField',
|
|
reference: 'mappingGrid',
|
|
name: 'mapping',
|
|
defaultBindProperty: 'value',
|
|
hidden: true,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
|
|
setDataStores: function(datastores) {
|
|
let me = this;
|
|
|
|
let label = me.lookup('mappingLabel');
|
|
let grid = me.lookup('mappingGrid');
|
|
let defaultField = me.lookup('defaultDatastore');
|
|
|
|
if (!datastores || datastores.length <= 1) {
|
|
label.setVisible(false);
|
|
grid.setVisible(false);
|
|
defaultField.setFieldLabel(gettext('Target Datastore'));
|
|
defaultField.setAllowBlank(false);
|
|
defaultField.setEmptyText("");
|
|
return;
|
|
}
|
|
|
|
label.setVisible(true);
|
|
defaultField.setFieldLabel(gettext('Default Datastore'));
|
|
defaultField.setAllowBlank(true);
|
|
defaultField.setEmptyText(Proxmox.Utils.NoneText);
|
|
|
|
grid.setDataStores(datastores);
|
|
grid.setVisible(true);
|
|
},
|
|
|
|
initComponent: function() {
|
|
let me = this;
|
|
|
|
me.callParent();
|
|
if (me.datastores) {
|
|
me.setDataStores(me.datastores);
|
|
} else {
|
|
// use timeout so that the window is rendered already
|
|
// for correct masking
|
|
setTimeout(function() {
|
|
Proxmox.Utils.API2Request({
|
|
waitMsgTarget: me,
|
|
url: `/tape/media/content?media-set=${me.uuid}`,
|
|
success: function(response, opt) {
|
|
let datastores = {};
|
|
for (const content of response.result.data) {
|
|
datastores[content.store] = true;
|
|
}
|
|
me.setDataStores(Object.keys(datastores));
|
|
},
|
|
failure: function() {
|
|
// ignore failing api call, maybe catalog is missing
|
|
me.setDataStores();
|
|
},
|
|
});
|
|
}, 10);
|
|
}
|
|
},
|
|
});
|
|
|
|
Ext.define('PBS.TapeManagement.DataStoreMappingGrid', {
|
|
extend: 'Ext.grid.Panel',
|
|
alias: 'widget.pbsDataStoreMappingField',
|
|
mixins: ['Ext.form.field.Field'],
|
|
|
|
getValue: function() {
|
|
let me = this;
|
|
let datastores = [];
|
|
me.getStore().each((rec) => {
|
|
let source = rec.data.source;
|
|
let target = rec.data.target;
|
|
if (target && target !== "") {
|
|
datastores.push(`${source}=${target}`);
|
|
}
|
|
});
|
|
|
|
return datastores.join(',');
|
|
},
|
|
|
|
// this determines if we need at least one valid mapping
|
|
needStores: false,
|
|
|
|
setNeedStores: function(needStores) {
|
|
let me = this;
|
|
me.needStores = needStores;
|
|
me.checkChange();
|
|
me.validate();
|
|
},
|
|
|
|
setValue: function(value) {
|
|
let me = this;
|
|
me.setDataStores(value);
|
|
return me;
|
|
},
|
|
|
|
getErrors: function(value) {
|
|
let me = this;
|
|
let error = false;
|
|
|
|
if (me.needStores) {
|
|
error = true;
|
|
me.getStore().each((rec) => {
|
|
if (rec.data.target) {
|
|
error = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
if (error) {
|
|
me.addCls(['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid']);
|
|
let errorMsg = gettext("Need at least one mapping");
|
|
me.getActionEl().dom.setAttribute('data-errorqtip', errorMsg);
|
|
|
|
return [errorMsg];
|
|
}
|
|
me.removeCls(['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid']);
|
|
me.getActionEl().dom.setAttribute('data-errorqtip', "");
|
|
return [];
|
|
},
|
|
|
|
setDataStores: function(datastores) {
|
|
let me = this;
|
|
let store = me.getStore();
|
|
let data = [];
|
|
|
|
for (const datastore of datastores) {
|
|
data.push({
|
|
source: datastore,
|
|
target: '',
|
|
});
|
|
}
|
|
|
|
store.setData(data);
|
|
},
|
|
|
|
viewConfig: {
|
|
markDirty: false,
|
|
},
|
|
|
|
store: { data: [] },
|
|
|
|
columns: [
|
|
{
|
|
text: gettext('Source Datastore'),
|
|
dataIndex: 'source',
|
|
flex: 1,
|
|
},
|
|
{
|
|
text: gettext('Target Datastore'),
|
|
xtype: 'widgetcolumn',
|
|
dataIndex: 'target',
|
|
flex: 1,
|
|
widget: {
|
|
xtype: 'pbsDataStoreSelector',
|
|
allowBlank: true,
|
|
emptyText: Proxmox.Utils.NoneText,
|
|
listeners: {
|
|
change: function(selector, value) {
|
|
let me = this;
|
|
let rec = me.getWidgetRecord();
|
|
if (!rec) {
|
|
return;
|
|
}
|
|
rec.set('target', value);
|
|
me.up('grid').checkChange();
|
|
},
|
|
},
|
|
},
|
|
},
|
|
],
|
|
});
|