Bug 17602: OPAC integration of RecordedBooks
authorSrdjan <srdjan@catalyst.net.nz>
Fri, 23 Sep 2016 06:13:10 +0000 (18:13 +1200)
committerNick Clemens <nick@bywatersolutions.com>
Mon, 1 Oct 2018 13:56:39 +0000 (13:56 +0000)
Add RecordedBooks to the users page in the public interface

To Test:
1/ Apply all 3 patches
2/ Set the sysprefs to valid values (you will need a test account with RecordedBooks)
3/ Try a search
4/ Login to the OPAC, try to place a hold, or check an item out
5/ Check the opac-user page, see if your items are showing on the oneclickdigital tab

Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>

Signed-off-by: Nick Clemens <nick@bywatersolutions.com>

koha-tmpl/opac-tmpl/bootstrap/css/src/opac.scss
koha-tmpl/opac-tmpl/bootstrap/en/includes/recordedbooks-checkout.inc [new file with mode: 0644]
koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-recordedbooks-search.tt [new file with mode: 0644]
koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results.tt
koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt
koha-tmpl/opac-tmpl/bootstrap/js/recordedbooks.js [new file with mode: 0644]
opac/opac-recordedbooks-search.pl [new file with mode: 0755]
opac/opac-search.pl
opac/opac-user.pl
opac/svc/recordedbooks [new file with mode: 0755]

