├── .gitignore ├── .travis.yml ├── Gemfile ├── LICENSE ├── README.md ├── RELEASE_NOTES.md ├── Rakefile ├── active_record_patch └── connection_pool.rb ├── app ├── assets │ ├── images │ │ ├── error.gif │ │ ├── frame_remove.gif │ │ ├── spinner.gif │ │ ├── umlaut_icons.png │ │ └── umlaut_icons │ │ │ ├── famfamfam-book-go.png │ │ │ ├── famfamfam-book-open.png │ │ │ ├── famfamfam-book.png │ │ │ ├── famfamfam-cross.png │ │ │ ├── famfamfam-error.png │ │ │ ├── famfamfam-help.png │ │ │ ├── famfamfam-information.png │ │ │ ├── famfamfam-link.png │ │ │ ├── famfamfam-page-sound.png │ │ │ ├── famfamfam-page-text.png │ │ │ ├── famfamfam-page-up.png │ │ │ ├── famfamfam-page-white.png │ │ │ ├── famfamfam-readme.html │ │ │ ├── famfamfam-tiny-cross.png │ │ │ ├── list-closed.png │ │ │ └── list-open.png │ ├── javascripts │ │ ├── bootstrap3-typeahead.js │ │ ├── umlaut.js │ │ ├── umlaut │ │ │ ├── ajax_windows.js │ │ │ ├── ensure_window_size.js.erb │ │ │ ├── expand_contract_toggle.js │ │ │ ├── load_permalink.js │ │ │ ├── search_autocomplete.js │ │ │ └── update_html.js │ │ └── umlaut_ui.js │ └── stylesheets │ │ ├── umlaut.css.scss │ │ └── umlaut │ │ ├── _admin.scss │ │ ├── _az.scss │ │ ├── _forms.scss │ │ ├── _icons.scss │ │ ├── _layout.scss │ │ ├── _misc.scss │ │ ├── _mixins.scss │ │ ├── _modal.scss │ │ ├── _resolve.scss │ │ ├── _results.scss │ │ ├── _search.scss │ │ ├── _spinner.scss │ │ └── _variables.scss ├── controllers │ ├── admin │ │ └── service_errors_controller.rb │ ├── export_email_controller.rb │ ├── feedback_controller.rb │ ├── js_helper_controller.rb │ ├── link_router_controller.rb │ ├── open_search_controller.rb │ ├── resolve_controller.rb │ ├── resource_controller.rb │ ├── search_controller.rb │ ├── search_methods │ │ ├── README.md │ │ ├── sfx4.rb │ │ └── sfx_api.rb │ ├── store_controller.rb │ ├── umlaut │ │ ├── controller_behavior.rb │ │ ├── controller_logic.rb │ │ └── error_handling.rb │ ├── umlaut_configurable.rb │ └── umlaut_controller.rb ├── helpers │ ├── emailer_helper.rb │ ├── export_email_helper.rb │ ├── open_search_helper.rb │ ├── resolve_helper.rb │ ├── search_helper.rb │ └── umlaut │ │ ├── footer_helper.rb │ │ ├── helper.rb │ │ ├── html_head_helper.rb │ │ ├── section_highlights.rb │ │ └── url_generation.rb ├── mailers │ ├── emailer.rb │ └── feedback_mailer.rb ├── mixin_logic │ ├── marc_helper.rb │ ├── metadata_helper.rb │ ├── umlaut_http.rb │ └── xml_schema_helper.rb ├── models │ ├── clickthrough.rb │ ├── collection.rb │ ├── crossref_lookup.rb │ ├── dispatched_service.rb │ ├── hip3 │ │ ├── bib.rb │ │ ├── bib_searcher.rb │ │ ├── custom_field_lookup.rb │ │ ├── holding.rb │ │ ├── item.rb │ │ ├── receipt.rb │ │ └── serial_copy.rb │ ├── permalink.rb │ ├── referent.rb │ ├── referent_value.rb │ ├── request.rb │ ├── service_response.rb │ ├── service_store.rb │ ├── service_wave.rb │ ├── sfx4 │ │ ├── abstract │ │ │ ├── README.md │ │ │ ├── az_extra_info.rb │ │ │ ├── az_letter_group.rb │ │ │ ├── az_title.rb │ │ │ ├── az_title_search.rb │ │ │ └── base.rb │ │ └── local │ │ │ ├── az_extra_info.rb │ │ │ ├── az_letter_group.rb │ │ │ ├── az_title.rb │ │ │ ├── az_title_search.rb │ │ │ └── base.rb │ └── sfx_url.rb ├── presentation │ └── section_renderer.rb ├── referent_filters │ ├── dissertation_catch.rb │ └── referent_filter.rb ├── service_adaptors │ ├── ajax_export.rb │ ├── all_books_dot_com.rb │ ├── amazon.rb │ ├── blacklight.rb │ ├── book_finder.rb │ ├── bx.rb │ ├── cover_thing.rb │ ├── dummy_service.rb │ ├── elsevier_cover.rb │ ├── email_export.rb │ ├── ezproxy.rb │ ├── google_book_search.rb │ ├── google_scholar_link.rb │ ├── gpo.rb │ ├── hathi_trust.rb │ ├── hip3_service.rb │ ├── hip_holding_search.rb │ ├── illiad.rb │ ├── internet_archive.rb │ ├── isbn_db.rb │ ├── isbn_link.rb │ ├── isi.rb │ ├── jcr.rb │ ├── opac.rb │ ├── open_library.rb │ ├── open_library_cover.rb │ ├── pubmed.rb │ ├── request_to_fixture.rb │ ├── scopus.rb │ ├── scopus2.rb │ ├── service.rb │ ├── sfx.rb │ ├── sfx_backchannel_record.rb │ ├── txt_holding_export.rb │ ├── ulrichs_cover.rb │ ├── ulrichs_link.rb │ ├── worldcat.rb │ └── worldcat_identities.rb └── views │ ├── admin │ └── service_errors │ │ ├── _dispatched_service.html.erb │ │ └── index.html.erb │ ├── emailer │ ├── citation.text.erb │ └── short_citation.text.erb │ ├── export_email │ ├── email.html.erb │ ├── send_email.html.erb │ ├── send_txt.html.erb │ └── txt.html.erb │ ├── feedback │ ├── _resolve_section.html.erb │ └── new.html.erb │ ├── feedback_mailer │ └── feedback.text.erb │ ├── js_helper │ └── loader.erb.js │ ├── layouts │ └── umlaut.html.erb │ ├── open_search │ └── index.html.erb │ ├── resolve │ ├── _api_in_progress.xml.erb │ ├── _background_progress.html.erb │ ├── _background_updater.html.erb │ ├── _citation.html.erb │ ├── _coins.html.erb │ ├── _compact_citation.html.erb │ ├── _cover_image.html.erb │ ├── _fulltext.html.erb │ ├── _help.html.erb │ ├── _holding.html.erb │ ├── _manually_entered_warning.html.erb │ ├── _modal.html.erb │ ├── _related_items.html.erb │ ├── _search_inside.html.erb │ ├── _section_display.html.erb │ ├── _section_heading.html.erb │ ├── _service_errors.html.erb │ ├── _standard_response_item.html.erb │ ├── api.xml.builder │ ├── background_status.html.erb │ ├── get_permalink.html.erb │ ├── index.html.erb │ └── partial_html_sections.xml.erb │ ├── search │ ├── _a_to_z.html.erb │ ├── _citation.html.erb │ ├── _pager.html.erb │ ├── books.html.erb │ ├── journal_search.html.erb │ └── journals.html.erb │ ├── testing │ └── index.html.erb │ └── umlaut │ ├── README │ ├── _alerts.html.erb │ ├── _footer.html.erb │ ├── _header.html.erb │ └── error.html.erb ├── bin └── umlaut ├── config └── locales │ └── en.yml ├── db ├── migrate │ ├── 01_umlaut_init.rb │ └── 02_umlaut_add_service_response_index.rb ├── orig_fixed_data │ └── service_type_values.yml └── seeds.rb ├── lib ├── aws_product_sign.rb ├── cron_tab.rb ├── generators │ ├── templates │ │ └── umlaut_services.yml │ ├── umlaut │ │ ├── asset_hooks_generator.rb │ │ ├── install_generator.rb │ │ └── remove_turbolinks_generator.rb │ └── umlaut_app_template.rb ├── service_type_value.rb ├── tasks │ ├── umlaut.rake │ ├── umlaut_asset_compile.rake │ └── umlaut_migrate_permalinks.rake ├── term_color.rb ├── truncate_to_db_limit.rb ├── umlaut.rb └── umlaut │ ├── routes.rb │ ├── test_help.rb │ ├── util.rb │ └── version.rb ├── test ├── README.md ├── dummy │ ├── Rakefile │ ├── app │ │ ├── assets │ │ │ ├── javascripts │ │ │ │ └── application.js │ │ │ └── stylesheets │ │ │ │ └── application.css │ │ ├── controllers │ │ │ ├── application_controller.rb │ │ │ └── umlaut_controller.rb │ │ ├── helpers │ │ │ └── application_helper.rb │ │ ├── mailers │ │ │ └── .gitkeep │ │ ├── models │ │ │ └── .gitkeep │ │ └── views │ │ │ └── layouts │ │ │ └── application.html.erb │ ├── config.ru │ ├── config │ │ ├── application.rb │ │ ├── boot.rb │ │ ├── database.yml.example │ │ ├── environment.rb │ │ ├── environments │ │ │ ├── development.rb │ │ │ ├── production.rb │ │ │ └── test.rb │ │ ├── initializers │ │ │ ├── backtrace_silencers.rb │ │ │ ├── inflections.rb │ │ │ ├── mime_types.rb │ │ │ ├── secret_token.rb │ │ │ ├── session_store.rb │ │ │ └── wrap_parameters.rb │ │ ├── locales │ │ │ └── en.yml │ │ ├── routes.rb │ │ ├── sunspot.yml │ │ ├── travis_database.yml │ │ └── umlaut_services.yml │ ├── db │ │ ├── migrate │ │ │ ├── 20120927164040_sfx4_local.rb │ │ │ └── README │ │ └── schema.rb │ ├── lib │ │ └── assets │ │ │ └── .gitkeep │ ├── log │ │ └── .gitkeep │ ├── public │ │ ├── 404.html │ │ ├── 422.html │ │ ├── 500.html │ │ └── favicon.ico │ └── script │ │ └── rails ├── fixtures │ ├── dispatched_services.yml │ ├── permalinks.yml │ ├── referent_values.yml │ ├── referents.yml │ ├── requests.yml │ ├── service_responses.yml │ ├── sfx4 │ │ └── local │ │ │ ├── AZ_EXTRA_INFO.yml │ │ │ ├── AZ_LETTER_GROUP.yml │ │ │ ├── AZ_TITLE.yml │ │ │ └── AZ_TITLE_SEARCH.yml │ └── sfx_urls.yml ├── functional │ ├── configuration_reorder_test.rb │ ├── export_email_controller_test.rb │ ├── feedback_controller_test.rb │ ├── link_router_controller_test.rb │ ├── resolve_controller_more_test.rb │ ├── resolve_controller_test.rb │ ├── search_controller_test.rb │ └── store_controller_test.rb ├── helper │ └── list_with_limit_test.rb ├── integration │ ├── permalinks_test.rb │ └── request_reuse_test.rb ├── support │ ├── search_methods │ │ └── test_case.rb │ └── service_adaptors │ │ └── test_case.rb ├── test_helper.rb ├── umlaut_test.rb ├── unit │ ├── README.md │ ├── active_record_connection_pool_test.rb │ ├── amazon_test.rb │ ├── aws_product_sign_test.rb │ ├── bx_test.rb │ ├── collection_test.rb │ ├── determine_services_test.rb │ ├── dispatched_service_test.rb │ ├── dissertation_catch_test.rb │ ├── feedback_mailer_test.rb │ ├── google_book_search_test.rb │ ├── google_scholar_link_test.rb │ ├── illiad_test.rb │ ├── internet_archive_test.rb │ ├── metadata_helper_test.rb │ ├── permalink_test.rb │ ├── referent_test.rb │ ├── referent_to_citation_test.rb │ ├── request_test.rb │ ├── scopus2_test.rb │ ├── section_highlights_test.rb │ ├── service_response_test.rb │ ├── service_store_test.rb │ ├── service_test.rb │ ├── service_type_value_test.rb │ ├── sfx │ │ ├── sfx_target_precedence_test.rb │ │ └── sfx_target_roll_up_test.rb │ ├── sfx4_search_test.rb │ ├── sfx_test.rb │ └── worldcat_test.rb ├── vcr_cassettes │ ├── amazon │ │ └── product_advertising_api_forbidden.yml │ ├── bx │ │ └── article_that_has_recommendations.yml │ ├── collection │ │ └── live_dispatch.yml │ ├── google_book_search │ │ ├── adds_abstract.yml │ │ ├── enhances_referent.yml │ │ ├── enhances_referent_and_other_data.yml │ │ └── frankenstein_by_OCLC_number.yml │ ├── internet_archive │ │ ├── Stalin_tricky_good_match.yml │ │ ├── capital_with_variety_of_links.yml │ │ ├── momo_by_title_author.yml │ │ ├── style_no_good_match.yml │ │ └── twain_no_good_match.yml │ ├── scopus │ │ ├── live_test_with_no_hits.yml │ │ ├── live_test_with_result.yml │ │ └── live_trigger_scopus_error.yml │ └── sfx │ │ └── nytimes_by_issn.yml └── view │ └── holding_test.rb └── umlaut.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle/ 2 | log/*.log 3 | pkg/ 4 | test/dummy/db/*.sqlite3 5 | test/dummy/log/ 6 | test/dummy/tmp/ 7 | # as we need a mysql db, we can't distro a database.yml 8 | # for test dummy app that works out of box, so leave it 9 | # out of git so developers can use their own without trouble. 10 | test/dummy/config/database.yml 11 | Gemfile.lock 12 | tmp/ 13 | coverage/ 14 | .rbx -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | sudo: false 3 | cache: bundler 4 | bundler_args: --without debug 5 | rvm: 6 | - 1.9.3 7 | - jruby-19mode 8 | # - rbx-19mode 9 | - 2.0 10 | - 2.1.5 11 | - 2.2.2 12 | env: 13 | global: 14 | - NOKOGIRI_USE_SYSTEM_LIBRARIES=true 15 | # We need to install latest version of bundler, because one in travis 16 | # image is too old to recognize platform => :mri_22 in Gemfile. 17 | before_install: 18 | - gem install bundler 19 | before_script: 20 | - mysql -e 'create database umlaut3_test;' 21 | - mysql -e 'create database sfxlcl41_test;' 22 | - mv test/dummy/config/travis_database.yml test/dummy/config/database.yml 23 | - RAILS_ENV=test bundle exec rake --trace db:schema:load db:migrate 24 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | # Declare your gem's dependencies in umlaut.gemspec. 4 | # Bundler will treat runtime dependencies like base dependencies, and 5 | # development dependencies will be added by default to the :development group. 6 | gemspec 7 | 8 | group :development, :test do 9 | 10 | platforms :jruby do 11 | gem 'activerecord-jdbc-adapter', "~> 1.2", ">= 1.2.9" 12 | gem 'jdbc-mysql', ">= 5.1.24", :require => false 13 | gem 'jruby-rack' 14 | gem 'therubyrhino' 15 | gem 'jruby-openssl' 16 | end 17 | 18 | platforms :ruby do 19 | gem 'mysql2', ">= 0.3.11", "< 0.4.0" # mysql2 0.4.0 does not work with rails 4.2.4. Later Rails it should. 20 | # the ruby racer needed for running app tests on platforms 21 | # without javascript runtime found. 0.12 is having a hard 22 | # time installing on my OSX, 0.11.x is good enough for these purposes. 23 | gem 'therubyracer', "~> 0.11.0" 24 | end 25 | 26 | platforms :mri do 27 | gem 'ruby-prof', "~> 0.13.0" 28 | end 29 | 30 | gem 'jquery-rails' 31 | gem "activerecord-import" 32 | end 33 | 34 | group :debug do 35 | gem 'debugger', :platform => :mri_19 36 | gem 'byebug', :platform => [:mri_21, :mri_22] 37 | gem 'ruby-debug', :platform => :jruby 38 | end 39 | 40 | # Add coveralls for testing. 41 | gem "coveralls", "~> 0.6.0", :require => false, :group => :test 42 | 43 | # This is experimental and mainly used for testing. If you want to test against 44 | # Rails 3.2, try: 45 | # $ RAILS_GEM_SPEC="~> 3.2.0" bundle update 46 | # $ RAILS_GEM_SPEC="~> 3.2.0" bundle exec rake test 47 | if ENV["RAILS_GEM_SPEC"] 48 | gem "rails", ENV["RAILS_GEM_SPEC"] 49 | # Our tests assume minitest, but Rails 3 is only compatible with 50 | # older versions of minitest. This works for now. 51 | if ENV["RAILS_GEM_SPEC"] =~ /([\d.]+)/ && $1.split(".")[0].to_i < 4 52 | gem "minitest", "~> 4.0" 53 | end 54 | end 55 | 56 | # Declare any dependencies that are still in development here instead of in 57 | # your gemspec. These might include edge Rails or gems from your path or 58 | # Git. Remember to move these dependencies to your gemspec before releasing 59 | # your gem to rubygems.org. 60 | 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Umlaut is copyright (c) 2006-2007 Ross Singer , Jonathan Rochkind , Georgia Tech University, and Johns Hopkins University. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | begin 3 | require 'bundler/setup' 4 | rescue LoadError 5 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks' 6 | end 7 | begin 8 | require 'rdoc/task' 9 | rescue LoadError 10 | require 'rdoc/rdoc' 11 | require 'rake/rdoctask' 12 | RDoc::Task = Rake::RDocTask 13 | end 14 | 15 | RDoc::Task.new(:rdoc) do |rdoc| 16 | rdoc.rdoc_dir = 'rdoc' 17 | rdoc.title = 'Umlaut' 18 | rdoc.options << '--line-numbers' 19 | rdoc.rdoc_files.include('README.rdoc') 20 | rdoc.rdoc_files.include('lib/**/*.rb') 21 | end 22 | 23 | APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__) 24 | load 'rails/tasks/engine.rake' 25 | 26 | Bundler::GemHelper.install_tasks 27 | 28 | require 'rake/testtask' 29 | 30 | Rake::TestTask.new(:test) do |t| 31 | t.libs << 'lib' 32 | t.libs << 'test' 33 | t.pattern = 'test/**/*_test.rb' 34 | t.verbose = false 35 | end 36 | 37 | task :default => :test 38 | -------------------------------------------------------------------------------- /app/assets/images/error.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/app/assets/images/error.gif -------------------------------------------------------------------------------- /app/assets/images/frame_remove.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/app/assets/images/frame_remove.gif -------------------------------------------------------------------------------- /app/assets/images/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/app/assets/images/spinner.gif -------------------------------------------------------------------------------- /app/assets/images/umlaut_icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/app/assets/images/umlaut_icons.png -------------------------------------------------------------------------------- /app/assets/images/umlaut_icons/famfamfam-book-go.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/app/assets/images/umlaut_icons/famfamfam-book-go.png -------------------------------------------------------------------------------- /app/assets/images/umlaut_icons/famfamfam-book-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/app/assets/images/umlaut_icons/famfamfam-book-open.png -------------------------------------------------------------------------------- /app/assets/images/umlaut_icons/famfamfam-book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/app/assets/images/umlaut_icons/famfamfam-book.png -------------------------------------------------------------------------------- /app/assets/images/umlaut_icons/famfamfam-cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/app/assets/images/umlaut_icons/famfamfam-cross.png -------------------------------------------------------------------------------- /app/assets/images/umlaut_icons/famfamfam-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/app/assets/images/umlaut_icons/famfamfam-error.png -------------------------------------------------------------------------------- /app/assets/images/umlaut_icons/famfamfam-help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/app/assets/images/umlaut_icons/famfamfam-help.png -------------------------------------------------------------------------------- /app/assets/images/umlaut_icons/famfamfam-information.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/app/assets/images/umlaut_icons/famfamfam-information.png -------------------------------------------------------------------------------- /app/assets/images/umlaut_icons/famfamfam-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/app/assets/images/umlaut_icons/famfamfam-link.png -------------------------------------------------------------------------------- /app/assets/images/umlaut_icons/famfamfam-page-sound.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/app/assets/images/umlaut_icons/famfamfam-page-sound.png -------------------------------------------------------------------------------- /app/assets/images/umlaut_icons/famfamfam-page-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/app/assets/images/umlaut_icons/famfamfam-page-text.png -------------------------------------------------------------------------------- /app/assets/images/umlaut_icons/famfamfam-page-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/app/assets/images/umlaut_icons/famfamfam-page-up.png -------------------------------------------------------------------------------- /app/assets/images/umlaut_icons/famfamfam-page-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/app/assets/images/umlaut_icons/famfamfam-page-white.png -------------------------------------------------------------------------------- /app/assets/images/umlaut_icons/famfamfam-tiny-cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/app/assets/images/umlaut_icons/famfamfam-tiny-cross.png -------------------------------------------------------------------------------- /app/assets/images/umlaut_icons/list-closed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/app/assets/images/umlaut_icons/list-closed.png -------------------------------------------------------------------------------- /app/assets/images/umlaut_icons/list-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/app/assets/images/umlaut_icons/list-open.png -------------------------------------------------------------------------------- /app/assets/javascripts/umlaut.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into including all 2 | // umlaut files. It can be included in a rails application.js manifest 3 | // as: 4 | // require 'umlaut' 5 | // to include all umlaut js. 6 | 7 | 8 | // jquery is required for umlaut, it's okay 9 | // if the manifest chain ends up 'require'ing twice because 10 | // it's mentioned in local manifest, sprockets is smart enough. 11 | //= require jquery 12 | 13 | // We're using a local bootstrap3-typeahead.js, to restore bootstrap2-style 14 | //= require bootstrap3-typeahead 15 | 16 | //= require bootstrap/transition 17 | //= require bootstrap/modal 18 | // #require bootstrap/typeahead 19 | //= require bootstrap/collapse 20 | 21 | // Require all js files inside the 'umlaut' subdir next to this file. 22 | //= require_tree './umlaut' 23 | -------------------------------------------------------------------------------- /app/assets/javascripts/umlaut/ajax_windows.js: -------------------------------------------------------------------------------- 1 | /* ajax_windows.js. Support for modal popup windows in Umlaut items. */ 2 | jQuery(document).ready(function($) { 3 | var populate_modal = function(data, textStatus, jqXHR) { 4 | // Wrap the data object in jquery object 5 | var body = $("
").html(data); 6 | // Remove the first heading from the returned data 7 | var header = body.find("h1, h2, h3, h4, h5, h6").eq(0).remove(); 8 | // Remove the first submit button from the returned data 9 | var footer = body.find("form").find("input[type=submit]").eq(0).remove(); 10 | 11 | // Add in content 12 | if (header) $("#modal").find("[data-role=modal-title-content]").text(header.text()); 13 | if (body) $("#modal").find("[data-role=modal-body-content]").html(body.html()); 14 | if (footer) $("#modal").find("[data-role=modal-footer-content]").html(footer); 15 | // Toggle the ajax-loader 16 | $("#modal").find(".ajax-loader").hide(); 17 | } 18 | var cleanup_modal = function() { 19 | $("#modal").find("[data-role=modal-title-content]").text(''); 20 | $("#modal").find("[data-role=modal-body-content]").text(''); 21 | $("#modal").find("[data-role=modal-footer-content]").text(''); 22 | $("#modal").find(".ajax-loader").hide(); 23 | } 24 | var display_modal = function(event) { 25 | event.preventDefault(); 26 | cleanup_modal(); 27 | $("#modal").find(".ajax-loader").show(); 28 | $("#modal").modal("show"); 29 | $.get(this.href, "", populate_modal, "html"); 30 | } 31 | var ajax_form_catch = function(event) { 32 | event.preventDefault(); 33 | $("#modal").find(".ajax-loader").show(); 34 | var form = $("#modal").find("form"); 35 | $.post(form.attr("action"), form.serialize(), populate_modal, "html"); 36 | cleanup_modal(); 37 | }; 38 | $(document).on("click", "a.ajax_window", display_modal); 39 | $(document).on("click", "#modal .modal-footer input[type=submit]", ajax_form_catch); 40 | $(document).on("submit", "#modal form", ajax_form_catch); 41 | }); -------------------------------------------------------------------------------- /app/assets/javascripts/umlaut/ensure_window_size.js.erb: -------------------------------------------------------------------------------- 1 | // Some code to re-size our window to config defined minimum height/width 2 | // We have little control over what size a content provider generates a 3 | // window for a link resolver. Often it's too small. So we resize in 4 | // js. 5 | 6 | // Some browsers won't let us resize the window though. Oh well, we try. 7 | // Those that wont' let us just silently no-op. 8 | 9 | jQuery(document).ready(function($) { 10 | var min_width = <%= UmlautController.umlaut_config.lookup!('minimum_window_width', 0).to_i %>; 11 | var min_height = <%= UmlautController.umlaut_config.lookup!('minimum_window_height', 0).to_i %>; 12 | 13 | // Default to something huge, so if we fail in getting dimensions, 14 | // we won't resize. 15 | var orig_width = 100000; 16 | var orig_height = 100000; 17 | 18 | // JQuery document viewport width/height 19 | 20 | orig_width = $(window).width(); 21 | orig_height = $(window).height(); 22 | 23 | 24 | 25 | width_diff = min_width - orig_width; 26 | height_diff = min_height - orig_height; 27 | 28 | if (width_diff < 0) { width_diff = 0 } 29 | if (height_diff < 0) { height_diff = 0 } 30 | 31 | if (width_diff >0 || height_diff > 0) { 32 | window.resizeBy(width_diff, height_diff) 33 | } 34 | }); 35 | -------------------------------------------------------------------------------- /app/assets/javascripts/umlaut/expand_contract_toggle.js: -------------------------------------------------------------------------------- 1 | /* expand_contract_toggle.js: Support for show more/hide more in lists of umlaut content. 2 | 3 | Expand/collapse elements are already controlled via Bootstrap toggle, 4 | this just adds some additional behavior in hooks to change our labels 5 | and disclosure icons appropriately, and prevent following non-js href links. 6 | */ 7 | jQuery(document).ready(function($) { 8 | $(document).on("click", ".collapse-toggle", function(event) { 9 | event.preventDefault(); 10 | return false; 11 | }); 12 | $(document).on("show.bs.collapse", ".collapse", function(event) { 13 | // Update the icon 14 | $(this).parent().find('.collapse-toggle i').removeClass("umlaut_icons-list-closed").addClass("umlaut_icons-list-open"); 15 | // Update the action label 16 | $(this).parent().find(".expand_contract_action_label").text("Hide "); 17 | 18 | }); 19 | $(document).on("hide.bs.collapse", ".collapse", function(event) { 20 | // Update the icon 21 | $(this).parent().find('.collapse-toggle i').removeClass("umlaut_icons-list-open").addClass("umlaut_icons-list-closed"); 22 | // Update the action label 23 | $(this).parent().find(".expand_contract_action_label").text("Show "); 24 | 25 | }); 26 | }); 27 | 28 | -------------------------------------------------------------------------------- /app/assets/javascripts/umlaut/load_permalink.js: -------------------------------------------------------------------------------- 1 | jQuery(document).ready(function($) { 2 | 3 | 4 | $("*[data-umlaut-toggle-permalink]").click(function(event) { 5 | event.preventDefault(); 6 | 7 | var originalLink = $(this) 8 | var valueContainer = $("#umlaut-permalink-container"); 9 | 10 | if (! valueContainer.data("loaded")) { 11 | valueContainer.html('').show(); 12 | 13 | $.getJSON( originalLink.attr('href'), function(data) { 14 | var href = data.permalink; 15 | var a = $(""); 16 | a.attr("href", href); 17 | a.text(href); 18 | valueContainer.html(a).data("loaded", true).show(); 19 | }); 20 | } 21 | else { 22 | valueContainer.toggle(); 23 | } 24 | }); 25 | 26 | }); 27 | -------------------------------------------------------------------------------- /app/assets/javascripts/umlaut_ui.js: -------------------------------------------------------------------------------- 1 | /* This JS file can be referenced by external apps to add Umlaut JS UI behaviors 2 | to a page that has had Umlaut content added to it via partial html snippets. 3 | 4 | This is a sprockets manifest file requiring individual js behavior 5 | files applicable. 6 | 7 | At present, only expand/contract toggle behavior is actually supported, 8 | others are non-applicable or hard to get working on an external site 9 | due to cross-domain-origin stuff. 10 | 11 | And even this has become VERY HACKY AND FRAGILE these days -- 12 | this whole concept may be nontenable. 13 | 14 | *= require 'umlaut/expand_contract_toggle.js' 15 | 16 | */ 17 | 18 | /* Normal umlaut uses bootstrap collapse, and expand_contract_toggle.js 19 | assumes bootstrap collapse. For vended use as here, provide our own 20 | simple kind of crappy replacement for bootstrap collapse, which 21 | will combine with expand_contract_toggle.js above to completely implement. */ 22 | 23 | jQuery(document).ready(function($) { 24 | 25 | // In ordinary umlaut bootstrap collapsible, it automatically hides 26 | // .collapse, but not with display:none. For our replacement here, 27 | // we need to make sure it's initially hidden with display:none, 28 | // we'll add a style to do so. 29 | $('html > head').append($('')); 30 | 31 | 32 | $(document).on("click", ".collapse-toggle", function(event) { 33 | content = $( $(this).attr('data-target') ); 34 | 35 | if ( content.is(":visible") ) { 36 | content.slideUp(); 37 | content.trigger("hide"); 38 | } 39 | else { 40 | content.slideDown(); 41 | content.trigger("show"); 42 | } 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /app/assets/stylesheets/umlaut.css.scss: -------------------------------------------------------------------------------- 1 | // Bootstrap Variable over-rides can go here 2 | 3 | // We default the Bootstrap 2.x colors for links, they worked 4 | // well with our design, and the Bootstrap 3.x colors seem 5 | // too muted and hard to read in our design. 6 | $link-color: #08c !default; 7 | $link-hover-color: darken($link-color, 15%) !default; 8 | 9 | 10 | @import "bootstrap-sprockets"; 11 | @import "bootstrap"; 12 | 13 | @import "umlaut/variables"; 14 | @import "umlaut/mixins"; 15 | @import "umlaut/layout"; 16 | @import "umlaut/icons"; 17 | @import "umlaut/forms"; 18 | @import "umlaut/misc"; 19 | @import "umlaut/az"; 20 | @import "umlaut/search"; 21 | @import "umlaut/results"; 22 | @import "umlaut/resolve"; 23 | @import "umlaut/spinner"; 24 | @import "umlaut/admin"; 25 | @import "umlaut/modal"; 26 | 27 | -------------------------------------------------------------------------------- /app/assets/stylesheets/umlaut/_admin.scss: -------------------------------------------------------------------------------- 1 | // ADMIN SCREENS 2 | // Most of admin screen CSS is about _undoing_ general customizations 3 | // umlaut css (or over-eager reset.css we're using, bah) 4 | // did to all HTML elements, oops. 5 | 6 | .admin { 7 | ul { 8 | list-style-type: disc; 9 | } 10 | 11 | form { 12 | margin-bottom: 1em; 13 | } 14 | } -------------------------------------------------------------------------------- /app/assets/stylesheets/umlaut/_az.scss: -------------------------------------------------------------------------------- 1 | // Az links based on bootstrap pagination, but tweaked, among other 2 | // things to wrap better. a-z links are ridiculous, but expected by 3 | // our users. 4 | .umlaut-az { 5 | @include clearfix; 6 | a, span { 7 | float: left; 8 | margin-bottom: $line-height-computed / 4; 9 | margin-right: $line-height-computed / 4; 10 | border-radius: 4px; 11 | padding: 4px 8px; 12 | border: 1px solid $umlautAzBorderColor; 13 | line-height: $line-height-base; 14 | text-decoration: none; 15 | color: $umlautAzLinkColor; 16 | background-color: $pagination-bg; 17 | &:hover { 18 | background-color: $pagination-hover-bg; 19 | } 20 | 21 | &.active { 22 | background-color: transparent; 23 | color: inherit; 24 | &:hover { 25 | background-color: transparent;; 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/assets/stylesheets/umlaut/_forms.scss: -------------------------------------------------------------------------------- 1 | input[type="submit"] { 2 | @extend .btn; 3 | } 4 | 5 | /* sub-groups of controls in span.control-sub-group */ 6 | .form-inline { 7 | .form-group { 8 | //display: inline-block; 9 | line-height: ($line-height-base * 2); 10 | margin-right: $umlautIndent; 11 | } 12 | .control-sub-group { 13 | display: inline-block; 14 | margin-right: $umlautIndent; 15 | } 16 | } 17 | 18 | @media (max-width: ($screen-sm-min - 1)) { 19 | .umlaut-citation-link { 20 | text-align: left; 21 | } 22 | } 23 | 24 | // even smaller than bootstrap's input-mini, for volume/issue/start page 25 | .form-inline .form-control.input-tiny, .input-tiny { 26 | width: 5em; 27 | } 28 | 29 | /* These seems to be a bug in bootstrap 3.2.0.1... */ 30 | input[type=submit].btn-default { 31 | border-color: $btn-default-border; 32 | } 33 | input.btn-primary[type="submit"]:hover, input[type="submit"].btn-primary:focus { 34 | color: white; 35 | background-color: #3071a9; 36 | border-color: #285e8e; 37 | } -------------------------------------------------------------------------------- /app/assets/stylesheets/umlaut/_icons.scss: -------------------------------------------------------------------------------- 1 | [class^="umlaut_icons-"], 2 | [class*=" umlaut_icons-"] { 3 | @include umlaut_icons; 4 | } 5 | 6 | .umlaut_icons-famfamfam-book-go { 7 | background-position: 0 -32px; 8 | } 9 | 10 | .umlaut_icons-famfamfam-book-open { 11 | background-position: 0 -96px; 12 | } 13 | 14 | .umlaut_icons-famfamfam-book { 15 | background-position: 0 -112px; 16 | } 17 | 18 | .umlaut_icons-famfamfam-cross { 19 | background-position: 0 -80px; 20 | } 21 | 22 | .umlaut_icons-famfamfam-error { 23 | background-position: 0 -64px; 24 | } 25 | 26 | .umlaut_icons-famfamfam-help { 27 | background-position: 0 0; 28 | } 29 | 30 | .umlaut_icons-famfamfam-information { 31 | background-position: 0 -16px; 32 | } 33 | 34 | .umlaut_icons-famfamfam-link { 35 | background-position: 0 -160px; 36 | } 37 | 38 | .umlaut_icons-famfamfam-page-sound { 39 | background-position: 0 -48px; 40 | } 41 | 42 | .umlaut_icons-famfamfam-page-text { 43 | background-position: 0 -144px; 44 | } 45 | 46 | .umlaut_icons-famfamfam-page-up { 47 | background-position: 0 -128px; 48 | } 49 | 50 | .umlaut_icons-famfamfam-page-white { 51 | background-position: 0 -184px; 52 | } 53 | 54 | .umlaut_icons-famfamfam-tiny-cross { 55 | background-position: 0 -176px; 56 | } 57 | 58 | .umlaut_icons-list-closed { 59 | background-position: 0 -216px; 60 | } 61 | 62 | .umlaut_icons-list-open { 63 | background-position: 0 -200px; 64 | } 65 | -------------------------------------------------------------------------------- /app/assets/stylesheets/umlaut/_layout.scss: -------------------------------------------------------------------------------- 1 | // Bootstrap does some weird things with body/container-fluid margin/padding 2 | // swapping it around at different sizes. We had trouble working with that, 3 | // so make it more consistent here. 4 | body { 5 | background-color: $umlautBackgroundColor; 6 | padding: 0; 7 | margin: 0; 8 | } 9 | .container-fluid { 10 | @include umlaut-container(); 11 | // max width and center beyond that 12 | max-width: $umlautContainerMaxWidth; 13 | } 14 | 15 | .header { 16 | @include umlaut-container(); 17 | @include clearfix(); 18 | background-color: $umlautHeaderBackgroundColor; 19 | border: { 20 | bottom: 1px solid $umlautHeaderBorderColor; 21 | } 22 | color: $navbar-default-brand-color; 23 | h1 { 24 | //semantically an h1, but let's make it not so big, inspired 25 | //by twitter's navbar styles, copied 26 | display: inline; 27 | float: left; 28 | margin: 0 $umlautIndent 0 0; 29 | padding: 0; 30 | font-size: 20px; 31 | font-weight: 200; 32 | line-height: 40px; 33 | } 34 | line-height: 40px; 35 | } 36 | 37 | 38 | .footer { 39 | @include umlaut-container(); 40 | @extend .text-muted; 41 | margin: { 42 | top: $line-height-computed; 43 | } 44 | padding: { 45 | top: 10px; 46 | bottom: 10px; 47 | } 48 | border: { 49 | top: 1px solid $umlautBorderColor; 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /app/assets/stylesheets/umlaut/_misc.scss: -------------------------------------------------------------------------------- 1 | .umlaut-separator { 2 | border-top: 1px solid $umlautBorderColor; 3 | padding-top: 15px; 4 | } 5 | 6 | .text-small { 7 | font: { 8 | size: $font-size-small 9 | } 10 | } 11 | 12 | .alert { 13 | li { 14 | margin-top: 1em; 15 | } 16 | } 17 | 18 | .collapsible { 19 | // Difficult to get space between the toggle and the expanded 20 | // content, that's only there when content is expanded, without 21 | // causing a jump on expand/contract. 22 | // We give up and provide the space even when collapsed. 23 | .collapse-toggle { 24 | display: block; 25 | margin-bottom: $line-height-computed / 2; 26 | } 27 | } 28 | 29 | /* Google Scholar Link: 30 | Make the example look like it does on google scholar: */ 31 | .gscholar_example { 32 | color: blue; 33 | font-variant: small-caps; 34 | } 35 | 36 | .umlaut-permalink { 37 | text-align: right; 38 | margin-left: $umlautIndent; 39 | } 40 | 41 | .umlaut-permalink-container { 42 | color: $gray-dark; 43 | @include text-overflow; 44 | } 45 | .umlaut-permalink-content { 46 | background-color: $umlautBackgroundColor; 47 | border-radius: $border-radius-small; 48 | border: 1px solid $btn-default-border; 49 | padding: 3px 6px; 50 | .spinner { 51 | vertical-align: baseline; 52 | } 53 | } 54 | 55 | .umlaut-load-permalink i { 56 | @include umlaut_icons; 57 | @extend .umlaut_icons-famfamfam-link; 58 | margin-top: 2px; 59 | } 60 | 61 | .umlaut-locale-selector { 62 | float: right; 63 | a { 64 | padding: 20px; 65 | } 66 | } 67 | 68 | // Used to conveniently add a mini spinner to page with 69 | // JS, usually by adding an 70 | .spinner { 71 | display: inline-block; 72 | width: 16px; 73 | height: 16px; 74 | line-height: 16px; 75 | vertical-align: top; 76 | margin-top: 1px; 77 | background-image: image-url("spinner.gif"); 78 | } 79 | -------------------------------------------------------------------------------- /app/assets/stylesheets/umlaut/_mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin umlaut-container { 2 | // double left gutter/margin when big enough 3 | padding-left: $umlautContainerPadding * 2; 4 | padding-right: $umlautContainerPadding * 2; 5 | 6 | // get rid of double left margin at smaller 7 | @media (max-width: $screen-sm-min) { 8 | padding: { 9 | left: $umlautContainerPadding; 10 | right: $umlautContainerPadding; 11 | } 12 | } 13 | 14 | // IE8 and other browsers without media query, 15 | // we need to keep it from getting too small, 16 | // cause it won't use the styles for small. 17 | // we use a media query as 'feature detection' in our CSS 18 | min-width: 600px; 19 | @media (min-width: 0) { 20 | min-width: 0; 21 | } 22 | } 23 | 24 | @mixin umlaut_icons { 25 | background: { 26 | image: image-url("umlaut_icons.png"); 27 | repeat: no-repeat; 28 | } 29 | display: inline-block; 30 | width: 16px; 31 | height: 16px; 32 | line-height: 16px; 33 | vertical-align: top; 34 | margin-top: 1px; 35 | } 36 | 37 | @mixin iconed-response { 38 | .response_item { 39 | a { 40 | font-weight: bold; 41 | } 42 | i { 43 | @include umlaut_icons; 44 | } 45 | } 46 | } 47 | 48 | @mixin umlaut-well($backgroundColor, $borderColor) { 49 | /* copied from bootstrap well, because we change parts, not worth 50 | it to override, we change so much and well is so simple. */ 51 | background-color: $backgroundColor; 52 | min-height: 20px; 53 | 54 | padding: 12px; 55 | margin-left: -12px; /* so left edge of text still lines up */ 56 | margin-right: -12px; 57 | 58 | 59 | border: 1px solid $borderColor; 60 | border-radius: $border-radius-base; 61 | @include box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); 62 | } 63 | 64 | 65 | -------------------------------------------------------------------------------- /app/assets/stylesheets/umlaut/_modal.scss: -------------------------------------------------------------------------------- 1 | .modal ul { 2 | margin-left: 0; 3 | list-style: none; 4 | } -------------------------------------------------------------------------------- /app/assets/stylesheets/umlaut/_results.scss: -------------------------------------------------------------------------------- 1 | .umlaut-results { 2 | @extend .umlaut-separator; 3 | background-color: $umlautResultsBackgroundColor; 4 | } 5 | 6 | .umlaut-search-container .pagination { 7 | margin: 10px 0; 8 | } 9 | 10 | .umlaut-result { 11 | background-color: $umlautResultItemBackgroundColor; 12 | border-color: $umlautResultItemBorderColor; 13 | margin-bottom: $line-height-computed/2; 14 | dl { 15 | margin: 0; 16 | dt, dd { 17 | margin-bottom: $line-height-computed/4; 18 | } 19 | } 20 | 21 | padding: $umlautIndent; 22 | 23 | .umlaut-citation-link { 24 | text-align: right; 25 | } 26 | } 27 | 28 | @media (max-width: $screen-sm-min) { 29 | .umlaut-result { 30 | .umlaut-citation-link { 31 | text-align: left; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /app/assets/stylesheets/umlaut/_search.scss: -------------------------------------------------------------------------------- 1 | .umlaut-search-form { 2 | padding: 10px; 3 | border: 1px solid $umlautSearchFormBorderColor; 4 | background-color: $umlautSearchFormBackgroundColor; 5 | margin-top: $line-height-computed; 6 | margin-bottom: $line-height-computed; 7 | border-radius: $border-radius-base; 8 | legend { 9 | border-color: $umlautSearchFormBorderColor; 10 | } 11 | .row { 12 | margin-bottom: $line-height-computed; 13 | } 14 | 15 | .form-control.year, .form-control.month, .form-control.day, .form-control.doi { 16 | width: 90px; 17 | } 18 | label { 19 | font-weight: normal; 20 | } 21 | 22 | input.title_search { 23 | width: 20em; 24 | } 25 | } -------------------------------------------------------------------------------- /app/assets/stylesheets/umlaut/_spinner.scss: -------------------------------------------------------------------------------- 1 | .background_progress_spinner { 2 | @extend .text-muted; 3 | img { 4 | vertical-align: top; 5 | margin-top: 1px; 6 | } 7 | } 8 | 9 | .umlaut-resource-info .background_progress_spinner { 10 | padding-left: 24px; 11 | padding-top: 8px; 12 | } -------------------------------------------------------------------------------- /app/controllers/admin/service_errors_controller.rb: -------------------------------------------------------------------------------- 1 | module Admin 2 | class ServiceErrorsController < UmlautController 3 | # by default, force ssl connection in production env, but can be 4 | # config'd. 5 | force_ssl if umlaut_config.lookup!("admin.force_ssl", Rails.env.production? ) 6 | # Cheesy way to do auth from config single or multiple. Alternately, 7 | # you might want to protect at apache level, for instance for shib 8 | # or SSO integration 9 | if (admin_auth = umlaut_config.lookup!("admin.auth")) 10 | before_filter lambda { 11 | authenticate_or_request_with_http_basic do |login, password| 12 | login == admin_auth[:username] && password == admin_auth[:password] 13 | end 14 | } 15 | end 16 | 17 | 18 | def index 19 | # grab the earliest dispatch to see how far back our db goes 20 | @earliest_dispatch = DispatchedService.select("updated_at"). 21 | order("updated_at"). 22 | limit(1). 23 | first. 24 | updated_at 25 | 26 | 27 | errors_base = DispatchedService. 28 | where(:status => [DispatchedService::FailedFatal, DispatchedService::FailedTemporary]) 29 | 30 | if params[:service_id] 31 | errors_base = errors_base.where(:service_id => params[:service_id]) 32 | end 33 | 34 | if params[:q] 35 | errors_base = errors_base.where("exception_info #{" NOT " if params[:q_not]} like ?", "%#{params[:q]}%") 36 | end 37 | 38 | # will miraculously return a hash whose key is service_id, value 39 | # is count of failed service dispatches. 40 | @failed_by_service = errors_base.select("service_id"). 41 | group("service_id"). 42 | count 43 | 44 | # And get the most recent batch of failed services 45 | # kaminari page/per 46 | @offset = params[:offset].to_i 47 | @limit = params[:per_page].to_i 48 | @limit = 10 if @limit == 0 49 | 50 | @dispatched_services = errors_base.order("updated_at DESC"). 51 | limit(@limit).offset(@offset) 52 | @dispatched_services_count = errors_base.count 53 | end 54 | 55 | 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /app/controllers/export_email_controller.rb: -------------------------------------------------------------------------------- 1 | class ExportEmailController < UmlautController 2 | 3 | before_filter :load_objects 4 | layout :search_layout_except_xhr 5 | 6 | def email 7 | end 8 | 9 | def send_email 10 | @email = params[:email] 11 | @fulltexts = @user_request.get_service_type('fulltext', { :refresh=>true }) 12 | @holdings = @user_request.get_service_type('holding', { :refresh=>true }) 13 | if valid_email? 14 | action_mailer_deliver Emailer.citation(@email, @user_request, @fulltexts, @holdings) 15 | else 16 | flash[:alert] = email_validation_error 17 | render :email and return 18 | end 19 | end 20 | 21 | def txt 22 | end 23 | 24 | def send_txt 25 | @number = params[:number] 26 | # Remove any punctuation or spaces etc 27 | @number.gsub!(/[^\d]/, '') if @number 28 | @provider = params[:provider] 29 | @email = "#{@number}@#{@provider}" unless @number.nil? or @provider.nil? 30 | @holding = params[:holding] 31 | if valid_txt_number? && valid_txt_holding? 32 | action_mailer_deliver Emailer.short_citation(@email, @user_request, holding_location(@holding), call_number(@holding)) 33 | else 34 | flash[:alert] = txt_validation_error 35 | render :txt and return 36 | end 37 | end 38 | 39 | protected 40 | 41 | def load_objects 42 | @svc_response = ServiceResponse.find(params[:id]) 43 | @user_request = @svc_response.request if @svc_response 44 | end 45 | 46 | def valid_txt_number? 47 | ((not @number.blank?) && @number.length == 10) 48 | end 49 | 50 | def valid_txt_holding? 51 | (not @holding.blank?) 52 | end 53 | 54 | def valid_email? 55 | (@email =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i) 56 | end 57 | 58 | def txt_validation_error 59 | errors = [] 60 | errors << "a valid number" unless valid_txt_number? 61 | errors << "the item you wish to send" unless valid_txt_holding? 62 | errors 63 | end 64 | 65 | def email_validation_error 66 | errors = [] 67 | errors << "a valid email address" 68 | errors 69 | end 70 | 71 | def holding(id) 72 | return ServiceResponse.find(id) unless id.nil? 73 | end 74 | 75 | def holding_location(id) 76 | return holding(id).view_data[:collection_str] unless holding(id).nil? 77 | end 78 | 79 | def call_number(id) 80 | return holding(id).view_data[:call_number] unless holding(id).nil? 81 | end 82 | end -------------------------------------------------------------------------------- /app/controllers/feedback_controller.rb: -------------------------------------------------------------------------------- 1 | class FeedbackController < UmlautController 2 | def new 3 | contact_email_lookup(params[:contact_id]) 4 | # default render 5 | end 6 | 7 | def create 8 | contact_config = contact_email_lookup(params[:contact_id]) 9 | to_address = contact_config[:email_address] 10 | 11 | options = params.slice(:name, :email, :feedback) 12 | if params[:request_id] && umlaut_request = Request.find_by_id(params[:request_id]) 13 | options = options.merge( 14 | :umlaut_request => umlaut_request 15 | ) 16 | end 17 | 18 | action_mailer_deliver FeedbackMailer.feedback(request.host_with_port, to_address, options) 19 | 20 | flash[:alert_success] = "Thanks, your message has been sent." 21 | 22 | if umlaut_request 23 | redirect_to :controller => "resolve", :action => :index, "umlaut.request_id" => umlaut_request.id 24 | else 25 | redirect_to root_url 26 | end 27 | end 28 | 29 | protected 30 | def contact_email_lookup(contact_id) 31 | unless contact_id 32 | raise NoFeedbackEmailFoundException.new("Missing a contact_id, needed to look up feedback destination email.") 33 | end 34 | contact_config = umlaut_config.feedback && umlaut_config.feedback.contacts && umlaut_config.feedback.contacts[contact_id] 35 | 36 | unless contact_config && contact_config[:email_address] 37 | raise NoFeedbackEmailFoundException.new("Could not find feedback destination email for contact_id: `#{contact_id}`") 38 | end 39 | 40 | return contact_config 41 | end 42 | 43 | class NoFeedbackEmailFoundException < ArgumentError 44 | end 45 | 46 | end -------------------------------------------------------------------------------- /app/controllers/js_helper_controller.rb: -------------------------------------------------------------------------------- 1 | # Controller that just has one helper action for external sites using 2 | # our Javascript HTML updater stuff. 3 | class JsHelperController < UmlautController 4 | 5 | def loader 6 | @generate_urls_with_host = true 7 | render :template => "js_helper/loader.erb.js" 8 | end 9 | end -------------------------------------------------------------------------------- /app/controllers/open_search_controller.rb: -------------------------------------------------------------------------------- 1 | class OpenSearchController < UmlautController 2 | 3 | layout false 4 | 5 | def index 6 | render(:content_type => "application/xml") 7 | end 8 | end -------------------------------------------------------------------------------- /app/controllers/search_methods/README.md: -------------------------------------------------------------------------------- 1 | SearchMethods 2 | === 3 | SearchMethods' allow Umlaut to search an arbitrary data source for A-Z Journals. 4 | Implementations must adhere to the following method signatures. 5 | The module is included in the SearchController and has access to instance methods and 6 | instance variables from the SearchController. 7 | 8 | Class Methods: 9 | --- 10 | These class methods are used only in the `:load_sfx_urls` rake task. 11 | They are expected to be deprecated in future Umlaut releases. 12 | 13 | - `::fetch_urls?() `==> `Boolean`
14 | Returns a Boolean indicating whether the module will return URLs 15 | 16 | - `::fetch_urls()` ==> `Array`
17 | Returns an array of strings representing URLs that are "owned" by the search 18 | system 19 | 20 | Instance Methods: 21 | --- 22 | - `#find_by_title()` ==> `[Array, integer]`
23 | Returns a two element array consisting of an Array of OpenURL::ContextObject 24 | and the total count of records returned by the search 25 | 26 | - `#find_by_group()` ==> `[Array, integer]`
27 | Returns a two element array consisting of an Array of OpenURL::ContextObject 28 | and the total count of records returned by the search 29 | -------------------------------------------------------------------------------- /app/controllers/search_methods/sfx_api.rb: -------------------------------------------------------------------------------- 1 | module SearchMethods 2 | 3 | # NON-WORKING sketch of a search method that contacts SFX api directly. 4 | # Problem with this was that SFX is way too slow; SFX api didn't take 5 | # account of year/volume/issue when displaying multiple results anwyay, 6 | # so there wasn't that functionality benefit. It just wasn't worth it. 7 | # 8 | # This code is basically copied and pasted from before the refactor, 9 | # it's not close to working yet, but is left for archival purposes 10 | # in case anyone wants to take a stab at it. 11 | module SfxApi 12 | 13 | def find_by_title 14 | ctx = context_object_from_params 15 | search_results = [] 16 | 17 | sfx_url = umlaut_config.sfx_base_url 18 | unless (sfx_url) 19 | # try to guess it from our institutions 20 | instutitions = Institution.find_all_by_default_institution(true) 21 | instutitions.each { |i| i.services.each { |s| 22 | sfx_url = s.base_url if s.kind_of?(Sfx) }} 23 | end 24 | 25 | transport = OpenURL::Transport.new(sfx_url, ctx) 26 | transport.extra_args["sfx.title_search"] = params["sfx.title_search"] 27 | transport.extra_args["sfx.response_type"] = 'multi_obj_xml' 28 | 29 | 30 | transport.transport_inline 31 | 32 | doc = REXML::Document.new transport.response 33 | 34 | #client = SfxClient.new(ctx, resolver) 35 | 36 | doc.elements.each('ctx_obj_set/ctx_obj') { | ctx_obj | 37 | ctx_attr = ctx_obj.elements['ctx_obj_attributes'] 38 | next unless ctx_attr and ctx_attr.has_text? 39 | 40 | perl_data = ctx_attr.get_text.value 41 | search_results << Sfx.parse_perl_data( perl_data ) 42 | } 43 | return [search_results, doc.elements.length] 44 | end 45 | 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /app/controllers/umlaut/error_handling.rb: -------------------------------------------------------------------------------- 1 | # logic. 2 | module Umlaut::ErrorHandling 3 | extend ActiveSupport::Concern 4 | 5 | included do 6 | # Only custom errors in production 7 | unless Rails.application.config.consider_all_requests_local 8 | # generic catch-all comes first, later ones will take priority 9 | rescue_from Exception, :with => :handle_general_error 10 | rescue_from ActiveRecord::RecordNotFound, :with => :handle_404_error 11 | end 12 | end 13 | 14 | def handle_general_error(exception) 15 | 16 | log_error_with_context(exception) 17 | @page_title = "Error!" 18 | # Only render this if we haven't done anything else 19 | # e.g. if some other gem may be handling its own errors 20 | unless performed? 21 | if request.format.html? 22 | render "error", :status => 500 23 | else 24 | render :text => "Unexpected fatal error, has been logged.", :status => 500 25 | end 26 | end 27 | end 28 | protected :handle_general_error 29 | 30 | # Just returns a generic 404 page. 31 | # Uses generic 404 page already stored in public/404.html as rails convention. 32 | def handle_404_error(exception=nil) 33 | render :file=>File.join(Rails.root ,"public/404"), :layout=>false, :status=>404 34 | end 35 | protected :handle_404_error 36 | 37 | def log_error_with_context(exception, severity = :fatal) 38 | message = "\n#{exception.class} (#{exception.message}):\n" 39 | message << " uri: #{request.fullpath}\n" 40 | message << " params: #{params.inspect}\n" 41 | message << " Referer: #{request.referer}\n" if request.referer 42 | message << " User-Agent: #{request.user_agent}\n" 43 | message << " Client IP: #{request.remote_addr}\n\n" 44 | message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code) 45 | message << " " << Rails.backtrace_cleaner.clean(exception.backtrace).join("\n ") 46 | logger.send(severity, "#{message}\n\n") 47 | end 48 | protected :log_error_with_context 49 | end 50 | -------------------------------------------------------------------------------- /app/helpers/emailer_helper.rb: -------------------------------------------------------------------------------- 1 | module EmailerHelper 2 | include ApplicationHelper 3 | include Umlaut::Helper 4 | 5 | 6 | # returns a plain text short citation 7 | def brief_citation(request, options = {}) 8 | options[:include_labels] ||= false 9 | rv ="" 10 | cite = request.referent.to_citation 11 | title = truncate(cite[:title].strip, :length => 70, :separator => ' ') 12 | 13 | rv << (cite[:title_label].strip + ": ")if options[:include_labels] && cite[:title_label] 14 | rv << title 15 | rv << "\n" 16 | if cite[:author] 17 | rv << "#{t 'umlaut.citation.author_label'}:" if options[:include_labels] 18 | rv << cite[:author].strip 19 | rv << "\n" 20 | end 21 | if cite[:container_title] 22 | rv << (cite[:container_label].strip + ": ") if options[:include_labels] && cite[:container_label].present? 23 | rv << cite[:container_title].strip 24 | rv << "\n" 25 | end 26 | pub = [] 27 | pub << date_format(cite[:date]) unless cite[:date].blank? 28 | pub << "#{t 'umlaut.citation.volume_abbr' }: " + cite[:volume].strip unless cite[:volume].blank? 29 | pub << "#{t 'umlaut.citation.issue_abbr'}: " + cite[:issue].strip unless cite[:issue].blank? 30 | pub << "#{t 'umlaut.citation.page_abbr'} " + cite[:page].strip unless cite[:page].blank? 31 | if pub.length > 0 32 | rv << "#{t 'umlaut.citation.published'}: " if options[:include_labels] 33 | rv << pub.join(' ') 34 | end 35 | return rv 36 | end 37 | 38 | def citation_identifiers(request, options = {}) 39 | citation = request.referent.to_citation 40 | str = "" 41 | 42 | str << "#{t 'umlaut.citation.issn'}: #{citation[:issn]}\n" if citation[:issn] 43 | str << "#{t 'umlaut.citation.isbn'}: #{citation[:isbn]}\n" if citation[:isbn] 44 | citation[:identifiers].each do |identifier| 45 | str << "#{identifier}\n" 46 | end 47 | 48 | return str 49 | end 50 | end -------------------------------------------------------------------------------- /app/helpers/export_email_helper.rb: -------------------------------------------------------------------------------- 1 | module ExportEmailHelper 2 | include EmailerHelper 3 | 4 | def formatted_txt_holding_status(view_data) 5 | output = "" 6 | output << view_data[:collection_str] if view_data[:collection_str] 7 | output << " " + view_data[:call_number] if view_data[:call_number] 8 | output << " " + view_data[:status] if view_data[:status] 9 | return output 10 | end 11 | 12 | def formatted_html_holding_status(view_data) 13 | output = "".html_safe 14 | if view_data[:collection_str] 15 | output << content_tag("span", view_data[:collection_str], :class => "collection") 16 | end 17 | if view_data[:call_number] 18 | output << " ".html_safe << content_tag("span", view_data[:call_number], :class => "call_no") 19 | end 20 | if view_data[:status] 21 | output << " ".html_safe << content_tag("span", view_data[:status], :class => "status") 22 | end 23 | return output 24 | end 25 | 26 | def validation_error(errors) 27 | validation_errors = content_tag(:div, :class => "alert alert-danger") do 28 | content_tag(:span, "Please provide the following:") + content_tag(:ul) do 29 | e = "".html_safe 30 | errors.each do |error| 31 | e << content_tag(:li, error) 32 | end 33 | e 34 | end 35 | end 36 | validation_errors 37 | end 38 | end -------------------------------------------------------------------------------- /app/helpers/open_search_helper.rb: -------------------------------------------------------------------------------- 1 | module OpenSearchHelper 2 | 3 | def opensearch_template_url 4 | url_for(:controller => "search", :action => "journal_search", :rfr_id => umlaut_config.lookup!('rfr_ids.opensearch'), :'umlaut.title_search_type' => 'contains', :only_path => false ) + '&rft.jtitle={searchTerms}' 5 | end 6 | end -------------------------------------------------------------------------------- /app/helpers/search_helper.rb: -------------------------------------------------------------------------------- 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 and :title labels 11 | # 12 | # Uses i18n 13 | # 14 | # Much of this duplicates Referent.type_thing_name and container_type_of_thing, 15 | # although we don't have a Referent here, that logic should be combined. TODO 16 | def referent_labels(context_obj = @current_context_object) 17 | ref_meta = context_obj.referent.metadata 18 | result = OpenStruct.new 19 | 20 | type_of_thing_key = ref_meta['genre'] 21 | type_of_thing_key = context_obj.referent.format if type_of_thing_key.blank? 22 | type_of_thing_key = type_of_thing_key.downcase 23 | 24 | a_key = type_of_thing_key 25 | if a_key == "journal" && ref_meta['atitle'].present? 26 | a_key = "article" 27 | end 28 | result.atitle = I18n.t(a_key, :scope => "umlaut.citation.genre", :default => "") 29 | 30 | c_key = type_of_thing_key 31 | c_key = 'journal' if c_key == "article" 32 | c_key = 'book' if c_key == "bookitem" 33 | result.title = I18n.t(c_key, :scope => "umlaut.citation.genre", :default => "") 34 | 35 | return result 36 | end 37 | 38 | # A-Z buttons in search page 39 | def group_list 40 | group_list ||= ('A'..'Z').to_a.push('0-9').push(t('umlaut.search.browse_other')) 41 | end 42 | 43 | # Date dropdowns in search page 44 | def search_date_select 45 | years + months + days 46 | end 47 | 48 | def years 49 | select_year(nil, {:prompt => true, :start_year => Date.today.year, :end_year => 1950}, {:name => "__year", :class=>"year form-control"}) 50 | end 51 | 52 | def months 53 | select_month(nil, {:prompt => true, :use_short_month => true}, {:name => "__month", :class=>"month form-control"}) 54 | end 55 | 56 | def days 57 | select_day(nil, {:prompt => true}, {:name => "__day", :class=>"day form-control"}) 58 | end 59 | end -------------------------------------------------------------------------------- /app/helpers/umlaut/html_head_helper.rb: -------------------------------------------------------------------------------- 1 | # Rails view helpers for outputting standard Umlaut content included 2 | # in html . Generally a layout will call #render_umlaut_head_content 3 | # to render all standard Umlaut 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", :rel => "search", :type => "application/opensearchdescription+xml", 9 | :title => umlaut_config.opensearch_short_name, 10 | :href => url_for(:controller=>'/open_search', :only_path=>false)) 11 | end 12 | 13 | # used on non-js progress page, we need to refresh the page 14 | # if requested by presence of @meta_refresh_self ivar. 15 | # this method usually called in a layout. 16 | def render_meta_refresh 17 | (@meta_refresh_self) ? 18 | tag("meta", "http-equiv" => "refresh", "content" => @meta_refresh_self) : "" 19 | end 20 | 21 | # standard umlaut head content, may later include more 22 | # stuff, local/custom layouts should call this in HEAD 23 | # to get forwards-compatible umlaut standard head content 24 | def render_umlaut_head_content 25 | render_opensearch_link + render_meta_refresh 26 | end 27 | 28 | # String meant for use in 29 | def umlaut_title_text 30 | umlaut_config.app_name + (@page_title ? " | #{@page_title}" : "") 31 | end 32 | 33 | end -------------------------------------------------------------------------------- /app/helpers/umlaut/url_generation.rb: -------------------------------------------------------------------------------- 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(*arguments) 16 | url = super 17 | if @generate_urls_with_host && url.starts_with?("/") 18 | #regex take root url and get just scheme/port part, no path. 19 | # the path we want is in our own url we will add on. 20 | url = root_url.gsub(/\A(.*\/\/[^\/]+)\/.*\Z/, '\1') + url 21 | end 22 | return url 23 | end 24 | 25 | # over-ride path_to_image to generate complete urls with hostname and everything 26 | # if @generate_url_with_host is set. This makes image_tag generate 27 | # src with full url with host. See #url_for 28 | def path_to_image(source) 29 | path = super(source) 30 | if @generate_urls_with_host 31 | protocol = request.protocol() 32 | path = protocol + request.host_with_port() + path 33 | end 34 | return path 35 | end 36 | # Rails2 uses 'path_to_image' instead, that's what we have to override, 37 | # we used to use image_path, so let's alias that too. 38 | alias :image_path :path_to_image 39 | 40 | 41 | # We want stylesheets and javascripts to do the exact same thing, 42 | # magic of polymorphous super() makes it work: 43 | def path_to_stylesheet(*args) 44 | path = super 45 | if @generate_urls_with_host 46 | path = request.protocol() + request.host_with_port() + path 47 | end 48 | return path 49 | end 50 | 51 | def path_to_javascript(*args) 52 | path = super 53 | if @generate_urls_with_host 54 | path = request.protocol() + request.host_with_port() + path 55 | end 56 | return path 57 | end 58 | 59 | end 60 | -------------------------------------------------------------------------------- /app/mailers/emailer.rb: -------------------------------------------------------------------------------- 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 49 | -------------------------------------------------------------------------------- /app/mailers/feedback_mailer.rb: -------------------------------------------------------------------------------- 1 | class FeedbackMailer < ActionMailer::Base 2 | add_template_helper(EmailerHelper) 3 | 4 | default from: UmlautController.umlaut_config.from_email_addr 5 | 6 | # feedback("findit.library.school.edu", "librarian@university.edu",:name => "Joe", :email => "joe@gmail.com", :feedback => "Whatever", :umlaut_request => urequest) 7 | # * umlaut_request is optional 8 | def feedback(host, to_address, options = {}) 9 | @host = host 10 | @umlaut_request = options[:umlaut_request] 11 | @name = options[:name] 12 | @email = options[:email] 13 | @feedback = options[:feedback] 14 | 15 | # Force permalink creation if we don't have one already 16 | if @umlaut_request && @umlaut_request.referent.permalinks.empty? 17 | permalink = Permalink.new_with_values!(@umlaut_request.referent, @umlaut_request.referrer_id) 18 | @umlaut_request.referent.permalinks << permalink 19 | @umlaut_request.save! 20 | end 21 | 22 | mail(:to => to_address, :subject => "#{UmlautController.umlaut_config.app_name} Feedback: #{options[:name]}", :reply_to => @email) 23 | end 24 | 25 | end -------------------------------------------------------------------------------- /app/models/clickthrough.rb: -------------------------------------------------------------------------------- 1 | class Clickthrough < ActiveRecord::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/models/crossref_lookup.rb: -------------------------------------------------------------------------------- 1 | class CrossrefLookup < ActiveRecord::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/models/hip3/custom_field_lookup.rb: -------------------------------------------------------------------------------- 1 | module Hip3 2 | 3 | # Certain fields we add to the Item/Copy/Bib display are in the XML, but they 4 | # are only findable by name of the field header as configured in HIP 5 | # admin, and then only by seeing what index in a list that header is, 6 | # and then finding the corresponding indexed value! This object 7 | # does that work for us, and caches it's calcuations while it's at it. 8 | # One of these objects has it's own rexml doc representing a particular 9 | # bib with item info, because the answer may be different for different bibs! 10 | class CustomFieldLookup 11 | attr_accessor :header_list 12 | 13 | def initialize(a_header_list) 14 | self.header_list = a_header_list 15 | 16 | end 17 | 18 | 19 | def index_for(label) 20 | return header_list.index(label) 21 | end 22 | 23 | # list can be either an array of strings, or a rexml element 24 | # representing a <row> element for this item. In either case, 25 | # we lookup the index i of label in our original header list, 26 | # and then return the text value of element i in the list arg. 27 | def text_value_for(list, label ) 28 | i = index_for(label) 29 | return nil if i.nil? 30 | 31 | if ( list.kind_of?(Hpricot::Node) ) 32 | # Assume they passed in a HIP 'row' element, turn it 33 | # into a nice array of strings. Can't figure out how 34 | # to test if it really is a 'row' element! 35 | list = list.search('/cell/data/text').collect {|e| e.inner_text} 36 | end 37 | 38 | return list.at( i ) 39 | 40 | end 41 | 42 | 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /app/models/hip3/holding.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | 4 | 5 | module Hip3 6 | # abstract superclass for copies and items (both serial and mono). Both a Copy 7 | # and an Item are Holdings. 8 | # coverage_str only applies to copies and serial items, not mono items, but 9 | # we put it here anyway, it'll just be nil for mono items. 10 | class Holding 11 | attr_accessor :id, :location_str, :collection_str, :call_no, :copy_str, :status_str, :coverage_str, :notes 12 | # Holdings sometimes use the bib to lazy load more stuff. 13 | attr_accessor :bib 14 | 15 | # If input is nil, returns nil, else returns input.inner_text 16 | def textValue(el) 17 | return ( el == nil ? nil : el.inner_text) 18 | end 19 | 20 | # Return an array of holding strings, possibly empty, possibly single-valued. 21 | # over-ridden by SerialCopy to give you an array, since SerialCopies have 22 | # multiple holdings strings. 23 | def coverage_str_to_a 24 | return coverage_str.nil? ? [] : [coverage_str] 25 | end 26 | 27 | # Some items are dummy/placeholder items which don't really represent 28 | # an item, and shouldn't be shown. Having trouble figuring out what 29 | # our 'business rules' for that are, so this is a messy guess. 30 | def dummy? 31 | #Mostly trying to rule out the weird internet holdings 32 | #that tell us nothing--url is already in the bib. 33 | return ( collection_str == "Internet" || 34 | collection_str == "Internet Resource" || 35 | collection_str == "Welch Online Journals" || 36 | collection_str == "Welch Online Journal" || 37 | collection_str == "Gibson-Electronic Journals & Indexes" || 38 | collection_str == "Gibson - Electronic Journals") 39 | 40 | #return (( (call_no == "World Wide Web" || call_no.blank?) && 41 | # ( collection_str == "Internet" || collection_str == "Welch Online Journals" || collection_str == "Welch Online Journal")) || 42 | # (collection_str == "Gibson-Electronic Journals & Indexes" && call_no="Online journal")) 43 | 44 | end 45 | end 46 | 47 | 48 | 49 | 50 | end 51 | -------------------------------------------------------------------------------- /app/models/hip3/receipt.rb: -------------------------------------------------------------------------------- 1 | module Hip3 2 | class Receipt 3 | def foo 4 | raise "foo" 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/models/permalink.rb: -------------------------------------------------------------------------------- 1 | # attribute context_obj_serialized has an XML OpenURL ContextObject sufficient to restore 2 | # the original request and resolve the permalink. A link to a referent is 3 | # also stored. But the referent may be purged, so self.referent may be null. 4 | # The serialized contextobject will still be there. 5 | class Permalink < ActiveRecord::Base 6 | belongs_to :referent 7 | 8 | # You should create Permalinks with this. Pass in a referent and referrer 9 | #. Will save permalink to db 10 | def self.new_with_values!(rft, rfr_id) 11 | permalink = Permalink.new 12 | permalink.referent = rft 13 | permalink.orig_rfr_id = rfr_id 14 | permalink.context_obj_serialized = permalink.referent.to_context_object.xml 15 | permalink.save! 16 | return permalink 17 | end 18 | 19 | # Takes the XML stored in self.context_obj_serialized, and turns it back 20 | # into an OpenURL ContextObject 21 | def restore_context_object 22 | return OpenURL::ContextObject.new_from_xml(self.context_obj_serialized) 23 | end 24 | end -------------------------------------------------------------------------------- /app/models/referent_value.rb: -------------------------------------------------------------------------------- 1 | class ReferentValue < ActiveRecord::Base 2 | if Rails::VERSION::MAJOR >= 4 3 | belongs_to :referent, lambda { includes :referent_values } 4 | else 5 | belongs_to :referent, :include => :referent_values 6 | end 7 | 8 | # Class method to normalize a string for normalized_value attribute. 9 | # Right now normalization is just downcasing. Only 10 | # metadata values should be normalized (ie, not 'identifier' or 'format'). 11 | # identifier and format shoudl be stored in normalized_value unchanged. 12 | def self.normalize(input) 13 | return input.scrub.downcase.to_s[0..254] 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /app/models/sfx4/abstract/README.md: -------------------------------------------------------------------------------- 1 | SFX4 Abstract AZ Models 2 | --- 3 | 4 | The modules in `Sfx4::Abstract` represent SFX4 A-Z tables for connecting to local SFX instances. 5 | They are abstracted out so that the Umlaut implementer can include the relevant module for the particular instance of the model. 6 | 7 | In addition to representing the AZ tables, individual modules implement specific functionality. 8 | 9 | - `Sfx4::Abstract::Base` implements functionality for searching SFX for "SFX controlled" URLs. 10 | - `Sfx4::Abstract::AzTitle` implements Sunspot functionality for indexing SFX records in Solr. 11 | 12 | Examples are the classes in the `Sfx4::Local` module. 13 | 14 | module Sfx4 15 | module Local 16 | class Base < ActiveRecord::Base 17 | self.establish_connection :sfx_db 18 | # ActiveRecord likes it when we tell it this is an abstract 19 | # class only. 20 | self.abstract_class = true 21 | extend Sfx4::Abstract::Base 22 | 23 | # All SFX things are read-only! 24 | def readonly?() 25 | return true 26 | end 27 | end 28 | end 29 | end 30 | 31 | module Sfx4 32 | module Local 33 | class AzTitle < Sfx4::Local::Base 34 | include Sfx4::Abstract::AzTitle 35 | end 36 | end 37 | end 38 | 39 | If your Umlaut implementation needs to point to an additional SFX DB (e.g. for consortial reasons), you can create another Sfx4 module 40 | that holds your classes for the additional SFX DB (as long as the configuration is specified in database.yml). 41 | 42 | # Current file: /app/model/sfx4/additional_instance/base.rb 43 | module Sfx4 44 | module AdditionalInstance 45 | class Base < ActiveRecord::Base 46 | self.establish_connection :sfx_db_additional_instance 47 | # ActiveRecord likes it when we tell it this is an abstract 48 | # class only. 49 | self.abstract_class = true 50 | extend Sfx4::Abstract::Base 51 | 52 | # All SFX things are read-only! 53 | def readonly?() 54 | return true 55 | end 56 | end 57 | end 58 | end 59 | 60 | # Current file: /app/model/sfx4/additional_instance/az_title.rb 61 | module Sfx4 62 | module AdditionalInstance 63 | class AzTitle < Sfx4::AdditionalInstance::Base 64 | include Sfx4::Abstract::AzTitle 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /app/models/sfx4/abstract/az_extra_info.rb: -------------------------------------------------------------------------------- 1 | # +------------------+---------------------+------+-----+---------+----------------+ 2 | # | Field | Type | Null | Key | Default | Extra | 3 | # +------------------+---------------------+------+-----+---------+----------------+ 4 | # | AZ_EXTRA_INFO_ID | int(10) unsigned | NO | PRI | NULL | auto_increment | 5 | # | AZ_PROFILE | varchar(100) | NO | | NULL | | 6 | # | OBJECT_ID | bigint(20) unsigned | NO | MUL | 0 | | 7 | # | EXTRA_INFO_XML | mediumtext | YES | | NULL | | 8 | # +------------------+---------------------+------+-----+---------+----------------+ 9 | module Sfx4 10 | module Abstract 11 | module AzExtraInfo 12 | 13 | def self.included(klass) 14 | klass.class_eval do 15 | require 'nokogiri' 16 | self.table_name = 'AZ_EXTRA_INFO' 17 | self.primary_key = 'AZ_EXTRA_INFO_ID' 18 | 19 | belongs_to :az_title, 20 | :foreign_key => 'OBJECT_ID', 21 | :class_name => "#{klass.to_s.deconstantize}::AzTitle" 22 | 23 | include MetadataHelper # for normalize_lccn 24 | end 25 | end 26 | 27 | def issn 28 | @issn ||= extra_info_xml.search("item[key=issn]").text 29 | end 30 | 31 | def isbn 32 | @isbn ||= extra_info_xml.search("item[key=isbn]").text 33 | end 34 | 35 | def lccn 36 | @lccn ||= normalize_lccn(extra_info_xml.search("item[key=lccn]").text) 37 | end 38 | 39 | def extra_info_xml 40 | @extra_info_xml ||= Nokogiri::XML(self.EXTRA_INFO_XML) 41 | end 42 | end 43 | end 44 | end -------------------------------------------------------------------------------- /app/models/sfx4/abstract/az_letter_group.rb: -------------------------------------------------------------------------------- 1 | # +----------------------+------------------+------+-----+---------+----------------+ 2 | # | Field | Type | Null | Key | Default | Extra | 3 | # +----------------------+------------------+------+-----+---------+----------------+ 4 | # | AZ_LETTER_GROUP_ID | int(10) unsigned | NO | PRI | NULL | auto_increment | 5 | # | AZ_TITLE_ID | int(10) unsigned | NO | | 0 | | 6 | # | AZ_LETTER_GROUP_NAME | char(10) | NO | MUL | | | 7 | # +----------------------+------------------+------+-----+---------+----------------+ 8 | module Sfx4 9 | module Abstract 10 | module AzLetterGroup 11 | def self.included(klass) 12 | klass.class_eval do 13 | self.table_name = 'AZ_LETTER_GROUP' 14 | self.primary_key = 'AZ_LETTER_GROUP_ID' 15 | 16 | belongs_to :az_title, 17 | :foreign_key => 'AZ_TITLE_ID', 18 | :class_name => "#{klass.to_s.deconstantize}::AzTitle" 19 | 20 | end 21 | end 22 | end 23 | end 24 | end -------------------------------------------------------------------------------- /app/models/sfx4/abstract/az_title_search.rb: -------------------------------------------------------------------------------- 1 | # +--------------------+------------------+------+-----+---------+----------------+ 2 | # | Field | Type | Null | Key | Default | Extra | 3 | # +--------------------+------------------+------+-----+---------+----------------+ 4 | # | AZ_TITLE_SEARCH_ID | int(10) unsigned | NO | PRI | NULL | auto_increment | 5 | # | AZ_PROFILE | varchar(100) | NO | MUL | NULL | | 6 | # | AZ_TITLE_ID | int(10) unsigned | NO | MUL | 0 | | 7 | # | TITLE_SEARCH | text | NO | MUL | NULL | | 8 | # +--------------------+------------------+------+-----+---------+----------------+ 9 | module Sfx4 10 | module Abstract 11 | module AzTitleSearch 12 | def self.included(klass) 13 | klass.class_eval do 14 | self.table_name = 'AZ_TITLE_SEARCH' 15 | self.primary_key = 'AZ_TITLE_SEARCH_ID' 16 | 17 | belongs_to :az_title, 18 | :foreign_key => 'AZ_TITLE_ID', 19 | :class_name => "#{klass.to_s.deconstantize}::AzTitle" 20 | end 21 | end 22 | end 23 | end 24 | end -------------------------------------------------------------------------------- /app/models/sfx4/local/az_extra_info.rb: -------------------------------------------------------------------------------- 1 | module Sfx4 2 | module Local 3 | class AzExtraInfo < Sfx4::Local::Base 4 | include Sfx4::Abstract::AzExtraInfo 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/models/sfx4/local/az_letter_group.rb: -------------------------------------------------------------------------------- 1 | module Sfx4 2 | module Local 3 | class AzLetterGroup < Sfx4::Local::Base 4 | include Sfx4::Abstract::AzLetterGroup 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/models/sfx4/local/az_title.rb: -------------------------------------------------------------------------------- 1 | module Sfx4 2 | module Local 3 | class AzTitle < Sfx4::Local::Base 4 | include Sfx4::Abstract::AzTitle 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/models/sfx4/local/az_title_search.rb: -------------------------------------------------------------------------------- 1 | module Sfx4 2 | module Local 3 | class AzTitleSearch < Sfx4::Local::Base 4 | include Sfx4::Abstract::AzTitleSearch 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/models/sfx4/local/base.rb: -------------------------------------------------------------------------------- 1 | module Sfx4 2 | module Local 3 | class Base < ActiveRecord::Base 4 | # Was a SFX DB connection set in database.yml to connect directly to sfx? 5 | def self.connection_configured? 6 | config = ActiveRecord::Base.configurations["sfx_db"] 7 | (not (config.nil? or config.blank? or config["adapter"].blank?)) 8 | end 9 | 10 | self.establish_connection :sfx_db if self.connection_configured? 11 | 12 | # ActiveRecord likes it when we tell it this is an abstract 13 | # class only. 14 | self.abstract_class = true 15 | 16 | extend Sfx4::Abstract::Base 17 | 18 | # All SFX things are read-only! 19 | def readonly?() 20 | return true 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/models/sfx_url.rb: -------------------------------------------------------------------------------- 1 | # Just an indexed list of URLs extracted from SFX, urls we believe are 2 | # sfx-controlled. Kind of an ugly hack, kind of duplicates the local journal 3 | # index if in use, but we need it to suppress catalog URLs if they duplicate 4 | # what SFX ought to control. 5 | class SfxUrl < ActiveRecord::Base 6 | 7 | # Pass in a string, we tell you if we think SFX controls this URL-- 8 | # that is, if the SFX KB handles resources at this URL, or not. 9 | # It's really just a guess for a bunch of reasons, but best we can 10 | # do. We just check hostname, which could create false positives. 11 | # Checking entire URL won't work. 12 | # Lots of things in SFX could create false negatives. 13 | def self.sfx_controls_url?(url) 14 | ActiveRecord::Base.connection_pool.with_connection do 15 | # Does it match any of our supplementary configged strings or regexps? 16 | UmlautController.umlaut_config.lookup!("sfx.additional_sfx_controlled_urls", []).each do |test| 17 | # '===' will match a regexp or equality on a string 18 | return true if test === url 19 | end 20 | 21 | begin 22 | uri = URI.parse(url) 23 | rescue 24 | # Bad uri in catalog? Fine, we don't know SFX controls it. 25 | return false; 26 | end 27 | host = uri.host 28 | 29 | # If URI was malformed, just punt and say no. 30 | return false unless host 31 | 32 | return SfxUrl.where(:url => host).count > 0 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /app/referent_filters/referent_filter.rb: -------------------------------------------------------------------------------- 1 | # Kind of analagous to SFX "source parser". Takes ContextObjects 2 | # passed in, and filters/mutates them. 3 | # 4 | # specific subclasses in lib/context_object_filters 5 | # 6 | # configured to apply in environment.rb 7 | 8 | class ReferentFilter 9 | 10 | # input: Referent object 11 | # will mutate/modify it. 12 | def filter(referent) 13 | # implement in subclass 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /app/service_adaptors/ajax_export.rb: -------------------------------------------------------------------------------- 1 | # This is an abstract superclass other services over-ride to get 2 | # extra ajaxy windows upon click on link. 3 | class AjaxExport < Service 4 | required_config_params :ajax_id, :controller 5 | 6 | def initialize(config) 7 | super(config) 8 | end 9 | 10 | # Standard method, used by background service updater. See Service docs. 11 | def service_types_generated 12 | types = [ ServiceTypeValue[:export_citation] ] 13 | 14 | return types 15 | end 16 | 17 | def handle(request) 18 | 19 | request.add_service_response(:service=>self, 20 | :display_text => @display_text, 21 | :display_text_i18n => @display_text_i18n, 22 | :link_supports_ajax_call => true, 23 | :notes=> @note, 24 | :service_type_value => 'export_citation' ) 25 | 26 | return request.dispatched(self, true) 27 | end 28 | 29 | def response_url(service_response, params) 30 | # Hash that caller will pass to url_for to create an internally 31 | # facing link. 32 | return {:controller=>@form_controller, 33 | :action=>@form_action, 34 | :id => service_response.id, 35 | :format => params[:format]} 36 | end 37 | 38 | end 39 | -------------------------------------------------------------------------------- /app/service_adaptors/all_books_dot_com.rb: -------------------------------------------------------------------------------- 1 | require 'isbn_link' 2 | 3 | # Blind (not pre-checked for hits) link to compare prices 4 | # at AllBookstores.com 5 | # -- because that site seemed good, includes shipping prices, has decent UX, 6 | # and includes independent bookstores like Powell's and The Strand. 7 | # 8 | # subclasses IsbnLink 9 | class AllBooksDotCom < IsbnLink 10 | def initialize(config) 11 | super(config) 12 | 13 | @display_text ||= "Compare online prices" 14 | @display_name ||= "AllBookstores.com" 15 | @link_template ||= "http://www.allbookstores.com/book/compare/%s" 16 | end 17 | end -------------------------------------------------------------------------------- /app/service_adaptors/book_finder.rb: -------------------------------------------------------------------------------- 1 | # Link to BookFinder.com to compare online new and used prices for a book. 2 | # Requires an ISBN. 3 | # Does not a pre-check, just generates the link blind, but I think almost any 4 | # ISBN will get results on BookFinder. 5 | class BookFinder < Service 6 | require 'isbn' 7 | 8 | def initialize(config) 9 | @display_text = "Compare online prices" 10 | @display_name = "BookFinder.com" 11 | # %s is where the ISBN goes 12 | @url_template = 'http://www.bookfinder.com/search/?isbn=%s&st=xl&ac=qr' 13 | 14 | super(config) 15 | end 16 | 17 | def service_types_generated 18 | return [ServiceTypeValue['highlighted_link']] 19 | end 20 | 21 | def handle(umlaut_request) 22 | isbn = umlaut_request.referent.isbn 23 | 24 | # Unless we have a valid isbn, give up 25 | return request.dispatched(self, true) unless isbn && ISBN.valid?(isbn) 26 | 27 | # Okay, make a link 28 | url = @url_template.sub('%s', isbn) 29 | 30 | umlaut_request.add_service_response( 31 | :service=>self, 32 | :url=> url, 33 | :display_text=> @display_text, 34 | :service_type_value => :highlighted_link 35 | ) 36 | 37 | return request.dispatched(self, true) 38 | end 39 | 40 | end 41 | -------------------------------------------------------------------------------- /app/service_adaptors/bx.rb: -------------------------------------------------------------------------------- 1 | # Uses bX recommender service to generate links to "similar" articles. 2 | # 3 | # REQUIREMENTS: You must be an bX customer. Required field "token" is specific to your institution. 4 | # Optional fields: 5 | # max_records - max number of records returned; default 10 6 | # threshold - minimum score a recommendation must have in order to be included in the results; scores range from 0 to 100; default 50 7 | # openurl_base - base for other link resolver; default "/resolve" 8 | # source - values "local"|"global"; default "global" 9 | class Bx < Service 10 | require 'open-uri' 11 | require 'nokogiri' 12 | 13 | required_config_params :token 14 | 15 | def initialize(config) 16 | @display_name = "Bx" 17 | @base_url = "http://recommender.service.exlibrisgroup.com/service/recommender/openurl" 18 | @max_records = "5" 19 | @threshold = "50" 20 | @source = "global" 21 | @openurl_base = "/resolve" 22 | super(config) 23 | end 24 | 25 | def service_types_generated 26 | return [ServiceTypeValue[:similar]] 27 | end 28 | 29 | def handle(request) 30 | format = "rss" 31 | bx_url = "#{@base_url}?res_dat=format%3D#{format}%26source%3D#{@source}%26token%3D#{@token}%26maxRecords%3D#{@max_records}%26threshold%3D#{@threshold}%26baseUrl%3D#{@openurl_base}&#{request.to_context_object.kev}" 32 | response = open(bx_url) 33 | response_xml_str = response.read 34 | response_xml = Nokogiri::XML(response_xml_str) 35 | response_xml.search("//item").each do |item| 36 | title = item.at("title").inner_text 37 | author = item.at("author").inner_text 38 | display_text = (author.nil?)? "#{title}" : "#{author}; #{title}" 39 | url = item.at("link").inner_text 40 | request.add_service_response( 41 | :service=>self, 42 | :display_text => display_text, 43 | :url => url, 44 | :service_type_value => :similar) 45 | end 46 | return request.dispatched(self, true) 47 | end 48 | 49 | end -------------------------------------------------------------------------------- /app/service_adaptors/dummy_service.rb: -------------------------------------------------------------------------------- 1 | # 2 | # A Dummy service that may be useful for testing (manual or perhaps in automated 3 | # tests), that simply creates the responses you tell it to, after sleeping the 4 | # amount you tell it to. 5 | # 6 | # DummyService: 7 | # type: DummyService 8 | # priority: 3 9 | # sleep: 4.5 # seconds 10 | # responses: 11 | # - service_type_value: fulltext 12 | # display_text: foo 13 | # url: http://google.com 14 | # - service_type_value: highlighted_link 15 | # display_text: bar 16 | # url: http://amazon.com 17 | # 18 | class DummyService < Service 19 | attr_accessor :responses, :sleep 20 | 21 | def initialize(config = {}) 22 | self.responses = [] 23 | self.sleep = 0 24 | super 25 | end 26 | 27 | def service_types_generated 28 | responses.collect {|r| ServiceTypeValue[ r["service_type_value"] ]}.compact.uniq 29 | end 30 | 31 | def handle(request) 32 | 33 | ::Kernel.sleep( self.sleep ) if self.sleep 34 | 35 | responses.each do |response| 36 | request.add_service_response( { :service => self }.merge(response.symbolize_keys ) ) 37 | end 38 | 39 | return request.dispatched(self, true) 40 | end 41 | 42 | 43 | end 44 | -------------------------------------------------------------------------------- /app/service_adaptors/elsevier_cover.rb: -------------------------------------------------------------------------------- 1 | # Elsevier provides publically available and linkable sample cover images 2 | # for journals they publish. Thanks Elsevier! This service does nothing 3 | # more than take an ISSN and look for a match from Elsevier. 4 | class ElsevierCover < Service 5 | require 'open-uri' 6 | 7 | def service_types_generated 8 | return [ServiceTypeValue[:cover_image]] 9 | end 10 | 11 | def initialize(config) 12 | #@base_url = "http://www1.elsevier.com/inca/covers/store/issn/" 13 | @base_url = "http://www.extranet.elsevier.com/inca_covers_store/issn/" 14 | 15 | super(config) 16 | end 17 | 18 | def handle(request) 19 | issn = request.referent.issn 20 | 21 | # We need an ISSN 22 | return request.dispatched(self, true) unless issn 23 | 24 | # No hyphens please 25 | issn = issn.gsub(/[^0-9X]/, '') 26 | 27 | check_url = @base_url + issn + '.gif' 28 | 29 | # does it exist? 30 | if ( url_resolves(check_url) ) 31 | request.add_service_response(:service => self, 32 | :service_type_value => ServiceTypeValue[:cover_image] , 33 | :url => check_url, 34 | :size => "medium" ) 35 | end 36 | 37 | return request.dispatched(self, true) 38 | end 39 | 40 | def url_resolves(url) 41 | uri_obj = URI.parse(url) 42 | response = Net::HTTP.start(uri_obj.host, uri_obj.port) {|http| 43 | http.head(uri_obj.request_uri) 44 | } 45 | if (response.kind_of?( Net::HTTPSuccess )) 46 | return true 47 | elsif ( response.kind_of?(Net::HTTPNotFound)) 48 | return false 49 | else 50 | # unexpected condition, raise 51 | response.value 52 | end 53 | 54 | 55 | end 56 | 57 | end 58 | -------------------------------------------------------------------------------- /app/service_adaptors/email_export.rb: -------------------------------------------------------------------------------- 1 | class EmailExport < AjaxExport 2 | 3 | def initialize(config) 4 | @display_text ||= "Email" 5 | @display_text_i18n ||= "display_text" 6 | @form_controller ||= "export_email" 7 | @form_action ||= "email" 8 | super(config) 9 | end 10 | 11 | end 12 | -------------------------------------------------------------------------------- /app/service_adaptors/isbn_link.rb: -------------------------------------------------------------------------------- 1 | require 'isbn' 2 | 3 | # A simple service to generate a blind link (NOT pre-checked for hits, just 4 | # blindly created from a template) out to a service based on ISBN. 5 | # 6 | # May likely be sub-classed for specific services (see AllBooks.com), 7 | # which set default values. 8 | # 9 | # * :link_template. => String where "%s" will be replaced with ISBN 10 | # * :display_name 11 | # * :dispaly_text. Such as "Compare online prices 12 | # * :isbn_normalize. Default nil, set to :ten or :thirteen if you need to normalize 13 | # ISBN before substituting in :link_template. 14 | class IsbnLink < Service 15 | include MetadataHelper 16 | 17 | def service_types_generated 18 | return [ServiceTypeValue['highlighted_link']] 19 | end 20 | 21 | def initialize(config) 22 | @display_text = "Compare online prices" 23 | @isbn_normalize = nil 24 | 25 | super(config) 26 | end 27 | 28 | def handle(umlaut_request) 29 | 30 | isbn = get_isbn(umlaut_request.referent) 31 | 32 | # remove hyphens, some services don't like them. 33 | isbn = isbn.gsub('-', '') if isbn.present? 34 | 35 | # No isbn, nothing we can do. 36 | return umlaut_request.dispatched(self, true) if isbn.blank? 37 | 38 | # invalid isbn? forget it. 39 | return umlaut_request.dispatched(self, true) unless ISBN.valid?(isbn) 40 | 41 | if @isbn_normalize == :ten 42 | isbn = ISBN.ten(isbn) 43 | elsif @isbn_normalize == :thirteen 44 | isbn = ISBN.thirteen(isbn) 45 | end 46 | 47 | # Add the link 48 | link = @link_template.gsub("%s", isbn) 49 | 50 | umlaut_request.add_service_response( 51 | :service=>self, 52 | :url=> link, 53 | :display_text=> @display_text, 54 | :service_type_value => ServiceTypeValue[:highlighted_link] 55 | ) 56 | 57 | return umlaut_request.dispatched(self, true) 58 | end 59 | 60 | end 61 | -------------------------------------------------------------------------------- /app/service_adaptors/open_library_cover.rb: -------------------------------------------------------------------------------- 1 | # Looks for cover images from OpenLibrary Cover API. 2 | # Lookig up covers in OL can require multiple HTTP requests, one for each 3 | # identifier, which can sometimes lead to slowness. 4 | # OL also doesn't have great cover image coverage. So if you have access to 5 | # Amazon or Google covers, you probably don't need this service. 6 | class OpenLibraryCover < Service 7 | require 'net/http' 8 | 9 | def service_types_generated 10 | return [ServiceTypeValue[:cover_image]] 11 | end 12 | 13 | def initialize(config) 14 | @base_url = "http://covers.openlibrary.org/b/" 15 | @size = "medium" # "small", "medium" or "large" 16 | 17 | @credits = { 18 | "OpenLibrary" => "http://openlibrary.org/" 19 | } 20 | 21 | super(config) 22 | end 23 | 24 | def handle(request) 25 | ids = { 26 | :isbn => request.referent.isbn, 27 | :oclc => request.referent.oclcnum, 28 | :lccn => request.referent.lccn 29 | } 30 | ids.delete_if {|k,v| v.blank?} 31 | 32 | # Return if we don't have any identifiers 33 | return request.dispatched(self, true) unless ids.size > 0 34 | 35 | # What order is best for trying first? 36 | [:isbn, :oclc, :lccn].each do |type| 37 | next unless ids[type] 38 | 39 | uri = cover_uri(type, ids[type] ) 40 | s_time = Time.now 41 | response = Net::HTTP.get_response(URI.parse(uri)) 42 | Rails.logger.debug("#{@id}: #{Time.now - s_time}s to lookup #{uri}") 43 | 44 | if response.kind_of?( Net::HTTPNotFound ) 45 | # OL has no cover 46 | next 47 | end 48 | 49 | unless response.kind_of?( Net::HTTPSuccess ) 50 | # unexpected response 51 | Rails.logger.error("#{@id}: Error in HTTP response when requesting #{uri}, #{response.inspect}") 52 | end 53 | 54 | # Got this far, we've got a response. 55 | request.add_service_response( 56 | :service => self, 57 | :display_text => "Cover Image", 58 | :size => "medium", 59 | :url => uri, 60 | :service_type_value => :cover_image 61 | ) 62 | break 63 | end 64 | 65 | return request.dispatched(self, true) 66 | end 67 | 68 | def cover_uri(type, id) 69 | @base_url + type.to_s + "/" + id.to_s + "-" + @size[0,1].upcase + ".jpg?default=false" 70 | end 71 | 72 | 73 | end 74 | -------------------------------------------------------------------------------- /app/service_adaptors/request_to_fixture.rb: -------------------------------------------------------------------------------- 1 | # This service helps create fixture data for testing purposes. 2 | # It really just writes out to Yaml the Request (attributes only), Referent, 3 | # and ReferentValues. 4 | # The Request, Referent and ReferentValues must be cut and pasted into the 5 | # relevant fixture files to be used in testing. 6 | 7 | 8 | class RequestToFixture < Service 9 | required_config_params :file 10 | attr_reader :file 11 | 12 | def service_types_generated 13 | end 14 | 15 | def initialize(config) 16 | super(config) 17 | end 18 | 19 | def handle(request) 20 | final_string = '' 21 | fh = File.open(@file, 'a') 22 | dump_request(request, final_string) 23 | dump_referent_values(request, final_string) 24 | 25 | cleanup(final_string) 26 | fh.puts final_string 27 | fh.close 28 | return request.dispatched(self, true) 29 | end 30 | 31 | def dump_request(request, string) 32 | #YAML.dump(request, fh) 33 | dump_proper(request, string, 'request') 34 | put_cutline(string) 35 | dump_proper(request.referent, string, 'referent') 36 | put_cutline(string) 37 | end 38 | 39 | def dump_referent_values(request, string) 40 | referent_values = request.referent.referent_values.sort_by{|rv| rv.id} 41 | referent_values.each do |rv| 42 | dump_proper(rv, string, 'referent_value') 43 | end 44 | put_cutline(string) 45 | end 46 | 47 | def dump_proper(data, string, type) 48 | values = {} 49 | data.attributes.each do |var, val| 50 | values[var] = val 51 | end 52 | fixture = {} 53 | fixture[type + '_' + data.id.to_s] = values 54 | string << YAML.dump(fixture) 55 | 56 | end 57 | 58 | def put_cutline(string) 59 | string << "\n-------------CUT HERE----------------\n" 60 | end 61 | 62 | # removes lines that only contain three dashes. These mess up our fixtures. 63 | def cleanup(string) 64 | string.gsub!(/^--- $/, "") 65 | end 66 | 67 | 68 | end 69 | -------------------------------------------------------------------------------- /app/service_adaptors/txt_holding_export.rb: -------------------------------------------------------------------------------- 1 | # Since this relies on finding holdings that exist, you need to run it in 2 | # a service wave AFTER anything that might generate holdings. 3 | class TxtHoldingExport < AjaxExport 4 | 5 | def initialize(config) 6 | @display_text = 'Send to phone' 7 | @display_text_i18n = "display_text" 8 | @form_controller = "export_email" 9 | @form_action = "txt" 10 | # providers is a hash of: 11 | # user-presentable-string => hostname for email to txt service. 12 | @providers = { 13 | "AT&T" => "txt.att.net", 14 | "Nextel" => "messaging.nextel.com", 15 | "Sprint" => "messaging.sprintpcs.com", 16 | "T-Mobile"=> "tmomail.net", 17 | "Verizon"=> "vtext.com", 18 | "Virgin"=> "vmobl.com" 19 | } 20 | super(config) 21 | end 22 | 23 | def handle(request) 24 | 25 | holdings = request.get_service_type('holding', { :refresh => true }) 26 | unless holdings.nil? or holdings.empty? 27 | super(request) 28 | else 29 | return request.dispatched(self, true) 30 | end 31 | end 32 | 33 | end 34 | -------------------------------------------------------------------------------- /app/service_adaptors/ulrichs_cover.rb: -------------------------------------------------------------------------------- 1 | # Ulrich's provides journal cover images that are _publically_ available, at 2 | # predictable urls based on issn. Not sure if this is intentional. But as 3 | # an ulrich's subscriber, I feel fine using it. If my insitution was not 4 | # an ulrich's subscriber, I probably wouldn't use this service. 5 | class UlrichsCover < Service 6 | require 'open-uri' 7 | 8 | def service_types_generated 9 | return [ServiceTypeValue[:cover_image]] 10 | end 11 | 12 | def initialize(config) 13 | @base_url = "http://images.serialssolutions.com/ulrichs/" 14 | 15 | super(config) 16 | end 17 | 18 | def handle(request) 19 | issn = request.referent.issn 20 | 21 | # We need an ISSN 22 | return request.dispatched(self, true) unless issn 23 | 24 | # No hyphens please 25 | issn = issn.gsub(/[^0-9X]/, '') 26 | 27 | check_url = @base_url + issn + '.gif' 28 | 29 | # does it exist? 30 | if ( url_resolves(check_url) ) 31 | request.add_service_response(:service => self, 32 | :service_type_value => :cover_image , 33 | :url => check_url, 34 | :size => "medium" ) 35 | end 36 | 37 | return request.dispatched(self, true) 38 | end 39 | 40 | def url_resolves(url) 41 | uri_obj = URI.parse(url) 42 | response = Net::HTTP.start(uri_obj.host, uri_obj.port) {|http| 43 | http.head(uri_obj.request_uri) 44 | } 45 | if (response.kind_of?( Net::HTTPSuccess )) 46 | return true 47 | elsif ( response.kind_of?(Net::HTTPNotFound)) 48 | return false 49 | else 50 | # unexpected condition, raise 51 | response.value 52 | end 53 | 54 | 55 | end 56 | 57 | end 58 | -------------------------------------------------------------------------------- /app/service_adaptors/ulrichs_link.rb: -------------------------------------------------------------------------------- 1 | # A very simple service that simply generates a "highlighted_link" to 2 | # the Ulrich's periodical directory, if an ISSN is present. 3 | # Does not actually look up first to make sure there are results, it's 4 | # a blind link. 5 | # config params: 6 | # display_text: Name to put on the link. Defaults to "Periodical information". Can also be changed via i18n tranlsations. 7 | class UlrichsLink < Service 8 | 9 | def initialize(config) 10 | # Original one, which just apes the UlrichsWeb html interface, and gives 11 | # you a search results screen even with only one hit. 12 | #@base_url = "http://www.ulrichsweb.com/ulrichsweb/Search/doAdvancedSearch.asp?QuickCriteria=ISSN&Action=Search&collection=SERIAL&QueryMode=Simple&ResultTemplate=quickSearchResults.hts&SortOrder=Asc&SortField=f_display_title&ScoreThreshold=0&ResultCount=25&SrchFrm=Home&setting_saving=on&QuickCriteriaText=" 13 | super(config) 14 | # better one, which Yvette at Ulrich's showed me for SFX, which seems to work better. 15 | @vendor ||= "Umlaut" 16 | @base_url ||= "https://ulrichsweb.serialssolutions.com/api/openurl?issn=" 17 | # Old one 18 | #@base_url ||= "http://www.ulrichsweb.com/ulrichsweb/Search/call_fullCitation.asp?/vendor_redirect.asp?oVendor=#{@vendor}&oIssn=" 19 | @display_text ||= "Periodical information" 20 | @display_text_i18n ||= "display_text" 21 | end 22 | 23 | def service_types_generated 24 | return [ServiceTypeValue[:highlighted_link]] 25 | end 26 | 27 | def handle(request) 28 | unless (request.referent.issn.blank?) 29 | url = url_for_issn( request.referent.issn ) 30 | 31 | request.add_service_response( 32 | :service=>self, 33 | :url=>url, 34 | :display_text=>@display_text, 35 | :display_text_i18n => @display_text_i18n, 36 | :service_type_value => :highlighted_link) 37 | end 38 | 39 | return request.dispatched(self, true) 40 | end 41 | 42 | def url_for_issn(issn) 43 | # with or without hyphen should work fine. 44 | return @base_url + issn 45 | end 46 | 47 | end 48 | -------------------------------------------------------------------------------- /app/views/admin/service_errors/_dispatched_service.html.erb: -------------------------------------------------------------------------------- 1 | <tr> 2 | <td><%= dispatched_service.updated_at.localtime.to_s(:short) %></td> 3 | <td><%= dispatched_service.service_id %></td> 4 | <td><%= dispatched_service.status %></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 %> 9 | </tr> 10 | <% if dispatched_service.exception_info %> 11 | <tr> 12 | <td colspan="5"> 13 | <%= dispatched_service.exception_info[:class_name] %>: <%= dispatched_service.exception_info[:message] %> 14 | <% if dispatched_service.exception_info[:backtrace] %> 15 | <ul> 16 | <% dispatched_service.exception_info[:backtrace].each do |line| %> 17 | <%= simple_format line %> 18 | <% end %> 19 | </ul> 20 | <% end %> 21 | </td> 22 | </tr> 23 | <% end %> -------------------------------------------------------------------------------- /app/views/admin/service_errors/index.html.erb: -------------------------------------------------------------------------------- 1 | <div class="admin"> 2 | <h1>Admin: Service Errors Report</h1> 3 | <p>Reports on rows in dispatched_services table that have failed status.</p> 4 | <ul> 5 | <li>Can be caused by uncaught exception in service.</li> 6 | <li>Can be caused by service intentionally reporting failure</li> 7 | <li>Other kinds of unexpected failure conditions may occur in your app that are not caught here, check logs</li> 8 | <li>Only can report on dispatched_services rows that haven't been purged yet. <b>Current oldest record: <%= @earliest_dispatch.localtime.to_s(:short) %></b> 9 | </ul> 10 | 11 | <% if params[:service_id] %> 12 | <h2>Service: <%= params[:service_id] %></h2> 13 | <%= link_to "[View All]", params.merge(:service_id => nil, :offset => nil) %> 14 | <% else %> 15 | <h2>By service</h2> 16 | <table class="table table-bordered table-striped"> 17 | <% @failed_by_service.each_pair do |service_id, count| %> 18 | <tr><td><%= link_to service_id, params.merge(:service_id => service_id) %></td><td><%= count %></td></tr> 19 | <% end %> 20 | </table> 21 | <% end %> 22 | 23 | <h2>Latest</h2> 24 | <form> 25 | <%= label_tag "q", "Filter on exception_info" %> 26 | <%= text_field_tag "q", params[:q] %> 27 | <%= check_box_tag 'q_not',"1", params[:q_not] %> 28 | <%= label_tag "q_not", "NOT having query term" %> 29 | <%= submit_tag "Filter" %> 30 | </form> 31 | <p> 32 | <%= @offset + 1 %> - <%= [@offset + @limit, @dispatched_services_count].min %> of <%= @dispatched_services_count %>. <%= link_to "[Next]", params.merge("offset" => @offset + @limit ) %> 33 | </p> 34 | <table class="table table-bordered"> 35 | <%= render :partial => "dispatched_service", :collection => @dispatched_services %> 36 | </table> 37 | <p> 38 | <%= @offset + 1 %> - <%= [@offset + @limit, @dispatched_services_count].min %> of <%= @dispatched_services_count %>. <%= link_to "[Next]", params.merge("offset" => @offset + @limit ) %> 39 | </p> 40 | </div> -------------------------------------------------------------------------------- /app/views/emailer/citation.text.erb: -------------------------------------------------------------------------------- 1 | Title: <%= @title%> 2 | <% 3 | unless @user_request.title_level_citation? 4 | puts brief_citation(@user_request) 5 | end 6 | unless @fulltexts.nil? or @fulltexts.empty? %> 7 | 8 | 9 | Electronic Versions: 10 | 11 | <% 12 | @fulltexts.each do |fulltext| 13 | view_data = fulltext.view_data %> 14 | * <%= view_data[:display_text] %>: <%= view_data[:url] %> 15 | 16 | <% 17 | end 18 | end 19 | unless @holdings.nil? or @holdings.empty? %> 20 | 21 | 22 | Holdings: 23 | 24 | <% @holdings.each do |holding| %> 25 | <% target = holding.view_data %> 26 | * <%= raw(target[:collection_str]) %> <%= raw(target[:call_number]) %> <%= raw(target[:status]) %> 27 | <% end %> 28 | <% end %> 29 | -------------------------------------------------------------------------------- /app/views/emailer/short_citation.text.erb: -------------------------------------------------------------------------------- 1 | <%= @call_number%> 2 | <%= @location%> 3 | <% unless @user_request.title_level_citation? %> 4 | <%= brief_citation(@user_request) %> 5 | <% else %> 6 | <%= @title%> 7 | <% end -%> 8 | 9 | -------------------------------------------------------------------------------- /app/views/export_email/email.html.erb: -------------------------------------------------------------------------------- 1 | <div class="email"> 2 | <% 3 | unless @user_request.nil? 4 | cite = @user_request.referent.to_citation %> 5 | <%= form_tag({:controller => "export_email", :action => "send_email", :id => params[:id]}, :class => "form-horizontal" ) do %> 6 | <h4 class="modal-title"><%=t 'umlaut.services.email_export.header' %></h4> 7 | <%= validation_error flash[:alert] if flash[:alert] %> 8 | <h4><%= cite[:title] %> / <%= cite[:author] %></h4> 9 | <div class="form-group"> 10 | <%= label_tag 'email', t('umlaut.services.email_export.enter_your_email'), :class => "control-label col-xs-4" %> 11 | <div class="col-xs-8"> 12 | <%= text_field_tag 'email', params[:email], :class => "form-control" %> 13 | </div> 14 | </div> 15 | <%= submit_tag t('umlaut.services.email_export.send_action'), :class => "btn btn-primary" %> 16 | <% 17 | end 18 | end %> 19 | </div> -------------------------------------------------------------------------------- /app/views/export_email/send_email.html.erb: -------------------------------------------------------------------------------- 1 | <h2>Sent</h2> 2 | <div id="email_success" class="alert alert-success">Sent to <%= @email %>.</div> -------------------------------------------------------------------------------- /app/views/export_email/send_txt.html.erb: -------------------------------------------------------------------------------- 1 | <h2><%=t('umlaut.services.txt_holding_export.text_sent', :number => @number)%></h2> 2 | <div id="txt_success" class="alert alert-success"> 3 | <%=t('umlaut.services.txt_holding_export.text_sent', :number => @number)%> 4 | </div> -------------------------------------------------------------------------------- /app/views/feedback/_resolve_section.html.erb: -------------------------------------------------------------------------------- 1 | <div class="help-text-icon">?</div> 2 | <%= render 'section_heading', :presenter => renderer, :force_render => true %> 3 | 4 | <ul class="response_list umlaut-help-list"> 5 | <% umlaut_config.feedback.contacts.each_pair do |key, config| %> 6 | <li> 7 | <% if config[:link_out] %> 8 | <%# simply link out to somewhere outside of umlaut %> 9 | <%= link_to config[:label], config[:link_out] %> 10 | <% else %> 11 | <%# link to our feedback form %> 12 | <%= link_to config[:label], feedback_path(umlaut_request.id, :contact_id => key) %> 13 | <% end %> 14 | </li> 15 | <% end %> 16 | </ul> -------------------------------------------------------------------------------- /app/views/feedback/new.html.erb: -------------------------------------------------------------------------------- 1 | <h1>Contact Us <small>Send us questions, problems, or comments</small></h1> 2 | 3 | <%= form_tag(feedback_path(params[:request_id], :contact_id => params[:contact_id]) , :method => :post, :class => "feedback-form form-horizontal", :role => "form") do %> 4 | 5 | <div class="form-group"> 6 | <label class="control-label col-sm-2" for="name">Your Name</label> 7 | <div class="controls col-sm-10"> 8 | <input type="text" id="name" name="name" class="form-control"> 9 | </div> 10 | </div> 11 | 12 | <div class="form-group"> 13 | <label class="control-label col-sm-2" for="email">Your Email</label> 14 | <div class="controls col-sm-10"> 15 | <input type="text" id="email" name="email" class="form-control"> 16 | </div> 17 | </div> 18 | 19 | <div class="form-group"> 20 | <label class="control-label col-sm-2" for="feedback">Question or Comment</label> 21 | <div class="controls col-sm-10"> 22 | <textarea id="feedback" name="feedback" class="form-control" rows=6></textarea> 23 | </div> 24 | </div> 25 | 26 | 27 | <div class="form-group"> 28 | <div class="controls col-sm-offset-2 col-sm-10"> 29 | <button type="submit" class="btn btn-primary">Send</button> 30 | </div> 31 | </div> 32 | 33 | <% end %> -------------------------------------------------------------------------------- /app/views/feedback_mailer/feedback.text.erb: -------------------------------------------------------------------------------- 1 | ** User ** 2 | 3 | <%= @name %> <<%= @email %>> 4 | 5 | ** User Comments ** 6 | 7 | <%= @feedback %> 8 | 9 | ** Citation ** 10 | 11 | <%- if @umlaut_request -%> 12 | <%= brief_citation(@umlaut_request, :include_labels => true) %> 13 | <%= citation_identifiers(@umlaut_request) %> 14 | <%- if @umlaut_request.referent.permalinks.present? -%> 15 | Permalink: <%= url_for(:controller=>"store", 16 | :id=> @umlaut_request.referent.permalinks.first.id, 17 | :only_path => false, :host => @host) %> 18 | 19 | Original Source: <%= @umlaut_request.referrer_id %> 20 | <%- end -%> 21 | <%- else -%> 22 | No citation supplied. 23 | <%- end -%> 24 | -------------------------------------------------------------------------------- /app/views/js_helper/loader.erb.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 3 | function Loader() { 4 | this.load = function(option_list) { 5 | throw('Umlaut Loader object no longer supported.'); 6 | } 7 | } 8 | //Export it to the global object. 9 | if (window.Umlaut == undefined) 10 | window.Umlaut = new Object(); 11 | window.Umlaut.Loader = Loader; 12 | 13 | })(jQuery); 14 | -------------------------------------------------------------------------------- /app/views/layouts/umlaut.html.erb: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="utf-8" /> 5 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 | <%= csrf_meta_tags %> 7 | <%= stylesheet_link_tag 'application' %> 8 | <%= javascript_include_tag 'application' %> 9 | <title><%= umlaut_title_text %> 10 | <%= render_umlaut_head_content %> 11 | 12 | 13 |
14 | <%= render :partial => "umlaut/header" %> 15 | 16 |
"> 17 | <%= render :partial => "umlaut/alerts" %> 18 | 19 | <%= yield %> 20 |
21 | 22 | <%= render :partial => "umlaut/footer" %> 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /app/views/open_search/index.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= umlaut_config.lookup!("opensearch_short_name", "Find Journals") %> 4 | <%= umlaut_config.lookup!("opensearch_description") %> 5 | UTF-8 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/views/resolve/_api_in_progress.xml.erb: -------------------------------------------------------------------------------- 1 | <% 2 | if any_services_in_progress? %> 3 | false 4 | 5 | <% # URLS delivered by url_for already come with & rather than & seperating query params, no need to XML-escape them. I used to not realize that and send them through REXML::Text to escape them. Old versions of REXML were buggy, and avoided double escaping. New versions are not buggy and the unneccesary trip through REXML::Text messed them up. Phew, that was crazy to debug 6 | %> 7 | <%= @user_request.id %> 8 | <%= 9 | url_for(params.merge( :'umlaut.request_id' => @user_request.id, :only_path => false ) ) %> 10 | <%= url_for(params.merge( :'umlaut.request_id' => @user_request.id, :only_path => true ) ) %> 11 | <%= url_for(params.merge( :'umlaut.request_id' => @user_request.id, :only_path => true, :skip_relative_url_root => true ) ) %> 12 | <%= umlaut_config.lookup!("poll_wait_seconds", 4)%> 13 | 14 | <% @user_request.services_in_progress.collect { |s| s.service_types_generated}.flatten.uniq.each do |type| %> 15 | 16 | <% end %> 17 | 18 | 19 | <% else %> 20 | true 21 | <% end %> 22 | -------------------------------------------------------------------------------- /app/views/resolve/_background_updater.html.erb: -------------------------------------------------------------------------------- 1 | <%- 2 | # Include this partial to put the background updater device on your page. 3 | # This actually just sets up the Umlaut JQuery background updater, for 4 | # unobtrusive adding of background html content to menu page. 5 | 6 | # Only include if we have any background services. We could check to make 7 | # sure we only have background services we care about, but it's more work-- 8 | # the updater will only run ONCE at most wrongly, then the 9 | # background_update action will stop it. 10 | -%> 11 | <% if (any_services_in_progress?) %> 12 | <% 27 | end %> -------------------------------------------------------------------------------- /app/views/resolve/_citation.html.erb: -------------------------------------------------------------------------------- 1 | <% 2 | # Get the citation from the user request 3 | cite = user_request.referent.to_citation 4 | @page_title = cite[:title] %> 5 | 6 | <% if user_request.referent.type_of_thing.present? %> 7 |

