View | Details | Raw Unified | Return to bug 16034
Collapse All | Expand All

(-)a/koha-tmpl/opac-tmpl/bootstrap/en/includes/overdrive-checkout.inc (+19 lines)
Line 0 Link Here
1
<div id="overdrive-checkout" class="modal hide" tabindex="-1" role="dialog" aria-labelledby="overdrive-checkout-label" aria-hidden="true">
2
    <div class="modal-header">
3
        <button type="button" class="closebtn" data-dismiss="modal" aria-hidden="true">×</button>
4
        <h3 id="overdrive-checkout-label">Checkout</h3>
5
    </div>
6
    <form action="#" method="post" id="overdrive-checkout-form">
7
        <div class="modal-body">
8
                <input type="hidden" name="id" value="" />
9
                <fieldset class="brief">
10
                    <label for="format">Format:</label>
11
                    <ul class="overdrive-format-list">
12
                </fieldset>
13
        </div>
14
        <div class="modal-footer">
15
            <input type="submit" class="btn btn-primary overdrive-checkout-submit" value="Checkout" />
16
            <a href="#" data-dismiss="modal" aria-hidden="true" class="cancel">Cancel</a>
17
        </div>
18
    </form> <!-- /#overdrive-checkout-form -->
19
</div>  <!-- /#overdrive-checkout  -->
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-overdrive-search.tt (-2 / +15 lines)
Lines 55-60 Link Here
55
        </div> <!-- / .container-fluid -->
55
        </div> <!-- / .container-fluid -->
56
    </div> <!-- / .main -->
56
    </div> <!-- / .main -->
57
57
58
[% INCLUDE 'overdrive-checkout.inc' %]
59
58
[% INCLUDE 'opac-bottom.inc' %]
60
[% INCLUDE 'opac-bottom.inc' %]
59
[% BLOCK jsinclude %]
61
[% BLOCK jsinclude %]
60
<script type="text/javascript" src="[% interface %]/[% theme %]/js/overdrive.js"></script>
62
<script type="text/javascript" src="[% interface %]/[% theme %]/js/overdrive.js"></script>
Lines 80-86 function fetch_availability( prod, $tr ) { Link Here
80
                $availability_summary.find( '.available' ).append( ', ' + _("waiting holds:") + ' <strong>' + data.numberOfHolds + '</strong>' );
82
                $availability_summary.find( '.available' ).append( ', ' + _("waiting holds:") + ' <strong>' + data.numberOfHolds + '</strong>' );
81
            }
83
            }
82
84
83
            $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>' );
85
            $tr.find( '.info' ).each(function() {
86
                KOHA.OverDriveCirculation.add_actions(this, data.id, data.copiesAvailable);
87
            });
84
        }
88
        }
85
    );
89
    );
86
}
90
}
Lines 179-185 $( document ).ready( function() { Link Here
179
        return false;
183
        return false;
180
    });
184
    });
181
185
182
    search( 0 );
186
    [% IF ( overdrive_error ) %]
187
    KOHA.OverDriveCirculation.display_error("#breadcrumbs", "[% overdrive_error.dquote %]");
188
    [% END %]
189
    [% IF ( loggedinusername ) %]
190
    KOHA.OverDriveCirculation.with_account_details("#breadcrumbs", function() {
191
        search( 0 );
192
    });
193
    [% ELSE %]
194
        search( 0 );
195
    [% END %]
183
} );
196
} );
184
</script>
197
</script>
185
[% END %]
198
[% END %]
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt (+23 lines)
Lines 135-142 Using this account is not recommended because some parts of Koha will not functi Link Here
135
                            [% END %]
135
                            [% END %]
136
                            [% IF ( RESERVES.count ) %]<li><a href="#opac-user-holds">Holds ([% RESERVES.count %])</a></li>[% END %]
136
                            [% IF ( RESERVES.count ) %]<li><a href="#opac-user-holds">Holds ([% RESERVES.count %])</a></li>[% END %]
137
                            [% IF Koha.Preference('ArticleRequests') && borrower.article_requests_current %]<li><a href="#opac-user-article-requests">Article requests ([% borrower.article_requests_current.count %])</a></li>[% END %]
137
                            [% IF Koha.Preference('ArticleRequests') && borrower.article_requests_current %]<li><a href="#opac-user-article-requests">Article requests ([% borrower.article_requests_current.count %])</a></li>[% END %]
