Skip to content

Commit 72d5db1

Browse files
admin can enable turnstile option
2 parents 8956ab9 + 6ff20f2 commit 72d5db1

9 files changed

Lines changed: 109 additions & 30 deletions

File tree

app/controllers/submissions_controller.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,12 @@ def create
8181
private
8282

8383
def create_in_local_database(submission)
84-
if submission.form.enable_turnstile? && !verify_turnstile(params["cf-turnstile-response"])
85-
submission.errors.add(:base, "Turnstile verification failed")
84+
if submission.form.enable_turnstile?
85+
if verify_turnstile(params["cf-turnstile-response"])
86+
submission.spam_prevention_mechanism = :turnstile
87+
else
88+
submission.errors.add(:base, "Turnstile verification failed")
89+
end
8690
end
8791

8892
respond_to do |format|

app/models/submission.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ def validate_custom_form
6565
answered_questions.delete('referer')
6666
answered_questions.delete('aasm_state')
6767
answered_questions.delete('tags')
68+
answered_questions.delete('spam_prevention_mechanism')
6869
answered_questions.delete('spam_score')
6970
answered_questions.delete('flagged')
7071
answered_questions.delete('spam')

app/views/admin/forms/_admin_options.html.erb

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,30 @@
5858
</div>
5959
<div class="grid-col-12 padding-top-2">
6060
<fieldset class="usa-fieldset">
61-
<legend class="usa-sr-only">enforce_new_submission_validations</legend>
61+
<legend class="usa-sr-only">Enable Cloudfront Turnstile</legend>
62+
<div class="usa-checkbox">
63+
<%= f.check_box :enable_turnstile, class: "usa-checkbox__input" %>
64+
<%= f.label :enable_turnstile, class: "usa-checkbox__label" do %>
65+
Enable Cloudfront Turnstile
66+
&nbsp;
67+
<small class="text-base">
68+
A spam prevention mechanism
69+
</small>
70+
<% end %>
71+
</div>
72+
</fieldset>
73+
</div>
74+
<div class="grid-col-12 padding-top-2">
75+
<fieldset class="usa-fieldset">
76+
<legend class="usa-sr-only">Enforce new submission validations</legend>
6277
<div class="usa-checkbox">
6378
<%= f.check_box :enforce_new_submission_validations, class: "usa-checkbox__input" %>
6479
<%= f.label :enforce_new_submission_validations, class: "usa-checkbox__label" do %>
65-
enforce_new_submission_validations
80+
Enforce submission validations
81+
&nbsp;
82+
<small class="text-base">
83+
Validate question responses match question types (helps some types of spam)
84+
</small>
6685
<% end %>
6786
</div>
6887
</fieldset>

app/views/admin/forms/_form_manager_options.html.erb

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,6 @@
9595
<%= f.text_field :expiration_date, class: "usa-input" %>
9696
</div>
9797

98-
<fieldset class="usa-fieldset margin-top-2">
99-
<legend class="usa-sr-only">Enable Cloudfront Turnstile</legend>
100-
<div class="usa-checkbox">
101-
<%= f.check_box :enable_turnstile, class: "usa-checkbox__input" %>
102-
<%= f.label :enable_turnstile, class: "usa-checkbox__label" do %>
103-
Enable Cloudfront Turnstile
104-
<small class="text-base">
105-
As a spam prevention mechanism
106-
</small>
107-
<% end %>
108-
</div>
109-
</fieldset>
11098
<fieldset class="usa-fieldset margin-top-2">
11199
<legend class="usa-sr-only">Append ID to the form's Success Text</legend>
112100
<div class="usa-checkbox">

