diff --git a/app/assets/javascripts/filters.js b/app/assets/javascripts/filters.js index 534296e3f..11b35ba47 100644 --- a/app/assets/javascripts/filters.js +++ b/app/assets/javascripts/filters.js @@ -16,6 +16,23 @@ document.addEventListener("turbolinks:load", function() { } }); + // Turn obfuscated filter links into real ones + $('[data-filter-link]').each(function () { + const element = $(this)[0]; + const a = document.createElement('a'); + a.href = atob(element.dataset['filterLink'].replace(/-/g, '+').replace(/_/g, '/')); + // Copy attributes + for (const attr of element.attributes) { + if (attr.name !== 'data-filter-link') { + a.setAttribute(attr.name, attr.value); + } + } + // Move child elements + while (element.firstChild) { + a.appendChild(element.firstChild); + } + element.replaceWith(a); + }); }); function updateShowMore($el) { diff --git a/app/assets/javascripts/show_private_groups.js b/app/assets/javascripts/show_private_groups.js index e6f9a5c41..70f219f88 100644 --- a/app/assets/javascripts/show_private_groups.js +++ b/app/assets/javascripts/show_private_groups.js @@ -1,21 +1,25 @@ // executed for space form function show_private_groups() { - let checkbox = document.getElementById("space_is_private") - let container = document.getElementById("groups_container") + let checkbox = document.getElementById("space_is_private"); + let container = document.getElementById("groups_container"); + + if (!checkbox || !container) { + return; + } const toggleGroups = () => { if (checkbox && checkbox.checked) { - container.style.display = "block" + container.style.display = "block"; } else { - container.style.display = "none" + container.style.display = "none"; } } if (checkbox) { - checkbox.addEventListener("change", toggleGroups) + checkbox.addEventListener("change", toggleGroups); } - toggleGroups() + toggleGroups(); } window.addEventListener('turbolinks:load', function() { diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index dcd7bd6ea..83b7f0d21 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -1,3 +1,5 @@ +require 'base64' + # The helper for searches module SearchHelper @@ -43,7 +45,13 @@ def filter_link(name, value, count, html_options = {}, &block) content_tag(:span, html_options, &content) else - link_to parameters, html_options, &content + link = url_for(parameters) + + if TeSS::Config.obfuscate_filters + content_tag('span', html_options.merge('data-filter-link': Base64.urlsafe_encode64(link, padding: false)), &content) + else + link_to(link, html_options, &content) + end end end diff --git a/config/tess.example.yml b/config/tess.example.yml index 7e05df924..5d0352295 100644 --- a/config/tess.example.yml +++ b/config/tess.example.yml @@ -289,6 +289,7 @@ default: &default primary: '#260252' secondary: '#5c29b1' filter_limit: # Maximum number of filter values allowed for anonymous users + obfuscate_filters: false # Whether to obfuscate filter links from initial HTML load to deter crawlers development: <<: *default diff --git a/test/controllers/materials_controller_test.rb b/test/controllers/materials_controller_test.rb index 6c181f12a..21064bf9a 100644 --- a/test/controllers/materials_controller_test.rb +++ b/test/controllers/materials_controller_test.rb @@ -1842,4 +1842,22 @@ class MaterialsControllerTest < ActionController::TestCase end end end + + test 'filters can be obfuscated' do + Material.stub(:search_and_filter, MockSearch.new(Material.all)) do + with_settings(solr_enabled: true, obfuscate_filters: false) do + get :index + + assert_select 'a.facet-option[href=?]', materials_path(target_audience: 'Fish') + assert_select 'span.facet-option', count: 0 + end + + with_settings(solr_enabled: true, obfuscate_filters: true) do + get :index + + assert_select 'a.facet-option[href=?]', materials_path(target_audience: 'Fish'), count: 0 + assert_select 'span.facet-option[data-filter-link]' + end + end + end end