blacklight 3.0pre1

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.
Files changed (170) hide show
  1. data/.gitignore +17 -0
  2. data/.gitmodules +6 -0
  3. data/.yardopts +4 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +14 -0
  6. data/README.rdoc +168 -0
  7. data/Rakefile +9 -0
  8. data/app/controllers/bookmarks_controller.rb +98 -0
  9. data/app/controllers/feedback_controller.rb +37 -0
  10. data/app/controllers/folder_controller.rb +49 -0
  11. data/app/controllers/saved_searches_controller.rb +45 -0
  12. data/app/controllers/search_history_controller.rb +25 -0
  13. data/app/helpers/blacklight_helper.rb +606 -0
  14. data/app/helpers/bookmarks_helper.rb +3 -0
  15. data/app/helpers/catalog_helper.rb +65 -0
  16. data/app/helpers/feedback_helper.rb +2 -0
  17. data/app/helpers/hash_as_hidden_fields.rb +57 -0
  18. data/app/helpers/render_constraints_helper.rb +120 -0
  19. data/app/helpers/saved_searches_helper.rb +2 -0
  20. data/app/helpers/search_history_helper.rb +2 -0
  21. data/app/models/bookmark.rb +6 -0
  22. data/app/models/record_mailer.rb +43 -0
  23. data/app/models/search.rb +19 -0
  24. data/app/views/_flash_msg.html.erb +6 -0
  25. data/app/views/_user_util_links.html.erb +13 -0
  26. data/app/views/bookmarks/index.html.erb +33 -0
  27. data/app/views/catalog/_bookmark_control.html.erb +25 -0
  28. data/app/views/catalog/_bookmark_form.html.erb +8 -0
  29. data/app/views/catalog/_citation.html.erb +15 -0
  30. data/app/views/catalog/_constraints.html.erb +7 -0
  31. data/app/views/catalog/_constraints_element.html.erb +33 -0
  32. data/app/views/catalog/_did_you_mean.html.erb +10 -0
  33. data/app/views/catalog/_document_list.html.erb +30 -0
  34. data/app/views/catalog/_email_form.html.erb +11 -0
  35. data/app/views/catalog/_facet_limit.html.erb +33 -0
  36. data/app/views/catalog/_facet_pagination.html.erb +28 -0
  37. data/app/views/catalog/_facets.html.erb +9 -0
  38. data/app/views/catalog/_folder_control.html.erb +12 -0
  39. data/app/views/catalog/_home.html.erb +6 -0
  40. data/app/views/catalog/_home_text.html.erb +6 -0
  41. data/app/views/catalog/_index_partials/_default.erb +11 -0
  42. data/app/views/catalog/_marc_view.html.erb +33 -0
  43. data/app/views/catalog/_opensearch_response_metadata.html.erb +3 -0
  44. data/app/views/catalog/_previous_next_doc.html.erb +6 -0
  45. data/app/views/catalog/_refworks_form.html.erb +7 -0
  46. data/app/views/catalog/_results_pagination.html.erb +11 -0
  47. data/app/views/catalog/_search_form.html.erb +14 -0
  48. data/app/views/catalog/_show_partials/_default.html.erb +9 -0
  49. data/app/views/catalog/_show_sidebar.html.erb +1 -0
  50. data/app/views/catalog/_show_tools.html.erb +46 -0
  51. data/app/views/catalog/_sms_form.html.erb +23 -0
  52. data/app/views/catalog/_solr_request.html.erb +5 -0
  53. data/app/views/catalog/_sort_and_per_page.html.erb +20 -0
  54. data/app/views/catalog/_unapi_microformat.html.erb +1 -0
  55. data/app/views/catalog/citation.html.erb +1 -0
  56. data/app/views/catalog/email.erb +1 -0
  57. data/app/views/catalog/endnote.endnote.erb +1 -0
  58. data/app/views/catalog/facet.html.erb +28 -0
  59. data/app/views/catalog/index.atom.builder +108 -0
  60. data/app/views/catalog/index.html.erb +37 -0
  61. data/app/views/catalog/index.rss.builder +19 -0
  62. data/app/views/catalog/librarian_view.html.erb +3 -0
  63. data/app/views/catalog/opensearch.json.erb +0 -0
  64. data/app/views/catalog/opensearch.xml.erb +11 -0
  65. data/app/views/catalog/send_email_record.erb +0 -0
  66. data/app/views/catalog/show.endnote.erb +1 -0
  67. data/app/views/catalog/show.html.erb +42 -0
  68. data/app/views/catalog/show.refworks.erb +1 -0
  69. data/app/views/catalog/sms.erb +1 -0
  70. data/app/views/catalog/unapi.xml.builder +6 -0
  71. data/app/views/feedback/complete.html.erb +3 -0
  72. data/app/views/feedback/show.html.erb +20 -0
  73. data/app/views/folder/_tools.html.erb +23 -0
  74. data/app/views/folder/index.html.erb +44 -0
  75. data/app/views/layouts/blacklight.html.erb +49 -0
  76. data/app/views/record_mailer/email_record.erb +6 -0
  77. data/app/views/record_mailer/sms_record.erb +4 -0
  78. data/app/views/saved_searches/index.html.erb +27 -0
  79. data/app/views/search_history/index.html.erb +23 -0
  80. data/blacklight.gemspec +50 -0
  81. data/config.ru +4 -0
  82. data/config/routes.rb +54 -0
  83. data/db/seeds.rb +7 -0
  84. data/features/generators.feature +77 -0
  85. data/features/support/aruba.rb +9 -0
  86. data/install.rb +0 -0
  87. data/install/solr.yml +8 -0
  88. data/lib/blacklight.rb +121 -0
  89. data/lib/blacklight/catalog.rb +311 -0
  90. data/lib/blacklight/comma_link_renderer.rb +27 -0
  91. data/lib/blacklight/configurable.rb +46 -0
  92. data/lib/blacklight/controller.rb +121 -0
  93. data/lib/blacklight/engine.rb +32 -0
  94. data/lib/blacklight/exceptions.rb +13 -0
  95. data/lib/blacklight/marc.rb +46 -0
  96. data/lib/blacklight/marc/citation.rb +251 -0
  97. data/lib/blacklight/search_fields.rb +107 -0
  98. data/lib/blacklight/solr.rb +7 -0
  99. data/lib/blacklight/solr/document.rb +239 -0
  100. data/lib/blacklight/solr/document/dublin_core.rb +40 -0
  101. data/lib/blacklight/solr/document/email.rb +15 -0
  102. data/lib/blacklight/solr/document/marc.rb +84 -0
  103. data/lib/blacklight/solr/document/marc_export.rb +430 -0
  104. data/lib/blacklight/solr/document/sms.rb +13 -0
  105. data/lib/blacklight/solr/facet_paginator.rb +93 -0
  106. data/lib/blacklight/solr_helper.rb +413 -0
  107. data/lib/blacklight/user.rb +55 -0
  108. data/lib/blacklight/version.rb +3 -0
  109. data/lib/colorize.rb +196 -0
  110. data/lib/generators/blacklight/blacklight_generator.rb +134 -0
  111. data/lib/generators/blacklight/templates/SolrMarc.jar +0 -0
  112. data/lib/generators/blacklight/templates/catalog_controller.rb +8 -0
  113. data/lib/generators/blacklight/templates/config/SolrMarc/config-test.properties +37 -0
  114. data/lib/generators/blacklight/templates/config/SolrMarc/config.properties +37 -0
  115. data/lib/generators/blacklight/templates/config/SolrMarc/index.properties +97 -0
  116. data/lib/generators/blacklight/templates/config/SolrMarc/index_scripts/dewey.bsh +47 -0
  117. data/lib/generators/blacklight/templates/config/SolrMarc/index_scripts/format.bsh +126 -0
  118. data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/README_MAPS +1 -0
  119. data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/callnumber_map.properties +407 -0
  120. data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/composition_era_map.properties +56 -0
  121. data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/country_map.properties +379 -0
  122. data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/format_map.properties +50 -0
  123. data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/instrument_map.properties +101 -0
  124. data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/language_map.properties +490 -0
  125. data/lib/generators/blacklight/templates/config/blacklight_config.rb +245 -0
  126. data/lib/generators/blacklight/templates/config/solr.yml +6 -0
  127. data/lib/generators/blacklight/templates/migrations/add_user_types_to_bookmarks_searches.rb +11 -0
  128. data/lib/generators/blacklight/templates/migrations/create_bookmarks.rb +17 -0
  129. data/lib/generators/blacklight/templates/migrations/create_searches.rb +15 -0
  130. data/lib/generators/blacklight/templates/migrations/remove_editable_fields_from_bookmarks.rb +11 -0
  131. data/lib/generators/blacklight/templates/public/images/blacklight/bg.png +0 -0
  132. data/lib/generators/blacklight/templates/public/images/blacklight/border.png +0 -0
  133. data/lib/generators/blacklight/templates/public/images/blacklight/bul_sq_gry.gif +0 -0
  134. data/lib/generators/blacklight/templates/public/images/blacklight/checkmark.gif +0 -0
  135. data/lib/generators/blacklight/templates/public/images/blacklight/logo.png +0 -0
  136. data/lib/generators/blacklight/templates/public/images/blacklight/magnifying_glass.gif +0 -0
  137. data/lib/generators/blacklight/templates/public/images/blacklight/remove.gif +0 -0
  138. data/lib/generators/blacklight/templates/public/images/blacklight/separator.gif +0 -0
  139. data/lib/generators/blacklight/templates/public/images/blacklight/start_over.gif +0 -0
  140. data/lib/generators/blacklight/templates/public/javascripts/blacklight.js +485 -0
  141. data/lib/generators/blacklight/templates/public/javascripts/jquery-1.4.2.min.js +154 -0
  142. data/lib/generators/blacklight/templates/public/javascripts/jquery-ui-1.8.1.custom.min.js +756 -0
  143. data/lib/generators/blacklight/templates/public/stylesheets/blacklight.css +487 -0
  144. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  145. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  146. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_flat_10_000000_40x100.png +0 -0
  147. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  148. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  149. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  150. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_gloss-wave_35_558fd0_500x100.png +0 -0
  151. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  152. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  153. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-icons_222222_256x240.png +0 -0
  154. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-icons_228ef1_256x240.png +0 -0
  155. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-icons_2e4f81_256x240.png +0 -0
  156. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-icons_ffd27a_256x240.png +0 -0
  157. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-icons_ffffff_256x240.png +0 -0
  158. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/jquery-ui-1.8.1.custom.css +486 -0
  159. data/lib/generators/blacklight/templates/public/stylesheets/yui.css +31 -0
  160. data/lib/generators/blacklight/templates/solr_document.rb +30 -0
  161. data/lib/railties/blacklight.rake +66 -0
  162. data/lib/railties/cucumber.rake +53 -0
  163. data/lib/railties/rspec.rake +188 -0
  164. data/lib/railties/solr_marc.rake +148 -0
  165. data/lib/railties/test_solr_server.rb +130 -0
  166. data/spec/helpers/catalog_helper_spec.rb +111 -0
  167. data/spec/views/catalog/_sms_form.html.erb_spec.rb +19 -0
  168. data/tasks/blacklight_tasks.rake +4 -0
  169. data/uninstall.rb +1 -0
  170. metadata +431 -0