<%=user_request.referent.type_of_thing.titlecase %>

8 | <% end %> 9 |

<%= cite[:title] %>

10 | <% if cite[:author].present? || cite[:date].present? %> 11 |

12 | <% if cite[:author].present? %> 13 | <%= cite[:author] %> 14 | <% end %> 15 | <% if cite[:author].present? && cite[:date].present? %> 16 | 17 | <% end %> 18 | <% if cite[:date].present? %> 19 | <%= date_format(cite[:date]) %> 20 | <% end %> 21 |

22 | <% end %> 23 |

24 | <% if [:container_title, :volume, :issue, :page].find {|k| cite[k].present? } %> 25 | 26 | <%="#{t('umlaut.citation.published_in')} #{cite[:container_label]}"%> 27 | <%= cite[:container_title] %>. 28 | <% end %> 29 | <% if cite[:volume].present? %> 30 | <%="#{t('umlaut.citation.volume').titlecase} #{cite[:volume]}" %>. 31 | <% end %> 32 | <% if cite[:issue].present? %> 33 | <%="#{t('umlaut.citation.issue').titlecase} #{cite[:issue]}" %>. 34 | <% end %> 35 | <% if cite[:page].present? %> 36 | <%="#{t('umlaut.citation.page').titlecase} #{cite[:page]}" %>. 37 | <% end %> 38 |

