]>
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 | ||
65 | fd.append("filename", file, filename); | |
66 | ||
67 | pbar.setVisible(true); | |
68 | updateProgress(0); | |
69 | ||
70 | const xhr = new XMLHttpRequest(); | |
71 | view.xhr = xhr; | |
72 | ||
73 | xhr.addEventListener("load", function(e) { | |
74 | if (xhr.status === 200) { | |
75 | view.close(); | |
76 | return; | |
77 | } | |
78 | const err = Ext.htmlEncode(xhr.statusText); | |
79 | let msg = `${gettext('Error')} ${xhr.status.toString()}: ${err}`; | |
80 | if (xhr.responseText !== "") { | |
81 | const result = Ext.decode(xhr.responseText); | |
82 | result.message = msg; | |
83 | msg = Proxmox.Utils.extractRequestError(result, true); | |
84 | } | |
85 | Ext.Msg.alert(gettext('Error'), msg, btn => view.close()); | |
86 | }, false); | |
87 | ||
88 | xhr.addEventListener("error", function(e) { | |
89 | const err = e.target.status.toString(); | |
90 | const msg = `Error '${err}' occurred while receiving the document.`; | |
91 | Ext.Msg.alert(gettext('Error'), msg, btn => view.close()); | |
92 | }); | |
93 | ||
94 | xhr.upload.addEventListener("progress", function(evt) { | |
95 | if (evt.lengthComputable) { | |
96 | const percentComplete = evt.loaded / evt.total; | |
97 | updateProgress(percentComplete, evt.loaded); | |
98 | } | |
99 | }, false); | |
100 | ||
101 | xhr.open("POST", `/api2/json${view.url}`, true); | |
102 | xhr.send(fd); | |
103 | }, | |
104 | ||
105 | validitychange: function(f, valid) { | |
106 | const submitBtn = this.lookup('submitBtn'); | |
107 | submitBtn.setDisabled(!valid); | |
108 | }, | |
109 | ||
110 | fileChange: function(input) { | |
111 | const vm = this.getViewModel(); | |
112 | const name = input.value.replace(/^.*(\/|\\)/, ''); | |
113 | const fileInput = input.fileInputEl.dom; | |
114 | vm.set('filename', name); | |
115 | vm.set('size', (fileInput.files[0] && Proxmox.Utils.format_size(fileInput.files[0].size)) || '-'); | |
116 | vm.set('mimetype', (fileInput.files[0] && fileInput.files[0].type) || '-'); | |
117 | }, | |
118 | }, | |
119 | ||
120 | items: [ | |
121 | { | |
122 | xtype: 'form', | |
123 | reference: 'formPanel', | |
26bfeca1 LS |
124 | method: 'POST', |
125 | waitMsgTarget: true, | |
126 | bodyPadding: 10, | |
127 | border: false, | |
f9f45aac | 128 | width: 400, |
26bfeca1 LS |
129 | fieldDefaults: { |
130 | labelWidth: 100, | |
131 | anchor: '100%', | |
132 | }, | |
133 | items: [ | |
134 | { | |
f9f45aac LS |
135 | xtype: 'filefield', |
136 | name: 'file', | |
137 | buttonText: gettext('Select File'), | |
26bfeca1 | 138 | allowBlank: false, |
f9f45aac LS |
139 | fieldLabel: gettext('File'), |
140 | cbind: { | |
141 | accept: '{extensions}', | |
142 | }, | |
26bfeca1 | 143 | listeners: { |
f9f45aac | 144 | change: 'fileChange', |
26bfeca1 LS |
145 | }, |
146 | }, | |
f9f45aac LS |
147 | { |
148 | xtype: 'textfield', | |
149 | name: 'filename', | |
150 | allowBlank: false, | |
151 | fieldLabel: gettext('File name'), | |
152 | bind: { | |
153 | value: '{filename}', | |
154 | }, | |
26bfeca1 | 155 | }, |
f9f45aac LS |
156 | { |
157 | xtype: 'displayfield', | |
158 | name: 'size', | |
159 | fieldLabel: gettext('File size'), | |
160 | bind: { | |
161 | value: '{size}', | |
162 | }, | |
26bfeca1 | 163 | }, |
f9f45aac LS |
164 | { |
165 | xtype: 'displayfield', | |
166 | name: 'mimetype', | |
167 | fieldLabel: gettext('MIME type'), | |
168 | bind: { | |
169 | value: '{mimetype}', | |
170 | }, | |
171 | }, | |
172 | { | |
173 | xtype: 'progressbar', | |
174 | text: 'Ready', | |
175 | hidden: true, | |
176 | reference: 'progressBar', | |
177 | }, | |
178 | { | |
179 | xtype: 'hiddenfield', | |
180 | name: 'content', | |
181 | cbind: { | |
182 | value: '{content}', | |
183 | }, | |
184 | }, | |
185 | ], | |
186 | listeners: { | |
187 | validitychange: 'validitychange', | |
188 | }, | |
189 | }, | |
190 | ], | |
191 | ||
192 | buttons: [ | |
193 | { | |
194 | xtype: 'button', | |
26bfeca1 | 195 | text: gettext('Abort'), |
f9f45aac | 196 | reference: 'abortBtn', |
26bfeca1 LS |
197 | disabled: true, |
198 | handler: function() { | |
f9f45aac LS |
199 | const me = this; |
200 | me.up('pveStorageUpload').close(); | |
26bfeca1 | 201 | }, |
f9f45aac LS |
202 | }, |
203 | { | |
26bfeca1 | 204 | text: gettext('Upload'), |
f9f45aac | 205 | reference: 'submitBtn', |
26bfeca1 | 206 | disabled: true, |
f9f45aac LS |
207 | handler: 'submit', |
208 | }, | |
209 | ], | |
210 | ||
211 | listeners: { | |
212 | close: function() { | |
213 | const me = this; | |
214 | if (me.xhr) { | |
215 | me.xhr.abort(); | |
216 | delete me.xhr; | |
217 | } | |
218 | }, | |
219 | }, | |
26bfeca1 | 220 | |
f9f45aac LS |
221 | initComponent: function() { |
222 | const me = this; | |
223 | ||
224 | if (!me.nodename) { | |
225 | throw "no node name specified"; | |
226 | } | |
227 | if (!me.storage) { | |
228 | throw "no storage ID specified"; | |
229 | } | |
230 | if (!me.acceptedExtensions[me.content]) { | |
231 | throw "content type not supported"; | |
232 | } | |
26bfeca1 LS |
233 | |
234 | me.callParent(); | |
235 | }, | |
236 | }); |