umlaut 3.0.0beta5 → 3.0.0beta6

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,8 +2,10 @@
2
2
  <td><%= dispatched_service.updated_at.localtime.to_s(:short) %></td>
3
3
  <td><%= dispatched_service.service_id %></td>
4
4
  <td><%= dispatched_service.status %></td>
5
- <td><%= link_to "[Re-request live]", url_for_with_co({:controller => "/resolve", :action => :index, "umlaut.force_new_request" => "true"}, dispatched_service.request.to_context_object) %></td>
6
- <td><%= link_to "[View cached request]", url_for(:controller => "/resolve", :action => :index, "umlaut.request_id" => dispatched_service.request_id ) %></td>
5
+ <% if dispatched_service.request %>
6
+ <td><%= link_to "[Re-request live]", url_for_with_co({:controller => "/resolve", :action => :index, "umlaut.force_new_request" => "true"}, dispatched_service.request.to_context_object) %></td>
7
+ <td><%= link_to "[View cached request]", url_for(:controller => "/resolve", :action => :index, "umlaut.request_id" => dispatched_service.request_id ) %></td>
8
+ <% end %>
7
9
  </tr>
8
10
  <tr>
9
11
  <% if dispatched_service.exception_info %>
@@ -13,7 +13,7 @@
13
13
 
14
14
  <% if params[:service_id] %>
15
15
  <h2>Service: <%= params[:service_id] %></h2>
16
- <%= link_to "[View All]", params.merge(:service_id => nil) %>
16
+ <%= link_to "[View All]", params.merge(:service_id => nil, :offset => nil) %>
17
17
  <% else %>
18
18
  <h2>By service</h2>
19
19
 
@@ -37,9 +37,14 @@
37
37
 
38
38
  <p>
39
39
  <%= @offset + 1 %> - <%= [@offset + @limit, @dispatched_services_count].min %> of <%= @dispatched_services_count %>. <%= link_to "[Next]", params.merge("offset" => @offset + @limit ) %>
40
+ </p>
40
41
 
41
42
  <table>
42
43
  <%= render :partial => "dispatched_service", :collection => @dispatched_services %>
43
44
  </table>
45
+
46
+ <p>
47
+ <%= @offset + 1 %> - <%= [@offset + @limit, @dispatched_services_count].min %> of <%= @dispatched_services_count %>. <%= link_to "[Next]", params.merge("offset" => @offset + @limit ) %>
48
+ </p>
44
49
 
45
50
  </div>
@@ -94,8 +94,15 @@ class Amazon < Service
94
94
  isbn = request.referent.metadata['isbn']
95
95
  isbn = isbn.gsub(/[^0-9X]/,'') if isbn
96
96
 
97
- return request.dispatched(self, true) if isbn.blank?
97
+ # does it look like a good ISBN?
98
+ return request.dispatched(self, true) if isbn.blank? || ! [10,13].include?(isbn.length)
98
99
 
100
+ # Make sure it's REALLY a good ISBN, and
101
+ # Convert 13 to 10 if neccesary, cause we're using it as an ASIN.
102
+ # An ISBN-13 is never an ASIN.
103
+ isbn = ISBN.ten( isbn )
104
+
105
+
99
106
  begin
100
107
 
101
108
  selected_aws_vals = {}
@@ -137,10 +144,7 @@ class Amazon < Service
137
144
  # We're assuming the ISBN is the ASIN Amazon ID. Not neccesarily valid
138
145
  # assumption, but works enough of the time and there's no easy
139
146
  # alternative.
140
- # Convert 13 to 10 if neccesary.
141
-
142
- # got to try converting to 10. An ISBN-13 is never an ASIN.
143
- isbn = ISBN.ten( isbn )
147
+
144
148
 
145
149
 
