]> git.proxmox.com Git - proxmox-backup.git/blob - www/tape/window/TapeRestore.js
tape: ui: TapeRestore: make datastore mapping selectable
[proxmox-backup.git] / www / tape / window / TapeRestore.js
1 Ext.define('PBS.TapeManagement.TapeRestoreWindow', {
2 extend: 'Proxmox.window.Edit',
3 alias: 'widget.pbsTapeRestoreWindow',
4 mixins: ['Proxmox.Mixin.CBind'],
5
6 width: 800,
7 title: gettext('Restore Media Set'),
8 url: '/api2/extjs/tape/restore',
9 method: 'POST',
10 showTaskViewer: true,
11 isCreate: true,
12
13 defaults: {
14 labelWidth: 120,
15 },
16
17 referenceHolder: true,
18
19 items: [
20 {
21 xtype: 'inputpanel',
22
23 onGetValues: function(values) {
24 let me = this;
25 let datastores = [];
26 if (values.store && values.store !== "") {
27 datastores.push(values.store);
28 delete values.store;
29 }
30
31 if (values.mapping) {
32 datastores.push(values.mapping);
33 delete values.mapping;
34 }
35
36 values.store = datastores.join(',');
37
38 return values;
39 },
40
41 column1: [
42 {
43 xtype: 'displayfield',
44 fieldLabel: gettext('Media Set'),
45 cbind: {
46 value: '{mediaset}',
47 },
48 },
49 {
50 xtype: 'displayfield',
51 fieldLabel: gettext('Media Set UUID'),
52 name: 'media-set',
53 submitValue: true,
54 cbind: {
55 value: '{uuid}',
56 },
57 },
58 {
59 xtype: 'pbsDriveSelector',
60 fieldLabel: gettext('Drive'),
61 name: 'drive',
62 },
63 ],
64
65 column2: [
66 {
67 xtype: 'pbsUserSelector',
68 name: 'notify-user',
69 fieldLabel: gettext('Notify User'),
70 emptyText: gettext('Current User'),
71 value: null,
72 allowBlank: true,
73 skipEmptyText: true,
74 renderer: Ext.String.htmlEncode,
75 },
76 {
77 xtype: 'pbsUserSelector',
78 name: 'owner',
79 fieldLabel: gettext('Owner'),
80 emptyText: gettext('Current User'),
81 value: null,
82 allowBlank: true,
83 skipEmptyText: true,
84 renderer: Ext.String.htmlEncode,
85 },
86 {
87 xtype: 'pbsDataStoreSelector',
88 fieldLabel: gettext('Datastore'),
89 reference: 'defaultDatastore',
90 name: 'store',
91 listeners: {
92 change: function(field, value) {
93 let me = this;
94 let grid = me.up('window').lookup('mappingGrid');
95 grid.setNeedStores(!value);
96 },
97 },
98 },
99 ],
100
101 columnB: [
102 {
103 fieldLabel: gettext('Datastore Mapping'),
104 labelWidth: 200,
105 hidden: true,
106 reference: 'mappingLabel',
107 xtype: 'displayfield',
108 },
109 {
110 xtype: 'pbsDataStoreMappingField',
111 reference: 'mappingGrid',
112 name: 'mapping',
113 defaultBindProperty: 'value',
114 hidden: true,
115 },
116 ],
117 },
118 ],
119
120 setDataStores: function(datastores) {
121 let me = this;
122
123 let label = me.lookup('mappingLabel');
124 let grid = me.lookup('mappingGrid');
125 let defaultField = me.lookup('defaultDatastore');
126
127 if (!datastores || datastores.length <= 1) {
128 label.setVisible(false);
129 grid.setVisible(false);
130 defaultField.setFieldLabel(gettext('Datastore'));
131 defaultField.setAllowBlank(false);
132 defaultField.setEmptyText("");
133 return;
134 }
135
136 label.setVisible(true);
137 defaultField.setFieldLabel(gettext('Default Datastore'));
138 defaultField.setAllowBlank(true);
139 defaultField.setEmptyText(Proxmox.Utils.NoneText);
140
141 grid.setDataStores(datastores);
142 grid.setVisible(true);
143 },
144
145 initComponent: function() {
146 let me = this;
147
148 me.callParent();
149 if (me.datastores) {
150 me.setDataStores(me.datastores);
151 } else {
152 // use timeout so that the window is rendered already
153 // for correct masking
154 setTimeout(function() {
155 Proxmox.Utils.API2Request({
156 waitMsgTarget: me,
157 url: `/tape/media/content?media-set=${me.uuid}`,
158 success: function(response, opt) {
159 let datastores = {};
160 for (const content of response.result.data) {
161 datastores[content.store] = true;
162 }
163 me.setDataStores(Object.keys(datastores));
164 },
165 failure: function() {
166 // ignore failing api call, maybe catalog is missing
167 me.setDataStores();
168 },
169 });
170 }, 10);
171 }
172 },
173 });
174
175 Ext.define('PBS.TapeManagement.DataStoreMappingGrid', {
176 extend: 'Ext.grid.Panel',
177 alias: 'widget.pbsDataStoreMappingField',
178 mixins: ['Ext.form.field.Field'],
179
180 getValue: function() {
181 let me = this;
182 let datastores = [];
183 me.getStore().each((rec) => {
184 let source = rec.data.source;
185 let target = rec.data.target;
186 if (target && target !== "") {
187 datastores.push(`${source}=${target}`);
188 }
189 });
190
191 return datastores.join(',');
192 },
193
194 // this determines if we need at least one valid mapping
195 needStores: false,
196
197 setNeedStores: function(needStores) {
198 let me = this;
199 me.needStores = needStores;
200 me.checkChange();
201 me.validate();
202 },
203
204 setValue: function(value) {
205 let me = this;
206 me.setDataStores(value);
207 return me;
208 },
209
210 getErrors: function(value) {
211 let me = this;
212 let error = false;
213
214 if (me.needStores) {
215 error = true;
216 me.getStore().each((rec) => {
217 if (rec.data.target) {
218 error = false;
219 }
220 });
221 }
222
223 if (error) {
224 me.addCls(['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid']);
225 let errorMsg = gettext("Need at least one mapping");
226 me.getActionEl().dom.setAttribute('data-errorqtip', errorMsg);
227
228 return [errorMsg];
229 }
230 me.removeCls(['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid']);
231 me.getActionEl().dom.setAttribute('data-errorqtip', "");
232 return [];
233 },
234
235 setDataStores: function(datastores) {
236 let me = this;
237 let store = me.getStore();
238 let data = [];
239
240 for (const datastore of datastores) {
241 data.push({
242 source: datastore,
243 target: '',
244 });
245 }
246
247 store.setData(data);
248 },
249
250 viewConfig: {
251 markDirty: false,
252 },
253
254 store: { data: [] },
255
256 columns: [
257 {
258 text: gettext('Source Datastore'),
259 dataIndex: 'source',
260 flex: 1,
261 },
262 {
263 text: gettext('Target Datastore'),
264 xtype: 'widgetcolumn',
265 dataIndex: 'target',
266 flex: 1,
267 widget: {
268 xtype: 'pbsDataStoreSelector',
269 allowBlank: true,
270 emptyText: Proxmox.Utils.NoneText,
271 listeners: {
272 change: function(selector, value) {
273 let me = this;
274 let rec = me.getWidgetRecord();
275 if (!rec) {
276 return;
277 }
278 rec.set('target', value);
279 me.up('grid').checkChange();
280 },
281 },
282 },
283 },
284 ],
285 });