नए पंजीकरण अस्थायी रूप से बंद हैं (Maximum Active Applications Reached / New Submissions Paused)
वर्तमान में सभी 200 सक्रिय स्थान पूर्ण हो चुके हैं। जब कोई आवेदन अस्वीकृत (Rejected) होगा या कोई छात्र केंद्र छोड़ेगा (Left/Dropped), तो स्थान रिक्त होने पर नए आवेदन स्वतः पुनः प्रारंभ हो जाएंगे।
💡 नोट: जो अभ्यर्थी पहले से पंजीकृत हैं, वे नीचे "पूर्व आवेदन खोजें (Fetch Live Status)" में अपना Reg ID या मोबाइल नंबर और जन्मतिथि (DOB) डालकर अपना फॉर्म और हस्ताक्षरित PDF अभी भी अपडेट (Overwrite) कर सकते हैं।
पूर्व आवेदन खोजें / स्वीकृति स्थिति जांचें व हस्ताक्षर करें (Two-Way Sync: Check Status, Allotted ID & Re-Sign)
यदि आपने पहले आवेदन किया है, तो अपनी गोपनीयता सुरक्षा के साथ Registration ID / मोबाइल नंबर / Allotted ID तथा उसके नीचे अपनी जन्मतिथि (DOB) डालकर Google Sheet से नवीनतम स्थिति लोड करें और हस्ताक्षर कर PDF अपडेट (Overwrite) करें:
चरण 1: सुविधा उपयोग एवं शैक्षणिक वातावरण हेतु अनिवार्य नियम (Step 1: Mandatory Guidelines for Facilities & Study Environment)
अंत तक स्क्रॉल करें / Scroll to Bottom to Unlock
महत्वपूर्ण सूचना / Important Notice: यह कोई व्यावसायिक कोचिंग संस्थान नहीं है, बल्कि संस्थान द्वारा गंभीर विद्यार्थियों को स्वाध्याय का अनुकूल वातावरण व शैक्षणिक संसाधन उपलब्ध कराने की एक पहल है। फॉर्म अनलॉक करने के लिए नीचे दिए गए सभी नियमों को अंत तक स्क्रॉल करके ध्यानपूर्वक पढ़ें और सहमति बॉक्स पर टिक करें। (This is NOT a commercial coaching institute, but an institutional initiative to provide a conducive self-study environment and educational resources. Scroll to the bottom to read all rules and check the agreement box to unlock the form.)
CSR एवं समान अवसर प्रकोष्ठ (EOC) — स्वाध्याय वातावरण एवं संसाधन उपयोग आचार संहिता (Code of Conduct for Study Environment & Resource Utilization)
कॉर्पोरेट सामाजिक उत्तरदायित्व (CSR) और समान अवसर प्रकोष्ठ (EOC) के अंतर्गत मेधावी, वंचित एवं गंभीर विद्यार्थियों को प्रतियोगी परीक्षाओं (UPSC, State PSC, NEET, SSC, Teacher Selection एवं One-Day Exams) की तैयारी हेतु शांत शैक्षणिक वातावरण, पुस्तकालय, डिजिटल संसाधन और सहपाठी अध्ययन समूह (Peer Learning Ecosystem) उपलब्ध कराने के लिए निम्नलिखित अनिवार्य नियम निर्धारित हैं: (Under the Corporate Social Responsibility (CSR) and Equal Opportunity Cell (EOC) initiative, the following mandatory rules are established to provide a quiet academic environment, reference library, digital resources, and peer learning ecosystem to meritorious, underprivileged, and dedicated aspirants preparing for competitive exams:)
1. पहल का स्वरूप: स्वाध्याय वातावरण एवं सुविधाएं, व्यावसायिक कोचिंग नहीं (Nature of Initiative: Study Environment & Facilities, Not Commercial Coaching)
यह पंजीकरण किसी व्यावसायिक कोचिंग या ट्यूशन क्लास के लिए नहीं है। इसका मुख्य उद्देश्य ऐसे गंभीर अभ्यर्थियों को शांत स्वाध्याय कक्ष (Reading Room), संदर्भ पुस्तकालय (Reference Library), डिजिटल संसाधन (Internet/Computer Access), सहपाठी चर्चा समूह (Peer Study Group) और समय-समय पर विषय-विशेषज्ञों का मार्गदर्शन उपलब्ध कराना है, जिन्हें घर पर उपयुक्त शैक्षणिक वातावरण या संसाधन उपलब्ध नहीं हो पाते। (This registration is not for a commercial coaching institute or tuition class. Its purpose is to provide a quiet reading room, reference library, digital/internet access, peer study groups, and periodic expert guidance to serious aspirants who lack a conducive study environment or resources at home.)
2. प्रथम 1 सप्ताह का मूल्यांकन अवसर, 90% नियमित उपस्थिति एवं 3 दिन की अनुपस्थिति का नियम (1-Week Evaluation Period, 90% Regular Attendance & 3-Day Absence Rule)
संस्थान के अध्ययन कक्ष (Study Space), पुस्तकालय और संसाधनों की संख्या सीमित है। अतः प्रत्येक चयनित अभ्यर्थी को प्रारंभ में 1 सप्ताह का अवसर देकर उसके स्वाध्याय अनुशासन, समयबद्धता और गंभीरता का मूल्यांकन (1-Week Evaluation Period) किया जाएगा। मूल्यांकन में खरा उतरने के पश्चात आवंटित समय-सारणी (Slot) में न्यूनतम 90% नियमित उपस्थिति अनिवार्य है। बिना पूर्व सूचना के लगातार 3 दिनों तक अनुपस्थित रहने पर आपका आवंटित पहचान-पत्र (Allotted ID) निरस्त कर दिया जाएगा और वह स्थान प्रतीक्षा सूची (Waiting List) के अगले पात्र विद्यार्थी को दे दिया जाएगा। (Institutional study space, library seats, and resources are limited. Therefore, every selected candidate will initially be given a 1-Week Evaluation/Trial opportunity to assess their self-study discipline, punctuality, and seriousness. Thereafter, a minimum of 90% regular attendance in the allotted study slot is mandatory. In case of absence for 3 days without prior notice, your Allotted ID will be cancelled and given to the next eligible student on the waiting list.)
3. शांत वातावरण, पुस्तकालय एवं डिजिटल संपत्ति की सुरक्षा (Pin-Drop Silence, Library Books & Digital Asset Protection)
अध्ययन कक्ष व पुस्तकालय में पूर्ण शांति (Pin-drop Silence) बनाए रखना अनिवार्य है। संस्थान द्वारा उपलब्ध कराई गई पुस्तकों, पत्रिकाओं (Magazines), समाचार-पत्रों, कंप्यूटर, वाई-फाई और फर्नीचर को किसी भी प्रकार की क्षति पहुँचाना, पन्ने फाड़ना या पुस्तकों पर पेन/पेंसिल से निशान लगाना सख्त वर्जित है। डिजिटल लैब और इंटरनेट का उपयोग केवल शैक्षणिक एवं परीक्षा संबंधी कार्यों के लिए ही किया जाएगा। (Maintaining pin-drop silence in the reading room and library is mandatory. Damaging, tearing pages, or marking library books, magazines, newspapers, computers, or furniture with pen/pencil is strictly prohibited. Digital lab and Wi-Fi facilities must be used strictly for academic and exam preparation purposes.)
4. सहयोगात्मक अध्ययन एवं नियमित स्व-मूल्यांकन (Peer Learning & Regular Self-Assessment Practice)
संस्थान में केवल अकेले बैठकर पढ़ने के साथ-साथ सामूहिक अध्ययन (Peer Study Group), उत्तर लेखन समीक्षा (Peer Answer Review) और साप्ताहिक अभ्यास सेट/मॉक टेस्ट (OMR/CBT Practice) का वातावरण उपलब्ध कराया जाता है। सभी पंजीकृत विद्यार्थियों को इन स्व-मूल्यांकन एवं शैक्षणिक चर्चा गतिविधियों में सक्रिय सहयोग देना अनिवार्य होगा। (Along with individual self-study, the institution fosters a peer-learning ecosystem including group discussions, peer answer reviews, and weekly OMR/CBT practice tests. Active and constructive participation in these self-assessment activities is compulsory for all registered students.)
5. अध्ययन सामग्री एवं डिजिटल संसाधनों का नैतिक उपयोग (Ethical Use of Study Materials & Digital Resources)
CSR एवं EOC केंद्र में उपलब्ध ई-लाइब्रेरी, संदर्भ पुस्तकें, अध्ययन नोट्स और प्रैक्टिस पेपर्स केवल केंद्र में पंजीकृत विद्यार्थियों के अध्ययन हेतु हैं। इन्हें बिना पूर्व अनुमति परिसर से बाहर ले जाना, व्यावसायिक उपयोग करना या अनधिकृत सोशल मीडिया ग्रुप्स पर साझा करना पूर्णतः वर्जित एवं दंडनीय है। (E-library access, reference books, study notes, and practice papers available at the CSR & EOC center are strictly for registered students. Taking library property outside without permission or sharing resources commercially/on unauthorized social media groups is strictly prohibited.)
6. सामाजिक-आर्थिक पात्रता एवं दस्तावेजों की सत्यता (Authenticity of Socio-Economic Eligibility & Documents)
ये शैक्षणिक सुविधाएं प्राथमिकता के आधार पर उन विद्यार्थियों के लिए हैं जो आर्थिक, सामाजिक या आवासीय चुनौतियों का सामना कर रहे हैं। आवेदन पत्र में भरी गई पारिवारिक आय, आवासीय स्थिति, श्रेणी एवं शैक्षणिक जानकारी 100% सत्य होनी चाहिए। सत्यापन में कोई भी तथ्य गलत पाए जाने पर प्रदत्त सुविधाएं तत्काल वापस ले ली जाएंगी। (These facilities prioritize students facing socio-economic or residential study constraints. All details regarding family income, housing constraints, category, and education must be 100% true. Any false declaration discovered during verification will lead to immediate withdrawal of facilities.)
7. परिसर की स्वच्छता, गरिमा एवं जीरो टॉलरेंस नीति (Campus Cleanliness, Dignity & Zero Tolerance Policy)
अध्ययन परिसर को स्वच्छ एवं सुव्यवस्थित रखना प्रत्येक विद्यार्थी की सामूहिक जिम्मेदारी है। सहपाठियों, महिला अभ्यर्थियों, समन्वयकों व स्टाफ के साथ पूर्णतः गरिमामय एवं सम्मानजनक व्यवहार अपेक्षित है। परिसर में किसी भी प्रकार के नशे, राजनीतिक/जातिगत/धार्मिक विवाद, अमर्यादित भाषा या अनुशासनहीनता पर 'जीरो टॉलरेंस' (तत्काल निष्कासन) की नीति लागू होगी। (Maintaining cleanliness and order in the study premises is every student's collective responsibility. Dignified and respectful behavior towards peers, female aspirants, coordinators, and staff is mandatory. A strict 'Zero Tolerance' policy (immediate expulsion) applies to any substance use, political/caste/religious disputes, or indiscipline.)
8. परीक्षा सहभागिता एवं शैक्षणिक प्रगति ट्रैकिंग (Exam Participation & Academic Progress Tracking)
संस्थान के संसाधनों का उपयोग करने वाले प्रत्येक अभ्यर्थी को अपनी लक्षित प्रतियोगी परीक्षा (UPSC, PSC, NEET, SSC, KVS/NVS आदि) के आधिकारिक आवेदन, प्रवेश-पत्र (Admit Card) और परीक्षा परिणाम की जानकारी EOC प्रकोष्ठ को उपलब्ध करानी होगी, ताकि CSR प्रभाव मूल्यांकन (Impact Assessment) किया जा सके और आगे के चरणों हेतु अतिरिक्त सहायता दी जा सके। (Every student utilizing institutional facilities must share their official exam application, Admit Card, and Result details with the EOC Cell for CSR impact assessment and advanced-stage academic support.)
बधाई! आपने स्वाध्याय वातावरण एवं सुविधा उपयोग के सभी 8 अनिवार्य नियमों को अंत तक पढ़ लिया है। अब कृपया नीचे सहमति बॉक्स पर टिक करें। / Congratulations! You have read all 8 mandatory facility & study environment guidelines. Please check the agreement box below to unlock the form.
एडमिन प्रमाणीकरण (Admin Access)
CSR & EOC एडमिन नियंत्रण कक्ष (Admin Control Panel - eoc.csr.html)
Checking...
1. एडमिन PIN बदलें (Cloud-Synced Admin PIN)
☁️ यहाँ से PIN बदलते ही यह सीधे आपकी Google Sheet (Admin_Settings) में सेव हो जाएगा और दुनिया के किसी भी मोबाइल/कंप्यूटर पर तुरंत नया PIN काम करेगा।
2. Google Drive एवं Sheet Web App URL जोड़ें
💡 Auto-Code Update: भविष्य में यदि कभी यह URL या PIN बदलना पड़े, तो यहाँ नया URL डालकर "eoc.csr.html डाउनलोड करें" दबाएं—कोड के अंदर नया URL अपने-आप स्थायी रूप से जुड़कर eoc.csr.html नाम से डाउनलोड हो जाएगा (जिससे आपकी वेबसाइट पर पहले से मौजूद https://groundreportindia.org/monthly/eoc.html पूरी तरह सुरक्षित रहेगी)!
2B. नियम, फॉर्म सीमा (200 कोटा) एवं फाइल साइज नियंत्रण कक्ष (Dynamic Quota & File Size Controls)
यहाँ से आप बिना किसी कोडिंग बदलाव के अधिकतम सक्रिय फॉर्म संख्या, फोटो साइज और PDF साइज कभी भी बदल सकते हैं। जब Google Sheet में किसी छात्र का स्टेटस REJECTED या LEFT / DROPPED किया जाएगा, तो सीट स्वतः खाली होकर फॉर्म फिर से खुल जाएगा।
Active Forms: 0 / 200
Default: 200 (Auto-locks when full)
Default: 1 MB (Passport & Sign Photo)
Default: 25 MB (Direct Scanned PDF)
Updates still work when paused
💡 सीट खाली कैसे होगी? Google Sheet के कॉलम C (Approval Status) में किसी फॉर्म को REJECTED या LEFT लिखते ही वह 200 की गिनती से बाहर हो जाएगा।
2C. एडमिन विशेषाधिकार: खाली फॉर्म प्रिंट व PDF डाउनलोड करें (Admin Only: Blank Form Print & PDF)
सामान्य अभ्यर्थियों के लिए खाली फॉर्म प्रिंट/डाउनलोड करना बंद है (वे केवल भरा हुआ फॉर्म ही प्रिंट/डाउनलोड कर सकते हैं)। कार्यालय या ऑफलाइन उपयोग हेतु एडमिन यहाँ से पूरी तरह खाली फॉर्म प्रिंट या डाउनलोड कर सकते हैं:
3. वेबसाइट पर इम्बेड करने का कोड (Website Iframe Embed Code - https://groundreportindia.org/monthly/eoc.csr.html)
4. टू-वे Google Sheet सिंक + Google Drive PDF Overwrite कोड (Two-Way Sync Apps Script)
✨ 1. Two-Way Google Sheet Sync (दो-तरफा सिंक): यदि आप Google Sheet में किसी विद्यार्थी का Approval Status (APPROVED / 1-WEEK EVALUATION / WAITLISTED) या Allotted ID (आवंटित ID) बदलते हैं, तो फॉर्म में "Fetch Status" दबाते ही वह बदलाव तुरंत फॉर्म पर दिखने लगेगा।
✨ 2. Automatic PDF Overwrite in Google Drive: जब विद्यार्थी स्वीकृति या बदलाव के बाद फॉर्म पर हस्ताक्षर करके दोबारा सबमिट (Update) करेगा, तो यह कोड Google Sheet की उसी पंक्ति (Row) को अपडेट करेगा और Google Drive में पुरानी PDF को हटाकर नई हस्ताक्षरित PDF से Overwrite कर देगा!
// ============================================================================
// CSR & EOC Study Environment Portal - Two-Way Sheet Sync, PDF Overwrite & Zero-Leak Security
// ============================================================================
const FOLDER_NAME = "CSR_EOC_Application_PDFs";
const OPTIONAL_FOLDER_ID = ""; // Optional: Leave blank to auto-create folder in Drive
function getOrCreateTargetFolder() {
if (OPTIONAL_FOLDER_ID && OPTIONAL_FOLDER_ID.trim() !== "") {
return DriveApp.getFolderById(OPTIONAL_FOLDER_ID.trim());
}
const folders = DriveApp.getFoldersByName(FOLDER_NAME);
if (folders.hasNext()) {
return folders.next();
}
const newFolder = DriveApp.createFolder(FOLDER_NAME);
newFolder.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
return newFolder;
}
const SHEET_HEADERS = [
"Last Updated Timestamp", "Registration ID", "Approval Status (APPROVED / 1-WEEK EVALUATION / WAITLISTED / REJECTED / LEFT)", "Allotted ID (आवंटित ID)",
"Primary Target Exam", "Secondary Exam", "Study Medium", "Target Year", "Self-Study Stage", "Preferred Study Slot",
"Optional / Stream", "Past Attempts", "Required Institutional Facilities",
"Full Name", "Father Name", "Mother Name", "DOB", "Gender", "Mobile", "Email",
"Category", "Annual Family Income", "Parent Occupation", "PwBD Status",
"First-Gen Learner", "Home Study Constraint", "Area & Digital Device",
"State", "District", "ID Proof (Last 4)", "Full Postal Address",
"10th Qualification", "12th Qualification", "Graduation Details", "Other Qualification",
"Facility Need Statement (SOP)", "Consenting Guardian Name", "Guardian Relationship", "Guardian Phone",
"Guardian Consent Declared", "Digital Signature", "Signature Verification Mode (DPDP Safe - No Raw Image Stored)", "Google Drive PDF Link", "Drive File ID"
];
function ensureSheetHeaders(sheet) {
if (sheet.getLastRow() === 0) {
sheet.appendRow(SHEET_HEADERS);
const headerRange = sheet.getRange(1, 1, 1, SHEET_HEADERS.length);
headerRange.setBackground("#1e3a8a").setFontColor("#ffffff").setFontWeight("bold");
sheet.setFrozenRows(1);
}
}
function normalizeDobString(val) {
if (!val) return "";
if (Object.prototype.toString.call(val) === "[object Date]" && !isNaN(val.getTime())) {
return Utilities.formatDate(val, Session.getScriptTimeZone(), "yyyy-MM-dd");
}
const str = val.toString().trim();
if (/^\d{4}-\d{2}-\d{2}/.test(str)) {
return str.substring(0, 10);
}
const parsed = new Date(str);
if (!isNaN(parsed.getTime())) {
return Utilities.formatDate(parsed, Session.getScriptTimeZone(), "yyyy-MM-dd");
}
return str;
}
function getPortalSettingsAndActiveCount(ss) {
const mainSheet = ss.getSheets()[0];
ensureSheetHeaders(mainSheet);
let settingsSheet = ss.getSheetByName("Admin_Settings");
if (!settingsSheet) {
settingsSheet = ss.insertSheet("Admin_Settings");
settingsSheet.appendRow(["Setting Key", "Setting Value", "Description"]);
settingsSheet.appendRow(["MAX_ACTIVE_FORMS", "200", "Maximum active forms allowed (excludes REJECTED / LEFT / DROPPED)"]);
settingsSheet.appendRow(["MAX_PHOTO_MB", "1", "Maximum Passport & Signature Photo size in MB"]);
settingsSheet.appendRow(["MAX_PDF_MB", "25", "Maximum Physically Signed Scanned PDF size in MB"]);
settingsSheet.appendRow(["FORM_MODE", "AUTO", "AUTO = Open until quota full | PAUSED = Manually paused"]);
settingsSheet.appendRow(["ADMIN_PIN", "201625", "Global Admin Panel PIN (Server-Side Verified, Hidden from Public)"]);
settingsSheet.getRange(1, 1, 1, 3).setBackground("#312e81").setFontColor("#ffffff").setFontWeight("bold");
}
const sRows = settingsSheet.getDataRange().getValues();
const publicConfig = { maxActiveForms: 200, maxPhotoMB: 1, maxPdfMB: 25, formMode: "AUTO" };
let secretAdminPin = "201625";
let hasPinRow = false;
for (let i = 1; i < sRows.length; i++) {
const k = (sRows[i][0] || "").toString().trim();
const v = (sRows[i][1] || "").toString().trim();
if (k === "MAX_ACTIVE_FORMS") publicConfig.maxActiveForms = parseInt(v, 10) || 200;
if (k === "MAX_PHOTO_MB") publicConfig.maxPhotoMB = parseFloat(v) || 1;
if (k === "MAX_PDF_MB") publicConfig.maxPdfMB = parseFloat(v) || 25;
if (k === "FORM_MODE") publicConfig.formMode = v || "AUTO";
if (k === "ADMIN_PIN") {
hasPinRow = true;
if (v.length >= 4) secretAdminPin = v;
}
}
if (!hasPinRow) {
settingsSheet.appendRow(["ADMIN_PIN", "201625", "Global Admin Panel PIN (Server-Side Verified, Hidden from Public)"]);
}
const mRows = mainSheet.getDataRange().getValues();
let activeCount = 0;
for (let i = 1; i < mRows.length; i++) {
const regId = (mRows[i][1] || "").toString().trim();
const status = (mRows[i][2] || "").toString().toUpperCase();
if (regId !== "") {
if (status.indexOf("REJECT") === -1 && status.indexOf("LEFT") === -1 && status.indexOf("DROP") === -1 && status.indexOf("CANCEL") === -1) {
activeCount++;
}
}
}
publicConfig.activeCount = activeCount;
return { config: publicConfig, secretAdminPin: secretAdminPin, mainSheet: mainSheet, settingsSheet: settingsSheet };
}
function doGet(e) {
try {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const info = getPortalSettingsAndActiveCount(ss);
const sheet = info.mainSheet;
const settingsSheet = info.settingsSheet;
const action = (e.parameter.action || "").toString().trim();
if (action === "meta") {
return ContentService.createTextOutput(JSON.stringify({
status: "success",
config: info.config
})).setMimeType(ContentService.MimeType.JSON);
}
if (action === "verifyPin") {
const enteredPin = (e.parameter.pin || "").toString().trim();
if (enteredPin && enteredPin === info.secretAdminPin) {
return ContentService.createTextOutput(JSON.stringify({
status: "success",
verified: true,
config: info.config
})).setMimeType(ContentService.MimeType.JSON);
}
return ContentService.createTextOutput(JSON.stringify({
status: "unauthorized",
verified: false,
message: "Invalid Admin PIN"
})).setMimeType(ContentService.MimeType.JSON);
}
if (action === "changeAdminPin") {
const oldPin = (e.parameter.oldPin || "").toString().trim();
const newPin = (e.parameter.newPin || "").toString().trim();
if (oldPin !== info.secretAdminPin) {
return ContentService.createTextOutput(JSON.stringify({
status: "unauthorized",
message: "Unauthorized: Current Admin PIN is incorrect."
})).setMimeType(ContentService.MimeType.JSON);
}
if (newPin.length >= 4) {
settingsSheet.getRange(6, 2).setValue(newPin);
}
const updatedInfo = getPortalSettingsAndActiveCount(ss);
return ContentService.createTextOutput(JSON.stringify({
status: "success",
message: "Admin PIN securely updated in Row 6 of Admin_Settings",
config: updatedInfo.config
})).setMimeType(ContentService.MimeType.JSON);
}
if (action === "saveAdminSettings") {
const authPin = (e.parameter.authPin || "").toString().trim();
if (authPin !== info.secretAdminPin) {
return ContentService.createTextOutput(JSON.stringify({
status: "unauthorized",
message: "Unauthorized: Valid Admin PIN required to modify settings."
})).setMimeType(ContentService.MimeType.JSON);
}
if (e.parameter.maxActiveForms) settingsSheet.getRange(2, 2).setValue(parseInt(e.parameter.maxActiveForms, 10) || 200);
if (e.parameter.maxPhotoMB) settingsSheet.getRange(3, 2).setValue(parseFloat(e.parameter.maxPhotoMB) || 1);
if (e.parameter.maxPdfMB) settingsSheet.getRange(4, 2).setValue(parseFloat(e.parameter.maxPdfMB) || 25);
if (e.parameter.formMode) settingsSheet.getRange(5, 2).setValue(e.parameter.formMode);
const updatedInfo = getPortalSettingsAndActiveCount(ss);
return ContentService.createTextOutput(JSON.stringify({
status: "success",
message: "Admin settings securely updated in Admin_Settings tab",
config: updatedInfo.config
})).setMimeType(ContentService.MimeType.JSON);
}
const query = (e.parameter.q || "").toString().trim().toLowerCase();
const dobQuery = normalizeDobString(e.parameter.dob || "");
if (!query || !dobQuery) {
return ContentService.createTextOutput(JSON.stringify({
status: "error",
message: "Both Reg ID/Mobile and Date of Birth (DOB) are required.",
config: info.config
})).setMimeType(ContentService.MimeType.JSON);
}
const rows = sheet.getDataRange().getValues();
let idMatchedButDobWrong = false;
for (let i = rows.length - 1; i >= 1; i--) {
const row = rows[i];
const regId = (row[1] || "").toString().trim().toLowerCase();
const allottedId = (row[3] || "").toString().trim().toLowerCase();
const mobile = (row[18] || "").toString().trim().toLowerCase();
const rowDob = normalizeDobString(row[16]);
if (regId === query || mobile === query || (allottedId !== "" && allottedId !== "not allotted" && allottedId === query)) {
if (rowDob !== dobQuery) {
idMatchedButDobWrong = true;
continue;
}
const record = {
formId: row[1],
approvalStatus: row[2] || "Pending Review (1-Week Evaluation Applicable)",
allottedId: row[3] || "Not Allotted",
primaryExam: row[4], secondaryExam: row[5], examMedium: row[6], targetYear: row[7],
prepStage: row[8], studySlot: row[9], optionalSubject: row[10], pastAttempts: row[11],
supportNeeded: row[12], fullName: row[13], fatherName: row[14], motherName: row[15],
dob: rowDob, gender: row[17], mobile: row[18], email: row[19],
category: row[20], familyIncome: row[21], parentOccupation: row[22], pwdStatus: row[23],
firstGen: row[24], homeConstraint: row[25], areaAndDevice: row[26],
state: row[27], district: row[28], idProof: row[29], fullAddress: row[30],
edu10: row[31], edu12: row[32], eduGrad: row[33], eduOther: row[34],
sopText: row[35], emergencyName: row[36], guardianRelation: row[37], emergencyPhone: row[38],
guardianConsentDeclared: row[39], digitalSign: row[40], signatureAuditStatus: row[41],
fileUrl: row[42], driveFileId: row[43]
};
return ContentService.createTextOutput(JSON.stringify({ status: "success", record: record, config: info.config }))
.setMimeType(ContentService.MimeType.JSON);
}
}
if (idMatchedButDobWrong) {
return ContentService.createTextOutput(JSON.stringify({
status: "dob_mismatch",
message: "Date of Birth (DOB) does not match the registered record.",
config: info.config
})).setMimeType(ContentService.MimeType.JSON);
}
return ContentService.createTextOutput(JSON.stringify({ status: "not_found", message: "No matching record found in Google Sheet.", config: info.config }))
.setMimeType(ContentService.MimeType.JSON);
} catch (err) {
return ContentService.createTextOutput(JSON.stringify({ status: "error", message: err.toString() }))
.setMimeType(ContentService.MimeType.JSON);
}
}
function doPost(e) {
try {
const data = JSON.parse(e.postData.contents);
const ss = SpreadsheetApp.getActiveSpreadsheet();
const info = getPortalSettingsAndActiveCount(ss);
const sheet = info.mainSheet;
const settingsSheet = info.settingsSheet;
if (data.isTestPing) {
const folder = getOrCreateTargetFolder();
return ContentService.createTextOutput(JSON.stringify({
status: "success",
config: info.config,
message: "Connected to Two-Way Sheet & Drive Folder: " + folder.getName()
})).setMimeType(ContentService.MimeType.JSON);
}
if (data.action === "saveAdminSettings") {
if ((data.authPin || "").toString().trim() !== info.secretAdminPin) {
return ContentService.createTextOutput(JSON.stringify({ status: "unauthorized", message: "Invalid Admin PIN" }))
.setMimeType(ContentService.MimeType.JSON);
}
settingsSheet.getRange(2, 2).setValue(data.maxActiveForms || 200);
settingsSheet.getRange(3, 2).setValue(data.maxPhotoMB || 1);
settingsSheet.getRange(4, 2).setValue(data.maxPdfMB || 25);
settingsSheet.getRange(5, 2).setValue(data.formMode || "AUTO");
const updatedInfo = getPortalSettingsAndActiveCount(ss);
return ContentService.createTextOutput(JSON.stringify({
status: "success",
config: updatedInfo.config
})).setMimeType(ContentService.MimeType.JSON);
}
if (data.action === "changeAdminPin") {
if ((data.oldPin || "").toString().trim() !== info.secretAdminPin) {
return ContentService.createTextOutput(JSON.stringify({ status: "unauthorized", message: "Invalid Old PIN" }))
.setMimeType(ContentService.MimeType.JSON);
}
if (data.newPin && data.newPin.toString().trim().length >= 4) {
settingsSheet.getRange(6, 2).setValue(data.newPin.toString().trim());
}
const updatedInfo = getPortalSettingsAndActiveCount(ss);
return ContentService.createTextOutput(JSON.stringify({
status: "success",
config: updatedInfo.config
})).setMimeType(ContentService.MimeType.JSON);
}
const folder = getOrCreateTargetFolder();
const rows = sheet.getDataRange().getValues();
let existingRowIndex = -1;
let existingStatus = data.approvalStatus || "1-WEEK EVALUATION / Pending Review";
let existingAllottedId = data.allottedId || "Not Allotted";
let oldFileId = data.driveFileId || "";
for (let i = 1; i < rows.length; i++) {
if ((rows[i][1] || "").toString().trim() === (data.formId || "").toString().trim()) {
existingRowIndex = i + 1;
if (rows[i][2]) existingStatus = rows[i][2];
if (rows[i][3]) existingAllottedId = rows[i][3];
if (rows[i][43]) oldFileId = rows[i][43];
break;
}
}
if (existingRowIndex === -1) {
if (info.config.formMode === "PAUSED") {
return ContentService.createTextOutput(JSON.stringify({
status: "quota_closed",
message: "New applications are currently paused by Admin."
})).setMimeType(ContentService.MimeType.JSON);
}
if (info.config.activeCount >= info.config.maxActiveForms) {
return ContentService.createTextOutput(JSON.stringify({
status: "quota_full",
activeCount: info.config.activeCount,
maxActiveForms: info.config.maxActiveForms,
message: "Maximum active applications limit (" + info.config.maxActiveForms + ") reached."
})).setMimeType(ContentService.MimeType.JSON);
}
}
let fileUrl = "No PDF Attached";
let newFileId = oldFileId;
if (data.pdfBase64) {
if (oldFileId) {
try { DriveApp.getFileById(oldFileId).setTrashed(true); } catch (ignore) {}
}
const existingFiles = folder.getFiles();
while (existingFiles.hasNext()) {
const f = existingFiles.next();
if (f.getName().indexOf(data.formId) === 0) {
try { f.setTrashed(true); } catch (ignore) {}
}
}
const decodedPdf = Utilities.base64Decode(data.pdfBase64);
const blob = Utilities.newBlob(decodedPdf, MimeType.PDF, data.fileName || (data.formId + ".pdf"));
const file = folder.createFile(blob);
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
fileUrl = file.getUrl();
newFileId = file.getId();
}
const rowValues = [
new Date(), data.formId, existingStatus, existingAllottedId,
data.primaryExam, data.secondaryExam, data.examMedium, data.targetYear, data.prepStage, data.studySlot,
data.optionalSubject, data.pastAttempts, data.supportNeeded,
data.fullName, data.fatherName, data.motherName, data.dob, data.gender, data.mobile, data.email,
data.category, data.familyIncome, data.parentOccupation, data.pwdStatus,
data.firstGen, data.homeConstraint, data.areaAndDevice,
data.state, data.district, data.idProof, data.fullAddress,
data.edu10, data.edu12, data.eduGrad, data.eduOther,
data.sopText, data.emergencyName, data.guardianRelation, data.emergencyPhone,
data.guardianConsentDeclared, data.digitalSign, data.signatureAuditStatus || "Watermarked & Merged inside PDF Only", fileUrl, newFileId
];
if (existingRowIndex > 1) {
sheet.getRange(existingRowIndex, 1, 1, rowValues.length).setValues([rowValues]);
} else {
sheet.appendRow(rowValues);
}
return ContentService.createTextOutput(JSON.stringify({
status: "success",
updated: existingRowIndex > 1,
fileUrl: fileUrl,
driveFileId: newFileId,
approvalStatus: existingStatus,
allottedId: existingAllottedId
})).setMimeType(ContentService.MimeType.JSON);
} catch (err) {
return ContentService.createTextOutput(JSON.stringify({
status: "error",
message: err.toString()
})).setMimeType(ContentService.MimeType.JSON);
}
}