Bugzilla – Attachment 130364 Details for
Bug 30055
Rewrite some of the patron searches to make them use the REST API routes
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 30055: Make patron searches use the REST API
Bug-30055-Make-patron-searches-use-the-REST-API.patch (text/plain), 43.10 KB, created by
Jonathan Druart
on 2022-02-09 14:25:05 UTC
(
hide
)
Description:
Bug 30055: Make patron searches use the REST API
Filename:
MIME Type:
Creator:
Jonathan Druart
Created:
2022-02-09 14:25:05 UTC
Size:
43.10 KB
patch
obsolete
>From c0868d3eedffcbfc2c1f45e754bae36c064e6a1b Mon Sep 17 00:00:00 2001 >From: Jonathan Druart <jonathan.druart@bugs.koha-community.org> >Date: Wed, 2 Feb 2022 18:00:58 +0100 >Subject: [PATCH] Bug 30055: Make patron searches use the REST API > >This patch will rewrite some of our patron searches to make them use the >REST API routes (and so the powerful the DataTables wrapper which will >bring all the nice DT feature to filter, sort, etc.) > >The patron searches we will take into account here are those that we use >to select a patron in a pop-up: > * Guarantor > * Suggestion's manager > * Patron's card > * Serial routing list > * Users to notify when order is received > * Manager of an acquisition basket > * Owner and users of a fund > >Regarding permissions there are two main problematics: > * Filter a patron set by patrons having a >specific subpermissions (in case of adding a manager to a suggestion or >when we deal with acquisition and funds). We added a new >Koha::Patrons->filter_by_have_subpermission method that will take in >parameter a subpermission. To make thing transparent for the callers we >are adding new routes, like /suggestions/managers to list the possible >managers of suggestions. > * Restrict/allow access to the default patron searches /patrons >We need to access it when a logged in patron does not have borrowers >permission. >Ideally we need a separate "search_borrowers" subpermissions but it's >considered outside the scope of this change. > >For each patch you will take care of testing the different permissions >that are into effect (either for the logged in patron or the patrons >returned by the search). > >The tables should contain the same columns as prior to this patch, >except for "categories" and "library". We have the filter on top of the >page and so we need to add them to the table as new columns if they >weren't there before. > >Test plan (for this patch): >Search for guarantor and select > >Test plan (for all patches): >Add/Select patrons from the correct place where you can search for >patrons, play extensively with the filters/pagination/etc > >Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org> >--- > .../prog/en/includes/js-patron-format.inc | 2 +- > .../prog/en/modules/common/patron_search.tt | 312 --------------- > .../prog/en/modules/members/search.tt | 368 ++++++++++++++++++ > koha-tmpl/intranet-tmpl/prog/js/datatables.js | 63 ++- > koha-tmpl/intranet-tmpl/prog/js/members.js | 8 +- > members/{guarantor_search.pl => search.pl} | 22 +- > 6 files changed, 433 insertions(+), 342 deletions(-) > delete mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/common/patron_search.tt > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/members/search.tt > rename members/{guarantor_search.pl => search.pl} (69%) > >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/js-patron-format.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/js-patron-format.inc >index 4dd47985640..0c1daae17ff 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/js-patron-format.inc >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/js-patron-format.inc >@@ -26,7 +26,7 @@ > firstname += ' (' + escape_str(patron.other_name) + ')'; > } > if ( config && config.invert_name ) { >- name = surname + ', ' + firstname; >+ name = surname + ( firstname ? ', ' + firstname : '' ); > } > else { > name = firstname + ' ' + surname; >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/common/patron_search.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/common/patron_search.tt >deleted file mode 100644 >index 9009d7a1875..00000000000 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/common/patron_search.tt >+++ /dev/null >@@ -1,312 +0,0 @@ >-[% USE raw %] >-[% USE Asset %] >-[% USE Koha %] >-[% USE Branches %] >-[% SET footerjs = 1 %] >-[% INCLUDE 'doc-head-open.inc' %] >-<title>Patron search › Koha</title> >-[% INCLUDE 'doc-head-close.inc' %] >-<style> .modal-body .close { display: none; } </style> >-</head> >- >-<body id="common_patron_search" class="common"> >-<div id="patron_search" class="yui-t7"> >- <div class="container-fluid"> >- >- <form id="searchform"> >- <fieldset class="brief"> >- <h3>Search for patron</h3> >- <ol> >- <li> >- <label for="searchmember_filter">Search:</label> >- <input type="text" id="searchmember_filter" value="[% searchmember | html %]" class="focus" /> >- </li> >- <li> >- <label for="categorycode_filter">Category:</label> >- <select id="categorycode_filter"> >- <option value="">Any</option> >- [% FOREACH category IN categories %] >- <option value="[% category.categorycode | html %]">[% category.description | html %]</option> >- [% END %] >- </select> >- </li> >- <li> >- <label for="branchcode_filter">Library:</label> >- <select id="branchcode_filter"> >- [% SET libraries = Branches.all( only_from_group => 1 ) %] >- [% IF libraries.size != 1 %] >- <option value="">Any</option> >- [% END %] >- [% FOREACH l IN libraries %] >- <option value="[% l.branchcode | html %]">[% l.branchname | html %]</option> >- [% END %] >- </select> >- </li> >- </ol> >- <fieldset class="action"> >- <input type="submit" value="Search" /> >- </fieldset> >- </fieldset> >- </form> >- >- [% IF patrons_with_acq_perm_only %] >- <div class="hint">Only staff with superlibrarian or acquisitions permissions (or order_manage permission if granular permissions are enabled) are returned in the search results</div> >- [% END %] >- >- [% IF patrons_with_suggestion_perm_only %] >- <div class="hint">Only staff with superlibrarian or suggestions_manage permissions are returned in the search results</div> >- [% END %] >- >- <div class="browse"> >- Browse by last name: >- [% FOREACH letter IN alphabet.split(' ') %] >- <a href="#" class="filterByLetter">[% letter | html %]</a> >- [% END %] >- </div> >- >- <div id="info" class="dialog message"></div> >- <div id="error" class="dialog alert"></div> >- >- <input type="hidden" id="firstletter_filter" value="" /> >- <div id="searchresults"> >- <table id="memberresultst"> >- <thead> >- <tr> >- [% FOR column IN columns %] >- [% SWITCH column %] >- [% CASE 'cardnumber' %]<th>Card</th> >- [% CASE 'dateofbirth' %]<th>Date of birth</th> >- [% CASE 'address' %]<th>Address</th> >- [% CASE 'name' %]<th>Name</th> >- [% CASE 'branch' %]<th>Library</th> >- [% CASE 'category' %]<th>Category</th> >- [% CASE 'dateexpiry' %]<th>Expires on</td> >- [% CASE 'borrowernotes' %]<th>Notes</th> >- [% CASE 'action' %]<th> </th> >- [% END %] >- [% END %] >- </tr> >- </thead> >- <tbody></tbody> >- </table> >- </div> >- >-<div id="closewindow"><a href="#" class="btn btn-default btn-default close">Close</a></div> >- >-<!-- Patron preview modal --> >-<div class="modal" id="patronPreview" tabindex="-1" role="dialog" aria-labelledby="patronPreviewLabel"> >- <div class="modal-dialog" role="document"> >- <div class="modal-content"> >- <div class="modal-header"> >- <button type="button" class="closebtn" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> >- <h4 class="modal-title" id="patronPreviewLabel"></h4> >- </div> >- <div class="modal-body"> >- <div id="loading"> >- <img src="[% interface | html %]/[% theme | html %]/img/spinner-small.gif" alt="" /> Loading >- </div> >- </div> >- <div class="modal-footer"> >- <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> >- </div> >- </div> >- </div> >-</div> >- >-</div> >-</div> >- >-[% MACRO jsinclude BLOCK %] >- [% INCLUDE 'datatables.inc' %] >- >- <script> >- var search = 1; >- $(document).ready(function(){ >- $("#info").hide(); >- $("#error").hide(); >- >- [% IF view != "show_results" %] >- $("#searchresults").hide(); >- search = 0; >- [% END %] >- >- // Apply DataTables on the results table >- dtMemberResults = $("#memberresultst").dataTable($.extend(true, {}, dataTablesDefaults, { >- 'bServerSide': true, >- 'sAjaxSource': "/cgi-bin/koha/svc/members/search", >- 'fnServerData': function(sSource, aoData, fnCallback) { >- if ( ! search ) { >- return; >- } >- aoData.push({ >- 'name': 'searchmember', >- 'value': $("#searchmember_filter").val() >- },{ >- 'name': 'firstletter', >- 'value': $("#firstletter_filter").val() >- },{ >- 'name': 'categorycode', >- 'value': $("#categorycode_filter").val() >- },{ >- 'name': 'branchcode', >- 'value': $("#branchcode_filter").val() >- },{ >- 'name': 'name_sorton', >- 'value': 'borrowers.surname borrowers.firstname' >- },{ >- 'name': 'cardnumber_sorton', >- 'value': 'borrowers.cardnumber', >- },{ >- 'name': 'dateofbirth_sorton', >- 'value': 'borrowers.dateofbirth', >- },{ >- 'name': 'dateexpiry_sorton', >- 'value': 'borrowers.dateexpiry', >- },{ >- 'name': 'category_sorton', >- 'value': 'categories.description', >- },{ >- 'name': 'branch_sorton', >- 'value': 'branches.branchname' >- },{ >- 'name': 'borrowernotes_sorton', >- 'value': 'borrowers.borrowernotes' >- },{ >- 'name': 'template_path', >- 'value': '[% json_template | html %]', >- },{ >- 'name': 'selection_type', >- 'value': '[% selection_type | html %]', >- } >- [% IF patrons_with_acq_perm_only %] >- ,{ >- 'name': 'has_permission', >- 'value': 'acquisition.order_manage', >- } >- [% ELSIF patrons_with_suggestion_perm_only %] >- ,{ >- 'name': 'has_permission', >- 'value': 'suggestions.suggestions_manage', >- } >- [% END %] >- ); >- $.ajax({ >- 'dataType': 'json', >- 'type': 'POST', >- 'url': sSource, >- 'data': aoData, >- 'success': function(json){ >- fnCallback(json); >- } >- }); >- }, >- 'aoColumns':[ >- [% FOR column IN columns %] >- [% IF column == 'action' %] >- { 'mDataProp': 'dt_action', 'bSortable': false, 'sClass': 'actions' } >- [% ELSIF column == 'address' %] >- { 'mDataProp': 'dt_address', 'bSortable': false } >- [% ELSE %] >- { 'mDataProp': 'dt_[% column | html %]' } >- [% END %] >- [% UNLESS loop.last %],[% END %] >- [% END %] >- ], >- 'bAutoWidth': false, >- 'sPaginationType': 'full_numbers', >- "iDisplayLength": [% Koha.Preference('PatronsPerPage') | html %], >- 'aaSorting': [[[% aaSorting || 0 | html %], 'asc']], >- 'bFilter': false, >- 'bProcessing': true, >- })); >- >- $("#searchform").on('submit', filter); >- $(".filterByLetter").on("click",function(e){ >- e.preventDefault(); >- filterByFirstLetterSurname($(this).text()); >- }); >- $("body").on("click",".add_user",function(e){ >- e.preventDefault(); >- var borrowernumber = $(this).data("borrowernumber"); >- var firstname = $(this).data("firstname"); >- var surname = $(this).data("surname"); >- add_user( borrowernumber, firstname + " " + surname ); >- }); >- >- $("body").on("click",".select_user",function(e){ >- e.preventDefault(); >- var borrowernumber = $(this).data("borrowernumber"); >- var borrower_data = $("#borrower_data"+borrowernumber).val(); >- select_user( borrowernumber, JSON.parse(borrower_data) ); >- }); >- >- $("body").on("click",".patron_preview", function( e ){ >- e.preventDefault(); >- var borrowernumber = $(this).data("borrowernumber"); >- var page = "/cgi-bin/koha/members/moremember.pl?print=brief&borrowernumber=" + borrowernumber; >- $("#patronPreview .modal-body").load( page + " div.container-fluid" ); >- $('#patronPreview').modal({show:true}); >- }); >- >- $("#patronPreview").on('hidden.bs.modal', function (e) { >- $("#patronPreview .modal-body").html("<img src=\"[% interface | html %]/[% theme | html %]/img/spinner-small.gif\" alt=\"\" /> Loading"); >- }); >- >- }); >- >- function filter() { >- search = 1; >- $("#firstletter_filter").val(''); >- $("#searchresults").show(); >- dtMemberResults.fnDraw(); >- return false; >- } >- >- // User has clicked on a letter >- function filterByFirstLetterSurname(letter) { >- $("#firstletter_filter").val(letter); >- search = 1; >- $("#searchresults").show(); >- dtMemberResults.fnDraw(); >- } >- >- // modify parent window owner element >- [% IF selection_type == 'add' %] >- function add_user(borrowernumber, borrowername) { >- var p = window.opener; >- // In one place (serials/routing.tt), the page is reload on every add >- // We have to wait for the page to be there >- function wait_for_opener () { >- if ( ! $(opener.document).find('body').size() ) { >- setTimeout(wait_for_opener, 500); >- } else { >- [%# Note that add_user could sent data instead of borrowername too %] >- $("#info").hide(); >- $("#error").hide(); >- if ( p.add_user(borrowernumber, borrowername) < 0 ) { >- $("#error").html(_("Patron '%s' is already in the list.").format(borrowername)); >- $("#error").show(); >- } else { >- $("#info").html(_("Patron '%s' added.").format(borrowername)); >- $("#info").show(); >- } >- } >- } >- wait_for_opener(); >- } >- [% ELSIF selection_type == 'select' %] >- function select_user(borrowernumber, data) { >- var p = window.opener; >- [% IF callback %] >- p.[% callback | html %](borrowernumber, data); >- [% ELSE %] >- p.select_user(borrowernumber, data); >- [% END %] >- window.close(); >- } >- [% END %] >- </script> >-[% END %] >- >-[% SET popup_window = 1 %] >-[% INCLUDE 'intranet-bottom.inc' %] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/search.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/search.tt >new file mode 100644 >index 00000000000..0bcc2cfd1d4 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/search.tt >@@ -0,0 +1,368 @@ >+[% USE raw %] >+[% USE To %] >+[% USE Asset %] >+[% USE Koha %] >+[% USE Branches %] >+[% USE Categories %] >+[% SET footerjs = 1 %] >+[% INCLUDE 'doc-head-open.inc' %] >+[% SET libraries = Branches.all %] >+[% SET categories = Categories.all.unblessed %] >+<title>Patron search › Koha</title> >+[% INCLUDE 'doc-head-close.inc' %] >+<style> .modal-body .close { display: none; } </style> >+</head> >+ >+<body id="common_patron_search" class="common"> >+<div id="patron_search" class="yui-t7"> >+ <div class="container-fluid"> >+ >+ <form id="searchform"> >+ <fieldset class="brief"> >+ <h3>Search for patron</h3> >+ <ol> >+ <li> >+ <label for="searchmember_filter">Search:</label> >+ <input type="text" id="searchmember_filter" value="[% searchmember | html %]" class="focus" /> >+ </li> >+ >+ [% FOR column IN columns %] >+ [% SWITCH column %] >+ [% CASE 'branch' %] >+ <li> >+ <label for="branchcode_filter">Library:</label> >+ <select id="branchcode_filter"> >+ [% SET libraries = Branches.all( only_from_group => 1 ) %] >+ [% IF libraries.size != 1 %] >+ <option value="">Any</option> >+ [% END %] >+ [% FOREACH l IN libraries %] >+ <option value="[% l.branchcode | html %]">[% l.branchname | html %]</option> >+ [% END %] >+ </select> >+ </li> >+ [% CASE 'category' %] >+ <li> >+ <label for="categorycode_filter">Category:</label> >+ <select id="categorycode_filter"> >+ <option value="">Any</option> >+ [% FOREACH category IN categories %] >+ <option value="[% category.categorycode | html %]">[% category.description | html %]</option> >+ [% END %] >+ </select> >+ </li> >+ [% END %] >+ [% END %] >+ </ol> >+ <fieldset class="action"> >+ <input type="submit" value="Search" /> >+ </fieldset> >+ </fieldset> >+ </form> >+ >+ [% IF patrons_with_acq_perm_only %] >+ <div class="hint">Only staff with superlibrarian or acquisitions permissions (or order_manage permission if granular permissions are enabled) are returned in the search results</div> >+ [% END %] >+ >+ [% IF patrons_with_suggestion_perm_only %] >+ <div class="hint">Only staff with superlibrarian or suggestions_manage permissions are returned in the search results</div> >+ [% END %] >+ >+ <div class="browse"> >+ Browse by last name: >+ [% FOREACH letter IN alphabet.split(' ') %] >+ <a href="#" class="filterByLetter">[% letter | html %]</a> >+ [% END %] >+ </div> >+ >+ <div id="info" class="dialog message"></div> >+ <div id="error" class="dialog alert"></div> >+ >+ <input type="hidden" id="firstletter_filter" value="" /> >+ <div id="searchresults"> >+ <table id="memberresultst"> >+ <thead> >+ <tr> >+ [% FOR column IN columns %] >+ [% SWITCH column %] >+ [% CASE 'cardnumber' %]<th>Card</th> >+ [% CASE 'dateofbirth' %]<th>Date of birth</th> >+ [% CASE 'address' %]<th>Address</th> >+ [% CASE 'name' %]<th>Name</th> >+ [% CASE 'branch' %]<th data-filter="libraries">Library</th> >+ [% CASE 'category' %]<th data-filter="categories">Category</th> >+ [% CASE 'dateexpiry' %]<th>Expires on</td> >+ [% CASE 'borrowernotes' %]<th>Notes</th> >+ [% CASE 'action' %]<th> </th> >+ [% END %] >+ [% END %] >+ </tr> >+ </thead> >+ <tbody></tbody> >+ </table> >+ </div> >+ >+<div id="closewindow"><a href="#" class="btn btn-default btn-default close">Close</a></div> >+ >+<!-- Patron preview modal --> >+<div class="modal" id="patronPreview" tabindex="-1" role="dialog" aria-labelledby="patronPreviewLabel"> >+ <div class="modal-dialog" role="document"> >+ <div class="modal-content"> >+ <div class="modal-header"> >+ <button type="button" class="closebtn" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> >+ <h4 class="modal-title" id="patronPreviewLabel"></h4> >+ </div> >+ <div class="modal-body"> >+ <div id="loading"> >+ <img src="[% interface | html %]/[% theme | html %]/img/spinner-small.gif" alt="" /> Loading >+ </div> >+ </div> >+ <div class="modal-footer"> >+ <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> >+ </div> >+ </div> >+ </div> >+</div> >+ >+</div> >+</div> >+ >+[% MACRO jsinclude BLOCK %] >+ <script> >+ let categories = [% To.json(categories) | $raw %]; >+ let categories_map = categories.reduce((map, c) => { >+ map[c.categorycode] = c; >+ return map; >+ }, {}); >+ let libraries = [% To.json(libraries) | $raw %]; >+ let libraries_map = libraries.reduce((map, l) => { >+ map[l.branchcode] = l; >+ return map; >+ }, {}); >+ </script> >+ [% INCLUDE 'datatables.inc' %] >+ [% INCLUDE 'js-date-format.inc' %] >+ [% INCLUDE 'js-patron-get-age.inc' %] >+ [% INCLUDE 'js-patron-format.inc' %] >+ [% INCLUDE 'js-patron-format-address.inc' %] >+ >+ <script> >+ var search = 0; >+ let patrons_table; >+ $(document).ready(function(){ >+ $("#info").hide(); >+ $("#error").hide(); >+ >+ $("#searchresults").hide(); >+ >+ let additional_filters = { >+ surname: function(){ >+ let start_with = $("#firstletter_filter").val() >+ if (!start_with) return ""; >+ return { "like": start_with + "%" } >+ } >+ }; >+ patrons_table = $("#memberresultst").kohaTable({ >+ "ajax": { >+ "url": '/api/v1/patrons' >+ }, >+ "order": [[ 1, "asc" ]], >+ "iDeferLoading": 0, >+ "columns": [ >+ [% FOR column IN columns %] >+ [% SWITCH column %] >+ [% CASE 'cardnumber' %] >+ { >+ "data": "cardnumber", >+ "searchable": true, >+ "orderable": true >+ } >+ [% CASE 'dateofbirth' %] >+ { >+ "data": "date_of_birth", >+ "searchable": true, >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ return data ? escape_str($date(data) + " (" + _("%s years").format($get_age(data)) + ")") : ""; >+ } >+ } >+ [% CASE 'address' %] >+ { >+ "data": "me.street_number:me.address:me.address2:me.city:me.state:me.postal_code:me.country", >+ "searchable": true, >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ return escape_str($format_address(row) + " ") + escape_str(libraries_map[row.library_id].branchname); >+ } >+ } >+ [% CASE 'name' %] >+ { >+ "data": "me.firstname:me.surname", >+ "searchable": true, >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ return $patron_to_html(row, { invert_name: 1 }); >+ } >+ } >+ [% CASE 'branch' %] >+ { >+ "data": "library_id", >+ "searchable": true, >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ return escape_str(libraries_map[data].branchname); >+ } >+ } >+ [% CASE 'category' %] >+ { >+ "data": "category_id", >+ "searchable": true, >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ return escape_str(categories_map[data].description); >+ } >+ } >+ [% CASE 'dateexpiry' %] >+ { >+ "data": "expiry_date", >+ "searchable": true, >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ return data ? escape_str($date(data)) : ""; >+ } >+ } >+ [% CASE 'borrowernotes' %] >+ { >+ "data": "staff_notes", >+ "searchable": true, >+ "orderable": true, >+ [%# We don't escape here, we allow html tag in staff notes %] >+ } >+ [% CASE 'action' %] >+ { >+ "data": function( row, type, val, meta ) { >+ >+ let patron_id = encodeURIComponent(row.patron_id); >+ [% IF selection_type == 'select' %] >+ return '<a href="#" class="btn btn-default btn-xs select_user" data-borrowernumber="' + patron_id + '">Select</a><input type="hidden" id="borrower_data' + patron_id + '" name="borrower_data'+ patron_id + '" value=\''+JSON.stringify(row)+'\' />'; >+ [% ELSE %] >+ return '<a href="#" class="btn btn-default btn-xs add_user" data-borrowernumber="' + patron_id + '" data-firstname="' + encodeURIComponent(row.firstname) + '" data-surname="' + encodeURIComponent(row.surname) + '">Add</a><input type="hidden" id="borrower_data' + patron_id + '" name="borrower_data'+ patron_id + '" />'; >+ [% END %] >+ }, >+ "searchable": false, >+ "orderable": false >+ } >+ [% END %] >+ [% UNLESS loop.last %],[% END %] >+ [% END %] >+ ], >+ 'bAutoWidth': false, >+ 'sPaginationType': 'full_numbers', >+ "iDisplayLength": [% Koha.Preference('PatronsPerPage') | html %], >+ }, null, 1, additional_filters); >+ >+ $("#searchform").on('submit', filter); >+ $(".filterByLetter").on("click",function(e){ >+ e.preventDefault(); >+ filterByFirstLetterSurname($(this).text()); >+ }); >+ $("body").on("click",".add_user",function(e){ >+ e.preventDefault(); >+ var borrowernumber = $(this).data("borrowernumber"); >+ var firstname = $(this).data("firstname"); >+ var surname = $(this).data("surname"); >+ add_user( borrowernumber, firstname + " " + surname ); >+ }); >+ >+ $("body").on("click",".select_user",function(e){ >+ e.preventDefault(); >+ var borrowernumber = $(this).data("borrowernumber"); >+ var borrower_data = $("#borrower_data"+borrowernumber).val(); >+ select_user( borrowernumber, JSON.parse(borrower_data) ); >+ }); >+ >+ $("body").on("click",".patron_preview", function( e ){ >+ e.preventDefault(); >+ var borrowernumber = $(this).data("borrowernumber"); >+ var page = "/cgi-bin/koha/members/moremember.pl?print=brief&borrowernumber=" + borrowernumber; >+ $("#patronPreview .modal-body").load( page + " div.container-fluid" ); >+ $('#patronPreview').modal({show:true}); >+ }); >+ >+ $("#patronPreview").on('hidden.bs.modal', function (e) { >+ $("#patronPreview .modal-body").html("<img src=\"[% interface | html %]/[% theme | html %]/img/spinner-small.gif\" alt=\"\" /> Loading"); >+ }); >+ >+ }); >+ >+ function filter() { >+ search = 1; >+ $("#firstletter_filter").val(''); >+ $("#searchresults").show(); >+ >+ let table_dt = patrons_table.DataTable(); >+ [% FOR c IN columns %] >+ [% SWITCH c %] >+ [% CASE 'branch' %] >+ let library_id = $("#branchcode_filter").val(); >+ patrons_table.find('thead tr:eq(1) th[data-filter="libraries"] select').val(library_id); >+ table_dt.column([% loop.count - 1 %]).search(library_id); >+ [% CASE 'category' %] >+ let category_id = $("#categorycode_filter").val(); >+ patrons_table.find('thead tr:eq(1) th[data-filter="categories"] select').val(category_id); >+ table_dt.column([% loop.count - 1 %]).search(category_id); >+ [% END %] >+ [% END %] >+ table_dt.search($("#searchmember_filter").val()); >+ table_dt.draw(); >+ return false; >+ } >+ >+ // User has clicked on a letter >+ function filterByFirstLetterSurname(letter) { >+ $("#firstletter_filter").val(letter); >+ search = 1; >+ $("#searchresults").show(); >+ patrons_table.fnDraw(); >+ } >+ >+ // modify parent window owner element >+ [% IF selection_type == 'add' %] >+ function add_user(borrowernumber, borrowername) { >+ var p = window.opener; >+ // In one place (serials/routing.tt), the page is reload on every add >+ // We have to wait for the page to be there >+ function wait_for_opener () { >+ if ( ! $(opener.document).find('body').size() ) { >+ setTimeout(wait_for_opener, 500); >+ } else { >+ [%# Note that add_user could sent data instead of borrowername too %] >+ $("#info").hide(); >+ $("#error").hide(); >+ if ( p.add_user(borrowernumber, borrowername) < 0 ) { >+ $("#error").html(_("Patron '%s' is already in the list.").format(borrowername)); >+ $("#error").show(); >+ } else { >+ $("#info").html(_("Patron '%s' added.").format(borrowername)); >+ $("#info").show(); >+ } >+ } >+ } >+ wait_for_opener(); >+ } >+ [% ELSIF selection_type == 'select' %] >+ function select_user(borrowernumber, data) { >+ var p = window.opener; >+ [% IF callback %] >+ p.[% callback | html %](borrowernumber, data); >+ [% ELSE %] >+ p.select_user(borrowernumber, data); >+ [% END %] >+ window.close(); >+ } >+ [% END %] >+ </script> >+[% END %] >+ >+[% SET popup_window = 1 %] >+[% INCLUDE 'intranet-bottom.inc' %] >diff --git a/koha-tmpl/intranet-tmpl/prog/js/datatables.js b/koha-tmpl/intranet-tmpl/prog/js/datatables.js >index c95a9723284..73e67fde4f7 100644 >--- a/koha-tmpl/intranet-tmpl/prog/js/datatables.js >+++ b/koha-tmpl/intranet-tmpl/prog/js/datatables.js >@@ -599,7 +599,20 @@ jQuery.fn.dataTable.ext.errMode = function(settings, note, message) { > }); > > if ( default_filters ) { >- and_query_parameters.push(default_filters); >+ let additional_filters = {}; >+ for ( f in default_filters ) { >+ if ( typeof(default_filters[f]) === 'function' ) { >+ let val = default_filters[f](); >+ if ( val != undefined && val != "" ) { >+ additional_filters[f] = val; >+ } >+ } else { >+ additional_filters[f] = default_filters[f]; >+ } >+ } >+ if ( Object.keys(additional_filters).length ) { >+ and_query_parameters.push(additional_filters); >+ } > } > query_parameters = and_query_parameters; > query_parameters.push(or_query_parameters); >@@ -772,16 +785,50 @@ jQuery.fn.dataTable.ext.errMode = function(settings, note, message) { > $(this).find('thead tr:eq(1) th').each( function (i) { > var is_searchable = table_dt.settings()[0].aoColumns[i].bSearchable; > if ( is_searchable ) { >- var title = $(this).text(); >- var existing_search = table_dt.column(i).search(); >- if ( existing_search ) { >- $(this).html( '<input type="text" value="%s" style="width: 100%" />'.format(existing_search) ); >+ let input_type = 'input'; >+ if ( $(this).data('filter') ) { >+ input_type = 'select' >+ let filter_type = $(this).data('filter'); >+ if ( filter_type == 'libraries' ) { >+ var existing_search = table_dt.column(i).search(); >+ let select = $('<select><option value=""></option></select'); >+ >+ $(libraries).each(function(){ >+ let o = $('<option value="%s">%s</option>'.format(this.branchcode, this.branchname)); >+ if ( existing_search == this.branchcode ) { >+ o.prop("selected", "selected"); >+ } >+ o.appendTo(select); >+ }); >+ $(this).html( select ); >+ } else if ( filter_type == 'categories' ) { >+ var existing_search = table_dt.column(i).search(); >+ let select = $('<select><option value=""></option></select'); >+ >+ $(categories).each(function(){ >+ let o = $('<option value="%s">%s</option>'.format(this.categorycode, this.description)); >+ if ( existing_search == this.categorycode ) { >+ o.prop("selected", "selected"); >+ } >+ o.appendTo(select); >+ }); >+ $(this).html( select ); >+ } else { >+ console.log("Unknown filter " + filter_type); >+ return; >+ } > } else { >- var search_title = _("%s search").format(title); >- $(this).html( '<input type="text" placeholder="%s" style="width: 100%" />'.format(search_title) ); >+ var title = $(this).text(); >+ var existing_search = table_dt.column(i).search(); >+ if ( existing_search ) { >+ $(this).html( '<input type="text" value="%s" style="width: 100%" />'.format(existing_search) ); >+ } else { >+ var search_title = _("%s search").format(title); >+ $(this).html( '<input type="text" placeholder="%s" style="width: 100%" />'.format(search_title) ); >+ } > } > >- $( 'input', this ).on( 'keyup change', function () { >+ $( input_type, this ).on( 'keyup change', function () { > if ( table_dt.column(i).search() !== this.value ) { > table_dt > .column(i) >diff --git a/koha-tmpl/intranet-tmpl/prog/js/members.js b/koha-tmpl/intranet-tmpl/prog/js/members.js >index a9e6a870ae5..5559ac03635 100644 >--- a/koha-tmpl/intranet-tmpl/prog/js/members.js >+++ b/koha-tmpl/intranet-tmpl/prog/js/members.js >@@ -77,12 +77,12 @@ function update_category_code(category_code) { > } > > function select_user(borrowernumber, borrower, relationship) { >- let is_guarantor = $(`.guarantor-details[data-borrowernumber=${borrower.borrowernumber}]`).length; >+ let is_guarantor = $(`.guarantor-details[data-borrowernumber=${borrowernumber}]`).length; > > if ( is_guarantor ) { > alert("Patron is already a guarantor for this patron"); > } else { >- $('#guarantor_id').val(borrower.borrowernumber); >+ $('#guarantor_id').val(borrowernumber); > $('#guarantor_surname').val(borrower.surname); > $('#guarantor_firstname').val(borrower.firstname); > >@@ -113,7 +113,7 @@ function select_user(borrowernumber, borrower, relationship) { > fieldset.find('.new_guarantor_relationship').first().val( guarantor_relationship ); > $('#relationship').find('option:eq(0)').prop('selected', true); > >- fieldset.find('.guarantor-details').first().attr( 'data-borrowernumber', borrower.borrowernumber ); >+ fieldset.find('.guarantor-details').first().attr( 'data-borrowernumber', borrowernumber ); > > $('#guarantor_relationships').append( fieldset ); > fieldset.show(); >@@ -197,7 +197,7 @@ $(document).ready(function(){ > > $('body').on('click', '#guarantor_search', function(e) { > e.preventDefault(); >- var newin = window.open('guarantor_search.pl','popup','width=800,height=600,resizable=no,toolbar=false,scrollbars=yes,top'); >+ var newin = window.open('/cgi-bin/koha/members/search.pl?columns=cardnumber,name,category,branch,dateofbirth,address,action','popup','width=800,height=600,resizable=no,toolbar=false,scrollbars=yes,top'); > }); > > $('#guarantor_relationships').on('click', '.guarantor_cancel', function(e) { >diff --git a/members/guarantor_search.pl b/members/search.pl >similarity index 69% >rename from members/guarantor_search.pl >rename to members/search.pl >index 40142eb9131..630f509177f 100755 >--- a/members/guarantor_search.pl >+++ b/members/search.pl >@@ -2,8 +2,6 @@ > > # This file is part of Koha. > # >-# Copyright 2014 BibLibre >-# > # 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 >@@ -22,35 +20,25 @@ use Modern::Perl; > use CGI qw ( -utf8 ); > use C4::Auth qw( get_template_and_user ); > use C4::Output qw( output_html_with_http_headers ); >-use C4::Members; >- >-use Koha::Patron::Categories; > > my $input = CGI->new; > >-my $dbh = C4::Context->dbh; >- > my ( $template, $loggedinuser, $cookie, $staff_flags ) = get_template_and_user( >- { template_name => "common/patron_search.tt", >+ { template_name => "members/search.tt", > query => $input, > type => "intranet", >- flagsrequired => { borrowers => 'edit_borrowers' }, >+ flagsrequired => { catalogue => '*' }, > } > ); > >-my $q = $input->param('q') || ''; >-my $op = $input->param('op') || ''; >- > my $referer = $input->referer(); > >-my $patron_categories = Koha::Patron::Categories->search_with_library_limits; >+my @columns = split ',', $input->param('columns'); >+ > $template->param( > view => ( $input->request_method() eq "GET" ) ? "show_form" : "show_results", >- columns => ['cardnumber', 'name', 'dateofbirth', 'address', 'action' ], >- json_template => 'members/tables/guarantor_search.tt', >+ columns => \@columns, > selection_type => 'select', > alphabet => ( C4::Context->preference('alphabet') || join ' ', 'A' .. 'Z' ), >- categories => $patron_categories, >- aaSorting => 1, > ); > output_html_with_http_headers( $input, $cookie, $template->output ); >-- >2.25.1
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 30055
:
130364
|
130365
|
130366
|
130367
|
130368
|
130369
|
130370
|
130371
|
130372
|
130385
|
130386
|
130387
|
130388
|
130389
|
130390
|
130391
|
130392
|
130393
|
130394
|
130395
|
130457
|
130487
|
130488
|
130491
|
130507
|
130511
|
130629
|
130630
|
130671
|
130672
|
130701
|
130702
|
130704
|
130716
|
130717
|
131829
|
132168
|
132419
|
132420
|
132421
|
132422
|
132423
|
132424
|
132425
|
132426
|
132427
|
132428
|
132429
|
132430
|
132431
|
132432
|
132433
|
132434
|
132435
|
132436
|
132437
|
132438
|
132439
|
132440
|
132441
|
132450
|
132451
|
132452
|
132453
|
132454
|
132455
|
132456
|
132457
|
132458
|
132459
|
132460
|
132461
|
132462
|
132463
|
132464
|
132465
|
132466
|
132467
|
132468
|
132469
|
132470
|
132471
|
132472
|
132473
|
132539
|
132616
|
132617
|
132618
|
132619
|
132620
|
132621
|
132622
|
132623
|
132624
|
132625
|
132627
|
132628
|
132629
|
132630
|
132631
|
132632
|
132633
|
132634
|
132635
|
132636
|
132637
|
132638
|
132639
|
132640
|
132641
|
132664
|
132665
|
132666
|
132667
|
132668
|
132669
|
132670
|
132671
|
132672
|
132673
|
132674
|
132675
|
132676
|
132677
|
132678
|
132679
|
132680
|
132681
|
132682
|
132683
|
132684
|
132685
|
132686
|
132687
|
132688