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,50 @@
1
+ require 'ostruct'
2
+
3
+ module SearchHelper
4
+
5
+ def search_result_target_window
6
+ umlaut_config.lookup!("search.result_link_target","")
7
+ end
8
+
9
+ # pass in an openurl context obj.
10
+ # return an OpenStruct with atitle_label, title_label
11
+ def referent_labels(context_obj = @current_context_object)
12
+ ref_meta = context_obj.referent.metadata
13
+
14
+ result = OpenStruct.new
15
+
16
+ if ref_meta['genre'].blank?
17
+ case @current_context_object.referent.format
18
+ when 'book'
19
+ result.atitle = 'Chapter/Part Title'
20
+ when @current_context_object.referent.format == 'journal'
21
+ result.atitle = 'Article Title'
22
+ end
23
+ result.title = 'Title'
24
+ else
25
+ case ref_meta["genre"]
26
+ when /article|journal|issue/
27
+ result.atitle = 'Article Title'
28
+ result.title = 'Journal Title'
29
+ when /bookitem|book/
30
+ result.atitle = 'Chapter/Part Title'
31
+ result.title = 'Book Title'
32
+ when /proceeding|conference/
33
+ result.atitle = 'Proceeding Title'
34
+ result.title = 'Conference Name'
35
+ when 'report'
36
+ result.atitle = 'Report Title'
37
+ result.title = 'Report'
38
+ end
39
+ end
40
+
41
+ return result
42
+ end
43
+
44
+ # A-Z buttons in search page
45
+ def group_list
46
+ group_list ||= ('A'..'Z').to_a.push('0-9').push('Other')
47
+ end
48
+
49
+
50
+ end
@@ -0,0 +1,64 @@
1
+ # Some rails view helpers useful for debugging links, and rendering credits
2
+ # in footer.
3
+ module Umlaut::FooterHelper
4
+
5
+ # Renders list of external services used by currently configured Umlaut,
6
+ # with URLs. In some cases ToS of third party services may require this.
7
+ # Gets list from "credits" config in Service plugins.
8
+ # Requires @collection ivar in controller holding an umlaut Collection
9
+ # object, as there will be in ResolveController.
10
+ def render_service_credits
11
+ if @collection
12
+ content = "".html_safe
13
+
14
+ content << "Powered by ".html_safe + link_to("Umlaut", "http://github.com/team_umlaut/umlaut") + ". ".html_safe
15
+
16
+ credit_segments = []
17
+
18
+ services = @collection.instantiate_services!
19
+
20
+ # put em all in one hash to eliminate exact-name dups
21
+ credits = {}
22
+ services.each {|s| credits.merge! s.credits }
23
+
24
+ credits.keys.sort.each do |name|
25
+ if credits[name].blank?
26
+ credit_segments << html_escape(name)
27
+ else
28
+ credit_segments << link_to(name, credits[name])
29
+ end
30
+ end
31
+
32
+
33
+ if credit_segments.length > 0
34
+ content << "Using services from ".html_safe
35
+ content << credit_segments.join(", ").html_safe
36
+ content << " and others.".html_safe
37
+ end
38
+
39
+ return content
40
+ end
41
+ end
42
+
43
+ # tiny [S] link directly to SFX, in footer. For debugging.
44
+ # Only if sfx.sfx_base_url is configured.
45
+ def link_to_direct_sfx
46
+ if (base = umlaut_config.lookup!("sfx.sfx_base_url")) && @user_request
47
+ url = base.chomp("?") + "?"
48
+ url += @user_request.to_context_object.kev
49
+ url += "&sfx.ignore_date_threshold=1" if respond_to?(:title_level_request) && title_level_request?
50
+
51
+ link_to "[S]", url
52
+ end
53
+ end
54
+
55
+ # If you have a config.test_resolve_base configured,
56
+ # will output a [T] link, usually for footer, for staff
57
+ # debugging.
58
+ def link_to_test_resolve
59
+ if (test_base = umlaut_config.lookup!("test_resolve_base")) && @user_request
60
+ link_to "[T]", test_base.chomp("?") + "?" + @user_request.to_context_object.kev
61
+ end
62
+ end
63
+
64
+ end
@@ -0,0 +1,62 @@
1
+ # Rails view helpers needed accross Umlaut controllers are collected
2
+ # here. Generally UmlautController will call "helper Umlaut::Helper" to
3
+ # expose these to all umlaut controllers.
4
+
5
+ module Umlaut::Helper
6
+ include Umlaut::UrlGeneration
7
+ include Umlaut::FooterHelper
8
+ include Umlaut::HtmlHeadHelper
9
+
10
+
11
+
12
+ # pass in an OpenURL::ContextObject, outputs a link.
13
+ def resolver_link(context_object, params={})
14
+
15
+ # Content of the link.
16
+ if ( umlaut_config.link_img_url && params[:text].blank? )
17
+ link_content = image_tag(umlaut_config.link_img_url, :border=>0, :alt=>umlaut_config.app_name)
18
+ elsif ! params[:text].blank?
19
+ link_content = params[:text]
20
+ else
21
+ link_content = umlaut_config.app_name
22
+ end
23
+
24
+ # url of the link.
25
+ if ( params[:params])
26
+ link_to_arg = params[:params]
27
+ else
28
+ link_params = {:controller=>'resolve'}
29
+ link_params.merge!( params[:extra_params] ) if params[:extra_params]
30
+ link_to_arg = url_for_with_co( link_params, context_object )
31
+ end
32
+
33
+ link_to(link_content, link_to_arg , :target=>params[:target])
34
+ end
35
+
36
+ # formats dates sent in an OpenURL into a more human-friendly
37
+ # format. Input Dates look like '20000304'. Can be just year, or just
38
+ # year/month, or all. Not sure what this format
39
+ # is officially called. Not sure if they can have dashes sometimes?
40
+ def date_format(date_string)
41
+ date_string =~ /(\d\d\d\d)\-?(\d\d)?\-?(\d\d)?/
42
+
43
+ begin
44
+ year, month, day_of_month = $1, $2, $3
45
+
46
+ if ( month )
47
+ date = Date.civil(year.to_i, month.to_i)
48
+ formatted_month = date.strftime('%b')
49
+ end
50
+
51
+ output = year
52
+ output += ' ' + formatted_month if formatted_month
53
+ output += ' ' + day_of_month if day_of_month && day_of_month.to_i != 0
54
+
55
+ return output
56
+ rescue
57
+ return date_string
58
+ end
59
+ end
60
+
61
+
62
+ end
@@ -0,0 +1,37 @@
1
+ # Rails view helpers for outputting standard Umlaut content included
2
+ # in html <head>. Generally a layout will call #render_umlaut_head_content
3
+ # to render all standard Umlaut <head> content in a future-compatible way.
4
+ module Umlaut::HtmlHeadHelper
5
+
6
+ # usually called in layout, render a link tag with opensearch auto-discovery
7
+ def render_opensearch_link
8
+ tag("link",
9
+ :rel => "search",
10
+ :type => "application/opensearchdescription+xml",
11
+ :title => umlaut_config.opensearch_short_name,
12
+ :href => url_for(:controller=>'open_search', :only_path=>false)
13
+ )
14
+ end
15
+
16
+ # used on non-js progress page, we need to refresh the page
17
+ # if requested by presence of @meta_refresh_self ivar.
18
+ # this method usually called in a layout.
19
+ def render_meta_refresh
20
+ if @meta_refresh_self
21
+ tag( "meta",
22
+ "http-equiv" => "refresh",
23
+ "content" => @meta_refresh_self
24
+ )
25
+ else
26
+ ""
27
+ end
28
+ end
29
+
30
+ # standard umlaut head content, may later include more
31
+ # stuff, local/custom layouts should call this in HEAD
32
+ # to get forwards-compatible umlaut standard head content
33
+ def render_umlaut_head_content
34
+ render_opensearch_link + render_meta_refresh
35
+ end
36
+
37
+ end
@@ -0,0 +1,77 @@
1
+ # A Rails view helper module, which over-rides #url_for and some other
2
+ # rails url-generating methods, so that they can be forced to generate
3
+ # absolute URLs if a controller iVar is set to say so.
4
+ #
5
+ # This is used by our partial HTML api responses, so make sure html snippets
6
+ # have absolute URLs in them.
7
+
8
+ module Umlaut::UrlGeneration
9
+
10
+ # Over-ride to allow default forcing of urls with hostnames.
11
+ # This is neccesary for our partial_html_sections service
12
+ # to work properly. Just set @generate_url_with_host = true
13
+ # in your controller, and urls will be generated with hostnames
14
+ # for the remainder of that action.
15
+ def url_for(argument = {})
16
+ if @generate_urls_with_host
17
+ case argument
18
+ when Hash
19
+ # Force only_path = false if not already set
20
+ argument[:only_path] = false if argument[:only_path].nil?
21
+ return super(argument)
22
+ when String
23
+ # We already have a straight string, if it looks relative,
24
+ # absolutize it.
25
+ if argument.starts_with?("/")
26
+ return root_url.chomp("/") + argument
27
+ else
28
+ return super(argument)
29
+ end
30
+ when :back
31
+ return super(argument)
32
+ else
33
+ # polymorphic, we want to force polymorphic_url instead
34
+ # of default polymorphic_path
35
+ return polymorphic_url(argument)
36
+ end
37
+ else
38
+ # @generate_urls_with_host not set, just super
39
+ super(argument)
40
+ end
41
+ end
42
+
43
+ # over-ride path_to_image to generate complete urls with hostname and everything
44
+ # if @generate_url_with_host is set. This makes image_tag generate
45
+ # src with full url with host. See #url_for
46
+ def path_to_image(source)
47
+ path = super(source)
48
+ if @generate_urls_with_host
49
+ protocol = request.protocol()
50
+ path = protocol + request.host_with_port() + path
51
+ end
52
+ return path
53
+ end
54
+ # Rails2 uses 'path_to_image' instead, that's what we have to override,
55
+ # we used to use image_path, so let's alias that too.
56
+ alias :image_path :path_to_image
57
+
58
+
59
+ # We want stylesheets and javascripts to do the exact same thing,
60
+ # magic of polymorphous super() makes it work:
61
+ def path_to_stylesheet(source)
62
+ path = super
63
+ if @generate_urls_with_host
64
+ path = request.protocol() + request.host_with_port() + path
65
+ end
66
+ return path
67
+ end
68
+
69
+ def path_to_javascript(source)
70
+ path = super
71
+ if @generate_urls_with_host
72
+ path = request.protocol() + request.host_with_port() + path
73
+ end
74
+ return path
75
+ end
76
+
77
+ end
@@ -0,0 +1,48 @@
1
+ class Emailer < ActionMailer::Base
2
+ include UmlautConfigurable
3
+
4
+ self.umlaut_config = UmlautController.umlaut_config
5
+
6
+ helper :application
7
+
8
+
9
+ def citation(recipient, user_request, fulltexts, holdings)
10
+ @title = find_good_title(user_request.referent)
11
+ @fulltexts = fulltexts
12
+ @holdings = holdings
13
+ @user_request = user_request
14
+
15
+ mail(:to => recipient,
16
+ :from => umlaut_config.from_email_addr,
17
+ :'Reply-to' => umlaut_config.from_email_addr,
18
+ :subject => "#{umlaut_config.app_name} result: #{find_good_title(user_request.referent)}")
19
+ end
20
+
21
+ def short_citation(recipient, user_request, location, call_number)
22
+
23
+
24
+ @title = find_good_title(user_request.referent)
25
+ @location = location
26
+ @call_number = call_number
27
+ @user_request = user_request
28
+
29
+ mail(:to => recipient,
30
+ :from => umlaut_config.from_email_addr,
31
+ :'Reply-to' => umlaut_config.from_email_addr,
32
+ :subject => "#{umlaut_config.app_name} result")
33
+
34
+ end
35
+
36
+ protected
37
+ def find_good_title(referent)
38
+ citation = referent.to_citation
39
+ if citation[:container_title]
40
+ return citation[:container_title]
41
+ else
42
+ return "#{citation[:title]} / #{citation[:author]}"
43
+ end
44
+ end
45
+
46
+
47
+
48
+ end
@@ -0,0 +1,2 @@
1
+ class Clickthrough < ActiveRecord::Base
2
+ end
@@ -0,0 +1,259 @@
1
+ require 'CronTab' # for understanding CronTab format for expiring responses.
2
+
3
+ # A Collection object encapsulates a given UmlautRequest, and a given
4
+ # list of Umlaut services that should be run off that request.
5
+ #
6
+ # That's exactly what it's initialized with: an umlaut request, and
7
+ # list of service definitions. Third parameter pass in an umlaut configuration
8
+ # object, to get various timeout values. If you don't pass one in, defaults
9
+ # will be used.
10
+ #
11
+ # The Collection holds and executes the logic for running those services,
12
+ # foreground and background, making sure no service is run twice if it's
13
+ # already in progress, timing out expired services, etc.
14
+ class Collection
15
+
16
+ attr_accessor :umlaut_request
17
+ attr_accessor :logger
18
+ # configs
19
+ attr_accessor :response_expire_interval, :response_expire_crontab_format, :background_service_timeout, :requeue_failedtemporary_services
20
+
21
+
22
+ def initialize(a_umlaut_request, service_hash, config = Confstruct::Configuration.new)
23
+ self.umlaut_request = a_umlaut_request
24
+
25
+ self.logger = Rails.logger
26
+
27
+ self.response_expire_interval = config.lookup!("response_expire_interval", 1.day)
28
+ self.response_expire_crontab_format = config.lookup!("response_expire_crontab_format", nil)
29
+ self.background_service_timeout = config.lookup!("background_service_timeout", 30.seconds)
30
+ self.requeue_failedtemporary_services = config.lookup!("requeue_failedtemporary_services", 500.seconds)
31
+
32
+ # @service_definitions will be a two-level hash, pointing to an array.. Task is Standard, LinkOut, etc.
33
+ # { [task] => { [priority_level] => [config1, config2, config3],
34
+ # [priority_level_2] => [configa], }
35
+ # [...]
36
+ # }
37
+ @service_definitions_flat = service_hash
38
+ @service_definitions = {}
39
+
40
+ # Arrange services by type and priority in @service_definitions
41
+ gather_services
42
+ end
43
+
44
+ # Starts running all services that are in this collection, for the given
45
+ # request set for this collection, if and only if they are not already
46
+ # in progress.
47
+ #
48
+ # This method can be run on a request multiple times, it'll only re-execute
49
+ # services that are executable (not already running, or timed out).
50
+ # That characteristic is used when this method is called on a page refresh
51
+ # or background update status check.
52
+ #
53
+ # Sets all services in collection to have a 'queued' status if appropriate.
54
+ # Then actually executes the services that are dispatchable (queued).
55
+ def dispatch_services!
56
+ # Go through currently dispatched services, looking for timed out
57
+ # services -- services still in progress that have taken too long,
58
+ # as well as service responses that are too old to be used.
59
+ umlaut_request.dispatched_services.each do | ds |
60
+ # go through dispatched_services and set stil in progress but too long to failed temporary
61
+ if ( (ds.status == DispatchedService::InProgress ||
62
+ ds.status == DispatchedService::Queued ) &&
63
+ (Time.now - ds.updated_at) > self.background_service_timeout)
64
+
65
+ 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
66
+ # Fail it temporary, it'll be run again.
67
+ ds.status = DispatchedService::FailedTemporary
68
+ ds.save!
69
+ logger.warn("Background service timed out, thread assumed dead. #{umlaut_request.id} / #{ds.service_id}")
70
+ end
71
+
72
+ # go through dispatched_services and delete:
73
+ # 1) old completed dispatches, too old to use.
74
+ # 2) failedtemporary dispatches that are older than our resurrection time
75
+ # -> And all responses associated with those dispatches.
76
+ # After being deleted, they'll end up re-queued.
77
+ if ( (ds.completed? && completed_dispatch_expired?(ds) ) ||
78
+ ( ds.status == DispatchedService::FailedTemporary &&
79
+ (Time.now - ds.updated_at) > self.requeue_failedtemporary_services
80
+ )
81
+ )
82
+
83
+ # Need to expire. Delete all the service responses, and
84
+ # the DispatchedService record, and service will be automatically
85
+ # run again.
86
+ serv_id = ds.service_id
87
+
88
+ umlaut_request.service_responses.each do |response|
89
+ if response.service_id == serv_id
90
+ umlaut_request.service_responses.delete(response)
91
+ response.destroy
92
+ end
93
+ end
94
+
95
+ umlaut_request.dispatched_services.delete(ds)
96
+ ds.destroy
97
+ end
98
+ end
99
+
100
+ # Queue any services without a dispatch marker at all, keeping
101
+ # track of queued services, already existing or newly created.
102
+
103
+ # Just in case, we're going to refetch dispatched_services from the db,
104
+ # in case some other http request or background service updated things
105
+ # recently.
106
+ umlaut_request.dispatched_services.reset
107
+
108
+ queued_service_ids = []
109
+ self.get_service_definitions.each do |service|
110
+ service_id = service['service_id']
111
+ # use in-memory #to_a search, don't go to db each time!
112
+ if found = umlaut_request.dispatched_services.to_a.find {|s| s.service_id == service_id}
113
+ queued_service_ids.push(service_id) if found.status == DispatchedService::Queued
114
+ else
115
+ umlaut_request.new_dispatch_object!(service_id, DispatchedService::Queued).save!
116
+ queued_service_ids.push(service_id)
117
+ end
118
+ end
119
+
120
+
121
+
122
+ # Now actually dispatch.
123
+
124
+ # Foreground services
125
+ (0..9).each do | priority |
126
+ services_to_run = self.instantiate_services!(:level => priority, :ids => queued_service_ids)
127
+ next if services_to_run.empty?
128
+ ServiceWave.new(services_to_run , priority).handle(umlaut_request, umlaut_request.session_id)
129
+ end
130
+
131
+ # Need to reload the request from db, so it gets changes
132
+ # made by services in threads.
133
+ umlaut_request.reload
134
+
135
+ # Now we run background services.
136
+ # Now we do some crazy magic, start a Thread to run our background
137
+ # services. We are NOT going to wait for this thread to join,
138
+ # we're going to let it keep doing it's thing in the background after
139
+ # we return a response to the browser
140
+ backgroundThread = Thread.new(self, umlaut_request) do | t_collection, t_request|
141
+ # Tell our AR extension not to allow implicit checkouts
142
+ ActiveRecord::Base.forbid_implicit_checkout_for_thread! if ActiveRecord::Base.respond_to?("forbid_implicit_checkout_for_thread!")
143
+
144
+ # got to reserve an AR connection for our main 'background traffic director'
145
+ # thread, so it has a connection to use to mark services as failed, at least.
146
+ ActiveRecord::Base.connection_pool.with_connection do
147
+ begin
148
+ # Deal with ruby's brain dead thread scheduling by setting
149
+ # bg threads to a lower priority so they don't interfere with fg
150
+ # threads.
151
+ Thread.current.priority = -1
152
+
153
+ ('a'..'z').each do | priority |
154
+ services_to_run = self.instantiate_services!(:level => priority, :ids => queued_service_ids)
155
+ next if services_to_run.empty?
156
+ ServiceWave.new(services_to_run , priority).handle(umlaut_request, umlaut_request.session_id)
157
+ end
158
+ rescue Exception => e
159
+ #debugger
160
+ # We are divorced from any request at this point, not much
161
+ # we can do except log it. Actually, we'll also store it in the
162
+ # db, and clean up after any dispatched services that need cleaning up.
163
+ # If we're catching an exception here, service processing was
164
+ # probably interrupted, which is bad. You should not intentionally
165
+ # raise exceptions to be caught here.
166
+ Thread.current[:exception] = e
167
+ logger.error("Background Service execution exception1: #{e}\n\n " + clean_backtrace(e).join("\n"))
168
+ end
169
+ end
170
+ end
171
+
172
+
173
+
174
+ end
175
+
176
+ def completed_dispatch_expired?(ds)
177
+ interval = self.response_expire_interval
178
+ crontab = self.response_expire_crontab_format
179
+ now = Time.now
180
+
181
+ return nil unless interval || crontab
182
+
183
+ expired_interval = interval && (now - ds.created_at > interval)
184
+ expired_crontab = crontab && (now > CronTab.new(crontab).nexttime(ds.created_at))
185
+
186
+ return expired_interval || expired_crontab
187
+ end
188
+
189
+
190
+
191
+
192
+ # Instantiate new copies of services included in this collection, which
193
+ # services specified by options, can combine:
194
+ # :task => Service::StandardTask (default) or Service::LinkOutFilterTask
195
+ # :level => priority level, default to returning services from all.
196
+ # :ids => list of id's, only those.
197
+ def instantiate_services!(options ={})
198
+ get_service_definitions(options).collect do |svc_def|
199
+ ServiceStore.instantiate_service!(svc_def, umlaut_request)
200
+ end
201
+ end
202
+
203
+
204
+ # Deprecated, use #instantiate_services! with :task => Service::LinkOutFilter.
205
+ def link_out_service_level(level)
206
+ instantiate_services!(:task => Service::LinkOutFilterTask,
207
+ :level => level)
208
+ end
209
+
210
+
211
+
212
+ # Get service definition hashes for services in this institution.
213
+ # options, returned in an array.
214
+ # Does return a mutatable array that Collection mutates
215
+ # internally, but clients really ought not to mutate.
216
+ # :task => Service::StandardTask (default) or Service::LinkOutFilterTask
217
+ # :level => priority level, default to returning services from all.
218
+ # :ids => list of service unique ids, return only these.
219
+ def get_service_definitions(options = {})
220
+ options[:task] ||= Service::StandardTask
221
+
222
+ configs_for_task = @service_definitions[ options[:task] ] || {}
223
+
224
+ service_configs = case options[:level]
225
+ when nil
226
+ # All of of them for this task
227
+ configs_for_task.values.flatten
228
+ else
229
+ configs_for_task[ options[:level] ] || []
230
+ end
231
+ if options[:ids]
232
+ service_configs = service_configs.find_all {|s| options[:ids].include? s["service_id"] }
233
+ end
234
+
235
+ return service_configs
236
+ end
237
+
238
+ protected
239
+
240
+ # Arrange services in hash according to task type and priority.
241
+ def gather_services
242
+ @service_definitions_flat.each_pair do | unique_id, svc_def |
243
+ next if svc_def.nil?
244
+
245
+ svc_def['service_id'] = unique_id
246
+ task = svc_def['task'] || Service::StandardTask
247
+ level = svc_def['priority'] || 3
248
+
249
+ @service_definitions[task] ||= {}
250
+ @service_definitions[task][level] ||= []
251
+ @service_definitions[task][level] << svc_def
252
+ end
253
+ end
254
+
255
+
256
+
257
+
258
+
259
+ end