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