Compare commits
8 Commits
52306d481f
...
7919f02ffd
| Author | SHA1 | Date | |
|---|---|---|---|
| 7919f02ffd | |||
| 04ae14c660 | |||
| cff79730b0 | |||
| 188cb67306 | |||
| abaa315b24 | |||
| 4f463c27e6 | |||
| d6f17ec4e8 | |||
| e3e2831da1 |
@@ -32,6 +32,10 @@ public class Annotation
|
|||||||
[Column("VALUE", TypeName = "nvarchar(max)")]
|
[Column("VALUE", TypeName = "nvarchar(max)")]
|
||||||
public string Value { get; set; }
|
public string Value { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[Column("TYPE", TypeName = "nvarchar(50)")]
|
||||||
|
public string Type { get; set; }
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
[Column("ADDED_WHEN", TypeName = "datetime")]
|
[Column("ADDED_WHEN", TypeName = "datetime")]
|
||||||
public DateTime AddedWhen { get; set; }
|
public DateTime AddedWhen { get; set; }
|
||||||
@@ -39,7 +43,7 @@ public class Annotation
|
|||||||
[Column("CHANGED_WHEN", TypeName = "datetime")]
|
[Column("CHANGED_WHEN", TypeName = "datetime")]
|
||||||
public DateTime? ChangedWhen { get; set; }
|
public DateTime? ChangedWhen { get; set; }
|
||||||
|
|
||||||
[Column("CHANGED_WHO", TypeName = "nchar(100)")]
|
[Column("CHANGED_WHO", TypeName = "nvarchar(100)")]
|
||||||
[StringLength(100)]
|
[StringLength(100)]
|
||||||
public string
|
public string
|
||||||
#if NET
|
#if NET
|
||||||
|
|||||||
@@ -307,4 +307,44 @@ function markFieldAsCity(formField) {
|
|||||||
|
|
||||||
function isCityField(formField) {
|
function isCityField(formField) {
|
||||||
return cityFieldNames.includes(formField.name)
|
return cityFieldNames.includes(formField.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
function fixBase64(escapedBase64) {
|
||||||
|
return escapedBase64
|
||||||
|
.replace(/\\u002B/g, "+")
|
||||||
|
.replace(/\\u002F/g, "/")
|
||||||
|
.replace(/\\u003D/g, "=");
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapSignature(iJSON) {
|
||||||
|
return {
|
||||||
|
formFields: iJSON.formFieldValues.filter(field => !field.name.includes("label")).map((field) => {
|
||||||
|
const nameParts = field.name.split('#');
|
||||||
|
field.elementId = Number(nameParts[2]);
|
||||||
|
field.name = nameParts[3];
|
||||||
|
return field;
|
||||||
|
}),
|
||||||
|
frames: iJSON.annotations.filter(annot => annot.description === 'FRAME').map((annot) => {
|
||||||
|
const preElement = findNearest(annot, e => e.bbox[0], e => e.bbox[1], ...iJSON.annotations.filter(field => field.id.includes("signature")));
|
||||||
|
const idPartsOfPre = preElement.id.split('#');
|
||||||
|
annot.elementId = Number(idPartsOfPre[2]);
|
||||||
|
annot.name = 'frame';
|
||||||
|
annot.value = fixBase64(iJSON.attachments[annot.imageAttachmentId]?.binary);
|
||||||
|
return annot;
|
||||||
|
}),
|
||||||
|
signatures: iJSON.annotations.filter(annot => annot.isSignature).map(annot => {
|
||||||
|
if (annot.imageAttachmentId)
|
||||||
|
annot.value = iJSON.attachments[annot.imageAttachmentId]?.binary;
|
||||||
|
else if (annot.lines && annot.strokeColor)
|
||||||
|
annot.value = JSON.stringify({
|
||||||
|
lines: annot.lines,
|
||||||
|
strokeColor: annot.strokeColor
|
||||||
|
});
|
||||||
|
else
|
||||||
|
throw new Error("Signature mapping failed: The data structure from the third-party library is incompatible or missing required fields.");
|
||||||
|
|
||||||
|
annot.name = 'signature';
|
||||||
|
return annot;
|
||||||
|
})
|
||||||
|
};
|
||||||
}
|
}
|
||||||
@@ -100,13 +100,6 @@ class App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fixBase64(escapedBase64) {
|
|
||||||
return escapedBase64
|
|
||||||
.replace(/\\u002B/g, "+")
|
|
||||||
.replace(/\\u002F/g, "/")
|
|
||||||
.replace(/\\u003D/g, "=");
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleClick(eventType) {
|
async handleClick(eventType) {
|
||||||
let result = false
|
let result = false
|
||||||
|
|
||||||
@@ -191,10 +184,7 @@ class App {
|
|||||||
async handleFinish(event) {
|
async handleFinish(event) {
|
||||||
const iJSON = await this.pdfKit.exportInstantJSON()
|
const iJSON = await this.pdfKit.exportInstantJSON()
|
||||||
|
|
||||||
console.log(iJSON);
|
const iFormFieldValues = iJSON.formFieldValues;
|
||||||
|
|
||||||
return
|
|
||||||
const iFormFieldValues = await iJSON.formFieldValues;
|
|
||||||
|
|
||||||
//check required
|
//check required
|
||||||
const iReqFields = iFormFieldValues.filter(f => isFieldRequired(f))
|
const iReqFields = iFormFieldValues.filter(f => isFieldRequired(f))
|
||||||
@@ -259,7 +249,10 @@ class App {
|
|||||||
|
|
||||||
// Export annotation data and save to database
|
// Export annotation data and save to database
|
||||||
try {
|
try {
|
||||||
const res = await signEnvelope(await iJSON);
|
const res = await signEnvelope({
|
||||||
|
instant: iJSON,
|
||||||
|
structured: mapSignature(iJSON)
|
||||||
|
});
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
if (res.status === 403) {
|
if (res.status === 403) {
|
||||||
@@ -299,7 +292,7 @@ class App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async handleReset(event) {
|
async handleReset(event) {
|
||||||
const result = await Swal.fire({
|
const result = Swal.fire({
|
||||||
title: 'Sind sie sicher?',
|
title: 'Sind sie sicher?',
|
||||||
text: 'Wollen Sie das Dokument und alle erstellten Signaturen zurücksetzen?',
|
text: 'Wollen Sie das Dokument und alle erstellten Signaturen zurücksetzen?',
|
||||||
icon: 'question',
|
icon: 'question',
|
||||||
|
|||||||
4
EnvelopeGenerator.Web/wwwroot/js/app.min.js
vendored
4
EnvelopeGenerator.Web/wwwroot/js/app.min.js
vendored
@@ -1,3 +1,3 @@
|
|||||||
class App{constructor(n,t,i,r,u,f){this.container=f??`#${this.constructor.name.toLowerCase()}`;this.envelopeKey=n;this.pdfKit=null;this.currentDocument=t.envelope.documents[0];this.currentReceiver=t.receiver;this.signatureCount=t.envelope.documents[0].elements.length;this.envelopeReceiver=t;this.documentBytes=i;this.licenseKey=r;this.locale=u}async init(){this.pdfKit=await loadPSPDFKit(this.documentBytes,this.container,this.licenseKey,this.locale);addToolbarItems(this.pdfKit,this.handleClick.bind(this));this.pdfKit.addEventListener("annotations.load",this.handleAnnotationsLoad.bind(this));this.pdfKit.addEventListener("annotations.change",this.handleAnnotationsChange.bind(this));this.pdfKit.addEventListener("annotations.create",this.handleAnnotationsCreate.bind(this));this.pdfKit.addEventListener("annotations.willChange",()=>{Comp.ActPanel.Toggle()});try{let n=await createAnnotations(this.currentDocument,this.envelopeReceiver.envelopeId,this.envelopeReceiver.receiverId);await this.pdfKit.create(n)}catch(n){console.error("Error loading annotations:",n)}[...document.getElementsByClassName("btn_refresh")].forEach(n=>n.addEventListener("click",()=>this.handleClick("RESET")));[...document.getElementsByClassName("btn_complete")].forEach(n=>n.addEventListener("click",()=>this.handleClick("FINISH")));[...document.getElementsByClassName("btn_reject")].forEach(n=>n.addEventListener("click",()=>this.handleClick("REJECT")))}handleAnnotationsLoad(n){n.toJS()}handleAnnotationsChange(){}async handleAnnotationsCreate(n){const t=n.toJS()[0],i=!!t.formFieldName,r=!!t.isSignature;if(i===!1&&r===!0){const r=t.boundingBox.left-20,u=t.boundingBox.top-20,n=150,i=75,f=new Date,e=await createAnnotationFrameBlob(this.envelopeReceiver.name,this.currentReceiver.signature,f,n,i),o=await fetch(e),s=await o.blob(),h=await this.pdfKit.createAttachment(s),c=createImageAnnotation(new PSPDFKit.Geometry.Rect({left:r,top:u,width:n,height:i}),t.pageIndex,h,generateId(this.envelopeReceiver.envelopeId,this.envelopeReceiver.receiverId,this.fakeElementId--,"signed"));this.pdfKit.create(c)}}fixBase64(n){return n.replace(/\\u002B/g,"+").replace(/\\u002F/g,"/").replace(/\\u003D/g,"=")}async handleClick(n){let t=!1;switch(n){case"RESET":t=await this.handleReset(null);Comp.SignatureProgress.SignedCount=0;t.isConfirmed&&Swal.fire({title:"Erfolg",text:"Dokument wurde zurückgesetzt",icon:"info"});break;case"FINISH":t=await this.handleFinish(null);t==!0&&(window.location.href=`/Envelope/${this.envelopeKey}`);break;case"REJECT":Swal.fire({title:localized.rejection,html:`<div class="text-start fs-6 p-0 m-0">${localized.rejectionReasonQ}</div>`,icon:"question",input:"text",inputAttributes:{autocapitalize:"off"},showCancelButton:!0,confirmButtonColor:"#3085d6",cancelButtonColor:"#d33",confirmButtonText:localized.complete,cancelButtonText:localized.back,showLoaderOnConfirm:!0,preConfirm:async n=>{try{return await rejectEnvelope(n)}catch(t){Swal.showValidationMessage(`
|
class App{constructor(n,t,i,r,u,f){this.container=f??`#${this.constructor.name.toLowerCase()}`;this.envelopeKey=n;this.pdfKit=null;this.currentDocument=t.envelope.documents[0];this.currentReceiver=t.receiver;this.signatureCount=t.envelope.documents[0].elements.length;this.envelopeReceiver=t;this.documentBytes=i;this.licenseKey=r;this.locale=u}async init(){this.pdfKit=await loadPSPDFKit(this.documentBytes,this.container,this.licenseKey,this.locale);addToolbarItems(this.pdfKit,this.handleClick.bind(this));this.pdfKit.addEventListener("annotations.load",this.handleAnnotationsLoad.bind(this));this.pdfKit.addEventListener("annotations.change",this.handleAnnotationsChange.bind(this));this.pdfKit.addEventListener("annotations.create",this.handleAnnotationsCreate.bind(this));this.pdfKit.addEventListener("annotations.willChange",()=>{Comp.ActPanel.Toggle()});try{let n=await createAnnotations(this.currentDocument,this.envelopeReceiver.envelopeId,this.envelopeReceiver.receiverId);await this.pdfKit.create(n)}catch(n){console.error("Error loading annotations:",n)}[...document.getElementsByClassName("btn_refresh")].forEach(n=>n.addEventListener("click",()=>this.handleClick("RESET")));[...document.getElementsByClassName("btn_complete")].forEach(n=>n.addEventListener("click",()=>this.handleClick("FINISH")));[...document.getElementsByClassName("btn_reject")].forEach(n=>n.addEventListener("click",()=>this.handleClick("REJECT")))}handleAnnotationsLoad(n){n.toJS()}handleAnnotationsChange(){}async handleAnnotationsCreate(n){const t=n.toJS()[0],i=!!t.formFieldName,r=!!t.isSignature;if(i===!1&&r===!0){const r=t.boundingBox.left-20,u=t.boundingBox.top-20,n=150,i=75,f=new Date,e=await createAnnotationFrameBlob(this.envelopeReceiver.name,this.currentReceiver.signature,f,n,i),o=await fetch(e),s=await o.blob(),h=await this.pdfKit.createAttachment(s),c=createImageAnnotation(new PSPDFKit.Geometry.Rect({left:r,top:u,width:n,height:i}),t.pageIndex,h,generateId(this.envelopeReceiver.envelopeId,this.envelopeReceiver.receiverId,this.fakeElementId--,"signed"));this.pdfKit.create(c)}}async handleClick(n){let t=!1;switch(n){case"RESET":t=await this.handleReset(null);Comp.SignatureProgress.SignedCount=0;t.isConfirmed&&Swal.fire({title:"Erfolg",text:"Dokument wurde zurückgesetzt",icon:"info"});break;case"FINISH":t=await this.handleFinish(null);t==!0&&(window.location.href=`/Envelope/${this.envelopeKey}`);break;case"REJECT":Swal.fire({title:localized.rejection,html:`<div class="text-start fs-6 p-0 m-0">${localized.rejectionReasonQ}</div>`,icon:"question",input:"text",inputAttributes:{autocapitalize:"off"},showCancelButton:!0,confirmButtonColor:"#3085d6",cancelButtonColor:"#d33",confirmButtonText:localized.complete,cancelButtonText:localized.back,showLoaderOnConfirm:!0,preConfirm:async n=>{try{return await rejectEnvelope(n)}catch(t){Swal.showValidationMessage(`
|
||||||
Request failed: ${t}
|
Request failed: ${t}
|
||||||
`)}},allowOutsideClick:()=>!Swal.isLoading()}).then(n=>{if(n.isConfirmed){const t=n.value;t.ok?reload():Swal.showValidationMessage(`Request failed: ${t.message}`)}});break;case"COPY_URL":const n=window.location.href.replace(/\/readonly/gi,"");navigator.clipboard.writeText(n).then(function(){bsNotify("Kopiert",{alert_type:"success",delay:4,icon_name:"check_circle"})}).catch(function(){bsNotify("Unerwarteter Fehler",{alert_type:"danger",delay:4,icon_name:"error"})});break;case"SHARE":Comp.ShareBackdrop.show();break;case"LOGOUT":await logout()}}async handleFinish(){const n=await this.pdfKit.exportInstantJSON();console.log(n);return;const t=await n.formFieldValues,i=t.filter(n=>isFieldRequired(n)),r=i.some(n=>n.value===undefined||n.value===null||n.value==="");const u=new RegExp("^[a-zA-Z\\u0080-\\u024F]+(?:([\\ \\-\\']|(\\.\\ ))[a-zA-Z\\u0080-\\u024F]+)*$"),f=t.filter(n=>isCityField(n));const o=await this.validateAnnotations(this.signatureCount)}async validateAnnotations(n){const t=await getAnnotations(this.pdfKit),i=t.map(n=>n.toJS()).filter(n=>n.isSignature);return n<=i.length}async handleReset(){const n=await Swal.fire({title:"Sind sie sicher?",text:"Wollen Sie das Dokument und alle erstellten Signaturen zurücksetzen?",icon:"question",showCancelButton:!0});if(n.isConfirmed){const n=await deleteAnnotations(this.pdfKit)}return n}fakeElementId=0;}
|
`)}},allowOutsideClick:()=>!Swal.isLoading()}).then(n=>{if(n.isConfirmed){const t=n.value;t.ok?reload():Swal.showValidationMessage(`Request failed: ${t.message}`)}});break;case"COPY_URL":const n=window.location.href.replace(/\/readonly/gi,"");navigator.clipboard.writeText(n).then(function(){bsNotify("Kopiert",{alert_type:"success",delay:4,icon_name:"check_circle"})}).catch(function(){bsNotify("Unerwarteter Fehler",{alert_type:"danger",delay:4,icon_name:"error"})});break;case"SHARE":Comp.ShareBackdrop.show();break;case"LOGOUT":await logout()}}async handleFinish(){const n=await this.pdfKit.exportInstantJSON(),t=n.formFieldValues,r=t.filter(n=>isFieldRequired(n)),u=r.some(n=>n.value===undefined||n.value===null||n.value==="");if(u)return Swal.fire({title:"Warnung",text:"Bitte füllen Sie alle Standortinformationen vollständig aus!",icon:"warning"}),!1;const f=new RegExp("^[a-zA-Z\\u0080-\\u024F]+(?:([\\ \\-\\']|(\\.\\ ))[a-zA-Z\\u0080-\\u024F]+)*$"),e=t.filter(n=>isCityField(n));for(var i of e)if(!IS_MOBILE_DEVICE&&!f.test(i.value))return Swal.fire({title:"Warnung",text:`Bitte überprüfen Sie die eingegebene Ortsangabe "${i.value}" auf korrekte Formatierung. Beispiele für richtige Formate sind: München, Île-de-France, Sauðárkrókur, San Francisco, St. Catharines usw.`,icon:"warning"}),!1;const o=await this.validateAnnotations(this.signatureCount);return o===!1?(Swal.fire({title:"Warnung",text:"Es wurden nicht alle Signaturfelder ausgefüllt!",icon:"warning"}),!1):Swal.fire({title:localized.confirmation,html:`<div class="text-start fs-6 p-0 m-0">${localized.sigAgree}</div>`,icon:"question",showCancelButton:!0,confirmButtonColor:"#3085d6",cancelButtonColor:"#d33",confirmButtonText:localized.finalize,cancelButtonText:localized.back}).then(async t=>{if(t.isConfirmed){try{await this.pdfKit.save()}catch(i){return Swal.fire({title:"Fehler",text:"Umschlag konnte nicht signiert werden!",icon:"error"}),!1}try{const t=await signEnvelope({instant:n,structured:mapSignature(n)});if(t.ok)return!0;if(t.status===403)return Swal.fire({title:"Warnung",text:"Umschlag ist nicht mehr verfügbar.",icon:"warning"}),!1;throw new Error;}catch(i){return Swal.fire({title:"Fehler",text:"Umschlag konnte nicht signiert werden!",icon:"error"}),!1}}else return!1})}async validateAnnotations(n){const t=await getAnnotations(this.pdfKit),i=t.map(n=>n.toJS()).filter(n=>n.isSignature);return n<=i.length}async handleReset(){const n=Swal.fire({title:"Sind sie sicher?",text:"Wollen Sie das Dokument und alle erstellten Signaturen zurücksetzen?",icon:"question",showCancelButton:!0});if(n.isConfirmed){const n=await deleteAnnotations(this.pdfKit)}return n}fakeElementId=0;}
|
||||||
@@ -12,4 +12,15 @@ function detailedCurrentDate() {
|
|||||||
second: '2-digit',
|
second: '2-digit',
|
||||||
timeZoneName: 'shortOffset'
|
timeZoneName: 'shortOffset'
|
||||||
}).format();
|
}).format();
|
||||||
|
}
|
||||||
|
|
||||||
|
function findNearest(origin, getX, getY, ...dests) {
|
||||||
|
const distanceToOrigin = (point) => Math.sqrt((getX(origin) - getX(point))**2 + (getY(origin) - getY(point))**2);
|
||||||
|
return dests.reduce(
|
||||||
|
(nearest, dest) => {
|
||||||
|
const dist = distanceToOrigin(dest);
|
||||||
|
return dist < nearest.dist ? {dist, dest} : nearest;
|
||||||
|
},
|
||||||
|
{dist: Infinity, dest: null}
|
||||||
|
).dest;
|
||||||
}
|
}
|
||||||
2
EnvelopeGenerator.Web/wwwroot/js/util.min.js
vendored
2
EnvelopeGenerator.Web/wwwroot/js/util.min.js
vendored
@@ -1 +1 @@
|
|||||||
function detailedCurrentDate(){return new Intl.DateTimeFormat("de-DE",{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit",timeZoneName:"shortOffset"}).format()}const B64ToBuff=n=>new Uint8Array(Array.from(atob(n),n=>n.charCodeAt(0))).buffer,getLocaleDateString=()=>(new Date).toLocaleDateString("de-DE");
|
function detailedCurrentDate(){return new Intl.DateTimeFormat("de-DE",{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit",timeZoneName:"shortOffset"}).format()}function findNearest(n,t,i,...r){const u=r=>Math.sqrt((t(n)-t(r))**2+(i(n)-i(r))**2);return r.reduce((n,t)=>{const i=u(t);return i<n.dist?{dist:i,dest:t}:n},{dist:Infinity,dest:null}).dest}const B64ToBuff=n=>new Uint8Array(Array.from(atob(n),n=>n.charCodeAt(0))).buffer,getLocaleDateString=()=>(new Date).toLocaleDateString("de-DE");
|
||||||
Reference in New Issue
Block a user