blacklight 5.3.0 → 5.4.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -1
  3. data/VERSION +1 -1
  4. data/app/assets/javascripts/blacklight/blacklight.js +1 -0
  5. data/app/assets/javascripts/blacklight/collapsable.js +9 -0
  6. data/app/controllers/bookmarks_controller.rb +72 -9
  7. data/app/helpers/blacklight/blacklight_helper_behavior.rb +15 -7
  8. data/app/helpers/blacklight/catalog_helper_behavior.rb +17 -9
  9. data/app/helpers/blacklight/configuration_helper_behavior.rb +59 -7
  10. data/app/helpers/blacklight/facets_helper_behavior.rb +1 -16
  11. data/app/helpers/blacklight/url_helper_behavior.rb +21 -2
  12. data/app/models/bookmark.rb +13 -4
  13. data/app/models/solr_document.rb +5 -0
  14. data/app/views/bookmarks/_tools.html.erb +10 -11
  15. data/app/views/catalog/_bookmark_control.html.erb +2 -2
  16. data/app/views/catalog/_refworks_form.html.erb +3 -2
  17. data/app/views/catalog/_sort_widget.html.erb +5 -5
  18. data/app/views/catalog/_view_type_group.html.erb +2 -2
  19. data/app/views/shared/_header_navbar.html.erb +1 -3
  20. data/config/locales/blacklight.pt-BR.yml +223 -0
  21. data/db/migrate/20140320000000_add_polymorphic_type_to_bookmarks.rb +8 -0
  22. data/gemfiles/rails4.1.gemfile +1 -1
  23. data/lib/blacklight.rb +5 -0
  24. data/lib/blacklight/catalog.rb +33 -5
  25. data/lib/blacklight/configuration.rb +12 -3
  26. data/lib/blacklight/configuration/facet_field.rb +1 -1
  27. data/lib/blacklight/configuration/search_field.rb +3 -2
  28. data/lib/blacklight/configuration/solr_field.rb +2 -1
  29. data/lib/blacklight/configuration/sort_field.rb +2 -1
  30. data/lib/blacklight/engine.rb +9 -1
  31. data/lib/blacklight/exceptions.rb +3 -0
  32. data/lib/blacklight/rails/routes.rb +12 -0
  33. data/lib/blacklight/request_builders.rb +19 -18
  34. data/lib/blacklight/search_fields.rb +0 -9
  35. data/lib/blacklight/solr/document.rb +15 -0
  36. data/lib/blacklight/solr/document/export.rb +4 -0
  37. data/lib/blacklight/solr_helper.rb +25 -28
  38. data/lib/blacklight/solr_response.rb +31 -29
  39. data/lib/blacklight/solr_response/response.rb +18 -0
  40. data/lib/blacklight/user.rb +17 -4
  41. data/lib/blacklight/utils.rb +30 -2
  42. data/lib/generators/blacklight/install_generator.rb +4 -0
  43. data/lib/generators/blacklight/templates/config/initializers/blacklight_initializer.rb +9 -0
  44. data/spec/controllers/application_controller_spec.rb +0 -1
  45. data/spec/controllers/bookmarks_controller_spec.rb +8 -4
  46. data/spec/controllers/catalog_controller_spec.rb +48 -55
  47. data/spec/controllers/saved_searches_controller_spec.rb +1 -4
  48. data/spec/controllers/search_history_controller_spec.rb +0 -1
  49. data/spec/features/bookmarks_spec.rb +14 -0
  50. data/spec/features/search_formats_spec.rb +45 -0
  51. data/spec/helpers/blacklight_helper_spec.rb +71 -30
  52. data/spec/helpers/configuration_helper_spec.rb +128 -4
  53. data/spec/helpers/facets_helper_spec.rb +1 -1
  54. data/spec/lib/blacklight/configuration_spec.rb +0 -5
  55. data/spec/lib/blacklight/search_fields_spec.rb +0 -22
  56. data/spec/lib/blacklight/solr/document_spec.rb +6 -0
  57. data/spec/lib/blacklight/solr_helper_spec.rb +31 -10
  58. data/spec/lib/blacklight/solr_response_spec.rb +8 -0
  59. data/spec/lib/blacklight/user_spec.rb +38 -4
  60. data/spec/models/bookmark_spec.rb +22 -17
  61. data/spec/spec_helper.rb +2 -0
  62. data/template.demo.rb +4 -9
  63. metadata +12 -6
  64. data/spec/data/sample_docs.yml +0 -655
