Ext.define('PVE.window.UploadToStorage', {
extend: 'Ext.window.Window',
alias: 'widget.pveStorageUpload',
+ mixins: ['Proxmox.Mixin.CBind'],
resizable: false,
-
modal: true,
- initComponent: function() {
- var me = this;
+ title: gettext('Upload'),
- if (!me.nodename) {
- throw "no node name specified";
- }
- if (!me.storage) {
- throw "no storage ID specified";
- }
+ acceptedExtensions: {
+ iso: ['.img', '.iso'],
+ vztmpl: ['.tar.gz', '.tar.xz'],
+ },
- let baseurl = `/nodes/${me.nodename}/storage/${me.storage}/upload`;
+ cbindData: function(initialConfig) {
+ const me = this;
+ const ext = me.acceptedExtensions[me.content] || [];
- let pbar = Ext.create('Ext.ProgressBar', {
- text: 'Ready',
- hidden: true,
- });
+ me.url = `/nodes/${me.nodename}/storage/${me.storage}/upload`;
- let acceptedExtensions = {
- iso: ".img, .iso",
- vztmpl: ".tar.gz, .tar.xz",
+ return {
+ extensions: ext.join(', '),
+ filenameRegex: RegExp('^.*(?:' + ext.join('|').replaceAll('.', '\\.') + ')$', 'i'),
};
+ },
- let defaultContent = me.contents[0] || '';
-
- let fileField = Ext.create('Ext.form.field.File', {
- name: 'filename',
- buttonText: gettext('Select File...'),
- allowBlank: false,
- setAccept: function(content) {
- let acceptString = acceptedExtensions[content] || '';
- this.fileInputEl.set({
- accept: acceptString,
- });
- },
- listeners: {
- afterrender: function(cmp) {
- cmp.setAccept(defaultContent);
- },
- },
- });
+ viewModel: {
+ data: {
+ size: '-',
+ mimetype: '-',
+ filename: '',
+ },
+ },
+
+ controller: {
+ submit: function(button) {
+ const view = this.getView();
+ const form = this.lookup('formPanel').getForm();
+ const abortBtn = this.lookup('abortBtn');
+ const pbar = this.lookup('progressBar');
+
+ const updateProgress = function(per, bytes) {
+ let text = (per * 100).toFixed(2) + '%';
+ if (bytes) {
+ text += " (" + Proxmox.Utils.format_size(bytes) + ')';
+ }
+ pbar.updateProgress(per, text);
+ };
+
+ const fd = new FormData();
+
+ button.setDisabled(true);
+ abortBtn.setDisabled(false);
+
+ fd.append("content", view.content);
+
+ const fileField = form.findField('file');
+ const file = fileField.fileInputEl.dom.files[0];
+ fileField.setDisabled(true);
+
+ const filenameField = form.findField('filename');
+ const filename = filenameField.getValue();
+ filenameField.setDisabled(true);
+
+ const algorithmField = form.findField('checksum-algorithm');
+ algorithmField.setDisabled(true);
+ if (algorithmField.getValue() !== '__default__') {
+ fd.append("checksum-algorithm", algorithmField.getValue());
+
+ const checksumField = form.findField('checksum');
+ fd.append("checksum", checksumField.getValue()?.trim());
+ checksumField.setDisabled(true);
+ }
- me.formPanel = Ext.create('Ext.form.Panel', {
+ fd.append("filename", file, filename);
+
+ pbar.setVisible(true);
+ updateProgress(0);
+
+ const xhr = new XMLHttpRequest();
+ view.xhr = xhr;
+
+ xhr.addEventListener("load", function(e) {
+ if (xhr.status === 200) {
+ view.hide();
+
+ const result = JSON.parse(xhr.response);
+ const upid = result.data;
+ Ext.create('Proxmox.window.TaskViewer', {
+ autoShow: true,
+ upid: upid,
+ taskDone: view.taskDone,
+ listeners: {
+ destroy: function() {
+ view.close();
+ },
+ },
+ });
+
+ return;
+ }
+ const err = Ext.htmlEncode(xhr.statusText);
+ let msg = `${gettext('Error')} ${xhr.status.toString()}: ${err}`;
+ if (xhr.responseText !== "") {
+ const result = Ext.decode(xhr.responseText);
+ result.message = msg;
+ msg = Proxmox.Utils.extractRequestError(result, true);
+ }
+ Ext.Msg.alert(gettext('Error'), msg, btn => view.close());
+ }, false);
+
+ xhr.addEventListener("error", function(e) {
+ const err = e.target.status.toString();
+ const msg = `Error '${err}' occurred while receiving the document.`;
+ Ext.Msg.alert(gettext('Error'), msg, btn => view.close());
+ });
+
+ xhr.upload.addEventListener("progress", function(evt) {
+ if (evt.lengthComputable) {
+ const percentComplete = evt.loaded / evt.total;
+ updateProgress(percentComplete, evt.loaded);
+ }
+ }, false);
+
+ xhr.open("POST", `/api2/json${view.url}`, true);
+ xhr.send(fd);
+ },
+
+ validitychange: function(f, valid) {
+ const submitBtn = this.lookup('submitBtn');
+ submitBtn.setDisabled(!valid);
+ },
+
+ fileChange: function(input) {
+ const vm = this.getViewModel();
+ const name = input.value.replace(/^.*(\/|\\)/, '');
+ const fileInput = input.fileInputEl.dom;
+ vm.set('filename', name);
+ vm.set('size', (fileInput.files[0] && Proxmox.Utils.format_size(fileInput.files[0].size)) || '-');
+ vm.set('mimetype', (fileInput.files[0] && fileInput.files[0].type) || '-');
+ },
+
+ hashChange: function(field, value) {
+ const checksum = this.lookup('downloadUrlChecksum');
+ if (value === '__default__') {
+ checksum.setDisabled(true);
+ checksum.setValue("");
+ } else {
+ checksum.setDisabled(false);
+ }
+ },
+ },
+
+ items: [
+ {
+ xtype: 'form',
+ reference: 'formPanel',
method: 'POST',
waitMsgTarget: true,
bodyPadding: 10,
border: false,
- width: 300,
+ width: 400,
fieldDefaults: {
labelWidth: 100,
anchor: '100%',
},
items: [
{
- xtype: 'pveContentTypeSelector',
- cts: me.contents,
- fieldLabel: gettext('Content'),
- name: 'content',
- value: defaultContent,
+ xtype: 'filefield',
+ name: 'file',
+ buttonText: gettext('Select File'),
allowBlank: false,
+ fieldLabel: gettext('File'),
+ cbind: {
+ accept: '{extensions}',
+ },
listeners: {
- change: function(cmp, newValue, oldValue) {
- fileField.setAccept(newValue);
- },
+ change: 'fileChange',
},
},
- fileField,
- pbar,
- ],
- });
-
- let form = me.formPanel.getForm();
-
- let doStandardSubmit = function() {
- form.submit({
- url: "/api2/htmljs" + baseurl,
- waitMsg: gettext('Uploading file...'),
- success: function(f, action) {
- me.close();
+ {
+ xtype: 'textfield',
+ name: 'filename',
+ allowBlank: false,
+ fieldLabel: gettext('File name'),
+ bind: {
+ value: '{filename}',
+ },
+ cbind: {
+ regex: '{filenameRegex}',
+ },
+ regexText: gettext('Wrong file extension'),
},
- failure: function(f, action) {
- var msg = PVE.Utils.extractFormActionError(action);
- Ext.Msg.alert(gettext('Error'), msg);
+ {
+ xtype: 'displayfield',
+ name: 'size',
+ fieldLabel: gettext('File size'),
+ bind: {
+ value: '{size}',
+ },
},
- });
- };
-
- let updateProgress = function(per, bytes) {
- var text = (per * 100).toFixed(2) + '%';
- if (bytes) {
- text += " (" + Proxmox.Utils.format_size(bytes) + ')';
- }
- pbar.updateProgress(per, text);
- };
+ {
+ xtype: 'displayfield',
+ name: 'mimetype',
+ fieldLabel: gettext('MIME type'),
+ bind: {
+ value: '{mimetype}',
+ },
+ },
+ {
+ xtype: 'pveHashAlgorithmSelector',
+ name: 'checksum-algorithm',
+ fieldLabel: gettext('Hash algorithm'),
+ allowBlank: true,
+ hasNoneOption: true,
+ value: '__default__',
+ listeners: {
+ change: 'hashChange',
+ },
+ },
+ {
+ xtype: 'textfield',
+ name: 'checksum',
+ fieldLabel: gettext('Checksum'),
+ allowBlank: false,
+ disabled: true,
+ emptyText: gettext('none'),
+ reference: 'downloadUrlChecksum',
+ },
+ {
+ xtype: 'progressbar',
+ text: 'Ready',
+ hidden: true,
+ reference: 'progressBar',
+ },
+ {
+ xtype: 'hiddenfield',
+ name: 'content',
+ cbind: {
+ value: '{content}',
+ },
+ },
+ ],
+ listeners: {
+ validitychange: 'validitychange',
+ },
+ },
+ ],
- let abortBtn = Ext.create('Ext.Button', {
+ buttons: [
+ {
+ xtype: 'button',
text: gettext('Abort'),
+ reference: 'abortBtn',
disabled: true,
handler: function() {
- me.close();
+ const me = this;
+ me.up('pveStorageUpload').close();
},
- });
-
- let submitBtn = Ext.create('Ext.Button', {
+ },
+ {
text: gettext('Upload'),
+ reference: 'submitBtn',
disabled: true,
- handler: function(button) {
- var fd;
- try {
- fd = new FormData();
- } catch (err) {
- doStandardSubmit();
- return;
- }
+ handler: 'submit',
+ },
+ ],
- button.setDisabled(true);
- abortBtn.setDisabled(false);
-
- var field = form.findField('content');
- fd.append("content", field.getValue());
- field.setDisabled(true);
-
- field = form.findField('filename');
- var file = field.fileInputEl.dom;
- fd.append("filename", file.files[0]);
- field.setDisabled(true);
-
- pbar.setVisible(true);
- updateProgress(0);
-
- let xhr = new XMLHttpRequest();
- me.xhr = xhr;
-
- xhr.addEventListener("load", function(e) {
- if (xhr.status === 200) {
- me.close();
- return;
- }
- let err = Ext.htmlEncode(xhr.statusText);
- let msg = `${gettext('Error')} ${xhr.status.toString()}: ${err}`;
- if (xhr.responseText !== "") {
- let result = Ext.decode(xhr.responseText);
- result.message = msg;
- msg = Proxmox.Utils.extractRequestError(result, true);
- }
- Ext.Msg.alert(gettext('Error'), msg, btn => me.close());
- }, false);
-
- xhr.addEventListener("error", function(e) {
- let err = e.target.status.toString();
- let msg = `Error '${err}' occurred while receiving the document.`;
- Ext.Msg.alert(gettext('Error'), msg, btn => me.close());
- });
-
- xhr.upload.addEventListener("progress", function(evt) {
- if (evt.lengthComputable) {
- let percentComplete = evt.loaded / evt.total;
- updateProgress(percentComplete, evt.loaded);
- }
- }, false);
-
- xhr.open("POST", `/api2/json${baseurl}`, true);
- xhr.send(fd);
- },
- });
-
- form.on('validitychange', (f, valid) => submitBtn.setDisabled(!valid));
-
- Ext.apply(me, {
- title: gettext('Upload'),
- items: me.formPanel,
- buttons: [abortBtn, submitBtn],
- listeners: {
- close: function() {
- if (me.xhr) {
- me.xhr.abort();
- delete me.xhr;
- }
- },
- },
- });
+ listeners: {
+ close: function() {
+ const me = this;
+ if (me.xhr) {
+ me.xhr.abort();
+ delete me.xhr;
+ }
+ },
+ },
+
+ initComponent: function() {
+ const me = this;
+
+ if (!me.nodename) {
+ throw "no node name specified";
+ }
+ if (!me.storage) {
+ throw "no storage ID specified";
+ }
+ if (!me.acceptedExtensions[me.content]) {
+ throw "content type not supported";
+ }
me.callParent();
},