[Koha-patches] [PATCH] bug_16034 Add overdrive info to the users page in the public interface

Srdjan srdjan at catalyst.net.nz
Thu Mar 10 05:31:39 CET 2016


---
 .../bootstrap/en/includes/overdrive-checkout.inc   |  19 ++
 .../bootstrap/en/modules/opac-overdrive-search.tt  |  17 +-
 .../opac-tmpl/bootstrap/en/modules/opac-user.tt    |  23 ++
 koha-tmpl/opac-tmpl/bootstrap/js/overdrive.js      | 359 +++++++++++++++++++++
 opac/opac-overdrive-search.pl                      |   1 +
 opac/opac-user.pl                                  |   6 +
 opac/svc/overdrive                                 | 139 ++++++++
 7 files changed, 562 insertions(+), 2 deletions(-)
 create mode 100644 koha-tmpl/opac-tmpl/bootstrap/en/includes/overdrive-checkout.inc
 create mode 100755 opac/svc/overdrive

diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/includes/overdrive-checkout.inc b/koha-tmpl/opac-tmpl/bootstrap/en/includes/overdrive-checkout.inc
new file mode 100644
index 0000000..a8d60bb
--- /dev/null
+++ b/koha-tmpl/opac-tmpl/bootstrap/en/includes/overdrive-checkout.inc
@@ -0,0 +1,19 @@
+<div id="overdrive-checkout" class="modal hide" tabindex="-1" role="dialog" aria-labelledby="overdrive-checkout-label" aria-hidden="true">
+    <div class="modal-header">
+        <button type="button" class="closebtn" data-dismiss="modal" aria-hidden="true">×</button>
+        <h3 id="overdrive-checkout-label">Checkout</h3>
+    </div>
+    <form action="#" method="post" id="overdrive-checkout-form">
+        <div class="modal-body">
+                <input type="hidden" name="id" value="" />
+                <fieldset class="brief">
+                    <label for="format">Format:</label>
+                    <ul class="overdrive-format-list">
+                </fieldset>
+        </div>
+        <div class="modal-footer">
+            <input type="submit" class="btn btn-primary overdrive-checkout-submit" value="Checkout" />
+            <a href="#" data-dismiss="modal" aria-hidden="true" class="cancel">Cancel</a>
+        </div>
+    </form> <!-- /#overdrive-checkout-form -->
+</div>  <!-- /#overdrive-checkout  -->
diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-overdrive-search.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-overdrive-search.tt
index 5f5a836..f5a26af 100644
--- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-overdrive-search.tt
+++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-overdrive-search.tt
@@ -55,6 +55,8 @@
         </div> <!-- / .container-fluid -->
     </div> <!-- / .main -->
 
+[% INCLUDE 'overdrive-checkout.inc' %]
+
 [% INCLUDE 'opac-bottom.inc' %]
 [% BLOCK jsinclude %]
 <script type="text/javascript" src="[% interface %]/[% theme %]/js/overdrive.js"></script>
@@ -80,7 +82,9 @@ function fetch_availability( prod, $tr ) {
                 $availability_summary.find( '.available' ).append( ', ' + _("waiting holds:") + ' <strong>' + data.numberOfHolds + '</strong>' );
             }
 
-            $tr.find( '.info' ).append( '<div class="actions-menu"><span class="actions"><a href="' + prod.contentDetails[0].href + '" ' + ( data.copiesAvailable ? ( ' class="addtocart">' + _("Check out") ) : ( ' class="hold">' + _("Place hold") ) ) + '</a></span></div>' );
+            $tr.find( '.info' ).each(function() {
+                KOHA.OverDriveCirculation.add_actions(this, data.id, data.copiesAvailable);
+            });
         }
     );
 }
@@ -179,7 +183,16 @@ $( document ).ready( function() {
         return false;
     });
 
-    search( 0 );
+    [% IF ( overdrive_error ) %]
+    KOHA.OverDriveCirculation.display_error("#breadcrumbs", "[% overdrive_error.dquote %]");
+    [% END %]
+    [% IF ( loggedinusername ) %]
+    KOHA.OverDriveCirculation.with_account_details("#breadcrumbs", function() {
+        search( 0 );
+    });
+    [% ELSE %]
+        search( 0 );
+    [% END %]
 } );
 </script>
 [% END %]
diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt
index d45222e..70ad94d 100644
--- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt
+++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt
@@ -119,8 +119,13 @@
                                 [% IF ( BORROWER_INFO.amountlessthanzero ) %]<li><a href="#opac-user-fines">Credits ([% BORROWER_INFO.amountoutstanding %])</a></li>[% END %]
                             [% END %]
                             [% IF ( RESERVES.count ) %]<li><a href="#opac-user-holds">Holds ([% RESERVES.count %])</a></li>[% END %]
+                            [% IF ( OverDriveCirculation ) %]
+                            <li><a href="#opac-user-overdrive">OverDrive Account</a></li>
+                            [% END %]
                         </ul>
 
+                        <div id="opac-user-overdrive">
+                        </div>
                         <div id="opac-user-checkouts">
                             [% IF ( issues_count ) %]
                                 <form id="renewselected" action="/cgi-bin/koha/opac-renew.pl" method="post">
@@ -701,6 +706,10 @@
     </div> <!-- /.container-fluid -->
 </div> <!-- /#main -->
 
+[% IF ( OverDriveCirculation ) %]
+[% INCLUDE 'overdrive-checkout.inc' %]
+[% END %]
+
 [% INCLUDE 'opac-bottom.inc' %]
 
 
@@ -764,4 +773,18 @@
         });
         //]]>
     </script>
+    <script type="text/javascript" src="[% interface %]//[% theme %]/js/overdrive.js"></script>
+    <script type="text/JavaScript">
+    $(document).ready(function() {
+    [% IF ( overdrive_error ) %]
+        KOHA.OverDriveCirculation.display_error("#opac-user-overdrive", "[% overdrive_error.dquote %]");
+    [% END %]
+    [% IF ( overdrive_tab ) %]
+        $("#opac-user-views").tabs("select", "#opac-user-overdrive");
+    [% END %]
+        $("#opac-user-overdrive").each( function() {
+            KOHA.OverDriveCirculation.display_account_details(this);
+        } );
+    });
+    </script>
 [% END %]
diff --git a/koha-tmpl/opac-tmpl/bootstrap/js/overdrive.js b/koha-tmpl/opac-tmpl/bootstrap/js/overdrive.js
index 1bc8c5d..e985d61 100644
--- a/koha-tmpl/opac-tmpl/bootstrap/js/overdrive.js
+++ b/koha-tmpl/opac-tmpl/bootstrap/js/overdrive.js
@@ -59,3 +59,362 @@ KOHA.OverDrive = ( function() {
         }
     };
 } )();
