Bug 17602: OPAC integration of RecordedBooks

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>
This commit is contained in:
Srdjan 2016-09-23 18:13:10 +12:00 committed by Nick Clemens
parent 7825ff69c2
commit eb4be268d8
10 changed files with 748 additions and 3 deletions

View File

@ -2677,6 +2677,7 @@ button.closebtn {
#overdrive-results,
#recordedbooks-results,
#openlibrary-results {
font-weight: bold;
padding-left: 1em;

View File

@ -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 -->

View File

@ -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 %]

View File

@ -590,6 +590,7 @@
[% 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( ' ' )

View File

@ -163,10 +163,15 @@
[% 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">
@ -893,6 +898,9 @@
[% IF ( OverDriveCirculation ) %]
[% INCLUDE 'overdrive-checkout.inc' %]
[% END %]
[% IF ( RecordedBooksCirculation ) %]
[% INCLUDE 'recordedbooks-checkout.inc' %]
[% END %]
[% INCLUDE 'opac-bottom.inc' %]
@ -1065,4 +1073,20 @@
});
</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 %]

View File

@ -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);
};
}

View File

@ -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;

View File

@ -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);

View File

@ -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(

148
opac/svc/recordedbooks Executable file
View File

@ -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;
}