146
150
  query_params = {
@@ -217,7 +217,7 @@ class GoogleBookSearch < Service
217
217
 
218
218
  Rails.logger.debug("GoogleBookSearch requesting: #{link}")
219
219
  response = http_fetch(link, :headers => headers, :raise_on_http_error_code => false)
220
- data = MultiJson.decode(response.body)
220
+ data = MultiJson.load(response.body)
221
221
 
222
222
  # If Google gives us an error cause it says it can't geo-locate,
223
223
  # remove the IP, log warning, and try again.
@@ -227,7 +227,7 @@ class GoogleBookSearch < Service
227
227
  Rails.logger.warn("GoogleBookSearch: geo-locate error, retrying without X-Forwarded-For: '#{link}' headers: #{headers.inspect} #{response.inspect}\n #{data.inspect}")
228
228
 
229
229
  response = http_fetch(link, :raise_on_http_error_code => false)
230
- data = MultiJson.decode(response.body)
230
+ data = MultiJson.load(response.body)
231
231
 
232
232
  end
233
233
 
@@ -133,9 +133,9 @@ class HathiTrust < Service
133
133
  end
134
134
 
135
135
  # conducts query and parses the JSON
136
- def do_query(params)
136
+ def do_query(params)
137
137
  link = @api_url + "/brief/json/" + params
138
- return MultiJson.decode( open(link).read )
138
+ return MultiJson.load( open(link).read )
139
139
  end
140
140
 
141
141
 
@@ -104,25 +104,16 @@ class InternetArchive < Service
104
104
 
105
105
  # using open() conveniently follows the redirect for us. Alas, it
106
106
  # doesn't give us access to the IA http status code response though.
107
- begin
108
- response = nil
109
- timeout(@http_timeout.to_i) {
110
- response = open(link).read
111
- }
112
- rescue Exception => e
113
- # Log more info for exception, and then just forward exception on,
114
- # we don't have any way to handle it.
115
- Rails.logger.error("InternetArchive exception, for url[[#{link}]] , Exception #{e.class}")
116
- raise e
117
- end
118
-
107
+ response = nil
108
+ timeout(@http_timeout.to_i) {
109
+ response = open(link).read
110
+ }
119
111
  if response.blank?
120
- Rails.logger.warn("InternetArchive returned empty response for #{link}")
121
- return nil
112
+ raise Exception.new("InternetArchive returned empty response for #{link}")
122
113
  end
123
114
 
124
115
 
125
- doc = MultiJson.decode(response)
116
+ doc = MultiJson.load(response)
126
117
  results = doc['response']['docs']
127
118
 
128
119
  @mediatypes.each do |type|
@@ -258,14 +249,14 @@ class InternetArchive < Service
258
249
  # commands even within quotes.
259
250
  output = string.downcase
260
251
 
261
- # Remove parens, semi-colons, and brackets -- they all mess
252
+ # Remove parens, semi-colons, brackets, hyphens -- they all mess
262
253
  # up IA, which thinks they are special chars. Remove double quote,
263
254
  # special char, which sometimes we want to use ourselves. Replace
264
255
  # all with spaces to avoid accidentally conjoining words.
265
256
  # (could be
266
257
  # escaping instead? Not worth it, we don't want to search
267
258
  # on these anyway. Remove ALL punctuation? Not sure.)
268
- output.gsub!(/[)(\]\[;"\=]/, ' ')
259
+ output.gsub!(/[)(\]\[;"\=\-]/, ' ')
269
260
 
270
261
  return output
271
262
  end
@@ -485,4 +476,4 @@ end
485
476
  # The Snow-Image: http://www.worldcat.org/oclc/5020610
486
477
  # Les Canadiens-Français: http://www.worldcat.org/oclc/186641188
487
478
  # FIXME should match 1 record and doesn't. character encoding problems?
488
- # John L. Stoddard's Lectures: http://www.worldcat.org/oclc/2181690
479
+ # John L. Stoddard's Lectures: http://www.worldcat.org/oclc/2181690
@@ -261,7 +261,9 @@ class PrimoService < Service
261
261
  service_data[:match_reliability] =
262
262
  (reliable_match?(:title => holding.title, :author => holding.author)) ?
263
263
  ServiceResponse::MatchExact : ServiceResponse::MatchUnsure
264
- service_data[:request_link_supports_ajax_call] = false
264
+ service_data[:request_link_supports_ajax_call] =
265
+ (holding.respond_to?(:request_link_supports_ajax_call)) ?
266
+ holding.request_link_supports_ajax_call : false
265
267
  # Only add one service type, either "primo_source" OR "holding", not both.
266
268
  service_type = (@service_types.include?("primo_source")) ? "primo_source" : "holding"
267
269
  # Add some other holding information for compatibility with default holding partial
@@ -447,4 +449,4 @@ class PrimoService < Service
447
449
  return false if @identifier.nil?
448
450
  return @identifier.start_with?('info:sid/primo.exlibrisgroup.com')
449
451
  end
450
- end
452
+ end
@@ -30,6 +30,7 @@ class PrimoSource < PrimoService
30
30
  # Overwrites PrimoService#new.
31
31
  def initialize(config)
32
32
  @service_types = ["holding"]
33
+ @source_attributes = []
33
34
  super(config)
34
35
  end
35
36
 
@@ -52,6 +53,9 @@ class PrimoSource < PrimoService
52
53
  @holding_attributes.each do |attr|
53
54
  service_data[attr] = holding.method(attr).call
54
55
  end
56
+ @source_attributes.each do |attr|
57
+ service_data[attr.to_sym] = holding.method(attr.to_sym).call
58
+ end
55
59
  service_data.merge!({
56
60
  :call_number => holding.call_number, :collection => holding.collection,
57
61
  :collection_str => "#{holding.library} #{holding.collection}",
@@ -110,7 +110,7 @@ class Scopus < Service
110
110
 
111
111
  # Take the first hit from scopus's results, hope they relevancy ranked it
112
112
  # well. For DOI/pmid search, there should ordinarly be only one hit!
113
- results = MultiJson.decode(response)
113
+ results = MultiJson.load(response)
114
114
 
115
115
  if ( results["ERROR"])
116
116
  Rails.logger.error("Error from Scopus API: #{results["ERROR"].inspect} openurl: ?#{request.referent.to_context_object.kev}")
data/lib/umlaut/routes.rb CHANGED
@@ -53,7 +53,7 @@ module Umlaut
53
53
  def a_z
54
54
  add_routes do |options|
55
55
  # Special one for alpha list
56
- match 'journal_list/:id/:page' => 'search#journal_list', :defaults => { :page => 1, :id => 'A' }
56
+ match 'journal_list(/:id(/:page))' => 'search#journal_list', :defaults => { :page => '1', :id => 'A' }
57
57
 
58
58
 
59
59
  # Catch redirected from SFX A-Z and citation linker urls
@@ -1,3 +1,3 @@
1
1
  module Umlaut
2
- VERSION = "3.0.0beta5"
2
+ VERSION = "3.0.0beta6"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: umlaut
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0beta5
4
+ version: 3.0.0beta6
5
5
  prerelease: 5
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-12 00:00:00.000000000 Z
12
+ date: 2012-04-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
16
- requirement: &17186100 !ruby/object:Gem::Requirement
16
+ requirement: &102344040 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 3.2.2
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *17186100
24
+ version_requirements: *102344040
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: jquery-rails
27
- requirement: &17185560 !ruby/object:Gem::Requirement
27
+ requirement: &102355740 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *17185560
35
+ version_requirements: *102355740
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: nokogiri
38
- requirement: &17184800 !ruby/object:Gem::Requirement
38
+ requirement: &102351520 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - =
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 1.5.0
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *17184800
46
+ version_requirements: *102351520
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: openurl
49
- requirement: &17184180 !ruby/object:Gem::Requirement
49
+ requirement: &102374560 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 0.3.0
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *17184180
57
+ version_requirements: *102374560
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: marc
60
- requirement: &17183420 !ruby/object:Gem::Requirement
60
+ requirement: &102378660 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: 0.4.3
66
66
  type: :runtime
67
67
  prerelease: false
68
- version_requirements: *17183420
68
+ version_requirements: *102378660
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: isbn
71
- requirement: &17182120 !ruby/object:Gem::Requirement
71
+ requirement: &102376760 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0'
77
77
  type: :runtime
78
78
  prerelease: false
79
- version_requirements: *17182120
79
+ version_requirements: *102376760
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: htmlentities
82
- requirement: &17180260 !ruby/object:Gem::Requirement
82
+ requirement: &102385880 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '0'
88
88
  type: :runtime
89
89
  prerelease: false
90
- version_requirements: *17180260
90
+ version_requirements: *102385880
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: multi_json
93
- requirement: &17179300 !ruby/object:Gem::Requirement
93
+ requirement: &102404440 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,10 +98,10 @@ dependencies:
98
98
  version: '0'
99
99
  type: :runtime
100
100
  prerelease: false
101
- version_requirements: *17179300
101
+ version_requirements: *102404440
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: confstruct
104
- requirement: &17132240 !ruby/object:Gem::Requirement
104
+ requirement: &102501700 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ~>
@@ -109,10 +109,10 @@ dependencies:
109
109
  version: '0.2'
110
110
  type: :runtime
111
111
  prerelease: false
112
- version_requirements: *17132240
112
+ version_requirements: *102501700
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: exlibris-primo
115
- requirement: &17130980 !ruby/object:Gem::Requirement
115
+ requirement: &102514240 !ruby/object:Gem::Requirement
116
116
  none: false
117
117
  requirements:
118
118
  - - ~>
@@ -120,10 +120,10 @@ dependencies:
120
120
  version: 0.1.0
121
121
  type: :runtime
122
122
  prerelease: false
123
- version_requirements: *17130980
123
+ version_requirements: *102514240
124
124
  - !ruby/object:Gem::Dependency
125
125
  name: single_test
126
- requirement: &17129720 !ruby/object:Gem::Requirement
126
+ requirement: &102512120 !ruby/object:Gem::Requirement
127
127
  none: false
128
128
  requirements:
129
129
  - - ~>
@@ -131,10 +131,10 @@ dependencies:
131
131
  version: 0.5.1
132
132
  type: :development
133
133
  prerelease: false
134
- version_requirements: *17129720
134
+ version_requirements: *102512120
135
135
  - !ruby/object:Gem::Dependency
136
136
  name: uglifier
137
- requirement: &17128300 !ruby/object:Gem::Requirement
137
+ requirement: &102509700 !ruby/object:Gem::Requirement
138
138
  none: false
139
139
  requirements:
140
140
  - - ! '>='
@@ -142,10 +142,10 @@ dependencies:
142
142
  version: '0'
143
143
  type: :development
144
144
  prerelease: false
145
- version_requirements: *17128300
145
+ version_requirements: *102509700
146
146
  - !ruby/object:Gem::Dependency
147
147
  name: therubyracer
148
- requirement: &17126180 !ruby/object:Gem::Requirement
148
+ requirement: &102507660 !ruby/object:Gem::Requirement
149
149
  none: false
150
150
  requirements:
151
151
  - - ! '>='
@@ -153,10 +153,10 @@ dependencies:
153
153
  version: '0'
154
154
  type: :development
155
155
  prerelease: false
156
- version_requirements: *17126180
156
+ version_requirements: *102507660
157
157
  - !ruby/object:Gem::Dependency
158
158
  name: ruby-prof
159
- requirement: &17523620 !ruby/object:Gem::Requirement
159
+ requirement: &102522180 !ruby/object:Gem::Requirement
160
160
  none: false
161
161
  requirements:
162
162
  - - ! '>='
@@ -164,7 +164,7 @@ dependencies:
164
164
  version: '0'
165
165
  type: :development
166
166
  prerelease: false
167
- version_requirements: *17523620
167
+ version_requirements: *102522180
168
168
  description:
169
169
  email:
170
170
  - umlaut-general@rubyforge.org
@@ -213,13 +213,11 @@ files:
213
213
  - app/assets/javascripts/umlaut_ui.js
214
214
  - app/assets/javascripts/umlaut.js
215
215
  - app/assets/javascripts/umlaut/update_html.js
216
- - app/assets/javascripts/umlaut/#simple_visible_toggle.js#
217
216
  - app/assets/javascripts/umlaut/ajax_windows.js
218
217
  - app/assets/javascripts/umlaut/simple_visible_toggle.js
219
218
  - app/assets/javascripts/umlaut/ensure_window_size.js.erb
220
219
  - app/assets/javascripts/umlaut/expand_contract_toggle.js
221
220
  - app/assets/javascripts/umlaut/search_autocomplete.js
222
- - app/assets/javascripts/umlaut/#update_html.js#
223
221
  - app/assets/stylesheets/umlaut.css
224
222
  - app/models/sfx_url.rb
225
223
  - app/models/crossref_lookup.rb
@@ -323,7 +321,6 @@ files:
323
321
  - lib/opensearch_feed.rb
324
322
  - lib/holding.rb
325
323
  - lib/umlaut_configurable.rb
326
- - lib/umlaut/#routes.rb#
327
324
  - lib/umlaut/routes.rb
328
325
  - lib/umlaut/version.rb
329
326
  - lib/umlaut/default_configuration.rb
@@ -336,7 +333,6 @@ files:
336
333
  - lib/service_adaptors/primo_service.rb
337
334
  - lib/service_adaptors/open_library.rb
338
335
  - lib/service_adaptors/elsevier_cover.rb
339
- - lib/service_adaptors/#blacklight.rb#
340
336
  - lib/service_adaptors/ulrichs_link.rb
341
337
  - lib/service_adaptors/ulrichs_cover.rb
342
338
  - lib/service_adaptors/scopus.rb
@@ -485,7 +481,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
485
481
  version: '0'
486
482
  segments:
487
483
  - 0
488
- hash: -986297622485492728
484
+ hash: 367390304893023156
489
485
  required_rubygems_version: !ruby/object:Gem::Requirement
490
486
  none: false
491
487
  requirements:
@@ -1,8 +0,0 @@
1
- /* simple_visible_toggle.js. Used for toggling visibility of error information. Can possibly be combined with more powerful expand_contract_toggle.js */
2
- jQuery(document).ready(function($) {
3
-
4
- $("a.simple_visible_toggle").live("click", function() {
5
- $(this).next().toggle();
6
- });
7
-
8
- });
@@ -1,181 +0,0 @@
1
- /* update_html.js: Provide functions to update content on page with background responses from Umlaut. Used by Umlaut itself, as well as by third party callers.*/
2
- (function($) {
3
-
4
- function SectionTarget(config) {
5
- //Add properties from config to ourself
6
- $.extend(this, config);
7
-
8
- //Defaults
9
- if (this.selector == undefined)
10
- this.selector = "#" + this.umlaut_section_id;
11
- if (this.position == undefined)
12
- this.position = "html";
13
-
14
- }
15
- //Callback default to no-op function please.
16
- var noop = function() {};
17
- SectionTarget.prototype.before_update = noop;
18
- SectionTarget.prototype.after_update = noop;
19
- SectionTarget.prototype.complete = noop;
20
-
21
- SectionTarget.prototype.ensure_placement_destination = function() {
22
- if ( this.selector == undefined) {
23
- return null;
24
- }
25
-
26
- //Already have it cached?
27
- if ( this.host_div_element ) {
28
- return this.host_div_element;
29
- }
30
-
31
- var new_div = $('<div class="umlaut" style="display:none"></div>');
32
- // Find the first thing matched by selector, and call the
33
- // method specified in "action" string on it, giving it our
34
- // HTML to replace. This works because our actions are
35
- // all arguments that will take one method: html, before, after, append,
36
- // prepend.
37
- $(this.selector).eq(0)[ this.position ]( new_div );
38
-
39
- //Cache for later
40
- this.host_div_element = new_div;
41
- return this.host_div_element;
42
- };
43
-
44
-
45
- // Define an object constructor on the global window object
46
- // For our UmlautHtmlUpdater object.
47
- function HtmlUpdater(umlaut_base, context_object) {
48
- if (context_object == undefined)
49
- context_object = "";
50
-
51
- umlaut_base = umlaut_base.replace(/\/$/,'');
52
- this.umlaut_uri = umlaut_base + '/resolve/partial_html_sections?umlaut.response_format=json&' + context_object;
53
-
54
- this.section_targets = [];
55
-
56
- this.add_section_target = function(config) {
57
- this.section_targets.push( new SectionTarget(config) );
58
- };
59
-
60
- //default no-op call-backs
61
- this.complete = noop;
62
- this.before_update = noop;
63
- this.after_update = noop;
64
-
65
-
66
- //Code for seeing if a URI is same origin or not borrowed from jQuery
67
- this.is_remote_url = function(url) {
68
- var regexp = /^(\w+:)?\/\/([^\/?#]+)/;
69
- var parts = regexp.exec( url );
70
- return (parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host));
71
- }
72
-
73
- this.update = function() {
74
- // Need to capture because we won't have 'this' inside the ajax
75
- // success handler.
76
- var myself = this;
77
- var dataType = this.is_remote_url( this.umlaut_uri ) ? "jsonp" : "json";
78
- $.ajax({
79
- url: myself.umlaut_uri,
80
- dataType: dataType,
81
- jsonp: "umlaut.jsonp",
82
- error: function() {
83
- $.error("Problem loading background elements.");
84
- },
85
- success: function(umlaut_response) {
86
- for (var i = 0; i < myself.section_targets.length; i++) {
87
- var section_target = myself.section_targets[i];
88
-
89
- var umlaut_html_section = myself.find_umlaut_response_section(umlaut_response, section_target.umlaut_section_id);
90
-
91
- if (umlaut_html_section == undefined) {
92
- continue;
93
- }
94
- var count = null;
95
- if (typeof umlaut_html_section.response_count != "undefined") {
96
- count = parseInt(umlaut_html_section.response_count.value);
97
- }
98
- var existing_element = section_target.ensure_placement_destination();
99
- var new_element = $('<div class="umlaut" style="display:none" class="' + section_target.umlaut_section_id +'"></div>');
100
- new_element.html(umlaut_html_section.html_content);
101
-
102
-
103
- var should_continue = section_target.before_update(new_element, count, section_target);
104
- if (should_continue != false) {
105
- should_continue = myself.before_update(new_element, count, section_target);
106
- }
107
-
108
- if (should_continue != false) {
109
- existing_element.replaceWith(new_element);
110
-
111
- section_target.host_div_element = new_element;
112
-
113
- new_element.show();
114
-
115
- section_target.after_update(new_element, count, section_target);
116
- myself.after_update(new_element, count, section_target);
117
-
118
- }
119
- }
120
-
121
- //Do we need to update again?
122
- if (umlaut_response.partial_html_sections.in_progress) {
123
- //Fix our update URI to be the one umlaut suggests
124
- //Except strip out the umlaut.jsonp parameter, jquery is
125
- //going to add that back in as desired.
126
- myself.umlaut_uri =
127
- umlaut_response.partial_html_sections.in_progress.refresh_url.replace(/[?;&]umlaut\.jsonp=[^;&]+/, '');
128
-
129
-
130
- var refresh_seconds =
131
- umlaut_response.partial_html_sections.in_progress.requested_wait_seconds;
132
- window.setTimeout(function() { myself.update(); }, refresh_seconds * 1000);
133
-
134
- } else {
135
- myself.complete();
136
- for (var i = 0; i < myself.section_targets.length; i++) {
137
- var section_target = myself.section_targets[i];
138
- section_target.complete(section_target);
139
- }
140
- }
141
-
142
- }
143
- });
144
- };
145
- this.find_umlaut_response_section = function(response, id) {
146
- return $.grep(response.partial_html_sections.html_section, function(section) {
147
- return section.id == id;
148
- })[0];
149
- };
150
-
151
- };
152
-
153
- //Put it in a global object, leave space for other things in "Umlaut" later.
154
- if (window.Umlaut == undefined)
155
- window.Umlaut = new Object();
156
- window.Umlaut.HtmlUpdater = HtmlUpdater;
157
-
158
- /* LEGACY Loader was recommended for loading Umlaut JS behaviors
159
- in an external page, for JQuery Content Utility.
160
-
161
- var loader = new Umlaut.Loader();
162
- loader.load();
163
-
164
- We will provide just enough code to keep that from
165
- error'ing (and halting js execution), although at present it does not
166
- actually load the JS behaviors using new style, app wont' have
167
- JS behaviors. */
168
-
169
- window.Umlaut.Loader = function() {
170
- this.load = function(option_list) {
171
- // log problem in browsers that support it.
172
- if (typeof console != "undefined" && typeof console.log != "undefined") {
173
- console.log("WARN: Umlaut.Loader no longer supported in Umlaut 3.x, you may have not loaded Umlaut JS Behaviors as desired. See Umlaut documentation for new way.");
174
-
175
- }
176
- }
177
- }
178
-
179
-
180
- })(jQuery);
181
-
@@ -1,327 +0,0 @@
1
- require 'nokogiri'
2
- require 'open-uri'
3
- require 'base64'
4
- require 'marc'
5
-
6
- # Searches a Blacklight with the cql extension installed.
7
- #
8
- #
9
- # Params include:
10
- # [base_url]
11
- # required. Complete URL to catalog.atom action. Eg "https://blacklight.mse.jhu.edu/catalog.atom"
12
- # [bl_fields]
13
- # required with at least some entries if you want this to do anything. Describe the names of given semantic fields in your BL instance.
14
- # * issn
15
- # * isbn
16
- # * lccn
17
- # * oclcnum
18
- # * id (defaults to 'id')
19
- # * title
20
- # * author
21
- # * serials_limit_clause => not an index name, full URL clause for a limit to apply to known serials searches, for instance "f[format][]=Serial"
22
- # [identifier_search]
23
- # Do catalog search on issn/isbn/oclcnum/lccn/bibId. Default true.
24
- # [keyword_search]
25
- # Do catalog search on title/author keywords where applicable. Generally only used when identifier_search finds no hits, if identifier_search is on. Default true.
26
- # [keyword_per_page]
27
- # How many records to fetch from blacklight when doing keyword searches.
28
- # [exclude_holdings]
29
- # Can be used to exclude certain 'dummy' holdings that have certain collection, location, or other values. Eg:
30
- # exclude_holdings:
31
- # collection_str:
32
- # - World Wide Web
33
- # - Internet
34
- # [rft_id_bibnum_prefixes]
35
- # Array of URI prefixes in an rft_id that indicate that the actual solr id comes next. For instance, if your blacklight will send "http://blacklight.com/catalog/some_id" in an rft_id, then include "http://blacklight.com/catalog/". Optional.
36
- class Blacklight < Service
37
- required_config_params :base_url, :display_name
38
- attr_reader :base_url, :cql_search_field
39
- attr_reader :bl_fields, :issn
40
-
41
- include UmlautHttp
42
- include MetadataHelper
43
- include MarcHelper
44
- include XmlSchemaHelper
45
-
46
- def initialize(config)
47
- # defaults
48
- # If you are sending an OpenURL from a library service, you may
49
- # have the HIP bibnum, and include it in the OpenURL as, eg.
50
- # rft_id=http://catalog.library.jhu.edu/bib/343434 (except URL-encoded)
51
- # Then you'd set rft_id_bibnum_prefix to http://catalog.library.jhu.edu/bib/
52
- @rft_id_bibnum_prefixes = []
53
- @cql_search_field = "cql"
54
- @keyword_per_page = 10
55
- @identifier_search = true
56
- @keyword_search = true
57
- @link_to_search = true
58
- super(config)
59
- @bl_fields = { "id" => "id "}.merge(@bl_fields)
60
- end
61
-
62
- # Standard method, used by background service updater. See Service docs.
63
- def service_types_generated
64
- types = [ ServiceTypeValue[:fulltext], ServiceTypeValue[:holding], ServiceTypeValue[:table_of_contents], ServiceTypeValue[:relevant_link] ]
65
-
66
- return types
67
- end
68
-
69
-
70
- def handle(request)
71
- ids_processed = []
72
- holdings_added = 0
73
-
74
- if (@identifier_search && url = blacklight_precise_search_url(request) )
75
- doc = Nokogiri::XML( http_fetch(url).body )
76
-
77
- ids_processed.concat( bib_ids_from_atom_entries( doc.xpath("atom:feed/atom:entry", xml_ns) ) )
78
-
79
- # namespaces make xpath harder than it should be, but css
80
- # selector still easy, thanks nokogiri! Grab the marc from our
81
- # results.
82
- marc_matches = doc.xpath("atom:feed/atom:entry/atom:content[@type='application/marc']", xml_ns).collect do |encoded_marc21|
83
- MARC::Reader.decode( Base64.decode64(encoded_marc21.text) )
84
- end
85
-
86
- add_856_links(request, marc_matches )
87
-
88
- # Got to make a second fetch for dlf_expanded info, cause BL doens't
89
- # (yet) let us ask for more than one at once
90
- holdings_url = blacklight_precise_search_url( request, "dlf_expanded" )
91
- holdings_added += add_holdings( holdings_url ) if holdings_url
92
- end
93
- #keyword search.
94
- if (@keyword_search &&
95
- url = blacklight_keyword_search_url(request))
96
-
97
- doc = Nokogiri::XML( http_fetch(url).body )
98
- # filter out matches whose titles don't really match at all, or
99
- # which have already been seen in identifier search.
100
- entries = filter_keyword_entries( doc.xpath("atom:feed/atom:entry", xml_ns) , :exclude_ids => ids_processed, :remove_subtitle => (! title_is_serial?(request.referent)) )
101
-
102
- marc_by_atom_id = {}
103
-
104
- # Grab the marc from our entries. Important not to do a // xpath
105
- # search, or we'll wind up matching parent elements not actually
106
- # included in our 'entries' list.
107
- marc_matches = entries.xpath("atom:content[@type='application/marc']", xml_ns).collect do |encoded_marc21|
108
- marc = MARC::Reader.decode( Base64.decode64(encoded_marc21.text) )
109
-
110
- marc_by_atom_id[ encoded_marc21.at_xpath("ancestor::atom:entry/atom:id/text()", xml_ns).to_s ] = marc
111
-
112
- marc
113
- end
114
-
115
- # We've filtered out those we consider just plain bad
116
- # matches, everything else we're going to call
117
- # an approximate match. Sort so that those with
118
- # a date close to our request date are first.
119
- if ( year = get_year(request.referent))
120
- marc_matches = marc_matches.partition {|marc| get_years(marc).include?( year )}.flatten
121
- end
122
- # And add in the 856's
123
- add_856_links(request, marc_matches, :match_reliability => ServiceResponse::MatchUnsure)
124
-
125
- # Fetch and add in the holdings
126
- url = blacklight_url_for_ids(bib_ids_from_atom_entries(entries))
127
-
128
- holdings_added += add_holdings( url, :match_reliability => ServiceResponse::MatchUnsure, :marc_data => marc_by_atom_id ) if url
129
-
130
- if (@link_to_search && holdings_added ==0)
131
- hit_count = doc.at_xpath("atom:feed/opensearch:totalResults/text()", xml_ns).to_s.to_i
132
- html_result_url = doc.at_xpath("atom:feed/atom:link[@rel='alternate'][@type='text/html']/attribute::href", xml_ns).to_s
133
-
134
- if hit_count > 0
135
- request.add_service_response(
136
- :service => self,
137
- :source_name => @display_name,
138
- :count => hit_count,
139
- :display_text => "#{hit_count} possible #{case; when hit_count > 1 ; 'matches' ; else; 'match' ; end} in #{@display_name}",
140
- :url => html_result_url,
141
- :service_type_value => :holding_search )
142
- end
143
- end
144
- end
145
-
146
-
147
-
148
-
149
- return request.dispatched(self, true)
150
-
151
-
152
- end
153
-
154
- # Send a CQL request for any identifiers present.
155
- # Ask for for an atom response with embedded marc21 back.
156
- def blacklight_precise_search_url(request, format = "marc")
157
- # Add search clauses for our identifiers, if we have them and have a configured search field for them.
158
- clauses = []
159
- added = []
160
- ["lccn", "isbn", "oclcnum"].each do |key|
161
- if bl_fields[key] && request.referent.send(key)
162
- clauses.push( "#{bl_fields[key]} = \"#{request.referent.send(key)}\"")
163
- added << key
164
- end
165
- end
166
- # Only add ISSN if we don't have an ISBN, reduces false matches
167
- if ( !added.include?("isbn") &&
168
- bl_fields["issn"] &&
169
- request.referent.issn)
170
- clauses.push("#{bl_fields["issn"]} = \"#{request.referent.issn}\"")
171
- end
172
-
173
-
174
- # Add Solr document identifier if we can get one from the URL
175
-
176
- if (id = get_solr_id(request.referent))
177
- clauses.push("#{bl_fields['id']} = \"#{id}\"")
178
- end
179
-
180
- # if we have nothing, we can do no search.
181
- return nil if clauses.length == 0
182
-
183
- cql = clauses.join(" OR ")
184
-
185
- return base_url + "?search_field=#{@cql_search_field}&content_format=#{format}&q=#{CGI.escape(cql)}"
186
- end
187
-
188
- # Construct a CQL search against blacklight for author and title,
189
- # possibly with serial limit. Ask for Atom with embedded MARC back.
190
- def blacklight_keyword_search_url(request, options = {})
191
- options[:format] ||= "atom"
192
- options[:content_format] ||= "marc"
193
-
194
- clauses = []
195
-
196
- # We need both title and author to search keyword style, or
197
- # we get too many false positives. Except serials we'll do
198
- # title only. sigh, logic tree.
199
- title = get_search_title(request.referent)
200
- author = get_top_level_creator(request.referent)
201
- return nil unless title && (author || (@bl_fields["serials_limit_clause"] && title_is_serial?(request.referent)))
202
- # phrase search for title, just raw dismax for author
203
- # Embed quotes inside the quoted value, need to backslash-quote for CQL,
204
- # and backslash the backslashes for ruby literal.
205
- clauses.push("#{@bl_fields["title"]} = \"\\\"#{title}\\\"\"")
206
- clauses.push("#{@bl_fields["author"]} = \"#{author}\"") if author
207
-
208
-
209
-
210
- url = base_url + "?search_field=#{@cql_search_field}&content_format=#{options[:content_format]}&q=#{CGI.escape(clauses.join(" AND "))}"
211
-
212
- if (@bl_fields["serials_limit_clause"] &&
213
- title_is_serial?(request.referent))
214
- url += "&" + @bl_fields["serials_limit_clause"]
215
- end
216
-
217
- return url
218
- end
219
-
220
- # Takes a url that will return atom response of dlf_expanded content.
221
- # Adds Umlaut "holding" ServiceResponses for dlf_expanded, as appropriate.
222
- # Returns number of holdings added.
223
- def add_holdings(holdings_url, options = {})
224
- options[:match_reliability] ||= ServiceResponse::MatchExact
225
- options[:marc_data] ||= {}
226
-
227
- atom = Nokogiri::XML( http_fetch(holdings_url).body )
228
- content_entries = atom.search("/atom:feed/atom:entry/atom:content", xml_ns)
229
-
230
- # For each atom entry, find the dlf_expanded record. For each dlf_expanded
231
- # record, take all of it's holdingsrec's if it has them, or all of it's
232
- # items if it doesn't, and add them to list. We wind up with a list
233
- # of mixed holdingsrec's and items.
234
- holdings_xml = content_entries.collect do |dlf_expanded|
235
- copies = dlf_expanded.xpath("dlf:record/dlf:holdings/dlf:holdingset/dlf:holdingsrec", xml_ns)
236
- copies.length > 0 ? copies : dlf_expanded.xpath("dlf:record/dlf:items/dlf:item", xml_ns)
237
- end.flatten
238
-
239
- service_data = holdings_xml.collect do | xml_metadata |
240
- atom_entry = xml_metadata.at_xpath("ancestor::atom:entry", xml_ns)
241
- atom_id = atom_entry.at_xpath("atom:id/text()", xml_ns).to_s
242
-
243
- edition_str = edition_statement(options[:marc_data][atom_id])
244
- url = atom_entry.at_xpath("atom:link[@rel='alternate'][@type='text/html']/attribute::href", xml_ns).to_s
245
-
246
- xml_to_holdings( xml_metadata ).merge(
247
- :service => self,
248
- :match_reliability => options[:match_reliability],
249
- :edition_str => edition_str,
250
- :url => url
251
- )
252
- end
253
-
254
- # strip out holdings that aren't really holdings
255
- service_data.delete_if do |data|
256
- @exclude_holdings.collect do |key, values|
257
- values.include?(data[key.to_sym])
258
- end.include?(true)
259
- end
260
-
261
- # Sort by "collection"
262
- service_data.sort do |a, b|
263
- a[:collection_str] <=> b[:collection_str]
264
- end
265
-
266
- service_data.each do |data|
267
- request.add_service_response(data.merge(:service => self, :service_type_value =>"holding"))
268
- end
269
-
270
- return service_data.length
271
- end
272
-
273
- def filter_keyword_entries(atom_entries, options = {})
274
- options[:exclude_ids] ||= []
275
- options[:remove_subtitle] ||= true
276
- request_title_forms = [
277
- raw_search_title(request.referent).downcase,
278
- normalize_title( raw_search_title(request.referent) )
279
- ]
280
- request_title_forms << normalize_title( raw_search_title(request.referent), :remove_subtitle => true) if options[:remove_subtitle]
281
- request_title_forms.compact
282
-
283
- # Only keep entries with title match, and that aren't in the
284
- # exclude_ids list.
285
- good_entries = atom_entries.find_all do |atom_entry|
286
- title = atom_entry.xpath("atom:title/text()", xml_ns).to_s
287
-
288
- entry_title_forms = [
289
- title.downcase,
290
- normalize_title(title)
291
- ]
292
- entry_title_forms << normalize_title(title, :remove_subtitle=>true) if options[:remove_subtitle]
293
- entry_title_forms.compact
294
-
295
- ((entry_title_forms & request_title_forms).length > 0 &&
296
- (bib_ids_from_atom_entries(atom_entry) & options[:exclude_ids]).length == 0)
297
- end
298
- return Nokogiri::XML::NodeSet.new( atom_entries.document, good_entries)
299
- end
300
-
301
- def bib_ids_from_atom_entries(entries)
302
- entries.xpath("atom:id/text()", xml_ns).to_a.collect do |atom_id|
303
- atom_id.to_s =~ /([^\/]+)$/
304
- $1
305
- end.compact
306
- end
307
-
308
- def blacklight_url_for_ids(ids, format="dlf_expanded")
309
- return nil unless ids.length > 0
310
-
311
- return base_url + "?search_field=#{@cql_search_field}&content_format=#{format}&q=" + CGI.escape("#{@bl_fields["id"]} any \"#{ids.join(" ")}\"")
312
- end
313
-
314
-
315
- def get_solr_id(rft)
316
- rft.identifiers.each do |id|
317
- @rft_id_bibnum_prefixes.each do |prefix|
318
- if id[0, prefix.length] == prefix
319
- return id[prefix.length, id.length]
320
- end
321
- end
322
- end
323
-
324
- return nil
325
- end
326
-
327
- end
@@ -1,164 +0,0 @@
1
- # -*- encoding : utf-8 -*-
2
- module Umlaut
3
- # Class to inject Umlaut routes, design copied from Blacklight project.
4
- # you would do a:
5
- # Umlaut::Routes.new(self, optional_args).draw
6
- # in local
7
- # app routes.rb, that line is generated into local app by Umlaut generator.
8
- # options include :only and :except to limit what route groups are generated.
9
- class Routes
10
-
11
- def initialize(router, options ={})
12
- @router = router
13
- @options = options
14
- end
15
-
16
- def draw
17
- route_sets.each do |r|
18
- self.send(r)
19
- end
20
- end
21
-
22
- protected
23
-
24
- def add_routes &blk
25
- @router.instance_exec(@options, &blk)
26
- end
27
-
28
- def route_sets
29
- # :admin is not included by default, needs to be turned on.
30
- (@options[:only] || default_route_sets) - (@options[:except] || []) + (@options[:admin] == true ? [:admin] : [])
31
- end
32
-
33
- def default_route_sets
34
- [:root, :permalinks, :a_z, :resolve, :open_search, :link_router, :export_email, :resources, :search, :javascript]
35
- end
36
-
37
- module RouteSets
38
- # for now include root generation in Umlaut auto-generation
39
- def root
40
- add_routes do |options|
41
- root :to => "search#index"
42
- end
43
- end
44
-
45
- def permalinks
46
- add_routes do |options|
47
- match 'go/:id' => 'store#index'
48
- end
49
- end
50
-
51
- # some special direct links to A-Z type searches, including
52
- # legacy redirects for SFX-style urls, to catch any bookmarks.
53
- def a_z
54
- add_routes do |options|
55
- # Special one for alpha list
56
- match 'journal_list/:id/:page' => 'search#journal_list', :defaults => { :page => 1, :id => 'A' }
57
-
58
-
59
- # Catch redirected from SFX A-Z and citation linker urls
60
- # v2 A-Z links redirected to umlaut, point to journal_list
61
- # code in journal_list filter picks out SFX URL vars for
62
- # letter.
63
- match '/resolve/azlist/default' => 'search#journal_list', :page => 1, :id => 'A'
64
-
65
- # SFX v3 A-Z list url format
66
- match 'resolve/az' => 'search#journal_list', :page => 1, :id => 'A'
67
- end
68
- end
69
-
70
- # This is a legacy wild controller route that's not recommended for RESTful applications.
71
- # Note: This route will make all actions in every controller accessible via GET requests.
72
- # match ':controller(/:action(/:id(.:format)))'
73
-
74
- def resolve
75
- add_routes do |options|
76
- # ResolveController still uses rails 2.0 style 'wildcard' routes,
77
- # TODO tighten this up to only match what oughta be matched.
78
- # Note: This route will make all actions in this controller accessible via GET requests.
79
-
80
- match 'resolve(/:action(/:id(.:format)))' => "resolve"
81
- end
82
- end
83
-
84
- def open_search
85
- add_routes do |options|
86
- # OpenSearchController still uses rails 2.0 style 'wildcard' routes,
87
- # TODO tighten this up to only match what oughta be matched.
88
- # Note: This route will make all actions in this controller accessible via GET requests.
89
-
90
- match 'open_search(/:action(/:id(.:format)))' => "open_search"
91
- end
92
- end
93
-
94
- def link_router
95
- add_routes do |options|
96
- # LinkRouterController still uses rails 2.0 style 'wildcard' routes,
97
- # TODO tighten this up to only match what oughta be matched.
98
- # Note: This route will make all actions in this controller accessible via GET requests.
99
-
100
- match 'link_router(/:action(/:id(.:format)))' => "link_router"
101
- end
102
- end
103
-
104
- def export_email
105
- add_routes do |options|
106
- # ExportEmailController still uses rails 2.0 style 'wildcard' routes,
107
- # TODO tighten this up to only match what oughta be matched.
108
- # Note: This route will make all actions in this controller accessible via GET requests.
109
-
110
- match 'export_email(/:action(/:id(.:format)))' => "export_email"
111
- end
112
- end
113
-
114
- def resources
115
- add_routes do |options|
116
- # ResourceController still uses rails 2.0 style 'wildcard' routes,
117
- # TODO tighten this up to only match what oughta be matched.
118
- # Note: This route will make all actions in this controller accessible via GET requests.
119
-
120
- match 'resource(/:action(/:id(.:format)))' => "resource"
121
- end
122
- end
123
-
124
- def search
125
- add_routes do |options|
126
- # SearchController still uses rails 2.0 style 'wildcard' routes,
127
- # TODO tighten this up to only match what oughta be matched.
128
- # Note: This route will make all actions in this controller accessible via GET requests.
129
-
130
- match 'search(/:action(/:id(.:format)))' => "search"
131
- end
132
- end
133
-
134
- def javascript
135
- add_routes do |options|
136
- # Legacy location for update_html.js used by JQuery Content Utility
137
- # to embed JS on external sites. Redirect to new location.
138
- # Intentionally non-fingerprinted, most efficient thing
139
- # we can do in this case is let the web server take care
140
- # of Last-modified-by etc headers.
141
- match 'javascripts/jquery/umlaut/update_html.js' => redirect("/assets/umlaut/update_html.js", :status => 301)
142
-
143
- # The loader doens't work _exactly_ like the new umlaut-ui.js, but
144
- # it's close enough that it'll work better redirecting than just
145
- # 404'ing.
146
- match 'js_helper/loader' => redirect("/assets/umlaut_ui.js")
147
-
148
-
149
- match 'images/spinner.gif' => redirect("/assets/spinner.gif")
150
- end
151
- end
152
-
153
- def admin
154
- add_routes do |options|
155
- namespace "admin" do
156
- match 'service_errors(/:service_id)' => "service_errors#index"
157
- end
158
- end
159
- end
160
-
161
- end
162
- include RouteSets
163
- end
164
- end