+
+KOHA.OverDriveCirculation = new function() {
+    var svc_url = '/cgi-bin/koha/svc/overdrive';
+
+    var error_div = $('<div class="overdrive-error">');
+    function display_error ( error ) {
+        error_div.text(error);
+    }
+
+    var login_link = $('<a href="#">')
+        .click(function(e) {
+            e.preventDefault();
+            login();
+        })
+        .text(_("Login to OverDrive account"));
+    var login_div = $('<div class="overdrive-login">').append(login_link);
+
+    var details = null;
+
+    function is_logged_in() {
+        return details ? details.is_logged_in : false;
+    }
+
+    var checkout_popup = null;
+    $( document ).ready(function() {
+        checkout_popup = $("#overdrive-checkout");
+    });
+
+    function display_account (container, data) {
+        if (!data.is_logged_in) {
+            $(container).append(login_div);
+            return;
+        }
+
+        var logout_link = $('<a href="#logout" class="overdrive-logout">')
+            .click(function(e) {
+                e.preventDefault();
+                $(container).empty().append(error_div);
+                logout(function(data) {
+                    display_account(container, data);
+                });
+            }).text(_("Logout from OverDrive account"));
+        $(container).append(logout_link);
+
+        if (data.checkouts) {
+            var checkouts_div = $('<div class="overdrive-div">').html('<h3>' + _("Checkouts") + '</h3>');
+            var checkouts_list = $('<ul class="overdrive-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="overdrive-div">').html('<h3>' + _("Holds") + '</h3>');
+            var holds_list = $('<ul class="overdrive-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="overdrive-item">');
+        if (item.images) {
+            var thumb_url = item.images.thumbnail;
+            if (thumb_url) {
+                $('<img class="overdrive-item-thumbnail">')
+                    .attr("src", thumb_url)
+                    .appendTo(line);
+            }
+        }
+        $('<div class="overdrive-item-title">')
+            .text(item.title)
+            .appendTo(line);
+        $('<div class="overdrive-item-subtitle">')
+            .text(item.subtitle)
+            .appendTo(line);
+        $('<div class="overdrive-item-author">')
+            .text(item.author)
+            .appendTo(line);
+        var actions = $('<span class="actions">');
+        display_actions(actions, item.id);
+        $('<div id="action_'+item.id+'" class="actions-menu">')
+            .append(actions)
+            .appendTo(line);
+
+        $(ul_el).append(line);
+    }
+
+    function svc_ajax ( method, params, success_callback ) {
+        return $.ajax({
+            method: method,
+            dataType: "json",
+            url: svc_url,
+            data: params,
+            success: function (data) {
+                if (data.error) {
+                    display_error(data.error);
+                }
+                success_callback(data);
+            },
+            error: function(jqXHR, textStatus, errorThrown) {
+                display_error(errorThrown);
+            }
+        });
+    }
+
+    function load_account_details ( callback ) {
+        svc_ajax('get', { action: "account" }, function(data) {
+            details = data;
+            callback(data);
+        });
+    }
+
+    function login() {
+        svc_ajax('get', { action: "login" }, function(data) {
+            details = null;
+            if (data.login_url) {
+                window.location = data.login_url;
+            }
+        });
+    }
+
+    function logout (callback) {
+        svc_ajax('post', { action: "logout" }, function(data) {
+            details = null;
+            callback(data);
+        });
+    }
+
+    function item_action (params, el, copies_available) {
+        var id = params.id;
+        svc_ajax('post', params, function(data) {
+            if (data.checkouts) {
+                details.checkouts = data.checkouts;
+            }
+            if (data.holds) {
+                details.holds = data.holds;
+            }
+            display_actions(el, id, copies_available);
+        });
+    }
+
+    function item_is_checked_out (id) {
+        if ( !(details && details.checkouts) ) {
+            return null;
+        }
+        var id_uc = id.toUpperCase();
+        var items = details.checkouts.items;
+        for (var i = 0; i < items.length; i++) {
+            if ( items[i].id.toUpperCase() == id_uc ) {
+                return items[i];
+            }
+        }
+        return null;
+    }
+
+    function item_is_on_hold (id) {
+        if ( !(details && details.holds) ) {
+            return false;
+        }
+        var id_uc = id.toUpperCase();
+        var items = details.holds.items;
+        for (var i = 0; i < items.length; i++) {
+            if ( items[i].id.toUpperCase() == id_uc ) {
+                return items[i];
+            }
+        }
+        return null;
+    }
+
+    function display_actions(el, id, copies_available) {
+        $(el).empty();
+        if (is_logged_in()) {
+
+            var item = item_is_checked_out(id);
+            if (item) {
+                var expires = new Date(item.expires);
+                $('<span class="overdrive-item-status">')
+                    .text(_("Checked out until") + " " + expires.toLocaleString())
+                    .appendTo(el);
+                $(el).append(" ");
+
+                if (item.format) {
+                    var download = $('<a href="#">').appendTo(el);
+                    decorate_button(download, _("Download") + " " + item.format);
+                    svc_ajax('get', {action: "download-url", id: id, format: item.format}, function(data) {
+                        download.attr("href", data.action);
+                    });
+                    $(el).append(" ");
+                }
+
+                if (item.formats) {
+                    var lockable_formats = [];
+                    for (var f in item.formats) {
+                        if (f == item.format) continue;
+
+                        if (item.formats[f]) {
+                            var access = $('<a target="_blank">').appendTo(el);
+                            decorate_button(access, _("Access online") + " " + f);
+                            svc_ajax('get', {action: "download-url", id: id, format: f}, function(data) {
+                                access.attr("href", data.action);
+                            });
+                            $(el).append(" ");
+                        }
+                        else {
+                            lockable_formats.push(f);
+                        }
+                    }
+                    if (lockable_formats.length > 0 && checkout_popup) {
+                        $(el).append( ajax_button(_("Download as"), function() {
+                            checkout_format(el, id, lockable_formats, copies_available);
+                        }) ).append(" ");
+                    }
+                }
+
+                if (item.format) return item;
+
+                $(el).append( ajax_button(_("Check in"), function() {
+                    if( confirm(_("Are you sure you want to return this item?")) ) {
+                        item_action({action: "return", id: id}, el, copies_available + 1);
+                    }
+                }) );
+
+                return item;
+            }
+
+            item = item_is_on_hold(id);
+            if (item) {
+                $('<span class="overdrive-status">')
+                    .text(_("On hold"))
+                    .appendTo(el);
+                $(el).append(" ");
+            }
+
+            if(copies_available && 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", id: id}, function(data) {
+                            if (data.checkouts) {
+                                details.checkouts = data.checkouts;
+                            }
+                            if (data.holds) {
+                                details.holds = data.holds;
+                            }
+                            item = display_actions(el, id, copies_available - 1);
+                            if (item && item.formats && !item.format) {
+                                var has_available_formats = false;
+                                var lockable_formats = [];
+                                for (var f in item.formats) {
+                                    if (item.formats[f]) {
+                                        has_available_formats = true;
+                                        break;
+                                    }
+                                    lockable_formats.push(f);
+                                }
+
+                                if (!has_available_formats) {
+                                    checkout_format(el, id, lockable_formats, copies_available - 1);
+                                }
+                            }
+                        });
+                    }
+                }) );
+            }
+            else if (!item) {
+                $(el).append( ajax_button(_("Place hold"), function() {
+                    item_action({action: "place_hold", id: id}, el, copies_available);
+                }) );
+            }
+
+            if (item) {
+                $(el).append( ajax_button(_("Cancel"), function() {
+                    if( confirm(_("Are you sure you want to cancel this hold?")) ) {
+                        item_action({action: "return", id: id}, el, copies_available);
+                    }
+                }) );
+            }
+            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);
+    }
+
+    function checkout_format(el, id, formats, copies_available) {
+        if (formats.length == 0) {
+            alert(_("Item cannot be checked out - no available formats"));
+            return false;
+        }
+
+        var checkout_format_list = checkout_popup.find("ul.overdrive-format-list").empty();
+        formats.forEach(function (item) {
+            var li = $('<li>').appendTo(checkout_format_list);
+            $('<input name="checkout-format" type="radio">')
+                .val(item)
+                .appendTo(li);
+            li.append(item);
+        });
+        checkout_popup.modal("show");
+        checkout_popup.find(".overdrive-checkout-submit").click(function(e) {
+            e.preventDefault();
+            var format = checkout_format_list.find("input[type='radio'][name='checkout-format']:checked").val();
+            item_action({action: "checkout-format", id: id, format: format}, el, copies_available);
+            $(this).unbind( e );
+            checkout_popup.modal("hide");
+        });
+    }
+
+    this.with_account_details = function( el, callback ) {
+        $(el).append(error_div);
+        load_account_details(function(data) {
+            if (!data.is_logged_in) {
+                $(el).append(login_div);
+            }
+            callback(data);
+        });
+    }
+
+    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_logged_in = is_logged_in;
+
+    this.add_actions = function(el, id, copies_available) {
+        var actions = $('<span class="actions">');
+        display_actions(actions, id, copies_available);
+        $('<div id="action_'+id+'" class="actions-menu">')
+            .append(actions)
+            .appendTo(el);
+    };
+}
diff --git a/opac/opac-overdrive-search.pl b/opac/opac-overdrive-search.pl
index dbf60af..1f8304b 100755
--- a/opac/opac-overdrive-search.pl
+++ b/opac/opac-overdrive-search.pl
@@ -41,5 +41,6 @@ $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');
+$template->param(overdrive_error => $cgi->param('overdrive_error'));
 
 output_html_with_http_headers $cgi, $cookie, $template->output;
