blacklight 5.7.2 → 5.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -3
  3. data/Gemfile +1 -4
  4. data/VERSION +1 -1
  5. data/app/assets/stylesheets/blacklight/_catalog.css.scss +38 -1
  6. data/app/assets/stylesheets/blacklight/_facets.css.scss +10 -0
  7. data/app/assets/stylesheets/blacklight/_layout.css.scss +6 -0
  8. data/app/controllers/bookmarks_controller.rb +3 -163
  9. data/app/helpers/blacklight/blacklight_helper_behavior.rb +18 -186
  10. data/app/helpers/blacklight/catalog_helper_behavior.rb +36 -2
  11. data/app/helpers/blacklight/component_helper_behavior.rb +77 -0
  12. data/app/helpers/blacklight/configuration_helper_behavior.rb +30 -21
  13. data/app/helpers/blacklight/render_partials_helper.rb +185 -0
  14. data/app/helpers/blacklight/url_helper_behavior.rb +24 -3
  15. data/app/helpers/component_helper.rb +3 -0
  16. data/app/views/_user_util_links.html.erb +2 -15
  17. data/app/views/blacklight/nav/_bookmark.html.erb +4 -0
  18. data/app/views/blacklight/nav/_saved_searches.html.erb +1 -0
  19. data/app/views/blacklight/nav/_search_history.html.erb +1 -0
  20. data/app/views/bookmarks/_tools.html.erb +6 -9
  21. data/app/views/bookmarks/index.html.erb +1 -1
  22. data/app/views/catalog/_bookmark_control.html.erb +8 -8
  23. data/app/views/catalog/_constraints_element.html.erb +1 -1
  24. data/app/views/catalog/_document_action.html.erb +4 -0
  25. data/app/views/catalog/_email_form.html.erb +1 -1
  26. data/app/views/catalog/_facet_limit.html.erb +1 -1
  27. data/app/views/catalog/_index_header_default.html.erb +5 -6
  28. data/app/views/catalog/_per_page_widget.html.erb +3 -1
  29. data/app/views/catalog/_results_pagination.html.erb +1 -1
  30. data/app/views/catalog/_search_form.html.erb +5 -8
  31. data/app/views/catalog/_show_more_like_this.html.erb +2 -2
  32. data/app/views/catalog/_show_tools.html.erb +5 -34
  33. data/app/views/catalog/_sms_form.html.erb +1 -1
  34. data/app/views/catalog/_sort_and_per_page.html.erb +2 -6
  35. data/app/views/catalog/_sort_widget.html.erb +1 -1
  36. data/app/views/catalog/_zero_results.html.erb +2 -2
  37. data/app/views/catalog/citation.js.erb +1 -1
  38. data/app/views/catalog/email_sent.html.erb +2 -9
  39. data/app/views/catalog/email_success.html.erb +9 -0
  40. data/app/views/catalog/sms_sent.html.erb +2 -9
  41. data/app/views/catalog/sms_success.html.erb +9 -0
  42. data/app/views/kaminari/blacklight/_gap.html.erb +1 -1
  43. data/app/views/kaminari/blacklight/_page.html.erb +5 -1
  44. data/app/views/shared/_header_navbar.html.erb +1 -1
  45. data/config/locales/blacklight.en.yml +1 -1
  46. data/config/locales/blacklight.es.yml +1 -1
  47. data/config/locales/blacklight.fr.yml +1 -1
  48. data/config/locales/blacklight.pt-BR.yml +1 -1
  49. data/lib/blacklight.rb +3 -0
  50. data/lib/blacklight/base.rb +0 -1
  51. data/lib/blacklight/bookmarks.rb +135 -0
  52. data/lib/blacklight/catalog.rb +58 -77
  53. data/lib/blacklight/catalog/component_configuration.rb +99 -0
  54. data/lib/blacklight/configuration.rb +82 -4
  55. data/lib/blacklight/configuration/tool_config.rb +4 -0
  56. data/lib/blacklight/controller.rb +5 -1
  57. data/lib/blacklight/document_presenter.rb +17 -8
  58. data/lib/blacklight/request_builders.rb +136 -4
  59. data/lib/blacklight/routes.rb +5 -0
  60. data/lib/blacklight/solr_helper.rb +90 -208
  61. data/lib/blacklight/solr_repository.rb +69 -0
  62. data/lib/blacklight/token_based_user.rb +58 -0
  63. data/lib/blacklight/utils.rb +13 -1
  64. data/lib/generators/blacklight/install_generator.rb +6 -7
  65. data/spec/controllers/alternate_controller_spec.rb +19 -0
  66. data/spec/controllers/catalog_controller_spec.rb +89 -4
  67. data/spec/features/alternate_controller_spec.rb +0 -1
  68. data/spec/features/bookmarks_spec.rb +31 -6
  69. data/spec/features/search_results_spec.rb +11 -0
  70. data/spec/features/search_spec.rb +5 -0
  71. data/spec/helpers/blacklight_helper_spec.rb +49 -8
  72. data/spec/helpers/catalog_helper_spec.rb +56 -8
  73. data/spec/helpers/configuration_helper_spec.rb +5 -5
  74. data/spec/helpers/url_helper_spec.rb +15 -8
  75. data/spec/lib/blacklight/catalog/component_configuration_spec.rb +29 -0
  76. data/spec/lib/blacklight/configuration_spec.rb +15 -0
  77. data/spec/lib/blacklight/solr_helper_spec.rb +44 -104
  78. data/spec/lib/blacklight/solr_repository_spec.rb +113 -0
  79. data/spec/lib/utils_spec.rb +27 -0
  80. data/spec/views/_user_util_links.html.erb_spec.rb +6 -3
  81. data/spec/views/catalog/_show_sidebar.erb_spec.rb +8 -2
  82. data/spec/views/catalog/_show_tools.html.erb_spec.rb +82 -0
  83. data/spec/views/catalog/_sort_and_per_page.html.erb_spec.rb +15 -1
  84. data/tasks/blacklight.rake +25 -1
  85. metadata +24 -2