39 |
40 | <%= if cite[:pub].present? 41 | content_tag(:dt, "#{t('umlaut.citation.publisher')}:", :class => "publisher") + 42 | content_tag(:dd, cite[:pub], :class => "publisher") 43 | end %> 44 | <%= if cite[:issn].present? 45 | content_tag(:dt, "#{t('umlaut.citation.issn')}:", :class => "issn") + 46 | content_tag(:dd, cite[:issn], :class => "issn") 47 | end %> 48 | <%= if cite[:isbn].present? 49 | content_tag(:dt, "#{t('umlaut.citation.isbn')}:", :class => "isbn") + 50 | content_tag(:dd, cite[:isbn], :class => "isbn") 51 | end %> 52 | <%# todo, doi %> 53 |
-------------------------------------------------------------------------------- /app/views/resolve/_coins.html.erb: -------------------------------------------------------------------------------- 1 | <%= @user_request.to_context_object.coins.html_safe %> 2 | -------------------------------------------------------------------------------- /app/views/resolve/_compact_citation.html.erb: -------------------------------------------------------------------------------- 1 | <% 2 | # When you need a citation in a tiny amount of space, just the 3 | # most important info, displayed compactly. 4 | cite = @user_request.referent.to_citation 5 | pub = [] 6 | pub << '.' << date_format(cite[:date]) unless cite[:date].blank? 7 | pub << '. ' + t(:volume).titlecase + ': '+cite[:volume] unless cite[:volume].blank? 8 | pub << '. ' + t(:issue).titlecase + ': '+cite[:issue] unless cite[:issue].blank? 9 | pub << '. ' + t(:page).titlecase + ': '+cite[:page] unless cite[:page].blank? %> 10 | <%= content_tag(:span, h(cite[:author].chomp), :class => "text-small") if cite[:author] %> 11 | <%= content_tag(:span, h(truncate(cite[:title], :length => 70, :seperator => '...'), :class => "text-small") %> 12 | <%= content_tag(((@user_request.referent.format =~ /article|journal|issue|report/i) ? 'em' : 'span'), cite[:subtitle], :class => "text-small") if cite[:subtitle] %> 13 | <%= content_tag(:span, pub.join(' '), :class => "text-small") if pub.length > 0 %> 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/views/resolve/_cover_image.html.erb: -------------------------------------------------------------------------------- 1 | <%= 2 | # Set parameter for size, small|medium|large if you like 3 | size ||= "medium" 4 | fix_size ||= true 5 | 6 | # small med and x-large correspond roughly to amazon sm, med and lg. 7 | # large corresponds roughly to google medium. Just one dimension to 8 | # prevent disproportionate scaling. We use CSS so we can set maximums 9 | # in BOTH dimensions but let aspect ratio remain the same. We put it 10 | # inline so HTML Snippet API clients get images constrained to reasonable 11 | # sizes without extra CSS. IE6 won't do max-height/max-width, sorry IE6, no 12 | # way to accomodate you (trying to put a width and a max-width triggers 13 | # weird IE8 bugs!). Best we can do. 14 | # 15 | # generally the 'max-height' is our operative maximum for ordinary ratios, 16 | # but we let the width be more than the height for weird ratio'd covers 17 | # that are wider than square. 18 | dimensions_style = { 19 | "small" => "max-height: 80px; max-width: 100px; box-sizing: content-box;", 20 | "medium" => "max-height: 160px; max-width: 200px; box-sizing: content-box;", 21 | "large" => "max-height: 200px; max-width: 240px; box-sizing: content-box;", 22 | "extra-large" => "max-height: 475px; max-width: 570px; box-sizing: content-box;" } 23 | 24 | img_st = cover_image_response(size) 25 | if img_st 26 | img_data = img_st.view_data 27 | img_params = {'src'=>img_data[:url], 28 | 'class'=>'cover_image', 29 | 'alt'=>h(img_data[:display_text])} 30 | # If we're in https mode, and the original url is http, use 31 | # Umlaut proxy to avoid browser security warning. 32 | if (request.protocol != img_params['src'][0..6]) 33 | img_params['src'] = url_for(:controller => "resource", :action => "proxy", :id => img_st.id ) 34 | end 35 | img_params.merge!( "style" => dimensions_style[size]) if fix_size 36 | tag('img', img_params ) 37 | end %> -------------------------------------------------------------------------------- /app/views/resolve/_fulltext.html.erb: -------------------------------------------------------------------------------- 1 | <% 2 | all_responses = fulltext 3 | (title_level_fulltxt, fulltxt) = all_responses.partition do |i| 4 | i.view_data[:coverage_checked] == false || i.view_data[:can_link_to_article] == false 5 | end 6 | # order em with full fulltxt before title_level_fulltxt 7 | all_responses = fulltxt + title_level_fulltxt 8 | %> 9 |
"> 10 | <%= render 'section_heading', :presenter => renderer, :force_render => true %> 11 |
12 | <%= list_with_limit('umlaut_fulltext', all_responses, 13 | :ul_class => "response_list", :limit => renderer.list_visible_limit ) do |item, index | %> 14 | <%- 15 | # Display special warning header if it's title-level only, 16 | # and it's an article or issue level link. Are we 17 | # at the beginning of title level display? 18 | -%> 19 | <% if (index == fulltxt.length && !title_level_request?) %> 20 | 21 | 25 |
    26 | <% end %> 27 | <%= render :partial => "standard_response_item", :object => item %> 28 | <% end %> 29 | <% if (fulltxt.empty? && title_level_fulltxt.empty? && !service_type_in_progress?('fulltext')) %> 30 |

    <%= t('umlaut.resolve.not_available') %>

    31 | <% end %> 32 | <%= render renderer.spinner_render_hash if renderer.services_in_progress? %> 33 |
