Adding Captcha On Guest and Login Page

RegistrationRecaptcha.GoogleRecaptcha.js

define('RegistrationRecaptcha.GoogleRecaptcha', [
    'jQuery',
    'underscore'
], function GoogleRecaptcha(
    jQuery,
    _
) {
    'use strict';

    let environmentGlobal;
    let containers = ['Recaptcha'];
    let recaptcha = {
        loadedPromise: jQuery.Deferred(),
        initialized: false,

        getModuleConfig: function getModuleConfig(environment) {
            return environmentGlobal.getConfig('recaptcha');
        },

        loadCaptcha: function loadCaptcha(options) {
            environmentGlobal = options.environment;
            if (options.containers) {
                containers = options.containers;
            }
            let url = this.getModuleConfig().apiUrl + '?onload=_loadCaptcha&render=explicit';

            window._loadCaptcha = function _loadCaptcha() {
                _.defer(function() {
                    _.each(containers, function (container) {
                        grecaptcha.render(container, {
                            sitekey: recaptcha.getModuleConfig().sitekey,
                            theme: recaptcha.getModuleConfig().theme
                        });
                    });
                });
            };

            if (SC.ENVIRONMENT.jsEnvironment === 'browser') {
                let promise = jQuery.ajax({
                    url: url,
                    dataType: 'script',
                    cache: true,
                    preventDefault: true
                }).done(function () {

                });
            } else {
                this.loadedPromise.rejectWith('Google ReCaptcha is a Browser Script only');
            }

            this.initialized = true;

            return this.loadedPromise;
        },

        getResponse: function getResponse(container) {
            return jQuery('#'+ container).find('.g-recaptcha-response').val();
        },

        resetCaptcha: function resetCaptcha() {
            grecaptcha.reset();
        }

    };

    return recaptcha;
});

RegistrationRecaptcha.js

define('RegistrationRecaptcha', [
    'RegistrationRecaptcha.View',
    'underscore'
], function RegistrationRecaptcha(
    GoogleRecaptchaView,
    _
) {
    'use strict';

    return {
        mountToApp: function mountToApp(application) {
            this.application = application;
            let layout = application.getComponent('Layout');
            this.environment = application.getComponent('Environment');
            this.loginRegister = application.getComponent('LoginRegisterPage');

            if (layout && this.loginRegister) {
                layout.on('afterShowContent', function() {
                    this.renderElements(application);
                }.bind(this));
            }
        },

        renderElements: function renderElements() {
            _.defer(function() {

                var containarry = ['RecaptchaRegister']
                if (jQuery('.login-register-checkout-as-guest-submit').length > 0) {
                    containarry.push('RecaptchaGuest')
                }


                let registerGuestRecaptcha = new GoogleRecaptchaView({
                    application: this.application,
                    environment: this.environment,
                    loginRegister: this.loginRegister,
                    container: 'RecaptchaGuest',
                    isGuest: true,
                    parent: this
                });
                let registerRecaptcha = new GoogleRecaptchaView({
                    application: this.application,
                    environment: this.environment,
                    loginRegister: this.loginRegister,
                    container: 'RecaptchaRegister',
                    containers: containarry,
                    triggerCaptcha: true,
                    parent: this
                });
                setTimeout(function() {
                    jQuery('.login-register-checkout-as-guest-submit').closest('.login-register-checkout-as-guest-control-group').before(
                        registerGuestRecaptcha.render().$el.html()
                    );
                    jQuery('.login-register-register-form-submit').closest('.login-register-register-form-controls-group').before(
                        registerRecaptcha.render().$el.html()
                    );
                }, 2000);

            }.bind(this));
        }
    };
});

RegistrationRecaptcha.View.js

