From 622e8e959f19902aa6a671a57dc3cd041ea4b32b Mon Sep 17 00:00:00 2001 From: Steve Taylor Date: Tue, 18 Jun 2024 08:36:28 -0700 Subject: [PATCH] Add reCAPTCHA to feedback form --- Gemfile | 2 + Gemfile.lock | 2 + app/assets/javascripts/record_recaptcha.js | 18 +++++ app/assets/stylesheets/footer.scss | 4 ++ app/controllers/record_feedback_controller.rb | 22 +++--- .../spotlight/contact_forms_controller.rb | 37 ++++++++++ app/views/record_feedback/new.html.erb | 8 ++- .../shared/_report_a_problem.html.erb | 23 +++++++ config/application.rb | 5 ++ config/locales/en.yml | 2 + .../record_feedback_controller_spec.rb | 67 ++++++++++++------- 11 files changed, 154 insertions(+), 36 deletions(-) create mode 100644 app/assets/javascripts/record_recaptcha.js create mode 100644 app/controllers/spotlight/contact_forms_controller.rb diff --git a/Gemfile b/Gemfile index fc744b40..a0a85ec5 100644 --- a/Gemfile +++ b/Gemfile @@ -113,3 +113,5 @@ gem 'importmap-rails', '~> 2.0' gem 'cssbundling-rails', '~> 1.1' gem 'stimulus-rails', '~> 1.2' + +gem 'recaptcha', '~> 5.16' diff --git a/Gemfile.lock b/Gemfile.lock index bcb2256a..ae96da89 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -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) @@ -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) diff --git a/app/assets/javascripts/record_recaptcha.js b/app/assets/javascripts/record_recaptcha.js new file mode 100644 index 00000000..64e735d9 --- /dev/null +++ b/app/assets/javascripts/record_recaptcha.js @@ -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(); + }); +}); \ No newline at end of file diff --git a/app/assets/stylesheets/footer.scss b/app/assets/stylesheets/footer.scss index c95062b2..673235a7 100644 --- a/app/assets/stylesheets/footer.scss +++ b/app/assets/stylesheets/footer.scss @@ -68,3 +68,7 @@ } } } + +.grecaptcha-badge { + visibility: hidden; +} diff --git a/app/controllers/record_feedback_controller.rb b/app/controllers/record_feedback_controller.rb index 9cd07ad7..75ad2abf 100644 --- a/app/controllers/record_feedback_controller.rb +++ b/app/controllers/record_feedback_controller.rb @@ -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 diff --git a/app/controllers/spotlight/contact_forms_controller.rb b/app/controllers/spotlight/contact_forms_controller.rb new file mode 100644 index 00000000..335d1e4a --- /dev/null +++ b/app/controllers/spotlight/contact_forms_controller.rb @@ -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 diff --git a/app/views/record_feedback/new.html.erb b/app/views/record_feedback/new.html.erb index 55901e8b..86be48a1 100644 --- a/app/views/record_feedback/new.html.erb +++ b/app/views/record_feedback/new.html.erb @@ -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 %> @@ -15,8 +15,12 @@ <%= f.text_area :message, rows: 4, label: t('.message') %> <%= f.email_field :email, label: t('.email') %> +
+

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

+
- + <%= f.hidden_field :current_url %> + <%= recaptcha_v3(action: 'record_feedback', inline_script: false) %>