diff --git a/opac/opac-user.pl b/opac/opac-user.pl
index 0828739..05141d6 100755
--- a/opac/opac-user.pl
+++ b/opac/opac-user.pl
@@ -313,6 +313,12 @@ if (C4::Context->preference("OPACAmazonCoverImages") or
         $template->param(JacketImages=>1);
 }
 
+$template->param(
+    OverDriveCirculation => C4::Context->preference('OverDriveCirculation') || 0,
+    overdrive_error      => $query->param('overdrive_error') || undef,
+    overdrive_tab        => $query->param('overdrive_tab') || 0,
+);
+
 my $patron_messages = Koha::Patron::Messages->search(
     {
         borrowernumber => $borrowernumber,
diff --git a/opac/svc/overdrive b/opac/svc/overdrive
new file mode 100755
index 0000000..ea20915
--- /dev/null
+++ b/opac/svc/overdrive
@@ -0,0 +1,139 @@
+#!/usr/bin/perl
+
+# script to log clicks on links to external urls
+
+# Copyright 2015 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::OverDrive;
+
+my $logger = Koha::Logger->get({ interface => 'opac' });
+my $cgi = new CGI;
+my $page_url = $cgi->referer();
+
+my ( $user, $cookie, $sessionID, $flags ) = checkauth( $cgi, 1, {}, 'opac' );
+$user && $sessionID or response_bad_request("User not logged in");
+
+my $action = $cgi->param('action') or response_bad_request("No 'action' specified");
+
+my $od = Koha::ExternalContent::OverDrive->new({ koha_session_id => $sessionID });
+my %data = (
+   is_logged_in => JSON::false,
+);
+local $@;
+eval {
+        {
+            $action eq 'login' && do {
+                $data{login_url} = $od->auth_url($page_url);
+                last;
+            };
+
+            if ($od->is_logged_in) {
+                $data{is_logged_in} = JSON::true;
+
+                $action eq 'logout' && do {
+                    $od->forget();
+                    $data{login_url} = $od->auth_url($page_url);
+                    $data{is_logged_in} = JSON::false;
+                    last;
+                };
+
+                $action eq 'account' && do {
+                    $data{account} = $od->patron;
+                    $data{checkouts} = $od->checkouts;
+                    $data{holds} = $od->holds;
+                    last;
+                };
+
+                $action eq 'checkout' && do {
+                    my $id = $cgi->param('id')
+                      or response_bad_request("No 'id' specified");
+                    my $format = $cgi->param('format');
+                    $data{action} = $od->checkout($id, $format);
+                    $data{checkouts} = $od->checkouts;
+                    $data{holds} = $od->holds;
+                    last;
+                };
+
+                $action eq 'checkout-format' && do {
+                    my $id = $cgi->param('id')
+                      or response_bad_request("No 'id' specified");
+                    my $format = $cgi->param('format')
+                      or response_bad_request("No 'format' specified");
+                    $data{action} = $od->lock_format($id, $format);
+                    $data{checkouts} = $od->checkouts;
+                    last;
+                };
+
+                $action eq 'download-url' && do {
+                    my $id = $cgi->param('id')
+                      or response_bad_request("No 'id' specified");
+                    my $format = $cgi->param('format')
+                      or response_bad_request("No 'format' specified");
+                    $data{action} = $od->checkout_download_url($id, $format, $page_url, $page_url);
+                    last;
+                };
+
+                $action eq 'place_hold' && do {
+                    my $id = $cgi->param('id')
+                      or response_bad_request("No 'id' specified");
+                    $data{action} = $od->place_hold($id);
+                    $data{holds} = $od->holds;
+                    last;
+                };
+
+                $action eq 'return' && do {
+                    my $id = $cgi->param('id')
+                      or response_bad_request("No 'id' specified");
+                    $data{action} = $od->return($id);
+                    $data{checkouts} = $od->checkouts;
+                    $data{holds} = $od->holds;
+                    last;
+                };
+
+                response_bad_request("Invalid 'action': $action");
+            }
+        }
+};
+if ($@) {
+    if ($od->is_not_authenticated_error($@)) {
+        $logger->debug("OverDrive session timeout");
+        $data{is_logged_in} = JSON::false;
+    } else {
+        $logger->error($@);
+        $data{error} = $od->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;
+}
-- 
1.9.1


More information about the Koha-patches mailing list