blacklight 7.25.1 → 7.25.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +1 -1
- data/.rubocop.yml +1 -0
- data/VERSION +1 -1
- data/app/components/blacklight/search_bar_component.rb +5 -1
- data/app/controllers/concerns/blacklight/bookmarks.rb +6 -3
- data/app/helpers/blacklight/blacklight_helper_behavior.rb +12 -1
- data/app/helpers/blacklight/catalog_helper_behavior.rb +1 -0
- data/app/models/blacklight/icon.rb +7 -1
- data/app/services/blacklight/search_service.rb +4 -0
- data/app/views/catalog/_previous_next_doc.html.erb +1 -0
- data/app/views/catalog/_search_form.html.erb +1 -1
- data/app/views/catalog/_show_main_content.html.erb +1 -1
- data/app/views/catalog/show.html.erb +1 -1
- data/blacklight.gemspec +1 -0
- data/lib/blacklight/configuration.rb +8 -1
- data/lib/blacklight/parameters.rb +97 -1
- data/lib/blacklight/search_builder.rb +3 -2
- data/lib/blacklight/search_state/filter_field.rb +12 -6
- data/lib/blacklight/search_state.rb +10 -64
- data/spec/controllers/bookmarks_controller_spec.rb +2 -1
- data/spec/helpers/blacklight/render_constraints_helper_behavior_spec.rb +1 -1
- data/spec/lib/blacklight/parameters_spec.rb +87 -0
- data/spec/lib/blacklight/search_state/filter_field_spec.rb +1 -14
- data/spec/lib/blacklight/search_state_spec.rb +1 -14
- data/spec/models/blacklight/search_builder_spec.rb +10 -1
- data/spec/models/blacklight/solr/search_builder_spec.rb +3 -1
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c6c1570bf376880fa59c30420730762bf4694b4681856c9b367a6ecd2bb4bb7
|
4
|
+
data.tar.gz: 459f8098798a27f3474f2036143adc65d80955f16136cda54a2a244f86223003
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2fee85026a8eff0f283ea7f4d8936248872cbe0e3e87793ecfc13d479f0233723db03fbc01d418f5e242899a909c115b104fd6b9a680a4d21c5132cfabf2d030
|
7
|
+
data.tar.gz: e0a50dbea2c14447fb1f43b77e1f36f0b93974bfba525af0a9f677252fa19f064a4efd37ef913cd917e4d0d063df3c1600523e8d98be94c15ad083a75c4d1944
|
data/.github/workflows/ruby.yml
CHANGED
@@ -43,7 +43,7 @@ jobs:
|
|
43
43
|
- name: Run tests
|
44
44
|
run: bundle exec rake ci
|
45
45
|
env:
|
46
|
-
ENGINE_CART_RAILS_OPTIONS: '--skip-git --skip-listen --skip-spring --skip-keeps --skip-action-cable --skip-coffee --skip-test'
|
46
|
+
ENGINE_CART_RAILS_OPTIONS: '-a propshaft --skip-git --skip-listen --skip-spring --skip-keeps --skip-action-cable --skip-coffee --skip-test'
|
47
47
|
test_bootstrap5:
|
48
48
|
runs-on: ubuntu-latest
|
49
49
|
strategy:
|
data/.rubocop.yml
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
7.25.
|
1
|
+
7.25.2
|
@@ -10,7 +10,7 @@ module Blacklight
|
|
10
10
|
# rubocop:disable Metrics/ParameterLists
|
11
11
|
def initialize(
|
12
12
|
url:, params:,
|
13
|
-
advanced_search_url: nil,
|
13
|
+
advanced_search_url: nil, presenter: nil,
|
14
14
|
classes: ['search-query-form'], prefix: nil,
|
15
15
|
method: 'GET', q: nil, query_param: :q,
|
16
16
|
search_field: nil, search_fields: nil, autocomplete_path: nil,
|
@@ -29,6 +29,10 @@ module Blacklight
|
|
29
29
|
@autofocus = autofocus
|
30
30
|
@search_fields = search_fields
|
31
31
|
@i18n = i18n
|
32
|
+
return if presenter.nil?
|
33
|
+
|
34
|
+
Deprecation.warn(self, 'SearchBarComponent no longer uses a SearchBarPresenter, the presenter: param will be removed in 8.0. ' \
|
35
|
+
'Set advanced_search.enabled, autocomplete_enabled, and enable_search_bar_autofocus on BlacklightConfiguration')
|
32
36
|
end
|
33
37
|
# rubocop:enable Metrics/ParameterLists
|
34
38
|
|
@@ -75,12 +75,15 @@ module Blacklight::Bookmarks
|
|
75
75
|
|
76
76
|
current_or_guest_user.save! unless current_or_guest_user.persisted?
|
77
77
|
|
78
|
-
|
79
|
-
|
78
|
+
bookmarks_to_add = @bookmarks.reject { |bookmark| current_or_guest_user.bookmarks.where(bookmark).exists? }
|
79
|
+
success = ActiveRecord::Base.transaction do
|
80
|
+
current_or_guest_user.bookmarks.create!(bookmarks_to_add)
|
81
|
+
rescue ActiveRecord::RecordInvalid
|
82
|
+
false
|
80
83
|
end
|
81
84
|
|
82
85
|
if request.xhr?
|
83
|
-
success ? render(json: { bookmarks: { count: current_or_guest_user.bookmarks.count } }) : render(
|
86
|
+
success ? render(json: { bookmarks: { count: current_or_guest_user.bookmarks.count } }) : render(json: current_or_guest_user.errors.full_messages, status: "500")
|
84
87
|
else
|
85
88
|
if @bookmarks.any? && success
|
86
89
|
flash[:notice] = I18n.t('blacklight.bookmarks.add.success', count: @bookmarks.length)
|
@@ -79,7 +79,18 @@ module Blacklight::BlacklightHelperBehavior
|
|
79
79
|
# Render the search navbar
|
80
80
|
# @return [String]
|
81
81
|
def render_search_bar
|
82
|
-
|
82
|
+
if search_bar_presenter_class == Blacklight::SearchBarPresenter && partial_from_blacklight?(Blacklight::SearchBarPresenter.partial)
|
83
|
+
component_class = blacklight_config&.view_config(document_index_view_type)&.search_bar_component || Blacklight::SearchBarComponent
|
84
|
+
component_class.new(
|
85
|
+
url: search_action_url,
|
86
|
+
advanced_search_url: search_action_url(action: 'advanced_search'),
|
87
|
+
params: search_state.params_for_search.except(:qt),
|
88
|
+
search_fields: Deprecation.silence(Blacklight::ConfigurationHelperBehavior) { search_fields },
|
89
|
+
autocomplete_path: search_action_path(action: :suggest)
|
90
|
+
)
|
91
|
+
else
|
92
|
+
search_bar_presenter.render
|
93
|
+
end
|
83
94
|
end
|
84
95
|
deprecation_deprecate render_search_bar: "Call `render Blacklight::SearchBarComponent.new' instead"
|
85
96
|
|
@@ -181,6 +181,7 @@ module Blacklight::CatalogHelperBehavior
|
|
181
181
|
def render_document_main_content_partial(_document = @document)
|
182
182
|
render partial: 'show_main_content'
|
183
183
|
end
|
184
|
+
deprecation_deprecate render_document_main_content_partial: "Use \"render 'show_main_content'\" instead"
|
184
185
|
|
185
186
|
##
|
186
187
|
# Should we display the sort and per page widget?
|
@@ -55,7 +55,10 @@ module Blacklight
|
|
55
55
|
def file_source
|
56
56
|
raise Blacklight::Exceptions::IconNotFound, "Could not find #{path}" if file.blank?
|
57
57
|
|
58
|
-
|
58
|
+
# Handle both Sprockets::Asset and Propshaft::Asset
|
59
|
+
data = file.respond_to?(:source) ? file.source : file.path.read
|
60
|
+
|
61
|
+
data.force_encoding('UTF-8')
|
59
62
|
end
|
60
63
|
|
61
64
|
def ng_xml
|
@@ -68,7 +71,10 @@ module Blacklight
|
|
68
71
|
[icon_name, additional_options[:label_context]].compact.join('_')
|
69
72
|
end
|
70
73
|
|
74
|
+
# @return [Sprockets::Asset,Propshaft::Asset]
|
71
75
|
def file
|
76
|
+
return Rails.application.assets.load_path.find(path) if defined? Propshaft
|
77
|
+
|
72
78
|
# Rails.application.assets is `nil` in production mode (where compile assets is enabled).
|
73
79
|
# This workaround is based off of this comment: https://github.com/fphilipe/premailer-rails/issues/145#issuecomment-225992564
|
74
80
|
(Rails.application.assets || ::Sprockets::Railtie.build_environment(Rails.application)).find_asset(path)
|
@@ -17,6 +17,10 @@ module Blacklight
|
|
17
17
|
search_builder_class.new(self)
|
18
18
|
end
|
19
19
|
|
20
|
+
def search_state_class
|
21
|
+
@search_state.class
|
22
|
+
end
|
23
|
+
|
20
24
|
# a solr query method
|
21
25
|
# @yield [search_builder] optional block yields configured SearchBuilder, caller can modify or create new SearchBuilder to be used. Block should return SearchBuilder to be used.
|
22
26
|
# @return [Blacklight::Solr::Response] the solr response object
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<%= warn "#{
|
1
|
+
<%= warn "#{__FILE__} is a deprecated partial." %>
|
2
2
|
<%= render((blacklight_config&.view_config(document_index_view_type)&.search_bar_component || Blacklight::SearchBarComponent).new(
|
3
3
|
url: search_action_url,
|
4
4
|
advanced_search_url: search_action_url(action: 'advanced_search'),
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<%= render
|
1
|
+
<%= render(Blacklight::SearchContextComponent.new(search_context: @search_context, search_session: search_session)) if search_session['document_id'] == @document.id %>
|
2
2
|
|
3
3
|
<% @page_title = t('blacklight.search.show.title', document_title: Deprecation.silence(Blacklight::BlacklightHelperBehavior) { document_show_html_title }, application_name: application_name).html_safe %>
|
4
4
|
<% content_for(:head) { render_link_rel_alternates } %>
|
data/blacklight.gemspec
CHANGED
@@ -33,6 +33,7 @@ Gem::Specification.new do |s|
|
|
33
33
|
s.add_dependency "i18n", '>= 1.7.0' # added named parameters
|
34
34
|
s.add_dependency "ostruct", '>= 0.3.2'
|
35
35
|
s.add_dependency "view_component", '~> 2.43'
|
36
|
+
s.add_dependency 'hashdiff'
|
36
37
|
|
37
38
|
s.add_development_dependency "rsolr", ">= 1.0.6", "< 3" # Library for interacting with rSolr.
|
38
39
|
s.add_development_dependency "rspec-rails", "~> 5.0"
|
@@ -296,7 +296,7 @@ module Blacklight
|
|
296
296
|
property :enable_search_bar_autofocus, default: false
|
297
297
|
|
298
298
|
BASIC_SEARCH_PARAMETERS = [:q, :qt, :page, :per_page, :search_field, :sort, :controller, :action, :'facet.page', :'facet.prefix', :'facet.sort', :rows, :format].freeze
|
299
|
-
ADVANCED_SEARCH_PARAMETERS = [:
|
299
|
+
ADVANCED_SEARCH_PARAMETERS = [{ clause: {} }, :op].freeze
|
300
300
|
# List the request parameters that compose the SearchState.
|
301
301
|
# If you use a plugin that adds to the search state, then you can add the parameters
|
302
302
|
# by modifiying this field.
|
@@ -305,6 +305,13 @@ module Blacklight
|
|
305
305
|
# @return [Array<Symbol>]
|
306
306
|
property :search_state_fields, default: BASIC_SEARCH_PARAMETERS + ADVANCED_SEARCH_PARAMETERS
|
307
307
|
|
308
|
+
# Have SearchState filter out unknown request parameters
|
309
|
+
#
|
310
|
+
# @!attribute filter_search_state_fields
|
311
|
+
# @since v8.0.0
|
312
|
+
# @return [Boolean]
|
313
|
+
property :filter_search_state_fields, default: false
|
314
|
+
|
308
315
|
##
|
309
316
|
# Create collections of solr field configurations.
|
310
317
|
# This will create array-like accessor methods for
|
@@ -1,6 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'hashdiff'
|
4
|
+
|
2
5
|
module Blacklight
|
3
|
-
|
6
|
+
class Parameters
|
7
|
+
extend Deprecation
|
8
|
+
|
4
9
|
##
|
5
10
|
# Sanitize the search parameters by removing unnecessary parameters
|
6
11
|
# from the provided parameters.
|
@@ -9,5 +14,96 @@ module Blacklight
|
|
9
14
|
params.reject { |_k, v| v.nil? }
|
10
15
|
.except(:action, :controller, :id, :commit, :utf8)
|
11
16
|
end
|
17
|
+
|
18
|
+
# rubocop:disable Naming/MethodParameterName
|
19
|
+
# Merge two Rails strong_params-style permissions into a single list of permitted parameters,
|
20
|
+
# deep-merging complex values as needed.
|
21
|
+
# @param [Array<Symbol, Hash>] a
|
22
|
+
# @param [Array<Symbol, Hash>] b
|
23
|
+
# @return [Array<Symbol, Hash>]
|
24
|
+
def self.deep_merge_permitted_params(a, b)
|
25
|
+
a = [a] if a.is_a? Hash
|
26
|
+
b = [b] if b.is_a? Hash
|
27
|
+
|
28
|
+
complex_params_from_a, scalar_params_from_a = a.flatten.uniq.partition { |x| x.is_a? Hash }
|
29
|
+
complex_params_from_a = complex_params_from_a.inject({}) { |tmp, h| _deep_merge_permitted_param_hashes(h, tmp) }
|
30
|
+
complex_params_from_b, scalar_params_from_b = b.flatten.uniq.partition { |x| x.is_a? Hash }
|
31
|
+
complex_params_from_b = complex_params_from_b.inject({}) { |tmp, h| _deep_merge_permitted_param_hashes(h, tmp) }
|
32
|
+
|
33
|
+
(scalar_params_from_a + scalar_params_from_b + [_deep_merge_permitted_param_hashes(complex_params_from_a, complex_params_from_b)]).reject(&:blank?).uniq
|
34
|
+
end
|
35
|
+
|
36
|
+
private_class_method def self._deep_merge_permitted_param_hashes(h1, h2)
|
37
|
+
h1.merge(h2) do |_key, old_value, new_value|
|
38
|
+
if (old_value.is_a?(Hash) && old_value.empty?) || (new_value.is_a?(Hash) && new_value.empty?)
|
39
|
+
{}
|
40
|
+
elsif old_value.is_a?(Hash) && new_value.is_a?(Hash)
|
41
|
+
_deep_merge_permitted_param_hashes(old_value, new_value)
|
42
|
+
elsif old_value.is_a?(Array) || new_value.is_a?(Array)
|
43
|
+
deep_merge_permitted_params(old_value, new_value)
|
44
|
+
else
|
45
|
+
new_value
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
# rubocop:enable Naming/MethodParameterName
|
50
|
+
|
51
|
+
attr_reader :params, :search_state
|
52
|
+
|
53
|
+
delegate :blacklight_config, :filter_fields, to: :search_state
|
54
|
+
|
55
|
+
def initialize(params, search_state)
|
56
|
+
@params = params.is_a?(Hash) ? params.with_indifferent_access : params
|
57
|
+
@search_state = search_state
|
58
|
+
end
|
59
|
+
|
60
|
+
# @param [Hash] params with unknown structure (not declared in the blacklight config or filters) stripped out
|
61
|
+
def permit_search_params
|
62
|
+
# if the parameters were generated internally, we can (probably) trust that they're fine
|
63
|
+
return params unless params.is_a?(ActionController::Parameters)
|
64
|
+
|
65
|
+
# if the parameters were permitted already, we should be able to trust them
|
66
|
+
return params if params.permitted?
|
67
|
+
|
68
|
+
permitted_params = filter_fields.inject(blacklight_config.search_state_fields) do |allowlist, filter|
|
69
|
+
Blacklight::Parameters.deep_merge_permitted_params(allowlist, filter.permitted_params)
|
70
|
+
end
|
71
|
+
|
72
|
+
deep_unmangle_params!(params, permitted_params)
|
73
|
+
|
74
|
+
if blacklight_config.filter_search_state_fields
|
75
|
+
params.permit(*permitted_params)
|
76
|
+
else
|
77
|
+
warn_about_deprecated_parameter_handling(params, permitted_params)
|
78
|
+
params.deep_dup.permit!
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def warn_about_deprecated_parameter_handling(params, permitted_params)
|
85
|
+
diff = Hashdiff.diff(params.to_unsafe_h, params.permit(*permitted_params).to_h)
|
86
|
+
return if diff.empty?
|
87
|
+
|
88
|
+
Deprecation.warn(Blacklight::Parameters, "Blacklight 8 will filter out non-search parameter, including: #{diff.map { |_op, key, *| key }.to_sentence}")
|
89
|
+
end
|
90
|
+
|
91
|
+
# Facebook's crawler turns array query parameters into a hash with numeric keys. Once we know
|
92
|
+
# the expected parameter structure, we can unmangle those parameters to match our expected values.
|
93
|
+
def deep_unmangle_params!(params, permitted_params)
|
94
|
+
permitted_params.select { |p| p.is_a?(Hash) }.each do |permission|
|
95
|
+
permission.each do |key, permitted_value|
|
96
|
+
if params[key].is_a?(ActionController::Parameters) && permitted_value.is_a?(Hash)
|
97
|
+
deep_unmangle_params!(params[key], [permitted_value])
|
98
|
+
elsif permitted_value.is_a?(Array) && permitted_value.empty?
|
99
|
+
if params[key].is_a?(ActionController::Parameters) && params[key]&.keys&.all? { |k| k.to_s =~ /\A\d+\z/ }
|
100
|
+
params[key] = params[key].values
|
101
|
+
elsif params[key].is_a?(String)
|
102
|
+
params[key] = Array(params[key])
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
12
108
|
end
|
13
109
|
end
|
@@ -27,7 +27,8 @@ module Blacklight
|
|
27
27
|
end
|
28
28
|
|
29
29
|
@blacklight_params = {}
|
30
|
-
|
30
|
+
search_state_class = @scope&.search_state_class || Blacklight::SearchState
|
31
|
+
@search_state = search_state_class.new(@blacklight_params, @scope&.blacklight_config, @scope)
|
31
32
|
@additional_filters = {}
|
32
33
|
@merged_params = {}
|
33
34
|
@reverse_merged_params = {}
|
@@ -48,7 +49,7 @@ module Blacklight
|
|
48
49
|
Deprecation.warn(Blacklight::SearchBuilder, "SearchBuilder#where must be called with a hash, received #{conditions.inspect}.") unless conditions.is_a? Hash
|
49
50
|
params_will_change!
|
50
51
|
@search_state = @search_state.reset(@search_state.params.merge(q: conditions))
|
51
|
-
@blacklight_params = @search_state.params
|
52
|
+
@blacklight_params = @search_state.params
|
52
53
|
@additional_filters = conditions
|
53
54
|
self
|
54
55
|
end
|
@@ -159,12 +159,18 @@ module Blacklight
|
|
159
159
|
end
|
160
160
|
end
|
161
161
|
|
162
|
-
def
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
162
|
+
def permitted_params
|
163
|
+
if config.pivot
|
164
|
+
{
|
165
|
+
filters_key => config.pivot.each_with_object({}) { |key, filter| filter.merge(key => [], "-#{key}" => []) },
|
166
|
+
inclusive_filters_key => config.pivot.each_with_object({}) { |key, filter| filter.merge(key => []) }
|
167
|
+
}
|
168
|
+
else
|
169
|
+
{
|
170
|
+
filters_key => { config.key => [], "-#{config.key}" => [] },
|
171
|
+
inclusive_filters_key => { config.key => [] }
|
172
|
+
}
|
173
|
+
end
|
168
174
|
end
|
169
175
|
|
170
176
|
private
|
@@ -19,71 +19,17 @@ module Blacklight
|
|
19
19
|
|
20
20
|
delegate :facet_configuration_for_field, to: :blacklight_config
|
21
21
|
|
22
|
-
def self.modifiable_params(params)
|
23
|
-
if params.respond_to?(:to_unsafe_h)
|
24
|
-
# This is the typical (not-ActionView::TestCase) code path.
|
25
|
-
params = params.to_unsafe_h
|
26
|
-
# In Rails 5 to_unsafe_h returns a HashWithIndifferentAccess, in Rails 4 it returns Hash
|
27
|
-
params = params.with_indifferent_access if params.instance_of? Hash
|
28
|
-
elsif params.is_a? Hash
|
29
|
-
# This is an ActionView::TestCase workaround for Rails 4.2.
|
30
|
-
params = params.dup.with_indifferent_access
|
31
|
-
else
|
32
|
-
params = params.dup.to_h.with_indifferent_access
|
33
|
-
end
|
34
|
-
params
|
35
|
-
end
|
36
|
-
|
37
22
|
# @param [ActionController::Parameters] params
|
38
23
|
# @param [Blacklight::Config] blacklight_config
|
39
24
|
# @param [ApplicationController] controller used for the routing helpers
|
40
25
|
def initialize(params, blacklight_config, controller = nil)
|
41
26
|
@blacklight_config = blacklight_config
|
42
27
|
@controller = controller
|
43
|
-
@params =
|
44
|
-
normalize_params! if needs_normalization?
|
45
|
-
end
|
46
|
-
|
47
|
-
def needs_normalization?
|
48
|
-
return false if params.blank?
|
49
|
-
return true if (params.keys.map(&:to_s) - permitted_fields.map(&:to_s)).present?
|
50
|
-
|
51
|
-
!!filters.detect { |filter| filter.values.detect { |value| filter.needs_normalization?(value) } }
|
52
|
-
end
|
53
|
-
|
54
|
-
def normalize_params!
|
55
|
-
@params = normalize_params
|
56
|
-
end
|
57
|
-
|
58
|
-
def normalize_params
|
59
|
-
return params unless needs_normalization?
|
60
|
-
|
61
|
-
base_params = params.slice(*blacklight_config.search_state_fields)
|
62
|
-
normal_state = blacklight_config.facet_fields.each_value.inject(reset(base_params)) do |working_state, filter_key|
|
63
|
-
f = filter(filter_key)
|
64
|
-
next working_state unless f.any?
|
65
|
-
|
66
|
-
filter_values = f.values(except: [:inclusive_filters]).inject([]) do |memo, filter_value|
|
67
|
-
# flatten arrays that had been mangled into integer-indexed hashes
|
68
|
-
memo.concat([f.normalize(filter_value)].flatten)
|
69
|
-
end
|
70
|
-
filter_values = f.values(except: [:filters, :missing]).inject(filter_values) do |memo, filter_value|
|
71
|
-
memo << f.normalize(filter_value)
|
72
|
-
end
|
73
|
-
filter_values.inject(working_state) do |memo, filter_value|
|
74
|
-
memo.filter(filter_key).add(filter_value)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
normal_state.params
|
78
|
-
end
|
79
|
-
|
80
|
-
def permitted_fields
|
81
|
-
filter_keys = filter_fields.inject(Set.new) { |memo, filter| memo.merge [filter.filters_key, filter.inclusive_filters_key] }
|
82
|
-
blacklight_config.search_state_fields + filter_keys.subtract([nil, '']).to_a
|
28
|
+
@params = Blacklight::Parameters.new(params, self).permit_search_params.to_h.with_indifferent_access
|
83
29
|
end
|
84
30
|
|
85
31
|
def to_hash
|
86
|
-
|
32
|
+
params.deep_dup
|
87
33
|
end
|
88
34
|
alias to_h to_hash
|
89
35
|
|
@@ -132,7 +78,7 @@ module Blacklight
|
|
132
78
|
|
133
79
|
# @return [Blacklight::SearchState]
|
134
80
|
def reset(params = nil)
|
135
|
-
self.class.new(params ||
|
81
|
+
self.class.new(params || {}, blacklight_config, controller)
|
136
82
|
end
|
137
83
|
|
138
84
|
# @return [Blacklight::SearchState]
|
@@ -162,6 +108,10 @@ module Blacklight
|
|
162
108
|
p
|
163
109
|
end
|
164
110
|
|
111
|
+
def filter_fields
|
112
|
+
blacklight_config.facet_fields.each_value.map { |value| filter(value) }
|
113
|
+
end
|
114
|
+
|
165
115
|
def filters
|
166
116
|
@filters ||= filter_fields.select(&:any?)
|
167
117
|
end
|
@@ -192,7 +142,7 @@ module Blacklight
|
|
192
142
|
# catalog/index with their new facet choice.
|
193
143
|
def add_facet_params_and_redirect(field, item)
|
194
144
|
new_params = Deprecation.silence(self.class) do
|
195
|
-
add_facet_params(field, item)
|
145
|
+
add_facet_params(field, item).to_h.with_indifferent_access
|
196
146
|
end
|
197
147
|
|
198
148
|
# Delete any request params from facet-specific action, needed
|
@@ -229,7 +179,7 @@ module Blacklight
|
|
229
179
|
# @yield [params] The merged parameters hash before being sanitized
|
230
180
|
def params_for_search(params_to_merge = {})
|
231
181
|
# params hash we'll return
|
232
|
-
my_params =
|
182
|
+
my_params = to_h.merge(self.class.new(params_to_merge, blacklight_config, controller))
|
233
183
|
|
234
184
|
if block_given?
|
235
185
|
yield my_params
|
@@ -297,11 +247,7 @@ module Blacklight
|
|
297
247
|
# and need to be reset when e.g. constraints change
|
298
248
|
# @return [ActionController::Parameters]
|
299
249
|
def reset_search_params
|
300
|
-
Parameters.sanitize(
|
301
|
-
end
|
302
|
-
|
303
|
-
def filter_fields
|
304
|
-
blacklight_config.facet_fields.each_value.map { |value| filter(value) }
|
250
|
+
Parameters.sanitize(to_h).except(:page, :counter)
|
305
251
|
end
|
306
252
|
end
|
307
253
|
end
|
@@ -24,7 +24,8 @@ RSpec.describe BookmarksController do
|
|
24
24
|
allow(@controller).to receive_message_chain(:current_or_guest_user, :existing_bookmark_for).and_return(false)
|
25
25
|
allow(@controller).to receive_message_chain(:current_or_guest_user, :persisted?).and_return(true)
|
26
26
|
allow(@controller).to receive_message_chain(:current_or_guest_user, :bookmarks, :where, :exists?).and_return(false)
|
27
|
-
allow(@controller).to receive_message_chain(:current_or_guest_user, :bookmarks, :create).
|
27
|
+
allow(@controller).to receive_message_chain(:current_or_guest_user, :bookmarks, :create!).and_raise(ActiveRecord::RecordInvalid)
|
28
|
+
allow(@controller).to receive_message_chain(:current_or_guest_user, :errors, :full_messages).and_return([1])
|
28
29
|
put :update, xhr: true, params: { id: 'iamabooboo', format: :js }
|
29
30
|
expect(response.code).to eq "500"
|
30
31
|
end
|
@@ -27,7 +27,7 @@ RSpec.describe Blacklight::RenderConstraintsHelperBehavior do
|
|
27
27
|
let(:params) { ActionController::Parameters.new(q: 'foobar', f: { type: 'journal' }) }
|
28
28
|
|
29
29
|
it "has a link relative to the current url" do
|
30
|
-
expect(subject).to have_link 'Remove constraint', href: '/catalog?f%5Btype%5D=journal'
|
30
|
+
expect(subject).to have_link 'Remove constraint', href: '/catalog?f%5Btype%5D%5B%5D=journal'
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
@@ -21,4 +21,91 @@ RSpec.describe Blacklight::Parameters do
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
24
|
+
|
25
|
+
describe '.deep_merge_permitted_params' do
|
26
|
+
it 'merges scalar values' do
|
27
|
+
expect(described_class.deep_merge_permitted_params([:a], [:b])).to eq [:a, :b]
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'appends complex values' do
|
31
|
+
expect(described_class.deep_merge_permitted_params([:a], { b: [] })).to eq [:a, { b: [] }]
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'merges lists of scalar values' do
|
35
|
+
expect(described_class.deep_merge_permitted_params({ f: [:a, :b] }, { f: [:b, :c] })).to eq [{ f: [:a, :b, :c] }]
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'merges complex value data structures' do
|
39
|
+
expect(described_class.deep_merge_permitted_params([{ f: { field1: [] } }], { f: { field2: [] } })).to eq [{ f: { field1: [], field2: [] } }]
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'takes the most permissive value' do
|
43
|
+
expect(described_class.deep_merge_permitted_params([{ f: {} }], { f: { field2: [] } })).to eq [{ f: {} }]
|
44
|
+
expect(described_class.deep_merge_permitted_params([{ f: {} }], { f: [:some_value] })).to eq [{ f: {} }]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#permit_search_params' do
|
49
|
+
subject(:params) { described_class.new(query_params, search_state) }
|
50
|
+
|
51
|
+
let(:query_params) { ActionController::Parameters.new(a: 1, b: 2, c: []) }
|
52
|
+
let(:search_state) { Blacklight::SearchState.new(query_params, blacklight_config) }
|
53
|
+
let(:blacklight_config) { Blacklight::Configuration.new }
|
54
|
+
|
55
|
+
context 'with facebooks badly mangled query parameters' do
|
56
|
+
let(:query_params) do
|
57
|
+
ActionController::Parameters.new(
|
58
|
+
f: { field: { '0': 'first', '1': 'second' } },
|
59
|
+
f_inclusive: { field: { '0': 'first', '1': 'second' } }
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
before do
|
64
|
+
blacklight_config.add_facet_field 'field'
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'normalizes the facets to the expected format' do
|
68
|
+
expect(params.permit_search_params.to_h.with_indifferent_access).to include f: { field: %w[first second] }, f_inclusive: { field: %w[first second] }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'with filter_search_state_fields set to false' do
|
73
|
+
let(:blacklight_config) { Blacklight::Configuration.new(filter_search_state_fields: false) }
|
74
|
+
|
75
|
+
it 'allows all params, but warns about the behavior' do
|
76
|
+
allow(Deprecation).to receive(:warn)
|
77
|
+
expect(params.permit_search_params.to_h.with_indifferent_access).to include(a: 1, b: 2, c: [])
|
78
|
+
|
79
|
+
expect(Deprecation).to have_received(:warn).with(described_class, /including: a, b, and c/).at_least(:once)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'with filter_search_state_fields set to true' do
|
84
|
+
let(:blacklight_config) { Blacklight::Configuration.new(filter_search_state_fields: true) }
|
85
|
+
|
86
|
+
it 'rejects unknown params' do
|
87
|
+
expect(params.permit_search_params.to_h).to be_empty
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'with some search parameters' do
|
91
|
+
let(:query_params) { ActionController::Parameters.new(q: 'abc', page: 5, f: { facet_field: %w[a b], unknown_field: ['a'] }) }
|
92
|
+
|
93
|
+
before do
|
94
|
+
blacklight_config.add_facet_field 'facet_field'
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'allows scalar params' do
|
98
|
+
expect(params.permit_search_params.to_h.with_indifferent_access).to include(q: 'abc', page: 5)
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'allows facet params' do
|
102
|
+
expect(params.permit_search_params.to_h.with_indifferent_access).to include(f: { facet_field: %w[a b] })
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'removes unknown facet fields parameters' do
|
106
|
+
expect(params.permit_search_params.to_h.with_indifferent_access[:f]).not_to include(:unknown_field)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
24
111
|
end
|
@@ -6,6 +6,7 @@ RSpec.describe Blacklight::SearchState::FilterField do
|
|
6
6
|
let(:params) { { f: { some_field: %w[1 2], another_field: ['3'] } } }
|
7
7
|
let(:blacklight_config) do
|
8
8
|
Blacklight::Configuration.new.configure do |config|
|
9
|
+
config.add_facet_field 'new_field'
|
9
10
|
config.add_facet_field 'another_field', single: true
|
10
11
|
simple_facet_fields.each { |simple_facet_field| config.add_facet_field simple_facet_field }
|
11
12
|
config.search_state_fields = config.search_state_fields + additional_search_fields
|
@@ -23,14 +24,6 @@ RSpec.describe Blacklight::SearchState::FilterField do
|
|
23
24
|
expect(new_state.filter('some_field').values).to eq %w[1 2 4]
|
24
25
|
end
|
25
26
|
|
26
|
-
it 'creates new parameter as needed' do
|
27
|
-
filter = search_state.filter('unknown_field')
|
28
|
-
new_state = filter.add('4')
|
29
|
-
|
30
|
-
expect(new_state.filter('unknown_field').values).to eq %w[4]
|
31
|
-
expect(new_state.params[:f]).to include(:unknown_field)
|
32
|
-
end
|
33
|
-
|
34
27
|
context 'without any parameters in the url' do
|
35
28
|
let(:params) { {} }
|
36
29
|
|
@@ -205,10 +198,4 @@ RSpec.describe Blacklight::SearchState::FilterField do
|
|
205
198
|
expect(search_state.filter('some_field').include?(OpenStruct.new(value: '1'))).to eq true
|
206
199
|
end
|
207
200
|
end
|
208
|
-
|
209
|
-
describe '#needs_normalization?' do
|
210
|
-
it 'returns false for Blacklight::SearchState::FilterField::MISSING' do
|
211
|
-
expect(search_state.filter('some_field').needs_normalization?(Blacklight::SearchState::FilterField::MISSING)).to be false
|
212
|
-
end
|
213
|
-
end
|
214
201
|
end
|
@@ -54,19 +54,6 @@ RSpec.describe Blacklight::SearchState do
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
-
context 'with facebooks badly mangled query parameters' do
|
58
|
-
let(:simple_facet_fields) { [:field] }
|
59
|
-
let(:params) do
|
60
|
-
{ f: { field: { '0': 'first', '1': 'second' } },
|
61
|
-
f_inclusive: { field: { '0': 'first', '1': 'second' } } }
|
62
|
-
end
|
63
|
-
|
64
|
-
it 'normalizes the facets to the expected format' do
|
65
|
-
expect(search_state.to_h).to include f: { field: %w[first second] }
|
66
|
-
expect(search_state.to_h).to include f_inclusive: { field: %w[first second] }
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
57
|
context 'deleting item from to_h' do
|
71
58
|
let(:additional_search_fields) { [:q_1] }
|
72
59
|
let(:params) { { q: 'foo', q_1: 'bar' } }
|
@@ -81,7 +68,7 @@ RSpec.describe Blacklight::SearchState do
|
|
81
68
|
end
|
82
69
|
|
83
70
|
context 'deleting deep item from to_h' do
|
84
|
-
let(:additional_search_fields) { [:
|
71
|
+
let(:additional_search_fields) { [{ foo: {} }] }
|
85
72
|
let(:params) { { foo: { bar: [] } } }
|
86
73
|
|
87
74
|
it 'does not mutate search_state to deep mutate search_state.to_h' do
|
@@ -5,7 +5,7 @@ RSpec.describe Blacklight::SearchBuilder, api: true do
|
|
5
5
|
|
6
6
|
let(:processor_chain) { [] }
|
7
7
|
let(:blacklight_config) { Blacklight::Configuration.new }
|
8
|
-
let(:scope) { double blacklight_config: blacklight_config }
|
8
|
+
let(:scope) { double blacklight_config: blacklight_config, search_state_class: nil }
|
9
9
|
|
10
10
|
context "with default processor chain" do
|
11
11
|
subject { described_class.new scope }
|
@@ -15,6 +15,15 @@ RSpec.describe Blacklight::SearchBuilder, api: true do
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
context "with scope search_state_class" do
|
19
|
+
let(:state_class) { Class.new(Blacklight::SearchState) }
|
20
|
+
let(:scope) { double blacklight_config: blacklight_config, search_state_class: state_class }
|
21
|
+
|
22
|
+
it "uses the class-level default_processor_chain" do
|
23
|
+
expect(subject.search_state).to be_a state_class
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
18
27
|
describe "#with" do
|
19
28
|
it "sets the blacklight params" do
|
20
29
|
params = {}
|
@@ -216,7 +216,9 @@ RSpec.describe Blacklight::Solr::SearchBuilderBehavior, api: true do
|
|
216
216
|
expect(subject["spellcheck.q"]).to be_blank
|
217
217
|
|
218
218
|
single_facet.each_value do |value|
|
219
|
-
|
219
|
+
Array(value).each do |v|
|
220
|
+
expect(subject[:fq]).to include("{!term f=#{single_facet.keys[0]}}#{v}")
|
221
|
+
end
|
220
222
|
end
|
221
223
|
end
|
222
224
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blacklight
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.25.
|
4
|
+
version: 7.25.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Rochkind
|
@@ -17,7 +17,7 @@ authors:
|
|
17
17
|
autorequire:
|
18
18
|
bindir: exe
|
19
19
|
cert_chain: []
|
20
|
-
date: 2022-05-
|
20
|
+
date: 2022-05-19 00:00:00.000000000 Z
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
23
23
|
name: rails
|
@@ -137,6 +137,20 @@ dependencies:
|
|
137
137
|
- - "~>"
|
138
138
|
- !ruby/object:Gem::Version
|
139
139
|
version: '2.43'
|
140
|
+
- !ruby/object:Gem::Dependency
|
141
|
+
name: hashdiff
|
142
|
+
requirement: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
type: :runtime
|
148
|
+
prerelease: false
|
149
|
+
version_requirements: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - ">="
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
140
154
|
- !ruby/object:Gem::Dependency
|
141
155
|
name: rsolr
|
142
156
|
requirement: !ruby/object:Gem::Requirement
|