]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/storage/ContentView.js
htmlEncode some comment fields
[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
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
87Ext.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
145Ext.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
319Ext.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});