@@ -10,9 +10,9 @@ module Blacklight
10
10
 
11
11
  self.collapse = true if self.collapse.nil?
12
12
  self.show = true if self.show.nil?
13
+ self.if ||= self.show
13
14
 
14
15
  super
15
16
  end
16
17
  end
17
18
  end
18
-
@@ -1,5 +1,5 @@
1
1
  module Blacklight
2
- class Configuration::SearchField < OpenStructWithHashAccess
2
+ class Configuration::SearchField < Blacklight::Configuration::SolrField
3
3
  def normalize! blacklight_config = nil
4
4
  # Some normalization, calculate display_label from key,
5
5
  # and make sure we have a qt from defaults.
@@ -7,6 +7,8 @@ module Blacklight
7
7
  self.field ||= self.key
8
8
  self.label ||= self.key.try(:titlecase)
9
9
  self.qt ||= blacklight_config.default_solr_params[:qt] if blacklight_config && blacklight_config.default_solr_params
10
+ self.if ||= self.include_in_simple_select
11
+ super
10
12
  end
11
13
 
12
14
  def validate!
@@ -14,4 +16,3 @@ module Blacklight
14
16
  end
15
17
  end
16
18
  end
17
-
@@ -2,6 +2,8 @@ module Blacklight
2
2
  class Configuration::SolrField < OpenStructWithHashAccess
3
3
  def normalize! blacklight_config = nil
4
4
  self.label ||= default_label
5
+ self.if = true if self.if.nil?
6
+ self.unless = false if self.unless.nil?
5
7
  self
6
8
  end
7
9
 
@@ -18,4 +20,3 @@ module Blacklight
18
20
  end
19
21
  end
20
22
  end
21
-
@@ -1,5 +1,5 @@
1
1
  module Blacklight
2
- class Configuration::SortField < OpenStructWithHashAccess
2
+ class Configuration::SortField < Blacklight::Configuration::SolrField
3
3
  def normalize! blacklight_config = nil
4
4
  self.sort ||= self.field
5
5
 
@@ -8,6 +8,7 @@ module Blacklight
8
8
  self.field ||= self.sort
9
9
 
10
10
  self.key ||= self.field
11
+ super
11
12
  end
12
13
 
13
14
  def validate!
@@ -19,6 +19,14 @@ module Blacklight
19
19
  end
20
20
  end
21
21
  end
22
+
23
+ initializer "blacklight.secret_key" do |app|
24
+ if app.respond_to?(:secrets)
25
+ Blacklight.secret_key ||= app.secrets.secret_key_base
26
+ elsif app.config.respond_to?(:secret_key_base)
27
+ Blacklight.secret_key ||= app.config.secret_key_base
28
+ end
29
+ end
22
30
 
23
31
  end
24
- end
32
+ end
@@ -9,6 +9,9 @@ module Blacklight
9
9
  # is not successful, raise this:
10
10
  class InvalidSolrID < RuntimeError
11
11
  end
12
+
13
+ class ExpiredSessionToken < Exception
14
+ end
12
15
 
13
16
  class ECONNREFUSED < ::Errno::ECONNREFUSED; end
14
17
 
@@ -7,11 +7,23 @@ module ActionDispatch::Routing
7
7
  # blacklight_for :catalog, only: [ :saved_searches, :solr_document ]
8
8
  # blacklight_for :catalog, constraints: {id: /[0-9]+/ }
9
9
  def blacklight_for(*resources)
10
+ raise_no_blacklight_secret_key unless Blacklight.secret_key
10
11
  options = resources.extract_options!
11
12
  resources.map!(&:to_sym)
12
13
 
13
14
  Blacklight::Routes.new(self, options.merge(resources: resources)).draw
14
15
 
15
16
  end
17
+
18
+ private
19
+ def raise_no_blacklight_secret_key #:nodoc:
20
+ raise <<-ERROR
21
+ Blacklight.secret_key was not set. Please add the following to an initializer:
22
+
23
+ Blacklight.secret_key = '#{SecureRandom.hex(64)}'
24
+
25
+ Please ensure you restarted your application after installing Blacklight or setting the key.
26
+ ERROR
27
+ end
16
28
  end