34 |
35 | -------------------------------------------------------------------------------- /app/views/resolve/_help.html.erb: -------------------------------------------------------------------------------- 1 |
?
2 | <%= render 'section_heading', :presenter => renderer, :force_render => true %> 3 |
    4 | <% help.each do |help| 5 | value_hash = help.view_data %> 6 |
  • <%= link_to value_hash[:display_text], {:controller=>'link_router', :id=>help.id}, 'target'=>"_blank" %> 7 | <% unless value_hash[:note].blank? %> 8 |
    9 | <%= value_hash[:note] %> 10 | <% end %> 11 |
  • 12 | <% end %> 13 |
14 | -------------------------------------------------------------------------------- /app/views/resolve/_manually_entered_warning.html.erb: -------------------------------------------------------------------------------- 1 | <%# Try to warn if this could be a typo. If we have an rft.object_id, then 2 | # SFX recognized it as an actually existing journal. If not, then it 3 | # might be a typo. If it came from a manually entered citation, warn them. 4 | %> 5 | 6 | <% if display_not_found_warning?(user_request) %> 7 |
8 | 9 | <%= t("umlaut.resolve.not_found_warning", :app_name => umlaut_config.app_name, :resource_type => user_request.referent.container_type_of_thing.downcase) %> 10 |
11 | <% end %> -------------------------------------------------------------------------------- /app/views/resolve/_modal.html.erb: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /app/views/resolve/_related_items.html.erb: -------------------------------------------------------------------------------- 1 | <%- 2 | # Get cited by and similar services 3 | cited_by = get_service_type('cited_by') 4 | similar = get_service_type('similar') 5 | # Render cited by services 6 | %><%= 7 | list_with_limit('umlaut_cited_by', cited_by, :ul_class => "related_items response_list") do |item, index | %><%= 8 | render(:partial => "standard_response_item", :object=> item, :locals => { :show_source => true, :li_class => "related_item"}) %><% 9 | end 10 | # Render similar services (Related items) 11 | %><%= 12 | list_with_limit('umlaut_similar_items', similar, :ul_class =>"related_items response_list") do |item, index | %><%= 13 | render(:partial => "standard_response_item", :object=> item, :locals => { :show_source => true}) %><% 14 | end 15 | # If there are no related or cited by services, tell the user. 16 | if (!service_types_in_progress?([ServiceTypeValue['cited_by'], ServiceTypeValue['similar']])) && similar.empty? && cited_by.empty? -%> 17 | <% 20 | end %> -------------------------------------------------------------------------------- /app/views/resolve/_search_inside.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <% 3 | search_providers = search_inside 4 | # We don't show this block until ALL possible search inside tools 5 | # have been found. Too hard to deal with AJAX update of a DIV 6 | # that has a text input box in it otherwise. 7 | # TODO: Make this a helper function!? 8 | if (search_providers.length > 0 and (not service_type_in_progress?('search_inside'))) %> 9 |
10 | <%= content_tag :h3, t('umlaut.display_sections.search_inside.search_inside_this_x', :x => @user_request.referent.container_type_of_thing.try(:downcase)) %> 11 |
12 |
13 | <%= content_tag("form", :target => "_blank", :action => url_for(:controller=>'link_router', :action=>'index'), :class => "form-inline") do %> 14 | 15 |
16 | <%=t 'umlaut.display_sections.search_inside.via' %> 17 | <% if search_providers.length > 1 %> 18 | <%= select_tag("id", options_for_select(search_providers.collect {|st| [st.view_data[:display_text], st.id]}), 19 | :class =>"form-control id") %> 20 | <% else %> 21 | 22 | <%= search_providers[0].view_data[:display_text] %> 23 | <% end %> 24 |
25 | 26 |
27 |
28 | 29 | 30 | 31 | 32 |
33 |
34 | <% end %> 35 |
36 | <% elsif (@user_request.referent.format == 'book' && service_type_in_progress?('search_inside')) %> 37 | <%# don't show the spinner unless it's identified as a book, otherwise too likely to be a false promise %> 38 | <%= render(:partial => "background_progress", :locals => {:svc_type => "search_inside", 39 | :progress_message => "#{t 'umlaut.display_sections.search_inside.progress_message'} #{@user_request.referent.container_type_of_thing}" }) %> 40 | <% end %> 41 | 42 |
-------------------------------------------------------------------------------- /app/views/resolve/_section_display.html.erb: -------------------------------------------------------------------------------- 1 | <%- 2 | # You generally shoudln't call this partial directly, it's called through 3 | # the render_section helper method in resolve_helper.rb 4 | -%> 5 | <% if presenter.visible? %> 6 | <% unless presenter.show_partial_only? %> 7 |
"> 8 | <% end %> 9 | <%= render 'section_heading', :presenter => presenter %> 10 | <% unless presenter.show_partial_only? %> 11 |
12 | <% end %> 13 | <% if presenter.custom_partial? %> 14 | <%= render presenter.content_render_hash %> 15 | <% elsif presenter.list_visible_limit %> 16 | <%= list_with_limit("#{presenter.div_id}_list_with_limit", presenter.responses_list, 17 | :limit => presenter.list_visible_limit, :ul_class => "response_list #{presenter.div_id}") do |item, index| 18 | render presenter.item_render_hash(item) 19 | end %> 20 | <% elsif presenter.responses_list.length == 0 && ! presenter.services_in_progress? %> 21 |
    22 |
  • None available.
  • 23 |
