blacklight 7.33.1 → 7.35.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/.env +1 -0
  3. data/.github/workflows/ruby.yml +21 -1
  4. data/.rubocop.yml +2 -0
  5. data/Gemfile +4 -0
  6. data/VERSION +1 -1
  7. data/app/components/blacklight/advanced_search_form_component.html.erb +1 -1
  8. data/app/components/blacklight/advanced_search_form_component.rb +5 -5
  9. data/app/components/blacklight/content_areas_shim.rb +1 -1
  10. data/app/components/blacklight/document/action_component.html.erb +2 -9
  11. data/app/components/blacklight/document/action_component.rb +18 -0
  12. data/app/components/blacklight/document/actions_component.rb +1 -1
  13. data/app/components/blacklight/document_component.rb +33 -10
  14. data/app/components/blacklight/document_metadata_component.html.erb +4 -2
  15. data/app/components/blacklight/document_metadata_component.rb +6 -2
  16. data/app/components/blacklight/facet_field_checkboxes_component.html.erb +2 -2
  17. data/app/components/blacklight/facet_field_list_component.html.erb +2 -2
  18. data/app/components/blacklight/header_component.html.erb +2 -0
  19. data/app/components/blacklight/header_component.rb +26 -0
  20. data/app/components/blacklight/metadata_field_component.html.erb +2 -2
  21. data/app/components/blacklight/metadata_field_layout_component.rb +7 -4
  22. data/app/components/blacklight/response/pagination_component.html.erb +1 -1
  23. data/app/components/blacklight/response/pagination_component.rb +5 -1
  24. data/app/components/blacklight/response/view_type_component.rb +1 -1
  25. data/app/components/blacklight/search_navbar_component.html.erb +5 -0
  26. data/app/components/blacklight/search_navbar_component.rb +34 -0
  27. data/app/components/blacklight/system/dropdown_component.rb +2 -2
  28. data/app/components/blacklight/system/flash_message_component.rb +1 -1
  29. data/app/components/blacklight/top_navbar_component.html.erb +12 -0
  30. data/app/components/blacklight/top_navbar_component.rb +17 -0
  31. data/app/controllers/concerns/blacklight/base.rb +5 -0
  32. data/app/controllers/concerns/blacklight/bookmarks.rb +5 -1
  33. data/app/controllers/concerns/blacklight/catalog.rb +14 -3
  34. data/app/controllers/concerns/blacklight/controller.rb +3 -2
  35. data/app/helpers/blacklight/blacklight_helper_behavior.rb +2 -2
  36. data/app/helpers/blacklight/render_partials_helper_behavior.rb +1 -0
  37. data/app/models/concerns/blacklight/document/active_model_shim.rb +10 -0
  38. data/app/models/concerns/blacklight/document/attributes.rb +50 -0
  39. data/app/models/concerns/blacklight/document.rb +12 -20
  40. data/app/models/search.rb +6 -1
  41. data/app/presenters/blacklight/rendering/join.rb +1 -1
  42. data/app/services/blacklight/field_retriever.rb +13 -11
  43. data/app/values/blacklight/types.rb +99 -11
  44. data/app/views/catalog/_document.html.erb +1 -1
  45. data/app/views/catalog/_facet_layout.html.erb +2 -2
  46. data/app/views/catalog/_search_form.html.erb +1 -1
  47. data/app/views/catalog/_show_main_content.html.erb +2 -2
  48. data/app/views/catalog/_show_sidebar.html.erb +1 -1
  49. data/app/views/catalog/_show_tools.html.erb +4 -3
  50. data/app/views/catalog/facet.html.erb +3 -3
  51. data/app/views/layouts/blacklight/base.html.erb +7 -7
  52. data/app/views/shared/_header_navbar.html.erb +1 -22
  53. data/blacklight.gemspec +3 -4
  54. data/config/locales/blacklight.en.yml +1 -0
  55. data/docker-compose.yml +1 -0
  56. data/lib/blacklight/configuration.rb +39 -2
  57. data/lib/blacklight/engine.rb +15 -0
  58. data/lib/blacklight/solr/repository.rb +14 -2
  59. data/lib/blacklight/solr/request.rb +2 -0
  60. data/lib/blacklight/solr/search_builder_behavior.rb +2 -1
  61. data/lib/blacklight.rb +1 -1
  62. data/lib/generators/blacklight/assets_generator.rb +1 -1
  63. data/lib/generators/blacklight/install_generator.rb +1 -1
  64. data/lib/generators/blacklight/templates/catalog_controller.rb +4 -0
  65. data/lib/generators/blacklight/templates/solr/conf/solrconfig.xml +69 -0
  66. data/lib/generators/blacklight/templates/solr_document.rb +1 -1
  67. data/lib/railties/blacklight.rake +4 -4
  68. data/package.json +1 -1
  69. data/spec/components/blacklight/document_component_spec.rb +60 -11
  70. data/spec/components/blacklight/facet_component_spec.rb +11 -1
  71. data/spec/components/blacklight/facet_item_pivot_component_spec.rb +3 -2
  72. data/spec/components/blacklight/header_component_spec.rb +20 -0
  73. data/spec/components/blacklight/search_bar_component_spec.rb +1 -1
  74. data/spec/controllers/blacklight/base_spec.rb +1 -1
  75. data/spec/features/advanced_search_spec.rb +56 -0
  76. data/spec/features/axe_spec.rb +5 -0
  77. data/spec/features/sitelinks_search_box_spec.rb +13 -0
  78. data/spec/helpers/blacklight/search_history_constraints_helper_behavior_spec.rb +8 -15
  79. data/spec/helpers/blacklight_helper_spec.rb +10 -5
  80. data/spec/models/blacklight/configuration_spec.rb +22 -0
  81. data/spec/models/blacklight/solr/repository_spec.rb +27 -0
  82. data/spec/models/blacklight/solr/search_builder_spec.rb +16 -0
  83. data/spec/models/solr_document_spec.rb +21 -3
  84. data/spec/presenters/blacklight/show_presenter_spec.rb +4 -10
  85. data/spec/services/blacklight/field_retriever_spec.rb +17 -0
  86. data/spec/spec_helper.rb +32 -5
  87. data/spec/support/view_component_test_helpers.rb +35 -0
  88. data/spec/views/catalog/_show_tools.html.erb_spec.rb +24 -10
  89. metadata +30 -27
  90. data/spec/features/sitelinks_search_box.rb +0 -13
  91. data/spec/support/view_component_capybara_test_helpers.rb +0 -8