index 30e1ca4..c5075d5 100644 (file)
@@ -2677,6 +2677,7 @@ button.closebtn {
 
 
 #overdrive-results,
+#recordedbooks-results,
 #openlibrary-results {
     font-weight: bold;
     padding-left: 1em;
diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/includes/recordedbooks-checkout.inc b/koha-tmpl/opac-tmpl/bootstrap/en/includes/recordedbooks-checkout.inc
new file mode 100644 (file)
index 0000000..bebf3d4
--- /dev/null
@@ -0,0 +1,15 @@
+<div id="recordedbooks-checkout" class="modal hide" tabindex="-1" role="dialog" aria-labelledby="recordedbooks-checkout-label" aria-hidden="true">
+    <div class="modal-header">
+        <button type="button" class="closebtn" data-dismiss="modal" aria-hidden="true">×</button>
+        <h3 id="recordedbooks-checkout-label">Checkout</h3>
+    </div>
+    <form action="#" method="post" id="recordedbooks-checkout-form">
+        <div class="modal-body">
+                <input type="hidden" name="id" value="" />
+        </div>
+        <div class="modal-footer">
+            <input type="submit" class="btn btn-primary recordedbooks-checkout-submit" value="Checkout" />
+            <a href="#" data-dismiss="modal" aria-hidden="true" class="cancel">Cancel</a>
+        </div>
+    </form> <!-- /#recordedbooks-checkout-form -->
+</div>  <!-- /#recordedbooks-checkout  -->
diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-recordedbooks-search.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-recordedbooks-search.tt
new file mode 100644 (file)
index 0000000..b90f6ac
--- /dev/null
@@ -0,0 +1,168 @@
+[% USE Koha %]
+[% INCLUDE 'doc-head-open.inc' %]
+<title>[% IF ( LibraryNameTitle ) %][% LibraryNameTitle %][% ELSE %]Koha online[% END %] catalog &rsaquo; RecordedBooks search for '[% q | html %]'</title>
+[% INCLUDE 'doc-head-close.inc' %]
+[% BLOCK cssinclude %]
+<link rel="stylesheet" type="text/css" href="[% interface %]/[% theme %]/css/jquery.rating.css" />
+<style>
+.actions a.addtocart {
+    display: inline;
+}
+</style>
+[% END %]
+</head>
+[% INCLUDE 'bodytag.inc' bodyid='recordedbooks-results-page' bodyclass='scrollto' %]
+[% INCLUDE 'masthead.inc' %]
+
+    <div class="main">
+        <ul class="breadcrumb">
+            <li><a href="/cgi-bin/koha/opac-main.pl">Home</a> <span class="divider">&rsaquo;</span></li>
+            <li><a href="#">RecordedBooks search for '[% q | html %]'</a></li>
+        </ul>
+
+        <div class="container-fluid">
+            <div class="row-fluid">
+                <div class="span2">
+                    [% IF ( OpacNav || OpacNavBottom ) %]
+                        [% INCLUDE 'navigation.inc' %]
+                    [% END %]
+                </div>
+                <div class="span10">
+                    <div id="recordedbooks-results-content" class="maincontent searchresults">
+                        <h1>RecordedBooks search for '[% q | html %]'</h1>
+                            <div id="breadcrumbs">
+                                <p></p>
+                            </div>
+
+                            <div id="top-pages">
+                                <div class="pagination pagination-small">
+                                </div>
+                            </div>
+
+                            <table id="recordedbooks-results-list" class="table table-striped">
+                                <tbody>
+                                </tbody>
+                            </table>
+
+                            <div id="bottom-pages">
+                                <div class="pagination pagination-small">
+                                </div>
+                            </div>
+
+                    </div> <!-- / #recordedbooks-results-content -->
+                </div> <!-- / .span10 -->
+            </div> <!-- / .row-fluid -->
+        </div> <!-- / .container-fluid -->
+    </div> <!-- / .main -->
+
+[% INCLUDE 'recordedbooks-checkout.inc' %]
+
+[% INCLUDE 'opac-bottom.inc' %]
+[% BLOCK jsinclude %]
+<script type="text/javascript" src="[% interface %]/[% theme %]/js/recordedbooks.js"></script>
+<script type="text/javascript" src="[% interface %]/[% theme %]/lib/jquery/plugins/jquery.rating.js"></script>
+<script type="text/javascript">
+var querystring = "[% q |replace( "'", "\'" ) |replace( '\n', '\\n' ) |replace( '\r', '\\r' ) |html %]";
+var results_per_page = [% OPACnumSearchResults || 20 %];
+
+function search( page ) {
+    $( '#recordedbooks-status' ).html( _("Searching RecordedBooks...") + ' <img class="throbber" src="[% interface %]/lib/jquery/plugins/themes/classic/throbber.gif" /></span>' );
+
+    KOHA.RecordedBooks.search( querystring, results_per_page, page, function( data ) {
+        if ( data.error ) {
+            $( '#recordedbooks-status' ).html( '<strong class="unavailable">' + _("Error searching RecordedBooks collection") + ': ' + data.error + '</strong>' );
+            return;
+        }
+
+        if ( !data.total ) {
+            $( '#recordedbooks-status' ).html( '<strong>' + _("No results found in the library's RecordedBooks collection.") + '</strong>' );
+            return;
+        }
+
+        $( '#recordedbooks-results-list tbody' ).empty();
+
+        $( '#recordedbooks-status' ).html( '<strong>' + _("Found") + ' ' + data.total + ' ' + _("results in the library's RecordedBooks collection.") + '</strong>' );
+
+        for ( var i = 0; data.items[i]; i++ ) {
+            var prod = data.items[i];
+            var results = [];
+
+            results.push( '<tr>' );
+
+            results.push( '<td class="info"><span class="title">' );
+            if (prod.url) results.push( '<a href="', prod.url, '" target="recordedbooks">' );
+            results.push( prod.title );
+            if (prod.url) results.push( '</a>' );
+            results.push( '</span>' );
+            results.push( '<p>' + _("by") + ' ', prod.author, '</p>' );
+            if (prod.description) results.push( '<p>' + prod.description, '</p>' );
+            results.push( '<span class="results_summary mediatype"><span class="label">' + _("Type:") + ' </span>', prod.media, '</span>' );
+
+            results.push( '</td>' );
+
+            results.push( '<td>' );
+            if ( prod.images && prod.images.medium ) {
+                if (prod.url) results.push( '<a href="', prod.url, '" target="recordedbooks">' );
+                results.push( '<img class="thumbnail" src="', prod.images.medium, '" />' );
+                if (prod.url) results.push( '</a>' );
+            }
+            results.push( '</td>' );
+
+            results.push( '</tr>' );
+            var $tr = $( results.join( '' ));
+            $( '#recordedbooks-results-list tbody' ).append( $tr );
+
+            $tr.find( '.info' ).each(function() {
+                KOHA.RecordedBooks.add_actions(this, prod.isbn);
+            });
+        }
+
+        $( '#recordedbooks-results-list tr:odd' ).addClass( 'highlight' );
+
+        var pages = [];
+
+        var max_page = Math.floor( data.total / results_per_page );
+        if (data.total == page*results_per_page) max_page++;
+
+        if ( page != 1 ) {
+            pages.push( '<li><a class="od-nav" href="#" data-page="' + (page - 1) + '">&laquo; ' + _("Previous") + '</a></li>' );
+        }
+
+        for ( var p = Math.max( 0, page - 9 ); p <= Math.min( max_page, p + 9 ); p++ ) {
+            if ( p == page ) {
+                pages.push( ' <li class="active"><a href="#">' + ( p + 1 ) + '</a></li>' );
+            } else {
+                pages.push( ' <li><a class="od-nav" href="#" data-page="' +  p + '">' + p + '</a></li>' );
+            }
+        }
+
+        if ( page < max_page ) {
+            pages.push( ' <li><a class="od-nav" href="#" data-page="' + (page + 1) + '">' + _("Next") + ' &raquo;</a></li>' );
+        }
+
+        if ( pages.length > 1 ) $( '#top-pages, #bottom-pages' ).find( '.pagination' ).html( '<ul>' + pages.join( '' ) + '</ul>');
+
+        $( '#recordedbooks-results-list input.star' ).rating();
+    } );
+}
+
+$( document ).ready( function() {
+    $( '#breadcrumbs p' )
+        .append( ' ' )
+        .append( '<span id="recordedbooks-status"></span>' );
+
+    $( document ).on( 'click', 'a.od-nav', function() {
+        search( $( this ).data( 'page' ) );
+        return false;
+    });
+
+    [% IF ( loggedinusername ) %]
+    KOHA.RecordedBooks.with_account_details("#breadcrumbs", function() {
+        search( 1 );
+    });
+    [% ELSE %]
+        search( 1 );
+    [% END %]
+} );
+</script>
+[% END %]
index b4a417f..40fd499 100644 (file)
 [% END %]
 [% IF ( OpacStarRatings == 'all' || Koha.Preference('Babeltheque') ) %][% Asset.js("lib/jquery/plugins/jquery.rating.js") | $raw %][% END %]
 [% IF ( OverDriveEnabled ) %][% Asset.js("js/overdrive.js") | $raw %][% END %]
+[% IF ( RecordedBooksEnabled ) %][% Asset.js("js/recordedbooks.js") | $raw %][% END %]
 [% Asset.js("lib/jquery/plugins/jquery.checkboxes.min.js") | $raw %]
 [% Asset.js("js/authtoresults.js") | $raw %]
 [% IF ( OpacHighlightedWords ) %][% Asset.js("lib/jquery/plugins/jquery.highlight-3.js") | $raw %]
@@ -902,6 +903,23 @@ $(document).ready(function(){
             }
         } );
     [% END %]