@@ -0,0 +1,413 @@
1
+ # SolrHelper is a controller layer mixin. It is in the controller scope: request params, session etc.
2
+ #
3
+ # NOTE: Be careful when creating variables here as they may be overriding something that already exists.
4
+ # The ActionController docs: http://api.rubyonrails.org/classes/ActionController/Base.html
5
+ #
6
+ # Override these methods in your own controller for customizations:
7
+ #
8
+ # class CatalogController < ActionController::Base
9
+ #
10
+ # include Blacklight::SolrHelper
11
+ #
12
+ # def solr_search_params
13
+ # super.merge :per_page=>10
14
+ # end
15
+ #
16
+ # end
17
+ #
18
+
19
+ module Blacklight::SolrHelper
20
+ MaxPerPage = 100
21
+
22
+ def self.included(klass)
23
+ if klass.respond_to?(:helper_method)
24
+ klass.helper_method(:facet_limit_hash)
25
+ klass.helper_method(:facet_limit_for)
26
+ end
27
+
28
+ # We want to install a class-level place to keep
29
+ # solr_search_params_logic method names. Compare to before_filter,
30
+ # similar design. Since we're a module, we have to add it in here.
31
+ # There are too many different semantic choices in ruby 'class variables',
32
+ # we choose this one for now, supplied by Rails.
33
+ klass.class_inheritable_accessor :solr_search_params_logic
34
+ # Set defaults. Each symbol identifies a _method_ that must be in
35
+ # this class, taking two parameters (solr_parameters, user_parameters)
36
+ # Can be changed in local apps or by plugins, eg:
37
+ # CatalogController.include ModuleDefiningNewMethod
38
+ # CatalogController.solr_search_params_logic << :new_method
39
+ # CatalogController.solr_search_params_logic.delete(:we_dont_want)
40
+ klass.solr_search_params_logic = [:default_solr_parameters , :add_query_to_solr, :add_facet_fq_to_solr, :add_facetting_to_solr, :add_sorting_paging_to_solr ]
41
+ end
42
+
43
+
44
+ # A helper method used for generating solr LocalParams, put quotes
45
+ # around the term unless it's a bare-word. Escape internal quotes
46
+ # if needed.
47
+ def solr_param_quote(val, options = {})
48
+ options[:quote] ||= '"'
49
+ unless val =~ /^[a-zA-Z$_\-\^]+$/
50
+ val = options[:quote] +
51
+ # Yes, we need crazy escaping here, to deal with regexp esc too!
52
+ val.gsub("'", "\\\\\'").gsub('"', "\\\\\"") +
53
+ options[:quote]
54
+ end
55
+ return val
56
+ end
57
+
58
+
59
+ # returns a params hash for searching solr.
60
+ # The CatalogController #index action uses this.
61
+ # Solr parameters can come from a number of places. From lowest
62
+ # precedence to highest:
63
+ # 1. General defaults in blacklight config (are trumped by)
64
+ # 2. defaults for the particular search field identified by params[:search_field] (are trumped by)
65
+ # 3. certain parameters directly on input HTTP query params
66
+ # * not just any parameter is grabbed willy nilly, only certain ones are allowed by HTTP input)
67
+ # * for legacy reasons, qt in http query does not over-ride qt in search field definition default.
68
+ # 4. extra parameters passed in as argument.
69
+ #
70
+ # spellcheck.q will be supplied with the [:q] value unless specifically
71
+ # specified otherwise.
72
+ #
73
+ # Incoming parameter :f is mapped to :fq solr parameter.
74
+ def solr_search_params(user_params = params || {})
75
+ solr_parameters = {}
76
+ solr_search_params_logic.each do |method_name|
77
+ send(method_name, solr_parameters, user_params)
78
+ end
79
+
80
+ return solr_parameters
81
+ end
82
+
83
+
84
+ ####
85
+ # Start with general defaults from BL config. Need to use custom
86
+ # merge to dup values, to avoid later mutating the original by mistake.
87
+ def default_solr_parameters(solr_parameters, user_params)
88
+ if Blacklight.config[:default_solr_params]
89
+ Blacklight.config[:default_solr_params].each_pair do |key, value|
90
+ solr_parameters[key] = value.dup rescue value
91
+ end
92
+ end
93
+ end
94
+
95
+ ###
96
+ # copy paging and sorting params from BL app over to solr, with
97
+ # fairly little transformation.
98
+ def add_sorting_paging_to_solr(solr_parameters, user_params)
99
+ # Omit empty strings and nil values.
100
+ # Apparently RSolr takes :per_page and converts it to Solr :rows,
101
+ # so we let it.
102
+ [:page, :sort, :per_page].each do |key|
103
+ solr_parameters[key] = user_params[key] unless user_params[key].blank?
104
+ end
105
+
106
+ # limit to MaxPerPage (100). Tests want this to be a string not an integer,
107
+ # not sure why.
108
+ solr_parameters[:per_page] = solr_parameters[:per_page].to_i > self.max_per_page ? self.max_per_page.to_s : solr_parameters[:per_page]
109
+ end
110
+
111
+ ##
112
+ # Take the user-entered query, and put it in the solr params,
113
+ # including config's "search field" params for current search field.
114
+ # also include setting spellcheck.q.
115
+ def add_query_to_solr(solr_parameters, user_parameters)
116
+ ###
117
+ # Merge in search field configured values, if present, over-writing general
118
+ # defaults
119
+ ###
120
+ # legacy behavior of user param :qt is passed through, but over-ridden
121
+ # by actual search field config if present. We might want to remove
122
+ # this legacy behavior at some point. It does not seem to be currently
123
+ # rspec'd.
124
+ solr_parameters[:qt] = user_parameters[:qt] if user_parameters[:qt]
125
+
126
+ search_field_def = Blacklight.search_field_def_for_key(user_parameters[:search_field])
127
+ if (search_field_def)
128
+ solr_parameters[:qt] = search_field_def[:qt] if search_field_def[:qt]
129
+ solr_parameters.merge!( search_field_def[:solr_parameters]) if search_field_def[:solr_parameters]
130
+ end
131
+
132
+ ##
133
+ # Create Solr 'q' including the user-entered q, prefixed by any
134
+ # solr LocalParams in config, using solr LocalParams syntax.
135
+ # http://wiki.apache.org/solr/LocalParams
136
+ ##
137
+ if (search_field_def && hash = search_field_def[:solr_local_parameters])
138
+ local_params = hash.collect do |key, val|
139
+ key.to_s + "=" + solr_param_quote(val, :quote => "'")
140
+ end.join(" ")
141
+ solr_parameters[:q] = "{!#{local_params}}#{user_parameters[:q]}"
142
+ else
143
+ solr_parameters[:q] = user_parameters[:q] if user_parameters[:q]
144
+ end
145
+
146
+
147
+ ##
148
+ # Set Solr spellcheck.q to be original user-entered query, without
149
+ # our local params, otherwise it'll try and spellcheck the local
150
+ # params! Unless spellcheck.q has already been set by someone,
151
+ # respect that.
152
+ #
153
+ # TODO: Change calling code to expect this as a symbol instead of
154
+ # a string, for consistency? :'spellcheck.q' is a symbol. Right now
155
+ # rspec tests for a string, and can't tell if other code may
156
+ # insist on a string.
157
+ solr_parameters["spellcheck.q"] = user_parameters[:q] unless solr_parameters["spellcheck.q"]
158
+ end
159
+
160
+ ##
161
+ # Add any existing facet limits, stored in app-level HTTP query
162
+ # as :f, to solr as appropriate :fq query.
163
+ def add_facet_fq_to_solr(solr_parameters, user_params)
164
+ # :fq, map from :f.
165
+ if ( user_params[:f])
166
+ f_request_params = user_params[:f]
167
+
168
+ solr_parameters[:fq] ||= []
169
+ f_request_params.each_pair do |facet_field, value_list|
170
+ value_list.each do |value|
171
+ solr_parameters[:fq] << "{!raw f=#{facet_field}}#{value}"
172
+ end
173
+ end
174
+ end
175
+ end
176
+
177
+ ##
178
+ # Add appropriate Solr facetting directives in, including
179
+ # taking account of our facet paging/'more'. This is not
180
+ # about solr 'fq', this is about solr facet.* params.
181
+ def add_facetting_to_solr(solr_parameters, user_params)
182
+ # While not used by BL core behavior, legacy behavior seemed to be
183
+ # to accept incoming params as "facet.field" or "facets", and add them
184
+ # on to any existing facet.field sent to Solr. Legacy behavior seemed
185
+ # to be accepting these incoming params as arrays (in Rails URL with []
186
+ # on end), or single values. At least one of these is used by
187
+ # Stanford for "faux hieararchial facets".
188
+ if user_params.has_key?("facet.field") || user_params.has_key?("facets")
189
+ solr_parameters[:"facet.field"] ||= []
190
+ solr_parameters[:"facet.field"].concat( [user_params["facet.field"], user_params["facets"]].flatten.compact ).uniq!
191
+ end
192
+
193
+ # Support facet paging and 'more'
194
+ # links, by sending a facet.limit one more than what we
195
+ # want to page at, according to configured facet limits.
196
+ facet_limit_hash.each_key do |field_name|
197
+ next if field_name.nil? # skip the 'default' key
198
+ next unless (limit = facet_limit_for(field_name))
199
+
200
+ solr_parameters[:"f.#{field_name}.facet.limit"] = (limit + 1)
201
+ end
202
+ end
203
+
204
+
205
+
206
+ # a solr query method
207
+ # given a user query, return a solr response containing both result docs and facets
208
+ # - mixes in the Blacklight::Solr::SpellingSuggestions module
209
+ # - the response will have a spelling_suggestions method
210
+ # Returns a two-element array (aka duple) with first the solr response object,
211
+ # and second an array of SolrDocuments representing the response.docs
212
+ def get_search_results(user_params = params || {}, extra_controller_params = {})
213
+
214
+ # In later versions of Rails, the #benchmark method can do timing
215
+ # better for us.
216
+ bench_start = Time.now
217
+
218
+ solr_response = Blacklight.solr.find( self.solr_search_params(user_params).merge(extra_controller_params))
219
+
220
+ document_list = solr_response.docs.collect {|doc| SolrDocument.new(doc, solr_response)}
221
+
222
+ Rails.logger.debug("Solr fetch: #{self.class}#get_search_results (#{'%.1f' % ((Time.now.to_f - bench_start.to_f)*1000)}ms)")
223
+
224
+ return [solr_response, document_list]
225
+ end
226
+
227
+ # returns a params hash for finding a single solr document (CatalogController #show action)
228
+ # If the id arg is nil, then the value is fetched from params[:id]
229
+ # This method is primary called by the get_solr_response_for_doc_id method.
230
+ def solr_doc_params(id=nil)
231
+ id ||= params[:id]
232
+ # just to be consistent with the other solr param methods:
233
+ {
234
+ :qt => :document,
235
+ :id => id
236
+ }
237
+ end
238
+
239
+ # a solr query method
240
+ # retrieve a solr document, given the doc id
241
+ # TODO: shouldn't hardcode id field; should be setable to unique_key field in schema.xml
242
+ def get_solr_response_for_doc_id(id=nil, extra_controller_params={})
243
+ solr_response = Blacklight.solr.find solr_doc_params(id).merge(extra_controller_params)
244
+ raise Blacklight::Exceptions::InvalidSolrID.new if solr_response.docs.empty?
245
+ document = SolrDocument.new(solr_response.docs.first, solr_response)
246
+ [solr_response, document]
247
+ end
248
+
249
+ # given a field name and array of values, get the matching SOLR documents
250
+ def get_solr_response_for_field_values(field, values, extra_solr_params = {})
251
+ value_str = "(\"" + values.to_a.join("\" OR \"") + "\")"
252
+ solr_params = {
253
+ :defType => "lucene", # need boolean for OR
254
+ :q => "#{field}:#{value_str}",
255
+ # not sure why fl * is neccesary, why isn't default solr_search_params
256
+ # sufficient, like it is for any other search results solr request?
257
+ # But tests fail without this. I think because some functionality requires
258
+ # this to actually get solr_doc_params, not solr_search_params. Confused
259
+ # semantics again.
260
+ :fl => "*",
261
+ :facet => 'false',
262
+ :spellcheck => 'false'
263
+ }.merge(extra_solr_params)
264
+
265
+ solr_response = Blacklight.solr.find( self.solr_search_params().merge(solr_params) )
266
+ document_list = solr_response.docs.collect{|doc| SolrDocument.new(doc, solr_response) }
267
+ [solr_response,document_list]
268
+ end
269
+
270
+ # returns a params hash for a single facet field solr query.
271
+ # used primary by the get_facet_pagination method.
272
+ # Looks up Facet Paginator request params from current request
273
+ # params to figure out sort and offset.
274
+ # Default limit for facet list can be specified by defining a controller
275
+ # method facet_list_limit, otherwise 20.
276
+ def solr_facet_params(facet_field, user_params=params || {}, extra_controller_params={})
277
+ input = user_params.deep_merge(extra_controller_params)
278
+
279
+ # First start with a standard solr search params calculations,
280
+ # for any search context in our request params.
281
+ solr_params = solr_search_params(user_params).merge(extra_controller_params)
282
+
283
+ # Now override with our specific things for fetching facet values
284
+ solr_params[:"facet.field"] = facet_field
285
+
286
+ # Need to set as f.facet_field.facet.limit to make sure we
287
+ # override any field-specific default in the solr request handler.
288
+ solr_params[:"f.#{facet_field}.facet.limit"] =
289
+ if solr_params["facet.limit"]
290
+ solr_params["facet.limit"] + 1
291
+ elsif respond_to?(:facet_list_limit)
292
+ facet_list_limit.to_s.to_i + 1
293
+ else
294
+ 20 + 1
295
+ end
296
+ solr_params['facet.offset'] = input[ Blacklight::Solr::FacetPaginator.request_keys[:offset] ].to_i # will default to 0 if nil
297
+ solr_params['facet.sort'] = input[ Blacklight::Solr::FacetPaginator.request_keys[:sort] ]
298
+ solr_params[:rows] = 0
299
+
300
+ return solr_params
301
+ end
302
+
303
+ # a solr query method
304
+ # used to paginate through a single facet field's values
305
+ # /catalog/facet/language_facet
306
+ def get_facet_pagination(facet_field, extra_controller_params={})
307
+
308
+ solr_params = solr_facet_params(facet_field, params, extra_controller_params)
309
+
310
+ # Make the solr call
311
+ response = Blacklight.solr.find(solr_params)
312
+
313
+ limit =
314
+ if respond_to?(:facet_list_limit)
315
+ facet_list_limit.to_s.to_i
316
+ elsif solr_params[:"f.#{facet_field}.facet.limit"]
317
+ solr_params[:"f.#{facet_field}.facet.limit"] - 1
318
+ else
319
+ nil
320
+ end
321
+
322
+
323
+ # Actually create the paginator!
324
+ # NOTE: The sniffing of the proper sort from the solr response is not
325
+ # currently tested for, tricky to figure out how to test, since the
326
+ # default setup we test against doesn't use this feature.
327
+ return Blacklight::Solr::FacetPaginator.new(response.facets.first.items,
328
+ :offset => solr_params['facet.offset'],
329
+ :limit => limit,
330
+ :sort => response["responseHeader"]["params"]["f.#{facet_field}.facet.sort"] || response["responseHeader"]["params"]["facet.sort"]
331
+ )
332
+ end
333
+
334
+ # a solr query method
335
+ # this is used when selecting a search result: we have a query and a
336
+ # position in the search results and possibly some facets
337
+ # Pass in an index where 1 is the first document in the list, and
338
+ # the Blacklight app-level request params that define the search.
339
+ def get_single_doc_via_search(index, request_params)
340
+ solr_params = solr_search_params(request_params)
341
+ solr_params[:start] = index - 1 # start at 0 to get 1st doc, 1 to get 2nd.
342
+ solr_params[:per_page] = 1
343
+ solr_params[:rows] = 1
344
+ solr_params[:fl] = '*'
345
+ Blacklight.solr.find(solr_params).docs.first
346
+ end
347
+
348
+ # returns a solr params hash
349
+ # if field is nil, the value is fetched from Blacklight.config[:index][:show_link]
350
+ # the :fl (solr param) is set to the "field" value.
351
+ # per_page is set to 10
352
+ def solr_opensearch_params(field=nil)
353
+ solr_params = solr_search_params
354
+ solr_params[:per_page] = 10
355
+ solr_params[:fl] = Blacklight.config[:index][:show_link]
356
+ solr_params
357
+ end
358
+
359
+ # a solr query method
360
+ # does a standard search but returns a simplified object.
361
+ # an array is returned, the first item is the query string,
362
+ # the second item is an other array. This second array contains
363
+ # all of the field values for each of the documents...
364
+ # where the field is the "field" argument passed in.
365
+ def get_opensearch_response(field=nil, extra_controller_params={})
366
+ solr_params = solr_opensearch_params().merge(extra_controller_params)
367
+ response = Blacklight.solr.find(solr_params)
368
+ a = [solr_params[:q]]
369
+ a << response.docs.map {|doc| doc[solr_params[:fl]].to_s }
370
+ end
371
+
372
+
373
+
374
+ # Look up facet limit for given facet_field. Will look at config, and
375
+ # if config is 'true' will look up from Solr @response if available. If
376
+ # no limit is avaialble, returns nil. Used from #solr_search_params
377
+ # to supply f.fieldname.facet.limit values in solr request (no @response
378
+ # available), and used in display (with @response available) to create
379
+ # a facet paginator with the right limit.
380
+ def facet_limit_for(facet_field)
381
+ limits_hash = facet_limit_hash
382
+ return nil if limits_hash.blank?
383
+
384
+ limit = limits_hash[facet_field]
385
+
386
+ if ( limit == true && @response &&
387
+ @response["responseHeader"] &&
388
+ @response["responseHeader"]["params"])
389
+ limit =
390
+ @response["responseHeader"]["params"]["f.#{facet_field}.facet.limit"] ||
391
+ @response["responseHeader"]["params"]["facet.limit"]
392
+ limit = (limit.to_i() -1) if limit
393
+ limit = nil if limit == -2 # -1-1==-2, unlimited.
394
+ elsif limit == true
395
+ limit = nil
396
+ end
397
+
398
+ return limit
399
+ end
400
+
401
+ # Returns complete hash of key=facet_field, value=limit.
402
+ # Used by SolrHelper#solr_search_params to add limits to solr
403
+ # request for all configured facet limits.
404
+ def facet_limit_hash
405
+ Blacklight.config[:facet][:limits] || {}
406
+ end
407
+
408
+ def max_per_page
409
+ MaxPerPage
410
+ end
411
+
412
+
413
+ end
@@ -0,0 +1,55 @@
1
+ module Blacklight::User
2
+
3
+ # This gives us an is_blacklight_user method that can be included in
4
+ # the containing applications models.
5
+ # SEE ALSO: The /lib/blacklight/engine.rb class for how when this
6
+ # is injected into the hosting application through ActiveRecord::Base extend
7
+ def self.included(base)
8
+ base.send :has_many, :bookmarks, :dependent => :destroy, :as => :user
9
+ base.send :has_many, :searches, :dependent => :destroy, :as => :user
10
+ base.send :include, InstanceMethods
11
+ end
12
+
13
+ # The following methods will be included in any active model object
14
+ # that calls "is_blacklight_user"
15
+ module InstanceMethods
16
+
17
+ # fixme: This needs to be re-implemented
18
+ def last_search_url
19
+ return self.searches.last
20
+ end
21
+
22
+ def has_bookmarks?
23
+ bookmarks.count > 0
24
+ end
25
+
26
+ def has_searches?
27
+ searches.count > 0
28
+ end
29
+
30
+ def bookmarked_document_ids
31
+ self.bookmarks.map{|bm|bm.document_id}
32
+ end
33
+
34
+ # see #current_bookmark_for, is easier
35
+ def document_is_bookmarked?(document_id)
36
+ bookmarked_document_ids.include? document_id
37
+ end
38
+
39
+ # returns a Bookmark object if there is one for document_id, else
40
+ # nil.
41
+ def existing_bookmark_for(document_id)
42
+ # to_a, we don't want to go to the database, we want to use cached
43
+ # copy.
44
+ self.bookmarks.to_a.find {|b| b.document_id == document_id}
45
+ end
46
+
47
+ def documents_to_bookmark=(docs)
48
+ docs.each do |doc|
49
+ self.bookmarks.create(doc) unless bookmarked_document_ids.include?(doc[:document_id])
50
+ end
51
+ end
52
+
53
+ end # /InstanceMethods
54
+
55
+ end