@@ -270,9 +270,9 @@ module Blacklight::BlacklightHelperBehavior
270
270
  #
271
271
  # @param [Hash] query_params the query parameters to check
272
272
  # @return [Symbol]
273
- def document_index_view_type query_params = params
273
+ def document_index_view_type query_params = params || {}
274
274
  view_param = query_params[:view]
275
- view_param ||= session[:preferred_view]
275
+ view_param ||= session[:preferred_view] if respond_to?(:session)
276
276
  if view_param && document_index_views.key?(view_param.to_sym)
277
277
  view_param.to_sym
278
278
  else
@@ -69,6 +69,7 @@ module Blacklight::RenderPartialsHelperBehavior
69
69
  if template
70
70
  template.render(self, locals.merge(document: doc))
71
71
  else
72
+ logger.warn("No template was found for base_name: '#{base_name}', view_type: '#{view_type}' in render_document_partial")
72
73
  ''
73
74
  end
74
75
  end
@@ -28,6 +28,16 @@ module Blacklight::Document
28
28
  def find id
29
29
  repository.find(id).documents.first
30
30
  end
31
+
32
+ # In Rails 7.1+, needs this method
33
+ def composite_primary_key?
34
+ false
35
+ end
36
+
37
+ # In Rails 7.1+, needs this method
38
+ def has_query_constraints?
39
+ false
40
+ end
31
41
  end
32
42
 