24 | <% else %> 25 |
    26 | <%= render presenter.content_render_hash %> 27 |
28 | <% end %> 29 | <% if presenter.show_spinner? %> 30 | <%= render presenter.spinner_render_hash %> 31 | <% end %> 32 | <% unless presenter.show_partial_only? %> 33 |
34 | <% end %> 35 | <% end %> 36 | -------------------------------------------------------------------------------- /app/views/resolve/_section_heading.html.erb: -------------------------------------------------------------------------------- 1 | <%# requires local :presenter with a SectionRenderer 2 | optional local :force_render=> true to ignore presentation logic. legacy. %> 3 | <% if presenter.show_heading? || local_assigns[:force_render] == true %> 4 |
5 | <% if presenter.section_title %> 6 |

<%= presenter.section_title %>

7 | <% if presenter.section_prompt %> 8 |

9 | <%= presenter.section_prompt %> 10 |

11 | <% end %> 12 | <% end %> 13 |
14 | <% end %> 15 | -------------------------------------------------------------------------------- /app/views/resolve/_service_errors.html.erb: -------------------------------------------------------------------------------- 1 | <% 2 | failed_dispatches = failed_service_dispatches() 3 | unless ( failed_dispatches.nil? || failed_dispatches.empty? ) %> 4 |
5 |
<%= t 'umlaut.error.service_errors_title' %>
6 |
27 |
28 | <% end %> 29 | -------------------------------------------------------------------------------- /app/views/resolve/background_status.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

Loading complete information

3 |

Please wait, the following information is still loading. You will be automatically returned to the <%= app_name %> screen when finished.

