Skip to content

Commit

Permalink
Add reCAPTCHA to feedback form
Browse files Browse the repository at this point in the history
  • Loading branch information
taylor-steve committed Nov 19, 2024
1 parent 09c6a0c commit 622e8e9
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 36 deletions.
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,5 @@ gem 'importmap-rails', '~> 2.0'

gem 'cssbundling-rails', '~> 1.1'
gem 'stimulus-rails', '~> 1.2'

gem 'recaptcha', '~> 5.16'
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,7 @@ GEM
rake (13.2.1)
rdoc (6.7.0)
psych (>= 4.0.0)
recaptcha (5.17.0)
redcarpet (3.6.0)
redis (5.3.0)
redis-client (>= 0.22.0)
Expand Down Expand Up @@ -816,6 +817,7 @@ DEPENDENCIES
rails (~> 7.1.3)
rails-controller-testing
rails_autolink
recaptcha (~> 5.16)
redis (~> 5.0)
riiif (~> 2.0)
rsolr (>= 1.0)
Expand Down
18 changes: 18 additions & 0 deletions app/assets/javascripts/record_recaptcha.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// This is the recaptcha submit handler for the record feedback modal.
// Normally this is inline, but the Blacklight modal filters out inline scripts.
// See: app/views/record_feedback/new.html.erb
document.addEventListener('show.blacklight.blacklight-modal', () => {
const form = document.forms.record_new_contact_form;
if (!form || form.dataset.recaptchaInitialized) return;

form.dataset.recaptchaInitialized = 'true';
form.addEventListener('submit', async function(e) {
e.preventDefault();
if (typeof grecaptcha !== 'undefined' && grecaptcha) {
const response = await grecaptcha.execute(this.dataset.recaptchaSiteKey, { action: 'record_feedback' });
const element = document.getElementById('g-recaptcha-response-data-record-feedback');
if (element) element.value = response;
}
this.submit();
});
});
4 changes: 4 additions & 0 deletions app/assets/stylesheets/footer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,7 @@
}
}
}

.grecaptcha-badge {
visibility: hidden;
}
22 changes: 12 additions & 10 deletions app/controllers/record_feedback_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@
# We're overriding to build from our RecordFeedbackForm class and
# to redirect to the correct place and set the correct notice
class RecordFeedbackController < Spotlight::ContactFormsController
def create
if @contact_form.valid?
Spotlight::ContactMailer.report_problem(@contact_form).deliver_now
def action
'record_feedback'
end

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

redirect_back(
fallback_location: spotlight.exhibit_solr_document_path(current_exhibit),
notice: t(:'helpers.submit.record_feedback.created')
)
else
render 'new'
end
def report_failure
redirect_back fallback_location: spotlight.exhibit_solr_document_path(current_exhibit),
alert: t(:'helpers.submit.record_feedback.error')
end

private
Expand Down
37 changes: 37 additions & 0 deletions app/controllers/spotlight/contact_forms_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# 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: action)
send_feedback
else
report_failure
end
end

def action
'feedback'
end

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
8 changes: 6 additions & 2 deletions app/views/record_feedback/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<% component.with_body do %>
<%= bootstrap_form_for(@contact_form || Spotlight::ContactForm.new(current_url: request.original_url),
url: main_app.exhibit_record_feedback_path(current_exhibit), as: 'contact_form',
html: { data: { turbo: false } }) do |f| %>
html: { id: 'record_new_contact_form',data: { turbo: false, recaptcha_site_key: Recaptcha.configuration.site_key } }) do |f| %>
<%= render '/spotlight/shared/honeypot_field', f: f %>
<%= f.hidden_field :current_url %>

Expand All @@ -15,8 +15,12 @@

<%= f.text_area :message, rows: 4, label: t('.message') %>
<%= f.email_field :email, label: t('.email') %>
<div>
<p>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: 'record_feedback', inline_script: false) %>
<div class="modal-footer">
<%= button_tag(t('.close'), class: 'btn btn-outline-dark', data: { 'dismiss': 'modal' }) if request.xhr? %>
<%= f.submit class: 'btn btn-primary' %>
Expand Down
23 changes: 23 additions & 0 deletions app/views/spotlight/shared/_report_a_problem.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,30 @@
<%= 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', inline_script: false) %>
<script type="text/javascript">
document.addEventListener('turbo:before-cache', function() {
const recaptchaElement = document.querySelector('.g-recaptcha');
if (recaptchaElement) {
recaptchaElement.innerHTML = '';
}
});
document.forms.new_contact_form.addEventListener('submit', async function(e) {
e.preventDefault();
if (typeof grecaptcha !== 'undefined' && grecaptcha) {
const response = await grecaptcha.execute("<%= Recaptcha.configuration.site_key %>", { action: 'feedback' });
const element = document.getElementById('g-recaptcha-response-data-feedback');
if (element) element.value = response;
}
this.submit();
});
</script>
<div class="form-actions row">
<div class="col offset-sm-3">
<%= f.submit nil, class: 'btn btn-primary' %>
Expand Down
5 changes: 5 additions & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,10 @@ class Application < Rails::Application
unless Rails.env.production?
config.slowpoke.timeout = 60
end

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
2 changes: 2 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,10 @@ en:
submit:
contact_form:
created: Thank you. Your feedback has been submitted.
error: There was a problem submitting feedback.
record_feedback:
created: Thank you. Your feedback has been submitted.
error: There was a problem submitting feedback.
metadata_collapse:
button:
less: less
Expand Down
67 changes: 43 additions & 24 deletions spec/controllers/record_feedback_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,25 @@
end

describe 'POST create' do
it 'sends an email' do
expect 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,
id: 'abc123',
contact_form: { name: 'Joe Doe', email: '[email protected]', message: 'Great record!', honeypot_field_name => '' }
}
)
end.to change { ActionMailer::Base.deliveries.count }.by(1)
end

it 'redirects back' do
post(
:create,
params: {
Expand All @@ -30,31 +47,33 @@
contact_form: { name: 'Joe Doe', email: '[email protected]', message: 'Great record!', honeypot_field_name => '' }
}
)
end.to change { ActionMailer::Base.deliveries.count }.by(1)
end
expect(response).to redirect_to 'http://test.host/'
end

it 'redirects back' do
post(
:create,
params: {
exhibit_id: exhibit.id,
id: 'abc123',
contact_form: { name: 'Joe Doe', email: '[email protected]', message: 'Great record!', honeypot_field_name => '' }
}
)
expect(response).to redirect_to 'http://test.host/'
it 'sets a flash message' do
post(
:create,
params: {
exhibit_id: exhibit.id,
id: 'abc123',
contact_form: { name: 'Joe Doe', email: '[email protected]', message: 'Great record!', honeypot_field_name => '' }
}
)
expect(flash[:notice]).to eq 'Thank you. Your feedback has been submitted.'
end
end

it 'sets a flash message' do
post(
:create,
params: {
exhibit_id: exhibit.id,
id: 'abc123',
contact_form: { name: 'Joe Doe', email: '[email protected]', message: 'Great record!', honeypot_field_name => '' }
}
)
expect(flash[:notice]).to eq 'Thank you. Your feedback has been submitted.'
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, id: 'abc123',
contact_form: { name: 'Joe Doe', email: '[email protected]', message: 'Great record!',
honeypot_field_name => '' } }
expect(flash[:alert]).to eq 'There was a problem submitting feedback.'
end
end
end
end

0 comments on commit 622e8e9

Please sign in to comment.