+    [% IF ( RecordedBooksEnabled ) %]
+        var $recordedbooks_results = $( '<div id="recordedbooks-results">' + _( 'Searching RecordedBooks...' ) + ' <img class="throbber" src="[% interface %]/lib/jquery/plugins/themes/classic/throbber.gif" /></div>' );
+        $( '#numresults' ) .append( ' ' )
+            .append( $recordedbooks_results );
+        KOHA.RecordedBooks.search( querystring, [% OPACnumSearchResults || "null" %], null, function( data ) {
+            if ( data.error ) {
+                $recordedbooks_results.html( _( 'Error searching RecordedBooks collection' )  + ': ' + data.error);
+                return;
+            }
+
+            if ( data.total > 0 ) {
+                $recordedbooks_results.html( _( 'Found' ) + ' <a href="/cgi-bin/koha/opac-recordedbooks-search.pl?q=' + escape( querystring ) + '">' + data.total + ' ' + _( 'results' ) + '</a> ' + _( 'in RecordedBooks collection' ) );
+            } else {
+                $recordedbooks_results.remove();
+            }
+        } );
+    [% END %]
     [% IF ( OpenLibrarySearch ) %]
         var $openlibrary_results = $( '<div id="openlibrary-results">' + _( 'Searching Open Library...' ) + ' <img class="throbber" src="[% interface | html %]/lib/jquery/plugins/themes/classic/throbber.gif" /></div>' );
         $( '#numresults' ) .append( ' ' )