4 |
5 | 6 | 7 | 8 | 9 | 10 | <% @user_request.services_in_progress.each do |service| %> 11 | 12 | 13 | 14 | 15 | <% end %> 16 |
SourceContent type
<%= service.display_name %><%= service.service_types_generated.collect {|st| html_escape(st.display_name_pluralize) }.join('
').html_safe %>
17 |
18 | <%= 19 | link_to( "View Current Results", url_for_with_co( 20 | {:action => 'index',:'umlaut.skip_resolve_menu' => 'false', 21 | 'umlaut.request_id' => @user_request.id}, @user_request.to_context_object)) %> 22 |
23 | -------------------------------------------------------------------------------- /app/views/resolve/get_permalink.html.erb: -------------------------------------------------------------------------------- 1 | 5 | 6 | <%= link_to t('umlaut.permalink.back'), :back %> -------------------------------------------------------------------------------- /app/views/resolve/index.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'background_updater', :user_request => @user_request %> 2 |
3 |
4 |
5 |
6 | <%= render 'manually_entered_warning', :user_request => @user_request %> 7 | <%= render 'citation', :user_request => @user_request %> 8 | <% html_sections(:resource_info).each do |section| %> 9 | <%= content_tag "div", :id => section[:div_id] do %> 10 | <%= render_section(section) %> 11 | <% end %> 12 | <% end %> 13 |
14 | <%# need a div here for ajax bg loader to put bg loaded cover into %> 15 |
16 | <%= render_section(html_section_by_div_id("cover_image")) %> 17 |
18 |
19 |
20 | <% html_sections(:main).each do |section| %> 21 | <%= content_tag "div", :id => section[:div_id] do %> 22 | <%= render_section(section) %> 23 | <% end %> 24 | <% end %> 25 |
26 |
27 |
28 |
29 | <% html_sections(:sidebar).each do |section| %> 30 | <%= content_tag "div", :id => section[:div_id] do %> 31 | <%= render_section(section) %> 32 | <% end %> 33 | <% end %> 34 |
35 | <% html_sections(:service_errors).each do |section| %> 36 | <%= render_section(section) %> 37 | <% end %> 38 |
39 |
40 |
41 | <%= render 'modal' %> 42 |
43 | -------------------------------------------------------------------------------- /app/views/resolve/partial_html_sections.xml.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= render(:partial => "api_in_progress", :layout => false) %> 4 | 5 | 6 | <% partial_html_sections.each do |section| 7 | 8 | renderer = SectionRenderer.new(@user_request, section) 9 | 10 | 11 | %> 12 | 13 | 14 | <% renderer.service_type_values.each do |svc_type| %> 15 | 16 | <% end %> 17 | 18 | 19 | <%= renderer.section_etag %> 20 | 21 | 22 | <%= with_format("html") do 23 | escape_xml( render_section(renderer) ).html_safe 24 | end %> 25 | 26 | 27 | <% end %> 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/views/search/_a_to_z.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <% group_list.each do |letter| %> 3 | <% if params[:id] == letter %> 4 | <%= content_tag "span", letter, :class => "active" %> 5 | <% else %> 6 | <%= link_to letter, {:action=>"journal_list", :id=>letter} %> 7 | <% end %> 8 | <% end %> 9 |
10 | -------------------------------------------------------------------------------- /app/views/search/_pager.html.erb: -------------------------------------------------------------------------------- 1 | <% # much of this paging code adopted from will_paginate plugin for view 2 | 3 | 4 | total_pages = (@hits / @batch_size) 5 | total_pages += 1 unless (@hits % @batch_size == 0) 6 | 7 | if total_pages > 1 8 | 9 | # Arguments for paging actions, just add 'page' 10 | args= params.clone 11 | # Need to take out that annoying legacy journal key, sorry. 12 | args.delete('journal') 13 | 14 | inner_window ||= 2 #how many links are shown on each side of current page 15 | outer_window ||= 2 #how many links are around the first and the last page 16 | 17 | min = @page - inner_window 18 | max = @page + inner_window 19 | 20 | # adjust lower or upper limit if other is out of bounds 21 | if max > total_pages 22 | min -= max - total_pages 23 | elsif min < 1 24 | max += 1 - min 25 | end 26 | 27 | current = min..max 28 | beginning = 1..(1 + outer_window) 29 | tail = (total_pages - outer_window)..total_pages 30 | visible = [beginning, current, tail].map(&:to_a).flatten.sort.uniq 31 | links, prev = [], 0 32 | 33 | visible.each do |n| 34 | next if n < 1 35 | break if n > total_pages 36 | 37 | unless n - prev > 1 38 | prev = n 39 | if ( n != @page ) 40 | links << (content_tag :li, (link_to n, args.merge({'page' => n })), :class => "hidden-xs") 41 | else 42 | links << (content_tag :li, (content_tag :span, n), :class => "active" ) 43 | end 44 | else 45 | # ellipsis represents the gap between windows 46 | prev = n - 1 47 | links << (content_tag :li, (content_tag :span, "…".html_safe), :class => "hidden-xs disabled") 48 | redo 49 | end 50 | end 51 | 52 | # Next and previous buttons 53 | links.unshift( content_tag :li, (link_to(t('umlaut.search.previous'), args.merge({'page' => @page -1 })))) if @page > 1 54 | links.push( content_tag :li, (link_to(t('umlaut.search.next'), args.merge({'page' => @page + 1})))) if @page < total_pages 55 | %> 56 | <%= content_tag :ul, links.join(' ').html_safe, :class => "pagination" %> 57 | <% 58 | end 59 | %> 60 | 61 | -------------------------------------------------------------------------------- /app/views/testing/index.html.erb: -------------------------------------------------------------------------------- 1 | Foo! 2 | -------------------------------------------------------------------------------- /app/views/umlaut/README: -------------------------------------------------------------------------------- 1 | This ./app/views/umlaut directory belongs to the UmlautController, 2 | superclass of all Umlaut controllers. So views in here are available 3 | to all Umlaut controllers, but can be 'over-ridden' in a sub-class 4 | controller, simply by providing a view in subclass view dir with 5 | same name. 6 | -------------------------------------------------------------------------------- /app/views/umlaut/_alerts.html.erb: -------------------------------------------------------------------------------- 1 | <%- 2 | text, css_class = nil, nil 3 | [:alert_success, :alert_info, :alert_error].each do |key| 4 | if flash[key] 5 | text = flash[key] 6 | css_class = key.to_s.dasherize 7 | end 8 | end 9 | -%> 10 | <% if text %> 11 |
12 | <%= text %> 13 |
14 | <% end %> -------------------------------------------------------------------------------- /app/views/umlaut/_footer.html.erb: -------------------------------------------------------------------------------- 1 | <%# local implementers may over-ride this partial for custom local header. 2 | This partial is called by default umlaut layout. %> 3 | 4 | 16 | -------------------------------------------------------------------------------- /app/views/umlaut/_header.html.erb: -------------------------------------------------------------------------------- 1 | <%# local implementers may over-ride this partial for custom local header. 2 | This partial is called by default umlaut layout. %> 3 | 4 | -------------------------------------------------------------------------------- /app/views/umlaut/error.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |

<%= t "umlaut.error.header" %>

7 |
8 | 9 |

<%= t 'umlaut.error.try_one_of_options' %>

10 |
    11 |
  1. <%= t 'umlaut.error.try_reenter_url' %>
  2. 12 | 13 | 14 | <% if params[:controller] # Unless this is really a 404 and we had no controller. 15 | %> 16 |
  3. <%= t('umlaut.error.try_again_prompt_html', link: link_to(t("umlaut.error.try_again"), params)) %>
  4. 17 | <% end %> 18 | 19 | <% if ( umlaut_config.lookup!("sfx.sfx_base_url") && 20 | params[:controller] == "resolve" && 21 | params[:action] == "index") %> 22 |
  5. <%= t 'umlaut.error.if_doesnt_work_html', :link => 23 | link_to(t('umlaut.error.try_backup_resolver'), 24 | umlaut_config.sfx.sfx_base_url.chomp("?") + '?' + 25 | (request.fullpath.split("?")[1] || "") + 26 | '&sfx.ignore_date_threshold=1') 27 | %> 28 |
  6. 29 | <% end %> 30 | 31 | <% if umlaut_config.help_url %> 32 |
  7. <%= t 'umlaut.error.to_report_a_problem_html', :contact_link => 33 | link_to(t('umlaut.error.contact_us_for_help'), umlaut_config.help_url) 34 | %>.
  8. 35 | <% end %> 36 | 37 |
38 | 39 |
40 | 41 |
42 |
-------------------------------------------------------------------------------- /db/migrate/02_umlaut_add_service_response_index.rb: -------------------------------------------------------------------------------- 1 | class UmlautAddServiceResponseIndex < ActiveRecord::Migration 2 | def up 3 | add_index "service_responses", ["request_id"] 4 | end 5 | 6 | def down 7 | remove_index "service_responses", ["request_id"] 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }]) 7 | # Mayor.create(:name => 'Daley', :city => cities.first) 8 | -------------------------------------------------------------------------------- /lib/generators/umlaut/asset_hooks_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators' 2 | 3 | module Umlaut 4 | class AssetHooks < Rails::Generators::Base 5 | 6 | def add_to_stylesheet_manifest 7 | existing = IO.read("app/assets/stylesheets/application.css") 8 | unless existing.include?('Umlaut') 9 | after = 10 | if existing.include?("require_self") 11 | "require_self" 12 | else 13 | "/*" 14 | end 15 | 16 | insert_into_file "app/assets/stylesheets/application.css", :after => after do 17 | %q{ 18 | * 19 | * The base Umlaut styles: 20 | *= require 'umlaut' 21 | *} 22 | end 23 | else 24 | say_status("skipped", "Your application.css already references Umlaut", :yellow) 25 | end 26 | end 27 | 28 | def add_to_javascript_manifest 29 | unless IO.read("app/assets/javascripts/application.js").include?('Umlaut') 30 | prepend_to_file "app/assets/javascripts/application.js" do 31 | %q{ 32 | // Umlaut javascript required for proper functionality. The 'umlaut' file 33 | // also forces require of jquery and jquery-ui, dependencies. 34 | //= require 'umlaut' 35 | } 36 | end 37 | else 38 | say_status("skipped", "Your application.js already references Umlaut", :yellow) 39 | end 40 | 41 | end 42 | 43 | 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/generators/umlaut/remove_turbolinks_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators' 2 | 3 | module Umlaut 4 | class RemoveTurbolinks < Rails::Generators::Base 5 | 6 | def remove_turbolinks_js_reference 7 | gsub_file "app/assets/javascripts/application.js", /\/\/\= require ['"]?turbolinks['"]?\n?/, '' 8 | end 9 | 10 | def remove_turbolinks_gem 11 | gsub_file "Gemfile", /( *\# Turbolinks.*\n)? *gem ['"]turbolinks['"].*\n/, '', :verbose => true 12 | end 13 | end 14 | end -------------------------------------------------------------------------------- /lib/generators/umlaut_app_template.rb: -------------------------------------------------------------------------------- 1 | # An app template to set up a Rails app with umlaut, with required 2 | # configuration and generated content. 3 | 4 | # ENV['UMLAUT_GEM_PATH'] can be used to add the line to Gemfile 5 | # with a path to local checkout of Umlaut. NOTE WELL the path 6 | # passed in ENV must be absolute path OR relative to the generated 7 | # app's gemfile (which is confusing). (We've already lost the actual 8 | # CWD by the the time the generator runs, so we can't correct to relative 9 | # to original command CWD) 10 | # 11 | 12 | # Add the lib path from the copy of Umlaut we're in to the load path. 13 | # Since we're an app template, our gem isn't loaded yet. 14 | umlaut_lib = File.expand_path(File.dirname(__FILE__) + '/../') 15 | $LOAD_PATH.unshift(umlaut_lib) if File.directory?(umlaut_lib) && !$LOAD_PATH.include?(umlaut_lib) 16 | 17 | require 'umlaut/version' 18 | 19 | umlaut_version = Umlaut::VERSION.split(".") 20 | 21 | gem_spec_str = 22 | "\ngem 'umlaut', '>= #{Umlaut::VERSION}', '< #{umlaut_version.first.to_i + 1}'" 23 | if ENV["UMLAUT_GEM_PATH"] 24 | path = File.expand_path( ENV["UMLAUT_GEM_PATH"] ) 25 | gem_spec_str += ", :path => '#{path}'" 26 | end 27 | 28 | 29 | append_file "Gemfile", gem_spec_str 30 | 31 | generate "umlaut:remove_turbolinks" 32 | 33 | generate "umlaut:install" 34 | 35 | # future rails will offer an after_bundle hook we could use for 36 | # a post-install message. Instead, the post-install message for now 37 | # is in the `umlaut` command wrapper. -------------------------------------------------------------------------------- /lib/tasks/umlaut_asset_compile.rake: -------------------------------------------------------------------------------- 1 | # Rails4 doesn't create un-fingerprinted assets anymore, but we 2 | # need a couple for umlaut's API. Let's try to hook in and make 3 | # symlinks. 4 | # 5 | # list of what file(globs) need non-digest-named copies is kept in 6 | # Umlaut::Engine.config.non_digest_named_assets 7 | # defined in lib/umlaut.rb, but app can modify it if desired. 8 | 9 | require 'umlaut' 10 | require 'pathname' 11 | 12 | 13 | # Every time assets:precompile is called, trigger umlaut:create_non_digest_assets afterwards. 14 | Rake::Task["assets:precompile"].enhance do 15 | Rake::Task["umlaut:create_non_digest_assets"].invoke 16 | end 17 | 18 | namespace :umlaut do 19 | 20 | # This seems to be basically how ordinary asset precompile 21 | # is logging, ugh. 22 | logger = Logger.new($stderr) 23 | 24 | # Based on suggestion at https://github.com/rails/sprockets-rails/issues/49#issuecomment-20535134 25 | # but limited to files in umlaut's namespaced asset directories. 26 | task :create_non_digest_assets => :"assets:environment" do 27 | manifest_path = Dir.glob(File.join(Rails.root, 'public/assets/manifest-*.json')).first 28 | manifest_data = JSON.load(File.new(manifest_path)) 29 | 30 | manifest_data["assets"].each do |logical_path, digested_path| 31 | logical_pathname = Pathname.new logical_path 32 | 33 | if Umlaut::Engine.config.non_digest_named_assets.any? {|testpath| logical_pathname.fnmatch?(testpath, File::FNM_PATHNAME) } 34 | full_digested_path = File.join(Rails.root, 'public/assets', digested_path) 35 | full_nondigested_path = File.join(Rails.root, 'public/assets', logical_path) 36 | 37 | logger.info "(Umlaut) Copying to #{full_nondigested_path}" 38 | 39 | # Use FileUtils.copy_file with true third argument to copy 40 | # file attributes (eg mtime) too, as opposed to FileUtils.cp 41 | # Making symlnks with FileUtils.ln_s would be another option, not 42 | # sure if it would have unexpected issues. 43 | FileUtils.copy_file full_digested_path, full_nondigested_path, true 44 | end 45 | end 46 | 47 | end 48 | end 49 | 50 | -------------------------------------------------------------------------------- /lib/term_color.rb: -------------------------------------------------------------------------------- 1 | # Just some methods for outputting colorized text to a terminal 2 | # with ansi codes, cribbed from rails. 3 | 4 | module TermColor 5 | mattr_accessor :colorize_logging 6 | # is nil in dev in rails 3.2, but supposed to default to true. okay. 7 | self.colorize_logging = if Rails.application.config.colorize_logging.nil? 8 | # In Rails 3.2, somehow we can't count on config.colorize_logging being 9 | # set, okay, as a default colorize in development only. 10 | Rails.env == "development" 11 | else 12 | Rails.application.config.colorize_logging 13 | end 14 | 15 | # Embed in a String to clear all previous ANSI sequences. 16 | CLEAR = "\e[0m" 17 | BOLD = "\e[1m" 18 | 19 | # Colors 20 | BLACK = "\e[30m" 21 | RED = "\e[31m" 22 | GREEN = "\e[32m" 23 | YELLOW = "\e[33m" 24 | BLUE = "\e[34m" 25 | MAGENTA = "\e[35m" 26 | CYAN = "\e[36m" 27 | WHITE = "\e[37m" 28 | 29 | # Set color by using a string or one of the defined constants. If a third 30 | # option is set to true, it also adds bold to the string. This is based 31 | # on the Highline implementation and will automatically append CLEAR to the 32 | # end of the returned String. 33 | # 34 | def self.color(text, color, bold=false) 35 | return text unless colorize_logging 36 | color = self.const_get(color.to_s.upcase) if color.is_a?(Symbol) 37 | bold = bold ? BOLD : "" 38 | "#{bold}#{color}#{text}#{CLEAR}" 39 | end 40 | 41 | end 42 | -------------------------------------------------------------------------------- /lib/truncate_to_db_limit.rb: -------------------------------------------------------------------------------- 1 | # An ActiveRecord extension that will let you automatically truncate 2 | # certain attributes to the maximum length allowed by the DB. 3 | # 4 | # require 'truncate_to_db_limit' 5 | # class Something < ActiveRecord::Base 6 | # include TruncateToDbLimit 7 | # truncate_to_db_limit :short_attr, :short_attr2 8 | # #... 9 | # 10 | # Truncation is done in a before_validate hook, so won't happen until 11 | # you try to save. 12 | # 13 | module TruncateToDbLimit 14 | extend ActiveSupport::Concern 15 | 16 | included do 17 | class_attribute :'_truncate_to_db_limit_attributes', instance_accessor: false 18 | before_validation :do_truncate_to_db_limit! 19 | 20 | 21 | def self.truncate_to_db_limit(*attribute_names) 22 | self._truncate_to_db_limit_attributes = attribute_names 23 | end 24 | end 25 | 26 | 27 | 28 | 29 | def do_truncate_to_db_limit! 30 | 31 | 32 | self.class._truncate_to_db_limit_attributes.each do |attribute_name| 33 | 34 | ar_attr = self.class.columns_hash[attribute_name.to_s] 35 | 36 | unless ar_attr 37 | raise ArgumentError.new("truncate_to_db_limit #{attribute_name}: No such attribute") 38 | end 39 | 40 | limit = ar_attr.limit 41 | 42 | unless limit && limit.to_i != 0 43 | return # we can do nothing 44 | #raise ArgumentError.new("truncate_to_db_limit #{attribute_name}: Limit not known") 45 | end 46 | 47 | normalized = send(attribute_name).try {|v| v.slice(0, limit)} 48 | send("#{attribute_name}=", normalized) 49 | end 50 | end 51 | 52 | end -------------------------------------------------------------------------------- /lib/umlaut/util.rb: -------------------------------------------------------------------------------- 1 | module Umlaut 2 | module Util 3 | 4 | module_function 5 | 6 | # Provide a prettified and cleaned exception backtrace array from an exception, 7 | # using the Rails backtrace cleaner, as configured in the app. Umlaut configures 8 | # it, in an initializer declared in umlaut.rb, to prettify and include Umlaut trace lines. 9 | # 10 | # This will produce a stack trace similar to what Rails logs by default for uncaught 11 | # exceptions. We use it for exception logging in several places. 12 | # 13 | # Pass in the exception itself, not `exception.backtrace`. 14 | def clean_backtrace(exception) 15 | if defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) 16 | trace = Rails.backtrace_cleaner.clean(exception.backtrace) 17 | trace = Rails.backtrace_cleaner.clean(exception.backtrace, :all) if trace.empty? 18 | return trace 19 | else 20 | return exception.backtrace 21 | end 22 | end 23 | end 24 | end -------------------------------------------------------------------------------- /lib/umlaut/version.rb: -------------------------------------------------------------------------------- 1 | module Umlaut 2 | VERSION = "4.1.7" 3 | 4 | # This is used in Umlaut's .gemspec for generating the gem, 5 | # and is also used in the umlaut app generator to make sure 6 | # we're generating with a compatible Rails version. 7 | RAILS_COMPAT_SPEC = [">= 3.2.12", "< 4.3.0"] 8 | end 9 | -------------------------------------------------------------------------------- /test/dummy/Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | # Add your own tasks in files placed in lib/tasks ending in .rake, 3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 4 | 5 | require File.expand_path('../config/application', __FILE__) 6 | 7 | Dummy::Application.load_tasks 8 | -------------------------------------------------------------------------------- /test/dummy/app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | 2 | // Umlaut javascript required for proper functionality. The 'umlaut' file 3 | // also forces require of jquery and jquery-ui, dependencies. 4 | //= require 'umlaut' 5 | // This is a manifest file that'll be compiled into including all the files listed below. 6 | // Add new JavaScript/Coffee code in separate files in this directory and they'll automatically 7 | // be included in the compiled file accessible from http://example.com/assets/application.js 8 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 9 | // the compiled file. 10 | // 11 | //= require jquery 12 | //= require jquery_ujs 13 | //= require_tree . 14 | -------------------------------------------------------------------------------- /test/dummy/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * The base Umlaut styles: 3 | *= require 'umlaut' 4 | 5 | * This is a manifest file that'll automatically include all the stylesheets available in this directory 6 | * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at 7 | * the top of the compiled file, but it's generally better to create a new file per style scope. 8 | *= require_self 9 | *= require_tree . 10 | */ 11 | 12 | /* Umlaut needs a jquery-ui theme CSS. Here's an easy way to get one, 13 | you can replace with another theme. */ 14 | @import url(http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/themes/ui-lightness/jquery-ui.css); 15 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery 3 | end 4 | -------------------------------------------------------------------------------- /test/dummy/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /test/dummy/app/mailers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/test/dummy/app/mailers/.gitkeep -------------------------------------------------------------------------------- /test/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/test/dummy/app/models/.gitkeep -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dummy 5 | <%= stylesheet_link_tag "application" %> 6 | <%= javascript_include_tag "application" %> 7 | <%= csrf_meta_tags %> 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/dummy/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run Dummy::Application 5 | -------------------------------------------------------------------------------- /test/dummy/config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require 'rails/all' 4 | 5 | Bundler.require 6 | require "umlaut" 7 | 8 | require 'jquery-rails' # Oddly neccesary in our dummy app see http://www.ruby-forum.com/topic/2484569#1021529 9 | 10 | module Dummy 11 | class Application < Rails::Application 12 | # Settings in config/environments/* take precedence over those specified here. 13 | # Application configuration should go into files in config/initializers 14 | # -- all .rb files in that directory are automatically loaded. 15 | 16 | # Custom directories with classes and modules you want to be autoloadable. 17 | # config.autoload_paths += %W(#{config.root}/extras) 18 | 19 | # Only load the plugins named here, in the order given (default is alphabetical). 20 | # :all can be used as a placeholder for all plugins not explicitly named. 21 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ] 22 | 23 | # Activate observers that should always be running. 24 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer 25 | 26 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 27 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 28 | # config.time_zone = 'Central Time (US & Canada)' 29 | 30 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 31 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 32 | # config.i18n.default_locale = :de 33 | 34 | # Configure the default encoding used in templates for Ruby 1.9. 35 | config.encoding = "utf-8" 36 | 37 | # Configure sensitive parameters which will be filtered from the log file. 38 | config.filter_parameters += [:password] 39 | 40 | # Enable the asset pipeline 41 | config.assets.enabled = true 42 | 43 | # Version of your assets, change this if you want to expire all your assets 44 | config.assets.version = '1.0' 45 | 46 | config.secret_key_base = "adfadf" 47 | 48 | I18n.enforce_available_locales = true 49 | end 50 | end 51 | 52 | -------------------------------------------------------------------------------- /test/dummy/config/boot.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | gemfile = File.expand_path('../../../../Gemfile', __FILE__) 3 | 4 | if File.exist?(gemfile) 5 | ENV['BUNDLE_GEMFILE'] = gemfile 6 | require 'bundler' 7 | Bundler.setup 8 | end 9 | 10 | $:.unshift File.expand_path('../../../../lib', __FILE__) -------------------------------------------------------------------------------- /test/dummy/config/database.yml.example: -------------------------------------------------------------------------------- 1 | # Umlaut dumm app database.yml.example. 2 | # 3 | # Copy this file to database.yml and configure 4 | # for your environment, in order to run tests, or run the dummy application. 5 | # 6 | # https://github.com/team-umlaut/umlaut/wiki/Developing#running-umlaut-tests 7 | 8 | 9 | # 10 | # You must fill out connection details for the 'test' database 11 | # below to run Umlaut tests: 12 | # 13 | # Warning: The database defined as "test" will be erased and 14 | # re-generated from your development database when you run "rake". 15 | # Do not set this db to the same as a development or production db. 16 | 17 | test: 18 | adapter: mysql2 19 | pool: 5 20 | database: your_database_name 21 | username: umlaut 22 | password: your_database_password 23 | host: your.database.host 24 | 25 | # Above is the minimum you need to do to run tests, you can just 26 | # go and run them now. 27 | 28 | # There is some Umlaut functionality that runs directly 29 | # against an SFX db, and to test it, we need a mock DB that 30 | # looks like SFX. Definining this `sfx_db` database is 31 | # optional, if you don't define it those tests just 32 | # won't be run, which is fine unless you know you want to run them. 33 | # Travis will catch them anyway. 34 | # 35 | # If you do want to run tests against it, uncomment and 36 | # configure, with mock_instance: true. 37 | # 38 | # Do NOT point this at a real SFX db, running the tests 39 | # WILL alter the database. 40 | 41 | # sfx_db: 42 | # adapter: mysql2 43 | # database: your_database_name 44 | # username: umlaut 45 | # password: your_database_password 46 | # host: your.database.host 47 | # # :mock_instance is used for SFX search testing. 48 | # # DO NOT SET :mock_instance TO TRUE FOR ANYTHING LIKE A REAL SFX DATABASE. 49 | # # If in doubt set :mock_instance => false 50 | # mock_instance: true 51 | 52 | 53 | 54 | 55 | 56 | 57 | # Sometimes it can be convenient to start up the included 58 | # dummy app and interact with it in a browser etc. Definining 59 | # a development database may be useful for that scenario, but 60 | # is not needed for testing. 61 | 62 | development: 63 | adapter: mysql2 64 | pool: 10 65 | database: your_database_name 66 | username: umlaut 67 | password: your_database_password 68 | host: your.database.host 69 | 70 | 71 | 72 | 73 | 74 | # There should ordinarily be no reason to define a production 75 | # db in this dummy app 76 | # production: 77 | # <<: *dev 78 | # database: umlaut3_prod 79 | 80 | -------------------------------------------------------------------------------- /test/dummy/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the rails application 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the rails application 5 | Dummy::Application.initialize! 6 | -------------------------------------------------------------------------------- /test/dummy/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Dummy::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | # 8 | # UMLAUT: Umlaut's use of threading is not compatible with class 9 | # reloading, even in development. Umlaut requires true here. 10 | # Rails 3.2 _might_ let you get away with false when it comes out. 11 | config.cache_classes = true 12 | 13 | # Log error messages when you accidentally call methods on nil. 14 | config.whiny_nils = true 15 | 16 | # Show full error reports and disable caching 17 | config.consider_all_requests_local = true 18 | config.action_controller.perform_caching = false 19 | 20 | # Don't care if the mailer can't send 21 | config.action_mailer.raise_delivery_errors = false 22 | 23 | # Print deprecation notices to the Rails logger 24 | config.active_support.deprecation = :log 25 | 26 | # Only use best-standards-support built into browsers 27 | config.action_dispatch.best_standards_support = :builtin 28 | 29 | # Do not compress assets 30 | config.assets.compress = false 31 | 32 | # Expands the lines which load the assets 33 | config.assets.debug = true 34 | 35 | 36 | # Log the query plan for queries taking more than this (works 37 | # with SQLite, MySQL, and PostgreSQL) 38 | # This seems to not work right in multi-threaded AR use as we're doing, 39 | # leave off, at least for now. 40 | #config.active_record.auto_explain_threshold_in_seconds = 0.5 41 | end 42 | -------------------------------------------------------------------------------- /test/dummy/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Dummy::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # Code is not reloaded between requests 5 | config.cache_classes = true 6 | 7 | # Full error reports are disabled and caching is turned on 8 | config.consider_all_requests_local = false 9 | config.action_controller.perform_caching = true 10 | 11 | # Disable Rails's static asset server (Apache or nginx will already do this) 12 | config.serve_static_assets = false 13 | 14 | # Compress JavaScripts and CSS 15 | config.assets.compress = true 16 | 17 | # Don't fallback to assets pipeline if a precompiled asset is missed 18 | config.assets.compile = false 19 | 20 | # Generate digests for assets URLs 21 | config.assets.digest = true 22 | 23 | # Defaults to Rails.root.join("public/assets") 24 | # config.assets.manifest = YOUR_PATH 25 | 26 | # Specifies the header that your server uses for sending files 27 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache 28 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx 29 | 30 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 31 | # config.force_ssl = true 32 | 33 | # See everything in the log (default is :info) 34 | # config.log_level = :debug 35 | 36 | # Use a different logger for distributed setups 37 | # config.logger = SyslogLogger.new 38 | 39 | # Use a different cache store in production 40 | # config.cache_store = :mem_cache_store 41 | 42 | # Enable serving of images, stylesheets, and JavaScripts from an asset server 43 | # config.action_controller.asset_host = "http://assets.example.com" 44 | 45 | # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added) 46 | # config.assets.precompile += %w( search.js ) 47 | 48 | # Disable delivery errors, bad email addresses will be ignored 49 | # config.action_mailer.raise_delivery_errors = false 50 | 51 | # Enable threaded mode 52 | # config.threadsafe! 53 | 54 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 55 | # the I18n.default_locale when a translation can not be found) 56 | config.i18n.fallbacks = true 57 | 58 | # Send deprecation notices to registered listeners 59 | config.active_support.deprecation = :notify 60 | end 61 | -------------------------------------------------------------------------------- /test/dummy/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Dummy::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | config.eager_load = false 10 | 11 | # Configure static asset server for tests with Cache-Control for performance 12 | config.serve_static_assets = true 13 | config.static_cache_control = "public, max-age=3600" 14 | 15 | # Log error messages when you accidentally call methods on nil 16 | config.whiny_nils = true 17 | 18 | # Show full error reports and disable caching 19 | config.consider_all_requests_local = true 20 | config.action_controller.perform_caching = false 21 | 22 | # Raise exceptions instead of rendering exception templates 23 | config.action_dispatch.show_exceptions = false 24 | 25 | # Disable request forgery protection in test environment 26 | config.action_controller.allow_forgery_protection = false 27 | 28 | # Tell Action Mailer not to deliver emails to the real world. 29 | # The :test delivery method accumulates sent emails in the 30 | # ActionMailer::Base.deliveries array. 31 | config.action_mailer.delivery_method = :test 32 | 33 | # Use SQL instead of Active Record's schema dumper when creating the test database. 34 | # This is necessary if your schema can't be completely dumped by the schema dumper, 35 | # like if you have constraints or database-specific column types 36 | # config.active_record.schema_format = :sql 37 | 38 | # Print deprecation notices to the stderr 39 | config.active_support.deprecation = :stderr 40 | 41 | config.active_support.test_order = :random 42 | end 43 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format 4 | # (all these examples are active by default): 5 | # ActiveSupport::Inflector.inflections do |inflect| 6 | # inflect.plural /^(ox)$/i, '\1en' 7 | # inflect.singular /^(ox)en/i, '\1' 8 | # inflect.irregular 'person', 'people' 9 | # inflect.uncountable %w( fish sheep ) 10 | # end 11 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | # Mime::Type.register_alias "text/html", :iphone 6 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | # Make sure the secret is at least 30 characters and all random, 6 | # no regular words or you'll be exposed to dictionary attacks. 7 | Dummy::Application.config.secret_token = 'c4943e40548e96896e3aa5d03bba3be644ddcf945569e8ef089578207dd8f55f57e83893158352dff0333e07231a64ce2ee4bf696e5e85334ec6f37d97d77253' 8 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Dummy::Application.config.session_store :cookie_store, :key => '_dummy_session' 4 | 5 | # Use the database for sessions instead of the cookie-based default, 6 | # which shouldn't be used to store highly confidential information 7 | # (create the session table with "rails generate session_migration") 8 | # Dummy::Application.config.session_store :active_record_store 9 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters :format => [:json] 9 | end 10 | 11 | # Disable root element in JSON by default. 12 | ActiveSupport.on_load(:active_record) do 13 | self.include_root_in_json = false 14 | end 15 | -------------------------------------------------------------------------------- /test/dummy/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Sample localization file for English. Add more files in this directory for other locales. 2 | # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. 3 | 4 | en: 5 | hello: "Hello world" 6 | -------------------------------------------------------------------------------- /test/dummy/config/routes.rb: -------------------------------------------------------------------------------- 1 | Dummy::Application.routes.draw do 2 | 3 | Umlaut::Routes.new(self, :admin => true).draw 4 | 5 | # The priority is based upon order of creation: 6 | # first created -> highest priority. 7 | 8 | # Sample of regular route: 9 | # match 'products/:id' => 'catalog#view' 10 | # Keep in mind you can assign values other than :controller and :action 11 | 12 | # Sample of named route: 13 | # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase 14 | # This route can be invoked with purchase_url(:id => product.id) 15 | 16 | # Sample resource route (maps HTTP verbs to controller actions automatically): 17 | # resources :products 18 | 19 | # Sample resource route with options: 20 | # resources :products do 21 | # member do 22 | # get 'short' 23 | # post 'toggle' 24 | # end 25 | # 26 | # collection do 27 | # get 'sold' 28 | # end 29 | # end 30 | 31 | # Sample resource route with sub-resources: 32 | # resources :products do 33 | # resources :comments, :sales 34 | # resource :seller 35 | # end 36 | 37 | # Sample resource route with more complex sub-resources 38 | # resources :products do 39 | # resources :comments 40 | # resources :sales do 41 | # get 'recent', :on => :collection 42 | # end 43 | # end 44 | 45 | # Sample resource route within a namespace: 46 | # namespace :admin do 47 | # # Directs /admin/products/* to Admin::ProductsController 48 | # # (app/controllers/admin/products_controller.rb) 49 | # resources :products 50 | # end 51 | 52 | # You can have the root of your site routed with "root" 53 | # just remember to delete public/index.html. 54 | # root :to => 'welcome#index' 55 | 56 | # See how all your routes lay out with "rake routes" 57 | 58 | # This is a legacy wild controller route that's not recommended for RESTful applications. 59 | # Note: This route will make all actions in every controller accessible via GET requests. 60 | # match ':controller(/:action(/:id(.:format)))' 61 | end 62 | -------------------------------------------------------------------------------- /test/dummy/config/sunspot.yml: -------------------------------------------------------------------------------- 1 | # Dummy config for testing sfx4solr search module. 2 | # Solr is not running on localhost. Instead we use a 3 | # VCR cassette to test the expected result from Solr. 4 | production: 5 | solr: 6 | hostname: localhost 7 | port: 8983 8 | log_level: WARNING 9 | 10 | development: 11 | solr: 12 | hostname: localhost 13 | port: 8982 14 | log_level: INFO 15 | 16 | test: 17 | solr: 18 | hostname: localhost 19 | port: 8981 20 | log_level: WARNING -------------------------------------------------------------------------------- /test/dummy/db/migrate/20120927164040_sfx4_local.rb: -------------------------------------------------------------------------------- 1 | # This is used for SFX search testing. 2 | # DO NOT USE THIS FOR ANYTHING LIKE A REAL SFX DATABASE. 3 | class Sfx4Local < ActiveRecord::Migration 4 | def connection 5 | if sfx4_mock_instance? 6 | Sfx4::Local::Base.connection.initialize_schema_migrations_table 7 | return Sfx4::Local::Base.connection 8 | end 9 | end 10 | 11 | def change 12 | if sfx4_mock_instance? 13 | create_table "AZ_TITLE", {:id => false} do |t| 14 | t.integer "AZ_TITLE_ID", :default => 0, :null => false 15 | t.string "AZ_PROFILE", :limit => 100, :null => false 16 | t.integer "OBJECT_ID", :default => 0, :null => false, :limit => 8 17 | t.string "TITLE_DISPLAY", :limit => 255, :null => false 18 | t.string "TITLE_SORT", :limit => 200, :null => false 19 | t.string "SCRIPT", :limit => 20, :null => false 20 | end 21 | execute "ALTER TABLE AZ_TITLE ADD PRIMARY KEY (AZ_TITLE_ID);" 22 | 23 | create_table "AZ_EXTRA_INFO", {:id => false} do |t| 24 | t.integer "AZ_EXTRA_INFO_ID", :default => 0, :null => false 25 | t.string "AZ_PROFILE", :limit => 100, :null => false 26 | t.integer "OBJECT_ID", :default => 0, :null => false, :limit => 8 27 | t.text "EXTRA_INFO_XML", :limit => 16777215 28 | end 29 | execute "ALTER TABLE AZ_EXTRA_INFO ADD PRIMARY KEY (AZ_EXTRA_INFO_ID);" 30 | 31 | create_table "AZ_TITLE_SEARCH", {:id => false} do |t| 32 | t.integer "AZ_TITLE_SEARCH_ID", :default => 0, :null => false 33 | t.string "AZ_PROFILE", :limit => 100, :null => false 34 | t.integer "AZ_TITLE_ID", :default => 0, :null => false 35 | t.text "TITLE_SEARCH", :null => false 36 | end 37 | execute "ALTER TABLE AZ_TITLE_SEARCH ADD PRIMARY KEY (AZ_TITLE_SEARCH_ID);" 38 | 39 | create_table "AZ_LETTER_GROUP", {:id => false} do |t| 40 | t.integer "AZ_LETTER_GROUP_ID", :default => 0, :null => false 41 | t.integer "AZ_TITLE_ID", :default => 0, :null => false 42 | t.string "AZ_LETTER_GROUP_NAME", :limit => 10, :null => false 43 | end 44 | execute "ALTER TABLE AZ_LETTER_GROUP ADD PRIMARY KEY (AZ_LETTER_GROUP_ID);" 45 | else 46 | puts "Skipping SFX DB migration since the SFX DB specified is not a mock instance." 47 | end 48 | end 49 | 50 | def sfx4_mock_instance? 51 | (ActiveRecord::Base.configurations["sfx_db"] and 52 | ActiveRecord::Base.configurations["sfx_db"]["mock_instance"]) 53 | end 54 | end -------------------------------------------------------------------------------- /test/dummy/db/migrate/README: -------------------------------------------------------------------------------- 1 | Migrations that are ordinarily created in an Umlaut-enabled app are 2 | _not_ included here in this test/dummy app used for testing. 3 | 4 | In a Rails plugin gem, Rails db:migrate seems to be able to use 5 | the migrations from the actual gem itself, in the gem source 6 | ./db/migrate, and if they are doubly included here, we get 7 | duplicate migration errors trying to db:migrate. 8 | 9 | We do have one migration in here, to create the mock SFX database for testing, 10 | something an ordinary real Umlaut app does not do. -------------------------------------------------------------------------------- /test/dummy/lib/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/test/dummy/lib/assets/.gitkeep -------------------------------------------------------------------------------- /test/dummy/log/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/test/dummy/log/.gitkeep -------------------------------------------------------------------------------- /test/dummy/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The page you were looking for doesn't exist.

