]>
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(', '), | |
26bfeca1 | 24 | }; |
f9f45aac | 25 | }, |
26bfeca1 | 26 | |
f9f45aac LS |
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); | |
26bfeca1 | 56 | |
f9f45aac LS |
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 | ||
20258203 LS |
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 | ||
f9f45aac LS |
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 | }, | |
20258203 LS |
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 | }, | |
f9f45aac LS |
138 | }, |
139 | ||
140 | items: [ | |
141 | { | |
142 | xtype: 'form', | |
143 | reference: 'formPanel', | |
26bfeca1 LS |
144 | method: 'POST', |
145 | waitMsgTarget: true, | |
146 | bodyPadding: 10, | |
147 | border: false, | |
f9f45aac | 148 | width: 400, |
26bfeca1 LS |
149 | fieldDefaults: { |
150 | labelWidth: 100, | |
151 | anchor: '100%', | |
152 | }, | |
153 | items: [ | |
154 | { | |
f9f45aac LS |
155 | xtype: 'filefield', |
156 | name: 'file', | |
157 | buttonText: gettext('Select File'), | |
26bfeca1 | 158 | allowBlank: false, |
f9f45aac LS |
159 | fieldLabel: gettext('File'), |
160 | cbind: { | |
161 | accept: '{extensions}', | |
162 | }, | |
26bfeca1 | 163 | listeners: { |
f9f45aac | 164 | change: 'fileChange', |
26bfeca1 LS |
165 | }, |
166 | }, | |
f9f45aac LS |
167 | { |
168 | xtype: 'textfield', | |
169 | name: 'filename', | |
170 | allowBlank: false, | |
171 | fieldLabel: gettext('File name'), | |
172 | bind: { | |
173 | value: '{filename}', | |
174 | }, | |
26bfeca1 | 175 | }, |
f9f45aac LS |
176 | { |
177 | xtype: 'displayfield', | |
178 | name: 'size', | |
179 | fieldLabel: gettext('File size'), | |
180 | bind: { | |
181 | value: '{size}', | |
182 | }, | |
26bfeca1 | 183 | }, |
f9f45aac LS |
184 | { |
185 | xtype: 'displayfield', | |
186 | name: 'mimetype', | |
187 | fieldLabel: gettext('MIME type'), | |
188 | bind: { | |
189 | value: '{mimetype}', | |
190 | }, | |
191 | }, | |
20258203 LS |
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 | }, | |
f9f45aac LS |
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', | |
26bfeca1 | 235 | text: gettext('Abort'), |
f9f45aac | 236 | reference: 'abortBtn', |
26bfeca1 LS |
237 | disabled: true, |
238 | handler: function() { | |
f9f45aac LS |
239 | const me = this; |
240 | me.up('pveStorageUpload').close(); | |
26bfeca1 | 241 | }, |
f9f45aac LS |
242 | }, |
243 | { | |
26bfeca1 | 244 | text: gettext('Upload'), |
f9f45aac | 245 | reference: 'submitBtn', |
26bfeca1 | 246 | disabled: true, |
f9f45aac LS |
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 | }, | |
26bfeca1 | 260 | |
f9f45aac LS |
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 | } | |
26bfeca1 LS |
273 | |
274 | me.callParent(); | |
275 | }, | |
276 | }); |