138
                            [% IF ( OverDriveCirculation ) %]
139
                            <li><a href="#opac-user-overdrive">OverDrive Account</a></li>
140
                            [% END %]
138
                        </ul>
141
                        </ul>
139
142
143
                        <div id="opac-user-overdrive">
144
                        </div>
140
                        <div id="opac-user-checkouts">
145
                        <div id="opac-user-checkouts">
141
                            [% IF ( issues_count ) %]
146
                            [% IF ( issues_count ) %]
142
                                <form id="renewselected" action="/cgi-bin/koha/opac-renew.pl" method="post">
147
                                <form id="renewselected" action="/cgi-bin/koha/opac-renew.pl" method="post">
Lines 824-829 Using this account is not recommended because some parts of Koha will not functi Link Here
824
    </div> <!-- /.container-fluid -->
829
    </div> <!-- /.container-fluid -->
825
</div> <!-- /#main -->
830
</div> <!-- /#main -->
826
831
832
[% IF ( OverDriveCirculation ) %]
833
[% INCLUDE 'overdrive-checkout.inc' %]
834
[% END %]
835
827
[% INCLUDE 'opac-bottom.inc' %]
836
[% INCLUDE 'opac-bottom.inc' %]
828
837
829
838
Lines 887-890 Using this account is not recommended because some parts of Koha will not functi Link Here
887
        });
896
        });
888
        //]]>
897
        //]]>
889
    </script>
898
    </script>
899
    <script type="text/javascript" src="[% interface %]//[% theme %]/js/overdrive.js"></script>
900
    <script type="text/JavaScript">
901
    $(document).ready(function() {
902
    [% IF ( overdrive_error ) %]
903
        KOHA.OverDriveCirculation.display_error("#opac-user-overdrive", "[% overdrive_error.dquote %]");
904
    [% END %]
905
    [% IF ( overdrive_tab ) %]
906
        $("#opac-user-views").tabs("select", "#opac-user-overdrive");
907
    [% END %]
908
        $("#opac-user-overdrive").each( function() {
909
            KOHA.OverDriveCirculation.display_account_details(this);
910
        } );
911
    });
912
    </script>
890
[% END %]
913
[% END %]
(-)a/koha-tmpl/opac-tmpl/bootstrap/js/overdrive.js (+377 lines)
Lines 59-61 KOHA.OverDrive = ( function() { Link Here
59
        }
59
        }
60
    };
60
    };
