Bugzilla – Attachment 55677 Details for
Bug 16034
Integration with OverDrive Patron API
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
bug_16034 Add overdrive info to the users page in the public interface
bug16034-Add-overdrive-info-to-the-users-page-in-t.patch (text/plain), 25.32 KB, created by
Srdjan Jankovic
on 2016-09-19 02:52:25 UTC
(
hide
)
Description:
bug_16034 Add overdrive info to the users page in the public interface
Filename:
MIME Type:
Creator:
Srdjan Jankovic
Created:
2016-09-19 02:52:25 UTC
Size:
25.32 KB
patch
obsolete
>From f9dbf124d8b74d0b11fd5672c56e87139fcedad3 Mon Sep 17 00:00:00 2001 >From: Srdjan <srdjan@catalyst.net.nz> >Date: Tue, 8 Dec 2015 19:07:41 +1300 >Subject: [PATCH] bug_16034 Add overdrive info to the users page in the public > interface > >Signed-off-by: Jesse Weaver <jweaver@bywatersolutions.com> >--- > .../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 9aa4cc3..8f8c600 100644 >--- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt >+++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt >@@ -147,8 +147,13 @@ Using this account is not recommended because some parts of Koha will not functi > [% IF ( BORROWER_INFO.amountlessthanzero ) %]<li><a href="#opac-user-fines">Credits ([% BORROWER_INFO.amountoutstanding | $Price %])</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"> >@@ -735,6 +740,10 @@ Using this account is not recommended because some parts of Koha will not functi > </div> <!-- /.container-fluid --> > </div> <!-- /#main --> > >+[% IF ( OverDriveCirculation ) %] >+[% INCLUDE 'overdrive-checkout.inc' %] >+[% END %] >+ > [% INCLUDE 'opac-bottom.inc' %] > > >@@ -798,4 +807,18 @@ Using this account is not recommended because some parts of Koha will not functi > }); > //]]> > </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 7eb8749..dbd3820 100755 >--- a/opac/opac-user.pl >+++ b/opac/opac-user.pl >@@ -302,6 +302,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; >+} >-- >2.7.4
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 16034
:
48919
|
48920
|
48921
|
49929
|
49930
|
49931
|
53793
|
55594
|
55675
|
55676
|
55677
|
55678
|
55763
|
55764
|
55765
|
55766
|
55815
|
55816
|
57398
|
57399
|
57400
|
57401
|
58518
|
58519
|
58520
|
58521
|
58522
|
58790
|
58791
|
59055
|
59056
|
59057
|
59058
|
59392
|
59393
|
59394
|
59395
|
59713
|
59714
|
59743
|
59744
|
59745
|
59746
|
60626
|
60654
|
60789