define('RegistrationRecaptcha.View', [
    'Backbone',
    'RegistrationRecaptcha.GoogleRecaptcha',
    'RegistrationRecaptcha.Model',
    'registration_recaptcha.tpl',
    'underscore'
], function RegistrationRecaptchaView(
    Backbone,
    GoogleRecaptcha,
    Model,
    template,
    _
) {
    'use strict';

    return Backbone.View.extend({
        template: template,

        initialize: function initialize() {
            this.captchaModel = new Model();
            let self = this;

            this.on('afterViewRender', function () {
                if (this.options.triggerCaptcha) {
                    _.defer(function() {
                        let containerLength = 0;
                        _.each(self.options.containers, function (container) {
                            containerLength += jQuery('#' + container).length;
                        });
                        // dirty fix for recaptcha not loading containers
                        if (containerLength !== self.options.containers.length) {
                            setTimeout(function() {
                                self.options.parent.renderElements();
                            }, 2000);
                            return;
                        }
                        GoogleRecaptcha.loadCaptcha({
                            environment: self.options.environment,
                            container: self.options.container,
                            containers: self.options.containers
                        });
                    });
                }
            }.bind(this));

            if (this.options.isGuest) {
                jQuery('form.login-register-checkout-as-guest-form').on('submit', function (event) {
                    event.preventDefault();
                    let form = this;
                    let promise = self.validateCaptcha(GoogleRecaptcha.getResponse(self.options.container));
                    promise.done(function (data) {
                        if (data.success) {
                            self.resetMessage({
                                container: self.options.container
                            });
                            jQuery(form).unbind('submit').submit();
                            return true;
                        } else {
                            self.showMessage({
                                container: self.options.container,
                                type: 'error',
                                message: _('Invalid Captcha!').translate()
                            });
                            GoogleRecaptcha.resetCaptcha();
                            return false;
                        }
                    });
                    return false;
                });
            } else {
                this.options.loginRegister.cancelableOn('beforeRegister', function (formFields) {
                    if (!self.options.isGuest) {
                        let promise = self.validateCaptcha(GoogleRecaptcha.getResponse(self.options.container));
                        promise.done(function (data) {
                            if (data.success) {
                                self.resetMessage({
                                    container: self.options.container
                                });
                                return new jQuery.Deferred().resolve();
                            } else {
                                self.showMessage({
                                    container: self.options.container,
                                    type: 'error',
                                    message: _('Invalid Captcha!').translate()
                                });
                                GoogleRecaptcha.resetCaptcha();
                                return new jQuery.Deferred().reject();
                            }
                        });
                    }
                });

                // fallback once containers are reloaded
                jQuery('form.login-register-register-form').on('submit', function (event) {
                    event.preventDefault();
                    let form = this;
                    let promise = self.validateCaptcha(GoogleRecaptcha.getResponse(self.options.container));
                    promise.done(function (data) {
                        if (data.success) {
                            self.resetMessage({
                                container: self.options.container
                            });
                            jQuery(form).unbind('submit').submit();
                            return true;
                        } else {
                            self.showMessage({
                                container: self.options.container,
                                type: 'error',
                                message: _('Invalid Captcha!').translate()
                            });
                            GoogleRecaptcha.resetCaptcha();
                            return false;
                        }
                    });
                    return false;
                });
            }
        },

        validateCaptcha: function validateCaptcha(response) {
            // @todo this needs to be removed once on production
            let config = this.options.environment.getSiteSetting('siteid') + '|' +
                (SC.ENVIRONMENT.currentHostString !== '386782-sb2.secure.netsuite.com' ? SC.ENVIRONMENT.currentHostString
                    : 'dev-valleyvet.production.netsuitestaging.com');
            let promise = this.captchaModel.save({
                config: config,
                response: response
            });

            return promise;
        },

        showMessage: function showMessage(options) {
            let $selector = jQuery('#' + options.container).parent().find('.recaptcha-message');
            $selector
                .removeClass('success').removeClass('error')
                .addClass(options.type).html(options.message);
        },

        resetMessage: function resetMessage(options) {
            let $selector = jQuery('#' + options.container).parent().find('.recaptcha-message');
            $selector.removeClass('success').removeClass('error').html('');
        },

        getContext: function getContext() {
            return {
                container: this.options.container
            };
        }
    });
});

RegistrationRecaptcha.Model.js

/**
 * @NApiVersion 2.x
 * RegistrationRecaptcha.Model
 */
define(['N/search', 'N/https'], function (search, https) {
    'use strict';

    function validateCaptcha(options) {
        var siteConfiguration = getSiteConfiguration(options.config);

        if (!siteConfiguration) {
            return;
        }

        var configuration = JSON.parse(siteConfiguration.configuration);
        var recaptchaError = {
            status: 400,
            code: 'ERR_RECAPTCHA_INVALID',
            message: 'ReCaptcha is invalid'
        };
        var response = https.post({
            url: configuration.recaptcha.verifyUrl,
            body: {
                secret: configuration.recaptcha.serverkey,
                response: options.response
            }
        });

        var result = JSON.parse(response.body);

        if (result.success !== true) {
            return recaptchaError;
        }

        return result;
    }

    function getSiteConfiguration(config) {
        var configuration = [];
        var filters = [];
        var columns = [];

        try {
            filters.push(
                search.createFilter({
                    name: 'isinactive',
                    operator: search.Operator.IS,
                    values: false
                })
            );
            filters.push(
                search.createFilter({
                    name: 'custrecord_ns_scc_key',
                    operator: search.Operator.ANYOF,
                    values: config
                })
            );
            columns.push(
                search.createColumn({
                    name: 'custrecord_ns_scc_value'
                })
            );

            var selectionSearch = search.create({
                type: 'customrecord_ns_sc_configuration',
                columns: columns,
                filters: filters
            });

            selectionSearch.run().each(function(item) {
                configuration.push({
                    internalid: item.id,
                    configuration: item.getValue({ name: 'custrecord_ns_scc_value' })
                });

                return true;
            });
        } catch (e) {
            log.error({
                title: 'getSiteSettings:' + e.name,
                details: e.message
            });
        }

        return configuration.length > 0 ? configuration.pop() : null;
    }

    return {
        validateCaptcha: validateCaptcha
    };
});

RegistrationRecaptcha.Service.ss

/**
 * @NApiVersion 2.x
 * @NModuleScope Public
 */
define(['./RegistrationRecaptcha.Model'], function (Model) {
    'use strict';

    var methodNotAllowedError = {
        status: 405,
        code: 'ERR_METHOD_NOT_ALLOWED',
        message: 'Sorry, you are not allowed to perform this action.'
    };

    var internalError = {
        status: 500,
        code: 'INTERNAL_ERR',
        message: 'Sorry, there was an internal error, please try again later.'
    };

    return {
        service: function service(context) {
            var response = {};

            switch (context.request.method) {
                case 'POST':
                    var requestBody = JSON.parse(context.request.body);

                    response = Model.validateCaptcha({
                        response: requestBody.response || null,
                        config: requestBody.config || null
                    });
                    break;
                default:
                    response = methodNotAllowedError;
            }

            context.response.write(JSON.stringify(response));
        }
    };
});

Template

<div class="login-register-register-form-controls-group recaptcha-container">
    <div>
        <div id="{{container}}"></div>
        <br />
        <div><small class="recaptcha-message"></small></div>
    </div>
</div>

Leave a comment

Your email address will not be published. Required fields are marked *