Anonymous
Firebase React Phone Auth Recaptcha уже была отображена в этом элементе
Сообщение
Anonymous » 06 июл 2025, 05:54
Я работаю над проектом, используя React, TS и Firebase. Ниже приведен полный код формы компонента студенческой формы, в которой мне нужно проверить номер телефона студента через OTP. Проблема заключается в том, что OTP отправляется нормально без какой -либо ошибки в первый раз, но когда кнопка повторной или нажимает, это дает ошибку. Кто-нибудь может помочь?
Код: Выделить всё
// At the top before imports
declare global {
interface Window {
grecaptcha: any;
recaptchaVerifier?: import("firebase/auth").RecaptchaVerifier| null ;
recaptchaWidgetId: number;
confirmationResult: import("firebase/auth").ConfirmationResult;
}
}
import { useParams } from "react-router-dom";
import { useEffect, useState } from "react";
import {
doc,
getDoc,
setDoc,
collection,
serverTimestamp,
query,
where,
getDocs,
} from "firebase/firestore";
import { auth, db, storage } from "../utils/firebase";
import { useForm } from "react-hook-form";
import { sendEmailOtp } from "../utils/sendOtp";
import { verifyEmailOtp } from "../utils/verifyOtp";
import { RecaptchaVerifier, signInWithPhoneNumber } from "firebase/auth";
import { getDownloadURL, ref, uploadBytes } from "firebase/storage";
import { generateCertificate } from "../utils/generateCertificate";
const regex = {
name: /^[A-Za-z\s.'-]{2,50}$/,
email: /^\S+@\S+\.\S+$/,
phone: /^[6-9]\d{9}$/,
course: /^[A-Za-z0-9\s().,-]{2,50}$/,
otp: /^\d{6}$/,
};
type FeedbackFormFields = {
name: string;
email: string;
phone: string;
course: string;
rating: number;
comments: string;
};
export default function FeedbackForm() {
const { id } = useParams();
const [formData, setFormData] = useState(null);
const [loading, setLoading] = useState(true);
const [invalid, setInvalid] = useState(false);
const [submitted, setSubmitted] = useState(false);
const [error, setError] = useState("");
const [emailOtpSent, setEmailOtpSent] = useState(false);
const [emailCooldown, setEmailCooldown] = useState(0);
const [emailOtpInput, setEmailOtpInput] = useState("");
const [emailOtpVerified, setEmailOtpVerified] = useState(false);
const [phoneOtpSent, setPhoneOtpSent] = useState(false);
const [phoneCooldown, setPhoneCooldown] = useState(0);
const [phoneOtpInput, setPhoneOtpInput] = useState("");
const [phoneOtpVerified, setPhoneOtpVerified] = useState(false);
const [loadingOtp, setLoadingOtp] = useState(false);
const [loadingOtpVerification, setLoadingOtpVerification] = useState(false);
const [loadingPhoneOtp, setLoadingPhoneOtp] = useState(false);
const [loadingPhoneVerification, setLoadingPhoneVerification] =
useState(false);
const [submitting, setSubmitting] = useState(false);
const [progress, setProgress] = useState(0);
const savedDraft = localStorage.getItem(`feedback_draft_${id}`);
const parsedDraft = savedDraft ? JSON.parse(savedDraft) : {};
const {
register,
handleSubmit,
watch,
formState: { errors },
} = useForm({
defaultValues: {
rating: 5,
...parsedDraft, // merge saved data with default
},
});
const watchedFields = watch();
useEffect(() => {
const subscription = watch((values) => {
localStorage.setItem(`feedback_draft_${id}`, JSON.stringify(values));
});
return () => subscription.unsubscribe(); // Cleanup
}, [watch, id]);
useEffect(() => {
const fetchForm = async () => {
if (!id) return;
const docRef = doc(db, "workshops", id);
const docSnap = await getDoc(docRef);
if (!docSnap.exists() || !docSnap.data()?.isActive) setInvalid(true);
else setFormData(docSnap.data());
setLoading(false);
};
fetchForm();
}, [id]);
useEffect(() => {
const totalFields = 5;
let filled = 0;
if (watchedFields.name?.match(regex.name)) filled++;
if (watchedFields.email?.match(regex.email)) filled++;
// if (emailOtpVerified) filled++;
if (watchedFields.phone?.match(regex.phone)) filled++;
// if (phoneOtpVerified) filled++;
if (watchedFields.course?.match(regex.course)) filled++;
if (watchedFields.rating) filled++;
// if (typeof watchedFields.comments === "string" && watchedFields.comments.trim()) filled++;
setProgress(Math.round((filled / totalFields) * 100));
}, [watchedFields]);
useEffect(() => {
return () => {
if (window.recaptchaVerifier) {
window.recaptchaVerifier.clear();
}
if (typeof window.recaptchaWidgetId !== "undefined" && window.grecaptcha) {
window.grecaptcha.reset(window.recaptchaWidgetId);
}
};
}, []);
useEffect(() => {
const interval = setInterval(() => {
setEmailCooldown((prev) => (prev > 0 ? prev - 1 : 0));
setPhoneCooldown((prev) => (prev > 0 ? prev - 1 : 0));
}, 1000);
return () => clearInterval(interval);
}, []);
const setupRecaptcha = async () => {
// Clear old if already created
if (window.recaptchaVerifier) {
if (window.recaptchaWidgetId !== undefined) {
window.grecaptcha.reset(window.recaptchaWidgetId);
}
window.recaptchaVerifier.clear(); // clean internal state
window.recaptchaVerifier = null;
}
// Create a new verifier
if(!window.recaptchaVerifier)
window.recaptchaVerifier = new RecaptchaVerifier(
auth,
"recaptcha-container",
{
size: "invisible",
callback: (response: string) => {
console.log("reCAPTCHA solved:", response);
},
}
);
// Store the widget ID for resetting later
window.recaptchaWidgetId = await window.recaptchaVerifier.render();
};
// const handleSendOtp = async () => {
// try {
// if(!watchedFields.email) setError("Please provide the email.")
// setLoadingOtp(true);
// const result = await sendEmailOtp(watchedFields.email);
// if (result.success) {
// setEmailOtpSent(true);
// alert("OTP sent to your email");
// }
// } catch {
// alert("Failed to send email OTP");
// } finally {
// setLoadingOtp(false);
// }
// };
const handleSendOtp = async () => {
try {
if (!watchedFields.email) {
setError("Please provide the email.");
return;
}
setLoadingOtp(true);
const result = await sendEmailOtp(watchedFields.email);
if (result.success) {
setEmailOtpSent(true);
setEmailCooldown(60); // ⏳ 60s cooldown starts here
alert("OTP sent to your email");
}
} catch {
alert("Failed to send email OTP");
} finally {
setLoadingOtp(false);
}
};
const handleVerifyEmailOtp = async () => {
try {
if (!regex.otp.test(emailOtpInput)) {
alert("Enter a valid 6-digit email OTP");
return;
}
setLoadingOtpVerification(true);
const result = await verifyEmailOtp(watchedFields.email, emailOtpInput);
if (result.success) {
setEmailOtpVerified(true);
alert("Email verified!");
}
} catch {
alert("Invalid or expired email OTP.");
} finally {
setLoadingOtpVerification(false);
}
};
// const handleSendPhoneOtp = async () => {
// try {
// setLoadingPhoneOtp(true);
// await setupRecaptcha();
// const confirmationResult = await signInWithPhoneNumber(
// auth,
// `+91${watchedFields.phone}`,
// window.recaptchaVerifier
// );
// window.confirmationResult = confirmationResult;
// setPhoneOtpSent(true);
// alert("OTP sent to phone");
// } catch (error) {
// console.error(error);
// alert("Failed to send phone OTP");
// } finally {
// setLoadingPhoneOtp(false);
// }
// };
const handleSendPhoneOtp = async () => {
try {
setLoadingPhoneOtp(true);
await setupRecaptcha();
const confirmationResult = await signInWithPhoneNumber(
auth,
`+91${watchedFields.phone}`,
window.recaptchaVerifier!
);
window.confirmationResult = confirmationResult;
setPhoneOtpSent(true);
setPhoneCooldown(60); // Start 60s cooldown
alert("OTP sent to phone");
} catch (error) {
console.error(error);
alert("Failed to send phone OTP");
} finally {
setLoadingPhoneOtp(false);
}
};
const handleVerifyPhoneOtp = async () => {
try {
if (!regex.otp.test(phoneOtpInput)) {
alert("Enter a valid 6-digit phone OTP");
return;
}
setLoadingPhoneVerification(true);
const result = await window.confirmationResult.confirm(phoneOtpInput);
if (result.user) {
setPhoneOtpVerified(true);
alert("Phone number verified!");
}
} catch {
alert("Invalid or expired phone OTP.");
} finally {
setLoadingPhoneVerification(false);
}
};
const onSubmit = async (data: FeedbackFormFields) => {
setError("");
setSubmitting(true);
if (!emailOtpVerified) {
setError("Please verify your email before submitting.");
setSubmitting(false);
return;
}
if (!phoneOtpVerified) {
setError("Please verify your phone before submitting.");
setSubmitting(false);
return;
}
try {
const feedbackRef = collection(db, "submissions");
const q = query(
feedbackRef,
where("formId", "==", id),
where("email", "==", data.email)
);
const snapshot = await getDocs(q);
if (!snapshot.empty) {
setError("You have already submitted this form.");
console.log("already submitted");
return;
}
const templateSnap = await getDocs(
query(
collection(db, "certificateTemplates"),
where("workshopId", "==", id)
)
);
if (templateSnap.empty) {
console.log("template not found");
return;
}
const templateData = templateSnap.docs[0].data();
const pdfBytes = await generateCertificate(
templateData.downloadURL,
templateData.fieldPositions || [],
{
name: data.name,
college: formData.college,
date: formData.date,
workshopName: formData.workshopName,
}
);
const certRef = ref(storage, `certificates/${id}_${data.email}.pdf`);
await uploadBytes(
certRef,
new Blob([new Uint8Array(pdfBytes)], { type: "application/pdf" })
);
const url = await getDownloadURL(certRef);
console.log(formData, id);
await setDoc(doc(db, "submissions", `${id}_${data.email}`), {
formId: id,
submittedAt: serverTimestamp(),
emailVerified: true,
phoneVerified: true,
certificateURL: url,
workshopName: formData.workshopName,
college: formData.college,
date: formData.date,
time: formData.time,
...data,
});
localStorage.removeItem(`feedback_draft_${id}`);
setSubmitted(true);
} catch (err) {
console.error("Submission failed", err);
setError("Submission failed. Try again later.");
} finally {
setSubmitting(false);
// setSubmitted(true);
}
};
if (loading) return Loading...;
if (invalid)
return (
Invalid or Inactive Form
);
if (submitted)
return (
Thank you for your feedback!
Your certificate will be sent to your registered email and WhatsApp
number shortly.
{/* {
window.open(url);
}}
>
Preview
*/}
);
return (
{/* Progress Bar */}
Form Progress: {progress}%
{formData?.workshopName}
{formData?.college}
{formData?.date} at {formData?.time}
{/* Name */}
Full Name *
{errors.name && (
{errors.name.message}
)}
{/* Email + OTP */}
Email *
{errors.email && (
{errors.email.message}
)}
{!emailOtpVerified && (
{!emailOtpSent || emailCooldown === 0 ? (
{loadingOtp
? "Sending..."
: emailOtpSent
? "Resend OTP"
: "Send OTP"}
) : (
Resend OTP in{" "}
{emailCooldown}s
)}
)}
{emailOtpSent && !emailOtpVerified && (
setEmailOtpInput(e.target.value)}
/>
{loadingOtpVerification ? "Verifying..." : "Verify OTP"}
)}
{emailOtpVerified && (
Email verified ✅
)}
{/* Phone + OTP */}
Phone *
{errors.phone && (
{errors.phone.message}
)}
{!phoneOtpVerified && (
{!phoneOtpSent || phoneCooldown === 0 ? (
{loadingPhoneOtp
? "Sending..."
: phoneOtpSent
? "Resend OTP"
: "Send OTP"}
) : (
Resend OTP in{" "}
{phoneCooldown}s
)}
)}
{phoneOtpSent && !phoneOtpVerified && (
setPhoneOtpInput(e.target.value)}
/>
{loadingPhoneVerification ? "Verifying..." : "Verify OTP"}
)}
{phoneOtpVerified && (
Phone verified ✅
)}
{/* Course */}
Course *
{errors.course && (
{errors.course.message}
)}
{/* Rating */}
Rating *
Excellent
Very Good
Good
Fair
Poor
{/* Comments */}
Comments
{/* Error Message */}
{error &&
{error}
}
{/* Submit Button */}
{submitting ? (
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"
/>
Submitting...
) : (
"Submit Feedback"
)}
{/* Recaptcha */}
);
}
ошибка:
Изображение ошибки
Подробнее здесь:
https://stackoverflow.com/questions/796 ... is-element
1751770446
Anonymous
Я работаю над проектом, используя React, TS и Firebase. Ниже приведен полный код формы компонента студенческой формы, в которой мне нужно проверить номер телефона студента через OTP. Проблема заключается в том, что OTP отправляется нормально без какой -либо ошибки в первый раз, но когда кнопка повторной или нажимает, это дает ошибку. Кто-нибудь может помочь?[code]// At the top before imports declare global { interface Window { grecaptcha: any; recaptchaVerifier?: import("firebase/auth").RecaptchaVerifier| null ; recaptchaWidgetId: number; confirmationResult: import("firebase/auth").ConfirmationResult; } } import { useParams } from "react-router-dom"; import { useEffect, useState } from "react"; import { doc, getDoc, setDoc, collection, serverTimestamp, query, where, getDocs, } from "firebase/firestore"; import { auth, db, storage } from "../utils/firebase"; import { useForm } from "react-hook-form"; import { sendEmailOtp } from "../utils/sendOtp"; import { verifyEmailOtp } from "../utils/verifyOtp"; import { RecaptchaVerifier, signInWithPhoneNumber } from "firebase/auth"; import { getDownloadURL, ref, uploadBytes } from "firebase/storage"; import { generateCertificate } from "../utils/generateCertificate"; const regex = { name: /^[A-Za-z\s.'-]{2,50}$/, email: /^\S+@\S+\.\S+$/, phone: /^[6-9]\d{9}$/, course: /^[A-Za-z0-9\s().,-]{2,50}$/, otp: /^\d{6}$/, }; type FeedbackFormFields = { name: string; email: string; phone: string; course: string; rating: number; comments: string; }; export default function FeedbackForm() { const { id } = useParams(); const [formData, setFormData] = useState(null); const [loading, setLoading] = useState(true); const [invalid, setInvalid] = useState(false); const [submitted, setSubmitted] = useState(false); const [error, setError] = useState(""); const [emailOtpSent, setEmailOtpSent] = useState(false); const [emailCooldown, setEmailCooldown] = useState(0); const [emailOtpInput, setEmailOtpInput] = useState(""); const [emailOtpVerified, setEmailOtpVerified] = useState(false); const [phoneOtpSent, setPhoneOtpSent] = useState(false); const [phoneCooldown, setPhoneCooldown] = useState(0); const [phoneOtpInput, setPhoneOtpInput] = useState(""); const [phoneOtpVerified, setPhoneOtpVerified] = useState(false); const [loadingOtp, setLoadingOtp] = useState(false); const [loadingOtpVerification, setLoadingOtpVerification] = useState(false); const [loadingPhoneOtp, setLoadingPhoneOtp] = useState(false); const [loadingPhoneVerification, setLoadingPhoneVerification] = useState(false); const [submitting, setSubmitting] = useState(false); const [progress, setProgress] = useState(0); const savedDraft = localStorage.getItem(`feedback_draft_${id}`); const parsedDraft = savedDraft ? JSON.parse(savedDraft) : {}; const { register, handleSubmit, watch, formState: { errors }, } = useForm({ defaultValues: { rating: 5, ...parsedDraft, // merge saved data with default }, }); const watchedFields = watch(); useEffect(() => { const subscription = watch((values) => { localStorage.setItem(`feedback_draft_${id}`, JSON.stringify(values)); }); return () => subscription.unsubscribe(); // Cleanup }, [watch, id]); useEffect(() => { const fetchForm = async () => { if (!id) return; const docRef = doc(db, "workshops", id); const docSnap = await getDoc(docRef); if (!docSnap.exists() || !docSnap.data()?.isActive) setInvalid(true); else setFormData(docSnap.data()); setLoading(false); }; fetchForm(); }, [id]); useEffect(() => { const totalFields = 5; let filled = 0; if (watchedFields.name?.match(regex.name)) filled++; if (watchedFields.email?.match(regex.email)) filled++; // if (emailOtpVerified) filled++; if (watchedFields.phone?.match(regex.phone)) filled++; // if (phoneOtpVerified) filled++; if (watchedFields.course?.match(regex.course)) filled++; if (watchedFields.rating) filled++; // if (typeof watchedFields.comments === "string" && watchedFields.comments.trim()) filled++; setProgress(Math.round((filled / totalFields) * 100)); }, [watchedFields]); useEffect(() => { return () => { if (window.recaptchaVerifier) { window.recaptchaVerifier.clear(); } if (typeof window.recaptchaWidgetId !== "undefined" && window.grecaptcha) { window.grecaptcha.reset(window.recaptchaWidgetId); } }; }, []); useEffect(() => { const interval = setInterval(() => { setEmailCooldown((prev) => (prev > 0 ? prev - 1 : 0)); setPhoneCooldown((prev) => (prev > 0 ? prev - 1 : 0)); }, 1000); return () => clearInterval(interval); }, []); const setupRecaptcha = async () => { // Clear old if already created if (window.recaptchaVerifier) { if (window.recaptchaWidgetId !== undefined) { window.grecaptcha.reset(window.recaptchaWidgetId); } window.recaptchaVerifier.clear(); // clean internal state window.recaptchaVerifier = null; } // Create a new verifier if(!window.recaptchaVerifier) window.recaptchaVerifier = new RecaptchaVerifier( auth, "recaptcha-container", { size: "invisible", callback: (response: string) => { console.log("reCAPTCHA solved:", response); }, } ); // Store the widget ID for resetting later window.recaptchaWidgetId = await window.recaptchaVerifier.render(); }; // const handleSendOtp = async () => { // try { // if(!watchedFields.email) setError("Please provide the email.") // setLoadingOtp(true); // const result = await sendEmailOtp(watchedFields.email); // if (result.success) { // setEmailOtpSent(true); // alert("OTP sent to your email"); // } // } catch { // alert("Failed to send email OTP"); // } finally { // setLoadingOtp(false); // } // }; const handleSendOtp = async () => { try { if (!watchedFields.email) { setError("Please provide the email."); return; } setLoadingOtp(true); const result = await sendEmailOtp(watchedFields.email); if (result.success) { setEmailOtpSent(true); setEmailCooldown(60); // ⏳ 60s cooldown starts here alert("OTP sent to your email"); } } catch { alert("Failed to send email OTP"); } finally { setLoadingOtp(false); } }; const handleVerifyEmailOtp = async () => { try { if (!regex.otp.test(emailOtpInput)) { alert("Enter a valid 6-digit email OTP"); return; } setLoadingOtpVerification(true); const result = await verifyEmailOtp(watchedFields.email, emailOtpInput); if (result.success) { setEmailOtpVerified(true); alert("Email verified!"); } } catch { alert("Invalid or expired email OTP."); } finally { setLoadingOtpVerification(false); } }; // const handleSendPhoneOtp = async () => { // try { // setLoadingPhoneOtp(true); // await setupRecaptcha(); // const confirmationResult = await signInWithPhoneNumber( // auth, // `+91${watchedFields.phone}`, // window.recaptchaVerifier // ); // window.confirmationResult = confirmationResult; // setPhoneOtpSent(true); // alert("OTP sent to phone"); // } catch (error) { // console.error(error); // alert("Failed to send phone OTP"); // } finally { // setLoadingPhoneOtp(false); // } // }; const handleSendPhoneOtp = async () => { try { setLoadingPhoneOtp(true); await setupRecaptcha(); const confirmationResult = await signInWithPhoneNumber( auth, `+91${watchedFields.phone}`, window.recaptchaVerifier! ); window.confirmationResult = confirmationResult; setPhoneOtpSent(true); setPhoneCooldown(60); // Start 60s cooldown alert("OTP sent to phone"); } catch (error) { console.error(error); alert("Failed to send phone OTP"); } finally { setLoadingPhoneOtp(false); } }; const handleVerifyPhoneOtp = async () => { try { if (!regex.otp.test(phoneOtpInput)) { alert("Enter a valid 6-digit phone OTP"); return; } setLoadingPhoneVerification(true); const result = await window.confirmationResult.confirm(phoneOtpInput); if (result.user) { setPhoneOtpVerified(true); alert("Phone number verified!"); } } catch { alert("Invalid or expired phone OTP."); } finally { setLoadingPhoneVerification(false); } }; const onSubmit = async (data: FeedbackFormFields) => { setError(""); setSubmitting(true); if (!emailOtpVerified) { setError("Please verify your email before submitting."); setSubmitting(false); return; } if (!phoneOtpVerified) { setError("Please verify your phone before submitting."); setSubmitting(false); return; } try { const feedbackRef = collection(db, "submissions"); const q = query( feedbackRef, where("formId", "==", id), where("email", "==", data.email) ); const snapshot = await getDocs(q); if (!snapshot.empty) { setError("You have already submitted this form."); console.log("already submitted"); return; } const templateSnap = await getDocs( query( collection(db, "certificateTemplates"), where("workshopId", "==", id) ) ); if (templateSnap.empty) { console.log("template not found"); return; } const templateData = templateSnap.docs[0].data(); const pdfBytes = await generateCertificate( templateData.downloadURL, templateData.fieldPositions || [], { name: data.name, college: formData.college, date: formData.date, workshopName: formData.workshopName, } ); const certRef = ref(storage, `certificates/${id}_${data.email}.pdf`); await uploadBytes( certRef, new Blob([new Uint8Array(pdfBytes)], { type: "application/pdf" }) ); const url = await getDownloadURL(certRef); console.log(formData, id); await setDoc(doc(db, "submissions", `${id}_${data.email}`), { formId: id, submittedAt: serverTimestamp(), emailVerified: true, phoneVerified: true, certificateURL: url, workshopName: formData.workshopName, college: formData.college, date: formData.date, time: formData.time, ...data, }); localStorage.removeItem(`feedback_draft_${id}`); setSubmitted(true); } catch (err) { console.error("Submission failed", err); setError("Submission failed. Try again later."); } finally { setSubmitting(false); // setSubmitted(true); } }; if (loading) return Loading...; if (invalid) return ( Invalid or Inactive Form ); if (submitted) return ( Thank you for your feedback! Your certificate will be sent to your registered email and WhatsApp number shortly. {/* { window.open(url); }} > Preview */} ); return ( {/* Progress Bar */} Form Progress: {progress}% {formData?.workshopName} {formData?.college} {formData?.date} at {formData?.time} {/* Name */} Full Name * {errors.name && ( {errors.name.message} )} {/* Email + OTP */} Email * {errors.email && ( {errors.email.message} )} {!emailOtpVerified && ( {!emailOtpSent || emailCooldown === 0 ? ( {loadingOtp ? "Sending..." : emailOtpSent ? "Resend OTP" : "Send OTP"} ) : ( Resend OTP in{" "} {emailCooldown}s )} )} {emailOtpSent && !emailOtpVerified && ( setEmailOtpInput(e.target.value)} /> {loadingOtpVerification ? "Verifying..." : "Verify OTP"} )} {emailOtpVerified && ( Email verified ✅ )} {/* Phone + OTP */} Phone * {errors.phone && ( {errors.phone.message} )} {!phoneOtpVerified && ( {!phoneOtpSent || phoneCooldown === 0 ? ( {loadingPhoneOtp ? "Sending..." : phoneOtpSent ? "Resend OTP" : "Send OTP"} ) : ( Resend OTP in{" "} {phoneCooldown}s )} )} {phoneOtpSent && !phoneOtpVerified && ( setPhoneOtpInput(e.target.value)} /> {loadingPhoneVerification ? "Verifying..." : "Verify OTP"} )} {phoneOtpVerified && ( Phone verified ✅ )} {/* Course */} Course * {errors.course && ( {errors.course.message} )} {/* Rating */} Rating * Excellent Very Good Good Fair Poor {/* Comments */} Comments {/* Error Message */} {error && {error} } {/* Submit Button */} {submitting ? ( className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z" /> Submitting... ) : ( "Submit Feedback" )} {/* Recaptcha */} ); } [/code] ошибка: Изображение ошибки Подробнее здесь: [url]https://stackoverflow.com/questions/79691227/firebase-react-phone-auth-recaptcha-has-already-been-rendered-in-this-element[/url]