33
43
  ##
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_model/conversion'
4
+
5
+ module Blacklight::Document
6
+ module Attributes
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ class_attribute :attribute_types, instance_accessor: false
11
+ self.attribute_types = Hash.new(Blacklight::Types::Value)
12
+ end
13
+
14
+ class_methods do
15
+ # Define an attribute reader on a document model
16
+ # @param [Symbol] name the name of the attribute to define
17
+ # @param [Symbol, Class] type the type of the attribute to define
18
+ # @param [String] field the name of the document's field to use for this attribute
19
+ # @param [any, Proc] default the default value for the attribute
20
+ # @example
21
+ # class SolrDocument
22
+ # include Blacklight::Solr::Document
23
+ # attribute :title, :string, 'title_tesim'
24
+ #
25
+ # Deprecated syntax:
26
+ # attribute :title, Blacklight::Types::String, 'title_tesim'
27
+ # end
28
+ #
29
+ # doc = SolrDocument.new(title_tesim: ["One flew over the cuckoo's nest"])
30
+ # doc.title
31
+ # #=> "One flew over the cuckoo's nest"
32
+ def attribute(name, type = :value, deprecated_field = name, field: nil, default: nil, **kwargs)
33
+ field ||= deprecated_field
34
+
35
+ define_method name do
36
+ if type.respond_to?(:coerce) && !(type < Blacklight::Types::Value)
37
+ # deprecated behavior using a bespoke api
38
+ type.coerce(fetch(field, default))
39
+ else
40
+ # newer behavior better aligned with ActiveModel::Type
41
+ instance = Blacklight::Types.lookup(type, **kwargs) if type.is_a? Symbol
42
+ instance ||= type.new(**kwargs)
43
+
44
+ instance.cast(fetch(field, default))
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -21,6 +21,7 @@ module Blacklight::Document
21
21
  include Blacklight::Document::SemanticFields
22
22
  include Blacklight::Document::CacheKey
23
23
  include Blacklight::Document::Export
24
+ include Blacklight::Document::Attributes
24
25
 
25
26
  included do
26
27
  extend ActiveModel::Naming
@@ -70,13 +71,20 @@ module Blacklight::Document
70
71
  alias has_field? has?
71
72
  alias has_key? key?
72
73
 
73
- def fetch key, *default
74
+ NO_DEFAULT_PROVIDED = Object.new # :nodoc:
75
+ def fetch key, default = NO_DEFAULT_PROVIDED
74
76
  if key? key
75
77
  self[key]
76
- elsif default.empty? && !block_given?
77
- raise KeyError, "key not found \"#{key}\""
78
+ elsif block_given?
79
+ yield(self) if block_given?
80
+ elsif default != NO_DEFAULT_PROVIDED
81
+ if default.respond_to?(:call)
82
+ default.call(self)
83
+ else
84
+ default
85
+ end
78
86
  else
79
- (yield(self) if block_given?) || default.first
87
+ raise KeyError, "key not found \"#{key}\""
80
88
  end
81
89
  end
82
90
 
@@ -116,21 +124,5 @@ module Blacklight::Document
116
124
  def unique_key
117
125
  @unique_key ||= 'id'
118
126
  end
119
-
120
- # Define an attribute reader on a document model
121
- # @example
122
- # class SolrDocument
123
- # include Blacklight::Solr::Document
124
- # attribute :title, Blacklight::Types::String, 'title_tesim'
125
- # end
126
- #
127
- # doc = SolrDocument.new(title_tesim: ["One flew over the cuckoo's nest"])
128
- # doc.title
129
- # #=> "One flew over the cuckoo's nest"
130
- def attribute(name, type, field)
131
- define_method name do
132
- type.coerce(self[field])
133
- end
134
- end
135
127
  end
136
128
  end
data/app/models/search.rb CHANGED
@@ -4,7 +4,12 @@ class Search < ApplicationRecord
4
4
  belongs_to :user, optional: true
5
5
 
6
6
  # use a backwards-compatible serializer until the Rails API stabilizes and we can evaluate for major-revision compatibility
7
- serialize :query_params, Blacklight::SearchParamsYamlCoder
7
+ if ::Rails.version.to_f >= 7.1
8
+ # non-deprecated coder: keyword arg for Rails 7.1+
9
+ serialize :query_params, coder: Blacklight::SearchParamsYamlCoder
10
+ else
11
+ serialize :query_params, Blacklight::SearchParamsYamlCoder
12
+ end
8
13
 
9
14
  # A Search instance is considered a saved search if it has a user_id.
10
15
  def saved?
@@ -5,7 +5,7 @@ module Blacklight
5
5
  class Join < AbstractStep
6
6
  def render
7
7
  options = config.separator_options || {}