index 17e950f..b69fb75 100644 (file)
                             [% IF ( OverDriveCirculation ) %]
                             <li><a href="#opac-user-overdrive">OverDrive Account</a></li>
                             [% END %]
+                            [% IF ( RecordedBooksCirculation ) %]
+                            <li><a href="#opac-user-recordedbooks">RecordedBooks Account</a></li>
+                            [% END %]
                         </ul>
 
                         <div id="opac-user-overdrive">
                         </div>
+                        <div id="opac-user-recordedbooks">
+                        </div>
                         <div id="opac-user-checkouts">
                             [% IF ( issues_count ) %]
                                 <form id="renewselected" action="/cgi-bin/koha/opac-renew.pl" method="post">
 [% IF ( OverDriveCirculation ) %]
 [% INCLUDE 'overdrive-checkout.inc' %]
 [% END %]
+[% IF ( RecordedBooksCirculation ) %]
+[% INCLUDE 'recordedbooks-checkout.inc' %]
+[% END %]
 
 [% INCLUDE 'opac-bottom.inc' %]
 
     });
     </script>
     [% END %]
+    [% IF RecordedBooksCirculation %]
+    <script type="text/javascript" src="[% interface %]//[% theme %]/js/recordedbooks.js"></script>
+    <script type="text/JavaScript">
+    $(document).ready(function() {
+    [% IF ( recordedbooks_error ) %]
+        KOHA.RecordedBooks.display_error("#opac-user-recordedbooks", "[% recordedbooks_error.dquote %]");
+    [% END %]
+    [% IF ( recordedbooks_tab ) %]
+        $("#opac-user-views").tabs("select", "#opac-user-recordedbooks");
+    [% END %]
+        $("#opac-user-recordedbooks").each( function() {
+            KOHA.RecordedBooks.display_account_details(this);
+        } );
+    });
+    [% END %]
+    </script>
 [% END %]
