diff --git a/.gitignore b/.gitignore index 57b25409..efb3f5c8 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,5 @@ local_dev_env .idea public/assets + +.byebug_history diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 00000000..45674f16 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.3.3 \ No newline at end of file diff --git a/Gemfile b/Gemfile index 84a76875..3100f220 100644 --- a/Gemfile +++ b/Gemfile @@ -115,3 +115,5 @@ gem 'blacklight-ris', :git => 'https://github.com/upenn-libraries/blacklight-ris gem 'oga' gem 'httparty' + +gem 'honeybadger' diff --git a/Gemfile.lock b/Gemfile.lock index af70dab8..0ced0530 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -175,6 +175,7 @@ GEM globalid (0.3.7) activesupport (>= 4.1.0) hashie (3.6.0) + honeybadger (4.7.2) htmlentities (4.3.4) httparty (0.16.4) mime-types (~> 3.0) @@ -390,6 +391,7 @@ DEPENDENCIES dotenv-rails font-awesome-rails globalid (= 0.3.7) + honeybadger httparty jbuilder (= 2.6.0) jdbc-sqlite3 (= 3.8.11.2) diff --git a/README.md b/README.md index e760d1b3..8ebae366 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,10 @@ Installation: - Checkout this repo. -- Make sure you have ruby 2.3.1 installed. It's recommended that you +- Make sure you have ruby 2.3.3 installed. It's recommended that you use [rbenv](https://github.com/rbenv/rbenv), but it may be quicker/easier to get running with [rvm](https://rvm.io/). + - You may have issues installing Ruby 2.3.3 in recent Linux distributions due to an OpenSSL version incompatibility. See [this guide](https://www.garron.me/en/linux/install-ruby-2-3-3-ubuntu.html) for help. - Run `bundle install` to install all gem dependencies. diff --git a/app/assets/images/amanpreet_kaur.jpg b/app/assets/images/amanpreet_kaur.jpg deleted file mode 100644 index 161f5452..00000000 Binary files a/app/assets/images/amanpreet_kaur.jpg and /dev/null differ diff --git a/app/assets/images/anne_larrivee.jpg b/app/assets/images/anne_larrivee.jpg deleted file mode 100644 index 76c16d37..00000000 Binary files a/app/assets/images/anne_larrivee.jpg and /dev/null differ diff --git a/app/assets/images/arthur_kiron.jpg b/app/assets/images/arthur_kiron.jpg deleted file mode 100644 index 5735cbfe..00000000 Binary files a/app/assets/images/arthur_kiron.jpg and /dev/null differ diff --git a/app/assets/images/brian_vivier.jpg b/app/assets/images/brian_vivier.jpg deleted file mode 100644 index 60ff74dd..00000000 Binary files a/app/assets/images/brian_vivier.jpg and /dev/null differ diff --git a/app/assets/images/bruce_nielsen.jpg b/app/assets/images/bruce_nielsen.jpg deleted file mode 100644 index 0fb6397f..00000000 Binary files a/app/assets/images/bruce_nielsen.jpg and /dev/null differ diff --git a/app/assets/images/carlos_rodriguez.jpg b/app/assets/images/carlos_rodriguez.jpg deleted file mode 100644 index 732aa8ba..00000000 Binary files a/app/assets/images/carlos_rodriguez.jpg and /dev/null differ diff --git a/app/assets/images/cathy_ogur.jpg b/app/assets/images/cathy_ogur.jpg deleted file mode 100644 index b9ef677c..00000000 Binary files a/app/assets/images/cathy_ogur.jpg and /dev/null differ diff --git a/app/assets/images/charles_cobine.jpg b/app/assets/images/charles_cobine.jpg deleted file mode 100644 index 2626ac6c..00000000 Binary files a/app/assets/images/charles_cobine.jpg and /dev/null differ diff --git a/app/assets/images/cynthia_cronin_kardon.jpg b/app/assets/images/cynthia_cronin_kardon.jpg deleted file mode 100644 index 9c53de6c..00000000 Binary files a/app/assets/images/cynthia_cronin_kardon.jpg and /dev/null differ diff --git a/app/assets/images/david_azzolina.jpg b/app/assets/images/david_azzolina.jpg deleted file mode 100644 index 3589074f..00000000 Binary files a/app/assets/images/david_azzolina.jpg and /dev/null differ diff --git a/app/assets/images/david_mcknight.jpg b/app/assets/images/david_mcknight.jpg deleted file mode 100644 index 7b67d70b..00000000 Binary files a/app/assets/images/david_mcknight.jpg and /dev/null differ diff --git a/app/assets/images/deborah_stewart.jpg b/app/assets/images/deborah_stewart.jpg deleted file mode 100644 index 22bc7e28..00000000 Binary files a/app/assets/images/deborah_stewart.jpg and /dev/null differ diff --git a/app/assets/images/dee_crandall.jpg b/app/assets/images/dee_crandall.jpg deleted file mode 100644 index e75671a1..00000000 Binary files a/app/assets/images/dee_crandall.jpg and /dev/null differ diff --git a/app/assets/images/dot_porter.jpg b/app/assets/images/dot_porter.jpg deleted file mode 100644 index 4c750a25..00000000 Binary files a/app/assets/images/dot_porter.jpg and /dev/null differ diff --git a/app/assets/images/douglas_mcgee.jpg b/app/assets/images/douglas_mcgee.jpg deleted file mode 100644 index 06ff7dc9..00000000 Binary files a/app/assets/images/douglas_mcgee.jpg and /dev/null differ diff --git a/app/assets/images/feedback.png b/app/assets/images/feedback.png deleted file mode 100644 index 3c994a15..00000000 Binary files a/app/assets/images/feedback.png and /dev/null differ diff --git a/app/assets/images/frank_campbell.jpg b/app/assets/images/frank_campbell.jpg deleted file mode 100644 index f9fa96c8..00000000 Binary files a/app/assets/images/frank_campbell.jpg and /dev/null differ diff --git a/app/assets/images/girmaye_misgna.jpg b/app/assets/images/girmaye_misgna.jpg deleted file mode 100644 index 6033de5c..00000000 Binary files a/app/assets/images/girmaye_misgna.jpg and /dev/null differ diff --git a/app/assets/images/gwen_collaco.jpg b/app/assets/images/gwen_collaco.jpg deleted file mode 100644 index f7deee45..00000000 Binary files a/app/assets/images/gwen_collaco.jpg and /dev/null differ diff --git a/app/assets/images/hannah_bennett.jpg b/app/assets/images/hannah_bennett.jpg deleted file mode 100644 index c8e7df89..00000000 Binary files a/app/assets/images/hannah_bennett.jpg and /dev/null differ diff --git a/app/assets/images/jef_pierce.jpg b/app/assets/images/jef_pierce.jpg deleted file mode 100644 index 2382ebbc..00000000 Binary files a/app/assets/images/jef_pierce.jpg and /dev/null differ diff --git a/app/assets/images/joseph_holub.jpg b/app/assets/images/joseph_holub.jpg deleted file mode 100644 index b7e35f39..00000000 Binary files a/app/assets/images/joseph_holub.jpg and /dev/null differ diff --git a/app/assets/images/judith_currano.jpg b/app/assets/images/judith_currano.jpg deleted file mode 100644 index 4aef33e3..00000000 Binary files a/app/assets/images/judith_currano.jpg and /dev/null differ diff --git a/app/assets/images/kenny_whitebloom.jpg b/app/assets/images/kenny_whitebloom.jpg deleted file mode 100644 index 952d36aa..00000000 Binary files a/app/assets/images/kenny_whitebloom.jpg and /dev/null differ diff --git a/app/assets/images/laurel_graham.jpg b/app/assets/images/laurel_graham.jpg deleted file mode 100644 index 6ed556d4..00000000 Binary files a/app/assets/images/laurel_graham.jpg and /dev/null differ diff --git a/app/assets/images/lauren_gala.jpg b/app/assets/images/lauren_gala.jpg deleted file mode 100644 index d818dba3..00000000 Binary files a/app/assets/images/lauren_gala.jpg and /dev/null differ diff --git a/app/assets/images/lauris_olson.jpg b/app/assets/images/lauris_olson.jpg deleted file mode 100644 index 4791cc19..00000000 Binary files a/app/assets/images/lauris_olson.jpg and /dev/null differ diff --git a/app/assets/images/liza_vick.jpg b/app/assets/images/liza_vick.jpg deleted file mode 100644 index 7d3f5ab1..00000000 Binary files a/app/assets/images/liza_vick.jpg and /dev/null differ diff --git a/app/assets/images/lynn_ransom.jpg b/app/assets/images/lynn_ransom.jpg deleted file mode 100644 index bdac0132..00000000 Binary files a/app/assets/images/lynn_ransom.jpg and /dev/null differ diff --git a/app/assets/images/lynne_farrington.jpg b/app/assets/images/lynne_farrington.jpg deleted file mode 100644 index 229d7f3d..00000000 Binary files a/app/assets/images/lynne_farrington.jpg and /dev/null differ diff --git a/app/assets/images/manuel_de_la_cruz_gutierrez.jpg b/app/assets/images/manuel_de_la_cruz_gutierrez.jpg deleted file mode 100644 index 03c5b066..00000000 Binary files a/app/assets/images/manuel_de_la_cruz_gutierrez.jpg and /dev/null differ diff --git a/app/assets/images/marcella_barnhart.jpg b/app/assets/images/marcella_barnhart.jpg deleted file mode 100644 index d77aeacc..00000000 Binary files a/app/assets/images/marcella_barnhart.jpg and /dev/null differ diff --git a/app/assets/images/margaret_janz.jpg b/app/assets/images/margaret_janz.jpg deleted file mode 100644 index a75c5246..00000000 Binary files a/app/assets/images/margaret_janz.jpg and /dev/null differ diff --git a/app/assets/images/margy_lindem.jpg b/app/assets/images/margy_lindem.jpg deleted file mode 100644 index cb46bdbd..00000000 Binary files a/app/assets/images/margy_lindem.jpg and /dev/null differ diff --git a/app/assets/images/maylene_qiu.jpg b/app/assets/images/maylene_qiu.jpg deleted file mode 100644 index 1621cea7..00000000 Binary files a/app/assets/images/maylene_qiu.jpg and /dev/null differ diff --git a/app/assets/images/melanie_cedrone.jpg b/app/assets/images/melanie_cedrone.jpg deleted file mode 100644 index 06602686..00000000 Binary files a/app/assets/images/melanie_cedrone.jpg and /dev/null differ diff --git a/app/assets/images/mia_wells.jpg b/app/assets/images/mia_wells.jpg deleted file mode 100644 index c97e581c..00000000 Binary files a/app/assets/images/mia_wells.jpg and /dev/null differ diff --git a/app/assets/images/mitch_fraas.jpg b/app/assets/images/mitch_fraas.jpg deleted file mode 100644 index ab265868..00000000 Binary files a/app/assets/images/mitch_fraas.jpg and /dev/null differ diff --git a/app/assets/images/molly_des_jardin.jpg b/app/assets/images/molly_des_jardin.jpg deleted file mode 100644 index b389d186..00000000 Binary files a/app/assets/images/molly_des_jardin.jpg and /dev/null differ diff --git a/app/assets/images/nicholas_herman.jpg b/app/assets/images/nicholas_herman.jpg deleted file mode 100644 index dddcf123..00000000 Binary files a/app/assets/images/nicholas_herman.jpg and /dev/null differ diff --git a/app/assets/images/nick_okrent.jpg b/app/assets/images/nick_okrent.jpg deleted file mode 100644 index 00ec478e..00000000 Binary files a/app/assets/images/nick_okrent.jpg and /dev/null differ diff --git a/app/assets/images/patricia_guardiola.jpg b/app/assets/images/patricia_guardiola.jpg deleted file mode 100644 index f6c456b7..00000000 Binary files a/app/assets/images/patricia_guardiola.jpg and /dev/null differ diff --git a/app/assets/images/patty_lynn.jpg b/app/assets/images/patty_lynn.jpg deleted file mode 100644 index b6a4b3f1..00000000 Binary files a/app/assets/images/patty_lynn.jpg and /dev/null differ diff --git a/app/assets/images/rebecca_stuhr.jpg b/app/assets/images/rebecca_stuhr.jpg deleted file mode 100644 index 43c9fb6a..00000000 Binary files a/app/assets/images/rebecca_stuhr.jpg and /dev/null differ diff --git a/app/assets/images/richard_james.jpg b/app/assets/images/richard_james.jpg deleted file mode 100644 index c359008c..00000000 Binary files a/app/assets/images/richard_james.jpg and /dev/null differ diff --git a/app/assets/images/samantha_kirk.jpg b/app/assets/images/samantha_kirk.jpg deleted file mode 100644 index 8fa56f83..00000000 Binary files a/app/assets/images/samantha_kirk.jpg and /dev/null differ diff --git a/app/assets/images/sarah_wipperman.jpg b/app/assets/images/sarah_wipperman.jpg deleted file mode 100644 index f820593b..00000000 Binary files a/app/assets/images/sarah_wipperman.jpg and /dev/null differ diff --git a/app/assets/images/sharon_black.jpg b/app/assets/images/sharon_black.jpg deleted file mode 100644 index 3b8dc2a4..00000000 Binary files a/app/assets/images/sharon_black.jpg and /dev/null differ diff --git a/app/assets/images/sherry_morgan.jpg b/app/assets/images/sherry_morgan.jpg deleted file mode 100644 index 404d6a85..00000000 Binary files a/app/assets/images/sherry_morgan.jpg and /dev/null differ diff --git a/app/assets/images/vickie_karasic.jpg b/app/assets/images/vickie_karasic.jpg deleted file mode 100644 index 0aa7e3cb..00000000 Binary files a/app/assets/images/vickie_karasic.jpg and /dev/null differ diff --git a/app/assets/images/william_noel.jpg b/app/assets/images/william_noel.jpg deleted file mode 100644 index 4487338a..00000000 Binary files a/app/assets/images/william_noel.jpg and /dev/null differ diff --git a/app/assets/javascripts/availability.js.erb b/app/assets/javascripts/availability.js.erb index 24aed695..aa858dce 100644 --- a/app/assets/javascripts/availability.js.erb +++ b/app/assets/javascripts/availability.js.erb @@ -4,7 +4,17 @@ var validRequestTypes = ["CentralCAT","MAINT-EC","CHI CAT","CatMet","HEB CAT", "Jap-KorCAT","MUSIC CAT","MidEastCat","SASIA CAT", "SCPC CAT","SLAV CAT","BindRepair","MAINT-enh", "MAINT-otf","MAINT-tr"]; -$.fn.dataTable.ext.errMode = 'none'; +$.fn.dataTable.ext.errMode = 'none'; + +// Hackily parses relevant context out of the DOM for the specified mmsid +function get_request_context(mmsid) { + return { + pickupable: null, + hathi_etas: (document.getElementById('hathi_etas-'+mmsid) != null), + hathi_pd: (document.getElementById('hathi_pd-'+mmsid) != null), + monograph: (document.getElementById('monograph-'+mmsid) != null) + }; +} $(document).ready(function() { @@ -13,7 +23,16 @@ $(document).ready(function() { BlacklightAlma.call(this); } - function constructAeonLink(mmsid, holding) { + // Based on hackily-parsed DOM context, determine whether the Pickup@Penn link should be suppressed + function disable_pickup_at_penn(ctx) { + return ctx.monograph && (ctx.pickupable === false || ctx.hathi_etas /*|| ctx.hathi_pd*/); + } + + function constructAeonLink(mmsid, holding, ctx) { + if (ctx.hathi_etas && ctx.monograph) { + return null; + } + if(holding['link_to_aeon']) { return 'Request to view'; } @@ -24,17 +43,24 @@ $(document).ready(function() { Franklin.prototype = Object.create(BlacklightAlma.prototype); Franklin.prototype.formatHolding = function (mms_id, holding) { + var ctx = get_request_context(mms_id); if (holding['inventory_type'] == 'physical') { var availability = "Unknown"; if (holding['availability'] == 'check_holdings') { availability = "See options"; } else if (holding['availability'] == 'unavailable') { - availability = "See request options"; + // pre-COVID-19 always mapped to "See request options"; + if (holding['link_to_aeon']) { + // Aeon materials show as "unavailable", but not in the sense of being unviewable. + availability = (ctx.hathi_etas && ctx.monograph) ? "Restricted (COVID-19)" : "See request options"; + } else { + availability = "Unavailable"; + } } else if (holding['availability'] == 'available') { - availability = "Available"; + availability = disable_pickup_at_penn(ctx) ? "Restricted (COVID-19)" : "Available"; } // TODO: pass in format to shelfLocatorLink() somehow - return [availability, holding['location'], holding['call_number'], $.shelfLocatorLink(mms_id, holding, "TODO"), constructAeonLink(mms_id, holding)] + return [availability, holding['location'], holding['call_number'], $.shelfLocatorLink(mms_id, holding, "TODO"), constructAeonLink(mms_id, holding, ctx)] .filter(function (item) { return item != null && item.length > 0; }).join(". "); @@ -43,7 +69,7 @@ $(document).ready(function() { if (holding['activation_status'] == 'Available') { var url = null; if (holding['portfolio_pid']) { - url = "https://<%= ENV['ALMA_DELIVERY_DOMAIN'] %>/view/uresolver/<%= ENV['ALMA_INSTITUTION_CODE'] %>/openurl?Force_direct=true&portfolio_pid=" + + url = "https://<%= ENV['ALMA_DELIVERY_DOMAIN'] %>/view/uresolver/<%= ENV['ALMA_INSTITUTION_CODE'] %>/openurl?Force_direct=true&test_access=true&&portfolio_pid=" + holding['portfolio_pid'] + "&rfr_id=info%3Asid%2Fprimo.exlibrisgroup.com&u.ignore_date_coverage=true" } @@ -75,7 +101,8 @@ $(document).ready(function() { }; Franklin.prototype.loadRequestOptionsAjax = function(mmsid) { - var url = "/alma/single_availability.json?mms_id=" + encodeURIComponent(mmsid); + var request_context = get_request_context(mmsid); + var url = "/alma/single_availability.json?mms_id=" + encodeURIComponent(mmsid) + "&request_context=" + encodeURIComponent(JSON.stringify(request_context)); $('#requestOptions-' + mmsid).on('error.dt', function(e, settings, techNote, message) { $(this).dataTable().fnSettings().oLanguage.sEmptyTable = 'An error has occurred.'; @@ -86,7 +113,7 @@ $(document).ready(function() { $.get(url, function(data) { globaldata = $.extend({}, globaldata, data["metadata"]); callback({"data": data["data"]}); - Franklin.prototype.loadRequestOptionListAjax(mmsid); + Franklin.prototype.loadRequestOptionListAjax(mmsid, request_context); }); }, "processing": true, @@ -113,10 +140,11 @@ $(document).ready(function() { var requests = globaldata[data[i][7]]; if(validRequestTypes.indexOf(requests[0]) != -1) { renderdata[mmsid].push(data[i][7]); - data[i][5][0] = "Request"; + // TODO: when libraries reopen: remove conditional, Pickup@Penn=>Request + data[i][5][0] = disable_pickup_at_penn(request_context) ? "" : "Pickup@Penn"; table.row(i).invalidate().draw(); } - } + } } } @@ -198,18 +226,59 @@ $(document).ready(function() { }); }; - Franklin.prototype.loadRequestOptionListAjax = function(mmsid) { + Franklin.prototype.loadRequestOptionListAjax = function(mmsid, request_context) { + request_context.pickupable = globaldata[mmsid]["pickupable"]; $.ajax({ - url: "/alma/request_options.json?mms_id="+mmsid, + url: "/alma/request_options.json?mms_id="+mmsid+"&request_context="+encodeURIComponent(JSON.stringify(request_context)), success: function(data, textStatus, jqXHR) { var optionList = $('#requestOptionList-' + mmsid); - if(globaldata[mmsid]["facultyexpress"]) { - for(i = 0; i < data.length; i++) { - if(data[i]['option_name'] == 'Interlibrary Loan') { - data[i]['option_name'] = 'FacultyEXPRESS'; + var openUrlTemplate = null; // grab openUrl params from here if necessary + var registeredFacEx = globaldata[mmsid]['facultyexpress'] ? false : null; // null if not applicable + var booksByMailOption = null; + for(var i = 0; i < data.length && (openUrlTemplate === null || booksByMailOption === null || registeredFacEx === false); i++) { + var requestOption = data[i]; + switch (requestOption['option_name']) { + case 'Interlibrary Loan': + openUrlTemplate = requestOption['option_url']; + if (registeredFacEx === false) { // careful! must distinguish from "falsey" null + requestOption['option_name'] = 'FacultyEXPRESS'; + registeredFacEx = true; + } break; - } + case 'Request Digital Delivery': + if (openUrlTemplate === null) { + // provisionally grab openUrl from here, but prefer from ILL if present for consistency + openUrlTemplate = requestOption['option_url']; + } + break; + case 'Books By Mail': + booksByMailOption = requestOption; + break; + } + } + if ((registeredFacEx === false || booksByMailOption != null) && (!request_context.monograph || !request_context.hathi_etas)) { + // assume first querystring param is 'requesttype=[...]' -- subsequent options (preceded by '&') should be openUrl params + var startOpenUrlParams = openUrlTemplate === null ? -1 : openUrlTemplate.indexOf('&'); + if (startOpenUrlParams < 0) { + appendOpenUrlParams = ''; + } else { + appendOpenUrlParams = openUrlTemplate.slice(startOpenUrlParams); + } + if (registeredFacEx === false) { + data.push({ + option_name: 'FacultyEXPRESS', + option_url: 'https://franklin.library.upenn.edu/forms/ill?requesttype=book' + appendOpenUrlParams, + highlightable: true + }); + } + if (booksByMailOption != null) { + // NOTE: we're conjuring an ILL link that would normally be generated by Alma, but is not now (COVID-19) because + // ILL-proper is disabled. Despite appearances, 80% confident that requesttype should always be "book" (e.g., even + // for "journals"), because "book" differentiates from things like reprographic requests, _not_ actually based on + // bibliographic material type. 'deliverytype=bbm' is handled in the ill form (franklinforms) to prepend "BBM " to + // otherwise-normal ILL requests (saving staff manually doing so); such ILL requests are then handled as BBM requests. + booksByMailOption['option_url'] = 'https://franklin.library.upenn.edu/forms/ill?requesttype=book&deliverytype=bbm' + appendOpenUrlParams; } } @@ -246,9 +315,8 @@ $(document).ready(function() { containingdiv.appendTo(optionList); }); $('#request-options-spinner-' + mmsid).remove(); - console.log(data); } - }); + }); }; Franklin.prototype.loadRequestOptions = function() { @@ -294,6 +362,7 @@ $(document).ready(function() { "emptyTable": "No item data available" }, "drawCallback": function(settings) { + var request_context = get_request_context(mmsid); var table = $('#holdingItems-' + mmsid).DataTable(); var pageSize = table.settings()[0]['_iDisplayLength']; var tableLen = table.data().length; @@ -305,10 +374,11 @@ $(document).ready(function() { var requests = globaldata[data[i][0]]; if(validRequestTypes.indexOf(requests[0]) != -1) { renderdata[mmsid].push(data[i][0]); - data[i][5].push("Request"); + // TODO: when libraries reopen: remove conditional, Pickup@Penn=>Request + data[i][5].push(disable_pickup_at_penn(request_context) ? "" : "Pickup@Penn"); table.row(i).invalidate().draw(); } - } + } } } @@ -352,11 +422,13 @@ $(document).ready(function() { ba.loadRequestOptions(); }); -function loadItems(mms_id, holding_id, location_code) { +function loadItems(mms_id, holding_id, location_code, pickupable) { + var request_context = get_request_context(mms_id); + request_context.pickupable = pickupable; renderdata[mms_id] = []; var holdingItemsTable = $('#holdingItems-' + mms_id).DataTable(); holdingItemsTable.clear().draw(); - holdingItemsTable.ajax.url('/alma/holding_items.json?mms_id=' + mms_id + "&holding_id=" + holding_id + "¤t_location=" + location_code).load(); + holdingItemsTable.ajax.url('/alma/holding_items.json?mms_id=' + mms_id + "&holding_id=" + holding_id + "¤t_location=" + location_code + "&request_context=" + encodeURIComponent(JSON.stringify(request_context))).load(); swapDataTables(mms_id); } diff --git a/app/assets/javascripts/expert_help.js b/app/assets/javascripts/expert_help.js new file mode 100644 index 00000000..8de4e28d --- /dev/null +++ b/app/assets/javascripts/expert_help.js @@ -0,0 +1,19 @@ +$(document).ready(function() { + var $expert; + $expert = $('#ExpertOptions'); + + if (window.matchMedia("(min-width: 992px)").matches && + !document.cookie.includes('franklin_hide_expert_help=true')) { + $expert.collapse('show'); + } else { + $expert.collapse('hide'); + } + + $expert.on('hide.bs.collapse', function() { + document.cookie = 'franklin_hide_expert_help=true;path=/' + }); + + $expert.on('show.bs.collapse', function() { + document.cookie = 'franklin_hide_expert_help=;path=/;max-age=0' + }); +}); diff --git a/app/assets/javascripts/login-ezproxy.js b/app/assets/javascripts/login-ezproxy.js index 140ac3eb..53a36ca0 100644 --- a/app/assets/javascripts/login-ezproxy.js +++ b/app/assets/javascripts/login-ezproxy.js @@ -45,7 +45,7 @@ $(document).ready(function() { var proxyUrl = PROXY_PREPEND + 'http://127.0.0.1:8082/?redirect=' + encodeURIComponent(currentUrl); $(div).find(".ezproxy-login-link").attr("href", proxyUrl); - if (auth !== null && auth !== undefined) { + if (auth !== null && auth !== undefined && auth.loggedIn) { $(div).find(".view-and-filter").find("a").each(function (idx, element) { // ezproxy handles url param specifically so we don't need to escape it var viewAndFilterUrl = $(element).attr("href"); diff --git a/app/assets/javascripts/no_db_results.js b/app/assets/javascripts/no_db_results.js index f2ea9313..e69de29b 100644 --- a/app/assets/javascripts/no_db_results.js +++ b/app/assets/javascripts/no_db_results.js @@ -1,5 +0,0 @@ -$(document).ajaxComplete(function() { - if ($('.gs-no-results-result').length) { - $('#bento-results-google').addClass('hidden'); - } -}); \ No newline at end of file diff --git a/app/assets/javascripts/tabs.js b/app/assets/javascripts/tabs.js index e934a53f..03888d7d 100644 --- a/app/assets/javascripts/tabs.js +++ b/app/assets/javascripts/tabs.js @@ -5,10 +5,6 @@ */ $(document).ready(function() { - $("#feedbackout").click(function(){ - $("#feedback").hide(); - }); - $("a[role='tab']").bind('click', function(event) { window.history.pushState({},"", $(event.currentTarget).attr("href")); }); diff --git a/app/assets/javascripts/two_columns.js b/app/assets/javascripts/two_columns.js deleted file mode 100644 index b7b127e4..00000000 --- a/app/assets/javascripts/two_columns.js +++ /dev/null @@ -1,6 +0,0 @@ -$(document).ajaxComplete(function() { - if($("#bento-results-databases .bento-item").length == 0 && $('.gs-no-results-result').length){ - $('#bento-results-catalog').addClass('fifty-percent') - $('#bento-results-summon').addClass('fifty-percent') - } -}); \ No newline at end of file diff --git a/app/assets/stylesheets/franklin.css.scss b/app/assets/stylesheets/franklin.css.scss index 08dc5d3f..5be5d8f6 100644 --- a/app/assets/stylesheets/franklin.css.scss +++ b/app/assets/stylesheets/franklin.css.scss @@ -3,19 +3,15 @@ @import 'base/colors.css.scss'; @import 'base/mixins.css.scss'; -@media (min-width: 1200px) { - .col-md-3 { - width: 24%; - } -} @media (min-width: 992px) and (max-width: 1199px) { - .col-md-3 { - width: 25%; + .bento-box.databases-results, .bento-box.colenda-results, + .bento-box.google-results { + width: auto; } - .bento-box { - width: 32%; + .bento-box, .libraries-results { + width: 32.5%; } } @@ -380,6 +376,10 @@ bg color to fill document-metadata's bottom margin */ display: none; } +.libraries-results { + padding: 0px; +} + .view-and-filter { padding: 6px; background: #ececec; @@ -613,7 +613,6 @@ h3.ongray { margin: 5px; padding: 0px; z-index: 10; - min-height: 250px; } .bento-box-highlight { @@ -710,6 +709,18 @@ h3.ongray { } } +.colenda-thumbnail { + text-align: center; + img { + height: 50px; + width: 90%; + object-fit: cover; + } +} +.colenda-help { + font-size: 13px; +} + /* hack */ #bento-results-google .source-info { display: none; @@ -721,26 +732,71 @@ h3.ongray { /* end of hack */ +.panel-default > .panel-heading.expert-box { + color: white; + background-color: #337ab7; + border-color: #ddd; +} +.expert-box.panel-heading.collapse-toggle .panel-title:after { + font-family: 'Glyphicons Halflings'; + content: "\e114"; + float: right; + color: white; + font-size: 1.03em; + line-height: normal; +} + +.expert-box.panel-heading.collapse-toggle.collapsed .panel-title:after { + font-family: 'Glyphicons Halflings'; + content: "\e113"; + float: right; + color: white; + font-size: 1.03em; + line-height: normal; +} + +h3.expert-heading { + margin-top: 0; + padding: 0; +} + +.expert-chat-link { + margin: 0 0 5px; + text-align: center; +} + +#libchat_7cde980ad423479da2585a498007774e button.libchat_online { + font-size: 12px; + padding: 1px 5px 3px; + height: 20px; +} + .expert-results { - display: none; /* TODO: remove when launching this feature */ - padding: 10px; - hr { - border-bottom: 1px dashed #d2d2d2; + position: fixed; + bottom: 0; + right: 0; + width: 235px; + min-height: auto; + margin: 0; + + .expert-p { + margin-bottom: 0; } .expert-profile { + padding: 8px; font-size: 12px; .upper-content { } .lower-content { clear: both; - padding-top: 10px; + padding-top: 3px; } img { @include rounded-corners; width: 100px; height: 100px; float: left; - margin-right: 20px; + margin-right: 8px; } a.link-to-profile { font-size: 16px; @@ -761,64 +817,54 @@ h3.ongray { } } +.hr-text { + line-height: 1em; + position: relative; + outline: 0; + border: 0; + color: black; + text-align: center; + margin-top: 5px; + margin-bottom: 5px; + height: 1.5em; + opacity: .5; + &:before { + content: ''; + background: linear-gradient(to right, transparent, #818078, transparent); + position: absolute; + left: 0; + top: 50%; + width: 100%; + height: 1px; + } + &:after { + content: attr(data-content); + position: relative; + display: inline-block; + color: black; + + padding: 0 .5em; + line-height: 1.5em; + background-color: #fcfcfa; + } +} + .databases-landing-content .heading { font-weight: bold; color: #900; font-size: 1.17em; } -#feedback { - display: none; - background-image: asset-url('feedback.png'); - background-repeat: no-repeat; - width: 77px; - height: 91px; - background-size: auto 89px; - position: absolute; - top: 16.0rem; - z-index: 1000; - right: 2.5rem; -} - .bento_item_body href { overflow: hidden; -ms-text-overflow: ellipsis; text-overflow: ellipsis; } -@media (min-width: 768px) { - #feedback { - display: block; - } -} - @media (min-width: 1200px) { - #feedback { - top: 11.0rem; - } .col-lg-4 { - width: 32.333333%; + width: 32.6%; } - -} - -#feedbacklink { - cursor: pointer; - display: block; - height: 7.7rem; - width: 8.9rem; - position: absolute; - top: 0; -} - -#feedbackout { - cursor: pointer; - display: block; - height: 1.6rem; - width: 1.6rem; - position: absolute; - right: 0; - top: 7.7rem; } .visually-hidden { @@ -880,6 +926,11 @@ h3.ongray { margin-bottom: 20px; } + .expert-results.bento-box { + min-width: auto; + margin-bottom: auto; + } + } .bento-all { diff --git a/app/controllers/catalog_controller.rb b/app/controllers/catalog_controller.rb index 809ea8b9..caf5af74 100644 --- a/app/controllers/catalog_controller.rb +++ b/app/controllers/catalog_controller.rb @@ -70,7 +70,7 @@ def expire_session PAGINATION_THRESHOLD=250 before_action only: :index do if params[:page] && params[:page].to_i > PAGINATION_THRESHOLD - flash[:error] = "You have paginated too deep into the result set. Please contact us using the feedback form if you have a need to view results past page #{PAGINATION_THRESHOLD}." + flash[:error] = "You have paginated too deep into the result set. Please contact us if you have a need to view results past page #{PAGINATION_THRESHOLD}." redirect_to root_path end end @@ -78,7 +78,7 @@ def expire_session FACET_PAGINATION_THRESHOLD=50 before_action only: :facet do if params['facet.page'] && params['facet.page'].to_i > FACET_PAGINATION_THRESHOLD - flash[:error] = "You have paginated too deep into facets. Please contact us using the feedback form if you have a need to view facets past page #{FACET_PAGINATION_THRESHOLD}." + flash[:error] = "You have paginated too deep into facets. Please contact us if you have a need to view facets past page #{FACET_PAGINATION_THRESHOLD}." redirect_to root_path end end @@ -105,6 +105,12 @@ def expire_session config.default_solr_params = { #cache: 'false', defType: 'perEndPosition_dense_shingle_graphSpans', + combo: '{!filters param=$q param=$fq excludeTags=cluster}', + post_1928: 'content_max_dtsort:[1929-01-01T00:00:00Z TO *]', + culture_filter: "{!bool should='{!terms f=subject_search v=literature,customs,religion,ethics,society,social,culture,cultural}' should='{!prefix f=subject_search v=art}'}", + #combo: '{!bool must=$q filter=\'{!filters param=$fq v=*:*}\'}', + #combo: '{!query v=$q}', + back: '*:*', # this list is annoying to maintain, but this avoids hard-coding a field list # in the search request handler in solrconfig.xml fl: %w{ @@ -201,6 +207,42 @@ def expire_session 'Include Partner Libraries' != a.params.dig(:f, :cluster, 0) } + # Some filters (e.g., subject_f) are capable of driving meaning correlations; + # others are not, and either generate spurious correlations, or at best pointlessly add extra + # overhead to Solr request. + # Keys below indicate filters that we should not attempt to use for purpose of cacluating + # correlations -- either entirely (nil value) or for an array of certain filter values + @@CORRELATION_IGNORELIST = { + :access_f => nil, + :record_source_f => nil, + :format_f => ['Database & Article Index'] + } + + actionable_filters = lambda { |a, b, c| + params = a.params + return true if params[:q].present? + f = params[:f] + return false if f.nil? + # we return true if there is at least one non-ignorelisted filter + return true if f.size > @@CORRELATION_IGNORELIST.size + f.symbolize_keys.each do |facet_key, facet_values| + return true unless @@CORRELATION_IGNORELIST.include?(facet_key) # no vals are ignored for this key + ignorelisted_vals = @@CORRELATION_IGNORELIST[facet_key] + next unless ignorelisted_vals # all vals are ignored for this key + return true if (facet_values - ignorelisted_vals).present? # there is at least one non-ignored val + end + return false + } + + get_hits = lambda { |v| + r1 = v[:r1] + r1.nil? ? 0 : (r1[:relatedness].to_f * 100000).to_i + } + + post_sort = lambda { |items| + items.sort { |a,b| b.hits <=> a.hits } + } + config.induce_sort = lambda { |blacklight_params| return 'title_nssort asc' if blacklight_params.dig(:f, :format_f)&.include?('Database & Article Index') } @@ -221,6 +263,11 @@ def expire_session } } + @@SUBJECT_SPECIALISTS = File.open("config/translation_maps/subject_specialists.solr-json", "rb").map do |line| + comment_idx = line.index('#') + (comment_idx.nil? ? line : line.slice(0, comment_idx)).strip.presence + end.compact.join + @@DATABASE_CATEGORY_TAXONOMY = [ '{', 'db_category_f:{', @@ -276,6 +323,9 @@ def expire_session config.add_facet_field 'db_type_f', label: 'Database Type', limit: -1, collapse: false, :if => database_selected, :facet_type => :database, solr_params: @@MINCOUNT + config.add_facet_field 'subject_specialists', label: 'Subject Area Correlation', collapse: true, :partial => 'blacklight/hierarchy/facet_hierarchy', + :json_facet => @@SUBJECT_SPECIALISTS, :top_level_field => 'subject_specialists', :get_hits => get_hits, :post_sort => post_sort, + :if => actionable_filters config.add_facet_field 'azlist', label: 'A-Z List', collapse: false, single: :manual, :facet_type => :header, options: {:layout => 'horizontal_facet_list'}, solr_params: { 'facet.mincount' => 0 }, :if => database_selected, query: { 'A' => { :label => 'A', :fq => "{!prefix tag=azlist ex=azlist f=title_xfacet v='a'}"}, @@ -307,7 +357,7 @@ def expire_session 'Other' => { :label => 'Other', :fq => "{!tag=azlist ex=azlist}title_xfacet:/[ -`{-~].*/"} } config.add_facet_field 'access_f', label: 'Access', collapse: false, solr_params: @@MINCOUNT, :if => local_only, query: { - 'Online' => { :label => 'Online', :fq => "{!join from=cluster_id to=cluster_id v=access_f:Online}"}, + 'Online' => { :label => 'Online', :fq => "{!join from=cluster_id to=cluster_id v='access_f:Online OR record_source_id:3'}"}, 'At the library' => { :label => 'At the library', :fq => "{!join from=cluster_id to=cluster_id v='{!term f=access_f v=\\'At the library\\'}'}"} } config.add_facet_field 'format_f', label: 'Format', limit: 5, collapse: false, solr_params: @@MINCOUNT, :if => local_only, query: { @@ -338,6 +388,11 @@ def expire_session config.add_facet_field 'language_f', label: 'Language', limit: 5, collapse: false, solr_params: @@MINCOUNT config.add_facet_field 'library_f', label: 'Library', limit: 5, collapse: false, :if => local_only, solr_params: @@MINCOUNT config.add_facet_field 'specific_location_f', label: 'Specific location', limit: 5, :if => local_only, solr_params: @@MINCOUNT + config.add_facet_field 'recently_published', label: 'Recently published', collapse: false, solr_params: @@MINCOUNT, :query => { + :last_5_years => { label: 'Last 5 years', fq: "pub_max_dtsort:[#{Date.current.year - 4}-01-01T00:00:00Z TO *]" }, + :last_10_years => { label: 'Last 10 years', fq: "pub_max_dtsort:[#{Date.current.year - 9}-01-01T00:00:00Z TO *]" }, + :last_15_years => { label: 'Last 15 years', fq: "pub_max_dtsort:[#{Date.current.year - 14}-01-01T00:00:00Z TO *]" }, + } config.add_facet_field 'publication_date_f', label: 'Publication date', limit: 5, collapse: false, solr_params: @@MINCOUNT config.add_facet_field 'classification_f', label: 'Classification', limit: 5, collapse: false, :if => local_only, solr_params: @@MINCOUNT config.add_facet_field 'genre_f', label: 'Form/Genre', limit: 5, solr_params: @@MINCOUNT diff --git a/app/controllers/collection_news_controller.rb b/app/controllers/collection_news_controller.rb index 1e335094..a928c8f6 100644 --- a/app/controllers/collection_news_controller.rb +++ b/app/controllers/collection_news_controller.rb @@ -7,7 +7,7 @@ class CollectionNewsController < ApplicationController include RssProxy def index - rss_proxy('https://pennlibnews.wordpress.com/category/collection-news/feed/') + rss_proxy('http://www.library.upenn.edu/blogs/libraries-news/category/Collections/rss.xml') end end diff --git a/app/controllers/franklin_alma_controller.rb b/app/controllers/franklin_alma_controller.rb index a915198b..c056e8b2 100644 --- a/app/controllers/franklin_alma_controller.rb +++ b/app/controllers/franklin_alma_controller.rb @@ -1,3 +1,4 @@ +require 'json' #class FranklinAlmaController < BlacklightAlma::AlmaController class FranklinAlmaController < ApplicationController @@ -167,6 +168,7 @@ def single_availability bib_data = bibapi.get_availability([mmsid]) holding_data = nil holding_map = {} + pickupable = false inventory_type = '' @@ -182,7 +184,7 @@ def single_availability # a 'holding_info' value. unless has_holding_info holding_data = api_instance.request(api.almaws_v1_bibs.mms_id_holdings_holding_id_items, :get, :mms_id => mmsid, :holding_id => 'ALL', :expand => 'due_date_policy', :user_id => userid) - + [holding_data['items']['item']].flatten.reject(&:nil?).each do |item| holding_id = item['holding_data']['holding_id'] item_pid = item['item_data']['pid'] @@ -216,14 +218,17 @@ def single_availability end .reject(&:nil?) else + ctx = JSON.parse(params[:request_context]) bib_data['availability'][mmsid]['holdings'].each do |holding| + holding_pickupable = holding['availability'] == 'available' + pickupable = true if holding_pickupable links = [] - links << "Request to view in reading room" if holding['link_to_aeon'] + links << "Request to view in reading room" if holding['link_to_aeon'] unless (ctx['hathi_etas'] && ctx['monograph']) holding['availability'] = availability_status[holding['availability']] || 'Requestable' if has_holding_info inventory_type = 'physical' - holding['location'] = %Q[#{holding['location']} >
#{holding['call_number']}] + holding['location'] = %Q[#{holding['location']} >
#{holding['call_number']}] holding['availability'] = "" elsif has_portfolio_info inventory_type = 'electronic' @@ -241,8 +246,16 @@ def single_availability if userid == 'GUEST' holding['availability'] = 'Log in & request below' else - holding['availability'] = 'Not on shelf; request below' + if suppress_pickup_at_penn(ctx) && session['user_group'] != 'Faculty Express' + # we're temporarily disabling all request options for non facex + holding['availability'] = 'Not on shelf' + else + # for non-request-suppressed items and FacEx users, still present the usual link + holding['availability'] = 'Not on shelf; request below' + end end + elsif holding['availability'] == 'Available' && suppress_pickup_at_penn(ctx) + holding['availability'] = 'Restricted (COVID-19)' end end @@ -265,7 +278,8 @@ def single_availability end metadata[mmsid][:inventory_type] = inventory_type - render :json => {"metadata": metadata, "data": table_data} + metadata[mmsid][:pickupable] = pickupable + render :json => { "metadata": metadata, "data": table_data } end def check_requestable(has_holding_info = false) @@ -322,18 +336,18 @@ def holding_items while options[:offset] + options[:limit] < response_data['total_record_count'] options[:offset] += options[:limit] response_data = api_instance.request(api.almaws_v1_bibs.mms_id_holdings_holding_id_items, :get, params.merge(options)) - + table_data += response_data['item'].map { |item| data = item['item_data'] unless(policies.has_key?(data['policy']['value']) || data['base_status']['desc'] != "Item in place" || userid.nil?) policies[data['policy']['value']] = nil pids_to_check << [data['pid'], data['policy']['value']] end - [data['policy']['value'], data['pid'], data['description'], due_date_policy || data['due_date_policy'], data['base_status']['desc'], data['barcode'], [], params['mms_id'], params['holding_id']] + [data['policy']['value'], data['pid'], data['description'], data['base_status']['desc'], data['barcode'], due_date_policy || data['due_date_policy'], [], params['mms_id'], params['holding_id']] } end - pids_to_check.each{ |pid, policy| + pids_to_check.each{ |pid, policy| options = {:user_id => userid, :item_pid => pid} response_data = api_instance.request(api.almaws_v1_bibs.mms_id_holdings_holding_id_items_item_pid_request_options, :get, params.merge(options)) not_requestable = true @@ -345,19 +359,28 @@ def holding_items end policies[policy] = "/alma/request/?mms_id=%{mms_id}&holding_id=%{holding_id}&item_pid=%{item_pid}" unless not_requestable } - + suppress = suppress_pickup_at_penn(JSON.parse(params['request_context'])) table_data.each { |item| policy = item.shift() request_url = (policies[policy] || '') % params.merge({:item_pid => item[0]}) - item[5] << "Request" unless (request_url.empty? || item[2] != 'Item in place') + # TODO: when libraries reopen: remove conditional, Pickup@Penn=>Request + item[5] << (suppress ? "" : "Pickup@Penn") unless (request_url.empty? || item[2] != 'Item in place') } render :json => {"data": table_data} end + def suppress_pickup_at_penn(ctx) + return false unless ctx['monograph'] + return true unless ctx['pickupable'] != false + return true if ctx['hathi_etas'] #|| ctx['hathi_pd'] + return false + end + def request_options userid = session['id'].presence || nil usergroup = session['user_group'].presence + ctx = JSON.parse(params['request_context']) api_instance = BlacklightAlma::BibsApi.instance api = api_instance.ezwadl_api[0] options = {:user_id => userid, :consider_dlr => true} @@ -373,13 +396,13 @@ def request_options case option['type']['value'] when 'HOLD' { - :option_name => 'Request', + :option_name => 'Pickup@Penn', #:option_url => option['request_url'], :option_url => "/alma/request?mms_id=#{params['mms_id']}", :avail_for_physical => true, :avail_for_electronic => true, :highlightable => true - } + } unless suppress_pickup_at_penn(ctx) # TODO: when libraries reopen: remove conditional, Pickup@Penn=>Request when 'GES' option_url = option['request_url'] if option_url.index('?') @@ -421,7 +444,7 @@ def request_options .uniq # Required due to request options API bug returning duplicate options .sort { |a,b| cmpRequestOptions(a,b) } # TODO: Remove when GES is updated in Alma & request option API is fixed (again) - results.reject! { |option| + results.reject! { |option| ['Send Penn Libraries a question','Books By Mail'].member?(option[:option_name]) || (option[:option_name] == 'FacultyEXPRESS' && usergroup != 'Faculty Express') } @@ -430,7 +453,7 @@ def request_options results.each { |option| case option[:option_name] when 'Suggest Fix / Enhance Record' - option[:option_name] = "Report Error" + option[:option_name] = "Report Cataloging Error" end } @@ -438,8 +461,9 @@ def request_options :option_name => "Books By Mail", :option_url => "https://franklin.library.upenn.edu/redir/booksbymail?bibid=#{params['mms_id']}", :avail_for_physical => true, - :avail_for_electronic => false - }) if ['Athenaeum Member','Faculty','Faculty Express','Grad Student','Library Staff'].member?(session['user_group']) + :avail_for_electronic => false, + :highlightable => true + }) if ['Athenaeum Member','Faculty','Faculty Express','Grad Student','Undergraduate Student','Staff','Associate','Retired Library Staff','Library Staff'].member?(session['user_group']) && !suppress_pickup_at_penn(ctx) # suppress for bbm is same as for Pickup@Penn render :json => results end @@ -452,7 +476,7 @@ def request_title? response_data = api_instance.request(api.almaws_v1_bibs.mms_id_request_options, :get, params.merge(options)) - return (response_data['request_option'] || []).map { |option| + return (response_data['request_option'] || []).map { |option| option['type']['value'] }.member?('HOLD') end @@ -465,7 +489,7 @@ def request_item? response_data = api_instance.request(api.almaws_v1_bibs.mms_id_holdings_holding_id_items_item_pid_request_options, :get, params.merge(options)) - return (response_data['request_option'] || []).map { |option| + return (response_data['request_option'] || []).map { |option| option['type']['value'] }.member?('HOLD') end @@ -478,7 +502,7 @@ def load_request render 'catalog/bad_request' return end - + if params['item_pid'].present? render 'catalog/bad_request' unless request_item? else @@ -503,20 +527,21 @@ def load_request #.reject { |k,v| exclude_libs.member?(k) } #) - libraries = { "Annenberg Library" => "AnnenLib", - "Athenaeum Library" => "AthLib", - "Biomedical Library" => "BiomLib", - "Chemistry Library" => "ChemLib", - "Dental Medicine Library" => "DentalLib", - "Fisher Fine Arts Library" => "FisherFAL", - "Library at the Katz Center" => "KatzLib", - "Math/Physics/Astronomy Library" => "MPALib", - "Museum Library" => "MuseumLib", - "Ormandy Music and Media Center" => "MusicLib", - "Pennsylvania Hospital Library" => "PAHospLib", - "Van Pelt Library" => "VanPeltLib", - "Veterinary Library - New Bolton Center" => "VetNBLib", - "Veterinary Library - Penn Campus" => "VetPennLib" + # Uncomment these as more libraries open up as delivery options + libraries = { #"Annenberg Library" => "AnnenLib", + #"Athenaeum Library" => "AthLib", + #"Biomedical Library" => "BiomLib", + #"Chemistry Library" => "ChemLib", + #"Dental Medicine Library" => "DentalLib", + #"Fisher Fine Arts Library" => "FisherFAL", + #"Library at the Katz Center" => "KatzLib", + #"Math/Physics/Astronomy Library" => "MPALib", + #"Museum Library" => "MuseumLib", + #"Ormandy Music and Media Center" => "MusicLib", + #"Pennsylvania Hospital Library" => "PAHospLib", + "Van Pelt Library" => "VanPeltLib" + #"Veterinary Library - New Bolton Center" => "VetNBLib", + #"Veterinary Library - Penn Campus" => "VetPennLib" } if params['item_pid'].present? @@ -538,7 +563,7 @@ def create_request render 'catalog/bad_request' return end - + if params['item_pid'].present? render 'catalog/bad_request' unless request_item? else diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 4e7a0ebd..fbe072ff 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -9,17 +9,24 @@ def summon_url(query, proxy = false) end end - def databases_results_url(query) - if !params.dig('f', 'format_f')&.include?('Database & Article Index') - url = path_for_facet('format_f', 'Database & Article Index') - end - return url + # Path for a BL catalog index page with Databases facet applied + # @return [String] + # @param [String] query param + def databases_results_path(query) + search_catalog_path params: { + q: query, utf8: '✓', search_field: 'keyword', + 'f[format_f][]': 'Database & Article Index' + } end def google_site_search_results_url(query) return "https://www.library.upenn.edu/search/web-pages?q=#{query}" end + def colenda_search_results_url(query) + return "https://colenda.library.upenn.edu/catalog?q=#{query}" + end + def catalog_results_url(query) return search_catalog_path(q: query, search_field: 'keyword') end @@ -62,6 +69,10 @@ def render_tab_link(tab_id, tab_label, anchor, url, data_target) attrs = { 'href': "http://www.library.upenn.edu/search/web-pages" } + elsif tab_id == 'colenda' && action_name == 'landing' + attrs = { + 'href': "https://colenda.library.upenn.edu" + } else attrs = { 'href': anchor, @@ -98,8 +109,18 @@ def my_library_card_url "https://#{ ENV['ALMA_DELIVERY_DOMAIN'] }/discovery/account?vid=#{ ENV['ALMA_INSTITUTION_CODE'] }:Services&lang=en§ion=overview" end - def profile_url(name) - "https://www.library.upenn.edu/people/staff/#{name.downcase.gsub(/\ +/, '-')}" + def subject_url(subject) + "https://www.library.upenn.edu/people/subject-specialists##{subject.dasherize}" + end + + def bolded_subject_list(subjects, match) + subjects.map do |s| + if match.downcase.gsub(/[^a-z]|amp/, '') == s.downcase.gsub(/[^a-z]/, '') + content_tag(:strong, s) + else + s + end + end end def refworks_bookmarks_path(opts = {}) @@ -201,5 +222,4 @@ def prepopulated_search_fields_for_advanced_search(num_fields, is_numeric: true, def resourcesharing_path '/forms/resourcesharing' end - end diff --git a/app/helpers/blacklight_helper.rb b/app/helpers/blacklight_helper.rb index 781ec4bd..51badd0b 100644 --- a/app/helpers/blacklight_helper.rb +++ b/app/helpers/blacklight_helper.rb @@ -10,16 +10,25 @@ def render_search_bar end def render_expert_help(specialists) - specialists.present? && specialists.items.first.hits > 50000 ? (render partial: 'catalog/expert_help', locals: {subject: specialists.items.first.value}) : (render partial: 'catalog/ask') + if specialists.present? && specialists.items.first.hits > 50000 + subject = specialists.items.first.value + specialist_data = PennLib::SubjectSpecialists.data + end + if specialist_data + specialist = specialist_data[subject.to_sym].sample + render partial: 'catalog/expert_help', locals: { specialist: specialist, subject: subject } + else + render partial: 'catalog/ask' + end end # override so that we can insert separators - def search_fields + def search_fields super.map do |option| field_def = blacklight_config.search_fields[option[1]] separator = (field_def && field_def.separator_beneath) ? - [ '--------', '--------', { disabled: 'true', class: 'hidden-xs' } ] : nil - [ option, separator].compact + [ '--------', '--------', { disabled: 'true', class: 'hidden-xs' } ] : nil + [option, separator].compact end.flatten(1) end diff --git a/app/helpers/document_render_helper.rb b/app/helpers/document_render_helper.rb index 6d68bd4a..639ae6f3 100644 --- a/app/helpers/document_render_helper.rb +++ b/app/helpers/document_render_helper.rb @@ -51,31 +51,75 @@ def render_electronic_holdings_links(electronic_holdings_str) end end + @@HATHI_PD_TEXT = 'HathiTrust Digital Library Connect to full text' + @@HATHI_TMP_TEXT = 'HathiTrust Digital Library Login for full text' + @@HATHI_REPLACEMENT_TEXT = 'COVID-19 Special Access from HathiTrust' + @@HATHI_INFO = ' — Full text access only for students, active faculty, and permanent staff' + @@HATHI_LOGIN_PREFIX = 'https://babel.hathitrust.org/Shibboleth.sso/Login?entityID=https://idp.pennkey.upenn.edu/idp/shibboleth&target=https%3A%2F%2Fbabel.hathitrust.org%2Fcgi%2Fping%2Fpong%3Ftarget%3D' + + def detect_monograph(document) + return nil unless (alma_mms_id = document[:alma_mms_id]).presence + return nil unless ['a','m'].include?(document.to_marc.leader[7]) + "
".html_safe + end + def render_online_resource_display_for_index_view(options) values = options[:value] suppress_remote_links = 'Include Partner Libraries' != params.dig('f', 'cluster', 0) + alma_mms_id = options[:document][:alma_mms_id] + hathi_pd = false + hathi_etas = false ret = values.map do |value| JSON.parse(value).map do |link_struct| url = link_struct['linkurl'] text = link_struct['linktext'] - (suppress_remote_links && text =~ /View record in .*\'s catalog/) ? nil : %Q{#{text}} + append = '' + if text == @@HATHI_PD_TEXT + hathi_pd = true + elsif text == @@HATHI_TMP_TEXT + hathi_etas = true + text = @@HATHI_REPLACEMENT_TEXT + url = @@HATHI_LOGIN_PREFIX + URI.encode_www_form_component(url) + append = @@HATHI_INFO + end + (suppress_remote_links && text =~ /View record in .*\'s catalog/) ? nil : %Q{#{text}#{append}} end.compact.join('
') end.reject { |item| item.blank? }.join('
') + unless alma_mms_id.nil? + if hathi_pd + ret = ret.concat(hathi_tag_id('pd', alma_mms_id)) + end + if hathi_etas + ret = ret.concat(hathi_tag_id('etas', alma_mms_id)) + end + end ret.blank? ? 'Has partner library holdings' : ret.html_safe end def render_online_display_for_show_view(options) values = options[:value] - - values.map do |value| + alma_mms_id = options[:document][:alma_mms_id] + hathi_pd = false + hathi_etas = false + ret = values.map do |value| JSON.parse(value).map do |link_struct| url = link_struct['linkurl'] text = link_struct['linktext'] - html = %Q{