blacklight 3.0pre1

Sign up to get free protection for your applications and to get access to all the features.
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