recaptcha 0.3.6 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +8 -0
- data/README.md +295 -0
- data/lib/recaptcha.rb +19 -7
- data/lib/recaptcha/client_helper.rb +68 -6
- data/lib/recaptcha/configuration.rb +23 -7
- data/lib/recaptcha/rails.rb +10 -2
- data/lib/recaptcha/token.rb +24 -0
- data/lib/recaptcha/verify.rb +72 -31
- data/lib/recaptcha/version.rb +1 -1
- metadata +77 -30
- data/.gitignore +0 -3
- data/Gemfile +0 -3
- data/README.rdoc +0 -146
- data/Rakefile +0 -9
- data/init.rb +0 -5
- data/lib/recaptcha/merb.rb +0 -4
- data/lib/recaptcha/railtie.rb +0 -15
- data/recaptcha.gemspec +0 -24
- data/test/recaptcha_test.rb +0 -62
- data/test/verify_recaptcha_test.rb +0 -151
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 15a8d9b4ffc50fb479df2f03065310e3b85be5f4
|
4
|
+
data.tar.gz: 1d2412b3ce72619d131388e77e9b82a4b1e9de46
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f1a53822196216dc231d60369edcce9719e724e4e66a35b57e35b7b57ca8104b2d5211a07231616fad499c13420daa3eb584b5337e87dd47631d40e61d801721
|
7
|
+
data.tar.gz: e3fef919669e86e48de71216c3def51609e1d525d1fe7cebc5ada112f5f9613d96783625c0d675cddfeafce97d7783006665371db42be17f027248945784ebc6
|
data/CHANGELOG
CHANGED
data/README.md
ADDED
@@ -0,0 +1,295 @@
|
|
1
|
+
# reCAPTCHA
|
2
|
+
|
3
|
+
Author: Jason L Perry (http://ambethia.com)<br/>
|
4
|
+
Copyright: Copyright (c) 2007-2013 Jason L Perry<br/>
|
5
|
+
License: [MIT](http://creativecommons.org/licenses/MIT/)<br/>
|
6
|
+
Info: https://github.com/ambethia/recaptcha<br/>
|
7
|
+
Bugs: https://github.com/ambethia/recaptcha/issues<br/>
|
8
|
+
|
9
|
+
This plugin adds helpers for the [reCAPTCHA API](https://www.google.com/recaptcha). In your
|
10
|
+
views you can use the `recaptcha_tags` method to embed the needed javascript,
|
11
|
+
and you can validate in your controllers with `verify_recaptcha` or `verify_recaptcha!`,
|
12
|
+
which throws an error on failiure.
|
13
|
+
|
14
|
+
Beforehand you need to configure Recaptcha with your custom private and public
|
15
|
+
key. You may find detailed examples below. Exceptions will be raised if you
|
16
|
+
call these methods and the keys can't be found.
|
17
|
+
|
18
|
+
## Rails Installation
|
19
|
+
|
20
|
+
```Ruby
|
21
|
+
gem "recaptcha", require: "recaptcha/rails"
|
22
|
+
```
|
23
|
+
|
24
|
+
(Rails < 3.0: install an older release and view it's README)
|
25
|
+
|
26
|
+
## Setting up your API Keys
|
27
|
+
|
28
|
+
There are multiple ways to setup your reCAPTCHA API key once you
|
29
|
+
[obtain a pair](https://www.google.com/recaptcha/admin).
|
30
|
+
|
31
|
+
### Recaptcha.configure
|
32
|
+
|
33
|
+
You may use the block style configuration. The following code could be placed
|
34
|
+
into a `config/initializers/recaptcha.rb` when used in a Rails project.
|
35
|
+
|
36
|
+
```Ruby
|
37
|
+
Recaptcha.configure do |config|
|
38
|
+
config.public_key = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
|
39
|
+
config.private_key = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
|
40
|
+
# Uncomment the following line if you are using a proxy server:
|
41
|
+
# config.proxy = 'http://myproxy.com.au:8080'
|
42
|
+
# Uncomment if you want to use the older version of the API,
|
43
|
+
# only works for versions >= 0.3.7, default value is 'v2':
|
44
|
+
# config.api_version = 'v1'
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
This way, you may also set additional options to fit recaptcha into your
|
49
|
+
deployment environment.
|
50
|
+
|
51
|
+
### Recaptcha.with_configuration
|
52
|
+
|
53
|
+
If you want to temporarily overwrite the configuration you set with
|
54
|
+
`Recaptcha.configure` (when testing, for example), you can use a
|
55
|
+
`Recaptcha#with_configuration` block:
|
56
|
+
|
57
|
+
```Ruby
|
58
|
+
Recaptcha.with_configuration(public_key: '12345') do
|
59
|
+
# Do stuff with the overwritten public_key.
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
### Heroku & Shell environment
|
64
|
+
|
65
|
+
Or, you can keep your keys out of your code base by exporting the following
|
66
|
+
environment variables. You might do this in the .profile/rc, or equivalent for
|
67
|
+
the user running your application. This would also be the preffered method
|
68
|
+
in an Heroku deployment.
|
69
|
+
|
70
|
+
```
|
71
|
+
export RECAPTCHA_PUBLIC_KEY = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
|
72
|
+
export RECAPTCHA_PRIVATE_KEY = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
|
73
|
+
```
|
74
|
+
|
75
|
+
### Per call
|
76
|
+
|
77
|
+
You can also pass in your keys as options at runtime, for example:
|
78
|
+
|
79
|
+
```Ruby
|
80
|
+
recaptcha_tags public_key: '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
|
81
|
+
```
|
82
|
+
|
83
|
+
and later,
|
84
|
+
|
85
|
+
```Ruby
|
86
|
+
verify_recaptcha private_key: '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
|
87
|
+
```
|
88
|
+
|
89
|
+
This option might be useful, if the same code base is used for multiple
|
90
|
+
reCAPTCHA setups.
|
91
|
+
|
92
|
+
## To use 'recaptcha'
|
93
|
+
|
94
|
+
Add `recaptcha_tags` to each form you want to protect.
|
95
|
+
Place it where you want the recaptcha widget to appear.
|
96
|
+
|
97
|
+
Example:
|
98
|
+
|
99
|
+
```Erb
|
100
|
+
<%= form_for @foo do |f| %>
|
101
|
+
# ... additional lines truncated for brevity ...
|
102
|
+
<%= recaptcha_tags %>
|
103
|
+
# ... additional lines truncated for brevity ...
|
104
|
+
<% end %>
|
105
|
+
```
|
106
|
+
|
107
|
+
And, add `verify_recaptcha` logic to each form action that you've protected.
|
108
|
+
|
109
|
+
### recaptcha_tags
|
110
|
+
|
111
|
+
Some of the options available:
|
112
|
+
|
113
|
+
| Option | Description |
|
114
|
+
|-------------|-------------|
|
115
|
+
| :ssl | Uses secure http for captcha widget (default `false`, but can be changed by setting `config.use_ssl_by_default`)|
|
116
|
+
| :noscript | Include <noscript> content (default `true`)|
|
117
|
+
| :display | Takes a hash containing the `theme` and `tabindex` options per the API. (default `nil`), options: 'red', 'white', 'blackglass', 'clean', 'custom'|
|
118
|
+
| :ajax | Render the dynamic AJAX captcha per the API. (default `false`)|
|
119
|
+
| :public_key | Your public API key, takes precedence over the ENV variable (default `nil`)|
|
120
|
+
| :error | Override the error code returned from the reCAPTCHA API (default `nil`)|
|
121
|
+
| :stoken | Include in reCAPTCHA API v2 the security token (default `true`)|
|
122
|
+
| :size | Specify a size (default `nil`)|
|
123
|
+
|
124
|
+
|
125
|
+
You can also override the html attributes for the sizes of the generated `textarea` and `iframe`
|
126
|
+
elements, if CSS isn't your thing. Inspect the source of `recaptcha_tags` to see these options.
|
127
|
+
|
128
|
+
### verify_recaptcha
|
129
|
+
|
130
|
+
This method returns `true` or `false` after processing the parameters from the reCAPTCHA widget. Why
|
131
|
+
isn't this a model validation? Because that violates MVC. You can use it like this, or how ever you
|
132
|
+
like. Passing in the ActiveRecord object is optional, if you do--and the captcha fails to verify--an
|
133
|
+
error will be added to the object for you to use.
|
134
|
+
|
135
|
+
Some of the options available:
|
136
|
+
|
137
|
+
| Option | Description |
|
138
|
+
|--------------|-------------|
|
139
|
+
| :model | Model to set errors
|
140
|
+
| :attribute | Model attribute to receive errors (default :base)
|
141
|
+
| :message | Custom error message
|
142
|
+
| :private_key | Your private API key, takes precedence over the ENV variable (default `nil`).
|
143
|
+
| :timeout | The number of seconds to wait for reCAPTCHA servers before give up. (default `3`)
|
144
|
+
|
145
|
+
```Ruby
|
146
|
+
respond_to do |format|
|
147
|
+
if verify_recaptcha(model @post, message: "Oh! It's error with reCAPTCHA!") && @post.save
|
148
|
+
# ...
|
149
|
+
else
|
150
|
+
# ...
|
151
|
+
end
|
152
|
+
end
|
153
|
+
```
|
154
|
+
|
155
|
+
### Add multiple widgets to the same page
|
156
|
+
|
157
|
+
This is an example taken from the [official google documentation](https://developers.google.com/recaptcha/docs/display).
|
158
|
+
|
159
|
+
Add a script tag for a callback
|
160
|
+
|
161
|
+
```Html
|
162
|
+
<script type="text/javascript">
|
163
|
+
var verifyCallback = function(response) {
|
164
|
+
alert(response);
|
165
|
+
};
|
166
|
+
var widgetId1;
|
167
|
+
var widgetId2;
|
168
|
+
var onloadCallback = function() {
|
169
|
+
// Renders the HTML element with id 'example1' as a reCAPTCHA widget.
|
170
|
+
// The id of the reCAPTCHA widget is assigned to 'widgetId1'.
|
171
|
+
widgetId1 = grecaptcha.render('example1', {
|
172
|
+
'sitekey' : "<%= Recaptcha.configuration.public_key %>",
|
173
|
+
'theme' : 'light'
|
174
|
+
});
|
175
|
+
widgetId2 = grecaptcha.render(document.getElementById('example2'), {
|
176
|
+
'sitekey' : "<%= Recaptcha.configuration.public_key %>"
|
177
|
+
});
|
178
|
+
grecaptcha.render('example3', {
|
179
|
+
'sitekey' : "<%= Recaptcha.configuration.public_key %>",
|
180
|
+
'callback' : verifyCallback,
|
181
|
+
'theme' : 'dark'
|
182
|
+
});
|
183
|
+
};
|
184
|
+
</script>
|
185
|
+
```
|
186
|
+
|
187
|
+
In the callback you will have the `sitekey` generated by this gem with `<%= Recaptcha.configuration.public_key %>`
|
188
|
+
|
189
|
+
Next you need to have some elements with an id matching those specified in the callback
|
190
|
+
|
191
|
+
```Erb
|
192
|
+
<%= form_for @foo do |f| %>
|
193
|
+
# ... additional lines truncated for brevity ...
|
194
|
+
<div id="example1"></div>
|
195
|
+
# ... additional lines truncated for brevity ...
|
196
|
+
<% end %>
|
197
|
+
<%= form_for @foo2 do |f| %>
|
198
|
+
# ... additional lines truncated for brevity ...
|
199
|
+
<div id="example2"></div>
|
200
|
+
# ... additional lines truncated for brevity ...
|
201
|
+
<% end %>
|
202
|
+
<%= form_for @foo3 do |f| %>
|
203
|
+
# ... additional lines truncated for brevity ...
|
204
|
+
<div id="example3"></div>
|
205
|
+
# ... additional lines truncated for brevity ...
|
206
|
+
<% end %>
|
207
|
+
```
|
208
|
+
|
209
|
+
And finally you need a script tag that gets the reCAPTCHA code from google and tells it that your code is explicit and gives it the callback.
|
210
|
+
|
211
|
+
```Html
|
212
|
+
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer></script>
|
213
|
+
```
|
214
|
+
|
215
|
+
Now all together:
|
216
|
+
|
217
|
+
```Html
|
218
|
+
<html>
|
219
|
+
<head>
|
220
|
+
<title>reCAPTCHA demo: Explicit render for multiple widgets</title>
|
221
|
+
<script type="text/javascript">
|
222
|
+
var verifyCallback = function(response) {
|
223
|
+
alert(response);
|
224
|
+
};
|
225
|
+
var widgetId1;
|
226
|
+
var widgetId2;
|
227
|
+
var onloadCallback = function() {
|
228
|
+
// Renders the HTML element with id 'example1' as a reCAPTCHA widget.
|
229
|
+
// The id of the reCAPTCHA widget is assigned to 'widgetId1'.
|
230
|
+
widgetId1 = grecaptcha.render('example1', {
|
231
|
+
'sitekey' : "<%= Recaptcha.configuration.public_key %>",
|
232
|
+
'theme' : 'light'
|
233
|
+
});
|
234
|
+
widgetId2 = grecaptcha.render(document.getElementById('example2'), {
|
235
|
+
'sitekey' : "<%= Recaptcha.configuration.public_key %>"
|
236
|
+
});
|
237
|
+
grecaptcha.render('example3', {
|
238
|
+
'sitekey' : "<%= Recaptcha.configuration.public_key %>",
|
239
|
+
'callback' : verifyCallback,
|
240
|
+
'theme' : 'dark'
|
241
|
+
});
|
242
|
+
};
|
243
|
+
</script>
|
244
|
+
</head>
|
245
|
+
<body>
|
246
|
+
<%= form_for @foo do |f| %>
|
247
|
+
# ... additional lines truncated for brevity ...
|
248
|
+
<div id="example1"></div>
|
249
|
+
# ... additional lines truncated for brevity ...
|
250
|
+
<% end %>
|
251
|
+
<%= form_for @foo2 do |f| %>
|
252
|
+
# ... additional lines truncated for brevity ...
|
253
|
+
<div id="example2"></div>
|
254
|
+
# ... additional lines truncated for brevity ...
|
255
|
+
<% end %>
|
256
|
+
<%= form_for @foo3 do |f| %>
|
257
|
+
# ... additional lines truncated for brevity ...
|
258
|
+
<div id="example3"></div>
|
259
|
+
# ... additional lines truncated for brevity ...
|
260
|
+
<% end %>
|
261
|
+
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer></script>
|
262
|
+
</body>
|
263
|
+
</html>
|
264
|
+
```
|
265
|
+
|
266
|
+
The only real difference between this example and the google example is you will use the `<%= Recaptcha.configuration.public_key %>` for the `sitekey`
|
267
|
+
|
268
|
+
If your callback has to live on another file (maybe a layout), then you would set the callback on window `window.onloadCallback = function() {...}`
|
269
|
+
|
270
|
+
Then on the backend, you will still use the `verify_recaptcha` as explained in this readme.
|
271
|
+
|
272
|
+
## I18n support
|
273
|
+
reCAPTCHA passes two types of error explanation to a linked model. It will use the I18n gem
|
274
|
+
to translate the default error message if I18n is available. To customize the messages to your locale,
|
275
|
+
add these keys to your I18n backend:
|
276
|
+
|
277
|
+
`recaptcha.errors.verification_failed` error message displayed if the captcha words didn't match
|
278
|
+
`recaptcha.errors.recaptcha_unreachable` displayed if a timeout error occured while attempting to verify the captcha
|
279
|
+
|
280
|
+
Also you can translate API response errors to human friendly by adding translations to the locale (`config/locales/en.yml`):
|
281
|
+
|
282
|
+
```Yaml
|
283
|
+
en:
|
284
|
+
recaptcha:
|
285
|
+
errors:
|
286
|
+
incorrect-captcha-sol: 'Fail'
|
287
|
+
```
|
288
|
+
|
289
|
+
## Testing
|
290
|
+
By default, reCAPTCHA is skipped on "test" env, so if you need to make sure it's working properly, just remove the "test" entry of the skip_environment inside Recapcha class, look:
|
291
|
+
|
292
|
+
|
293
|
+
```Ruby
|
294
|
+
Recaptcha.configuration.skip_verify_env.delete("test")
|
295
|
+
```
|
data/lib/recaptcha.rb
CHANGED
@@ -1,13 +1,26 @@
|
|
1
1
|
require 'recaptcha/configuration'
|
2
2
|
require 'recaptcha/client_helper'
|
3
3
|
require 'recaptcha/verify'
|
4
|
+
require 'recaptcha/token'
|
4
5
|
|
5
6
|
module Recaptcha
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
CONFIG =
|
8
|
+
{
|
9
|
+
'v1' => {
|
10
|
+
'server_url' => '//www.google.com/recaptcha/api',
|
11
|
+
'secure_server_url' => 'https://www.google.com/recaptcha/api',
|
12
|
+
'verify_url' => 'http://www.google.com/recaptcha/api/verify'
|
13
|
+
},
|
14
|
+
|
15
|
+
'v2' => {
|
16
|
+
'server_url' => '//www.google.com/recaptcha/api.js',
|
17
|
+
'secure_server_url' => 'https://www.google.com/recaptcha/api.js',
|
18
|
+
'verify_url' => 'https://www.google.com/recaptcha/api/siteverify'
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
RECAPTCHA_API_VERSION = 'v2'
|
9
23
|
USE_SSL_BY_DEFAULT = false
|
10
|
-
|
11
24
|
HANDLE_TIMEOUTS_GRACEFULLY = true
|
12
25
|
SKIP_VERIFY_ENV = ['test', 'cucumber']
|
13
26
|
|
@@ -43,8 +56,7 @@ module Recaptcha
|
|
43
56
|
|
44
57
|
class RecaptchaError < StandardError
|
45
58
|
end
|
46
|
-
end
|
47
59
|
|
48
|
-
|
49
|
-
|
60
|
+
class VerifyError < RecaptchaError
|
61
|
+
end
|
50
62
|
end
|
@@ -3,6 +3,11 @@ module Recaptcha
|
|
3
3
|
# Your public API can be specified in the +options+ hash or preferably
|
4
4
|
# using the Configuration.
|
5
5
|
def recaptcha_tags(options = {})
|
6
|
+
return v1_tags(options) if Recaptcha.configuration.v1?
|
7
|
+
return v2_tags(options) if Recaptcha.configuration.v2?
|
8
|
+
end # recaptcha_tags
|
9
|
+
|
10
|
+
def v1_tags(options)
|
6
11
|
# Default options
|
7
12
|
key = options[:public_key] ||= Recaptcha.configuration.public_key
|
8
13
|
raise RecaptchaError, "No public key specified." unless key
|
@@ -16,11 +21,18 @@ module Recaptcha
|
|
16
21
|
html << %{</script>\n}
|
17
22
|
end
|
18
23
|
if options[:ajax]
|
24
|
+
if options[:display] && options[:display][:custom_theme_widget]
|
25
|
+
widget = options[:display][:custom_theme_widget]
|
26
|
+
else
|
27
|
+
widget = "dynamic_recaptcha"
|
28
|
+
html << <<-EOS
|
29
|
+
<div id="#{widget}"></div>
|
30
|
+
EOS
|
31
|
+
end
|
19
32
|
html << <<-EOS
|
20
|
-
<div id="dynamic_recaptcha"></div>
|
21
33
|
<script type="text/javascript">
|
22
34
|
var rc_script_tag = document.createElement('script'),
|
23
|
-
rc_init_func = function(){Recaptcha.create("#{key}", document.getElementById("
|
35
|
+
rc_init_func = function(){Recaptcha.create("#{key}", document.getElementById("#{widget}")#{',RecaptchaOptions' if options[:display]});}
|
24
36
|
rc_script_tag.src = "#{uri}/js/recaptcha_ajax.js";
|
25
37
|
rc_script_tag.type = 'text/javascript';
|
26
38
|
rc_script_tag.onload = function(){rc_init_func.call();};
|
@@ -48,7 +60,57 @@ module Recaptcha
|
|
48
60
|
end
|
49
61
|
end
|
50
62
|
return (html.respond_to?(:html_safe) && html.html_safe) || html
|
51
|
-
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def v2_tags(options)
|
66
|
+
public_key = options[:public_key] ||= Recaptcha.configuration.public_key
|
67
|
+
raise RecaptchaError, "No public key specified." unless public_key
|
68
|
+
private_key = options[:private_key] ||= Recaptcha.configuration.private_key
|
69
|
+
raise RecaptchaError, "No private key specified." unless private_key
|
70
|
+
error = options[:error] ||= ((defined? flash) ? flash[:recaptcha_error] : "") # TODO not being used !?
|
71
|
+
uri = Recaptcha.configuration.api_server_url(options[:ssl])
|
72
|
+
uri += "?hl=#{options[:hl]}" unless options[:hl].blank?
|
73
|
+
|
74
|
+
html = ""
|
75
|
+
html << %{<script src="#{uri}" async defer></script>\n}
|
76
|
+
|
77
|
+
data_attributes = options.slice(:theme, :type, :callback, :expired_callback, :size)
|
78
|
+
data_attributes[:sitekey] = public_key
|
79
|
+
|
80
|
+
if options[:stoken] != false
|
81
|
+
data_attributes[:stoken] = Recaptcha::Token.secure_token
|
82
|
+
end
|
83
|
+
|
84
|
+
data_attributes = data_attributes.map {|k,v| %{data-#{k.to_s.gsub(/_/,'-')}="#{v}"} }.join(" ")
|
85
|
+
|
86
|
+
html << %{<div class="g-recaptcha" #{data_attributes}></div>\n}
|
87
|
+
|
88
|
+
unless options[:noscript] == false
|
89
|
+
fallback_uri = "#{uri.chomp('.js')}/fallback?k=#{public_key}"
|
90
|
+
html << %{<noscript>}
|
91
|
+
html << %{<div style="width: 302px; height: 352px;">}
|
92
|
+
html << %{ <div style="width: 302px; height: 352px; position: relative;">}
|
93
|
+
html << %{ <div style="width: 302px; height: 352px; position: absolute;">}
|
94
|
+
html << %{ <iframe src="#{fallback_uri}"}
|
95
|
+
html << %{ frameborder="0" scrolling="no"}
|
96
|
+
html << %{ style="width: 302px; height:352px; border-style: none;">}
|
97
|
+
html << %{ </iframe>}
|
98
|
+
html << %{ </div>}
|
99
|
+
html << %{ <div style="width: 250px; height: 80px; position: absolute; border-style: none; }
|
100
|
+
html << %{ bottom: 21px; left: 25px; margin: 0px; padding: 0px; right: 25px;">}
|
101
|
+
html << %{ <textarea id="g-recaptcha-response" name="g-recaptcha-response" }
|
102
|
+
html << %{ class="g-recaptcha-response" }
|
103
|
+
html << %{ style="width: 250px; height: 80px; border: 1px solid #c1c1c1; }
|
104
|
+
html << %{ margin: 0px; padding: 0px; resize: none;" value=""> }
|
105
|
+
html << %{ </textarea>}
|
106
|
+
html << %{ </div>}
|
107
|
+
html << %{ </div>}
|
108
|
+
html << %{ </div>}
|
109
|
+
html << %{</noscript>}
|
110
|
+
end
|
111
|
+
|
112
|
+
(html.respond_to?(:html_safe) && html.html_safe) || html
|
113
|
+
end
|
52
114
|
|
53
115
|
private
|
54
116
|
|
@@ -57,7 +119,7 @@ module Recaptcha
|
|
57
119
|
result << hash.map do |k, v|
|
58
120
|
if v.is_a?(Hash)
|
59
121
|
"\"#{k}\": #{hash_to_json(v)}"
|
60
|
-
elsif ! v.is_a?(String) || k.to_s
|
122
|
+
elsif ! v.is_a?(String) || k.to_s =~ %r{(callback|expired-callback)}
|
61
123
|
"\"#{k}\": #{v}"
|
62
124
|
else
|
63
125
|
"\"#{k}\": \"#{v}\""
|
@@ -65,5 +127,5 @@ module Recaptcha
|
|
65
127
|
end.join(", ")
|
66
128
|
result << "}"
|
67
129
|
end
|
68
|
-
end
|
69
|
-
end
|
130
|
+
end
|
131
|
+
end
|