Merge branch '11-block-posting-if-catpcha-isn-t-ok' into 'config'

Resolve "Block posting if catpcha isn't ok"

Closes #11

See merge request leftypol/leftypol!22
This commit is contained in:
Zankaria Auxa 2025-02-08 17:21:22 +00:00
commit ae6b9edf45
4 changed files with 57 additions and 15 deletions

View file

@ -1,6 +1,6 @@
{% if config.hcaptcha %} {% if config.captcha.mode == 'hcaptcha' %}
<script src="https://js.hcaptcha.com/1/api.js?recaptchacompat=off&render=explicit&onload=onCaptchaLoadHcaptcha" async defer></script> <script src="https://js.hcaptcha.com/1/api.js?recaptchacompat=off&render=explicit&onload=onCaptchaLoadHcaptcha" async defer></script>
{% endif %} {% endif %}
{% if config.turnstile %} {% if config.captcha.mode == 'turnstile' %}
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit&onload=onCaptchaLoadTurnstile_{{ form_action_type }}" async defer></script> <script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit&onload=onCaptchaLoadTurnstile_{{ form_action_type }}" async defer></script>
{% endif %} {% endif %}

View file

@ -28,6 +28,6 @@
<script type="text/javascript" src="/js/mod/mod_snippets.js?v={{ config.resource_version }}"></script> <script type="text/javascript" src="/js/mod/mod_snippets.js?v={{ config.resource_version }}"></script>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if config.recaptcha %} {% if config.captcha.mode == 'recaptcha' %}
<script src="//www.google.com/recaptcha/api.js"></script> <script src="//www.google.com/recaptcha/api.js"></script>
{% endif %} {% endif %}

View file