@@ -5,6 +5,7 @@ module Blacklight
5
5
  class Configuration < OpenStructWithHashAccess
6
6
 
7
7
  require 'blacklight/configuration/view_config'
8
+ require 'blacklight/configuration/tool_config'
8
9
  # XXX this isn't very pretty, but it works.
9
10
  require 'blacklight/configuration/fields'
10
11
  require 'blacklight/configuration/solr_field'
@@ -31,6 +32,7 @@ module Blacklight
31
32
  :default_solr_params => {},
32
33
  # the model to load solr response documents into; set below in #initialize_default_values
33
34
  :solr_document_model => nil,
35
+ :solr_response_model => nil,
34
36
  # The solr rqeuest handler to use when requesting only a single document
35
37
  :document_solr_request_handler => 'document',
36
38
  # THe path to send single document requests to solr
@@ -56,6 +58,8 @@ module Blacklight
56
58
  :display_type_field => 'format',
57
59
  # partials to render for each document(see #render_document_partials)
58
60
  :partials => [:index_header, :thumbnail, :index],
61
+ :document_actions => NestedOpenStructWithHashAccess.new(ToolConfig),
62
+ :collection_actions => NestedOpenStructWithHashAccess.new(ToolConfig),
59
63
  # what field, if any, to use to render grouped results
60
64
  :group => false,
61
65
  # additional response formats for search results
@@ -69,8 +73,10 @@ module Blacklight
69
73
  # current controller.
70
74
  route: nil,
71
75
  # partials to render for each document(see #render_document_partials)
72
- partials: [:show_header, :show]
76
+ partials: [:show_header, :show],
77
+ document_actions: NestedOpenStructWithHashAccess.new(ToolConfig)
73
78
  ),
79
+ :navbar => OpenStructWithHashAccess.new(partials: { }),
74
80
  # Configurations for specific types of index views
75
81
  :view => NestedOpenStructWithHashAccess.new(ViewConfig, 'list'),
76
82
  # Maxiumum number of spelling suggestions to offer
@@ -137,6 +143,10 @@ module Blacklight
137
143
  super || SolrDocument
138
144
  end
139
145
 
146
+ def solr_response_model
147
+ super || Blacklight::SolrResponse
148
+ end
149
+
140
150
  ##
141
151
  # DSL helper
142
152
  def configure
@@ -226,10 +236,16 @@ module Blacklight
226
236
  # Provide a 'deep copy' of Blacklight::Configuration that can be modifyed without affecting
227
237
  # the original Blacklight::Configuration instance.
228
238
  #
229
- def deep_copy
230
- Marshal.load(Marshal.dump(self))
239
+ # The Rails 3.x version only copies hashes, and ignores arrays and similar structures
240
+ if ::Rails.version < "4.0"
241
+ def deep_copy
242
+ Marshal.load(Marshal.dump(self))
243
+ end
244
+ alias_method :inheritable_copy, :deep_copy
245
+ else
246
+ alias_method :deep_copy, :deep_dup
247
+ alias_method :inheritable_copy, :deep_dup
231
248
  end
232
- alias_method :inheritable_copy, :deep_copy
233
249
 
234
250
  ##
235
251
  # Get a view configuration for the given view type
@@ -241,5 +257,67 @@ module Blacklight
241
257
  self.index.merge view.fetch(view_type, {})
242
258
  end
243
259
  end
