fix: shift the fonts.css file to CDN, ensure emails are sent to all signers when sendInOrder is set to false in public-sign, and add validation for the sequence of public roles when sendInOrder is enabled

This commit is contained in:
RaktimaNXG
2024-07-18 18:56:42 +05:30
parent 4b1e13d39a
commit c59c921a8b
16 changed files with 228 additions and 28518 deletions
@@ -1,3 +1,5 @@
import { replaceMailVaribles } from '../../Utils.js';
// `saveRoleContact` is used to save user in contracts_Guest role and create contact
const saveRoleContact = async contact => {
try {
@@ -80,6 +82,7 @@ const createDocumentFromTemplate = async (template, existContact, index) => {
},
};
object.set('Placeholders', Placeholders);
object.set('SendMail', true);
const resDoc = await object.save(null, { useMasterKey: true });
return resDoc;
}
@@ -88,6 +91,137 @@ const createDocumentFromTemplate = async (template, existContact, index) => {
}
};
//`sendMailToAllSigners` is used to send email to all signers at a time if send-in-order false
const sendMailToAllSigners = async docId => {
try {
//get document details that recenlty created from public template
const docQuery = new Parse.Query('contracts_Document');
docQuery.include('ExtUserPtr');
docQuery.include('Signers');
const docRes = await docQuery.get(docId, { useMasterKey: true });
const Doc = JSON.parse(JSON.stringify(docRes));
const templateOwnerUserId = Doc?.CreatedBy?.objectId;
const tenantCreditsQuery = new Parse.Query('partners_Tenant');
tenantCreditsQuery.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: templateOwnerUserId,
});
const res = await tenantCreditsQuery.first();
if (res) {
const existUserId = Doc?.ExtUserPtr?.objectId;
try {
const getSubscriptionDetails = await Parse.Cloud.run('getsubscriptions', {
extUserId: existUserId,
ispublic: true,
});
if (getSubscriptionDetails) {
const tenantRes = JSON.parse(JSON.stringify(res));
const extUserDetails = Doc?.ExtUserPtr;
const signerMail = Doc?.Signers;
const requestBody = tenantRes?.RequestBody;
const requestSubject = tenantRes?.RequestSubject;
const subscription_json = JSON.parse(JSON.stringify(getSubscriptionDetails));
const billingDate =
subscription_json?.result?.Next_billing_date &&
subscription_json?.result?.Next_billing_date?.iso;
const isSubscribed = billingDate ? new Date(billingDate) > new Date() : false;
for (let i = 0; i < signerMail.length; i++) {
try {
const senderEmail = Doc?.ExtUserPtr?.Email;
const senderPhone = Doc?.ExtUserPtr?.Phone;
const expireDate = Doc?.ExpiryDate?.iso || 15;
const newDate = new Date(expireDate);
const localExpireDate = newDate.toLocaleDateString('en-US', {
day: 'numeric',
month: 'long',
year: 'numeric',
});
const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
const objectId = signerMail[i].objectId;
const hostPublicUrl = 'https://staging-app.opensignlabs.com';
//encode this url value `${Doc.objectId}/${signerMail[i].Email}/${objectId}` to base64 using `btoa` function
const encodeBase64 = btoa(`${Doc?.objectId}/${signerMail[i].Email}/${objectId}`);
let signPdf = `${hostPublicUrl}/login/${encodeBase64}`;
const openSignUrl = 'https://www.opensignlabs.com/';
const orgName = Doc?.ExtUserPtr?.Company || '';
const themeBGcolor = '#47a3ad';
const senderName = `${Doc?.ExtUserPtr.Name}`;
const documentName = `${Doc?.Name}`;
let replaceVar;
if (requestBody && requestSubject && isSubscribed) {
const replacedRequestBody = requestBody.replace(/"/g, "'");
htmlReqBody =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" +
replacedRequestBody +
'</body> </html>';
const variables = {
document_title: documentName,
sender_name: senderName,
sender_mail: senderEmail,
sender_phone: senderPhone || '',
receiver_name: signerMail[i].Name,
receiver_email: signerMail[i].Email,
receiver_phone: signerMail[i]?.Phone || '',
expiry_date: localExpireDate,
company_name: orgName,
signing_url: `<a href=${signPdf}>Sign here</a>`,
};
replaceVar = replaceMailVaribles(requestSubject, htmlReqBody, variables);
}
let params = {
mailProvider: extUserDetails?.activeMailAdapter,
extUserId: existUserId,
recipient: signerMail[i].Email,
subject:
replaceVar?.subject ||
`${senderName} has requested you to sign "${documentName}"`,
from: senderEmail,
html:
replaceVar?.body ||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'=> <div style=' box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" +
imgPng +
" height='50' style='padding: 20px,width:170px,height:40px' /></div> <div style=' padding: 2px;font-family: system-ui;background-color:" +
themeBGcolor +
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
Doc?.ExtUserPtr.Name +
' has requested you to review and sign <strong> ' +
Doc?.Name +
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
senderEmail +
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
orgName +
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expire on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
localExpireDate +
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
signPdf +
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
senderEmail +
' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href= ' +
openSignUrl +
' target=_blank>here</a>.</p> </div></div></body> </html>',
};
await Parse.Cloud.run('sendmailv3', params);
} catch (error) {
console.log('error', error);
}
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
} catch (e) {
console.log('err in create document from template', err);
}
}
} catch (e) {
console.log('err in create document from template', err);
}
};
// `PublicUserLinkContactToDoc` cloud function is used to create contact, add this contact in contracts_Guest role and
// create new document from template and save contact pointer in placeholder, signers and ACL of Document
export default async function PublicUserLinkContactToDoc(req) {
@@ -122,9 +256,14 @@ export default async function PublicUserLinkContactToDoc(req) {
contactCls.notEqualTo('IsDeleted', true);
const existContact = await contactCls.first({ useMasterKey: true });
if (existContact) {
const template_json = JSON.parse(JSON.stringify(tempRes));
//update contact in placeholder, signers and update ACl in provide document
const docRes = await createDocumentFromTemplate(tempRes, existContact, index);
const docRes = await createDocumentFromTemplate(template_json, existContact, index);
if (docRes) {
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
if (!template_json?.SendinOrder) {
await sendMailToAllSigners(docRes.id);
}
return { contactId: existContact.id, docId: docRes.id };
}
} else {
@@ -142,10 +281,15 @@ export default async function PublicUserLinkContactToDoc(req) {
CreatedBy: _tempRes.CreatedBy,
TenantId: _tempRes.ExtUserPtr.TenantId,
};
const template_json = JSON.parse(JSON.stringify(tempRes));
// if user present on platform create contact on the basis of extended user details
const contactRes = await saveRoleContact(contact);
const docRes = await createDocumentFromTemplate(tempRes, contactRes, index);
const docRes = await createDocumentFromTemplate(template_json, contactRes, index);
if (docRes) {
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
if (!template_json?.SendinOrder) {
await sendMailToAllSigners(docRes.id);
}
return { contactId: contactRes.id, docId: docRes.id };
}
} else if (name) {
@@ -163,11 +307,16 @@ export default async function PublicUserLinkContactToDoc(req) {
CreatedBy: _tempRes.CreatedBy,
TenantId: _tempRes.ExtUserPtr.TenantId,
};
const template_json = JSON.parse(JSON.stringify(tempRes));
// Create new contract on the basis provided contact details by user and userId from _User class
const contactRes = await saveRoleContact(contact);
//update contact in placeholder, signers and update ACl in provide document
const docRes = await createDocumentFromTemplate(tempRes, contactRes, index);
const docRes = await createDocumentFromTemplate(template_json, contactRes, index);
if (docRes) {
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
if (!template_json?.SendinOrder) {
await sendMailToAllSigners(docRes.id);
}
return { contactId: contactRes.id, docId: docRes.id };
}
} else {
@@ -190,11 +339,16 @@ export default async function PublicUserLinkContactToDoc(req) {
CreatedBy: _tempRes.CreatedBy,
TenantId: _tempRes.ExtUserPtr.TenantId,
};
const template_json = JSON.parse(JSON.stringify(tempRes));
// Create new contract on the basis provided contact details by user and userId from _User class
const contactRes = await saveRoleContact(contact);
//update contact in placeholder, signers and update ACl in provide document
const docRes = await createDocumentFromTemplate(tempRes, contactRes, index);
const docRes = await createDocumentFromTemplate(template_json, contactRes, index);
if (docRes) {
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
if (!template_json?.SendinOrder) {
await sendMailToAllSigners(docRes.id);
}
return { contactId: contactRes.id, docId: docRes.id };
}
}
@@ -4,17 +4,22 @@ const appId = process.env.APP_ID;
export default async function getSubscription(request) {
const extUserId = request.params.extUserId || '';
const contactId = request.params.contactId || '';
const ispublic = request.params.ispublic || false;
if (extUserId) {
try {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
const userId = userRes.data && userRes.data.objectId;
if (userId) {
let userId;
//`ispublic` is used in public profile to get subscription details
if (!ispublic) {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
userId = userRes.data && userRes.data.objectId;
}
if (userId || ispublic) {
const extCls = new Parse.Query('contracts_Users');
const exUser = await extCls.get(extUserId, { useMasterKey: true });
if (exUser) {
@@ -333,6 +333,7 @@ export default function reportJson(id, userId) {
'Placeholders',
'IsPublic',
'SharedWith.Name',
'SendinOrder',
],
};
default: