From 9952afcc3829a7748b82b9975d5309e40a40f045 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs Date: Tue, 29 Oct 2024 15:41:35 +0530 Subject: [PATCH] feat: draft template api --- apps/OpenSign/src/App.js | 5 + apps/OpenSign/src/components/BulkSendUi.js | 33 +- .../components/shared/fields/SelectSigners.js | 26 +- .../shared/fields/SuggestionInput.js | 3 +- apps/OpenSign/src/constant/Utils.js | 87 +- apps/OpenSign/src/pages/DraftTemplate.js | 1829 +++++++++++++++++ apps/OpenSign/src/pages/PdfRequestFiles.js | 6 - apps/OpenSign/src/primitives/AddContact.js | 179 +- apps/OpenSign/src/primitives/LinkUserModal.js | 7 +- apps/OpenSignServer/Utils.js | 4 + .../cloud/customRoute/v1/apiV1.js | 8 +- .../v1/routes/CreateDocumentWithTemplate.js | 2 +- .../v1/routes/createDocumentwithCoordinate.js | 2 +- .../v1/routes/createTemplatewithCoordinate.js | 2 +- .../customRoute/v1/routes/draftDocument.js | 168 -- .../customRoute/v1/routes/draftTemplate.js | 238 +++ apps/OpenSignServer/cloud/main.js | 16 + .../cloud/parsefunction/AllowedCredits.js | 83 +- .../cloud/parsefunction/GetTemplate.js | 59 +- .../cloud/parsefunction/createBatchDocs.js | 340 +-- .../cloud/parsefunction/getDocument.js | 41 +- .../cloud/parsefunction/getSigners.js | 66 + .../cloud/parsefunction/getSubscriptions.js | 48 +- .../cloud/parsefunction/getTenant.js | 59 + .../cloud/parsefunction/getUserDetails.js | 46 + .../parsefunction/isUserInContactBook.js | 47 + .../cloud/parsefunction/saveFile.js | 131 ++ .../cloud/parsefunction/saveTemplate.js | 110 + .../cloud/parsefunction/savecontact.js | 180 ++ .../cloud/parsefunction/sendMailv3.js | 2 +- .../parsefunction/updateToPublicTemplate.js | 63 + .../cloud/parsefunction/updateTourStatus.js | 58 + 32 files changed, 3391 insertions(+), 557 deletions(-) create mode 100644 apps/OpenSign/src/pages/DraftTemplate.js delete mode 100644 apps/OpenSignServer/cloud/customRoute/v1/routes/draftDocument.js create mode 100644 apps/OpenSignServer/cloud/customRoute/v1/routes/draftTemplate.js create mode 100644 apps/OpenSignServer/cloud/parsefunction/getSigners.js create mode 100644 apps/OpenSignServer/cloud/parsefunction/getTenant.js create mode 100644 apps/OpenSignServer/cloud/parsefunction/isUserInContactBook.js create mode 100644 apps/OpenSignServer/cloud/parsefunction/saveFile.js create mode 100644 apps/OpenSignServer/cloud/parsefunction/saveTemplate.js create mode 100644 apps/OpenSignServer/cloud/parsefunction/savecontact.js create mode 100644 apps/OpenSignServer/cloud/parsefunction/updateToPublicTemplate.js create mode 100644 apps/OpenSignServer/cloud/parsefunction/updateTourStatus.js diff --git a/apps/OpenSign/src/App.js b/apps/OpenSign/src/App.js index e7183f4ce..91fb8eb5f 100644 --- a/apps/OpenSign/src/App.js +++ b/apps/OpenSign/src/App.js @@ -35,6 +35,7 @@ const GenerateToken = lazy(() => import("./pages/GenerateToken")); const Webhook = lazy(() => import("./pages/Webhook")); const AddAdmin = lazy(() => import("./pages/AddAdmin")); const UpdateExistUserAdmin = lazy(() => import("./pages/UpdateExistUserAdmin")); +const DraftTemplate = lazy(() => import("./pages/DraftTemplate")); pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`; const AppLoader = () => { @@ -200,6 +201,10 @@ function App() { } /> } /> + } + /> } /> diff --git a/apps/OpenSign/src/components/BulkSendUi.js b/apps/OpenSign/src/components/BulkSendUi.js index 4a4b872e7..49883fbad 100644 --- a/apps/OpenSign/src/components/BulkSendUi.js +++ b/apps/OpenSign/src/components/BulkSendUi.js @@ -69,7 +69,21 @@ const BulkSendUi = (props) => { if (subscription?.plan === "freeplan") { setIsFreePlan(true); } - const resCredits = await Parse.Cloud.run("allowedcredits"); + const token = props.jwttoken + ? { jwttoken: props.jwttoken } + : { "X-Parse-Session-Token": localStorage.getItem("accesstoken") }; + const axiosres = await axios.post( + `${localStorage.getItem("baseUrl")}functions/allowedcredits`, + {}, + { + headers: { + "Content-Type": "application/json", + "X-Parse-Application-Id": localStorage.getItem("parseAppId"), + ...token + } + } + ); + const resCredits = axiosres.data && axiosres.data.result; if (resCredits) { const allowedcredits = resCredits?.allowedcredits || 0; const addoncredits = resCredits?.addoncredits || 0; @@ -81,7 +95,7 @@ const BulkSendUi = (props) => { } setAmount((obj) => ({ ...obj, totalcredits: totalcredits })); } - const getPlaceholder = props.item?.Placeholders; + const getPlaceholder = props?.Placeholders; const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) => placeholderObj?.placeHolder?.some((holder) => holder?.pos?.some((posItem) => posItem?.type === "signature") @@ -97,7 +111,7 @@ const BulkSendUi = (props) => { } else { setIsBulkAvailable(true); setAdmin((obj) => ({ ...obj, isAdmin: true })); - const getPlaceholder = props.item?.Placeholders; + const getPlaceholder = props?.Placeholders; const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) => placeholderObj?.placeHolder?.some((holder) => holder?.pos?.some((posItem) => posItem?.type === "signature") @@ -194,7 +208,7 @@ const BulkSendUi = (props) => { setIsSubmit(true); // Create a copy of Placeholders array from props.item - let Placeholders = [...props.item.Placeholders]; + let Placeholders = [...props.Placeholders]; // Initialize an empty array to store updated documents let Documents = []; // Loop through each form @@ -251,12 +265,16 @@ const BulkSendUi = (props) => { }; const batchQuery = async (Documents) => { - const serverUrl = localStorage.getItem("baseUrl"); - const functionsUrl = `${serverUrl}functions/batchdocuments`; + const token = props.jwttoken + ? { jwttoken: props.jwttoken } + : { "X-Parse-Session-Token": localStorage.getItem("accesstoken") }; + const functionsUrl = `${localStorage.getItem( + "baseUrl" + )}functions/batchdocuments`; const headers = { "Content-Type": "application/json", "X-Parse-Application-Id": localStorage.getItem("parseAppId"), - sessionToken: localStorage.getItem("accesstoken") + ...token }; const params = { Documents: JSON.stringify(Documents) }; try { @@ -362,6 +380,7 @@ const BulkSendUi = (props) => { fieldIndex ) } + jwttoken={props?.jwttoken} /> ))} diff --git a/apps/OpenSign/src/components/shared/fields/SelectSigners.js b/apps/OpenSign/src/components/shared/fields/SelectSigners.js index b38cd71cd..b4d95db72 100644 --- a/apps/OpenSign/src/components/shared/fields/SelectSigners.js +++ b/apps/OpenSign/src/components/shared/fields/SelectSigners.js @@ -1,7 +1,7 @@ import React, { useState } from "react"; -import Parse from "parse"; import AsyncSelect from "react-select/async"; import { useTranslation } from "react-i18next"; +import axios from "axios"; const SelectSigners = (props) => { const { t } = useTranslation(); @@ -31,17 +31,19 @@ const SelectSigners = (props) => { const loadOptions = async (inputValue) => { try { - const currentUser = Parse.User.current(); - const contactbook = new Parse.Query("contracts_Contactbook"); - contactbook.equalTo( - "CreatedBy", - Parse.User.createWithoutData(currentUser.id) - ); - if (inputValue.length > 1) { - contactbook.matches("Name", new RegExp(inputValue, "i")); - } - contactbook.notEqualTo("IsDeleted", true); - const contactRes = await contactbook.find(); + const baseURL = localStorage.getItem("baseUrl"); + const url = `${baseURL}functions/getsigners`; + const token = props?.jwttoken + ? { jwttoken: props?.jwttoken } + : { "X-Parse-Session-Token": localStorage.getItem("accesstoken") }; + const headers = { + "Content-Type": "application/json", + "X-Parse-Application-Id": localStorage.getItem("parseAppId"), + ...token + }; + const search = inputValue; + const axiosRes = await axios.post(url, { search }, { headers }); + const contactRes = axiosRes?.data?.result || []; if (contactRes) { const res = JSON.parse(JSON.stringify(contactRes)); //compareArrays is a function where compare between two array (total signersList and dcument signers list) diff --git a/apps/OpenSign/src/components/shared/fields/SuggestionInput.js b/apps/OpenSign/src/components/shared/fields/SuggestionInput.js index 07fc11cbf..797515aec 100644 --- a/apps/OpenSign/src/components/shared/fields/SuggestionInput.js +++ b/apps/OpenSign/src/components/shared/fields/SuggestionInput.js @@ -25,7 +25,7 @@ const SuggestionInput = (props) => { if (timer) clearTimeout(timer); timer = setTimeout(() => { (async () => { - const res = await findContact(inputValue); + const res = await findContact(inputValue, props.jwttoken); if (res?.length > 0) { setSuggestions(res); setShowSuggestions(true); @@ -37,6 +37,7 @@ const SuggestionInput = (props) => { }, 1000); } return () => clearTimeout(timer); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [inputValue]); const handleInputChange = async (e) => { diff --git a/apps/OpenSign/src/constant/Utils.js b/apps/OpenSign/src/constant/Utils.js index 5a86efe33..d5efe160d 100644 --- a/apps/OpenSign/src/constant/Utils.js +++ b/apps/OpenSign/src/constant/Utils.js @@ -34,7 +34,8 @@ export async function fetchSubscription( extUserId, contactObjId, isGuestSign = false, - isPublic = false + isPublic = false, + jwtToken ) { try { const Extand_Class = localStorage.getItem("Extand_Class"); @@ -48,10 +49,13 @@ export async function fetchSubscription( } const baseURL = localStorage.getItem("baseUrl"); const url = `${baseURL}functions/getsubscriptions`; + const token = jwtToken + ? { jwttoken: jwtToken } + : { sessionToken: localStorage.getItem("accesstoken") }; const headers = { "Content-Type": "application/json", "X-Parse-Application-Id": localStorage.getItem("parseAppId"), - sessionToken: localStorage.getItem("accesstoken") + ...token }; const params = isGuestSign ? { contactId: contactObjId } @@ -226,12 +230,25 @@ export const pdfNewWidthFun = (divRef) => { }; //`contractUsers` function is used to get contract_User details -export const contractUsers = async () => { +export const contractUsers = async (jwttoken) => { try { - const userDetails = await Parse.Cloud.run("getUserDetails"); + const url = `${localStorage.getItem("baseUrl")}functions/getUserDetails`; + const parseAppId = localStorage.getItem("parseAppId"); + const accesstoken = localStorage.getItem("accesstoken"); + const token = jwttoken + ? { jwttoken: jwttoken } + : { "X-Parse-Session-Token": accesstoken }; + const headers = { + headers: { + "Content-Type": "application/json", + "X-Parse-Application-Id": parseAppId, + ...token + } + }; + const userDetails = await axios.post(url, {}, headers); let data = []; - if (userDetails) { - const json = JSON.parse(JSON.stringify(userDetails)); + if (userDetails?.data?.result) { + const json = JSON.parse(JSON.stringify(userDetails.data.result)); data.push(json); } return data; @@ -1762,14 +1779,17 @@ export const contactBook = async (objectId) => { }; //function for getting document details from contract_Documents class -export const contractDocument = async (documentId) => { +export const contractDocument = async (documentId, JwtToken) => { const data = { docId: documentId }; + const token = JwtToken + ? { jwtToken: JwtToken } + : { sessionToken: localStorage.getItem("accesstoken") }; const documentDeatils = await axios .post(`${localStorage.getItem("baseUrl")}functions/getDocument`, data, { headers: { "Content-Type": "application/json", "X-Parse-Application-Id": localStorage.getItem("parseAppId"), - sessionToken: localStorage.getItem("accesstoken") + ...token } }) .then((Listdata) => { @@ -1969,20 +1989,28 @@ export const getAppLogo = async () => { } }; -export const getTenantDetails = async (objectId) => { +export const getTenantDetails = async (objectId, jwttoken) => { try { - const tenantCreditsQuery = new Parse.Query("partners_Tenant"); - tenantCreditsQuery.equalTo("UserId", { - __type: "Pointer", - className: "_User", - objectId: objectId + const url = `${localStorage.getItem("baseUrl")}functions/gettenant`; + const parseAppId = localStorage.getItem("parseAppId"); + const accesstoken = localStorage.getItem("accesstoken"); + const token = jwttoken + ? { jwttoken: jwttoken } + : { "X-Parse-Session-Token": accesstoken }; + const data = jwttoken ? {} : { userId: objectId }; + const res = await axios.post(url, data, { + headers: { + "Content-Type": "application/json", + "X-Parse-Application-Id": parseAppId, + ...token + } }); - const res = await tenantCreditsQuery.first(); if (res) { - const updateRes = JSON.parse(JSON.stringify(res)); + const updateRes = JSON.parse(JSON.stringify(res.data.result)); return updateRes; } - } catch (e) { + } catch (err) { + console.log("err in gettenant", err); return "user does not exist!"; } }; @@ -2231,18 +2259,21 @@ export const handleDownloadCertificate = async ( } } }; -export async function findContact(value) { +export async function findContact(value, jwttoken) { try { - const currentUser = Parse.User.current(); - const contactbook = new Parse.Query("contracts_Contactbook"); - contactbook.equalTo( - "CreatedBy", - Parse.User.createWithoutData(currentUser.id) - ); - contactbook.notEqualTo("IsDeleted", true); - contactbook.matches("Email", new RegExp(value, "i")); - - const contactRes = await contactbook.find(); + const baseURL = localStorage.getItem("baseUrl"); + const url = `${baseURL}functions/getsigners`; + const token = jwttoken + ? { jwttoken: jwttoken } + : { "X-Parse-Session-Token": localStorage.getItem("accesstoken") }; + const headers = { + "Content-Type": "application/json", + "X-Parse-Application-Id": localStorage.getItem("parseAppId"), + ...token + }; + const searchEmail = value; + const axiosRes = await axios.post(url, { searchEmail }, { headers }); + const contactRes = axiosRes?.data?.result || []; if (contactRes) { const res = JSON.parse(JSON.stringify(contactRes)); return res; diff --git a/apps/OpenSign/src/pages/DraftTemplate.js b/apps/OpenSign/src/pages/DraftTemplate.js new file mode 100644 index 000000000..593563780 --- /dev/null +++ b/apps/OpenSign/src/pages/DraftTemplate.js @@ -0,0 +1,1829 @@ +import React, { useEffect, useState, useRef } from "react"; +import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage"; +import { useParams } from "react-router-dom"; +import axios from "axios"; +import "../styles/signature.css"; +import { isEnableSubscription, isStaging } from "../constant/const"; +import { DndProvider } from "react-dnd"; +import { HTML5Backend } from "react-dnd-html5-backend"; +import { useDrag, useDrop } from "react-dnd"; +import WidgetComponent from "../components/pdf/WidgetComponent"; +import Tour from "reactour"; +import SignerListPlace from "../components/pdf/SignerListPlace"; +import Header from "../components/pdf/PdfHeader"; +import WidgetNameModal from "../components/pdf/WidgetNameModal"; +import { + pdfNewWidthFun, + contractUsers, + randomId, + addZIndex, + defaultWidthHeight, + addWidgetOptions, + textInputWidget, + radioButtonWidget, + fetchSubscription, + getContainerScale, + convertBase64ToFile, + rotatePdfPage, + onClickZoomOut, + onClickZoomIn, + handleRemoveWidgets, + handleRotateWarning, + color, + copytoData +} from "../constant/Utils"; +import RenderPdf from "../components/pdf/RenderPdf"; +import "../styles/AddUser.css"; +import Title from "../components/Title"; +import EditTemplate from "../components/pdf/EditTemplate"; +import AddRoleModal from "../components/pdf/AddRoleModal"; +import PlaceholderCopy from "../components/pdf/PlaceholderCopy"; +import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption"; +import { useSelector } from "react-redux"; +import PdfZoom from "../components/pdf/PdfZoom"; +import { useTranslation } from "react-i18next"; +import RotateAlert from "../components/RotateAlert"; +import ModalUi from "../primitives/ModalUi"; +import TourContentWithBtn from "../primitives/TourContentWithBtn"; +import HandleError from "../primitives/HandleError"; +import LoaderWithMsg from "../primitives/LoaderWithMsg"; +import LinkUserModal from "../primitives/LinkUserModal"; +import { jwtDecode } from "jwt-decode"; +import BulkSendUi from "../components/BulkSendUi"; +import Alert from "../primitives/Alert"; +import { RWebShare } from "react-web-share"; + +const DraftTemplate = () => { + const { t } = useTranslation(); + const isHeader = useSelector((state) => state.showHeader); + const { jwttoken } = useParams(); + const signRef = useRef(null); + const dragRef = useRef(null); + const divRef = useRef(null); + const numPages = 1; + const isMobile = window.innerWidth < 767; + const [pdfDetails, setPdfDetails] = useState([]); + const [isMailSend, setIsMailSend] = useState(false); + const [allPages, setAllPages] = useState(null); + const [pageNumber, setPageNumber] = useState(1); + const [signBtnPosition, setSignBtnPosition] = useState([]); + const [xySignature, setXYSignature] = useState({}); + const [dragKey, setDragKey] = useState(); + const [signersdata, setSignersData] = useState([]); + const [signerPos, setSignerPos] = useState([]); + const [isSelectListId, setIsSelectId] = useState(); + const [isSendAlert, setIsSendAlert] = useState(false); + const [isCreateDocModal, setIsCreateDocModal] = useState(false); + const [isSubscribe, setIsSubscribe] = useState(false); + const [isRotate, setIsRotate] = useState({ status: false, degree: 0 }); + const [isLoading, setIsLoading] = useState({ + isLoad: true, + message: t("loading-mssg") + }); + const [handleError, setHandleError] = useState(); + const [pdfNewWidth, setPdfNewWidth] = useState(); + const [templateTour, setTemplateTour] = useState(true); + const [checkTourStatus, setCheckTourStatus] = useState(false); + const [tourStatus, setTourStatus] = useState([]); + const [signerUserId, setSignerUserId] = useState(); + const [pdfOriginalWH, setPdfOriginalWH] = useState([]); + const [containerWH, setContainerWH] = useState(); + const [isShowEmail, setIsShowEmail] = useState(false); + const [selectedEmail, setSelectedEmail] = useState(false); + const [isResize, setIsResize] = useState(false); + const [isSigners, setIsSigners] = useState(false); + const [zIndex, setZIndex] = useState(1); + const [showDropdown, setShowDropdown] = useState(false); + const [widgetType, setWidgetType] = useState(""); + const [isRadio, setIsRadio] = useState(false); + const [blockColor, setBlockColor] = useState(""); + const [selectWidgetId, setSelectWidgetId] = useState(""); + const [isNameModal, setIsNameModal] = useState(false); + const [isTextSetting, setIsTextSetting] = useState(false); + const [pdfLoad, setPdfLoad] = useState(false); + const [pdfRotateBase64, setPdfRotatese64] = useState(""); + const [uniqueId, setUniqueId] = useState(""); + const [isModalRole, setIsModalRole] = useState(false); + const [roleName, setRoleName] = useState(""); + const [isAddUser, setIsAddUser] = useState({}); + const [isEditTemplate, setIsEditTemplate] = useState(false); + const [isPageCopy, setIsPageCopy] = useState(false); + const [signKey, setSignKey] = useState(); + const [IsReceipent, setIsReceipent] = useState(true); + const [isDontShow, setIsDontShow] = useState(false); + const [isDragging, setIsDragging] = useState(false); + const [currWidgetsDetails, setCurrWidgetsDetails] = useState([]); + const [isCheckbox, setIsCheckbox] = useState(false); + const [widgetName, setWidgetName] = useState(false); + const [isAddRole, setIsAddRole] = useState(false); + const [fontSize, setFontSize] = useState(); + const [fontColor, setFontColor] = useState(); + const [zoomPercent, setZoomPercent] = useState(0); + const [scale, setScale] = useState(1); + const [isSubscriptionExpired, setIsSubscriptionExpired] = useState(false); + const [isAlert, setIsAlert] = useState({ type: "success", msg: "" }); + const [isPublic, setIsPublic] = useState(""); + const [copied, setCopied] = useState(false); + // const [isPublicErr, setIsPublicError] = useState(false); + const [isAllRoleLinked, setIsAllRolelinked] = useState(false); + const [, drop] = useDrop({ + accept: "BOX", + drop: (item, monitor) => addPositionOfSignature(item, monitor), + collect: (monitor) => ({ isOver: !!monitor.isOver() }) + }); + const [{ isDragSign }, dragSignature] = useDrag({ + type: "BOX", + item: { type: "BOX", id: 1, text: "signature" }, + collect: (monitor) => ({ isDragSign: !!monitor.isDragging() }) + }); + const [{ isDragStamp }, dragStamp] = useDrag({ + type: "BOX", + item: { type: "BOX", id: 2, text: "stamp" }, + collect: (monitor) => ({ isDragStamp: !!monitor.isDragging() }) + }); + const decode = jwtDecode(jwttoken); + const templateId = decode?.template_id; + useEffect(() => { + fetchTemplate(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + const updateSize = () => { + if (divRef.current) { + const pdfWidth = pdfNewWidthFun(divRef); + setPdfNewWidth(pdfWidth); + setContainerWH({ + width: divRef.current.offsetWidth, + height: divRef.current.offsetHeight + }); + setScale(1); + setZoomPercent(0); + } + }; + + // Use setTimeout to wait for the transition to complete + const timer = setTimeout(updateSize, 100); // match the transition duration + + return () => clearTimeout(timer); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [divRef.current, isHeader]); + + const handleNavigation = () => { + // navigate("/subscription"); + setIsSubscriptionExpired(true); + }; + async function checkIsSubscribed() { + const res = await fetchSubscription("", "", false, false, jwttoken); + const plan = res.plan; + const billingDate = res.billingDate; + if (plan === "freeplan") { + return true; + } else if (billingDate) { + if (new Date(billingDate) > new Date()) { + setIsSubscribe(true); + return true; + } else { + handleNavigation(plan); + } + } else { + handleNavigation(plan); + } + } + // `fetchTemplate` function in used to get Template from server and setPlaceholder ,setSigner if present + const fetchTemplate = async () => { + try { + const params = { templateId: templateId }; + const token = jwttoken + ? { jwttoken: jwttoken } + : { sessionToken: localStorage.getItem("accesstoken") }; + const templateDeatils = await axios.post( + `${localStorage.getItem("baseUrl")}functions/getTemplate`, + params, + { + headers: { + "Content-Type": "application/json", + "X-Parse-Application-Id": localStorage.getItem("parseAppId"), + ...token + } + } + ); + const documentData = + templateDeatils.data && templateDeatils.data.result + ? [templateDeatils.data.result] + : []; + if (templateDeatils?.data?.result?.IsPublic !== undefined) { + if (templateDeatils?.data?.result?.IsPublic === true) { + setIsPublic("true"); + } else { + setIsPublic("false"); + } + } else { + setIsPublic("undefined"); + } + + if (documentData && documentData.length > 0) { + if (isEnableSubscription) { + checkIsSubscribed(documentData[0]?.ExtUserPtr?.Email); + } + setPdfDetails(documentData); + setIsSigners(true); + if (documentData[0].Signers && documentData[0].Signers.length > 0) { + setIsSelectId(0); + if ( + documentData[0].Placeholders && + documentData[0].Placeholders.length > 0 + ) { + setSignerPos(documentData[0].Placeholders); + let signers = [...documentData[0].Signers]; + let updatedSigners = documentData[0].Placeholders.map((x) => { + let matchingSigner = signers.find( + (y) => x.signerObjId && x.signerObjId === y.objectId + ); + + if (matchingSigner) { + return { + ...matchingSigner, + Role: x.Role ? x.Role : matchingSigner.Role, + Id: x.Id, + blockColor: x.blockColor + }; + } else { + return { Role: x.Role, Id: x.Id, blockColor: x.blockColor }; + } + }); + setSignersData(updatedSigners); + setUniqueId(updatedSigners[0].Id); + setBlockColor(updatedSigners[0].blockColor); + } else { + const updatedSigners = documentData[0].Signers.map((x, index) => ({ + ...x, + Id: randomId(), + Role: "User " + (index + 1) + })); + setSignersData(updatedSigners); + setUniqueId(updatedSigners[0].Id); + setBlockColor(updatedSigners[0].blockColor); + } + } else { + setRoleName("User 1"); + if ( + documentData[0].Placeholders && + documentData[0].Placeholders.length > 0 + ) { + let updatedSigners = documentData[0].Placeholders.map((x) => { + return { Role: x.Role, Id: x.Id, blockColor: x.blockColor }; + }); + setSignerPos(documentData[0].Placeholders); + setUniqueId(updatedSigners[0].Id); + setSignersData(updatedSigners); + setIsSelectId(0); + setBlockColor(updatedSigners[0].blockColor); + } + } + } else if ( + documentData === "Error: Something went wrong!" || + (documentData.result && documentData.result.error) + ) { + setHandleError(t("something-went-wrong-mssg")); + setIsLoading({ isLoad: false }); + } else { + setHandleError(t("no-data-avaliable")); + setIsLoading({ isLoad: false }); + } + } catch (err) { + console.log("err ", err); + if (err?.response?.data?.code === 101) { + setHandleError(t("no-data-avaliable")); + } else { + setHandleError(t("something-went-wrong-mssg")); + } + } + const res = await contractUsers(jwttoken); + if (res[0] && res.length) { + setSignerUserId(res[0].objectId); + const tourstatus = res[0].TourStatus && res[0].TourStatus; + if (tourstatus && tourstatus.length > 0) { + setTourStatus(tourstatus); + const checkTourRecipients = tourstatus.filter( + (data) => data.templateTour + ); + if (checkTourRecipients && checkTourRecipients.length > 0) { + setCheckTourStatus(checkTourRecipients[0].templateTour); + } + } + setIsLoading({ isLoad: false }); + } else if (res === "Error: Something went wrong!") { + setHandleError(t("something-went-wrong-mssg")); + setIsLoading({ isLoad: false }); + } else if (res.length === 0) { + setHandleError(t("no-data-avaliable")); + setIsLoading({ isLoad: false }); + } + }; + //function for setting position after drop signature button over pdf + const addPositionOfSignature = (item, monitor) => { + if (item && item.text) { + setWidgetName(item.text); + } + getSignerPos(item, monitor); + }; + + // `getSignerPos` is used to get placeholder position when user place it and save it in array + const getSignerPos = (item, monitor) => { + if (uniqueId) { + const signer = signersdata.find((x) => x.Id === uniqueId); + if (signer) { + const posZIndex = zIndex + 1; + setZIndex(posZIndex); + const containerScale = getContainerScale( + pdfOriginalWH, + pageNumber, + containerWH + ); + const key = randomId(); + const dragTypeValue = item?.text ? item.text : monitor.type; + const widgetWidth = defaultWidthHeight(dragTypeValue).width; + const widgetHeight = defaultWidthHeight(dragTypeValue).height; + let dropData = [], + currentPagePosition; + let placeHolder; + if (item === "onclick") { + // `getBoundingClientRect()` is used to get accurate measurement height of the div + const divHeight = divRef.current.getBoundingClientRect().height; + const dropObj = { + //onclick put placeholder center on pdf + xPosition: widgetWidth / 4 + containerWH.width / 2, + yPosition: widgetHeight + divHeight / 2, + isStamp: + (dragTypeValue === "stamp" || dragTypeValue === "image") && true, + key: key, + scale: containerScale, + zIndex: posZIndex, + type: dragTypeValue, + options: addWidgetOptions(dragTypeValue), + Width: widgetWidth / (containerScale * scale), + Height: widgetHeight / (containerScale * scale) + }; + dropData.push(dropObj); + placeHolder = { pageNumber: pageNumber, pos: dropData }; + } else { + const offset = monitor.getClientOffset(); + //adding and updating drop position in array when user drop signature button in div + const containerRect = document + .getElementById("container") + .getBoundingClientRect(); + const x = offset.x - containerRect.left; + const y = offset.y - containerRect.top; + const getXPosition = signBtnPosition[0] + ? x - signBtnPosition[0].xPos + : x; + const getYPosition = signBtnPosition[0] + ? y - signBtnPosition[0].yPos + : y; + const dropObj = { + xPosition: getXPosition / (containerScale * scale), + yPosition: getYPosition / (containerScale * scale), + isStamp: + (dragTypeValue === "stamp" || dragTypeValue === "image") && true, + key: key, + scale: containerScale, + // isMobile: isMobile, + zIndex: posZIndex, + type: item.text, + options: addWidgetOptions(dragTypeValue), + Width: widgetWidth / (containerScale * scale), + Height: widgetHeight / (containerScale * scale) + }; + + dropData.push(dropObj); + placeHolder = { pageNumber: pageNumber, pos: dropData }; + } + let filterSignerPos = signerPos.find((data) => data.Id === uniqueId); + const getPlaceHolder = filterSignerPos?.placeHolder; + if (getPlaceHolder) { + //checking exist placeholder on same page + currentPagePosition = getPlaceHolder.find( + (data) => data.pageNumber === pageNumber + ); + } + //checking current page has already some placeholders then update that placeholder and add upcoming placehoder position + if (getPlaceHolder && currentPagePosition) { + const updatePlace = getPlaceHolder.filter( + (data) => data.pageNumber !== pageNumber + ); + const getPos = currentPagePosition?.pos; + const newSignPos = getPos.concat(dropData); + let xyPos = { pageNumber: pageNumber, pos: newSignPos }; + updatePlace.push(xyPos); + const updatesignerPos = signerPos.map((x) => + x.Id === uniqueId ? { ...x, placeHolder: updatePlace } : x + ); + setSignerPos(updatesignerPos); + } else { + //else condition to add placeholder widgets on multiple page first time + const updatesignerPos = signerPos.map((x) => + x.Id === uniqueId && x?.placeHolder + ? { ...x, placeHolder: [...x.placeHolder, placeHolder] } + : x.Id === uniqueId + ? { ...x, placeHolder: [placeHolder] } + : x + ); + setSignerPos(updatesignerPos); + } + + if (dragTypeValue === "dropdown") { + setShowDropdown(true); + } else if (dragTypeValue === "checkbox") { + setIsCheckbox(true); + } else if (dragTypeValue === radioButtonWidget) { + setIsRadio(true); + } else if ( + [textInputWidget, "name", "company", "job title", "email"].includes( + dragTypeValue + ) + ) { + setFontSize(12); + setFontColor("black"); + } + setCurrWidgetsDetails({}); + setWidgetType(dragTypeValue); + setSignKey(key); + setSelectWidgetId(key); + setIsMailSend(false); + } else { + setIsReceipent(false); + } + } else { + setIsAddRole(true); + } + }; + + const tourAddRole = [ + { + selector: '[data-tut="reactourAddbtn"]', + content: t("add-user-template"), + position: "top", + style: { fontSize: "13px" } + } + ]; + + //function for get pdf page details + const pageDetails = async (pdf) => { + let pdfWHObj = []; + const totalPages = pdf?.numPages; + for (let index = 0; index < totalPages; index++) { + const getPage = await pdf.getPage(index + 1); + const scale = 1; + const { width, height } = getPage.getViewport({ scale }); + pdfWHObj.push({ pageNumber: index + 1, width, height }); + } + setPdfOriginalWH(pdfWHObj); + setPdfLoad(true); + }; + //function for save x and y position and show signature tab on that position + const handleTabDrag = (key) => { + setDragKey(key); + setIsDragging(true); + }; + + //function for set and update x and y postion after drag and drop signature tab + const handleStop = (event, dragElement, signerId, key) => { + setFontColor(); + setFontSize(); + if (!isResize && isDragging) { + const dataNewPlace = addZIndex(signerPos, key, setZIndex); + let updateSignPos = [...signerPos]; + updateSignPos.splice(0, updateSignPos.length, ...dataNewPlace); + const signId = signerId; //? signerId : signerObjId; + const keyValue = key ? key : dragKey; + const containerScale = getContainerScale( + pdfOriginalWH, + pageNumber, + containerWH + ); + if (keyValue >= 0) { + const filterSignerPos = updateSignPos.filter( + (data) => data.Id === signId + ); + + if (filterSignerPos.length > 0) { + const getPlaceHolder = filterSignerPos[0].placeHolder; + + const getPageNumer = getPlaceHolder.filter( + (data) => data.pageNumber === pageNumber + ); + + if (getPageNumer.length > 0) { + const getXYdata = getPageNumer[0].pos; + const getPosData = getXYdata; + const addSignPos = getPosData.map((url) => { + if (url.key === keyValue) { + return { + ...url, + xPosition: dragElement.x / (containerScale * scale), + yPosition: dragElement.y / (containerScale * scale) + }; + } + return url; + }); + + const newUpdateSignPos = getPlaceHolder.map((obj) => { + if (obj.pageNumber === pageNumber) { + return { ...obj, pos: addSignPos }; + } + return obj; + }); + const newUpdateSigner = updateSignPos.map((obj) => { + if (obj.Id === signId) { + return { ...obj, placeHolder: newUpdateSignPos }; + } + return obj; + }); + setSignerPos(newUpdateSigner); + } + } + } + } + setIsMailSend(false); + setTimeout(() => setIsDragging(false), 200); + }; + //function for delete signature block + const handleDeleteSign = (key, Id) => { + const updateData = []; + const filterSignerPos = signerPos.filter((data) => data.Id === Id); + + if (filterSignerPos.length > 0) { + const getPlaceHolder = filterSignerPos[0].placeHolder; + + const getPageNumer = getPlaceHolder.filter( + (data) => data.pageNumber === pageNumber + ); + + if (getPageNumer.length > 0) { + const getXYdata = getPageNumer[0].pos.filter( + (data) => data.key !== key + ); + + if (getXYdata.length > 0) { + updateData.push(getXYdata); + const newUpdatePos = getPlaceHolder.map((obj) => { + if (obj.pageNumber === pageNumber) { + return { ...obj, pos: updateData[0] }; + } + return obj; + }); + + const newUpdateSigner = signerPos.map((obj) => { + if (obj.Id === Id) { + return { ...obj, placeHolder: newUpdatePos }; + } + return obj; + }); + + setSignerPos(newUpdateSigner); + } else { + const getRemainPage = filterSignerPos[0].placeHolder.filter( + (data) => data.pageNumber !== pageNumber + ); + //condition to check placeholder length is greater than 1 do not need to remove whole placeholder + //array only resove particular widgets + if (getRemainPage && getRemainPage.length > 0) { + const newUpdatePos = filterSignerPos.map((obj) => { + if (obj.Id === Id) { + return { ...obj, placeHolder: getRemainPage }; + } + return obj; + }); + let signerupdate = []; + signerupdate = signerPos.filter((data) => data.Id !== Id); + signerupdate.push(newUpdatePos[0]); + + setSignerPos(signerupdate); + } else { + const updatedData = signerPos.map((item) => { + if (item.Id === Id) { + // Create a copy of the item object and delete the placeHolder field + const updatedItem = { ...item }; + delete updatedItem.placeHolder; + return updatedItem; + } + return item; + }); + setSignerPos(updatedData); + } + } + } + } + }; + + //function for change page + function changePage(offset) { + setSignBtnPosition([]); + setPageNumber((prevPageNumber) => prevPageNumber + offset); + } + + //function for capture position on hover or touch widgets + const handleDivClick = (e) => { + const isTouchEvent = e.type.startsWith("touch"); + const divRect = e.currentTarget.getBoundingClientRect(); + let mouseX, mouseY; + if (isTouchEvent) { + const touch = e.touches[0]; // Get the first touch point + mouseX = touch.clientX - divRect.left; + mouseY = touch.clientY - divRect.top; + setSignBtnPosition([{ xPos: mouseX, yPos: mouseY }]); + } else { + mouseX = e.clientX - divRect.left; + mouseY = e.clientY - divRect.top; + const xyPosition = { xPos: mouseX, yPos: mouseY }; + setXYSignature(xyPosition); + } + }; + + //function for capture position of x and y on hover signature button last position + const handleMouseLeave = () => setSignBtnPosition([xySignature]); + const alertSendEmail = async () => { + const isPlaceholderExist = signerPos.every((data) => data.placeHolder); + if (isPlaceholderExist) { + handleSaveTemplate(); + } else { + setIsSendAlert(true); + } + }; + + // `saveTemplate` is used to update template on server using savetemplate endpoint + const saveTemplate = async () => { + if (signersdata?.length) { + const loadObj = { isLoad: true, message: t("loading-mssg") }; + setIsLoading(loadObj); + setIsSendAlert(false); + let signers = []; + if (signersdata?.length > 0) { + signersdata.forEach((x) => { + if (x.objectId) { + const obj = { + __type: "Pointer", + className: "contracts_Contactbook", + objectId: x.objectId + }; + signers.push(obj); + } + }); + } + let pdfUrl = pdfDetails[0]?.URL; + if (pdfRotateBase64) { + try { + pdfUrl = await convertBase64ToFile( + pdfDetails[0].Name, + pdfRotateBase64 + ); + } catch (e) { + console.log("error to convertBase64ToFile", e); + } + } + try { + const data = { + templateId: templateId, + Placeholders: signerPos, + Signers: signers, + URL: pdfUrl, + Name: pdfDetails[0]?.Name || "", + Note: pdfDetails[0]?.Note || "", + Description: pdfDetails[0]?.Description || "", + SendinOrder: pdfDetails[0]?.SendinOrder || false, + AutomaticReminders: pdfDetails[0]?.AutomaticReminders, + RemindOnceInEvery: parseInt(pdfDetails[0]?.RemindOnceInEvery), + NextReminderDate: pdfDetails[0]?.NextReminderDate, + IsEnableOTP: pdfDetails[0]?.IsEnableOTP === true ? true : false, + IsTourEnabled: pdfDetails[0]?.IsTourEnabled === true ? true : false + }; + const baseURL = localStorage.getItem("baseUrl"); + const url = `${baseURL}functions/savetemplate`; + const headers = { + "Content-Type": "application/json", + "X-Parse-Application-Id": localStorage.getItem("parseAppId"), + jwttoken: jwttoken + }; + await axios.post(url, data, { headers }); + // console.log("savetemplate", res.data); + const isSignersLinked = + signerPos?.some((x) => !x.signerObjId) === false; + setIsAllRolelinked(isSignersLinked); + setIsCreateDocModal(true); + setIsLoading({ isLoad: false }); + } catch (e) { + setIsLoading(false); + alert(t("something-went-wrong-mssg")); + console.log("error", e); + } + } else { + setIsReceipent(false); + } + }; + // `handleSaveTemplate` is used to save template based of validation + const handleSaveTemplate = async () => { + const isPublicTemplate = isPublic === "true"; + if (isPublicTemplate) { + const unlinkSignerIndex = signerPos?.findIndex((x) => !x?.signerObjId); + const unlinkSigners = signerPos?.filter((x) => !x?.signerObjId)?.length; // unLink with role + if (unlinkSignerIndex === 0 && unlinkSigners === 1) { + await saveTemplate(); + } else { + // setIsPublicError(true); + alert( + "Only one signer without a role is allowed, and it should be at the first index." + ); + } + } else { + await saveTemplate(); + } + }; + + const handleDontShow = (isChecked) => setIsDontShow(isChecked); + + //here you can add your messages in content and selector is key of particular steps + const tourConfig = [ + { + selector: '[data-tut="reactourAddbtn"]', + content: () => ( + + ), + position: "top", + observe: '[data-tut="reactourAddbtn--observe"]', + style: { fontSize: "13px" } + }, + { + selector: '[data-tut="reactourFirst"]', + content: () => ( + + ), + position: "top", + style: { fontSize: "13px" }, + action: () => handleCloseRoleModal() + }, + { + selector: '[data-tut="addWidgets"]', + content: () => ( + + ), + position: "top", + style: { fontSize: "13px" } + }, + { + selector: '[data-tut="reactourThird"]', + content: () => ( + + ), + position: "top", + style: { fontSize: "13px" } + }, + { + selector: '[data-tut="headerArea"]', + content: () => ( + + ), + position: "top", + style: { fontSize: "13px" } + } + ]; + + //function for update TourStatus + const closeTour = async () => { + setTemplateTour(false); + if (isDontShow) { + let updatedTourStatus = []; + if (tourStatus.length > 0) { + updatedTourStatus = [...tourStatus]; + const templatetourIndex = tourStatus.findIndex( + (obj) => obj["templateTour"] === false || obj["templateTour"] === true + ); + if (templatetourIndex !== -1) { + updatedTourStatus[templatetourIndex] = { templateTour: true }; + } else { + updatedTourStatus.push({ templateTour: true }); + } + } else { + updatedTourStatus = [{ templateTour: true }]; + } + try { + const url = `${localStorage.getItem( + "baseUrl" + )}functions/updatetourstatus`; + const parseAppId = localStorage.getItem("parseAppId"); + const accesstoken = localStorage.getItem("accesstoken"); + const token = jwttoken + ? { jwttoken: jwttoken } + : { "X-Parse-Session-Token": accesstoken }; + const data = { TourStatus: updatedTourStatus, ExtUserId: signerUserId }; + await axios.post(url, data, { + headers: { + "Content-Type": "application/json", + "X-Parse-Application-Id": parseAppId, + ...token + } + }); + } catch (err) { + console.log("axios err while update tourstatus", err); + } + } + }; + + // `handleAddSigner` is used to open Add Role Modal + const handleAddSigner = () => { + setIsModalRole(true); + setRoleName(""); + }; + + // `handleAddRole` function is called when use click on add button in addRole modal + // save Role in entry in signerList and user + const handleAddRole = (e) => { + e.preventDefault(); + const count = signersdata.length > 0 ? signersdata.length + 1 : 1; + const Id = randomId(); + const index = signersdata.length; + const obj = { + Role: roleName || "User " + count, + Id: Id, + blockColor: color[index] + }; + setSignersData((prevArr) => [...prevArr, obj]); + const signerPosObj = { + signerPtr: {}, + signerObjId: "", + blockColor: color[index], + Role: roleName || "User " + count, + Id: Id + }; + setSignerPos((prev) => [...prev, signerPosObj]); + setIsModalRole(false); + setRoleName(""); + setUniqueId(Id); + setBlockColor(color[index]); + setIsSelectId(index); + setIsMailSend(false); + }; + // `handleDeleteUser` function is used to delete record and placeholder when user click on delete which is place next user name in recipients list + const handleDeleteUser = (Id) => { + const updateSigner = signersdata + .filter((x) => x.Id !== Id) + .map((x, i) => ({ ...x, blockColor: color[i] })); + setSignersData(updateSigner); + const updatePlaceholderUser = signerPos + .filter((x) => x.Id !== Id) + .map((x, i) => ({ ...x, blockColor: color[i] })); + const index = signersdata.findIndex((x) => x.Id === Id); + if (index === signersdata.length - 1) { + setUniqueId(updateSigner[updateSigner.length - 1]?.Id || ""); + setIsSelectId(index - 1 || 0); + setBlockColor(color[index - 1 || 0]); + } else { + setUniqueId(updateSigner[index]?.Id || ""); + setIsSelectId(index); + setBlockColor(color[index]); + } + + setSignerPos(updatePlaceholderUser); + setIsMailSend(false); + }; + + // `handleLinkUser` is used to open Add/Choose Signer Modal when user can link existing or new User with placeholder + // and update entry in signersList + const handleLinkUser = (id) => { + setIsAddUser({ [id]: true }); + }; + // `handleAddUser` is used to adduser + const handleAddUser = (data) => { + const signerPtr = { + __type: "Pointer", + className: "contracts_Contactbook", + objectId: data.objectId + }; + + const updatePlaceHolder = signerPos.map((x) => { + if (x.Id === uniqueId) { + return { ...x, signerPtr: signerPtr, signerObjId: data.objectId }; + } + return { ...x }; + }); + setSignerPos(updatePlaceHolder); + + const updateSigner = signersdata.map((x) => { + if (x.Id === uniqueId) { + return { ...x, ...data, className: "contracts_Contactbook" }; + } + return { ...x }; + }); + setSignersData(updateSigner); + setIsMailSend(false); + const index = signersdata.findIndex((x) => x.Id === uniqueId); + setIsSelectId(index); + }; + + // `closePopup` is used to close Add/Choose signer modal + const closePopup = () => setIsAddUser({}); + + // `handleRoleChange` function is call when user update Role name from recipients list + const handleRoleChange = (event, roleId) => { + // Update the role when the content changes + const updatedRoles = signersdata.map((role) => + role.Id === roleId ? { ...role, Role: event.target.value } : role + ); + setSignersData(updatedRoles); + const updatedPlaceholder = signerPos.map((role) => + role.Id === roleId ? { ...role, Role: event.target.value } : role + ); + setSignerPos(updatedPlaceholder); + setIsMailSend(false); + }; + + // `handleOnBlur` function is call when user click outside input box + const handleOnBlur = (updateRole, roleId) => { + // Update the role when the content changes + if (!updateRole) { + const updatedRoles = signersdata.map((role) => + role.Id === roleId ? { ...role, Role: roleName } : role + ); + setSignersData(updatedRoles); + const updatedPlaceholder = signerPos?.map((role) => + role.Id === roleId ? { ...role, Role: roleName } : role + ); + setSignerPos(updatedPlaceholder); + } + }; + + const handleEditTemplateModal = () => setIsEditTemplate(!isEditTemplate); + + const handleEditTemplateForm = (data) => { + setIsEditTemplate(false); + const updateTemplate = pdfDetails.map((x) => { + return { ...x, ...data }; + }); + setPdfDetails(updateTemplate); + setIsMailSend(false); + }; + + const handleCloseRoleModal = () => { + setIsModalRole(false); + }; + + const handleSaveWidgetsOptions = ( + dropdownName, + dropdownOptions, + minCount, + maxCount, + isReadOnly, + addOption, + deleteOption, + status, + defaultValue, + isHideLabel + ) => { + const filterSignerPos = signerPos.filter((data) => data.Id === uniqueId); + if (filterSignerPos.length > 0) { + const getPlaceHolder = filterSignerPos[0].placeHolder; + + const getPageNumer = getPlaceHolder.filter( + (data) => data.pageNumber === pageNumber + ); + + if (getPageNumer.length > 0) { + const getXYdata = getPageNumer[0].pos; + + const getPosData = getXYdata; + const addSignPos = getPosData.map((position) => { + if (position.key === signKey) { + if (widgetType === radioButtonWidget) { + if (addOption) { + return { + ...position, + Height: position.Height + ? position.Height + 15 + : defaultWidthHeight(widgetType).height + 15 + }; + } else if (deleteOption) { + return { + ...position, + Height: position.Height + ? position.Height - 15 + : defaultWidthHeight(widgetType).height - 15 + }; + } else { + return { + ...position, + options: { + ...position.options, + name: dropdownName, + values: dropdownOptions, + status: status, + defaultValue: defaultValue, + isReadOnly: isReadOnly || false, + isHideLabel: isHideLabel || false, + fontSize: + fontSize || currWidgetsDetails?.options?.fontSize || "12", + fontColor: + fontColor || + currWidgetsDetails?.options?.fontColor || + "black" + } + }; + } + } else if (widgetType === "checkbox") { + if (addOption) { + return { + ...position, + Height: position.Height + ? position.Height + 15 + : defaultWidthHeight(widgetType).height + 15 + }; + } else if (deleteOption) { + return { + ...position, + Height: position.Height + ? position.Height - 15 + : defaultWidthHeight(widgetType).height - 15 + }; + } else { + return { + ...position, + options: { + ...position.options, + name: dropdownName, + values: dropdownOptions, + validation: { + minRequiredCount: minCount, + maxRequiredCount: maxCount + }, + isReadOnly: isReadOnly || false, + defaultValue: defaultValue, + isHideLabel: isHideLabel || false, + fontSize: + fontSize || currWidgetsDetails?.options?.fontSize || "12", + fontColor: + fontColor || + currWidgetsDetails?.options?.fontColor || + "black" + } + }; + } + } else { + return { + ...position, + options: { + ...position.options, + name: dropdownName, + status: status, + values: dropdownOptions, + defaultValue: defaultValue, + fontSize: + fontSize || currWidgetsDetails?.options?.fontSize || "12", + fontColor: + fontColor || + currWidgetsDetails?.options?.fontColor || + "black" + } + }; + } + } + return position; + }); + + const newUpdateSignPos = getPlaceHolder.map((obj) => { + if (obj.pageNumber === pageNumber) { + return { ...obj, pos: addSignPos }; + } + return obj; + }); + const newUpdateSigner = signerPos.map((obj) => { + if (obj.Id === uniqueId) { + return { ...obj, placeHolder: newUpdateSignPos }; + } + return obj; + }); + + setSignerPos(newUpdateSigner); + if (!addOption && !deleteOption) { + handleNameModal(); + } + } + } + setFontSize(); + setFontColor(); + }; + + const handleWidgetdefaultdata = (defaultdata) => { + const options = ["email", "number", "text"]; + let inputype; + if (defaultdata.textvalidate) { + inputype = options.includes(defaultdata.textvalidate) + ? defaultdata.textvalidate + : "regex"; + } + + const filterSignerPos = signerPos.filter((data) => data.Id === uniqueId); + if (filterSignerPos.length > 0) { + const getPlaceHolder = filterSignerPos[0].placeHolder; + + const getPageNumer = getPlaceHolder.filter( + (data) => data.pageNumber === pageNumber + ); + + if (getPageNumer.length > 0) { + const getXYdata = getPageNumer[0].pos; + const getPosData = getXYdata; + const addSignPos = getPosData.map((position) => { + if (position.key === signKey) { + if (position.type === textInputWidget) { + return { + ...position, + options: { + ...position.options, + name: defaultdata?.name || "text", + status: defaultdata?.status || "required", + hint: defaultdata?.hint || "", + defaultValue: defaultdata?.defaultValue || "", + validation: + isSubscribe && inputype + ? { + type: inputype, + pattern: + inputype === "regex" ? defaultdata.textvalidate : "" + } + : {}, + fontSize: + fontSize || currWidgetsDetails?.options?.fontSize || "12", + fontColor: + fontColor || + currWidgetsDetails?.options?.fontColor || + "black" + } + }; + } else { + return { + ...position, + options: { + ...position.options, + name: defaultdata.name, + status: defaultdata.status, + defaultValue: defaultdata.defaultValue, + fontSize: + fontSize || currWidgetsDetails?.options?.fontSize || "12", + fontColor: + fontColor || + currWidgetsDetails?.options?.fontColor || + "black" + } + }; + } + } + return position; + }); + + const newUpdateSignPos = getPlaceHolder.map((obj) => { + if (obj.pageNumber === pageNumber) { + return { ...obj, pos: addSignPos }; + } + return obj; + }); + const newUpdateSigner = signerPos.map((obj) => { + if (obj.Id === uniqueId) { + return { ...obj, placeHolder: newUpdateSignPos }; + } + return obj; + }); + setSignerPos(newUpdateSigner); + } + } + setFontSize(); + setFontColor(); + setCurrWidgetsDetails({}); + handleNameModal(); + }; + + const handleNameModal = () => { + setIsNameModal(false); + setCurrWidgetsDetails({}); + setShowDropdown(false); + setIsRadio(false); + setIsCheckbox(false); + }; + + const clickOnZoomIn = () => { + onClickZoomIn(scale, zoomPercent, setScale, setZoomPercent); + }; + const clickOnZoomOut = () => { + onClickZoomOut(zoomPercent, scale, setZoomPercent, setScale); + }; + //`handleRotationFun` function is used to roatate pdf particular page + const handleRotationFun = async (rotateDegree) => { + const isRotate = handleRotateWarning(signerPos, pageNumber); + if (isRotate) { + setIsRotate({ status: true, degree: rotateDegree }); + } else { + const urlDetails = await rotatePdfPage( + pdfDetails[0].URL, + rotateDegree, + pageNumber - 1, + pdfRotateBase64 + ); + setPdfRotatese64(urlDetails.base64); + } + }; + const handleRemovePlaceholder = async () => { + handleRemoveWidgets(setSignerPos, signerPos, pageNumber, setIsRotate); + const urlDetails = await rotatePdfPage( + pdfDetails[0].URL, + isRotate.degree, + pageNumber - 1, + pdfRotateBase64 + ); + setPdfRotatese64(urlDetails.base64); + }; + // `handleQuickSendClose` is trigger when bulk send component trigger close event + const handleQuickSendClose = (status, count) => { + if (status === "success") { + setIsCreateDocModal(false); + if (count > 1) { + setIsAlert({ + type: "success", + msg: count + " " + t("document-sent-alert") + }); + setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500); + } else { + setIsAlert({ + type: "success", + msg: count + " " + t("document-sent-alert") + }); + setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500); + } + } else { + setIsAlert({ + type: "danger", + message: t("something-went-wrong-mssg") + }); + setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500); + } + }; + const handleGeneratePublic = async () => { + const unlinkSignerIndex = signerPos?.findIndex((x) => !x?.signerObjId); + const unlinkSigners = signerPos?.filter((x) => !x?.signerObjId)?.length; // unLink with role + if (unlinkSignerIndex === 0 && unlinkSigners === 1) { + try { + const data = { templateId: templateId, IsPublic: true }; + const baseURL = localStorage.getItem("baseUrl"); + const url = `${baseURL}functions/updatetopublictemplate`; + const headers = { + "Content-Type": "application/json", + "X-Parse-Application-Id": localStorage.getItem("parseAppId"), + jwttoken: jwttoken + }; + await axios.post(url, data, { headers }); + // console.log("updatetopublictemplate", res.data); + setIsPublic("true"); + } catch (e) { + alert(t("something-went-wrong-mssg")); + console.log("error", e); + } + } else { + // setIsPublicError(true); + alert( + "Only one signer without a role is allowed, and it should be at the first index." + ); + } + }; + const copytoclipboard = (publicUrl) => { + copytoData(publicUrl); + setCopied(true); + setTimeout(() => setCopied(false), 1500); // Reset copied state after 1.5 seconds + }; + + // `handleSendDocument` is trigger when users are linked to all role + const handleSendDocument = async () => { + setIsLoading({ isLoad: true, message: t("loading-mssg") }); + const signers = signersdata?.filter((x) => x.objectId) || []; + let pdfUrl = pdfDetails[0]?.URL; + if (pdfRotateBase64) { + try { + pdfUrl = await convertBase64ToFile(pdfDetails[0].Name, pdfRotateBase64); + } catch (e) { + console.log("error to convertBase64ToFile", e); + } + } + let template = pdfDetails[0]; + template.URL = pdfUrl; + template.Signers = signers; + template.Placeholders = signerPos; + const Documents = [template]; + const token = jwttoken + ? { jwttoken: jwttoken } + : { "X-Parse-Session-Token": localStorage.getItem("accesstoken") }; + const headers = { + "Content-Type": "application/json", + "X-Parse-Application-Id": localStorage.getItem("parseAppId"), + ...token + }; + const functionsUrl = `${localStorage.getItem( + "baseUrl" + )}functions/batchdocuments`; + const params = { Documents: JSON.stringify(Documents) }; + try { + const res = await axios.post(functionsUrl, params, { headers: headers }); + if (res.data && res.data.result === "success") { + setIsAlert({ + type: "success", + msg: Documents?.length + " " + t("document-sent-alert") + }); + } + } catch (err) { + console.log("err in send document ", err); + setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") }); + } finally { + setIsLoading({ isLoad: false }); + setIsCreateDocModal(false); + setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500); + } + }; + //function show signer list and share link to share signUrl + const handlePublicShare = () => { + let publicUrl; + if (isStaging) { + publicUrl = `https://staging.opensign.me/publicsign?templateid=${templateId}`; + } else { + publicUrl = `https://opensign.me/publicsign?templateid=${templateId}`; + } + return ( +
+ {copied && {t("copied")}} + + {publicUrl} + +
+ + + + +
+
+ ); + }; + + return ( + <> + + {isAlert.msg && <Alert type={isAlert.type}>{isAlert.msg}</Alert>} + <DndProvider backend={HTML5Backend}> + {isLoading.isLoad ? ( + <LoaderWithMsg isLoading={isLoading} /> + ) : isSubscriptionExpired ? ( + <ModalUi + title={t("subscription-expired")} + isOpen={isSubscriptionExpired} + showClose={false} + > + <div className="flex flex-col justify-center items-center py-4 md:py-5 gap-5"> + <p className="text-sm md:text-lg font-normal"> + {t("owner-subscription-expired")} + </p> + </div> + </ModalUi> + ) : handleError ? ( + <HandleError handleError={handleError} /> + ) : ( + <div> + <div className="relative op-card overflow-hidden flex flex-col md:flex-row justify-between bg-base-300"> + {/* this component used for UI interaction and show their functionality */} + {!checkTourStatus && ( + //this tour component used in your html component where you want to put + //onRequestClose function to close tour + //steps is defined what will be your messages and style also + //isOpen is takes boolean value to open + <Tour + onRequestClose={closeTour} + steps={tourConfig} + isOpen={templateTour} + rounded={5} + closeWithMask={false} + /> + )} + {isAddRole && ( + <Tour + onRequestClose={() => setIsAddRole(false)} + steps={tourAddRole} + isOpen={isAddRole} + rounded={5} + closeWithMask={false} + /> + )} + {/* this component used to render all pdf pages in left side */} + <RenderAllPdfPage + signPdfUrl={pdfDetails[0].URL} + allPages={allPages} + setAllPages={setAllPages} + setPageNumber={setPageNumber} + setSignBtnPosition={setSignBtnPosition} + pageNumber={pageNumber} + /> + + {/* pdf render view */} + <div className=" w-full md:w-[57%] flex mr-4"> + <PdfZoom + clickOnZoomIn={clickOnZoomIn} + clickOnZoomOut={clickOnZoomOut} + handleRotationFun={handleRotationFun} + /> + <div className="w-full md:w-[95%]"> + {/* this modal is used show alert set placeholder for all signers before send mail */} + <ModalUi + isOpen={isSendAlert} + title={t("fields-required")} + handleClose={() => setIsSendAlert(false)} + > + <div className="h-full p-[20px]"> + <p>{t("template-signature-field")}</p> + </div> + </ModalUi> + <ModalUi + isOpen={!IsReceipent} + title={t("roles")} + handleClose={() => setIsReceipent(true)} + > + <div className="h-full p-[20px] text-center font-medium"> + <p>{t("template-role-alert")}</p> + </div> + </ModalUi> + {/* this modal is used show send mail message and after send mail success message */} + {isCreateDocModal && ( + <ModalUi + isOpen + title={isAllRoleLinked ? "Send Document" : "Bulk send"} + handleClose={() => setIsCreateDocModal(false)} + > + {isAllRoleLinked ? ( + <div className="px-[20px] pb-3"> + <div className="mb-3 mt-2 text-base"> + Are you sure want to send document? + </div> + <div className="flex flex-row gap-2"> + <button + className="op-btn op-btn-primary w-[80px]" + onClick={() => handleSendDocument()} + > + Yes + </button> + <button + className="op-btn op-btn-secondary w-[80px]" + onClick={() => setIsCreateDocModal(false)} + > + No + </button> + </div> + </div> + ) : ( + <> + <BulkSendUi + Placeholders={signerPos} + item={pdfDetails?.[0]} + handleClose={handleQuickSendClose} + jwttoken={jwttoken} + /> + {isPublic !== "false" && ( + <div className="px-[20px] pb-3"> + <div className="flex justify-center items-center mt-3"> + <span className="h-[1px] w-[20%] bg-[#ccc]"></span> + <span className="ml-[5px] mr-[5px]"> + {t("or")} + </span> + <span className="h-[1px] w-[20%] bg-[#ccc]"></span> + </div> + <div className="my-3 "> + <h3 className="text-base-content font-bold text-lg pb-[15px]"> + Copy/share signing link + </h3> + + {isPublic === "undefined" ? ( + <button + className="op-btn op-btn-secondary w-full" + onClick={() => handleGeneratePublic()} + > + Generate Public Link + </button> + ) : ( + handlePublicShare() + )} + </div> + </div> + )} + </> + )} + </ModalUi> + )} + <ModalUi + isOpen={isShowEmail} + title={t("signers-alert")} + handleClose={() => setIsShowEmail(false)} + > + <div className="h-full p-[20px]"> + <p>{t("template-creation-alert-1")}</p> + <div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div> + <button + onClick={() => setIsShowEmail(false)} + type="button" + className="op-btn op-btn-primary" + > + {t("ok")} + </button> + </div> + </ModalUi> + <DropdownWidgetOption + type={radioButtonWidget} + title={t("radio-group")} + showDropdown={isRadio} + setShowDropdown={setIsRadio} + handleSaveWidgetsOptions={handleSaveWidgetsOptions} + currWidgetsDetails={currWidgetsDetails} + setCurrWidgetsDetails={setCurrWidgetsDetails} + handleClose={handleNameModal} + isSubscribe={isSubscribe} + fontSize={fontSize} + setFontSize={setFontSize} + fontColor={fontColor} + setFontColor={setFontColor} + /> + <DropdownWidgetOption + type="checkbox" + title={t("checkbox")} + showDropdown={isCheckbox} + setShowDropdown={setIsCheckbox} + handleSaveWidgetsOptions={handleSaveWidgetsOptions} + currWidgetsDetails={currWidgetsDetails} + setCurrWidgetsDetails={setCurrWidgetsDetails} + handleClose={handleNameModal} + isSubscribe={isSubscribe} + fontSize={fontSize} + setFontSize={setFontSize} + fontColor={fontColor} + setFontColor={setFontColor} + /> + <DropdownWidgetOption + type="dropdown" + title={t("dropdown-options")} + showDropdown={showDropdown} + setShowDropdown={setShowDropdown} + handleSaveWidgetsOptions={handleSaveWidgetsOptions} + currWidgetsDetails={currWidgetsDetails} + setCurrWidgetsDetails={setCurrWidgetsDetails} + handleClose={handleNameModal} + isSubscribe={isSubscribe} + fontSize={fontSize} + setFontSize={setFontSize} + fontColor={fontColor} + setFontColor={setFontColor} + /> + <PlaceholderCopy + isPageCopy={isPageCopy} + setIsPageCopy={setIsPageCopy} + xyPostion={signerPos} + setXyPostion={setSignerPos} + allPages={allPages} + pageNumber={pageNumber} + signKey={signKey} + Id={uniqueId} + /> + {/* pdf header which contain funish back button */} + <Header + completeBtnTitle={t("send")} + isPlaceholder={true} + pageNumber={pageNumber} + allPages={allPages} + changePage={changePage} + pdfDetails={pdfDetails} + signerPos={signerPos} + signersdata={signersdata} + isMailSend={isMailSend} + alertSendEmail={alertSendEmail} + isShowHeader={true} + currentSigner={true} + setIsEditTemplate={handleEditTemplateModal} + dataTut4="reactourFour" + handleRotationFun={handleRotationFun} + clickOnZoomIn={clickOnZoomIn} + clickOnZoomOut={clickOnZoomOut} + /> + <div + ref={divRef} + data-tut="reactourThird" + className="h-[95%]" + > + {containerWH && ( + <RenderPdf + pageNumber={pageNumber} + pdfNewWidth={pdfNewWidth} + pdfDetails={pdfDetails} + signerPos={signerPos} + successEmail={false} + numPages={numPages} + pageDetails={pageDetails} + placeholder={true} + drop={drop} + handleDeleteSign={handleDeleteSign} + handleTabDrag={handleTabDrag} + handleStop={handleStop} + setPdfLoad={setPdfLoad} + pdfLoad={pdfLoad} + setSignerPos={setSignerPos} + containerWH={containerWH} + setIsResize={setIsResize} + setZIndex={setZIndex} + handleLinkUser={handleLinkUser} + setUniqueId={setUniqueId} + signersdata={signersdata} + setIsPageCopy={setIsPageCopy} + setSignKey={setSignKey} + isDragging={isDragging} + setShowDropdown={setShowDropdown} + setCurrWidgetsDetails={setCurrWidgetsDetails} + setWidgetType={setWidgetType} + setIsRadio={setIsRadio} + setSelectWidgetId={setSelectWidgetId} + selectWidgetId={selectWidgetId} + setIsCheckbox={setIsCheckbox} + handleNameModal={setIsNameModal} + pdfOriginalWH={pdfOriginalWH} + setScale={setScale} + scale={scale} + setIsSelectId={setIsSelectId} + pdfRotateBase64={pdfRotateBase64} + fontSize={fontSize} + setFontSize={setFontSize} + fontColor={fontColor} + setFontColor={setFontColor} + isResize={isResize} + /> + )} + </div> + </div> + </div> + {/* signature button */} + {isMobile ? ( + <div> + <WidgetComponent + dataTut2="reactourSecond" + pdfUrl={isMailSend} + dragSignature={dragSignature} + signRef={signRef} + handleDivClick={handleDivClick} + handleMouseLeave={handleMouseLeave} + isDragSign={isDragSign} + dragStamp={dragStamp} + dragRef={dragRef} + isDragStamp={isDragStamp} + isSignYourself={false} + addPositionOfSignature={addPositionOfSignature} + signerPos={signerPos} + signersdata={signersdata} + isSelectListId={isSelectListId} + setIsSelectId={setIsSelectId} + isSigners={isSigners} + setIsShowEmail={setIsShowEmail} + isMailSend={isMailSend} + setSelectedEmail={setSelectedEmail} + selectedEmail={selectedEmail} + handleAddSigner={handleAddSigner} + setUniqueId={setUniqueId} + setRoleName={setRoleName} + handleDeleteUser={handleDeleteUser} + handleRoleChange={handleRoleChange} + handleOnBlur={handleOnBlur} + title={t("roles")} + initial={true} + isTemplateFlow={true} + sendInOrder={pdfDetails[0].SendinOrder} + setSignersData={setSignersData} + blockColor={blockColor} + setBlockColor={setBlockColor} + /> + </div> + ) : ( + <div className="w-[23%] bg-base-100 min-h-screen overflow-y-auto hide-scrollbar"> + <div className="max-h-screen"> + <SignerListPlace + isMailSend={isMailSend} + signerPos={signerPos} + setSignerPos={setSignerPos} + signersdata={signersdata} + isSelectListId={isSelectListId} + setRoleName={setRoleName} + setIsSelectId={setIsSelectId} + handleAddSigner={handleAddSigner} + setUniqueId={setUniqueId} + handleDeleteUser={handleDeleteUser} + handleRoleChange={handleRoleChange} + handleOnBlur={handleOnBlur} + title={t("roles")} + sendInOrder={pdfDetails[0]?.SendinOrder} + setSignersData={setSignersData} + blockColor={blockColor} + setBlockColor={setBlockColor} + uniqueId={uniqueId} + /> + <div data-tut="addWidgets"> + <WidgetComponent + isMailSend={isMailSend} + dragSignature={dragSignature} + signRef={signRef} + handleDivClick={handleDivClick} + handleMouseLeave={handleMouseLeave} + isDragSign={isDragSign} + dragStamp={dragStamp} + dragRef={dragRef} + isDragStamp={isDragStamp} + isSignYourself={false} + addPositionOfSignature={addPositionOfSignature} + title={t("roles")} + initial={true} + isTemplateFlow={true} + /> + </div> + </div> + </div> + )} + </div> + </div> + )} + <div data-tut="reactourAddbtn--observe"> + <AddRoleModal + isModalRole={isModalRole} + roleName={roleName} + signersdata={signersdata} + setRoleName={setRoleName} + handleAddRole={handleAddRole} + handleCloseRoleModal={handleCloseRoleModal} + /> + </div> + <div> + <LinkUserModal + handleAddUser={handleAddUser} + isAddUser={isAddUser} + uniqueId={uniqueId} + closePopup={closePopup} + signersData={signersdata} + jwttoken={jwttoken} + /> + </div> + <ModalUi + title={t("edit-template")} + isOpen={isEditTemplate} + handleClose={handleEditTemplateModal} + > + <EditTemplate + template={pdfDetails?.[0]} + onSuccess={handleEditTemplateForm} + /> + </ModalUi> + <WidgetNameModal + widgetName={widgetName} + defaultdata={currWidgetsDetails} + isOpen={isNameModal} + handleClose={handleNameModal} + handleData={handleWidgetdefaultdata} + isSubscribe={isSubscribe} + isTextSetting={isTextSetting} + setIsTextSetting={setIsTextSetting} + fontSize={fontSize} + setFontSize={setFontSize} + fontColor={fontColor} + setFontColor={setFontColor} + /> + <RotateAlert + isRotate={isRotate.status} + setIsRotate={setIsRotate} + handleRemoveWidgets={handleRemovePlaceholder} + /> + </DndProvider> + </> + ); +}; + +export default DraftTemplate; diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index 6d9d98211..0e0748d3f 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -132,12 +132,10 @@ function PdfRequestFiles(props) { const [publicRes, setPublicRes] = useState({}); const [documentId, setDocumentId] = useState(""); const [isPublicContact, setIsPublicContact] = useState(false); - const [pdfArrayBuffer, setPdfArrayBuffer] = useState(""); const [plancode, setPlanCode] = useState(""); const isHeader = useSelector((state) => state.showHeader); const divRef = useRef(null); const [isDownloadModal, setIsDownloadModal] = useState(false); - const isMobile = window.innerWidth < 767; let isGuestSignFlow = false; @@ -323,8 +321,6 @@ function PdfRequestFiles(props) { const arrayBuffer = await convertPdfArrayBuffer(url); if (arrayBuffer === "Error") { setHandleError(t("something-went-wrong-mssg")); - } else { - setPdfArrayBuffer(arrayBuffer); } } else { setHandleError(t("something-went-wrong-mssg")); @@ -408,8 +404,6 @@ function PdfRequestFiles(props) { const arrayBuffer = await convertPdfArrayBuffer(url); if (arrayBuffer === "Error") { setHandleError(t("something-went-wrong-mssg")); - } else { - setPdfArrayBuffer(arrayBuffer); } } else { setHandleError(t("something-went-wrong-mssg")); diff --git a/apps/OpenSign/src/primitives/AddContact.js b/apps/OpenSign/src/primitives/AddContact.js index c25024c85..0365c2b5b 100644 --- a/apps/OpenSign/src/primitives/AddContact.js +++ b/apps/OpenSign/src/primitives/AddContact.js @@ -1,7 +1,8 @@ import React, { useState, useEffect } from "react"; -import Parse from "parse"; import Loader from "./Loader"; import { useTranslation } from "react-i18next"; +import axios from "axios"; +import { getTenantDetails } from "../constant/Utils"; const AddContact = (props) => { const { t } = useTranslation(); const [name, setName] = useState(""); @@ -12,6 +13,7 @@ const AddContact = (props) => { const [isUserExist, setIsUserExist] = useState(false); useEffect(() => { checkUserExist(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // Load user details from localStorage when the component mounts useEffect(() => { @@ -26,19 +28,24 @@ const AddContact = (props) => { }, [addYourself]); const checkUserExist = async () => { - const user = Parse.User.current(); try { - const query = new Parse.Query("contracts_Contactbook"); - query.equalTo("CreatedBy", user); - query.notEqualTo("IsDeleted", true); - query.equalTo("Email", user.getEmail()); - const res = await query.first(); - // console.log(res); - if (!res) { + const baseURL = localStorage.getItem("baseUrl"); + const url = `${baseURL}functions/isuserincontactbook`; + const token = props?.jwttoken + ? { jwttoken: props?.jwttoken } + : { "X-Parse-Session-Token": localStorage.getItem("accesstoken") }; + const headers = { + "Content-Type": "application/json", + "X-Parse-Application-Id": localStorage.getItem("parseAppId"), + ...token + }; + const axiosRes = await axios.post(url, {}, { headers }); + const contactRes = axiosRes?.data?.result || {}; + if (!contactRes?.objectId) { setIsUserExist(true); } } catch (err) { - console.log("err", err); + console.log("err ", err); } }; // Define a function to handle form submission @@ -46,123 +53,51 @@ const AddContact = (props) => { e.preventDefault(); e.stopPropagation(); setIsLoader(true); - if (localStorage.getItem("TenantId")) { + const user = JSON.parse( + localStorage.getItem( + `Parse/${localStorage.getItem("parseAppId")}/currentUser` + ) + ); + const userId = user?.objectId || ""; + const tenantDetails = await getTenantDetails(userId, props.jwttoken); + console.log("tenantDetails", tenantDetails); + const tenantId = tenantDetails?.objectId || ""; + if (tenantId) { try { - const user = Parse.User.current(); - const query = new Parse.Query("contracts_Contactbook"); - query.equalTo("CreatedBy", user); - query.notEqualTo("IsDeleted", true); - query.equalTo("Email", email); - const res = await query.first(); - if (!res) { - const contactQuery = new Parse.Object("contracts_Contactbook"); - contactQuery.set("Name", name); - if (phone) { - contactQuery.set("Phone", phone); - } - contactQuery.set("Email", email); - contactQuery.set("UserRole", "contracts_Guest"); - - if (localStorage.getItem("TenantId")) { - contactQuery.set("TenantId", { - __type: "Pointer", - className: "partners_Tenant", - objectId: localStorage.getItem("TenantId") - }); + const baseURL = localStorage.getItem("baseUrl"); + const url = `${baseURL}functions/savecontact`; + const token = props?.jwttoken + ? { jwttoken: props?.jwttoken } + : { "X-Parse-Session-Token": localStorage.getItem("accesstoken") }; + const data = { name, email, phone, tenantId }; + const headers = { + "Content-Type": "application/json", + "X-Parse-Application-Id": localStorage.getItem("parseAppId"), + ...token + }; + const axiosRes = await axios.post(url, data, { headers }); + const contactRes = axiosRes?.data?.result || {}; + if (contactRes?.objectId) { + console.log("contactRes ", contactRes); + props.details(contactRes); + if (props.closePopup) { + props.closePopup(); + setIsLoader(false); + // Reset the form fields + setAddYourself(false); + setName(""); + setPhone(""); + setEmail(""); } - - try { - const _users = Parse.Object.extend("User"); - const _user = new _users(); - _user.set("name", name); - _user.set("username", email); - _user.set("email", email); - _user.set("password", email); - if (phone) { - _user.set("phone", phone); - } - - const user = await _user.save(); - if (user) { - const currentUser = Parse.User.current(); - contactQuery.set( - "CreatedBy", - Parse.User.createWithoutData(currentUser.id) - ); - - contactQuery.set("UserId", user); - const acl = new Parse.ACL(); - acl.setPublicReadAccess(true); - acl.setPublicWriteAccess(true); - acl.setReadAccess(currentUser.id, true); - acl.setWriteAccess(currentUser.id, true); - - contactQuery.setACL(acl); - - const res = await contactQuery.save(); - - const parseData = JSON.parse(JSON.stringify(res)); - props.details(parseData); - if (props.closePopup) { - props.closePopup(); - } - - setIsLoader(false); - // Reset the form fields - setAddYourself(false); - setName(""); - setPhone(""); - setEmail(""); - } - } catch (err) { - console.log("err ", err); - if (err.code === 202) { - const params = { email: email }; - const userRes = await Parse.Cloud.run("getUserId", params); - const currentUser = Parse.User.current(); - contactQuery.set( - "CreatedBy", - Parse.User.createWithoutData(currentUser.id) - ); - - contactQuery.set("UserId", { - __type: "Pointer", - className: "_User", - objectId: userRes.id - }); - const acl = new Parse.ACL(); - acl.setPublicReadAccess(true); - acl.setPublicWriteAccess(true); - acl.setReadAccess(currentUser.id, true); - acl.setWriteAccess(currentUser.id, true); - - contactQuery.setACL(acl); - const res = await contactQuery.save(); - - const parseData = JSON.parse(JSON.stringify(res)); - if (props.details) { - props.details(parseData); - } - - if (props.closePopup) { - props.closePopup(); - } - setIsLoader(false); - // Reset the form fields - setAddYourself(false); - setName(""); - setPhone(""); - setEmail(""); - } - } - } else { - alert(t("add-signer-alert")); - setIsLoader(false); } } catch (err) { - // console.log("err", err); + console.log("Err", err); setIsLoader(false); - alert(t("something-went-wrong-mssg")); + if (err?.response?.data?.error?.includes("already exists")) { + alert(t("add-signer-alert")); + } else { + alert(t("something-went-wrong-mssg")); + } } } else { setIsLoader(false); diff --git a/apps/OpenSign/src/primitives/LinkUserModal.js b/apps/OpenSign/src/primitives/LinkUserModal.js index ed3bef0db..dd7479ff9 100644 --- a/apps/OpenSign/src/primitives/LinkUserModal.js +++ b/apps/OpenSign/src/primitives/LinkUserModal.js @@ -19,11 +19,16 @@ const LinkUserModal = (props) => { details={props.handleAddUser} closePopup={props.closePopup} signersData={props?.signersData} + jwttoken={props?.jwttoken} /> <div className="op-divider text-base-content mx-[25%] my-1"> {t("or")} </div> - <AddContact details={props.handleAddUser} closePopup={props.closePopup} /> + <AddContact + details={props.handleAddUser} + closePopup={props.closePopup} + jwttoken={props?.jwttoken} + /> </ModalUi> ); }; diff --git a/apps/OpenSignServer/Utils.js b/apps/OpenSignServer/Utils.js index a721fff43..776aca636 100644 --- a/apps/OpenSignServer/Utils.js +++ b/apps/OpenSignServer/Utils.js @@ -234,3 +234,7 @@ export const planCredits = { 'teams-monthly': 100, 'teams-yearly': 500, }; + +export function parseJwt(token) { + return JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()); +} diff --git a/apps/OpenSignServer/cloud/customRoute/v1/apiV1.js b/apps/OpenSignServer/cloud/customRoute/v1/apiV1.js index 2ccd5a1c7..dbac393b4 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/apiV1.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/apiV1.js @@ -9,7 +9,6 @@ import getDocument from './routes/getDocument.js'; import getContact from './routes/getContact.js'; import deleteContact from './routes/deleteContact.js'; import getContactList from './routes/getContactList.js'; -import draftDocument from './routes/draftDocument.js'; import getTemplate from './routes/getTemplate.js'; import deletedTemplate from './routes/deleteTemplate.js'; import getTemplatetList from './routes/getTemplateList.js'; @@ -30,6 +29,7 @@ import createFolder from './routes/createFolder.js'; import updateFolder from './routes/updateFolder.js'; import getFolderList from './routes/getFolderList.js'; import deleteFolder from './routes/deleteFolder.js'; +import draftTemplate from './routes/draftTemplate.js'; dotenv.config(); const storage = multer.memoryStorage(); const upload = multer({ storage: storage }); @@ -59,9 +59,6 @@ app.post('/createdocumentwithbinary', upload.array('file', 1), createDocumentwit // create Document with co-ordinate app.post('/createdocument', createDocumentwithCoordinate); -// create Document with base64 without placeholder -app.post('/draftdocument', draftDocument); - // create Document with templateId app.post('/createdocument/:template_id', createDocumentWithTemplate); @@ -77,6 +74,9 @@ app.delete('/document/:document_id', deleteDocument); // get all types of documents on the basis of doctype app.get('/documentlist/:doctype', getDocumentList); +// create draft template with file base64 +app.post('/drafttemplate', draftTemplate); + // create Template with co-ordinate app.post('/createtemplate', createTemplatewithCoordinate); diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js index 988d97efa..72e119ff1 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js @@ -49,7 +49,7 @@ export default async function createDocumentWithTemplate(request, response) { const send_email = request.body.send_email; const email_subject = request.body.email_subject; const email_body = request.body.email_body; - const sendInOrder = request.body.sendInOrder || false; + const sendInOrder = request.body.sendInOrder || true; const TimeToCompleteDays = request.body.timeToCompleteDays || 15; try { diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js index a6d061266..3ed23217d 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js @@ -62,7 +62,7 @@ export default async function createDocumentwithCoordinate(request, response) { const fileData = request.files?.[0] ? request.files[0].buffer : null; const email_subject = request.body.email_subject; const email_body = request.body.email_body; - const sendInOrder = request.body.sendInOrder || false; + const sendInOrder = request.body.sendInOrder || true; const TimeToCompleteDays = request.body.timeToCompleteDays || 15; const IsEnableOTP = request.body?.enableOTP === true ? true : false; const isTourEnabled = request.body?.enableTour || false; diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/createTemplatewithCoordinate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/createTemplatewithCoordinate.js index 04e87e5fe..d93c1dce0 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/createTemplatewithCoordinate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/createTemplatewithCoordinate.js @@ -17,7 +17,7 @@ export default async function createTemplatewithCoordinate(request, response) { const folderId = request.body.folderId; const base64File = request.body.file; const fileData = request.files?.[0] ? request.files[0].buffer : null; - const SendinOrder = request.body.sendInOrder || false; + const SendinOrder = request.body.sendInOrder || true; const isEnableOTP = request.body?.enableOTP === true ? true : false; const isTourEnabled = request.body?.enableTour || false; diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/draftDocument.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/draftDocument.js deleted file mode 100644 index 9a04bbbfd..000000000 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/draftDocument.js +++ /dev/null @@ -1,168 +0,0 @@ -import axios from 'axios'; -import { customAPIurl, saveFileUsage } from '../../../../Utils.js'; - -// const randomId = () => Math.floor(1000 + Math.random() * 9000); -export default async function draftDocument(request, response) { - const name = request.body.title; - const note = request.body.note; - const description = request.body.description; - const signers = request.body.signers; - const folderId = request.body.folderId; - const base64File = request.body.file; - const send_email = request.body.send_email || true; - const fileData = request.files?.[0] ? request.files[0].buffer : null; - const SendinOrder = request.body.sendInOrder || false; - const isEnableOTP = request.body?.enableOTP === true ? true : false; - const isTourEnabled = request.body?.enableTour || false; - - // console.log('fileData ', fileData); - const protocol = customAPIurl(); - const baseUrl = new URL(process.env.PUBLIC_URL); - - try { - const reqToken = request.headers['x-api-token']; - if (!reqToken) { - return response.status(400).json({ error: 'Please Provide API Token' }); - } - const tokenQuery = new Parse.Query('appToken'); - tokenQuery.equalTo('token', reqToken); - tokenQuery.include('userId'); - const token = await tokenQuery.first({ useMasterKey: true }); - if (token !== undefined) { - const parseUser = JSON.parse(JSON.stringify(token)); - const userPtr = { - __type: 'Pointer', - className: '_User', - objectId: parseUser.userId.objectId, - }; - // Valid Token then proceed request - if (signers && signers.length > 0) { - let fileUrl; - if (request.files?.[0]) { - const base64 = fileData?.toString('base64'); - const file = new Parse.File(request.files?.[0]?.originalname, { - base64: base64, - }); - await file.save({ useMasterKey: true }); - fileUrl = file.url(); - const buffer = Buffer.from(base64, 'base64'); - saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId); - } else { - const file = new Parse.File(`${name}.pdf`, { - base64: base64File, - }); - await file.save({ useMasterKey: true }); - fileUrl = file.url(); - const buffer = Buffer.from(base64File, 'base64'); - saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId); - } - const contractsUser = new Parse.Query('contracts_Users'); - contractsUser.equalTo('UserId', userPtr); - const extUser = await contractsUser.first({ useMasterKey: true }); - const extUserPtr = { - __type: 'Pointer', - className: 'contracts_Users', - objectId: extUser.id, - }; - - const folderPtr = { - __type: 'Pointer', - className: 'contracts_Document', - objectId: folderId, - }; - - const object = new Parse.Object('contracts_Document'); - object.set('Name', name); - if (note) { - object.set('Note', note); - } - if (description) { - object.set('Description', description); - } - if (SendinOrder) { - object.set('SendinOrder', SendinOrder); - } - object.set('URL', fileUrl); - object.set('CreatedBy', userPtr); - object.set('ExtUserPtr', extUserPtr); - object.set('IsSendMail', send_email); - object.set('IsEnableOTP', isEnableOTP); - object.set('IsTourEnabled', isTourEnabled); - if (signers && signers.length > 0) { - let parseSigners; - if (base64File) { - parseSigners = signers; - } else { - parseSigners = JSON.parse(signers); - } - let createContactUrl = protocol + '/v1/createcontact'; - - let contact = []; - for (const obj of parseSigners) { - const body = { - name: obj?.name || '', - email: obj?.email || '', - phone: obj?.phone || '', - }; - try { - const res = await axios.post(createContactUrl, body, { - headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken }, - }); - // console.log('res ', res.data); - contact.push({ - __type: 'Pointer', - className: 'contracts_Contactbook', - objectId: res.data?.objectId, - }); - } catch (err) { - // console.log('err ', err.response); - if (err?.response?.data?.objectId) { - contact.push({ - __type: 'Pointer', - className: 'contracts_Contactbook', - objectId: err.response.data?.objectId, - }); - } - } - } - object.set('Signers', contact); - } - if (folderId) { - object.set('Folder', folderPtr); - } - const newACL = new Parse.ACL(); - newACL.setPublicReadAccess(false); - newACL.setPublicWriteAccess(false); - newACL.setReadAccess(userPtr.objectId, true); - newACL.setWriteAccess(userPtr.objectId, true); - object.setACL(newACL); - const res = await object.save(null, { useMasterKey: true }); - if (request.posthog) { - request.posthog?.capture({ - distinctId: parseUser.userId.email, - event: 'api_draft_document', - properties: { response_code: 200 }, - }); - } - return response.json({ - objectId: res.id, - url: `${baseUrl.origin}/placeholdersign/${res.id}`, - }); - } else { - if (request.posthog) { - request.posthog?.capture({ - distinctId: parseUser.userId.email, - event: 'api_draft_document', - properties: { response_code: 400 }, - }); - } - return response.status(400).json({ error: 'Please provide signers!' }); - } - } else { - return response.status(405).json({ error: 'Invalid API Token!' }); - } - } catch (err) { - console.log('err ', err); - return response.status(400).json({ error: 'Something went wrong, please try again later!' }); - } -} diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/draftTemplate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/draftTemplate.js new file mode 100644 index 000000000..135ad7ec3 --- /dev/null +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/draftTemplate.js @@ -0,0 +1,238 @@ +import axios from 'axios'; +import { + color, + customAPIurl, + saveFileUsage, + formatWidgetOptions, + sanitizeFileName, +} from '../../../../Utils.js'; +import uploadFileToS3 from '../../../parsefunction/uploadFiletoS3.js'; +import jwt from 'jsonwebtoken'; + +const randomId = () => Math.floor(1000 + Math.random() * 9000); +export default async function draftTemplate(request, response) { + const name = request.body.title; + const note = request.body.note; + const description = request.body.description; + const signers = request.body.signers; + const folderId = request.body.folderId; + const base64File = request.body.file; + const fileData = request.files?.[0] ? request.files[0].buffer : null; + const SendinOrder = request.body.sendInOrder || true; + const isEnableOTP = request.body?.enableOTP === true ? true : false; + const isTourEnabled = request.body?.enableTour || false; + const isPublic = request.body?.isPublic; + const protocol = customAPIurl(); + const baseUrl = new URL(process.env.PUBLIC_URL).origin; + + try { + const reqToken = request.headers['x-api-token']; + if (!reqToken) { + return response.status(400).json({ error: 'Please Provide API Token' }); + } + const tokenQuery = new Parse.Query('appToken'); + tokenQuery.equalTo('token', reqToken); + tokenQuery.include('userId'); + const token = await tokenQuery.first({ useMasterKey: true }); + if (token !== undefined) { + // Valid Token then proceed request + const parseUser = JSON.parse(JSON.stringify(token)); + const userPtr = { + __type: 'Pointer', + className: '_User', + objectId: parseUser.userId.objectId, + }; + const contractsUser = new Parse.Query('contracts_Users'); + contractsUser.equalTo('UserId', userPtr); + contractsUser.include('TenantId'); + const extUser = await contractsUser.first({ useMasterKey: true }); + const parseExtUser = JSON.parse(JSON.stringify(extUser)); + let fileUrl; + if (request.files?.[0]) { + const base64 = fileData?.toString('base64'); + const file = new Parse.File(request.files?.[0]?.originalname, { + base64: base64, + }); + await file.save({ useMasterKey: true }); + fileUrl = file.url(); + const buffer = Buffer.from(base64, 'base64'); + saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId); + } else { + const filename = sanitizeFileName(`${name}.pdf`); + let adapter = {}; + const ActiveFileAdapter = parseExtUser?.TenantId?.ActiveFileAdapter || ''; + if (ActiveFileAdapter) { + adapter = + parseExtUser?.TenantId?.FileAdapters?.find(x => (x.id = ActiveFileAdapter)) || {}; + } + if (adapter?.id) { + const filedata = Buffer.from(base64File, 'base64'); + // `uploadFileToS3` is used to save document in user's file storage + fileUrl = await uploadFileToS3(filedata, filename, 'application/pdf', adapter); + } else { + const file = new Parse.File(filename, { base64: base64File }, 'application/pdf'); + await file.save({ useMasterKey: true }); + fileUrl = file.url(); + } + const buffer = Buffer.from(base64File, 'base64'); + saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId); + } + const extUserPtr = { + __type: 'Pointer', + className: 'contracts_Users', + objectId: extUser.id, + }; + const object = new Parse.Object('contracts_Template'); + object.set('Name', name); + if (note) { + object.set('Note', note); + } + if (description) { + object.set('Description', description); + } + if (SendinOrder) { + object.set('SendinOrder', SendinOrder); + } + object.set('URL', fileUrl); + object.set('CreatedBy', userPtr); + object.set('ExtUserPtr', extUserPtr); + object.set('IsEnableOTP', isEnableOTP); + object.set('IsTourEnabled', isTourEnabled); + const Public = isPublic !== undefined ? template?.IsPublic : false; + if (template?.IsPublic !== undefined) { + object.set('IsPublic', Public); + } + let contact = []; + if (signers && signers.length > 0) { + let parseSigners; + if (base64File) { + parseSigners = signers; + } else { + parseSigners = JSON.parse(signers); + } + let createContactUrl = protocol + '/v1/createcontact'; + + for (const [index, element] of parseSigners.entries()) { + if (element?.name && element?.email) { + const body = { + name: element?.name, + email: element?.email, + phone: element?.phone || '', + }; + try { + const res = await axios.post(createContactUrl, body, { + headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken }, + }); + // console.log('res ', res.data); + const contactPtr = { + __type: 'Pointer', + className: 'contracts_Contactbook', + objectId: res.data?.objectId, + }; + const newObj = { ...element, contactPtr: contactPtr, index: index }; + contact.push(newObj); + } catch (err) { + // console.log('err ', err.response); + if (err?.response?.data?.objectId) { + const contactPtr = { + __type: 'Pointer', + className: 'contracts_Contactbook', + objectId: err.response.data?.objectId, + }; + const newObj = { ...element, contactPtr: contactPtr, index: index }; + contact.push(newObj); + } + } + } else { + const newObj = { ...element, contactPtr: {}, index: index }; + contact.push(newObj); + } + } + const updatedSigners = contact?.filter(x => x.contactPtr && x.contactPtr.objectId); + if (updatedSigners && updatedSigners.length > 0) { + object.set( + 'Signers', + updatedSigners?.map(x => x.contactPtr) + ); + } + let updatePlaceholders = []; + for (const signer of contact) { + const placeHolder = []; + if (signer.widgets?.length > 0) { + for (const widget of signer.widgets) { + const pageNumber = widget.page; + const page = placeHolder.find(page => page.pageNumber === pageNumber); + const options = formatWidgetOptions(widget.type, widget.options); + const widgetData = { + isStamp: widget.type === 'stamp' || widget.type === 'image', + key: randomId(), + isDrag: false, + scale: 1, + isMobile: false, + zIndex: 1, + type: widget.type === 'textbox' ? 'text input' : widget.type, + options: options, + Width: widget.w, + Height: widget.h, + xPosition: widget.x, + yPosition: widget.y, + }; + + if (page) { + page.pos.push(widgetData); + } else { + placeHolder.push({ pageNumber, pos: [widgetData] }); + } + } + } + updatePlaceholders.push({ + signerObjId: signer?.contactPtr?.objectId || '', + signerPtr: signer?.contactPtr, + Role: signer.role, + Id: randomId(), + blockColor: color[signer?.index], + placeHolder, + }); + } + if (updatePlaceholders?.length > 0) { + object.set('Placeholders', updatePlaceholders); + } + } + if (folderId) { + object.set('Folder', { + __type: 'Pointer', + className: 'contracts_Template', + objectId: folderId, + }); + } + if (parseExtUser?.TenantId?.ActiveFileAdapter) { + object.set('FileAdapterId', parseExtUser?.TenantId?.ActiveFileAdapter); + } + const newACL = new Parse.ACL(); + newACL.setPublicReadAccess(false); + newACL.setPublicWriteAccess(false); + newACL.setReadAccess(userPtr.objectId, true); + newACL.setWriteAccess(userPtr.objectId, true); + object.setACL(newACL); + const res = await object.save(null, { useMasterKey: true }); + if (request.posthog) { + request.posthog?.capture({ + distinctId: parseUser.userId.email, + event: 'api_draft_template', + properties: { response_code: 200 }, + }); + } + const JwtToken = jwt.sign( + { user_email: parseUser.userId.email, template_id: res.id }, + reqToken + ); + + return response.json({ objectId: res.id, url: `${baseUrl}/drafttemplate/${JwtToken}` }); + } else { + return response.status(405).json({ error: 'Invalid API Token!' }); + } + } catch (err) { + console.log('err ', err); + return response.status(400).json({ error: 'Something went wrong, please try again later!' }); + } +} diff --git a/apps/OpenSignServer/cloud/main.js b/apps/OpenSignServer/cloud/main.js index b2f55d90a..343bc979b 100644 --- a/apps/OpenSignServer/cloud/main.js +++ b/apps/OpenSignServer/cloud/main.js @@ -75,6 +75,14 @@ import saveToFileAdapter from './parsefunction/saveToFileAdapter.js'; import getFileAdapter from './parsefunction/getFileAdapter.js'; import addPfxFile from './parsefunction/addPfxFile.js'; import getPfxFile from './parsefunction/getPfxFile.js'; +import getTenant from './parsefunction/getTenant.js'; +import getSigners from './parsefunction/getSigners.js'; +import saveFile from './parsefunction/saveFile.js'; +import savecontact from './parsefunction/savecontact.js'; +import isUserInContactBook from './parsefunction/isUserInContactBook.js'; +import updateTourStatus from './parsefunction/updateTourStatus.js'; +import saveTemplate from './parsefunction/saveTemplate.js'; +import updateToPublicTemplate from './parsefunction/updateToPublicTemplate.js'; // This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic. Parse.Cloud.afterSave('contracts_Document', DocumentAftersave); @@ -162,3 +170,11 @@ Parse.Cloud.define('addfileadapter', addFileAdapter); Parse.Cloud.define('getfileadapter', getFileAdapter); Parse.Cloud.define('addpfx', addPfxFile); Parse.Cloud.define('getpfx', getPfxFile); +Parse.Cloud.define('gettenant', getTenant); +Parse.Cloud.define('getsigners', getSigners); +Parse.Cloud.define('savetemplate', saveTemplate); +Parse.Cloud.define('savefile', saveFile); +Parse.Cloud.define('savecontact', savecontact); +Parse.Cloud.define('isuserincontactbook', isUserInContactBook); +Parse.Cloud.define('updatetourstatus', updateTourStatus); +Parse.Cloud.define('updatetopublictemplate', updateToPublicTemplate); diff --git a/apps/OpenSignServer/cloud/parsefunction/AllowedCredits.js b/apps/OpenSignServer/cloud/parsefunction/AllowedCredits.js index 04b0eb79e..dc9dfe1cc 100644 --- a/apps/OpenSignServer/cloud/parsefunction/AllowedCredits.js +++ b/apps/OpenSignServer/cloud/parsefunction/AllowedCredits.js @@ -1,33 +1,64 @@ -export default async function AllowedCredits(request) { - if (!request?.user) { - throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); - } - try { - const extUser = new Parse.Query('contracts_Users'); - extUser.equalTo('UserId', { +import { parseJwt } from '../../Utils.js'; +import jwt from 'jsonwebtoken'; +async function checkCredits(userId) { + const extUser = new Parse.Query('contracts_Users'); + extUser.equalTo('UserId', { + __type: 'Pointer', + className: '_User', + objectId: userId, + }); + const resExtUser = await extUser.first({ useMasterKey: true }); + if (resExtUser) { + const _resExtUser = JSON.parse(JSON.stringify(resExtUser)); + const subscription = new Parse.Query('contracts_Subscriptions'); + subscription.equalTo('TenantId', { __type: 'Pointer', - className: '_User', - objectId: request.user.id, + className: 'partners_Tenant', + objectId: _resExtUser.TenantId.objectId, }); - const resExtUser = await extUser.first({ useMasterKey: true }); - if (resExtUser) { - const _resExtUser = JSON.parse(JSON.stringify(resExtUser)); - const subscription = new Parse.Query('contracts_Subscriptions'); - subscription.equalTo('TenantId', { - __type: 'Pointer', - className: 'partners_Tenant', - objectId: _resExtUser.TenantId.objectId, - }); - subscription.include('ExtUserPtr'); - const resSub = await subscription.first({ useMasterKey: true }); - if (resSub) { - const _resSub = JSON.parse(JSON.stringify(resSub)); - const AllowedCredits = _resSub?.AllowedCredits || 0; - const AddonCredits = _resSub?.AddonCredits || 0; - return { allowedcredits: AllowedCredits, addoncredits: AddonCredits }; + subscription.include('ExtUserPtr'); + const resSub = await subscription.first({ useMasterKey: true }); + if (resSub) { + const _resSub = JSON.parse(JSON.stringify(resSub)); + const AllowedCredits = _resSub?.AllowedCredits || 0; + const AddonCredits = _resSub?.AddonCredits || 0; + return { allowedcredits: AllowedCredits, addoncredits: AddonCredits }; + } + } else { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.'); + } +} +export default async function AllowedCredits(request) { + const jwttoken = request?.headers?.jwttoken || ''; + + try { + if (request?.user) { + return await checkCredits(request.user.id); + } else if (jwttoken) { + const jwtDecode = parseJwt(jwttoken); + if (jwtDecode?.user_email) { + const userCls = new Parse.Query(Parse.User); + userCls.equalTo('email', jwtDecode?.user_email); + const userRes = await userCls.first({ useMasterKey: true }); + const userId = userRes?.id; + const tokenQuery = new Parse.Query('appToken'); + tokenQuery.equalTo('userId', { + __type: 'Pointer', + className: '_User', + objectId: userId, + }); + const appRes = await tokenQuery.first({ useMasterKey: true }); + const decoded = jwt.verify(jwttoken, appRes?.get('token')); + if (decoded?.user_email) { + return await checkCredits(userId); + } else { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token.'); + } + } else { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token.'); } } else { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.'); + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); } } catch (err) { console.log('err in allowedCredits', err); diff --git a/apps/OpenSignServer/cloud/parsefunction/GetTemplate.js b/apps/OpenSignServer/cloud/parsefunction/GetTemplate.js index 7e2821a6a..9ca1a7af6 100644 --- a/apps/OpenSignServer/cloud/parsefunction/GetTemplate.js +++ b/apps/OpenSignServer/cloud/parsefunction/GetTemplate.js @@ -1,21 +1,54 @@ import axios from 'axios'; -import { cloudServerUrl } from '../../Utils.js'; +import { cloudServerUrl, parseJwt } from '../../Utils.js'; +import jwt from 'jsonwebtoken'; export default async function GetTemplate(request) { const serverUrl = cloudServerUrl; //process.env.SERVER_URL; const templateId = request.params.templateId; const ispublic = request.params.ispublic; - + const jwttoken = request.headers?.jwttoken; + const sessiontoken = request.headers?.sessiontoken; try { if (!ispublic) { - const userRes = await axios.get(serverUrl + '/users/me', { - headers: { - 'X-Parse-Application-Id': process.env.APP_ID, - 'X-Parse-Session-Token': request.headers['sessiontoken'], - }, - }); - const userId = userRes.data && userRes.data.objectId; - if (templateId && userId) { + let userEmail; + if (jwttoken) { + try { + const jwtDecode = parseJwt(jwttoken); + if (jwtDecode?.user_email) { + const userCls = new Parse.Query(Parse.User); + userCls.equalTo('email', jwtDecode?.user_email); + const userRes = await userCls.first({ useMasterKey: true }); + const userId = userRes?.id; + const tokenQuery = new Parse.Query('appToken'); + tokenQuery.equalTo('userId', { + __type: 'Pointer', + className: '_User', + objectId: userId, + }); + const appRes = await tokenQuery.first({ useMasterKey: true }); + const decoded = jwt.verify(jwttoken, appRes?.get('token')); + if (decoded?.user_email) { + userEmail = decoded?.user_email; + } else { + return { error: 'Invalid token!' }; + } + } else { + return { error: 'Invalid token!' }; + } + } catch (err) { + console.log('err in jwt', err); + return { error: "You don't have access of this document!" }; + } + } else if (sessiontoken) { + const userRes = await axios.get(serverUrl + '/users/me', { + headers: { + 'X-Parse-Application-Id': process.env.APP_ID, + 'X-Parse-Session-Token': sessiontoken, + }, + }); + userEmail = userRes.data && userRes.data.email; + } + if (templateId && userEmail) { try { let template = new Parse.Query('contracts_Template'); template.equalTo('objectId', templateId); @@ -24,7 +57,7 @@ export default async function GetTemplate(request) { template.include('CreatedBy'); template.include('ExtUserPtr.TenantId'); const extUserQuery = new Parse.Query('contracts_Users'); - extUserQuery.equalTo('Email', userRes.data.email); + extUserQuery.equalTo('Email', userEmail); extUserQuery.include('TeamIds'); const extUser = await extUserQuery.first({ useMasterKey: true }); if (extUser) { @@ -64,6 +97,8 @@ export default async function GetTemplate(request) { console.log('err', err); return err; } + } else { + return { error: "You don't have access of this document!" }; } } else if (templateId && ispublic) { try { @@ -92,7 +127,7 @@ export default async function GetTemplate(request) { } } catch (err) { console.log('err', err); - if (err.code == 209) { + if (err?.response?.data?.code === 209 || err.code == 209) { return { error: 'Invalid session token' }; } else { return { error: "You don't have access of this document!" }; diff --git a/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js b/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js index d81f67786..1be43db49 100644 --- a/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js +++ b/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js @@ -1,6 +1,6 @@ import axios from 'axios'; -import { cloudServerUrl } from '../../Utils.js'; - +import { cloudServerUrl, parseJwt } from '../../Utils.js'; +import jwt from 'jsonwebtoken'; const serverUrl = cloudServerUrl; //process.env.SERVER_URL; const appId = process.env.APP_ID; const licenseKey = process.env.LICENSE_KEY; @@ -30,7 +30,8 @@ async function deductcount(docsCount, extUserId, subscription) { console.log('Err in deduct in quick send', err); } } -async function sendMail(document, sessionToken) { +async function sendMail(document) { + //sessionToken const baseUrl = new URL(process.env.PUBLIC_URL); // console.log("pdfDetails", pdfDetails); @@ -57,7 +58,7 @@ async function sendMail(document, sessionToken) { const headers = { 'Content-Type': 'application/json', 'X-Parse-Application-Id': appId, - sessionToken: sessionToken, + // sessionToken: sessionToken, }; const objectId = signerMail[i]?.signerObjId; const hostUrl = baseUrl.origin; @@ -112,13 +113,161 @@ async function sendMail(document, sessionToken) { } } } +async function batchQuery(userId, Documents, Ip, parseConfig) { + const extCls = new Parse.Query('contracts_Users'); + extCls.equalTo('UserId', { + __type: 'Pointer', + className: '_User', + objectId: userId, + }); + const resExt = await extCls.first({ useMasterKey: true }); + if (resExt) { + const _resExt = JSON.parse(JSON.stringify(resExt)); + try { + const requests = Documents.map(x => { + const Signers = x.Signers; + const allSigner = x?.Placeholders?.map( + item => Signers?.find(e => item?.signerPtr?.objectId === e?.objectId) || item?.signerPtr + ).filter(signer => Object.keys(signer).length > 0); + const date = new Date(); + const isoDate = date.toISOString(); + let Acl = { [x.CreatedBy.objectId]: { read: true, write: true } }; + if (allSigner && allSigner.length > 0) { + allSigner.forEach(x => { + const obj = { [x.CreatedBy.objectId]: { read: true, write: true } }; + Acl = { ...Acl, ...obj }; + }); + } + return { + method: 'POST', + path: '/app/classes/contracts_Document', + body: { + Name: x.Name, + URL: x.URL, + Note: x.Note, + Description: x.Description, + CreatedBy: x.CreatedBy, + SendinOrder: x.SendinOrder || true, + ExtUserPtr: { + __type: 'Pointer', + className: x.ExtUserPtr.className, + objectId: x.ExtUserPtr?.objectId, + }, + Placeholders: x.Placeholders.map(y => + y?.signerPtr?.objectId + ? { + ...y, + signerPtr: { + __type: 'Pointer', + className: 'contracts_Contactbook', + objectId: y.signerPtr.objectId, + }, + signerObjId: y.signerObjId, + } + : { ...y, signerPtr: {}, signerObjId: '' } + ), + SignedUrl: x.URL || x.SignedUrl, + SentToOthers: true, + Signers: allSigner?.map(y => ({ + __type: 'Pointer', + className: 'contracts_Contactbook', + objectId: y.objectId, + })), + ACL: Acl, + SentToOthers: true, + RemindOnceInEvery: x.RemindOnceInEvery || 5, + AutomaticReminders: x.AutomaticReminders || false, + TimeToCompleteDays: x.TimeToCompleteDays || 15, + OriginIp: Ip, + DocSentAt: { __type: 'Date', iso: isoDate }, + IsEnableOTP: x?.IsEnableOTP || false, + IsTourEnabled: x?.IsTourEnabled || false, + FileAdapterId: x?.FileAdapterId || '', + }, + }; + }); + // console.log('requests ', requests); + if (licenseKey) { + const subscription = new Parse.Query('contracts_Subscriptions'); + subscription.equalTo('TenantId', { + __type: 'Pointer', + className: 'partners_Tenant', + objectId: _resExt.TenantId.objectId, + }); + subscription.include('ExtUserPtr'); + subscription.greaterThanOrEqualTo('Next_billing_date', new Date()); + const resSub = await subscription.first({ useMasterKey: true }); + if (resSub) { + const _resSub = JSON.parse(JSON.stringify(resSub)); + const allowedCredits = _resSub?.AllowedCredits || 0; + const addonCredits = _resSub?.AddonCredits || 0; + const totalcredits = allowedCredits + addonCredits; + if (requests?.length <= totalcredits) { + const response = await axios.post('batch', { requests: requests }, parseConfig); + // Handle the batch query response + // console.log('Batch query response:', response.data); + if (response.data && response.data.length > 0) { + const updateDocuments = Documents.map((x, i) => ({ + ...x, + objectId: response.data[i]?.success?.objectId, + createdAt: response.data[i]?.success?.createdAt, + })); + deductcount(response.data.length, resExt.id, _resSub); + for (let i = 0; i < updateDocuments.length; i++) { + sendMail(updateDocuments[i], ''); //sessionToken + } + return 'success'; + } + } else { + throw new Parse.Error(429, 'Quota reached, Please buy credits and try again later.'); + } + } else { + throw new Parse.Error( + Parse.Error.INVALID_QUERY, + 'Please purchase or renew your subscription.' + ); + } + } else { + if (requests?.length > 0) { + const newrequests = [requests?.[0]]; + const response = await axios.post('batch', { requests: newrequests }, parseConfig); + // Handle the batch query response + // console.log('Batch query response:', response.data); + if (response.data && response.data.length > 0) { + const document = Documents?.[0]; + const updateDocuments = { + ...document, + objectId: response.data[0]?.success?.objectId, + createdAt: response.data[0]?.success?.createdAt, + }; + deductcount(response.data.length, resExt.id); + sendMail(updateDocuments); //sessionToken + return 'success'; + } + } + } + } catch (error) { + const code = error?.response?.data?.code || error?.response?.status || error?.code || 400; + const msg = + error?.response?.data?.error || + error?.response?.data || + error?.message || + 'Something went wrong.'; + console.log('Error performing batch query:', code, msg); + throw new Parse.Error(code, msg); + } + } else { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.'); + } +} export default async function createBatchDocs(request) { const strDocuments = request.params.Documents; - const sessionToken = request.headers['sessiontoken']; + const sessionToken = request.headers?.sessiontoken; + const jwttoken = request.headers?.jwttoken; const Documents = JSON.parse(strDocuments); const Ip = request?.headers?.['x-real-ip'] || ''; const parseConfig = { - baseURL: serverUrl, //localStorage.getItem('baseUrl'), + baseURL: serverUrl, headers: { 'X-Parse-Application-Id': appId, 'X-Parse-Session-Token': sessionToken, @@ -126,167 +275,38 @@ export default async function createBatchDocs(request) { }, }; try { - const user = await axios.get(serverUrl + '/users/me', { - headers: { - 'X-Parse-Application-Id': appId, - 'X-Parse-Session-Token': request.headers['sessiontoken'], - }, - }); - if (user.data) { - const extCls = new Parse.Query('contracts_Users'); - extCls.equalTo('UserId', { - __type: 'Pointer', - className: '_User', - objectId: user.data.objectId, - }); - const resExt = await extCls.first({ useMasterKey: true }); - if (resExt) { - const _resExt = JSON.parse(JSON.stringify(resExt)); - try { - const requests = Documents.map(x => { - const Signers = x.Signers; - const allSigner = x?.Placeholders?.map( - item => - Signers?.find(e => item?.signerPtr?.objectId === e?.objectId) || item?.signerPtr - ).filter(signer => Object.keys(signer).length > 0); - const date = new Date(); - const isoDate = date.toISOString(); - let Acl = { [x.CreatedBy.objectId]: { read: true, write: true } }; - if (allSigner && allSigner.length > 0) { - allSigner.forEach(x => { - const obj = { [x.CreatedBy.objectId]: { read: true, write: true } }; - Acl = { ...Acl, ...obj }; - }); - } - return { - method: 'POST', - path: '/app/classes/contracts_Document', - body: { - Name: x.Name, - URL: x.URL, - Note: x.Note, - Description: x.Description, - CreatedBy: x.CreatedBy, - SendinOrder: x.SendinOrder || true, - ExtUserPtr: { - __type: 'Pointer', - className: x.ExtUserPtr.className, - objectId: x.ExtUserPtr.objectId, - }, - Placeholders: x.Placeholders.map(y => - y?.signerPtr?.objectId - ? { - ...y, - signerPtr: { - __type: 'Pointer', - className: 'contracts_Contactbook', - objectId: y.signerPtr.objectId, - }, - signerObjId: y.signerObjId, - } - : { ...y, signerPtr: {}, signerObjId: '' } - ), - SignedUrl: x.URL || x.SignedUrl, - SentToOthers: true, - Signers: allSigner?.map(y => ({ - __type: 'Pointer', - className: 'contracts_Contactbook', - objectId: y.objectId, - })), - ACL: Acl, - SentToOthers: true, - RemindOnceInEvery: x.RemindOnceInEvery || 5, - AutomaticReminders: x.AutomaticReminders || false, - TimeToCompleteDays: x.TimeToCompleteDays || 15, - OriginIp: Ip, - DocSentAt: { __type: 'Date', iso: isoDate }, - IsEnableOTP: x?.IsEnableOTP || false, - IsTourEnabled: x?.IsTourEnabled || false, - FileAdapterId: x?.FileAdapterId || '', - }, - }; - }); - // console.log('requests ', requests); - if (licenseKey) { - const subscription = new Parse.Query('contracts_Subscriptions'); - subscription.equalTo('TenantId', { - __type: 'Pointer', - className: 'partners_Tenant', - objectId: _resExt.TenantId.objectId, - }); - subscription.include('ExtUserPtr'); - subscription.greaterThanOrEqualTo('Next_billing_date', new Date()); - const resSub = await subscription.first({ useMasterKey: true }); - if (resSub) { - const _resSub = JSON.parse(JSON.stringify(resSub)); - const allowedCredits = _resSub?.AllowedCredits || 0; - const addonCredits = _resSub?.AddonCredits || 0; - const totalcredits = allowedCredits + addonCredits; - if (requests?.length <= totalcredits) { - const response = await axios.post('batch', { requests: requests }, parseConfig); - // // Handle the batch query response - // console.log('Batch query response:', response.data); - if (response.data && response.data.length > 0) { - const updateDocuments = Documents.map((x, i) => ({ - ...x, - objectId: response.data[i]?.success?.objectId, - createdAt: response.data[i]?.success?.createdAt, - })); - deductcount(response.data.length, resExt.id, _resSub); - for (let i = 0; i < updateDocuments.length; i++) { - sendMail(updateDocuments[i], sessionToken); - } - return 'success'; - } - } else { - throw new Parse.Error( - 429, - 'Quota reached, Please buy credits and try again later.' - ); - } - } else { - throw new Parse.Error( - Parse.Error.INVALID_QUERY, - 'Please purchase or renew your subscription.' - ); - } - } else { - if (requests?.length > 0) { - const newrequests = [requests?.[0]]; - const response = await axios.post('batch', { requests: newrequests }, parseConfig); - // Handle the batch query response - // console.log('Batch query response:', response.data); - if (response.data && response.data.length > 0) { - const document = Documents?.[0]; - const updateDocuments = { - ...document, - objectId: response.data[0]?.success?.objectId, - createdAt: response.data[0]?.success?.createdAt, - }; - deductcount(response.data.length, resExt.id); - sendMail(updateDocuments, sessionToken); - return 'success'; - } - } - } - } catch (error) { - const code = error?.response?.data?.code || error?.response?.status || error?.code || 400; - const msg = - error?.response?.data?.error || - error?.response?.data || - error?.message || - 'Something went wrong.'; - console.log('Error performing batch query:', code, msg); - throw new Parse.Error(code, msg); + if (request?.user) { + return await batchQuery(request.user.id, Documents, Ip, parseConfig); + } else if (jwttoken) { + const jwtDecode = parseJwt(jwttoken); + if (jwtDecode?.user_email) { + const userCls = new Parse.Query(Parse.User); + userCls.equalTo('email', jwtDecode?.user_email); + const userRes = await userCls.first({ useMasterKey: true }); + const userId = userRes?.id; + const tokenQuery = new Parse.Query('appToken'); + tokenQuery.equalTo('userId', { + __type: 'Pointer', + className: '_User', + objectId: userId, + }); + const appRes = await tokenQuery.first({ useMasterKey: true }); + const decoded = jwt.verify(jwttoken, appRes?.get('token')); + if (decoded?.user_email) { + return await batchQuery(userId, Documents, Ip, parseConfig); + } else { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token.'); } + } else { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token.'); } } else { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.'); + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); } } catch (err) { console.log('err in createbatchdoc', err); const code = err?.code || 400; const msg = err?.message || 'Something went wrong.'; - new Parse.Error(code, msg); + throw new Parse.Error(code, msg); } } diff --git a/apps/OpenSignServer/cloud/parsefunction/getDocument.js b/apps/OpenSignServer/cloud/parsefunction/getDocument.js index 8096ee915..112374e46 100644 --- a/apps/OpenSignServer/cloud/parsefunction/getDocument.js +++ b/apps/OpenSignServer/cloud/parsefunction/getDocument.js @@ -1,10 +1,12 @@ import axios from 'axios'; -import { cloudServerUrl } from '../../Utils.js'; +import { cloudServerUrl, parseJwt } from '../../Utils.js'; +import jwt from 'jsonwebtoken'; export default async function getDocument(request) { const serverUrl = cloudServerUrl; //process.env.SERVER_URL; const docId = request.params.docId; - + const jwttoken = request?.headers?.jwttoken || ''; + const sessiontoken = request?.headers?.sessiontoken || ''; try { if (docId) { try { @@ -27,12 +29,12 @@ export default async function getDocument(request) { if (!IsEnableOTP) { return document; } else { - if (request?.headers?.['sessiontoken']) { + if (sessiontoken) { try { const userRes = await axios.get(serverUrl + '/users/me', { headers: { 'X-Parse-Application-Id': process.env.APP_ID, - 'X-Parse-Session-Token': request.headers['sessiontoken'], + 'X-Parse-Session-Token': sessiontoken, }, }); const userId = userRes.data && userRes.data?.objectId; @@ -46,6 +48,37 @@ export default async function getDocument(request) { console.log('err user in not authenticated', err); return { error: "You don't have access of this document!" }; } + } else if (jwttoken) { + try { + const jwtDecode = parseJwt(jwttoken); + if (jwtDecode?.user_email) { + const userCls = new Parse.Query(Parse.User); + userCls.equalTo('email', jwtDecode?.user_email); + const userRes = await userCls.first({ useMasterKey: true }); + const userId = userRes?.id; + const tokenQuery = new Parse.Query('appToken'); + tokenQuery.equalTo('userId', { + __type: 'Pointer', + className: '_User', + objectId: userId, + }); + const appRes = await tokenQuery.first({ useMasterKey: true }); + const decoded = jwt.verify(jwttoken, appRes?.get('token')); + if (decoded?.user_email) { + const acl = res.getACL(); + if (userId && acl && acl.getReadAccess(userId)) { + return document; + } else { + return { error: "You don't have access of this document!" }; + } + } else { + return { status: 'error', result: 'Invalid token!' }; + } + } + } catch (err) { + console.log('err in jwt', err); + return { error: "You don't have access of this document!" }; + } } else { return { error: "You don't have access of this document!" }; } diff --git a/apps/OpenSignServer/cloud/parsefunction/getSigners.js b/apps/OpenSignServer/cloud/parsefunction/getSigners.js new file mode 100644 index 000000000..a8b1f9bb4 --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/getSigners.js @@ -0,0 +1,66 @@ +import { parseJwt } from '../../Utils.js'; +import jwt from 'jsonwebtoken'; + +export default async function getSigners(request) { + const jwttoken = request.headers.jwttoken || ''; + const search = request.params.search || ''; + const searchEmail = request.params.searchEmail || ''; + try { + if (request.user) { + const contactbook = new Parse.Query('contracts_Contactbook'); + contactbook.equalTo('CreatedBy', { + __type: 'Pointer', + className: '_User', + objectId: request?.user?.id, + }); + + if (search) { + contactbook.matches('Name', new RegExp(search, 'i')); + } else if (searchEmail) { + contactbook.matches('Email', new RegExp(searchEmail, 'i')); + } + contactbook.notEqualTo('IsDeleted', true); + const contactRes = await contactbook.find(); + const _contactRes = JSON.parse(JSON.stringify(contactRes)); + return _contactRes; + } else if (jwttoken) { + const jwtDecode = parseJwt(jwttoken); + const userCls = new Parse.Query(Parse.User); + userCls.equalTo('email', jwtDecode?.user_email); + const userRes = await userCls.first({ useMasterKey: true }); + const userId = userRes?.id; + const tokenQuery = new Parse.Query('appToken'); + tokenQuery.equalTo('userId', { + __type: 'Pointer', + className: '_User', + objectId: userId, + }); + const appRes = await tokenQuery.first({ useMasterKey: true }); + const decoded = jwt.verify(jwttoken, appRes?.get('token')); + if (decoded?.user_email) { + const contactbook = new Parse.Query('contracts_Contactbook'); + contactbook.equalTo('CreatedBy', { + __type: 'Pointer', + className: '_User', + objectId: userId, + }); + if (search) { + contactbook.matches('Name', new RegExp(search, 'i')); + } else if (searchEmail) { + contactbook.matches('Email', new RegExp(searchEmail, 'i')); + } + contactbook.notEqualTo('IsDeleted', true); + const contactRes = await contactbook.find({ useMasterKey: true }); + const _contactRes = JSON.parse(JSON.stringify(contactRes)); + return _contactRes; + } else { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token'); + } + } else { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token'); + } + } catch (err) { + console.log('err in get signers', err); + throw err; + } +} diff --git a/apps/OpenSignServer/cloud/parsefunction/getSubscriptions.js b/apps/OpenSignServer/cloud/parsefunction/getSubscriptions.js index 201acdc46..0538ae757 100644 --- a/apps/OpenSignServer/cloud/parsefunction/getSubscriptions.js +++ b/apps/OpenSignServer/cloud/parsefunction/getSubscriptions.js @@ -1,13 +1,57 @@ import axios from 'axios'; -import { cloudServerUrl } from '../../Utils.js'; +import { cloudServerUrl, parseJwt } from '../../Utils.js'; +import jwt from 'jsonwebtoken'; const serverUrl = cloudServerUrl; //process.env.SERVER_URL; 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; + const jwttoken = request.headers?.jwttoken || ''; - if (extUserId) { + if (jwttoken) { + const jwtDecode = parseJwt(jwttoken); + if (jwtDecode?.user_email) { + const verifyToken = jwttoken; + const userCls = new Parse.Query(Parse.User); + userCls.equalTo('email', jwtDecode?.user_email); + const userRes = await userCls.first({ useMasterKey: true }); + const userId = userRes?.id; + const tokenQuery = new Parse.Query('appToken'); + tokenQuery.equalTo('userId', { + __type: 'Pointer', + className: '_User', + objectId: userId, + }); + const appRes = await tokenQuery.first({ useMasterKey: true }); + const decoded = jwt.verify(verifyToken, appRes?.get('token')); + if (decoded?.user_email) { + const extCls = new Parse.Query('contracts_Users'); + extCls.equalTo('Email', decoded?.user_email); + const exUser = await extCls.first({ useMasterKey: true }); + if (exUser) { + const subscriptionCls = new Parse.Query('contracts_Subscriptions'); + subscriptionCls.equalTo('TenantId', { + __type: 'Pointer', + className: 'partners_Tenant', + objectId: exUser.get('TenantId').id, + }); + subscriptionCls.descending('createdAt'); + const subcripitions = await subscriptionCls.first({ useMasterKey: true }); + if (subcripitions) { + const _subcripitions = JSON.parse(JSON.stringify(subcripitions)); + return { status: 'success', result: _subcripitions }; + } else { + return { status: 'success', result: {} }; + } + } else { + return { status: 'error', result: 'User not found!' }; + } + } else { + return { status: 'error', result: 'Invalid token!' }; + } + } + } else if (extUserId) { try { let userId; //`ispublic` is used in public profile to get subscription details diff --git a/apps/OpenSignServer/cloud/parsefunction/getTenant.js b/apps/OpenSignServer/cloud/parsefunction/getTenant.js new file mode 100644 index 000000000..2b5957a0a --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/getTenant.js @@ -0,0 +1,59 @@ +import { parseJwt } from '../../Utils.js'; +import jwt from 'jsonwebtoken'; +export default async function getTenant(request) { + const jwttoken = request.headers.jwttoken || ''; + const userId = request.params.userId || ''; + if (jwttoken) { + const jwtDecode = parseJwt(jwttoken); + if (jwtDecode?.user_email) { + const verifyToken = jwttoken; + const userCls = new Parse.Query(Parse.User); + userCls.equalTo('email', jwtDecode?.user_email); + const userRes = await userCls.first({ useMasterKey: true }); + const userId = userRes?.id; + const tokenQuery = new Parse.Query('appToken'); + tokenQuery.equalTo('userId', { + __type: 'Pointer', + className: '_User', + objectId: userId, + }); + const appRes = await tokenQuery.first({ useMasterKey: true }); + const decoded = jwt.verify(verifyToken, appRes?.get('token')); + if (decoded?.user_email) { + try { + const tenantCreditsQuery = new Parse.Query('partners_Tenant'); + tenantCreditsQuery.equalTo('UserId', { + __type: 'Pointer', + className: '_User', + objectId: userId, + }); + tenantCreditsQuery.exclude('FileAdapters'); + tenantCreditsQuery.exclude('PfxFile'); + const res = await tenantCreditsQuery.first({ useMasterKey: true }); + if (res) { + return res; + } + } catch (e) { + return 'user does not exist!'; + } + } else { + return { status: 'error', result: 'Invalid token!' }; + } + } + } else if (userId) { + try { + const tenantCreditsQuery = new Parse.Query('partners_Tenant'); + tenantCreditsQuery.equalTo('UserId', { + __type: 'Pointer', + className: '_User', + objectId: userId, + }); + const res = await tenantCreditsQuery.first(); + if (res) { + return res; + } + } catch (e) { + return 'user does not exist!'; + } + } +} diff --git a/apps/OpenSignServer/cloud/parsefunction/getUserDetails.js b/apps/OpenSignServer/cloud/parsefunction/getUserDetails.js index 5feb7e16c..fa8228ed0 100644 --- a/apps/OpenSignServer/cloud/parsefunction/getUserDetails.js +++ b/apps/OpenSignServer/cloud/parsefunction/getUserDetails.js @@ -1,5 +1,9 @@ +import { parseJwt } from '../../Utils.js'; +import jwt from 'jsonwebtoken'; + async function getUserDetails(request) { const reqEmail = request.params.email; + const jwttoken = request?.headers?.jwttoken || ''; if (reqEmail || request.user) { try { const userId = request.params.userId; @@ -36,6 +40,48 @@ async function getUserDetails(request) { const msg = err?.message || 'Something went wrong.'; throw new Parse.Error(code, msg); } + } else if (jwttoken) { + const jwtDecode = parseJwt(jwttoken); + if (jwtDecode?.user_email) { + const userCls = new Parse.Query(Parse.User); + userCls.equalTo('email', jwtDecode?.user_email); + const userRes = await userCls.first({ useMasterKey: true }); + const userId = userRes?.id; + const tokenQuery = new Parse.Query('appToken'); + tokenQuery.equalTo('userId', { + __type: 'Pointer', + className: '_User', + objectId: userId, + }); + const appRes = await tokenQuery.first({ useMasterKey: true }); + const decoded = jwt.verify(jwttoken, appRes?.get('token')); + if (decoded?.user_email) { + try { + const userQuery = new Parse.Query('contracts_Users'); + userQuery.equalTo('Email', decoded?.user_email); + userQuery.include('TenantId'); + userQuery.include('UserId'); + userQuery.include('CreatedBy'); + userQuery.exclude('CreatedBy.authData'); + userQuery.exclude('TenantId.FileAdapters'); + userQuery.exclude('google_refresh_token'); + userQuery.exclude('TenantId.PfxFile'); + const res = await userQuery.first({ useMasterKey: true }); + if (res) { + return res; + } else { + return ''; + } + } catch (err) { + console.log('Err ', err); + const code = err?.code || 400; + const msg = err?.message || 'Something went wrong.'; + throw new Parse.Error(code, msg); + } + } else { + return { status: 'error', result: 'Invalid token!' }; + } + } } else { throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); } diff --git a/apps/OpenSignServer/cloud/parsefunction/isUserInContactBook.js b/apps/OpenSignServer/cloud/parsefunction/isUserInContactBook.js new file mode 100644 index 000000000..f9bcfe849 --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/isUserInContactBook.js @@ -0,0 +1,47 @@ +import { parseJwt } from '../../Utils.js'; +import jwt from 'jsonwebtoken'; + +export default async function isUserInContactBook(request) { + try { + const jwttoken = request.headers.jwttoken; + if (jwttoken) { + const jwtDecode = parseJwt(jwttoken); + const userCls = new Parse.Query(Parse.User); + userCls.equalTo('email', jwtDecode?.user_email); + const userRes = await userCls.first({ useMasterKey: true }); + const userId = userRes?.id; + const tokenQuery = new Parse.Query('appToken'); + tokenQuery.equalTo('userId', { + __type: 'Pointer', + className: '_User', + objectId: userId, + }); + const appRes = await tokenQuery.first({ useMasterKey: true }); + const decoded = jwt.verify(jwttoken, appRes?.get('token')); + if (decoded?.user_email) { + const email = userRes?.get('email'); + const userPtr = { __type: 'Pointer', className: '_User', objectId: userId }; + const query = new Parse.Query('contracts_Contactbook'); + query.equalTo('CreatedBy', userPtr); + query.notEqualTo('IsDeleted', true); + query.equalTo('Email', email); + const res = await query.first({ useMasterKey: true }); + return res; + } else { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token'); + } + } else if (request.user) { + const email = request.user.get('email'); + const userPtr = { __type: 'Pointer', className: '_User', objectId: request.user?.id }; + const query = new Parse.Query('contracts_Contactbook'); + query.equalTo('CreatedBy', userPtr); + query.notEqualTo('IsDeleted', true); + query.equalTo('Email', email); + const res = await query.first(); + return res; + } + } catch (err) { + console.log('err', err); + throw err; + } +} diff --git a/apps/OpenSignServer/cloud/parsefunction/saveFile.js b/apps/OpenSignServer/cloud/parsefunction/saveFile.js new file mode 100644 index 000000000..6538f70ab --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/saveFile.js @@ -0,0 +1,131 @@ +import uploadFileToS3 from './uploadFiletoS3.js'; +import { parseJwt } from '../../Utils.js'; +import jwt from 'jsonwebtoken'; +export default async function saveFile(request) { + const jwttoken = request.headers.jwttoken || ''; + + if (!request.params.fileBase64) { + throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide file.'); + } + const fileBase64 = request.params.fileBase64; + const id = request.params.id; + try { + if (request.user) { + const extCls = new Parse.Query('contracts_Users'); + extCls.equalTo('UserId', request.user); + extCls.include('TenantId'); + const resExt = await extCls.first({ useMasterKey: true }); + if (resExt) { + const _resExt = JSON.parse(JSON.stringify(resExt)); + const fileAdapters = _resExt?.TenantId.FileAdapters || []; + const fileAdapter = fileAdapters?.find(x => x.id === id) || {}; + if (fileAdapter?.accessKeyId) { + const adapterConfig = { + id: id, + fileAdapter: fileAdapter?.fileAdapter, + bucketName: fileAdapter?.bucketName, + region: fileAdapter?.region, + endpoint: fileAdapter?.endpoint, + accessKeyId: fileAdapter?.accessKeyId, + secretAccessKey: fileAdapter?.secretAccessKey, + baseUrl: fileAdapter?.baseUrl, + }; + const buffer = Buffer.from(fileBase64, 'base64'); + const fileName = request.params.fileName; + const ext = request.params.fileName?.split('.')?.pop(); + let mimeType; + if (ext === 'pdf') { + mimeType = 'application/pdf'; + } else if (ext === 'png' || ext === 'jpeg' || ext === 'jpg') { + mimeType = `image/${ext}`; + } + try { + const presignedUrl = await uploadFileToS3(buffer, fileName, mimeType, adapterConfig); + return { url: presignedUrl }; + } catch (err) { + console.error('Error generate presigned url:', err); + const msg = 'Fileadapter credentials are invalid.'; + throw new Parse.Error(400, msg); + } + } else { + const fileName = request.params.fileName; + const pdfFile = new Parse.File(fileName, { base64: fileBase64 }); + // Save the Parse File if needed + const pdfData = await pdfFile.save({ useMasterKey: true }); + const presignedUrl = pdfData.url(); + return { url: presignedUrl }; + } + } else { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.'); + } + } else if (jwttoken) { + const jwtDecode = parseJwt(jwttoken); + const userCls = new Parse.Query(Parse.User); + userCls.equalTo('email', jwtDecode?.user_email); + const userRes = await userCls.first({ useMasterKey: true }); + const userId = userRes?.id; + const tokenQuery = new Parse.Query('appToken'); + tokenQuery.equalTo('userId', { + __type: 'Pointer', + className: '_User', + objectId: userId, + }); + const appRes = await tokenQuery.first({ useMasterKey: true }); + const decoded = jwt.verify(jwttoken, appRes?.get('token')); + if (decoded?.user_email) { + const extCls = new Parse.Query('contracts_Users'); + extCls.equalTo('Email', decoded?.user_email); + extCls.include('TenantId'); + const resExt = await extCls.first({ useMasterKey: true }); + if (resExt) { + const _resExt = JSON.parse(JSON.stringify(resExt)); + const fileAdapters = _resExt?.TenantId.FileAdapters || []; + const fileAdapter = fileAdapters?.find(x => x.id === id) || {}; + if (fileAdapter?.accessKeyId) { + const adapterConfig = { + id: id, + fileAdapter: fileAdapter?.fileAdapter, + bucketName: fileAdapter?.bucketName, + region: fileAdapter?.region, + endpoint: fileAdapter?.endpoint, + accessKeyId: fileAdapter?.accessKeyId, + secretAccessKey: fileAdapter?.secretAccessKey, + baseUrl: fileAdapter?.baseUrl, + }; + const buffer = Buffer.from(fileBase64, 'base64'); + const fileName = request.params.fileName; + const ext = request.params.fileName?.split('.')?.pop(); + let mimeType; + if (ext === 'pdf') { + mimeType = 'application/pdf'; + } else if (ext === 'png' || ext === 'jpeg' || ext === 'jpg') { + mimeType = `image/${ext}`; + } + try { + const presignedUrl = await uploadFileToS3(buffer, fileName, mimeType, adapterConfig); + return { url: presignedUrl }; + } catch (err) { + console.error('Error generate presigned url:', err); + const msg = 'Fileadapter credentials are invalid.'; + throw new Parse.Error(400, msg); + } + } else { + const fileName = request.params.fileName; + const pdfFile = new Parse.File(fileName, { base64: fileBase64 }); + // Save the Parse File if needed + const pdfData = await pdfFile.save({ useMasterKey: true }); + const presignedUrl = pdfData.url(); + return { url: presignedUrl }; + } + } else { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.'); + } + } + } else { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); + } + } catch (err) { + console.log('err in savetoS3', err); + throw err; + } +} diff --git a/apps/OpenSignServer/cloud/parsefunction/saveTemplate.js b/apps/OpenSignServer/cloud/parsefunction/saveTemplate.js new file mode 100644 index 000000000..687a3e334 --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/saveTemplate.js @@ -0,0 +1,110 @@ +import { parseJwt } from '../../Utils.js'; +import jwt from 'jsonwebtoken'; + +async function updateTemplate(template, isJwt = false) { + try { + if (template?.Id) { + const updateTemplate = new Parse.Object('contracts_Template'); + updateTemplate.id = template.Id; + if (template?.URL) { + updateTemplate.set('URL', template.URL); + } + if (template?.Name) { + updateTemplate.set('Name', template.Name); + } + if (template?.Note) { + updateTemplate.set('Note', template.Note); + } + if (template?.Description) { + updateTemplate.set('Description', template.Description); + } + if (template?.SendinOrder) { + updateTemplate.set('SendinOrder', template.SendinOrder); + } + if (template?.AutomaticReminders) { + updateTemplate.set('AutomaticReminders', template.AutomaticReminders); + } + if (template?.RemindOnceInEvery) { + updateTemplate.set('RemindOnceInEvery', template.RemindOnceInEvery); + } + if (template?.NextReminderDate) { + updateTemplate.set('NextReminderDate', new Date(template.NextReminderDate)); + } + if (template?.IsEnableOTP) { + updateTemplate.set('IsEnableOTP', template.IsEnableOTP); + } + if (template?.IsTourEnabled) { + updateTemplate.set('IsTourEnabled', template.IsTourEnabled); + } + const isPublic = template?.IsPublic !== undefined ? template?.IsPublic : false; + if (template?.IsPublic !== undefined) { + updateTemplate.set('IsPublic', isPublic); + } + updateTemplate.set('Placeholders', template.Placeholders); + updateTemplate.set('Signers', template.Signers); + + let updateTemplateRes; + if (isJwt) { + updateTemplateRes = await updateTemplate.save(null, { useMasterKey: true }); + } else { + updateTemplateRes = await updateTemplate.save(); + } + return updateTemplateRes; + } else { + throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide Id.'); + } + } catch (err) { + console.log('err in update template', err); + throw err; + } +} +export default async function saveTemplate(request) { + const jwttoken = request.headers.jwttoken || ''; + const template = { + Id: request.params?.templateId, + URL: request.params?.URL || '', + Name: request.params?.Name, + Note: request.params?.Note, + Description: request.params?.Description, + Placeholders: request.params?.Placeholders, + Signers: request.params?.Signers, + SendMail: request.params?.SendMail || false, + SendinOrder: request.params?.SendinOrder || true, + AutomaticReminders: request.params?.AutomaticReminders, + RemindOnceInEvery: parseInt(request.params.RemindOnceInEvery) || 15, + NextReminderDate: request.params?.NextReminderDate, + IsEnableOTP: request.params?.IsEnableOTP === true ? true : false, + IsTourEnabled: request.params?.IsTourEnabled === true ? true : false, + IsPublic: request.params?.IsPublic, + }; + + try { + if (request.user) { + return await updateTemplate(template); + } else if (jwttoken) { + const jwtDecode = parseJwt(jwttoken); + const userCls = new Parse.Query(Parse.User); + userCls.equalTo('email', jwtDecode?.user_email); + const userRes = await userCls.first({ useMasterKey: true }); + const userId = userRes?.id; + const tokenQuery = new Parse.Query('appToken'); + tokenQuery.equalTo('userId', { + __type: 'Pointer', + className: '_User', + objectId: userId, + }); + const appRes = await tokenQuery.first({ useMasterKey: true }); + const decoded = jwt.verify(jwttoken, appRes?.get('token')); + if (decoded?.user_email) { + return await updateTemplate(template, true); + } else { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token'); + } + } else { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token'); + } + } catch (err) { + console.log('err in get signers', err); + throw err; + } +} diff --git a/apps/OpenSignServer/cloud/parsefunction/savecontact.js b/apps/OpenSignServer/cloud/parsefunction/savecontact.js new file mode 100644 index 000000000..6cf16b5b2 --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/savecontact.js @@ -0,0 +1,180 @@ +import { parseJwt } from '../../Utils.js'; +import jwt from 'jsonwebtoken'; + +export default async function savecontact(request) { + const name = request.params.name; + const phone = request.params.phone; + const email = request.params.email; + const tenantId = request.params.tenantId; + const jwttoken = request.headers.jwttoken; + + if (request.user) { + const currentUser = request?.user; + const currentUserPtr = { __type: 'Pointer', className: '_User', objectId: currentUser?.id }; + const query = new Parse.Query('contracts_Contactbook'); + query.equalTo('CreatedBy', currentUserPtr); + query.notEqualTo('IsDeleted', true); + query.equalTo('Email', email); + const res = await query.first(); + if (!res) { + const contactQuery = new Parse.Object('contracts_Contactbook'); + contactQuery.set('Name', name); + if (phone) { + contactQuery.set('Phone', phone); + } + contactQuery.set('Email', email); + contactQuery.set('UserRole', 'contracts_Guest'); + + if (tenantId) { + contactQuery.set('TenantId', { + __type: 'Pointer', + className: 'partners_Tenant', + objectId: tenantId, + }); + } + + try { + const _users = Parse.Object.extend('User'); + const _user = new _users(); + _user.set('name', name); + _user.set('username', email); + _user.set('email', email); + _user.set('password', email); + if (phone) { + _user.set('phone', phone); + } + + const user = await _user.save(); + if (user) { + contactQuery.set('CreatedBy', currentUserPtr); + contactQuery.set('UserId', user); + const acl = new Parse.ACL(); + acl.setPublicReadAccess(true); + acl.setPublicWriteAccess(true); + acl.setReadAccess(currentUser.id, true); + acl.setWriteAccess(currentUser.id, true); + contactQuery.setACL(acl); + + const res = await contactQuery.save(); + const parseData = JSON.parse(JSON.stringify(res)); + return parseData; + } + } catch (err) { + console.log('err ', err); + if (err.code === 202) { + const params = { email: email }; + const userRes = await Parse.Cloud.run('getUserId', params); + contactQuery.set('CreatedBy', currentUserPtr); + contactQuery.set('UserId', { + __type: 'Pointer', + className: '_User', + objectId: userRes.id, + }); + const acl = new Parse.ACL(); + acl.setPublicReadAccess(true); + acl.setPublicWriteAccess(true); + acl.setReadAccess(currentUser.id, true); + acl.setWriteAccess(currentUser.id, true); + contactQuery.setACL(acl); + const res = await contactQuery.save(); + const parseData = JSON.parse(JSON.stringify(res)); + return parseData; + } + } + } else { + throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Contact already exists.'); + } + } else if (jwttoken) { + const jwtDecode = parseJwt(jwttoken); + const userCls = new Parse.Query(Parse.User); + userCls.equalTo('email', jwtDecode?.user_email); + const userRes = await userCls.first({ useMasterKey: true }); + const userId = userRes?.id; + const tokenQuery = new Parse.Query('appToken'); + tokenQuery.equalTo('userId', { + __type: 'Pointer', + className: '_User', + objectId: userId, + }); + const appRes = await tokenQuery.first({ useMasterKey: true }); + const decoded = jwt.verify(jwttoken, appRes?.get('token')); + const currentUser = userRes; + const currentUserPtr = { __type: 'Pointer', className: '_User', objectId: userId }; + if (decoded?.user_email) { + const query = new Parse.Query('contracts_Contactbook'); + query.equalTo('CreatedBy', currentUserPtr); + query.notEqualTo('IsDeleted', true); + query.equalTo('Email', email); + const res = await query.first(); + if (!res) { + const contactQuery = new Parse.Object('contracts_Contactbook'); + contactQuery.set('Name', name); + if (phone) { + contactQuery.set('Phone', phone); + } + contactQuery.set('Email', email); + contactQuery.set('UserRole', 'contracts_Guest'); + + if (tenantId) { + contactQuery.set('TenantId', { + __type: 'Pointer', + className: 'partners_Tenant', + objectId: tenantId, + }); + } + try { + const _users = Parse.Object.extend('User'); + const _user = new _users(); + _user.set('name', name); + _user.set('username', email); + _user.set('email', email); + _user.set('password', email); + if (phone) { + _user.set('phone', phone); + } + + const user = await _user.save(); + if (user) { + contactQuery.set('CreatedBy', currentUserPtr); + contactQuery.set('UserId', user); + const acl = new Parse.ACL(); + acl.setPublicReadAccess(true); + acl.setPublicWriteAccess(true); + acl.setReadAccess(currentUser.id, true); + acl.setWriteAccess(currentUser.id, true); + contactQuery.setACL(acl); + + const res = await contactQuery.save(); + const parseData = JSON.parse(JSON.stringify(res)); + return parseData; + } + } catch (err) { + console.log('err ', err); + if (err.code === 202) { + const params = { email: email }; + const userRes = await Parse.Cloud.run('getUserId', params); + contactQuery.set('CreatedBy', currentUserPtr); + contactQuery.set('UserId', { + __type: 'Pointer', + className: '_User', + objectId: userRes.id, + }); + const acl = new Parse.ACL(); + acl.setPublicReadAccess(true); + acl.setPublicWriteAccess(true); + acl.setReadAccess(currentUser.id, true); + acl.setWriteAccess(currentUser.id, true); + contactQuery.setACL(acl); + const res = await contactQuery.save(); + const parseData = JSON.parse(JSON.stringify(res)); + return parseData; + } + } + } else { + throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Contact already exists.'); + } + } else { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token.'); + } + } +} diff --git a/apps/OpenSignServer/cloud/parsefunction/sendMailv3.js b/apps/OpenSignServer/cloud/parsefunction/sendMailv3.js index fc3aab26b..5c44e5b88 100644 --- a/apps/OpenSignServer/cloud/parsefunction/sendMailv3.js +++ b/apps/OpenSignServer/cloud/parsefunction/sendMailv3.js @@ -65,7 +65,7 @@ async function sendMailProvider(req, plan, monthchange) { const pdfName = req.params.pdfName && `${req.params.pdfName}.pdf`; const file = { filename: pdfName || 'exported.pdf', - content: smtpenable ? PdfBuffer : undefined, //fs.readFileSync('./exports/exported_file_1223.pdf'), + content: smtpenable ? PdfBuffer : undefined, data: smtpenable ? undefined : PdfBuffer, }; diff --git a/apps/OpenSignServer/cloud/parsefunction/updateToPublicTemplate.js b/apps/OpenSignServer/cloud/parsefunction/updateToPublicTemplate.js new file mode 100644 index 000000000..40af4f876 --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/updateToPublicTemplate.js @@ -0,0 +1,63 @@ +import { parseJwt } from '../../Utils.js'; +import jwt from 'jsonwebtoken'; + +async function updateTemplate(template, isJwt = false) { + try { + if (template?.Id) { + const updateTemplate = new Parse.Object('contracts_Template'); + updateTemplate.id = template.Id; + + const isPublic = template?.IsPublic !== undefined ? template?.IsPublic : false; + if (template?.IsPublic !== undefined) { + updateTemplate.set('IsPublic', isPublic); + } + + let updateTemplateRes; + if (isJwt) { + updateTemplateRes = await updateTemplate.save(null, { useMasterKey: true }); + } else { + updateTemplateRes = await updateTemplate.save(); + } + return updateTemplateRes; + } else { + throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide Id.'); + } + } catch (err) { + console.log('err in update template', err); + throw err; + } +} +export default async function updateToPublicTemplate(request) { + const jwttoken = request.headers.jwttoken || ''; + const template = { Id: request.params.templateId, IsPublic: request.params?.IsPublic }; + + try { + if (request.user) { + return await updateTemplate(template); + } else if (jwttoken) { + const jwtDecode = parseJwt(jwttoken); + const userCls = new Parse.Query(Parse.User); + userCls.equalTo('email', jwtDecode?.user_email); + const userRes = await userCls.first({ useMasterKey: true }); + const userId = userRes?.id; + const tokenQuery = new Parse.Query('appToken'); + tokenQuery.equalTo('userId', { + __type: 'Pointer', + className: '_User', + objectId: userId, + }); + const appRes = await tokenQuery.first({ useMasterKey: true }); + const decoded = jwt.verify(jwttoken, appRes?.get('token')); + if (decoded?.user_email) { + return await updateTemplate(template, true); + } else { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token'); + } + } else { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token'); + } + } catch (err) { + console.log('err in get signers', err); + throw err; + } +} diff --git a/apps/OpenSignServer/cloud/parsefunction/updateTourStatus.js b/apps/OpenSignServer/cloud/parsefunction/updateTourStatus.js new file mode 100644 index 000000000..1636ac278 --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/updateTourStatus.js @@ -0,0 +1,58 @@ +import { parseJwt } from '../../Utils.js'; +import jwt from 'jsonwebtoken'; + +export default async function updateTourStatus(request) { + const tourstatus = request.params.TourStatus; + const extUserId = request.params.ExtUserId; + const jwttoken = request?.headers?.jwttoken || ''; + if (request.user) { + try { + const updateUser = new Parse.Object('contracts_Users'); + updateUser.id = extUserId; + updateUser.set('TourStatus', tourstatus); + const res = await updateUser.save(); + return res; + } catch (err) { + console.log('Err ', err); + const code = err?.code || 400; + const msg = err?.message || 'Something went wrong.'; + throw new Parse.Error(code, msg); + } + } else if (jwttoken) { + const jwtDecode = parseJwt(jwttoken); + if (jwtDecode?.user_email) { + const userCls = new Parse.Query(Parse.User); + userCls.equalTo('email', jwtDecode?.user_email); + const userRes = await userCls.first({ useMasterKey: true }); + const userId = userRes?.id; + const tokenQuery = new Parse.Query('appToken'); + tokenQuery.equalTo('userId', { + __type: 'Pointer', + className: '_User', + objectId: userId, + }); + const appRes = await tokenQuery.first({ useMasterKey: true }); + const decoded = jwt.verify(jwttoken, appRes?.get('token')); + if (decoded?.user_email) { + try { + const updateUser = new Parse.Object('contracts_Users'); + updateUser.id = extUserId; + updateUser.set('TourStatus', tourstatus); + const res = await updateUser.save(null, { useMasterKey: true }); + return res; + } catch (err) { + console.log('Err ', err); + const code = err?.code || 400; + const msg = err?.message || 'Something went wrong.'; + throw new Parse.Error(code, msg); + } + } else { + return { status: 'error', result: 'Invalid token!' }; + } + } else { + return { status: 'error', result: 'Invalid token!' }; + } + } else { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); + } +}