260
+
261
+ ##
262
+ # Add a partial to the tools when rendering a document.
263
+ # @param partial [String] the name of the document partial
264
+ # @param opts [Hash]
265
+ # @option opts [Symbol,Proc] :if render this action if the method identified by the symbol or the proc evaluates to true.
266
+ # The proc will receive the action configuration and the document or documents for the action.
267
+ # @option opts [Symbol,Proc] :unless render this action unless the method identified by the symbol or the proc evaluates to true
268
+ # The proc will receive the action configuration and the document or documents for the action.
269
+ def add_show_tools_partial(name, opts = {})
270
+ opts[:partial] ||= 'document_action'
271
+ add_action(show.document_actions, name, opts)
272
+ end
273
+
274
+ ##
275
+ # Add a tool for the search result list itself
276
+ # @param partial [String] the name of the document partial
277
+ # @param opts [Hash]
278
+ # @option opts [Symbol,Proc] :if render this action if the method identified by the symbol or the proc evaluates to true.
279
+ # The proc will receive the action configuration and the document or documents for the action.
280
+ # @option opts [Symbol,Proc] :unless render this action unless the method identified by the symbol or the proc evaluates to true
281
+ # The proc will receive the action configuration and the document or documents for the action.
282
+ def add_results_collection_tool(name, opts = {})
283
+ add_action(index.collection_actions, name, opts)
284
+ end
285
+
286
+ ##
287
+ # Add a partial to the tools for each document in the search results.
288
+ # @param partial [String] the name of the document partial
289
+ # @param opts [Hash]
290
+ # @option opts [Symbol,Proc] :if render this action if the method identified by the symbol or the proc evaluates to true.
291
+ # The proc will receive the action configuration and the document or documents for the action.
292
+ # @option opts [Symbol,Proc] :unless render this action unless the method identified by the symbol or the proc evaluates to true
293
+ # The proc will receive the action configuration and the document or documents for the action.
294
+ def add_results_document_tool(name, opts = {})
295
+ add_action(index.document_actions, name, opts)
296
+ end
297
+
298
+ ##
299
+ # Add a partial to the header navbar
300
+ # @param partial [String] the name of the document partial
301
+ # @param opts [Hash]
302
+ # @option opts [Symbol,Proc] :if render this action if the method identified by the symbol or the proc evaluates to true.
303
+ # The proc will receive the action configuration and the document or documents for the action.
304
+ # @option opts [Symbol,Proc] :unless render this action unless the method identified by the symbol or the proc evaluates to true
305
+ # The proc will receive the action configuration and the document or documents for the action.
306
+ def add_nav_action name, opts = {}
307
+ add_action(navbar.partials, name, opts)
308
+ end
309
+
310
+ private
311
+
312
+ def add_action config_hash, name, opts
313
+ config = Blacklight::Configuration::ToolConfig.new opts
314
+ config.name = name
315
+
316
+ if block_given?
317
+ yield config
318
+ end
319
+
320
+ config_hash[name] = config
321
+ end
244
322
  end
245
323
  end
@@ -0,0 +1,4 @@
1
+ module Blacklight
2
+ class Configuration::ToolConfig < OpenStructWithHashAccess
3
+ end
4
+ end
@@ -23,7 +23,7 @@ module Blacklight::Controller
23
23
  # extra head content
24
24
  helper_method :has_user_authentication_provider?
25
25
  helper_method :blacklight_config
26
- helper_method :search_action_url, :search_action_path
26
+ helper_method :search_action_url, :search_action_path, :search_facet_url
27
27
 
28
28
 
29
29
  # This callback runs when a user first logs in
@@ -59,6 +59,10 @@ module Blacklight::Controller
59
59
  search_action_url *args
60
60
  end
61
61
 
62
+ def search_facet_url options = {}
63
+ url_for params.merge(action: "facet").merge(options).except(:page)
64
+ end
65
+
62
66
  # Returns a list of Searches from the ids in the user's history.
63
67
  def searches_from_history
64
68
  session[:history].blank? ? Search.none : Search.where(:id => session[:history]).order("updated_at desc")
@@ -2,6 +2,7 @@ module Blacklight
2
2
  class DocumentPresenter
3
3
  include ActionView::Helpers::OutputSafetyHelper
4
4
  include ActionView::Helpers::TagHelper
5
+ extend Deprecation
5
6
 
6
7
  # @param [SolrDocument] document
7
8
  # @param [ActionController::Base] controller scope for linking and generating urls
@@ -55,17 +56,25 @@ module Blacklight
55
56
  ##
56
57
  # Render the document index heading
57
58
  #
58
- # @param [Hash] opts
59
+ # @param [Hash] opts (Deprecated)
59
60
  # @option opts [Symbol] :label Render the given field from the document
60
61
  # @option opts [Proc] :label Evaluate the given proc
61
62
  # @option opts [String] :label Render the given string
62
- def render_document_index_label opts = {}
63
- label = nil
64
- label ||= @document.get(opts[:label], :sep => nil) if opts[:label].instance_of? Symbol
65
- label ||= opts[:label].call(@document, opts) if opts[:label].instance_of? Proc
66
- label ||= opts[:label] if opts[:label].is_a? String
67
- label ||= @document.id
68
- render_field_value label
63
+ # @param [Symbol, Proc, String] field Render the given field or evaluate the proc or render the given string
64
+ def render_document_index_label field, opts ={}
65
+ if field.kind_of? Hash
66
+ Deprecation.warn DocumentPresenter, "Calling render_document_index_label with a hash is deprecated"
67
+ field = field[:label]
68
+ end
69
+ label = case field
70
+ when Symbol
71
+ @document.get(field, :sep => nil)
72
+ when Proc
73
+ field.call(@document, opts)
74
+ when String
75
+ field
76
+ end
77
+ render_field_value label || @document.id
69
78
  end
70
79
 
71
80
  ##
@@ -6,6 +6,8 @@ module Blacklight
6
6
  #
7
7
  module RequestBuilders
8
8
  extend ActiveSupport::Concern
9
+ extend Deprecation
10
+ self.deprecation_horizon = 'blacklight 6.0'
9
11
 
10
12
  included do
11
13
  # We want to install a class-level place to keep
@@ -21,7 +23,11 @@ module Blacklight
21
23
  # CatalogController.include ModuleDefiningNewMethod
22
24
  # CatalogController.solr_search_params_logic += [:new_method]
23
25
  # CatalogController.solr_search_params_logic.delete(:we_dont_want)
24
- self.solr_search_params_logic = [:default_solr_parameters , :add_query_to_solr, :add_facet_fq_to_solr, :add_facetting_to_solr, :add_solr_fields_to_query, :add_paging_to_solr, :add_sorting_to_solr, :add_group_config_to_solr ]
26
+ self.solr_search_params_logic = [:default_solr_parameters, :add_query_to_solr, :add_facet_fq_to_solr, :add_facetting_to_solr, :add_solr_fields_to_query, :add_paging_to_solr, :add_sorting_to_solr, :add_group_config_to_solr ]
27
+
28
+ if self.respond_to?(:helper_method)
29
+ helper_method(:facet_limit_for)
30
+ end
25
31
  end
