]>
Commit | Line | Data |
---|---|---|
4a580e60 DM |
1 | Ext.define('PVE.grid.TemplateSelector', { |
2 | extend: 'Ext.grid.GridPanel', | |
3 | ||
3f90858a | 4 | alias: 'widget.pveTemplateSelector', |
4a580e60 | 5 | |
1b14c875 DC |
6 | stateful: true, |
7 | stateId: 'grid-template-selector', | |
3b422683 DC |
8 | viewConfig: { |
9 | trackOver: false | |
10 | }, | |
4a580e60 DM |
11 | initComponent : function() { |
12 | var me = this; | |
13 | ||
14 | if (!me.nodename) { | |
15 | throw "no node name specified"; | |
16 | } | |
17 | ||
18 | var baseurl = "/nodes/" + me.nodename + "/aplinfo"; | |
19 | var store = new Ext.data.Store({ | |
20 | model: 'pve-aplinfo', | |
21 | groupField: 'section', | |
22 | proxy: { | |
23 | type: 'pve', | |
24 | url: '/api2/json' + baseurl | |
25 | } | |
26 | }); | |
27 | ||
28 | var sm = Ext.create('Ext.selection.RowModel', {}); | |
29 | ||
30 | var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{ | |
31 | groupHeaderTpl: '{[ "Section: " + values.name ]} ({rows.length} Item{[values.rows.length > 1 ? "s" : ""]})' | |
32 | }); | |
33 | ||
34 | var reload = function() { | |
35 | store.load(); | |
36 | }; | |
37 | ||
e7ade592 | 38 | Proxmox.Utils.monStoreErrors(me, store); |
4a580e60 DM |
39 | |
40 | Ext.apply(me, { | |
41 | store: store, | |
42 | selModel: sm, | |
4a580e60 DM |
43 | features: [ groupingFeature ], |
44 | columns: [ | |
45 | { | |
46 | header: gettext('Type'), | |
47 | width: 80, | |
48 | dataIndex: 'type' | |
49 | }, | |
50 | { | |
51 | header: gettext('Package'), | |
52 | flex: 1, | |
53 | dataIndex: 'package' | |
54 | }, | |
55 | { | |
56 | header: gettext('Version'), | |
57 | width: 80, | |
58 | dataIndex: 'version' | |
59 | }, | |
60 | { | |
61 | header: gettext('Description'), | |
62 | flex: 1.5, | |
91535f2b | 63 | renderer: Ext.String.htmlEncode, |
4a580e60 DM |
64 | dataIndex: 'headline' |
65 | } | |
66 | ], | |
67 | listeners: { | |
68 | afterRender: reload | |
69 | } | |
70 | }); | |
71 | ||
72 | me.callParent(); | |
73 | } | |
74 | ||
75 | }, function() { | |
76 | ||
77 | Ext.define('pve-aplinfo', { | |
78 | extend: 'Ext.data.Model', | |
177de3de DC |
79 | fields: [ |
80 | 'template', 'type', 'package', 'version', 'headline', 'infopage', | |
4a580e60 DM |
81 | 'description', 'os', 'section' |
82 | ], | |
83 | idProperty: 'template' | |
84 | }); | |
85 | ||
86 | }); | |
87 | ||
88 | Ext.define('PVE.storage.TemplateDownload', { | |
89 | extend: 'Ext.window.Window', | |
3f90858a | 90 | alias: 'widget.pveTemplateDownload', |
4a580e60 DM |
91 | |
92 | modal: true, | |
3b422683 DC |
93 | title: gettext('Templates'), |
94 | layout: 'fit', | |
95 | width: 600, | |
96 | height: 400, | |
4a580e60 DM |
97 | initComponent : function() { |
98 | /*jslint confusion: true */ | |
99 | var me = this; | |
100 | ||
101 | var grid = Ext.create('PVE.grid.TemplateSelector', { | |
102 | border: false, | |
3b422683 | 103 | scrollable: true, |
4a580e60 DM |
104 | nodename: me.nodename |
105 | }); | |
106 | ||
107 | var sm = grid.getSelectionModel(); | |
108 | ||
5720fafa | 109 | var submitBtn = Ext.create('Proxmox.button.Button', { |
4a580e60 DM |
110 | text: gettext('Download'), |
111 | disabled: true, | |
112 | selModel: sm, | |
113 | handler: function(button, event, rec) { | |
e7ade592 | 114 | Proxmox.Utils.API2Request({ |
4a580e60 | 115 | url: '/nodes/' + me.nodename + '/aplinfo', |
177de3de DC |
116 | params: { |
117 | storage: me.storage, | |
4a580e60 DM |
118 | template: rec.data.template |
119 | }, | |
120 | method: 'POST', | |
121 | failure: function (response, opts) { | |
122 | Ext.Msg.alert(gettext('Error'), response.htmlStatus); | |
123 | }, | |
124 | success: function(response, options) { | |
125 | var upid = response.result.data; | |
177de3de | 126 | |
8cbc11a7 | 127 | Ext.create('Proxmox.window.TaskViewer', { |
7e06d55d EK |
128 | upid: upid, |
129 | listeners: { | |
130 | destroy: me.reloadGrid | |
131 | } | |
132 | }).show(); | |
133 | ||
4a580e60 DM |
134 | me.close(); |
135 | } | |
136 | }); | |
137 | } | |
138 | }); | |
139 | ||
3b422683 | 140 | Ext.apply(me, { |
4a580e60 DM |
141 | items: grid, |
142 | buttons: [ submitBtn ] | |
143 | }); | |
144 | ||
145 | me.callParent(); | |
146 | } | |
147 | }); | |
148 | ||
149 | Ext.define('PVE.storage.Upload', { | |
150 | extend: 'Ext.window.Window', | |
3f90858a | 151 | alias: 'widget.pveStorageUpload', |
4a580e60 DM |
152 | |
153 | resizable: false, | |
154 | ||
155 | modal: true, | |
156 | ||
157 | initComponent : function() { | |
158 | /*jslint confusion: true */ | |
159 | var me = this; | |
160 | ||
161 | var xhr; | |
162 | ||
163 | if (!me.nodename) { | |
164 | throw "no node name specified"; | |
165 | } | |
166 | ||
177de3de | 167 | if (!me.storage) { |
4a580e60 DM |
168 | throw "no storage ID specified"; |
169 | } | |
170 | ||
171 | var baseurl = "/nodes/" + me.nodename + "/storage/" + me.storage + "/upload"; | |
172 | ||
173 | var pbar = Ext.create('Ext.ProgressBar', { | |
174 | text: 'Ready', | |
175 | hidden: true | |
176 | }); | |
177 | ||
178 | me.formPanel = Ext.create('Ext.form.Panel', { | |
179 | method: 'POST', | |
180 | waitMsgTarget: true, | |
181 | bodyPadding: 10, | |
182 | border: false, | |
183 | width: 300, | |
184 | fieldDefaults: { | |
185 | labelWidth: 100, | |
186 | anchor: '100%' | |
187 | }, | |
188 | items: [ | |
189 | { | |
190 | xtype: 'pveContentTypeSelector', | |
6c1a2b86 | 191 | cts: me.contents, |
4a580e60 DM |
192 | fieldLabel: gettext('Content'), |
193 | name: 'content', | |
6c1a2b86 DC |
194 | value: me.contents[0] || '', |
195 | allowBlank: false | |
4a580e60 DM |
196 | }, |
197 | { | |
198 | xtype: 'filefield', | |
199 | name: 'filename', | |
200 | buttonText: gettext('Select File...'), | |
22f2f9d6 | 201 | allowBlank: false |
4a580e60 DM |
202 | }, |
203 | pbar | |
204 | ] | |
205 | }); | |
206 | ||
207 | var form = me.formPanel.getForm(); | |
208 | ||
209 | var doStandardSubmit = function() { | |
210 | form.submit({ | |
211 | url: "/api2/htmljs" + baseurl, | |
212 | waitMsg: gettext('Uploading file...'), | |
213 | success: function(f, action) { | |
214 | me.close(); | |
215 | }, | |
216 | failure: function(f, action) { | |
217 | var msg = PVE.Utils.extractFormActionError(action); | |
218 | Ext.Msg.alert(gettext('Error'), msg); | |
219 | } | |
220 | }); | |
221 | }; | |
222 | ||
223 | var updateProgress = function(per, bytes) { | |
224 | var text = (per * 100).toFixed(2) + '%'; | |
225 | if (bytes) { | |
e7ade592 | 226 | text += " (" + Proxmox.Utils.format_size(bytes) + ')'; |
4a580e60 DM |
227 | } |
228 | pbar.updateProgress(per, text); | |
229 | }; | |
177de3de | 230 | |
4a580e60 DM |
231 | var abortBtn = Ext.create('Ext.Button', { |
232 | text: gettext('Abort'), | |
233 | disabled: true, | |
234 | handler: function() { | |
235 | me.close(); | |
236 | } | |
237 | }); | |
238 | ||
239 | var submitBtn = Ext.create('Ext.Button', { | |
240 | text: gettext('Upload'), | |
241 | disabled: true, | |
242 | handler: function(button) { | |
243 | var fd; | |
244 | try { | |
245 | fd = new FormData(); | |
246 | } catch (err) { | |
247 | doStandardSubmit(); | |
248 | return; | |
249 | } | |
250 | ||
251 | button.setDisabled(true); | |
252 | abortBtn.setDisabled(false); | |
253 | ||
254 | var field = form.findField('content'); | |
255 | fd.append("content", field.getValue()); | |
256 | field.setDisabled(true); | |
257 | ||
258 | field = form.findField('filename'); | |
259 | var file = field.fileInputEl.dom; | |
260 | fd.append("filename", file.files[0]); | |
261 | field.setDisabled(true); | |
262 | ||
263 | pbar.setVisible(true); | |
264 | updateProgress(0); | |
265 | ||
266 | xhr = new XMLHttpRequest(); | |
267 | ||
177de3de | 268 | xhr.addEventListener("load", function(e) { |
4a580e60 DM |
269 | if (xhr.status == 200) { |
270 | me.close(); | |
177de3de | 271 | } else { |
4a580e60 DM |
272 | var msg = gettext('Error') + " " + xhr.status.toString() + ": " + Ext.htmlEncode(xhr.statusText); |
273 | var result = Ext.decode(xhr.responseText); | |
274 | result.message = msg; | |
e7ade592 | 275 | var htmlStatus = Proxmox.Utils.extractRequestError(result, true); |
4a580e60 DM |
276 | Ext.Msg.alert(gettext('Error'), htmlStatus, function(btn) { |
277 | me.close(); | |
278 | }); | |
279 | ||
177de3de | 280 | } |
4a580e60 DM |
281 | }, false); |
282 | ||
283 | xhr.addEventListener("error", function(e) { | |
284 | var msg = "Error " + e.target.status.toString() + " occurred while receiving the document."; | |
285 | Ext.Msg.alert(gettext('Error'), msg, function(btn) { | |
286 | me.close(); | |
287 | }); | |
288 | }); | |
177de3de | 289 | |
4a580e60 | 290 | xhr.upload.addEventListener("progress", function(evt) { |
177de3de DC |
291 | if (evt.lengthComputable) { |
292 | var percentComplete = evt.loaded / evt.total; | |
4a580e60 | 293 | updateProgress(percentComplete, evt.loaded); |
177de3de | 294 | } |
4a580e60 DM |
295 | }, false); |
296 | ||
297 | xhr.open("POST", "/api2/json" + baseurl, true); | |
177de3de | 298 | xhr.send(fd); |
4a580e60 DM |
299 | } |
300 | }); | |
301 | ||
302 | form.on('validitychange', function(f, valid) { | |
303 | submitBtn.setDisabled(!valid); | |
304 | }); | |
305 | ||
3b422683 | 306 | Ext.apply(me, { |
4a580e60 DM |
307 | title: gettext('Upload'), |
308 | items: me.formPanel, | |
309 | buttons: [ abortBtn, submitBtn ], | |
310 | listeners: { | |
311 | close: function() { | |
312 | if (xhr) { | |
313 | xhr.abort(); | |
314 | } | |
315 | } | |
316 | } | |
317 | }); | |
318 | ||
319 | me.callParent(); | |
320 | } | |
321 | }); | |
322 | ||
323 | Ext.define('PVE.storage.ContentView', { | |
324 | extend: 'Ext.grid.GridPanel', | |
325 | ||
3f90858a | 326 | alias: 'widget.pveStorageContentView', |
4a580e60 | 327 | |
1b14c875 DC |
328 | stateful: true, |
329 | stateId: 'grid-storage-content', | |
c90539de DC |
330 | viewConfig: { |
331 | trackOver: false, | |
22f2f9d6 | 332 | loadMask: false |
c90539de | 333 | }, |
93cdb626 DC |
334 | features: [ |
335 | { | |
336 | ftype: 'grouping', | |
337 | groupHeaderTpl: '{name} ({rows.length} Item{[values.rows.length > 1 ? "s" : ""]})' | |
338 | } | |
339 | ], | |
4a580e60 DM |
340 | initComponent : function() { |
341 | var me = this; | |
342 | ||
343 | var nodename = me.pveSelNode.data.node; | |
344 | if (!nodename) { | |
345 | throw "no node name specified"; | |
346 | } | |
347 | ||
348 | var storage = me.pveSelNode.data.storage; | |
177de3de | 349 | if (!storage) { |
4a580e60 DM |
350 | throw "no storage ID specified"; |
351 | } | |
352 | ||
353 | var baseurl = "/nodes/" + nodename + "/storage/" + storage + "/content"; | |
3b422683 | 354 | var store = Ext.create('Ext.data.Store',{ |
4a580e60 DM |
355 | model: 'pve-storage-content', |
356 | groupField: 'content', | |
357 | proxy: { | |
358 | type: 'pve', | |
359 | url: '/api2/json' + baseurl | |
360 | }, | |
177de3de DC |
361 | sorters: { |
362 | property: 'volid', | |
363 | order: 'DESC' | |
4a580e60 DM |
364 | } |
365 | }); | |
366 | ||
367 | var sm = Ext.create('Ext.selection.RowModel', {}); | |
368 | ||
4a580e60 DM |
369 | var reload = function() { |
370 | store.load(); | |
6c1a2b86 | 371 | me.statusStore.load(); |
4a580e60 DM |
372 | }; |
373 | ||
e7ade592 | 374 | Proxmox.Utils.monStoreErrors(me, store); |
4a580e60 | 375 | |
5720fafa | 376 | var templateButton = Ext.create('Proxmox.button.Button',{ |
6c1a2b86 DC |
377 | itemId: 'tmpl-btn', |
378 | text: gettext('Templates'), | |
379 | handler: function() { | |
380 | var win = Ext.create('PVE.storage.TemplateDownload', { | |
381 | nodename: nodename, | |
8b97744c EK |
382 | storage: storage, |
383 | reloadGrid: reload | |
6c1a2b86 DC |
384 | }); |
385 | win.show(); | |
6c1a2b86 DC |
386 | } |
387 | }); | |
388 | ||
5720fafa | 389 | var uploadButton = Ext.create('Proxmox.button.Button', { |
6c1a2b86 DC |
390 | contents : ['iso','vztmpl'], |
391 | text: gettext('Upload'), | |
392 | handler: function() { | |
393 | var me = this; | |
394 | var win = Ext.create('PVE.storage.Upload', { | |
395 | nodename: nodename, | |
396 | storage: storage, | |
22f2f9d6 | 397 | contents: me.contents |
6c1a2b86 DC |
398 | }); |
399 | win.show(); | |
400 | win.on('destroy', reload); | |
401 | } | |
402 | }); | |
403 | ||
404 | me.statusStore = Ext.create('PVE.data.ObjectStore', { | |
22f2f9d6 | 405 | url: '/api2/json/nodes/' + nodename + '/storage/' + storage + '/status' |
6c1a2b86 DC |
406 | }); |
407 | ||
4a580e60 DM |
408 | Ext.apply(me, { |
409 | store: store, | |
410 | selModel: sm, | |
4a580e60 DM |
411 | tbar: [ |
412 | { | |
5720fafa | 413 | xtype: 'proxmoxButton', |
4a580e60 DM |
414 | text: gettext('Restore'), |
415 | selModel: sm, | |
416 | disabled: true, | |
417 | enableFn: function(rec) { | |
418 | return rec && rec.data.content === 'backup'; | |
419 | }, | |
420 | handler: function(b, e, rec) { | |
421 | var vmtype; | |
422 | if (rec.data.volid.match(/vzdump-qemu-/)) { | |
423 | vmtype = 'qemu'; | |
424 | } else if (rec.data.volid.match(/vzdump-openvz-/) || rec.data.volid.match(/vzdump-lxc-/)) { | |
425 | vmtype = 'lxc'; | |
426 | } else { | |
427 | return; | |
428 | } | |
429 | ||
430 | var win = Ext.create('PVE.window.Restore', { | |
431 | nodename: nodename, | |
432 | volid: rec.data.volid, | |
433 | volidText: PVE.Utils.render_storage_content(rec.data.volid, {}, rec), | |
434 | vmtype: vmtype | |
435 | }); | |
436 | win.show(); | |
437 | win.on('destroy', reload); | |
438 | } | |
439 | }, | |
440 | { | |
3b1ca3ff | 441 | xtype: 'proxmoxStdRemoveButton', |
4a580e60 | 442 | selModel: sm, |
4a580e60 DM |
443 | enableFn: function(rec) { |
444 | return rec && rec.data.content !== 'images'; | |
445 | }, | |
3b1ca3ff DC |
446 | callback: function() { |
447 | reload(); | |
448 | }, | |
449 | baseurl: baseurl + '/' | |
4a580e60 | 450 | }, |
6c1a2b86 DC |
451 | templateButton, |
452 | uploadButton, | |
772042ea | 453 | { |
5720fafa | 454 | xtype: 'proxmoxButton', |
772042ea DC |
455 | text: gettext('Show Configuration'), |
456 | disabled: true, | |
457 | selModel: sm, | |
458 | enableFn: function(rec) { | |
459 | return rec && rec.data.content === 'backup'; | |
460 | }, | |
461 | handler: function(b,e,rec) { | |
462 | var win = Ext.create('PVE.window.BackupConfig', { | |
463 | volume: rec.data.volid, | |
464 | pveSelNode: me.pveSelNode | |
465 | }); | |
466 | ||
467 | win.show(); | |
468 | } | |
469 | }, | |
4a580e60 DM |
470 | '->', |
471 | gettext('Search') + ':', ' ', | |
472 | { | |
473 | xtype: 'textfield', | |
474 | width: 200, | |
475 | enableKeyEvents: true, | |
476 | listeners: { | |
477 | buffer: 500, | |
478 | keyup: function(field) { | |
479 | store.clearFilter(true); | |
480 | store.filter([ | |
481 | { | |
482 | property: 'text', | |
483 | value: field.getValue(), | |
484 | anyMatch: true, | |
485 | caseSensitive: false | |
486 | } | |
487 | ]); | |
488 | } | |
489 | } | |
490 | } | |
491 | ], | |
492 | columns: [ | |
493 | { | |
494 | header: gettext('Name'), | |
495 | flex: 1, | |
496 | sortable: true, | |
497 | renderer: PVE.Utils.render_storage_content, | |
498 | dataIndex: 'text' | |
499 | }, | |
500 | { | |
501 | header: gettext('Format'), | |
502 | width: 100, | |
503 | dataIndex: 'format' | |
504 | }, | |
93cdb626 DC |
505 | { |
506 | header: gettext('Type'), | |
507 | width: 100, | |
508 | dataIndex: 'content', | |
509 | renderer: PVE.Utils.format_content_types | |
510 | }, | |
4a580e60 DM |
511 | { |
512 | header: gettext('Size'), | |
513 | width: 100, | |
e7ade592 | 514 | renderer: Proxmox.Utils.format_size, |
4a580e60 DM |
515 | dataIndex: 'size' |
516 | } | |
517 | ], | |
518 | listeners: { | |
3b422683 | 519 | activate: reload |
4a580e60 DM |
520 | } |
521 | }); | |
522 | ||
523 | me.callParent(); | |
6c1a2b86 | 524 | |
3d9bc0a2 | 525 | // disable the buttons/restrict the upload window |
6c1a2b86 | 526 | // if templates or uploads are not allowed |
540fdc8b | 527 | me.mon(me.statusStore, 'load', function(s,records,succes) { |
6c1a2b86 DC |
528 | var availcontent = []; |
529 | Ext.Array.each(records, function(item){ | |
530 | if (item.id === 'content') { | |
531 | availcontent = item.data.value.split(','); | |
532 | } | |
533 | }); | |
534 | var templ = false; | |
535 | var upload = false; | |
536 | var cts = []; | |
537 | ||
538 | Ext.Array.each(availcontent, function(content) { | |
539 | if (content === 'vztmpl') { | |
540 | templ = true; | |
541 | cts.push('vztmpl'); | |
542 | } else if (content === 'iso') { | |
543 | upload = true; | |
544 | cts.push('iso'); | |
545 | } | |
546 | }); | |
547 | ||
548 | if (templ !== upload) { | |
549 | uploadButton.contents = cts; | |
550 | } | |
551 | ||
552 | templateButton.setDisabled(!templ); | |
553 | uploadButton.setDisabled(!upload && !templ); | |
554 | }); | |
4a580e60 DM |
555 | } |
556 | }, function() { | |
557 | ||
558 | Ext.define('pve-storage-content', { | |
559 | extend: 'Ext.data.Model', | |
177de3de DC |
560 | fields: [ |
561 | 'volid', 'content', 'format', 'size', 'used', 'vmid', | |
4a580e60 | 562 | 'channel', 'id', 'lun', |
177de3de DC |
563 | { |
564 | name: 'text', | |
4a580e60 | 565 | convert: function(value, record) { |
86cc7049 DC |
566 | // check for volid, because if you click on a grouping header, |
567 | // it calls convert (but with an empty volid) | |
568 | if (value || record.data.volid === null) { | |
4a580e60 DM |
569 | return value; |
570 | } | |
571 | return PVE.Utils.render_storage_content(value, {}, record); | |
572 | } | |
573 | } | |
574 | ], | |
575 | idProperty: 'volid' | |
576 | }); | |
577 | ||
578 | }); |