[Koha-patches] [PATCH] [SIGNED-OFF] Bug 10320 - Integrate OverDrive search into OPAC

Srdjan srdjan at catalyst.net.nz
Thu Jun 6 03:02:21 CEST 2013


From: Jesse Weaver <pianohacker at gmail.com>

This show results from the OverDrive ebook/audiobook service in the
OPAC.

Added the sysprefs and copied the relevant JS to CCSR.

Signed-off-by: Srdjan <srdjan at catalyst.net.nz>
---
 C4/External/OverDrive.pm                           | 141 ++++++++++++++++++
 installer/data/mysql/updatedatabase.pl             |  15 ++
 .../admin/preferences/enhanced_content.pref        |  11 ++
 koha-tmpl/opac-tmpl/ccsr/en/js/overdrive.js        |  61 ++++++++
 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 +++++++++++
 17 files changed, 621 insertions(+), 2 deletions(-)
 create mode 100644 C4/External/OverDrive.pm
 create mode 100644 koha-tmpl/opac-tmpl/ccsr/en/js/overdrive.js
 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 at gmail.com>
+
+=cut
diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl
index f3a90d8..dc7f5f8 100755
--- a/installer/data/mysql/updatedatabase.pl
+++ b/installer/data/mysql/updatedatabase.pl
@@ -6984,6 +6984,21 @@ INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES (
 }
 
 
+$DBversion = "3.13.00.XXX";
+if(CheckVersion($DBversion)) {
+    $dbh->do(
+"INSERT IGNORE INTO systempreferences (variable,value,explanation,options,type) VALUES('OverDriveClientKey','','Client key for OverDrive integration','30','Free')"
+    );
+    $dbh->do(
+"INSERT IGNORE INTO systempreferences (variable,value,explanation,options,type) VALUES('OverDriveClientSecret','','Client key for OverDrive integration','30','YesNo')"
+    );
+    $dbh->do(
+"INSERT IGNORE INTO systempreferences (variable,value,explanation,options,type) VALUES('OpacDriveLibraryID','','Library ID for OverDrive integration','','Integer')"
+    );
+    print "Upgrade to $DBversion done (Bug 10320 - Show results from library's OverDrive collection in OPAC search)\n";
+    SetVersion($DBversion);
+}
+
 =head1 FUNCTIONS
 
 =head2 TableExists($table)
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/ccsr/en/js/overdrive.js b/koha-tmpl/opac-tmpl/ccsr/en/js/overdrive.js
new file mode 100644
index 0000000..1bc8c5d
--- /dev/null
+++ b/koha-tmpl/opac-tmpl/ccsr/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/css/opac.css b/koha-tmpl/opac-tmpl/prog/en/css/opac.css
index 84b8489..f990387 100644
--- a/koha-tmpl/opac-tmpl/prog/en/css/opac.css
+++ b/koha-tmpl/opac-tmpl/prog/en/css/opac.css
@@ -2964,3 +2964,12 @@ padding: 0.1em 0;
 .thumbnail-shelfbrowser span {
     margin: 0px auto;
 }
+
+#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 923effa..9b2ea7c 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 at QbD?!KQy%MEWCe at R)@7yB at 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 at NWrCG+AdU}
z$%W)xat8Mhg;~jjWLz at 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 at itJI`2%ml~nw2@$L9}dwP8Jw9!>*MTgAJuV#)L{Gfiv
z#B+u5V_VN(9M*QcCZu{=o at dI~clIYgne?k?-Q+(Cymu at Ywne5y<=(!2u5o`u>ZOMC
z-}j}oRi(y$`rz#L*UOf!jm;Z+#Z%r^ot at fX^R=XZknE2BuxEVEvlHjfKDx18g{+(F
zdewYpXJpz%WvT5v-813crW(uo&fgqM%Cb|IUCneInA>Q5?rNCn?6s45{;KS#oGT{_
zM at 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 at 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 at IGbTHX_AJk=y4Fb}W_ at 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 at 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 at o3r_n}@$8a$O34fqP#hLy%bgsU8>!$C7I%eNh^sFyvow_eOz3PiYAHnBPBl$$@
zoA`8SU>`I-2V4o^!IO{bB+&_?bfRpcT|~J=c|->WpW>4FM1 at 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 at 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 at 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 at r7DryiOUI9IUovy;v7<#5;TGpD
zgU9LruRF!^tWS=N`q7kSG*=pCm*n|(WR}djY}&f+6wOX~ST<(j<r2%2Df at 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 at vOmX;N?c}%$VS<X<NJ}Qek;2U5Yo^4b0cvBHhnnI8
z9~}bnF-asqB{wK28U!wnz(OSzNG^K0B64#VMbPfI&$;Kn at 4nyf=k@*lqE at 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 at 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 at -nzi@aFSj(NTAHq!FD)o-&Rh6L
zcZ#34f7qD`na*t6k!iIBUf=l7+A(E&dwRJ2S)b9jBqne0%Bg^<Ux>)m4Yfn at 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 at y~J3BX5g*ocdI&%VjzjC at 6Hl~f6nUcT0JEb~0<6eRz`s<<h
zY}2#f{&Y at Jj`t3q-fZKuaYs{opGTfuI`hP(?t?=~zTfBF&%SUZqQAzFc$m+Q*=!0H
z=DvuL+vd;GUJrJ7AB)J1eRSMd;p4UFT at 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 at YuMEm=
zHT|=#IzQC9$1$<Yy}R(~;-|x7<rOsp8-A>6 at 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 at 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 at 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 at 16^>3oKN^EY*Ge4>jOZSx%db at iBt80*|Na&`G&%UdHi)<3-!HGFBw
zgIX^`!>Y$uH{3n%_IJI{&?Wyn+QnV at cH-cx`1D<;EoZ?fc at 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 at n>>pvZ)knf^hdM)
zxfyxTPv6jZzvD&P-lq at 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 at EcIa!;myzq_yJc|$JC%Mjxl1$Any at 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 at IOQ-9g-8-ibsKQoz!7PYkjJPLBacyos}4kg;ZpWcUW$!^
zNyH at 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 at 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- at 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 at 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 at Ua|ic<-B2;x|Z;7
z-%jVBNmHGM=_ at 9OZQZO$nC*RYvORG0^18{cSEsLTn`rT~HwN6PGmSI`Pqc<<?J-lG
zTW;09bo-oPq9y2`hS2e5v!mTIRu`!?Mh{f at 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 at 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 at kaB=NPgC&38$*0<Fy6Cw-
z;=N)XYw=^TZ+&22S-zLGrkAA_MxKpLzgRq8 at nl#2*@~pxq|%t|W%sO=?z7yOci3>x
dSba#BtyLDYQLU!bVzSqMx#OMhH at 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.8.1.2



More information about the Koha-patches mailing list