26
32
 
27
33
  # @returns a params hash for searching solr.
@@ -47,6 +53,86 @@ module Blacklight
47
53
  end
48
54
  end
49
55
 
56
+ ##
57
+ # Retrieve the results for a list of document ids
58
+ def solr_document_ids_params(ids = [])
59
+ solr_documents_by_field_values_params blacklight_config.solr_document_model.unique_key, ids
60
+ end
61
+
62
+ ##
63
+ # Retrieve the results for a list of document ids
64
+ # @deprecated
65
+ def solr_documents_by_field_values_params(field, values)
66
+ q = if Array(values).empty?
67
+ "{!lucene}NOT *:*"
68
+ else
69
+ "{!lucene}#{field}:(#{ Array(values).map { |x| solr_param_quote(x) }.join(" OR ")})"
70
+ end
71
+
72
+ { q: q, spellcheck: 'false', fl: "*" }
73
+ end
74
+
75
+ ##
76
+ # Retrieve a facet's paginated values.
77
+ def solr_facet_params(facet_field, user_params=params || {}, extra_controller_params={})
78
+ input = user_params.deep_merge(extra_controller_params)
79
+ facet_config = blacklight_config.facet_fields[facet_field]
80
+
81
+ solr_params = {}
82
+
83
+ # Now override with our specific things for fetching facet values
84
+ solr_params[:"facet.field"] = with_ex_local_param((facet_config.ex if facet_config.respond_to?(:ex)), facet_field)
85
+
86
+ limit = if respond_to?(:facet_list_limit)
87
+ facet_list_limit.to_s.to_i
88
+ elsif solr_params["facet.limit"]
89
+ solr_params["facet.limit"].to_i
90
+ else
91
+ 20
92
+ end
93
+
94
+ # Need to set as f.facet_field.facet.* to make sure we
95
+ # override any field-specific default in the solr request handler.
96
+ solr_params[:"f.#{facet_field}.facet.limit"] = limit + 1
97
+ solr_params[:"f.#{facet_field}.facet.offset"] = ( input.fetch(Blacklight::Solr::FacetPaginator.request_keys[:page] , 1).to_i - 1 ) * ( limit )
98
+ solr_params[:"f.#{facet_field}.facet.sort"] = input[ Blacklight::Solr::FacetPaginator.request_keys[:sort] ] if input[ Blacklight::Solr::FacetPaginator.request_keys[:sort] ]
99
+ solr_params[:rows] = 0
100
+
101
+ solr_params
102
+ end
103
+
104
+ ##
105
+ # Opensearch autocomplete parameters for plucking a field's value from the results
106
+ def solr_opensearch_params(field=nil)
107
+ if field.nil?
108
+ Deprecation.warn(Blacklight::RequestBuilders, "Calling Blacklight::RequestBuilders#solr_opensearch_params without a field name is deprecated and will be required in Blacklight 6.0.")
109
+ end
110
+
111
+ solr_params = {}
112
+ solr_params[:rows] ||= 10
113
+ solr_params[:fl] = field || blacklight_config.view_config('opensearch').title_field
114
+ solr_params
115
+ end
116
+
117
+ ##
118
+ # Pagination parameters for selecting the previous and next documents
119
+ # out of a result set.
120
+ def previous_and_next_document_params(index, window = 1)
121
+ solr_params = {}
122
+
123
+ if index > 0
124
+ solr_params[:start] = index - window # get one before
125
+ solr_params[:rows] = 2*window + 1 # and one after
126
+ else
127
+ solr_params[:start] = 0 # there is no previous doc
128
+ solr_params[:rows] = 2*window # but there should be one after
129
+ end
130
+
131
+ solr_params[:fl] = '*'
132
+ solr_params[:facet] = false
133
+ solr_params
134
+ end
135
+
50
136
  ####
51
137
  # Start with general defaults from BL config. Need to use custom
52
138
  # merge to dup values, to avoid later mutating the original by mistake.
@@ -55,7 +141,7 @@ module Blacklight
55
141
  solr_parameters[key] = value.dup rescue value
56
142
  end
57
143
  end
58
-
144
+
59
145
  ##
60
146
  # Take the user-entered query, and put it in the solr params,
61
147
  # including config's "search field" params for current search field.
@@ -71,7 +157,7 @@ module Blacklight
71
157
  # rspec'd.
72
158
  solr_parameters[:qt] = user_parameters[:qt] if user_parameters[:qt]
73
159
 
74
- search_field_def = search_field_def_for_key(user_parameters[:search_field])
160
+ search_field_def = blacklight_config.search_fields[user_parameters[:search_field]]
75
161
  if (search_field_def)
76
162
  solr_parameters[:qt] = search_field_def.qt
77
163
  solr_parameters.merge!( search_field_def.solr_parameters) if search_field_def.solr_parameters
@@ -253,8 +339,55 @@ module Blacklight
253
339
  end
254
340
  end
255
341
 
