diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index 5db2c30ae..c1b53f17e 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -1578,7 +1578,10 @@ function PdfRequestFiles() { handleClose={() => { setIsCompleted((prev) => ({ ...prev, isModal: false })); }} - reduceWidth={!isCompleted?.message} + reduceWidth={ + !isCompleted?.message && + "md:min-w-[440px] md:max-w-[400px]" + } >
{isCompleted?.message ? ( diff --git a/apps/OpenSign/src/pages/UserProfile.js b/apps/OpenSign/src/pages/UserProfile.js index 6b7cd6172..fe27836eb 100644 --- a/apps/OpenSign/src/pages/UserProfile.js +++ b/apps/OpenSign/src/pages/UserProfile.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useRef } from "react"; import { Navigate, useNavigate } from "react-router-dom"; import Parse from "parse"; import { SaveFileSize } from "../constant/saveFileSize"; @@ -27,6 +27,10 @@ function UserProfile() { const [percentage, setpercentage] = useState(0); const [isDisableDocId, setIsDisableDocId] = useState(false); const [isSubscribe, setIsSubscribe] = useState(false); + const [publicUserName, setPublicUserName] = useState( + extendUser && extendUser?.[0]?.UserName + ); + const previousPublicUserName = useRef(publicUserName); const [company, setCompany] = useState( extendUser && extendUser?.[0]?.Company ); @@ -37,6 +41,7 @@ function UserProfile() { const [otp, setOtp] = useState(""); const [otpLoader, setOtpLoader] = useState(false); const [isEmailVerified, setIsEmailVerified] = useState(false); + const [userNameError, setUserNameError] = useState(""); useEffect(() => { getUserDetail(); }, []); @@ -74,46 +79,72 @@ function UserProfile() { } } }; + //function to check public username already exist + const handleCheckPublicUserName = async () => { + try { + const res = await Parse.Cloud.run("getpublicusername", { + username: publicUserName + }); + if (res) { + setIsLoader(false); + setUserNameError("user name already exist"); + setTimeout(() => { + setUserNameError(""); + }, 3000); + return res; + } + } catch (e) { + console.log("error in getpublicusername cloud function"); + } + }; const handleSubmit = async (e) => { e.preventDefault(); - setIsLoader(true); - let phn = Phone; - - try { - const tour = Parse.Object.extend("_User"); - const query = new Parse.Query(tour); - - await query.get(UserProfile.objectId).then((object) => { - object.set("name", name); - object.set("ProfilePic", Image); - object.set("phone", phn || ""); - - object.save().then( - async (response) => { - if (response) { - let res = response.toJSON(); - let rr = JSON.stringify(res); - localStorage.setItem("UserInformation", rr); - SetName(res.name); - SetPhone(res?.phone || ""); - setImage(res.ProfilePic); - localStorage.setItem("username", res.name); - localStorage.setItem("profileImg", res.ProfilePic); - await updateExtUser({ Name: res.name, Phone: res?.phone || "" }); - alert("Profile updated successfully."); - setEditMode(false); - navigate("/dashboard/35KBoSgoAK"); + let phn = Phone, + res; + //condition to call cloud function when user change publicUserName + if (previousPublicUserName.current !== publicUserName) { + res = await handleCheckPublicUserName(); + } + if (!res) { + setIsLoader(true); + try { + const userQuery = Parse.Object.extend("_User"); + const query = new Parse.Query(userQuery); + await query.get(UserProfile.objectId).then((object) => { + object.set("name", name); + object.set("ProfilePic", Image); + object.set("phone", phn || ""); + object.save().then( + async (response) => { + if (response) { + let res = response.toJSON(); + let rr = JSON.stringify(res); + localStorage.setItem("UserInformation", rr); + SetName(res.name); + SetPhone(res?.phone || ""); + setImage(res.ProfilePic); + localStorage.setItem("username", res.name); + localStorage.setItem("profileImg", res.ProfilePic); + await updateExtUser({ + Name: res.name, + Phone: res?.phone || "" + }); + alert("Profile updated successfully."); + setEditMode(false); + setIsLoader(false); + //navigate("/dashboard/35KBoSgoAK"); + } + }, + (error) => { + alert("Something went wrong."); + console.error("Error while updating tour", error); + setIsLoader(false); } - }, - (error) => { - alert("Something went wrong."); - console.error("Error while updating tour", error); - setIsLoader(false); - } - ); - }); - } catch (error) { - console.log("err", error); + ); + }); + } catch (error) { + console.log("err", error); + } } }; @@ -122,14 +153,15 @@ function UserProfile() { const extClass = localStorage.getItem("extended_class"); const extData = JSON.parse(localStorage.getItem("Extand_Class")); const ExtUserId = extData[0].objectId; - const body = { Phone: obj?.Phone || "", Name: obj.Name, HeaderDocId: isDisableDocId, JobTitle: jobTitle, - Company: company + Company: company, + UserName: publicUserName || "" }; + await axios.put( parseBaseUrl + "classes/" + extClass + "/" + ExtUserId, body, @@ -147,7 +179,6 @@ function UserProfile() { const json = JSON.parse(JSON.stringify([res])); const extRes = JSON.stringify(json); - localStorage.setItem("Extand_Class", extRes); // console.log("updateRes ", updateRes); }; @@ -241,6 +272,27 @@ function UserProfile() { setOtpLoader(false); alert("OTP sent on you email"); }; + //function to handle onchange username and restrict 6-characters username for free users + const handleOnchangeUserName = (e) => { + const value = e.target.value; + if (value.length > 6 && !isSubscribe) { + setUserNameError("Please upgrade to allow more than 6 characters."); + setTimeout(() => { + setUserNameError(""); + }, 2000); + } else { + setPublicUserName(e.target.value); + } + }; + const handleCancel = () => { + setEditMode(false); + SetName(localStorage.getItem("username")); + SetPhone(UserProfile && UserProfile.phone); + setImage(localStorage.getItem("profileImg")); + setPublicUserName(extendUser && extendUser?.[0]?.UserName); + setCompany(extendUser && extendUser?.[0]?.Company); + setJobTitle(extendUser?.[0]?.JobTitle); + }; return ( @@ -262,7 +314,14 @@ function UserProfile() { ></div> </div> ) : ( - <div className="flex justify-center items-center w-full"> + <div className="flex justify-center items-center w-full relative"> + {userNameError && ( + <div + className={`z-[1000] fixed top-[50%] transform border-[1px] text-sm border-[#f0a8a8] bg-[#f4bebe] text-[#c42121] rounded py-[.75rem] px-[1.25rem]`} + > + {userNameError} + </div> + )} <div className="bg-white flex flex-col justify-center shadow rounded w-[450px]"> <div className="flex flex-col justify-center items-center my-4"> <div className="w-[200px] h-[200px] overflow-hidden rounded-full"> @@ -390,6 +449,37 @@ function UserProfile() { )} </span> </li> + {!isEnableSubscription && ( + <li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all"> + <span className="font-semibold"> + Public profile :{" "} + <Tooltip + message={`this is your public URL. Copy or share it + with the signer, and you will be able to see + all your publicly set templates.`} + /> + </span> + <div className="flex items-center"> + <span>opensign-me.vercel.app/</span> + {editmode ? ( + <input + onChange={handleOnchangeUserName} + value={publicUserName} + disabled={!editmode} + placeholder="enter user name" + className="border-[1px] border-gray-200 rounded-[3px]" + /> + ) : ( + <input + value={extendUser?.[0]?.UserName} + disabled + placeholder="enter user name" + className="border-[1px] border-gray-200 rounded-[3px]" + /> + )} + </div> + </li> + )} <li className="border-y-[1px] border-gray-300 break-all"> <div className="flex justify-between items-center py-2"> <span @@ -460,7 +550,7 @@ function UserProfile() { type="button" onClick={() => { if (editmode) { - setEditMode(false); + handleCancel(); } else { navigate("/changepassword"); } diff --git a/apps/OpenSign/src/primitives/GetReportDisplay.js b/apps/OpenSign/src/primitives/GetReportDisplay.js index 2daf17a70..a20f72c55 100644 --- a/apps/OpenSign/src/primitives/GetReportDisplay.js +++ b/apps/OpenSign/src/primitives/GetReportDisplay.js @@ -4,14 +4,19 @@ import { useNavigate } from "react-router-dom"; import axios from "axios"; import ModalUi from "./ModalUi"; import AddSigner from "../components/AddSigner"; -import { modalSubmitBtnColor, modalCancelBtnColor } from "../constant/const"; +import { + modalSubmitBtnColor, + modalCancelBtnColor, + isEnableSubscription +} from "../constant/const"; import Alert from "./Alert"; import Tooltip from "./Tooltip"; import { RWebShare } from "react-web-share"; import Tour from "reactour"; import Parse from "parse"; import { saveAs } from "file-saver"; -import { replaceMailVaribles } from "../constant/Utils"; +import { copytoData, replaceMailVaribles } from "../constant/Utils"; +import Confetti from "react-confetti"; import EditorToolbar, { module1, formats @@ -36,6 +41,7 @@ const ReportTable = (props) => { const [isTour, setIsTour] = useState(false); const [tourStatusArr, setTourStatusArr] = useState([]); const [isResendMail, setIsResendMail] = useState({}); + const [isMakePublic, setIsMakePublic] = useState({}); const [mail, setMail] = useState({ subject: "", body: "" }); const [userDetails, setUserDetails] = useState({}); const [isNextStep, setIsNextStep] = useState({}); @@ -43,6 +49,12 @@ const ReportTable = (props) => { const [templateDeatils, setTemplateDetails] = useState({}); const [placeholders, setPlaceholders] = useState([]); const [isLoader, setIsLoader] = useState({}); + const [selectedPublicRole, setSelectedPublicRole] = useState(""); + const [isCelebration, setIsCelebration] = useState(false); + const [currentLists, setCurrentLists] = useState([]); + const [isPublic, setIsPublic] = useState({}); + const [isPublicProfile, setIsPublicProfile] = useState({}); + const [publicUserName, setIsPublicUserName] = useState(""); const [isViewShare, setIsViewShare] = useState({}); const startIndex = (currentPage - 1) * props.docPerPage; const { isMoreDocs, setIsNextRecord } = props; @@ -234,8 +246,19 @@ const ReportTable = (props) => { // Get current list const indexOfLastDoc = currentPage * props.docPerPage; const indexOfFirstDoc = indexOfLastDoc - props.docPerPage; - // `currentLists` is total record render on current page - const currentLists = props.List?.slice(indexOfFirstDoc, indexOfLastDoc); + useEffect(() => { + // `currentLists` is total record render on current page + const currentList = props.List?.slice(indexOfFirstDoc, indexOfLastDoc); + //check public template and save in a object to show public and private template + setIsPublic( + currentList.reduce((acc, item) => { + acc[item.objectId] = item?.IsPublic || false; + return acc; + }, {}) + ); + setCurrentLists(currentList); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [indexOfLastDoc, indexOfFirstDoc]); // Change page const paginateFront = () => setCurrentPage(currentPage + 1); @@ -297,9 +320,16 @@ const ReportTable = (props) => { setActLoader({}); } }; - const handleClose = () => { + const handleClose = (item) => { setIsRevoke({}); setIsDeleteModal({}); + setIsMakePublic({}); + setSelectedPublicRole(""); + setIsPublic((prevStates) => ({ + ...prevStates, + [item.objectId]: !prevStates[item.objectId] + })); + setIsPublicProfile({}); }; const handleShare = (item) => { @@ -687,9 +717,118 @@ const ReportTable = (props) => { setTimeout(() => setIsAlert(false), 1500); } }; + + //function to make template public and set public role + const handlePublicTemplate = async (item) => { + if (selectedPublicRole || !isPublic[item.objectId]) { + setActLoader({ [item.objectId]: true }); + setIsMakePublic(false); + try { + const res = await Parse.Cloud.run("createpublictemplate", { + templateid: item.objectId, + ispublic: isPublic[item.objectId], + publicrole: [selectedPublicRole] + }); + + if (res.status === "success") { + setIsAlert(true); + setTimeout(() => setIsAlert(false), 1500); + if (isPublic[item.objectId]) { + setAlertMsg({ + type: "success", + message: "You have successfully made the template public." + }); + setIsCelebration(true); + setTimeout(() => { + setIsCelebration(false); + }, 5000); + setIsPublicProfile({ [item.objectId]: isPublic[item.objectId] }); + } else { + setAlertMsg({ + type: "success", + message: "You have successfully made the template private." + }); + setSelectedPublicRole(""); + } + const updateList = props.List.map((x) => + x.objectId === item.objectId + ? { ...x, IsPublic: isPublic[item.objectId] } + : x + ); + props.setList(updateList); + setActLoader({}); + } + } catch (e) { + console.log("error in createpublictemplate", e); + setIsAlert(true); + setAlertMsg({ + type: "danger", + message: "Something went wrong, Please try again later!" + }); + setTimeout(() => setIsAlert(false), 1500); + setIsPublic((prevStates) => ({ + ...prevStates, + [item.objectId]: !prevStates[item.objectId] + })); + } + } else { + setIsAlert(true); + setAlertMsg({ + type: "danger", + message: "You need to select a role for the public signers." + }); + setTimeout(() => setIsAlert(false), 1500); + } + }; + const handleViewSigners = (item) => { setIsViewShare({ [item.objectId]: true }); }; + //function to handle change template status is public or private + const handlePublicChange = async (e, item) => { + const getPlaceholder = item?.Placeholders; + //condiiton to check role is exist or not + if (getPlaceholder && getPlaceholder.length > 0) { + let extendUser = JSON.parse(localStorage.getItem("Extand_Class")); + const userName = extendUser[0]?.UserName; + setIsPublicUserName(extendUser[0]?.UserName); + //condition to check user have public url or not + if (userName) { + setIsPublic((prevStates) => ({ + ...prevStates, + [item.objectId]: e.target.checked + })); + const getPlaceholder = item?.Placeholders; + if (getPlaceholder.length === 1) { + setSelectedPublicRole(getPlaceholder[0]?.Role); + } + + setIsMakePublic({ [item.objectId]: true }); + } else { + setIsPublicProfile({ [item.objectId]: true }); + } + } else { + setIsAlert(true); + setAlertMsg({ + type: "danger", + message: "Please assign at least one role to make this template public." + }); + setTimeout(() => setIsAlert(false), 3000); + } + }; + + //function to copy public profile links + const copytoProfileLink = () => { + const url = `https://opensign-me.vercel.app/${publicUserName}`; + copytoData(url); + setIsAlert(true); + setAlertMsg({ + type: "success", + message: "Copied." + }); + setTimeout(() => setIsAlert(false), 1500); + }; + return ( <div className="relative"> {Object.keys(actLoader)?.length > 0 && ( @@ -701,6 +840,11 @@ const ReportTable = (props) => { </div> )} <div className="p-2 overflow-x-scroll w-full bg-white rounded-md"> + {isCelebration && ( + <div style={{ position: "relative", zIndex: "1000" }}> + <Confetti width={window.innerWidth} height={window.innerHeight} /> + </div> + )} {isAlert && <Alert type={alertMsg.type}>{alertMsg.message}</Alert>} {props.tourData && props.ReportName === "Templates" && ( <Tour @@ -747,6 +891,9 @@ const ReportTable = (props) => { {props.actions?.length > 0 && ( <th className="px-4 py-2 font-thin">Action</th> )} + {props.ReportName === "Templates" && !isEnableSubscription && ( + <th className="px-4 py-2 font-thin">Public</th> + )} </tr> </thead> <tbody className="text-[12px]"> @@ -1186,6 +1333,170 @@ const ReportTable = (props) => { </ModalUi> )} </td> + {!isEnableSubscription && ( + <td className=" pl-[20px] py-2 "> + {props.ReportName === "Templates" && ( + <div className=" flex flex-row-"> + <label + className={ + "cursor-pointer relative inline-flex items-center mb-0" + } + > + <input + checked={isPublic?.[item.objectId]} + onChange={(e) => handlePublicChange(e, item)} + type="checkbox" + value="" + className="sr-only peer" + /> + <div className="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-1 peer-focus:ring-black rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all dark:border-black peer-checked:bg-blue-600"></div> + </label> + </div> + )} + {isMakePublic[item.objectId] && ( + <ModalUi + isOpen + title={ + isPublic[item.objectId] + ? "Make template public" + : "Make template private" + } + handleClose={() => { + setIsMakePublic({}); + setSelectedPublicRole(""); + setIsPublic((prevStates) => ({ + ...prevStates, + [item.objectId]: !prevStates[item.objectId] + })); + }} + > + <div className="m-[20px]"> + <div className="font-normal text-black"> + <p className="text-lg"> + {" "} + {isPublic[item.objectId] + ? `Are you sure you want tof make this templat public ?` + : `Are you sure you want to make this template private? This will remove it from your public profile ?`} + </p> + {isPublic[item.objectId] && ( + <div className="flex mt-2 gap-2 md:items-center"> + <p className="text-[15px]"> + Public role :{" "} + </p> + {item?.Placeholders?.length > 1 ? ( + <select + className="w-[60%] md:w-[70%] border-[1px] border-gray-200 rounded-sm p-[2px]" + name="textvalidate" + value={selectedPublicRole} + onChange={(e) => + setSelectedPublicRole( + e.target.value + ) + } + > + <option + disabled + style={{ fontSize: "13px" }} + value="" + > + Select... + </option> + {item?.Placeholders.map( + (data, ind) => { + return ( + <option + style={{ fontSize: "13px" }} + key={ind} + value={data?.Role} + > + {data?.Role} + </option> + ); + } + )} + </select> + ) : ( + <div className="w-[60%] md:w-[70%] border-[1px] border-gray-200 rounded-sm p-[2px]"> + {item?.Placeholders[0]?.Role} + </div> + )} + </div> + )} + </div> + + <hr className="bg-[#ccc] mt-2 " /> + <div className="flex items-center mt-3 gap-2 text-white"> + <button + onClick={() => handlePublicTemplate(item)} + className="px-4 py-1.5 text-white rounded shadow-md text-center focus:outline-none " + style={{ + backgroundColor: modalSubmitBtnColor + }} + > + Submit + </button> + <button + onClick={() => handleClose(item)} + className="px-4 py-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none" + style={{ + backgroundColor: modalCancelBtnColor + }} + > + No + </button> + </div> + </div> + </ModalUi> + )} + {isPublicProfile[item.objectId] && ( + <ModalUi + isOpen + title={"Public URL"} + handleClose={() => { + setIsPublicProfile({}); + }} + reduceWidth={"md:min-w-[440px] md:max-w-[400px]"} + > + <div className="m-[20px]"> + {publicUserName ? ( + <div className="font-normal text-black"> + <p> + Here’s your public URL. Copy or share it + with the signer, and you will be able to + see all your publicly set templates. + </p> + <div className=" flex items-center gap-5 mt-2 p-[2px] w-[75%]"> + <span className="font-bold"> + Public URL :{" "} + </span> + <span + onClick={() => copytoProfileLink()} + className="underline underline-offset-2 cursor-pointer" + >{`https://opensign.me/${publicUserName}`}</span> + </div> + </div> + ) : ( + <div className="font-normal text-black"> + <p> + Please add your public URL, and you will + be able to make a public template. + </p> + <button + className="px-4 py-1.5 mt-3 text-white rounded shadow-md text-center focus:outline-none " + style={{ + backgroundColor: modalSubmitBtnColor + }} + onClick={() => navigate("/profile")} + > + Add + </button> + </div> + )} + </div> + </ModalUi> + )} + </td> + )} </tr> ) )} diff --git a/apps/OpenSign/src/primitives/ModalUi.js b/apps/OpenSign/src/primitives/ModalUi.js index 778d483d1..b0413b430 100644 --- a/apps/OpenSign/src/primitives/ModalUi.js +++ b/apps/OpenSign/src/primitives/ModalUi.js @@ -19,9 +19,7 @@ const ModalUi = ({ <div className="fixed z-[999] top-0 left-0 w-[100%] h-[100%] bg-black bg-opacity-[75%]"> <div className={`${ - reduceWidth - ? "md:min-w-[440px] md:max-w-[400px]" - : "md:min-w-[500px]" + reduceWidth || "md:min-w-[500px]" } fixed z-[1000] top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-sm bg-white rounded shadow-md max-h-90 min-w-[90%] overflow-y-auto hide-scrollbar `} > {showHeader && ( diff --git a/apps/OpenSignServer/cloud/main.js b/apps/OpenSignServer/cloud/main.js index 0552d2930..6de2c181a 100644 --- a/apps/OpenSignServer/cloud/main.js +++ b/apps/OpenSignServer/cloud/main.js @@ -39,6 +39,9 @@ import encryptedpdf from './parsefunction/encryptedPdf.js'; import { getSignedUrl } from './parsefunction/getSignedUrl.js'; import createBatchDocs from './parsefunction/createBatchDocs.js'; import linkContactToDoc from './parsefunction/linkContactToDoc.js'; +import CreatePublicTemplate from './parsefunction/CreatePublicTemplate.js'; +import GetPublicUserName from './parsefunction/GetPublicUserName.js'; +import GetPublicTemplate from './parsefunction/GetPublicTemplate.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); @@ -88,3 +91,6 @@ Parse.Cloud.define('encryptedpdf', encryptedpdf); Parse.Cloud.define('getsignedurl', getSignedUrl); Parse.Cloud.define('batchdocuments', createBatchDocs); Parse.Cloud.define('linkcontacttodoc', linkContactToDoc); +Parse.Cloud.define('createpublictemplate', CreatePublicTemplate); +Parse.Cloud.define('getpublicusername', GetPublicUserName); +Parse.Cloud.define('getpublictemplate', GetPublicTemplate); diff --git a/apps/OpenSignServer/cloud/parsefunction/CreatePublicTemplate.js b/apps/OpenSignServer/cloud/parsefunction/CreatePublicTemplate.js new file mode 100644 index 000000000..1831b9f47 --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/CreatePublicTemplate.js @@ -0,0 +1,41 @@ +export default async function CreatePublicTemplate(request) { + const templateid = request.params.templateid; + const ispublic = request.params.ispublic; + const publicrole = request.params.publicrole; + try { + if (!request?.user) { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); + } else { + const userId = request?.user && request?.user?.id; + if (templateid) { + const updateTemplate = new Parse.Object('contracts_Template'); + updateTemplate.id = templateid; + if (ispublic) { + updateTemplate.set('PublicRole', publicrole); + } + updateTemplate.set('IsPublic', ispublic); + const Acl = new Parse.ACL(); + if (ispublic) { + Acl.setPublicReadAccess(true); + } + Acl.setReadAccess(userId, true); + Acl.setWriteAccess(userId, true); + updateTemplate.setACL(Acl); + const savedObject = await updateTemplate.save(null, { useMasterKey: true }); + const res = savedObject.toJSON(); + if (res) { + return { + status: 'success', + }; + } + } else { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Please provide required parameters!'); + } + } + } catch (err) { + const code = err.code || 400; + const msg = err.message; + const error = new Parse.Error(code, msg); + throw error; + } +} diff --git a/apps/OpenSignServer/cloud/parsefunction/GetPublicTemplate.js b/apps/OpenSignServer/cloud/parsefunction/GetPublicTemplate.js new file mode 100644 index 000000000..2b356bd32 --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/GetPublicTemplate.js @@ -0,0 +1,32 @@ +async function GetPublicTemplate(request) { + try { + const username = request.params.username; + if (username) { + const extUserQuery = new Parse.Query('contracts_Users'); + extUserQuery.equalTo('UserName', username); + const res = await extUserQuery.first({ useMasterKey: true }); + const userId = res.get('UserId').id; + if (userId) { + const templatQuery = new Parse.Query('contracts_Template'); + templatQuery.equalTo('CreatedBy', { + __type: 'Pointer', + className: '_User', + objectId: userId, + }); + templatQuery.equalTo('IsPublic', true); + const getTemplate = await templatQuery.find({ useMasterKey: true }); + return getTemplate; + } else { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Template not found'); + } + } else { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Please provide required parameters!'); + } + } catch (err) { + const code = err.code || 400; + const msg = err.message; + const error = new Parse.Error(code, msg); + throw error; + } +} +export default GetPublicTemplate; diff --git a/apps/OpenSignServer/cloud/parsefunction/GetPublicUserName.js b/apps/OpenSignServer/cloud/parsefunction/GetPublicUserName.js new file mode 100644 index 000000000..94b08a330 --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/GetPublicUserName.js @@ -0,0 +1,23 @@ +async function GetPublicUserName(request) { + try { + if (!request?.user) { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); + } else { + const username = request.params.username; + if (username) { + const userQuery = new Parse.Query('contracts_Users'); + userQuery.equalTo('UserName', username); + const res = await userQuery.first({ useMasterKey: true }); + return res; + } else { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Please provide required parameters!'); + } + } + } catch (err) { + const code = err.code || 400; + const msg = err.message; + const error = new Parse.Error(code, msg); + throw error; + } +} +export default GetPublicUserName; diff --git a/apps/OpenSignServer/cloud/parsefunction/reportsJson.js b/apps/OpenSignServer/cloud/parsefunction/reportsJson.js index 64c5cf522..37b53b95a 100644 --- a/apps/OpenSignServer/cloud/parsefunction/reportsJson.js +++ b/apps/OpenSignServer/cloud/parsefunction/reportsJson.js @@ -334,6 +334,7 @@ export default function reportJson(id, userId) { 'Signers.Email', 'Signers.Phone', 'Placeholders', + 'IsPublic', ], }; default: diff --git a/apps/OpenSignServer/databases/migrations/20240527194216-update_contracts_template_cls_cjs.cjs b/apps/OpenSignServer/databases/migrations/20240527194216-update_contracts_template_cls_cjs.cjs new file mode 100644 index 000000000..94489b287 --- /dev/null +++ b/apps/OpenSignServer/databases/migrations/20240527194216-update_contracts_template_cls_cjs.cjs @@ -0,0 +1,35 @@ +/** + * + * @param {Parse} Parse + */ +exports.up = async Parse => { + // TODO: set className here + const className = 'contracts_Template'; + const schema = new Parse.Schema(className); + schema.addArray('PublicRole'); + schema.addBoolean('IsPublic'); + + // TODO: Set the schema here + // Example: + // schema.addString('name').addNumber('cash'); + + return schema.update(); +}; + +/** + * + * @param {Parse} Parse + */ +exports.down = async Parse => { + // TODO: set className here + const className = 'contracts_Template'; + const schema = new Parse.Schema(className); + schema.deleteField('PublicRole'); + schema.deleteField('IsPublic'); + + // TODO: Set the schema here + // Example: + // schema.deleteField('name').deleteField('cash'); + + return schema.update(); +}; diff --git a/apps/OpenSignServer/test.pdf b/apps/OpenSignServer/test.pdf index 5facc82df..dfe01c088 100644 Binary files a/apps/OpenSignServer/test.pdf and b/apps/OpenSignServer/test.pdf differ