17
29
  end
@@ -201,27 +201,25 @@ module Blacklight
201
201
  # app level per_page and page to Solr rows and start.
202
202
  def add_paging_to_solr(solr_params, user_params)
203
203
 
204
- # Now any over-rides from current URL?
205
- solr_params[:rows] = user_params[:rows].to_i unless user_params[:rows].blank?
206
- solr_params[:rows] = user_params[:per_page].to_i unless user_params[:per_page].blank?
204
+ # user-provided parameters should override any default row
205
+ solr_params[:rows] = user_params[:rows].to_i unless user_params[:rows].blank?
206
+ solr_params[:rows] = user_params[:per_page].to_i unless user_params[:per_page].blank?
207
+
208
+ # configuration defaults should only set a default value, not override a value set elsewhere (e.g. search field parameters)
209
+ solr_params[:rows] ||= blacklight_config.default_per_page unless blacklight_config.default_per_page.blank?
210
+ solr_params[:rows] ||= blacklight_config.per_page.first unless blacklight_config.per_page.blank?
211
+
212
+ # set a reasonable default
213
+ Rails.logger.info "Solr :rows parameter not set (by the user, configuration, or default solr parameters); using 10 rows by default"
214
+ solr_params[:rows] ||= 10
207
215
 
208
- # Do we need to translate :page to Solr :start?
216
+ # ensure we don't excede the max page size
217
+ solr_params[:rows] = blacklight_config.max_per_page if solr_params[:rows].to_i > blacklight_config.max_per_page
209
218
  unless user_params[:page].blank?
210
- # already set solr_params["rows"] might not be the one we just set,
211
- # could have been from app defaults too. But we need one.
212
- # raising is consistent with prior RSolr magic keys behavior.
213
- # We could change this to default to 10, or to raise on startup
214
- # from config instead of at runtime.
215
- if solr_params[:rows].blank?
216
- raise Exception.new("To use pagination when no :per_page is supplied in the URL, :rows must be configured in blacklight_config default_solr_params")
217
- end
218
219
  solr_params[:start] = solr_params[:rows].to_i * (user_params[:page].to_i - 1)
219
220
  solr_params[:start] = 0 if solr_params[:start].to_i < 0
220
221
  end
221
222
 
222
- solr_params[:rows] ||= blacklight_config.per_page.first unless blacklight_config.per_page.blank?
223
-
224
- solr_params[:rows] = blacklight_config.max_per_page if solr_params[:rows].to_i > blacklight_config.max_per_page
225
223
  end
226
224
 
227
225
  ###
@@ -270,11 +268,14 @@ module Blacklight
270
268
  fq = case
271
269
  when (facet_config and facet_config.query)
272
270
  facet_config.query[value][:fq]
273
- when (facet_config and facet_config.date),
274
- (value.is_a?(TrueClass) or value.is_a?(FalseClass) or value == 'true' or value == 'false'),
271
+ when (facet_config and facet_config.date)
272
+ # in solr 3.2+, this could be replaced by a !term query
273
+ "#{prefix}#{facet_field}:#{RSolr.escape(value)}"
274
+ when (value.is_a?(DateTime) or value.is_a?(Date) or value.is_a?(Time))
275
+ "#{prefix}#{facet_field}:#{RSolr.escape(value.to_time.utc.strftime("%Y-%m-%dT%H:%M:%SZ"))}"
276
+ when (value.is_a?(TrueClass) or value.is_a?(FalseClass) or value == 'true' or value == 'false'),
275
277
  (value.is_a?(Integer) or (value.to_i.to_s == value if value.respond_to? :to_i)),
276
278
  (value.is_a?(Float) or (value.to_f.to_s == value if value.respond_to? :to_f))
277
- (value.is_a?(DateTime) or value.is_a?(Date) or value.is_a?(Time))
278
279
  "#{prefix}#{facet_field}:#{value}"
279
280
  when value.is_a?(Range)
280
281
  "#{prefix}#{facet_field}:[#{value.first} TO #{value.last}]"
@@ -38,15 +38,6 @@ module Blacklight::SearchFields
38
38
  blacklight_config.search_fields.values
39
39
  end
40
40
 