342
+
343
+ DEFAULT_FACET_LIMIT = 10
344
+
345
+ # Look up facet limit for given facet_field. Will look at config, and
346
+ # if config is 'true' will look up from Solr @response if available. If
347
+ # no limit is avaialble, returns nil. Used from #solr_search_params
348
+ # to supply f.fieldname.facet.limit values in solr request (no @response
349
+ # available), and used in display (with @response available) to create
350
+ # a facet paginator with the right limit.
351
+ def facet_limit_for(facet_field)
352
+ facet = blacklight_config.facet_fields[facet_field]
353
+ return if facet.blank?
354
+
355
+ if facet.limit and @response and @response.facet_by_field_name(facet_field)
356
+ limit = @response.facet_by_field_name(facet_field).limit
357
+
358
+ if limit.nil? # we didn't get or a set a limit, so infer one.
359
+ facet.limit if facet.limit != true
360
+ elsif limit == -1 # limit -1 is solr-speak for unlimited
361
+ nil
362
+ else
363
+ limit.to_i - 1 # we added 1 to find out if we needed to paginate
364
+ end
365
+ elsif facet.limit
366
+ facet.limit == true ? DEFAULT_FACET_LIMIT : facet.limit
367
+ end
368
+ end
369
+
370
+ ##
371
+ # A helper method used for generating solr LocalParams, put quotes
372
+ # around the term unless it's a bare-word. Escape internal quotes
373
+ # if needed.
374
+ def solr_param_quote(val, options = {})
375
+ options[:quote] ||= '"'
376
+ unless val =~ /^[a-zA-Z0-9$_\-\^]+$/
377
+ val = options[:quote] +
378
+ # Yes, we need crazy escaping here, to deal with regexp esc too!
379
+ val.gsub("'", "\\\\\'").gsub('"', "\\\\\"") +
380
+ options[:quote]
381
+ end
382
+ return val
383
+ end
384
+
256
385
  private
257
386
 
387
+ def should_add_to_solr field_name, field
388
+ field.include_in_request || (field.include_in_request.nil? && blacklight_config.add_field_configuration_to_solr_request)
389
+ end
390
+
258
391
  ##
259
392
  # Convert a facet/value pair into a solr fq parameter
260
393
  def facet_value_to_fq_string(facet_field, value)
@@ -284,7 +417,6 @@ module Blacklight
284
417
  "{!raw f=#{facet_field}#{(" " + local_params.join(" ")) unless local_params.empty?}}#{value}"
285
418
  end
286
419
 
287
-
288
420
  end
289
421
  end
290
422
  end
@@ -61,6 +61,11 @@ module Blacklight
61
61
  def bookmarks(_)
62
62
  add_routes do |options|
63
63
  delete "bookmarks/clear", :to => "bookmarks#clear", :as => "clear_bookmarks"
64
+ get "bookmarks/email", :as => "email_bookmarks"
65
+ post "bookmarks/email"
66
+ get "bookmarks/sms", :as => "sms_bookmarks"
67
+ post "bookmarks/sms"
68
+ get "bookmarks/citation", :as => "citation_bookmarks"
64
69
  resources :bookmarks
65
70
  end
66
71
  end
@@ -47,65 +47,30 @@
47
47
  module Blacklight::SolrHelper
48
48
  extend ActiveSupport::Concern
49
49
  extend Deprecation
50
- include Blacklight::SearchFields
51
- include Blacklight::Facet
52
- include ActiveSupport::Benchmarkable
50
+ self.deprecation_horizon = 'blacklight 6.0'
53
51
 
54
- included do
55
- if self.respond_to?(:helper_method)
56
- helper_method(:facet_limit_for)
57
- end
58
-
59
- include Blacklight::RequestBuilders
60
-
61
- end
52
+ include Blacklight::RequestBuilders
62
53
 
63
54
  ##
64
55
  # Execute a solr query
65
- # @see [RSolr::Client#send_and_receive]
66
- # @overload find(solr_path, params)
67
- # Execute a solr query at the given path with the parameters
68
- # @param [String] solr path (defaults to blacklight_config.solr_path)
69
- # @param [Hash] parameters for RSolr::Client#send_and_receive
70
- # @overload find(params)
71
- # @param [Hash] parameters for RSolr::Client#send_and_receive
56
+ # @see [Blacklight::SolrRepository#send_and_receive]
72
57
  # @return [Blacklight::SolrResponse] the solr response object
73
- def find(*args)
74
- # In later versions of Rails, the #benchmark method can do timing
75
- # better for us.
76
- benchmark("Solr fetch", level: :debug) do
77
- solr_params = args.extract_options!
78
- path = args.first || blacklight_config.solr_path
79
- solr_params[:qt] ||= blacklight_config.qt
80
- # delete these parameters, otherwise rsolr will pass them through.
81
- key = blacklight_config.http_method == :post ? :data : :params
82
- res = blacklight_solr.send_and_receive(path, {key=>solr_params.to_hash, method:blacklight_config.http_method})
83
-
84
- solr_response = Blacklight::SolrResponse.new(res, solr_params, solr_document_model: blacklight_config.solr_document_model)
85
-
86
- Rails.logger.debug("Solr query: #{solr_params.inspect}")
87
- Rails.logger.debug("Solr response: #{solr_response.inspect}") if defined?(::BLACKLIGHT_VERBOSE_LOGGING) and ::BLACKLIGHT_VERBOSE_LOGGING
88
- solr_response
89
- end
90
- rescue Errno::ECONNREFUSED => e
91
- raise Blacklight::Exceptions::ECONNREFUSED.new("Unable to connect to Solr instance using #{blacklight_solr.inspect}")
58
+ def find *args
59
+ solr_params = args.extract_options!
60
+ path = args.first || blacklight_config.solr_path
61
+
62
+ solr_params[:qt] ||= blacklight_config.qt
63
+
64
+ solr_repository.send_and_receive path, solr_params
92
65
  end
