--- /dev/null
+package Koha::Illrequest::Availability;
+
+# Copyright 2019 PTFS Europe Ltd
+#
+# 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, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+use Modern::Perl;
+
+use JSON;
+use MIME::Base64 qw( encode_base64 );
+use URI::Escape qw ( uri_escape );
+
+use Koha::Plugins;
+
+=head1 NAME
+
+Koha::Illrequest::Availability - Koha ILL Availability Searching
+
+=head1 SYNOPSIS
+
+Object-oriented class that provides availability searching via
+availability plugins
+
+=head1 DESCRIPTION
+
+This class provides the ability to identify and fetch API services
+that can be used to search for item availability
+
+=head1 API
+
+=head2 Class Methods
+
+=head3 new
+
+ my $availability = Koha::Illrequest::Logger->new($metadata);
+
+Create a new Koha::Illrequest::Availability object.
+We also store the metadata to be used for searching
+
+=cut
+
+sub new {
+ my ( $class, $metadata ) = @_;
+ my $self = {};
+
+ $self->{metadata} = $metadata;
+
+ bless $self, $class;
+
+ return $self;
+}
+
+=head3 get_services
+
+ my $services = Koha::Illrequest::Availability->get_services($params);
+
+Given our metadata, iterate plugins with the right method and
+check if they can service our request and, if so, return an arrayref
+of services. Optionally accept a hashref specifying additional filter
+parameters
+
+=cut
+
+sub get_services {
+ my ( $self, $params ) = @_;
+
+ my $plugin_filter = {
+ method => 'ill_availability_services'
+ };
+
+ if ($params->{metadata}) {
+ $plugin_filter->{metadata} = $params->{metadata};
+ }
+
+ my @candidates = Koha::Plugins->new()->GetPlugins($plugin_filter);
+ my @services = ();
+ foreach my $plugin(@candidates) {
+ my $valid_service = $plugin->ill_availability_services({
+ metadata => $self->{metadata},
+ ui_context => $params->{ui_context}
+ });
+ push @services, $valid_service if $valid_service;
+ }
+
+ return \@services;
+}
+
+=head3 prep_metadata
+
+ my $prepared = Koha::Illrequest::Availability->prep_metadata($metadata);
+
+Given our metadata, return a string representing that metadata that can be
+passed in a URL (encoded in JSON then Base64 encoded)
+
+=cut
+
+sub prep_metadata {
+ my ( $self, $metadata ) = @_;
+
+ # We sort the metadata hashref by key before encoding it, primarily
+ # so this function returns something predictable that we can test!
+ my $json = JSON->new;
+ $json->canonical([1]);
+ return uri_escape(encode_base64($json->encode($metadata)));
+}
+
+=head1 AUTHOR
+
+Andrew Isherwood <andrew.isherwood@ptfs-europe.com>
+
+=cut
+
+1;
use Koha::AuthorisedValues;
use Koha::Illcomment;
use Koha::Illrequests;
+use Koha::Illrequest::Availability;
use Koha::Libraries;
use Koha::Token;
use Try::Tiny;
use URI::Escape;
+use JSON;
our $cgi = CGI->new;
my $illRequests = Koha::Illrequests->new;
} elsif ( $op eq 'create' ) {
# We're in the process of creating a request
my $request = Koha::Illrequest->new->load_backend( $params->{backend} );
- my $backend_result = $request->backend_create($params);
- $template->param(
- whole => $backend_result,
- request => $request
- );
- handle_commit_maybe($backend_result, $request);
+ # Does this backend enable us to insert an availability stage and should
+ # we? If not, proceed as normal.
+ if (
+ C4::Context->preference("ILLCheckAvailability") &&
+ $request->_backend_capability(
+ 'should_display_availability',
+ $params
+ ) &&
+ # If the user has elected to continue with the request despite
+ # having viewed availability info, this flag will be set
+ !$params->{checked_availability}
+ ) {
+ # Establish which of the installed availability providers
+ # can service our metadata
+ my $availability = Koha::Illrequest::Availability->new($params);
+ my $services = $availability->get_services({
+ ui_context => 'staff'
+ });
+ if (scalar @{$services} > 0) {
+ # Modify our method so we use the correct part of the
+ # template
+ $op = 'availability';
+ $params->{method} = 'availability';
+ delete $params->{stage};
+ # Prepare the metadata we're sending them
+ my $metadata = $availability->prep_metadata($params);
+ $template->param(
+ whole => $params,
+ metadata => $metadata,
+ services_json => scalar encode_json($services),
+ services => $services
+ );
+ } else {
+ # No services can process this metadata, so continue as normal
+ my $backend_result = $request->backend_create($params);
+ $template->param(
+ whole => $backend_result,
+ request => $request
+ );
+ handle_commit_maybe($backend_result, $request);
+ }
+ } else {
+ my $backend_result = $request->backend_create($params);
+ $template->param(
+ whole => $backend_result,
+ request => $request
+ );
+ handle_commit_maybe($backend_result, $request);
+ }
} elsif ( $op eq 'migrate' ) {
# We're in the process of migrating a request
$request = Koha::Illrequests->find($params->{illrequest_id});
$params->{current_branchcode} = C4::Context->mybranch;
$backend_result = $request->generic_confirm($params);
+
$template->param(
whole => $backend_result,
request => $request,
);
+
+ # Prepare availability searching, if required
+ # Get the definition for the z39.50 plugin
+ my $availability = Koha::Illrequest::Availability->new($request->metadata);
+ my $services = $availability->get_services({
+ ui_context => 'partners',
+ metadata => {
+ name => 'ILL availability - z39.50'
+ }
+ });
+ # Only pass availability searching stuff to the template if
+ # appropriate
+ if (
+ C4::Context->preference('ILLCheckAvailability') &&
+ scalar @{$services} > 0
+ ) {
+ my $metadata = $availability->prep_metadata($request->metadata);
+ $template->param( metadata => $metadata );
+ $template->param(
+ services_json => scalar encode_json($services)
+ );
+ $template->param( services => $services );
+ }
+
$template->param( error => $params->{error} )
if $params->{error};
}
top: 50%;
transform: translateY(-50%);
}
+
+ #generic_confirm_search {
+ display: block;
+ visibility: hidden;
+ margin: 1em 0 1em 10em;
+ }
+
+ #partnerSearch {
+ .modal-dialog {
+ width: 50vw;
+ }
+ .modal-body {
+ max-height: 70vh;
+ }
+ }
}
.ill-view-panel {
margin: 20px 0 30px 0;
}
+.ill_availability_sourcename {
+ margin-top: 20px;
+}
+
#stockrotation {
h3 {
margin: 30px 0 10px 0;
--- /dev/null
+<div>
+ <div>[% service.name %]</div>
+ <table class="ill-availability" id="[% service.id %]">
+ <thead id="[% service.id %]-header">
+ <tr>
+ <th>Source</th>
+ <th>Title</th>
+ <th>Author</th>
+ <th>ISBN</th>
+ <th>ISSN</th>
+ <th>Date</th>
+ </tr>
+ </thead>
+ <tbody id="[% service.id %]-body">
+ </tbody>
+ </table>
+</div>
<select size="5" multiple="true" id="partners" name="partners" required="required">
[% FOREACH partner IN whole.value.partners %]
[% IF partner.email && partner.email.length > 0 %]
- <option value="[% partner.email | html %]">
+ <option data-partner-id="[% partner.id | html %]" value=[% partner.email | html %]>
[% partner.branchcode _ " - " _ partner.surname %]
</option>
[% END %]
[% END %]
</select>
-
+ [% IF Koha.Preference('ILLCheckAvailability') %]
+ <button type="button" id="generic_confirm_search">Search selected partners</button>
+ [% END %]
</li>
<li>
<label for="subject" class="required">Subject line:</label>
<span><a href="[% ill_url | url %]" title="Return to request details">Cancel</a></span>
</fieldset>
</form>
+ [% IF Koha.Preference('ILLCheckAvailability') %]
+ <div id="partnerSearch" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="partnerSearchLabel" aria-hidden="true">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="closebtn" data-dismiss="modal" aria-hidden="true">×</button>
+ <h3 id="partnerSearchLabel"> Search partners</h3>
+ </div>
+ <div class="modal-body">
+ [% FOR service IN services %]
+ <h4 class="ill_availability_sourcename">[% service.plugin %]</h4>
+ [% INCLUDE 'ill-availability-table.inc' service=service %]
+ [% END %]
+ <span id="service_id_restrict" data-service_id_restrict_plugin="ILL availability - z39.50" data-service_id_restrict_ids=""></span>
+ </div>
+ <div class="modal-footer">
+ <button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Close</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ [% END %]
+
[% ELSE %]
<fieldset class="rows">
<legend>Interlibrary loan request details</legend>
[% INCLUDE 'ill-list-table.inc' %]
</div> <!-- /#results -->
+ [% ELSIF query_type == 'availability' %]
+ <!-- availability -->
+ <h1>Availability</h1>
+ <div id="results">
+ <h3>Displaying availability results</h3>
+ <form method="POST" action="/cgi-bin/koha/ill/ill-requests.pl">
+ [% FOREACH key IN whole.keys %]
+ [% value = whole.$key %]
+ [% IF key != 'method' && key != 'custom_key' && key != 'custom_value' %]
+ <input type="hidden" name="[% key | html %]" value="[% value | html %]">
+ [% END %]
+ [% END %]
+ [% custom_keys = whole.custom_key.split('\0') %]
+ [% custom_values = whole.custom_value.split('\0') %]
+ [% i = 0 %]
+ [% FOREACH custom_key IN custom_keys %]
+ <input type="hidden" name="custom_key" value="[% custom_key %]">
+ <input type="hidden" name="custom_value" value="[% custom_values.$i %]">
+ [% i = i + 1 %]
+ [% END %]
+ <input type="hidden" name="method" value="create">
+ <input type="hidden" name="stage" value="form">
+ <input type="hidden" name="checked_availability" value="1">
+ <div id="continue-request-row" class="alert">
+ If you can't find what you are looking for, you can
+ <button class="button" type="submit">continue creating your request</button> or
+ <a href="/cgi-bin/koha/ill/ill-requests.pl">cancel your request</a>
+ </div>
+ </form>
+ [% FOR service IN services %]
+ <h4 class="ill_availability_sourcename">[% service.plugin %]</h4>
+ [% INCLUDE 'ill-availability-table.inc' service=service %]
+ [% END %]
+ </div>
[% ELSE %]
<!-- Custom Backend Action -->
[% PROCESS $whole.template %]
}).on("change", function(e, value) {
if ( ! is_valid_date( $(this).val() ) ) {$(this).val("");}
});
+ [% IF services_json.length > 0 %]
+ var services = [% services_json | $raw %];
+ [% ELSE %]
+ var services = [];
+ [% END %]
+ [% IF metadata.length > 0 %]
+ var metadata = "[% metadata | $raw %]";
+ [% END %]
</script>
<script>
$('#ill_checkout_inhouse_select').on('change', function() {
</script>
[% INCLUDE 'ill-list-table-strings.inc' %]
[% Asset.js("js/ill-list-table.js") | $raw %]
+ [% IF (query_type == 'availability' || query_type == 'generic_confirm') && Koha.Preference('ILLCheckAvailability') %]
+ [% Asset.js("js/ill-availability.js") | $raw %]
+ [% END %]
+ [% IF query_type == 'availability' && Koha.Preference('ILLCheckAvailability') %]
+ <script>
+ $(document).ready(function() {
+ window.doSearch();
+ });
+ </script>
+ [% END %]
+ [% IF query_type == 'generic_confirm' && Koha.Preference('ILLCheckAvailability') %]
+ [% Asset.js("js/ill-availability-partner.js") | $raw %]
+ [% END %]
[% END %]
[% TRY %]
<li><a href="/cgi-bin/koha/plugins/plugins-home.pl?method=to_marc">View MARC conversion plugins</a></li>
<li><a href="/cgi-bin/koha/plugins/plugins-home.pl?method=opac_online_payment">View online payment plugins</a></li>
<li><a href="/cgi-bin/koha/plugins/plugins-home.pl?method=intranet_catalog_biblio_enhancements">View intranet catalog biblio enhancement plugins</a></li>
+ <li><a href="/cgi-bin/koha/plugins/plugins-home.pl?method=ill_availability_services">View ILL availability plugins</a></li>
</ul>
</div>
--- /dev/null
+$(document).ready(function() {
+ $('#partners').change(function() {
+ var selected = [];
+ $('#partners option:selected').each(function() {
+ selected.push($(this).data('partner-id'));
+ });
+ if (selected.length > 0) {
+ $('#generic_confirm_search').css('visibility', 'initial');
+ } else {
+ $('#generic_confirm_search').css('visibility', 'hidden');
+ }
+ $('#service_id_restrict').
+ attr('data-service_id_restrict_ids', selected.join('|'));
+ });
+ $('#generic_confirm_search').click(function(e) {
+ $('#partnerSearch').modal({show:true});
+ });
+ $('#partnerSearch').on('show.bs.modal', function() {
+ doSearch();
+ });
+ $('#partnerSearch').on('hide.bs.modal', function() {
+ $.fn.dataTable.tables({ api: true }).destroy();
+ });
+});
--- /dev/null
+$(document).ready(function() {
+
+ window.doSearch = function() {
+ // In case the source doesn't supply data required for DT to calculate
+ // pagination, we need to do it ourselves
+ var ownPagination = false;
+ var directionSet = false;
+ var start = 0;
+ var forward = true; // true == forward, false == backwards
+ // Arbitrary starting value, it will be corrected by the first
+ // page of results
+ var pageSize = 20;
+
+ var tableTmpl = {
+ ajax: {
+ cache: true, // Prevent DT appending a "_" cache param
+ },
+ columns: [
+ // defaultContent prevents DT from choking if
+ // the API response doesn't return a column
+ {
+ title: 'Source',
+ data: 'source',
+ defaultContent: ''
+ },
+ {
+ data: 'title',
+ defaultContent: ''
+ },
+ {
+ data: 'author',
+ defaultContent: ''
+ },
+ {
+ data: 'isbn',
+ defaultContent: ''
+ },
+ {
+ data: 'issn',
+ defaultContent: ''
+ },
+ {
+ data: 'date',
+ defaultContent: ''
+ }
+ ]
+ };
+
+ // render functions don't get copied across when we make a dereferenced
+ // copy of them, so we have to reattach them once we have a copy
+ // Here we store them
+ var renders = {
+ title: function(data, type, row) {
+ return row.url ?
+ '<a href="'+row.url+'" target="_blank">'+row.title+'</a>' :
+ row.title;
+ },
+ source: function(data, type, row) {
+ return row.opac_url ?
+ '<a href="'+row.opac_url+'" target="_blank">'+row.source+'</a>' :
+ row.source;
+ }
+ };
+
+ services.forEach(function(service) {
+ // Create a deferenced copy of our table definition object
+ var tableDef = JSON.parse(JSON.stringify(tableTmpl));
+ // Iterate the table's columns array and add render functions
+ // as necessary
+ tableDef.columns.forEach(function(column) {
+ if (renders[column.data]) {
+ column.render = renders[column.data];
+ }
+ });
+ tableDef.ajax.dataSrc = function(data) {
+ var results = data.results.search_results;
+ // The source appears to be returning it's own pagination
+ // data
+ if (
+ data.hasOwnProperty('recordsFiltered') ||
+ data.hasOwnProperty('recordsTotal')
+ ) {
+ return results;
+ }
+ // Set up our own pagination values based on what we just
+ // got back
+ ownPagination = true;
+ directionSet = false;
+ pageSize = results.length;
+ // These values are completely arbitrary, but they enable
+ // us to display pagination links
+ data.recordsFiltered = 5000,
+ data.recordsTotal = 5000;
+
+ return results;
+ };
+ tableDef.ajax.data = function(data) {
+ // Datatables sends a bunch of superfluous params
+ // that we don't want to litter our API schema
+ // with, so just remove them from the request
+ if (data.hasOwnProperty('columns')) {
+ delete data.columns;
+ }
+ if (data.hasOwnProperty('draw')) {
+ delete data.draw;
+ }
+ if (data.hasOwnProperty('order')) {
+ delete data.order;
+ }
+ if (data.hasOwnProperty('search')) {
+ delete data.search;
+ }
+ // If we're handling our own pagination, set the properties
+ // that DT will send in the request
+ if (ownPagination) {
+ start = forward ? start + pageSize : start - pageSize;
+ data.start = start;
+ data['length'] = pageSize;
+ }
+ // We may need to restrict the service IDs being queries, this
+ // needs to be handled in the plugin's API module
+ var restrict = $('#service_id_restrict').
+ attr('data-service_id_restrict_ids');
+ if (restrict && restrict.length > 0) {
+ data.restrict = restrict;
+ }
+ };
+ // Add any datatables config options passed from the service
+ // to the table definition
+ tableDef.ajax.url = service.endpoint + metadata;
+ if (service.hasOwnProperty('datatablesConfig')) {
+ var conf = service.datatablesConfig;
+ for (var key in conf) {
+ // The config from the service definition comes from a Perl
+ // hashref, therefore can't contain true/false, so we
+ // special case it
+ if (conf.hasOwnProperty(key)) {
+ if (conf[key] == 'false') {
+ // Special case false values
+ tableDef[key] = false;
+ } else if (conf[key] == 'true') {
+ // Special case true values
+ tableDef[key] = true;
+ } else {
+ // Copy the property value
+ tableDef[key] = conf[key];
+ }
+ }
+ }
+ }
+ // Create event watchers for the "next" and "previous" pagination
+ // links, this enables us to set the direction the next request is
+ // going in when we're doing our own pagination. We use "hover"
+ // because the click event is caught after the request has been
+ // sent
+ tableDef.drawCallback = function() {
+ $('.paginate_button.next:not(.disabled)',
+ this.api().table().container()
+ ).on('hover', function() {
+ forward = true;
+ directionSet = true;
+ });
+ $('.paginate_button.previous:not(.disabled)',
+ this.api().table().container()
+ ).on('hover', function() {
+ forward = false;
+ directionSet = true;
+ });
+ }
+ // Initialise the table
+ // Since we're not able to use the columns settings in core,
+ // we need to mock the object that it would return
+ var columns_settings = [
+ {
+ cannot_be_modified: 0,
+ cannot_be_toggled: 0,
+ columnname: 'source',
+ is_hidden: 0
+ },
+ {
+ cannot_be_modified: 0,
+ cannot_be_toggled: 0,
+ columnname: 'title',
+ is_hidden: 0
+ },
+ {
+ cannot_be_modified: 0,
+ cannot_be_toggled: 0,
+ columnname: 'author',
+ is_hidden: 0
+ },
+ {
+ cannot_be_modified: 0,
+ cannot_be_toggled: 0,
+ columnname: 'isbn',
+ is_hidden: 0
+ },
+ {
+ cannot_be_modified: 0,
+ cannot_be_toggled: 0,
+ columnname: 'issn',
+ is_hidden: 0
+ },
+ {
+ cannot_be_modified: 0,
+ cannot_be_toggled: 0,
+ columnname: 'date',
+ is_hidden: 0
+ }
+ ];
+ // Hide pagination buttons if appropriate
+ tableDef.drawCallback = function() {
+ var pagination = $(this).closest('.dataTables_wrapper')
+ .find('.dataTables_paginate');
+ pagination.toggle(this.api().page.info().pages > 1);
+ }
+ KohaTable(service.id, tableDef, columns_settings);
+ });
+ }
+
+
+});
.dropdown:hover .dropdown-menu.nojs {
display: block;
}
+
+}
+
+.ill_availability_sourcename {
+ margin-top: 20px;
+}
+
+#continue-request-row {
+ text-align: center;
}
#dc_fieldset {
--- /dev/null
+<div>
+ <div>[% service.name %]</div>
+ <table class="ill-availability table table-bordered table-striped" id="[% service.id %]">
+ <thead id="[% service.id %]-header">
+ <tr>
+ <th>Source</th>
+ <th>Title</th>
+ <th>Author</th>
+ <th>ISBN</th>
+ <th>ISSN</th>
+ <th>Date</th>
+ </tr>
+ </thead>
+ <tbody id="[% service.id %]-body">
+ </tbody>
+ </table>
+</div>
<span class="cancel"><a href="/cgi-bin/koha/opac-illrequests.pl">Cancel</a></span>
</fieldset>
</form>
+ [% ELSIF method == 'availability' %]
+ <h2>Interlibrary loan item availability</h2>
+ <div id="results">
+ <h3>Displaying availability results</h3>
+ <form method="POST" action="/cgi-bin/koha/opac-illrequests.pl">
+ [% FOREACH key IN whole.keys %]
+ [% value = whole.$key %]
+ [% IF key != 'custom_key' && key != 'custom_value' %]
+ <input type="hidden" name="[% key | html %]" value="[% value | html %]">
+ [% END %]
+ [% END %]
+ [% custom_keys = whole.custom_key.split('\0') %]
+ [% custom_values = whole.custom_value.split('\0') %]
+ [% i = 0 %]
+ [% FOREACH custom_key IN custom_keys %]
+ <input type="hidden" name="custom_key" value="[% custom_key %]">
+ <input type="hidden" name="custom_value" value="[% custom_values.$i %]">
+ [% i = i + 1 %]
+ [% END %]
+ <input type="hidden" name="checked_availability" value="1">
+ <div id="continue-request-row" class="alert">
+ If you can't find what you are looking for, you can
+ <button class="button" type="submit">continue creating your request</button> or
+ <a href="/cgi-bin/koha/opac-illrequests.pl">cancel your request</a>
+ </div>
+ </form>
+ [% FOR service IN services %]
+ <h4 class="ill_availability_sourcename">[% service.plugin %]</h4>
+ [% INCLUDE 'ill-availability-table.inc' service=service %]
+ [% END %]
+ </div>
[% END %]
</div> <!-- / .maincontent -->
[% END %]
"deferRender": true
}));
$("#backend-dropdown-options").removeClass("nojs");
+ [% IF services_json.length > 0 %]
+ var services = [% services_json | $raw %];
+ [% ELSE %]
+ var services = [];
+ [% END %]
+ [% IF metadata.length > 0 %]
+ var metadata = "[% metadata | $raw %]";
+ [% END %]
//]]>
</script>
+[% IF method == 'availability' %]
+ [% Asset.js("js/ill-availability.js") | $raw %]
+ <script>
+ $(document).ready(function() {
+ window.doSearch();
+ })
+ </script>
+[% END %]
[% TRY %]
[% PROCESS backend_jsinclude %]
[% CATCH %]
--- /dev/null
+$(document).ready(function() {
+
+ window.doSearch = function() {
+ // In case the source doesn't supply data required for DT to calculate
+ // pagination, we need to do it ourselves
+ var ownPagination = false;
+ var directionSet = false;
+ var start = 0;
+ var forward = true; // true == forward, false == backwards
+ // Arbitrary starting value, it will be corrected by the first
+ // page of results
+ var pageSize = 20;
+
+ var tableTmpl = {
+ ajax: {
+ cache: true, // Prevent DT appending a "_" cache param
+ },
+ columns: [
+ // defaultContent prevents DT from choking if
+ // the API response doesn't return a column
+ {
+ title: 'Source',
+ data: 'source',
+ defaultContent: ''
+ },
+ {
+ data: 'title',
+ defaultContent: ''
+ },
+ {
+ data: 'author',
+ defaultContent: ''
+ },
+ {
+ data: 'isbn',
+ defaultContent: ''
+ },
+ {
+ data: 'issn',
+ defaultContent: ''
+ },
+ {
+ data: 'date',
+ defaultContent: ''
+ }
+ ]
+ };
+
+ // render functions don't get copied across when we make a dereferenced
+ // copy of them, so we have to reattach them once we have a copy
+ // Here we store them
+ var renders = {
+ title: function(data, type, row) {
+ return row.url ?
+ '<a href="'+row.url+'" target="_blank">'+row.title+'</a>' :
+ row.title;
+ },
+ source: function(data, type, row) {
+ return row.opac_url ?
+ '<a href="'+row.opac_url+'" target="_blank">'+row.source+'</a>' :
+ row.source;
+ }
+ };
+
+ services.forEach(function(service) {
+ // Create a deferenced copy of our table definition object
+ var tableDef = JSON.parse(JSON.stringify(tableTmpl));
+ // Iterate the table's columns array and add render functions
+ // as necessary
+ tableDef.columns.forEach(function(column) {
+ if (renders[column.data]) {
+ column.render = renders[column.data];
+ }
+ });
+ tableDef.ajax.dataSrc = function(data) {
+ var results = data.results.search_results;
+ // The source appears to be returning it's own pagination
+ // data
+ if (
+ data.hasOwnProperty('recordsFiltered') ||
+ data.hasOwnProperty('recordsTotal')
+ ) {
+ return results;
+ }
+ // Set up our own pagination values based on what we just
+ // got back
+ ownPagination = true;
+ directionSet = false;
+ pageSize = results.length;
+ // These values are completely arbitrary, but they enable
+ // us to display pagination links
+ data.recordsFiltered = 5000,
+ data.recordsTotal = 5000;
+
+ return results;
+ };
+ tableDef.ajax.data = function(data) {
+ // Datatables sends a bunch of superfluous params
+ // that we don't want to litter our API schema
+ // with, so just remove them from the request
+ if (data.hasOwnProperty('columns')) {
+ delete data.columns;
+ }
+ if (data.hasOwnProperty('draw')) {
+ delete data.draw;
+ }
+ if (data.hasOwnProperty('order')) {
+ delete data.order;
+ }
+ if (data.hasOwnProperty('search')) {
+ delete data.search;
+ }
+ // If we're handling our own pagination, set the properties
+ // that DT will send in the request
+ if (ownPagination) {
+ start = forward ? start + pageSize : start - pageSize;
+ data.start = start;
+ data['length'] = pageSize;
+ }
+ // We may need to restrict the service IDs being queries, this
+ // needs to be handled in the plugin's API module
+ var restrict = $('#service_id_restrict').
+ attr('data-service_id_restrict_ids');
+ if (restrict && restrict.length > 0) {
+ data.restrict = restrict;
+ }
+ };
+ // Add any datatables config options passed from the service
+ // to the table definition
+ tableDef.ajax.url = service.endpoint + metadata;
+ if (service.hasOwnProperty('datatablesConfig')) {
+ var conf = service.datatablesConfig;
+ for (var key in conf) {
+ // The config from the service definition comes from a Perl
+ // hashref, therefore can't contain true/false, so we
+ // special case it
+ if (conf.hasOwnProperty(key)) {
+ if (conf[key] == 'false') {
+ // Special case false values
+ tableDef[key] = false;
+ } else if (conf[key] == 'true') {
+ // Special case true values
+ tableDef[key] = true;
+ } else {
+ // Copy the property value
+ tableDef[key] = conf[key];
+ }
+ }
+ }
+ }
+ // Create event watchers for the "next" and "previous" pagination
+ // links, this enables us to set the direction the next request is
+ // going in when we're doing our own pagination. We use "hover"
+ // because the click event is caught after the request has been
+ // sent
+ tableDef.drawCallback = function() {
+ $('.paginate_button.next:not(.disabled)',
+ this.api().table().container()
+ ).on('hover', function() {
+ forward = true;
+ directionSet = true;
+ });
+ $('.paginate_button.previous:not(.disabled)',
+ this.api().table().container()
+ ).on('hover', function() {
+ forward = false;
+ directionSet = true;
+ });
+ }
+ // Initialise the table
+ $('#'+service.id ).dataTable(
+ $.extend(true, {}, dataTablesDefaults, tableDef)
+ );
+ });
+ }
+
+
+});
use Modern::Perl;
+use JSON qw( encode_json );
+
use CGI qw ( -utf8 );
use C4::Auth;
use C4::Koha;
use Koha::Illrequests;
use Koha::Libraries;
use Koha::Patrons;
+use Koha::Illrequest::Availability;
my $query = new CGI;
} else {
my $request = Koha::Illrequest->new
->load_backend($params->{backend});
+
+ # Does this backend enable us to insert an availability stage and should
+ # we? If not, proceed as normal.
+ if (
+ C4::Context->preference("ILLCheckAvailability") &&
+ $request->_backend_capability(
+ 'should_display_availability',
+ $params
+ ) &&
+ # If the user has elected to continue with the request despite
+ # having viewed availability info, this flag will be set
+ !$params->{checked_availability}
+ ) {
+ # Establish which of the installed availability providers
+ # can service our metadata, if so, jump in
+ my $availability = Koha::Illrequest::Availability->new($params);
+ my $services = $availability->get_services({
+ ui_context => 'opac'
+ });
+ if (scalar @{$services} > 0) {
+ # Modify our method so we use the correct part of the
+ # template
+ $op = 'availability';
+ # Prepare the metadata we're sending them
+ my $metadata = $availability->prep_metadata($params);
+ $template->param(
+ metadata => $metadata,
+ services_json => encode_json($services),
+ services => $services,
+ illrequestsview => 1,
+ message => $params->{message},
+ method => $op,
+ whole => $params
+ );
+ output_html_with_http_headers $query, $cookie,
+ $template->output, undef, { force_no_caching => 1 };
+ exit;
+ }
+ }
+
$params->{cardnumber} = Koha::Patrons->find({
borrowernumber => $loggedinuser
})->cardnumber;