From f6a09c4904eaa880ea36f6fe7330dffef28d53ad Mon Sep 17 00:00:00 2001 From: Patrizio Bekerle <patrizio@bekerle.com> Date: Fri, 26 Jun 2020 10:07:18 +0200 Subject: [PATCH] Integration of vpu-file-source and implementation of queuing and uploading for qualified signing (#28) --- src/vpu-qualified-signature-pdf-upload.js | 106 ++++------------ src/vpu-signature-lit-element.js | 142 ++++++++++++++++++++++ vendor/file-handling | 2 +- 3 files changed, 170 insertions(+), 80 deletions(-) diff --git a/src/vpu-qualified-signature-pdf-upload.js b/src/vpu-qualified-signature-pdf-upload.js index 6bd8c3c..b722775 100644 --- a/src/vpu-qualified-signature-pdf-upload.js +++ b/src/vpu-qualified-signature-pdf-upload.js @@ -6,11 +6,11 @@ import VPUSignatureLitElement from "./vpu-signature-lit-element"; import {PdfPreview} from "./vpu-pdf-preview"; import * as commonUtils from 'vpu-common/utils'; import * as utils from './utils'; -import {Icon, MiniSpinner, Button} from 'vpu-common'; +import {Button, Icon, MiniSpinner} from 'vpu-common'; import FileSaver from 'file-saver'; import * as commonStyles from 'vpu-common/styles'; import {classMap} from 'lit-html/directives/class-map.js'; -import {FileUpload} from 'vpu-file-handling'; +import {FileSource} from 'vpu-file-handling'; import JSONLD from "vpu-common/jsonld"; import {TextSwitch} from './textswitch.js'; import nextcloudWebAppPasswordURL from 'consts:nextcloudWebAppPasswordURL'; @@ -29,17 +29,12 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(VPUSignatureLitEle this.signedFilesCount = 0; this.errorFiles = []; this.errorFilesCount = 0; - this.uploadInProgress = false; - this.queueingInProgress = false; this.uploadStatusFileName = ""; this.uploadStatusText = ""; this.currentFile = {}; this.currentFileName = ""; this.currentFilePlacementMode = ""; this.currentFileSignaturePlacement = {}; - this.queueBlockEnabled = false; - this.queuedFiles = []; - this.queuedFilesCount = 0; this.signingProcessEnabled = false; this.signingProcessActive = false; this.signaturePlacementInProgress = false; @@ -48,10 +43,6 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(VPUSignatureLitEle this.queuedFilesPlacementModes = []; this.currentPreviewQueueKey = ''; - // will be set in function update - this.signingRequestUrl = ""; - this.signingUrl = ""; - this._onReceiveIframeMessage = this.onReceiveIframeMessage.bind(this); this._onReceiveBeforeUnload = this.onReceiveBeforeUnload.bind(this); } @@ -59,7 +50,7 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(VPUSignatureLitEle static get scopedElements() { return { 'vpu-icon': Icon, - 'vpu-fileupload': FileUpload, + 'vpu-file-source': FileSource, 'vpu-pdf-preview': PdfPreview, 'vpu-mini-spinner': MiniSpinner, 'vpu-button': Button, @@ -77,7 +68,6 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(VPUSignatureLitEle errorFiles: { type: Array, attribute: false }, errorFilesCount: { type: Number, attribute: false }, uploadInProgress: { type: Boolean, attribute: false }, - queueingInProgress: { type: Boolean, attribute: false }, uploadStatusFileName: { type: String, attribute: false }, uploadStatusText: { type: String, attribute: false }, externalAuthInProgress: { type: Boolean, attribute: false }, @@ -114,14 +104,6 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(VPUSignatureLitEle super.disconnectedCallback(); } - onQueuedFilesChanged(ev) { - const detail = ev.detail; - if (!this.queueBlockEnabled && detail.queuedFilesCount) - this.queueBlockEnabled = true; - this.queuedFiles = detail.queuedFiles; - this.queuedFilesCount = detail.queuedFilesCount; - } - /** * Processes queued files */ @@ -160,14 +142,17 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(VPUSignatureLitEle } } - await this._("#file-upload").uploadFile(file, params); + this.uploadStatusText = i18n.t('qualified-pdf-upload.upload-status-file-text', { + fileName: file.name, + fileSize: humanFileSize(file.size, false), + }); + + await this.uploadFile(file, params); this.uploadInProgress = false; } storePDFData(event) { - const data = event.detail; - - this.queuedFilesSignaturePlacements[this.currentPreviewQueueKey] = data; + this.queuedFilesSignaturePlacements[this.currentPreviewQueueKey] = event.detail; this.signaturePlacementInProgress = false; } @@ -313,28 +298,9 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(VPUSignatureLitEle /** * @param ev */ - onAllUploadStarted(ev) { - console.log("Start queuing process!"); - this.queueingInProgress = true; - } - - /** - * @param ev - */ - onAllUploadFinished(ev) { - console.log("Finished queuing process!"); - this.queueingInProgress = false; - } - - /** - * @param ev - */ - onFileUploadStarted(ev) { - this.uploadStatusFileName = ev.detail.fileName; - this.uploadStatusText = i18n.t('qualified-pdf-upload.upload-status-file-text', { - fileName: ev.detail.fileName, - fileSize: humanFileSize(ev.detail.fileSize, false), - }); + onFileSelected(ev) { + console.log("File was selected: ev", ev); + this.queueFile(ev.detail.file); } addToErrorFiles(file) { @@ -351,14 +317,13 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(VPUSignatureLitEle } /** - * @param ev + * @param data */ - onFileUploadFinished(ev) { - if (ev.detail.status !== 201) { - this.addToErrorFiles(ev.detail); - } else if (ev.detail.json["@type"] === "http://schema.org/EntryPoint" ) { + onFileUploadFinished(data) { + if (data.status !== 201) { + this.addToErrorFiles(data); + } else if (data.json["@type"] === "http://schema.org/EntryPoint" ) { // after the "real" upload we immediately start with the 2FA process - const data = ev.detail; // show the iframe and lock processing this.externalAuthInProgress = true; @@ -384,8 +349,7 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(VPUSignatureLitEle case "entryPointUrl": JSONLD.initialize(this.entryPointUrl, (jsonld) => { const apiUrlBase = jsonld.getApiUrlForEntityName("QualifiedSigningRequest"); - this.signingRequestUrl = apiUrlBase + "/create"; - this.signingUrl = apiUrlBase + "/sign"; + this.fileSourceUrl = apiUrlBase + "/create"; }); break; } @@ -467,7 +431,7 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(VPUSignatureLitEle async fileQueueingClickHandler(file, id) { this.takeFailedFileFromQueue(id); - return this._("#file-upload").queueFile(file); + return this.queueFile(file); } /** @@ -481,7 +445,7 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(VPUSignatureLitEle return; } - const file = this._("#file-upload").getQueuedFile(key); + const file = this.getQueuedFile(key); this.currentFile = file; this.currentPreviewQueueKey = key; console.log(file); @@ -496,15 +460,6 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(VPUSignatureLitEle this.queuedFilesSignaturePlacements[key]); } - /** - * Takes a file off of the queue - * - * @param key - */ - takeFileFromQueue(key) { - return this._("#file-upload").takeFileFromQueue(key); - } - /** * Takes a failed file off of the queue * @@ -520,7 +475,7 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(VPUSignatureLitEle clearQueuedFiles() { this.queuedFilesSignaturePlacements = []; this.queuedFilesPlacementModes = []; - this._("#file-upload").clearQueuedFiles(); + super.clearQueuedFiles(); } clearSignedFiles() { @@ -902,7 +857,7 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(VPUSignatureLitEle this.signingProcessActive = false; if (this.currentFile.file !== undefined) { - const key = await this._("#file-upload").queueFile(this.currentFile.file); + const key = await this.queueFile(this.currentFile.file); // set placement mode and parameters so they are restore when canceled this.queuedFilesPlacementModes[key] = this.currentFilePlacementMode; @@ -919,7 +874,7 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(VPUSignatureLitEle <div class="field ${classMap({"is-disabled": this.isUserInterfaceDisabled()})}"> <h2>${i18n.t('qualified-pdf-upload.upload-field-label')}</h2> <div class="control"> - <vpu-fileupload id="file-upload" + <vpu-file-source id="file-upload" allowed-mime-types="application/pdf" nextcloud-auth-url="${showTestNextcloudFilePicker ? nextcloudWebAppPasswordURL : ""}" nextcloud-web-dav-url="${nextcloudWebDavURL}" @@ -927,16 +882,12 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(VPUSignatureLitEle always-send-file deferred lang="${this.lang}" - url="${this.signingRequestUrl}" + url="${this.fileSourceUrl}" ?disabled="${this.signingProcessActive}" text="${i18n.t('qualified-pdf-upload.upload-area-text')}" button-label="${i18n.t('qualified-pdf-upload.upload-button-label')}" - @vpu-fileupload-all-start="${this.onAllUploadStarted}" - @vpu-fileupload-file-start="${this.onFileUploadStarted}" - @vpu-fileupload-file-finished="${this.onFileUploadFinished}" - @vpu-fileupload-all-finished="${this.onAllUploadFinished}" - @vpu-fileupload-queued-files-changed="${this.onQueuedFilesChanged}" - ></vpu-fileupload> + @vpu-file-source-file-selected="${this.onFileSelected}" + ></vpu-file-source> </div> </div> <div id="grid-container"> @@ -945,9 +896,6 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(VPUSignatureLitEle <!-- Queued files headline and queueing spinner --> <h2 class="${classMap({"is-disabled": this.isUserInterfaceDisabled()})}"> ${i18n.t('qualified-pdf-upload.queued-files-label')} - <vpu-mini-spinner id="queueing-in-progress-spinner" - style="font-size: 0.7em" - class="${classMap({hidden: !this.queueingInProgress})}"></vpu-mini-spinner> </h2> <!-- Buttons to start/stop signing process and clear queue --> <div class="control field"> diff --git a/src/vpu-signature-lit-element.js b/src/vpu-signature-lit-element.js index b7058c9..43df839 100644 --- a/src/vpu-signature-lit-element.js +++ b/src/vpu-signature-lit-element.js @@ -2,6 +2,148 @@ import {LitElement} from "lit-element"; import {EventBus} from 'vpu-common'; export default class VPUSignatureLitElement extends LitElement { + constructor() { + super(); + this.queuedFiles = []; + this.queuedFilesCount = 0; + this.uploadInProgress = false; + this.queueBlockEnabled = false; + this._queueKey = 0; + this.fileSourceUrl = ""; + + // will be set in function update + this.fileSourceUrl = ""; + } + + /** + * @param file + * @returns {Promise<number>} key of the queued item + */ + async queueFile(file) { + this._queueKey++; + const key = this._queueKey; + this.queuedFiles[key] = file; + this.updateQueuedFilesCount(); + console.log("file", file); + + const data = {"file": file}; + const event = new CustomEvent("vpu-fileupload-file-queued", { "detail": data, bubbles: true, composed: true }); + this.dispatchEvent(event); + + return key; + } + + /** + * Takes a file off of the queue + * + * @param key + */ + takeFileFromQueue(key) { + const file = this.queuedFiles[key]; + delete this.queuedFiles[key]; + this.updateQueuedFilesCount(); + + return file; + } + + uploadOneQueuedFile() { + const file = this.takeFileFromQueue(); + + return this.uploadFile(file); + } + + getQueuedFile(key) { + return this.queuedFiles[key]; + } + + getQueuedFiles() { + return this.queuedFiles; + } + + clearQueuedFiles() { + this.queuedFiles = []; + this.updateQueuedFilesCount(); + } + + updateQueuedFilesCount() { + this.queuedFilesCount = Object.keys(this.queuedFiles).length; + + if (!this.queueBlockEnabled && this.queuedFilesCount > 0) { + this.queueBlockEnabled = true; + } + + return this.queuedFilesCount; + } + + getQueuedFilesCount() { + return this.queuedFilesCount; + } + + /** + * @param file + * @param params + * @returns {Promise<void>} + */ + async uploadFile(file, params = {}) { + this.uploadInProgress = true; + this.uploadStatusFileName = file.name; + let url = new URL(this.fileSourceUrl); + url.search = new URLSearchParams(params).toString(); + let formData = new FormData(); + formData.append('file', file); + + // I got a 60s timeout in Google Chrome and found no way to increase that + await fetch(url, { + method: 'POST', + headers: { + 'Authorization': 'Bearer ' + window.VPUAuthToken, + }, + body: formData + }) + .then((response) => { + /* Done. Inform the user */ + console.log(`Status: ${response.status} for file ${file.name}`); + this.sendFinishedEvent(response, file); + }) + .catch((response) => { + /* Error. Inform the user */ + console.log(`Error status: ${response.status} for file ${file.name}`); + this.sendFinishedEvent(response, file); + }); + + this.uploadInProgress = false; + } + + async sendFinishedEvent(response, file) { + if (response === undefined) { + return; + } + + let data = { + fileName: file.name, + status: response.status, + json: {"hydra:description": ""} + }; + + try { + await response.json().then((json) => { + data.json = json; + }); + } catch (e) {} + + data.file = file; + + // const event = new CustomEvent("vpu-fileupload-file-finished", { "detail": data, bubbles: true, composed: true }); + // this.dispatchEvent(event); + this.onFileUploadFinished(data); + } + + /** + * @param data + */ + onFileUploadFinished(data) { + console.log("Override me"); + } _(selector) { return this.shadowRoot === null ? this.querySelector(selector) : this.shadowRoot.querySelector(selector); diff --git a/vendor/file-handling b/vendor/file-handling index dedb1f6..764b144 160000 --- a/vendor/file-handling +++ b/vendor/file-handling @@ -1 +1 @@ -Subproject commit dedb1f6176ce97305826e62a6635beaada222a56 +Subproject commit 764b144fc8762cae42b221524147d34e90a8f5fa -- GitLab