41
- # Returns suitable argument to options_for_select method, to create
42
- # an html select based on #search_field_list. Skips search_fields
43
- # marked :include_in_simple_select => false
44
- def search_field_options_for_select
45
- blacklight_config.search_fields.collect do |key, field_def|
46
- [field_def.label, field_def.key] unless field_def.include_in_simple_select == false
47
- end.compact
48
- end
49
-
50
41
  # Looks up a search field blacklight_config hash from search_field_list having
51
42
  # a certain supplied :key.
52
43
  def search_field_def_for_key(key)
@@ -151,7 +151,14 @@ module Blacklight::Solr::Document
151
151
  return @semantic_value_hash
152
152
  end
153
153
 
154
+ def destroyed?
155
+ false
156
+ end
154
157
 
158
+ def new_record?
159
+ false
160
+ end
161
+
155
162
  # Certain class-level methods needed for the document-specific
156
163
  # extendability architecture
157
164
  module ClassMethods
@@ -160,6 +167,14 @@ module Blacklight::Solr::Document
160
167
  def unique_key
161
168
  @unique_key ||= 'id'
162
169
  end
170
+
171
+ def primary_key
172
+ 'id'
173
+ end
174
+
175
+ def base_class
176
+ self
177
+ end
163
178
 
164
179
  # Returns array of hashes of registered extensions. Each hash
165
180
  # has a :module_obj key and a :condition_proc key. Usually this
@@ -100,4 +100,8 @@ module Blacklight::Solr::Document::Export
100
100
  def export_as(short_name)
101
101
  send("export_as_#{short_name.to_s}")
102
102
  end
103
+
104
+ def exports_as? short_name
105
+ respond_to? "export_as_#{short_name.to_s}"
106
+ end
103
107
  end
@@ -46,6 +46,7 @@
46
46
 
47
47
  module Blacklight::SolrHelper
48
48
  extend ActiveSupport::Concern
49
+ extend Deprecation
49
50
  include Blacklight::SearchFields
50
51
  include Blacklight::Facet
51
52
  include ActiveSupport::Benchmarkable
@@ -59,18 +60,6 @@ module Blacklight::SolrHelper
59
60
 
60
61
  end
61
62
 
62
- def force_to_utf8(value)
63
- case value
64
- when Hash
65
- value.each { |k, v| value[k] = force_to_utf8(v) }
66
- when Array
67
- value.each { |v| force_to_utf8(v) }
68
- when String
69
- value.force_encoding("utf-8") if value.respond_to?(:force_encoding)
70
- end
71
- value
72
- end
73
-
74
63
  ##
75
64
  # Execute a solr query
76
65
  # @see [RSolr::Client#send_and_receive]
@@ -80,6 +69,7 @@ module Blacklight::SolrHelper
80
69
  # @param [Hash] parameters for RSolr::Client#send_and_receive
81
70
  # @overload find(params)
82
71
  # @param [Hash] parameters for RSolr::Client#send_and_receive
72
+ # @return [Blacklight::SolrResponse] the solr response object
83
73
  def find(*args)
84
74
  # In later versions of Rails, the #benchmark method can do timing
85
75
  # better for us.
@@ -91,7 +81,7 @@ module Blacklight::SolrHelper
91
81
  key = blacklight_config.http_method == :post ? :data : :params
92
82
  res = blacklight_solr.send_and_receive(path, {key=>solr_params.to_hash, method:blacklight_config.http_method})
93
83
 
94
- solr_response = Blacklight::SolrResponse.new(force_to_utf8(res), solr_params)
84
+ solr_response = Blacklight::SolrResponse.new(res, solr_params, solr_document_model: blacklight_config.solr_document_model)
95
85
 
96
86
  Rails.logger.debug("Solr query: #{solr_params.inspect}")
97
87
  Rails.logger.debug("Solr response: #{solr_response.inspect}") if defined?(::BLACKLIGHT_VERBOSE_LOGGING) and ::BLACKLIGHT_VERBOSE_LOGGING
@@ -131,15 +121,14 @@ module Blacklight::SolrHelper
131
121
  when (solr_response.grouped? && solr_response.grouped.length == 1)
132
122
  [solr_response.grouped.first, []]
133
123
  else
134
- document_list = solr_response.docs.collect {|doc| SolrDocument.new(doc, solr_response)}
135
- [solr_response, document_list]
124
+ [solr_response, solr_response.documents]
136
125
  end
137
126
  end