61
} )();
61
} )();
62
63
KOHA.OverDriveCirculation = new function() {
64
    var svc_url = '/cgi-bin/koha/svc/overdrive';
65
66
    var error_div = $('<div class="overdrive-error">');
67
    function display_error ( error ) {
68
        error_div.text(error);
69
    }
70
71
    var login_link = $('<a href="#">')
72
        .click(function(e) {
73
            e.preventDefault();
74
            login(window.open());
75
        })
76
        .text(_("Login to OverDrive account"));
77
    var login_div = $('<div class="overdrive-login">').append(login_link);
78
79
    var details = null;
80
81
    function is_logged_in() {
82
        return details ? details.is_logged_in : false;
83
    }
84
85
    var checkout_popup = null;
86
    $( document ).ready(function() {
87
        var p = window.opener;
88
        if (p) {
89
            cb = p.refresh_overdrive_account_details;
90
            if (cb) {
91
                cb();
92
            } else {
93
                p.location.reload();
94
            }
95
            window.close();
96
        }
97
        checkout_popup = $("#overdrive-checkout");
98
    });
99
100
    function display_account (container, data) {
101
        if (!data.is_logged_in) {
102
            $(container).append(login_div);
103
            return;
104
        }
105
106
        var overdrive_link = $('<a href="https://www.overdrive.com/account/" target="overdrive-account" class="overdrive-link" style="float:right">')
107
            .text("OverDrive Account Page");
108
        $(container).append(overdrive_link);
109
110
        var logout_link = $('<a href="#logout" class="overdrive-logout" style="float:left">')
111
            .click(function(e) {
112
                e.preventDefault();
113
                $(container).empty().append(error_div);
114
                logout(function(data) {
115
                    display_account(container, data);
116
                });
117
            }).text(_("Logout from OverDrive account"));
118
        $(container).append(logout_link);
119
        $(container).append('<br style="clear:both;"/>');
120
121
        if (data.checkouts) {
122
            var checkouts_div = $('<div class="overdrive-div">').html('<h3>' + _("Checkouts") + '</h3>');
123
            var checkouts_list = $('<ul class="overdrive-list">');
124
            data.checkouts.items.forEach(function(item) {
125
                item_line(checkouts_list, item);
126
            });
127
            checkouts_div.append(checkouts_list);
128
            $(container).append(checkouts_div);
129
        }
130
131
        if (data.holds) {
132
            var holds_div = $('<div class="overdrive-div">').html('<h3>' + _("Holds") + '</h3>');
133
            var holds_list = $('<ul class="overdrive-list">');
134
            data.holds.items.forEach(function(item) {
135
                item_line(holds_list, item);
136
            });
137
            holds_div.append(holds_list);
138
            $(container).append(holds_div);
139
        }
140
    }
141
142
    function item_line(ul_el, item) {
143
        var line = $('<li class="overdrive-item">');
144
        if (item.images) {
145
            var thumb_url = item.images.thumbnail;
146
            if (thumb_url) {
147
                $('<img class="overdrive-item-thumbnail">')
148
                    .attr("src", thumb_url)
149
                    .appendTo(line);
150
            }
151
        }
152
        $('<div class="overdrive-item-title">')
153
            .text(item.title)
154
            .appendTo(line);
155
        $('<div class="overdrive-item-subtitle">')
156
            .html(item.subtitle)
157
            .appendTo(line);
158
        $('<div class="overdrive-item-author">')
159
            .text(item.author)
160
            .appendTo(line);
161
        var actions = $('<span class="actions">');
162
        display_actions(actions, item.id);
163
        $('<div id="action_'+item.id+'" class="actions-menu">')
164
            .append(actions)
165
            .appendTo(line);
166
167
        $(ul_el).append(line);
168
    }
169
170
    function svc_ajax ( method, params, success_callback ) {
171
        return $.ajax({
172
            method: method,
173
            dataType: "json",
174
            url: svc_url,
175
            data: params,
176
            success: function (data) {
177
                if (data.error) {
178
                    display_error(data.error);
179
                }
180
                success_callback(data);
181
            },
182
            error: function(jqXHR, textStatus, errorThrown) {
183
                display_error(errorThrown);
184
            }
185
        });
186
    }
187
188
    function load_account_details ( callback ) {
189
        svc_ajax('get', { action: "account" }, function(data) {
190
            details = data;
191
            callback(data);
192
        });
193
    }
194
195
    function login(w) {
196
        svc_ajax('get', { action: "login" }, function(data) {
197
            details = null;
198
            if (data.login_url) {
199
                w.location = data.login_url;
200
            }
201
        });
202
    }
203
204
    function logout (callback) {
205
        svc_ajax('post', { action: "logout" }, function(data) {
206
            details = null;
207
            callback(data);
208
        });
209
    }
210
211
    function item_action (params, el, copies_available) {
212
        var id = params.id;
213
        svc_ajax('post', params, function(data) {
214
            if (data.checkouts) {
215
                details.checkouts = data.checkouts;
216
            }
217
            if (data.holds) {
218
                details.holds = data.holds;
219
            }
220
            display_actions(el, id, copies_available);
221
        });
222
    }
223
224
    function item_is_checked_out (id) {
225
        if ( !(details && details.checkouts) ) {
226
            return null;
227
        }
228
        var id_uc = id.toUpperCase();
229
        var items = details.checkouts.items;
230
        for (var i = 0; i < items.length; i++) {
231
            if ( items[i].id.toUpperCase() == id_uc ) {
232
                return items[i];
233
            }
234
        }
235
        return null;
236
    }
237
238
    function item_is_on_hold (id) {
239
        if ( !(details && details.holds) ) {
240
            return false;
241
        }
242
        var id_uc = id.toUpperCase();
243
        var items = details.holds.items;
244
        for (var i = 0; i < items.length; i++) {
245
            if ( items[i].id.toUpperCase() == id_uc ) {
246
                return items[i];
247
            }
248
        }
249
        return null;
250
    }
251
252
    function display_actions(el, id, copies_available) {
253
        $(el).empty();
254
        if (is_logged_in()) {
255
256
            var item = item_is_checked_out(id);
257
            if (item) {
258
                var expires = new Date(item.expires);
259
                $('<span class="overdrive-item-status">')
260
                    .text(_("Checked out until") + " " + expires.toLocaleString())
261
                    .appendTo(el);
262
                $(el).append(" ");
263
264
                if (item.format) {
265
                    var download = $('<a href="#">').appendTo(el);
266
                    decorate_button(download, _("Download") + " " + item.format);
267
                    svc_ajax('get', {action: "download-url", id: id, format: item.format}, function(data) {
268
                        download.attr("href", data.action);
269
                    });
270
                    $(el).append(" ");
271
                }
272
273
                if (item.formats) {
274
                    var lockable_formats = [];
275
                    for (var f in item.formats) {
276
                        if (f == item.format) continue;
277
278
                        if (item.formats[f]) {
279
                            var access = $('<a target="_blank">').appendTo(el);
280
                            decorate_button(access, _("Access online") + " " + f);
281
                            svc_ajax('get', {action: "download-url", id: id, format: f}, function(data) {
282
                                access.attr("href", data.action);
283
                            });
284
                            $(el).append(" ");
285
                        }
286
                        else {
287
                            lockable_formats.push(f);
288
                        }
289
                    }
290
                    if (lockable_formats.length > 0 && checkout_popup) {
291
                        $(el).append( ajax_button(_("Download as"), function() {
292
                            checkout_format(el, id, lockable_formats, copies_available);
293
                        }) ).append(" ");
294
                    }
295
                }
296
297
                if (item.format) return item;
298
299
                $(el).append( ajax_button(_("Check in"), function() {
300
                    if( confirm(_("Are you sure you want to return this item?")) ) {
301
                        item_action({action: "return", id: id}, el, copies_available + 1);
302
                    }
303
                }) );
304
305
                return item;
306
            }
307
308
            item = item_is_on_hold(id);
309
            if (item) {
310
                $('<span class="overdrive-status">')
311
                    .text(_("On hold"))
312
                    .appendTo(el);
313
                $(el).append(" ");
314
            }
315
316
            if(copies_available && checkout_popup) {
317
                $(el).append( ajax_button(_("Check out"), function() {
318
                    if( confirm(_("Are you sure you want to checkout this item?")) ) {
319
                        svc_ajax('post', {action: "checkout", id: id}, function(data) {
320
                            if (data.checkouts) {
321
                                details.checkouts = data.checkouts;
322
                            }
323
                            if (data.holds) {
324
                                details.holds = data.holds;
325
                            }
326
                            item = display_actions(el, id, copies_available - 1);
327
                            if (item && item.formats && !item.format) {
328
                                var has_available_formats = false;
329
                                var lockable_formats = [];
330
                                for (var f in item.formats) {
331
                                    if (item.formats[f]) {
332
                                        has_available_formats = true;
333
                                        break;
334
                                    }
335
                                    lockable_formats.push(f);
336
                                }
337
338
                                if (!has_available_formats) {
339
                                    checkout_format(el, id, lockable_formats, copies_available - 1);
340
                                }
341
                            }
342
                        });
343
                    }
344
                }) );
345
            }
346
            else if (!item) {
347
                $(el).append( ajax_button(_("Place hold"), function() {
348
                    item_action({action: "place-hold", id: id}, el, copies_available);
349
                }) );
350
            }
351
352
            if (item) {
353
                $(el).append( ajax_button(_("Cancel"), function() {
354
                    if( confirm(_("Are you sure you want to cancel this hold?")) ) {
355
                        item_action({action: "remove-hold", id: id}, el, copies_available);
356
                    }
357
                }) );
358
            }
359
            return item;
360
        }
361
    }
362
363
    function ajax_button(label, on_click) {
364
        var button = $('<a href="#">')
365
            .click(function(e) {
366
                e.preventDefault();
367
                on_click();
368
            });
369
        decorate_button(button, label);
370
        return button;
371
    }
372
373
    function decorate_button(button, label) {
374
        $(button)
375
            .addClass("btn btn-primary btn-mini")
376
            .css("color","white")
377
            .text(label);
378
    }
379
380
    function checkout_format(el, id, formats, copies_available) {
381
        if (formats.length == 0) {
382
            alert(_("Item cannot be checked out - no available formats"));
383
            return false;
384
        }
385
386
        var checkout_format_list = checkout_popup.find("ul.overdrive-format-list").empty();
387
        formats.forEach(function (item) {
388
            var li = $('<li>').appendTo(checkout_format_list);
389
            $('<input name="checkout-format" type="radio">')
390
                .val(item)
391
                .appendTo(li);
392
            li.append(item);
393
        });
394
        checkout_popup.modal("show");
395
        checkout_popup.find(".overdrive-checkout-submit").click(function(e) {
396
            e.preventDefault();
397
            var format = checkout_format_list.find("input[type='radio'][name='checkout-format']:checked").val();
398
            item_action({action: "checkout-format", id: id, format: format}, el, copies_available);
399
            $(this).unbind( e );
400
            checkout_popup.modal("hide");
401
        });
402
    }
403
404
    this.with_account_details = function( el, callback ) {
405
        $(el).append(error_div);
406
        load_account_details(function(data) {
407
            if (!data.is_logged_in) {
408
                $(el).append(login_div);
409
            }
410
            callback(data);
411
        });
412
    }
413
414
    this.display_account_details = function( el ) {
415
        window.refresh_overdrive_account_details = function () {
416
            KOHA.OverDriveCirculation.display_account_details( el );
417
        }
418
        $(el).empty().append(error_div);
419
        load_account_details(function(data) {
420
            display_account(el, data);
421
        });
422
    };
423
424
    this.display_error = function( el, error ) {
425
        $(el).empty().append(error_div);
426
        display_error(error);
427
    };
428
429
    this.is_logged_in = is_logged_in;
430
431
    this.add_actions = function(el, id, copies_available) {
432
        var actions = $('<span class="actions">');
433
        display_actions(actions, id, copies_available);
434
        $('<div id="action_'+id+'" class="actions-menu">')
435
            .append(actions)
436
            .appendTo(el);
437
    };
438
}
(-)a/opac/opac-overdrive-search.pl (+1 lines)
Lines 41-45 $template->{'VARS'}->{'q'} = $cgi->param('q'); Link Here
41
$template->{'VARS'}->{'limit'} = C4::Context->preference('OPACnumSearchResults') || 20;
41
$template->{'VARS'}->{'limit'} = C4::Context->preference('OPACnumSearchResults') || 20;
42
$template->{'VARS'}->{'OPACnumSearchResults'} = C4::Context->preference('OPACnumSearchResults') || 20;
42
$template->{'VARS'}->{'OPACnumSearchResults'} = C4::Context->preference('OPACnumSearchResults') || 20;
43
$template->{'VARS'}->{'OverDriveLibraryID'} = C4::Context->preference('OverDriveLibraryID');
43
$template->{'VARS'}->{'OverDriveLibraryID'} = C4::Context->preference('OverDriveLibraryID');
44
$template->param(overdrive_error => $cgi->param('overdrive_error'));
44
45
45
output_html_with_http_headers $cgi, $cookie, $template->output;
46
output_html_with_http_headers $cgi, $cookie, $template->output;
(-)a/opac/opac-user.pl (+6 lines)
Lines 306-311 if (C4::Context->preference("OPACAmazonCoverImages") or Link Here
306
        $template->param(JacketImages=>1);
306
        $template->param(JacketImages=>1);
307
}
307
}
308
308
309
$template->param(
310
    OverDriveCirculation => C4::Context->preference('OverDriveCirculation') || 0,
311
    overdrive_error      => $query->param('overdrive_error') || undef,
312
    overdrive_tab        => $query->param('overdrive_tab') || 0,
313
);
314
309
my $patron_messages = Koha::Patron::Messages->search(
315
my $patron_messages = Koha::Patron::Messages->search(
310
    {
316
    {
311
        borrowernumber => $borrowernumber,
317
        borrowernumber => $borrowernumber,
(-)a/opac/svc/overdrive (-1 / +150 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
# script to action OverDrive API calls
4
5
# Copyright 2015 Catalyst IT
6
# This file is part of Koha.
7
#
8
# Koha is free software; you can redistribute it and/or modify it
9
# under the terms of the GNU General Public License as published by
10
# the Free Software Foundation; either version 3 of the License, or
11
# (at your option) any later version.
12
#
13
# Koha is distributed in the hope that it will be useful, but
14
# WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
# GNU General Public License for more details.
17
#
18
# You should have received a copy of the GNU General Public License
19
# along with Koha; if not, see <http://www.gnu.org/licenses>.
20
21
use Modern::Perl;
22
use CGI qw ( -utf8 );
23
use JSON qw(encode_json);
24
use C4::Auth qw(checkauth);
25
use C4::Output;
26
use Koha::Logger;
27
use Koha::ExternalContent::OverDrive;
28
29
my $logger = Koha::Logger->get({ interface => 'opac' });
30
our $cgi = new CGI;
31
my $page_url = $cgi->referer();
32
33
my ( $user, $cookie, $sessionID, $flags ) = checkauth( $cgi, 1, {}, 'opac' );
34
$user && $sessionID or response_bad_request("User not logged in");
35
36
my $action = $cgi->param('action') or response_bad_request("No 'action' specified");
37
38
my $od = Koha::ExternalContent::OverDrive->new({ koha_session_id => $sessionID });
39
my %data = (
40
   is_logged_in => JSON::false,
41
);
42
local $@;
43
eval {
44
        {
45
            $action eq 'login' && do {
46
                $data{login_url} = $od->auth_url($page_url);
47
                last;
48
            };
49
50
            if ($od->is_logged_in) {
51
                $data{is_logged_in} = JSON::true;
52
53
                $action eq 'logout' && do {
54
                    $od->forget();
55
                    $data{login_url} = $od->auth_url($page_url);
56
                    $data{is_logged_in} = JSON::false;
57
                    last;
58
                };
59
60
                $action eq 'account' && do {
61
                    $data{account} = $od->patron;
62
                    $data{checkouts} = $od->checkouts;
63
                    $data{holds} = $od->holds;
64
                    last;
65
                };
66
67
                $action eq 'checkout' && do {
68
                    my $id = $cgi->param('id')
69
                      or response_bad_request("No 'id' specified");
70
                    my $format = $cgi->param('format');
71
                    $data{action} = $od->checkout($id, $format);
72
                    $data{checkouts} = $od->checkouts;
73
                    $data{holds} = $od->holds;
74
                    last;
75
                };
76
77
                $action eq 'checkout-format' && do {
78
                    my $id = $cgi->param('id')
79
                      or response_bad_request("No 'id' specified");
80
                    my $format = $cgi->param('format')
81
                      or response_bad_request("No 'format' specified");
82
                    $data{action} = $od->lock_format($id, $format);
83
                    $data{checkouts} = $od->checkouts;
84
                    last;
85
                };
86
87
                $action eq 'download-url' && do {
88
                    my $id = $cgi->param('id')
89
                      or response_bad_request("No 'id' specified");
90
                    my $format = $cgi->param('format')
91
                      or response_bad_request("No 'format' specified");
92
                    $data{action} = $od->checkout_download_url($id, $format, $page_url, $page_url);
93
                    last;
94
                };
95
96
                $action eq 'return' && do {
97
                    my $id = $cgi->param('id')
98
                      or response_bad_request("No 'id' specified");
99
                    local $@;
100
                    $data{action} = eval { $od->return($id) };
101
                    $data{action} = $@ if $@;
102
                    $data{checkouts} = $od->checkouts;
103
                    last;
104
                };
105
106
                $action eq 'place-hold' && do {
107
                    my $id = $cgi->param('id')
108
                      or response_bad_request("No 'id' specified");
109
                    $data{action} = $od->place_hold($id);
110
                    $data{holds} = $od->holds;
111
                    last;
112
                };
113
114
                $action eq 'remove-hold' && do {
115
                    my $id = $cgi->param('id')
116
                      or response_bad_request("No 'id' specified");
117
                    local $@;
118
                    $data{action} = eval { $od->remove_hold($id) };
119
                    $data{action} = $@ if $@;
120
                    $data{holds} = $od->holds;
121
                    last;
122
                };
123
124
                response_bad_request("Invalid 'action': $action");
125
            }
126
        }
127
};
128
if ($@) {
129
    if ($od->is_not_authenticated_error("$@")) {
130
        $logger->debug("OverDrive session timeout");
131
        $data{is_logged_in} = JSON::false;
132
    } else {
133
        $logger->error($@);
134
        $data{error} = $od->error_message("$@");
135
    }
136
}
137
138
response(\%data);
139
140
141
sub response_bad_request {
142
    my ($error) = @_;
143
    response({error => $error}, "400 $error");
144
}
145
sub response {
146
    my ($data, $status_line) = @_;
147
    $status_line ||= "200 OK";
148
    output_with_http_headers $cgi, undef, encode_json($data), 'json', $status_line;
149
    exit;
150
}

Return to bug 16034