93
-
94
-
95
- # A helper method used for generating solr LocalParams, put quotes
96
- # around the term unless it's a bare-word. Escape internal quotes
97
- # if needed.
98
- def solr_param_quote(val, options = {})
99
- options[:quote] ||= '"'
100
- unless val =~ /^[a-zA-Z0-9$_\-\^]+$/
101
- val = options[:quote] +
102
- # Yes, we need crazy escaping here, to deal with regexp esc too!
103
- val.gsub("'", "\\\\\'").gsub('"', "\\\\\"") +
104
- options[:quote]
105
- end
106
- return val
66
+ deprecation_deprecate :find
67
+
68
+ # returns a params hash for finding a single solr document (CatalogController #show action)
69
+ def solr_doc_params(id=nil)
70
+ default_solr_doc_params(id)
107
71
  end
108
-
72
+ deprecation_deprecate :solr_doc_params
73
+
109
74
  # a solr query method
110
75
  # given a user query, return a solr response containing both result docs and facets
111
76
  # - mixes in the Blacklight::Solr::SpellingSuggestions module
@@ -132,111 +97,66 @@ module Blacklight::SolrHelper
132
97
  def query_solr(user_params = params || {}, extra_controller_params = {})
133
98
  solr_params = self.solr_search_params(user_params).merge(extra_controller_params)
134
99
 
135
- find(solr_params)
100
+ solr_repository.search(solr_params)
136
101
  end
137
-
138
- # returns a params hash for finding a single solr document (CatalogController #show action)
139
- # If the id arg is nil, then the value is fetched from params[:id]
140
- # This method is primary called by the get_solr_response_for_doc_id method.
141
- def solr_doc_params(id=nil)
142
- id ||= params[:id]
143
102
 
144
- # add our document id to the document_unique_id_param query parameter
145
- p = blacklight_config.default_document_solr_params.merge({
146
- # this assumes the request handler will map the unique id param
147
- # to the unique key field using either solr local params, the
148
- # real-time get handler, etc.
149
- blacklight_config.document_unique_id_param => id
150
- })
151
-
152
- p[:qt] ||= blacklight_config.document_solr_request_handler
153
-
154
- p
155
- end
156
-
157
103
  # a solr query method
158
104
  # retrieve a solr document, given the doc id
159
105
  # @return [Blacklight::SolrResponse, Blacklight::SolrDocument] the solr response object and the first document
160
106
  def get_solr_response_for_doc_id(id=nil, extra_controller_params={})
161
- solr_params = solr_doc_params(id).merge(extra_controller_params)
162
- solr_response = find(blacklight_config.document_solr_path, solr_params)
163
- raise Blacklight::Exceptions::InvalidSolrID.new if solr_response.documents.empty?
107
+ if id.nil?
108
+ Deprecation.warn Blacklight::SolrHelper, "Calling #get_solr_response_for_doc_id without an explicit id argument is deprecated"
109
+ id ||= params[:id]
110
+ end
111
+
112
+ old_solr_doc_params = Deprecation.silence(Blacklight::SolrHelper) do
113
+ solr_doc_params(id)
114
+ end
115
+
116
+ if default_solr_doc_params(id) != old_solr_doc_params
117
+ Deprecation.warn Blacklight::SolrHelper, "The #solr_doc_params method is deprecated. Instead, you should provide a custom SolrRepository implementation for the additional behavior you're offering"
118
+ extra_controller_params = extra_controller_params.merge(old_solr_doc_params)
119
+ end
120
+
121
+ solr_response = solr_repository.find id, extra_controller_params
164
122
  [solr_response, solr_response.documents.first]
165
123
  end
166
124
 
167
- def get_solr_response_for_document_ids(ids=[], extra_solr_params = {})
168
- get_solr_response_for_field_values(blacklight_config.solr_document_model.unique_key, ids, extra_solr_params)
169
- end
170
-
171
- # given a field name and array of values, get the matching SOLR documents
172
- # @return [Blacklight::SolrResponse, Array<Blacklight::SolrDocument>] the solr response object and a list of solr documents
173
- def get_solr_response_for_field_values(field, values, extra_solr_params = {})
174
- q = if Array(values).empty?
175
- "NOT *:*"
125
+ ##
126
+ # Retrieve a set of documents by id
127
+ # @overload get_solr_response_for_document_ids(ids, extra_controller_params)
128
+ # @overload get_solr_response_for_document_ids(ids, user_params, extra_controller_params)
129
+ def get_solr_response_for_document_ids(ids=[], *args)
130
+ # user_params = params || {}, extra_controller_params = {}
131
+ if args.length == 1
132
+ user_params = params
133
+ extra_controller_params = args.first || {}
176
134
  else
177
- "#{field}:(#{ Array(values).map { |x| solr_param_quote(x)}.join(" OR ")})"
135
+ user_params, extra_controller_params = args
136
+ user_params ||= params
137
+ extra_controller_params ||= {}
178
138
  end
179
139
 
