]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/storage/ContentView.js
ui: storage content view: eslint fixes and code cleanup
[pve-manager.git] / www / manager6 / storage / ContentView.js
CommitLineData
4a580e60
DM
1Ext.define('PVE.storage.Upload', {
2 extend: 'Ext.window.Window',
3f90858a 3 alias: 'widget.pveStorageUpload',
4a580e60
DM
4
5 resizable: false,
6
7 modal: true,
8
1d8306e4 9 initComponent: function() {
4a580e60
DM
10 var me = this;
11
4a580e60
DM
12 if (!me.nodename) {
13 throw "no node name specified";
14 }
177de3de 15 if (!me.storage) {
4a580e60
DM
16 throw "no storage ID specified";
17 }
18
1d8306e4 19 let baseurl = `/nodes/${me.nodename}/storage/${me.storage}/upload`;
4a580e60 20
1d8306e4 21 let pbar = Ext.create('Ext.ProgressBar', {
4a580e60 22 text: 'Ready',
1d8306e4 23 hidden: true,
4a580e60
DM
24 });
25
c128543f
FE
26 let acceptedExtensions = {
27 iso: ".img, .iso",
28 vztmpl: ".tar.gz, .tar.xz",
29 };
30
31 let defaultContent = me.contents[0] || '';
32
33 let fileField = Ext.create('Ext.form.field.File', {
34 name: 'filename',
35 buttonText: gettext('Select File...'),
36 allowBlank: false,
37 setAccept: function(content) {
38 let acceptString = acceptedExtensions[content] || '';
39 this.fileInputEl.set({
40 accept: acceptString,
41 });
42 },
43 listeners: {
44 afterrender: function(cmp) {
45 cmp.setAccept(defaultContent);
46 },
47 },
48 });
49
4a580e60
DM
50 me.formPanel = Ext.create('Ext.form.Panel', {
51 method: 'POST',
52 waitMsgTarget: true,
53 bodyPadding: 10,
54 border: false,
55 width: 300,
56 fieldDefaults: {
57 labelWidth: 100,
1d8306e4 58 anchor: '100%',
4a580e60
DM
59 },
60 items: [
61 {
62 xtype: 'pveContentTypeSelector',
6c1a2b86 63 cts: me.contents,
4a580e60
DM
64 fieldLabel: gettext('Content'),
65 name: 'content',
c128543f 66 value: defaultContent,
518a3974
SR
67 allowBlank: false,
68 listeners: {
c128543f
FE
69 change: function(cmp, newValue, oldValue) {
70 fileField.setAccept(newValue);
71 },
72 },
4a580e60 73 },
c128543f 74 fileField,
1d8306e4
TL
75 pbar,
76 ],
4a580e60
DM
77 });
78
1d8306e4 79 let form = me.formPanel.getForm();
4a580e60 80
1d8306e4 81 let doStandardSubmit = function() {
4a580e60
DM
82 form.submit({
83 url: "/api2/htmljs" + baseurl,
84 waitMsg: gettext('Uploading file...'),
85 success: function(f, action) {
86 me.close();
87 },
88 failure: function(f, action) {
89 var msg = PVE.Utils.extractFormActionError(action);
90 Ext.Msg.alert(gettext('Error'), msg);
1d8306e4 91 },
4a580e60
DM
92 });
93 };
94
1d8306e4 95 let updateProgress = function(per, bytes) {
4a580e60
DM
96 var text = (per * 100).toFixed(2) + '%';
97 if (bytes) {
e7ade592 98 text += " (" + Proxmox.Utils.format_size(bytes) + ')';
4a580e60
DM
99 }
100 pbar.updateProgress(per, text);
101 };
177de3de 102
1d8306e4 103 let abortBtn = Ext.create('Ext.Button', {
4a580e60
DM
104 text: gettext('Abort'),
105 disabled: true,
106 handler: function() {
107 me.close();
1d8306e4 108 },
4a580e60
DM
109 });
110
1d8306e4 111 let submitBtn = Ext.create('Ext.Button', {
4a580e60
DM
112 text: gettext('Upload'),
113 disabled: true,
114 handler: function(button) {
115 var fd;
116 try {
117 fd = new FormData();
118 } catch (err) {
119 doStandardSubmit();
120 return;
121 }
122
123 button.setDisabled(true);
124 abortBtn.setDisabled(false);
125
126 var field = form.findField('content');
127 fd.append("content", field.getValue());
128 field.setDisabled(true);
129
130 field = form.findField('filename');
131 var file = field.fileInputEl.dom;
132 fd.append("filename", file.files[0]);
133 field.setDisabled(true);
134
135 pbar.setVisible(true);
136 updateProgress(0);
137
1d8306e4
TL
138 let xhr = new XMLHttpRequest();
139 me.xhr = xhr;
4a580e60 140
177de3de 141 xhr.addEventListener("load", function(e) {
1d8306e4 142 if (xhr.status === 200) {
4a580e60 143 me.close();
1d8306e4
TL
144 return;
145 }
146 let err = Ext.htmlEncode(xhr.statusText);
147 let msg = `${gettext('Error')} ${xhr.status.toString()}: ${err}`;
148 if (xhr.responseText !== "") {
149 let result = Ext.decode(xhr.responseText);
150 result.message = msg;
151 msg = Proxmox.Utils.extractRequestError(result, true);
177de3de 152 }
1d8306e4 153 Ext.Msg.alert(gettext('Error'), msg, btn => me.close());
4a580e60
DM
154 }, false);
155
156 xhr.addEventListener("error", function(e) {
1d8306e4
TL
157 let err = e.target.status.toString();
158 let msg = `Error '${err}' occurred while receiving the document.`;
159 Ext.Msg.alert(gettext('Error'), msg, btn => me.close());
4a580e60 160 });
177de3de 161
4a580e60 162 xhr.upload.addEventListener("progress", function(evt) {
177de3de 163 if (evt.lengthComputable) {
1d8306e4 164 let percentComplete = evt.loaded / evt.total;
4a580e60 165 updateProgress(percentComplete, evt.loaded);
177de3de 166 }
4a580e60
DM
167 }, false);
168
1d8306e4 169 xhr.open("POST", `/api2/json${baseurl}`, true);
177de3de 170 xhr.send(fd);
1d8306e4 171 },
4a580e60
DM
172 });
173
1d8306e4 174 form.on('validitychange', (f, valid) => submitBtn.setDisabled(!valid));
4a580e60 175
1d8306e4
TL
176 Ext.apply(me, {
177 title: gettext('Upload'),
4a580e60 178 items: me.formPanel,
1d8306e4 179 buttons: [abortBtn, submitBtn],
4a580e60
DM
180 listeners: {
181 close: function() {
1d8306e4
TL
182 if (me.xhr) {
183 me.xhr.abort();
184 delete me.xhr;
4a580e60 185 }
1d8306e4
TL
186 },
187 },
4a580e60
DM
188 });
189
190 me.callParent();
1d8306e4 191 },
4a580e60
DM
192});
193
194Ext.define('PVE.storage.ContentView', {
195 extend: 'Ext.grid.GridPanel',
196
3f90858a 197 alias: 'widget.pveStorageContentView',
4a580e60 198
c90539de
DC
199 viewConfig: {
200 trackOver: false,
1d8306e4 201 loadMask: false,
c90539de 202 },
1d8306e4 203 initComponent: function() {
4a580e60
DM
204 var me = this;
205
dbeddeb3
FE
206 if (!me.nodename) {
207 me.nodename = me.pveSelNode.data.node;
208 if (!me.nodename) {
209 throw "no node name specified";
210 }
4a580e60 211 }
1d8306e4 212 const nodename = me.nodename;
4a580e60 213
dbeddeb3
FE
214 if (!me.storage) {
215 me.storage = me.pveSelNode.data.storage;
216 if (!me.storage) {
217 throw "no storage ID specified";
218 }
4a580e60 219 }
1d8306e4 220 const storage = me.storage;
4a580e60 221
e8b422bc
FE
222 var content = me.content;
223 if (!content) {
224 throw "no content type specified";
225 }
226
1d8306e4
TL
227 const baseurl = `/nodes/${nodename}/storage/${storage}/content`;
228 let store = me.store = Ext.create('Ext.data.Store', {
4a580e60 229 model: 'pve-storage-content',
4a580e60 230 proxy: {
56a353b9 231 type: 'proxmox',
e8b422bc
FE
232 url: '/api2/json' + baseurl,
233 extraParams: {
234 content: content,
235 },
4a580e60 236 },
177de3de
DC
237 sorters: {
238 property: 'volid',
1d8306e4
TL
239 order: 'DESC',
240 },
4a580e60
DM
241 });
242
dbeddeb3
FE
243 if (!me.sm) {
244 me.sm = Ext.create('Ext.selection.RowModel', {});
245 }
1d8306e4 246 let sm = me.sm;
4a580e60 247
1d8306e4 248 let reload = () => store.load();
4a580e60 249
e7ade592 250 Proxmox.Utils.monStoreErrors(me, store);
4a580e60 251
1d8306e4 252 let uploadButton = Ext.create('Proxmox.button.Button', {
6c1a2b86
DC
253 text: gettext('Upload'),
254 handler: function() {
1d8306e4 255 let win = Ext.create('PVE.storage.Upload', {
6c1a2b86
DC
256 nodename: nodename,
257 storage: storage,
8798c35b 258 contents: [content],
6c1a2b86
DC
259 });
260 win.show();
261 win.on('destroy', reload);
1d8306e4 262 },
6c1a2b86
DC
263 });
264
1d8306e4 265 let removeButton = Ext.create('Proxmox.button.StdRemoveButton', {
c5e224fc 266 selModel: sm,
4d23cdef 267 delay: 5,
c5e224fc
WL
268 callback: function() {
269 reload();
270 },
1d8306e4 271 baseurl: baseurl + '/',
c5e224fc
WL
272 });
273
9ce0c258
FE
274 if (!me.tbar) {
275 me.tbar = [];
276 }
8798c35b
FE
277 if (me.useUploadButton) {
278 me.tbar.push(uploadButton);
279 }
f5e17f15
FE
280 if (!me.useCustomRemoveButton) {
281 me.tbar.push(removeButton);
282 }
9ce0c258 283 me.tbar.push(
9ce0c258 284 '->',
1d8306e4
TL
285 gettext('Search') + ':',
286 ' ',
9ce0c258
FE
287 {
288 xtype: 'textfield',
289 width: 200,
290 enableKeyEvents: true,
291 listeners: {
292 buffer: 500,
293 keyup: function(field) {
294 store.clearFilter(true);
295 store.filter([
296 {
297 property: 'text',
298 value: field.getValue(),
299 anyMatch: true,
300 caseSensitive: false
301 }
302 ]);
4a580e60
DM
303 }
304 }
9ce0c258
FE
305 }
306 );
307
6ecc7420 308 let availableColumns = {
7ef0f4c3
FE
309 'name': {
310 header: gettext('Name'),
311 flex: 2,
312 sortable: true,
313 renderer: PVE.Utils.render_storage_content,
1d8306e4 314 dataIndex: 'text',
7ef0f4c3
FE
315 },
316 'comment': {
317 header: gettext('Comment'),
318 flex: 1,
319 renderer: Ext.htmlEncode,
320 dataIndex: 'comment',
321 },
322 'date': {
323 header: gettext('Date'),
324 width: 150,
1d8306e4 325 dataIndex: 'vdate',
7ef0f4c3
FE
326 },
327 'format': {
328 header: gettext('Format'),
329 width: 100,
1d8306e4 330 dataIndex: 'format',
7ef0f4c3
FE
331 },
332 'size': {
333 header: gettext('Size'),
334 width: 100,
335 renderer: Proxmox.Utils.format_size,
1d8306e4 336 dataIndex: 'size',
7ef0f4c3
FE
337 },
338 };
339
6ecc7420
TL
340 if (me.hideColumns) {
341 me.hideColumns.forEach(key => delete availableColumns[key]);
342 } else if (!me.hasCommentColumn) {
343 delete availableColumns.comment;
7ef0f4c3 344 }
6ecc7420 345 const columns = Object.values(availableColumns);
7ef0f4c3 346
9ce0c258
FE
347 Ext.apply(me, {
348 store: store,
349 selModel: sm,
350 tbar: me.tbar,
7ef0f4c3 351 columns: columns,
4a580e60 352 listeners: {
1d8306e4
TL
353 activate: reload,
354 },
4a580e60
DM
355 });
356
357 me.callParent();
1d8306e4 358 },
4a580e60 359}, function() {
4a580e60
DM
360 Ext.define('pve-storage-content', {
361 extend: 'Ext.data.Model',
177de3de
DC
362 fields: [
363 'volid', 'content', 'format', 'size', 'used', 'vmid',
957a53bd 364 'channel', 'id', 'lun', 'comment', 'verification',
177de3de
DC
365 {
366 name: 'text',
4a580e60 367 convert: function(value, record) {
86cc7049
DC
368 // check for volid, because if you click on a grouping header,
369 // it calls convert (but with an empty volid)
370 if (value || record.data.volid === null) {
4a580e60
DM
371 return value;
372 }
373 return PVE.Utils.render_storage_content(value, {}, record);
1d8306e4 374 },
12d50fcd
TL
375 },
376 {
377 name: 'vdate',
378 convert: function(value, record) {
379 // check for volid, because if you click on a grouping header,
380 // it calls convert (but with an empty volid)
381 if (value || record.data.volid === null) {
382 return value;
383 }
384 let t = record.data.content;
385 if (t === "backup") {
386 let v = record.data.volid;
387 let match = v.match(/(\d{4}_\d{2}_\d{2})-(\d{2}_\d{2}_\d{2})/);
388 if (match) {
a4a86fe9 389 let date = match[1].replace(/_/g, '-');
12d50fcd
TL
390 let time = match[2].replace(/_/g, ':');
391 return date + " " + time;
392 }
393 }
a4a86fe9
DM
394 if (record.data.ctime) {
395 let ctime = new Date(record.data.ctime * 1000);
1d8306e4 396 return Ext.Date.format(ctime, 'Y-m-d H:i:s');
a4a86fe9 397 }
12d50fcd 398 return '';
1d8306e4 399 },
12d50fcd 400 },
4a580e60 401 ],
1d8306e4 402 idProperty: 'volid',
4a580e60 403 });
4a580e60 404});