8
- next_step(values.map { |x| html_escape(x) }.to_sentence(options).html_safe)
8
+ next_step(values.map { |x| x.html_safe? ? x : html_escape(x) }.to_sentence(options).html_safe)
9
9
  end
10
10
 
11
11
  private
@@ -22,17 +22,19 @@ module Blacklight
22
22
 
23
23
  # @return [Array]
24
24
  def fetch
25
- Array.wrap(
26
- if field_config.highlight
27
- retrieve_highlight
28
- elsif field_config.accessor
29
- retieve_using_accessor
30
- elsif field_config.values
31
- retrieve_values
32
- else
33
- retrieve_simple
34
- end
35
- )
25
+ if field_config.highlight
26
+ value = retrieve_highlight
27
+ end
28
+ if value.blank?
29
+ value = if field_config.accessor
30
+ retieve_using_accessor
31
+ elsif field_config.values
32
+ retrieve_values
33
+ else
34
+ retrieve_simple
35
+ end
36
+ end
37
+ Array.wrap(value)
36
38
  end
37
39
 
38
40
  private
@@ -1,31 +1,119 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_model"
4
+
3
5
  module Blacklight
4
6
  # These are data types that blacklight can use to coerce values from the index
5
7
  module Types
6
- class Array
8
+ @registry = ActiveModel::Type::Registry.new
9
+
10
+ class << self
11
+ delegate :lookup, :register, to: :@registry
12
+ end
13
+
14
+ class Value
7
15
  def self.coerce(input)
8
- ::Array.wrap(input)
16
+ new.cast(input)
17
+ end
18
+
19
+ def initialize(**kwargs)
20
+ @kwargs = kwargs
21
+ end
22
+
23
+ def cast(input)
24
+ if input.is_a?(::Array)
25
+ input.first
26
+ else
27
+ input
28
+ end
9
29
  end
10
30
  end
11
31
 
12
- class String
13
- def self.coerce(input)
14
- ::Array.wrap(input).first
32
+ class Array < Value
33
+ def initialize(of: nil, **kwargs)
34
+ @of = of
35
+ @kwargs = kwargs
36
+ end
37
+
38
+ def cast(input)
39
+ ::Array.wrap(input).map do |value|
40
+ lookup_type.cast(value)
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def lookup_type
47
+ return Blacklight::Types::Value.new(**@kwargs) if @of.nil?
48
+
49
+ @lookup_type ||= Blacklight::Types.lookup(@of, **@kwargs)
15
50
  end
16
51
  end
17
52
 
18
- class Date
19
- def self.coerce(input)
20
- field = String.coerce(input)
21
- return if field.blank?
53
+ class String < Value
54
+ def cast(input)
55
+ super&.to_s
56
+ end
57
+ end
58
+
59
+ class Date < Value
60
+ def cast(input)
61
+ value = super
62
+ return if value.blank?
22
63
 
23
64
  begin
24
- ::Date.parse(field)
65
+ ::Date.parse(value.to_s)
25
66
  rescue ArgumentError
26
- Rails.logger&.info "Unable to parse date: #{field.first.inspect}"
67
+ Rails.logger&.info "Unable to parse date: #{value.inspect}"
27
68
  end
28
69
  end
29
70
  end
71
+
72
+ class Boolean < Value
73
+ def cast(input)
74
+ ActiveModel::Type::Boolean.new.cast(super)
75
+ end
76
+ end
77
+
78
+ class JsonValue < Value
79
+ def cast(input)
80
+ value = super
81
+
82
+ return value unless value.is_a?(String)
83
+
84
+ JSON.parse(value)
85
+ end
86
+ end
87
+
88
+ class Selector < Array
89
+ def initialize(by: nil, block: nil, **kwargs)
90
+ super(**kwargs)
91
+ @by = by
92
+ @block = block
93
+ end
94
+
95
+ def cast(input)
96
+ return super.public_send(@by) unless @block
97
+
98
+ super.public_send(@by, &@block)
99
+ end
100
+ end
101
+
102
+ # rubocop:disable Rails/OutputSafety
103
+ class Html < String
104
+ def cast(input)
105
+ super&.html_safe
106
+ end
107
+ end
108
+ # rubocop:enable Rails/OutputSafety
109
+
110
+ register :boolean, Boolean
111
+ register :string, String
112
+ register :date, Date
113
+ register :array, Array
114
+ register :json, JsonValue
115
+ register :html, Html
116
+ register :select, Selector
117
+ register :value, Value
30
118
  end
