Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions app/controllers/submissions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,12 @@ def create
private

def create_in_local_database(submission)
if submission.form.enable_turnstile? && !verify_turnstile(params["cf-turnstile-response"])
submission.errors.add(:base, "Turnstile verification failed")
if submission.form.enable_turnstile?
if verify_turnstile(params["cf-turnstile-response"])
submission.spam_prevention_mechanism = :turnstile
else
submission.errors.add(:base, "Turnstile verification failed")
end
end

respond_to do |format|
Expand Down
1 change: 1 addition & 0 deletions app/models/submission.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ def validate_custom_form
answered_questions.delete('referer')
answered_questions.delete('aasm_state')
answered_questions.delete('tags')
answered_questions.delete('spam_prevention_mechanism')
answered_questions.delete('spam_score')
answered_questions.delete('flagged')
answered_questions.delete('spam')
Expand Down
23 changes: 21 additions & 2 deletions app/views/admin/forms/_admin_options.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,30 @@
</div>
<div class="grid-col-12 padding-top-2">
<fieldset class="usa-fieldset">
<legend class="usa-sr-only">enforce_new_submission_validations</legend>
<legend class="usa-sr-only">Enable Cloudfront Turnstile</legend>
<div class="usa-checkbox">
<%= f.check_box :enable_turnstile, class: "usa-checkbox__input" %>
<%= f.label :enable_turnstile, class: "usa-checkbox__label" do %>
Enable Cloudfront Turnstile
&nbsp;
<small class="text-base">
A spam prevention mechanism
</small>
<% end %>
</div>
</fieldset>
</div>
<div class="grid-col-12 padding-top-2">
<fieldset class="usa-fieldset">
<legend class="usa-sr-only">Enforce new submission validations</legend>
<div class="usa-checkbox">
<%= f.check_box :enforce_new_submission_validations, class: "usa-checkbox__input" %>
<%= f.label :enforce_new_submission_validations, class: "usa-checkbox__label" do %>
enforce_new_submission_validations
Enforce submission validations
&nbsp;
<small class="text-base">
Validate question responses match question types (helps some types of spam)
</small>
<% end %>
</div>
</fieldset>
Expand Down
12 changes: 0 additions & 12 deletions app/views/admin/forms/_form_manager_options.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -95,18 +95,6 @@
<%= f.text_field :expiration_date, class: "usa-input" %>
</div>

