@@ -, +, @@
---
Koha/REST/V1/Illrequests.pm | 11 +-
ill/ill-requests.pl | 5 +-
.../intranet-tmpl/prog/en/includes/circ-menu.inc | 2 +-
.../prog/en/includes/ill-list-table.inc | 36 ++
.../prog/en/modules/ill/ill-requests.tt | 541 +--------------------
.../prog/en/modules/members/ill-requests.tt | 63 +++
koha-tmpl/intranet-tmpl/prog/js/ill-list-table.js | 488 +++++++++++++++++++
members/ill-requests.pl | 55 +++
8 files changed, 673 insertions(+), 528 deletions(-)
create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/ill-list-table.inc
create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/members/ill-requests.tt
create mode 100644 koha-tmpl/intranet-tmpl/prog/js/ill-list-table.js
create mode 100755 members/ill-requests.pl
--- a/Koha/REST/V1/Illrequests.pm
+++ a/Koha/REST/V1/Illrequests.pm
@@ -55,8 +55,17 @@ sub list {
delete $args->{embed};
}
+
# Get all requests
- my @requests = Koha::Illrequests->as_list;
+ # If necessary, only get those from a specified patron
+ my @requests;
+ if ($args->{borrowernumber}) {
+ @requests = Koha::Illrequests->search(
+ { borrowernumber => $args->{borrowernumber} }
+ );
+ } else {
+ @requests = Koha::Illrequests->as_list;
+ }
# Identify patrons & branches that
# we're going to need and get them
--- a/ill/ill-requests.pl
+++ a/ill/ill-requests.pl
@@ -277,13 +277,12 @@ if ( $backends_available ) {
my $active_filters = [];
foreach my $filter(@{$possible_filters}) {
if ($params->{$filter}) {
- push @{$active_filters},
- { name => $filter, value => $params->{$filter}};
+ push @{$active_filters}, "$filter=$params->{$filter}";
}
}
if (scalar @{$active_filters} > 0) {
$template->param(
- prefilters => $active_filters
+ prefilters => join(",", @{$active_filters})
);
}
--- a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc
+++ a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc
@@ -127,7 +127,7 @@
[% IF houseboundview %]
+
+
+
+
+
+
ILL requests
+ [% INCLUDE 'ill-list-table.inc' %]
+
+
+
+
+ [% INCLUDE 'circ-menu.inc' %]
+
+
+
+ [% MACRO jsinclude BLOCK %]
+ [% INCLUDE 'datatables.inc' %]
+ [% INCLUDE 'columns_settings.inc' %]
+ [% INCLUDE 'calendar.inc' %]
+ [% Asset.js("lib/jquery/plugins/jquery.checkboxes.min.js") | $raw %]
+
+ [% Asset.js("js/ill-list-table.js") | $raw %]
+ [% END %]
+
+[% INCLUDE 'intranet-bottom.inc' %]
--- a/koha-tmpl/intranet-tmpl/prog/js/ill-list-table.js
+++ a/koha-tmpl/intranet-tmpl/prog/js/ill-list-table.js
@@ -0,0 +1,488 @@
+$(document).ready(function() {
+
+ // Illview Datatable setup
+
+ var table;
+
+ // Filters that are active
+ var activeFilters = {};
+
+ $('#illfilter_dateplaced_start, #illfilter_dateplaced_end, #illfilter_datemodified_start, #illfilter_datemodified_end').datepicker(
+ 'option', 'dateFormat', dateFormat
+ );
+
+ // Get any prefilters
+ var prefilters = $('table#ill-requests').data('prefilters');
+
+ // Fields we need to expand (flatten)
+ var expand = [
+ 'metadata',
+ 'patron',
+ 'library'
+ ];
+
+ // Expanded fields
+ // This is auto populated
+ var expanded = {};
+
+ // Filterable columns
+ var filterable = {
+ status: {
+ prep: function(tableData, oData) {
+ var uniques = {};
+ tableData.forEach(function(row) {
+ var resolvedName = getStatusName(
+ oData[0].capabilities[row.status].name,
+ row
+ );
+ uniques[resolvedName] = 1
+ });
+ Object.keys(uniques).sort().forEach(function(unique) {
+ $('#illfilter_status').append(
+ '
'
+ );
+ });
+ },
+ listener: function() {
+ var me = 'status';
+ $('#illfilter_status').change(function() {
+ var sel = $('#illfilter_status option:selected').val();
+ if (sel && sel.length > 0) {
+ activeFilters[me] = function() {
+ table.api().column(15).search(sel);
+ }
+ } else {
+ if (activeFilters.hasOwnProperty(me)) {
+ delete activeFilters[me];
+ }
+ }
+ });
+ },
+ clear: function() {
+ $('#illfilter_status').val('');
+ }
+ },
+ pickupBranch: {
+ prep: function(tableData, oData) {
+ var uniques = {};
+ tableData.forEach(function(row) {
+ uniques[row.library_branchname] = 1
+ });
+ Object.keys(uniques).sort().forEach(function(unique) {
+ $('#illfilter_branchname').append(
+ '
'
+ );
+ });
+ },
+ listener: function() {
+ var me = 'pickupBranch';
+ $('#illfilter_branchname').change(function() {
+ var sel = $('#illfilter_branchname option:selected').val();
+ if (sel && sel.length > 0) {
+ activeFilters[me] = function() {
+ table.api().column(14).search(sel);
+ }
+ } else {
+ if (activeFilters.hasOwnProperty(me)) {
+ delete activeFilters[me];
+ }
+ }
+ });
+ },
+ clear: function() {
+ $('#illfilter_branchname').val('');
+ }
+ },
+ barcode: {
+ listener: function() {
+ var me = 'barcode';
+ $('#illfilter_barcode').change(function() {
+ var val = $('#illfilter_barcode').val();
+ if (val && val.length > 0) {
+ activeFilters[me] = function() {
+ table.api().column(11).search(val);
+ }
+ } else {
+ if (activeFilters.hasOwnProperty(me)) {
+ delete activeFilters[me];
+ }
+ }
+ });
+ },
+ clear: function() {
+ $('#illfilter_barcode').val('');
+ }
+ },
+ dateModified: {
+ clear: function() {
+ $('#illfilter_datemodified_start, #illfilter_datemodified_end').val('');
+ }
+ },
+ datePlaced: {
+ clear: function() {
+ $('#illfilter_dateplaced_start, #illfilter_dateplaced_end').val('');
+ }
+ }
+ };
+
+ // Expand any fields we're expanding
+ var expandExpand = function(row) {
+ expand.forEach(function(thisExpand) {
+ if (row.hasOwnProperty(thisExpand)) {
+ if (!expanded.hasOwnProperty(thisExpand)) {
+ expanded[thisExpand] = [];
+ }
+ var expandObj = row[thisExpand];
+ Object.keys(expandObj).forEach(
+ function(thisExpandCol) {
+ var expColName = thisExpand + '_' + thisExpandCol.replace(/\s/g,'_');
+ // Keep a list of fields that have been expanded
+ // so we can create toggle links for them
+ if (expanded[thisExpand].indexOf(expColName) == -1) {
+ expanded[thisExpand].push(expColName);
+ }
+ expandObj[expColName] =
+ expandObj[thisExpandCol];
+ delete expandObj[thisExpandCol];
+ }
+ );
+ $.extend(true, row, expandObj);
+ delete row[thisExpand];
+ }
+ });
+ };
+
+ // Strip the expand prefix if it exists, we do this for display
+ var stripPrefix = function(value) {
+ expand.forEach(function(thisExpand) {
+ var regex = new RegExp(thisExpand + '_', 'g');
+ value = value.replace(regex, '');
+ });
+ return value;
+ };
+
+ // Our 'render' function for borrowerlink
+ var createPatronLink = function(data, type, row) {
+ var patronLink = '
';
+ if ( row.patron_firstname ) {
+ patronLink = patronLink + row.patron_firstname + ' ';
+ }
+ patronLink = patronLink + row.patron_surname + '';
+ return patronLink;
+ };
+
+ // Our 'render' function for biblio_id
+ var createBiblioLink = function(data, type, row) {
+ return (row.biblio_id) ?
+ '
' +
+ row.biblio_id +
+ '' : '';
+ };
+
+ // Render function for request ID
+ var createRequestId = function(data, type, row) {
+ return row.id_prefix + row.illrequest_id;
+ };
+
+ // Render function for type
+ var createType = function(data, type, row) {
+ if (!row.hasOwnProperty('metadata_Type') || !row.metadata_Type) {
+ if (row.hasOwnProperty('medium') && row.medium) {
+ row.metadata_Type = row.medium;
+ } else {
+ row.metadata_Type = null;
+ }
+ }
+ return row.metadata_Type;
+ };
+
+ // Render function for request status
+ var createStatus = function(data, type, row, meta) {
+ if (row.status_alias) {
+ return row.status_alias.lib
+ ? row.status_alias.lib
+ : row.status_alias.authorised_value;
+ } else {
+ var origData = meta.settings.oInit.originalData;
+ if (origData.length > 0) {
+ var status_name = meta.settings.oInit.originalData[0].capabilities[
+ row.status
+ ].name;
+ return getStatusName(status_name, row);
+ } else {
+ return '';
+ }
+ }
+ };
+
+ var getStatusName = function(origName, row) {
+ switch( origName ) {
+ case "New request":
+ return _("New request");
+ case "Requested":
+ return _("Requested");
+ case "Requested from partners":
+ var statStr = _("Requested from partners");
+ if (
+ row.hasOwnProperty('requested_partners') &&
+ row.requested_partners &&
+ row.requested_partners.length > 0
+ ) {
+ statStr += ' (' + row.requested_partners + ')';
+ }
+ return statStr;
+ case "Request reverted":
+ return _("Request reverted");
+ case "Queued request":
+ return _("Queued request");
+ case "Cancellation requested":
+ return _("Cancellation requested");
+ case "Completed":
+ return _("Completed");
+ case "Delete request":
+ return _("Delete request");
+ default:
+ return origName;
+ }
+ };
+
+ // Render function for creating a row's action link
+ var createActionLink = function(data, type, row) {
+ return '
' + _("Manage request") + '';
+ };
+
+ // Columns that require special treatment
+ var specialCols = {
+ action: {
+ func: createActionLink
+ },
+ patron: {
+ func: createPatronLink
+ },
+ illrequest_id: {
+ func: createRequestId
+ },
+ status: {
+ func: createStatus
+ },
+ biblio_id: {
+ name: _("Bibliograpic record ID"),
+ func: createBiblioLink
+ },
+ metadata_Type: {
+ func: createType
+ },
+ updated: {
+ name: _("Updated on"),
+ },
+ patron_cardnumber: {
+ name: _("Patron barcode")
+ }
+ };
+
+ // Display the modal containing request supplier metadata
+ $('#ill-request-display-log').on('click', function(e) {
+ e.preventDefault();
+ $('#requestLog').modal({show:true});
+ });
+
+ // Toggle request attributes in Illview
+ $('#toggle_requestattributes').on('click', function(e) {
+ e.preventDefault();
+ $('#requestattributes').toggleClass('content_hidden');
+ });
+
+ // Toggle new comment form in Illview
+ $('#toggle_addcomment').on('click', function(e) {
+ e.preventDefault();
+ $('#addcomment').toggleClass('content_hidden');
+ });
+
+ // Filter partner list
+ $('#partner_filter').keyup(function() {
+ var needle = $('#partner_filter').val();
+ $('#partners > option').each(function() {
+ var regex = new RegExp(needle, 'i');
+ if (
+ needle.length == 0 ||
+ $(this).is(':selected') ||
+ $(this).text().match(regex)
+ ) {
+ $(this).show();
+ } else {
+ $(this).hide();
+ }
+ });
+ });
+
+ // Display the modal containing request supplier metadata
+ $('#ill-request-display-metadata').on('click', function(e) {
+ e.preventDefault();
+ $('#dataPreview').modal({show:true});
+ });
+
+ // Get our data from the API and process it prior to passing
+ // it to datatables
+ var filterParam = prefilters ? '&' + prefilters : '';
+ var ajax = $.ajax(
+ '/api/v1/illrequests?embed=metadata,patron,capabilities,library,comments,status_alias,requested_partners'
+ + filterParam
+ ).done(function() {
+ var data = JSON.parse(ajax.responseText);
+ // Make a copy, we'll be removing columns next and need
+ // to be able to refer to data that has been removed
+ var dataCopy = $.extend(true, [], data);
+ // Expand columns that need it and create an array
+ // of all column names
+ $.each(dataCopy, function(k, row) {
+ expandExpand(row);
+ });
+
+ // Assemble an array of column definitions for passing
+ // to datatables
+ var colData = [];
+ columns_settings.forEach(function(thisCol) {
+ var colName = thisCol.columnname;
+ // Create the base column object
+ var colObj = $.extend({}, thisCol);
+ colObj.name = colName;
+ colObj.className = colName;
+ colObj.defaultContent = '';
+
+ // We may need to process the data going in this
+ // column, so do it if necessary
+ if (
+ specialCols.hasOwnProperty(colName) &&
+ specialCols[colName].hasOwnProperty('func')
+ ) {
+ colObj.render = specialCols[colName].func;
+ } else {
+ colObj.data = colName;
+ }
+ // Make sure properties that aren't present in the API
+ // response are populated with null to avoid Datatables
+ // choking on their absence
+ dataCopy.forEach(function(thisData) {
+ if (!thisData.hasOwnProperty(colName)) {
+ thisData[colName] = null;
+ }
+ });
+ colData.push(colObj);
+ });
+
+ // Initialise the datatable
+ table = KohaTable("ill-requests", {
+ 'aoColumnDefs': [
+ { // Last column shouldn't be sortable or searchable
+ 'aTargets': [ 'actions' ],
+ 'bSortable': false,
+ 'bSearchable': false
+ },
+ { // When sorting 'placed', we want to use the
+ // unformatted column
+ 'aTargets': [ 'placed_formatted'],
+ 'iDataSort': 16
+ },
+ { // When sorting 'updated', we want to use the
+ // unformatted column
+ 'aTargets': [ 'updated_formatted'],
+ 'iDataSort': 18
+ }
+ ],
+ 'aaSorting': [[ 18, 'desc' ]], // Default sort, updated descending
+ 'processing': true, // Display a message when manipulating
+ 'sPaginationType': "full_numbers", // Pagination display
+ 'deferRender': true, // Improve performance on big datasets
+ 'data': dataCopy,
+ 'columns': colData,
+ 'originalData': data, // Enable render functions to access
+ // our original data
+ 'initComplete': function() {
+ // Prepare any filter elements that need it
+ for (var el in filterable) {
+ if (filterable.hasOwnProperty(el)) {
+ if (filterable[el].hasOwnProperty('prep')) {
+ filterable[el].prep(dataCopy, data);
+ }
+ if (filterable[el].hasOwnProperty('listener')) {
+ filterable[el].listener();
+ }
+ }
+ }
+
+ }
+ }, columns_settings);
+
+ // Custom date range filtering
+ $.fn.dataTable.ext.search.push(function(settings, data, dataIndex) {
+ var placedStart = $('#illfilter_dateplaced_start').datepicker('getDate');
+ var placedEnd = $('#illfilter_dateplaced_end').datepicker('getDate');
+ var modifiedStart = $('#illfilter_datemodified_start').datepicker('getDate');
+ var modifiedEnd = $('#illfilter_datemodified_end').datepicker('getDate');
+ var rowPlaced = data[16] ? new Date(data[16]) : null;
+ var rowModified = data[18] ? new Date(data[18]) : null;
+ var placedPassed = true;
+ var modifiedPassed = true;
+ if (placedStart && rowPlaced && rowPlaced < placedStart) {
+ placedPassed = false
+ };
+ if (placedEnd && rowPlaced && rowPlaced > placedEnd) {
+ placedPassed = false;
+ }
+ if (modifiedStart && rowModified && rowModified < modifiedStart) {
+ modifiedPassed = false
+ };
+ if (modifiedEnd && rowModified && rowModified > modifiedEnd) {
+ modifiedPassed = false;
+ }
+
+ return placedPassed && modifiedPassed;
+
+ });
+
+ }
+ );
+
+ var clearSearch = function() {
+ table.api().search('').columns().search('');
+ activeFilters = {};
+ for (var filter in filterable) {
+ if (
+ filterable.hasOwnProperty(filter) &&
+ filterable[filter].hasOwnProperty('clear')
+ ) {
+ filterable[filter].clear();
+ }
+ }
+ table.api().draw();
+ };
+
+ // Apply any search filters, or clear any previous
+ // ones
+ $('#illfilter_form').submit(function(event) {
+ event.preventDefault();
+ table.api().search('').columns().search('');
+ for (var active in activeFilters) {
+ if (activeFilters.hasOwnProperty(active)) {
+ activeFilters[active]();
+ }
+ }
+ table.api().draw();
+ });
+
+ // Clear all filters
+ $('#clear_search').click(function() {
+ clearSearch();
+ });
+
+});
--- a/members/ill-requests.pl
+++ a/members/ill-requests.pl
@@ -0,0 +1,55 @@
+#!/usr/bin/perl
+
+# This file is part of Koha.
+#
+# Copyright 2018 PTFS Europe
+#
+# 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
.
+
+use Modern::Perl;
+
+use CGI qw ( -utf8 );
+use C4::Auth;
+use C4::Output;
+use C4::Members;
+use C4::Members::Attributes qw(GetBorrowerAttributes);
+use Koha::Patrons;
+
+my $input = new CGI;
+
+my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
+ {
+ template_name => "members/ill-requests.tt",
+ query => $input,
+ type => "intranet",
+ authnotrequired => 0,
+ flagsrequired => { borrowers => 'edit_borrowers' },
+ debug => 1,
+ }
+);
+
+my $borrowernumber = $input->param('borrowernumber');
+
+my $logged_in_user = Koha::Patrons->find( $loggedinuser ) or die "Not logged in";
+my $patron = Koha::Patrons->find( $borrowernumber );
+output_and_exit_if_error( $input, $cookie, $template, { module => 'members', logged_in_user => $logged_in_user, current_patron => $patron } );
+
+my $category = $patron->category;
+$template->param(
+ prefilters => "borrowernumber=$borrowernumber",
+ patron => $patron,
+ illview => 1,
+);
+
+output_html_with_http_headers $input, $cookie, $template->output;
--