31
119
  end
@@ -1,7 +1,7 @@
1
1
  <% # container for a single doc -%>
2
2
  <% view_config = local_assigns[:view_config] || blacklight_config.view_config(document_index_view_type) %>
3
3
  <%= render (view_config.document_component || Blacklight::DocumentComponent).new(presenter: document_presenter(document), counter: document_counter_with_offset(document_counter)) do |component| %>
4
- <% component.public_send(view_config.document_component.blank? && view_config.partials.any? ? :body : :partial) do %>
4
+ <% component.public_send(view_config.document_component.blank? && view_config.partials.any? ? :with_body : :with_partial) do %>
5
5
  <%= render_document_partials document, view_config.partials, component: component, document_counter: document_counter %>
6
6
  <% end %>
7
7
  <% end %>
@@ -1,8 +1,8 @@
1
1
  <%= render(Blacklight::FacetFieldComponent.new(facet_field: facet_field_presenter(facet_field, nil))) do |component| %>
2
- <% component.label do %>
2
+ <% component.with_label do %>
3
3
  <%= facet_field_label(facet_field.key) %>
4
4
  <% end %>
5
- <% component.body do %>
5
+ <% component.with_body do %>
6
6
  <%= yield %>
7
7
  <% end %>
8
8
  <% end %>
@@ -4,4 +4,4 @@
4
4
  advanced_search_url: search_action_url(action: 'advanced_search'),
5
5
  params: search_state.params_for_search.except(:qt),
6
6
  search_fields: Deprecation.silence(Blacklight::ConfigurationHelperBehavior) { search_fields },
7
- autocomplete_path: search_action_path(action: :suggest))) %>
7
+ autocomplete_path: suggest_index_catalog_path)) %>
@@ -4,7 +4,7 @@
4
4
  <% content_for(:head) { render_link_rel_alternates } %>
5
5
 
6
6
  <%= render (blacklight_config.view_config(:show).document_component || Blacklight::DocumentComponent).new(presenter: document_presenter(@document), component: :div, title_component: :h1, show: true) do |component| %>
7
- <% component.footer do %>
7
+ <% component.with_footer do %>
8
8
  <% if @document.respond_to?(:export_as_openurl_ctx_kev) %>
9
9
  <!--
10
10
  // COinS, for Zotero among others.
@@ -17,7 +17,7 @@
17
17
 
18
18
  <%# Use :body for complete backwards compatibility (overriding the component body markup),
19
19
  but if the app explicitly opted-in to components, make the partials data available as :partials to ease migrations pain %>
20
- <% component.public_send(blacklight_config.view_config(:show).document_component.blank? && blacklight_config.view_config(:show).partials.any? ? :body : :partial) do %>
20
+ <% component.public_send(blacklight_config.view_config(:show).document_component.blank? && blacklight_config.view_config(:show).partials.any? ? :with_body : :with_partial) do %>
21
21
  <div id="doc_<%= @document.id.to_s.parameterize %>">
22
22
  <%= render_document_partials @document, blacklight_config.view_config(:show).partials, component: component %>
23
23
  </div>
@@ -1,2 +1,2 @@
1
- <%= render 'show_tools' %>
1
+ <%= render 'show_tools', document: document %>
2
2
  <%= render(Blacklight::Document::MoreLikeThisComponent.new(document: document)) %>
@@ -1,14 +1,15 @@
1
- <% if show_doc_actions? %>
1
+ <% document = @document if local_assigns[:document].nil? %>
2
+ <% if show_doc_actions? document %>
2
3
  <div class="card show-tools">
3
4
  <div class="card-header">
4
5
  <h2 class="mb-0 h6"><%= t('blacklight.tools.title') %></h2>
5
6
  </div>
6
7
  <% if render_show_doc_actions_method_from_blacklight? %>
