Skip to content

Commit

Permalink
Merge pull request #2436 from sul-dlss/2411-add-recaptcha
Browse files Browse the repository at this point in the history
Add reCAPTCHA to feedback form
  • Loading branch information
corylown authored Jul 1, 2024
2 parents e5a2dde + e5b1a2a commit 2738713
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 0 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,4 @@ gem 'view_component', '~> 2.82' # ViewComponent 3.x breaks blacklight_range_limi

# Used for shared reporting https://github.com/sul-dlss/exhibits/issues/2069
gem 'redis', '~> 5.0'
gem 'recaptcha', '~> 5.16'
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,7 @@ GEM
ffi (~> 1.0)
rdoc (6.7.0)
psych (>= 4.0.0)
recaptcha (5.17.0)
redcarpet (3.6.0)
redis (5.2.0)
redis-client (>= 0.22.0)
Expand Down Expand Up @@ -864,6 +865,7 @@ DEPENDENCIES
rack-mini-profiler
rails (~> 7.0)
rails-controller-testing
recaptcha (~> 5.16)
redis (~> 5.0)
riiif
rsolr
Expand Down
4 changes: 4 additions & 0 deletions app/assets/stylesheets/modules/sul_footer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,7 @@ footer {
#global-footer .container {
box-sizing: border-box !important;
}

.grecaptcha-badge {
visibility: hidden;
}
35 changes: 35 additions & 0 deletions app/controllers/spotlight/contact_forms_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

# This unpleasantness allows us to include the upstream controller before overriding it
spotlight_path = Gem::Specification.find_by_name('blacklight-spotlight').full_gem_path
require_dependency File.join(spotlight_path, 'app/controllers/spotlight/contact_forms_controller')

# Override the upstream controller to add recaptcha
module Spotlight
##
# Controller for routing exhibit feedback from users
class ContactFormsController
def create
return render 'new' unless @contact_form.valid?

if verify_recaptcha(action: 'feedback')
send_feedback
else
report_failure
end
end

private

def send_feedback
ContactMailer.report_problem(@contact_form).deliver_now
redirect_back fallback_location: spotlight.new_exhibit_contact_form_path(current_exhibit),
notice: t(:'helpers.submit.contact_form.created')
end

def report_failure
redirect_back fallback_location: spotlight.new_exhibit_contact_form_path(current_exhibit),
alert: t(:'helpers.submit.contact_form.error')
end
end
end
36 changes: 36 additions & 0 deletions app/views/spotlight/shared/_report_a_problem.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<div class="container">
<div class="row justify-content-center">
<% contact_form ||= Spotlight::ContactForm.new current_url: request.original_url %>
<%= bootstrap_form_for contact_form, url: spotlight.exhibit_contact_form_path(current_exhibit, contact_form), layout: :horizontal, label_col: 'col-sm-3', control_col: 'col-sm-9', html: { class: 'col-md-offset-2 col-md-8 my-3 '} do |f| %>
<h2><%= t(:'.title') %></h2>
<div class="alert alert-info"><%= t('.reporting_from', url: contact_form.current_url) %></div>
<%= f.text_area :message, rows: 4 %>
<%= f.text_field :name %>
<%= render '/spotlight/shared/honeypot_field', f: f %>
<%= f.email_field :email %>
<div class="row">
<div class="form-group col-sm-9 offset-sm-3">
<p class="mt-2 mb-0">This site is protected by reCAPTCHA and the Google <a href="https://policies.google.com/privacy">Privacy Policy</a> and <a href="https://policies.google.com/terms">Terms of Service</a> apply.</p>
</div>
</div>
<%= f.hidden_field :current_url %>
<%= recaptcha_v3(action: 'feedback') %>
<script type="text/javascript">
document.forms.new_contact_form.addEventListener('submit', async function(e) {
e.preventDefault();
const response = await window.executeRecaptchaForFeedbackAsync();
if (response) {
setInputWithRecaptchaResponseTokenForFeedback('g-recaptcha-response-data-feedback', response);
}
this.submit();
});
</script>
<div class="form-actions row">
<div class="col offset-sm-3">
<%= f.submit nil, class: 'btn btn-primary' %>
<%= link_to t(:'helpers.action.cancel'), '#', class: 'btn-sizing', data: { 'behavior' => 'cancel-link' } %>
</div>
</div>
<% end %>
</div>
</div>
5 changes: 5 additions & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,10 @@ class Application < Rails::Application
end

config.druid_regex = /([a-z]{2}[0-9]{3}[a-z]{2}[0-9]{4})/

Recaptcha.configure do |config|
config.site_key = ENV.fetch('RECAPTCHA_SITE_KEY', '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy')
config.secret_key = ENV.fetch('RECAPTCHA_SECRET_KEY', '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx')
end
end
end
4 changes: 4 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ en:
index: 'exhibit title'
view_title:
heatmaps: "Map"
helpers:
submit:
contact_form:
error: "There was a problem submitting feedback."
metadata:
abstract: Abstract/Contents
access: Access conditions
Expand Down
61 changes: 61 additions & 0 deletions spec/controllers/spotlight/contact_forms_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# frozen_string_literal: true

require 'rails_helper'

describe Spotlight::ContactFormsController do
routes { Spotlight::Engine.routes }
let(:exhibit) { create(:exhibit) }
let(:honeypot_field_name) { Spotlight::Engine.config.spambot_honeypot_email_field }

before do
request.env['HTTP_REFERER'] = '/whatever'
exhibit.contact_emails_attributes = [{ 'email' => '[email protected]' }, { 'email' => '[email protected]' }]
exhibit.save!
exhibit.contact_emails.first.tap do |e|
if e.respond_to? :confirm
e.confirm
else
e.confirm!
end
end
end

describe 'POST create' do
context 'when recaptcha verification succeeds' do
before do
allow(controller).to receive(:verify_recaptcha).and_return(true)
end

it 'sends an email' do
expect do
post :create, params: { exhibit_id: exhibit.id, contact_form: { name: 'Joe Doe', email: '[email protected]',
honeypot_field_name => '' } }
end.to change { ActionMailer::Base.deliveries.count }.by(1)
end

it 'redirects back' do
post :create, params: { exhibit_id: exhibit.id, contact_form: { name: 'Joe Doe', email: '[email protected]',
honeypot_field_name => '' } }
expect(response).to redirect_to end_with('/whatever')
end

it 'sets a flash message' do
post :create, params: { exhibit_id: exhibit.id, contact_form: { name: 'Joe Doe', email: '[email protected]',
honeypot_field_name => '' } }
expect(flash[:notice]).to eq 'Thanks. Your feedback has been sent.'
end
end

context 'when recaptcha verification fails' do
before do
allow(controller).to receive(:verify_recaptcha).and_return(false)
end

it 'alerts the failure in the flash message' do
post :create, params: { exhibit_id: exhibit.id, contact_form: { name: 'Joe Doe', email: '[email protected]',
honeypot_field_name => '' } }
expect(flash[:alert]).to eq 'There was a problem submitting feedback.'
end
end
end
end

0 comments on commit 2738713

Please sign in to comment.