180
- solr_params = {
181
- :defType => "lucene", # need boolean for OR
182
- :q => q,
183
- # not sure why fl * is neccesary, why isn't default solr_search_params
184
- # sufficient, like it is for any other search results solr request?
185
- # But tests fail without this. I think because some functionality requires
186
- # this to actually get solr_doc_params, not solr_search_params. Confused
187
- # semantics again.
188
- :fl => "*",
189
- :facet => 'false',
190
- :spellcheck => 'false'
191
- }.merge(extra_solr_params)
192
-
193
- solr_response = find(self.solr_search_params().merge(solr_params) )
140
+ solr_response = query_solr(user_params, extra_controller_params.merge(solr_document_ids_params(ids)))
141
+
194
142
  [solr_response, solr_response.documents]
195
143
  end
196
-
197
- # returns a params hash for a single facet field solr query.
198
- # used primary by the get_facet_pagination method.
199
- # Looks up Facet Paginator request params from current request
200
- # params to figure out sort and offset.
201
- # Default limit for facet list can be specified by defining a controller
202
- # method facet_list_limit, otherwise 20.
203
- def solr_facet_params(facet_field, user_params=params || {}, extra_controller_params={})
204
- input = user_params.deep_merge(extra_controller_params)
205
- facet_config = blacklight_config.facet_fields[facet_field]
206
-
207
- # First start with a standard solr search params calculations,
208
- # for any search context in our request params.
209
- solr_params = solr_search_params(user_params).merge(extra_controller_params)
210
-
211
- # Now override with our specific things for fetching facet values
212
- solr_params[:"facet.field"] = with_ex_local_param((facet_config.ex if facet_config.respond_to?(:ex)), facet_field)
213
-
214
- limit =
215
- if respond_to?(:facet_list_limit)
216
- facet_list_limit.to_s.to_i
217
- elsif solr_params["facet.limit"]
218
- solr_params["facet.limit"].to_i
219
- else
220
- 20
221
- end
222
-
223
- # Need to set as f.facet_field.facet.* to make sure we
224
- # override any field-specific default in the solr request handler.
225
- solr_params[:"f.#{facet_field}.facet.limit"] = limit + 1
226
- solr_params[:"f.#{facet_field}.facet.offset"] = ( input.fetch(Blacklight::Solr::FacetPaginator.request_keys[:page] , 1).to_i - 1 ) * ( limit )
227
- solr_params[:"f.#{facet_field}.facet.sort"] = input[ Blacklight::Solr::FacetPaginator.request_keys[:sort] ] if input[ Blacklight::Solr::FacetPaginator.request_keys[:sort] ]
228
- solr_params[:rows] = 0
229
-
230
- return solr_params
144
+
145
+ # given a field name and array of values, get the matching SOLR documents
146
+ # @return [Blacklight::SolrResponse, Array<Blacklight::SolrDocument>] the solr response object and a list of solr documents
147
+ def get_solr_response_for_field_values(field, values, extra_controller_params = {})
148
+ solr_response = query_solr(params, extra_controller_params.merge(solr_documents_by_field_values_params(field, values)))
149
+
150
+ [solr_response, solr_response.documents]
231
151
  end
232
-
152
+ deprecation_deprecate :get_solr_response_for_field_values
153
+
233
154
  ##
234
155
  # Get the solr response when retrieving only a single facet field
235
156
  # @return [Blacklight::SolrResponse] the solr response
236
157
  def get_facet_field_response(facet_field, user_params = params || {}, extra_controller_params = {})
237
158
  solr_params = solr_facet_params(facet_field, user_params, extra_controller_params)
238
- # Make the solr call
239
- find(solr_params)
159
+ query_solr(user_params, extra_controller_params.merge(solr_facet_params(facet_field, user_params, extra_controller_params)))
240
160
  end
241
161
 
242
162
  # a solr query method
@@ -251,28 +171,28 @@ module Blacklight::SolrHelper
251
171
  # Actually create the paginator!
252
172
  # NOTE: The sniffing of the proper sort from the solr response is not
253
173
  # currently tested for, tricky to figure out how to test, since the