7
- <%= render(Blacklight::Document::ActionsComponent.new(document: @document, tag: 'ul', classes: 'list-group list-group-flush', wrapping_tag: 'li', wrapping_classes: 'list-group-item', actions: document_actions(@document), url_opts: Blacklight::Parameters.sanitize(params.to_unsafe_h))) %>
8
+ <%= render(Blacklight::Document::ActionsComponent.new(document: document, tag: 'ul', classes: 'list-group list-group-flush', wrapping_tag: 'li', wrapping_classes: 'list-group-item', actions: document_actions(document), url_opts: Blacklight::Parameters.sanitize(params.to_unsafe_h))) %>
8
9
  <% else %>
9
10
  <% Deprecation.warn(self, '#render_show_doc_actions is deprecated; use ActionComponents instead') %>
10
11
  <ul class="list-group list-group-flush">
11
- <%= render_show_doc_actions @document do |config, inner| %>
12
+ <%= render_show_doc_actions document do |config, inner| %>
12
13
  <li class="list-group-item <%= config.key %>">
13
14
  <%= inner %>
14
15
  </li>
@@ -1,11 +1,11 @@
1
1
  <%= render Blacklight::System::ModalComponent.new do |component| %>
2
- <% component.prefix do %>
2
+ <% component.with_prefix do %>
3
3
  <div class="facet-pagination top row justify-content-between">
4
4
  <%= render :partial=>'facet_pagination' %>
5
5
  </div>
6
6
  <% end %>
7
7
 
8
- <% component.title { facet_field_label(@facet.key) } %>
8
+ <% component.with_title { facet_field_label(@facet.key) } %>
9
9
 
10
10
  <%= render partial: 'facet_index_navigation' if @facet.index_range && @display_facet.index? %>
11
11
 
@@ -13,7 +13,7 @@
13
13
  <%= render_facet_limit(@display_facet, layout: false) %>
14
14
  </div>
15
15
 
16
- <% component.footer do %>
16
+ <% component.with_footer do %>
17
17
  <div class="facet-pagination bottom row justify-content-between">
18
18
  <%= render :partial=>'facet_pagination' %>
19
19
  </div>
@@ -28,15 +28,15 @@
28
28
  </nav>
29
29
  <%= render partial: 'shared/header_navbar' %>
30
30
 
31
- <main id="main-container" class="<%= container_classes %>" role="main" aria-label="<%= t('blacklight.main.aria.main_container') %>">
32
- <%= content_for(:container_header) %>
31
+ <main id="main-container" class="<%= container_classes %>" role="main" aria-label="<%= t('blacklight.main.aria.main_container') %>">
32
+ <%= content_for(:container_header) %>
33
33
 
34
- <%= render partial: 'shared/flash_msg', layout: 'shared/flash_messages' %>
34
+ <%= render partial: 'shared/flash_msg', layout: 'shared/flash_messages' %>
35
35
 
36
- <div class="row">
37
- <%= content_for?(:content) ? yield(:content) : yield %>
38
- </div>
39
- </main>
36
+ <div class="row">
37
+ <%= content_for?(:content) ? yield(:content) : yield %>
38
+ </div>
39
+ </main>
40
40
 
41
41
  <%= render partial: 'shared/footer' %>
42
42
  <%= render partial: 'shared/modal' %>
@@ -1,22 +1 @@
1
- <nav class="navbar navbar-expand-md navbar-dark bg-dark topbar" role="navigation">
2
- <div class="<%= container_classes %>">
3
- <%= link_to application_name, root_path, class: 'mb-0 navbar-brand navbar-logo' %>
4
- <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-bs-toggle="collapse" data-target="#user-util-collapse" data-bs-target="#user-util-collapse" aria-controls="user-util-collapse" aria-expanded="false" aria-label="Toggle navigation">
5
- <span class="navbar-toggler-icon"></span>
6
- </button>
7
-
8
- <div class="collapse navbar-collapse justify-content-md-end" id="user-util-collapse">
9
- <%= render 'shared/user_util_links' %>
10
- </div>
11
- </div>
12
- </nav>
13
-
14
- <%= content_tag :div, class: 'navbar-search navbar navbar-light bg-light', role: 'navigation', aria: { label: t('blacklight.search.header') } do %>
15
- <div class="<%= container_classes %>">
16
- <%= render((blacklight_config&.view_config(document_index_view_type)&.search_bar_component ||Blacklight::SearchBarComponent).new(
17
- url: search_action_url,
18
- advanced_search_url: search_action_url(action: 'advanced_search'),
19
- params: search_state.params_for_search.except(:qt),
20
- autocomplete_path: search_action_path(action: :suggest))) %>
21
- </div>
22
- <% end %>
1
+ <%= render blacklight_config.header_component.new(blacklight_config: blacklight_config) %>
data/blacklight.gemspec CHANGED
@@ -25,22 +25,21 @@ Gem::Specification.new do |s|
25
25
 