@ -243,27 +243,46 @@ function getCookie(cookie_name) {
{% endraw %} {% endraw %}
/* BEGIN CAPTCHA REGION */ /* BEGIN CAPTCHA REGION */
{% if config.hcaptcha or config.turnstile %} // If any captcha {% if config.captcha.mode == 'hcaptcha' or config.captcha.mode == 'turnstile' %} // If any captcha
// Global captcha object. Assigned by `onCaptchaLoad()`. // Global captcha object. Assigned by `onCaptchaLoad()`.
var captcha_renderer = null; var captcha_renderer = null;
// Captcha widget id of the post form.
var postCaptchaId = null;
{% if config.hcaptcha %} // If hcaptcha {% if config.captcha.mode == 'hcaptcha' %} // If hcaptcha
function onCaptchaLoadHcaptcha() { function onCaptchaLoadHcaptcha() {
if (captcha_renderer === null && (active_page === 'index' || active_page === 'catalog' || active_page === 'thread')) { if (captcha_renderer === null && (active_page === 'index' || active_page === 'catalog' || active_page === 'thread')) {
let renderer = { let renderer = {
/**
* @returns {object} Opaque widget id.
*/
applyOn: (container, params) => hcaptcha.render(container, { applyOn: (container, params) => hcaptcha.render(container, {
sitekey: "{{ config.hcaptcha_public }}", sitekey: "{{ config.captcha.hcaptcha.public }}",
callback: params['on-success'], callback: params['on-success'],
}), }),
/**
* @returns {void}
*/
remove: (widgetId) => { /* Not supported */ }, remove: (widgetId) => { /* Not supported */ },
reset: (widgetId) => hcaptcha.reset(widgetId) /**
* @returns {void}
*/
reset: (widgetId) => hcaptcha.reset(widgetId),
/**
* @returns {bool}
*/
hasResponse: (widgetId) => !!hcaptcha.getResponse(widgetId),
/**
* @returns {void}
*/
execute: (widgetId) => hcaptcha.execute(widgetId)
}; };
onCaptchaLoad(renderer); onCaptchaLoad(renderer);
} }
} }
{% endif %} // End if hcaptcha {% endif %} // End if hcaptcha
{% if config.turnstile %} // If turnstile {% if config.captcha.mode == 'turnstile' %} // If turnstile
// Wrapper function to be called from thread.html // Wrapper function to be called from thread.html
window.onCaptchaLoadTurnstile_post_reply = function() { window.onCaptchaLoadTurnstile_post_reply = function() {
@ -279,9 +298,12 @@ window.onCaptchaLoadTurnstile_post_thread = function() {
function onCaptchaLoadTurnstile(action) { function onCaptchaLoadTurnstile(action) {
if (captcha_renderer === null && (active_page === 'index' || active_page === 'catalog' || active_page === 'thread')) { if (captcha_renderer === null && (active_page === 'index' || active_page === 'catalog' || active_page === 'thread')) {
let renderer = { let renderer = {
/**
* @returns {object} Opaque widget id.
*/
applyOn: function(container, params) { applyOn: function(container, params) {
let widgetId = turnstile.render('#' + container, { let widgetId = turnstile.render('#' + container, {
sitekey: "{{ config.turnstile_public }}", sitekey: "{{ config.captcha.turnstile.public }}",
action: action, action: action,
callback: params['on-success'], callback: params['on-success'],
}); });
@ -290,8 +312,22 @@ function onCaptchaLoadTurnstile(action) {
} }
return widgetId; return widgetId;
}, },
/**
* @returns {void}
*/
remove: (widgetId) => turnstile.remove(widgetId), remove: (widgetId) => turnstile.remove(widgetId),
reset: (widgetId) => turnstile.reset(widgetId) /**
* @returns {void}
*/
reset: (widgetId) => turnstile.reset(widgetId),
/**
* @returns {bool}
*/
hasResponse: (widgetId) => !!turnstile.getResponse(widgetId),
/**
* @returns {void}
*/
execute: (widgetId) => turnstile.execute(widgetId)
}; };
onCaptchaLoad(renderer); onCaptchaLoad(renderer);
@ -313,6 +349,7 @@ function onCaptchaLoad(renderer) {
if (widgetId === null) { if (widgetId === null) {
console.error('Could not render captcha!'); console.error('Could not render captcha!');
} }
postCaptchaId = widgetId;
document.addEventListener('afterdopost', function(e) { document.addEventListener('afterdopost', function(e) {
// User posted! Reset the captcha. // User posted! Reset the captcha.
renderer.reset(widgetId); renderer.reset(widgetId);
@ -390,6 +427,11 @@ function doPost(form) {
saved[document.location] = form.elements['body'].value; saved[document.location] = form.elements['body'].value;
sessionStorage.body = JSON.stringify(saved); sessionStorage.body = JSON.stringify(saved);
if (captcha_renderer && postCaptchaId && !captcha_renderer.hasResponse(postCaptchaId)) {
captcha_renderer.execute(postCaptchaId);
return false;
}
// Needs to be delayed by at least 1 frame, otherwise it may reset the form (read captcha) fields before they're sent. // Needs to be delayed by at least 1 frame, otherwise it may reset the form (read captcha) fields before they're sent.
setTimeout(() => document.dispatchEvent(new Event('afterdopost'))); setTimeout(() => document.dispatchEvent(new Event('afterdopost')));
return form.elements['body'].value != "" || (form.elements['file'] && form.elements['file'].value != "") || (form.elements.file_url && form.elements['file_url'].value != ""); return form.elements['body'].value != "" || (form.elements['file'] && form.elements['file'].value != "") || (form.elements.file_url && form.elements['file_url'].value != "");

View file

@ -90,8 +90,8 @@
{% endif %} {% endif %}
</td> </td>
</tr>{% endif %} </tr>{% endif %}
{% if config.recaptcha %} {% if config.captcha.mode == 'recaptcha' %}
{% if config.dynamic_captcha %} {% if config.captcha.dynamic %}
<tr id="captcha" style="display: none;"> <tr id="captcha" style="display: none;">
{% else %} {% else %}
<tr> <tr>
@ -101,13 +101,13 @@
{{ antibot.html() }} {{ antibot.html() }}
</th> </th>
<td> <td>
<div class="g-recaptcha" data-sitekey="{{ config.recaptcha_public }}"></div> <div class="g-recaptcha" data-sitekey="{{ config.captcha.recaptcha.public }}"></div>
{{ antibot.html() }} {{ antibot.html() }}
</td> </td>
</tr> </tr>
{% endif %} {% endif %}
{% if config.hcaptcha or config.turnstile %} {% if config.captcha.mode == 'hcaptcha' or config.captcha.mode == 'turnstile' %}
{% if config.dynamic_captcha %} {% if config.captcha.dynamic %}
<tr id="captcha" style="display: none;"> <tr id="captcha" style="display: none;">
{% else %} {% else %}
<tr> <tr>