@@ -, +, @@ --- Koha/REST/V1/Illrequests.pm | 10 +- 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 | 549 +-------------------- .../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, 672 insertions(+), 536 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 100644 members/ill-requests.pl --- a/Koha/REST/V1/Illrequests.pm +++ a/Koha/REST/V1/Illrequests.pm @@ -56,7 +56,15 @@ sub list { } # 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 @@ -278,13 +278,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 %]
  • [% ELSE %]
  • [% END %]Housebound
  • [% END %] [% IF Koha.Preference('ILLModule') && CAN_user_ill %] -
  • Interlibrary loans
  • + [% IF illview %]
  • [% ELSE %]
  • [% END %]Interlibrary loans
  • [% END %] [% END %] --- a/koha-tmpl/intranet-tmpl/prog/en/includes/ill-list-table.inc +++ a/koha-tmpl/intranet-tmpl/prog/en/includes/ill-list-table.inc @@ -0,0 +1,36 @@ + 0 %] data-prefilters="[% prefilters %]"[% END %]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Request IDAuthorTitleArticle titleIssueVolumeYearPagesTypeOrder IDPatron IDPatron barcodePatronBiblio IDBranchStatus Placed on Updated onRepliedCompleted onAccess URLCostOPAC notesStaff notesBackend
    --- a/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt @@ -499,44 +499,7 @@

    View ILL requests

    Details for all requests

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Request IDAuthorTitleArticle titleIssueVolumeYearPagesTypeOrder IDPatron IDPatron barcodePatronBiblio IDBranchStatus Placed onReplied Updated onRepliedCompleted onAccess URLCostOPAC notesStaff notesBackend
    + [% INCLUDE 'ill-list-table.inc' %]
    [% ELSE %] @@ -548,506 +511,30 @@ -[% TRY %] -[% PROCESS backend_jsinclude %] -[% CATCH %] -[% END %] - [% 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 %] + +[% TRY %] +[% PROCESS backend_jsinclude %] +[% CATCH %] [% END %] [% INCLUDE 'intranet-bottom.inc' %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/ill-requests.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/members/ill-requests.tt @@ -0,0 +1,63 @@ +[% USE raw %] +[% USE Asset %] +[% USE Branches %] +[% USE Koha %] +[% USE KohaDates %] +[% SET footerjs = 1 %] +[% USE AuthorisedValues %] +[% USE ColumnsSettings %] + +[% INCLUDE 'doc-head-open.inc' %] +Koha › Patrons › ILL requests for [% INCLUDE 'patron-title.inc' no_html = 1 %] +[% INCLUDE 'doc-head-close.inc' %] +[% Asset.css("css/datatables.css") %] + + + + [% INCLUDE 'header.inc' %] + [% INCLUDE 'patron-search.inc' %] + + + +
    + +
    +
    +
    +
    +

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