blacklight 9.0.0.beta8 → 9.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.env +3 -3
- data/.github/matrix.json +18 -34
- data/.github/workflows/lint.yml +1 -1
- data/.github/workflows/test.yml +4 -3
- data/.rubocop.yml +72 -7
- data/.rubocop_todo.yml +142 -268
- data/Gemfile +1 -1
- data/README.md +4 -11
- data/VERSION +1 -1
- data/app/assets/builds/blacklight.css +37 -23
- data/app/assets/images/blacklight/logo-dark.svg +184 -0
- data/app/assets/images/blacklight/logo.svg +184 -0
- data/app/assets/javascripts/blacklight/blacklight.esm.js +134 -48
- data/app/assets/javascripts/blacklight/blacklight.esm.js.map +1 -1
- data/app/assets/javascripts/blacklight/blacklight.js +134 -48
- data/app/assets/javascripts/blacklight/blacklight.js.map +1 -1
- data/app/assets/stylesheets/blacklight/_balanced_list.scss +1 -1
- data/app/assets/stylesheets/blacklight/_bootstrap_overrides.scss +17 -0
- data/app/assets/stylesheets/blacklight/_constraints.scss +2 -2
- data/app/assets/stylesheets/blacklight/_facets.scss +14 -10
- data/app/assets/stylesheets/blacklight/_header.scss +0 -4
- data/app/assets/stylesheets/blacklight/blacklight_defaults.scss +5 -9
- data/app/components/blacklight/facets/filters_component.rb +1 -1
- data/app/components/blacklight/facets/inclusive_constraint_component.html.erb +1 -1
- data/app/components/blacklight/facets/suggest_component.html.erb +17 -12
- data/app/components/blacklight/icons/checkmark_component.rb +16 -0
- data/app/components/blacklight/icons/circle_half_component.rb +16 -0
- data/app/components/blacklight/icons/moon_stars_component.rb +17 -0
- data/app/components/blacklight/icons/sun_component.rb +16 -0
- data/app/components/blacklight/metadata_field_component.rb +1 -1
- data/app/components/blacklight/search_navbar_component.html.erb +1 -1
- data/app/components/blacklight/theme_switcher_component.html.erb +31 -0
- data/app/components/blacklight/theme_switcher_component.rb +6 -0
- data/app/components/blacklight/top_navbar_component.html.erb +1 -1
- data/app/controllers/concerns/blacklight/bookmarks.rb +8 -14
- data/app/controllers/concerns/blacklight/catalog.rb +12 -14
- data/app/controllers/concerns/blacklight/controller.rb +54 -11
- data/app/controllers/concerns/blacklight/guest_user.rb +51 -0
- data/app/controllers/concerns/blacklight/search_context.rb +1 -1
- data/app/controllers/concerns/blacklight/search_history.rb +3 -1
- data/app/controllers/concerns/blacklight/searchable.rb +18 -0
- data/app/helpers/blacklight/layout_helper_behavior.rb +7 -0
- data/app/javascript/blacklight-frontend/checkbox_submit.js +2 -2
- data/app/javascript/blacklight-frontend/color_theme_switcher.js +85 -0
- data/app/javascript/blacklight-frontend/facet_suggest.js +4 -1
- data/app/javascript/blacklight-frontend/index.js +2 -0
- data/app/models/concerns/blacklight/document/dublin_core.rb +2 -2
- data/app/models/concerns/blacklight/document.rb +1 -1
- data/app/models/concerns/blacklight/user.rb +36 -0
- data/app/presenters/blacklight/field_presenter.rb +1 -1
- data/app/presenters/blacklight/rendering/microdata.rb +1 -0
- data/app/services/blacklight/bookmarks_search_builder.rb +4 -4
- data/app/services/blacklight/search_service.rb +50 -35
- data/app/values/blacklight/types.rb +1 -1
- data/app/views/catalog/_email_form.html.erb +3 -3
- data/app/views/catalog/_home_text.html.erb +1 -1
- data/app/views/catalog/_sms_form.html.erb +4 -4
- data/app/views/catalog/index.rss.builder +4 -4
- data/app/views/catalog/opensearch.json.jbuilder +3 -0
- data/app/views/catalog/opensearch.xml.builder +2 -2
- data/app/views/layouts/blacklight/base.html.erb +13 -1
- data/app/views/shared/_user_util_links.html.erb +18 -5
- data/blacklight.gemspec +3 -3
- data/config/locales/blacklight.ar.yml +5 -0
- data/config/locales/blacklight.ca.yml +5 -0
- data/config/locales/blacklight.de.yml +6 -2
- data/config/locales/blacklight.en.yml +5 -0
- data/config/locales/blacklight.es.yml +5 -0
- data/config/locales/blacklight.fr.yml +5 -0
- data/config/locales/blacklight.hu.yml +5 -0
- data/config/locales/blacklight.it.yml +6 -2
- data/config/locales/blacklight.nl.yml +5 -0
- data/config/locales/blacklight.pt-BR.yml +6 -2
- data/config/locales/blacklight.sq.yml +5 -0
- data/config/locales/blacklight.zh.yml +5 -0
- data/lib/blacklight/component.rb +1 -1
- data/lib/blacklight/configuration/action_config_map_entry.rb +31 -0
- data/lib/blacklight/configuration/display_field.rb +1 -0
- data/lib/blacklight/configuration/field.rb +6 -6
- data/lib/blacklight/configuration/fields.rb +1 -1
- data/lib/blacklight/configuration/sort_field.rb +2 -2
- data/lib/blacklight/configuration.rb +30 -23
- data/lib/blacklight/parameters.rb +1 -1
- data/lib/blacklight/routes/searchable.rb +1 -1
- data/lib/blacklight/search_builder.rb +208 -10
- data/lib/blacklight/search_state.rb +4 -0
- data/lib/blacklight/solr/request.rb +8 -1
- data/lib/blacklight/solr/response/group_response.rb +1 -1
- data/lib/blacklight/solr/response/spelling.rb +8 -8
- data/lib/blacklight/solr/response.rb +2 -21
- data/lib/blacklight/solr/search_builder_behavior.rb +59 -16
- data/lib/blacklight.rb +2 -2
- data/lib/generators/blacklight/assets/importmap_generator.rb +2 -2
- data/lib/generators/blacklight/assets_generator.rb +1 -1
- data/lib/generators/blacklight/controller_generator.rb +1 -1
- data/lib/generators/blacklight/document_generator.rb +1 -1
- data/lib/generators/blacklight/install_generator.rb +6 -4
- data/lib/generators/blacklight/models_generator.rb +1 -1
- data/lib/generators/blacklight/search_builder_generator.rb +1 -1
- data/lib/generators/blacklight/solr_generator.rb +1 -1
- data/lib/generators/blacklight/templates/catalog_controller.rb +21 -2
- data/lib/generators/blacklight/templates/solr/conf/schema.xml +264 -178
- data/lib/generators/blacklight/templates/solr/conf/solrconfig.xml +6 -14
- data/lib/generators/blacklight/test_support_generator.rb +1 -1
- data/lib/generators/blacklight/user_generator.rb +66 -5
- data/lib/railties/blacklight.rake +4 -4
- data/package.json +2 -2
- data/spec/components/blacklight/document/action_component_spec.rb +1 -1
- data/spec/components/blacklight/document/group_component_spec.rb +1 -1
- data/spec/components/blacklight/document_component_spec.rb +9 -9
- data/spec/components/blacklight/facet_component_spec.rb +2 -2
- data/spec/components/blacklight/facets/filters_component_spec.rb +1 -1
- data/spec/components/blacklight/facets/index_navigation_component_spec.rb +1 -2
- data/spec/components/blacklight/facets/list_component_spec.rb +1 -1
- data/spec/components/blacklight/facets/suggest_component_spec.rb +2 -3
- data/spec/components/blacklight/metadata_field_component_spec.rb +26 -0
- data/spec/components/blacklight/skip_link_component_spec.rb +2 -2
- data/spec/controllers/application_controller_spec.rb +71 -0
- data/spec/controllers/bookmarks_controller_spec.rb +18 -5
- data/spec/controllers/catalog_controller_spec.rb +7 -5
- data/spec/features/advanced_search_spec.rb +29 -16
- data/spec/features/alternate_controller_spec.rb +4 -4
- data/spec/features/axe_spec.rb +34 -22
- data/spec/features/bookmarks_spec.rb +13 -13
- data/spec/features/dark_mode_spec.rb +42 -0
- data/spec/features/did_you_mean_spec.rb +27 -27
- data/spec/features/facet_missing_spec.rb +3 -3
- data/spec/features/facets_spec.rb +9 -0
- data/spec/features/record_view_spec.rb +14 -14
- data/spec/features/search_context_spec.rb +8 -8
- data/spec/features/search_filters_spec.rb +16 -16
- data/spec/features/search_formats_spec.rb +2 -2
- data/spec/features/search_history_spec.rb +11 -11
- data/spec/features/search_pagination_spec.rb +17 -17
- data/spec/features/search_results_spec.rb +3 -3
- data/spec/features/search_sort_spec.rb +4 -4
- data/spec/features/search_spec.rb +20 -20
- data/spec/helpers/blacklight/url_helper_behavior_spec.rb +15 -15
- data/spec/helpers/blacklight_helper_spec.rb +1 -1
- data/spec/helpers/catalog_helper_spec.rb +7 -7
- data/spec/integration/generators/blacklight/user_generator_spec.rb +60 -0
- data/spec/lib/blacklight/search_state/pivot_filter_field_spec.rb +1 -1
- data/spec/models/blacklight/configurable_spec.rb +1 -1
- data/spec/models/blacklight/configuration_spec.rb +0 -19
- data/spec/models/blacklight/document/dublin_core_spec.rb +2 -2
- data/spec/models/blacklight/facet_paginator_spec.rb +1 -1
- data/spec/models/blacklight/search_builder_spec.rb +26 -4
- data/spec/models/blacklight/solr/repository_spec.rb +10 -2
- data/spec/models/blacklight/solr/search_builder_behavior_spec.rb +165 -7
- data/spec/models/blacklight/user_spec.rb +32 -5
- data/spec/models/bookmark_spec.rb +1 -1
- data/spec/models/record_mailer_spec.rb +8 -8
- data/spec/models/search_spec.rb +1 -1
- data/spec/models/solr_document_spec.rb +4 -0
- data/spec/presenters/blacklight/index_presenter_spec.rb +1 -0
- data/spec/presenters/blacklight/rendering/pipeline_spec.rb +1 -0
- data/spec/presenters/blacklight/show_presenter_spec.rb +1 -0
- data/spec/presenters/blacklight/thumbnail_presenter_spec.rb +1 -0
- data/spec/services/blacklight/search_service_spec.rb +7 -7
- data/spec/spec_helper.rb +3 -12
- data/spec/support/features/session_helpers.rb +13 -4
- data/spec/support/features/turbo_helpers.rb +13 -0
- data/spec/support/features.rb +2 -0
- data/spec/support/user_helpers.rb +24 -0
- data/spec/test_app_templates/lib/generators/test_app_generator.rb +1 -1
- data/spec/views/catalog/_document.html.erb_spec.rb +4 -4
- data/spec/views/catalog/_paginate_compact.html.erb_spec.rb +2 -2
- data/spec/views/catalog/_sort_and_per_page.html.erb_spec.rb +5 -5
- data/spec/views/catalog/index.html.erb_spec.rb +2 -2
- data/spec/views/catalog/show.html.erb_spec.rb +3 -4
- data/spec/views/shared/_user_util_links.html.erb_spec.rb +56 -0
- data/template.demo.rb +2 -1
- metadata +30 -29
- data/.hound.yml +0 -4
- data/.jshintrc +0 -22
- data/app/assets/images/blacklight/logo.png +0 -0
- data/app/controllers/concerns/blacklight/facetable.rb +0 -34
- data/app/models/facet_search_builder.rb +0 -5
- data/app/services/blacklight/facet_search_service.rb +0 -44
- data/lib/blacklight/abstract_search_builder.rb +0 -154
- data/lib/blacklight/facet_search_builder.rb +0 -18
- data/lib/blacklight/solr/facet_search_builder_behavior.rb +0 -62
- data/spec/models/blacklight/facet_search_builder_spec.rb +0 -19
- data/spec/models/blacklight/solr/facet_search_builder_behavior_spec.rb +0 -929
|
@@ -47,7 +47,12 @@ module Blacklight
|
|
|
47
47
|
property :header_component, default: Blacklight::HeaderComponent
|
|
48
48
|
property :full_width_layout, default: false
|
|
49
49
|
|
|
50
|
-
#
|
|
50
|
+
# @!attribute dark_mode_support
|
|
51
|
+
# @since v9.1.0
|
|
52
|
+
# @return [Boolean] Whether to enable the light/dark mode them switcher.
|
|
53
|
+
# @note Requires Bootstrap v5.3+; see: https://getbootstrap.com/docs/5.3/customize/color-modes/#dark-mode
|
|
54
|
+
property :dark_mode_support, default: true
|
|
55
|
+
|
|
51
56
|
bootstrap_version = ENV['BOOTSTRAP_VERSION'].presence || "5"
|
|
52
57
|
property :bootstrap_version, default: /(\d)(?:\.\d){0,2}/.match(bootstrap_version)[1].to_i
|
|
53
58
|
|
|
@@ -104,9 +109,6 @@ module Blacklight
|
|
|
104
109
|
# @!attribute search_builder_class
|
|
105
110
|
# @return [Class] class for converting Blacklight parameters to request parameters for the repository_class
|
|
106
111
|
property :search_builder_class, default: ::SearchBuilder
|
|
107
|
-
# @!attribute search_builder_class
|
|
108
|
-
# @return [Class] class for converting Blacklight parameters to request parameters for the repository_class
|
|
109
|
-
property :facet_search_builder_class, default: ::FacetSearchBuilder
|
|
110
112
|
# @!attribute response_model
|
|
111
113
|
# model that maps index responses to the blacklight response model
|
|
112
114
|
# @return [Class]
|
|
@@ -212,14 +214,14 @@ module Blacklight
|
|
|
212
214
|
|
|
213
215
|
# @!attribute action_mapping
|
|
214
216
|
# @since v7.16.0
|
|
215
|
-
# @return [Hash{Symbol => Blacklight::Configuration::
|
|
217
|
+
# @return [Hash{Symbol => Blacklight::Configuration::ActionConfigMapEntry}]
|
|
216
218
|
property :action_mapping, default: NestedOpenStructWithHashAccess.new(
|
|
217
|
-
|
|
218
|
-
default: {
|
|
219
|
-
show: {
|
|
220
|
-
citation: {
|
|
221
|
-
email_record: {
|
|
222
|
-
sms_record: {
|
|
219
|
+
ActionConfigMapEntry,
|
|
220
|
+
default: { blacklight_config_property: :index, default: [:index] },
|
|
221
|
+
show: { blacklight_config_property: :show },
|
|
222
|
+
citation: { parent_action_key: :show },
|
|
223
|
+
email_record: { blacklight_config_property: :email },
|
|
224
|
+
sms_record: { blacklight_config_property: :sms }
|
|
223
225
|
)
|
|
224
226
|
|
|
225
227
|
# @!attribute sms
|
|
@@ -552,7 +554,7 @@ module Blacklight
|
|
|
552
554
|
conf.klass = klass
|
|
553
555
|
end
|
|
554
556
|
end
|
|
555
|
-
|
|
557
|
+
alias inheritable_copy build
|
|
556
558
|
|
|
557
559
|
# Get a view configuration for the given view type + action. The effective
|
|
558
560
|
# view configuration is inherited from:
|
|
@@ -574,12 +576,7 @@ module Blacklight
|
|
|
574
576
|
view_type = nil
|
|
575
577
|
end
|
|
576
578
|
|
|
577
|
-
@view_config[[view_type, action_name]] ||= if view_type
|
|
578
|
-
action_config(action_name)
|
|
579
|
-
else
|
|
580
|
-
base_config = action_config(action_name)
|
|
581
|
-
base_config.merge(view.fetch(view_type, {}))
|
|
582
|
-
end
|
|
579
|
+
@view_config[[view_type, action_name]] ||= action_config(action_name, (view.fetch(view_type, nil) if view_type))
|
|
583
580
|
end
|
|
584
581
|
|
|
585
582
|
# YARD will include inline disabling as docs, cannot do multiline inside @!macro. AND this must be separate from doc block.
|
|
@@ -692,8 +689,8 @@ module Blacklight
|
|
|
692
689
|
end
|
|
693
690
|
end
|
|
694
691
|
|
|
695
|
-
def action_config(
|
|
696
|
-
action_config = action_mapping[
|
|
692
|
+
def action_config(action_name, view_type_specific_config, default: :index)
|
|
693
|
+
action_config = action_mapping[action_name]
|
|
697
694
|
action_config ||= action_mapping[:default]
|
|
698
695
|
|
|
699
696
|
if action_config.parent_config && action_config.parent_config != :default
|
|
@@ -704,9 +701,19 @@ module Blacklight
|
|
|
704
701
|
end
|
|
705
702
|
action_config = action_config.reverse_merge(action_mapping[:default]) if action_config != action_mapping[:default]
|
|
706
703
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
704
|
+
view_config = if action_config.blacklight_config_property
|
|
705
|
+
self[action_config.blacklight_config_property]
|
|
706
|
+
else
|
|
707
|
+
self[default]
|
|
708
|
+
end
|
|
709
|
+
|
|
710
|
+
view_config = Array(action_config.default - [action_config.blacklight_config_property || default]).inject(view_config) do |config, top_level_config|
|
|
711
|
+
config.reverse_merge(self[top_level_config])
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
view_config = view_config.merge(view_type_specific_config) if view_type_specific_config
|
|
715
|
+
|
|
716
|
+
view_config
|
|
710
717
|
end
|
|
711
718
|
end
|
|
712
719
|
end
|
|
@@ -79,7 +79,7 @@ module Blacklight
|
|
|
79
79
|
# Facebook's crawler turns array query parameters into a hash with numeric keys. Once we know
|
|
80
80
|
# the expected parameter structure, we can unmangle those parameters to match our expected values.
|
|
81
81
|
def deep_unmangle_params!(params, permitted_params)
|
|
82
|
-
permitted_params.
|
|
82
|
+
permitted_params.grep(Hash).each do |permission|
|
|
83
83
|
permission.each do |key, permitted_value|
|
|
84
84
|
next unless params[key].is_a?(ActionController::Parameters)
|
|
85
85
|
|
|
@@ -18,7 +18,7 @@ module Blacklight
|
|
|
18
18
|
mapper.get "opensearch"
|
|
19
19
|
mapper.get 'suggest', as: 'suggest_index'
|
|
20
20
|
mapper.get "facet/:id", action: 'facet', as: 'facet'
|
|
21
|
-
mapper.get "facet_suggest/:id
|
|
21
|
+
mapper.get "facet_suggest/:id", action: 'facet', as: 'facet_suggest', defaults: { only_values: true }
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
end
|
|
@@ -5,7 +5,54 @@ module Blacklight
|
|
|
5
5
|
# Blacklight's SearchBuilder converts blacklight request parameters into
|
|
6
6
|
# query parameters appropriate for search index. It does so by evaluating a
|
|
7
7
|
# chain of processing methods to populate a result hash (see {#to_hash}).
|
|
8
|
-
class SearchBuilder
|
|
8
|
+
class SearchBuilder
|
|
9
|
+
class_attribute :default_processor_chain
|
|
10
|
+
self.default_processor_chain = []
|
|
11
|
+
|
|
12
|
+
attr_reader :processor_chain, :blacklight_config
|
|
13
|
+
|
|
14
|
+
# @overload initialize(scope)
|
|
15
|
+
# @param [Object] scope the scope where the filter methods reside in.
|
|
16
|
+
# @overload initialize(processor_chain, scope)
|
|
17
|
+
# @param [List<Symbol>,TrueClass] processor_chain options a list of filter methods to run or true, to use the default methods
|
|
18
|
+
# @param [Object] scope the scope where the filter methods reside in.
|
|
19
|
+
def initialize(*options, blacklight_config: nil)
|
|
20
|
+
case options.size
|
|
21
|
+
when 1
|
|
22
|
+
@processor_chain = default_processor_chain.dup
|
|
23
|
+
@scope = options.first
|
|
24
|
+
when 2
|
|
25
|
+
@processor_chain, @scope = options
|
|
26
|
+
else
|
|
27
|
+
raise ArgumentError, "wrong number of arguments. (#{options.size} for 1..2)"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
@blacklight_config = blacklight_config || @scope&.blacklight_config
|
|
31
|
+
@additional_filters = {}
|
|
32
|
+
@merged_params = {}
|
|
33
|
+
@reverse_merged_params = {}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def search_state
|
|
37
|
+
@search_state ||= begin
|
|
38
|
+
search_state_class = @scope.try(:search_state_class) || Blacklight::SearchState
|
|
39
|
+
@search_state = search_state_class.new({}, blacklight_config, @scope)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def blacklight_params
|
|
44
|
+
search_state.params
|
|
45
|
+
end
|
|
46
|
+
Blacklight.deprecation.deprecate_methods(self, blacklight_params: 'Use search_state.params instead of blacklight_params directly')
|
|
47
|
+
|
|
48
|
+
##
|
|
49
|
+
# Set the parameters to pass through the processor chain
|
|
50
|
+
def with(blacklight_params_or_search_state = {})
|
|
51
|
+
params_will_change!
|
|
52
|
+
@search_state = blacklight_params_or_search_state.is_a?(Blacklight::SearchState) ? blacklight_params_or_search_state : search_state.reset(blacklight_params_or_search_state)
|
|
53
|
+
self
|
|
54
|
+
end
|
|
55
|
+
|
|
9
56
|
##
|
|
10
57
|
# Update the :q (query) parameter
|
|
11
58
|
# @param [Hash<Symbol,Object>] conditions the field and values to query on
|
|
@@ -13,14 +60,14 @@ module Blacklight
|
|
|
13
60
|
# search_builder.where(id: [1,2,3]) # produces: q:"{!lucene}id:(1 OR 2 OR 3)"
|
|
14
61
|
def where(conditions)
|
|
15
62
|
params_will_change!
|
|
16
|
-
@search_state =
|
|
17
|
-
@blacklight_params = @search_state.params
|
|
63
|
+
@search_state = search_state.reset(search_state.params.merge(q: conditions))
|
|
18
64
|
@additional_filters = conditions
|
|
19
65
|
self
|
|
20
66
|
end
|
|
21
67
|
|
|
22
68
|
##
|
|
23
69
|
# Append additional processor chain directives
|
|
70
|
+
# This is used in blacklight_range_limit
|
|
24
71
|
def append(*addl_processor_chain)
|
|
25
72
|
params_will_change!
|
|
26
73
|
builder = self.class.new(processor_chain + addl_processor_chain, scope)
|
|
@@ -55,14 +102,103 @@ module Blacklight
|
|
|
55
102
|
builder
|
|
56
103
|
end
|
|
57
104
|
|
|
105
|
+
##
|
|
106
|
+
# Merge additional, repository-specific parameters
|
|
107
|
+
def merge(extra_params, &)
|
|
108
|
+
if extra_params
|
|
109
|
+
params_will_change!
|
|
110
|
+
@merged_params.merge!(extra_params.to_hash, &)
|
|
111
|
+
end
|
|
112
|
+
self
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
##
|
|
116
|
+
# "Reverse merge" additional, repository-specific parameters
|
|
117
|
+
def reverse_merge(extra_params, &)
|
|
118
|
+
if extra_params
|
|
119
|
+
params_will_change!
|
|
120
|
+
@reverse_merged_params.reverse_merge!(extra_params.to_hash, &)
|
|
121
|
+
end
|
|
122
|
+
self
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
delegate :[], :key?, to: :to_hash
|
|
126
|
+
|
|
127
|
+
# a solr query method
|
|
128
|
+
# @return [Blacklight::Solr::Response] the solr response object
|
|
129
|
+
def to_hash
|
|
130
|
+
return @params unless params_need_update?
|
|
131
|
+
|
|
132
|
+
@params = processed_parameters
|
|
133
|
+
.reverse_merge(@reverse_merged_params)
|
|
134
|
+
.merge(@merged_params)
|
|
135
|
+
.tap { clear_changes }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
alias query to_hash
|
|
139
|
+
alias to_h to_hash
|
|
140
|
+
|
|
141
|
+
# The CatalogController #index action uses this.
|
|
142
|
+
# Solr parameters can come from a number of places. From lowest
|
|
143
|
+
# precedence to highest:
|
|
144
|
+
# 1. General defaults in blacklight config (are trumped by)
|
|
145
|
+
# 2. defaults for the particular search field identified by params[:search_field] (are trumped by)
|
|
146
|
+
# 3. certain parameters directly on input HTTP query params
|
|
147
|
+
# * not just any parameter is grabbed willy nilly, only certain ones are allowed by HTTP input)
|
|
148
|
+
# * for legacy reasons, qt in http query does not over-ride qt in search field definition default.
|
|
149
|
+
# 4. extra parameters passed in as argument.
|
|
150
|
+
#
|
|
151
|
+
# spellcheck.q will be supplied with the [:q] value unless specifically
|
|
152
|
+
# specified otherwise.
|
|
153
|
+
#
|
|
154
|
+
# Incoming parameter :f is mapped to :fq solr parameter.
|
|
155
|
+
#
|
|
156
|
+
# @return a params hash for searching solr.
|
|
157
|
+
def processed_parameters
|
|
158
|
+
request.tap do |request_parameters|
|
|
159
|
+
processor_chain.each do |method_name|
|
|
160
|
+
send(method_name, request_parameters)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def default_document_pagination_params
|
|
166
|
+
{ fl: blacklight_config.document_model.unique_key }
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def for_previous_and_next_documents(index, window = 1)
|
|
170
|
+
document_pagination_params = blacklight_config.document_pagination_params.dup
|
|
171
|
+
|
|
172
|
+
if document_pagination_params.empty?
|
|
173
|
+
merge(default_document_pagination_params)
|
|
174
|
+
else
|
|
175
|
+
merge(document_pagination_params)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
if index > 0
|
|
179
|
+
self.start = [index - window, 0].max
|
|
180
|
+
self.rows = (2 * window) + 1
|
|
181
|
+
else
|
|
182
|
+
self.start = 0
|
|
183
|
+
self.rows = 2 * window
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
self
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# An undefined value that can be used to detect if a parameter was passed in or not, since nil and false may be valid values for some parameters.
|
|
190
|
+
UNDEFINED = Object.new.freeze
|
|
191
|
+
|
|
58
192
|
def start=(value)
|
|
193
|
+
return if value.nil?
|
|
194
|
+
|
|
59
195
|
params_will_change!
|
|
60
196
|
@start = value.to_i
|
|
61
197
|
end
|
|
62
198
|
|
|
63
199
|
# @param [#to_i] value
|
|
64
|
-
def start(value =
|
|
65
|
-
if value
|
|
200
|
+
def start(value = Blacklight::SearchBuilder::UNDEFINED)
|
|
201
|
+
if value != Blacklight::SearchBuilder::UNDEFINED
|
|
66
202
|
self.start = value
|
|
67
203
|
return self
|
|
68
204
|
end
|
|
@@ -71,17 +207,19 @@ module Blacklight
|
|
|
71
207
|
val = 0 if @start < 0
|
|
72
208
|
val
|
|
73
209
|
end
|
|
74
|
-
|
|
210
|
+
alias padding start
|
|
75
211
|
|
|
76
212
|
def page=(value)
|
|
213
|
+
return if value.nil?
|
|
214
|
+
|
|
77
215
|
params_will_change!
|
|
78
216
|
@page = value.to_i
|
|
79
217
|
@page = 1 if @page < 1
|
|
80
218
|
end
|
|
81
219
|
|
|
82
220
|
# @param [#to_i] value
|
|
83
|
-
def page(value =
|
|
84
|
-
if value
|
|
221
|
+
def page(value = Blacklight::SearchBuilder::UNDEFINED)
|
|
222
|
+
if value != Blacklight::SearchBuilder::UNDEFINED
|
|
85
223
|
self.page = value
|
|
86
224
|
return self
|
|
87
225
|
end
|
|
@@ -89,13 +227,15 @@ module Blacklight
|
|
|
89
227
|
end
|
|
90
228
|
|
|
91
229
|
def rows=(value)
|
|
230
|
+
return if value.nil?
|
|
231
|
+
|
|
92
232
|
params_will_change!
|
|
93
233
|
@rows = [value, blacklight_config.max_per_page].map(&:to_i).min
|
|
94
234
|
end
|
|
95
235
|
|
|
96
236
|
# @param [#to_i] value
|
|
97
|
-
def rows(value =
|
|
98
|
-
if value
|
|
237
|
+
def rows(value = Blacklight::SearchBuilder::UNDEFINED)
|
|
238
|
+
if value != Blacklight::SearchBuilder::UNDEFINED
|
|
99
239
|
self.rows = value
|
|
100
240
|
return self
|
|
101
241
|
end
|
|
@@ -109,6 +249,34 @@ module Blacklight
|
|
|
109
249
|
|
|
110
250
|
alias per rows
|
|
111
251
|
|
|
252
|
+
# sets the facet that this query pertains to, for the purpose of facet pagination
|
|
253
|
+
def facet=(value)
|
|
254
|
+
params_will_change!
|
|
255
|
+
@facet = value
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# @param [Object] value
|
|
259
|
+
def facet(value = nil)
|
|
260
|
+
if value
|
|
261
|
+
self.facet = value
|
|
262
|
+
return self
|
|
263
|
+
end
|
|
264
|
+
@facet
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def facet_suggestion_query=(value)
|
|
268
|
+
params_will_change!
|
|
269
|
+
@facet_suggestion_query = value
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def facet_suggestion_query(value = nil)
|
|
273
|
+
if value
|
|
274
|
+
self.facet_suggestion_query = value
|
|
275
|
+
return self
|
|
276
|
+
end
|
|
277
|
+
@facet_suggestion_query
|
|
278
|
+
end
|
|
279
|
+
|
|
112
280
|
# Decode the user provided 'sort' parameter into a sort string that can be
|
|
113
281
|
# passed to the search. This sanitizes the input by ensuring only
|
|
114
282
|
# configured search values are passed through to the search.
|
|
@@ -116,5 +284,35 @@ module Blacklight
|
|
|
116
284
|
def sort
|
|
117
285
|
search_state.sort_field&.sort
|
|
118
286
|
end
|
|
287
|
+
|
|
288
|
+
delegate :search_field, to: :search_state
|
|
289
|
+
|
|
290
|
+
private
|
|
291
|
+
|
|
292
|
+
def request
|
|
293
|
+
Blacklight::Solr::Request.new
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def should_add_field_to_request? _field_name, field
|
|
297
|
+
field.include_in_request || (field.include_in_request.nil? && blacklight_config.add_field_configuration_to_solr_request)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
attr_reader :scope
|
|
301
|
+
|
|
302
|
+
def params_will_change!
|
|
303
|
+
@dirty = true
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def params_changed?
|
|
307
|
+
!!@dirty
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def params_need_update?
|
|
311
|
+
params_changed? || @params.nil?
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def clear_changes
|
|
315
|
+
@dirty = false
|
|
316
|
+
end
|
|
119
317
|
end
|
|
120
318
|
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
class Blacklight::Solr::InvalidParameter < ArgumentError; end
|
|
4
4
|
|
|
5
|
-
class Blacklight::Solr::Request < ActiveSupport::HashWithIndifferentAccess
|
|
5
|
+
class Blacklight::Solr::Request < ActiveSupport::HashWithIndifferentAccess # rubocop:disable Style/OneClassPerFile
|
|
6
6
|
def initialize(constructor = {})
|
|
7
7
|
if constructor.is_a?(Hash)
|
|
8
8
|
super()
|
|
@@ -52,6 +52,13 @@ class Blacklight::Solr::Request < ActiveSupport::HashWithIndifferentAccess
|
|
|
52
52
|
self['fq'] << query
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
+
# Filter the results to a specific set of values in the given field.
|
|
56
|
+
# @param [String] field
|
|
57
|
+
# @param [Array<String>] values
|
|
58
|
+
def append_ids_filter(field, values)
|
|
59
|
+
append_filter_query("{!terms f=#{field}}#{Array(values).join(',')}")
|
|
60
|
+
end
|
|
61
|
+
|
|
55
62
|
def append_facet_fields(values)
|
|
56
63
|
self['facet.field'] ||= []
|
|
57
64
|
self['facet.field'] += Array(values)
|
|
@@ -53,14 +53,14 @@ module Blacklight::Solr::Response::Spelling
|
|
|
53
53
|
# suggestion => [{ frequency =>, word => }] # for extended results
|
|
54
54
|
# suggestion => ['word'] # for non-extended results
|
|
55
55
|
orig_freq = term_info['origFreq']
|
|
56
|
-
if term_info['suggestion'].first.is_a?(Hash)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
word_suggestions << if term_info['suggestion'].first.is_a?(Hash)
|
|
57
|
+
term_info['suggestion'].map do |suggestion|
|
|
58
|
+
suggestion['word'] if suggestion['freq'] > orig_freq
|
|
59
|
+
end
|
|
60
|
+
else
|
|
61
|
+
# only extended suggestions have frequency so we just return all suggestions
|
|
62
|
+
term_info['suggestion']
|
|
63
|
+
end
|
|
64
64
|
end
|
|
65
65
|
end
|
|
66
66
|
end
|
|
@@ -18,7 +18,7 @@ class Blacklight::Solr::Response < ActiveSupport::HashWithIndifferentAccess
|
|
|
18
18
|
def initialize(data, request_params, options = {})
|
|
19
19
|
@search_builder = request_params if request_params.is_a?(Blacklight::SearchBuilder)
|
|
20
20
|
|
|
21
|
-
super(
|
|
21
|
+
super(ActiveSupport::HashWithIndifferentAccess.new(data))
|
|
22
22
|
@request_params = ActiveSupport::HashWithIndifferentAccess.new(request_params)
|
|
23
23
|
self.blacklight_config = options[:blacklight_config]
|
|
24
24
|
self.options = options
|
|
@@ -31,7 +31,7 @@ class Blacklight::Solr::Response < ActiveSupport::HashWithIndifferentAccess
|
|
|
31
31
|
def documents
|
|
32
32
|
@documents ||= (response['docs'] || []).collect { |doc| document_factory.build(doc, self, options) }
|
|
33
33
|
end
|
|
34
|
-
|
|
34
|
+
alias docs documents
|
|
35
35
|
|
|
36
36
|
def grouped
|
|
37
37
|
@groups ||= self["grouped"].map do |field, group|
|
|
@@ -61,23 +61,4 @@ class Blacklight::Solr::Response < ActiveSupport::HashWithIndifferentAccess
|
|
|
61
61
|
def export_formats
|
|
62
62
|
documents.map { |x| x.export_formats.keys }.flatten.uniq
|
|
63
63
|
end
|
|
64
|
-
|
|
65
|
-
private
|
|
66
|
-
|
|
67
|
-
def force_to_utf8(value)
|
|
68
|
-
case value
|
|
69
|
-
when Hash
|
|
70
|
-
value.each { |k, v| value[k] = force_to_utf8(v) }
|
|
71
|
-
when Array
|
|
72
|
-
value.each { |v| force_to_utf8(v) }
|
|
73
|
-
when String
|
|
74
|
-
if value.encoding == Encoding::UTF_8
|
|
75
|
-
value
|
|
76
|
-
else
|
|
77
|
-
Blacklight.logger&.warn "Found a non utf-8 value in Blacklight::Solr::Response. \"#{value}\" Encoding is #{value.encoding}"
|
|
78
|
-
value.dup.force_encoding('UTF-8')
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
value
|
|
82
|
-
end
|
|
83
64
|
end
|
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Blacklight::Solr
|
|
4
|
+
# This depends on being imported into a class that extends AbstractSearchBuilder
|
|
4
5
|
module SearchBuilderBehavior
|
|
5
6
|
extend ActiveSupport::Concern
|
|
6
7
|
|
|
7
8
|
included do
|
|
8
9
|
self.default_processor_chain = [
|
|
9
|
-
:default_solr_parameters,
|
|
10
|
-
:
|
|
11
|
-
:
|
|
12
|
-
:
|
|
13
|
-
:
|
|
10
|
+
:default_solr_parameters,
|
|
11
|
+
:add_search_field_default_parameters,
|
|
12
|
+
:add_query_to_solr,
|
|
13
|
+
:add_facet_fq_to_solr,
|
|
14
|
+
:add_facetting_to_solr,
|
|
15
|
+
:add_solr_fields_to_query,
|
|
16
|
+
:add_paging_to_solr,
|
|
17
|
+
:add_sorting_to_solr,
|
|
18
|
+
:add_group_config_to_solr,
|
|
19
|
+
:add_adv_search_clauses,
|
|
20
|
+
:add_facets_for_advanced_search_form,
|
|
21
|
+
:add_facet_paging_to_solr,
|
|
22
|
+
:add_facet_suggestion_parameters,
|
|
14
23
|
:add_additional_filters
|
|
15
24
|
]
|
|
16
25
|
end
|
|
@@ -93,7 +102,7 @@ module Blacklight::Solr
|
|
|
93
102
|
|
|
94
103
|
# Transform "clause" parameters into the Solr JSON Query DSL
|
|
95
104
|
def add_adv_search_clauses(solr_parameters)
|
|
96
|
-
return if search_state.clause_params.blank?
|
|
105
|
+
return if search_state.clause_params.blank? || search_state.clause_params.all? { |_k, v| v[:query].blank? }
|
|
97
106
|
|
|
98
107
|
# We need to specify lucene as the top-level defType when using JSON Query DSL in Solr versions
|
|
99
108
|
# between 7.2.0 & 9.4.0. After 9.4.0 this is no longer necessary, but also not harmful to include.
|
|
@@ -103,7 +112,7 @@ module Blacklight::Solr
|
|
|
103
112
|
solr_parameters[:spellcheck] = 'false'
|
|
104
113
|
|
|
105
114
|
defaults = { must: [], must_not: [], should: [] }
|
|
106
|
-
default_op =
|
|
115
|
+
default_op = search_state.query_boolean_operator&.to_sym || :must
|
|
107
116
|
solr_parameters[:mm] = 1 if default_op == :should && search_state.clause_params.values.any? { |clause| }
|
|
108
117
|
|
|
109
118
|
search_state.clause_params.each_value do |clause|
|
|
@@ -191,7 +200,7 @@ module Blacklight::Solr
|
|
|
191
200
|
if facet.pivot
|
|
192
201
|
solr_parameters.append_facet_pivot with_ex_local_param(facet.ex, facet.pivot.join(","))
|
|
193
202
|
elsif facet.query
|
|
194
|
-
solr_parameters.append_facet_query
|
|
203
|
+
solr_parameters.append_facet_query(facet.query.values.map { |x| with_ex_local_param(facet.ex, x[:fq]) })
|
|
195
204
|
else
|
|
196
205
|
solr_parameters.append_facet_fields with_ex_local_param(facet.ex, facet.field)
|
|
197
206
|
end
|
|
@@ -232,8 +241,6 @@ module Blacklight::Solr
|
|
|
232
241
|
# copy paging params from BL app over to solr, changing
|
|
233
242
|
# app level per_page and page to Solr rows and start.
|
|
234
243
|
def add_paging_to_solr(solr_params)
|
|
235
|
-
rows(solr_params[:rows] || 10) if rows.nil?
|
|
236
|
-
|
|
237
244
|
solr_params[:rows] = rows
|
|
238
245
|
|
|
239
246
|
solr_params[:start] = start if start.nonzero?
|
|
@@ -250,6 +257,48 @@ module Blacklight::Solr
|
|
|
250
257
|
solr_parameters[:group] = false if search_state.filter(grouped_key_for_results).any?
|
|
251
258
|
end
|
|
252
259
|
|
|
260
|
+
def add_facet_paging_to_solr(solr_params)
|
|
261
|
+
return if facet.blank?
|
|
262
|
+
|
|
263
|
+
facet_config = blacklight_config.facet_fields[facet]
|
|
264
|
+
|
|
265
|
+
solr_params[:rows] = 0
|
|
266
|
+
|
|
267
|
+
limit = if solr_params["facet.limit"]
|
|
268
|
+
solr_params["facet.limit"].to_i
|
|
269
|
+
else
|
|
270
|
+
facet_config.fetch(:more_limit, blacklight_config.default_more_limit)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
page = search_state.facet_page
|
|
274
|
+
sort = search_state.facet_sort
|
|
275
|
+
prefix = search_state.facet_prefix
|
|
276
|
+
offset = (page - 1) * limit
|
|
277
|
+
|
|
278
|
+
if facet_config.json
|
|
279
|
+
add_solr_facet_json_params(solr_parameters, facet, facet_config, limit: limit + 1, offset: offset, sort: sort, prefix: prefix)
|
|
280
|
+
return
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Now override with our specific things for fetching facet values
|
|
284
|
+
facet_ex = facet_config.respond_to?(:ex) ? facet_config.ex : nil
|
|
285
|
+
solr_params[:'facet.field'] = with_ex_local_param(facet_ex, facet_config.field)
|
|
286
|
+
|
|
287
|
+
# Need to set as f.facet_field.facet.* to make sure we
|
|
288
|
+
# override any field-specific default in the solr request handler.
|
|
289
|
+
solr_params[:"f.#{facet_config.field}.facet.limit"] = limit + 1
|
|
290
|
+
solr_params[:"f.#{facet_config.field}.facet.offset"] = offset
|
|
291
|
+
solr_params[:"f.#{facet_config.field}.facet.sort"] = sort if sort
|
|
292
|
+
solr_params[:"f.#{facet_config.field}.facet.prefix"] = prefix if prefix
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def add_facet_suggestion_parameters(solr_params)
|
|
296
|
+
return if facet.blank? || facet_suggestion_query.blank?
|
|
297
|
+
|
|
298
|
+
solr_params[:'facet.contains'] = facet_suggestion_query[0..50]
|
|
299
|
+
solr_params[:'facet.contains.ignoreCase'] = true
|
|
300
|
+
end
|
|
301
|
+
|
|
253
302
|
def with_ex_local_param(ex, value)
|
|
254
303
|
if ex
|
|
255
304
|
"{!ex=#{ex}}#{value}"
|
|
@@ -318,12 +367,6 @@ module Blacklight::Solr
|
|
|
318
367
|
end
|
|
319
368
|
end
|
|
320
369
|
|
|
321
|
-
def search_state
|
|
322
|
-
return super if defined?(super)
|
|
323
|
-
|
|
324
|
-
@search_state ||= Blacklight::SearchState.new(blacklight_params, blacklight_config)
|
|
325
|
-
end
|
|
326
|
-
|
|
327
370
|
def add_search_field_query_builder_params(solr_parameters)
|
|
328
371
|
q, additional_parameters = search_field.query_builder.call(self, search_field, solr_parameters)
|
|
329
372
|
|
data/lib/blacklight.rb
CHANGED
|
@@ -85,7 +85,7 @@ module Blacklight
|
|
|
85
85
|
else
|
|
86
86
|
YAML.safe_load(blacklight_erb, [], [], true)
|
|
87
87
|
end
|
|
88
|
-
rescue => e
|
|
88
|
+
rescue StandardError => e
|
|
89
89
|
raise("#{blacklight_config_file} was found, but could not be parsed.\n#{e.inspect}")
|
|
90
90
|
end
|
|
91
91
|
|
|
@@ -122,6 +122,6 @@ module Blacklight
|
|
|
122
122
|
end
|
|
123
123
|
|
|
124
124
|
def self.deprecation
|
|
125
|
-
@deprecation ||= ActiveSupport::Deprecation.new('
|
|
125
|
+
@deprecation ||= ActiveSupport::Deprecation.new('10.0', 'Blacklight')
|
|
126
126
|
end
|
|
127
127
|
end
|
|
@@ -8,7 +8,7 @@ module Blacklight
|
|
|
8
8
|
def import_javascript_assets
|
|
9
9
|
append_to_file 'config/importmap.rb' do
|
|
10
10
|
<<~CONTENT
|
|
11
|
-
pin "@github/auto-complete-element", to: "https://cdn.
|
|
11
|
+
pin "@github/auto-complete-element", to: "https://cdn.jsdelivr.net/npm/@github/auto-complete-element@3.8.0/+esm"
|
|
12
12
|
pin "@popperjs/core", to: "https://ga.jspm.io/npm:@popperjs/core@2.11.6/dist/umd/popper.min.js"
|
|
13
13
|
pin "bootstrap", to: "https://ga.jspm.io/npm:bootstrap@#{(defined?(Bootstrap) && Bootstrap::VERSION) || '5.3.5'}/dist/js/bootstrap.js"
|
|
14
14
|
CONTENT
|
|
@@ -49,7 +49,7 @@ module Blacklight
|
|
|
49
49
|
else
|
|
50
50
|
append_to_file 'app/assets/stylesheets/application.css' do
|
|
51
51
|
<<~CONTENT
|
|
52
|
-
@import url(https://cdn.jsdelivr.net/npm/bootstrap@5.3.
|
|
52
|
+
@import url(https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css);
|
|
53
53
|
@import url("blacklight.css");
|
|
54
54
|
CONTENT
|
|
55
55
|
end
|