ARCANEDEV / noCAPTCHA

:passport_control: Helper for Google's new noCAPTCHA (reCAPTCHA v2 & v3)
MIT License
358 stars 56 forks source link

How to reset multiple captchas? #74

Closed madaolex closed 6 years ago

madaolex commented 6 years ago

Hi! How can I reset captcha by opt_widget_id (as they say on google captcha docs)?

When I try to reset captcha by attribute 'id' in div with 'g-recaptcha' class, it does not work. On StackOverflow there is a answer with that solution:

var widgetId = grecaptcha.render(container);
grecaptcha.reset(widgetId);

but in \Arcanedev\NoCaptcha\NoCaptcha::renderCaptchas function you do render captchas without saving widget ID to anywhere

return implode(PHP_EOL, array_map(function($captcha) {
    return "if (document.getElementById('$captcha')) { grecaptcha.render('$captcha', {'sitekey' : '{$this->siteKey}'}); }";
}, $captchas));

Maybe there is some function to get widgetID?

madaolex commented 6 years ago

Well I can save widgetID to variable in this function, but it works only if "-" symbol replace to "_".

private function renderCaptchas(array $captchas)
{
    return implode(PHP_EOL, array_map(function($captcha) {
        return "if (document.getElementById('$captcha')) { ".str_replace('-', '_', $captcha)." = grecaptcha.render('$captcha', {'sitekey' : '{$this->siteKey}'}); }";
    }, $captchas));
}

and then use that variable in js like this

if (typeof grecaptcha !== 'undefined') {
    var id = $('.g-recaptcha').attr('id');
    grecaptcha.reset(eval(id.replace('-', '_')));
}

It works, but maybe there is some out-of-box function?

arcanedev-maroc commented 6 years ago

I'm working on it :+1:

arcanedev-maroc commented 6 years ago

The following PR allows you to access the rendered captchas by using the window.noCaptcha.renderedCaptchas (global) variable.

Try to make the same changes as this class and tell me if it fixes your issue!

Example:

<!--Other scripts-->
<script>
function resetAllCaptchas() {
    window.noCaptcha.renderedCaptchas.forEach(function (captcha) {
        grecaptcha.reset(captcha.id);
    });
}
</script>
madaolex commented 6 years ago

Thank you, it worked!

But may I ask you, maybe you can add captchas in array like this?

private function renderCaptchas(array $captchas)
   {
        return implode(PHP_EOL, array_map(function($captcha) {
            return "if (document.getElementById('{$captcha}')) { ".
                "window.noCaptcha.renderedCaptchas['{$captcha}'] = ".
                "grecaptcha.render('{$captcha}', {'sitekey' : '{$this->siteKey}'});".
                " }";
        }, $captchas));
    }

Now we can apply to id of captcha using name as a key, no need to foreach whole array if we need to reset just one captcha.

grecaptcha.reset(window.noCaptcha.renderedCaptchas['your-captcha-name-here'])

It is necessary to check keys in array before reset or it can crash, though. I just want to know your opinion, because I do not have much experience.

arcanedev-maroc commented 6 years ago

You can use this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find

But I think I can extend the window.noCaptcha functionalities. Stay tuned

arcanedev-maroc commented 6 years ago

I've added a new JS API to manage the captchas with ease:


// Get all the rendered captchas collection
window.noCaptcha.captchas

// Reset a captcha by name
window.noCaptcha.reset(name)

// Reset a captcha by id
window.noCaptcha.resetById(id)

// Get a captcha object by name
window.noCaptcha.get(name)

// Get a captcha object by id
window.noCaptcha.getById(id)

// Find a captcha object in the collection
window.noCaptcha.find(callback)

// Render a captcha
window.noCaptcha.render(name, sitekey)