diff --git a/koha-tmpl/opac-tmpl/bootstrap/js/recordedbooks.js b/koha-tmpl/opac-tmpl/bootstrap/js/recordedbooks.js
new file mode 100644 (file)
index 0000000..5df3f3a
--- /dev/null
@@ -0,0 +1,325 @@
+if ( typeof KOHA == "undefined" || !KOHA ) {
+    var KOHA = {};
+}
+
+KOHA.RecordedBooks = new function() {
+    var svc_url = '/cgi-bin/koha/svc/recordedbooks';
+
+    var error_div = $('<div class="recordedbooks-error">');
+    function display_error ( error ) {
+        error_div.text(error);
+    }
+
+    var details = null;
+
+    function is_identified() {
+        return details ? details.is_identified : false;
+    }
+
+    var checkout_popup = null;
+    $( document ).ready(function() {
+        checkout_popup = $("#recordedbooks-checkout");
+    });
+
+    function display_account (container, data) {
+        if (!data.is_identified) {
+            return;
+        }
+
+        if (data.checkouts) {
+            var checkouts_div = $('<div class="recordedbooks-div">').html('<h3>' + _("Checkouts") + '</h3>');
+            var items = data.checkouts.items;
+            var checkouts_list;
+            if (items.length == 0) {
+                checkouts_list = _("No checkouts");
+            } else {
+                checkouts_list = $('<ul class="recordedbooks-list">');
+                data.checkouts.items.forEach(function(item) {
+                    item_line(checkouts_list, item);
+                });
+            }
+            checkouts_div.append(checkouts_list);
+            $(container).append(checkouts_div);
+        }
+
+        if (data.holds) {
+            var holds_div = $('<div class="recordedbooks-div">').html('<h3>' + _("Holds") + '</h3>');
+            var items = data.holds.items;
+            var holds_list;
+            if (items.length == 0) {
+                holds_list = _("No holds");
+            } else {
+                holds_list = $('<ul class="recordedbooks-list">');
+                data.holds.items.forEach(function(item) {
+                    item_line(holds_list, item);
+                });
+            }
+            holds_div.append(holds_list);
+            $(container).append(holds_div);
+        }
+    }
+
+    function item_line(ul_el, item) {
+        var line = $('<li class="recordedbooks-item">');
+        if (item.images) {
+            var thumb_url = item.images.small;
+            if (thumb_url) {
+                $('<img class="recordedbooks-item-thumbnail">')
+                    .attr("src", thumb_url)
+                    .appendTo(line);
+            }
+        }
+        $('<div class="recordedbooks-item-title">')
+            .text(item.title)
+            .appendTo(line);
+        $('<div class="recordedbooks-item-subtitle">')
+            .text(item.subtitle)
+            .appendTo(line);
+        $('<div class="recordedbooks-item-author">')
+            .text(item.author)
+            .appendTo(line);
+        if (item.files && item.files.length > 0) {
+            downloads = $('<div class="recordedbooks-item-author">')
+                .text("Downloads")
+                .appendTo(line);
+            render_downloads(downloads, item.files);
+        }
+        var actions = $('<span class="actions">');
+        display_actions(actions, item.isbn);
+        $('<div id="action_'+item.isbn+'" class="actions-menu">')
+            .append(actions)
+            .appendTo(line);
+
+        $(ul_el).append(line);
+    }
+
+    function render_downloads(el, files) {
+        if (files.length == 0) return;
+        var file_spec = files.shift();
+        if (/^https?:\/\/api\./.test(file_spec.url)) {
+            $.ajax({
+                dataType: "json",
+                url: file_spec.url,
+                success: function (data) {
+                    append_download_link(el, data.url, data.id);
+                    render_downloads(el, files);
+                },
+                error: function(jqXHR, textStatus, errorThrown) {
+                    display_error(errorThrown);
+                }
+            });
+        } else {
+            append_download_link(el, file_spec.url, file_spec.filename);
+            render_downloads(el, files);
+        }
+    }
+    function append_download_link(el, url, text) {
+        var p = $("<p>");
+        $( '<a href="' + url + '" target="recordedbooks">' )
+            .text(text)
+            .appendTo(p);
+        el.append(p);
+    }
+
+    function svc_ajax ( method, params, success_callback, callback_for_error_too ) {
+        // remove when jquery is upgraded
+        for (var key in params) {
+            if (params[key] === null) delete params[key];
+        }
+        return $.ajax({
+            method: method,
+            dataType: "json",
+            url: svc_url,
+            data: params,
+            success: function (data) {
+                if (data.error && !callback_for_error_too) {
+                    display_error(data.error);
+                }
+                success_callback(data);
+            },
+            error: function(jqXHR, textStatus, errorThrown) {
+                if (callback_for_error_too) {
+                    success_callback({error: errorThrown});
+                    return;
+                }
+                display_error(errorThrown);
+            }
+        });
+    }
+
+    function load_account_details ( callback ) {
+        svc_ajax('get', { action: "account" }, function(data) {
+            details = data;
+            callback(data);
+        });
+    }
+
+    function item_action (params, el) {
+        var isbn = params.isbn;
+        svc_ajax('post', params, function(data) {
+            if (data.checkouts) {
+                details.checkouts = data.checkouts;
+            }
+            if (data.holds) {
+                details.holds = data.holds;
+            }
+            display_actions(el, isbn);
+        });
+    }
+
+    function item_is_checked_out (isbn) {
+        if ( !(details && details.checkouts) ) {
+            return null;
+        }
+        var isbn_uc = isbn.toUpperCase();
+        var items = details.checkouts.items;
+        for (var i = 0; i < items.length; i++) {
+            if ( items[i].isbn.toUpperCase() == isbn_uc ) {
+                return items[i];
+            }
+        }
+        return null;
+    }
+
+    function item_is_on_hold (isbn) {
+        if ( !(details && details.holds) ) {
+            return false;
+        }
+        var isbn_uc = isbn.toUpperCase();
+        var items = details.holds.items;
+        for (var i = 0; i < items.length; i++) {
+            if ( items[i].isbn.toUpperCase() == isbn_uc ) {
+                return items[i];
+            }
+        }
+        return null;
+    }
+
+    function display_actions(el, isbn) {
+        $(el).empty();
+        if (is_identified()) {
+
+            var item = item_is_checked_out(isbn);
+            if (item) {
+                var expires = new Date(item.expires);
+                $('<span class="recordedbooks-item-status">')
+                    .text(_("Checked out until") + " " + expires.toLocaleString())
+                    .appendTo(el);
+                $(el).append(" ");
+
+                if (item.url) {
+                    var download = $('<a href="'+item.url+'">').appendTo(el);
+                    decorate_button(download, _("Download"));
+                    $(el).append(" ");
+                }
+
+                $(el).append( ajax_button(_("Check in"), function() {
+                    if( confirm(_("Are you sure you want to return this item?")) ) {
+                        item_action({action: "return", isbn: isbn}, el);
+                    }
+                }) );
+
+                return item;
+            }
+
+            item = item_is_on_hold(isbn);
+            if (item) {
+                $('<span class="recordedbooks-status">')
+                    .text(_("On hold"))
+                    .appendTo(el);
+                $(el).append(" ");
+            }
+
+            if(checkout_popup) {
+                $(el).append( ajax_button(_("Check out"), function() {
+                    if( confirm(_("Are you sure you want to checkout this item?")) ) {
+                        svc_ajax('post', {action: "checkout", isbn: isbn}, function(data) {
+                            if (data.checkouts) {
+                                details.checkouts = data.checkouts;
+                            }
+                            if (data.holds) {
+                                details.holds = data.holds;
+                            }
+                            item = display_actions(el, isbn);
+                        });
+                    }
+                }) );
+            }
+            if (!item) {
+                $(el).append( ajax_button(_("Place hold"), function() {
+                    item_action({action: "place_hold", isbn: isbn}, el);
+                }) );
+            }
+
+            if (item) {
+                $(el).append( ajax_button(_("Cancel"), function() {
+                    if( confirm(_("Are you sure you want to cancel this hold?")) ) {
+                        item_action({action: "remove_hold", isbn: isbn}, el);
+                    }
+                }) );
+            }
+            return item;
+        }
+    }
+
+    function ajax_button(label, on_click) {
+        var button = $('<a href="#">')
+            .click(function(e) {
+                e.preventDefault();
+                on_click();
+            });
+        decorate_button(button, label);
+        return button;
+    }
+
+    function decorate_button(button, label) {
+        $(button)
+            .addClass("btn btn-primary btn-mini")
+            .css("color","white")
+            .text(label);
+    }
+
+    this.with_account_details = function( el, callback ) {
+        $(el).append(error_div);
+        load_account_details( callback );
+    }
+
+    this.display_account_details = function( el ) {
+        $(el).empty().append(error_div);
+        load_account_details(function(data) {
+            display_account(el, data);
+        });
+    };
+
+    this.display_error = function( el, error ) {
+        $(el).empty().append(error_div);
+        display_error(error);
+    };
+
+    this.is_identified = is_identified;
+
+    this.add_actions = function(el, isbn) {
+        var actions = $('<span class="actions">');
+        display_actions(actions, isbn);
+        $('<div id="action_'+isbn+'" class="actions-menu">')
+            .append(actions)
+            .appendTo(el);
+    };
+
+    this.search = function( q, page_size, page, callback ) {
+        svc_ajax('get', { action: "search", q: q, page_size: page_size, page: page }, function (data) {
+            var results;
+            if (data.results) {
+                results = data.results;
+                if (!results.total) {
+                    var total = results.items.length;
+                    if ( total == results.page_size ) total = total + "+";
+                    results.total = total;
+                }
+            }
+            else results = {};
+            results.error = data.error;
+            callback(results);
+        }, true);
+    };
+}
diff --git a/opac/opac-recordedbooks-search.pl b/opac/opac-recordedbooks-search.pl
new file mode 100755 (executable)
index 0000000..ba1ef7f
--- /dev/null
@@ -0,0 +1,42 @@
+#!/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 3 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, see <http://www.gnu.org/licenses>.
+
+use strict;
+use warnings;
+
+use CGI qw ( -utf8 );
+
+use C4::Auth qw(:DEFAULT);
+use C4::Output;
+
+my $cgi = new CGI;
+
+# Getting the template and auth
+my ($template, $loggedinuser, $cookie)
+= get_template_and_user({template_name => "opac-recordedbooks-search.tt",
+                                query => $cgi,
+                                type => "opac",
+                                debug => 1,
+                                authnotrequired => 1,
+                                });
+
+$template->{'VARS'}->{'q'} = $cgi->param('q');
+$template->{'VARS'}->{'limit'} = C4::Context->preference('OPACnumSearchResults');
+
+output_html_with_http_headers $cgi, $cookie, $template->output;
index ce2cefc..7aaffef 100755 (executable)
@@ -998,9 +998,12 @@ $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'));
+if ($offset == 0) {
+    if (IsOverDriveEnabled()) {
+        $template->param(OverDriveEnabled => 1);
+        $template->param(OverDriveLibraryID => C4::Context->preference('OverDriveLibraryID'));
+    }
+    $template->param(RecordedBooksEnabled => C4::Context->preference( 'RecordedBooksClientSecret' ) && C4::Context->preference( 'RecordedBooksLibraryID' ));
 }
 
     $template->param( borrowernumber    => $borrowernumber);
