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