umlaut 3.0.0alpha1

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 (293) hide show
  1. data/LICENSE +7 -0
  2. data/README.md +49 -0
  3. data/Rakefile +37 -0
  4. data/app/assets/images/error.gif +0 -0
  5. data/app/assets/images/export_bg_bot.gif +0 -0
  6. data/app/assets/images/export_bg_mid.gif +0 -0
  7. data/app/assets/images/export_bg_top.gif +0 -0
  8. data/app/assets/images/famfamfam/book_open.png +0 -0
  9. data/app/assets/images/famfamfam/cross.png +0 -0
  10. data/app/assets/images/famfamfam/page_sound.gif +0 -0
  11. data/app/assets/images/famfamfam/page_text.gif +0 -0
  12. data/app/assets/images/famfamfam/page_up.gif +0 -0
  13. data/app/assets/images/famfamfam/page_white.png +0 -0
  14. data/app/assets/images/famfamfam/readme.html +1495 -0
  15. data/app/assets/images/famfamfam/tiny_cross.png +0 -0
  16. data/app/assets/images/frame_remove.gif +0 -0
  17. data/app/assets/images/ico_go.gif +0 -0
  18. data/app/assets/images/jhu_findit.gif +0 -0
  19. data/app/assets/images/list_closed.png +0 -0
  20. data/app/assets/images/list_open.png +0 -0
  21. data/app/assets/images/more_info.gif +0 -0
  22. data/app/assets/images/rails.png +0 -0
  23. data/app/assets/images/request.gif +0 -0
  24. data/app/assets/images/spinner.gif +0 -0
  25. data/app/assets/javascripts/umlaut/ajax_windows.js +35 -0
  26. data/app/assets/javascripts/umlaut/ensure_window_size.js.erb +34 -0
  27. data/app/assets/javascripts/umlaut/expand_contract_toggle.js +25 -0
  28. data/app/assets/javascripts/umlaut/search_autocomplete.js +46 -0
  29. data/app/assets/javascripts/umlaut/simple_visible_toggle.js +8 -0
  30. data/app/assets/javascripts/umlaut/update_html.js +152 -0
  31. data/app/assets/javascripts/umlaut.js +17 -0
  32. data/app/assets/stylesheets/umlaut.css +857 -0
  33. data/app/controllers/application_controller.rb +14 -0
  34. data/app/controllers/export_email_controller.rb +123 -0
  35. data/app/controllers/js_helper_controller.rb +10 -0
  36. data/app/controllers/link_router_controller.rb +87 -0
  37. data/app/controllers/open_search_controller.rb +9 -0
  38. data/app/controllers/resolve_controller.rb +288 -0
  39. data/app/controllers/resource_controller.rb +83 -0
  40. data/app/controllers/search_controller.rb +328 -0
  41. data/app/controllers/search_methods/sfx3.rb +148 -0
  42. data/app/controllers/search_methods/sfx4.rb +257 -0
  43. data/app/controllers/search_methods/sfx_api.rb +47 -0
  44. data/app/controllers/store_controller.rb +64 -0
  45. data/app/controllers/umlaut/controller_behavior.rb +20 -0
  46. data/app/controllers/umlaut/controller_logic.rb +96 -0
  47. data/app/controllers/umlaut/error_handling.rb +48 -0
  48. data/app/controllers/umlaut_controller.rb +112 -0
  49. data/app/helpers/application_helper.rb +4 -0
  50. data/app/helpers/emailer_helper.rb +43 -0
  51. data/app/helpers/export_email_helper.rb +34 -0
  52. data/app/helpers/open_search_helper.rb +7 -0
  53. data/app/helpers/resolve_helper.rb +225 -0
  54. data/app/helpers/search_helper.rb +50 -0
  55. data/app/helpers/umlaut/footer_helper.rb +64 -0
  56. data/app/helpers/umlaut/helper.rb +62 -0
  57. data/app/helpers/umlaut/html_head_helper.rb +37 -0
  58. data/app/helpers/umlaut/url_generation.rb +77 -0
  59. data/app/mailers/emailer.rb +48 -0
  60. data/app/models/clickthrough.rb +2 -0
  61. data/app/models/collection.rb +259 -0
  62. data/app/models/crossref_lookup.rb +2 -0
  63. data/app/models/dispatched_service.rb +58 -0
  64. data/app/models/permalink.rb +29 -0
  65. data/app/models/referent.rb +473 -0
  66. data/app/models/referent_value.rb +14 -0
  67. data/app/models/request.rb +449 -0
  68. data/app/models/service_response.rb +179 -0
  69. data/app/models/service_store.rb +59 -0
  70. data/app/models/service_type_value.rb +58 -0
  71. data/app/models/service_wave.rb +150 -0
  72. data/app/models/sfx_db/az_additional_title.rb +11 -0
  73. data/app/models/sfx_db/az_letter_group.rb +11 -0
  74. data/app/models/sfx_db/az_title.rb +38 -0
  75. data/app/models/sfx_db/az_title_v2.rb +34 -0
  76. data/app/models/sfx_db/isbn.rb +12 -0
  77. data/app/models/sfx_db/issn.rb +12 -0
  78. data/app/models/sfx_db/object.rb +35 -0
  79. data/app/models/sfx_db/object_portfolio.rb +6 -0
  80. data/app/models/sfx_db/publisher.rb +10 -0
  81. data/app/models/sfx_db/sfx_db_base.rb +54 -0
  82. data/app/models/sfx_db/target.rb +9 -0
  83. data/app/models/sfx_db/target_service.rb +10 -0
  84. data/app/models/sfx_db/title.rb +10 -0
  85. data/app/models/sfx_db.rb +10 -0
  86. data/app/models/sfx_url.rb +35 -0
  87. data/app/views/emailer/citation.text.erb +28 -0
  88. data/app/views/emailer/short_citation.text.erb +8 -0
  89. data/app/views/export_email/_email.html.erb +25 -0
  90. data/app/views/export_email/_send_email.html.erb +3 -0
  91. data/app/views/export_email/_send_txt.html.erb +3 -0
  92. data/app/views/export_email/_txt.html.erb +62 -0
  93. data/app/views/export_email/email.html.erb +3 -0
  94. data/app/views/export_email/send_email.html.erb +1 -0
  95. data/app/views/export_email/send_txt.html.erb +1 -0
  96. data/app/views/export_email/txt.html.erb +3 -0
  97. data/app/views/js_helper/loader.erb.js +13 -0
  98. data/app/views/layouts/umlaut.html.erb +52 -0
  99. data/app/views/open_search/index.html.erb +9 -0
  100. data/app/views/resolve/_api_in_progress.xml.erb +21 -0
  101. data/app/views/resolve/_background_progress.html.erb +51 -0
  102. data/app/views/resolve/_background_updater.html.erb +38 -0
  103. data/app/views/resolve/_citation.html.erb +87 -0
  104. data/app/views/resolve/_coins.html.erb +1 -0
  105. data/app/views/resolve/_compact_citation.html.erb +33 -0
  106. data/app/views/resolve/_cover_image.html.erb +35 -0
  107. data/app/views/resolve/_fulltext.html.erb +55 -0
  108. data/app/views/resolve/_help.html.erb +17 -0
  109. data/app/views/resolve/_holding.html.erb +91 -0
  110. data/app/views/resolve/_related_items.html.erb +35 -0
  111. data/app/views/resolve/_search_inside.html.erb +62 -0
  112. data/app/views/resolve/_section_display.html.erb +49 -0
  113. data/app/views/resolve/_service_errors.html.erb +29 -0
  114. data/app/views/resolve/_standard_response_item.html.erb +89 -0
  115. data/app/views/resolve/api.xml.builder +72 -0
  116. data/app/views/resolve/background_status.html.erb +26 -0
  117. data/app/views/resolve/index.html.erb +73 -0
  118. data/app/views/resolve/partial_html_sections.xml.erb +30 -0
  119. data/app/views/search/_a_to_z.html.erb +6 -0
  120. data/app/views/search/_citation.html.erb +94 -0
  121. data/app/views/search/_pager.html.erb +60 -0
  122. data/app/views/search/books.html.erb +103 -0
  123. data/app/views/search/journal_search.html.erb +90 -0
  124. data/app/views/search/journals.html.erb +167 -0
  125. data/app/views/search/opensearch_description.rxml +10 -0
  126. data/app/views/testing/index.html.erb +1 -0
  127. data/app/views/umlaut/README +5 -0
  128. data/app/views/umlaut/error.html.erb +45 -0
  129. data/db/migrate/01_umlaut_init.rb +113 -0
  130. data/db/orig_fixed_data/service_type_values.yml +120 -0
  131. data/db/seeds.rb +7 -0
  132. data/lib/CronTab.rb +192 -0
  133. data/lib/aws_product_sign.rb +146 -0
  134. data/lib/exlibris/aleph/patron.rb +64 -0
  135. data/lib/exlibris/aleph/record.rb +54 -0
  136. data/lib/exlibris/aleph/rest_api.rb +29 -0
  137. data/lib/exlibris/primo/holding.rb +192 -0
  138. data/lib/exlibris/primo/rsrc.rb +17 -0
  139. data/lib/exlibris/primo/searcher.rb +276 -0
  140. data/lib/exlibris/primo/source/aleph.rb +46 -0
  141. data/lib/exlibris/primo/source/distribution/nyu_aleph.rb +323 -0
  142. data/lib/exlibris/primo/toc.rb +17 -0
  143. data/lib/exlibris/primo_ws.rb +140 -0
  144. data/lib/generators/templates/umlaut_services.yml +237 -0
  145. data/lib/generators/umlaut/asset_hooks_generator.rb +44 -0
  146. data/lib/generators/umlaut/install_generator.rb +110 -0
  147. data/lib/hip3/bib.rb +291 -0
  148. data/lib/hip3/bib_searcher.rb +302 -0
  149. data/lib/hip3/custom_field_lookup.rb +44 -0
  150. data/lib/hip3/holding.rb +50 -0
  151. data/lib/hip3/item.rb +65 -0
  152. data/lib/hip3/receipt.rb +7 -0
  153. data/lib/hip3/serial_copy.rb +82 -0
  154. data/lib/holding.rb +32 -0
  155. data/lib/marc_helper.rb +254 -0
  156. data/lib/metadata_helper.rb +312 -0
  157. data/lib/opensearch_feed.rb +398 -0
  158. data/lib/opensearch_query.rb +98 -0
  159. data/lib/referent_filter.rb +16 -0
  160. data/lib/referent_filters/dissertation_catch.rb +45 -0
  161. data/lib/section_renderer.rb +503 -0
  162. data/lib/service.rb +336 -0
  163. data/lib/service_adaptors/ajax_export.rb +37 -0
  164. data/lib/service_adaptors/amazon.rb +412 -0
  165. data/lib/service_adaptors/blacklight.rb +327 -0
  166. data/lib/service_adaptors/book_finder.rb +40 -0
  167. data/lib/service_adaptors/bx.rb +51 -0
  168. data/lib/service_adaptors/cover_thing.rb +73 -0
  169. data/lib/service_adaptors/elsevier_cover.rb +57 -0
  170. data/lib/service_adaptors/email_export.rb +10 -0
  171. data/lib/service_adaptors/ezproxy.rb +171 -0
  172. data/lib/service_adaptors/google_book_search.rb +442 -0
  173. data/lib/service_adaptors/gpo.rb +124 -0
  174. data/lib/service_adaptors/hathi_trust.rb +308 -0
  175. data/lib/service_adaptors/hip3_service.rb +150 -0
  176. data/lib/service_adaptors/hip_holding_search.rb +237 -0
  177. data/lib/service_adaptors/internet_archive.rb +488 -0
  178. data/lib/service_adaptors/isbn_db.rb +86 -0
  179. data/lib/service_adaptors/isi.rb +258 -0
  180. data/lib/service_adaptors/jcr.rb +146 -0
  181. data/lib/service_adaptors/opac.rb +351 -0
  182. data/lib/service_adaptors/open_library.rb +316 -0
  183. data/lib/service_adaptors/open_library_cover.rb +73 -0
  184. data/lib/service_adaptors/primo_service.rb +392 -0
  185. data/lib/service_adaptors/primo_source.rb +78 -0
  186. data/lib/service_adaptors/pubmed.rb +133 -0
  187. data/lib/service_adaptors/request_to_fixture.rb +68 -0
  188. data/lib/service_adaptors/scopus.rb +295 -0
  189. data/lib/service_adaptors/sfx-new.rb +557 -0
  190. data/lib/service_adaptors/sfx.rb +566 -0
  191. data/lib/service_adaptors/sfx_backchannel_record.rb +69 -0
  192. data/lib/service_adaptors/txt_holding_export.rb +32 -0
  193. data/lib/service_adaptors/ulrichs_cover.rb +57 -0
  194. data/lib/service_adaptors/ulrichs_link.rb +47 -0
  195. data/lib/service_adaptors/worldcat.rb +116 -0
  196. data/lib/service_adaptors/worldcat_identities.rb +591 -0
  197. data/lib/tasks/umlaut.rake +134 -0
  198. data/lib/umlaut/default_configuration.rb +5 -0
  199. data/lib/umlaut/routes.rb +136 -0
  200. data/lib/umlaut/version.rb +3 -0
  201. data/lib/umlaut.rb +37 -0
  202. data/lib/umlaut_configurable.rb +343 -0
  203. data/lib/umlaut_http.rb +100 -0
  204. data/lib/xml_schema_helper.rb +109 -0
  205. data/test/dummy/Rakefile +7 -0
  206. data/test/dummy/app/assets/javascripts/application.js +13 -0
  207. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  208. data/test/dummy/app/controllers/application_controller.rb +3 -0
  209. data/test/dummy/app/controllers/umlaut_controller.rb +112 -0
  210. data/test/dummy/app/helpers/application_helper.rb +2 -0
  211. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  212. data/test/dummy/config/application.rb +45 -0
  213. data/test/dummy/config/boot.rb +10 -0
  214. data/test/dummy/config/database-jhu.yml +44 -0
  215. data/test/dummy/config/database.yml +25 -0
  216. data/test/dummy/config/environment.rb +5 -0
  217. data/test/dummy/config/environments/development.rb +34 -0
  218. data/test/dummy/config/environments/production.rb +60 -0
  219. data/test/dummy/config/environments/test.rb +39 -0
  220. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  221. data/test/dummy/config/initializers/inflections.rb +10 -0
  222. data/test/dummy/config/initializers/mime_types.rb +5 -0
  223. data/test/dummy/config/initializers/secret_token.rb +7 -0
  224. data/test/dummy/config/initializers/session_store.rb +8 -0
  225. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  226. data/test/dummy/config/locales/en.yml +5 -0
  227. data/test/dummy/config/routes.rb +61 -0
  228. data/test/dummy/config/umlaut_services.yml +237 -0
  229. data/test/dummy/config.ru +4 -0
  230. data/test/dummy/db/migrate/20111228211210_umlaut_init.rb +113 -0
  231. data/test/dummy/db/schema.rb +124 -0
  232. data/test/dummy/log/development.log +12981 -0
  233. data/test/dummy/log/production.log +0 -0
  234. data/test/dummy/public/404.html +26 -0
  235. data/test/dummy/public/422.html +26 -0
  236. data/test/dummy/public/500.html +26 -0
  237. data/test/dummy/public/favicon.ico +0 -0
  238. data/test/dummy/script/rails +6 -0
  239. data/test/dummy/tmp/cache/assets/C5F/340/sprockets%2F99692920160b7a279b86a80415b79db7 +0 -0
  240. data/test/dummy/tmp/cache/assets/C70/4D0/sprockets%2F034ad2036e623081bd352800786dfe80 +0 -0
  241. data/test/dummy/tmp/cache/assets/C73/920/sprockets%2Fd371318f22900492fd180f17c5e2a504 +9268 -0
  242. data/test/dummy/tmp/cache/assets/C80/980/sprockets%2Fc94807409c1523d43e18d25f35d93c41 +0 -0
  243. data/test/dummy/tmp/cache/assets/C8F/780/sprockets%2Fe47e28558116fb5f8038754e60d1961d +11769 -0
  244. data/test/dummy/tmp/cache/assets/CAA/EB0/sprockets%2F1d179210e8b76f1ea63c802688a015e4 +9271 -0
  245. data/test/dummy/tmp/cache/assets/CBB/9C0/sprockets%2F706f28923fb754cad04b9107c89986a1 +0 -0
  246. data/test/dummy/tmp/cache/assets/CBF/B60/sprockets%2F08ca89671549936265dcb673bf02e36f +0 -0
  247. data/test/dummy/tmp/cache/assets/CC9/9F0/sprockets%2F306166316e2cafd13c15e62b51a2339d +0 -0
  248. data/test/dummy/tmp/cache/assets/CF6/F20/sprockets%2F5b2ffa1103079dfd555197838f87a99f +0 -0
  249. data/test/dummy/tmp/cache/assets/CF7/2B0/sprockets%2F25a7c73655bd3598173b39d9f98bcd46 +862 -0
  250. data/test/dummy/tmp/cache/assets/CFE/080/sprockets%2F37fe9f4255baddbd549a659914929398 +0 -0
  251. data/test/dummy/tmp/cache/assets/D22/060/sprockets%2F9aec77b768e91a802d284271c58e2f7e +21357 -0
  252. data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  253. data/test/dummy/tmp/cache/assets/D33/6D0/sprockets%2F500129c57f1146e556ec3aacd6cd38c1 +0 -0
  254. data/test/dummy/tmp/cache/assets/D33/FD0/sprockets%2F2ba0b4e6334a77b923e5f770381bb2bf +0 -0
  255. data/test/dummy/tmp/cache/assets/D42/C20/sprockets%2Fbcf14e437b1582bf93b77670acf8e090 +21353 -0
  256. data/test/dummy/tmp/cache/assets/D50/A30/sprockets%2F7d8b294ac433db5d056538f8cf7c66b9 +0 -0
  257. data/test/dummy/tmp/cache/assets/D54/ED0/sprockets%2F71c9fa01091d432b131da3bb73faf3d4 +872 -0
  258. data/test/dummy/tmp/cache/assets/D65/590/sprockets%2Fc1bb92fc3406a126b7dd302edc96d629 +0 -0
  259. data/test/dummy/tmp/cache/assets/D71/6B0/sprockets%2Fde558b71b494cf09b1bf055c8dff0353 +0 -0
  260. data/test/dummy/tmp/cache/assets/D72/610/sprockets%2Fa8c708eeb30ef93de34d755d4f45d023 +859 -0
  261. data/test/dummy/tmp/cache/assets/D76/AD0/sprockets%2Fe2158cde93188cf5ab6457bc6d6602ec +0 -0
  262. data/test/dummy/tmp/cache/assets/D7A/E40/sprockets%2F9622ffcc499a57627cd1bb18fe31b8e4 +11772 -0
  263. data/test/dummy/tmp/cache/assets/D84/210/sprockets%2Fabd0103ccec2b428ac62c94e4c40b384 +0 -0
  264. data/test/dummy/tmp/cache/assets/D9B/770/sprockets%2F8aacf02eb7dbb0949704b28f27b87e0b +0 -0
  265. data/test/dummy/tmp/cache/assets/DA6/A80/sprockets%2F92e26d8e58d5bcc8b8f6c25d1b05b9c1 +0 -0
  266. data/test/dummy/tmp/cache/assets/DE8/790/sprockets%2Fd1333bde2b9aafcc712d11dd09ab35d8 +0 -0
  267. data/test/dummy/tmp/cache/assets/DF7/F30/sprockets%2F7bc16c4109b17fabe29f8ddbbf732d1c +374 -0
  268. data/test/dummy/tmp/cache/assets/E03/570/sprockets%2F493bdc0ac14cd4f57fdfe4253f992bde +0 -0
  269. data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  270. data/test/dummy/tmp/cache/assets/E0B/4B0/sprockets%2F7988df51a61c81ce6ede4a2d4c8cce4f +377 -0
  271. data/test/dummy/tmp/cache/assets/E5F/960/sprockets%2Fdc007b6cad5c7ef08e33ec28cfff0ef6 +0 -0
  272. data/test/fixtures/dispatched_services.yml +5 -0
  273. data/test/fixtures/permalinks.yml +5 -0
  274. data/test/fixtures/referent_values.yml +1734 -0
  275. data/test/fixtures/referents.yml +156 -0
  276. data/test/fixtures/requests.yml +284 -0
  277. data/test/fixtures/service_responses.yml +5 -0
  278. data/test/fixtures/sfx_urls.yml +4 -0
  279. data/test/performance/browsing_test.rb +9 -0
  280. data/test/test_helper.rb +10 -0
  281. data/test/umlaut_test.rb +7 -0
  282. data/test/unit/aleph_patron_test.rb +39 -0
  283. data/test/unit/aleph_record_benchmarks.rb +28 -0
  284. data/test/unit/aleph_record_test.rb +30 -0
  285. data/test/unit/aws_product_sign_test.rb +93 -0
  286. data/test/unit/collection_test.rb +76 -0
  287. data/test/unit/google_book_search_test.rb +101 -0
  288. data/test/unit/primo_searcher_test.rb +403 -0
  289. data/test/unit/primo_service_test.rb +939 -0
  290. data/test/unit/primo_ws_test.rb +131 -0
  291. data/test/unit/service_response_test.rb +9 -0
  292. data/test/unit/service_test.rb +33 -0
  293. metadata +580 -0
