summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/views/file_upload_mixin.js
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/web/static/src/js/views/file_upload_mixin.js
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/web/static/src/js/views/file_upload_mixin.js')
-rw-r--r--addons/web/static/src/js/views/file_upload_mixin.js234
1 files changed, 234 insertions, 0 deletions
diff --git a/addons/web/static/src/js/views/file_upload_mixin.js b/addons/web/static/src/js/views/file_upload_mixin.js
new file mode 100644
index 00000000..84dddcd9
--- /dev/null
+++ b/addons/web/static/src/js/views/file_upload_mixin.js
@@ -0,0 +1,234 @@
+odoo.define('web.fileUploadMixin', function (require) {
+'use strict';
+
+/**
+ * Mixin to be used in view Controllers to manage uploads and generate progress bars.
+ * supported views: kanban, list
+ */
+
+const { csrf_token, _t } = require('web.core');
+const ProgressBar = require('web.ProgressBar');
+const ProgressCard = require('web.ProgressCard');
+
+const ProgressBarMixin = {
+
+ custom_events: {
+ progress_bar_abort: '_onProgressBarAbort',
+ },
+
+ init() {
+ /**
+ * Contains the uploads currently happening, used to attach progress bars.
+ * e.g: {'fileUploadId45': {progressBar, progressCard, ...params}}
+ */
+ this._fileUploads = {};
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * used to use a mocked version of Xhr in the tests.
+ *
+ * @private
+ * @returns {XMLHttpRequest}
+ */
+ _createXhr() {
+ return new window.XMLHttpRequest();
+ },
+ /**
+ * @private
+ */
+ _getFileUploadRenderOptions() {
+ return {
+ predicate: () => true,
+ targetCallback: undefined,
+ };
+ },
+ /**
+ * @private
+ * @returns {string} upload route
+ */
+ _getFileUploadRoute() {
+ return '/web/binary/upload_attachment';
+ },
+ /**
+ * @private
+ * @param {Object} params
+ * @param {Object[]} params.files
+ * @param {XMLHttpRequest} params.xhr
+ */
+ _makeFileUpload(params) {
+ const { files, xhr } = params;
+ const fileUploadId = _.uniqueId('fileUploadId');
+ const formData = new FormData();
+ const formDataKeys = this._makeFileUploadFormDataKeys(Object.assign({ fileUploadId }, params));
+
+ formData.append('csrf_token', csrf_token);
+ for (const key in formDataKeys) {
+ if (formDataKeys[key] !== undefined) {
+ formData.append(key, formDataKeys[key]);
+ }
+ }
+ for (const file of files) {
+ formData.append('ufile', file);
+ }
+
+ return {
+ fileUploadId,
+ xhr,
+ title: files.length === 1
+ ? files[0].name
+ : _.str.sprintf(_t("%s Files"), files.length),
+ type: files.length === 1 ? files[0].type : undefined,
+ formData,
+ };
+ },
+ /**
+ * @private
+ * @param {Object} param0
+ * @param {string} param0.fileUploadId
+ * @returns {Object} the list of the form entries of a file upload.
+ */
+ _makeFileUploadFormDataKeys({ fileUploadId }) {
+ return {
+ callback: fileUploadId,
+ };
+ },
+ /**
+ * @private
+ * @param {integer} fileUploadId
+ */
+ async _removeFileUpload(fileUploadId) {
+ const upload = this._fileUploads[fileUploadId];
+ upload.progressCard && upload.progressCard.destroy();
+ upload.progressBar && upload.progressBar.destroy();
+ delete this._fileUploads[fileUploadId];
+ await this.reload();
+ },
+ /**
+ * @private
+ */
+ async _renderFileUploads() {
+ const { predicate, targetCallback } = this._getFileUploadRenderOptions();
+
+ for (const fileUploadId in this._fileUploads) {
+ const upload = this._fileUploads[fileUploadId];
+ if (!predicate(upload)) {
+ continue;
+ }
+
+ if (!upload.progressBar) {
+ if (!upload.recordId || this.viewType !== 'kanban') {
+ upload.progressCard = new ProgressCard(this, {
+ title: upload.title,
+ type: upload.type,
+ viewType: this.viewType,
+ });
+ }
+ upload.progressBar = new ProgressBar(this, {
+ xhr: upload.xhr,
+ title: upload.title,
+ fileUploadId,
+ });
+ }
+
+ let $targetCard;
+ if (upload.progressCard) {
+ await upload.progressCard.prependTo(this.renderer.$el);
+ $targetCard = upload.progressCard.$el;
+ } else if (targetCallback) {
+ $targetCard = targetCallback(upload);
+ }
+ await upload.progressBar.appendTo($targetCard);
+ }
+ },
+ /**
+ * @private
+ * @param {Object[]} files
+ * @param {Object} [params] optional additional data
+ */
+ async _uploadFiles(files, params={}) {
+ if (!files || !files.length) { return; }
+
+ await new Promise(resolve => {
+ const xhr = this._createXhr();
+ xhr.open('POST', this._getFileUploadRoute());
+ const fileUploadData = this._makeFileUpload(Object.assign({ files, xhr }, params));
+ const { fileUploadId, formData } = fileUploadData;
+ this._fileUploads[fileUploadId] = fileUploadData;
+ xhr.upload.addEventListener("progress", ev => {
+ this._updateFileUploadProgress(fileUploadId, ev);
+ });
+ const progressPromise = this._onBeforeUpload();
+ xhr.onload = async () => {
+ await progressPromise;
+ resolve();
+ this._onUploadLoad({ fileUploadId, xhr });
+ };
+ xhr.onerror = async () => {
+ await progressPromise;
+ resolve();
+ this._onUploadError({ fileUploadId, xhr });
+ };
+ xhr.send(formData);
+ });
+ },
+ /**
+ * @private
+ * @param {string} fileUploadId
+ * @param {ProgressEvent} ev
+ */
+ _updateFileUploadProgress(fileUploadId, ev) {
+ const { progressCard, progressBar } = this._fileUploads[fileUploadId];
+ progressCard && progressCard.update(ev.loaded, ev.total);
+ progressBar && progressBar.update(ev.loaded, ev.total);
+ },
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Hook to customize the behaviour of _uploadFiles() before an upload is made.
+ *
+ * @private
+ */
+ async _onBeforeUpload() {
+ await this._renderFileUploads();
+ },
+ /**
+ * @private
+ * @param {OdooEvent} ev
+ * @param {integer} ev.data.fileUploadId
+ */
+ _onProgressBarAbort(ev) {
+ this._removeFileUpload(ev.data.fileUploadId);
+ },
+ /**
+ * Hook to customize the behaviour of the xhr.onload of an upload.
+ *
+ * @private
+ * @param {string} param0.fileUploadId
+ */
+ _onUploadLoad({ fileUploadId }) {
+ this._removeFileUpload(fileUploadId);
+ },
+ /**
+ * Hook to customize the behaviour of the xhr.onerror of an upload.
+ *
+ * @private
+ * @param {string} param1.fileUploadId
+ * @param {XMLHttpRequest} param0.xhr
+ */
+ _onUploadError({ fileUploadId, xhr }) {
+ this.do_notify(xhr.status, _.str.sprintf(_t('message: %s'), xhr.reponseText), true);
+ this._removeFileUpload(fileUploadId);
+ },
+
+};
+
+return ProgressBarMixin;
+
+});