umlaut 4.0.3 → 4.1.0.pre.2

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 (39) hide show
  1. checksums.yaml +8 -8
  2. data/README.md +6 -0
  3. data/app/assets/javascripts/umlaut/update_html.js +81 -23
  4. data/app/assets/stylesheets/umlaut/_misc.scss +1 -2
  5. data/app/assets/stylesheets/umlaut/_resolve.scss +44 -10
  6. data/app/controllers/export_email_controller.rb +2 -2
  7. data/app/controllers/feedback_controller.rb +1 -1
  8. data/app/controllers/umlaut/controller_behavior.rb +14 -0
  9. data/app/controllers/umlaut_configurable.rb +52 -21
  10. data/app/helpers/open_search_helper.rb +1 -1
  11. data/app/helpers/resolve_helper.rb +36 -35
  12. data/app/helpers/umlaut/section_highlights.rb +85 -0
  13. data/app/mixin_logic/metadata_helper.rb +11 -5
  14. data/app/models/collection.rb +129 -96
  15. data/app/models/dispatched_service.rb +4 -0
  16. data/app/models/referent.rb +7 -1
  17. data/app/models/request.rb +21 -0
  18. data/app/models/service_store.rb +16 -3
  19. data/app/presentation/section_renderer.rb +16 -17
  20. data/app/service_adaptors/blacklight.rb +11 -4
  21. data/app/service_adaptors/internet_archive.rb +58 -25
  22. data/app/service_adaptors/isi.rb +14 -4
  23. data/app/service_adaptors/jcr.rb +13 -4
  24. data/app/views/resolve/_fulltext.html.erb +1 -1
  25. data/app/views/resolve/_section_display.html.erb +1 -1
  26. data/app/views/resolve/_service_errors.html.erb +3 -3
  27. data/app/views/search/books.html.erb +1 -1
  28. data/app/views/search/journal_search.html.erb +2 -1
  29. data/app/views/search/journals.html.erb +2 -1
  30. data/db/orig_fixed_data/service_type_values.yml +15 -37
  31. data/lib/aws_product_sign.rb +1 -1
  32. data/lib/generators/templates/umlaut_services.yml +5 -2
  33. data/lib/generators/umlaut/install_generator.rb +1 -0
  34. data/{app/models → lib}/service_type_value.rb +36 -22
  35. data/lib/umlaut/routes.rb +34 -5
  36. data/lib/umlaut/test_help.rb +159 -0
  37. data/lib/umlaut/version.rb +2 -2
  38. data/lib/umlaut.rb +44 -9
  39. metadata +10 -8
