Skip to content
Snippets Groups Projects
Unverified Commit b99e8c19 authored by Bekerle, Patrizio's avatar Bekerle, Patrizio :fire:
Browse files

Start qualified-pdf-upload implementation (#4)

parent 2cd25d34
No related branches found
No related tags found
No related merge requests found
Pipeline #9951 passed with warnings
{
"element": "vpu-qualified-signature-pdf-upload",
"module_src": "vpu-qualified-signature-pdf-upload.js",
"routing_name": "qualified-pdf-upload",
"name": {
"de": "Persönliche aufbringen",
"en": "Qualifiedly sign"
},
"short_name": {
"de": "Persönliche Signatur aufbringen",
"en": "Qualifiedly sign"
},
"description": {
"de": "Erlaubt das Hochladen von PDF Dateien um sie mit einer persönlichen Signatur zu versehen",
"en": "Allows upload of PDF files to qualifiedly sign them"
}
}
...@@ -13,7 +13,8 @@ ...@@ -13,7 +13,8 @@
}, },
"routing_name": "signature", "routing_name": "signature",
"activities": [ "activities": [
{"path": "vpu-official-signature-pdf-upload.metadata.json"} {"path": "vpu-official-signature-pdf-upload.metadata.json"},
{"path": "vpu-qualified-signature-pdf-upload.metadata.json"}
], ],
"attributes": [] "attributes": []
} }
\ No newline at end of file
...@@ -143,6 +143,7 @@ export default { ...@@ -143,6 +143,7 @@ export default {
input: (build != 'test') ? [ input: (build != 'test') ? [
'src/vpu-signature.js', 'src/vpu-signature.js',
'src/vpu-official-signature-pdf-upload.js', 'src/vpu-official-signature-pdf-upload.js',
'src/vpu-qualified-signature-pdf-upload.js',
] : glob.sync('test/**/*.js'), ] : glob.sync('test/**/*.js'),
output: { output: {
dir: 'dist', dir: 'dist',
......
...@@ -13,6 +13,20 @@ ...@@ -13,6 +13,20 @@
"re-upload-all-button": "Alle erneut hochladen", "re-upload-all-button": "Alle erneut hochladen",
"re-upload-all-button-title": "Alle fehlgeschlagen Uploads erneut hochladen" "re-upload-all-button-title": "Alle fehlgeschlagen Uploads erneut hochladen"
}, },
"qualified-pdf-upload": {
"upload-field-label": "PDF Dateien zum Signieren hochladen",
"upload-area-text": "Sie können in diesem Bereich PDF Dateien per Drag & Drop oder per Direktauswahl hochladen",
"signed-files-label": "Signierte Dateien",
"download-zip-button": "Als ZIP Datei herunterladen",
"download-zip-button-tooltip": "Alle signierten Dateien als ZIP Datei herunterladen",
"upload-button-label": "PDF Dateien auswählen",
"download-file-button-title": "Signiertes PDF herunterladen",
"error-files-label": "Fehlgeschlagene Signiervorgänge",
"re-upload-file-button-title": "Erneut hochladen",
"upload-status-file-text": "({{fileSize}}) wird hochgeladen und verarbeitet...",
"re-upload-all-button": "Alle erneut hochladen",
"re-upload-all-button-title": "Alle fehlgeschlagen Uploads erneut hochladen"
},
"error-summary": "Ein Fehler ist aufgetreten", "error-summary": "Ein Fehler ist aufgetreten",
"error-permission-message": "Sie müssen das Recht auf Amtssignaturen besitzen um diese Funktion nutzen zu können!", "error-permission-message": "Sie müssen das Recht auf Amtssignaturen besitzen um diese Funktion nutzen zu können!",
"error-login-message": "Sie müssen eingeloggt sein um diese Funktion nutzen zu können!" "error-login-message": "Sie müssen eingeloggt sein um diese Funktion nutzen zu können!"
......
...@@ -13,6 +13,20 @@ ...@@ -13,6 +13,20 @@
"re-upload-all-button": "Upload all", "re-upload-all-button": "Upload all",
"re-upload-all-button-title": "Upload all failed uploads again" "re-upload-all-button-title": "Upload all failed uploads again"
}, },
"qualified-pdf-upload": {
"upload-field-label": "Upload PDF files to sign",
"upload-area-text": "In this area you can upload PDF files via Drag & Drop or by selecting them directly",
"signed-files-label": "Signed files",
"download-zip-button": "Download ZIP",
"download-zip-button-tooltip": "Download all signed files as ZIP file",
"upload-button-label": "Select PDF files",
"download-file-button-title": "Download signed PDF",
"error-files-label": "Failed signing processes",
"re-upload-file-button-title": "Upload again",
"upload-status-file-text": "({{fileSize}}) is currently uploading and being processed...",
"re-upload-all-button": "Upload all",
"re-upload-all-button-title": "Upload all failed uploads again"
},
"error-summary": "An error occurred", "error-summary": "An error occurred",
"error-permission-message": "You need have permissions to use the official signature to use this function!", "error-permission-message": "You need have permissions to use the official signature to use this function!",
"error-login-message": "You need to be logged in to use this function!" "error-login-message": "You need to be logged in to use this function!"
......
import {createI18nInstance} from './i18n.js';
import {humanFileSize} from 'vpu-common/i18next.js';
import {css, html} from 'lit-element';
import VPUSignatureLitElement from "./vpu-signature-lit-element";
import * as commonUtils from 'vpu-common/utils';
import * as utils from './utils';
import JSZip from 'jszip/dist/jszip.js';
import 'file-saver';
import * as commonStyles from 'vpu-common/styles';
import {classMap} from 'lit-html/directives/class-map.js';
import 'vpu-file-upload';
const i18n = createI18nInstance();
class QualifiedSignaturePdfUpload extends VPUSignatureLitElement {
constructor() {
super();
this.lang = i18n.language;
this.entryPointUrl = commonUtils.getAPiUrl();
this.signingUrl = this.entryPointUrl + "/qualifiedly_signed_documents/sign";
this.signedFiles = [];
this.signedFilesCount = 0;
this.errorFiles = [];
this.errorFilesCount = 0;
this.uploadInProgress = false;
this.uploadStatusFileName = "";
this.uploadStatusText = "";
}
static get properties() {
return {
lang: { type: String },
entryPointUrl: { type: String, attribute: 'entry-point-url' },
signedFiles: { type: Array, attribute: false },
signedFilesCount: { type: Number, attribute: false },
errorFiles: { type: Array, attribute: false },
errorFilesCount: { type: Number, attribute: false },
uploadInProgress: { type: Boolean, attribute: false },
uploadStatusFileName: { type: String, attribute: false },
uploadStatusText: { type: String, attribute: false },
};
}
connectedCallback() {
super.connectedCallback();
this.updateComplete.then(()=>{
const fileUpload = this._("#file-upload");
fileUpload.addEventListener('vpu-fileupload-all-start', this.onAllUploadStarted.bind(this));
fileUpload.addEventListener('vpu-fileupload-file-start', this.onFileUploadStarted.bind(this));
fileUpload.addEventListener('vpu-fileupload-file-finished', this.onFileUploadFinished.bind(this));
fileUpload.addEventListener('vpu-fileupload-all-finished', this.onAllUploadFinished.bind(this));
});
}
/**
* @param ev
*/
onAllUploadStarted(ev) {
console.log("Start upload process!");
this.uploadInProgress = true;
}
/**
* @param ev
*/
onFileUploadStarted(ev) {
console.log(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),
});
}
/**
* @param ev
*/
onFileUploadFinished(ev) {
if (ev.detail.status !== 201) {
// this doesn't seem to trigger an update() execution
this.errorFiles[Math.floor(Math.random() * 1000000)] = ev.detail;
// this triggers the correct update() execution
this.errorFilesCount++;
} else if (ev.detail.json["@type"] === "http://schema.org/MediaObject" ) {
// this doesn't seem to trigger an update() execution
this.signedFiles.push(ev.detail.json);
// this triggers the correct update() execution
this.signedFilesCount++;
}
}
/**
* @param ev
*/
onAllUploadFinished(ev) {
console.log("Finished upload process!");
this.uploadInProgress = false;
}
update(changedProperties) {
changedProperties.forEach((oldValue, propName) => {
if (propName === "lang") {
i18n.changeLanguage(this.lang);
}
console.log(propName, oldValue);
});
super.update(changedProperties);
}
onLanguageChanged(e) {
this.lang = e.detail.lang;
}
/**
* Download signed pdf files as zip
*/
zipDownloadClickHandler() {
// see: https://stuk.github.io/jszip/
let zip = new JSZip();
const that = this;
let fileNames = [];
// add all signed pdf files
this.signedFiles.forEach((file) => {
let fileName = file.name;
// add pseudo-random string on duplicate file name
if (fileNames.indexOf(fileName) !== -1) {
fileName = utils.baseName(fileName) + "-" + Math.random().toString(36).substring(7) + ".pdf";
}
fileNames.push(fileName);
zip.file(fileName, utils.getPDFFileBase64Content(file), {base64: true});
});
zip.generateAsync({type:"blob"})
.then(function(content) {
// save with FileSaver.js
// see: https://github.com/eligrey/FileSaver.js
saveAs(content, "signed-documents.zip");
that._("#zip-download-button").stop();
});
}
/**
* Re-Upload all failed files
*/
reUploadAllClickHandler() {
const that = this;
// we need to make a copy and reset the queue or else our queue will run crazy
const errorFilesCopy = {...this.errorFiles};
this.errorFiles = [];
this.errorFilesCount = 0;
commonUtils.asyncObjectForEach(errorFilesCopy, async (file, id) => {
await this.fileUploadClickHandler(file.file, id);
});
that._("#re-upload-all-button").stop();
}
/**
* Download one signed pdf file
*
* @param file
*/
fileDownloadClickHandler(file) {
const arr = utils.convertDataURIToBinary(file.contentUrl);
const blob = new Blob([arr], { type: utils.getDataURIContentType(file.contentUrl) });
// see: https://github.com/eligrey/FileSaver.js
saveAs(blob, file.name);
}
/**
* Uploads a failed pdf file again
*
* @param file
* @param id
*/
async fileUploadClickHandler(file, id) {
this.uploadInProgress = true;
this.errorFiles.splice(id, 1);
this.errorFilesCount = this.errorFiles.length;
await this._("#file-upload").uploadFile(file);
this.uploadInProgress = false;
}
static get styles() {
// language=css
return css`
${commonStyles.getThemeCSS()}
${commonStyles.getGeneralCSS()}
${commonStyles.getButtonCSS()}
${commonStyles.getNotificationCSS()}
h2 {
margin-bottom: inherit;
}
.hidden {
display: none;
}
.files-block .file {
margin: 10px 0;
}
.error-files .file {
display: grid;
grid-template-columns: 40px auto;
}
.files-block .file .button-box {
display: flex;
align-items: center;
}
.files-block .file .info {
display: inline-block;
vertical-align: middle;
}
.file .info strong {
font-weight: 600;
}
.notification vpu-mini-spinner {
position: relative;
top: 2px;
margin-right: 5px;
}
.error {
color: #e4154b;
}
`;
}
getSignedFilesHtml() {
return this.signedFiles.map(file => html`
<div class="file">
<a class="is-download"
title="${i18n.t('qualified-pdf-upload.download-file-button-title')}"
@click="${() => {this.fileDownloadClickHandler(file);}}">
${file.name} (${humanFileSize(file.contentSize)}) <vpu-icon name="download"></vpu-icon></a>
</div>
`);
}
getErrorFilesHtml() {
return this.errorFiles.map((data, id) => html`
<div class="file">
<div class="button-box">
<button class="button is-small"
title="${i18n.t('qualified-pdf-upload.re-upload-file-button-title')}"
@click="${() => {this.fileUploadClickHandler(data.file, id);}}"><vpu-icon name="reload"></vpu-icon></button>
</div>
<div class="info">
${data.file.name} (${humanFileSize(data.file.size)})
<strong class="error">${data.json["hydra:description"]}</strong>
</div>
</div>
`);
}
render() {
return html`
<div class="${classMap({hidden: !this.isLoggedIn() || !this.hasSignaturePermissions()})}">
<div class="field">
<h2>${i18n.t('qualified-pdf-upload.upload-field-label')}</h2>
<div class="control">
<vpu-fileupload id="file-upload" lang="${this.lang}" url="${this.signingUrl}" accept="application/pdf"
text="${i18n.t('qualified-pdf-upload.upload-area-text')}" button-label="${i18n.t('qualified-pdf-upload.upload-button-label')}"></vpu-fileupload>
</div>
</div>
<div class="field notification is-info ${classMap({hidden: !this.uploadInProgress})}">
<vpu-mini-spinner></vpu-mini-spinner>
<strong>${this.uploadStatusFileName}</strong>
${this.uploadStatusText}
</div>
<div class="files-block field ${classMap({hidden: this.signedFilesCount === 0})}">
<h2>${i18n.t('qualified-pdf-upload.signed-files-label')}</h2>
<div class="control">
${this.getSignedFilesHtml()}
</div>
</div>
<div class="field ${classMap({hidden: this.signedFilesCount === 0})}">
<div class="control">
<vpu-button id="zip-download-button" value="${i18n.t('qualified-pdf-upload.download-zip-button')}" title="${i18n.t('qualified-pdf-upload.download-zip-button-tooltip')}" @click="${this.zipDownloadClickHandler}" type="is-primary"></vpu-button>
</div>
</div>
<div class="files-block error-files field ${classMap({hidden: this.errorFilesCount === 0})}">
<h2 class="error">${i18n.t('qualified-pdf-upload.error-files-label')}</h2>
<div class="control">
${this.getErrorFilesHtml()}
</div>
</div>
<div class="field ${classMap({hidden: this.errorFilesCount === 0})}">
<div class="control">
<vpu-button id="re-upload-all-button" ?disabled="${this.uploadInProgress}" value="${i18n.t('qualified-pdf-upload.re-upload-all-button')}" title="${i18n.t('qualified-pdf-upload.re-upload-all-button-title')}" @click="${this.reUploadAllClickHandler}" type="is-primary"></vpu-button>
</div>
</div>
</div>
<div class="notification is-warning ${classMap({hidden: this.isLoggedIn()})}">
${i18n.t('error-login-message')}
</div>
<div class="notification is-danger ${classMap({hidden: this.hasSignaturePermissions() || !this.isLoggedIn()})}">
${i18n.t('error-permission-message')}
</div>
`;
}
}
commonUtils.defineCustomElement('vpu-qualified-signature-pdf-upload', QualifiedSignaturePdfUpload);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment