Add code to display the invisible ReCaptcha in the frontend
This commit adds the required code to display the invisible ReCaptcha in the frontend when a form is rendered and this type of captcha is selected in the admin. [MAILPOET-4145]
This commit is contained in:
@@ -77,6 +77,76 @@ jQuery(($) => {
|
||||
});
|
||||
}
|
||||
|
||||
function submitSubscribeForm(form, formData, parsley) {
|
||||
form.addClass('mailpoet_form_sending');
|
||||
// ajax request
|
||||
MailPoet.Ajax.post({
|
||||
url: window.MailPoetForm.ajax_url,
|
||||
token: formData.token,
|
||||
api_version: formData.api_version,
|
||||
endpoint: 'subscribers',
|
||||
action: 'subscribe',
|
||||
data: formData.data,
|
||||
})
|
||||
.fail((response) => {
|
||||
if (
|
||||
response.meta !== undefined &&
|
||||
response.meta.redirect_url !== undefined
|
||||
) {
|
||||
// go to page
|
||||
window.top.location.href = response.meta.redirect_url;
|
||||
} else {
|
||||
if (response.meta && response.meta.refresh_captcha) {
|
||||
updateCaptcha();
|
||||
}
|
||||
form
|
||||
.find('.mailpoet_validate_error')
|
||||
.html(response.errors.map((error) => error.message).join('<br />'))
|
||||
.show();
|
||||
}
|
||||
})
|
||||
.done((response) => {
|
||||
if (window.grecaptcha && formData.recaptcha) {
|
||||
window.grecaptcha.reset(formData.recaptcha);
|
||||
}
|
||||
return response;
|
||||
})
|
||||
.done((response) => {
|
||||
// successfully subscribed
|
||||
if (
|
||||
response.meta !== undefined &&
|
||||
response.meta.redirect_url !== undefined
|
||||
) {
|
||||
setFormCookieAfterSubscription(form);
|
||||
// go to page
|
||||
window.location.href = response.meta.redirect_url;
|
||||
} else {
|
||||
displaySuccessMessage(form);
|
||||
}
|
||||
|
||||
// reset form
|
||||
form.trigger('reset');
|
||||
// reset validation
|
||||
parsley.reset();
|
||||
// reset captcha
|
||||
if (window.grecaptcha && formData.recaptcha) {
|
||||
window.grecaptcha.reset(formData.recaptcha);
|
||||
}
|
||||
|
||||
// resize iframe
|
||||
if (
|
||||
window.frameElement !== null &&
|
||||
MailPoet !== undefined &&
|
||||
MailPoet.Iframe
|
||||
) {
|
||||
MailPoet.Iframe.autoSize(window.frameElement);
|
||||
}
|
||||
})
|
||||
.always(() => {
|
||||
form.removeClass('mailpoet_form_sending');
|
||||
});
|
||||
}
|
||||
|
||||
function renderCaptcha(element, iteration) {
|
||||
if (!window.recaptcha || !window.grecaptcha.ready) {
|
||||
if (iteration < 20) {
|
||||
@@ -85,12 +155,31 @@ jQuery(($) => {
|
||||
return;
|
||||
}
|
||||
const recaptcha = $(element);
|
||||
const form = $(recaptcha).parent('form');
|
||||
const sitekey = recaptcha.attr('data-sitekey');
|
||||
let size = recaptcha.attr('data-size');
|
||||
|
||||
// Users should not be able to change the size if it is equal to 'invisible' as this would
|
||||
// change the type of the ReCaptcha.
|
||||
if (size !== 'invisible') {
|
||||
size = Hooks.applyFilters('mailpoet_re_captcha_size', 'compact');
|
||||
}
|
||||
|
||||
const container = recaptcha.find('> .mailpoet_recaptcha_container').get(0);
|
||||
const field = recaptcha.find('> .mailpoet_recaptcha_field');
|
||||
if (sitekey) {
|
||||
const size = Hooks.applyFilters('mailpoet_re_captcha_size', 'compact');
|
||||
const widgetId = window.grecaptcha.render(container, { sitekey, size });
|
||||
const params = { sitekey, size };
|
||||
|
||||
if (size === 'invisible') {
|
||||
params.callback = function invisibleReCaptchaCallback(token) {
|
||||
const formData = form.mailpoetSerializeObject() || {};
|
||||
formData.data.recaptcha = token;
|
||||
|
||||
submitSubscribeForm(form, formData, form.parsley());
|
||||
};
|
||||
}
|
||||
|
||||
const widgetId = window.grecaptcha.render(container, params);
|
||||
field.val(widgetId);
|
||||
}
|
||||
}
|
||||
@@ -299,88 +388,36 @@ jQuery(($) => {
|
||||
}, 2500);
|
||||
return false;
|
||||
}
|
||||
const formData = form.mailpoetSerializeObject() || {};
|
||||
|
||||
// check if we're on the same domain
|
||||
if (isSameDomain(window.MailPoetForm.ajax_url) === false) {
|
||||
// non ajax post request
|
||||
return true;
|
||||
}
|
||||
|
||||
const formData = form.mailpoetSerializeObject() || {};
|
||||
const size = form.children('.mailpoet_recaptcha').attr('data-size');
|
||||
|
||||
if (window.grecaptcha && formData.recaptcha) {
|
||||
formData.data.recaptcha = window.grecaptcha.getResponse(
|
||||
formData.recaptcha,
|
||||
);
|
||||
// The API for the invisible and checkbox ReCaptchas is slightly different. For the
|
||||
// former, we need to call execute() and then the ReCaptcha API calls the callback set
|
||||
// inside renderCaptcha() with a token if the captcha was solved successfully. The
|
||||
// callback then calls submitSubscribeForm() with the token. For the latter, we get the
|
||||
// token here after calling getResponse() and then we can call submitSubscribeForm()
|
||||
// directly.
|
||||
if (size === 'invisible') {
|
||||
window.grecaptcha.execute();
|
||||
} else {
|
||||
formData.data.recaptcha = window.grecaptcha.getResponse(
|
||||
formData.recaptcha,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
form.addClass('mailpoet_form_sending');
|
||||
// ajax request
|
||||
MailPoet.Ajax.post({
|
||||
url: window.MailPoetForm.ajax_url,
|
||||
token: formData.token,
|
||||
api_version: formData.api_version,
|
||||
endpoint: 'subscribers',
|
||||
action: 'subscribe',
|
||||
data: formData.data,
|
||||
})
|
||||
.fail((response) => {
|
||||
if (
|
||||
response.meta !== undefined &&
|
||||
response.meta.redirect_url !== undefined
|
||||
) {
|
||||
// go to page
|
||||
window.top.location.href = response.meta.redirect_url;
|
||||
} else {
|
||||
if (response.meta && response.meta.refresh_captcha) {
|
||||
updateCaptcha();
|
||||
}
|
||||
form
|
||||
.find('.mailpoet_validate_error')
|
||||
.html(
|
||||
response.errors.map((error) => error.message).join('<br />'),
|
||||
)
|
||||
.show();
|
||||
}
|
||||
})
|
||||
.done((response) => {
|
||||
if (window.grecaptcha && formData.recaptcha) {
|
||||
window.grecaptcha.reset(formData.recaptcha);
|
||||
}
|
||||
return response;
|
||||
})
|
||||
.done((response) => {
|
||||
// successfully subscribed
|
||||
if (
|
||||
response.meta !== undefined &&
|
||||
response.meta.redirect_url !== undefined
|
||||
) {
|
||||
setFormCookieAfterSubscription(form);
|
||||
// go to page
|
||||
window.location.href = response.meta.redirect_url;
|
||||
} else {
|
||||
displaySuccessMessage(form);
|
||||
}
|
||||
if (size !== 'invisible') {
|
||||
submitSubscribeForm(form, formData, parsley);
|
||||
}
|
||||
|
||||
// reset form
|
||||
form.trigger('reset');
|
||||
// reset validation
|
||||
parsley.reset();
|
||||
// reset captcha
|
||||
if (window.grecaptcha && formData.recaptcha) {
|
||||
window.grecaptcha.reset(formData.recaptcha);
|
||||
}
|
||||
|
||||
// resize iframe
|
||||
if (
|
||||
window.frameElement !== null &&
|
||||
MailPoet !== undefined &&
|
||||
MailPoet.Iframe
|
||||
) {
|
||||
MailPoet.Iframe.autoSize(window.frameElement);
|
||||
}
|
||||
})
|
||||
.always(() => {
|
||||
form.removeClass('mailpoet_form_sending');
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
@@ -33,14 +33,14 @@ class AssetsController {
|
||||
public function printScripts() {
|
||||
ob_start();
|
||||
$captcha = $this->settings->get('captcha');
|
||||
if (!empty($captcha['type']) && $captcha['type'] === Captcha::TYPE_RECAPTCHA) {
|
||||
if (!empty($captcha['type']) && Captcha::isReCaptcha($captcha['type'])) {
|
||||
echo '<script src="' . self::RECAPTCHA_API_URL . '" async defer></script>';
|
||||
}
|
||||
|
||||
$this->wp->wpPrintScripts('jquery');
|
||||
$this->wp->wpPrintScripts('mailpoet_vendor');
|
||||
$this->wp->wpPrintScripts('mailpoet_public');
|
||||
|
||||
|
||||
$scripts = ob_get_contents();
|
||||
ob_end_clean();
|
||||
if ($scripts === false) {
|
||||
@@ -62,7 +62,7 @@ class AssetsController {
|
||||
|
||||
public function setupFrontEndDependencies() {
|
||||
$captcha = $this->settings->get('captcha');
|
||||
if (!empty($captcha['type']) && $captcha['type'] === Captcha::TYPE_RECAPTCHA) {
|
||||
if (!empty($captcha['type']) && Captcha::isRecaptcha($captcha['type'])) {
|
||||
$this->wp->wpEnqueueScript(
|
||||
'mailpoet_recaptcha',
|
||||
self::RECAPTCHA_API_URL
|
||||
|
@@ -70,7 +70,7 @@ class Renderer {
|
||||
if (
|
||||
$captchaEnabled
|
||||
&& $block['type'] === FormEntity::SUBMIT_BLOCK_TYPE
|
||||
&& $this->settings->get('captcha.type') === Captcha::TYPE_RECAPTCHA
|
||||
&& Captcha::isRecaptcha($this->settings->get('captcha.type'))
|
||||
) {
|
||||
$html .= $this->renderReCaptcha();
|
||||
}
|
||||
@@ -89,8 +89,15 @@ class Renderer {
|
||||
}
|
||||
|
||||
private function renderReCaptcha(): string {
|
||||
$siteKey = $this->settings->get('captcha.recaptcha_site_token');
|
||||
return '<div class="mailpoet_recaptcha" data-sitekey="' . $siteKey . '">
|
||||
if ($this->settings->get('captcha.type') === Captcha::TYPE_RECAPTCHA) {
|
||||
$siteKey = $this->settings->get('captcha.recaptcha_site_token');
|
||||
$size = '';
|
||||
} else {
|
||||
$siteKey = $this->settings->get('captcha.recaptcha_invisible_site_token');
|
||||
$size = 'invisible';
|
||||
}
|
||||
|
||||
$html = '<div class="mailpoet_recaptcha" data-sitekey="' . $siteKey . '" ' . ($size === 'invisible' ? 'data-size="invisible"' : '') . '>
|
||||
<div class="mailpoet_recaptcha_container"></div>
|
||||
<noscript>
|
||||
<div>
|
||||
@@ -108,5 +115,7 @@ class Renderer {
|
||||
</noscript>
|
||||
<input class="mailpoet_recaptcha_field" type="hidden" name="recaptcha">
|
||||
</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
|
@@ -216,15 +216,21 @@ class SubscriberSubscribeController {
|
||||
}
|
||||
}
|
||||
|
||||
if ($captchaSettings['type'] === Captcha::TYPE_RECAPTCHA && empty($data['recaptcha'])) {
|
||||
if (Captcha::isReCaptcha($captchaSettings['type']) && empty($data['recaptcha'])) {
|
||||
return ['error' => __('Please check the CAPTCHA.', 'mailpoet')];
|
||||
}
|
||||
|
||||
if ($captchaSettings['type'] === Captcha::TYPE_RECAPTCHA) {
|
||||
if (Captcha::isReCaptcha($captchaSettings['type'])) {
|
||||
if ($captchaSettings['type'] === Captcha::TYPE_RECAPTCHA_INVISIBLE) {
|
||||
$secretToken = $captchaSettings['recaptcha_invisible_secret_token'];
|
||||
} else {
|
||||
$secretToken = $captchaSettings['recaptcha_secret_token'];
|
||||
}
|
||||
|
||||
$response = empty($data['recaptcha']) ? $data['recaptcha-no-js'] : $data['recaptcha'];
|
||||
$response = $this->wp->wpRemotePost('https://www.google.com/recaptcha/api/siteverify', [
|
||||
'body' => [
|
||||
'secret' => $captchaSettings['recaptcha_secret_token'],
|
||||
'secret' => $secretToken,
|
||||
'response' => $response,
|
||||
],
|
||||
]);
|
||||
|
@@ -11,6 +11,7 @@ use MailPoetVendor\Gregwar\Captcha\CaptchaBuilder;
|
||||
class Captcha {
|
||||
const TYPE_BUILTIN = 'built-in';
|
||||
const TYPE_RECAPTCHA = 'recaptcha';
|
||||
const TYPE_RECAPTCHA_INVISIBLE = 'recaptcha-invisible';
|
||||
const TYPE_DISABLED = null;
|
||||
|
||||
/** @var WPFunctions */
|
||||
@@ -22,6 +23,10 @@ class Captcha {
|
||||
/** @var SubscriberIPsRepository */
|
||||
private $subscriberIPsRepository;
|
||||
|
||||
public static function isReCaptcha(?string $captchaType) {
|
||||
return in_array($captchaType, [self::TYPE_RECAPTCHA, self::TYPE_RECAPTCHA_INVISIBLE]);
|
||||
}
|
||||
|
||||
public function __construct(
|
||||
SubscriberIPsRepository $subscriberIPsRepository,
|
||||
WPFunctions $wp = null,
|
||||
|
@@ -697,6 +697,18 @@ class SubscribersTest extends \MailPoetTest {
|
||||
$this->settings->set('captcha', []);
|
||||
}
|
||||
|
||||
public function testItCannotSubscribeWithoutInvisibleReCaptchaWhenEnabled() {
|
||||
$this->settings->set('captcha', ['type' => Captcha::TYPE_RECAPTCHA_INVISIBLE]);
|
||||
$response = $this->endpoint->subscribe([
|
||||
$this->obfuscatedEmail => 'toto@mailpoet.com',
|
||||
'form_id' => $this->form->getId(),
|
||||
$this->obfuscatedSegments => [$this->segment1->getId(), $this->segment2->getId()],
|
||||
]);
|
||||
expect($response->status)->equals(APIResponse::STATUS_BAD_REQUEST);
|
||||
expect($response->errors[0]['message'])->equals('Please check the CAPTCHA.');
|
||||
$this->settings->set('captcha', []);
|
||||
}
|
||||
|
||||
public function testItCannotSubscribeWithoutBuiltInCaptchaWhenEnabled() {
|
||||
$this->settings->set('captcha', ['type' => Captcha::TYPE_BUILTIN]);
|
||||
$email = 'toto@mailpoet.com';
|
||||
|
Reference in New Issue
Block a user