module Recaptcha::Helpers
Constants
- DEFAULT_ERRORS
Public Class Methods
Source
# File lib/recaptcha/helpers.rb, line 125 def self.components(options) html = +'' attributes = {} fallback_uri = +'' options = options.dup env = options.delete(:env) class_attribute = options.delete(:class) site_key = options.delete(:site_key) hl = options.delete(:hl) onload = options.delete(:onload) render = options.delete(:render) script_async = options.delete(:script_async) script_defer = options.delete(:script_defer) nonce = options.delete(:nonce) skip_script = (options.delete(:script) == false) || (options.delete(:external_script) == false) ui = options.delete(:ui) options.delete(:ignore_no_element) options.delete(:inline_script) data_attribute_keys = [:badge, :theme, :type, :callback, :expired_callback, :error_callback, :size] data_attribute_keys << :tabindex unless ui == :button data_attributes = {} data_attribute_keys.each do |data_attribute| value = options.delete(data_attribute) data_attributes["data-#{data_attribute.to_s.tr('_', '-')}"] = value if value end unless Recaptcha.skip_env?(env) site_key ||= Recaptcha.configuration.site_key! script_url = Recaptcha.configuration.api_server_url query_params = hash_to_query( hl: hl, onload: onload, render: render ) script_url += "?#{query_params}" unless query_params.empty? async_attr = "async" if script_async != false defer_attr = "defer" if script_defer != false nonce_attr = " nonce='#{nonce}'" if nonce html << %(<script src="#{script_url}" #{async_attr} #{defer_attr} #{nonce_attr}></script>\n) unless skip_script fallback_uri = %(#{script_url.chomp(".js")}/fallback?k=#{site_key}) attributes["data-sitekey"] = site_key attributes.merge! data_attributes end # The remaining options will be added as attributes on the tag. attributes["class"] = "g-recaptcha #{class_attribute}" tag_attributes = attributes.merge(options).map { |k, v| %(#{k}="#{v}") }.join(" ") [html, tag_attributes, fallback_uri] end
Source
# File lib/recaptcha/helpers.rb, line 327 def self.dasherize_action(action) action.to_s.gsub(/\W/, '-').tr('_', '-') end
Returns a dasherized string that is safe for use as an HTML ID dasherize_action
(‘my/action’) => ‘my-action’
Source
# File lib/recaptcha/helpers.rb, line 276 def self.default_callback(options = {}) nonce = options[:nonce] nonce_attr = " nonce='#{nonce}'" if nonce selector_attr = options[:id] ? "##{options[:id]}" : ".g-recaptcha" <<-HTML <script#{nonce_attr}> var invisibleRecaptchaSubmit = function () { var closestForm = function (ele) { var curEle = ele.parentNode; while (curEle.nodeName !== 'FORM' && curEle.nodeName !== 'BODY'){ curEle = curEle.parentNode; } return curEle.nodeName === 'FORM' ? curEle : null }; var el = document.querySelector("#{selector_attr}") if (!!el) { var form = closestForm(el); if (form) { form.submit(); } } }; </script> HTML end
v2
Source
# File lib/recaptcha/helpers.rb, line 312 def self.default_callback_required?(options) options[:callback] == 'invisibleRecaptchaSubmit' && !Recaptcha.skip_env?(options[:env]) && options[:script] != false && options[:inline_script] != false end
Source
# File lib/recaptcha/helpers.rb, line 335 def self.element_check_condition(options) options[:ignore_no_element] ? "if (element !== null)" : "" end
Source
# File lib/recaptcha/helpers.rb, line 331 def self.hash_to_query(hash) hash.delete_if { |_, val| val.nil? || val.empty? }.to_a.map { |pair| pair.join('=') }.join('&') end
Source
# File lib/recaptcha/helpers.rb, line 304 def self.recaptcha_execute_method_name Recaptcha.configuration.enterprise ? "grecaptcha.enterprise.execute" : "grecaptcha.execute" end
Source
# File lib/recaptcha/helpers.rb, line 308 def self.recaptcha_ready_method_name Recaptcha.configuration.enterprise ? "grecaptcha.enterprise.ready" : "grecaptcha.ready" end
Source
# File lib/recaptcha/helpers.rb, line 10 def self.recaptcha_v3(options = {}) site_key = options[:site_key] ||= Recaptcha.configuration.site_key! action = options.delete(:action) || raise(Recaptcha::RecaptchaError, 'action is required') id = options.delete(:id) || "g-recaptcha-response-data-#{dasherize_action(action)}" name = options.delete(:name) || "g-recaptcha-response-data[#{action}]" turbo = options.delete(:turbo) || options.delete(:turbolinks) options[:render] = site_key options[:script_async] ||= false options[:script_defer] ||= false options[:ignore_no_element] = options.key?(:ignore_no_element) ? options[:ignore_no_element] : true element = options.delete(:element) element = element == false ? false : :input if element == :input callback = options.delete(:callback) || recaptcha_v3_default_callback_name(action) end options[:class] = "g-recaptcha-response #{options[:class]}" if turbo options[:onload] = recaptcha_v3_execute_function_name(action) end html, tag_attributes = components(options) if turbo html << recaptcha_v3_onload_script(site_key, action, callback, id, options) elsif recaptcha_v3_inline_script?(options) html << recaptcha_v3_inline_script(site_key, action, callback, id, options) end case element when :input html << %(<input type="hidden" name="#{name}" id="#{id}" #{tag_attributes}/>\n) when false # No tag nil else raise(RecaptchaError, "ReCAPTCHA element `#{options[:element]}` is not valid.") end html.respond_to?(:html_safe) ? html.html_safe : html end
Source
# File lib/recaptcha/helpers.rb, line 266 def self.recaptcha_v3_async_execute_function_name(action) "#{recaptcha_v3_execute_function_name(action)}Async" end
Returns the name of an async JavaScript function that executes the reCAPTCHA code.
Source
# File lib/recaptcha/helpers.rb, line 270 def self.recaptcha_v3_default_callback_name(action) "setInputWithRecaptchaResponseTokenFor#{sanitize_action_for_js(action)}" end
Source
# File lib/recaptcha/helpers.rb, line 241 def self.recaptcha_v3_define_default_callback(callback, options) <<-HTML var #{callback} = function(id, token) { var element = document.getElementById(id); #{element_check_condition(options)} element.value = token; } HTML end
Source
# File lib/recaptcha/helpers.rb, line 253 def self.recaptcha_v3_define_default_callback?(callback, action, options) callback == recaptcha_v3_default_callback_name(action) && recaptcha_v3_inline_script?(options) end
Returns true if we should be adding the default callback. That is, if the given callback name is the default callback name (for the given action) and we are not skipping inline scripts for any reason.
Source
# File lib/recaptcha/helpers.rb, line 261 def self.recaptcha_v3_execute_function_name(action) "executeRecaptchaFor#{sanitize_action_for_js(action)}" end
Returns the name of the JavaScript function that actually executes the reCAPTCHA code (calls ‘grecaptcha.execute` or `grecaptcha.enterprise.execute`). You can call it again later to reset it.
Source
# File lib/recaptcha/helpers.rb, line 183 def self.recaptcha_v3_inline_script(site_key, action, callback, id, options = {}) nonce = options[:nonce] nonce_attr = " nonce='#{nonce}'" if nonce <<-HTML <script#{nonce_attr}> // Define function so that we can call it again later if we need to reset it // This executes reCAPTCHA and then calls our callback. function #{recaptcha_v3_execute_function_name(action)}() { #{recaptcha_ready_method_name}(function() { #{recaptcha_execute_method_name}('#{site_key}', {action: '#{action}'}).then(function(token) { #{callback}('#{id}', token) }); }); }; // Invoke immediately #{recaptcha_v3_execute_function_name(action)}() // Async variant so you can await this function from another async function (no need for // an explicit callback function then!) // Returns a Promise that resolves with the response token. async function #{recaptcha_v3_async_execute_function_name(action)}() { return new Promise((resolve, reject) => { #{recaptcha_ready_method_name}(async function() { resolve(await #{recaptcha_execute_method_name}('#{site_key}', {action: '#{action}'})) }); }) }; #{recaptcha_v3_define_default_callback(callback, options) if recaptcha_v3_define_default_callback?(callback, action, options)} </script> HTML end
Renders a script that calls ‘grecaptcha.execute` or `grecaptcha.enterprise.execute` for the given `site_key` and `action` and calls the `callback` with the resulting response token.
Source
# File lib/recaptcha/helpers.rb, line 235 def self.recaptcha_v3_inline_script?(options) !Recaptcha.skip_env?(options[:env]) && options[:script] != false && options[:inline_script] != false end
Source
# File lib/recaptcha/helpers.rb, line 217 def self.recaptcha_v3_onload_script(site_key, action, callback, id, options = {}) nonce = options[:nonce] nonce_attr = " nonce='#{nonce}'" if nonce <<-HTML <script#{nonce_attr}> function #{recaptcha_v3_execute_function_name(action)}() { #{recaptcha_ready_method_name}(function() { #{recaptcha_execute_method_name}('#{site_key}', {action: '#{action}'}).then(function(token) { #{callback}('#{id}', token) }); }); }; #{recaptcha_v3_define_default_callback(callback, options) if recaptcha_v3_define_default_callback?(callback, action, options)} </script> HTML end
Source
# File lib/recaptcha/helpers.rb, line 321 def self.sanitize_action_for_js(action) action.to_s.gsub(/\W/, '_').split(/\/|_/).map(&:capitalize).join end
Returns a camelized string that is safe for use in a JavaScript variable/function name. sanitize_action_for_js
(‘my/action’) => ‘MyAction’
Source
# File lib/recaptcha/helpers.rb, line 110 def self.to_error_message(key) default = DEFAULT_ERRORS.fetch(key) { raise ArgumentError "Unknown reCAPTCHA error - #{key}" } to_message("recaptcha.errors.#{key}", default) end
Source
# File lib/recaptcha/helpers.rb, line 116 def self.to_message(key, default) I18n.translate(key, default: default) end