@@ -0,0 +1,85 @@
1
+ module Umlaut
2
+ # NOT Rails helper methods, but a helper class with logic to determine
3
+ # whether a given umlaut display section should be given the
4
+ # umlaut-section-highlighted class, used to mark recommended access
5
+ # methods. (For instance, fulltext if it's available, or maybe
6
+ # document_delivery if it's not, but it gets more complicated.)
7
+ class SectionHighlights
8
+ attr_reader :umlaut_request, :umlaut_config
9
+
10
+ # * First arg is the umlaut Request
11
+ # * Second optional is an UmlautConfiguration object, used for
12
+ # `section_highlights_filter` lambda -- will default to
13
+ # UmlautController.umlaut_config
14
+ def initialize(umlaut_request, umlaut_config = UmlautController.umlaut_config)
15
+ @umlaut_config = umlaut_config
16
+ @umlaut_request = umlaut_request
17
+ end
18
+
19
+ def should_highlight_section?(section_id)
20
+ highlighted_sections.include? section_id.to_s
21
+ end
22
+
23
+ # array of section div_id's that should be highlighted for
24
+ # the current request in it's current state. Calculated
25
+ # with calc_highlighted_sections!, then cached.
26
+ def highlighted_sections
27
+ @highlighted_sections ||= calc_highlighted_sections!
28
+ end
29
+
30
+ # Returns an array of zero or more sections to display with
31
+ # .umlaut-section-highlighted -- usually the recommended section,
32
+ # fulltext if we have it, etc.
33
+ #
34
+ # A bit hard to get exactly right for both technical and contextual
35
+ # policy issues, this is a basic starting point.
36
+ def calc_highlighted_sections!
37
+ sections = []
38
+
39
+ if umlaut_request.get_service_type("fulltext").present?
40
+ sections << "fulltext"
41
+ end
42
+
43
+ # Highlight holdings if it's present AND:
44
+ # no fulltext is present OR it's a book (non-serial) type
45
+ # We think people want print for books more often.
46
+ if umlaut_request.get_service_type("holding").present? &&
47
+ ( umlaut_request.get_service_type("fulltext").blank? || (! MetadataHelper.title_is_serial?(umlaut_request.referent)) )
48
+ sections << "holding"
49
+ end
50
+
51
+
52
+ # Return document_delivery as highlighted only if
53
+ # fulltext and holdings are done being fetched. AND.
54
+ # If there's no fulltext or holdings, OR there's holdings, but
55
+ # it's a journal type thing, where we probably don't know if the
56
+ # particular volume/issue wanted is present. Ugh.
57
+ if ( umlaut_request.get_service_type("document_delivery").present? &&
58
+ umlaut_request.get_service_type("fulltext").empty? &&
59
+ (! umlaut_request.service_types_in_progress?(["fulltext", "holding"])) && (
60
+ umlaut_request.get_service_type("holding").empty? ||
61
+ umlaut_request.referent.format == "journal"
62
+ )
63
+ )
64
+ sections << "document_delivery"
65
+ end
66
+
67
+ sections = apply_filters!(sections)
68
+
69
+ return sections
70
+ end
71
+
72
+
73
+ def apply_filters!(sections)
74
+ sections = sections.dup
75
+
76
+ (umlaut_config.section_highlights_filter || []).each do |filter|
77
+ # filters are expected to mutate 'sections' if they want
78
+ filter.call(umlaut_request, sections, self)
79
+ end
80
+
81
+ return sections
82
+ end
83
+
84
+ end
85
+ end
@@ -74,7 +74,7 @@ module MetadataHelper
74
74
  title.gsub!(/\&/, ' and ') if options[:normalize_ampersand]
75
75
 
76
76
  # remove non-alphanumeric, excluding apostrophe
