diff --git a/src/dbp-official-signature-pdf-upload.js b/src/dbp-official-signature-pdf-upload.js index 10f6a1fc730d732d62e61c5024e39965d8d633f4..481e7997cfeab0e87c425af4eb3e6e5bef852964 100644 --- a/src/dbp-official-signature-pdf-upload.js +++ b/src/dbp-official-signature-pdf-upload.js @@ -17,6 +17,8 @@ import nextcloudWebDavURL from 'consts:nextcloudWebDavURL'; import nextcloudName from 'consts:nextcloudName'; import {FileSink} from "@dbp-toolkit/file-handling"; import {name as pkgName} from './../package.json'; +import {getPDFSignatureCount} from './utils.js'; +import {send as notify} from '@dbp-toolkit/common/notification'; const i18n = createI18nInstance(); @@ -42,6 +44,7 @@ class OfficialSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitElem this.withSigBlock = false; this.queuedFilesSignaturePlacements = []; this.queuedFilesPlacementModes = []; + this.queuedFilesMissingPlacement = new Map(); this.currentPreviewQueueKey = ''; } @@ -87,6 +90,17 @@ class OfficialSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitElem setInterval(() => { this.handleQueuedFiles(); }, 1000); } + async _updateMissingPlacementStatus(id) { + let file = this.queuedFiles[id]; + let isManual = this.queuedFilesPlacementModes[id] === 'manual'; + this.queuedFilesMissingPlacement.delete(id); + if (!isManual) { + let sigCount = await getPDFSignatureCount(file); + if (sigCount > 0) + this.queuedFilesMissingPlacement.set(id, true); + } + } + /** * Processes queued files */ @@ -101,13 +115,28 @@ class OfficialSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitElem if (!this.signingProcessEnabled || this.uploadInProgress) { return; } - this.signaturePlacementInProgress = false; - const key = Object.keys(this.queuedFiles)[0]; + // Validate that all PDFs with a signature have manual placement + this.queuedFilesMissingPlacement.clear(); + for (const key of Object.keys(this.queuedFiles)) { + await this._updateMissingPlacementStatus(key); + } + + // Some have a signature but are not "manual", stop everything + if (this.queuedFilesMissingPlacement.size) { + notify({ + "body": i18n.t('error-manual-positioning-missing'), + "type": "danger", + }); + this.signingProcessEnabled = false; + this.signingProcessActive = false; + return; + } // take the file off the queue - let file = this.takeFileFromQueue(key); + const key = Object.keys(this.queuedFiles)[0]; + const file = this.takeFileFromQueue(key); this.currentFile = file; // set placement mode and parameters to restore them when canceled @@ -136,7 +165,13 @@ class OfficialSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitElem } storePDFData(event) { - this.queuedFilesSignaturePlacements[this.currentPreviewQueueKey] = event.detail; + let placement = event.detail; + let placementMode = 'manual'; + + let key = this.currentPreviewQueueKey; + this.queuedFilesSignaturePlacements[key] = placement; + this.queuedFilesPlacementModes[key] = placementMode; + this.queuedFilesMissingPlacement.delete(key); this.signaturePlacementInProgress = false; } @@ -208,6 +243,13 @@ class OfficialSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitElem this.queueFile(ev.detail.file); } + async queueFile(file) { + let id = await super.queueFile(file); + await this._updateMissingPlacementStatus(id); + this.requestUpdate(); + return id; + } + addToErrorFiles(file) { this.endSigningProcessIfQueueEmpty(); @@ -286,8 +328,6 @@ class OfficialSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitElem that._("#re-upload-all-button").stop(); } - - /** * Queues a failed pdf-file again * @@ -338,6 +378,7 @@ class OfficialSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitElem clearQueuedFiles() { this.queuedFilesSignaturePlacements = []; this.queuedFilesPlacementModes = []; + this.queuedFilesMissingPlacement.clear(); super.clearQueuedFiles(); } @@ -425,7 +466,7 @@ class OfficialSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitElem .error, #cancel-signing-process { color: #e4154b; } - + #cancel-signing-process:hover { color: white; } @@ -492,9 +533,14 @@ class OfficialSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitElem .file-block div.bottom-line { display: grid; align-items: center; - grid-template-columns: auto 190px; - grid-gap: 10px; - margin-top: 10px; + grid-template-columns: auto auto; + grid-gap: 6px; + margin-top: 6px; + } + + .file-block .error-line { + margin-top: 6px; + color: var(--dbp-override-danger-bg-color); } .file-block.error div.bottom-line { @@ -558,6 +604,10 @@ class OfficialSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitElem border-top: 1px solid black; } + .placement-missing { + border: solid 2px var(--dbp-override-danger-bg-color); + } + /* Handling for small displays (like mobile devices) */ @media (max-width: 680px) { /* Modal preview, upload and external auth */ @@ -590,7 +640,6 @@ class OfficialSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitElem max-width: inherit; } } - `; } @@ -605,6 +654,7 @@ class OfficialSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitElem ids.forEach((id) => { const file = this.queuedFiles[id]; + const placementMissing = this.queuedFilesMissingPlacement.get(id); results.push(html` <div class="file-block"> @@ -625,12 +675,17 @@ class OfficialSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitElem <dbp-textswitch name1="auto" name2="manual" name="${this.queuedFilesPlacementModes[id] || "auto"}" - class="switch" + class="${classMap({'placement-missing': placementMissing, 'switch': true})}" value1="${i18n.t('official-pdf-upload.positioning-automatic')}" value2="${i18n.t('official-pdf-upload.positioning-manual')}" ?disabled="${this.signingProcessEnabled}" @change=${ (e) => this.queuePlacementSwitch(id, e.target.name) }></dbp-textswitch> </div> + <div class="error-line"> + ${ placementMissing ? html` + ${i18n.t('label-manual-positioning-missing')} + ` : '' } + </div> </div> `); }); diff --git a/src/dbp-pdf-preview.js b/src/dbp-pdf-preview.js index d89e0a3376bd9033b176373c008a89df47d737b3..7821e253bcdcd2f5293a07d2b4f91fc0ff1be3a5 100644 --- a/src/dbp-pdf-preview.js +++ b/src/dbp-pdf-preview.js @@ -10,7 +10,7 @@ import * as commonStyles from '@dbp-toolkit/common/styles'; import pdfjs from 'pdfjs-dist/es5/build/pdf.js'; import buildinfo from 'consts:buildinfo'; import {name as pkgName} from './../package.json'; -import {getPDFSignatureCount, readBinaryFileContent} from './utils.js'; +import {readBinaryFileContent} from './utils.js'; const i18n = createI18nInstance(); @@ -232,8 +232,6 @@ export class PdfPreview extends ScopedElementsMixin(DBPLitElement) { // fix width adaption after "this.isPageLoaded = true" await this.showPage(page); - - console.log(`Signature count: ${await getPDFSignatureCount(file)}`); } getSignatureRect() { diff --git a/src/dbp-qualified-signature-pdf-upload.js b/src/dbp-qualified-signature-pdf-upload.js index f382da9e04c3dda087ad3653ea4152bf6090d938..ce4fb3c4c4a31e85505dbd36e8048f376b943947 100644 --- a/src/dbp-qualified-signature-pdf-upload.js +++ b/src/dbp-qualified-signature-pdf-upload.js @@ -17,6 +17,8 @@ import nextcloudWebDavURL from 'consts:nextcloudWebDavURL'; import nextcloudName from 'consts:nextcloudName'; import {FileSink} from "@dbp-toolkit/file-handling"; import {name as pkgName} from './../package.json'; +import {getPDFSignatureCount} from './utils.js'; +import {send as notify} from '@dbp-toolkit/common/notification'; const i18n = createI18nInstance(); @@ -43,6 +45,7 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitEle this.withSigBlock = false; this.queuedFilesSignaturePlacements = []; this.queuedFilesPlacementModes = []; + this.queuedFilesMissingPlacement = new Map(); this.currentPreviewQueueKey = ''; this._onReceiveIframeMessage = this.onReceiveIframeMessage.bind(this); @@ -108,33 +111,58 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitEle super.disconnectedCallback(); } + async _updateMissingPlacementStatus(id) { + let file = this.queuedFiles[id]; + let isManual = this.queuedFilesPlacementModes[id] === 'manual'; + this.queuedFilesMissingPlacement.delete(id); + if (!isManual) { + let sigCount = await getPDFSignatureCount(file); + if (sigCount > 0) + this.queuedFilesMissingPlacement.set(id, true); + } + } + /** * Processes queued files */ async handleQueuedFiles() { + this.endSigningProcessIfQueueEmpty(); if (this.queuedFilesCount === 0) { // reset signingProcessEnabled button this.signingProcessEnabled = false; - return; } if (!this.signingProcessEnabled || this.externalAuthInProgress || this.uploadInProgress) { return; } - this.signaturePlacementInProgress = false; - const key = Object.keys(this.queuedFiles)[0]; + // Validate that all PDFs with a signature have manual placement + this.queuedFilesMissingPlacement.clear(); + for (const key of Object.keys(this.queuedFiles)) { + await this._updateMissingPlacementStatus(key); + } + + // Some have a signature but are not "manual", stop everything + if (this.queuedFilesMissingPlacement.size) { + notify({ + "body": i18n.t('error-manual-positioning-missing'), + "type": "danger", + }); + this.signingProcessEnabled = false; + this.signingProcessActive = false; + return; + } // take the file off the queue + const key = Object.keys(this.queuedFiles)[0]; const file = this.takeFileFromQueue(key); this.currentFile = file; // set placement mode and parameters to restore them when canceled this.currentFilePlacementMode = this.queuedFilesPlacementModes[key]; this.currentFileSignaturePlacement = this.queuedFilesSignaturePlacements[key]; - this.uploadInProgress = true; let params = {}; @@ -156,7 +184,13 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitEle } storePDFData(event) { - this.queuedFilesSignaturePlacements[this.currentPreviewQueueKey] = event.detail; + let placement = event.detail; + let placementMode = 'manual'; + + let key = this.currentPreviewQueueKey; + this.queuedFilesSignaturePlacements[key] = placement; + this.queuedFilesPlacementModes[key] = placementMode; + this.queuedFilesMissingPlacement.delete(key); this.signaturePlacementInProgress = false; } @@ -170,7 +204,6 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitEle if (this.queuedFilesSignaturePlacements[this.currentPreviewQueueKey] === undefined) { this.queuedFilesPlacementModes[this.currentPreviewQueueKey] = "auto"; } - this.signaturePlacementInProgress = false; } @@ -333,6 +366,13 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitEle this.queueFile(ev.detail.file); } + async queueFile(file) { + let id = await super.queueFile(file); + await this._updateMissingPlacementStatus(id); + this.requestUpdate(); + return id; + } + addToErrorFiles(file) { this.endSigningProcessIfQueueEmpty(); @@ -420,7 +460,6 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitEle */ async fileQueueingClickHandler(file, id) { this.takeFailedFileFromQueue(id); - return this.queueFile(file); } @@ -442,7 +481,6 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitEle // start signature placement process this.signaturePlacementInProgress = true; this.withSigBlock = withSigBlock; - const previewTag = this.constructor.getScopedTagName("dbp-pdf-preview"); await this._(previewTag).showPDF( file, @@ -458,13 +496,13 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitEle takeFailedFileFromQueue(key) { const file = this.errorFiles.splice(key, 1); this.errorFilesCount = Object.keys(this.errorFiles).length; - return file; } clearQueuedFiles() { this.queuedFilesSignaturePlacements = []; this.queuedFilesPlacementModes = []; + this.queuedFilesMissingPlacement.clear(); super.clearQueuedFiles(); } @@ -516,16 +554,6 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitEle display: none; } - #iframe { - width: 100%; - height: 240px; - /* "overflow" should not be supported by browsers, but some seem to use it */ - overflow: hidden; - border-width: 0; - /* keeps the A-Trust webpage aligned left */ - max-width: 575px; - } - .files-block.field:not(:last-child) { margin-bottom: 40px; } @@ -629,9 +657,14 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitEle .file-block div.bottom-line { display: grid; align-items: center; - grid-template-columns: auto 190px; - grid-gap: 10px; - margin-top: 10px; + grid-template-columns: auto auto; + grid-gap: 6px; + margin-top: 6px; + } + + .file-block .error-line { + margin-top: 6px; + color: var(--dbp-override-danger-bg-color); } .file-block.error div.bottom-line { @@ -651,7 +684,7 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitEle white-space: nowrap; } - #pdf-preview .button.is-cancel, #external-auth .button.is-cancel { + #pdf-preview .button.is-cancel { color: #e4154b; } @@ -659,6 +692,20 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitEle margin-top: 0.5em; } + #external-auth .button.is-cancel { + color: #e4154b; + } + + #iframe { + width: 100%; + height: 240px; + /* "overflow" should not be supported by browsers, but some seem to use it */ + overflow: hidden; + border-width: 0; + /* keeps the A-Trust webpage aligned left */ + max-width: 575px; + } + .is-right { float: right; } @@ -699,6 +746,10 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitEle border-top: 1px solid black; } + .placement-missing { + border: solid 2px var(--dbp-override-danger-bg-color); + } + /* Handling for small displays (like mobile devices) */ @media (max-width: 680px) { /* Modal preview, upload and external auth */ @@ -745,6 +796,7 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitEle ids.forEach((id) => { const file = this.queuedFiles[id]; + const placementMissing = this.queuedFilesMissingPlacement.get(id); results.push(html` <div class="file-block"> @@ -765,12 +817,17 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitEle <dbp-textswitch name1="auto" name2="manual" name="${this.queuedFilesPlacementModes[id] || "auto"}" - class="switch" + class="${classMap({'placement-missing': placementMissing, 'switch': true})}" value1="${i18n.t('qualified-pdf-upload.positioning-automatic')}" value2="${i18n.t('qualified-pdf-upload.positioning-manual')}" ?disabled="${this.signingProcessEnabled}" @change=${ (e) => this.queuePlacementSwitch(id, e.target.name) }></dbp-textswitch> </div> + <div class="error-line"> + ${ placementMissing ? html` + ${i18n.t('label-manual-positioning-missing')} + ` : '' } + </div> </div> `); }); @@ -881,7 +938,6 @@ class QualifiedSignaturePdfUpload extends ScopedElementsMixin(DBPSignatureLitEle class="button is-primary"> ${i18n.t('qualified-pdf-upload.upload-button-label')} </button> - <dbp-file-source id="file-source" context="${i18n.t('qualified-pdf-upload.upload-field-label')}" diff --git a/src/dbp-signature-lit-element.js b/src/dbp-signature-lit-element.js index 9dc0d57517bf5428e940b5d0dd93aa902403cea3..7d2931b7ef3825fcfbf3eaebbef787880c77623c 100644 --- a/src/dbp-signature-lit-element.js +++ b/src/dbp-signature-lit-element.js @@ -71,15 +71,14 @@ export default class DBPSignatureLitElement extends DBPSignatureBaseLitElement { /** * @param file - * @returns {Promise<number>} key of the queued item + * @returns {Promise<string>} key of the queued item */ async queueFile(file) { this._queueKey++; const key = this._queueKey; this.queuedFiles[key] = file; this.updateQueuedFilesCount(); - - return key; + return String(key); } /** diff --git a/src/i18n/de/translation.json b/src/i18n/de/translation.json index a6f13140b346e3b0d3e2c9e1f7f33f680a2cfd79..8745ea6c92faab31d897172e176f9a363d8712b3 100644 --- a/src/i18n/de/translation.json +++ b/src/i18n/de/translation.json @@ -114,5 +114,7 @@ "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-cancel-message": "Der Signaturprozess wurde manuell abgebrochen.", - "error-rights-message": "Abbruch auf Grund mangelnder Rechte Ihres Accounts." + "error-rights-message": "Abbruch auf Grund mangelnder Rechte Ihres Accounts.", + "label-manual-positioning-missing": "Bestehende Signatur vorhanden, bitte wählen Sie ihre Positionierung manuell.", + "error-manual-positioning-missing": "Ein oder mehrere Signaturen in der Warteschlange müssen manuell positioniert werden." } diff --git a/src/i18n/en/translation.json b/src/i18n/en/translation.json index 515ba26e2ef6f5a5df0242145fe0d7d1a8c31faf..4397f9991f4ab1a15c277e9939dcfc8f6a3cd9e3 100644 --- a/src/i18n/en/translation.json +++ b/src/i18n/en/translation.json @@ -113,5 +113,7 @@ "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-cancel-message": "The signature process was manually aborted.", - "error-rights-message": "Abort due to insufficient rights of your account." + "error-rights-message": "Abort due to insufficient rights of your account.", + "label-manual-positioning-missing": "Existing signature present, please select its positioning manually.", + "error-manual-positioning-missing": "One or more signatures in the queue must be positioned manually." }