Bugzilla – Attachment 82513 Details for
Bug 18589
Show ILLs as part of patron profile
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 18589: Show ILLs as part of patron profile
Bug-18589-Show-ILLs-as-part-of-patron-profile.patch (text/plain), 53.77 KB, created by
Andrew Isherwood
on 2018-11-19 15:14:51 UTC
(
hide
)
Description:
Bug 18589: Show ILLs as part of patron profile
Filename:
MIME Type:
Creator:
Andrew Isherwood
Created:
2018-11-19 15:14:51 UTC
Size:
53.77 KB
patch
obsolete
>From 3ac412e338fba3df54f957819d31e29e7bf3bfa7 Mon Sep 17 00:00:00 2001 >From: Andrew Isherwood <andrew.isherwood@ptfs-europe.com> >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 >--- > 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 > >diff --git a/Koha/REST/V1/Illrequests.pm b/Koha/REST/V1/Illrequests.pm >index afbdf8665f..664987dbe9 100644 >--- a/Koha/REST/V1/Illrequests.pm >+++ b/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 >diff --git a/ill/ill-requests.pl b/ill/ill-requests.pl >index 3bfea5e669..7dc68cd70d 100755 >--- a/ill/ill-requests.pl >+++ b/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}) > ); > } > >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 8f76a9fe68..d6ed880dbc 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc >@@ -127,7 +127,7 @@ > [% IF houseboundview %]<li class="active">[% ELSE %]<li>[% END %]<a href="/cgi-bin/koha/members/housebound.pl?borrowernumber=[% patron.borrowernumber | uri %]">Housebound</a></li> > [% END %] > [% IF Koha.Preference('ILLModule') && CAN_user_ill %] >- <li><a href="/cgi-bin/koha/ill/ill-requests.pl?borrowernumber=[% patron.borrowernumber | uri %]">Interlibrary loans</a></li> >+ [% IF illview %]<li class="active">[% ELSE %]<li>[% END %]<a href="/cgi-bin/koha/members/ill-requests.pl?borrowernumber=[% patron.borrowernumber | uri %]">Interlibrary loans</a></li> > [% END %] > </ul></div> > [% 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..348b36e2c3 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/ill-list-table.inc >@@ -0,0 +1,36 @@ >+<table id="ill-requests"[% IF prefilters.length > 0 %] data-prefilters="[% prefilters %]"[% END %]> >+ <thead> >+ <tr id="illview-header"> >+ <th scope="col">Request ID</th> >+ <th scope="col">Author</th> >+ <th scope="col">Title</th> >+ <th scope="col">Article title</th> >+ <th scope="col">Issue</th> >+ <th scope="col">Volume</th> >+ <th scope="col">Year</th> >+ <th scope="col">Pages</th> >+ <th scope="col">Type</th> >+ <th scope="col">Order ID</th> >+ <th scope="col">Patron ID</th> >+ <th scope="col" class="patron_cardnumber">Patron barcode</th> >+ <th scope="col">Patron</th> >+ <th scope="col">Biblio ID</th> >+ <th scope="col">Branch</th> >+ <th scope="col">Status</th> >+ <th scope="col" class="placed"> </th> >+ <th scope="col" class="placed_formatted">Placed on</th> >+ <th scope="col" class="updated"> </th> >+ <th scope="col" class="updated_formatted">Updated on</th> >+ <th scope="col">Replied</th> >+ <th scope="col">Completed on</th> >+ <th scope="col">Access URL</th> >+ <th scope="col">Cost</th> >+ <th scope="col">OPAC notes</th> >+ <th scope="col">Staff notes</th> >+ <th scope="col">Backend</th> >+ <th scope="col" class="actions"></th> >+ </tr> >+ </thead> >+ <tbody id="illview-body"> >+ </tbody> >+</table> >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 d590717827..e867dd6dad 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 >@@ -499,44 +499,7 @@ > <h1>View ILL requests</h1> > <div id="results"> > <h3>Details for all requests</h3> >- >- <table id="ill-requests"> >- <thead> >- <tr id="illview-header"> >- <th scope="col">Request ID</th> >- <th scope="col">Author</th> >- <th scope="col">Title</th> >- <th scope="col">Article title</th> >- <th scope="col">Issue</th> >- <th scope="col">Volume</th> >- <th scope="col">Year</th> >- <th scope="col">Pages</th> >- <th scope="col">Type</th> >- <th scope="col">Order ID</th> >- <th scope="col">Patron ID</th> >- <th scope="col" class="patron_cardnumber">Patron barcode</th> >- <th scope="col">Patron</th> >- <th scope="col">Biblio ID</th> >- <th scope="col">Branch</th> >- <th scope="col">Status</th> >- <th scope="col" class="placed"> </th> >- <th scope="col" class="placed_formatted">Placed on</th> >- <th scope="col">Replied</th> >- <th scope="col" class="updated"> </th> >- <th scope="col" class="updated_formatted">Updated on</th> >- <th scope="col">Replied</th> >- <th scope="col">Completed on</th> >- <th scope="col">Access URL</th> >- <th scope="col">Cost</th> >- <th scope="col">OPAC notes</th> >- <th scope="col">Staff notes</th> >- <th scope="col">Backend</th> >- <th scope="col" class="actions"></th> >- </tr> >- </thead> >- <tbody id="illview-body"> >- </tbody> >- </table> >+ [% INCLUDE 'ill-list-table.inc' %] > </div> > [% ELSE %] > <!-- Custom Backend Action --> >@@ -548,506 +511,30 @@ > </div> > </div> > >-[% 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 %] > <script> >- $(document).ready(function() { >- >- // Illview Datatable setup >- >- var columns_settings = [% ColumnsSettings.GetColumns( 'illrequests', 'ill-requests', 'ill-requests', 'json' ) %]; >- >- var table; >- >- // Filters that are active >- var activeFilters = {}; >- >- // 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( >- '<option value="' + unique + >- '">' + unique + '</option>' >- ); >- }); >- }, >- 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( >- '<option value="' + unique + >- '">' + unique + '</option>' >- ); >- }); >- }, >- 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 = '<a title="' + _("View borrower details") + '" ' + >- 'href="/cgi-bin/koha/members/moremember.pl?' + >- 'borrowernumber='+row.borrowernumber+'">'; >- if ( row.patron_firstname ) { >- patronLink = patronLink + row.patron_firstname + ' '; >- } >- patronLink = patronLink + row.patron_surname + '</a>'; >- return patronLink; >- }; >- >- // Our 'render' function for biblio_id >- var createBiblioLink = function(data, type, row) { >- return (row.biblio_id) ? >- '<a title="' + _("View biblio details") + '" ' + >- 'href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=' + >- row.biblio_id + '">' + >- row.biblio_id + >- '</a>' : ''; >- }; >- >- // 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 '<a class="btn btn-default btn-sm" ' + >- 'href="/cgi-bin/koha/ill/ill-requests.pl?' + >- 'method=illview&illrequest_id=' + >- row.illrequest_id + >- '">' + _("Manage request") + '</a>'; >- }; >- >- // Columns that require special treatment >- var specialCols = { >- action: { >- func: createActionLink >- }, >- borrowernumber: { >- 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 ajax = $.ajax( >- '/api/v1/illrequests?embed=metadata,patron,capabilities,library,comments,status_alias,requested_partners' >- ).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", {}) >- var illTable = 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' ], >- 'bVisible': false, >- '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.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(); >- }); >- >- }); >+ // Date format for datepickers >+ var dateMap = { >+ dmydot: 'dd.mm.yy', >+ iso: 'yy-mm-dd', >+ metric: 'dd/mm/yy', >+ us: 'mm/dd/yy' >+ }; >+ var dateFormat = dateMap['[% Koha.Preference('dateformat') %]']; >+ var prefilters = '[% prefilters %]'; >+ // Set column settings >+ var columns_settings = [% ColumnsSettings.GetColumns( 'illrequests', 'ill-requests', 'ill-requests', 'json' ) %]; > </script> >+ [% 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..28a774c278 >--- /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' %] >+<title>Koha › Patrons › ILL requests for [% INCLUDE 'patron-title.inc' no_html = 1 %]</title> >+[% INCLUDE 'doc-head-close.inc' %] >+[% Asset.css("css/datatables.css") %] >+</head> >+ >+<body id="pat_ill_requests"> >+ [% INCLUDE 'header.inc' %] >+ [% INCLUDE 'patron-search.inc' %] >+ >+ <div id="breadcrumbs"> >+ <a href="/cgi-bin/koha/mainpage.pl">Home</a> >+ › <a href="/cgi-bin/koha/members/members-home.pl">Patrons</a> >+ › ILL requests for [% INCLUDE 'patron-title.inc' %] >+ </div> >+ >+ <div id="doc3" class="yui-t1"> >+ >+ <div id="bd"> >+ <div id="yui-main"> >+ <div class="yui-b"> >+ <div class="yui-g"> >+ <h2>ILL requests</h2> >+ [% INCLUDE 'ill-list-table.inc' %] >+ </div> >+ </div> >+ </div> >+ <div class="yui-b"> >+ [% INCLUDE 'circ-menu.inc' %] >+ </div> >+ </div> >+ >+ [% MACRO jsinclude BLOCK %] >+ [% INCLUDE 'datatables.inc' %] >+ [% INCLUDE 'columns_settings.inc' %] >+ [% INCLUDE 'calendar.inc' %] >+ [% Asset.js("lib/jquery/plugins/jquery.checkboxes.min.js") | $raw %] >+ <script> >+ // Date format for datepickers >+ var dateMap = { >+ dmydot: 'dd.mm.yy', >+ iso: 'yy-mm-dd', >+ metric: 'dd/mm/yy', >+ us: 'mm/dd/yy' >+ }; >+ var dateFormat = dateMap['[% Koha.Preference('dateformat') %]']; >+ var prefilters = '[% prefilters %]'; >+ // Set column settings >+ var columns_settings = [% ColumnsSettings.GetColumns( 'illrequests', 'ill-requests', 'ill-requests', 'json' ) %]; >+ </script> >+ [% 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..58904192e4 >--- /dev/null >+++ b/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( >+ '<option value="' + unique + >+ '">' + unique + '</option>' >+ ); >+ }); >+ }, >+ 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( >+ '<option value="' + unique + >+ '">' + unique + '</option>' >+ ); >+ }); >+ }, >+ 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 = '<a title="' + _("View borrower details") + '" ' + >+ 'href="/cgi-bin/koha/members/moremember.pl?' + >+ 'borrowernumber='+row.borrowernumber+'">'; >+ if ( row.patron_firstname ) { >+ patronLink = patronLink + row.patron_firstname + ' '; >+ } >+ patronLink = patronLink + row.patron_surname + '</a>'; >+ return patronLink; >+ }; >+ >+ // Our 'render' function for biblio_id >+ var createBiblioLink = function(data, type, row) { >+ return (row.biblio_id) ? >+ '<a title="' + _("View biblio details") + '" ' + >+ 'href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=' + >+ row.biblio_id + '">' + >+ row.biblio_id + >+ '</a>' : ''; >+ }; >+ >+ // 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 '<a class="btn btn-default btn-sm" ' + >+ 'href="/cgi-bin/koha/ill/ill-requests.pl?' + >+ 'method=illview&illrequest_id=' + >+ row.illrequest_id + >+ '">' + _("Manage request") + '</a>'; >+ }; >+ >+ // 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(); >+ }); >+ >+}); >diff --git a/members/ill-requests.pl b/members/ill-requests.pl >new file mode 100644 >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 <http://www.gnu.org/licenses>. >+ >+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
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 18589
:
74809
|
79692
|
82177
|
82513
|
84197
|
84199
|
84204
|
84792
|
84796
|
86432
|
86433
|
86456
|
86465
|
86504
|
86506
|
86507
|
86714
|
86715
|
86716
|
86717
|
86718
|
86719
|
86720
|
86721
|
86722
|
86735
|
86736
|
86737