]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/window/UploadToStorage.js
fix #3505: ui/UploadToStorage: add checksum and algorithm
[pve-manager.git] / www / manager6 / window / UploadToStorage.js
1 Ext.define('PVE.window.UploadToStorage', {
2 extend: 'Ext.window.Window',
3 alias: 'widget.pveStorageUpload',
4 mixins: ['Proxmox.Mixin.CBind'],
5
6 resizable: false,
7 modal: true,
8
9 title: gettext('Upload'),
10
11 acceptedExtensions: {
12 iso: ['.img', '.iso'],
13 vztmpl: ['.tar.gz', '.tar.xz'],
14 },
15
16 cbindData: function(initialConfig) {
17 const me = this;
18 const ext = me.acceptedExtensions[me.content] || [];
19
20 me.url = `/nodes/${me.nodename}/storage/${me.storage}/upload`;
21
22 return {
23 extensions: ext.join(', '),
24 };
25 },
26
27 viewModel: {
28 data: {
29 size: '-',
30 mimetype: '-',
31 filename: '',
32 },
33 },
34
35 controller: {
36 submit: function(button) {
37 const view = this.getView();
38 const form = this.lookup('formPanel').getForm();
39 const abortBtn = this.lookup('abortBtn');
40 const pbar = this.lookup('progressBar');
41
42 const updateProgress = function(per, bytes) {
43 let text = (per * 100).toFixed(2) + '%';
44 if (bytes) {
45 text += " (" + Proxmox.Utils.format_size(bytes) + ')';
46 }
47 pbar.updateProgress(per, text);
48 };
49
50 const fd = new FormData();
51
52 button.setDisabled(true);
53 abortBtn.setDisabled(false);
54
55 fd.append("content", view.content);
56
57 const fileField = form.findField('file');
58 const file = fileField.fileInputEl.dom.files[0];
59 fileField.setDisabled(true);
60
61 const filenameField = form.findField('filename');
62 const filename = filenameField.getValue();
63 filenameField.setDisabled(true);
64
65 const algorithmField = form.findField('checksum-algorithm');
66 algorithmField.setDisabled(true);
67 if (algorithmField.getValue() !== '__default__') {
68 fd.append("checksum-algorithm", algorithmField.getValue());
69
70 const checksumField = form.findField('checksum');
71 fd.append("checksum", checksumField.getValue());
72 checksumField.setDisabled(true);
73 }
74
75 fd.append("filename", file, filename);
76
77 pbar.setVisible(true);
78 updateProgress(0);
79
80 const xhr = new XMLHttpRequest();
81 view.xhr = xhr;
82
83 xhr.addEventListener("load", function(e) {
84 if (xhr.status === 200) {
85 view.close();
86 return;
87 }
88 const err = Ext.htmlEncode(xhr.statusText);
89 let msg = `${gettext('Error')} ${xhr.status.toString()}: ${err}`;
90 if (xhr.responseText !== "") {
91 const result = Ext.decode(xhr.responseText);
92 result.message = msg;
93 msg = Proxmox.Utils.extractRequestError(result, true);
94 }
95 Ext.Msg.alert(gettext('Error'), msg, btn => view.close());
96 }, false);
97
98 xhr.addEventListener("error", function(e) {
99 const err = e.target.status.toString();
100 const msg = `Error '${err}' occurred while receiving the document.`;
101 Ext.Msg.alert(gettext('Error'), msg, btn => view.close());
102 });
103
104 xhr.upload.addEventListener("progress", function(evt) {
105 if (evt.lengthComputable) {
106 const percentComplete = evt.loaded / evt.total;
107 updateProgress(percentComplete, evt.loaded);
108 }
109 }, false);
110
111 xhr.open("POST", `/api2/json${view.url}`, true);
112 xhr.send(fd);
113 },
114
115 validitychange: function(f, valid) {
116 const submitBtn = this.lookup('submitBtn');
117 submitBtn.setDisabled(!valid);
118 },
119
120 fileChange: function(input) {
121 const vm = this.getViewModel();
122 const name = input.value.replace(/^.*(\/|\\)/, '');
123 const fileInput = input.fileInputEl.dom;
124 vm.set('filename', name);
125 vm.set('size', (fileInput.files[0] && Proxmox.Utils.format_size(fileInput.files[0].size)) || '-');
126 vm.set('mimetype', (fileInput.files[0] && fileInput.files[0].type) || '-');
127 },
128
129 hashChange: function(field, value) {
130 const checksum = this.lookup('downloadUrlChecksum');
131 if (value === '__default__') {
132 checksum.setDisabled(true);
133 checksum.setValue("");
134 } else {
135 checksum.setDisabled(false);
136 }
137 },
138 },
139
140 items: [
141 {
142 xtype: 'form',
143 reference: 'formPanel',
144 method: 'POST',
145 waitMsgTarget: true,
146 bodyPadding: 10,
147 border: false,
148 width: 400,
149 fieldDefaults: {
150 labelWidth: 100,
151 anchor: '100%',
152 },
153 items: [
154 {
155 xtype: 'filefield',
156 name: 'file',
157 buttonText: gettext('Select File'),
158 allowBlank: false,
159 fieldLabel: gettext('File'),
160 cbind: {
161 accept: '{extensions}',
162 },
163 listeners: {
164 change: 'fileChange',
165 },
166 },
167 {
168 xtype: 'textfield',
169 name: 'filename',
170 allowBlank: false,
171 fieldLabel: gettext('File name'),
172 bind: {
173 value: '{filename}',
174 },
175 },
176 {
177 xtype: 'displayfield',
178 name: 'size',
179 fieldLabel: gettext('File size'),
180 bind: {
181 value: '{size}',
182 },
183 },
184 {
185 xtype: 'displayfield',
186 name: 'mimetype',
187 fieldLabel: gettext('MIME type'),
188 bind: {
189 value: '{mimetype}',
190 },
191 },
192 {
193 xtype: 'pveHashAlgorithmSelector',
194 name: 'checksum-algorithm',
195 fieldLabel: gettext('Hash algorithm'),
196 allowBlank: true,
197 hasNoneOption: true,
198 value: '__default__',
199 listeners: {
200 change: 'hashChange',
201 },
202 },
203 {
204 xtype: 'textfield',
205 name: 'checksum',
206 fieldLabel: gettext('Checksum'),
207 allowBlank: false,
208 disabled: true,
209 emptyText: gettext('none'),
210 reference: 'downloadUrlChecksum',
211 },
212 {
213 xtype: 'progressbar',
214 text: 'Ready',
215 hidden: true,
216 reference: 'progressBar',
217 },
218 {
219 xtype: 'hiddenfield',
220 name: 'content',
221 cbind: {
222 value: '{content}',
223 },
224 },
225 ],
226 listeners: {
227 validitychange: 'validitychange',
228 },
229 },
230 ],
231
232 buttons: [
233 {
234 xtype: 'button',
235 text: gettext('Abort'),
236 reference: 'abortBtn',
237 disabled: true,
238 handler: function() {
239 const me = this;
240 me.up('pveStorageUpload').close();
241 },
242 },
243 {
244 text: gettext('Upload'),
245 reference: 'submitBtn',
246 disabled: true,
247 handler: 'submit',
248 },
249 ],
250
251 listeners: {
252 close: function() {
253 const me = this;
254 if (me.xhr) {
255 me.xhr.abort();
256 delete me.xhr;
257 }
258 },
259 },
260
261 initComponent: function() {
262 const me = this;
263
264 if (!me.nodename) {
265 throw "no node name specified";
266 }
267 if (!me.storage) {
268 throw "no storage ID specified";
269 }
270 if (!me.acceptedExtensions[me.content]) {
271 throw "content type not supported";
272 }
273
274 me.callParent();
275 },
276 });