138
127
 
139
128
 
140
129
  # a solr query method
141
130
  # given a user query,
142
- # Returns a solr response object
131
+ # @return [Blacklight::SolrResponse] the solr response object
143
132
  def query_solr(user_params = params || {}, extra_controller_params = {})
144
133
  solr_params = self.solr_search_params(user_params).merge(extra_controller_params)
145
134
 
@@ -167,22 +156,25 @@ module Blacklight::SolrHelper
167
156
 
168
157
  # a solr query method
169
158
  # retrieve a solr document, given the doc id
159
+ # @return [Blacklight::SolrResponse, Blacklight::SolrDocument] the solr response object and the first document
170
160
  def get_solr_response_for_doc_id(id=nil, extra_controller_params={})
171
161
  solr_params = solr_doc_params(id).merge(extra_controller_params)
172
162
  solr_response = find(blacklight_config.document_solr_path, solr_params)
173
- raise Blacklight::Exceptions::InvalidSolrID.new if solr_response.docs.empty?
174
- document = SolrDocument.new(solr_response.docs.first, solr_response)
175
- [solr_response, document]
163
+ raise Blacklight::Exceptions::InvalidSolrID.new if solr_response.documents.empty?
164
+ [solr_response, solr_response.documents.first]
165
+ end
166
+
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)
176
169
  end
177
170
 
178
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
179
173
  def get_solr_response_for_field_values(field, values, extra_solr_params = {})
180
- values = Array(values) unless values.respond_to? :each
181
-
182
- q = if values.empty?
174
+ q = if Array(values).empty?
183
175
  "NOT *:*"
184
176
  else
185
- "#{field}:(#{ values.to_a.map { |x| solr_param_quote(x)}.join(" OR ")})"
177
+ "#{field}:(#{ Array(values).map { |x| solr_param_quote(x)}.join(" OR ")})"
186
178
  end
187
179
 
188
180
  solr_params = {
@@ -199,8 +191,7 @@ module Blacklight::SolrHelper
199
191
  }.merge(extra_solr_params)
200
192
 
201
193
  solr_response = find(self.solr_search_params().merge(solr_params) )
202
- document_list = solr_response.docs.collect{|doc| SolrDocument.new(doc, solr_response) }
203
- [solr_response,document_list]
194
+ [solr_response, solr_response.documents]
204
195
  end
205
196
 
206
197
  # returns a params hash for a single facet field solr query.
@@ -241,6 +232,7 @@ module Blacklight::SolrHelper
241
232
 
242
233
  ##
243
234
  # Get the solr response when retrieving only a single facet field
235
+ # @return [Blacklight::SolrResponse] the solr response
244
236
  def get_facet_field_response(facet_field, user_params = params || {}, extra_controller_params = {})
245
237
  solr_params = solr_facet_params(facet_field, user_params, extra_controller_params)
246
238
  # Make the solr call
@@ -266,12 +258,14 @@ module Blacklight::SolrHelper
266
258
  :sort => response.params[:"f.#{facet_field}.facet.sort"] || response.params["facet.sort"]
267
259
  )
268
260
  end
261
+ deprecation_deprecate :get_facet_pagination
269
262
 
270
263
  # a solr query method
271
264
  # this is used when selecting a search result: we have a query and a
272
265
  # position in the search results and possibly some facets
273
266
  # Pass in an index where 1 is the first document in the list, and
274
267
  # the Blacklight app-level request params that define the search.
268
+ # @return [Blacklight::SolrDocument, nil] the found document or nil if not found
275
269
  def get_single_doc_via_search(index, request_params)
276
270
  solr_params = solr_search_params(request_params)
277
271
 
@@ -279,10 +273,12 @@ module Blacklight::SolrHelper
279
273
  solr_params[:rows] = 1
280
274
  solr_params[:fl] = '*'
281
275
  solr_response = find(solr_params)
282
- SolrDocument.new(solr_response.docs.first, solr_response) unless solr_response.docs.empty?
276
+ solr_response.documents.first
283
277
  end
278
+ deprecation_deprecate :get_single_doc_via_search
284
279
 
285
280
  # Get the previous and next document from a search result
281
+ # @return [Blacklight::SolrResponse, Array<Blacklight::SolrDocument>] the solr response and a list of the first and last document
286
282
  def get_previous_and_next_documents_for_search(index, request_params, extra_controller_params={})