23 |

You may have mistyped the address or the page may have moved.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /test/dummy/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The change you wanted was rejected.

23 |

Maybe you tried to change something you didn't have access to.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /test/dummy/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

We're sorry, but something went wrong.

23 |

We've been notified about this issue and we'll take a look at it shortly.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /test/dummy/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/test/dummy/public/favicon.ico -------------------------------------------------------------------------------- /test/dummy/script/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. 3 | 4 | APP_PATH = File.expand_path('../../config/application', __FILE__) 5 | require File.expand_path('../../config/boot', __FILE__) 6 | require 'rails/commands' 7 | -------------------------------------------------------------------------------- /test/fixtures/dispatched_services.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html 2 | -------------------------------------------------------------------------------- /test/fixtures/permalinks.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html 2 | nytimes: 3 | referent: nytimes 4 | 5 | expired_referent: 6 | context_obj_serialized: | 7 | 8 | 9 | 10 | 11 | info:ofi/fmt:xml:xsd:journal 12 | 13 | 14 | 0028-792X 15 | The New Yorker 16 | 110975413975944 17 | 18 | 19 | 20 | 21 | info:lccn/2011201780 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/fixtures/referents.yml: -------------------------------------------------------------------------------- 1 | # Referent fixtures 2 | frankenstein: 3 | isbn: "9780393964585" 4 | title: "frankenstein : the 1818 text, contexts, nineteenth-century responses, modern criticism" 5 | year: "1997" 6 | 7 | nytimes: 8 | issn: 0362-4331 9 | title: The New York times 10 | 11 | simple: 12 | issn: "0098-7484" 13 | 14 | critical_inquiry: 15 | title: "Critical inquiry" 16 | issn: "0093-1896" 17 | 18 | sfx_multi_obj_request_no_fulltext_referent: 19 | title: "Mobile Information Systems" 20 | 21 | momo: 22 | title: Momo 23 | isbn: 038519093X 24 | 25 | advocate: 26 | title: Advocate 27 | issn: 1832-9373 28 | 29 | coffeemakers: 30 | atitle: "A blend of different tastes: the language of coffeemakers" 31 | title: 'Environment and planning B, Planning and design' 32 | issn: 0265-8135 33 | year: 1998 34 | volume: 25 35 | 36 | manually_entered: 37 | title: Entry Manual 38 | -------------------------------------------------------------------------------- /test/fixtures/requests.yml: -------------------------------------------------------------------------------- 1 | # Request fixtures 2 | frankenstein: 3 | client_ip_is_simulated: 4 | session_id: 9a9f27cb54d8ccf83e272d8bce87eeec 5 | referrer_id: 1 6 | contextobj_fingerprint: ae44376f47a8636b3f99faad660222fb 7 | referent: frankenstein 8 | client_ip_addr: 127.0.0.1 9 | 10 | nytimes: 11 | referent: nytimes 12 | session_id: 06727b665a0594d926d91e49ef6ad8db 13 | contextobj_fingerprint: 07b546cc6a0899602c2b835d645dcb4a 14 | 15 | simple: 16 | session_id: "f747f4273c1ecb12f1f764cc10c646d3" 17 | referent: simple 18 | client_ip_addr: "128.220.205.186" 19 | contextobj_fingerprint: "b75283f6fb365d5901fbdc6726301370" 20 | http_env: { SERVER_NAME : "app01.mse.jhu.edu", "HTTP_ACCEPT_ENCODING" : "gzip,deflate", "HTTP_USER_AGENT" : "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.10) Gecko/2009042316 Firefox/3.0.10 (.NET CLR 3.5.30729)", "HTTP_ACCEPT_LANGUAGE" : "en-us,en;q=0.5", "HTTP_HOST" : "app01.mse.jhu.edu:3000", "HTTP_KEEP_ALIVE" : "300", "HTTP_ACCEPT_CHARSET" : "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "HTTP_VERSION" : "HTTP/1.1", "REQUEST_URI" : "/resolve?url_ver=Z39.88-2004&url_ctx_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Actx&ctx_enc=info%3Aofi%2Fenc%3AUTF-8&rft.issn=0098-7484&rft.genre=journal&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal", "HTTP_ACCEPT" : "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "HTTP_CONNECTION" : "keep-alive" } 21 | 22 | critical_inquiry: 23 | referent: critical_inquiry 24 | session_id: "f747f4273c1ecb12f1f764cc10c646d7" 25 | client_ip_addr: "127.0.0.1" 26 | contextobj_fingerprint: "b75283f6fb365d5901fbdc6726301377" 27 | 28 | sfx_multi_obj_request_no_fulltext_request: 29 | referent: sfx_multi_obj_request_no_fulltext_referent 30 | session_id: "f747f4273c1ecb12f1f764cc10c646d7" 31 | client_ip_addr: "127.0.0.1" 32 | contextobj_fingerprint: "b75283f6fb365d5901fbdc6726301377" 33 | 34 | momo: 35 | referent: momo 36 | 37 | advocate: 38 | referent: advocate 39 | referrer_id: info:sid/sfxit.com:citation 40 | 41 | coffeemakers: 42 | referent: coffeemakers 43 | 44 | manually_entered: 45 | referent: manually_entered 46 | referrer_id: info:sid/sfxit.com:citation 47 | -------------------------------------------------------------------------------- /test/fixtures/sfx_urls.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html 2 | gateway: 3 | id: 1 4 | url: gateway.ovid.com 5 | proquest: 6 | id: 2 7 | url: proquest.umi.com -------------------------------------------------------------------------------- /test/functional/export_email_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | 4 | # The ExportEmailController provides custom interactions 5 | # for the ExportEmail service. 6 | class ExportEmailControllerTest < ActionController::TestCase 7 | fixtures :requests, :referents, :referent_values, :dispatched_services, :service_responses 8 | 9 | setup do 10 | @email_service_response = service_responses(:service_response3) 11 | @txt_holding_service_response = service_responses(:service_response9) 12 | end 13 | 14 | test "layout" do 15 | get(:email, :id => @email_service_response.id) 16 | assert_response :success 17 | assert_select "body div.umlaut-container", 1 18 | assert_select "div.email", 1 19 | end 20 | 21 | test "layout xhr" do 22 | xhr :get, :email, :id => @email_service_response.id 23 | assert_response :success 24 | # Assert that no layout was included in the request 25 | assert_select "body", 0 26 | assert_select "div.umlaut-container", 0 27 | assert_select "div.email", 1 28 | end 29 | 30 | test "send email" do 31 | to_addr = "nobody@example.org" 32 | post :send_email, :id => @email_service_response.id, :email => to_addr 33 | 34 | 35 | # Mail was sent 36 | assert !ActionMailer::Base.deliveries.empty? 37 | assert ActionMailer::Base.deliveries.find do |message| 38 | message.to.include? to_addr 39 | end 40 | 41 | assert_select "div", :text => /Sent to #{to_addr}/ 42 | end 43 | end -------------------------------------------------------------------------------- /test/functional/link_router_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | class LinkRouterControllerTest < ActionController::TestCase 3 | fixtures :service_responses 4 | test "index" do 5 | service_response = service_responses(:service_response8) 6 | get :index, {id: service_response.id} 7 | assert_response :redirect 8 | assert_redirected_to "http://holding.library.edu/DOCID" 9 | end 10 | 11 | test "error" do 12 | assert_raises(ActiveRecord::RecordNotFound) { 13 | get :index, {id: "this should throw an error"} 14 | } 15 | end 16 | end -------------------------------------------------------------------------------- /test/functional/resolve_controller_more_test.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | require 'test_helper' 3 | 4 | 5 | # Another class for resolve controller tests, trying out other ways 6 | # of testing, with isolated mock service definitions. 7 | # 8 | # Still need to call @controller.bg_thread.wait to wait on 9 | # all background threads before the test ends. 10 | class ResolveControllerMoreTest < ActionController::TestCase 11 | 12 | setup do 13 | @controller = ResolveController.new 14 | end 15 | 16 | # threads and transactional_fixtures are unhappy 17 | self.use_transactional_fixtures = false 18 | 19 | 20 | # A mess to test, indicates messy architecture, but we do what we can. 21 | def test_retries_failed_temporary 22 | service_def = { "DummyService" => 23 | { "type" => "DummyService", 24 | "priority" => 3, 25 | "responses" => [ 26 | { "service_type_value" => "fulltext", 27 | "display_text" => "created" 28 | } 29 | ] 30 | } 31 | } 32 | config = {"default" => {"services" => service_def}} 33 | 34 | 35 | with_service_config(config) do 36 | @controller = ResolveController.new 37 | 38 | original_updated_at = Time.now - @controller.umlaut_config.requeue_failedtemporary_services_in - 1 39 | 40 | umlaut_request = fake_umlaut_request("/?foo=bar") 41 | umlaut_request.dispatched_services.create( 42 | :status => DispatchedService::FailedTemporary, 43 | :service_id => "DummyService", 44 | :updated_at => original_updated_at 45 | ) 46 | 47 | get(:index, {'umlaut.request_id' => umlaut_request.id}) 48 | @controller.bg_thread.join 49 | 50 | ds = umlaut_request.dispatched_services(true).find {|ds| ds.service_id == "DummyService"} 51 | 52 | assert ds, "DispatchedService does not exist for DummyService" 53 | assert ds.status == DispatchedService::Successful, "DispatchedService not marked successful" 54 | assert ds.updated_at > original_updated_at, "DispatchedService updated_at not updated" 55 | 56 | assert umlaut_request.service_responses.to_a.find {|sr| sr.service_id == "DummyService" }, "ServiceResponse not created" 57 | end 58 | end 59 | 60 | end 61 | -------------------------------------------------------------------------------- /test/functional/search_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | class SearchControllerTest < ActionController::TestCase 3 | setup do 4 | @controller = SearchController.new 5 | end 6 | 7 | test "index" do 8 | get :index 9 | assert_response :success 10 | assert_select "title", "Find It | Journals" 11 | assert_select ".umlaut-search-form", 2 12 | assert_select ".umlaut-results", 0 13 | end 14 | 15 | # Tests don't currently support contains searching because sdalton can't/won't 16 | # figure out FULLTEXT indexing in MySQL, so we'll test begins with searching. 17 | test "journal search" do 18 | return unless Sfx4::Local::AzTitle.connection_configured? 19 | get :journal_search, "rft.jtitle"=>"Account", "umlaut.title_search_type"=>"begins" 20 | assert_response :success 21 | 22 | # some versions of rails escape apostrophes here others don't, we don't care 23 | assert_select "title", /\AFind It | Journal titles that begin with (')|(\')Account(')|(\')\Z/ 24 | assert_select ".umlaut-search-form", 1 25 | assert_select ".umlaut-results", 1 26 | assert_select ".umlaut-results .umlaut-result", :minimum => 1 27 | assert_select ".umlaut-pagination", 2 28 | assert_select ".umlaut-az", 0 29 | end 30 | 31 | test "journal list" do 32 | return unless Sfx4::Local::AzTitle.connection_configured? 33 | get :journal_list, :id => "A" 34 | assert_response :success 35 | assert_select "title", "Find It | Browse by Journal Title: A" 36 | assert_select ".umlaut-search-form", 1 37 | assert_select ".umlaut-results", 1 38 | assert_select ".umlaut-results .umlaut-result", :minimum => 1 39 | assert_select ".umlaut-pagination", 2 40 | assert_select ".umlaut-az", 1 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /test/functional/store_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | class StoreControllerTest < ActionController::TestCase 3 | fixtures :permalinks, :requests, :referents, :referent_values 4 | test "index with permalink referent" do 5 | permalink = permalinks(:nytimes) 6 | referent = permalink.referent 7 | get :index, {id: permalink.id} 8 | # assert_redirected_to doesn't work as advertised so HACK! 9 | assert(@controller.location.starts_with?("http://test.host/resolve?umlaut.referent_id=#{referent.id}&url_ver=Z39.88-2004&url_ctx_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Actx&ctx_ver=Z39.88-2004&"), "Not redirecting to the correct location.") 10 | assert(/rft\.issn=0362-4331/===@controller.location, "In the redirect url, rft.issn is expected to be \"0362-4331\", but isn't. Actual location: #{@controller.location}") 11 | assert(/rft\.jtitle=The\+New\+York\+times/===@controller.location, "In the redirect url, rft.jtitle is expected to be \"The New York times\", but isn't. Actual location: #{@controller.location}") 12 | end 13 | 14 | test "index without permalink referent" do 15 | permalink = permalinks(:expired_referent) 16 | get :index, {id: permalink.id} 17 | assert_response :redirect 18 | # assert_redirected_to doesn't work as advertised so HACK! 19 | assert(@controller.location.starts_with?("http://test.host/resolve?umlaut.referent_id="), "Not redirecting to the correct location.") 20 | assert(/rft\.issn=0028792X/===@controller.location, "In the redirect url, rft.issn is expected to be \"0028792X\", but isn't. Actual location: #{@controller.location}") 21 | assert(/rft\.jtitle=The\+New\+Yorker/===@controller.location, "In the redirect url, rft.jtitle is expected to be \"The New Yorker\", but isn't. Actual location: #{@controller.location}") 22 | end 23 | 24 | test "error" do 25 | get :index, {id: "this should not be found"} 26 | assert_response :not_found 27 | assert_select 'title', "The page you were looking for doesn't exist (404)" 28 | end 29 | end -------------------------------------------------------------------------------- /test/integration/permalinks_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PermalinksTest < ActionDispatch::IntegrationTest 4 | 5 | 6 | test "missing id" do 7 | get "/go/999999999" 8 | 9 | assert_response(:missing) 10 | end 11 | 12 | 13 | 14 | end 15 | -------------------------------------------------------------------------------- /test/integration/request_reuse_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class RequestReuseTest < ActionDispatch::IntegrationTest 4 | def setup 5 | # for these tests, we don't want to actually execute any services, zero it out. 6 | @orig_service_store_config = ServiceStore.config["default"]["services"] 7 | ServiceStore.config["default"]["services"] = {} 8 | end 9 | 10 | def teardown 11 | # restore original services, see setup 12 | ServiceStore.config["default"]["services"] = @orig_service_store_config 13 | end 14 | 15 | test "reuse_of_request_in_session" do 16 | sess = open_session 17 | 18 | request_params = { :issn => "012345678" } 19 | 20 | sess.get "/resolve", request_params 21 | 22 | created_request_id = sess.assigns[:user_request].id 23 | 24 | get "/resolve", request_params 25 | 26 | # re-use same user_request object 27 | assert_equal( created_request_id, sess.assigns[:user_request].id ) 28 | 29 | end 30 | 31 | test "no re-use from different session" do 32 | request_params = { :issn => "012345678" } 33 | 34 | sess1 = open_session 35 | sess1.get "/resolve", request_params 36 | created_request_id = sess1.assigns[:user_request].id 37 | 38 | sess2 = open_session 39 | sess2.get "/resolve", request_params 40 | # no re-use, different umlaut Reqeust object. 41 | assert_not_equal( created_request_id, sess2.assigns[:user_request].id ) 42 | end 43 | 44 | test "umlaut.force_new_request" do 45 | 46 | request_params = { :issn => "012345679" } 47 | 48 | sess1 = open_session 49 | sess1.get "/resolve", request_params 50 | created_request_id = sess1.assigns[:user_request].id 51 | 52 | # Wind up with new different request with force_new_request 53 | sess1.get "/resolve", request_params.merge("umlaut.force_new_request" => "true") 54 | request_after_forced = sess1.assigns[:user_request].id 55 | 56 | assert_not_equal( request_after_forced, created_request_id ) 57 | 58 | # Make a request again without force param, still get the latter request, 59 | # the one created when we forced it. 60 | sess1.get "/resolve", request_params 61 | assert_equal( sess1.assigns[:user_request].id, request_after_forced ) 62 | end 63 | 64 | 65 | 66 | end 67 | -------------------------------------------------------------------------------- /test/support/search_methods/test_case.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/test/support/search_methods/test_case.rb -------------------------------------------------------------------------------- /test/support/service_adaptors/test_case.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/team-umlaut/umlaut/b954895e0aa0a7cd0a9ec6bb716c1886c813601e/test/support/service_adaptors/test_case.rb -------------------------------------------------------------------------------- /test/umlaut_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UmlautTest < ActiveSupport::TestCase 4 | test "truth" do 5 | assert_kind_of Module, Umlaut 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/unit/active_record_connection_pool_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'minitest/unit' 3 | 4 | class ActiveRecordConnectionPoolTest < ActiveSupport::TestCase 5 | 6 | # Test that the bug in AR ConnectionPool is fixed, either becuase of a local 7 | # patch (temporarily in config/initializers/patch/connection_pool.rb) 8 | # or because of a future version of Rails. 9 | # See https://github.com/rails/rails/issues/5330 10 | # 11 | # This is fixed in current rails, but it's painful enough to debug that we're going 12 | # to leave our own regression test in here 13 | def test_threaded_with_connection 14 | # Neccesary to have a checked out connection in thread 15 | # other than one we will test, in order to trigger bug 16 | # we are testing fix for. 17 | main_thread_conn = ActiveRecord::Base.connection_pool.checkout 18 | 19 | aThread = Thread.new do 20 | ActiveRecord::Base.connection_pool.with_connection do 21 | ActiveRecord::Base.connection # need to do something AR to trigger the checkout 22 | 23 | reserved_thread_ids = ActiveRecord::Base.connection_pool.instance_variable_get(:@reserved_connections) 24 | 25 | assert reserved_thread_ids.keys.include?( Thread.current.object_id ), "thread should be in reserved connections" 26 | end 27 | reserved_thread_ids = ActiveRecord::Base.connection_pool.instance_variable_get(:@reserved_connections) 28 | assert !reserved_thread_ids.keys.include?( Thread.current.object_id ), "thread should not be in reserved connections" 29 | end 30 | aThread.join 31 | 32 | ActiveRecord::Base.connection_pool.checkin main_thread_conn 33 | 34 | reserved_thread_ids = ActiveRecord::Base.connection_pool.instance_variable_get(:@reserved_connections) 35 | assert !reserved_thread_ids.keys.include?( aThread.object_id ), "thread should not be in reserved connections" 36 | end 37 | 38 | # Our own monkey-patched behavior 39 | def test_forbid_implicit_checkout 40 | assert_raises(ActiveRecord::ImplicitConnectionForbiddenError) do 41 | t = Thread.new do 42 | # Avoid warning to console for implicit checkout, we're doing it on 43 | # purpose for testing. 44 | Thread.current[:ar_implicit_checkout_warning_silenced] = true 45 | 46 | ActiveRecord::Base.forbid_implicit_checkout_for_thread! 47 | ActiveRecord::Base.connection 48 | end 49 | t.join 50 | end 51 | end 52 | 53 | end 54 | -------------------------------------------------------------------------------- /test/unit/amazon_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | # Warning, amazon.rb is very poorly covered by tests, but we have 4 | # to start somewhere. 5 | # 6 | # To regenerate VCR requests, you need to set in your ENV an AMAZON_API_KEY, 7 | # AMAZON_SECRET_KEY, and AMAZON_AFFILIATE_CODE 8 | class AmazonTest < ActiveSupport::TestCase 9 | extend TestWithCassette 10 | 11 | @@amazon_api_key = (ENV['AMAZON_API_KEY'] || 'DUMMY_API_KEY') 12 | @@amazon_secret_key = (ENV['AMAZON_SECRET_KEY'] || 'DUMMY_SECRET_KEY') 13 | @@amazon_associate_tag = (ENV['AMAZON_ASSOCIATE_TAG'] || "DUMMY_ASSOCIATE_TAG") 14 | 15 | VCR.configure do |c| 16 | c.filter_sensitive_data("DUMMY_API_KEY", :amazon) { @@amazon_api_key } 17 | c.filter_sensitive_data("DUMMY_SECRET_KEY", :amazon) { @@amazon_secret_key } 18 | c.filter_sensitive_data("DUMMY_ASSOCIATE_TAG", :amazon) { @@amazon_associate_tag } 19 | end 20 | 21 | def setup 22 | @service_id = 'test_amazon' 23 | @service = Amazon.new('service_id' => @service_id, 24 | 'priority' => 0, 25 | 'api_key' => @@amazon_api_key, 26 | 'secret_key' => @@amazon_secret_key, 27 | 'associate_tag' => @@amazon_associate_tag 28 | ) 29 | end 30 | 31 | 32 | test_with_cassette("product_advertising_api_forbidden", :amazon, 33 | :match_requests_on => [:method, VCR.request_matchers.uri_without_param(:Signature, :Timestamp)]) do 34 | # For some reason, Amazon won't allow some items to be looked up 35 | # in Product Advertising API -- this usually probably means the item 36 | # simply isn't listed on Amazon. Returns: 37 | # 38 | # AWS.ECommerceService.ItemNotAccessible 39 | # This item is not accessible through the Product Advertising API. 40 | # 41 | # 42 | # We want the adapter to handle that smootly and just give up. 43 | # This is an item that at least at time of writing was such. 44 | 45 | umlaut_request = fake_umlaut_request("/resolve?rft.date=2011&rft.genre=book&rft.isbn=9780759113473&rft.place=Lanham&rft.pub=AltaMira+Press&rft.title=Stewardship%3A+Collections+and+Historic+Preservation") 46 | 47 | @service.handle(umlaut_request) 48 | 49 | ds = umlaut_request.dispatched_services.to_a.find {|ds| ds.service_id == @service_id} 50 | 51 | assert_present ds, "Missing DispatchedService for #{@service_id}" 52 | assert_equal DispatchedService::Successful, ds.status, "Expected status Succesful not #{ds.status}" 53 | end 54 | end -------------------------------------------------------------------------------- /test/unit/bx_test.rb: -------------------------------------------------------------------------------- 1 | # Bx Recommender Service Adaptor Test 2 | # To inject your bX token into the testing environment, use 3 | # export BX_TOKEN=yourtoken 4 | # but this isn't really necessary or useful, since this test 5 | # will just use VCR anyway 6 | require 'test_helper' 7 | class BxTest < ActiveSupport::TestCase 8 | extend TestWithCassette 9 | fixtures :requests, :referents, :referent_values 10 | 11 | def setup 12 | bx_token = ENV['BX_TOKEN'] || 'BX_TOKEN' 13 | config = { 14 | "service_id" => "Bx", 15 | "priority" => "1", 16 | "token" => bx_token 17 | } 18 | @bx_service_adaptor = Bx.new(config) 19 | end 20 | 21 | # This test has recorded a Bx request via VCR 22 | # It has recommendations 23 | # Ignore ctx_time in the URL for VCR matching purposes 24 | test_with_cassette("article that has recommendations", :bx, :match_requests_on => [:method, :uri_without_ctx_tim]) do 25 | # Get the relevant request fixture 26 | coffeemakers_request = requests(:coffeemakers) 27 | @bx_service_adaptor.handle(coffeemakers_request) 28 | 29 | # Refresh with the latest from the DB after handling the service. 30 | coffeemakers_request.dispatched_services.reset 31 | coffeemakers_request.service_responses.reset 32 | 33 | # Get the returned 'similar' service responses 34 | similars = coffeemakers_request.get_service_type('similar') 35 | 36 | # There should be 5 'similar' service responses 37 | assert_equal(5, similars.length, "Ack. Similar responses have gone awry!") 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /test/unit/dispatched_service_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class DispatchedServiceTest < ActiveSupport::TestCase 4 | 5 | def test_can_generate_service_types 6 | request = fake_umlaut_request("?title=foo") 7 | ds = DispatchedService.new(:service_id => "Ulrichs") 8 | assert_equal ServiceStore.instantiate_service!("Ulrichs", request).service_types_generated, ds.can_generate_service_types 9 | end 10 | 11 | def test_can_generate_service_types_with_bum_service 12 | ds = DispatchedService.new(:service_id => "no_such_service") 13 | assert_equal [], ds.can_generate_service_types 14 | end 15 | 16 | end -------------------------------------------------------------------------------- /test/unit/dissertation_catch_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | require 'referent' 4 | require 'dissertation_catch' 5 | 6 | 7 | class DissertationCatchTest < ActiveSupport::TestCase 8 | # this was a regression 9 | def test_blank_atitle 10 | context_object = OpenURL::ContextObject.new_from_kev("atitle=&title=Between+Us+and+Artistic+Appreciation%3a+Nabokov+and+the+Problem+of+Distortion&issn=04194209") 11 | 12 | referent = Referent.new 13 | referent.set_values_from_context_object(context_object) 14 | referent.save! 15 | referent.referent_values(true) # reload 16 | 17 | DissertationCatch.new.filter(referent) 18 | 19 | assert_equal "Between Us and Artistic Appreciation: Nabokov and the Problem of Distortion", referent.metadata['title'] 20 | end 21 | end -------------------------------------------------------------------------------- /test/unit/permalink_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | class PermalinkTest < ActiveSupport::TestCase 3 | fixtures :referents, :referent_values 4 | test "new with values" do 5 | request = fake_umlaut_request("/resolve?format=journal&issn=0362-4331&jtitle=The+New+York+times&sid=info%3Asid%2Fsfxit.com%3Acitation") 6 | referent = request.referent 7 | 8 | permalink = nil 9 | assert_difference('Permalink.count') { 10 | permalink = Permalink.new_with_values!(referent, "info:sid/sfxit.com:citation") 11 | } 12 | assert_equal(referent.id, permalink.referent_id) 13 | assert_equal("info:sid/sfxit.com:citation", permalink.orig_rfr_id) 14 | assert_not_nil(permalink.context_obj_serialized) 15 | assert_equal("The New York times", permalink.restore_context_object.referent.jtitle) 16 | end 17 | end -------------------------------------------------------------------------------- /test/unit/request_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | # Some basic tests for referent.to_citation, which returns a hash of various citation 4 | # information. 5 | class RequestTest < ActiveSupport::TestCase 6 | 7 | test "add_service_response" do 8 | request = fake_umlaut_request("/resolve?title=foo&author=bar") 9 | request.service_responses.to_a 10 | service = DummyService.new("priority" => 3, "service_id" => "DummyService") 11 | 12 | request.add_service_response( 13 | :service=>service, 14 | :url=>"http://example.com", 15 | :display_text=>"something", 16 | :service_type_value => :highlighted_link 17 | ) 18 | 19 | assert_length 1, request.service_responses 20 | 21 | response = request.service_responses.first 22 | 23 | assert_equal service.service_id, response.service_id 24 | 25 | assert_equal request.id, response.request.id 26 | assert_equal ServiceTypeValue[:highlighted_link], response.service_type_value 27 | 28 | assert_equal "http://example.com", response.view_data[:url] 29 | assert_equal "something", response.view_data[:display_text] 30 | 31 | end 32 | 33 | test "get_service_type_with_bum_response" do 34 | request = fake_umlaut_request("/resolve?title=foo&author=bar") 35 | request.add_service_response( 36 | :service => Service.new('service_id' => "NO_SUCH", "priority" => 3), 37 | :service_type_value => :highlighted_link 38 | ) 39 | request.add_service_response( 40 | :service => ServiceStore.instantiate_service!('DummyService', request), 41 | :service_type_value => :highlighted_link 42 | ) 43 | 44 | assert_length 1, request.get_service_type(:highlighted_link) 45 | end 46 | 47 | test "DC format metadata does not raise" do 48 | # Can't promise we can do much useful with it, but it shouldn't raise 49 | params = Rack::Utils.parse_nested_query 'rfr_id=info%3Asid%2Fzotero.org%3A2&rft.source=The+New+Yorker&rft.type=webpage&rft.description=How+Xi+Jinping+took+control+of+China.&rft.identifier=http%3A%2F%2Fwww.newyorker.com%2Fmagazine%2F2015%2F04%2F06%2Fborn-red&ctx_ver=Z39.88-2004&url_ver=Z39.88-2004&rft.title=Rise+of+the+Red+Prince&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Adc¨aut.force_new_request=true' 50 | co = OpenURL::ContextObject.new_from_form_vars( params ) 51 | rft = Referent.create_by_context_object(co) 52 | end 53 | 54 | 55 | 56 | end -------------------------------------------------------------------------------- /test/unit/service_store_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | class ServiceStoreTest < ActiveSupport::TestCase 3 | setup :reset_service_store 4 | teardown :reset_service_store, :force_lazy_load_service_store 5 | 6 | 7 | test "missing umlaut services yaml" do 8 | FileUtils.mv(File.join(Rails.root, "config", "umlaut_services.yml"), File.join(Rails.root, "config", "umlaut_services.yml.moved")) 9 | assert_nothing_raised{ ServiceStore.config } 10 | FileUtils.mv(File.join(Rails.root, "config", "umlaut_services.yml.moved"), File.join(Rails.root, "config", "umlaut_services.yml")) 11 | end 12 | 13 | test "group added to service" do 14 | sfx_definition = ServiceStore.service_definition_for("SFX") 15 | assert_equal("default", sfx_definition["group"]) 16 | assert_equal("default", ServiceStore.instantiate_service!(sfx_definition, nil).group) 17 | end 18 | 19 | test "manually set services" do 20 | # force original from disk to load 21 | ServiceStore.config 22 | ServiceStore.service_definitions 23 | 24 | # But then set our own instead 25 | ServiceStore.config = { 26 | "default" => { 27 | "services" => { 28 | "dummy" => {"type" => "DummyService", "priority" => 3} 29 | } 30 | } 31 | } 32 | 33 | assert_length 1, ServiceStore.service_definitions 34 | assert_present ServiceStore.service_definition_for("dummy") 35 | end 36 | 37 | test "ERB in umlaut_services.yml" do 38 | dummy_service_config = ServiceStore.service_definition_for("DummyService") 39 | assert_equal "this value comes from ERB: test", dummy_service_config["value_from_erb"] 40 | end 41 | 42 | 43 | def reset_service_store 44 | ServiceStore.reset! 45 | end 46 | def force_lazy_load_service_store 47 | ServiceStore.config 48 | ServiceStore.service_definitions 49 | end 50 | 51 | 52 | end -------------------------------------------------------------------------------- /test/unit/service_test.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../test_helper' 2 | 3 | class ServiceTest < ActiveSupport::TestCase 4 | # A service that does nothing! 5 | class DummyService < Service 6 | def handle(request) 7 | return request.dispatched(self, true) 8 | end 9 | end 10 | 11 | # A preempted by service that does nothing! 12 | class PreemptedByDummyService < Service 13 | def initialize(config) 14 | @preempted_by = ["existing_service" => "MyDummyService"] 15 | super(config) 16 | end 17 | 18 | def handle(request) 19 | return request.dispatched(self, true) 20 | end 21 | end 22 | 23 | def setup 24 | I18n.reload! 25 | @dummy_config = {"priority" => 1, "service_id" => "MyDummyService", "type" => "DummyService"} 26 | @umlaut_request = fake_umlaut_request("/resolve?genre=journal&issn=0098-7484") 27 | end 28 | 29 | test "preempted by wildcard other type" do 30 | DummyService.new(@dummy_config).handle(@umlaut_request) 31 | assert(PreemptedByDummyService.new(@dummy_config).preempted_by(@umlaut_request)) 32 | end 33 | 34 | test "Service#translate" do 35 | I18n.with_locale(:en) do 36 | I18n.backend.store_translations("en", 37 | {"umlaut" => 38 | {"services" => 39 | { "service_test/dummy_service" => 40 | {"class_key" => "class_key_value"}, 41 | "my_dummy_service" => 42 | {"service_id_key" => "service_id_key_value"} 43 | } 44 | } 45 | } 46 | ) 47 | # Just make sure we set our test i18n translations right 48 | assert_equal "class_key_value", I18n.t("umlaut.services.service_test/dummy_service.class_key") 49 | assert_equal "service_id_key_value", I18n.t("umlaut.services.my_dummy_service.service_id_key") 50 | 51 | # Now actually test translate 52 | service = DummyService.new(@dummy_config) 53 | 54 | assert_equal "service_id_key_value", service.translate("service_id_key") 55 | assert_equal "class_key_value", service.translate("class_key") 56 | 57 | assert_equal "default_value", service.translate("missing_key", :default => "default_value") 58 | end 59 | end 60 | 61 | test "#display_name" do 62 | 63 | end 64 | end -------------------------------------------------------------------------------- /test/unit/service_type_value_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'tempfile' 3 | 4 | # Only tests a few things at present 5 | class ServiceTypeValueTest < ActiveSupport::TestCase 6 | 7 | def test_display_name 8 | I18n.locale = :en 9 | 10 | st = ServiceTypeValue.find("fulltext") 11 | 12 | assert_equal I18n.t("umlaut.service_type_names.fulltext", :count => 1), st.display_name 13 | assert_equal I18n.t("umlaut.service_type_names.fulltext", :count => 10), st.display_name_pluralize 14 | 15 | end 16 | 17 | def test_display_name_uses_default_when_missing 18 | I18n.locale = :en 19 | 20 | st = ServiceTypeValue.new(:id => 100000, :name => "made_up_thing") 21 | 22 | assert_equal I18n.t("umlaut.service_type_names.default", :count => 1), st.display_name 23 | assert_equal I18n.t("umlaut.service_type_names.default", :count => 10), st.display_name_pluralize 24 | end 25 | 26 | def test_can_load_custom_yaml 27 | file = Tempfile.new('custom_yaml') 28 | file.write <<-EOF 29 | my_custom_thing: 30 | display_name: Custom Thing 31 | EOF 32 | file.close 33 | 34 | ServiceTypeValue.merge_yml_file!(file.path) 35 | 36 | assert_present ServiceTypeValue.find("my_custom_thing") 37 | 38 | file.unlink 39 | end 40 | 41 | end 42 | -------------------------------------------------------------------------------- /test/unit/sfx_test.rb: -------------------------------------------------------------------------------- 1 | # tests for SFX service 2 | # Only the the simplest of tests are done so far. To do more advanced tests, 3 | # request fixtures for more cases should be created. 4 | require 'test_helper' 5 | class SfxTest < ActiveSupport::TestCase 6 | extend TestWithCassette 7 | 8 | def setup 9 | @sfx_default = ServiceStore.instantiate_service!("SFX", nil) 10 | end 11 | 12 | def test_initialize_minimum 13 | sfx = Sfx.new({"priority"=>1, "base_url" => "http://sfx.library.example.edu/local"}) 14 | assert_equal(1, sfx.priority) 15 | end 16 | 17 | # Use VCR to provide a deterministic SFX search. 18 | # Ignore ctx_time in the URL for VCR matching purposes 19 | # TODO: Check more of the response 20 | test_with_cassette("nytimes by issn", :sfx, :match_requests_on => [:method, :uri_without_ctx_tim]) do 21 | #nytimes_request = requests(:nytimes) 22 | 23 | nytimes_request = fake_umlaut_request("/resolve?format=journal&genre=journal&jtitle=The+New+York+times&issn=0362-4331") 24 | 25 | # Clear request 26 | nytimes_request.service_responses.each do |service_response| 27 | service_response.destroy 28 | end 29 | nytimes_request.dispatched_services.reload 30 | nytimes_request.service_responses.reload 31 | assert_equal 0, nytimes_request.service_responses.count 32 | response = @sfx_default.do_request(@sfx_default.initialize_client(nytimes_request)) 33 | @sfx_default.parse_response(response, nytimes_request) 34 | nytimes_request.dispatched_services.reload 35 | nytimes_request.service_responses.reload 36 | assert_equal 10, nytimes_request.service_responses.size 37 | first_service_response = nytimes_request.service_responses.first 38 | assert_not_nil(first_service_response.view_data[:proxy], "Proxy is nil") 39 | assert(first_service_response.view_data[:proxy], "Proxy is false") 40 | end 41 | end -------------------------------------------------------------------------------- /test/unit/worldcat_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | class WorldCatTest < ActiveSupport::TestCase 3 | def setup 4 | end 5 | end -------------------------------------------------------------------------------- /test/vcr_cassettes/amazon/product_advertising_api_forbidden.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: http://webservices.amazon.com/onca/xml?AWSAccessKeyId=DUMMY_API_KEY&AssociateTag=DUMMY_ASSOCIATE_TAG&ItemId=0759113475&Operation=ItemLookup&ResponseGroup=Large&Service=AWSECommerceService&Signature=tOjTljzXThFLdtw58/XAX0D3PAvyqN3Ui3/7C2FVnYQ=&Timestamp=2014-09-30T14:16:51-04:00 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Accept: 11 | - ! '*/*' 12 | User-Agent: 13 | - Ruby 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | Date: 20 | - Tue, 30 Sep 2014 18:16:51 GMT 21 | Server: 22 | - Apache-Coyote/1.1 23 | Content-Type: 24 | - text/xml;charset=UTF-8 25 | Vary: 26 | - Accept-Encoding,User-Agent 27 | Cneonction: 28 | - close 29 | Transfer-Encoding: 30 | - chunked 31 | body: 32 | encoding: US-ASCII 33 | string:
7ed35c2f-5b52-4fe6-963c-680587702a350.0296040000000000
TrueASIN0759113475LargeAllAWS.ECommerceService.ItemNotAccessibleThis 41 | item is not accessible through the Product Advertising API.
42 | http_version: 43 | recorded_at: Tue, 30 Sep 2014 18:16:51 GMT 44 | recorded_with: VCR 2.9.3 45 | -------------------------------------------------------------------------------- /test/vcr_cassettes/scopus/live_test_with_no_hits.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: http://api.elsevier.com/content/search/index:SCOPUS?apiKey=DUMMY_API_KEY&query=VOLUME(%224900%22)%20AND%20ISSUE(%22700%22)%20AND%20PAGEFIRST(%2293900%22)%20%20AND%20(ISSN(%2200123706%22)%20OR%20EISSN(%2200123706%22)) 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Accept: 11 | - application/xml 12 | User-Agent: 13 | - Ruby 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | Server: 20 | - api.elsevier.com 9999 21 | X-Els-Status: 22 | - NO_SEARCH_RESULTS(Result set was empty) 23 | X-Els-Reqid: 24 | - 000001458afaadb1-220e6ff 25 | X-Els-Apikey: 26 | - DUMMY_API_KEY 27 | X-Els-Transid: 28 | - 495f47aa-0aef-4cd2-889d-18e8c466779d 29 | Allow: 30 | - GET 31 | Content-Type: 32 | - application/atom+xml;charset=UTF-8 33 | Content-Length: 34 | - '1001' 35 | Date: 36 | - Thu, 08 May 2014 17:49:28 GMT 37 | X-Re-Ref: 38 | - 1 440851037 39 | P3p: 40 | - CP="IDC DSP LAW ADM DEV TAI PSA PSD IVA IVD CON HIS TEL OUR DEL SAM OTR IND 41 | OTC" 42 | body: 43 | encoding: US-ASCII 44 | string: 000Result set was empty 50 | http_version: 51 | recorded_at: Thu, 08 May 2014 17:49:29 GMT 52 | recorded_with: VCR 2.5.0 53 | -------------------------------------------------------------------------------- /test/vcr_cassettes/scopus/live_trigger_scopus_error.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: http://api.elsevier.com/content/search/index:SCOPUS?apiKey=DUMMY_API_KEY&query=DOI( 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Accept: 11 | - application/xml 12 | User-Agent: 13 | - Ruby 14 | response: 15 | status: 16 | code: 400 17 | message: Bad Request 18 | headers: 19 | Server: 20 | - api.elsevier.com 9999 21 | X-Els-Status: 22 | - INVALID_INPUT(Unable to translate query provided) 23 | X-Els-Reqid: 24 | - 000001458ad94e47-221faa3 25 | X-Els-Apikey: 26 | - DUMMY_API_KEY 27 | X-Els-Transid: 28 | - 31f73f96-ec26-4ccf-8404-0fa010688248 29 | Allow: 30 | - GET 31 | Content-Type: 32 | - text/xml;charset=UTF-8 33 | Content-Length: 34 | - '156' 35 | Date: 36 | - Thu, 08 May 2014 17:49:31 GMT 37 | X-Cnection: 38 | - close 39 | X-Re-Ref: 40 | - 1 444135611 41 | P3p: 42 | - CP="IDC DSP LAW ADM DEV TAI PSA PSD IVA IVD CON HIS TEL OUR DEL SAM OTR IND 43 | OTC" 44 | body: 45 | encoding: US-ASCII 46 | string: ! "\n\t\n\t\tINVALID_INPUT\n\t\tUnable 47 | to translate query provided\n\t\n" 48 | http_version: 49 | recorded_at: Thu, 08 May 2014 17:49:32 GMT 50 | recorded_with: VCR 2.5.0 51 | -------------------------------------------------------------------------------- /test/view/holding_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | # The default partial for testing holding section. 4 | # 5 | # NOT fully tested, this is just a start. 6 | # 7 | # Mocking up things to test a real request is worse than it should be 8 | # in Umlaut, 10 year old code some places, sorry! 9 | class HoldingTest < ActionView::TestCase 10 | def setup 11 | # Not sure why ActionView::TestCase isn't initializing a rails request for us 12 | request ||= ActionController::TestRequest.new 13 | @user_request = Request.find_or_create({}, session, request) 14 | end 15 | 16 | def test_includes_holding_search 17 | holding_responses = [ 18 | ServiceResponse.create_from_hash(:count => "10", 19 | :display_text => "10 possible matches in catalog", 20 | :url => "http://catalog.example.org", 21 | :service_type_value => "holding_search", 22 | :service_id => "DummyService") 23 | ] 24 | 25 | render "resolve/holding", :holding => holding_responses 26 | 27 | assert_select ".umlaut-holding-search" 28 | assert_select ".umlaut-unavailable", 0 29 | end 30 | end --------------------------------------------------------------------------------