index e599cec..500042d 100755 (executable)
@@ -325,6 +325,7 @@ $template->param(
     OverDriveCirculation => C4::Context->preference('OverDriveCirculation') || 0,
     overdrive_error      => scalar $query->param('overdrive_error') || undef,
     overdrive_tab        => scalar $query->param('overdrive_tab') || 0,
+    RecordedBooksCirculation => C4::Context->preference('RecordedBooksClientSecret') && C4::Context->preference('RecordedBooksLibraryID'),
 );
 
 my $patron_messages = Koha::Patron::Messages->search(
diff --git a/opac/svc/recordedbooks b/opac/svc/recordedbooks
new file mode 100755 (executable)
index 0000000..d55a15c
--- /dev/null
@@ -0,0 +1,148 @@
+#!/usr/bin/perl
+
+# Copyright 2016 Catalyst IT
+# 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 3 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, see <http://www.gnu.org/licenses>.
+
+use Modern::Perl;
+use CGI qw ( -utf8 );
+use JSON qw(encode_json);
+use C4::Auth qw(checkauth);
+use C4::Output;
+use Koha::Logger;
+use Koha::ExternalContent::RecordedBooks;
+
+our $cgi = new CGI;
+my $logger = Koha::Logger->get({ interface => 'opac' });
+my $page_url = $cgi->referer();
+
+my ( $user, $cookie, $sessionID, $flags ) = checkauth( $cgi, 1, {}, 'opac' );
+
+my $action = $cgi->param('action') or response_bad_request("No 'action' specified");
+
+($user && $sessionID) || $action eq 'search' or response_bad_request("User not logged in");
+
+local $@;
+my $rb = eval { Koha::ExternalContent::RecordedBooks->new({ koha_session_id => $sessionID }) };
+unless ($rb) {
+    $logger->error($@) if $@;
+    response({
+        error => $@,
+        is_identified => JSON::false,
+    });
+}
+
+my $is_identified = $rb->is_identified;
+my %data = (
+    is_identified => $is_identified ? JSON::true : JSON::false,
+);
+response(\%data) unless $is_identified || $action eq 'search';
+
+eval {
+    {
+        $action eq 'search' && do {
+            my $query = $cgi->param('q');
+            my $page  = $cgi->param('page');
+            my $page_size = $cgi->param('page_size');
+            my $sort = $cgi->param('sort');
+            my $res = $rb->search({
+                query => $query,
+                page  => $page,
+                page_size => $page_size,
+                sort => $sort,
+            });
+            $data{results} = $res;
+            last;
+        };
+
+        $action eq 'account' && do {
+            eval {
+                $data{account} = $rb->patron;
+                $data{checkouts} = $rb->checkouts;
+                $data{holds} = $rb->holds;
+            };
+            response_bad_request($rb->error_message($@)) if $@;
+            last;
+        };
+
+        $action eq 'checkout' && do {
+            my $isbn = $cgi->param('isbn')
+              or response_bad_request("No 'isbn' specified");
+            my $format = $cgi->param('format');
+            $data{action} = eval {  $rb->checkout($isbn, $format) };
+            response_bad_request($rb->error_message($@)) if $@;
+
+            eval {
+                $data{checkouts} = $rb->checkouts;
+                $data{holds} = $rb->holds;
+            };
+            $data{error} = $rb->error_message($@) if $@;
+            last;
+        };
+
+        $action eq 'return' && do {
+            my $isbn = $cgi->param('isbn')
+              or response_bad_request("No 'isbn' specified");
+            $data{action} = eval { $rb->return($isbn) };
+            response_bad_request($rb->error_message($@)) if $@;
+
+            $data{checkouts} = eval { $rb->checkouts };
+            $data{error} = $rb->error_message($@) if $@;
+            last;
+        };
+
+        $action eq 'place_hold' && do {
+            my $isbn = $cgi->param('isbn')
+              or response_bad_request("No 'isbn' specified");
+            $data{action} = eval { $rb->place_hold($isbn) };
+            response_bad_request($rb->error_message($@)) if $@;
+
+            $data{holds} = eval { $rb->holds };
+            $data{error} = $rb->error_message($@) if $@;
+            last;
+        };
+
+        $action eq 'remove_hold' && do {
+            my $isbn = $cgi->param('isbn')
+              or response_bad_request("No 'isbn' specified");
+            $data{action} = eval { $rb->remove_hold($isbn) };
+            response_bad_request($rb->error_message($@)) if $@;
+
+            $data{holds} = eval { $rb->holds };
+            $data{error} = $rb->error_message($@) if $@;
+            last;
+        };
+
+        response_bad_request("Invalid 'action': $action");
+    }
+};
+if ($@) {
+    $logger->error($@);
+    $data{error} = $rb->error_message("$@");
+}
+
+response(\%data);
+
+
+sub response_bad_request {
+    my ($error) = @_;
+    response({error => $error}, "400 $error");
+}
+sub response {
+    my ($data, $status_line) = @_;
+    $status_line ||= "200 OK";
+    output_with_http_headers $cgi, undef, encode_json($data), 'json', $status_line;
+    exit;
+}