From ae10f2287b9101f9d48395a0a60905440258d560 Mon Sep 17 00:00:00 2001 From: Eugen Ciur <eugen@papermerge.com> Date: Sat, 29 Jan 2022 06:07:11 +0100 Subject: [PATCH] add download archives functionality --- app/components/commander/action_buttons.hbs | 2 + app/components/commander/action_buttons.js | 7 ++ app/components/commander/index.hbs | 1 + app/components/commander/index.js | 5 + app/services/requests.js | 119 +++++++++++--------- app/utils/index.js | 45 +++++++- 6 files changed, 126 insertions(+), 53 deletions(-) diff --git a/app/components/commander/action_buttons.hbs b/app/components/commander/action_buttons.hbs index 99b100f..e4bf903 100644 --- a/app/components/commander/action_buttons.hbs +++ b/app/components/commander/action_buttons.hbs @@ -9,6 +9,7 @@ </button> <button class="btn btn-success" + {{on "click" this.onDownloadNodes}} type="button"> <i class="fa fa-download"></i> Download @@ -23,6 +24,7 @@ {{else if this.multiple_nodes_selected}} <button class="btn btn-success" + {{on "click" this.onDownloadNodes}} type="button"> <i class="fa fa-download"></i> Download diff --git a/app/components/commander/action_buttons.js b/app/components/commander/action_buttons.js index 2b773e1..d0ac8f0 100644 --- a/app/components/commander/action_buttons.js +++ b/app/components/commander/action_buttons.js @@ -25,4 +25,11 @@ export default class ActionButtonsComponent extends Component { this.args.selectedNodes[0] ); } + + @action + onDownloadNodes() { + this.args.onDownloadNodes( + this.args.selectedNodes + ); + } } diff --git a/app/components/commander/index.hbs b/app/components/commander/index.hbs index 7f6366a..ec5b188 100644 --- a/app/components/commander/index.hbs +++ b/app/components/commander/index.hbs @@ -8,6 +8,7 @@ @openRenameModal={{this.openRenameModal}} @openConfirmDeletionModal={{this.openConfirmDeletionModal}} @onCreateDocumentModel={{this.onCreateDocumentModel}} + @onDownloadNodes={{this.onDownloadNodes}} @selectedNodes={{this.selected_nodes}} @lang={{this.lang}} @node={{@node}} /> diff --git a/app/components/commander/index.js b/app/components/commander/index.js index 0c5cb66..bcf6270 100644 --- a/app/components/commander/index.js +++ b/app/components/commander/index.js @@ -178,6 +178,11 @@ export default class CommanderComponent extends Component { this.__new_record = new_record; // workaround of ember bug } + @action + onDownloadNodes(selected_nodes) { + this.requests.downloadNodes(selected_nodes); + } + @action onViewModeChange(new_view_mode) { this.view_mode = new_view_mode; diff --git a/app/services/requests.js b/app/services/requests.js index c3825ce..a370cbef 100644 --- a/app/services/requests.js +++ b/app/services/requests.js @@ -2,7 +2,11 @@ import Service from '@ember/service'; // eslint-disable-next-line ember/no-computed-properties-in-native-classes import { computed } from '@ember/object'; import { service } from '@ember/service'; -import { base_url } from 'papermerge/utils'; +import { + base_url, + insert_blob, + extract_file_name +} from 'papermerge/utils'; export default class Requests extends Service { @@ -28,44 +32,47 @@ export default class Requests extends Service { }); } + /** + * `document_version` contains following attributes: + * id + * number + * file_name + * lang + * pages + * size + * page_count + * short_description + * + * attributes which correspond to server side (or client side) DocumentVersion model + */ async downloadDocumentVersion(document_version) { - /* - `document_version` contains following attributes: - id - number - file_name - lang - pages - size - page_count - short_description - - attributes which correspond to server side (or client side) DocumentVersion model - */ - let url, headers_copy = {}; + let response, blob; - url = `${base_url()}/document-versions/${document_version.id}/download/`; - Object.assign(headers_copy, this.headers); - //headers_copy['Access-Control-Allow-Origin'] = ENV.APP.HOST; + response = await this._get(`/document-versions/${document_version.id}/download/`); - return fetch(url, { - method: 'GET', - headers: headers_copy - }).then( - response => response.blob() - ).then((blob) => { - let url = window.URL.createObjectURL(blob); - let a = document.createElement('a'); - - a.href = url; - a.download = document_version.file_name; - // we need to append the element to the dom -> otherwise it will not - // work in firefox - document.body.appendChild(a); - a.click(); - //afterwards we remove the element again - a.remove(); - }); + blob = await response.blob(); + insert_blob( + document_version.file_name, + blob + ); + } + + async downloadNodes(selected_nodes) { + let params_arr, + params_str, + response, + blob, + file_name; + + params_arr = selected_nodes.map(node => `node_ids=${node.id}`); + params_str = params_arr.join('&'); + + response = await this._get('/nodes/download/', params_str); + + file_name = extract_file_name(response, 'fallback.zip'); + blob = await response.blob(); + + insert_blob(file_name, blob); } async nodesMove(data) { @@ -84,29 +91,20 @@ export default class Requests extends Service { } async search(query) { - let url; - - url = `${base_url()}/search/?q=${query}`; - - return fetch(url, { - method: 'GET', - headers: this.headers - }); + return this._get('/search/', `q=${query}`); } async preferences({section_name}={}) { - let url; + let params = {}; if (section_name) { - url = `${base_url()}/preferences/?section=${section_name}` - } else { - url = `${base_url()}/preferences/`; + params = {'section': section_name}; } - return fetch(url, { - method: 'GET', - headers: this.headers - }); + return this._get( + '/preferences/', + new URLSearchParams(params).toString() + ); } async preferencesUpdate(data) { @@ -143,6 +141,23 @@ export default class Requests extends Service { }); } + async _get(url, params_str) { + let url_with_base, + headers_copy = {}; + + if (params_str) { + url_with_base = `${base_url()}${url}?${params_str}`; + } else { + url_with_base = `${base_url()}${url}`; + } + Object.assign(headers_copy, this.headers); + + return fetch(url_with_base, { + method: 'GET', + headers: headers_copy, + }); + } + @computed('session.{data.authenticated.token,isAuthenticated}') get headers() { let _headers = {}, diff --git a/app/utils/index.js b/app/utils/index.js index c86c1f5..6a23321 100644 --- a/app/utils/index.js +++ b/app/utils/index.js @@ -113,9 +113,52 @@ function ws_base_url() { return `${ENV.APP.WS_HOST}/${ENV.APP.WS_NAMESPACE}`; } +/** + * Extracts file name from a response with accessible Content-Disposition header + */ +function extract_file_name(response, fallback) { + let file_name = fallback, + content_disp, + match; + + content_disp = response.headers.get('content-disposition'); + + if (content_disp) { + match = content_disp.match('filename=(.*)$'); + if (match) { + file_name = match[1]; + } + } else { + console.warn('Could not read content disposition header'); + console.warn('Returning default file name'); + } + + return file_name; +} + +/** + * Insert a blob data into DOM and prompt use to download it + */ +function insert_blob(file_name, blob) { + let url, a; + + url = window.URL.createObjectURL(blob); + a = document.createElement('a'); + a.href = url; + a.download = file_name; + // we need to append the element to the dom -> otherwise it will not + // work in firefox + document.body.appendChild(a); + a.click(); + //afterwards we remove the element again + a.remove(); +} + export { group_perms_by_model, are_sets_equal, base_url, - ws_base_url + ws_base_url, + insert_blob, + extract_file_name }; -- GitLab