Context-sensitive request: Displaying link(s) based on user and item attributes
Place a hold (via ALMA)
Book a visual item (via ALMA)
Book an archive item for viewing
Book an item from special collections for viewing
Place an item on reserves
Document delivery (scanning service)
Document delivery (hold a book)
Non-Alma requests appear as tabs in the tab row.
* Requires primo sign-in
Primo view's footer contains javascript code
Javascript invokes Mehmet Celik's jQuery.PRIMO
jQuery.PRIMO provides PNX record, user.isLoggedIn() and ALMA's user_id
PNX indicates whether physical item belongs to Emory. If so, it contains ALMA's mms_id(s)
<script type="text/javascript">
jQuery(document).ready(function () {
if (typeof(Storage) !== "undefined"){
var isLoggedIn=jQuery.PRIMO.session.user.isLoggedIn();
var record_count=jQuery.PRIMO.records.length;
if (isLoggedIn ){
bg_user_id=jQuery.PRIMO.session.user.id;
}
else{
bg_user_id="0";
}
re_pattern_gatech=new RegExp(".*GALI_GIT_ALMA.*"),
re_pattern_emo=new RegExp(".*01EMORY_ALMA.*"),
var gatech_item=false;
var emory_item=false;
if (record_count == 1){
pnx=jQuery.PRIMO.records[0].getPNX();
parser=new DOMParser();
xmlDoc = parser.parseFromString(pnx,"text/xml");
x = xmlDoc.documentElement.childNodes;
txt="";
for (i = 0; i < x.length ;i++) {
if (x[i].nodeName == "search"){
search_node=x[i].childNodes;
for (j=0; j< search_node.length;j++){
if (search_node[j].nodeName == "addsrcrecordid"){
record_id=search_node[j].childNodes[0].nodeValue;
if (merged_record_id == ""){
merged_record_id=record_id;
}
else{
merged_record_id=merged_record_id+","+record_id;
}
}
}
}
if (x[i].nodeName == "control"){
control_node=x[i].childNodes;
for (j=0; j< control_node.length;j++){
if (control_node[j].nodeName == "sourceid"){
source_id=control_node[j].childNodes[0].nodeValue;
if (re_pattern_emo.test(source_id)){
emory_item=true;
}
if (re_pattern_gatech.test(source_id)){
gatech_item=true;
}
}
}
}
}
});
if (emory_item ){
medusa_url="https://libapiproxyprod1.library.emory.edu/cgi-bin/primo_request?doc_id="+merged_record_id+"&user_id="+bg_user_id;
jQuery.ajaxSetup({async:false});
jQuery.get(medusa_url,function(xdata){
parser=new DOMParser();
xml_doc = parser.parseFromString(xdata,"text/xml");
xnode=xml_doc.documentElement.childNodes;
// more...
}
<result>
<code>OK</code>
<link>
<request_link>
<reserves>https://libapiproxyprod1.library.emory.edu/cgi-bin/process_reserves_primo_new?doc_id=990025556220302486&user_id=0036487</reserves>
<docdelivery>https://libapiproxyprod1.library.emory.edu/cgi-bin/document_delivery_primo_new?doc_id=990025556220302486&user_id=0036487</docdelivery>
</request_link>
<worldcat_identity>http://worldcat.org/identities/lccn-n2005074653/</worldcat_identity>
</link>
<user_group>01</user_group>
</result>
if (reserves_url != ""){
this_url="<a href="+reserves_url+" target=\"_blank\" id=\"reserves_bg\" title=\"Place this item on Reserves\">gt;Place on Reserves</a>gt;";
$('#exlidResult0-TabsList').append('<li id="EULAresLink" class="EXLResultTab">gt;'+this_url+'</li>gt;');
}
What is CORS? Browser received HTML page from Primo's domain but it is issuing an HTTP request to emory's domain.
What does the browser do? It issues request method "OPTIONS" to emory's API proxy.
OPTIONS /cgi-bin/primo_request?doc_id=990031707790302486&user_id=0036487 HTTP/1.1
Host: libapiproxyprod1.library.emory.edu
Access-Control-Request-Method: GET
Origin: http://discovere.emory.edu
Access-Control-Request-Headers: exlrequesttype
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://discovere.emory.edu
Access-Control-Allow-Methods: GET
Access-Control-Allow-Headers: EXLREQUESTTYPE,authorization,x-from-exl-api-gateway
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import cgi
def print_ok_cors(result,origin):
"""
it prints an xml response with the appropriate headers
to indicate a successful OPTIONS request.
"""
print "Content-Type: text/xml; charset=utf-8"
print "Access-Control-Allow-Origin: "+origin
print "Access-Control-Allow-Methods: GET"
print "Access-Control-Allow-Headers: EXLREQUESTTYPE,authorization,x-from-exl-api-gateway"
print ""
print result
return
def main():
###
# configuration file has:
#preflight_hostnames=discovere.emory.edu;discoveretest.emory.edu;emory-primosb-alma.hosted.exlibrisgroup.com;primodbe.library.emory.edu;emory-primoprod.hosted.exlibrisgroup.com
###
http_method=os.environ["REQUEST_METHOD"]
if http_method == "OPTIONS":
try:
preflight_list=preflight_hostnames.split(";")
except:
preflight_list=[]
origin_rule=re.compile("(http://|https://)(.*)")
try:
origin=os.environ["HTTP_ORIGIN"]
except:
print_error("NO HTTP_ORIGIN")
return 1
m=origin_rule.match(origin)
if m:
if m.group(2) in preflight_list:
print_ok_cors("<result><code>OK</code></result>",origin)
else:
print_notok_cors("<result><code>ERROR</code></result>")
return 0
#####
columns (context): user_group|library|location|material_type|archive|request_list title is an "archive" if MARC leader[11] is "p"
webservice relies on API utility to retrieve ALMA record
https://aeon.library.emory.edu/Logon/?Action=10&Form=30&ctx_ver=Z39.88-2004&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&rfr_id=info:sid/primo%3A010001338843&rft.genre=book&rft.btitle=The%20Women%27s%20Center%20Reid%20Lectureship,%20November%2011,%201975%20:%20papers%20by%20Alice%20W&rft.title=The%20Women%27s%20Center%20Reid%20Lectureship,%20November%2011,%201975%20:%20papers%20by%20Alice%20W&rft.au=Jordan,%20June&rft.date=[1976]&rft.place=New%20York%20:&rft.pub=Barnard%20College&rft.edition=&rft.isbn=&rft.callnumber=PS3573%20.A425%20W66%201976&rft.item_location=MARBL%20STACK&rft.barcode=010001338843&rft.doctype=RB&rft.lib=EMU
https://illiad.library.emory.edu/illiad/illiad.dll?Action=10&Form=30&ctx_ver=Z39.88-2004&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&rfr_id=info:sid/primo%3A070000157851&rft.genre=conference&rft.btitle=Quarterly%20journal%20of%20studies%20on%20alcohol.%20&rft.title=Quarterly%20journal%20of%20studies%20on%20alcohol.%20&rft.date=&rft.place=New%20Brunswick,%20N.J.%20etc.%20:&rft.pub=Journal%20of%20Studies%20on%20Alcohol,%20inc.%20%5Betc%5D&rft.edition=&rft.isbn=0033-5649&rft.callnumber=PERIODICAL%20VOL.%2015%201954&rft.item_location=LSC%20HSTORJ&rft.barcode=070000157851&rft.doctype=RB&rft.lib=LSCMAIN
app.component('prmActionListAfter',{
bindings: {parentCtrl: '<'},
controller: 'prmActionListAfterController',
template: `<div class="action-list-addon">
<div ng-if=$ctrl.display_reserve_link><a ng-href="{{$ctrl.reserves_link}}" target="_blank">
<div layout="column" layout-align="center center" class="layout-align-center-center layout-column">
<prm-icon style="z-index:1; color: rgba(0, 0, 0, 0.57);" icon-type="svg" svg-icon-set="primo-actions" icon-definition="refworks"></prm-icon>
<span class="action-list-addon-text">Place Item on Reserve</span>
</div>
</a>
</div>
<div ng-if=$ctrl.display_docdelivery_link><a ng-href="{{$ctrl.docdelivery_link}}" target="_blank">
<div layout="column" layout-align="center center" class="layout-align-center-center layout-column">
<prm-icon style="z-index:1; color: rgba(0, 0, 0, 0.57);" icon-type="svg" svg-icon-set="primo-actions" icon-definition="refworks"></prm-icon>
<span class="action-list-addon-text">Document Delivery</span>
</div>
</a>
</div>
<div ng-if=$ctrl.display_marbl_link><a ng-href="{{$ctrl.marbl_link}}" target="_blank">
<div layout="column" layout-align="center center" class="layout-align-center-center layout-column">
<prm-icon style="z-index:1; color: rgba(0, 0, 0, 0.57);" icon-type="svg" svg-icon-set="primo-actions" icon-definition="refworks"></prm-icon>
<span class="action-list-addon-text">Request from Rose Library(MARBL)</span>
</div>
</a>
</div>
<div ng-if=$ctrl.display_finding_aids_link><a ng-href="{{$ctrl.finding_aids_link}}" target="_blank">
<div layout="column" layout-align="center center" class="layout-align-center-center layout-column">
<prm-icon style="z-index:1; color: rgba(0, 0, 0, 0.57);" icon-type="svg" svg-icon-set="primo-actions" icon-definition="refworks"></prm-icon>
<span class="action-list-addon-text">Request from Rose Library(MARBL)</span>
</div>
</a>
</div>
<div style="clear:both;"></div>`
});
app.controller('prmActionListAfterController', ['$http',function($http){
var vm = this;
var mms_id;
var source_id;
var emory_item=false;
var gatech_item=false;
var re_pattern_emo=/.*01EMORY_ALMA.*/;
var re_pattern_gatech=/.*GALI_GIT_ALMA.*/;
var id_list="";
var mlen;
var i;
var j;
var k;
var rr;
var medusa_url="";
var parser;
var xml_doc;
var xnode;
var bg_patron_status="XX";
var links;
var reqnode;
var reserves_url="";
var docdelivery_url="";
var marbl_url="";
var finding_aids_url="";
var bg_user_id;
var xdata="";
var item_list;
var item_count=0;
var element_value="";
source_id=vm.parentCtrl.item.pnx.control.sourceid;
bg_user_id=vm.parentCtrl.primolyticsService.userSessionManagerService.jwtUtilService.getDecodedToken()['user'];
if (bg_user_id.length > 8){
if (bg_user_id.substr(0,10) == "anonymous-"){
bg_user_id="0";
}
}
if (re_pattern_emo.test(source_id)){
emory_item=true;
}
if (re_pattern_gatech.test(source_id)){
gatech_item=true;
}
if (emory_item){
###
# pnx.search.addsrcrecordid provides list of mms_ids
mms_id=vm.parentCtrl.item.pnx.search.addsrcrecordid;
# pnx.display.availlibrary indicates the present of ALMA physical items.
item_list=vm.parentCtrl.item.pnx.display.availlibrary;
###
if (item_list == null){
item_count=0;
}
else{
item_count=1;
}
mlen=mms_id.length;
for (i=0; i < mlen;i++){
id_list+=mms_id[i]+",";
}
if (id_list != ""){
id_list=id_list.slice(0, -1);
}
this.doc_id=id_list;
this.display_it=true;
if (item_count > 0){
medusa_url="https://kleene.library.emory.edu/cgi-bin/primo_request?doc_id="+id_list+"&user_id="+bg_user_id;
$http.get(medusa_url).then(function(response) {
xdata=response.data;
parser=new DOMParser();
xml_doc = parser.parseFromString(xdata,"text/xml");
xnode=xml_doc.documentElement.childNodes;
vm.reserves_link="";
vm.display_reserve_link=false;
vm.display_docdelivery_link=false;
for (k = 0; k < xnode.length ;k++) {
if (xnode[k].nodeName == "user_group"){
bg_patron_status=xnode[k].childNodes[0].nodeValue;
}
if (xnode[k].nodeName == "link"){
links=xnode[k].childNodes;
for (j=0; j< links.length; j++){
if (links[j].nodeName == "request_link"){
reqnode=links[j].childNodes;
if (reqnode.length > 0){
for (rr = 0; rr< reqnode.length; rr++){
if (reqnode[rr].nodeName == "reserves"){
reserves_url=reqnode[rr].childNodes[0].nodeValue;
vm.reserves_link=reserves_url;
vm.display_reserve_link=true;
}
if (reqnode[rr].nodeName == "docdelivery"){
docdelivery_url=reqnode[rr].childNodes[0].nodeValue;
vm.docdelivery_link=docdelivery_url;
vm.display_docdelivery_link=true;
}
if (reqnode[rr].nodeName == "marbl_finding_aids"){
finding_aids_url=reqnode[rr].childNodes[0].nodeValue;
vm.finding_aids_link=finding_aids_url;
vm.display_finding_aids_link=true;
}
if (reqnode[rr].nodeName == "marbl_booking"){
marbl_url=reqnode[rr].childNodes[0].nodeValue;
vm.marbl_link=marbl_url;
vm.display_marbl_link=true;
}
}
}
}
}
}
}
});
}
}
Primo view's footer contains javascript code
Javascript invokes Mehmet Celik's jQuery.PRIMO
jQuery.PRIMO provides PNX record, user.isLoggedIn() and ALMA's user_id
PNX indicates whether physical item belongs to Emory. If so, it contains ALMA's mms_id(s)
<script type="text/javascript">
jQuery(document).ready(function () {
if (typeof(Storage) !== "undefined"){
var isLoggedIn=jQuery.PRIMO.session.user.isLoggedIn();
var record_count=jQuery.PRIMO.records.length;
if (isLoggedIn ){
bg_user_id=jQuery.PRIMO.session.user.id;
}
else{
bg_user_id="0";
}
re_pattern_gatech=new RegExp(".*GALI_GIT_ALMA.*"),
re_pattern_emo=new RegExp(".*01EMORY_ALMA.*"),
var gatech_item=false;
var emory_item=false;
if (record_count == 1){
pnx=jQuery.PRIMO.records[0].getPNX();
parser=new DOMParser();
xmlDoc = parser.parseFromString(pnx,"text/xml");
x = xmlDoc.documentElement.childNodes;
txt="";
for (i = 0; i < x.length ;i++) {
if (x[i].nodeName == "search"){
search_node=x[i].childNodes;
for (j=0; j< search_node.length;j++){
if (search_node[j].nodeName == "addsrcrecordid"){
record_id=search_node[j].childNodes[0].nodeValue;
if (merged_record_id == ""){
merged_record_id=record_id;
}
else{
merged_record_id=merged_record_id+","+record_id;
}
}
}
}
if (x[i].nodeName == "control"){
control_node=x[i].childNodes;
for (j=0; j< control_node.length;j++){
if (control_node[j].nodeName == "sourceid"){
source_id=control_node[j].childNodes[0].nodeValue;
if (re_pattern_emo.test(source_id)){
emory_item=true;
}
if (re_pattern_gatech.test(source_id)){
gatech_item=true;
}
}
}
}
}
});
if (emory_item ){
medusa_url="https://kleene.library.emory.edu/cgi-bin/display_custom_request?doc_id="+merged_record_id+"&user_id="+bg_user_id;
item_list_url="https://kleene.library.emory.edu/cgi-bin/alma_item_list?doc_id="+merged_record_id;
jQuery.ajaxSetup({async:false});
jQuery.get(medusa_url,function(xdata){
parser=new DOMParser();
xml_doc = parser.parseFromString(xdata,"text/xml");
xnode=xml_doc.documentElement.childNodes;
for (k = 0; k < xnode.length ;k++) {
if (xnode[k].nodeName == "user_group"){
bg_patron_status=xnode[k].childNodes[0].nodeValue;
}
if (xnode[k].nodeName == "request_link"){
request_url=xnode[k].childNodes[0].nodeValue;;
}
}
},"text");
// placing the link in the "Get it" tab.
if (request_url != ""){
this_url="<a href="+request_url+" target=\"_blank\" id=\"reserves_bg\" title=\"Request this title\">Get it</a>";
$('#exlidResult0-TabsList').append('<li id="EULAresLink" class="EXLResultTab">'+this_url+'</li>');
}
}
<result>
<code>OK</code>
<request_link>https://kleene.library.emory.edu/cgi-bin/process_custom_request?doc_id=990030289410302486,990031945630302486,990030842310302486&user_id=0036487&req_type=alma_booking,hold,reserves</request_link>
<user_group>01</user_group>
</result>
Example request menu
Screenshots
Note: webservice "display_custom_request" supports OPTIONS method to deal with CORS
POST https://api-na.hosted.exlibrisgroup.com/almaws/v1/users/nnnn/requests?apikey=your-api-key&user_id_type=all_unique&item_pid=23249592970002466
<?xml version="1.0" encoding="UTF-8"?>
<user_request>
<request_type>HOLD</request_type>
<pickup_location_type>LIBRARY</pickup_location_type>
<pickup_location_library>MUSME</pickup_location_library>
</user_request>
POST https://api-na.hosted.exlibrisgroup.com/almaws/v1/users/nnnn/requests?item_pid=23319409310002486&apikey=your-api-key <?xml version="1.0" encoding="UTF-8"?>
<user_request>
<request_type>BOOKING</request_type>
<pickup_location_type>LIBRARY</pickup_location_type>
<pickup_location_library>MUSME</pickup_location_library>
<booking_start_date>2017-07-13T09:00:00-04:00</booking_start_date>
<booking_end_date>2017-07-13T14:00:00-04:00</booking_end_date>
</user_request>