UndefinedOffset / silverstripe-nocaptcha

A spam protector and form field using Google's reCAPTCHA v2 or optionally a foundation v3 implementation
BSD 3-Clause "New" or "Revised" License
31 stars 37 forks source link

Multiple form with nocaptcha (invisible) on one page #39

Closed goldfinch closed 5 years ago

goldfinch commented 7 years ago

Hi there,

Looking at NocaptchaField.ss and NocaptchaFields.js seems that it should work with multiple nocaptcha fields, in case when we have more than one form on the same page, but it doesn't. By pressing submit button (on both forms) and completing nocaptcha, the request is sending to the last loaded form on the page.

Any ideas?

goldfinch commented 6 years ago

Thanks, have found a way.

UndefinedOffset commented 6 years ago

It does sound like it maybe a bug in the module, do you mind elaborating on what you found as a work around?

goldfinch commented 6 years ago

Yep, sure. But it's more like a quick hack to fix this issue as I don't have much time at the moment. So, what I found is when you have two forms on the same page, no matter what form you are submitting, behind the scene it triggers the last loaded one

NocaptchaField.js

form.submit();

I presume that callback does not work properly.

So, basicly I just added event to submit button on each form to get the form id and rewrite wrong form.

var _noCaptchaFields=_noCaptchaFields || [];

// onclick event for submit button
window.catchNocaptcha = function(e){
    window.nocaptchaFormID = e.id.replace('_action_submit', '');
};

function noCaptchaFieldRender() {
    var submitListener=function(e) {
        e.preventDefault();

        let widgetID = e.target.querySelectorAll('.g-recaptcha')[0].getAttribute('data-widgetid');
        grecaptcha.execute( widgetID );
    };

    for(var i=0;i<_noCaptchaFields.length;i++) {
        var field=document.getElementById('Nocaptcha-'+_noCaptchaFields[i]);

        //For the invisible captcha we need to setup some callback listeners
        if(field.getAttribute('data-size')=='invisible' && field.getAttribute('data-callback')==null) {
            var form=document.getElementById(field.getAttribute('data-form'));
            var superHandler=false;

            if(typeof jQuery!='undefined' && typeof jQuery.fn.validate!='undefined') {
                var formValidator=jQuery(form).data('validator');
                var superHandler=formValidator.settings.submitHandler;
                formValidator.settings.submitHandler=function(form) {
                    grecaptcha.execute();
                };
            }else {
                if(form && form.addEventListener) {
                    form.addEventListener('submit', submitListener);
                }else if(form && form.attachEvent) {
                    window.attachEvent('onsubmit', submitListener);
                }else if(console.error) {
                    console.error('Could not attach event to the form');
                }
            }

            window['Nocaptcha-'+_noCaptchaFields[i]]=function() {
                if(typeof jQuery!='undefined' && typeof jQuery.fn.validate!='undefined' && superHandler) {
                    superHandler(form);
                }else {
                    var form=document.getElementById( window.nocaptchaFormID );
                    form.submit();
                }
            };
        }

        var options={
            'sitekey': field.getAttribute('data-sitekey'),
            'theme': field.getAttribute('data-theme'),
            'type': field.getAttribute('data-type'),
            'size': field.getAttribute('data-size'),
            'badge': field.getAttribute('data-badge'),
            'callback': (field.getAttribute('data-callback') ? field.getAttribute('data-callback') : 'Nocaptcha-'+_noCaptchaFields[i])
        };

        var widget_id = grecaptcha.render(field, options);
        field.setAttribute("data-widgetid", widget_id);
    }
}

Hope this help to fix it.

UndefinedOffset commented 6 years ago

Thanks I'm sure it will help, I'll look into a fix from my end when I'm back from vacation.

markhagemann commented 6 years ago

Any update on this? I believe I am encountering this issue at the moment with two of the same forms on one page. Thanks.

UndefinedOffset commented 6 years ago

@markhagemann one thought, make sure that you have a unique name for the captcha on both forms. I.e if it's named "Captcha" on both forms there will be a collision in the names on the front end which would cause issues.

markhagemann commented 6 years ago

Thanks @UndefinedOffset I have managed to resolve it by creating another form function in my controller with a different captcha name and another form template to use it.

christopherbolt commented 5 years ago

This issue still exists. It's a scope issue caused by rendering the fields in a loop resulting in the last field rendered to always be the one processed when any form submits. The solution is to break the rendering into a separate function and then just call that function in the loop. I will submit a pull request soon when I have a spare moment.

UndefinedOffset commented 5 years ago

Fixed in #54, tag coming next week

UndefinedOffset commented 5 years ago

2.0.5 has been tagged