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