From f0854661ef1e7a923d9f8a6a401eeeac8984ab84 Mon Sep 17 00:00:00 2001 From: Jesse Weaver <pianohacker@gmail.com> Date: Thu, 16 May 2013 17:11:15 -0600 Subject: [PATCH] Bug 10320 - Integrate OverDrive search into OPAC (1/2) This show results from the OverDrive ebook/audiobook service in the OPAC. To test: 1) Fill in the OverDrive system preferences (you will need an Overdrive client key, client secret and library ID). 2) Perform a search on the OPAC, verify that, if available, a link to results from OverDrive will appear at the top. 3) Click on the link, verify that the list of results appears. --- C4/External/OverDrive.pm | 141 +++++++++++++++++ .../admin/preferences/enhanced_content.pref | 11 ++ koha-tmpl/opac-tmpl/prog/en/css/opac.css | 9 ++ koha-tmpl/opac-tmpl/prog/en/js/overdrive.js | 61 ++++++++ .../prog/en/modules/opac-overdrive-search.tt | 161 ++++++++++++++++++++ .../opac-tmpl/prog/en/modules/opac-results.tt | 28 +++- koha-tmpl/opac-tmpl/prog/images/Star0.gif | Bin 0 -> 1360 bytes koha-tmpl/opac-tmpl/prog/images/Star1.gif | Bin 0 -> 1395 bytes koha-tmpl/opac-tmpl/prog/images/Star2.gif | Bin 0 -> 1410 bytes koha-tmpl/opac-tmpl/prog/images/Star3.gif | Bin 0 -> 1391 bytes koha-tmpl/opac-tmpl/prog/images/Star4.gif | Bin 0 -> 1380 bytes koha-tmpl/opac-tmpl/prog/images/Star5.gif | Bin 0 -> 1284 bytes opac/opac-overdrive-search.pl | 47 ++++++ opac/opac-search.pl | 6 + opac/svc/overdrive_proxy | 83 ++++++++++ 15 files changed, 545 insertions(+), 2 deletions(-) create mode 100644 C4/External/OverDrive.pm create mode 100644 koha-tmpl/opac-tmpl/prog/en/js/overdrive.js create mode 100644 koha-tmpl/opac-tmpl/prog/en/modules/opac-overdrive-search.tt create mode 100644 koha-tmpl/opac-tmpl/prog/images/Star0.gif create mode 100644 koha-tmpl/opac-tmpl/prog/images/Star1.gif create mode 100644 koha-tmpl/opac-tmpl/prog/images/Star2.gif create mode 100644 koha-tmpl/opac-tmpl/prog/images/Star3.gif create mode 100644 koha-tmpl/opac-tmpl/prog/images/Star4.gif create mode 100644 koha-tmpl/opac-tmpl/prog/images/Star5.gif create mode 100755 opac/opac-overdrive-search.pl create mode 100755 opac/svc/overdrive_proxy diff --git a/C4/External/OverDrive.pm b/C4/External/OverDrive.pm new file mode 100644 index 0000000..91316c0 --- /dev/null +++ b/C4/External/OverDrive.pm @@ -0,0 +1,141 @@ +package C4::External::OverDrive; + +# Copyright (c) 2013 ByWater +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or ( at your option ) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use strict; +use warnings; + +use JSON; +use Koha::Cache; +use HTTP::Request; +use HTTP::Request::Common; +use LWP::Authen::Basic; +use LWP::UserAgent; + +BEGIN { + require Exporter; + our $VERSION = 3.07.00.049; + our @ISA = qw( Exporter ) ; + our @EXPORT = qw( + IsOverDriveEnabled + GetOverDriveToken + ); +} + +sub _request { + my ( $request ) = @_; + my $ua = LWP::UserAgent->new( "Koha " . C4::Context->KOHAVERSION ); + + my $response; + eval { + $response = $ua->request( $request ) ; + }; + if ( $@ ) { + warn "OverDrive request failed: $@"; + return; + } + + return $response; +} + +=head1 NAME + +C4::External::OverDrive - Retrieve OverDrive content availability information + +=head2 FUNCTIONS + +This module provides content search for OverDrive, + +=over + +=item IsOverDriveEnabled + +Returns 1 if all of the necessary system preferences for OverDrive are set. + +=back + +=cut + +sub IsOverDriveEnabled { + return ( + C4::Context->preference( 'OverDriveClientKey' ) && + C4::Context->preference( 'OverDriveClientSecret' ) + ); +} + +=item GetOverDriveToken + +Fetches an OAuth2 auth token for the OverDrive API, reusing an existing token in +Memcache if possible. + +Returns the token ( as "bearer ..." ) or undef on failure. + +=back + +=cut + +sub GetOverDriveToken { + my $key = C4::Context->preference( 'OverDriveClientKey' ); + my $secret = C4::Context->preference( 'OverDriveClientSecret' ); + + return unless ( $key && $secret ) ; + + my $cache; + + eval { $cache = Koha::Cache->new() }; + + my $token; + $cache and $token = $cache->get_from_cache( "overdrive_token" ) and return $token; + + my $request = HTTP::Request::Common::POST( 'https://oauth.overdrive.com/token', [ + grant_type => 'client_credentials' + ] ) ; + $request->header( Authorization => LWP::Authen::Basic->auth_header( $key, $secret ) ); + + my $response = _request( $request ) or return; + if ( $response->header('Content-Type') !~ m!application/json! ) { + warn "Could not connect to OverDrive: " . $response->message; + return; + } + my $contents = from_json( $response->decoded_content ); + + if ( !$response->is_success ) { + warn "Could not log into OverDrive: " . ( $contents ? $contents->{'error_description'} : $response->decoded_content ); + return; + } + + $token = $contents->{'token_type'} . ' ' . $contents->{'access_token'}; + + # Fudge factor to prevent spurious failures + $cache->set_in_cache( 'overdrive_token', $token, $contents->{'expires_in'} - 5 ); + + return $token; +} + +1; +__END__ + +=head1 NOTES + +=cut + +=head1 AUTHOR + +Jesse Weaver <pianohacker@gmail.com> + +=cut diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/enhanced_content.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/enhanced_content.pref index ba6a6ac..7a0cdcb 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/enhanced_content.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/enhanced_content.pref @@ -326,3 +326,14 @@ Enhanced Content: yes: Enable no: "Don't enable" - the ability to use Koha Plugins. Note, the plugin system must also be enabled in the Koha configuration file to be fully enabled. + OverDrive: + - + - Include OverDrive availability information with the client key + - pref: OverDriveClientKey + - and client secret + - pref: OverDriveClientSecret + - . + - + - "Show items from the OverDrive catalog of library #" + - pref: OverDriveLibraryID + - . diff --git a/koha-tmpl/opac-tmpl/prog/en/css/opac.css b/koha-tmpl/opac-tmpl/prog/en/css/opac.css index 5e5d2f4..2d89755 100644 --- a/koha-tmpl/opac-tmpl/prog/en/css/opac.css +++ b/koha-tmpl/opac-tmpl/prog/en/css/opac.css @@ -2960,3 +2960,12 @@ padding: 0.1em 0; .notesrow textarea { width: 100%; } + +#overdrive-results { + font-weight: bold; + padding-left: 1em; +} + +.throbber { + vertical-align: middle; +} diff --git a/koha-tmpl/opac-tmpl/prog/en/js/overdrive.js b/koha-tmpl/opac-tmpl/prog/en/js/overdrive.js new file mode 100644 index 0000000..0a04a0b --- /dev/null +++ b/koha-tmpl/opac-tmpl/prog/en/js/overdrive.js @@ -0,0 +1,61 @@ +if ( typeof KOHA == "undefined" || !KOHA ) { + var KOHA = {}; +} + +KOHA.OverDrive = ( function() { + var proxy_base_url = '/cgi-bin/koha/svc/overdrive_proxy'; + var library_base_url = 'http://api.overdrive.com/v1/libraries/'; + return { + Get: function( url, params, callback ) { + $.ajax( { + type: 'GET', + url: url.replace( /https?:\/\/api.overdrive.com\/v1/, proxy_base_url ), + dataType: 'json', + data: params, + error: function( xhr, error ) { + try { + callback( JSON.parse( xhr.responseText )); + } catch ( e ) { + callback( {error: xhr.responseText || true} ); + } + }, + success: callback + } ); + }, + GetCollectionURL: function( library_id, callback ) { + if ( KOHA.OverDrive.collection_url ) { + callback( KOHA.OverDrive.collection_url ); + return; + } + + KOHA.OverDrive.Get( + library_base_url + library_id, + {}, + function ( data ) { + if ( data.error ) { + callback( data ); + return; + } + + KOHA.OverDrive.collection_url = data.links.products.href; + + callback( data.links.products.href ); + } + ); + }, + Search: function( library_id, q, limit, offset, callback ) { + KOHA.OverDrive.GetCollectionURL( library_id, function( data ) { + if ( data.error ) { + callback( data ); + return; + } + + KOHA.OverDrive.Get( + data, + {q: q, limit: limit, offset: offset}, + callback + ); + } ); + } + }; +} )(); diff --git a/koha-tmpl/opac-tmpl/prog/en/modules/opac-overdrive-search.tt b/koha-tmpl/opac-tmpl/prog/en/modules/opac-overdrive-search.tt new file mode 100644 index 0000000..4ba9f8d --- /dev/null +++ b/koha-tmpl/opac-tmpl/prog/en/modules/opac-overdrive-search.tt @@ -0,0 +1,161 @@ +[% INCLUDE 'doc-head-open.inc' %] +[% IF ( LibraryNameTitle ) %][% LibraryNameTitle %][% ELSE %]Koha online[% END %] catalog › OverDrive search for '[% q | html %]' +[% INCLUDE 'doc-head-close.inc' %] +<script type="text/javascript" src="[% themelang %]/js/overdrive.js"></script> +<script type="text/javascript"> +var querystring = "[% q |replace( "'", "\'" ) |replace( '\n', '\\n' ) |replace( '\r', '\\r' ) |html %]"; +var results_per_page = [% OPACnumSearchResults %]; + +function fetch_availability( prod, $tr ) { + var $availability_summary = $( '<span class="results_summary"></span>' ); + $tr.find( '.info' ).append( $availability_summary ); + $availability_summary.html( '<span class="label">Availability: </span> Loading...' ); + + KOHA.OverDrive.Get( + prod.links.availability.href, + {}, + function ( data ) { + if ( data.error ) return; + + $availability_summary.html( '<span class="label">Copies available: </span><span class="available"><strong>' + data.copiesAvailable + '</strong> out of ' + data.copiesOwned + '</span>' ); + + if ( data.numberOfHolds ) { + $availability_summary.find( '.available' ).append( ', waiting holds: <strong>' + data.numberOfHolds + '</strong>' ); + } + + $tr.find( '.info' ).append( '<span class="results_summary actions"><span class="label">Actions: </span><a href="http://' + prod.contentDetails[0].href + '" ' + ( data.copiesAvailable ? ' class="addtocart">Check out' : ' class="hold">Place hold' ) + '</a></span>' ); + } + ); +} + +function search( offset ) { + $( '#overdrive-status' ).html( 'Searching OverDrive... <img class="throbber" src="/opac-tmpl/lib/jquery/plugins/themes/classic/throbber.gif" /></span>' ); + + KOHA.OverDrive.Search( "[% OverDriveLibraryID %]", querystring, results_per_page, offset, function( data ) { + if ( data.error ) { + $( '#overdrive-status' ).html( '<strong class="unavailable">Error searching OverDrive collection.</strong>' ); + return; + } + + if ( !data.totalItems ) { + $( '#overdrive-status' ).html( '<strong>No results found in the library\'s OverDrive collection.</strong>' ); + return; + } + + $( '#results tbody' ).empty(); + + $( '#overdrive-status' ).html( '<strong>Found ' + data.totalItems + ' results in the library\'s OverDrive collection.</strong>' ); + + for ( var i = 0; data.products[i]; i++ ) { + var prod = data.products[i]; + var results = []; + + results.push( '<tr>' ); + + results.push( '<td class="info"><a class="title" href="http://', prod.contentDetails[0].href, '">' ); + results.push( prod.title ); + if ( prod.subtitle ) results.push( ', ', prod.subtitle ); + results.push( '</a>' ); + results.push( '<p>by ', prod.primaryCreator.name, '</p>' ); + results.push( '<span class="results_summary"><span class="label">Type: </span>', prod.mediaType, '</span>' ); + if ( prod.starRating ) results.push( '<span class="results_summary"><span class="label">Average rating: <img src="[% themelang %]/../images/Star', Math.round( parseInt( prod.starRating )), '.gif" title="" style="max-height: 15px; vertical-align: bottom"/></span>' ); + results.push( '</td>' ); + + results.push( '<td>' ); + if ( prod.images.thumbnail ) { + results.push( '<a href="http://', prod.contentDetails[0].href, '">' ); + results.push( '<img class="thumbnail" src="', prod.images.thumbnail.href, '" />' ); + results.push( '</a>' ); + } + results.push( '</td>' ); + + results.push( '</tr>' ); + var $tr = $( results.join( '' )); + $( '#results tbody' ).append( $tr ); + + fetch_availability( prod, $tr ); + } + + $( '#results tr:odd' ).addClass( 'highlight' ); + + var pages = []; + var cur_page = offset / results_per_page; + var max_page = Math.floor( data.totalItems / results_per_page ); + + if ( cur_page != 0 ) { + pages.push( '<a class="nav" href="#" data-offset="' + (offset - results_per_page) + '"><< Previous</a>' ); + } + + for ( var page = Math.max( 0, cur_page - 9 ); page <= Math.min( max_page, cur_page + 9 ); page++ ) { + if ( page == cur_page ) { + pages.push( ' <span class="current">' + ( page + 1 ) + '</span>' ); + } else { + pages.push( ' <a class="nav" href="#" data-offset="' + ( page * results_per_page ) + '">' + ( page + 1 ) + '</a>' ); + } + } + + if ( cur_page < max_page ) { + pages.push( ' <a class="nav" href="#" data-offset="' + (offset + results_per_page) + '">Next >></a>' ); + } + + if ( pages.length > 1 ) $( '#top-pages, #bottom-pages' ).find( '.pages' ).html( pages.join( '' ) ); + } ); +} + +$( document ).ready( function() { + $( '#breadcrumbs p' ) + .append( ' ' ) + .append( '<span id="overdrive-status"></span>' ); + + $( document ).on( 'click', 'a.nav', function() { + search( $( this ).data( 'offset' ) ); + return false; + }); + + search( 0 ); +} ); +</script> +<style> +.actions a.addtocart { + display: inline; +} +</style> +</head> +<body> +[% IF ( OpacNav ) %] +<div id="doc3" class="yui-t1"> +[% ELSE %] +<div id="doc3" class="yui-t7"> +[% END %] + <div id="bd"> +[% INCLUDE 'masthead.inc' %] + + <h1>OverDrive search for '[% q | html %]'</h1> + <div id="breadcrumbs"> + <p></p> + </div> + + <div id="yui-main"><div class="yui-b searchresults"> + <div id="top-pages"> + <div class="pages"> + </div> + </div> + <table id="results"> + <tbody> + </tbody> + </table> + <div id="bottom-pages"> + <div class="pages"> + </div> + </div> + </div></div> + +[% IF ( OpacNav ) %] +<div class="yui-b"><div id="opacnav" class="container"> +[% INCLUDE 'navigation.inc' %] +</div></div> +[% END %] + + +</div> +[% INCLUDE 'opac-bottom.inc' %] diff --git a/koha-tmpl/opac-tmpl/prog/en/modules/opac-results.tt b/koha-tmpl/opac-tmpl/prog/en/modules/opac-results.tt index 85a4d45..ca22b88 100644 --- a/koha-tmpl/opac-tmpl/prog/en/modules/opac-results.tt +++ b/koha-tmpl/opac-tmpl/prog/en/modules/opac-results.tt @@ -13,6 +13,7 @@ [% IF ( OpacStarRatings == 'all' ) %]<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.rating.js"></script> <link rel="stylesheet" type="text/css" href="[% themelang %]/css/jquery.rating.css" />[% END %] +<script type="text/javascript" src="[% themelang %]/js/overdrive.js"></script> <script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.checkboxes.min.js"></script> [% IF ( OpacHighlightedWords ) %]<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.highlight-3.js"></script> [% END %]<script type="text/javascript"> @@ -247,7 +248,10 @@ $(document).ready(function(){ [% END %] $("#holdDetails").hide(); -[% IF ( query_desc ) %][% IF ( OpacHighlightedWords ) %]var query_desc = "[% query_desc |replace("'", "\'") |replace('\n', '\\n') |replace('\r', '\\r') |html %]"; +[% IF ( query_desc ) %] + var query_desc = "[% query_desc |replace("'", "\'") |replace('\n', '\\n') |replace('\r', '\\r') |html %]"; + var querystring = "[% querystring |replace("'", "\'") |replace('\n', '\\n') |replace('\r', '\\r') |html %]"; + [% IF ( OpacHighlightedWords ) %] q_array = query_desc.split(" "); // ensure that we don't have "" at the end of the array, which can // break the highlighter @@ -256,7 +260,27 @@ $(document).ready(function(){ } highlightOn(); $("#highlight_toggle_on" ).hide().click(function() {highlightOn() ;}); - $("#highlight_toggle_off").show().click(function() {highlightOff();});[% END %][% END %] + $("#highlight_toggle_off").show().click(function() {highlightOff();}); + [% END %] + [% IF ( OverDriveEnabled ) %] + var $overdrive_results = $( '<span id="overdrive-results">Searching OverDrive... <img class="throbber" src="/opac-tmpl/lib/jquery/plugins/themes/classic/throbber.gif" /></span>' ); + $( '#breadcrumbs p' ).eq(0) + .append( ' ' ) + .append( $overdrive_results ); + KOHA.OverDrive.Search( "[% OverDriveLibraryID %]", querystring, 1, 0, function( data ) { + if ( data.error ) { + $overdrive_results.html( 'Error searching OverDrive collection' ); + return; + } + + if ( data.totalItems ) { + $overdrive_results.html( 'Found <a href="/cgi-bin/koha/opac-overdrive-search.pl?q=' + escape( querystring ) + '">' + data.totalItems + ' results</a> in OverDrive collection' ); + } else { + $overdrive_results.remove(); + } + } ); + [% END %] +[% END %] [% IF ( TagsInputEnabled && loggedinusername ) %] $("#tagsel_tag").click(function(){ diff --git a/koha-tmpl/opac-tmpl/prog/images/Star0.gif b/koha-tmpl/opac-tmpl/prog/images/Star0.gif new file mode 100644 index 0000000000000000000000000000000000000000..44ffdf488560eebbf1f8a5db485fabe581db9625 GIT binary patch literal 1360 zcmW+#dr*{B6u&wqk14g)nX*VzN^ONQZ;1<v3Wdm&qN$aMgR)!!6D$>5lbw+Tc?E;3 zLD#52O_$V^9*)WkqwO@QbD?!KQy%MEWCe@R)@7yB@9zEY%sJ=xI^Q>UUcz+KN;4b9 zyo`~4ibxU(B2L6~r4%ViN|55Dm~Ki%Qj!!TIZ38xg(4wI2ojtG)2mz&m&65ePMiUW zm?9>L31XZW3Sa{oSXjn1(>(e~rG!#kDW+*rNvWhzk}Juy3N(dOLMXwNU|JMrxl~*z z&J}0Sh*ia;VnQ*l7+9eiU=R(JkcA$IfemP2VVP+|y2cYyaw(bi2U=7@NWrCG+AdU} z$%W)xat8Mhg;~jjWLz@XLmrTcZNvfp5CMl!4KRp?N{m7Wh=C1gU@@JXZllKILNFa9 z{DBq~=Ylij3cFB&reIt!<cOpZ0CZs-LBlPgL>`cdZNvdhFbs!K4KRqtCS*Yeh=C1g zrcb1&(MFBOa4`f4f1pLhnf^F*u7P{&X5fT6=D=0-Y#?ZzdLTQ!8i+$5A>dFW1w`wc z1axTN05m=aTnXX9Q-G?8=x3tui4GARAo`kUD^Vd)5m6~o1(BVoYTywLyPIe~Q9aQ? zqQgW#5H%7xANjTp__q!8mkjtT{9e2N-vZx1h5iQ>5ARvMp3V1ftnqbz)ay3iYhKcI zYM$qK!i|F_eEQ>jLk7Vpi+~2E{|Och(;;DrM`};mf=3#cpFUFOEE*A>RFl~2exuCL zo6`RD!WE{HF>|cl(+#T<i@itJI`2%ml~nw2@$L9}dwP8Jw9!>*MTgAJuV#)L{Gfiv z#B+u5V_VN(9M*QcCZu{=o@dI~clIYgne?k?-Q+(Cymu@Ywne5y<=(!2u5o`u>ZOMC z-}j}oRi(y$`rz#L*UOf!jm;Z+#Z%r^ot@fX^R=XZknE2BuxEVEvlHjfKDx18g{+(F zdewYpXJpz%WvT5v-813crW(uo&fgqM%Cb|IUCneInA>Q5?rNCn?6s45{;KS#oGT{_ zM@D6DPWkJ~_59(ltZ4N0-8?Vbaw}$>_}CM4tDe?)W0oJwasRaOfZMXg*VpxD_^1We z8q0mp<u7w{##;aLJ?ePs8|%K6&hky}Ezahc&@*Yz=Z8-HqS6*LwEAlP6R{a93q~&u zZYea3-#I+GJF&BzbzIo!Dt<9^)2h<YYw^2FCJn9{QTEy|pWOKD<=u`|sjV6DEvEKM zt6SbmeavLuel4uNa!H&a^96fw$DX}A-(F;?{CwfmF7x8MM|L!uI@Z}*s+Sd-gj}7K zSvfPyo;hJx(5aX9)vPs|J9n)=@xE)u<qdtqV*8JU&yTVihB#^qo+)+2l{M{IP**e| zI(cvT($LIW+oJxAxW6Aya>e@IGbTHX_AJk=y4Fb}W_@dnEburQ@{OL^-x+Ua%^A2; lntV8<$5uJ3Y0Qv_xJJW<h&R==yWKy!5+)ytA2f)?{0}p#!-fC= literal 0 HcmV?d00001 diff --git a/koha-tmpl/opac-tmpl/prog/images/Star1.gif b/koha-tmpl/opac-tmpl/prog/images/Star1.gif new file mode 100644 index 0000000000000000000000000000000000000000..203863874dcba824ea7b948e0ef00c750cefce19 GIT binary patch literal 1395 zcmW+#dr*{B6u*citjX$%PLY!4Bo5PL<1G|vVb<}1Ha;3lY6caOxr&(OqfB<N;icjx zuIQMz40I_(WsO8p2{Q-NY_6*bga)Xv$ZHLTz(HK`^t*fiJ9EzYz0UW|n?H9(WMUEv zX8nwjeu_vE2_jC!bfpw2NlK97q?m3>MN*O!Bsoc@XN4jmNeB|01k<Zr5tqaTaZa28 ziI^fLi3wty7z$tm8dzAyG}Ao#Nu`8RTq&k$QAw$!P?9Ujv<ftZR6;1hm0(&FX1P>c zD9#mU(1=yVq+&ubt{7OM8ek9&m5_xVh=C1gU}2eQL%PNjQgSJo_6J&2LP)`-VA?KJ zpvi^gTyh5Y5QSOEgk)SY*h3zWiEYFI{}2I(Pz^AMhDwY=2Z(_UXkam&oNlAW<3cbU zB>aIE73YF8<O;h`fu>+wFyx4&5dd^y96`e^qC_5$iEYFIO)w0HPz^AM#wKJz2Z(_U zXr@o3r_n}@$8a$O34fqP#hLy%bgsU8>!$C7I%eNh^sFyvow_eOz3PiYAHnBPBl$$@ zoA`8SU>`I-2V4o^!IO{bB+&_?bfRpcT|~J=c|->WpW>4FM1@4fL{6epL}!R9iK?Cs zY<oJ8G3ebs_#|i0o9pf0`=mG9`zZHu*S7x7)IN90K--4iyB~Y5zTNFw&{G}fIsdBX z;+%$~VJ)X4>JEk8*f|QH{=|WyelW^jhX$tq2^Ij;f#YIJ4`0d*7#$w}N9mEO9U~_# zI260rQJK>_dBy&7qa0?dsWE+c<TOWYo~gUhe_%;y<i_zYyR7q#j&~16th|!6#`sF~ zfuKv@N5xHRzv&o}ZOSeV?wDom?`*2RdL!|jb?vU*_0k@?%o#sw#r2BX&bm{Em|4G{ z-54IT%^UQmHO!P!{#o1QGuE)Ft1PKK_tVR4aotg=k6&;(ZC&f{UPv2LJ-*@Gn@Jb_ z>;A5<{LgJpd%wxFIj(D5MRiN%=c5x&#z$-zQnUMww2<YUwa%5pW2e`@wYIiodDQ{4 z{q(2xzil#34LGx6wROPJRv2=2LI-pIdaSkFX)V9~{7<Xt-rcnNEzM;=r*vOwjQY~^ zpl08eJ^2Z}du|=IMYji?_+ouSa^}Jrk9M~fOlRvSO-K)Tw8Qn4U(kmM8-`6jcR$O& zr)uoxTOCW6XSO9(<!7`y3++)8bBh<|gcZFw!4`OAO0(_G!D4&#<CgwqF^0dmD{`!1 zWRMtky?2$psixY!*UvEj2TSyjJ1urYPhp<<@SLm0J%QIUM?~EFdf$kQ=9-$?ZxVB_ zEtH0>fkg+CzRj_Jmwe@;2j+F}Jr@%5Y)bIr<kp>|7e%;B<;{Y&p)=PU*%+Et?8piU zAA2}s?mxvAZ-akb^k83^vA}R))uI_5wRG{!#;i@r7DryiOUI9IUovy;v7<#5;TGpD zgU9LruRF!^tWS=N`q7kSG*=pCm*n|(WR}djY}&f+6wOX~ST<(j<r2%2Df@3*K6vf) aCzi%twk7B96rJd*cv$W^`HP<)i~1ik=F+49 literal 0 HcmV?d00001 diff --git a/koha-tmpl/opac-tmpl/prog/images/Star2.gif b/koha-tmpl/opac-tmpl/prog/images/Star2.gif new file mode 100644 index 0000000000000000000000000000000000000000..2b60940ffc660d254c308ebf1df7e94c59c0f600 GIT binary patch literal 1410 zcmW+#drZ|;96wCRdzpfUOty@vOmX;N?c}%$VS<X<NJ}Qek;2U5Yo^4b0cvBHhnnI8 z9~}bnF-asqB{wK28U!wnz(OSzNG^K0B64#VMbPfI&$;Kn@4nyf=k@*lqE@XiElT{H zO<^O9k$#Fu5(y$s#B`+;DM?C@;-r{vN<~tV6eKxGre}pBAxQ`loCMRWToISV1#wQC z0g0F*CW#4RoEQpV0~%OZ#x&DB`bnjPQd}vfX;De3q)?J8$+QYIg;YW)!IfZI6lS?p zTqw>JXV8dM#iU|FF|HU`p&DQi4V93E9*BVrXkcNPX+yfk6H;<1nf3=-R6<C>rC{1F zRG`U)<XmzF_Yj3y$%JHFGT1{Nkcn-?0sjyIhfobLh=xjxLI;R}4QOC7ot$o?#^XXT z9VGmL78U1$Gvo@pP=TgkTrlK_q!9pgVH`ojEuusokcn-?0ZlLrhfobLh{h&lK?jI| z4QQrMq^HqFjmK~?1POnjMa7x^ICQSAd+Vm_ggR!|RrIVYXq~z%JH6_PLm$EAP$RiS z>zlZAXkZsKJ_lS0;lY!Os*)(n>C7ePA#&yuo%rt!ZdgKef#@<(1yMCo9Z}<((X7`a z$H#_qUcbm6d7A0y+cDao^r9!het(nwcC_Q~xF^kv9UUu%T36Yd=RR*=KTtD$uqn#^ z=UU6P`CX;6t=CNhb;~-7{M!yp#-~4H)ZGn6**s`q`k!D1m^MyZdFAZyW`j>q{EaK; z8uKOvtS(x4)zXyeb7#v*zhM8Uqm$dx3+9}7bmM@-nzi@aFSj(NTAHq!FD)o-&Rh6L zcZ#34f7qD`na*t6k!iIBUf=l7+A(E&dwRJ2S)b9jBqne0%Bg^<Ux>)m4Yfn@hP0hE zSI37on}dQkez1SkGBf7p-TCpgZ`~{Vs6V>AX7{6pq{x=m;-#s!Wn&)m{E8PJ_Lw|( z!AQuz4_iF?>dl>%)nOUKmfl}lc5e#b=e{uc<%A9IZ|}G?zT};#%A#FK-8DwPkOk9B zZdOBU<-X97p`PE&H%oVYcP**s*Q~AkJBRqTjF;EW<QEtIoZ5cx*)9q(g*Mo`9{f}` zHLyNm)Y0`<)$ZWEMd@y~J3BX5g*ocdI&%VjzjC@6Hl~f6nUcT0JEb~0<6eRz`s<<h zY}2#f{&Y@Jj`t3q-fZKuaYs{opGTfuI`hP(?t?=~zTfBF&%SUZqQAzFc$m+Q*=!0H z=DvuL+vd;GUJrJ7AB)J1eRSMd;p4UFT@la_a%@IZYr!WU$30#e8(}<m+VgR8Ti9?{ z@|yS+xr>C{YAp~EaftyRntDx!{8&TQtia+lOZu^r<h!-EiW4pCyu&X(Ust#!$G^~a z-m@(7f$8Di$iQ>PRja)&o*uLICl;*Fc=ChCV{=>hn5|>YlCi<YZ4ssCH(d@YuMEm= zHT|=#IzQC9$1$<Yy}R(~;-|x7<rOsp8-A>6@H#ng&{=n=z1m}W`Pz)!i7DQpw>(ew uoPM<@v!c>H=Rw7CpF8IE7~^&Gq$QrUnUm^lcX!(BUs=nmmb<yJ(EkB}4Az1G literal 0 HcmV?d00001 diff --git a/koha-tmpl/opac-tmpl/prog/images/Star3.gif b/koha-tmpl/opac-tmpl/prog/images/Star3.gif new file mode 100644 index 0000000000000000000000000000000000000000..3ff6739cd12809662b6a09abdd29d70b839197d8 GIT binary patch literal 1391 zcmW+#ZBUg(6kY=nEF32cK|n?2V=(!%2`F4ZP=O}I8Fk2sB0v@ikWxs+A_p8&Qogcu z^MaBJgh4Q3e5ehEnlXC;gGp|R4%|R7#0wWfFx{TL`|r#-=lMGC8y2zAZ%y0|vVcqx zBGnU8aw(aVR7#>MA*A3^Fe#`ML^ZjPoJ-Cmr;-yj%Y<ZHGA0?7jHp#AB;}GaNvWg+ zNRkLixFk#xDhUc;0~%OZ#xzkp>Tw~M5L5`FXi;%4I1`)-PLv8X85fKRMg=2E6lN(G zlnF`&CD4dfLAW4H5Gn{*p&DQi4V93E9*BVrXkcNPC_}2oW1LgYiSh?pRE%*(IU~w0 zRG>*2r<79y_Yj3yP8cVY6WBu@kcn-?0sjyIhfobLh=xjxLI;R}4QOBym7Hp$#-of8 z6(szD78Rw8666ZIP=O{Rlo8~Jq!9pgVH`ojEuusokcn-?0ZlLrhfobLh{h&lK?jI| z4QQfHq^8kEjYn`X1POnjMMa7FI8?6Yd#k4Tgeqq9Rn)9GXqCD-JGE+#Lmk2FP$8K` ztDBg0C}1-*J_lS0;lUHAUcNXaUmlgRM9MNL&q`S><xf&JNqO!0RLP7fcXBZ0+3>!} z`yW2-i8S?YH@16^>3oKN^EY*Ge4>jOZSx%db@iBt80*|Na&`G&%UdHi)<3-!HGFBw zgIX^`!>Y$uH{3n%_IJI{&?Wyn+QnV@cH-cx`1D<;EoZ?fc@r9l`X`7rOxrkz*PXeR zYi;KhbE~er?aTSg-`0lL53J9$`#te^)x3o31<sm~*F1ukMI0IFZT{-y?!nN^Rjwtb zp-4j;U+r4$zqjLjTd5{<d_upu)%Rn!C5tx9l(=;6AJ1N^nX~P2ca?2Yj>R3FduUC} z)ny~rrh6?JMq}UQr1aFV%B1evw3MP$ebYz!KitAMIY*?&G=6UQHqyEI6RYPhZ3CK& z{W*_C>B_MEE-jyqKDyo37!@0sQ|uA_i+2B>hn*IoTfCDTFS3p@n>>pvZ)knf^hdM) zxfyxTPv6jZzvD&P-lq@F=;}A^ZGQhs)}--9-jDyKnjYS+^oY%NY02nmwV059<~g03 z-t|hIqjuE?WIA=IzC1d;%jNR!N8Smtper!@#r@>yqA#Y`EL^&8w$7^KfHBUX^O>lb z<B&U~aVTFmesb_$wsm|}T!6FJ@&2_=S$E=cViLR0e73Om#6W&u!_KY%qr<y}0nd#W z=O6PKs7eSKHy$o6oLk(j^Y!}9qt)lI+m00_w%+;`Ayajx0)La(6Qx<>wRP~D?Ys1e zA+hm4xSa@EcIa!;myzq_yJc|$JC%Mjxl1$Any@HPJ84KPJMiyxWtn!Yx$v~j5rfV* zH?7;<H{S7_=h=IH?eCqNtq<)CoX%*E3Y=M*UsR#Znp<5pr}(erz|(ovMSc&0-|_44 z>)m<Lw!-q7*UFlo2i6v8O6_|JgXYe<+h2FX%71gvEqBXUt?{VgLU8Bn3!XICM`nB< feBFKPypU&}6HnIlvy{>1M->-FTkL0B694}JB*n>K literal 0 HcmV?d00001 diff --git a/koha-tmpl/opac-tmpl/prog/images/Star4.gif b/koha-tmpl/opac-tmpl/prog/images/Star4.gif new file mode 100644 index 0000000000000000000000000000000000000000..473cb32087da78d40ebd8968a542b0bc50b76c55 GIT binary patch literal 1380 zcmW+#dr*{B6u;;yU^Oz@IOQ-9g-8-ibsKQoz!7PYkjJPLBacyos}4kg;ZpWcUW$!^ zNyH@u9Yeqwlu|-eQrbYl%fnD?f<%Eua9bX+kdbox-M#;vIp_Rd=lg;~^eeSn63DaU zA3~&hLP{<rlafkFR3(HITnZ)ym4c`y7m{<yndDS*qGp+pj7!EOqmmJ|N`<6cQYI;t zlmJN*Aqkg+NkSz-0c=163(J@$ibp*z1QUV^K@=@2&IM<JQ^AQ+fhOaEF~O){M2W&I z<$^Lnsh|WJu__1`gb6|g0V`Ak45FbDvd{xDumKG$EE8o&)p(3^$~jT~K#Pho&M0R@ z*@X%;DdUuKO5h%%Fv|(!gmMCV$OAI5jX2;RBH$3J0S3`fiBaePF|Yv*ETWQAZPa*_ zF`|NmKhUD0lu?3QVHYaUWP~z;9Fa5vfG&(9Xt+g`$OAI5jX0nQhT#yZ0S3|7ge>R) zF|Ywm)QQwI+NkjeE`}iC545N#Q6GoO)pl>yw4G4JY`cn@wFRwGw`HeRZE>h0*c>V( zn`m_tn+^qRgU088D<M31vQbq_d1dx-&g|oYM<eM|cejjotsC!tck=g5V=X=tZN4Kd z{*zsLOIygO(Q~9pJ0U_QI-<;%mJMEhW8k9B+!Qo)b<J37#7M1c-x;rgbM#@o?{LE^ z%P*llr#yygb>;?r-|5#)XSBoT0`44j?fh!7*z*)VeYbx#b}&j_fd-=f3E}|Lj?Qaq zjyGmGEM5_Nz2<~*pVLe499{cE|F5Zw8^Z=YQk|RkyXd~ij!qdgelW+q$jji<-&(Zr zT0^!&ipg((Z_Sp0N8`PIADLg6(X6m;>nzs0Ex2CfU2fcIy1F;XQ0TU?YGaxH*rxRR zb=8(ut!Vl9L16xSEqS5A5%HHZ9B%5PZghLcX1QD|kL#JX#AV+5xm6Qf=dCkXo=)q^ zT@>S+96B$#tGPO+G4<}%^qzG-N9%WvJ-mNud56ntQKH>F-@D}e$lgjngZZ)6I{cfa zA+adzyD7^R`IDB-WW>CGY>sBTS4MkJa%yQ?RhIoi*Oa05+xoKjCvP?mb<}N;b+aX# z>;LL(3vnImK0Zzg4n}mJdgkx6tjJ3_lcJ&2Fk~9O=9)hHjs5XG=Ee=1W9>%2y_ae4 zc&9Mcdb=Vv(Cl=|#qj94?UA{I$tz3u&D-m_HQjwtP=2oCuF6i|rl{z?&mC)zglA6n z8ao1g`zucdnyuI44(U9shYD_)Jo5t++A6}*-g0w~&<<yJh3mSW6n7Rz#q-3&>(}pC zRy4jnFETpr>rWyM%r$9vc(bFEY4ybABkekCZ`Q(q|Ewn?j~RyQx0QYQM_xtgXEWT% zKX*av@?!hLInHT2mu`GeGCgk0_n+}R&{;m6aB)XP&eqc}SCqQAcj%ISG`3e(EnS`9 zmiXtZ9uLY+EV-4x>cXcNlFn+TCX)g`tX~ruV0<yCEOBnRF|2&a+c!MUIzGSK>HGPw WB{>D$+-0`Z-OKe#Id5l2bpHd;>%uJn literal 0 HcmV?d00001 diff --git a/koha-tmpl/opac-tmpl/prog/images/Star5.gif b/koha-tmpl/opac-tmpl/prog/images/Star5.gif new file mode 100644 index 0000000000000000000000000000000000000000..0a61173924a33b422e21e9d649b9f50b36321465 GIT binary patch literal 1284 zcmW+#eN2^A7(XnUkaYoX)3$;I<h^-iOgZw^MG$CL(TO!nC2*w3`6B}=`H0dsG(l!v zFe^Bb2tv-9GLR1=qy{$}$uXKjph$)RdoLFTbOpqZPQP={f6t!h`F%a_yD`BM5xHj{ zd6p~?BGnU8aw(aVR7#>MA*A3^Fe#`ML^ZjPoJ-Cmr;-yj%Y<ZHGA0?7jHp#AB;}Ga zNvWg+NRkLixFk#xDhUc;0~%OZ#xzkp>Tw~M5L5`FXi;%4I1`)-PLv8X85fKRMg=2E z6lN(GlnF`&CD4dfLAW4H5Gn{*p&DQi4V93E9*BVrXkcNPC_}2oW1LgYiSh?pRE%*( zIU~w0RG>*2r<79y_Yj3yP8cVY6WBu@kcn-?0sjyIhfobLh=xjxLI;R}4QOBym7Hp$ z#-of86(szD78Rw8666ZIP=O{Rlo8~Jq!9pgVH`ojEuusokcn-?0ZlLrhfobLh{h&l zK?jI|4QQfHq^8kEjYn`X1POnjMMa7FI8?5#d#k4Fgeqp&Rn)92XqCDvJGJVHLmk28 zP$9WQtDCrVC}0;fJ_lS0;lUHA=10<|`?fl7CC?3~&h!M&UJKK@Ua|ic<-B2;x|Z;7 z-%jVBNmHGM=_@9OZQZO$nC*RYvORG0^18{cSEsLTn`rT~HwN6PGmSI`Pqc<<?J-lG zTW;09bo-oPq9y2`hS2e5v!mTIRu`!?Mh{f@4%e7^kNM!!_g{2(gHaL$4MhDD!~>>1 zU)WS%)p5i_Z%XN|ueKF=2X6j$Q^T3A<Im|od1!b)ZKKs^eWAPO+LVYapRH$t@<Y<M ze7)NDBYk+H@2@;G>w43ow{aUce_VJYEOWT`=wF4)*Z;6))$27OU&Uo;+4j!Q6FYL^ zjb472^E?`K{a5=}4<6aud}8WB=;WpI$u}!Q%X2Rz{dBV_V`!;0Ir6~d_?RuP#IGYO ze<9QHFns3k&LX{YZt1Qydu&hmI8NOj3>g()tk|A=u(j&ayMs*+R+iPBYCCw^vodiw zBgZk&u;lk0yRFUp2g}oI#;2aodvDmbf8pn69=&}%qh+YMqAWQ#LhJc%drYQp+qAtu zCv~JmSK0RA^o1Vd8{xW?`GIlIi+QP0sgDYrcZPPB|MK6Vg2!tYPH%7=bMB71KhyYH z!4kudRRwncb5(IqTeKICdT%yX$IX0tz#8c|pH^aC5zseM<Zs<|H`aLg<$z-SH(K>^ z)0OVKpRMZb_BU(wfnL#zqm3mo=K1?+rSZ${fu(WFxOY@kaB=NPgC&38$*0<Fy6Cw- z;=N)XYw=^TZ+&22S-zLGrkAA_MxKpLzgRq8@nl#2*@~pxq|%t|W%sO=?z7yOci3>x dSba#BtyLDYQLU!bVzSqMx#OMhH@LYG^Z&S!h!y|< literal 0 HcmV?d00001 diff --git a/opac/opac-overdrive-search.pl b/opac/opac-overdrive-search.pl new file mode 100755 index 0000000..8c9d999 --- /dev/null +++ b/opac/opac-overdrive-search.pl @@ -0,0 +1,47 @@ +#!/usr/bin/perl + +# Copyright 2009 BibLibre SARL +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use strict; +use warnings; + +use CGI; + +use C4::Auth qw(:DEFAULT get_session); +use C4::Output; + +my $cgi = new CGI; + +# Getting the template and auth +my ($template, $loggedinuser, $cookie) += get_template_and_user({template_name => "opac-overdrive-search.tmpl", + query => $cgi, + type => "opac", + authnotrequired => 1, + flagsrequired => {borrowers => 1}, + debug => 1, + }); + +$template->{'VARS'}->{'q'} = $cgi->param('q'); +$template->{'VARS'}->{'limit'} = C4::Context->preference('OPACnumSearchResults') || 20; +$template->{'VARS'}->{'OPACnumSearchResults'} = C4::Context->preference('OPACnumSearchResults') || 20; +$template->{'VARS'}->{'OverDriveLibraryID'} = C4::Context->preference('OverDriveLibraryID'); + +output_html_with_http_headers $cgi, $cookie, $template->output; + + diff --git a/opac/opac-search.pl b/opac/opac-search.pl index dfe651e..0603caa 100755 --- a/opac/opac-search.pl +++ b/opac/opac-search.pl @@ -51,6 +51,7 @@ use C4::Tags qw(get_tags); use C4::Branch; # GetBranches use C4::SocialData; use C4::Ratings; +use C4::External::OverDrive; use POSIX qw(ceil floor strftime); use URI::Escape; @@ -899,5 +900,10 @@ $template->{VARS}->{IDreamBooksReviews} = C4::Context->preference('IDreamBooksRe $template->{VARS}->{IDreamBooksReadometer} = C4::Context->preference('IDreamBooksReadometer'); $template->{VARS}->{IDreamBooksResults} = C4::Context->preference('IDreamBooksResults'); +if ($offset == 0 && IsOverDriveEnabled()) { + $template->param(OverDriveEnabled => 1); + $template->param(OverDriveLibraryID => C4::Context->preference('OverDriveLibraryID')); +} + $template->param( borrowernumber => $borrowernumber); output_with_http_headers $cgi, $cookie, $template->output, $content_type; diff --git a/opac/svc/overdrive_proxy b/opac/svc/overdrive_proxy new file mode 100755 index 0000000..5c3086b --- /dev/null +++ b/opac/svc/overdrive_proxy @@ -0,0 +1,83 @@ +#!/usr/bin/perl + +# Copyright 2013 ByWater +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +=head1 NAME + +svc/overdrive_proxy: Proxy OAuth'd requests to OverDrive + +=head1 SYNOPSIS + +svc/overdrive_proxy/libraries/9001 -> https://api.overdrive.com/v1/libraries/9001 + +=head1 DESCRIPTION + +This service proxies incoming requests to the OverDrive OAuth API, to keep the +JS side from having to deal with cross-origin/authentication issues. + +=cut + +use strict; +use warnings; + +use CGI qw(-oldstyle_urls); +use JSON; + +use C4::Context; +use C4::External::OverDrive; +use C4::Output; + +my $query = new CGI; + +my $token; + +if ( !IsOverDriveEnabled() || !( $token = GetOverDriveToken() ) ) { + print $query->header( + -status => '400 Bad Request', + -content + ); + + print to_json({ + error => 'invalid_client', + error_description => 'OverDrive login failed' + }); + + exit; +} + +my $request = HTTP::Request::Common::GET( "https://api.overdrive.com/v1" . $query->path_info . '?' . $query->query_string ); +$request->header( Authorization => $token ); + +my $ua = LWP::UserAgent->new( "Koha " . C4::Context->KOHAVERSION ); + +my $response = $ua->request( $request ) ; +if ( $response->code eq '500' ) { + print $query->header( + -status => '500 Internal Server Error' + ); + + warn "OverDrive request failed: " . $response->message; + print to_json({ + error => 'invalid_client', + error_description => 'OverDrive request failed' + }); + + exit; +} + +output_with_http_headers $query, undef, $response->content, 'json', $response->status_line; -- 1.7.10.4