From 49fc75a7755905dce4fcf8f75dcb2457f955a9e6 Mon Sep 17 00:00:00 2001 From: Andrew Isherwood Date: Fri, 9 Nov 2018 15:53:50 +0000 Subject: [PATCH] Bug 18589: Show ILLs as part of patron profile This patch moves the display of a patron's ILL requests to be inline in the Patron Profile page, as per all other patron information. It includes a substantial refactor of koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt in that it moves the display of the illlist table (which is populated by an API request) into it's own separate include file. It also moves the datatable related Javascript for this table into it's own JS file. Doing this allows us to reuse both in both the new members/ill-requests.tt template and the pre-existing ill/ill-requests.tt template. To test: 1) Ensure ILL is enabled and your user has sufficient permissions 2) Ensure your user has some ILL requests 3) Navigate to the user's patron profile page 4) Click on the "Interlibrary loans" tab 5) Observe that the requests table is displayed inline 6) Observe that only your user's requests are displayed Signed-off-by: Niamh.Walker-Headon@it-tallaght.ie Bug 18589: (follow-up) Fix QA error Bug 18589: (follow-up) Fix QA failure This patch removes the id and class attributes on the body tag in: koha-tmpl/intranet-tmpl/prog/en/modules/members/ill-requests.tt To test: - Apply the patch - Run the QA tools - Note that the "Failed test ' tag with id and class attributes'" no longer is reported Bug 18589: (follow-up) Fix QA errors This patch fixes the "missing_filter" QA errors Bug 18589: (follow-up) Fix rebase errors Minor rebase errors Bug 18589: (follow-up) More merge errors --- 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 | 43 ++ .../prog/en/modules/ill/ill-requests.tt | 554 +-------------------- .../prog/en/modules/members/ill-requests.tt | 63 +++ koha-tmpl/intranet-tmpl/prog/js/ill-list-table.js | 496 ++++++++++++++++++ members/ill-requests.pl | 55 ++ 8 files changed, 687 insertions(+), 541 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 diff --git a/Koha/REST/V1/Illrequests.pm b/Koha/REST/V1/Illrequests.pm index 51c2877b85..b040edde93 100644 --- a/Koha/REST/V1/Illrequests.pm +++ b/Koha/REST/V1/Illrequests.pm @@ -53,7 +53,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 diff --git a/ill/ill-requests.pl b/ill/ill-requests.pl index 77953210d4..2ec495cd06 100755 --- a/ill/ill-requests.pl +++ b/ill/ill-requests.pl @@ -273,13 +273,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}) ); } diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc index f6c3d5e284..0adbccee66 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc @@ -132,7 +132,7 @@ [% IF houseboundview %]
  • [% ELSE %]
  • [% END %]Housebound
  • [% END %] [% IF Koha.Preference('ILLModule') && CAN_user_ill %] -
  • Interlibrary loans
  • + [% IF illview %]
  • [% ELSE %]
  • [% END %]Interlibrary loans
  • [% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/ill-list-table.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/ill-list-table.inc new file mode 100644 index 0000000000..0cdb4b4bb1 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/ill-list-table.inc @@ -0,0 +1,43 @@ +
    + [% IF prefilters.length > 0 %] + + [% ELSE %] +
    + [% END %] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Request IDAuthorTitleArticle titleIssueVolumeYearPagesTypeOrder IDPatron IDPatron barcodePatronBiblio IDBranchStatus Placed on Updated onRepliedCompleted onAccess URLCostCommentsOPAC notesStaff notesBackend
    +
    diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt index 2d6764367d..2b794523d6 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt @@ -508,45 +508,7 @@

    View ILL requests

    Details for all requests

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Request IDAuthorTitleArticle titleIssueVolumeYearPagesTypeOrder IDPatron IDPatron barcodePatronBiblio IDBranchStatus Placed onReplied Updated onRepliedCompleted onAccess URLCostCommentsOPAC notesStaff notesBackend
    + [% INCLUDE 'ill-list-table.inc' %]
    [% ELSE %] @@ -558,510 +520,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' %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/ill-requests.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/ill-requests.tt new file mode 100644 index 0000000000..fd77b38f10 --- /dev/null +++ b/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") | $raw %] + + + + [% 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' %] diff --git a/koha-tmpl/intranet-tmpl/prog/js/ill-list-table.js b/koha-tmpl/intranet-tmpl/prog/js/ill-list-table.js new file mode 100644 index 0000000000..db45499fd7 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/ill-list-table.js @@ -0,0 +1,496 @@ +$(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; + if (row.status_alias) { + resolvedName = row.status_alias.lib; + } else { + resolvedName = getStatusName( + oData[0].capabilities[row.status].name + ); + } + 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: _("Cardnumber") + } + }; + + // 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 + }, + { + 'aTargets': [ 'patron_cardnumber' ], + 'bSearchable': true + } + ], + '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(); + }); + +}); diff --git a/members/ill-requests.pl b/members/ill-requests.pl new file mode 100755 index 0000000000..5cdb8f7f8d --- /dev/null +++ b/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; -- 2.11.0