287
283
 
288
284
  solr_params = solr_search_params(request_params).merge(extra_controller_params)
@@ -299,7 +295,7 @@ module Blacklight::SolrHelper
299
295
  solr_params[:facet] = false
300
296
  solr_response = find(solr_params)
301
297
 
302
- document_list = solr_response.docs.collect{|doc| SolrDocument.new(doc, solr_response) }
298
+ document_list = solr_response.documents
303
299
 
304
300
  # only get the previous doc if there is one
305
301
  prev_doc = document_list.first if index > 0
@@ -328,7 +324,7 @@ module Blacklight::SolrHelper
328
324
  solr_params = solr_opensearch_params().merge(extra_controller_params)
329
325
  response = find(solr_params)
330
326
  a = [solr_params[:q]]
331
- a << response.docs.map {|doc| doc[solr_params[:fl]].to_s }
327
+ a << response.documents.map {|doc| doc[solr_params[:fl]].to_s }
332
328
  end
333
329
 
334
330
 
@@ -377,4 +373,5 @@ module Blacklight::SolrHelper
377
373
  def should_add_to_solr field_name, field
378
374
  field.include_in_request || (field.include_in_request.nil? && blacklight_config.add_field_configuration_to_solr_request)
379
375
  end
376
+
380
377
  end
@@ -2,22 +2,26 @@ class Blacklight::SolrResponse < HashWithIndifferentAccess
2
2
 
3
3
  require 'blacklight/solr_response/pagination_methods'
4
4
 
5
- autoload :Spelling, 'blacklight/solr_response/spelling'
6
- autoload :Facets, 'blacklight/solr_response/facets'
7
- autoload :MoreLikeThis, 'blacklight/solr_response/more_like_this'
5
+ require 'blacklight/solr_response/response'
6
+ require 'blacklight/solr_response/spelling'
7
+ require 'blacklight/solr_response/facets'
8
+ require 'blacklight/solr_response/more_like_this'
8
9
  autoload :GroupResponse, 'blacklight/solr_response/group_response'
9
10
  autoload :Group, 'blacklight/solr_response/group'
10
11
 
11
12
  include PaginationMethods
13
+ include Spelling
14
+ include Facets
15
+ include Response
16
+ include MoreLikeThis
12
17
 
13
18
  attr_reader :request_params
14
- def initialize(data, request_params)
15
- super(data)
19
+ attr_accessor :solr_document_model
20
+
21
+ def initialize(data, request_params, options = {})
22
+ super(force_to_utf8(data))
16
23
  @request_params = request_params
17
- extend Spelling
18
- extend Facets
19
- extend Response
20
- extend MoreLikeThis
24
+ self.solr_document_model = options[:solr_document_model] || SolrDocument
21
25
  end
22
26
 
23
27
  def header
@@ -42,9 +46,9 @@ class Blacklight::SolrResponse < HashWithIndifferentAccess
42
46
  response['docs'] || []
43
47
  end
44
48
  end
45
-
46
- def spelling
47
- self['spelling']
49
+
50
+ def documents
51
+ docs.collect{|doc| solr_document_model.new(doc, self) }
48
52
  end
49
53
 
50
54
  def grouped
@@ -72,23 +76,21 @@ class Blacklight::SolrResponse < HashWithIndifferentAccess
72
76
  self.has_key? "grouped"
73
77
  end
74
78
 
75
- module Response
76
- def response
77
- self[:response] || {}
78
- end
79
-
80
- # short cut to response['numFound']
81
- def total
82
- response[:numFound].to_s.to_i
83
- end
84
-
85
- def start
86
- response[:start].to_s.to_i
87
- end
79
+ def export_formats
80
+ documents.map { |x| x.export_formats.keys }.flatten.uniq
81
+ end
82
+
83
+ private
88
84
 
89
- def empty?
90
- total == 0
85
+ def force_to_utf8(value)
86
+ case value
87
+ when Hash
88
+ value.each { |k, v| value[k] = force_to_utf8(v) }
89
+ when Array
90
+ value.each { |v| force_to_utf8(v) }
91
+ when String
92
+ value.force_encoding("utf-8") if value.respond_to?(:force_encoding)
93
+ end
94
+ value
91
95
  end
92
-
93
- end
94
96
  end