app/views/admin/submissions/show.html.erb

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,11 @@
126126
<tr>
127127
<td>
128128
Location code
129+
&nbsp;
130+
<a
131+
href="https://github.com/GSA/touchpoints/wiki/Location-Codes"
132+
target="_blank">
133+
<i class="text-base fa fa-info-circle"></i></a>
129134
</td>
130135
<td>
131136
<%= h(@submission.location_code) %>
@@ -139,6 +144,14 @@
139144
<%= h(@submission.user_agent) %>
140145
</td>
141146
</tr>
147+
<tr>
148+
<td>
149+
IP Address
150+
</td>
151+
<td>
152+
<%= h(@submission.ip_address) %>
153+
</td>
154+
</tr>
142155
<tr>
143156
<td>
144157
Submitted from hostname
@@ -168,15 +181,15 @@
168181
Referer
169182
</td>
170183
<td>
171-
<%= sanitize(@submission.referer) %>
184+
<%= h(@submission.referer) %>
172185
</td>
173186
</tr>
174187
<tr>
175188
<td>
176189
Language
177190
</td>
178191
<td>
179-
<%= @submission.language %>
192+
<%= h(@submission.language) %>
180193
</td>
181194
</tr>
182195
<tr>

app/views/components/widget/_fba.js.erb

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ function FBAform(d, N) {
1010
formElement: function() {
1111
return this.formComponent().querySelector("form");
1212
},
13+
formLocalStorageKey: function() {
14+
return `touchpoints:${this.options.formId}`
15+
},
1316
isFormSubmitted: false, // defaults to false
1417
// enable Javascript experience
1518
javascriptIsEnabled: function() {
@@ -38,9 +41,10 @@ function FBAform(d, N) {
3841
if (this.options.formSpecificScript) {
3942
this.options.formSpecificScript();
4043
}
41-
<% if form.enable_turnstile? %>
42-
this.loadTurnstile()
44+
<%- if form.enable_turnstile? %>
45+
this.loadTurnstile();
4346
<% end %>
47+
this.enableLocalStorage();
4448
d.dispatchEvent(new CustomEvent('onTouchpointsFormLoaded', {
4549
detail: {
4650
formComponent: this
@@ -433,6 +437,7 @@ function FBAform(d, N) {
433437
if (formElement) {
434438
// And clear the Form's Fields
435439
formElement.reset();
440+
localStorage.removeItem(this.formLocalStorageKey());
436441
if (formElement.querySelector('.touchpoints-form-body')) {
437442
var formBody = formElement.querySelector('.touchpoints-form-body');
438443
if(formBody) {
@@ -555,7 +560,7 @@ function FBAform(d, N) {
555560
xhr.setRequestHeader("Content-Type", "application/json; charset=UTF-8;");
556561
xhr.onload = callback.bind(this);
557562
xhr.send(JSON.stringify({
558-
<% if form.enable_turnstile? %>
563+
<%- if form.enable_turnstile? %>
559564
"cf-turnstile-response" : form.querySelector("input[name='cf-turnstile-response']") ? form.querySelector("input[name='cf-turnstile-response']").value : null,
560565
<% end %>
561566
"submission": params,
@@ -645,23 +650,46 @@ function FBAform(d, N) {
645650
modalId: function() {
646651
return `fba-modal-${this.options.formId}`;
647652
},
648-
<% if form.enable_turnstile? %>
653+
<%- if form.enable_turnstile? %>
649654
loadTurnstile: function() {
650655
let script = document.createElement("script");
651656
script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js";
652657
script.async = true;
653658
script.defer = true;
654-
script.onload = function() {
655-
document.querySelector("input[name='cf-turnstile-response']").value = token;
656-
};
657659
document.head.appendChild(script);
658660
},
659661
<% end %>
662+
enableLocalStorage: function() {
663+
const form = this.formElement();
664+
const savedData = localStorage.getItem(this.formLocalStorageKey());
665+
666+
<%# Restore form data from localStorage %>
667+
if (savedData) {
668+
const formData = JSON.parse(savedData);
669+
for (const key in formData) {
670+
const input = form.querySelector(`[name="${key}"]`);
671+
if (input) {
672+
input.value = formData[key];
673+
}
674+
}
675+
}
676+
677+
<%# Save data to localStorage as the user types %>
678+
form.addEventListener('input', (event) => {
679+
const inputData = {};
680+
const formData = new FormData(form);
681+
formData.forEach((value, key) => {
682+
inputData[key] = value;
683+
});
684+
685+
localStorage.setItem(this.formLocalStorageKey(), JSON.stringify(inputData));
686+
});
687+
},
660688
};
661689
};
662690
663691
// Specify the options for your form
664-
const touchpointFormOptions<%= form.short_uuid %> = {
692+
var touchpointFormOptions<%= form.short_uuid %> = {
665693
'formId': "<%= form.short_uuid %>",
666694
'modalButtonText': "<%= form.modal_button_text %>",
667695
'elementSelector': "<%= form.element_selector %>",
@@ -703,10 +731,10 @@ const touchpointFormOptions<%= form.short_uuid %> = {
703731
}
704732
705733
// Create an instance of a Touchpoints form object
706-
const touchpointForm<%= form.short_uuid %> = new FBAform(document, window).init(touchpointFormOptions<%= form.short_uuid %>);
734+
window.touchpointForm<%= form.short_uuid %> = new FBAform(document, window);
735+
window.touchpointForm<%= form.short_uuid %>.init(touchpointFormOptions<%= form.short_uuid %>);
707736
708737
<%- if form.load_css && form.delivery_method != "touchpoints-hosted-only" %>
709-
710738
// Load the USWDS JS, loads as module 'fbaUswds' in global scope
711739
<%= render partial: 'components/widget/widget-uswds', formats: :js %>
712740
@@ -724,5 +752,4 @@ const touchpointForm<%= form.short_uuid %> = new FBAform(document, window).init(
724752
fbaUswds.Modal.on(fbaModalElement);
725753
}
726754
})();
727-
728755
<% end %>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class SubmissionSpamPreventionMechanism < ActiveRecord::Migration[8.0]
2+
def change
3+
add_column :submissions, :spam_prevention_mechanism, :string, default: "", comment: "Specify which spam prevention mechanism was used, if any."
4+
end
5+
end

db/schema.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema[8.0].define(version: 2025_04_01_223209) do
13+
ActiveRecord::Schema[8.0].define(version: 2025_04_02_195517) do
1414
# These are extensions that must be enabled in order to support this database
1515
enable_extension "pg_catalog.plpgsql"
1616

@@ -613,6 +613,7 @@
613613
t.boolean "deleted", default: false
614614
t.datetime "deleted_at"
615615
t.string "preview", default: ""
616+
t.string "spam_prevention_mechanism", default: "", comment: "Specify which spam prevention mechanism was used, if any."
616617
t.index ["archived"], name: "index_submissions_on_archived"
617618
t.index ["created_at"], name: "index_submissions_on_created_at"
618619
t.index ["flagged"], name: "index_submissions_on_flagged"

spec/features/touchpoints_spec.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,27 @@
2020
expect(page).to be_axe_clean
2121
end
2222

23+
describe 'persist text responses in localStorage' do
24+
let(:two_question_form) { FactoryBot.create(:form, :two_question_open_ended_form, organization:) }
25+
26+
before do
27+
visit touchpoint_path(two_question_form)
28+
fill_in(two_question_form.ordered_questions.first.ui_selector, with: 'Question one')
29+
fill_in(two_question_form.ordered_questions.last.ui_selector, with: 'Question two')
30+
visit touchpoint_path(two_question_form)
31+
end
32+
33+
it "enters text, refreshes to ensure it still there, submits, and ensures it has been cleared" do
34+
expect(find("#" + two_question_form.ordered_questions.first.ui_selector).value).to eq('Question one')
35+
expect(find("#" + two_question_form.ordered_questions.last.ui_selector).value).to eq('Question two')
36+
click_button 'Submit'
37+
expect(page).to have_content('Thank you. Your feedback has been received.')
38+
visit touchpoint_path(two_question_form)
39+
expect(find("#" + two_question_form.ordered_questions.first.ui_selector).value).to be_blank
40+
expect(find("#" + two_question_form.ordered_questions.last.ui_selector).value).to be_blank
41+
end
42+
end
43+
2344
context 'default success text' do
2445
before do
2546
visit touchpoint_path(form)

0 commit comments

Comments
 (0)