Commit 4fe87ea2452f92429c2f7af9d9bc1b54a9928792
Committed by
Joenio Costa
1 parent
fd5c2e61
Exists in
master
and in
8 other branches
Adding recaptcha to comments and article suggestion
(ActionItem2075)
Showing
65 changed files
with
792 additions
and
452 deletions
Show diff stats
app/controllers/my_profile/cms_controller.rb
... | ... | @@ -285,7 +285,7 @@ class CmsController < MyProfileController |
285 | 285 | @task = SuggestArticle.new(params[:task]) |
286 | 286 | if request.post? |
287 | 287 | @task.target = profile |
288 | - if @task.save | |
288 | + if verify_recaptcha(:model => @task, :message => _('Please type the words correctly')) && @task.save | |
289 | 289 | session[:notice] = _('Thanks for your suggestion. The community administrators were notified.') |
290 | 290 | redirect_to @back_to |
291 | 291 | end | ... | ... |
app/controllers/public/account_controller.rb
... | ... | @@ -2,8 +2,6 @@ class AccountController < ApplicationController |
2 | 2 | |
3 | 3 | no_design_blocks |
4 | 4 | |
5 | - inverse_captcha :field => 'e_mail' | |
6 | - | |
7 | 5 | require_ssl :except => [ :login_popup, :logout_popup, :profile_details ] |
8 | 6 | |
9 | 7 | before_filter :login_required, :only => [:activation_question, :accept_terms, :activate_enterprise] |
... | ... | @@ -69,7 +67,7 @@ class AccountController < ApplicationController |
69 | 67 | @user.person_data = params[:profile_data] |
70 | 68 | @person = Person.new(params[:profile_data]) |
71 | 69 | @person.environment = @user.environment |
72 | - if request.post? && params[self.icaptcha_field].blank? | |
70 | + if request.post? | |
73 | 71 | @user.signup! |
74 | 72 | owner_role = Role.find_by_name('owner') |
75 | 73 | @user.person.affiliate(@user.person, [owner_role]) if owner_role | ... | ... |
app/controllers/public/contact_controller.rb
... | ... | @@ -4,10 +4,9 @@ class ContactController < PublicController |
4 | 4 | |
5 | 5 | needs_profile |
6 | 6 | |
7 | - inverse_captcha :field => 'e_mail' | |
8 | 7 | def new |
9 | 8 | @contact |
10 | - if request.post? && params[self.icaptcha_field].blank? && params[:confirm] == 'true' | |
9 | + if request.post? && params[:confirm] == 'true' | |
11 | 10 | @contact = user.build_contact(profile, params[:contact]) |
12 | 11 | @contact.city = (!params[:city].blank? && City.exists?(params[:city])) ? City.find(params[:city]).name : nil |
13 | 12 | @contact.state = (!params[:state].blank? && State.exists?(params[:state])) ? State.find(params[:state]).name : nil | ... | ... |
app/controllers/public/content_viewer_controller.rb
... | ... | @@ -2,8 +2,6 @@ class ContentViewerController < ApplicationController |
2 | 2 | |
3 | 3 | needs_profile |
4 | 4 | |
5 | - inverse_captcha :field => 'e_mail' | |
6 | - | |
7 | 5 | helper ProfileHelper |
8 | 6 | helper TagsHelper |
9 | 7 | |
... | ... | @@ -76,7 +74,7 @@ class ContentViewerController < ApplicationController |
76 | 74 | |
77 | 75 | @form_div = params[:form] |
78 | 76 | |
79 | - if params[:comment] && params[self.icaptcha_field].blank? && params[:confirm] == 'true' | |
77 | + if params[:comment] && params[:confirm] == 'true' | |
80 | 78 | @comment = Comment.new(params[:comment]) |
81 | 79 | if request.post? && @page.accept_comments? |
82 | 80 | add_comment |
... | ... | @@ -121,7 +119,7 @@ class ContentViewerController < ApplicationController |
121 | 119 | def add_comment |
122 | 120 | @comment.author = user if logged_in? |
123 | 121 | @comment.article = @page |
124 | - if @comment.save | |
122 | + if (@comment.reply_of_id || verify_recaptcha(:model => @comment, :message => _('Please type the words correctly'))) && @comment.save | |
125 | 123 | @page.touch |
126 | 124 | @comment = nil # clear the comment form |
127 | 125 | redirect_to :action => 'view_page', :profile => params[:profile], :page => @page.explode_path, :view => params[:view] | ... | ... |
app/models/comment.rb
app/models/suggest_article.rb
app/views/account/_signup_form.rhtml
app/views/cms/suggest_an_article.rhtml
... | ... | @@ -18,13 +18,10 @@ |
18 | 18 | |
19 | 19 | <%= render :partial => 'shared/lead_and_body', :locals => {:tiny_mce => true, :object => :task, :abstract_method => 'article_abstract', :body_method => 'article_body'} %> |
20 | 20 | |
21 | - <div id="captcha"> | |
22 | - <%= labelled_form_field(_("What is the result of '%s = ?'") % @task.captcha.task, text_field(:task, 'captcha_solution')) %> | |
23 | - <%= hidden_field :task, :captcha_secret %> | |
24 | - <br style="clear: both;"> | |
25 | - </div> | |
26 | - | |
27 | 21 | <%= hidden_field_tag('back_to', @back_to) %> |
22 | + | |
23 | + <%= recaptcha_tags(:display => { :theme => 'clean' }, :ajax => true) %> | |
24 | + | |
28 | 25 | <% button_bar do %> |
29 | 26 | <%= submit_button :save, _('Save') %> |
30 | 27 | <%= button :cancel, _('Cancel'), @back_to %> | ... | ... |
app/views/contact/new.rhtml
app/views/content_viewer/_comment_form.rhtml
... | ... | @@ -20,7 +20,6 @@ |
20 | 20 | </h4> |
21 | 21 | |
22 | 22 | <% form_tag( url_for(@page.view_url.merge({:only_path => true})), { :class => 'comment_form' } ) do %> |
23 | - <%= icaptcha_field() %> | |
24 | 23 | <%= hidden_field_tag(:confirm, 'false') %> |
25 | 24 | |
26 | 25 | <%= required_fields_message %> |
... | ... | @@ -29,7 +28,6 @@ |
29 | 28 | |
30 | 29 | <%= required labelled_form_field(_('Name'), text_field(:comment, :name)) %> |
31 | 30 | <%= required labelled_form_field(_('e-mail'), text_field(:comment, :email)) %> |
32 | - | |
33 | 31 | <p> |
34 | 32 | <%= _('If you are a registered user, you can login and be automatically recognized.') %> |
35 | 33 | </p> |
... | ... | @@ -39,8 +37,7 @@ |
39 | 37 | <%= required labelled_form_field(_('Title'), text_field(:comment, :title)) %> |
40 | 38 | <%= required labelled_form_field(_('Enter your comment'), text_area(:comment, :body, :rows => 5)) %> |
41 | 39 | |
42 | - <%= required labelled_form_field(_("What is the result of '%s = ?'") % @comment.captcha.task, text_field(:comment, :captcha_solution)) %> | |
43 | - <%= hidden_field(:comment, :captcha_secret) %> | |
40 | + <%= recaptcha_tags(:display => { :theme => 'clean' }, :ajax => true) unless logged_in? %> | |
44 | 41 | |
45 | 42 | <% button_bar do %> |
46 | 43 | <%= submit_button('add', _('Post comment'), :onclick => "this.form.confirm.value = 'true'; this.disabled = true; this.form.submit(); return true;") %> | ... | ... |
app/views/profile_editor/edit.rhtml
features/comment.feature
... | ... | @@ -82,11 +82,6 @@ Feature: comment |
82 | 82 | And I should be exactly on /booking/article-with-comment |
83 | 83 | And I should be moved to anchor "comment_form" |
84 | 84 | |
85 | - Scenario: ask captcha question | |
86 | - Given I am on /booking/article-with-comment | |
87 | - When I follow "Post a comment" within ".post-comment-button" | |
88 | - Then I should see "What is the result of " | |
89 | - | |
90 | 85 | @selenium |
91 | 86 | Scenario: keep comments field filled while trying to do a comment |
92 | 87 | Given I am on /booking/article-with-comment | ... | ... |
features/comment_reply.feature
... | ... | @@ -64,8 +64,7 @@ Feature: comment |
64 | 64 | |
65 | 65 | @selenium |
66 | 66 | Scenario: reply a comment |
67 | - Given skip comments captcha | |
68 | - And I go to /booking/another-article | |
67 | + Given I go to /booking/another-article | |
69 | 68 | And I follow "Reply" within ".comment-balloon" |
70 | 69 | And I fill in "Name" within "comment-balloon" with "Joey" |
71 | 70 | And I fill in "e-mail" within "comment-balloon" with "joey@ramones.com" | ... | ... |
features/step_definitions/noosfero_steps.rb
... | ... | @@ -368,7 +368,6 @@ Given /^the articles of "(.+)" are moderated$/ do |organization| |
368 | 368 | end |
369 | 369 | |
370 | 370 | Given /^the following comments?$/ do |table| |
371 | - Comment.skip_captcha! | |
372 | 371 | table.hashes.each do |item| |
373 | 372 | data = item.dup |
374 | 373 | article = Article.find_by_name(data.delete("article")) |
... | ... | @@ -388,7 +387,6 @@ Given /^the community "(.+)" is closed$/ do |community| |
388 | 387 | end |
389 | 388 | |
390 | 389 | Given /^someone suggested the following article to be published$/ do |table| |
391 | - SuggestArticle.skip_captcha! | |
392 | 390 | table.hashes.map{|item| item.dup}.each do |item| |
393 | 391 | target = Community[item.delete('target')] |
394 | 392 | task = SuggestArticle.create!(:target => target, :data => item) |
... | ... | @@ -422,10 +420,6 @@ Given /^the environment domain is "([^\"]*)"$/ do |domain| |
422 | 420 | d.save(false) |
423 | 421 | end |
424 | 422 | |
425 | -Given /^skip comments captcha$/ do | |
426 | - Comment.any_instance.stubs(:skip_captcha?).returns(true) | |
427 | -end | |
428 | - | |
429 | 423 | When /^([^\']*)'s account is activated$/ do |person| |
430 | 424 | Person.find_by_name(person).user.activate |
431 | 425 | end | ... | ... |
features/step_definitions/selenium_steps.rb
... | ... | @@ -93,12 +93,6 @@ When /^I type "([^\"]*)" in TinyMCE field "([^\"]*)"$/ do |value, field_id| |
93 | 93 | response.selenium.type("dom=document.getElementById('#{field_id}_ifr').contentDocument.body", value) |
94 | 94 | end |
95 | 95 | |
96 | -When /^I answer the captcha$/ do | |
97 | - question = response.selenium.get_text("//label[@for='task_captcha_solution']").match(/What is the result of '(.+) = \?'/)[1] | |
98 | - answer = eval(question) | |
99 | - response.selenium.type("id=task_captcha_solution", answer) | |
100 | -end | |
101 | - | |
102 | 96 | When /^I refresh the page$/ do |
103 | 97 | response.selenium.refresh |
104 | 98 | end | ... | ... |
features/suggest_article.feature
... | ... | @@ -31,7 +31,6 @@ Feature: suggest article |
31 | 31 | And I fill in "Email" with "someguy@somewhere.com" |
32 | 32 | And I type "This is my suggestion's lead" in TinyMCE field "task_article_abstract" |
33 | 33 | And I type "I like free software" in TinyMCE field "task_article_body" |
34 | - And I answer the captcha | |
35 | 34 | And I press "Save" |
36 | 35 | And I am logged in as "joaosilva" |
37 | 36 | And I go to Sample Community's control panel | ... | ... |
public/javascripts/application.js
... | ... | @@ -662,6 +662,7 @@ function add_comment_reply_form(button, comment_id) { |
662 | 662 | var f = container.find('.comment_form'); |
663 | 663 | if (f.length == 0) { |
664 | 664 | f = jQuery('#page-comment-form .comment_form').clone(); |
665 | + f.find('#dynamic_recaptcha').remove(); | |
665 | 666 | f.find('.fieldWithErrors').map(function() { jQuery(this).replaceWith(jQuery(this).contents()); }); |
666 | 667 | f.prepend('<input type="hidden" name="comment[reply_of_id]" value="' + comment_id + '" />'); |
667 | 668 | container.append(f); | ... | ... |
public/stylesheets/application.css
... | ... | @@ -6136,3 +6136,30 @@ h1#agenda-title { |
6136 | 6136 | #user a#pending-tasks-count { |
6137 | 6137 | color: #FFFFFF; |
6138 | 6138 | } |
6139 | + | |
6140 | +/* Captcha */ | |
6141 | + | |
6142 | +.comment_reply #recaptcha_area { | |
6143 | + margin-bottom: 3px !important; | |
6144 | +} | |
6145 | + | |
6146 | +.comment_reply .recaptchatable tr td + td + td { | |
6147 | + display: none !important; | |
6148 | +} | |
6149 | + | |
6150 | +.comment_reply .recaptcha-container { | |
6151 | + width: 100%; | |
6152 | + overflow: hidden; | |
6153 | +} | |
6154 | + | |
6155 | +.comment_reply .recaptcha-container:hover { | |
6156 | + overflow: visible; | |
6157 | +} | |
6158 | + | |
6159 | +.comment_reply .recaptcha-container tr:hover td { | |
6160 | + background: transparent; | |
6161 | +} | |
6162 | + | |
6163 | +.comment_reply .recaptcha_image_cell { | |
6164 | + background: transparent !important; | |
6165 | +} | ... | ... |
test/functional/account_controller_test.rb
... | ... | @@ -571,17 +571,6 @@ class AccountControllerTest < Test::Unit::TestCase |
571 | 571 | |
572 | 572 | # end of enterprise activation tests |
573 | 573 | |
574 | - should 'not be able to signup while inverse captcha field filled' do | |
575 | - assert_no_difference User, :count do | |
576 | - new_user({}, @controller.icaptcha_field => 'bli@bla.email.foo') | |
577 | - end | |
578 | - end | |
579 | - | |
580 | - should 'render inverse captcha field' do | |
581 | - get :signup | |
582 | - assert_tag :tag => 'input', :attributes => { :type => 'text', :name => @controller.icaptcha_field } | |
583 | - end | |
584 | - | |
585 | 574 | should 'use the current environment for the template of user' do |
586 | 575 | template = create_user('test_template', :email => 'test@bli.com', :password => 'pass', :password_confirmation => 'pass').person |
587 | 576 | template.boxes.destroy_all | ... | ... |
test/functional/cms_controller_test.rb
... | ... | @@ -1320,7 +1320,6 @@ class CmsControllerTest < Test::Unit::TestCase |
1320 | 1320 | should 'create a task suggest task to a profile' do |
1321 | 1321 | c = Community.create!(:name => 'test comm', :identifier => 'test_comm', :moderated_articles => true) |
1322 | 1322 | |
1323 | - SuggestArticle.any_instance.stubs(:skip_captcha?).returns(true) | |
1324 | 1323 | assert_difference SuggestArticle, :count do |
1325 | 1324 | post :suggest_an_article, :profile => c.identifier, :back_to => 'action_view', :task => {:article_name => 'some name', :article_body => 'some body', :email => 'some@localhost.com', :name => 'some name'} |
1326 | 1325 | end | ... | ... |
test/functional/contact_controller_test.rb
... | ... | @@ -74,13 +74,6 @@ class ContactControllerTest < Test::Unit::TestCase |
74 | 74 | assert_no_tag :tag => 'select', :attributes => {:name => 'state'} |
75 | 75 | end |
76 | 76 | |
77 | - should 'not be able to post contact while inverse captcha field filled' do | |
78 | - post :new, :profile => enterprise.identifier, @controller.icaptcha_field => 'filled', :contact => {:subject => 'Hi', :message => 'Hi, all', :state => '', :city => ''} | |
79 | - | |
80 | - assert_response :success | |
81 | - assert_template 'new' | |
82 | - end | |
83 | - | |
84 | 77 | should 'not allow if not logged' do |
85 | 78 | logout |
86 | 79 | get :new, :profile => profile.identifier |
... | ... | @@ -93,12 +86,6 @@ class ContactControllerTest < Test::Unit::TestCase |
93 | 86 | assert_equal Person['contact_test_user'], assigns(:contact).sender |
94 | 87 | end |
95 | 88 | |
96 | - should 'send contact while inverse captcha field not filled' do | |
97 | - post :new, :profile => enterprise.identifier, :contact => {:subject => 'Hi', :message => 'Hi, all', :state => '', :city => ''}, :confirm => 'true' | |
98 | - assert_response :redirect | |
99 | - assert_redirected_to :action => 'new' | |
100 | - end | |
101 | - | |
102 | 89 | should 'deliver contact if subject and message are filled' do |
103 | 90 | post :new, :profile => enterprise.identifier, :contact => {:subject => 'Hi', :message => 'Hi, all'}, :confirm => 'true' |
104 | 91 | assert_response :redirect | ... | ... |
test/functional/content_viewer_controller_test.rb
... | ... | @@ -15,7 +15,6 @@ class ContentViewerControllerTest < Test::Unit::TestCase |
15 | 15 | |
16 | 16 | @profile = create_user('testinguser').person |
17 | 17 | @environment = @profile.environment |
18 | - Comment.skip_captcha! | |
19 | 18 | end |
20 | 19 | attr_reader :profile, :environment |
21 | 20 | |
... | ... | @@ -188,17 +187,6 @@ class ContentViewerControllerTest < Test::Unit::TestCase |
188 | 187 | end |
189 | 188 | end |
190 | 189 | |
191 | - should 'not be able to post comment while inverse captcha field filled' do | |
192 | - profile = create_user('popstar').person | |
193 | - page = profile.articles.build(:name => 'myarticle', :body => 'the body of the text') | |
194 | - page.save! | |
195 | - profile.home_page = page; profile.save! | |
196 | - | |
197 | - assert_no_difference Comment, :count do | |
198 | - post :view_page, :profile => profile.identifier, :page => [ 'myarticle' ], @controller.icaptcha_field => 'filled', :comment => { :title => 'crap!', :body => 'I think that this article is crap', :name => 'Anonymous coward', :email => 'coward@anonymous.com' } | |
199 | - end | |
200 | - end | |
201 | - | |
202 | 190 | should 'be able to remove comments if is moderator' do |
203 | 191 | commenter = create_user('commenter_user').person |
204 | 192 | community = Community.create!(:name => 'Community test', :identifier => 'community-test') |
... | ... | @@ -212,15 +200,6 @@ class ContentViewerControllerTest < Test::Unit::TestCase |
212 | 200 | end |
213 | 201 | end |
214 | 202 | |
215 | - should 'render inverse captcha field' do | |
216 | - profile = create_user('popstar').person | |
217 | - page = profile.articles.build(:name => 'myarticle', :body => 'the body of the text') | |
218 | - page.save! | |
219 | - profile.home_page = page; profile.save! | |
220 | - get :view_page, :profile => profile.identifier, :page => [ 'myarticle' ] | |
221 | - assert_tag :tag => 'input', :attributes => { :type => 'text', :name => @controller.icaptcha_field } | |
222 | - end | |
223 | - | |
224 | 203 | should 'filter html content from body' do |
225 | 204 | login_as @profile.identifier |
226 | 205 | page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text') | ... | ... |
test/functional/search_controller_test.rb
test/functional/tasks_controller_test.rb
... | ... | @@ -238,7 +238,6 @@ class TasksControllerTest < Test::Unit::TestCase |
238 | 238 | c = fast_create(Community) |
239 | 239 | c.add_admin profile |
240 | 240 | @controller.stubs(:profile).returns(c) |
241 | - SuggestArticle.skip_captcha! | |
242 | 241 | t = SuggestArticle.create!(:article_name => 'test name', :article_abstract => 'test abstract', :article_body => 'test body', :name => 'some name', :email => 'test@localhost.com', :target => c) |
243 | 242 | |
244 | 243 | get :index |
... | ... | @@ -251,7 +250,6 @@ class TasksControllerTest < Test::Unit::TestCase |
251 | 250 | c = fast_create(Community) |
252 | 251 | c.affiliate(profile, Profile::Roles.all_roles(profile.environment.id)) |
253 | 252 | @controller.stubs(:profile).returns(c) |
254 | - SuggestArticle.skip_captcha! | |
255 | 253 | t = SuggestArticle.create!(:article_name => 'test name', :article_body => 'test body', :name => 'some name', :email => 'test@localhost.com', :target => c) |
256 | 254 | |
257 | 255 | post :close, :tasks => {t.id => { :task => {}, :decision => "finish"}} |
... | ... | @@ -263,7 +261,6 @@ class TasksControllerTest < Test::Unit::TestCase |
263 | 261 | c = fast_create(Community) |
264 | 262 | c.affiliate(profile, Profile::Roles.all_roles(profile.environment.id)) |
265 | 263 | @controller.stubs(:profile).returns(c) |
266 | - SuggestArticle.skip_captcha! | |
267 | 264 | t = SuggestArticle.new |
268 | 265 | t.article_name = 'test name' |
269 | 266 | t.article_body = 'test body' | ... | ... |
test/unit/article_test.rb
test/unit/category_finder_test.rb
... | ... | @@ -7,7 +7,6 @@ class CategoryFinderTest < ActiveSupport::TestCase |
7 | 7 | @finder = CategoryFinder.new(@category) |
8 | 8 | @product_category = fast_create(ProductCategory, :name => 'Products') |
9 | 9 | |
10 | - Comment.skip_captcha! | |
11 | 10 | end |
12 | 11 | |
13 | 12 | should 'search for articles in a specific category' do | ... | ... |
test/unit/category_test.rb
test/unit/comment_notifier_test.rb
... | ... | @@ -10,7 +10,6 @@ class CommentNotifierTest < Test::Unit::TestCase |
10 | 10 | ActionMailer::Base.deliveries = [] |
11 | 11 | @profile = create_user('user_comment_test').person |
12 | 12 | @article = fast_create(Article, :name => 'Article test', :profile_id => @profile.id, :notify_comments => true) |
13 | - Comment.skip_captcha! | |
14 | 13 | end |
15 | 14 | |
16 | 15 | should 'deliver mail after make aarticle commment' do | ... | ... |
test/unit/comment_test.rb
... | ... | @@ -3,7 +3,6 @@ require File.dirname(__FILE__) + '/../test_helper' |
3 | 3 | class CommentTest < Test::Unit::TestCase |
4 | 4 | |
5 | 5 | def setup |
6 | - Comment.skip_captcha! | |
7 | 6 | end |
8 | 7 | |
9 | 8 | should 'have a name and require it' do |
... | ... | @@ -331,12 +330,4 @@ class CommentTest < Test::Unit::TestCase |
331 | 330 | assert_nil Comment.new(:email => 'my@email.com').author_url |
332 | 331 | end |
333 | 332 | |
334 | - should 'have the captcha_solution be solved' do | |
335 | - Comment.dont_skip_captcha! | |
336 | - c = Comment.new | |
337 | - assert !c.valid? && c.errors.invalid?(:captcha_solution) | |
338 | - c.skip_captcha! | |
339 | - assert !c.valid? && !c.errors.invalid?(:captcha_solution) | |
340 | - end | |
341 | - | |
342 | 333 | end | ... | ... |
test/unit/community_test.rb
test/unit/forum_helper_test.rb
... | ... | @@ -12,7 +12,6 @@ class ForumHelperTest < Test::Unit::TestCase |
12 | 12 | @environment = Environment.default |
13 | 13 | @profile = create_user('forum_helper_test').person |
14 | 14 | @forum = fast_create(Forum, :profile_id => profile.id, :name => 'Forum test') |
15 | - Comment.skip_captcha! | |
16 | 15 | end |
17 | 16 | |
18 | 17 | attr :profile | ... | ... |
test/unit/suggest_article_test.rb
... | ... | @@ -45,18 +45,6 @@ class SuggestArticleTest < ActiveSupport::TestCase |
45 | 45 | assert t.errors.invalid?(:target_id) |
46 | 46 | end |
47 | 47 | |
48 | - should 'have the captcha_solution be solved' do | |
49 | - t = SuggestArticle.new | |
50 | - assert !t.errors.invalid?(:captcha_solution) | |
51 | - t.valid? | |
52 | - assert t.errors.invalid?(:captcha_solution) | |
53 | - | |
54 | - t.skip_captcha! | |
55 | - assert t.skip_captcha? | |
56 | - t.valid? | |
57 | - assert !t.errors.invalid?(:captcha_solution) | |
58 | - end | |
59 | - | |
60 | 48 | should 'have the article_abstract' do |
61 | 49 | t = SuggestArticle.new |
62 | 50 | assert t.respond_to?(:article_abstract) | ... | ... |
vendor/plugins/inverse_captcha/README
... | ... | @@ -1,29 +0,0 @@ |
1 | -InverseCaptcha | |
2 | -============== | |
3 | - | |
4 | -Implement simple Anti-Comment-Spam Technique. | |
5 | - | |
6 | -Basic Usage | |
7 | -=========== | |
8 | - | |
9 | -Add method inverse_captcha on controller: | |
10 | - | |
11 | -ps.: dont use this method more than once time on same controller. | |
12 | - | |
13 | - inverse_captcha :field => 'e-mail'[, class => 'ghost'] | |
14 | - | |
15 | -Add the field in view form: | |
16 | - | |
17 | - <%= icaptcha_field() %> | |
18 | - | |
19 | -Check if field is blank in your controller: | |
20 | - | |
21 | - if params[controller.icaptcha_field].blank? | |
22 | - do... | |
23 | - else | |
24 | - dont... | |
25 | - end | |
26 | - | |
27 | ---- | |
28 | -Joenio Costa <joenio@colivre.coop.br> | |
29 | -Qui Mar 27 15:48:12 BRT 2008 |
vendor/plugins/inverse_captcha/init.rb
vendor/plugins/inverse_captcha/lib/inverse_captcha.rb
... | ... | @@ -1,18 +0,0 @@ |
1 | -module InverseCaptcha | |
2 | - | |
3 | - module ClassMethods | |
4 | - def inverse_captcha(opt = {}) | |
5 | - InverseCaptcha.const_set("ICAPTCHA_FIELD", opt[:field]) unless InverseCaptcha.const_defined? "ICAPTCHA_FIELD" | |
6 | - InverseCaptcha.const_set("ICAPTCHA_LABEL", opt[:label] || N_("Don't fill this field")) unless InverseCaptcha.const_defined? "ICAPTCHA_LABEL" | |
7 | - InverseCaptcha.const_set("ICAPTCHA_STYLECLASS", opt[:class] || 'ghost') unless InverseCaptcha.const_defined? "ICAPTCHA_STYLECLASS" | |
8 | - self.send(:include, InverseCaptcha) | |
9 | - end | |
10 | - end | |
11 | - | |
12 | - module InstanceMethods | |
13 | - def icaptcha_field | |
14 | - ICAPTCHA_FIELD | |
15 | - end | |
16 | - end | |
17 | - | |
18 | -end |
vendor/plugins/inverse_captcha/lib/inverse_captcha_helper.rb
... | ... | @@ -1,11 +0,0 @@ |
1 | -module InverseCaptchaHelper | |
2 | - | |
3 | - def icaptcha_field(opt = {}) | |
4 | - label = controller.class::ICAPTCHA_LABEL | |
5 | - field = controller.class::ICAPTCHA_FIELD | |
6 | - opt.merge!({:class => controller.class::ICAPTCHA_STYLECLASS}) | |
7 | - stylesheet = "<style type='text/css'> span.#{opt[:class]} { display: none; } </style>" | |
8 | - stylesheet + content_tag('span', labelled_form_field(_(label), text_field_tag(field, nil)), opt) | |
9 | - end | |
10 | - | |
11 | -end |
vendor/plugins/rails-math-captcha/MIT-LICENSE
... | ... | @@ -1,20 +0,0 @@ |
1 | -Copyright (c) 2007 [name of plugin creator] | |
2 | - | |
3 | -Permission is hereby granted, free of charge, to any person obtaining | |
4 | -a copy of this software and associated documentation files (the | |
5 | -"Software"), to deal in the Software without restriction, including | |
6 | -without limitation the rights to use, copy, modify, merge, publish, | |
7 | -distribute, sublicense, and/or sell copies of the Software, and to | |
8 | -permit persons to whom the Software is furnished to do so, subject to | |
9 | -the following conditions: | |
10 | - | |
11 | -The above copyright notice and this permission notice shall be | |
12 | -included in all copies or substantial portions of the Software. | |
13 | - | |
14 | -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
15 | -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
16 | -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
17 | -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |
18 | -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |
19 | -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
20 | -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
vendor/plugins/rails-math-captcha/README
... | ... | @@ -1,45 +0,0 @@ |
1 | -MathCaptcha | |
2 | -=========== | |
3 | - | |
4 | -TODO: Create form helpers | |
5 | - | |
6 | -Validates your Model to be saved by a human (or very clever script) by asking | |
7 | -the user to solve a very basic mathematical equation. | |
8 | - | |
9 | -It does not use the session to store the secret, but uses AES from EzCrypto. | |
10 | - | |
11 | - | |
12 | -Requirements | |
13 | -============ | |
14 | - * gem ezcrypto | |
15 | - * users who can calculate | |
16 | - | |
17 | -Usage | |
18 | -===== | |
19 | - | |
20 | -In your Model < ActiveRecord::Base | |
21 | - | |
22 | - has_captcha | |
23 | - | |
24 | - | |
25 | -In your form view: | |
26 | - | |
27 | - <%= @model.captcha.task %> = ? | |
28 | - <%= form.text_field :captcha_solution %> | |
29 | - <%= form.hidden_field :captcha_secret %> | |
30 | - | |
31 | - | |
32 | -If you want to skip the validation (in tests/specs), you can | |
33 | - | |
34 | - Model.skip_captcha! # or | |
35 | - @model.skip_captcha! | |
36 | - | |
37 | -and turn that off by | |
38 | - | |
39 | - Model.dont_skip_captcha! # or | |
40 | - @model.dont_skip_captcha! | |
41 | - | |
42 | - | |
43 | - | |
44 | -Copyright (c) 2007 Pat Nakajima, released under the MIT license | |
45 | -Copyright (c) 2009 Niklas Hofer, released under the MIT license |
vendor/plugins/rails-math-captcha/Rakefile
... | ... | @@ -1,22 +0,0 @@ |
1 | -require 'rake' | |
2 | -require 'rake/testtask' | |
3 | -require 'rake/rdoctask' | |
4 | - | |
5 | -desc 'Default: run unit tests.' | |
6 | -task :default => :test | |
7 | - | |
8 | -desc 'Test the math_captcha plugin.' | |
9 | -Rake::TestTask.new(:test) do |t| | |
10 | - t.libs << 'lib' | |
11 | - t.pattern = 'test/**/*_test.rb' | |
12 | - t.verbose = true | |
13 | -end | |
14 | - | |
15 | -desc 'Generate documentation for the math_captcha plugin.' | |
16 | -Rake::RDocTask.new(:rdoc) do |rdoc| | |
17 | - rdoc.rdoc_dir = 'rdoc' | |
18 | - rdoc.title = 'MathCaptcha' | |
19 | - rdoc.options << '--line-numbers' << '--inline-source' | |
20 | - rdoc.rdoc_files.include('README') | |
21 | - rdoc.rdoc_files.include('lib/**/*.rb') | |
22 | -end |
vendor/plugins/rails-math-captcha/init.rb
vendor/plugins/rails-math-captcha/install.rb
... | ... | @@ -1 +0,0 @@ |
1 | -# Install hook code here |
vendor/plugins/rails-math-captcha/lib/math_captcha.rb
vendor/plugins/rails-math-captcha/lib/math_captcha/captcha.rb
... | ... | @@ -1,68 +0,0 @@ |
1 | -require 'rubygems' | |
2 | -require 'ezcrypto' | |
3 | -class Captcha | |
4 | - NUMBERS = (1..9).to_a | |
5 | - OPERATORS = [:+, :-, :*] | |
6 | - | |
7 | - attr_reader :x, :y, :operator | |
8 | - | |
9 | - def initialize(x=nil, y=nil, operator=nil) | |
10 | - @x = x || NUMBERS.sort_by{rand}.first | |
11 | - @y = y || NUMBERS.sort_by{rand}.first | |
12 | - @operator = operator || OPERATORS.sort_by{rand}.first | |
13 | - end | |
14 | - | |
15 | - # Only the #to_secret is shared with the client. | |
16 | - # It can be reused here to create the Captcha again | |
17 | - def self.from_secret(secret) | |
18 | - yml = cipher.decrypt64 secret | |
19 | - args = YAML.load(yml) | |
20 | - new(args[:x], args[:y], args[:operator]) | |
21 | - end | |
22 | - | |
23 | - def self.cipher | |
24 | - EzCrypto::Key.with_password key, 'bad_fixed_salt' | |
25 | - end | |
26 | - | |
27 | - def self.key | |
28 | - 'ultrasecret' | |
29 | - end | |
30 | - | |
31 | - | |
32 | - def check(answer) | |
33 | - answer == solution | |
34 | - end | |
35 | - | |
36 | - def task | |
37 | - "#{@x} #{@operator.to_s} #{@y}" | |
38 | - end | |
39 | - def task_with_questionmark | |
40 | - "#{@x} #{@operator.to_s} #{@y} = ?" | |
41 | - end | |
42 | - alias_method :to_s, :task | |
43 | - | |
44 | - def solution | |
45 | - @x.send @operator, @y | |
46 | - end | |
47 | - | |
48 | - def to_secret | |
49 | - cipher.encrypt64(to_yaml) | |
50 | - end | |
51 | - | |
52 | - def to_yaml | |
53 | - YAML::dump({ | |
54 | - :x => x, | |
55 | - :y => y, | |
56 | - :operator => operator | |
57 | - }) | |
58 | - end | |
59 | - | |
60 | - private | |
61 | - def cipher | |
62 | - @cipher ||= self.class.cipher | |
63 | - end | |
64 | - def reset_cipher | |
65 | - @cipher = nil | |
66 | - end | |
67 | - | |
68 | -end |
vendor/plugins/rails-math-captcha/lib/math_captcha/has_captcha.rb
... | ... | @@ -1,54 +0,0 @@ |
1 | -module MathCaptcha | |
2 | - module HasCaptcha | |
3 | - module InstanceMethods | |
4 | - def must_solve_captcha | |
5 | - self.errors.add(:captcha_solution, "wrong answer.") unless self.captcha.check(self.captcha_solution.to_i) | |
6 | - end | |
7 | - def skip_captcha! | |
8 | - self.class.skip_captcha! | |
9 | - end | |
10 | - def skip_captcha? | |
11 | - self.class.skip_captcha? | |
12 | - end | |
13 | - def captcha | |
14 | - @captcha ||= Captcha.new | |
15 | - end | |
16 | - def captcha_secret=(secret) | |
17 | - @captcha = Captcha.from_secret(secret) | |
18 | - end | |
19 | - def captcha_secret | |
20 | - captcha.to_secret | |
21 | - end | |
22 | - end | |
23 | - | |
24 | - module ClassMethods | |
25 | - def has_captcha | |
26 | - include InstanceMethods | |
27 | - attr_accessor :captcha_solution | |
28 | - dont_skip_captcha! | |
29 | - validates_presence_of :captcha_solution, | |
30 | - :on => :create, :message => "can't be blank", | |
31 | - :unless => Proc.new {|record| record.skip_captcha? } | |
32 | - validate_on_create :must_solve_captcha, | |
33 | - :unless => Proc.new {|record| record.skip_captcha? } | |
34 | - end | |
35 | - def skip_captcha! | |
36 | - @@skip_captcha = true | |
37 | - end | |
38 | - def dont_skip_captcha! | |
39 | - @@skip_captcha = false | |
40 | - end | |
41 | - def skip_captcha? | |
42 | - @@skip_captcha | |
43 | - end | |
44 | - def skipping_captcha(&block) | |
45 | - skipping_before = skip_captcha? | |
46 | - skip_captcha! | |
47 | - yield | |
48 | - dont_skip_captcha! if skipping_before | |
49 | - end | |
50 | - end | |
51 | - end | |
52 | -end | |
53 | - | |
54 | -ActiveRecord::Base.send(:extend, MathCaptcha::HasCaptcha::ClassMethods) |
vendor/plugins/rails-math-captcha/spec/captcha_spec.rb
... | ... | @@ -1,49 +0,0 @@ |
1 | -require 'lib/math_captcha/captcha' | |
2 | -require 'base64' | |
3 | - | |
4 | -describe Captcha do | |
5 | - describe "with a random task" do | |
6 | - before(:each) do | |
7 | - @captcha = Captcha.new | |
8 | - end | |
9 | - it "should have arguments and an operator" do | |
10 | - @captcha.x.should_not be_nil | |
11 | - @captcha.y.should_not be_nil | |
12 | - @captcha.operator.should_not be_nil | |
13 | - end | |
14 | - it "should use numbers bigger than zero" do | |
15 | - @captcha.x.should > 0 | |
16 | - @captcha.y.should > 0 | |
17 | - end | |
18 | - it "should offer a human readable task" do | |
19 | - @captcha.task.should =~ /^\d+\s*[\+\-\*]\s*\d+$/ | |
20 | - end | |
21 | - it "should have a secret to use in forms" do | |
22 | - @captcha.to_secret.should_not be_nil | |
23 | - @captcha.to_secret.should_not be_empty | |
24 | - end | |
25 | - | |
26 | - it "should re-use its cipher" do | |
27 | - @captcha.send(:cipher).should == @captcha.send(:cipher) | |
28 | - end | |
29 | - | |
30 | - it "should have a base64 encoded secret" do | |
31 | - lambda { Base64.decode64(@captcha.to_secret).should_not be_nil }.should_not raise_error | |
32 | - end | |
33 | - | |
34 | - describe "re-creating another from secret" do | |
35 | - before(:each) do | |
36 | - @secret = @captcha.to_secret | |
37 | - @new_captcha = Captcha.from_secret(@secret) | |
38 | - end | |
39 | - it "should have the same arguments and operator" do | |
40 | - @new_captcha.x.should == @captcha.x | |
41 | - @new_captcha.y.should == @captcha.y | |
42 | - @new_captcha.operator.should == @captcha.operator | |
43 | - end | |
44 | - it "should have the same string" do | |
45 | - @new_captcha.task.should == @captcha.task | |
46 | - end | |
47 | - end | |
48 | - end | |
49 | -end |
vendor/plugins/rails-math-captcha/spec/spec.opts
... | ... | @@ -0,0 +1,23 @@ |
1 | +== 0.2.2 / 2009-09-14 | |
2 | + | |
3 | +* Add a timeout to the validator | |
4 | +* Give the documentation some love | |
5 | + | |
6 | +== 0.2.1 / 2009-09-14 | |
7 | + | |
8 | +* Removed Ambethia namespace, and restructured classes a bit | |
9 | +* Added an example rails app in the example-rails branch | |
10 | + | |
11 | +== 0.2.0 / 2009-09-12 | |
12 | + | |
13 | +* RecaptchaOptions AJAX API Fix | |
14 | +* Added 'cucumber' as a test environment to skip | |
15 | +* Ruby 1.9 compat fixes | |
16 | +* Added option :message => 'Custom error message' to verify_recaptcha | |
17 | +* Removed dependency on ActiveRecord constant | |
18 | +* Add I18n | |
19 | + | |
20 | +== 0.1.0 / 2008-2-8 | |
21 | + | |
22 | +* 1 major enhancement | |
23 | + * Initial Gem Release | |
0 | 24 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,19 @@ |
1 | +Copyright (c) 2007 Jason L Perry | |
2 | + | |
3 | +Permission is hereby granted, free of charge, to any person obtaining a copy | |
4 | +of this software and associated documentation files (the "Software"), to deal | |
5 | +in the Software without restriction, including without limitation the rights | |
6 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
7 | +copies of the Software, and to permit persons to whom the Software is | |
8 | +furnished to do so, subject to the following conditions: | |
9 | + | |
10 | +The above copyright notice and this permission notice shall be included in | |
11 | +all copies or substantial portions of the Software. | |
12 | + | |
13 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
14 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
15 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
16 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
17 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
18 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
19 | +THE SOFTWARE. | |
0 | 20 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,150 @@ |
1 | += reCAPTCHA | |
2 | + | |
3 | +Author:: Jason L Perry (http://ambethia.com) | |
4 | +Copyright:: Copyright (c) 2007 Jason L Perry | |
5 | +License:: {MIT}[http://creativecommons.org/licenses/MIT/] | |
6 | +Info:: http://ambethia.com/recaptcha | |
7 | +Git:: http://github.com/ambethia/recaptcha/tree/master | |
8 | +Bugs:: http://github.com/ambethia/recaptcha/issues | |
9 | + | |
10 | +This plugin adds helpers for the {reCAPTCHA API}[http://recaptcha.net]. In your | |
11 | +views you can use the +recaptcha_tags+ method to embed the needed javascript, | |
12 | +and you can validate in your controllers with +verify_recaptcha+. | |
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 | +== About this fork | |
19 | + | |
20 | +This fork tries to introduces a more convenient way to configure recaptcha's | |
21 | +settings. The API will be inspired by {Thoughtbot's | |
22 | +Hoptoad}[http://robots.thoughtbot.com/post/344833329/mygem-configure-block]. | |
23 | + | |
24 | +== Rails Installation | |
25 | + | |
26 | +reCAPTCHA for Rails, add this to your Gemfile: | |
27 | + | |
28 | + gem "recaptcha", :require => "recaptcha/rails" | |
29 | + | |
30 | +Or, it can be installed as a gem: | |
31 | + | |
32 | + config.gem "recaptcha", :lib => "recaptcha/rails" | |
33 | + | |
34 | +Or, as a standard rails plugin: | |
35 | + | |
36 | + script/plugin install git://github.com/ambethia/recaptcha.git | |
37 | + | |
38 | +== Merb Installation | |
39 | + | |
40 | +reCAPTCHA can also be used in a Merb application when installed as a gem: | |
41 | + | |
42 | + dependency "alm-recaptcha", ">=0.2.2.1", :require_as => "recaptcha/merb" | |
43 | + | |
44 | +Initial Merb compatability funded by ALM Labs. | |
45 | + | |
46 | +== Setting up your API Keys | |
47 | + | |
48 | +There are multiple ways to setup your reCAPTCHA API key once you | |
49 | +{obtain}[http://recaptcha.net/whyrecaptcha.html] a pair. | |
50 | + | |
51 | +=== Recaptcha.configure | |
52 | + | |
53 | +You may use the block style configuration. The following code could be placed | |
54 | +into a +config/initializers/recaptcha.rb+ when used in a Rails project. | |
55 | + | |
56 | + Recaptcha.configure do |config| | |
57 | + config.public_key = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy' | |
58 | + config.private_key = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx' | |
59 | + config.proxy = 'http://myrpoxy.com.au:8080' | |
60 | + end | |
61 | + | |
62 | +This way, you may also set additional options to fit recaptcha into your | |
63 | +deployment environment. | |
64 | + | |
65 | +== Recaptcha#with_configuration | |
66 | + | |
67 | +If you want to temporarily overwrite the configuration you set with `Recaptcha.configure` (when testing, for example), you can use a `Recaptcha#with_configuration` block: | |
68 | + | |
69 | + Recaptcha.configure(:public_key => '12345') do | |
70 | + # Do stuff with the overwritten public_key. | |
71 | + end | |
72 | + | |
73 | +=== Shell environment | |
74 | + | |
75 | +Or, you can keep your keys out of your code base by exporting the following | |
76 | +environment variables. You might do this in the .profile/rc, or equivalent for | |
77 | +the user running your application: | |
78 | + | |
79 | + export RECAPTCHA_PUBLIC_KEY = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy' | |
80 | + export RECAPTCHA_PRIVATE_KEY = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx' | |
81 | + | |
82 | +=== Per call | |
83 | + | |
84 | +You can also pass in your keys as options at runtime, for example: | |
85 | + | |
86 | + recaptcha_tags :public_key => '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy' | |
87 | + | |
88 | +and later, | |
89 | + | |
90 | + verify_recaptcha :private_key => '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx' | |
91 | + | |
92 | +This option might be useful, if the same code base is used for multiple | |
93 | +reCAPTCHA setups. | |
94 | + | |
95 | +== To use 'recaptcha' | |
96 | + | |
97 | +Add +recaptcha_tags+ to each form you want to protect. | |
98 | + | |
99 | +And, add +verify_recaptcha+ logic to each form action that you've protected. | |
100 | + | |
101 | +=== +recaptcha_tags+ | |
102 | + | |
103 | +Some of the options available: | |
104 | + | |
105 | +<tt>:ssl</tt>:: Uses secure http for captcha widget (default +false+) | |
106 | +<tt>:noscript</tt>:: Include <noscript> content (default +true+) | |
107 | +<tt>:display</tt>:: Takes a hash containing the +theme+ and +tabindex+ options per the API. (default +nil+) | |
108 | +<tt>:ajax</tt>:: Render the dynamic AJAX captcha per the API. (default +false+) | |
109 | +<tt>:public_key</tt>:: Your public API key, takes precedence over the ENV variable (default +nil+) | |
110 | +<tt>:error</tt>:: Override the error code returned from the reCAPTCHA API (default +nil+) | |
111 | + | |
112 | +You can also override the html attributes for the sizes of the generated +textarea+ and +iframe+ | |
113 | +elements, if CSS isn't your thing. Inspect the source of +recaptcha_tags+ to see these options. | |
114 | + | |
115 | +=== +verify_recaptcha+ | |
116 | + | |
117 | +This method returns +true+ or +false+ after processing the parameters from the reCAPTCHA widget. Why | |
118 | +isn't this a model validation? Because that violates MVC. You can use it like this, or how ever you | |
119 | +like. Passing in the ActiveRecord object is optional, if you do--and the captcha fails to verify--an | |
120 | +error will be added to the object for you to use. | |
121 | + | |
122 | +Some of the options available: | |
123 | + | |
124 | +<tt>:model</tt>:: Model to set errors | |
125 | +<tt>:attribute</tt>:: Model attribute to receive errors (default :base) | |
126 | +<tt>:message</tt>:: Custom error message | |
127 | +<tt>:private_key</tt>:: Your private API key, takes precedence over the ENV variable (default +nil+). | |
128 | +<tt>:timeout</tt>:: The number of seconds to wait for reCAPTCHA servers before give up. (default +3+) | |
129 | + | |
130 | + respond_to do |format| | |
131 | + if verify_recaptcha(:model => @post, :message => "Oh! It's error with reCAPTCHA!") && @post.save | |
132 | + # ... | |
133 | + else | |
134 | + # ... | |
135 | + end | |
136 | + end | |
137 | + | |
138 | +== I18n support | |
139 | +reCAPTCHA passes two types of error explanation to a linked model. It will use the I18n gem | |
140 | +to translate the default error message if I18n is available. To customize the messages to your locale, | |
141 | +add these keys to your I18n backend: | |
142 | + | |
143 | +<tt>recaptcha.errors.verification_failed</tt>:: error message displayed if the captcha words didn't match | |
144 | +<tt>recaptcha.errors.recaptcha_unavailable</tt>:: displayed if a timout error occured while attempting to verify the captcha | |
145 | + | |
146 | +== TODO | |
147 | +* Remove Rails/ActionController dependencies | |
148 | +* Framework agnostic | |
149 | +* Add some helpers to use in before_filter and what not | |
150 | +* Better documentation | ... | ... |
... | ... | @@ -0,0 +1,60 @@ |
1 | +require 'rake' | |
2 | + | |
3 | +begin | |
4 | + require 'jeweler' | |
5 | + Jeweler::Tasks.new do |gem| | |
6 | + gem.name = "recaptcha" | |
7 | + gem.description = "This plugin adds helpers for the reCAPTCHA API " | |
8 | + gem.summary = "Helpers for the reCAPTCHA API" | |
9 | + gem.homepage = "http://ambethia.com/recaptcha" | |
10 | + gem.authors = ["Jason L. Perry"] | |
11 | + gem.email = "jasper@ambethia.com" | |
12 | + gem.files.reject! { |fn| fn.include? ".gitignore" } | |
13 | + gem.add_development_dependency "mocha" | |
14 | + gem.add_development_dependency "activesupport" | |
15 | + end | |
16 | + Jeweler::GemcutterTasks.new | |
17 | +rescue LoadError | |
18 | + puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler" | |
19 | +end | |
20 | + | |
21 | +require 'rake/rdoctask' | |
22 | +Rake::RDocTask.new do |rd| | |
23 | + if File.exist?('VERSION.yml') | |
24 | + config = YAML.load(File.read('VERSION.yml')) | |
25 | + version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}" | |
26 | + else | |
27 | + version = "" | |
28 | + end | |
29 | + | |
30 | + rd.main = "README.rdoc" | |
31 | + rd.rdoc_files.include "README.rdoc", "LICENSE", "lib/**/*.rb" | |
32 | + rd.rdoc_dir = 'rdoc' | |
33 | + rd.options << '-N' # line numbers | |
34 | + rd.options << '-S' # inline source | |
35 | +end | |
36 | + | |
37 | +require 'rake/testtask' | |
38 | +Rake::TestTask.new(:test) do |test| | |
39 | + test.libs << 'test' | |
40 | + test.pattern = 'test/**/*_test.rb' | |
41 | + # test.verbose = true | |
42 | +end | |
43 | + | |
44 | +begin | |
45 | + require 'rcov/rcovtask' | |
46 | + Rcov::RcovTask.new do |test| | |
47 | + test.libs << 'test' | |
48 | + test.pattern = 'test/**/*_test.rb' | |
49 | + test.verbose = true | |
50 | + end | |
51 | +rescue LoadError | |
52 | + task :rcov do | |
53 | + abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov" | |
54 | + end | |
55 | +end | |
56 | + | |
57 | +task :default => :test | |
58 | + | |
59 | + | |
60 | + | ... | ... |
... | ... | @@ -0,0 +1 @@ |
1 | +0.3.2 | ... | ... |
... | ... | @@ -0,0 +1,58 @@ |
1 | +require 'recaptcha/configuration' | |
2 | +require 'recaptcha/client_helper' | |
3 | +require 'recaptcha/verify' | |
4 | + | |
5 | +module Recaptcha | |
6 | + module VERSION #:nodoc: | |
7 | + MAJOR = 0 | |
8 | + MINOR = 2 | |
9 | + TINY = 2 | |
10 | + PATCH = 1 | |
11 | + | |
12 | + STRING = [MAJOR, MINOR, TINY, PATCH].join('.') | |
13 | + end | |
14 | + | |
15 | + | |
16 | + RECAPTCHA_API_SERVER_URL = 'http://www.google.com/recaptcha/api' | |
17 | + RECAPTCHA_API_SECURE_SERVER_URL = 'https://www.google.com/recaptcha/api' | |
18 | + RECAPTCHA_VERIFY_URL = 'http://www.google.com/recaptcha/api/verify' | |
19 | + | |
20 | + SKIP_VERIFY_ENV = ['test', 'cucumber'] | |
21 | + | |
22 | + # Gives access to the current Configuration. | |
23 | + def self.configuration | |
24 | + @configuration ||= Configuration.new | |
25 | + end | |
26 | + | |
27 | + # Allows easy setting of multiple configuration options. See Configuration | |
28 | + # for all available options. | |
29 | + #-- | |
30 | + # The temp assignment is only used to get a nicer rdoc. Feel free to remove | |
31 | + # this hack. | |
32 | + #++ | |
33 | + def self.configure | |
34 | + config = configuration | |
35 | + yield(config) | |
36 | + end | |
37 | + | |
38 | + def self.with_configuration(config) | |
39 | + original_config = {} | |
40 | + | |
41 | + config.each do |key, value| | |
42 | + original_config[key] = configuration.send(key) | |
43 | + configuration.send("#{key}=", value) | |
44 | + end | |
45 | + | |
46 | + result = yield if block_given? | |
47 | + | |
48 | + original_config.each { |key, value| configuration.send("#{key}=", value) } | |
49 | + result | |
50 | + end | |
51 | + | |
52 | + class RecaptchaError < StandardError | |
53 | + end | |
54 | +end | |
55 | + | |
56 | +if defined?(Rails) | |
57 | + require 'recaptcha/rails' | |
58 | +end | ... | ... |
... | ... | @@ -0,0 +1,51 @@ |
1 | +module Recaptcha | |
2 | + module ClientHelper | |
3 | + # Your public API can be specified in the +options+ hash or preferably | |
4 | + # using the Configuration. | |
5 | + def recaptcha_tags(options = {}) | |
6 | + # Default options | |
7 | + key = options[:public_key] ||= Recaptcha.configuration.public_key | |
8 | + raise RecaptchaError, "No public key specified." unless key | |
9 | + error = options[:error] ||= (defined? flash ? flash[:recaptcha_error] : "") | |
10 | + uri = Recaptcha.configuration.api_server_url(options[:ssl]) | |
11 | + html = "" | |
12 | + if options[:display] | |
13 | + html << %{<script type="text/javascript">\n} | |
14 | + html << %{ var RecaptchaOptions = #{options[:display].to_json};\n} | |
15 | + html << %{</script>\n} | |
16 | + end | |
17 | + if options[:ajax] | |
18 | + html << <<-EOS | |
19 | + <div id="dynamic_recaptcha"></div> | |
20 | + <script type="text/javascript"> | |
21 | + var rc_script_tag = document.createElement('script'), | |
22 | + rc_init_func = function(){Recaptcha.create("#{key}", document.getElementById("dynamic_recaptcha")#{',RecaptchaOptions' if options[:display]});} | |
23 | + rc_script_tag.src = "#{uri}/js/recaptcha_ajax.js"; | |
24 | + rc_script_tag.type = 'text/javascript'; | |
25 | + rc_script_tag.onload = function(){rc_init_func.call();}; | |
26 | + rc_script_tag.onreadystatechange = function(){ | |
27 | + if (rc_script_tag.readyState == 'loaded' || rc_script_tag.readyState == 'complete') {rc_init_func.call();} | |
28 | + }; | |
29 | + (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(rc_script_tag); | |
30 | + </script> | |
31 | + EOS | |
32 | + else | |
33 | + html << %{<script type="text/javascript" src="#{uri}/challenge?k=#{key}} | |
34 | + html << %{#{error ? "&error=#{CGI::escape(error)}" : ""}"></script>\n} | |
35 | + unless options[:noscript] == false | |
36 | + html << %{<noscript>\n } | |
37 | + html << %{<iframe src="#{uri}/noscript?k=#{key}" } | |
38 | + html << %{height="#{options[:iframe_height] ||= 300}" } | |
39 | + html << %{width="#{options[:iframe_width] ||= 500}" } | |
40 | + html << %{style="border:none;"></iframe><br/>\n } | |
41 | + html << %{<textarea name="recaptcha_challenge_field" } | |
42 | + html << %{rows="#{options[:textarea_rows] ||= 3}" } | |
43 | + html << %{cols="#{options[:textarea_cols] ||= 40}"></textarea>\n } | |
44 | + html << %{<input type="hidden" name="recaptcha_response_field" value="manual_challenge"/>} | |
45 | + html << %{</noscript>\n} | |
46 | + end | |
47 | + end | |
48 | + return (html.respond_to?(:html_safe) && html.html_safe) || html | |
49 | + end # recaptcha_tags | |
50 | + end # ClientHelper | |
51 | +end # Recaptcha | ... | ... |
... | ... | @@ -0,0 +1,53 @@ |
1 | +module Recaptcha | |
2 | + # This class enables detailed configuration of the recaptcha services. | |
3 | + # | |
4 | + # By calling | |
5 | + # | |
6 | + # Recaptcha.configuration # => instance of Recaptcha::Configuration | |
7 | + # | |
8 | + # or | |
9 | + # Recaptcha.configure do |config| | |
10 | + # config # => instance of Recaptcha::Configuration | |
11 | + # end | |
12 | + # | |
13 | + # you are able to perform configuration updates. | |
14 | + # | |
15 | + # Your are able to customize all attributes listed below. All values have | |
16 | + # sensitive default and will very likely not need to be changed. | |
17 | + # | |
18 | + # Please note that the public and private key for the reCAPTCHA API Access | |
19 | + # have no useful default value. The keys may be set via the Shell enviroment | |
20 | + # or using this configuration. Settings within this configuration always take | |
21 | + # precedence. | |
22 | + # | |
23 | + # Setting the keys with this Configuration | |
24 | + # | |
25 | + # Recaptcha.configure do |config| | |
26 | + # config.public_key = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy' | |
27 | + # config.private_key = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx' | |
28 | + # end | |
29 | + # | |
30 | + class Configuration | |
31 | + attr_accessor :nonssl_api_server_url, | |
32 | + :ssl_api_server_url, | |
33 | + :verify_url, | |
34 | + :skip_verify_env, | |
35 | + :private_key, | |
36 | + :public_key, | |
37 | + :proxy | |
38 | + | |
39 | + def initialize #:nodoc: | |
40 | + @nonssl_api_server_url = RECAPTCHA_API_SERVER_URL | |
41 | + @ssl_api_server_url = RECAPTCHA_API_SECURE_SERVER_URL | |
42 | + @verify_url = RECAPTCHA_VERIFY_URL | |
43 | + @skip_verify_env = SKIP_VERIFY_ENV | |
44 | + | |
45 | + @private_key = ENV['RECAPTCHA_PRIVATE_KEY'] | |
46 | + @public_key = ENV['RECAPTCHA_PUBLIC_KEY'] | |
47 | + end | |
48 | + | |
49 | + def api_server_url(ssl = false) #:nodoc: | |
50 | + ssl ? ssl_api_server_url : nonssl_api_server_url | |
51 | + end | |
52 | + end | |
53 | +end | ... | ... |
... | ... | @@ -0,0 +1,15 @@ |
1 | +require 'net/http' | |
2 | +require 'recaptcha' | |
3 | +module Rails | |
4 | + module Recaptcha | |
5 | + class Railtie < Rails::Railtie | |
6 | + initializer "setup config" do | |
7 | + begin | |
8 | + ActionView::Base.send(:include, ::Recaptcha::ClientHelper) | |
9 | + ActionController::Base.send(:include, ::Recaptcha::Verify) | |
10 | + end | |
11 | + end | |
12 | + end | |
13 | + end | |
14 | +end | |
15 | + | ... | ... |
... | ... | @@ -0,0 +1,61 @@ |
1 | +require "uri" | |
2 | +module Recaptcha | |
3 | + module Verify | |
4 | + # Your private API can be specified in the +options+ hash or preferably | |
5 | + # using the Configuration. | |
6 | + def verify_recaptcha(options = {}) | |
7 | + if !options.is_a? Hash | |
8 | + options = {:model => options} | |
9 | + end | |
10 | + | |
11 | + env = options[:env] || ENV['RAILS_ENV'] | |
12 | + return true if Recaptcha.configuration.skip_verify_env.include? env | |
13 | + model = options[:model] | |
14 | + attribute = options[:attribute] || :base | |
15 | + private_key = options[:private_key] || Recaptcha.configuration.private_key | |
16 | + raise RecaptchaError, "No private key specified." unless private_key | |
17 | + | |
18 | + begin | |
19 | + recaptcha = nil | |
20 | + if(Recaptcha.configuration.proxy) | |
21 | + proxy_server = URI.parse(Recaptcha.configuration.proxy) | |
22 | + http = Net::HTTP::Proxy(proxy_server.host, proxy_server.port) | |
23 | + else | |
24 | + http = Net::HTTP | |
25 | + end | |
26 | + | |
27 | + Timeout::timeout(options[:timeout] || 3) do | |
28 | + recaptcha = http.post_form(URI.parse(Recaptcha.configuration.verify_url), { | |
29 | + "privatekey" => private_key, | |
30 | + "remoteip" => request.remote_ip, | |
31 | + "challenge" => params[:recaptcha_challenge_field], | |
32 | + "response" => params[:recaptcha_response_field] | |
33 | + }) | |
34 | + end | |
35 | + answer, error = recaptcha.body.split.map { |s| s.chomp } | |
36 | + unless answer == 'true' | |
37 | + flash[:recaptcha_error] = error | |
38 | + if model | |
39 | + message = "Word verification response is incorrect, please try again." | |
40 | + message = I18n.translate(:'recaptcha.errors.verification_failed', {:default => message}) if defined?(I18n) | |
41 | + model.errors.add attribute, options[:message] || message | |
42 | + end | |
43 | + return false | |
44 | + else | |
45 | + flash[:recaptcha_error] = nil | |
46 | + return true | |
47 | + end | |
48 | + rescue Timeout::Error | |
49 | + flash[:recaptcha_error] = "recaptcha-not-reachable" | |
50 | + if model | |
51 | + message = "Oops, we failed to validate your word verification response. Please try again." | |
52 | + message = I18n.translate(:'recaptcha.errors.recaptcha_unreachable', :default => message) if defined?(I18n) | |
53 | + model.errors.add attribute, options[:message] || message | |
54 | + end | |
55 | + return false | |
56 | + rescue Exception => e | |
57 | + raise RecaptchaError, e.message, e.backtrace | |
58 | + end | |
59 | + end # verify_recaptcha | |
60 | + end # Verify | |
61 | +end # Recaptcha | ... | ... |
... | ... | @@ -0,0 +1,65 @@ |
1 | +# Generated by jeweler | |
2 | +# DO NOT EDIT THIS FILE DIRECTLY | |
3 | +# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' | |
4 | +# -*- encoding: utf-8 -*- | |
5 | + | |
6 | +Gem::Specification.new do |s| | |
7 | + s.name = %q{recaptcha} | |
8 | + s.version = "0.3.2" | |
9 | + | |
10 | + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= | |
11 | + s.authors = ["Jason L. Perry"] | |
12 | + s.date = %q{2010-12-20} | |
13 | + s.description = %q{This plugin adds helpers for the reCAPTCHA API } | |
14 | + s.email = %q{jasper@ambethia.com} | |
15 | + s.extra_rdoc_files = [ | |
16 | + "LICENSE", | |
17 | + "README.rdoc" | |
18 | + ] | |
19 | + s.files = [ | |
20 | + "CHANGELOG", | |
21 | + "LICENSE", | |
22 | + "README.rdoc", | |
23 | + "Rakefile", | |
24 | + "VERSION", | |
25 | + "init.rb", | |
26 | + "lib/recaptcha.rb", | |
27 | + "lib/recaptcha/client_helper.rb", | |
28 | + "lib/recaptcha/configuration.rb", | |
29 | + "lib/recaptcha/merb.rb", | |
30 | + "lib/recaptcha/rails.rb", | |
31 | + "lib/recaptcha/verify.rb", | |
32 | + "recaptcha.gemspec", | |
33 | + "tasks/recaptcha_tasks.rake", | |
34 | + "test/recaptcha_test.rb", | |
35 | + "test/verify_recaptcha_test.rb" | |
36 | + ] | |
37 | + s.homepage = %q{http://ambethia.com/recaptcha} | |
38 | + s.require_paths = ["lib"] | |
39 | + s.rubygems_version = %q{1.3.7} | |
40 | + s.summary = %q{Helpers for the reCAPTCHA API} | |
41 | + s.test_files = [ | |
42 | + "test/recaptcha_test.rb", | |
43 | + "test/verify_recaptcha_test.rb" | |
44 | + ] | |
45 | + | |
46 | + if s.respond_to? :specification_version then | |
47 | + current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION | |
48 | + s.specification_version = 3 | |
49 | + | |
50 | + if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then | |
51 | + s.add_development_dependency(%q<mocha>, [">= 0"]) | |
52 | + s.add_development_dependency(%q<activesupport>, [">= 0"]) | |
53 | + s.add_dependency(%q<i18n>, [">= 0"]) | |
54 | + else | |
55 | + s.add_dependency(%q<mocha>, [">= 0"]) | |
56 | + s.add_dependency(%q<activesupport>, [">= 0"]) | |
57 | + s.add_dependency(%q<i18n>, [">= 0"]) | |
58 | + end | |
59 | + else | |
60 | + s.add_dependency(%q<mocha>, [">= 0"]) | |
61 | + s.add_dependency(%q<activesupport>, [">= 0"]) | |
62 | + s.add_dependency(%q<i18n>, [">= 0"]) | |
63 | + end | |
64 | +end | |
65 | + | ... | ... |
... | ... | @@ -0,0 +1,52 @@ |
1 | +require 'test/unit' | |
2 | +require 'cgi' | |
3 | +require File.dirname(File.expand_path(__FILE__)) + '/../lib/recaptcha' | |
4 | + | |
5 | +class RecaptchaClientHelperTest < Test::Unit::TestCase | |
6 | + include Recaptcha | |
7 | + include Recaptcha::ClientHelper | |
8 | + include Recaptcha::Verify | |
9 | + | |
10 | + attr_accessor :session | |
11 | + | |
12 | + def setup | |
13 | + @session = {} | |
14 | + Recaptcha.configure do |config| | |
15 | + config.public_key = '0000000000000000000000000000000000000000' | |
16 | + config.private_key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' | |
17 | + end | |
18 | + end | |
19 | + | |
20 | + def test_recaptcha_tags | |
21 | + # Might as well match something... | |
22 | + assert_match /http:\/\/www.google.com\/recaptcha\/api\/challenge/, recaptcha_tags | |
23 | + end | |
24 | + | |
25 | + def test_recaptcha_tags_with_ssl | |
26 | + assert_match /https:\/\/www.google.com\/recaptcha\/api\/challenge/, recaptcha_tags(:ssl => true) | |
27 | + end | |
28 | + | |
29 | + def test_recaptcha_tags_without_noscript | |
30 | + assert_no_match /noscript/, recaptcha_tags(:noscript => false) | |
31 | + end | |
32 | + | |
33 | + def test_should_raise_exception_without_public_key | |
34 | + assert_raise RecaptchaError do | |
35 | + Recaptcha.configuration.public_key = nil | |
36 | + recaptcha_tags | |
37 | + end | |
38 | + end | |
39 | + | |
40 | + def test_different_configuration_within_with_configuration_block | |
41 | + key = Recaptcha.with_configuration(:public_key => '12345') do | |
42 | + Recaptcha.configuration.public_key | |
43 | + end | |
44 | + | |
45 | + assert_equal('12345', key) | |
46 | + end | |
47 | + | |
48 | + def test_reset_configuration_after_with_configuration_block | |
49 | + Recaptcha.with_configuration(:public_key => '12345') | |
50 | + assert_equal('0000000000000000000000000000000000000000', Recaptcha.configuration.public_key) | |
51 | + end | |
52 | +end | ... | ... |
... | ... | @@ -0,0 +1,120 @@ |
1 | +# coding: utf-8 | |
2 | + | |
3 | +require 'test/unit' | |
4 | +require 'rubygems' | |
5 | +require 'active_support/core_ext/string' | |
6 | +require 'mocha' | |
7 | +require 'i18n' | |
8 | +require 'net/http' | |
9 | +require File.dirname(File.expand_path(__FILE__)) + '/../lib/recaptcha' | |
10 | + | |
11 | +class RecaptchaVerifyTest < Test::Unit::TestCase | |
12 | + def setup | |
13 | + Recaptcha.configuration.private_key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' | |
14 | + @controller = TestController.new | |
15 | + @controller.request = stub(:remote_ip => "1.1.1.1") | |
16 | + @controller.params = {:recaptcha_challenge_field => "challenge", :recaptcha_response_field => "response"} | |
17 | + | |
18 | + @expected_post_data = {} | |
19 | + @expected_post_data["privatekey"] = Recaptcha.configuration.private_key | |
20 | + @expected_post_data["remoteip"] = @controller.request.remote_ip | |
21 | + @expected_post_data["challenge"] = "challenge" | |
22 | + @expected_post_data["response"] = "response" | |
23 | + | |
24 | + @expected_uri = URI.parse(Recaptcha.configuration.verify_url) | |
25 | + end | |
26 | + | |
27 | + def test_should_raise_exception_without_private_key | |
28 | + assert_raise Recaptcha::RecaptchaError do | |
29 | + Recaptcha.configuration.private_key = nil | |
30 | + @controller.verify_recaptcha | |
31 | + end | |
32 | + end | |
33 | + | |
34 | + def test_should_return_false_when_key_is_invalid | |
35 | + expect_http_post(response_with_body("false\ninvalid-site-private-key")) | |
36 | + | |
37 | + assert !@controller.verify_recaptcha | |
38 | + assert_equal "invalid-site-private-key", @controller.flash[:recaptcha_error] | |
39 | + end | |
40 | + | |
41 | + def test_returns_true_on_success | |
42 | + @controller.flash[:recaptcha_error] = "previous error that should be cleared" | |
43 | + expect_http_post(response_with_body("true\n")) | |
44 | + | |
45 | + assert @controller.verify_recaptcha | |
46 | + assert_nil @controller.flash[:recaptcha_error] | |
47 | + end | |
48 | + | |
49 | + def test_errors_should_be_added_to_model | |
50 | + expect_http_post(response_with_body("false\nbad-news")) | |
51 | + | |
52 | + errors = mock | |
53 | + errors.expects(:add).with(:base, "Word verification response is incorrect, please try again.") | |
54 | + model = mock(:errors => errors) | |
55 | + | |
56 | + assert !@controller.verify_recaptcha(:model => model) | |
57 | + assert_equal "bad-news", @controller.flash[:recaptcha_error] | |
58 | + end | |
59 | + | |
60 | + def test_returns_true_on_success_with_optional_key | |
61 | + @controller.flash[:recaptcha_error] = "previous error that should be cleared" | |
62 | + # reset private key | |
63 | + @expected_post_data["privatekey"] = 'ADIFFERENTPRIVATEKEYXXXXXXXXXXXXXX' | |
64 | + expect_http_post(response_with_body("true\n")) | |
65 | + | |
66 | + assert @controller.verify_recaptcha(:private_key => 'ADIFFERENTPRIVATEKEYXXXXXXXXXXXXXX') | |
67 | + assert_nil @controller.flash[:recaptcha_error] | |
68 | + end | |
69 | + | |
70 | + def test_timeout | |
71 | + expect_http_post(Timeout::Error, :exception => true) | |
72 | + assert !@controller.verify_recaptcha() | |
73 | + assert_equal "recaptcha-not-reachable", @controller.flash[:recaptcha_error] | |
74 | + end | |
75 | + | |
76 | + def test_message_should_use_i18n | |
77 | + I18n.locale = :de | |
78 | + verification_failed_translated = "Sicherheitscode konnte nicht verifiziert werden." | |
79 | + verification_failed_default = "Word verification response is incorrect, please try again." | |
80 | + recaptcha_unreachable_translated = "Netzwerkfehler, bitte versuchen Sie es später erneut." | |
81 | + recaptcha_unreachable_default = "Oops, we failed to validate your word verification response. Please try again." | |
82 | + I18n.expects(:translate).with(:'recaptcha.errors.verification_failed', :default => verification_failed_default).returns(verification_failed_translated) | |
83 | + I18n.expects(:translate).with(:'recaptcha.errors.recaptcha_unreachable', :default => recaptcha_unreachable_default).returns(recaptcha_unreachable_translated) | |
84 | + | |
85 | + errors = mock | |
86 | + errors.expects(:add).with(:base, verification_failed_translated) | |
87 | + errors.expects(:add).with(:base, recaptcha_unreachable_translated) | |
88 | + model = mock; model.stubs(:errors => errors) | |
89 | + | |
90 | + expect_http_post(response_with_body("false\nbad-news")) | |
91 | + @controller.verify_recaptcha(:model => model) | |
92 | + | |
93 | + expect_http_post(Timeout::Error, :exception => true) | |
94 | + @controller.verify_recaptcha(:model => model) | |
95 | + | |
96 | + end | |
97 | + | |
98 | + private | |
99 | + | |
100 | + class TestController | |
101 | + include Recaptcha::Verify | |
102 | + attr_accessor :request, :params, :flash | |
103 | + | |
104 | + def initialize | |
105 | + @flash = {} | |
106 | + end | |
107 | + end | |
108 | + | |
109 | + def expect_http_post(response, options = {}) | |
110 | + unless options[:exception] | |
111 | + Net::HTTP.expects(:post_form).with(@expected_uri, @expected_post_data).returns(response) | |
112 | + else | |
113 | + Net::HTTP.expects(:post_form).raises response | |
114 | + end | |
115 | + end | |
116 | + | |
117 | + def response_with_body(body) | |
118 | + stub(:body => body) | |
119 | + end | |
120 | +end | ... | ... |