<%= show_pubtype(record) %>
diff --git a/.bundle/config b/.bundle/config index b81abe028..7544be9dd 100644 --- a/.bundle/config +++ b/.bundle/config @@ -1,3 +1,2 @@ --- -BUNDLE_PATH: vendor -BUNDLE_DISABLE_SHARED_GEMS: '1' +BUNDLE_FROZEN: '1' diff --git a/.gitignore b/.gitignore index f7c9fa9ea..1b6e09ab2 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ /tmp APIauthentication.txt /vendor +token.txt diff --git a/Gemfile b/Gemfile index bb631c96e..62c2056e8 100755 --- a/Gemfile +++ b/Gemfile @@ -48,3 +48,5 @@ gem "ebsco-discovery-service-api", "1.0.4" gem "addressable", "2.3.2" gem "htmlentities" gem "activerecord-session_store" + +gem "factory_girl", "~> 4.0", group: [:test, :development] diff --git a/Gemfile.lock b/Gemfile.lock index e5241f5f6..938a4196a 100755 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -71,6 +71,8 @@ GEM ensure_valid_encoding (0.5.3) erubis (2.7.0) execjs (2.2.1) + factory_girl (4.4.0) + activesupport (>= 3.0.0) ffi (1.9.3) hike (1.2.3) htmlentities (4.3.2) @@ -184,6 +186,7 @@ DEPENDENCIES devise devise-guests (~> 0.3) ebsco-discovery-service-api (= 1.0.4) + factory_girl (~> 4.0) htmlentities jbuilder (~> 2.0) jettywrapper (~> 1.7) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 3bb0f8664..b24368f26 100755 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -9,4 +9,30 @@ class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception + + def after_sign_in_path_for(resource) + if session[:session_key].present? + connection = EDSApi::ConnectionHandler.new(2) + connection.end_session(session[:session_key]) + session[:session_key] = nil + end + if session[:results].present? + session[:results] = nil + end + if session[:current_url].present? + session[:current_url] + end + end + + def after_sign_out_path_for(resource) + if session[:session_key].present? + connection = EDSApi::ConnectionHandler.new(2) + connection.end_session(session[:session_key]) + session[:session_key] = nil + end + if session[:results].present? + session[:results] = nil + end + root_path + end end diff --git a/app/controllers/articles_controller.rb b/app/controllers/articles_controller.rb new file mode 100755 index 000000000..ce96ac52c --- /dev/null +++ b/app/controllers/articles_controller.rb @@ -0,0 +1,9 @@ +# -*- encoding : utf-8 -*- +require 'blacklight/catalog' + +class ArticlesController < ApplicationController + #before_filter :authenticate_user! + + include Blacklight::Catalog + +end diff --git a/app/helpers/articles_helper.rb b/app/helpers/articles_helper.rb new file mode 100755 index 000000000..8542a3703 --- /dev/null +++ b/app/helpers/articles_helper.rb @@ -0,0 +1,3 @@ +module ArticlesHelper + include Blacklight::ArticlesHelperBehavior +end diff --git a/app/helpers/blacklight/articles_helper_behavior.rb b/app/helpers/blacklight/articles_helper_behavior.rb new file mode 100755 index 000000000..62c471057 --- /dev/null +++ b/app/helpers/blacklight/articles_helper_behavior.rb @@ -0,0 +1,1465 @@ +# -*- encoding : utf-8 -*- + +## +# Copyright 2013 EBSCO Information Services +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module Blacklight::ArticlesHelperBehavior + + ############ + # Utility functions and libraries + ############ + + require 'fileutils' # for interacting with files + require "addressable/uri" # for manipulating URLs in the interface + require "htmlentities" # for deconding/encoding URLs + require "cgi" + require 'open-uri' + + def html_unescape(text) + return CGI.unescape(text) + end + + def deep_clean(parameters) + tempArray = Array.new; + parameters.each do |k,v| + unless v.nil? + if v.is_a?(Array) + deeperClean = deep_clean(v) + parameters[k] = deeperClean + else + parameters[k] = h(v) + end + else + clean_key = h(k) + tempArray.push(clean_key) + end + end + unless tempArray.empty? + parameters = tempArray + end + return parameters + end + + # cleans response from EBSCO API + def processAPItags(apiString) + processed = HTMLEntities.new.decode apiString + return processed.html_safe + end + + ########### + # API Interaction + ########### + + # called at the beginning of every page load + def eds_connect + + # use session[:debugNotes] to store any debug text + # this will display if you render the _debug partial + session[:debugNotes] = "" + + # creates EDS API connection object, initializing it with application login credentials + @connection = EDSApi::ConnectionHandler.new(2) + File.open(auth_file_location,"r") {|f| + @api_userid = f.readline.strip + @api_password = f.readline.strip + @api_profile = f.readline.strip + } + if authenticated_user? + session[:debugNotes] << "
Sending NO as guest.
" + @connection.uid_init(@api_userid, @api_password, @api_profile, 'n') + else + session[:debugNotes] << "Sending YES as guest.
" + @connection.uid_init(@api_userid, @api_password, @api_profile, 'y') + end + + # generate a new authentication token if their isn't one or the one stored on server is invalid or expired + if has_valid_auth_token? + @auth_token = getAuthToken + else + @connection.uid_authenticate(:json) + @auth_token = @connection.show_auth_token + writeAuthToken(@auth_token) + end + + # generate a new session key if there isn't one, or if the existing one is invalid + if session[:session_key].present? + if session[:session_key].include?('Error') + @session_key = @connection.create_session(@auth_token) + session[:session_key] = @session_key + get_info + else + @session_key = session[:session_key] + end + else + @session_key = @connection.create_session(@auth_token, 'n') + session[:session_key] = @session_key + get_info + end + + # at this point, we should have a valid authentication and session token + + end + + # after basic functions like SEARCH, INFO, and RETRIEVE, check to make sure a new session token wasn't generated + # if it was, use the new session key from here on out + def checkSessionCurrency + currentSessionKey = @connection.show_session_token + if currentSessionKey != get_session_key + session[:session_key] = currentSessionKey + @session_key = currentSessionKey + get_info + end + end + + # generates parameters for the API call given URL parameters + # options is usually the params hash + # this function strips out unneeded parameters and reformats them to form a string that the API accepts as input + def generate_api_query(options) + + #removing Rails and Blacklight parameters + options.delete("action") + options.delete("controller") + options.delete("utf8") + + #translate Blacklight search_field into query index + if options["search_field"].present? + if options["search_field"] == "author" + fieldcode = "AU:" + elsif options["search_field"] == "subject" + fieldcode = "SU:" + elsif options["search_field"] == "title" + fieldcode = "TI:" + else + fieldcode = "" + end + else + fieldcode = "" + end + + #should write something to allow this to be overridden + searchmode = "AND" + + #build 'query-1' API URL parameter + searchquery_extras = searchmode + "," + fieldcode + + #filter to make sure the only parameters put into the API query are those that are expected by the API + edsKeys = ["eds_action","q","query-1","facetfilter[]","facetfilter","sort","includefacets","searchmode","view","resultsperpage","sort","pagenumber","highlight", "limiter", "limiter[]"] + edsSubset = {} + options.each do |key,value| + if edsKeys.include?(key) + edsSubset[key] = value + end + end + + #rename parameters to expected names + #action and query-1 were renamed due to Rails and Blacklight conventions respectively + mappings = {"eds_action" => "action", "q" => "query-1"} + newoptions = Hash[edsSubset.map {|k, v| [mappings[k] || k, v] }] + + #repace the raw query, adding searchmode and fieldcode + changeQuery = newoptions["query-1"] + uri = Addressable::URI.new + uri.query_values = newoptions + searchquery = uri.query + searchtermindex = searchquery.index('query-1=') + 8 + searchquery.insert searchtermindex, searchquery_extras + + # , : ( ) - unencoding expected punctuation + searchquery = searchquery.gsub('%28','(').gsub('%3A',':').gsub('%29',')').gsub('%23',',') + return searchquery + end + + # main search function. accepts string to be tacked on to API endpoint URL + def search(apiquery) + results = @connection.search(apiquery, @session_key, @auth_token, :json).to_hash + + #update session_key if new one was generated in the call + checkSessionCurrency + + #results are stored in the session to facilitate faster navigation between detailed records and results list without needed a new API call + session[:results] = results + session[:apiquery] = apiquery + end + + def retrieve(dbid, an, highlight = "") + debugNotes << "HIGHLIGHTBEFORE:" << highlight.to_s + highlight.downcase! + highlight.gsub! ',and,',',' + highlight.gsub! ',or,',',' + highlight.gsub! ',not,',',' + debugNotes << "HIGHLIGHTAFTER: " << highlight.to_s + record = @connection.retrieve(dbid, an, highlight, @session_key, @auth_token, :json).to_hash + debugNotes << "RECORD: " << record.to_s + #update session_key if new one was generated in the call + checkSessionCurrency + + return record + end + + def termsToHighlight(terms = "") + if terms.present? + words = terms.split(/\W+/) + return words.join(",").to_s + else + return "" + end + end + + # helper function for iterating through results from + def switch_link(params,qurl) + + # check to see if the user is navigating to a record that was not included in the current page of results + # if so, run a new search API call, getting the appropriate page of results + if params[:resultId].to_i > (params[:pagenumber].to_i * params[:resultsperpage].to_i) + nextPage = params[:pagenumber].to_i + 1 + newParams = params + newParams[:eds_action] = "GoToPage(" + nextPage.to_s + ")" + options = generate_api_query(newParams) + search(options) + elsif params[:resultId].to_i < (((params[:pagenumber].to_i - 1) * params[:resultsperpage].to_i) + 1) + nextPage = params[:pagenumber].to_i - 1 + newParams = params + newParams[:eds_action] = "GoToPage(" + nextPage.to_s + ")" + options = generate_api_query(newParams) + search(options) + end + + link = "" + # generate the link for the target record + if session[:results]['SearchResult']['Data']['Records'].present? + session[:results]['SearchResult']['Data']['Records'].each do |result| + nextId = show_resultid(result).to_s + if nextId == params[:resultId].to_s + nextAn = show_an(result).to_s + nextDbId = show_dbid(result).to_s + nextrId = params[:resultId].to_s + nextHighlight = params[:q].to_s + link = request.fullpath.split("/switch")[0].to_s + "/" + nextDbId.to_s + "/" + nextAn.to_s + "/?resultId=" + nextrId.to_s + "&highlight=" + nextHighlight.to_s + end + end + end + return link.to_s + + end + + # calls the INFO API method + # counts how many limiters are available + def get_info + + numLimiters = 0 + session[:info] = @connection.info(@session_key, @auth_token, :json).to_hash + checkSessionCurrency + + if session[:info].present? + if session[:info]['AvailableSearchCriteria'].present? + if session[:info]['AvailableSearchCriteria']['AvailableLimiters'].present? + session[:info]['AvailableSearchCriteria']['AvailableLimiters'].each do |limiter| + if limiter["Type"] == "select" + numLimiters += 1 + end + end + end + end + end + + session[:numLimiters] = numLimiters + end + + ############ + # File / Token Handling / End User Auth + ############ + + def authenticated_user? + if user_signed_in? + return true + end + # need to define function for detecting on-campus IP ranges + return false + end + + def clear_session_key + session.delete(:session_key) + end + + def get_session_key + if session[:session_key].present? + return session[:session_key].to_s + else + return "no session key" + end + end + + def token_file_exists? + if File.exists?(token_file_location) + return true + end + return Rails.root.to_s + "/token.txt" + end + + def auth_file_exists? + if File.exists?(auth_file_location) + return true + end + return Rails.root.to_s + "/APIauthentication.txt" + end + + def token_file_location + if request.domain.to_s.include?("localhost") + return "token.txt" + end + return Rails.root.to_s + "/token.txt" + end + + def auth_file_location + if request.domain.to_s.include?("localhost") + return "APIauthentication.txt" + end + return Rails.root.to_s + "/APIauthentication.txt" + end + + # returns true if the authtoken stored in token.txt is valid + # false if otherwise + def has_valid_auth_token? + token = timeout = timestamp = '' + if token_file_exists? + File.open(token_file_location,"r") {|f| + if f.readlines.size <= 2 + return false + end + } + File.open(token_file_location,"r") {|f| + token = f.readline.strip + timeout = f.readline.strip + timestamp = f.readline.strip + } + else + return false + end + if Time.now.getutc.to_i < timestamp.to_i + return true + else + session[:debugNotes] << "Looks like the auth token is out of date.. It expired at " << Time.at(timestamp.to_i).to_s << "
" + return false + end + end + + def get_token_timeout + timestamp = ''; + File.open(token_file_location,"r") {|f| + token = f.readline.strip + timeout = f.readline.strip + timestamp = f.readline.strip + } + return Time.at(timestamp.to_i) + end + + # writes an authentication token to token.txt. + # will create token.txt if it does not exist + def writeAuthToken(auth_token) + timeout = "1800" + timestamp = Time.now.getutc.to_i + timeout.to_i + timestamp = timestamp.to_s + auth_token = auth_token.to_s + + if File.exists?(token_file_location) + File.open(token_file_location,"w") {|f| + f.write(auth_token) + f.write("\n" + timeout) + f.write("\n" + timestamp) + } + else + File.new(token_file_location,"w") {|f| + f.write(auth_token) + f.write("\n" + timeout) + f.write("\n" + timestamp) + } + File.chmod(664,token_file_location) + File.open(token_file_location,"w") {|f| + f.write(auth_token) + f.write("\n" + timeout) + f.write("\n" + timestamp) + } + end + end + + def getAuthToken + token = '' + timeout = '' + timestamp = '' + if has_valid_auth_token? + File.open(token_file_location,"r") {|f| + token = f.readline.strip + } + return token + end + return token + end + + + ############ + # Linking Utilities + ############ + + # pullsAuth_Token: <%= getAuthToken %> <% if has_valid_auth_token? %> @@ -9,4 +10,6 @@
Session_Token: <%= session[:session_key] %>
Notes: <%= debugNotes.html_safe %>
+<%= session.to_s %>
You may close this window to return to your search results.
- <% retrieve(params[:dbid].to_s, params[:an].to_s) %> - <% if has_pdf?(session[:record]['Record']) %> - - - - <% else %> - - <% end %> -<% else %> +<% session[:current_url] = request.original_url %> + +<% eds_connect %> <% if has_search_parameters? %> - <% options = generate_api_query(params) %> - <% search(options) %> + <% options = generate_api_query(params) %> + <% clean_params = deep_clean(params) %> + <% params = clean_params %> + + <% unless params[:fromDetail] == 'y' and session[:results] %> + <% search(options) %> + <% end %> <% end %>