diff --git a/apps/OpenSign/src/constant/Utils.js b/apps/OpenSign/src/constant/Utils.js index 67b399d03..1f28db8f1 100644 --- a/apps/OpenSign/src/constant/Utils.js +++ b/apps/OpenSign/src/constant/Utils.js @@ -572,7 +572,8 @@ export const signPdfFun = async ( headers: { "Content-Type": "application/json", "X-Parse-Application-Id": localStorage.getItem("parseAppId"), - sessionToken: localStorage.getItem("accesstoken") + // sessionToken: localStorage.getItem("accesstoken") + "X-Parse-Session-Token": localStorage.getItem("accesstoken") } }) .then((Listdata) => { diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index 4f433ba2f..435d613f1 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -271,6 +271,7 @@ function PdfRequestFiles() { ) { const params = { event: "viewed", + contactId: currUserId, body: { objectId: documentData?.[0].objectId, file: documentData?.[0]?.SignedUrl || documentData?.[0]?.URL, @@ -1139,9 +1140,9 @@ function PdfRequestFiles() { isDecline.currnt === "Sure" ? "Are you sure want to decline this document ?" : isDecline.currnt === "YouDeclined" - ? "You have declined this document!" - : isDecline.currnt === "another" && - "You can not sign this document as it has been declined/revoked." + ? "You have declined this document!" + : isDecline.currnt === "another" && + "You can not sign this document as it has been declined/revoked." } footerMessage={isDecline.currnt === "Sure"} declineDoc={declineDoc} diff --git a/apps/OpenSign/src/pages/SignyourselfPdf.js b/apps/OpenSign/src/pages/SignyourselfPdf.js index b14c6934c..d3f3c9aa8 100644 --- a/apps/OpenSign/src/pages/SignyourselfPdf.js +++ b/apps/OpenSign/src/pages/SignyourselfPdf.js @@ -443,13 +443,13 @@ function SignYourSelf() { Width: widgetTypeExist ? calculateInitialWidthHeight(dragTypeValue, widgetValue).getWidth : dragTypeValue === "initials" - ? defaultWidthHeight(dragTypeValue).width - : "", + ? defaultWidthHeight(dragTypeValue).width + : "", Height: widgetTypeExist ? calculateInitialWidthHeight(dragTypeValue, widgetValue).getHeight : dragTypeValue === "initials" - ? defaultWidthHeight(dragTypeValue).height - : "", + ? defaultWidthHeight(dragTypeValue).height + : "", options: addWidgetOptions(dragTypeValue) }; @@ -626,7 +626,8 @@ function SignYourSelf() { headers: { "Content-Type": "application/json", "X-Parse-Application-Id": localStorage.getItem("parseAppId"), - sessionToken: localStorage.getItem("accesstoken") + // sessionToken: localStorage.getItem("accesstoken") + "X-Parse-Session-Token": localStorage.getItem("accesstoken") } }) .then((Listdata) => { diff --git a/apps/OpenSignServer/cloud/parsefunction/callWebhook.js b/apps/OpenSignServer/cloud/parsefunction/callWebhook.js index 7e2fa4056..a9d127c36 100644 --- a/apps/OpenSignServer/cloud/parsefunction/callWebhook.js +++ b/apps/OpenSignServer/cloud/parsefunction/callWebhook.js @@ -2,6 +2,8 @@ import axios from 'axios'; export default async function callWebhook(request) { const event = request.params.event; const body = request.params.body; + const docId = body.objectId; + const contactId = request.params.contactId; const serverUrl = process.env.SERVER_URL; const appId = process.env.APP_ID; const userRes = await axios.get(serverUrl + '/users/me', { @@ -13,6 +15,39 @@ export default async function callWebhook(request) { const userId = userRes.data && userRes.data.objectId; if (userId) { + if (event === 'viewed' && contactId) { + const docQuery = new Parse.Query('contracts_Document'); + const res = await docQuery.get(docId, { useMasterKey: true }); + if (res) { + const _res = res.toJSON(); + const userPtr = { + __type: 'Pointer', + className: 'contracts_Contactbook', + objectId: contactId, + }; + const date = new Date().toISOString(); + const obj = { + UserPtr: userPtr, + SignedUrl: _res.SignedUrl, + Activity: 'Viewed', + ipAddress: request.headers['x-real-ip'], + ViewedOn: date, + }; + const isUserExist = _res?.AuditTrail?.some( + x => x.UserPtr.objectId === contactId && x?.ViewedOn + ); + if (!isUserExist) { + const updateDoc = new Parse.Object('contracts_Document'); + updateDoc.id = res.id; + if (_res?.AuditTrail && _res?.AuditTrail?.length > 0) { + updateDoc.set('AuditTrail', [..._res?.AuditTrail, obj]); + } else { + updateDoc.set('AuditTrail', [obj]); + } + await updateDoc.save(null, { useMasterKey: true }); + } + } + } const extendcls = new Parse.Query('contracts_Users'); extendcls.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: userId }); const res = await extendcls.first({ useMasterKey: true }); @@ -39,11 +74,11 @@ export default async function callWebhook(request) { }); webhook.save(null, { useMasterKey: true }); } catch (err) { - console.log('err save in contracts_Webhook', err); + console.log('err save in contracts_Webhook', err.message); } }) .catch(err => { - console.log('Err send data to webhook', err); + console.log('Err send data to webhook', err.message); try { const webhook = new Parse.Object('contracts_Webhook'); webhook.set('Log', err?.status); @@ -54,7 +89,7 @@ export default async function callWebhook(request) { }); webhook.save(null, { useMasterKey: true }); } catch (err) { - console.log('err save in contracts_Webhook', err); + console.log('err save in contracts_Webhook', err.message); } }); } diff --git a/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js b/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js index 2b9b7625f..739ea3a9a 100644 --- a/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js +++ b/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js @@ -17,21 +17,36 @@ export default async function GenerateCertificate(docDetails) { const text = 14; const textKeyColor = rgb(0.12, 0.12, 0.12); const textValueColor = rgb(0.3, 0.3, 0.3); - const completedAt = new Date(docDetails.updatedAt); + const completedAt = new Date(); const completedUTCtime = completedAt.toUTCString(); const signersCount = docDetails?.Signers?.length || 1; - const createdAt = new Date(); - const createdUTCTime = createdAt.toUTCString(); - const createDate = 'Generated On ' + createdUTCTime; + const generateAt = new Date(); + const generatedUTCTime = generateAt.toUTCString(); + const generatedOn = 'Generated On ' + generatedUTCTime; const company = docDetails?.ExtUserPtr?.Company || ''; const auditTrail = docDetails.AuditTrail?.length > 1 ? docDetails.AuditTrail.map(x => { const data = docDetails.Signers.find(y => y.objectId === x.UserPtr.objectId); - return { ...data, ipAddress: x.ipAddress }; + return { + ...data, + ipAddress: x.ipAddress, + SignedOn: x?.SignedOn || generatedUTCTime, + ViewedOn: x?.ViewedOn || generatedUTCTime, + Signature: x?.Signature || '', + }; }) - : [{ ...docDetails.ExtUserPtr, ipAddress: docDetails?.AuditTrail[0].ipAddress }]; + : [ + { + ...docDetails.ExtUserPtr, + ipAddress: docDetails?.AuditTrail[0].ipAddress, + SignedOn: docDetails?.AuditTrail[0]?.SignedOn || generatedUTCTime, + ViewedOn: docDetails?.AuditTrail[0]?.ViewedOn || generatedUTCTime, + Signature: docDetails?.AuditTrail[0]?.Signature || '', + }, + ]; + const half = width / 2; // Draw a border page.drawRectangle({ x: startX, @@ -48,7 +63,7 @@ export default async function GenerateCertificate(docDetails) { height: 25, }); - page.drawText(createDate, { + page.drawText(generatedOn, { x: 320, y: 810, size: 12, @@ -127,8 +142,7 @@ export default async function GenerateCertificate(docDetails) { font: timesRomanFont, color: textValueColor, }); - - page.drawText('Completed on :', { + page.drawText('Created on :', { x: 30, y: 625, size: text, @@ -136,15 +150,14 @@ export default async function GenerateCertificate(docDetails) { color: textKeyColor, }); - page.drawText(`${completedUTCtime}`, { - x: 120, + page.drawText(`${new Date(docDetails.createdAt).toUTCString()}`, { + x: 105, y: 625, size: text, font: timesRomanFont, color: textValueColor, }); - - page.drawText('Signers :', { + page.drawText('Completed on :', { x: 30, y: 605, size: text, @@ -152,35 +165,102 @@ export default async function GenerateCertificate(docDetails) { color: textKeyColor, }); + page.drawText(`${completedUTCtime}`, { + x: 125, + y: 605, + size: text, + font: timesRomanFont, + color: textValueColor, + }); + page.drawText('Signers :', { + x: 30, + y: 585, + size: text, + font: timesRomanFont, + color: textKeyColor, + }); + page.drawText(`${signersCount}`, { x: 80, - y: 605, + y: 585, + size: text, + font: timesRomanFont, + color: textValueColor, + }); + page.drawText('Document originator', { + x: 30, + y: 565, + size: 17, + font: timesRomanFont, + color: titleColor, + }); + page.drawText('Name :', { + x: 60, + y: 545, + size: text, + font: timesRomanFont, + color: textKeyColor, + }); + page.drawText(`${docDetails.ExtUserPtr.Name}`, { + x: 105, + y: 545, + size: text, + font: timesRomanFont, + color: textValueColor, + }); + page.drawText('Email :', { + x: 60, + y: 525, + size: text, + font: timesRomanFont, + color: textKeyColor, + }); + page.drawText(`${docDetails.ExtUserPtr.Email}`, { + x: 105, + y: 525, + size: text, + font: timesRomanFont, + color: textValueColor, + }); + page.drawText('IP address :', { + x: 60, + y: 505, + size: text, + font: timesRomanFont, + color: textKeyColor, + }); + page.drawText(`152.58.21.622`, { + x: 130, + y: 505, size: text, font: timesRomanFont, color: textValueColor, }); page.drawLine({ - start: { x: 30, y: 565 }, - end: { x: width - 30, y: 565 }, + start: { x: 30, y: 495 }, + end: { x: width - 30, y: 495 }, color: rgb(0.12, 0.12, 0.12), thickness: 0.5, }); - page.drawText('Recipients', { - x: 30, - y: 575, - size: subtitle, - font: timesRomanFont, - color: titleColor, - }); - let yPosition1 = 550; - let yPosition2 = 530; - let yPosition3 = 510; - let yPosition4 = 500; - auditTrail.forEach(x => { - page.drawText('Name :', { + let yPosition1 = 475; + let yPosition2 = 455; + let yPosition3 = 435; + let yPosition4 = 415; + let yPosition5 = 395; + let yPosition6 = 360; + auditTrail.slice(0, 3).forEach(async (x, i) => { + const embedPng = x.signature ? await pdfDoc.embedPng(x.signature) : ''; + page.drawText(`Signer ${i + 1}`, { x: 30, y: yPosition1, + size: subtitle, + font: timesRomanFont, + color: titleColor, + }); + page.drawText('Name :', { + x: 30, + y: yPosition2, size: text, font: timesRomanFont, color: textKeyColor, @@ -188,7 +268,23 @@ export default async function GenerateCertificate(docDetails) { page.drawText(x?.Name, { x: 75, - y: yPosition1, + y: yPosition2, + size: text, + font: timesRomanFont, + color: textValueColor, + }); + + page.drawText('Viewed on :', { + x: half, + y: yPosition2, + size: text, + font: timesRomanFont, + color: textKeyColor, + }); + + page.drawText(`${new Date(x.SignedOn).toUTCString()}`, { + x: half + 75, + y: yPosition2, size: text, font: timesRomanFont, color: textValueColor, @@ -196,7 +292,7 @@ export default async function GenerateCertificate(docDetails) { page.drawText('Email :', { x: 30, - y: yPosition2, + y: yPosition3, size: text, font: timesRomanFont, color: textKeyColor, @@ -204,41 +300,271 @@ export default async function GenerateCertificate(docDetails) { page.drawText(x?.Email, { x: 75, - y: yPosition2, + y: yPosition3, size: text, font: timesRomanFont, color: textValueColor, }); - page.drawText('Accessed from :', { - x: 30, + page.drawText('Signed on :', { + x: half, y: yPosition3, size: text, font: timesRomanFont, color: textKeyColor, }); - page.drawText(x?.ipAddress, { - x: 125, + page.drawText(`${new Date(x.SignedOn).toUTCString()}`, { + x: half + 70, y: yPosition3, size: text, font: timesRomanFont, color: textValueColor, }); + page.drawText('IP address :', { + x: 30, + y: yPosition4, + size: text, + font: timesRomanFont, + color: textKeyColor, + }); + + page.drawText(x?.ipAddress, { + x: 100, + y: yPosition4, + size: text, + font: timesRomanFont, + color: textValueColor, + }); + page.drawText('Security level :', { + x: half, + y: yPosition4, + size: text, + font: timesRomanFont, + color: textKeyColor, + }); + + page.drawText(`Email, OTP Auth`, { + x: half + 90, + y: yPosition4, + size: text, + font: timesRomanFont, + color: textValueColor, + }); + + page.drawText('Signature :', { + x: 30, + y: yPosition5, + size: text, + font: timesRomanFont, + color: textKeyColor, + }); + + page.drawRectangle({ + x: 98, + y: yPosition5 - 27, + width: 104, + height: 44, + borderColor: rgb(0.22, 0.18, 0.47), + borderWidth: 1, + }); + if (embedPng) { + page.drawImage(embedPng, { + x: 100, + y: yPosition5 - 25, + width: 100, + height: 40, + }); + } page.drawLine({ - start: { x: 30, y: yPosition4 }, - end: { x: width - 30, y: yPosition4 }, + start: { x: 30, y: yPosition6 }, + end: { x: width - 30, y: yPosition6 }, color: rgb(0.12, 0.12, 0.12), thickness: 0.5, }); - yPosition1 = yPosition4 - 20; + yPosition1 = yPosition6 - 20; yPosition2 = yPosition1 - 20; yPosition3 = yPosition2 - 20; - yPosition4 = yPosition4 - 70; + yPosition4 = yPosition3 - 20; + yPosition5 = yPosition4 - 20; + yPosition6 = yPosition6 - 140; }); + if (auditTrail.length > 3) { + let currentPageIndex = 1; + let currentPage = page; + auditTrail.slice(3).forEach(async (x, i) => { + const embedPng = x.signature ? await pdfDoc.embedPng(x.signature) : ''; + + // Calculate remaining space on current page + const remainingSpace = yPosition6; + + // If there's not enough space for the next entry, create a new page + if (remainingSpace < 90) { + // Adjust the value as needed + currentPageIndex++; + currentPage = pdfDoc.addPage(); + currentPage.drawRectangle({ + x: startX, + y: startY, + width: width - 2 * startX, + height: height - 2 * startY, + borderColor: borderColor, + borderWidth: 1, + }); + yPosition1 = currentPage.getHeight() - 40; + yPosition2 = yPosition1 - 20; + yPosition3 = yPosition2 - 20; + yPosition4 = yPosition3 - 20; + yPosition5 = yPosition4 - 20; + yPosition6 = currentPage.getHeight() - 160; + } + + currentPage.drawText(`Signer ${4 + i}`, { + x: 30, + y: yPosition1, + size: subtitle, + font: timesRomanFont, + color: titleColor, + }); + currentPage.drawText('Name :', { + x: 30, + y: yPosition2, + size: text, + font: timesRomanFont, + color: textKeyColor, + }); + + currentPage.drawText(x?.Name, { + x: 75, + y: yPosition2, + size: text, + font: timesRomanFont, + color: textValueColor, + }); + + currentPage.drawText('Viewed on :', { + x: half, + y: yPosition2, + size: text, + font: timesRomanFont, + color: textKeyColor, + }); + + currentPage.drawText(`${new Date(x.SignedOn).toUTCString()}`, { + x: half + 75, + y: yPosition2, + size: text, + font: timesRomanFont, + color: textValueColor, + }); + + currentPage.drawText('Email :', { + x: 30, + y: yPosition3, + size: text, + font: timesRomanFont, + color: textKeyColor, + }); + + currentPage.drawText(x?.Email, { + x: 75, + y: yPosition3, + size: text, + font: timesRomanFont, + color: textValueColor, + }); + + currentPage.drawText('Signed on :', { + x: half, + y: yPosition3, + size: text, + font: timesRomanFont, + color: textKeyColor, + }); + + currentPage.drawText(`${new Date(x.SignedOn).toUTCString()}`, { + x: half + 70, + y: yPosition3, + size: text, + font: timesRomanFont, + color: textValueColor, + }); + + currentPage.drawText('IP address :', { + x: 30, + y: yPosition4, + size: text, + font: timesRomanFont, + color: textKeyColor, + }); + + currentPage.drawText(x?.ipAddress, { + x: 100, + y: yPosition4, + size: text, + font: timesRomanFont, + color: textValueColor, + }); + currentPage.drawText('Security level :', { + x: half, + y: yPosition4, + size: text, + font: timesRomanFont, + color: textKeyColor, + }); + + currentPage.drawText(`Email, OTP Auth`, { + x: half + 90, + y: yPosition4, + size: text, + font: timesRomanFont, + color: textValueColor, + }); + + currentPage.drawText('Signature :', { + x: 30, + y: yPosition5, + size: text, + font: timesRomanFont, + color: textKeyColor, + }); + currentPage.drawRectangle({ + x: 98, + y: yPosition5 - 27, + width: 104, + height: 44, + borderColor: rgb(0.22, 0.18, 0.47), + borderWidth: 1, + }); + if (embedPng) { + currentPage.drawImage(embedPng, { + x: 100, + y: yPosition5 - 25, + width: 100, + height: 40, + }); + } + + currentPage.drawLine({ + start: { x: 30, y: yPosition6 }, + end: { x: width - 30, y: yPosition6 }, + color: rgb(0.12, 0.12, 0.12), + thickness: 0.5, + }); + + // Update y positions for the next entry + yPosition1 = yPosition6 - 20; + yPosition2 = yPosition1 - 20; + yPosition3 = yPosition2 - 20; + yPosition4 = yPosition3 - 20; + yPosition5 = yPosition4 - 20; + yPosition6 = yPosition6 - 140; + }); + } + const pdfBytes = await pdfDoc.save(); return pdfBytes; } diff --git a/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js b/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js index 6b249f797..a84ceb9a4 100644 --- a/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js +++ b/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js @@ -8,85 +8,96 @@ import GenerateCertificate from './GenerateCertificate.js'; const serverUrl = process.env.SERVER_URL, APPID = process.env.APP_ID, masterKEY = process.env.MASTER_KEY; -async function uploadFile(e, a) { +async function uploadFile(e, t) { try { - var t = fs.readFileSync(a), - s = new Parse.File(e, [...t], 'application/pdf'), - r = (await s.save({ useMasterKey: !0 }), s.url()); - return { imageUrl: r }; + var a = fs.readFileSync(t), + r = new Parse.File(e, [...a], 'application/pdf'), + i = (await r.save({ useMasterKey: !0 }), r.url()); + return { imageUrl: i }; } catch (e) { - console.log('Err ', e), fs.unlinkSync(a); + console.log('Err ', e), fs.unlinkSync(t); } } -async function updateDoc(t, s, r, i, o, n) { +async function updateDoc(a, r, i, s, o, n, l) { try { - var l = { - UserPtr: { __type: 'Pointer', className: n, objectId: r }, - SignedUrl: s, - Activity: 'Signed', - ipAddress: i, - }; + var c, + d, + p = { + UserPtr: { __type: 'Pointer', className: n, objectId: i }, + SignedUrl: r, + Activity: 'Signed', + ipAddress: s, + SignedOn: new Date(), + Signature: l, + }; let e; - var d = (e = o.AuditTrail && 0 < o.AuditTrail.length ? [...o.AuditTrail, l] : [l]).filter( - e => 'Signed' === e.Activity - ); - let a = !1; - !((o.Signers && 0 < o.Signers.length && d.length !== o.Signers.length) || !(a = !0)); - var c = { SignedUrl: s, AuditTrail: e, IsCompleted: a }; - await axios.put(serverUrl + '/classes/contracts_Document/' + t, c, { + var m = (e = + o.AuditTrail && 0 < o.AuditTrail.length + ? (-1 !== + (d = (c = JSON.parse(JSON.stringify(o.AuditTrail))).findIndex( + e => e.UserPtr.objectId === i && 'Created' !== e.Activity + )) + ? (c[d] = { ...c[d], ...p }) + : c.push(p), + c) + : [p]).filter(e => 'Signed' === e.Activity); + let t = !1; + !((o.Signers && 0 < o.Signers.length && m.length !== o.Signers.length) || !(t = !0)); + var g = { SignedUrl: r, AuditTrail: e, IsCompleted: t }; + await axios.put(serverUrl + '/classes/contracts_Document/' + a, g, { headers: { 'Content-Type': 'application/json', 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY, }, }); - return { isCompleted: a, message: 'success', AuditTrail: e }; + return { isCompleted: t, message: 'success', AuditTrail: e }; } catch (e) { return console.log('update doc err ', e), 'err'; } } async function sendCompletedMail(e) { - var a = e.url, - t = e.doc, - s = e.doc.ExtUserPtr, - r = t.Name, - i = s.Email; - let o = `Document "${r}" has been signed by all parties`, + var t = e.url, + a = e.doc, + r = e.doc.ExtUserPtr, + i = a.Name, + s = r.Email; + let o = `Document "${i}" has been signed by all parties`, n = "
Document signed successfully
All parties have successfully signed the document " + - `"${r}"` + + `"${i}"` + '. Kindly download the document from the attachment.
This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender ' + - s.Email + + r.Email + ' directly. If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ here.