<fieldset class="usa-fieldset margin-top-2">
<legend class="usa-sr-only">Enable Cloudfront Turnstile</legend>
<div class="usa-checkbox">
<%= f.check_box :enable_turnstile, class: "usa-checkbox__input" %>
<%= f.label :enable_turnstile, class: "usa-checkbox__label" do %>
Enable Cloudfront Turnstile
<small class="text-base">
As a spam prevention mechanism
</small>
<% end %>
</div>
</fieldset>
<fieldset class="usa-fieldset margin-top-2">
<legend class="usa-sr-only">Append ID to the form's Success Text</legend>
<div class="usa-checkbox">
Expand Down
17 changes: 15 additions & 2 deletions app/views/admin/submissions/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@
<tr>
<td>
Location code
&nbsp;
<a
href="https://github.com/GSA/touchpoints/wiki/Location-Codes"
target="_blank">
<i class="text-base fa fa-info-circle"></i></a>
</td>
<td>
<%= h(@submission.location_code) %>
Expand All @@ -139,6 +144,14 @@
<%= h(@submission.user_agent) %>
</td>
</tr>
<tr>
<td>
IP Address
</td>
<td>
<%= h(@submission.ip_address) %>
</td>
</tr>
<tr>
<td>
Submitted from hostname
Expand Down Expand Up @@ -168,15 +181,15 @@
Referer
</td>
<td>
<%= sanitize(@submission.referer) %>
<%= h(@submission.referer) %>
</td>
</tr>
<tr>
<td>
Language
</td>
<td>
<%= @submission.language %>
<%= h(@submission.language) %>
</td>
</tr>
<tr>
Expand Down
49 changes: 38 additions & 11 deletions app/views/components/widget/_fba.js.erb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ function FBAform(d, N) {
formElement: function() {
return this.formComponent().querySelector("form");
},
formLocalStorageKey: function() {
return `touchpoints:${this.options.formId}`
},
isFormSubmitted: false, // defaults to false
// enable Javascript experience
javascriptIsEnabled: function() {
Expand Down Expand Up @@ -38,9 +41,10 @@ function FBAform(d, N) {
if (this.options.formSpecificScript) {
this.options.formSpecificScript();
}
<% if form.enable_turnstile? %>
this.loadTurnstile()
<%- if form.enable_turnstile? %>
this.loadTurnstile();
<% end %>
this.enableLocalStorage();
d.dispatchEvent(new CustomEvent('onTouchpointsFormLoaded', {
detail: {
formComponent: this
Expand Down Expand Up @@ -433,6 +437,7 @@ function FBAform(d, N) {
if (formElement) {
// And clear the Form's Fields
formElement.reset();
localStorage.removeItem(this.formLocalStorageKey());
if (formElement.querySelector('.touchpoints-form-body')) {
var formBody = formElement.querySelector('.touchpoints-form-body');
if(formBody) {
Expand Down Expand Up @@ -555,7 +560,7 @@ function FBAform(d, N) {
xhr.setRequestHeader("Content-Type", "application/json; charset=UTF-8;");
xhr.onload = callback.bind(this);
xhr.send(JSON.stringify({
<% if form.enable_turnstile? %>
<%- if form.enable_turnstile? %>
"cf-turnstile-response" : form.querySelector("input[name='cf-turnstile-response']") ? form.querySelector("input[name='cf-turnstile-response']").value : null,
<% end %>
"submission": params,
Expand Down Expand Up @@ -645,23 +650,46 @@ function FBAform(d, N) {
modalId: function() {
return `fba-modal-${this.options.formId}`;
},
<% if form.enable_turnstile? %>
<%- if form.enable_turnstile? %>
loadTurnstile: function() {
let script = document.createElement("script");
script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js";
script.async = true;
script.defer = true;
script.onload = function() {
document.querySelector("input[name='cf-turnstile-response']").value = token;
};
document.head.appendChild(script);
},
<% end %>
enableLocalStorage: function() {
const form = this.formElement();
const savedData = localStorage.getItem(this.formLocalStorageKey());

<%# Restore form data from localStorage %>
if (savedData) {
const formData = JSON.parse(savedData);
for (const key in formData) {
const input = form.querySelector(`[name="${key}"]`);
if (input) {
input.value = formData[key];
}
}
}

<%# Save data to localStorage as the user types %>
form.addEventListener('input', (event) => {
const inputData = {};
const formData = new FormData(form);
formData.forEach((value, key) => {
inputData[key] = value;
});

localStorage.setItem(this.formLocalStorageKey(), JSON.stringify(inputData));
});
},
};
};

// Specify the options for your form
const touchpointFormOptions<%= form.short_uuid %> = {
var touchpointFormOptions<%= form.short_uuid %> = {
'formId': "<%= form.short_uuid %>",
'modalButtonText': "<%= form.modal_button_text %>",
'elementSelector': "<%= form.element_selector %>",
Expand Down Expand Up @@ -703,10 +731,10 @@ const touchpointFormOptions<%= form.short_uuid %> = {
}

// Create an instance of a Touchpoints form object
const touchpointForm<%= form.short_uuid %> = new FBAform(document, window).init(touchpointFormOptions<%= form.short_uuid %>);
window.touchpointForm<%= form.short_uuid %> = new FBAform(document, window);
window.touchpointForm<%= form.short_uuid %>.init(touchpointFormOptions<%= form.short_uuid %>);

<%- if form.load_css && form.delivery_method != "touchpoints-hosted-only" %>

// Load the USWDS JS, loads as module 'fbaUswds' in global scope
<%= render partial: 'components/widget/widget-uswds', formats: :js %>

Expand All @@ -724,5 +752,4 @@ const touchpointForm<%= form.short_uuid %> = new FBAform(document, window).init(
fbaUswds.Modal.on(fbaModalElement);
}
})();

<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class SubmissionSpamPreventionMechanism < ActiveRecord::Migration[8.0]
def change
add_column :submissions, :spam_prevention_mechanism, :string, default: "", comment: "Specify which spam prevention mechanism was used, if any."
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

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

Expand Down Expand Up @@ -613,6 +613,7 @@
t.boolean "deleted", default: false
t.datetime "deleted_at"
t.string "preview", default: ""
t.string "spam_prevention_mechanism", default: "", comment: "Specify which spam prevention mechanism was used, if any."
t.index ["archived"], name: "index_submissions_on_archived"
t.index ["created_at"], name: "index_submissions_on_created_at"
t.index ["flagged"], name: "index_submissions_on_flagged"
Expand Down
21 changes: 21 additions & 0 deletions spec/features/touchpoints_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,27 @@
expect(page).to be_axe_clean
end

describe 'persist text responses in localStorage' do
let(:two_question_form) { FactoryBot.create(:form, :two_question_open_ended_form, organization:) }

before do
visit touchpoint_path(two_question_form)
fill_in(two_question_form.ordered_questions.first.ui_selector, with: 'Question one')
fill_in(two_question_form.ordered_questions.last.ui_selector, with: 'Question two')
visit touchpoint_path(two_question_form)
end

it "enters text, refreshes to ensure it still there, submits, and ensures it has been cleared" do
expect(find("#" + two_question_form.ordered_questions.first.ui_selector).value).to eq('Question one')
expect(find("#" + two_question_form.ordered_questions.last.ui_selector).value).to eq('Question two')
click_button 'Submit'
expect(page).to have_content('Thank you. Your feedback has been received.')
visit touchpoint_path(two_question_form)
expect(find("#" + two_question_form.ordered_questions.first.ui_selector).value).to be_blank
expect(find("#" + two_question_form.ordered_questions.last.ui_selector).value).to be_blank
end
end

context 'default success text' do
before do
visit touchpoint_path(form)
Expand Down