77
- title.gsub!(/[^\w\s\']/, ' ') if options[:remove_punctuation]
77
+ title.gsub!(/[^[[:alnum:]][[:space:]]\']/, ' ') if options[:remove_punctuation]
78
78
 
79
79
  # apostrophe not to space, just eat it.
80
80
  title.gsub!(/[\']/, '') if options[:remove_punctuation] && ! options[:keep_apostrophes]
@@ -341,10 +341,16 @@ module MetadataHelper
341
341
  # Look at weird bad OpenURLs, use heuristics to see if the 'title' probably
342
342
  # represents a journal rather than a book. A guess at best, based on the bad
343
343
  # data we've seen, sigh.
344
- def title_is_serial?(rft)
345
- rft.format != "book" &&
346
- ( rft.metadata['jtitle'].present? || %w{journal article}.include?(rft.metadata["genre"]) ) &&
347
- rft.metadata['btitle'].blank?
344
+ def title_is_serial?(rft)
345
+ ( rft.format != "book" && rft.format != "dissertation") &&
346
+ ( rft.metadata["btitle"].blank? ) &&
347
+ ( %w{journal article}.include?(rft.metadata["genre"]) ||
348
+ rft.metadata['jtitle'].present? ||
349
+ (rft.metadata["genre"].blank? && rft.metadata["issn"].present?)
350
+ )
348
351
  end
352
+ # Mark it a module function so it can be called as a utility as
353
+ # MetadataHelper.title_is_serial?(referent)
354
+ module_function :title_is_serial?
349
355
 
350
356
  end
@@ -12,6 +12,8 @@ require 'confstruct'
12
12
  # The Collection holds and executes the logic for running those services,
13
13
  # foreground and background, making sure no service is run twice if it's
14
14
  # already in progress, timing out expired services, etc.
15
+ #
16
+ # This code is a mess, sorry.
15
17
  class Collection
16
18
  attr_accessor :umlaut_request
17
19
  attr_accessor :logger
@@ -66,26 +68,22 @@ class Collection
66
68
  #
67
69
  # Returns the Thread object used for dispatching background services
68
70
  def dispatch_services!
69
- queued_service_ids = prepare_for_dispatch!
70
-
71
- dispatch_foreground!(queued_service_ids)
71
+ freshen_dispatches!
72
+ mark_queued_if_empty!
73
+
74
+ dispatch_foreground!
72
75
 
73
76
  # return main thread for background services.
74
- return dispatch_background!(queued_service_ids)
77
+ return dispatch_background!
75
78
  end
76
79
 
77
- # Call prepare_for_dispatch! first, the return value from that call
78
- # is suitable as argument for this call: queued_service_ids, list of
79
- # service id's already identified as suitable for running, and
80
- # marked queued in the DispatchedService table.
81
- #
82
80
  # Will run such services in foreground priority waves. And then reload
83
81
  # the UmlautRequest object in the current thread, to pick up any
84
82
  # changes made in service threads.
85
- def dispatch_foreground!(queued_service_ids)
83
+ def dispatch_foreground!
86
84
  # Foreground services
87
85
  (0..9).each do | priority |
88
- services_to_run = self.instantiate_services!(:level => priority, :ids => queued_service_ids)
86
+ services_to_run = self.instantiate_services!(:level => priority, :ids => runnable_services_for_priority(priority))
89
87
  next if services_to_run.empty?
90
88
  ServiceWave.new(services_to_run , priority).handle(umlaut_request, umlaut_request.session_id)
91
89
  end
@@ -96,15 +94,13 @@ class Collection
96
94
  umlaut_request.reload
97
95
  end
98
96
 
99
- # Call prepare_for_dispatch! first, the return value from that call
100
- # is suitable as argument for this call: queued_service_ids, list of
101
- # service id's already identified as suitable for running, and
102
- # marked queued in the DispatchedService table.
103
- #
104
- # Will run such services in background priority waves.
97
+ # Will run such services in background priority waves. If some
98
+ # services are already running, will not run services in subsequent
99
+ # waves until they are done -- guard against multiple HTTP
100
+ # requests while services in progress.
105
101
  #
106
102
  # Returns the Thread object used for dispatching background services.
107
- def dispatch_background!(queued_service_ids)
103
+ def dispatch_background!
108
104
  # Now we do some crazy magic, start a Thread to run our background
109
105
  # services. We are NOT going to wait for this thread to join,
110
106
  # we're going to let it keep doing it's thing in the background after
@@ -124,11 +120,22 @@ class Collection
124
120
  # other stuff done before this thread.
125
121
  Thread.pass
126
122
 
123
+ force_refresh = false
127
124
 
128
125
  ('a'..'z').each do | priority |
129
- services_to_run = self.instantiate_services!(:level => priority, :ids => queued_service_ids)
130
- next if services_to_run.empty?
126
+ # force refresh only if we just ran some services, otherwise not enough
127
+ # time has gone by to be worthwhile.
128
+ runnable_ids = runnable_services_for_priority(priority, :refresh => force_refresh)
129
+
130
+ services_to_run = self.instantiate_services!(:level => priority, :ids => runnable_ids)
131
+
132
+ if services_to_run.empty?
133
+ force_refresh = false
134
+ next
135
+ end
136
+
131
137
  ServiceWave.new(services_to_run , priority).handle(umlaut_request, umlaut_request.session_id)
138
+ force_refresh = true
132
139
  end
133
140
  rescue Exception => e
134
141
  # We are divorced from any HTTP request at this point, and may not
@@ -154,90 +161,116 @@ class Collection
154
161
  end
155
162
  end
156
163
 
157
-
158
- # Goes through all services and marks them with a DispatchedService
159
- # record in 'queued' state.
160
- #
161
- # Will time out any too-old services in a running state.
162
- #
163
- # Will remove DispatchedService status for
164
- # any services marked failed that are old enough to re-run, or services
165
- # that are too old to re-use. Such services are then queuable.
166
- #
167
- # Returns array of Service identifiers for services that are now
168
- # queued and execable.
169
- def prepare_for_dispatch!
170
- # Go through currently dispatched services, looking for timed out
171
- # services -- services still in progress that have taken too long,
172
- # as well as service responses that are too old to be used.
173
-
174
- queued_service_ids = []
175
- DispatchedService.transaction do
176
- umlaut_request.dispatched_services.each do | ds |
177
- # go through dispatched_services and set stil in progress but too long to failed temporary
178
- if ( (ds.status == DispatchedService::InProgress ||
179
- ds.status == DispatchedService::Queued ) &&
180
- (Time.now - ds.updated_at) > self.background_service_timeout)
181
-
182
- ds.store_exception( Exception.new("background service timed out (took longer than #{self.background_service_timeout} to run); thread assumed dead.")) unless ds.exception_info
183
- # Fail it temporary, it'll be run again.
184
- ds.status = DispatchedService::FailedTemporary
185
- ds.save!
186
- logger.warn("Background service timed out, thread assumed dead. #{umlaut_request.id} / #{ds.service_id}")
187
- end
188
-
189
-
190
-
191
- # go through dispatched_services and delete:
192
- # 1) old completed dispatches, too old to use.
193
- # 2) failedtemporary dispatches that are older than our resurrection time
194
- # -> And all responses associated with those dispatches.
195
- # After being deleted, they'll end up re-queued.
196
- if ( (ds.completed? && completed_dispatch_expired?(ds) ) ||
197
- ( ds.status == DispatchedService::FailedTemporary &&
198
- (Time.now - ds.updated_at) > self.requeue_failedtemporary_services_in
199
- )
200
- )
201
-
202
- # Need to expire. Delete all the service responses, and
203
- # the DispatchedService record, and service will be automatically
204
- # run again.
205
- serv_id = ds.service_id
206
-
207
- umlaut_request.service_responses.each do |response|
208
- if response.service_id == serv_id
209
- umlaut_request.service_responses.delete(response)
210
- response.destroy
211
- end
212
- end
213
-
214
- umlaut_request.dispatched_services.destroy(ds)
164
+ # Goes through existing DispatchedService objects, and freshens them up:
165
+ # * If a service is marked in progress longer than timeout, mark
166
+ # it failed temporary.
167
+ # * If an existing failed temporary is older than our resurrection time,
168
+ # delete the dispatch (and all it's responses), so it can be re-queued.
169
+ def freshen_dispatches!
170
+ umlaut_request.dispatched_services.each do | ds |
171
+ # go through dispatched_services and set still in progress but too long to failed temporary
172
+ if ( (ds.status == DispatchedService::InProgress ||
173
+ ds.status == DispatchedService::Queued ) &&
174
+ (Time.now - ds.updated_at) > self.background_service_timeout)
175
+
176
+ ds.store_exception( Exception.new("background service timed out (took longer than #{self.background_service_timeout} to run); thread assumed dead.")) unless ds.exception_info
177
+ # Fail it temporary, it'll be run again.
178
+ ds.status = DispatchedService::FailedTemporary
179
+ ds.save!
180
+ logger.warn("Background service timed out, thread assumed dead. #{umlaut_request.id} / #{ds.service_id}")
181
+ end
182
+
183
+
184
+
185
+ # go through dispatched_services and delete:
186
+ # 1) old completed dispatches, too old to use.
187
+ # 2) failedtemporary dispatches that are older than our resurrection time
188
+ # -> And all responses associated with those dispatches.
189
+ # After being deleted, they'll end up re-queued.
190
+ if ( (ds.completed? && completed_dispatch_expired?(ds) ) ||
191
+ ( ds.status == DispatchedService::FailedTemporary &&
192
+ (Time.now - ds.updated_at) > self.requeue_failedtemporary_services_in
193
+ )
194
+ )
195
+
196
+ # Need to expire. Delete all the service responses, and
197
+ # the DispatchedService record, and service will be automatically
198
+ # run again.
199
+ serv_id = ds.service_id
200
+
201
+ umlaut_request.service_responses.each do |response|
202
+ if response.service_id == serv_id
203
+ umlaut_request.service_responses.delete(response)
204
+ response.destroy
215
205
  end
206
+ end
207
+
208
+ umlaut_request.dispatched_services.destroy(ds)
216
209
  end
210
+ end
211
+ end
212
+
213
+ # For all configured services, if they have NO DispatchedService
214
+ # object, then create one with status Queued
215
+ def mark_queued_if_empty!
216
+ our_service_ids = self.get_service_definitions.collect {|d| d["service_id"]}
217
+
218
+ existing_dispatches = umlaut_request.dispatched_services.collect {|d| d.service_id}
219
+
220
+ not_yet_existing = our_service_ids - existing_dispatches
217
221
 
218
- # Queue any services without a dispatch marker at all, keeping
219
- # track of queued services, already existing or newly created.
220
-
221
- # Just in case, we're going to refetch dispatched_services from the db,
222
- # in case some other http request or background service updated things
223
- # recently.
224
- umlaut_request.dispatched_services.reset
225
-
226
- self.get_service_definitions.each do |service|
227
- service_id = service['service_id']
228
- # use in-memory #to_a search, don't go to db each time!
229
- if found = umlaut_request.dispatched_services.to_a.find {|s| s.service_id == service_id}
230
- queued_service_ids.push(service_id) if found.status == DispatchedService::Queued
231
- else
232
- umlaut_request.new_dispatch_object!(service_id, DispatchedService::Queued).save!
233
- queued_service_ids.push(service_id)
222
+ not_yet_existing.each do |service_id|
223
+ umlaut_request.new_dispatch_object!(service_id, DispatchedService::Queued).save!
224
+ end
225
+ end
226
+
227
+ # All services for priority that are marked Queued, so long as
228
+ # no previous waves are still marked running.
229
+ #
230
+ # Pass `:refresh => true` as second argument to force trip
231
+ # to the database to get fresh DispatchedService objects.
232
+ #
233
+ # Returns array of service_id's, or empty array.
234
+ def runnable_services_for_priority(priority, options = {})
235
+ DispatchedService.connection_pool.with_connection do
236
+ service_definitions = self.get_service_definitions
237
+
238
+ # Make a hash where key is service id, and value is priority.to_s
239
+ service_to_priority = Hash[
240
+ service_definitions.collect do |d|
241
+ [ d["service_id"], d["priority"].to_s ]
234
242
  end
243
+ ]
244
+
245
+ if options[:refresh]
246
+ # force a refresh
247
+ umlaut_request.dispatched_services(true)
235
248
  end
236
- end
237
249
 
238
- return queued_service_ids
250
+ # If there is any service earlier than this wave still marked InProgress,
251
+ # we're not ready to run this wave, return empty array.
252
+ # Important to avoid race condition on HTTP requests, don't
253
+ # dispatch later background waves unless earlier are actually complete,
254
+ # even on an HTTP status check.
255
+ previous_waves_running = umlaut_request.dispatched_services.find do |ds|
256
+ ds.status == DispatchedService::InProgress &&
257
+ service_to_priority[ ds.service_id ] < priority.to_s
258
+ end.present?
259
+ return [] if previous_waves_running
260
+
261
+ # otherwise, the services for this priority are runnable if
262
+ # they are already marked Queued
263
+ # We use .to_a, we want to use the already in memory array, not
264
+ # go to the db here.
265
+ return umlaut_request.dispatched_services.to_a.find_all do |ds|
266
+ ds.status == DispatchedService::Queued &&
267
+ service_to_priority[ ds.service_id ] == priority.to_s
268
+ end.collect {|ds| ds.service_id}
269
+ end
239
270
  end
240
271
 
272
+
273
+
241
274
  def completed_dispatch_expired?(ds)
242
275
  interval = self.response_expire_interval
243
276
  crontab = self.response_expire_crontab_format
@@ -68,4 +68,8 @@ class DispatchedService < ActiveRecord::Base
68
68
  def completed?
69
69
  return (self.status != InProgress) && (self.status != Queued)
70
70
  end
71
+
72
+ def failed?
73
+ return (self.status == FailedTemporary) || (self.status == FailedFatal)
74
+ end
71
75
  end
@@ -390,11 +390,17 @@ class Referent < ActiveRecord::Base
390
390
 
391
391
  # options => { :overwrite => false } to only enhance if not already there
392
392
  def enhance_referent(key, value, metadata=true, private_data=false, options = {})
393
+
394
+
393
395
  ActiveRecord::Base.connection_pool.with_connection do
394
396
  return if value.nil?
395
397
 
396
398
  matches = self.referent_values.to_a.find_all do |rv|
397
- (rv.key_name == key) && (rv.metadata == metadata) && (rv.private_data == private_data)
399
+ # We ignore #metadata and #private_data matches in overwriting
400
+ # existing value. We used to take them into account, but it triggered
401
+ # a bug in Jruby, and pretty much isn't neccesary, those fields
402
+ # are pretty useless and mostly not used and should prob be removed.
403
+ (rv.key_name == key) # && (rv.metadata == metadata) && (rv.private_data == private_data)
398
404
  end
399
405
 
400
406
  matches.each do |rv|
@@ -387,6 +387,25 @@ class Request < ActiveRecord::Base
387
387
  self.dispatched_services << ds
388
388
  return ds
389
389
  end
390
+
391
+ # Returns an array of 0 or more ServiceDispatch objects matching
392
+ # specified conditions. Right now only one condition is supported:
393
+ #
394
+ # dispatch_objects_with(:service_type_values => values)
395
+ # values can be one or more string names of service types, returns
396
+ # DispatchedServices for services whose generated values include
397
+ # one or more of what you specified.
398
+ def dispatch_objects_with(options = {})
399
+ value_names = Array(options[:service_type_values])
400
+
401
+ raise ArgumentError.new("Need to supply a :service_type_values argument") unless value_names.present?
402
+
403
+ list = self.dispatched_services.to_a.find_all do |ds|
404
+ (value_names & ds.service.service_types_generated.collect(&:name)).present?
405
+ end
406
+
407
+ return list
408
+ end
390
409
 
391
410
  protected
392
411
 
@@ -465,6 +484,8 @@ class Request < ActiveRecord::Base
465
484
  return self.dispatched_services.where(:service_id => service.service_id).first
466
485
  end
467
486
 
487
+
488
+
468
489
  # Input is a CGI::parse style of HTTP params (array values)
469
490
  # output is a string "fingerprint" canonically representing the input
470
491
  # params, which can be stored in the db, so that when another request
@@ -33,12 +33,19 @@ class ServiceStore
33
33
  end
34
34
 
35
35
 
36
- # Returns complete hash loaded from services.yml
36
+ # Returns complete hash loaded from config/umlaut_services.yml
37
+ # Passes through ERB first, allowing ERB in umlaut_services.yml
37
38
  def config
38
39
  # cache hash loaded from YAML, ensure it has the keys we expect.
39
40
  unless defined? @services_config_list
40
41
  yaml_path = File.expand_path("config/umlaut_services.yml", Rails.root)
41
- @services_config_list = (File.exists? yaml_path) ? YAML::load(File.open(yaml_path)) : {}
42
+
43
+ @services_config_list = if File.exists? yaml_path
44
+ YAML::load(ERB.new(File.open(yaml_path).read).result)
45
+ else
46
+ {}
47
+ end
48
+
42
49
  @services_config_list["default"] ||= {}
43
50
  @services_config_list["default"]["services"] ||= {}
44
51
  end
@@ -120,13 +127,19 @@ class ServiceStore
120
127
  # pass in string unique key OR a service definition hash,
121
128
  # and a current UmlautRequest.
122
129
  # get back instantiated Service object.
130
+ #
131
+ # If string service_id is passed in, but is not defined in application services,
132
+ # a ServiceStore::NoSuchService exception will be raised.
123
133
  def instantiate_service!(service, request)
124
134
  definition = service.kind_of?(Hash) ? service : service_definition_for(service.to_s)
125
- raise "Service '#{service}'' does not exist in umlaut-services.yml" if definition.nil?
135
+ raise NoSuchService.new("Service '#{service}'' does not exist in umlaut-services.yml") if definition.nil?
126
136
  className = definition["type"] || definition["service_id"]
127
137
  classConst = Kernel.const_get(className)
128
138
  service = classConst.new(definition)
129
139
  service.request = request
130
140
  return service
131
141
  end
142
+
143
+ class NoSuchService < RuntimeError ; end
144
+
132
145
  end
@@ -236,7 +236,6 @@
236
236
  # {:div_id => "search_inside", :partial => "search_inside", :show_partial_only => true}
237
237
  class SectionRenderer
238
238
  include ActionView::Helpers::TagHelper
239
- @@bg_update_sections = @@partial_update_sections = nil
240
239
 
241
240
  # First argument is the current umlaut Request object.
242
241
  # Second argument is a session description hash. See class overview
@@ -266,12 +265,12 @@ class SectionRenderer
266
265
  def initialize(a_umlaut_request, section_def = {})
267
266
  @umlaut_request = a_umlaut_request
268
267
 
269
- @section_id = section_def[:id] || section_def[:div_id]
270
- raise Exception.new("SectionRenderer needs an :id passed in arguments hash") unless @section_id
268
+ @div_id = section_def[:div_id] || section_def[:id]
269
+ raise Exception.new("SectionRenderer needs a :div_id passed in arguments hash") unless @div_id
270
+
271
271
 
272
272
  # Merge in default arguments for this section from config.
273
273
  construct_options(section_def)
274
-
275
274
  end
276
275
 
277
276
  # Returns all ServiceTypeValue objects contained in this section, as
@@ -319,7 +318,7 @@ class SectionRenderer
319
318
  end
320
319
 
321
320
  def div_id
322
- return @section_id
321
+ return @div_id
323
322
  end
324
323
 
325
324
  def show_heading?
@@ -341,7 +340,7 @@ class SectionRenderer
341
340
 
342
341
  { :partial => "background_progress",
343
342
  :locals =>{ :svc_types => service_type_values,
344
- :div_id => "progress_#{@section_id}",
343
+ :div_id => "progress_#{self.div_id}",
345
344
  :current_set_empty => responses_empty?,
346
345
  :item_name => custom_item_name
347
346
  }
@@ -404,7 +403,7 @@ class SectionRenderer
404
403
  when :complete_with_responses
405
404
  (! responses.empty?) && ! (services_in_progress?)
406
405
  when Proc
407
- # It's a lambda, which takes @umlaut_request as an arg
406
+ # It's a lambda, which takes this SectionRenderer as an arg
408
407
  @options[:visibility].call(self)
409
408
  else true
410
409
  end
@@ -504,20 +503,20 @@ class SectionRenderer
504
503
 
505
504
 
506
505
  # service type value default to same name as section_id
507
- @options[:service_type_values] ||= [@section_id]
506
+ @options[:service_type_values] ||= [self.div_id]
508
507
 
509
508
  # Partials to display. Default to _standard_response_item item partial.
510
509
  if ( @options[:partial] == true)
511
- @options[:partial] = @section_id
512
- end
513
- if (@options[:partial].blank?)
514
- @options[:item_partial] =
515
- case @options[:item_partial]
516
- when true then @section_id + "_item"
517
- when String then options[:item_partial]
518
- else "standard_response_item"
519
- end
510
+ @options[:partial] = self.div_id
520
511
  end
512
+
513
+ @options[:item_partial] =
514
+ case @options[:item_partial]
515
+ when true then self.div_id + "_item"
516
+ when String then options[:item_partial]
517
+ else "standard_response_item"
518
+ end
519
+
521
520
 
522
521
  # sanity check
523
522
  if ( @options[:show_partial_only] && ! @options[:partial])
@@ -90,6 +90,7 @@ class Blacklight < Service
90
90
  holdings_url = blacklight_precise_search_url( request, "dlf_expanded" )
91
91
  holdings_added += add_holdings( holdings_url ) if holdings_url
92
92
  end
93
+
93
94
  #keyword search.
94
95
  if (@keyword_search &&
95
96
  url = blacklight_keyword_search_url(request))
@@ -210,8 +211,8 @@ class Blacklight < Service
210
211
  # phrase search for title, just raw dismax for author
211
212
  # Embed quotes inside the quoted value, need to backslash-quote for CQL,
212
213
  # and backslash the backslashes for ruby literal.
213
- clauses.push("#{@bl_fields["title"]} = \"\\\"#{remove_quotes_for_phrase_value title}\\\"\"")
214
- clauses.push("#{@bl_fields["author"]} = \"#{remove_quotes_for_phrase_value author}\"") if author
214
+ clauses.push("#{@bl_fields["title"]} = \"\\\"#{escape_for_cql_double_quotes title}\\\"\"")
215
+ clauses.push("#{@bl_fields["author"]} = \"#{escape_for_cql_double_quotes author}\"") if author
215
216
 
216
217
  url = base_url + "?search_field=#{@cql_search_field}&content_format=#{options[:content_format]}&q=#{CGI.escape(clauses.join(" AND "))}"
217
218
 
@@ -228,8 +229,14 @@ class Blacklight < Service
228
229
  # error if we do nothing. Can we escape it somehow? CQL is really
229
230
  # unclear, we're ALREADY backslash escaping the phrase quotes themselves!
230
231
  # We just replace them with space, should work for our actual indexing.
231
- def remove_quotes_for_phrase_value(str)
232
- str.gsub('"', " ")
232
+ #
233
+ # Single quotes (apostrophes) need to be escaped with an apostrophe itself,
234
+ # `''`, apparently. http://mail-archives.apache.org/mod_mbox/cassandra-user/201108.mbox/%3C20110803152250.294300@gmx.net%3E
235
+ def escape_for_cql_double_quotes(str)
236
+ str = str.gsub('"', " ")
237
+ str = str.gsub("'", "''")
238
+
239
+ return str
233
240
  end
234
241
 
235
242