254
- # default setup we test against doesn't use this feature.
255
- return Blacklight::Solr::FacetPaginator.new(response.facets.first.items,
256
- :offset => response.params[:"f.#{facet_field}.facet.offset"],
174
+ # default setup we test against doesn't use this feature.
175
+ return Blacklight::Solr::FacetPaginator.new(response.facets.first.items,
176
+ :offset => response.params[:"f.#{facet_field}.facet.offset"],
257
177
  :limit => limit,
258
178
  :sort => response.params[:"f.#{facet_field}.facet.sort"] || response.params["facet.sort"]
259
179
  )
260
180
  end
261
181
  deprecation_deprecate :get_facet_pagination
262
-
182
+
263
183
  # a solr query method
264
- # this is used when selecting a search result: we have a query and a
184
+ # this is used when selecting a search result: we have a query and a
265
185
  # position in the search results and possibly some facets
266
186
  # Pass in an index where 1 is the first document in the list, and
267
- # the Blacklight app-level request params that define the search.
187
+ # the Blacklight app-level request params that define the search.
268
188
  # @return [Blacklight::SolrDocument, nil] the found document or nil if not found
269
189
  def get_single_doc_via_search(index, request_params)
270
190
  solr_params = solr_search_params(request_params)
271
191
 
272
- solr_params[:start] = (index - 1) # start at 0 to get 1st doc, 1 to get 2nd.
192
+ solr_params[:start] = (index - 1) # start at 0 to get 1st doc, 1 to get 2nd.
273
193
  solr_params[:rows] = 1
274
194
  solr_params[:fl] = '*'
275
- solr_response = find(solr_params)
195
+ solr_response = solr_repository.search(solr_params)
276
196
  solr_response.documents.first
277
197
  end
278
198
  deprecation_deprecate :get_single_doc_via_search
@@ -281,19 +201,7 @@ module Blacklight::SolrHelper
281
201
  # @return [Blacklight::SolrResponse, Array<Blacklight::SolrDocument>] the solr response and a list of the first and last document
282
202
  def get_previous_and_next_documents_for_search(index, request_params, extra_controller_params={})
283
203
 
284
- solr_params = solr_search_params(request_params).merge(extra_controller_params)
285
-
286
- if index > 0
287
- solr_params[:start] = index - 1 # get one before
288
- solr_params[:rows] = 3 # and one after
289
- else
290
- solr_params[:start] = 0 # there is no previous doc
291
- solr_params[:rows] = 2 # but there should be one after
292
- end
293
-
294
- solr_params[:fl] = '*'
295
- solr_params[:facet] = false
296
- solr_response = find(solr_params)
204
+ solr_response = query_solr(request_params, extra_controller_params.merge(previous_and_next_document_params(index)))
297
205
 
298
206
  document_list = solr_response.documents
299
207
 
@@ -303,16 +211,6 @@ module Blacklight::SolrHelper
303
211
 
304
212
  [solr_response, [prev_doc, next_doc]]
305
213
  end
306
-
307
- # returns a solr params hash
308
- # the :fl (solr param) is set to the "field" value.
309
- # per_page is set to 10
310
- def solr_opensearch_params(field=nil)
311
- solr_params = solr_search_params
312
- solr_params[:per_page] = 10
313
- solr_params[:fl] = field || blacklight_config.view_config('opensearch').title_field
314
- solr_params
315
- end
316
214
 
317
215
  # a solr query method
318
216
  # does a standard search but returns a simplified object.
@@ -320,38 +218,12 @@ module Blacklight::SolrHelper
320
218
  # the second item is an other array. This second array contains
321
219
  # all of the field values for each of the documents...
322
220
  # where the field is the "field" argument passed in.
323
- def get_opensearch_response(field=nil, extra_controller_params={})
324
- solr_params = solr_opensearch_params().merge(extra_controller_params)
325
- response = find(solr_params)
326
- a = [solr_params[:q]]
327
- a << response.documents.map {|doc| doc[solr_params[:fl]].to_s }
328
- end
329
-
330
- DEFAULT_FACET_LIMIT = 10
331
-
332
- # Look up facet limit for given facet_field. Will look at config, and
333
- # if config is 'true' will look up from Solr @response if available. If
334
- # no limit is avaialble, returns nil. Used from #solr_search_params
335
- # to supply f.fieldname.facet.limit values in solr request (no @response
336
- # available), and used in display (with @response available) to create
337
- # a facet paginator with the right limit.
338
- def facet_limit_for(facet_field)
339
- facet = blacklight_config.facet_fields[facet_field]
340
- return if facet.blank?
341
-
342
- if facet.limit and @response and @response.facet_by_field_name(facet_field)
343
- limit = @response.facet_by_field_name(facet_field).limit
344
-
345
- if limit.nil? # we didn't get or a set a limit, so infer one.
346
- facet.limit if facet.limit != true
347
- elsif limit == -1 # limit -1 is solr-speak for unlimited
348
- nil
349
- else
350
- limit.to_i - 1 # we added 1 to find out if we needed to paginate
351
- end
352
- elsif facet.limit
353
- facet.limit == true ? DEFAULT_FACET_LIMIT : facet.limit
354
- end
221
+ def get_opensearch_response(field=nil, request_params = params || {}, extra_controller_params={})
222
+ field ||= blacklight_config.view_config('opensearch').title_field
223
+
224
+ response = query_solr(request_params, solr_opensearch_params(field).merge(extra_controller_params))
225
+
226
+ [response.params[:q], response.documents.flat_map {|doc| doc[field] }.uniq]
355
227
  end
356
228
 
357
229
  ##
@@ -360,18 +232,28 @@ module Blacklight::SolrHelper
360
232
  blacklight_config.index.group
361
233
  end
362
234
 
363
- def blacklight_solr
364
- @solr ||= RSolr.connect(blacklight_solr_config)
365
- end
366
-
367
- def blacklight_solr_config
368
- Blacklight.solr_config
235
+ def solr_repository
236
+ @solr_repository ||= Blacklight::SolrRepository.new(blacklight_config)
369
237
  end
370
238
 
371
239
  private
372
240
 
373
- def should_add_to_solr field_name, field
374
- field.include_in_request || (field.include_in_request.nil? && blacklight_config.add_field_configuration_to_solr_request)
241
+ ##
242
+ # @deprecated
243
+ def default_solr_doc_params(id=nil)
244
+ id ||= params[:id]
245
+
246
+ # add our document id to the document_unique_id_param query parameter
247
+ p = blacklight_config.default_document_solr_params.merge({
248
+ # this assumes the request handler will map the unique id param
249
+ # to the unique key field using either solr local params, the
250
+ # real-time get handler, etc.
251
+ blacklight_config.document_unique_id_param => id
252
+ })
253
+
254
+ p[:qt] ||= blacklight_config.document_solr_request_handler
255
+
256
+ p
375
257
  end
376
258
 
377
259
  end