Files
intotheeast-com-content/plugins/form/templates/forms/fields/recaptcha/recaptcha.html.twig
T

394 lines
20 KiB
Twig

{% extends "forms/field.html.twig" %}
{% block label %}{% endblock %}
{% block input %}
{% set config = grav.config %}
{% set formId = form.id ?: form.name %}
{% set callbackId = formId|underscorize %}
{% set lang = grav.language.language %}
{# Get configuration values with fallbacks #}
{% set version = field.recaptcha_version ?? config.plugins.form.recaptcha.version ?? '2-checkbox' %}
{% set site_key = field.recaptcha_site_key ?? config.plugins.form.recaptcha.site_key %}
{% set theme = field.recaptcha_theme ?? config.plugins.form.recaptcha.theme ?? 'light' %}
{% if not site_key %}
<div class="form-error">reCAPTCHA site key is not set. Please set it in the form field or plugin configuration.</div>
{% else %}
{% if version == 3 or version == '3' %}
{# --- reCAPTCHA v3 Handling --- #}
{% set action = (page.route|trim('/') ~ '-' ~ form.name)|underscorize|md5 %}
<div class="g-recaptcha-container"
data-form-id="{{ formId }}"
data-recaptcha-version="3"
data-captcha-provider="recaptcha"
data-intercepts-submit="true"
data-sitekey="{{ site_key }}"
data-version="3"
data-action="{{ action }}"
data-theme="{{ theme }}"
data-lang="{{ lang }}"
data-callback-id="{{ callbackId }}">
{# Container for v3 - will be managed by JS #}
<input type="hidden" name="data[token]" value="">
<input type="hidden" name="data[action]" value="{{ action }}">
</div>
{% do assets.addJs('https://www.google.com/recaptcha/api.js?render=' ~ site_key, { group: 'bottom' }) %}
<script>
(function() {
window.GravRecaptchaInitializers = window.GravRecaptchaInitializers || {};
function addHiddenInput(form, name, value) {
const existing = form.querySelector('input[type="hidden"][name="' + name + '"]');
if (existing) {
existing.value = value;
} else {
const input = document.createElement('input');
input.setAttribute('type', 'hidden');
input.setAttribute('name', name);
input.setAttribute('value', value);
form.insertBefore(input, form.firstChild);
}
}
function initRecaptchaV3(container) {
const formId = container.dataset.formId;
const siteKey = container.dataset.sitekey;
const action = container.dataset.action;
const form = document.getElementById(formId);
if (!form) return;
console.log(`Initializing reCAPTCHA v3 for form ${formId}`);
const submitHandler = function(event) {
event.preventDefault();
console.log(`reCAPTCHA v3 intercepting submit for form ${formId}`);
grecaptcha.ready(function() {
grecaptcha.execute(siteKey, { action: action })
.then(function(token) {
console.log(`reCAPTCHA v3 token received for form ${formId}`);
addHiddenInput(form, 'data[token]', token);
addHiddenInput(form, 'data[action]', action);
form.removeEventListener('submit', submitHandler);
if (form.dataset.xhrEnabled === 'true' && window.GravFormXHR && typeof window.GravFormXHR.submit === 'function') {
window.GravFormXHR.submit(form);
} else {
if (typeof form.requestSubmit === 'function') {
form.requestSubmit();
} else {
form.submit();
}
}
setTimeout(() => {
const currentForm = document.getElementById(formId);
if (currentForm && !currentForm.dataset.recaptchaListenerAttached) {
currentForm.addEventListener('submit', submitHandler);
currentForm.dataset.recaptchaListenerAttached = 'true';
} else if (currentForm) {
delete currentForm.dataset.recaptchaListenerAttached;
}
}, 0);
});
});
};
delete form.dataset.recaptchaListenerAttached;
if (!form.dataset.recaptchaListenerAttached) {
form.addEventListener('submit', submitHandler);
form.dataset.recaptchaListenerAttached = 'true';
}
}
// Register the initializer function
const initializerFunctionName = 'initRecaptcha_{{ formId }}';
window.GravRecaptchaInitializers[initializerFunctionName] = function() {
const container = document.querySelector('[data-form-id="{{ formId }}"][data-captcha-provider="recaptcha"]');
if (!container) return;
initRecaptchaV3(container);
};
// Initial call
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', window.GravRecaptchaInitializers[initializerFunctionName]);
} else {
setTimeout(window.GravRecaptchaInitializers[initializerFunctionName], 0);
}
})();
</script>
{% elseif version == '2-invisible' %}
{# --- reCAPTCHA v2 Invisible Handling --- #}
<div class="g-recaptcha-container"
data-form-id="{{ formId }}"
data-recaptcha-version="2-invisible"
data-captcha-provider="recaptcha"
data-intercepts-submit="true"
data-sitekey="{{ site_key }}"
data-version="2-invisible"
data-theme="{{ theme }}"
data-lang="{{ lang }}"
data-callback-id="{{ callbackId }}">
{# Container for v2 invisible - will be managed by JS #}
<div id="g-recaptcha-{{ formId }}" class="g-recaptcha"></div>
</div>
<script>
(function() {
window.GravRecaptchaInitializers = window.GravRecaptchaInitializers || {};
function addHiddenInput(form, name, value) {
const existing = form.querySelector('input[type="hidden"][name="' + name + '"]');
if (existing) {
existing.value = value;
} else {
const input = document.createElement('input');
input.setAttribute('type', 'hidden');
input.setAttribute('name', name);
input.setAttribute('value', value);
form.insertBefore(input, form.firstChild);
}
}
function initRecaptchaV2Invisible(container) {
const formId = container.dataset.formId;
const siteKey = container.dataset.sitekey;
const lang = container.dataset.lang;
const theme = container.dataset.theme;
const callbackId = container.dataset.callbackId || formId;
const form = document.getElementById(formId);
let widgetId = null;
if (!form) return;
console.log(`Initializing reCAPTCHA v2 Invisible for form ${formId}`);
const callbackName = 'captchaInvisibleOnloadCallback_' + callbackId;
if (typeof window[callbackName] !== 'function') {
window[callbackName] = function() {
console.log('reCAPTCHA Invisible API ready for form ' + formId);
};
if (!document.querySelector('script[src*="recaptcha/api.js?onload=' + callbackName + '"]')) {
const script = document.createElement('script');
script.src = 'https://www.google.com/recaptcha/api.js?onload=' + callbackName + '&hl=' + lang + '&theme=' + theme;
script.async = true;
script.defer = true;
document.head.appendChild(script);
}
}
const submitHandler = function(event) {
event.preventDefault();
console.log(`reCAPTCHA v2 Invisible intercepting submit for form ${formId}`);
if (typeof grecaptcha === 'undefined' || typeof grecaptcha.render === 'undefined') {
console.error('grecaptcha not ready for invisible captcha');
return;
}
const recaptchaId = 'g-recaptcha-' + formId;
let captchaElement = document.getElementById(recaptchaId);
if (!captchaElement) {
captchaElement = document.createElement('div');
captchaElement.setAttribute('id', recaptchaId);
captchaElement.className = 'g-recaptcha';
form.appendChild(captchaElement);
}
const renderCaptcha = () => {
if (widgetId !== null) {
try {
grecaptcha.reset(widgetId);
} catch (e) {
console.warn("Error resetting captcha", e);
}
}
widgetId = grecaptcha.render(recaptchaId, {
sitekey: siteKey,
size: 'invisible',
callback: function(token) {
console.log(`reCAPTCHA v2 Invisible token received for form ${formId}`);
addHiddenInput(form, 'g-recaptcha-response', token);
form.removeEventListener('submit', submitHandler);
if (form.dataset.xhrEnabled === 'true' && window.GravFormXHR && typeof window.GravFormXHR.submit === 'function') {
window.GravFormXHR.submit(form);
} else {
if (typeof form.requestSubmit === 'function') {
form.requestSubmit();
} else {
form.submit();
}
}
setTimeout(() => {
const currentForm = document.getElementById(formId);
if (currentForm && !currentForm.dataset.recaptchaListenerAttached) {
currentForm.addEventListener('submit', submitHandler);
currentForm.dataset.recaptchaListenerAttached = 'true';
} else if (currentForm) {
delete currentForm.dataset.recaptchaListenerAttached;
}
}, 0);
}
});
grecaptcha.execute(widgetId);
};
if (typeof grecaptcha !== 'undefined' && grecaptcha.render) {
renderCaptcha();
} else {
const originalOnload = window[callbackName];
window[callbackName] = function() {
if(originalOnload) originalOnload();
renderCaptcha();
};
console.warn("grecaptcha object not found immediately, waiting for onload callback: " + callbackName);
}
};
delete form.dataset.recaptchaListenerAttached;
if (!form.dataset.recaptchaListenerAttached) {
form.addEventListener('submit', submitHandler);
form.dataset.recaptchaListenerAttached = 'true';
}
}
// Register the initializer function
const initializerFunctionName = 'initRecaptcha_{{ formId }}';
window.GravRecaptchaInitializers[initializerFunctionName] = function() {
const container = document.querySelector('[data-form-id="{{ formId }}"][data-captcha-provider="recaptcha"]');
if (!container) return;
initRecaptchaV2Invisible(container);
};
// Initial call
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', window.GravRecaptchaInitializers[initializerFunctionName]);
} else {
setTimeout(window.GravRecaptchaInitializers[initializerFunctionName], 0);
}
})();
</script>
{% else %}
{# --- reCAPTCHA v2 Checkbox Handling --- #}
{# Add script and container #}
{% set container_id = 'g-recaptcha-' ~ formId %}
{% set onloadCallback = 'captchaCheckboxOnloadCallback_' ~ callbackId %}
<div class="g-recaptcha-container"
data-form-id="{{ formId }}"
data-captcha-provider="recaptcha"
data-sitekey="{{ site_key }}"
data-version="2-checkbox"
data-theme="{{ theme }}"
data-lang="{{ lang }}"
data-callback-id="{{ callbackId }}">
<div id="{{ container_id }}" class="g-recaptcha"></div>
</div>
{% do assets.addJs('https://www.google.com/recaptcha/api.js?onload=' ~ onloadCallback ~ '&render=explicit', { 'group': 'bottom', 'loading': 'defer' }) %}
<script>
(function() {
// Explicit rendering for reCAPTCHA v2 Checkbox
window.GravExplicitCaptchaInitializers = window.GravExplicitCaptchaInitializers || {};
const initializerFunctionName = 'initExplicitCaptcha_{{ formId }}';
// Define the initializer function
window.GravExplicitCaptchaInitializers[initializerFunctionName] = function() {
const containerId = '{{ container_id }}';
const container = document.getElementById(containerId);
if (!container) {
console.warn('reCAPTCHA container #' + containerId + ' not found.');
return;
}
// Prevent re-rendering if widget already exists
if (container.innerHTML.trim() !== '' && container.querySelector('iframe')) {
return;
}
// Get configuration from parent container
const parentContainer = container.closest('.g-recaptcha-container');
if (!parentContainer) {
console.error('Cannot find parent container for #' + containerId);
return;
}
const sitekey = parentContainer.dataset.sitekey;
const theme = parentContainer.dataset.theme;
if (!sitekey) {
console.error('reCAPTCHA sitekey missing for #' + containerId);
return;
}
console.log('Attempting to render reCAPTCHA in #' + containerId);
if (typeof grecaptcha !== 'undefined' && typeof grecaptcha.render === 'function') {
try {
grecaptcha.render(containerId, {
sitekey: sitekey,
theme: theme,
callback: function(token) {
console.log('reCAPTCHA challenge successful for #' + containerId);
}
});
} catch (e) {
console.error('Error calling grecaptcha.render for #' + containerId, e);
container.innerHTML = '<p style="color:red;">Error initializing reCAPTCHA.</p>';
}
} else {
console.warn('grecaptcha API not available yet for #' + containerId + '. Waiting for onload.');
}
};
// Define the global onload callback
window['{{ onloadCallback }}'] = function() {
console.log('reCAPTCHA API loaded, triggering init for #{{ container_id }}');
if (window.GravExplicitCaptchaInitializers[initializerFunctionName]) {
window.GravExplicitCaptchaInitializers[initializerFunctionName]();
} else {
console.error("Initializer " + initializerFunctionName + " not found!");
}
};
// Form submit handler to check if captcha is completed
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('{{ formId }}');
if (form) {
form.addEventListener('submit', function(event) {
const response = grecaptcha.getResponse();
if (!response) {
event.preventDefault();
alert("{{ field.captcha_not_validated|t|default('Please complete the captcha')|e('js') }}");
} else if (form.dataset.xhrEnabled === 'true' && window.GravFormXHR && typeof window.GravFormXHR.submit === 'function') {
event.preventDefault();
window.GravFormXHR.submit(form);
}
});
}
});
})();
</script>
{% endif %}
{% endif %}
{% endblock %}