26
26
  s.required_ruby_version = '>= 2.5'
27
27
 
28
- s.add_dependency "rails", '>= 5.1', '< 7.1'
28
+ s.add_dependency "rails", '>= 5.1', '< 7.2'
29
29
  s.add_dependency "globalid"
30
30
  s.add_dependency "jbuilder", '~> 2.7'
31
31
  s.add_dependency "kaminari", ">= 0.15" # the pagination (page 1,2,3, etc..) of our search results
32
32
  s.add_dependency "deprecation"
33
33
  s.add_dependency "i18n", '>= 1.7.0' # added named parameters
34
34
  s.add_dependency "ostruct", '>= 0.3.2'
35
- s.add_dependency "view_component", '~> 2.66'
35
+ s.add_dependency "view_component", '>= 2.66', '< 4'
36
36
  s.add_dependency 'hashdiff'
37
37
 
38
38
  s.add_development_dependency "rsolr", ">= 1.0.6", "< 3" # Library for interacting with rSolr.
39
- s.add_development_dependency "rspec-rails", "~> 5.0"
39
+ s.add_development_dependency "rspec-rails", ">= 5.0" # some versions tested need >= 6.0
40
40
  s.add_development_dependency "rspec-collection_matchers", ">= 1.0"
41
41
  s.add_development_dependency 'axe-core-rspec'
42
42
  s.add_development_dependency "capybara", '~> 3'
43
- s.add_development_dependency 'webdrivers'
44
43
  s.add_development_dependency 'selenium-webdriver'
45
44
  s.add_development_dependency 'engine_cart', '~> 2.1'
46
45
  s.add_development_dependency "equivalent-xml"
@@ -249,6 +249,7 @@ en:
249
249
  more_options: More options
250
250
  any_of: 'Any of:'
251
251
  op:
252
+ label: search operator
252
253
  must: all
253
254
  should: any
254
255
  page_title: Advanced search - %{application_name}
data/docker-compose.yml CHANGED
@@ -17,6 +17,7 @@ services:
17
17
  environment:
18
18
  - SOLR_URL # Set via environment variable or use default defined in .env file
19
19
  - RAILS_VERSION # Set via environment variable or use default defined in .env file
20
+ - VIEW_COMPONENT_VERSION # Set via environment variable or use default defined in .env file
20
21
  - SOLR_ENV=docker-compose
21
22
  - ENGINE_CART_RAILS_OPTIONS=--skip-git --skip-listen --skip-spring --skip-keeps --skip-action-cable --skip-coffee --skip-test --skip-solr
22
23
 
@@ -20,16 +20,42 @@ module Blacklight
20
20
  autoload :ShowField
21
21
  end
22
22
 
23
+ class_attribute :default_values, default: {}
24
+
23
25
  # Set up Blacklight::Configuration.default_values to contain the basic, required Blacklight fields
24
26
  class << self
25
27
  def property(key, default: nil)
26
28
  default_values[key] = default
27
29
  end
28
30
 
29
- def default_values
30
- @default_values ||= {}
31
+ def default_configuration(&block)
32
+ @default_configurations ||= []
33
+
34
+ if block
35
+ @default_configurations << block
36
+
37
+ block.call if @default_configuration_initialized
38
+ end
39
+
40
+ @default_configurations
41
+ end
42
+
43
+ def initialize_default_configuration
44
+ @default_configurations&.map(&:call)
45
+ @default_configuration_initialized = true
31
46
  end
47
+
48
+ def initialized_default_configuration?
49
+ @default_configuration_initialized
50
+ end
51
+ end
52
+
53
+ property :logo_link
54
+ property :header_component, default: nil
55
+ def header_component
56
+ super || Blacklight::HeaderComponent
32
57
  end
58
+
33
59
  # === Search request configuration
34
60
 
35
61
  # @!attribute http_method
@@ -52,6 +78,10 @@ module Blacklight
52
78
  # @since v5.2.0