@@ -0,0 +1,83 @@
1
+ # We proxy cover images from foreign sources through Umlaut sometimes, in order
2
+ # to switch their access from http to https to avoid browser warnings on an
3
+ # https page. This controller does that.
4
+ #
5
+ # It does NOT take URL in request parameters, but instead takes a response ID.
6
+ # it will only proxy urls already stored in umlaut responses, so this is not
7
+ # an open proxy with the security problems that would cause.
8
+ class ResourceController < UmlautController
9
+ require 'open-uri'
10
+
11
+ # We really ought to _stream_ the remote response to our client, but I
12
+ # couldn't get that to work how I wanted in Rails2. Even using
13
+ # render :text=>proc, problem is we needed to know the content-type before
14
+ # we read any of the response, which we didn't. This implementation holds
15
+ # the whole image in memory for a second while it delivers it, oh well.
16
+ # doesn't seem to effect speed much, even though it's not optimal.
17
+ def proxy
18
+ svc_type = ServiceType.find(params[:id])
19
+ url_str = svc_type.view_data[:url]
20
+ uri = nil
21
+ begin
22
+ uri = URI(url_str)
23
+ rescue
24
+ raise Exception.new("resource#proxy can only handle String urls, not '#{url.inspect}'")
25
+ end
26
+
27
+ proxied_headers = proxy_headers( request, uri.host )
28
+ remote_response = open(uri, 'rb', proxied_headers)
29
+
30
+ # copy certain headers to our proxied response
31
+ ["content-type", "cache-control", "expires", "content-length", "last-modified", "etag", "date"].each do |key|
32
+ response.headers[key] = remote_response.meta[key]
33
+ end
34
+ response.headers["X-Original-Url"] = url_str
35
+
36
+ # And send the actual result out
37
+ render(:text => remote_response.read)
38
+
39
+ end
40
+
41
+ protected
42
+
43
+ # Generate headers as if we are a proxy server, to be more or less honest
44
+ # and to maybe keep Google et al from rate throttling us.
45
+ # This is kind of copy and paste of UmlautHttp#proxy_like_headers, but that
46
+ # method was written to require an UmlautRequest, which we don't have here.
47
+ # TODO should refactor to DRY.
48
+ # Argument here is a Rails Request
49
+ def proxy_headers( request, host )
50
+ orig_env = request.headers
51
+
52
+ header = {}
53
+
54
+
55
+ header["User-Agent"] = orig_env['HTTP_USER_AGENT'] || 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0'
56
+ header['Accept'] = orig_env['HTTP_ACCEPT'] || 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
57
+ header['Accept-Language'] = orig_env['HTTP_ACCEPT_LANGUAGE'] || 'en-us,en;q=0.5'
58
+ header['Accept-Encoding'] = orig_env['HTTP_ACCEPT_ENCODING'] || ''
59
+ header["Accept-Charset"] = orig_env['HTTP_ACCEPT_CHARSET'] || 'UTF-8,*'
60
+
61
+ # Set referrer to be, well, an Umlaut page, like the one we are
62
+ # currently generating would be best. That is, the resolve link.
63
+
64
+ header["Referer"] = "http://#{orig_env['HTTP_HOST']}#{orig_env['REQUEST_URI']}"
65
+
66
+ # Proxy X-Forwarded headers.
67
+
68
+ # The original Client's ip, most important and honest. Look for
69
+ # and add on to any existing x-forwarded-for, if neccesary, as per
70
+ # x-forwarded-for convention.
71
+ header['X-Forwarded-For'] = (orig_env['HTTP_X_FORWARDED_FOR']) ?
72
+ (orig_env['HTTP_X_FORWARDED_FOR'].to_s + ', ' + request.remote_ip.to_s) :
73
+ request.remote_ip.to_s
74
+
75
+ #Theoretically the original host requested by the client in the Host HTTP request header. We're disembling a bit.
76
+ header['X-Forwarded-Host'] = host if host
77
+ # The proxy server: That is, Umlaut, us.
78
+ header['X-Forwarded-Server'] = orig_env['SERVER_NAME'] || ''
79
+
80
+ return header
81
+ end
82
+
83
+ end
@@ -0,0 +1,328 @@
1
+ # The search controller handles searches fo manually entered citations,
2
+ # or possibly ambiguous citations generally. It also provides an A-Z list.
3
+ #
4
+ # As a source of this data, it generally talks to the SFX database directly.
5
+ # The particular method it uses to get this data is defined in a SearchMethod
6
+ # module (app/controllers/search_methods), that gets applied to the controller.
7
+ # Currently Sfx3 direct database or Sfx4 direct database are supported. In
8
+ # either case with database connection info in your database.yml file under
9
+ # sfx_db.
10
+ #
11
+ # Future plans include a local database of titles, perhaps loaded from an
12
+ # external KB. Not done yet.
13
+ #
14
+ # = SearchMethod module implementation
15
+ # A search method is just a ruby module, that will be applied to a controller,
16
+ # that defines two methods:
17
+ # [#find_by_title]
18
+ # Takes no arguments, instead use methods in the controller like
19
+ # #sfx_az_profile, #title_query_param, #search_type_param, #batch_size and
20
+ # #page to return state. Returns a two-element array pair, first element
21
+ # is a list of OpenURL::ContextObject for current batch, send element
22
+ # is int total hit count.
23
+ # [#find_by_group]
24
+ # Used for clicks on "A", "B" ... "0-9", "Other" links. Find the group
25
+ # link clicked on in params[:id]. Use #batch_size and #page for paging.
26
+ # As in #find_by_title, return two element array, first elememt is array
27
+ # of OpenURL::ContextObject, second element is total hit count.
28
+ class SearchController < UmlautController
29
+ @@search_batch_size = 20
30
+ @@az_batch_size = 20
31
+ @@autocomplete_limit = 15
32
+
33
+ layout umlaut_config.search_layout, :except => [ :opensearch, :opensearch_description ]
34
+
35
+ before_filter :normalize_params
36
+
37
+ def initialize(*params)
38
+ super(*params)
39
+
40
+ self.extend( search_method_module )
41
+ end
42
+
43
+ def index
44
+ @page_title = "Journals"
45
+ journals()
46
+ end
47
+
48
+ def journals
49
+ @submit_hash = params["umlaut.display_coins"] ? {:controller=>'resolve', :action=>'display_coins'} : {:controller=>'search', :action=>'journal_search'}
50
+
51
+ # Render configed view, if configed, or default
52
+ render umlaut_config.lookup!("search_view", "journals")
53
+ end
54
+
55
+ # Not sure if this action actually works or does anything at present.
56
+ def books
57
+ @submit_action = params["umlaut.display_coins"] ? "display_coins" : "index"
58
+ end
59
+
60
+
61
+ # @display_results is left as an array of ContextObject objects.
62
+ # Or, redirect to resolve action for single hit.
63
+ # O hit also redirects to resolve action, as per SFX behavior--this
64
+ # gives a catalog lookup and an ILL form for 0-hit.
65
+ # param umlaut.title_search_type (aka sfx.title_search)
66
+ # can be 'begins', 'exact', or 'contains'. Other
67
+ # form params should be OpenURL, generally
68
+ def journal_search
69
+ @batch_size = batch_size
70
+ @start_result_num = (page * batch_size) - (batch_size - 1)
71
+
72
+ @search_context_object = context_object_from_params
73
+
74
+ if (! params["rft.object_id"].blank? ||
75
+ ! params["rft.issn"].blank? ||
76
+ ! params["rft_id"].blank? )
77
+ # If we have an exact-type 'search', just switch to 'resolve' action
78
+ redirect_to url_for_with_co( {:controller => 'resolve'}, context_object_from_params )
79
+
80
+ # don't do anything else.
81
+ return
82
+ elsif (params['rft.jtitle'].blank?)
83
+ #Bad, error condition. If we don't have any of that other stuff above,
84
+ # we need a title! Send them back to entry page with an error message.
85
+ flash[:error] = "You must enter a journal title or other identifying information."
86
+ redirect_to :controller=>:search, :action=>:index
87
+ return
88
+ end
89
+
90
+
91
+
92
+ # Call our particular search method, #find_by_title added by search
93
+ # method module.
94
+ (@display_results, @hits) = self.find_by_title
95
+ #find_by_title_via_sfx_db
96
+
97
+ # Calculate end-result number for display
98
+ @end_result_num = @start_result_num + batch_size - 1
99
+ if @end_result_num > @hits
100
+ @end_result_num = @hits
101
+ end
102
+
103
+ if (@page == 1) && (@display_results.length == 1)
104
+ # If we narrowed down to one result redirect
105
+ # to resolve action.
106
+ redirect_to( url_for_with_co({:controller => 'resolve'}, @display_results[0]) )
107
+ elsif (@display_results.length == 0)
108
+ # 0 hits, do it too.
109
+ redirect_to( url_for_with_co({:controller => 'resolve'}, @search_context_object) )
110
+ end
111
+
112
+ end
113
+
114
+
115
+ # Used for browse-by-letter
116
+ def journal_list
117
+ @batch_size = batch_size
118
+ @page = page
119
+ @start_result_num = (@page * @batch_size) - (@batch_size - 1)
120
+
121
+
122
+ (@display_results, @hits) = find_by_group
123
+
124
+
125
+ # Calculate end-result number for display
126
+ @end_result_num = @start_result_num + @batch_size - 1
127
+ if @end_result_num > @hits
128
+ @end_result_num = @hits
129
+ end
130
+
131
+ # Use our ordinary search displayer to display
132
+ # It'll notice the action and do just a bit of special stuff.
133
+ render(:template => "search/journal_search")
134
+ end
135
+
136
+
137
+
138
+
139
+
140
+ # Should return an array of hashes, with each has having :title and :object_id
141
+ # keys. Can come from local journal index or SFX or somewhere else.
142
+ # :object_id is the SFX rft.object_id, and can be blank. (I think it's SFX
143
+ # rft.object_id for local journal index too)
144
+ def auto_complete_for_journal_title
145
+ # Don't search on blank query.
146
+ query = params['rft.jtitle']
147
+ search_type = params["umlaut.title_search_type"] || "contains"
148
+ unless ( query.blank? )
149
+ (context_objects, total_count) = find_by_title
150
+ @titles = context_objects.collect do |co|
151
+ metadata = co.referent.metadata
152
+ {:object_id => metadata["object_id"], :title => (metadata["jtitle"] || metadata["btitle"] || metadata["title"])}
153
+ end
154
+ end
155
+ render :text => @titles.to_json, :content_type => "application/json"
156
+ end
157
+
158
+ def opensearch_description
159
+ @headers['Content-Type'] = 'application/opensearchdescription+xml'
160
+ end
161
+
162
+ protected
163
+
164
+ def normalize_params
165
+ # citation search params
166
+
167
+ # sfx.title_search and umlaut.title_search_type are synonyms
168
+ params["sfx.title_search"] = params["umlaut.title_search_type"] if params["sfx.title_search"].blank?
169
+ params["umlaut.title_search_type"] = params["sfx.title_search"] if params["umlaut.title_search_type"].blank?
170
+
171
+ # Likewise, params[:journal][:title] is legacy params['rft.jtitle']
172
+ unless (params[:journal].blank? || params[:journal][:title].blank? ||
173
+ ! params['rft.jtitle'].blank? )
174
+ params['rft.jtitle'] = params[:journal][:title]
175
+ end
176
+
177
+ if ( (params[:journal].blank? || params[:journal][:title].blank?) &&
178
+ params['rft.jtitle'] )
179
+ params[:journal] ||= {}
180
+ params[:journal][:title] = params['rft.jtitle']
181
+ end
182
+
183
+
184
+ # Grab identifiers out of the way we've encoded em
185
+ # Accept legacy SFX-style encodings too
186
+ if ( ! params['rft_id_value'].blank? ||
187
+ ! params['pmid_value'].blank? ||
188
+ ! params['doi_value'].blank? )
189
+
190
+ if (! params['rft_id_value'].blank?)
191
+ id_type = params['rft_id_type'] || 'doi'
192
+ id_value = params['rft_id_value']
193
+ elsif (! params['pmid_value'].blank?)
194
+ id_type = params['pmid_id'] || 'pmid'
195
+ id_value = params['pmid_value']
196
+ else # sfx-style doi
197
+ id_type = params['doi_id'] || 'doi'
198
+ id_value = params['doi_value']
199
+ end
200
+
201
+ params['rft_id'] = "info:#{id_type}/#{id_value}"
202
+ end
203
+
204
+ # SFX v2 A-Z list url format---convert to Umlaut
205
+ if params[:letter_group]
206
+ params[:id] = case params[:letter_group].to_i
207
+ when 1 then '0-9'
208
+ # 2-27 mean A-Z, convert via ASCII value arithmetic.
209
+ when 2..27 then ((params[:letter_group].to_i) +63 ).chr
210
+ when 28 then 'Others'
211
+ end
212
+ params.delete(:letter_group) if params[:id]
213
+ end
214
+
215
+ # SFX v3 A-Z list url format--convert to Umlaut
216
+ if params[:param_letter_group_value]
217
+ params[:id] = case params[:param_letter_group_value]
218
+ when /^0/ then '0-9'
219
+ when 'Others' then 'Other'
220
+ else params[:param_letter_group_value]
221
+ end
222
+ end
223
+
224
+ # Normalize request for 'Others'
225
+ if params[:id] =~ /^other/i
226
+ params[:id] = 'Others'
227
+ end
228
+
229
+ # for reasons I can't tell, our JS on IE ends up putting some
230
+ # newlines in the object_id, which messes us all up.
231
+ params['rft.object_id'].strip! if params['rft.object_id']
232
+
233
+ ## If needed combine date elements to an OpenURL date
234
+ unless (params["__year"].blank? &&
235
+ params["__month"].blank? &&
236
+ params["__day"].blank?)
237
+ isoDate = ""
238
+ unless ["", "****", "Year"].include?(params["__year"])
239
+ isoDate += params["__year"]
240
+ unless ["", "***", "Month"].include?(params["__month"])
241
+ isoDate += "-" + params["__month"]
242
+ unless ["", "**", "Day"].include?(params["__day"])
243
+ isoDate += "-" + params["__day"]
244
+ end
245
+ end
246
+ end
247
+ unless isoDate.blank?
248
+ params["date"] = isoDate
249
+ end
250
+ end
251
+
252
+ end
253
+
254
+ def context_object_from_params
255
+ @context_object_from_params ||=
256
+ begin
257
+ params_c = params.clone
258
+
259
+ # Take out the weird ones that aren't really part of the OpenURL
260
+ ignored_keys = [:journal, "utf8", "__year", "__month", "__day", "action", "controller", "Generate_OpenURL2", "rft_id_type", "rft_id_value"]
261
+ ignored_keys.each { |k| params_c.delete(k) }
262
+
263
+ # Normalize ISSN to have dash
264
+ if ( ! params['rft.issn'].blank? && params['rft.issn'][4,1] != '-' && params['rft.issn'].length >= 4)
265
+ params['rft.issn'].insert(4,'-')
266
+ end
267
+
268
+ ctx = OpenURL::ContextObject.new
269
+ # Make sure it uses a journal type referent please, that's what we've
270
+ # got here.
271
+ ctx.referent = OpenURL::ContextObjectEntity.new_from_format( 'info:ofi/fmt:xml:xsd:journal' )
272
+ ctx.import_hash( params_c )
273
+
274
+ # Not sure where ":rft_id_value" as opposed to 'rft_id' comes from, but
275
+ # it was in old code. We do it after CO creation to handle multiple
276
+ # identifiers
277
+ if (! params_c[:rft_id_value].blank?)
278
+ ctx.referent.add_identifier( params_c[:rft_id_value] )
279
+ end
280
+ ctx
281
+ end
282
+ end
283
+
284
+ def search_method_module
285
+ umlaut_config.lookup!("az_search_method", SearchMethods::Sfx4)
286
+ end
287
+
288
+ # sfx a-z profile as defined in config, used for direct db connections
289
+ # to sfx.
290
+ def sfx_az_profile
291
+ umlaut_config.lookup!("sfx_az_profile", "default")
292
+ end
293
+ helper_method :sfx_az_profile
294
+
295
+ def title_query_param
296
+ params['rft.jtitle']
297
+ end
298
+ helper_method :title_query_param
299
+
300
+ def search_type_param
301
+ params['umlaut.title_search_type'] || 'contains'
302
+ end
303
+ helper_method :search_type_param
304
+
305
+ def batch_size
306
+ case params[:action]
307
+ when "journal_list"
308
+ @@az_batch_size
309
+ when "auto_complete_for_journal_title"
310
+ @@autocomplete_limit
311
+ else
312
+ @@search_batch_size
313
+ end
314
+ end
315
+ helper_method :batch_size
316
+
317
+ def page
318
+ @page ||= params['page'].blank? ? 1 : params['page'].to_i
319
+ end
320
+ helper_method :page
321
+
322
+
323
+
324
+
325
+
326
+
327
+
328
+ end
@@ -0,0 +1,148 @@
1
+ module SearchMethods
2
+ module Sfx3
3
+
4
+ protected
5
+
6
+ #returns pair of 1) array of context object results for current page, 2) hit count
7
+ def find_by_title
8
+ (object_ids, hit_count) = object_ids_az_v3(title_query_param, search_type_param, batch_size, page )
9
+
10
+ # Now fetch objects with publication information
11
+ # Sometimes SFX db lacks referential integrity, so we don't count
12
+ # on these object_ids actually being there, doing a find all instead
13
+ # of just a 'find'.
14
+ sfx_objects = SfxDb::Object.find( :all, :conditions => {:OBJECT_ID => object_ids},
15
+ :include => [:publishers, :main_titles, :primary_issns, :primary_isbns])
16
+
17
+ # We got the right set of @batch_size objects, but they're not sorted
18
+ # by title.
19
+ # Too hard to include the sort in the SQL, let's re-sort in memory
20
+ sfx_objects.sort! do |a,b|
21
+ if (a.main_titles.first && b.main_titles.first)
22
+ a.main_titles.first.TITLE_DISPLAY <=> b.main_titles.first.TITLE_DISPLAY
23
+ else
24
+ 0
25
+ end
26
+ end
27
+
28
+ # Now we need to convert to ContextObjects.
29
+ context_objects = sfx_objects.collect do |sfx_obj|
30
+ ctx = OpenURL::ContextObject.new
31
+ # Start out with everything in the search, to preserve date/vol info
32
+ ctx.import_context_object( context_object_from_params )
33
+
34
+ # Put SFX object id in rft.object_id, that's what SFX does.
35
+ ctx.referent.set_metadata('object_id', sfx_obj.id.to_s)
36
+
37
+ publisher_obj = sfx_obj.publishers.first
38
+ if ( publisher_obj )
39
+ ctx.referent.set_metadata('pub', publisher_obj.PUBLISHER_DISPLAY)
40
+ ctx.referent.set_metadata('place', publisher_obj.PLACE_OF_PUBLICATION_DISPLAY)
41
+ end
42
+
43
+ title_obj = sfx_obj.main_titles.first
44
+ title = title_obj ? title_obj.TITLE_DISPLAY : "Unknown Title"
45
+ ctx.referent.set_metadata('jtitle', title)
46
+
47
+ issn_obj = sfx_obj.primary_issns.first
48
+ ctx.referent.set_metadata('issn', issn_obj.ISSN_ID) if issn_obj
49
+
50
+ isbn_obj = sfx_obj.primary_isbns.first
51
+ ctx.referent.set_metadata('isbn', isbn_obj.ISBN_ID) if isbn_obj
52
+
53
+ ctx
54
+ end
55
+ return [context_objects, hit_count]
56
+ end
57
+
58
+
59
+
60
+ # Object Ids from SFX A-Z list 'version 3'. The A-Z v3 title list
61
+ # is cranky, so we have a v2 version too.
62
+ # input title query, search_type
63
+ # Returns an array [ batch_obj_id_array, count ].
64
+ def object_ids_az_v3(title_q, search_type, batch_size, page)
65
+ # MySQL 'like' is case-insensitive, fortunately, don't need to worry
66
+ # about that. But to deal with non-filing chars, need to search against
67
+ # TITLE_DISPLAY and TITLE_SORT for begins with. We're going to join
68
+ # to AZ_TITLE_SEARCH_VER3 for alternate titles too.
69
+ conditions = case search_type
70
+ when 'contains'
71
+ ['ts.AZ_PROFILE = ? AND TITLE_DISPLAY like ? OR ts.TITLE_SEARCH like ?',
72
+ sfx_az_profile,
73
+ "%" + title_q.upcase + "%", "%" + title_q.upcase + "%"]
74
+ when 'begins'
75
+ ['ts.AZ_PROFILE = ? AND TITLE_DISPLAY like ? OR TITLE_SORT like ? OR ts.TITLE_SEARCH like ?',
76
+ sfx_az_profile,
77
+ title_q + '%', title_q + '%', title_q + "%"]
78
+ else # exact
79
+ ['ts.AZ_PROFILE = ? AND TITLE_DISPLAY = ? OR TITLE_SORT = ? OR ts.TITLE_SEARCH = ?',
80
+ sfx_az_profile,
81
+ title_q, title_q, title_q]
82
+ end
83
+
84
+ # First get object_ids we're interested in, then
85
+ # we'll bulk fetch with all their data.
86
+ # Tricky-ass query for efficiency and power, sorry.
87
+ joins = "left outer join AZ_TITLE_SEARCH_VER3 as ts on `AZ_TITLE_VER3`.AZ_TITLE_VER3_ID = ts.AZ_TITLE_VER3_ID"
88
+
89
+
90
+ # Actually, _first_ we'll do a total count.
91
+ total_hits = SfxDb::AzTitle.count(:OBJECT_ID,
92
+ :distinct=>true,
93
+ :conditions=>conditions,
94
+ :joins=>joins,
95
+ :order=>'TITLE_SORT ASC')
96
+
97
+ # Now fetch object_ids for just the display batch
98
+ object_ids = SfxDb::AzTitle.find(:all, :select=>"distinct (OBJECT_ID)",
99
+ :conditions => conditions,
100
+ :joins => joins,
101
+ :limit => batch_size,
102
+ :offset => batch_size * (page - 1),
103
+ :order=>'TITLE_SORT ASC').collect { |title_obj| title_obj.OBJECT_ID}
104
+
105
+
106
+ return [ object_ids, total_hits]
107
+ end
108
+
109
+ # params[:id] will have a capital letter, or "0-9" or "Other"
110
+ def find_by_group
111
+
112
+ joins = " inner join AZ_LETTER_GROUP_VER3 as lg on AZ_TITLE_VER3.AZ_TITLE_VER3_ID = lg.AZ_TITLE_VER3_ID"
113
+ # Need a special condition for 0-9
114
+ if ( params[:id] == '0-9')
115
+ conditions = ["AZ_PROFILE=? AND lg.AZ_LETTER_GROUP_VER3_NAME IN ('0','1','2','3','4','5','6','7','8','9')",
116
+ sfx_az_profile]
117
+ else
118
+ conditions = ["AZ_PROFILE=? AND lg.AZ_LETTER_GROUP_VER3_NAME = ?",
119
+ sfx_az_profile,
120
+ params[:id].upcase]
121
+ end
122
+
123
+ hits = SfxDb::AzTitle.count(:joins => joins,
124
+ :conditions => conditions)
125
+
126
+
127
+ # Sorry this find is so crazy, trying to manage to do it
128
+ # efficiently and get what we need in large SFX db.
129
+ # For crazy nested include below, see:
130
+ # http://snippets.dzone.com/posts/show/2089
131
+ az_titles = SfxDb::AzTitle.find(:all,
132
+ :joins => joins,
133
+ :conditions => conditions,
134
+ :limit => batch_size,
135
+ :offset=> batch_size * (page - 1),
136
+ :order=>'TITLE_SORT',
137
+ :include=>[{:object => [:publishers, :titles] }])
138
+
139
+ # Convert to context objects
140
+ display_results = az_titles.collect do | azt |
141
+ co = azt.to_context_object
142
+ co.referrer.add_identifier('info:sid/umlaut.code4lib.org:azlist')
143
+ co
144
+ end
145
+ return [display_results, hits]
146
+ end
147
+ end
148
+ end