53
79
  # @return [String] The url path (relative to the solr base url) to use when requesting only a single document
54
80
  property :document_solr_path, default: 'get'
81
+ # @!attribute json_solr_path
82
+ # @since v7.34.0
83
+ # @return [String] The url path (relative to the solr base url) to use when using Solr's JSON Query DSL (as with the advanced search)
84
+ property :json_solr_path, default: nil
55
85
  # @!attribute document_unique_id_param
56
86
  # @since v5.2.0
57
87
  # @return [Symbol] The solr query parameter used for sending the unique identifiers for one or more documents
@@ -357,6 +387,8 @@ module Blacklight
357
387
  define_field_access :email_field, Blacklight::Configuration::DisplayField
358
388
 
359
389
  def initialize(hash = {})
390
+ self.class.initialize_default_configuration unless self.class.initialized_default_configuration?
391
+
360
392
  super(self.class.default_values.deep_dup.merge(hash))
361
393
  yield(self) if block_given?
362
394
 
@@ -368,6 +400,11 @@ module Blacklight
368
400
  repository_class.new(self)
369
401
  end
370
402
 
403
+ # @return [String] The destination for the link around the logo in the header
404
+ def logo_link
405
+ super || Rails.application.routes.url_helpers.root_path
406
+ end
407
+
371
408
  # DSL helper
372
409
  # @yield [config]
373
410
  # @yieldparam [Blacklight::Configuration]
@@ -6,6 +6,17 @@ module Blacklight
6
6
  class Engine < Rails::Engine
7
7
  engine_name "blacklight"
8
8
 
9
+ config.before_configuration do
10
+ # see https://github.com/fxn/zeitwerk#for_gem
11
+ # Blacklight puts a generator into LOCAL APP lib/generators, so tell
12
+ # zeitwerk to ignore the whole directory? If we're using zeitwerk
13
+ #
14
+ # https://github.com/cbeer/engine_cart/issues/117
15
+ if Rails.try(:autoloaders).try(:main).respond_to?(:ignore)
16
+ Rails.autoloaders.main.ignore(Rails.root.join('lib/generators'))
17
+ end
18
+ end
19
+
9
20
  # BlacklightHelper is needed by all helpers, so we inject it
10
21
  # into action view base here.
11
22
  initializer 'blacklight.helpers' do
@@ -14,6 +25,10 @@ module Blacklight
14
25
  end
15
26
  end
16
27
 
28
+ config.after_initialize do
29
+ Blacklight::Configuration.initialize_default_configuration
30
+ end
31
+
17
32
  # This makes our rake tasks visible.
18
33
  rake_tasks do
19
34
  Dir.chdir(File.expand_path(File.join(File.dirname(__FILE__), '..'))) do
@@ -20,7 +20,7 @@ module Blacklight::Solr
20
20
  # Execute a search query against solr
21
21
  # @param [Hash] params solr query parameters
22
22
  def search params = {}
23
- send_and_receive blacklight_config.solr_path, params.reverse_merge(qt: blacklight_config.qt)
23
+ send_and_receive search_path(params), params.reverse_merge(qt: blacklight_config.qt)
24
24
  end
25
25
 
26
26
  # @param [Hash] request_params
@@ -77,7 +77,7 @@ module Blacklight::Solr
77
77
  # @return [Hash]
78
78
  # @!visibility private
79
79
  def build_solr_request(solr_params)
80
- if solr_params[:json].present?
80
+ if uses_json_query_dsl?(solr_params)
81
81
  {
82
82
  data: { params: solr_params.to_hash.except(:json) }.merge(solr_params[:json]).to_json,
83
83
  method: :post,
@@ -121,5 +121,17 @@ module Blacklight::Solr
121
121
  []
122
122
  end
123
123
  end
124
+
125
+ # @return [String]
126
+ def search_path(solr_params)
127
+ return blacklight_config.json_solr_path if blacklight_config.json_solr_path && uses_json_query_dsl?(solr_params)
128
+
129
+ blacklight_config.solr_path
130
+ end
131
+
132
+ # @return [Boolean]
133
+ def uses_json_query_dsl?(solr_params)
134
+ solr_params[:json].present?
135
+ end
124
136
  end
125
137
  end