Bugzilla – Attachment 26067 Details for
Bug 9811
Patrons search improvements
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 9811: Patron search improvement
Bug-9811-Patron-search-improvement.patch (text/plain), 77.40 KB, created by
Jonathan Druart
on 2014-03-11 10:34:28 UTC
(
hide
)
Description:
Bug 9811: Patron search improvement
Filename:
MIME Type:
Creator:
Jonathan Druart
Created:
2014-03-11 10:34:28 UTC
Size:
77.40 KB
patch
obsolete
>From 0e2afd7339499273f78460e8b5446f397d34e888 Mon Sep 17 00:00:00 2001 >From: Jonathan Druart <jonathan.druart@biblibre.com> >Date: Tue, 19 Mar 2013 11:12:26 +0100 >Subject: [PATCH] Bug 9811: Patron search improvement > >This patch add DataTables using server-side processing for the patrons >search. > >It adds: >- 1 module C4/Utils/DataTables/Members.pm >- 2 services svc/members/search and svc/members/add_to_list >- 1 template members/tables/members_results.tt >- 1 new practice which is to add template for DataTables in a > subdirectory named 'tables'. > >Impacted scripts: members/members-home.pl and members/members.pl > >To go further: We can imagine that all patrons searches use the same >service with no big changes: 1 little template creates a JSON file and >to implement DataTables on the template page, that's all. > >Amended patch: Since bug 10565 has been pushed, these patches don't >apply cleanly. I had to rewrite a part of the patron list feature. >I removed the choice to add all resultant patrons from a search. I think >this choice is useless with this patch: we are able to display the >number of patrons we want and to select all of them. > >Test plan: >- Check that there is no regression on searching patrons. >- Try filters on the left of the screen. >- Try to sort each column. >- Try the "Browse by last name" links. >- Check that the "Clear" button clears yours filters. >- Try with IndependantBranches ON and OFF. >- Verify this feature does not break the patron list feature (cf bug > 10565). > >Signed-off-by: Cedric Vita <cedric.vita@dracenie.com> >Signed-off-by: Katrin Fischer <Katrin.Fischer.83@web.de> >Passes all tests and QA script, couldn't find any regressions >or problems. Some notes left on the bug. > >Bug 9811: Add unit tests for C4::Utils::DT::Members > >Signed-off-by: Katrin Fischer <Katrin.Fischer.83@web.de> > >Bug 9811: QA followup > >- removes 2 tabs >- removes mysqlisms >- add sort on borrowernotes >- fix wrong capitalization >- cat => Category > >Signed-off-by: Katrin Fischer <Katrin.Fischer.83@web.de> >Thx for fixing these! > >Bug 9811 - multilines notes brakes JSON > >In new patron search feature, the search results are fetched using Ajax and returned in JSON format. >The JSON is created by TT using koha-tmpl/intranet-tmpl/prog/en/modules/members/tables/members_results.tt. >One of the fields is the borrower notes. When this notes contains several lines, the JSON is broken. > >This patch uses TT fileters to consert in notes linefeeds into HTML line break (html_line_break) and then remove linefeeds (collapse). > >Test plan : >- perform a member search that does not return a borrower with a circ note >- edit one of the borrowers returned by this search >- enter serveral lines of text in "Circulation note" and save >- reperform the member search >=> circ note is well displayed on several lines > >Bug 9811: use count(primary_key) instead of count(*) > >Bug 9811: A limit clause should be always added. > >By default, we want to retrieve 20 first results. > >Bug 9811: Load the page without any data. > >Displaying the first 20 patrons is not useful. With this patch, the >table is hidden and no record is retrieved by default. >On the same way, the existing side effect on redirect disappears. >--- > C4/Members.pm | 2 +- > C4/Utils/DataTables/Members.pm | 213 +++++++ > .../intranet-tmpl/prog/en/includes/home-search.inc | 3 +- > .../prog/en/includes/patron-search.inc | 147 +++-- > .../prog/en/includes/patron-title.inc | 44 +- > koha-tmpl/intranet-tmpl/prog/en/js/datatables.js | 2 +- > .../prog/en/modules/members/member.tt | 603 ++++++++++++++------ > .../en/modules/members/tables/members_results.tt | 35 ++ > members/member.pl | 230 ++------ > members/members-home.pl | 27 +- > members/moremember.pl | 20 +- > svc/members/add_to_list | 63 ++ > svc/members/search | 119 ++++ > t/DataTables/Members.t | 14 + > 14 files changed, 1078 insertions(+), 444 deletions(-) > create mode 100644 C4/Utils/DataTables/Members.pm > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/members/tables/members_results.tt > create mode 100755 svc/members/add_to_list > create mode 100755 svc/members/search > create mode 100644 t/DataTables/Members.t > >diff --git a/C4/Members.pm b/C4/Members.pm >index a0769ca..8dbdfd9 100644 >--- a/C4/Members.pm >+++ b/C4/Members.pm >@@ -275,7 +275,7 @@ sub Search { > else { > $filter = { '' => $filter, branchcode => $branch }; > } >- } >+ } > } > } > >diff --git a/C4/Utils/DataTables/Members.pm b/C4/Utils/DataTables/Members.pm >new file mode 100644 >index 0000000..f667cdb >--- /dev/null >+++ b/C4/Utils/DataTables/Members.pm >@@ -0,0 +1,213 @@ >+package C4::Utils::DataTables::Members; >+ >+use C4::Branch qw/onlymine/; >+use C4::Context; >+use C4::Members qw/GetMemberIssuesAndFines/; >+use C4::Utils::DataTables; >+use Modern::Perl; >+ >+sub search { >+ my ( $params ) = @_; >+ my $searchmember = $params->{searchmember}; >+ my $firstletter = $params->{firstletter}; >+ my $categorycode = $params->{categorycode}; >+ my $branchcode = $params->{branchcode}; >+ my $searchtype = $params->{searchtype}; >+ my $searchfieldstype = $params->{searchfieldstype}; >+ my $dt_params = $params->{dt_params}; >+ >+ my ($iTotalRecords, $iTotalDisplayRecords); >+ >+ # If branches are independant and user is not superlibrarian >+ # The search has to be only on the user branch >+ if ( C4::Branch::onlymine ) { >+ my $userenv = C4::Context->userenv; >+ $branchcode = $userenv->{'branch'}; >+ >+ } >+ >+ my $dbh = C4::Context->dbh; >+ my $select = "SELECT >+ borrowers.borrowernumber, borrowers.surname, borrowers.firstname, borrowers.address, >+ borrowers.address2, borrowers.city, borrowers.zipcode, borrowers.country, >+ CAST(borrowers.cardnumber AS UNSIGNED) AS cardnumber, borrowers.dateexpiry, >+ borrowers.borrowernotes, borrowers.branchcode, >+ categories.description AS category_description, categories.category_type, >+ branches.branchname"; >+ my $from = "FROM borrowers >+ LEFT JOIN branches ON borrowers.branchcode = branches.branchcode >+ LEFT JOIN categories ON borrowers.categorycode = categories.categorycode"; >+ my @where_args; >+ my @where_strs; >+ if(defined $firstletter and $firstletter ne '') { >+ push @where_strs, "borrowers.surname LIKE ?"; >+ push @where_args, "$firstletter%"; >+ } >+ if(defined $categorycode and $categorycode ne '') { >+ push @where_strs, "borrowers.categorycode = ?"; >+ push @where_args, $categorycode; >+ } >+ if(defined $branchcode and $branchcode ne '') { >+ push @where_strs, "borrowers.branchcode = ?"; >+ push @where_args, $branchcode; >+ } >+ >+ # split on coma >+ $searchmember =~ s/,/ /g if $searchmember; >+ my @where_strs_or; >+ my $searchfields = { >+ standard => 'surname,firstname,othernames,cardnumber', >+ email => 'email,emailpro,B_email', >+ borrowernumber => 'borrowernumber', >+ phone => 'phone,phonepro,B_phone,altcontactphone,mobile', >+ address => 'streettype,address,address2,city,state,zipcode,country', >+ dateofbirth => 'dateofbirth', >+ sort1 => 'sort1', >+ sort2 => 'sort2', >+ }; >+ for my $searchfield ( split /,/, $searchfields->{$searchfieldstype} ) { >+ foreach my $term ( split / /, $searchmember) { >+ next unless $term; >+ $searchmember =~ s/\*/%/g; # * is replaced with % for sql >+ $term .= '%' # end with anything >+ if $term !~ /%$/; >+ $term = "%$term" # begin with anythin unless start_with >+ if $term !~ /^%/ >+ and $searchtype eq "contain"; >+ push @where_strs_or, "borrowers." . $dbh->quote_identifier($searchfield) . " LIKE ?"; >+ push @where_args, $term; >+ } >+ } >+ push @where_strs, '('. join (' OR ', @where_strs_or) . ')' >+ if @where_strs_or; >+ >+ my $where; >+ $where = " WHERE " . join (" AND ", @where_strs) if @where_strs; >+ my $orderby = dt_build_orderby($dt_params); >+ >+ my $limit; >+ # If iDisplayLength == -1, we want to display all patrons >+ if ( $dt_params->{iDisplayLength} > -1 ) { >+ # In order to avoid sql injection >+ $dt_params->{iDisplayStart} =~ s/\D//g; >+ $dt_params->{iDisplayLength} =~ s/\D//g; >+ $dt_params->{iDisplayStart} //= 0; >+ $dt_params->{iDisplayLength} //= 20; >+ $limit = "LIMIT $dt_params->{iDisplayStart},$dt_params->{iDisplayLength}"; >+ } >+ >+ my $query = join( >+ " ", >+ ($select ? $select : ""), >+ ($from ? $from : ""), >+ ($where ? $where : ""), >+ ($orderby ? $orderby : ""), >+ ($limit ? $limit : "") >+ ); >+ my $sth = $dbh->prepare($query); >+ $sth->execute(@where_args); >+ my $patrons = $sth->fetchall_arrayref({}); >+ >+ # Get the iTotalDisplayRecords DataTable variable >+ $query = "SELECT COUNT(borrowers.borrowernumber) " . $from . ($where ? $where : ""); >+ $sth = $dbh->prepare($query); >+ $sth->execute(@where_args); >+ ($iTotalDisplayRecords) = $sth->fetchrow_array; >+ >+ # Get the iTotalRecords DataTable variable >+ $query = "SELECT COUNT(borrowers.borrowernumber) FROM borrowers"; >+ $sth = $dbh->prepare($query); >+ $sth->execute; >+ ($iTotalRecords) = $sth->fetchrow_array; >+ >+ # Get some information on patrons >+ foreach my $patron (@$patrons) { >+ ($patron->{overdues}, $patron->{issues}, $patron->{fines}) = >+ GetMemberIssuesAndFines($patron->{borrowernumber}); >+ if($patron->{dateexpiry} and $patron->{dateexpiry} ne '0000-00-00') { >+ $patron->{dateexpiry} = C4::Dates->new($patron->{dateexpiry}, "iso")->output(); >+ } else { >+ $patron->{dateexpiry} = ''; >+ } >+ $patron->{fines} = sprintf("%.2f", $patron->{fines} || 0); >+ } >+ >+ return { >+ iTotalRecords => $iTotalRecords, >+ iTotalDisplayRecords => $iTotalDisplayRecords, >+ patrons => $patrons >+ } >+} >+ >+1; >+__END__ >+ >+=head1 NAME >+ >+C4::Utils::DataTables::Members - module for using DataTables with patrons >+ >+=head1 SYNOPSIS >+ >+This module provides (one for the moment) routines used by the patrons search >+ >+=head2 FUNCTIONS >+ >+=head3 search >+ >+ my $dt_infos = C4::Utils::DataTables::Members->search($params); >+ >+$params is a hashref with some keys: >+ >+=over 4 >+ >+=item searchmember >+ >+ String to search in the borrowers sql table >+ >+=item firstletter >+ >+ Introduced to contain 1 letter but can contain more. >+ The search will done on the borrowers.surname field >+ >+=item categorycode >+ >+ Search patrons with this categorycode >+ >+=item branchcode >+ >+ Search patrons with this branchcode >+ >+=item searchtype >+ >+ Can be 'contain' or 'start_with'. Used for the searchmember parameter. >+ >+=item searchfieldstype >+ >+ Can be 'standard', 'email', 'borrowernumber', 'phone', 'address' or 'dateofbirth', 'sort1', 'sort2' >+ >+=item dt_params >+ >+ Is the reference of C4::Utils::DataTables::dt_get_params($input); >+ >+=cut >+ >+=back >+ >+=head1 LICENSE >+ >+Copyright 2013 BibLibre >+ >+This file is part of Koha. >+ >+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 2 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, write to the Free Software Foundation, Inc., >+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/home-search.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/home-search.inc >index 46d8b45..f61ce85 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/home-search.inc >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/home-search.inc >@@ -15,7 +15,8 @@ > <div id="patron_search" class="residentsearch"> > <p class="tip">Enter patron card number or partial name:</p> > <form action="/cgi-bin/koha/members/member.pl" method="post"> >- <input name="member" id="searchmember" size="40" type="text"/> >+ <input name="searchmember" id="searchmember" size="40" type="text"/> >+ <input type="hidden" name="quicksearch" value="1" /> > <input value="Submit" class="submit" type="submit" /> > </form> > </div>[% END %] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/patron-search.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/patron-search.inc >index bccd39c..a1f400c 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/patron-search.inc >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/patron-search.inc >@@ -5,69 +5,100 @@ > <div id="patron_search" class="residentsearch"> > <p class="tip">Enter patron card number or partial name:</p> > <form action="/cgi-bin/koha/members/member.pl" method="post"> >- <input id="searchmember" data-toggle="tooltip" size="25" class="focus" name="member" type="text" value="[% member %]"/> >+ <input id="searchmember" data-toggle="tooltip" size="25" class="focus" name="searchmember" type="text" value="[% searchmember %]"/> >+ <input type="hidden" name="quicksearch" value="1" /> > <span class="filteraction" id="filteraction_off"> <a href="#" onclick="$('#filters').toggle();$('.filteraction').toggle();">[-]</a></span> > <span class="filteraction" id="filteraction_on"> <a href="#" onclick="$('#filters').toggle();$('.filteraction').toggle();">[+]</a></span> > >- <input value="Search" class="submit" type="submit" /> >+ <label for="searchfieldstype">Search fields:</label> >+ <select name="searchfieldstype" id="searchfieldstype"> >+ [% IF searchfieldstype == "standard" %] >+ <option selected="selected" value='standard'>Standard</option> >+ [% ELSE %] >+ <option value='standard'>Standard</option> >+ [% END %] >+ [% IF searchfieldstype == "email" %] >+ <option selected="selected" value='email'>Email</option> >+ [% ELSE %] >+ <option value='email'>Email</option> >+ [% END %] >+ [% IF searchfieldstype == "borrowernumber" %] >+ <option selected="selected" value='borrowernumber'>Borrower number</option> >+ [% ELSE %] >+ <option value='borrowernumber'>Borrower number</option> >+ [% END %] >+ [% IF searchfieldstype == "phone" %] >+ <option selected="selected" value='phone'>Phone number</option> >+ [% ELSE %] >+ <option value='phone'>Phone number</option> >+ [% END %] >+ [% IF searchfieldstype == "address" %] >+ <option selected="selected" value='address'>Street Address</option> >+ [% ELSE %] >+ <option value='address'>Street Address</option> >+ [% END %] >+ [% IF searchfieldstype == "dateofbirth" %] >+ <option selected="selected" value='dateofbirth'>Date of birth</option> >+ [% ELSE %] >+ <option value='dateofbirth'>Date of birth</option> >+ [% END %] >+ [% IF searchfieldstype == "sort1" %] >+ <option selected="selected" value='sort1'>Sort field 1</option> >+ [% ELSE %] >+ <option value='sort1'>Sort field 1</option> >+ [% END %] >+ [% IF searchfieldstype == "sort2" %] >+ <option selected="selected" value='sort2'>Sort field 2</option> >+ [% ELSE %] >+ <option value='sort2'>Sort field 2</option> >+ [% END %] >+ </select> >+ >+ <script type="text/javascript"> >+ [% SET dateformat = Koha.Preference('dateformat') %] >+ $("#searchfields").change(function() { >+ if ( $(this).val() == 'dateofbirth' ) { >+ [% IF dateformat == 'us' %] >+ var MSG_DATE_FORMAT = _("Dates of birth should be entered in the format 'MM/DD/YYYY'"); >+ [% ELSIF dateformat == 'iso' %] >+ var MSG_DATE_FORMAT = _("Dates of birth should be entered in the format 'YYYY-MM-DD'"); >+ [% ELSIF dateformat == 'metric' %] >+ var MSG_DATE_FORMAT = _("Dates of birth should be entered in the format 'DD/MM/YYYY'"); >+ [% END %] >+ $('#searchmember').attr("title",MSG_DATE_FORMAT).tooltip('show'); >+ } else { >+ $('#searchmember').tooltip('destroy'); >+ } >+ }); >+ >+ </script> > >- <div id="filters"> >- <p><label for="searchfields">Search fields:</label> >- <select name="searchfields" id="searchfields"> >- <option selected="selected" value=''>Standard</option> >- <option value='email,emailpro,B_email,'>Email</option> >- <option value='borrowernumber'>Borrower number</option> >- <option value='phone,phonepro,B_phone,altcontactphone,mobile'>Phone number</option> >- <option value='streettype,address,address2,city,state,zipcode,country'>Street Address</option> >- <option value='dateofbirth'>Date of birth</option> >- <option value='sort1'>Sort field 1</option> >- <option value='sort2'>Sort field 2</option> >- </select> >- <script type="text/javascript"> >- [% SET dateformat = Koha.Preference('dateformat') %] >- $("#searchfields").change(function() { >- if ( $(this).val() == 'dateofbirth' ) { >- [% IF dateformat == 'us' %] >- var MSG_DATE_FORMAT = _("Dates of birth should be entered in the format 'MM/DD/YYYY'"); >- [% ELSIF dateformat == 'iso' %] >- var MSG_DATE_FORMAT = _("Dates of birth should be entered in the format 'YYYY-MM-DD'"); >- [% ELSIF dateformat == 'metric' %] >- var MSG_DATE_FORMAT = _("Dates of birth should be entered in the format 'DD/MM/YYYY'"); >- [% END %] >- $('#searchmember').attr("title",MSG_DATE_FORMAT).tooltip('show'); >- } else { >- $('#searchmember').tooltip('destroy'); >- } >- }); >- </script> >- </p> >- <p><label for="searchtype">Search type:</label> >- <select name="searchtype" id="searchtype"> >- <option selected="selected" value=''>Starts with</option> >- <option value='contain'>Contains</option> >- </select></p> >+ <label for="searchtype">Search type:</label> >+ <select name="searchtype" id="searchtype"> >+ <option selected="selected" value='start_with'>Starts with</option> >+ <option value='contain'>Contains</option> >+ </select> > >- <p><label for="searchorderby">Order by:</label> >- <select name="orderby" id="searchorderby"> >- <option value="">Surname, Firstname</option> >- <option value="cardnumber,0">Cardnumber</option> >- </select></p> >- [% IF ( branchloop ) %] <p><label for="branchcode">Library: </label><select name="branchcode" id="branchcode"> >- <option value="">Any</option>[% FOREACH branchloo IN branchloop %] >- [% IF ( branchloo.selected ) %] >- <option value="[% branchloo.value %]" selected="selected">[% branchloo.branchname %]</option>[% ELSE %] >- <option value="[% branchloo.value %]">[% branchloo.branchname %]</option>[% END %] >- [% END %]</select></p> >- [% END %] >- [% IF ( categories ) %] >- <p><label for="categorycode">Category: </label><select name="categorycode" id="categorycode"> >- <option value="">Any</option>[% FOREACH categorie IN categories %] >- [% IF ( categorie.selected ) %] >- <option value="[% categorie.categorycode %]" selected="selected">[% categorie.description |html_entity %]</option>[% ELSE %] >- <option value="[% categorie.categorycode %]">[% categorie.description |html_entity %]</option>[% END %] >- [% END %]</select></p> >- [% END %] >- </div> >+ <input value="Search" class="submit" type="submit" /> >+ [% IF ( branchloop ) %] >+ <p id="filters"> <label for="branchcode">Library: </label> >+ <select name="branchcode" id="branchcode"> >+ [% IF branchloop.size != 1 %] >+ <option value="">Any</option> >+ [% END %] >+ [% FOREACH branchloo IN branchloop %] >+ [% IF ( branchloo.selected ) %] >+ <option value="[% branchloo.value %]" selected="selected">[% branchloo.branchname %]</option>[% ELSE %] >+ <option value="[% branchloo.value %]">[% branchloo.branchname %]</option>[% END %] >+ [% END %]</select> >+ <label for="categorycode">Category: </label><select name="categorycode" id="categorycode"> >+ <option value="">Any</option>[% FOREACH categorie IN categories %] >+ [% IF ( categorie.selected ) %] >+ <option value="[% categorie.categorycode %]" selected="selected">[% categorie.description |html_entity %]</option>[% ELSE %] >+ <option value="[% categorie.categorycode %]">[% categorie.description |html_entity %]</option>[% END %] >+ [% END %]</select> >+ </p> >+ [% END %] > </form> > </div> > >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/patron-title.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/patron-title.inc >index ac2f388..3a3b90c 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/patron-title.inc >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/patron-title.inc >@@ -1,23 +1,23 @@ >-[% IF ( borrower.borrowernumber ) %] >- [% IF borrower.category_type == 'I' %] >- [% borrower.surname %] [% IF borrower.othernames %] ([% borrower.othernames %]) [% END %] >- [% ELSE %] >- [% IF invert_name %] >- [% borrower.surname %], [% borrower.firstname %] [% IF borrower.othernames %] ([% borrower.othernames %]) [% END %] >- [% ELSE %] >- [% borrower.firstname %] [% IF borrower.othernames %] ([% borrower.othernames %]) [% END %] [% borrower.surname %] >- [% END %] >- [% END %] >+[%- IF ( borrower.borrowernumber ) %] >+ [%- IF borrower.category_type == 'I' %] >+ [%- borrower.surname %] [% IF borrower.othernames %] ([% borrower.othernames %]) [% END %] >+ [%- ELSE %] >+ [%- IF invert_name %] >+ [%- borrower.surname %], [% borrower.firstname %] [% IF borrower.othernames %] ([% borrower.othernames %]) [% END %] >+ [%- ELSE %] >+ [%- borrower.firstname %] [% IF borrower.othernames %] ([% borrower.othernames %]) [% END %] [% borrower.surname %] >+ [%- END -%] >+ [%- END -%] > ([% borrower.cardnumber %]) >-[% ELSIF ( borrowernumber ) %] >- [% IF category_type == 'I' %] >- [% surname %] [% IF othernames %] ([% othernames %]) [% END %] >- [% ELSE %] >- [% IF invert_name %] >- [% surname %], [% firstname %] [% IF othernames %] ([% othernames %]) [% END %] >- [% ELSE %] >- [% firstname %] [% IF othernames %] ([% othernames %]) [% END %] [% surname %] >- [% END %] >- [% END %] >- ([% cardnumber %]) >-[% END %] >\ No newline at end of file >+[%- ELSIF ( borrowernumber ) %] >+ [%- IF category_type == 'I' %] >+ [%- surname %] [% IF othernames %] ([% othernames %]) [% END %] >+ [%- ELSE %] >+ [%- IF invert_name %] >+ [%- surname %], [% firstname %] [% IF othernames %] ([% othernames %]) [% END %] >+ [%- ELSE %] >+ [%- firstname %] [% IF othernames %] ([% othernames %]) [% END %] [% surname %] >+ [%- END %] >+ [%- END -%] >+ ([% cardnumber -%]) >+[%- END -%] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/js/datatables.js b/koha-tmpl/intranet-tmpl/prog/en/js/datatables.js >index aa2dcca..e190eee 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/js/datatables.js >+++ b/koha-tmpl/intranet-tmpl/prog/en/js/datatables.js >@@ -23,7 +23,7 @@ var dataTablesDefaults = { > "sSearch" : window.MSG_DT_SEARCH || "Search:", > "sZeroRecords" : window.MSG_DT_ZERO_RECORDS || "No matching records found" > }, >- "sDom": '<"top pager"ilpf>t<"bottom pager"ip>', >+ "sDom": '<"top pager"ilpf>tr<"bottom pager"ip>', > "aLengthMenu": [[10, 20, 50, 100, -1], [10, 20, 50, 100, window.MSG_DT_ALL || "All"]], > "iDisplayLength": 20 > }; >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/member.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/member.tt >index b906e2b..a017726 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/member.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/member.tt >@@ -1,7 +1,8 @@ > [% INCLUDE 'doc-head-open.inc' %] > <title>Koha › Patrons [% IF ( searching ) %]› Search results[% END %]</title> > [% INCLUDE 'doc-head-close.inc' %] >- >+<link rel="stylesheet" type="text/css" href="[% themelang %]/css/datatables.css" /> >+[% INCLUDE 'datatables.inc' %] > <script type="text/javascript"> > //<![CDATA[ > $(document).ready(function() { >@@ -21,7 +22,6 @@ $(document).ready(function() { > $('#new_patron_list').hide(); > $('#add_to_patron_list_submit').attr('disabled', 'disabled'); > } >- > }); > > $('#new_patron_list').on('input', function() { >@@ -31,43 +31,249 @@ $(document).ready(function() { > $('#add_to_patron_list_submit').attr('disabled', 'disabled'); > } > }); >+ >+ $("#patron_list_dialog").hide(); >+ $("#add_to_patron_list_submit").on('click', function(e){ >+ if ( $('#add_to_patron_list').val() == 'new' ) { >+ if ( $('#new_patron_list').val() ) { >+ $("#add_to_patron_list option").each(function() { >+ if ( $(this).text() == $('#new_patron_list').val() ) { >+ alert( _("You already have a list with that name!") ); >+ return false; >+ } >+ }); >+ } else { >+ alert( _("You must give your new patron list a name!") ); >+ return false; >+ } >+ } >+ >+ if ( $("#memberresultst input:checkbox:checked").length == 0 ) { >+ alert( _("You have not selected any patrons to add to a list!") ); >+ return false; >+ } >+ >+ var borrowernumbers = []; >+ $("#memberresultst").find("input:checkbox:checked").each(function(){ >+ borrowernumbers.push($(this).val()); >+ }); >+ var data = { >+ add_to_patron_list: encodeURIComponent($("#add_to_patron_list").val()), >+ new_patron_list: encodeURIComponent($("#new_patron_list").val()), >+ borrowernumbers: borrowernumbers >+ }; >+ $.ajax({ >+ data: data, >+ type: 'POST', >+ url: '/cgi-bin/koha/svc/members/add_to_list', >+ success: function(data) { >+ $("#patron_list_dialog").show(); >+ $("#patron_list_dialog > span.patrons-length").html(data.patrons_added_to_list); >+ $("#patron_list_dialog > a").attr("href", "/cgi-bin/koha/patron_lists/list.pl?patron_list_id=" + data.patron_list.patron_list_id); >+ $("#patron_list_dialog > a").html(data.patron_list.name); >+ }, >+ error: function() { >+ alert("an error occurred"); >+ } >+ }); >+ return true; >+ }); > }); > >-function CheckForm() { >- if ( $('#add_to_patron_list').val() == 'new' ) { >- if ( $('#new_patron_list').val() ) { >- var exists = false; >- $("#add_to_patron_list option").each(function() { >- if ( $(this).text() == $('#new_patron_list').val() ) { >- exists = true; >- return false; >+var dtMemberResults; >+var search = 1; >+$(document).ready(function() { >+ [% IF searchmember %] >+ $("#searchmember_filter").val("[% searchmember %]"); >+ [% END %] >+ [% IF searchfieldstype %] >+ $("searchfieldstype_filter").val("[% searchfieldstype %]"); >+ [% END %] >+ [% IF searchtype %] >+ $("#searchtype_filter").val("[% searchtype %]"); >+ [% END %] >+ [% IF categorycode %] >+ $("#categorycode_filter").val("[% categorycode %]"); >+ [% END %] >+ [% IF branchcode %] >+ $("#branchcode_filter").val("[% branchcode %]"); >+ [% END %] >+ >+ [% IF view != "show_results" %] >+ $("#searchresults").hide(); >+ search = 0; >+ [% END %] >+ >+ // Build the aLengthMenu >+ var aLengthMenu = [ >+ [%PatronsPerPage %], 10, 20, 50, 100, -1 >+ ]; >+ jQuery.unique(aLengthMenu); >+ aLengthMenu.sort(function( a, b ){ >+ // Put "All" at the end >+ if ( a == -1 ) { >+ return 1; >+ } else if ( b == -1 ) { >+ return -1; >+ } >+ return parseInt(a) < parseInt(b) ? -1 : 1;} >+ ); >+ var aLengthMenuLabel = []; >+ $(aLengthMenu).each(function(){ >+ if ( this == -1 ) { >+ // Label for -1 is "All" >+ aLengthMenuLabel.push("All"); >+ } else { >+ aLengthMenuLabel.push(this); >+ } >+ }); >+ >+ // 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': 'searchfieldstype', >+ 'value': $("#searchfieldstype_filter").val() >+ },{ >+ 'name': 'searchtype', >+ 'value': $("#searchtype_filter").val() >+ },{ >+ 'name': 'categorycode', >+ 'value': $("#categorycode_filter").val() >+ },{ >+ 'name': 'branchcode', >+ 'value': $("#branchcode_filter").val() >+ },{ >+ 'name': 'name_sorton', >+ 'value': 'borrowers.surname borrowers.firstname' >+ },{ >+ 'name': 'category_sorton', >+ 'value': 'categories.description', >+ },{ >+ 'name': 'branch_sorton', >+ 'value': 'branches.branchname' >+ },{ >+ 'name': 'template_path', >+ 'value': 'members/tables/members_results.tt', >+ }); >+ $.ajax({ >+ 'dataType': 'json', >+ 'type': 'POST', >+ 'url': sSource, >+ 'data': aoData, >+ 'success': function(json){ >+ // redirect if there is only 1 result. >+ if ( json.aaData.length == 1 ) { >+ var borrowernumber = json.aaData[0].borrowernumber; >+ document.location.href="/cgi-bin/koha/members/moremember.pl?borrowernumber="+borrowernumber; >+ return false; >+ } >+ fnCallback(json); > } > }); >+ }, >+ 'aoColumns':[ >+ [% IF CAN_user_tools_manage_patron_lists %] >+ { 'mDataProp': 'dt_borrowernumber' }, >+ [% END %] >+ { 'mDataProp': 'dt_cardnumber' }, >+ { 'mDataProp': 'dt_name' }, >+ { 'mDataProp': 'dt_category' }, >+ { 'mDataProp': 'dt_branch' }, >+ { 'mDataProp': 'dt_dateexpiry' }, >+ { 'mDataProp': 'dt_od_checkouts', 'bSortable': false }, >+ { 'mDataProp': 'dt_fines', 'bSortable': false }, >+ { 'mDataProp': 'dt_borrowernotes' }, >+ { 'mDataProp': 'dt_action', 'bSortable': false } >+ ], >+ 'fnRowCallback': function(nRow, aData, iDisplayIndex, iDisplayIndexFull) { >+ /* Center text for 6th column */ >+ $("td:eq(5)", nRow).css("text-align", "center"); > >- if ( exists ) { >- alert( _("You already have a list with that name!") ); >- return false; >- } >+ return nRow; >+ }, >+ 'bFilter': false, >+ 'bAutoWidth': false, >+ [% IF orderby_cardnumber_0 %] >+ 'aaSorting': [[0, 'asc']], >+ [% ELSE %] >+ 'aaSorting': [[1, 'asc']], >+ [% END %] >+ "aLengthMenu": [aLengthMenu, aLengthMenuLabel], >+ 'sPaginationType': 'full_numbers', >+ "iDisplayLength": [% PatronsPerPage %], >+ })); >+ update_searched(); >+}); >+ >+// Update the string "Results found ..." >+function update_searched(){ >+ var searched = ""; >+ searched += "on " + $("#searchfieldstype_filter").find("option:selected").text().toLowerCase() + " fields"; >+ if ( $("#searchmember_filter").val() ) { >+ if ( $("#searchtype_filter").val() == 'start_with' ) { >+ searched += _(" starting with "); > } else { >- alert( _("You must give your new patron list a name!") ); >- return false; >+ searched += _(" containing "); > } >+ searched += $("#searchmember_filter").val(); >+ } >+ if ( $("#firstletter_filter").val() ) { >+ searched += _(" begin with ") + $("#firstletter_filter").val(); >+ } >+ if ( $("#categorycode_filter").val() ) { >+ searched += _(" with category ") + $("#categorycode_filter").find("option:selected").text(); >+ } >+ if ( $("#branchcode_filter").val() ) { >+ searched += _(" in library ") + $("#branchcode_filter").find("option:selected").text(); > } >+ $("#searchpattern").text("for patron " + searched); >+} >+ >+// Redraw the table >+function filter() { >+ $("#firstletter_filter").val(''); >+ update_searched(); >+ search = 1; >+ $("#searchresults").show(); >+ dtMemberResults.fnDraw(); >+ return false; >+} > >- if ( $('#add_to_patron_list_which').val() == 'all' ) { >- return confirm( _("Are you sure you want to add the entire set of patron results to this list ( including results on other pages )?") ); >- } else { >- if ( $("#add-patrons-to-list-form input:checkbox:checked").length == 0 ) { >- alert( _("You have not selected any patrons to add to a list!") ); >- return false; >- } >+// User has clicked on the Clear button >+function clearFilters(redraw) { >+ $("#searchform select").val(''); >+ $("#firstletter_filter").val(''); >+ $("#searchmember_filter").val(''); >+ if(redraw) { >+ search = 1; >+ $("#searchresults").show(); >+ dtMemberResults.fnDraw(); > } >+} > >- return true; >+// User has clicked on a letter >+function filterByFirstLetterSurname(letter) { >+ clearFilters(false); >+ $("#firstletter_filter").val(letter); >+ update_searched(); >+ search = 1; >+ $("#searchresults").show(); >+ dtMemberResults.fnDraw(); > } > //]]> > </script> >- > </head> > <body id="pat_member" class="pat"> > [% INCLUDE 'header.inc' %] >@@ -75,163 +281,204 @@ function CheckForm() { > > <div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> › [% IF ( searching ) %]<a href="/cgi-bin/koha/members/members-home.pl">Patrons</a> › Search results[% ELSE %]Patrons[% END %]</div> > >-<div id="doc2" class="yui-t7"> >+<div id="doc3" class="yui-t2"> >+ <div id="bd"> >+ <div id="yui-main"> >+ <div class="yui-b"> >+ <div class="yui-g"> >+ [% IF CAN_user_tools_manage_patron_lists %] >+ <div id="patron_list_dialog" class="dialog alert"> >+ Added <span class="patrons-length"></span> patrons to <a></a>. >+ </div> >+ [% END %] > >- <div id="bd"> >- <div id="yui-main"> >- <div class="yui-b"> >- <div class="yui-g"> >+ [% INCLUDE 'patron-toolbar.inc' %] >+ [% IF ( no_add ) %] >+ <div class="dialog alert"> >+ <h3>Cannot add patron</h3> >+ [% IF ( no_branches ) %] >+ <p>There are <strong>no libraries defined</strong>. [% IF ( CAN_user_parameters ) %]Please <a href="/cgi-bin/koha/admin/branches.pl">add a library</a>.[% ELSE %]An administrator must define at least one library.[% END %]</p> >+ [% END %] >+ [% IF ( no_categories ) %] >+ <p>There are <strong>no patron categories defined</strong>. [% IF ( CAN_user_parameters ) %]Please <a href="/cgi-bin/koha/admin/categorie.pl">add a patron category</a>.[% ELSE %]An administrator must define at least one patron category.[% END %]</p> >+ [% END %] >+ </div> >+ [% END %] >+ <div class="browse"> >+ Browse by last name: >+ [% FOREACH letter IN alphabet.split(' ') %] >+ <a style="cursor:pointer" onclick="filterByFirstLetterSurname('[% letter %]');">[% letter %]</a> >+ [% END %] >+ </div> > >- [% IF patron_list %] >- <div class="dialog alert"> >- Added [% patrons_added_to_list.size %] patrons to <a href="/cgi-bin/koha/patron_lists/list.pl?patron_list_id=[% patron_list.patron_list_id %]">[% patron_list.name %]</a>. >- </div> >- [% END %] >+ [% IF ( CAN_user_borrowers && pending_borrower_modifications ) %] >+ <div class="pending-info" id="patron_updates_pending"> >+ <a href="/cgi-bin/koha/members/members-update.pl">Patrons requesting modifications</a>: >+ <span class="holdcount"><a href="/cgi-bin/koha/members/members-update.pl">[% pending_borrower_modifications %]</a></span> >+ </div> >+ [% END %] >+ >+ <div id="searchresults"> >+ <div id="searchheader"> >+ <h3>Results found <span id="searchpattern">[% IF searchmember %] for '[% searchmember %]'[% END %]</span></h3> >+ </div> >+ [% IF CAN_user_tools_manage_patron_lists %] >+ <div id="searchheader"> >+ <div> >+ <a href="javascript:void(0)" onclick="$('.selection').prop('checked', true)">Select all</a> >+ | >+ <a href="javascript:void(0)" onclick="$('.selection').prop('checked', false)">Clear all</a> >+ | >+ <span> >+ Add selected patrons >+ <label for="add_to_patron_list">to:</label> >+ <select id="add_to_patron_list" name="add_to_patron_list"> >+ <option value=""></option> >+ [% IF patron_lists %] >+ <optgroup label="Patron lists:"> >+ [% FOREACH pl IN patron_lists %] >+ <option value="[% pl.patron_list_id %]">[% pl.name %]</option> >+ [% END %] >+ </optgroup> >+ [% END %] >+ >+ <option value="new">[ New list ]</option> >+ </select> >+ >+ <input type="text" id="new_patron_list" name="new_patron_list" id="new_patron_list" /> > >- [% INCLUDE 'patron-toolbar.inc' %] >- >- [% IF ( no_add ) %]<div class="dialog alert"><h3>Cannot add patron</h3> >- [% IF ( no_branches ) %]<p>There are <strong>no libraries defined</strong>. [% IF ( CAN_user_parameters ) %]Please <a href="/cgi-bin/koha/admin/branches.pl">add a library</a>.[% ELSE %]An administrator must define at least one library.[% END %]</p>[% END %] >- [% IF ( no_categories ) %]<p>There are <strong>no patron categories defined</strong>. [% IF ( CAN_user_parameters ) %]Please <a href="/cgi-bin/koha/admin/categorie.pl">add a patron category</a>.[% ELSE %]An administrator must define at least one patron category.[% END %]</p>[% END %]</div> >- [% END %] >- >- <div class="browse"> >- Browse by last name: >- [% FOREACH letter IN alphabet.split(' ') %] >- <a href="/cgi-bin/koha/members/member.pl?quicksearch=1&surname=[% letter %]">[% letter %]</a> >- [% END %] >- </div> >- >- [% IF ( CAN_user_borrowers && pending_borrower_modifications ) %] >- <div class="pending-info" id="patron_updates_pending"> >- <a href="/cgi-bin/koha/members/members-update.pl">Patrons requesting modifications</a>: >- <span class="holdcount"><a href="/cgi-bin/koha/members/members-update.pl">[% pending_borrower_modifications %]</a></span> >- </div> >- [% END %] >- >- [% IF ( resultsloop ) %] >- [% IF (CAN_user_tools_manage_patron_lists) %] >- <form id="add-patrons-to-list-form" method="post" action="member.pl" onsubmit="return CheckForm()"> >- [% END %] >- <div id="searchheader"> >- <h3>Results [% from %] to [% to %] of [% numresults %] found for [% IF ( member ) %]'<span class="ex">[% member %]</span>'[% END %][% IF ( surname ) %]'<span class="ex">[% surname %]</span>'[% END %]</h3> >- >- [% IF (CAN_user_tools_manage_patron_lists) %] >- <div> >- <a href="javascript:void(0)" onclick="$('.selection').prop('checked', true)">Select all</a> >- | >- <a href="javascript:void(0)" onclick="$('.selection').prop('checked', false)">Clear all</a> >- | >- <span> >- <label for="add_to_patron_list_which">Add:</label> >- <select id="add_to_patron_list_which" name="add_to_patron_list_which"> >- <option value="selected">Selected patrons</option> >- <option value="all">All resultant patrons</option> >- </select> >- >- <label for="add_to_patron_list">to:</label> >- <select id="add_to_patron_list" name="add_to_patron_list"> >- <option value=""></option> >- [% IF patron_lists %] >- <optgroup label="Patron lists:"> >- [% FOREACH pl IN patron_lists %] >- <option value="[% pl.patron_list_id %]">[% pl.name %]</option> >- [% END %] >- </optgroup> >- [% END %] >- >- <option value="new">[ New list ]</option> >- </select> >- >- <input type="text" id="new_patron_list" name="new_patron_list" id="new_patron_list" /> >- >- [% FOREACH key IN search_parameters.keys %] >- <input type="hidden" name="[% key %]" value="[% search_parameters.$key %]" /> >- [% END %] >- >- <input id="add_to_patron_list_submit" type="submit" class="submit" value="Save"> >- </span> >- </div> >- [% END %] >- </div> >- <div class="searchresults"> >- >- <table id="memberresultst"> >- <thead> >- <tr> >- [% IF (CAN_user_tools_manage_patron_lists) %] >- <th> </th> >- [% END %] >- <th>Card</th> >- <th>Name</th> >- <th>Cat</th> >- <th>Library</th> >- <th>Expires on</th> >- <th>OD/Checkouts</th> >- <th>Fines</th> >- <th>Circ note</th> >- <th> </th> >- </tr> >- </thead> >- <tbody> >- [% FOREACH resultsloo IN resultsloop %] >- [% IF ( resultsloo.overdue ) %] >- <tr class="problem"> >- [% ELSE %] >- [% UNLESS ( loop.odd ) %] >- <tr class="highlight"> >- [% ELSE %] >- <tr> >- [% END %] >- [% END %] >- [% IF (CAN_user_tools_manage_patron_lists) %] >- <td><input type="checkbox" class="selection" name="borrowernumber" value="[% resultsloo.borrowernumber %]" /></td> >- [% END %] >- <td>[% resultsloo.cardnumber %]</td> >- <td style="white-space: nowrap;"> >- <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% resultsloo.borrowernumber %]"> >- [% INCLUDE 'patron-title.inc' borrowernumber = resultsloo.borrowernumber category_type = resultsloo.category_type firstname = resultsloo.firstname surname = resultsloo.surname othernames = resultsloo.othernames cardnumber = resultsloo.cardnumber invert_name = 1%] >- </a> <br /> >- [% IF ( resultsloo.streetnumber ) %][% resultsloo.streetnumber %] [% END %][% resultsloo.address %][% IF ( resultsloo.address2 ) %]<br />[% resultsloo.address2 %][% END %][% IF ( resultsloo.city ) %]<br />[% resultsloo.city %][% IF ( resultsloo.state ) %],[% END %][% END %][% IF ( resultsloo.state ) %] [% resultsloo.state %][% END %] [% IF ( resultsloo.zipcode ) %] [% resultsloo.zipcode %][% END %][% IF ( resultsloo.country ) %], [% resultsloo.country %][% END %] >- [% IF (resultsloo.email ) %]<br/>Email: <a href="mailto:[% resultsloo.email %]">[% resultsloo.email %]</a>[% END %] >- </td> >- <td>[% resultsloo.category_description %] ([% resultsloo.category_type %])</td> >- <td>[% resultsloo.branchname %]</td> >- <td>[% resultsloo.dateexpiry %]</td> >- <td>[% IF ( resultsloo.overdues ) %]<span class="overdue"><strong>[% resultsloo.overdues %]</strong></span>[% ELSE %][% resultsloo.overdues %][% END %]/[% resultsloo.issues %]</td> >- <td>[% IF ( resultsloo.fines < 0 ) %]<span class="credit">[% resultsloo.fines %]</span> [% ELSIF resultsloo.fines > 0 %] <span class="debit"><strong>[% resultsloo.fines %]</strong></span> [% ELSE %] [% resultsloo.fines %] [% END %]</td> >- <td>[% resultsloo.borrowernotes %]</td> >- <td>[% IF ( resultsloo.category_type ) %] >- <a href="/cgi-bin/koha/members/memberentry.pl?op=modify&destination=circ&borrowernumber=[% resultsloo.borrowernumber %]&category_type=[% resultsloo.category_type %]">Edit</a> >- [% ELSE %] <!-- try with categorycode if no category_type --> >- [% IF ( resultsloo.categorycode ) %] >- <a href="/cgi-bin/koha/members/memberentry.pl?op=modify&destination=circ&borrowernumber=[% resultsloo.borrowernumber %]&categorycode=[% resultsloo.categorycode %]">Edit</a> >- [% ELSE %] <!-- if no categorycode, set category_type to A by default --> >- <a href="/cgi-bin/koha/members/memberentry.pl?op=modify&destination=circ&borrowernumber=[% resultsloo.borrowernumber %]&category_type=A">Edit</a> >- [% END %] >- [% END %]</td> >- </tr> >- [% END %] >- </tbody> >- </table> >- <div class="pages">[% IF ( multipage ) %][% paginationbar %][% END %]</div> >- </div> >- [% IF (CAN_user_tools_manage_patron_lists) %] >- </form> >- [% END %] >- [% ELSE %] >- [% IF ( searching ) %] >- <div class="dialog alert">No results found</div> >- [% END %] >- [% END %] >- >- </div> >- </div> >- >- <div class="yui-g"> >- [% INCLUDE 'members-menu.inc' %] >- </div> >+ <input id="add_to_patron_list_submit" type="submit" class="submit" value="Save"> >+ </span> >+ </div> >+ </div> >+ [% END %] > >+ <table id="memberresultst"> >+ <thead> >+ <tr> >+ <th> </th> >+ <th>Card</th> >+ <th>Name</th> >+ <th>Category</th> >+ <th>Library</th> >+ <th>Expires on</th> >+ <th>OD/Checkouts</th> >+ <th>Fines</th> >+ <th>Circ note</th> >+ <th> </th> >+ </tr> >+ </thead> >+ <tbody></tbody> >+ </table> >+ </div> >+ </div> >+ </div> >+ </div> >+ <div class="yui-b"> >+ <form onsubmit="return filter();" id="searchform"> >+ <input type="hidden" id="firstletter_filter" value="" /> >+ <fieldset class="brief"> >+ <h3>Filters</h3> >+ <ol> >+ <li> >+ <label for="searchmember_filter">Search:</label> >+ <input type="text" id="searchmember_filter" value="[% searchmember %]"/> >+ </li> >+ <li> >+ <label for="searchfieldstype_filter">Search fields:</label> >+ <select name="searchfieldstype" id="searchfieldstype_filter"> >+ [% IF searchfieldstype == "standard" %] >+ <option selected="selected" value='standard'>Standard</option> >+ [% ELSE %] >+ <option value='standard'>Standard</option> >+ [% END %] >+ [% IF searchfieldstype == "email" %] >+ <option selected="selected" value='email'>Email</option> >+ [% ELSE %] >+ <option value='email'>Email</option> >+ [% END %] >+ [% IF searchfieldstype == "borrowernumber" %] >+ <option selected="selected" value='borrowernumber'>Borrower number</option> >+ [% ELSE %] >+ <option value='borrowernumber'>Borrower number</option> >+ [% END %] >+ [% IF searchfieldstype == "phone" %] >+ <option selected="selected" value='phone'>Phone number</option> >+ [% ELSE %] >+ <option value='phone'>Phone number</option> >+ [% END %] >+ [% IF searchfieldstype == "address" %] >+ <option selected="selected" value='address'>Street address</option> >+ [% ELSE %] >+ <option value='address'>Street address</option> >+ [% END %] >+ [% IF searchfieldstype == "dateofbirth" %] >+ <option selected="selected" value='dateofbirth'>Date of birth</option> >+ [% ELSE %] >+ <option value='dateofbirth'>Date of birth</option> >+ [% END %] >+ [% IF searchfieldstype == "sort1" %] >+ <option selected="selected" value='sort1'>Sort field 1</option> >+ [% ELSE %] >+ <option value='sort1'>Sort field 1</option> >+ [% END %] >+ [% IF searchfieldstype == "sort2" %] >+ <option selected="selected" value='sort2'>Sort field 2</option> >+ [% ELSE %] >+ <option value='sort2'>Sort field 2</option> >+ [% END %] >+ </select> >+ </li> >+ <li> >+ <label for="searchtype_filter">Search type:</label> >+ <select name="searchtype" id="searchtype_filter"> >+ <option value='start_with'>Starts with</option> >+ [% IF searchtype == "contain" %] >+ <option value="contain" selected="selected">Contains</option> >+ [% ELSE %] >+ <option value="contain" selected="selected">Contains</option> >+ [% END %] >+ </select> >+ </li> >+ <li> >+ <label for="categorycode_filter">Category:</label> >+ <select id="categorycode_filter"> >+ <option value="">Any</option> >+ [% FOREACH cat IN categories %] >+ [% IF cat.selected %] >+ <option selected="selected" value="[% cat.categorycode %]">[% cat.description %]</option> >+ [% ELSE %] >+ <option value="[% cat.categorycode %]">[% cat.description %]</option> >+ [% END %] >+ [% END %] >+ </select> >+ </li> >+ <li> >+ <label for="branchcode_filter">Branch:</label> >+ <select id="branchcode_filter"> >+ [% IF branchloop.size != 1 %] >+ <option value="">Any</option> >+ [% END %] >+ [% FOREACH b IN branchloop %] >+ [% IF b.selected %] >+ <option selected="selected" value="[% b.branchcode %]">[% b.branchname %]</option> >+ [% ELSE %] >+ <option value="[% b.branchcode %]">[% b.branchname %]</option> >+ [% END %] >+ [% END %] >+ </select> >+ </li> >+ </ol> >+ <fieldset class="action"> >+ <input type="submit" value="Search" /> >+ <input type="button" value="Clear" onclick="clearFilters(true);" /> >+ </fieldset> >+ </fieldset> >+ </form> > </div> >+ </div> >+ <div class="yui-g"> >+ [% INCLUDE 'members-menu.inc' %] >+ </div> > </div> > [% INCLUDE 'intranet-bottom.inc' %] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/tables/members_results.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/tables/members_results.tt >new file mode 100644 >index 0000000..1a975b9 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/tables/members_results.tt >@@ -0,0 +1,35 @@ >+{ >+ "sEcho": [% sEcho %], >+ "iTotalRecords": [% iTotalRecords %], >+ "iTotalDisplayRecords": [% iTotalDisplayRecords %], >+ "aaData": [ >+ [% FOREACH data IN aaData %] >+ { >+ [% IF CAN_user_tools_manage_patron_lists %] >+ "dt_borrowernumber": >+ "<input type='checkbox' class='selection' name='borrowernumber' value='[% data.borrowernumber %]' />", >+ [% END %] >+ "dt_cardnumber": >+ "[% data.cardnumber %]", >+ "dt_name": >+ "<span style='white-space:nowrap'><a href='/cgi-bin/koha/members/moremember.pl?borrowernumber=[% data.borrowernumber %]'>[% INCLUDE 'patron-title.inc' borrowernumber = data.borrowernumber category_type = data.category_type firstname = data.firstname surname = data.surname othernames = data.othernames cardnumber = data.cardnumber invert_name = 1%]</a><br />[% IF ( data.streetnumber ) %][% data.streetnumber %] [% END %][% data.address %][% IF ( data.address2 ) %]<br />[% data.address2 %][% END %][% IF ( data.city ) %]<br />[% data.city %][% IF ( data.state ) %],[% END %][% END %][% IF ( data.state ) %] [% data.state %][% END %] [% IF ( data.zipcode ) %] [% data.zipcode %][% END %][% IF ( data.country ) %], [% data.country %][% END %]</span>", >+ "dt_category": >+ "[% data.category_description |html %]([% data.category_type |html %])", >+ "dt_branch": >+ "[% data.branchname |html %]", >+ "dt_dateexpiry": >+ "[% data.dateexpiry %]", >+ "dt_od_checkouts": >+ "[% IF data.overdues %]<span class='overdue'><strong>[% data.overdues %]</strong></span>[% ELSE %][% data.overdues %][% END %] / [% data.issues %]", >+ "dt_fines": >+ "[% IF data.fines < 0 %]<span class='credit'>[% data.fines |html %]</span> [% ELSIF data.fines > 0 %] <span class='debit'><strong>[% data.fines |html %]</strong></span> [% ELSE %] [% data.fines |html%] [% END %]</td>", >+ "dt_borrowernotes": >+ "[% data.borrowernotes |html |html_line_break |collapse %]", >+ "dt_action": >+ "[% IF data.category_type %]<a href='/cgi-bin/koha/members/memberentry.pl?op=modify&destination=circ&borrowernumber=[% data.borrowernumber %]&category_type=[% data.category_type %]'>Edit</a>[% ELSE %][% IF data.categorycode %]<a href='/cgi-bin/koha/members/memberentry.pl?op=modify&destination=circ&borrowernumber=[% data.borrowernumber %]&categorycode=[% data.categorycode %]'>Edit</a>[% ELSE %]<a href='/cgi-bin/koha/members/memberentry.pl?op=modify&destination=circ&borrowernumber=[% data.borrowernumber %]&category_type=A'>Edit</a>[% END %][% END %]", >+ "borrowernumber": >+ "[% data.borrowernumber %]" >+ }[% UNLESS loop.last %],[% END %] >+ [% END %] >+ ] >+} >diff --git a/members/member.pl b/members/member.pl >index ff6e41a..0e4150a 100755 >--- a/members/member.pl >+++ b/members/member.pl >@@ -6,7 +6,7 @@ > > > # Copyright 2000-2002 Katipo Communications >-# Copyright 2010 BibLibre >+# Copyright 2013 BibLibre > # > # This file is part of Koha. > # >@@ -27,20 +27,17 @@ use Modern::Perl; > use C4::Auth; > use C4::Output; > use CGI; >-use C4::Members; > use C4::Branch; > use C4::Category; >+use C4::Members qw( GetMember ); > use Koha::DateUtils; > use File::Basename; > use Koha::List::Patron; > > my $input = new CGI; >-my $quicksearch = $input->param('quicksearch') || ''; >-my $startfrom = $input->param('startfrom') || 1; >-my $resultsperpage = $input->param('resultsperpage') || C4::Context->preference("PatronsPerPage") || 20; > > my ($template, $loggedinuser, $cookie) >- = get_template_and_user({template_name => "members/member.tmpl", >+ = get_template_and_user({template_name => "members/member.tt", > query => $input, > type => "intranet", > authnotrequired => 0, >@@ -49,189 +46,88 @@ my ($template, $loggedinuser, $cookie) > > my $theme = $input->param('theme') || "default"; > >-my $add_to_patron_list = $input->param('add_to_patron_list'); >-my $add_to_patron_list_which = $input->param('add_to_patron_list_which'); >-my $new_patron_list = $input->param('new_patron_list'); >-my @borrowernumbers = $input->param('borrowernumber'); >-$input->delete( >- 'add_to_patron_list', 'add_to_patron_list_which', >- 'new_patron_list', 'borrowernumber', >-); >- > my $patron = $input->Vars; > foreach (keys %$patron){ >- delete $$patron{$_} unless($$patron{$_}); >-} >-my @categories=C4::Category->all; >- >-my $branches = GetBranches; >-my @branchloop; >- >-foreach (sort { $branches->{$a}->{branchname} cmp $branches->{$b}->{branchname} } keys %$branches) { >- my $selected; >- $selected = 1 if $patron->{branchcode} && $branches->{$_}->{branchcode} eq $patron->{branchcode}; >- my %row = ( value => $_, >- selected => $selected, >- branchname => $branches->{$_}->{branchname}, >- ); >- push @branchloop, \%row; >-} >- >-my %categories_dislay; >- >-foreach my $category (@categories){ >- my $hash={ >- category_description=>$$category{description}, >- category_type=>$$category{category_type} >- }; >- $categories_dislay{$$category{categorycode}} = $hash; >+ delete $patron->{$_} unless($patron->{$_}); > } >-my $AddPatronLists = C4::Context->preference("AddPatronLists") || ''; >-$template->param( >- "AddPatronLists_$AddPatronLists" => "1", >- ); >-if ($AddPatronLists=~/code/){ >- $categories[0]->{'first'}=1; >-} >- >-my $member=$input->param('member') || ''; >-my $orderbyparams=$input->param('orderby') || ''; >-my @orderby; >-if ($orderbyparams){ >- my @orderbyelt=split(/,/,$orderbyparams); >- push @orderby, {$orderbyelt[0]=>$orderbyelt[1]||0}; >-} >-else { >- @orderby = ({surname=>0},{firstname=>0}); >-} >- >-$member =~ s/,//g; #remove any commas from search string >-$member =~ s/\*/%/g; > >-my $from = ( $startfrom - 1 ) * $resultsperpage; >-my $to = $from + $resultsperpage; >+my $searchmember = $input->param('searchmember'); >+my $quicksearch = $input->param('quicksearch') // 0; > >-my ($count,$results); >-if ($member || keys %$patron) { >- my $searchfields = $input->param('searchfields') || ''; >- my @searchfields = $searchfields ? split( ',', $searchfields ) : ( "firstname", "surname", "othernames", "cardnumber", "userid", "email" ); >- >- if ( $searchfields eq "dateofbirth" ) { >- $member = output_pref({dt => dt_from_string($member), dateformat => 'iso', dateonly => 1}); >+if ( $quicksearch ) { >+ my $branchcode; >+ if ( C4::Branch::onlymine ) { >+ my $userenv = C4::Context->userenv; >+ $branchcode = $userenv->{'branch'}; >+ } >+ my $member = GetMember( >+ cardnumber => $searchmember, >+ ( $branchcode ? ( branchcode => $branchcode ) : () ), >+ ); >+ if( $member ){ >+ print $input->redirect("/cgi-bin/koha/members/moremember.pl?borrowernumber=" . $member->{borrowernumber}); >+ exit; > } >+} > >- my $searchtype = $input->param('searchtype'); >- my $search_scope = >- $quicksearch ? "field_start_with" >- : $searchtype ? $searchtype >- : "start_with"; >+my $searchfieldstype = $input->param('searchfieldstype') || 'standard'; > >- ($results) = Search( $member || $patron, \@orderby, undef, undef, \@searchfields, $search_scope ); >+if ( $searchfieldstype eq "dateofbirth" ) { >+ $searchmember = output_pref({dt => dt_from_string($searchmember), dateformat => 'iso', dateonly => 1}); > } > >-if ($add_to_patron_list) { >- my $patron_list; >- >- if ( $add_to_patron_list eq 'new' ) { >- $patron_list = AddPatronList( { name => $new_patron_list } ); >- } >- else { >- $patron_list = >- [ GetPatronLists( { patron_list_id => $add_to_patron_list } ) ]->[0]; >+my $branches = C4::Branch::GetBranches; >+my @branches_loop; >+if ( C4::Branch::onlymine ) { >+ my $userenv = C4::Context->userenv; >+ my $branch = C4::Branch::GetBranchDetail( $userenv->{'branch'} ); >+ push @branches_loop, { >+ value => $branch->{branchcode}, >+ branchcode => $branch->{branchcode}, >+ branchname => $branch->{branchname}, >+ selected => 1 > } >- >- if ( $add_to_patron_list_which eq 'all' ) { >- @borrowernumbers = map { $_->{borrowernumber} } @$results; >+} else { >+ foreach ( sort { lc($branches->{$a}->{branchname}) cmp lc($branches->{$b}->{branchname}) } keys %$branches ) { >+ my $selected = 0; >+ $selected = 1 if($patron->{branchcode} and $patron->{branchcode} eq $_); >+ push @branches_loop, { >+ value => $_, >+ branchcode => $_, >+ branchname => $branches->{$_}->{branchname}, >+ selected => $selected >+ }; > } >- >- my @patrons_added_to_list = AddPatronsToList( { list => $patron_list, borrowernumbers => \@borrowernumbers } ); >- >- $template->param( >- patron_list => $patron_list, >- patrons_added_to_list => \@patrons_added_to_list, >- ) > } > >-if ($results) { >- for my $field ('categorycode','branchcode'){ >- next unless ($patron->{$field}); >- @$results = grep { $_->{$field} eq $patron->{$field} } @$results; >- } >- $count = scalar(@$results); >-} else { >- $count = 0; >+my @categories = C4::Category->all; >+if ( $patron->{categorycode} ) { >+ foreach my $category ( grep { $_->{categorycode} eq $patron->{categorycode} } @categories ) { >+ $category->{selected} = 1; >+ } > } > >-if($count == 1){ >- print $input->redirect("/cgi-bin/koha/members/moremember.pl?borrowernumber=" . @$results[0]->{borrowernumber}); >- exit; >-} >+$template->param( 'alphabet' => C4::Context->preference('alphabet') || join ' ', 'A' .. 'Z' ); > >-my @resultsdata; >-$to=($count>$to?$to:$count); >-my $index=$from; >-foreach my $borrower(@$results[$from..$to-1]){ >- #find out stats >- my ($od,$issue,$fines)=GetMemberIssuesAndFines($$borrower{'borrowernumber'}); >- $fines ||= 0; >- $$borrower{'dateexpiry'}= C4::Dates->new($$borrower{'dateexpiry'},'iso')->output('syspref'); >- >- my %row = ( >- count => $index++, >- %$borrower, >- (defined $categories_dislay{ $borrower->{categorycode} }? %{ $categories_dislay{ $borrower->{categorycode} } }:()), >- overdues => $od, >- issues => $issue, >- odissue => "$od/$issue", >- fines => sprintf("%.2f",$fines), >- branchname => $branches->{$borrower->{branchcode}}->{branchname}, >- ); >- push(@resultsdata, \%row); >+my $orderby = $input->param('orderby') // ''; >+if(defined $orderby and $orderby ne '') { >+ $orderby =~ s/[, ]/_/g; > } > >-if ($$patron{categorycode}){ >- foreach my $category (grep{$_->{categorycode} eq $$patron{categorycode}}@categories){ >- $$category{selected}=1; >- } >-} >-my %parameters= >- ( %$patron >- , 'orderby' => $orderbyparams >- , 'resultsperpage' => $resultsperpage >- , 'type'=> 'intranet'); >-my $base_url = >- 'member.pl?&' >- . join( >- '&', >- map { "$_=$parameters{$_}" } (keys %parameters) >- ); >- >-my @letters = map { {letter => $_} } ( 'A' .. 'Z'); >+my $view = $input->request_method() eq "GET" ? "show_form" : "show_results"; > > $template->param( >- %$patron, >- letters => \@letters, >- paginationbar => pagination_bar( >- $base_url, >- int( $count / $resultsperpage ) + ( $count % $resultsperpage ? 1 : 0 ), >- $startfrom, >- 'startfrom' >- ), >- startfrom => $startfrom, >- from => ( $startfrom - 1 ) * $resultsperpage + 1, >- to => $to, >- multipage => ( $count != $to || $startfrom != 1 ), >- advsearch => ( $$patron{categorycode} || $$patron{branchcode} ), >- branchloop => \@branchloop, >- categories => \@categories, >- searching => "1", >- actionname => basename($0), >- numresults => $count, >- resultsloop => \@resultsdata, >- results_per_page => $resultsperpage, >- member => $member, >- search_parameters => \%parameters, > patron_lists => [ GetPatronLists() ], >+ searchmember => $searchmember, >+ branchloop => \@branches_loop, >+ categories => \@categories, >+ branchcode => $patron->{branchcode}, >+ categorycode => $patron->{categorycode}, >+ searchtype => $input->param('searchtype') || 'start_with', >+ searchfieldstype => $searchfieldstype, >+ "orderby_$orderby" => 1, >+ PatronsPerPage => C4::Context->preference("PatronsPerPage") || 20, >+ view => $view, > ); > > output_html_with_http_headers $input, $cookie, $template->output; >diff --git a/members/members-home.pl b/members/members-home.pl >index 1d8f1e5..5e816eb 100755 >--- a/members/members-home.pl >+++ b/members/members-home.pl >@@ -45,12 +45,23 @@ my ($template, $loggedinuser, $cookie) > > my $branches = GetBranches; > my @branchloop; >-foreach (sort { $branches->{$a}->{branchname} cmp $branches->{$b}->{branchname} } keys %{$branches}) { >+if ( C4::Branch::onlymine ) { >+ my $userenv = C4::Context->userenv; >+ my $branch = C4::Branch::GetBranchDetail( $userenv->{'branch'} ); > push @branchloop, { >- value => $_, >- selected => ($branches->{$_}->{branchcode} eq $branch), >- branchname => $branches->{$_}->{branchname}, >- }; >+ value => $branch->{branchcode}, >+ branchcode => $branch->{branchcode}, >+ branchname => $branch->{branchname}, >+ selected => 1 >+ } >+} else { >+ foreach (sort { $branches->{$a}->{branchname} cmp $branches->{$b}->{branchname} } keys %{$branches}) { >+ push @branchloop, { >+ value => $_, >+ selected => ($branches->{$_}->{branchcode} eq $branch), >+ branchname => $branches->{$_}->{branchname}, >+ }; >+ } > } > > my @categories; >@@ -86,6 +97,10 @@ $template->param( > no_add => $no_add, > pending_borrower_modifications => $pending_borrower_modifications, > ); >-$template->param( 'alphabet' => C4::Context->preference('alphabet') || join ' ', 'A' .. 'Z' ); >+ >+$template->param( >+ alphabet => C4::Context->preference('alphabet') || join (' ', 'A' .. 'Z'), >+ PatronsPerPage => C4::Context->preference("PatronsPerPage") || 20, >+); > > output_html_with_http_headers $query, $cookie, $template->output; >diff --git a/members/moremember.pl b/members/moremember.pl >index e794efa..6cc48e9 100755 >--- a/members/moremember.pl >+++ b/members/moremember.pl >@@ -52,8 +52,7 @@ use C4::Form::MessagingPreferences; > use List::MoreUtils qw/uniq/; > use C4::Members::Attributes qw(GetBorrowerAttributes); > use Koha::Borrower::Debarments qw(GetDebarments); >-#use Smart::Comments; >-#use Data::Dumper; >+ > use DateTime; > use Koha::DateUtils; > >@@ -81,21 +80,21 @@ my $template_name; > my $quickslip = 0; > > my $flagsrequired; >-if ($print eq "page") { >+if (defined $print and $print eq "page") { > $template_name = "members/moremember-print.tmpl"; > # circ staff who process checkouts but can't edit > # patrons still need to be able to access print view > $flagsrequired = { circulate => "circulate_remaining_permissions" }; >-} elsif ($print eq "slip") { >+} elsif (defined $print and $print eq "slip") { > $template_name = "members/moremember-receipt.tmpl"; > # circ staff who process checkouts but can't edit > # patrons still need to be able to print receipts > $flagsrequired = { circulate => "circulate_remaining_permissions" }; >-} elsif ($print eq "qslip") { >+} elsif (defined $print and $print eq "qslip") { > $template_name = "members/moremember-receipt.tmpl"; > $quickslip = 1; > $flagsrequired = { circulate => "circulate_remaining_permissions" }; >-} elsif ($print eq "brief") { >+} elsif (defined $print and $print eq "brief") { > $template_name = "members/moremember-brief.tmpl"; > $flagsrequired = { borrowers => 1 }; > } else { >@@ -156,7 +155,7 @@ if ($debar) { > } > > $data->{'ethnicity'} = fixEthnicity( $data->{'ethnicity'} ); >-$data->{ "sex_".$data->{'sex'}."_p" } = 1; >+$data->{ "sex_".$data->{'sex'}."_p" } = 1 if defined $data->{sex}; > > my $catcode; > if ( $category_type eq 'C') { >@@ -283,13 +282,13 @@ if ($borrowernumber) { > $getreserv{barcodereserv} = $getiteminfo->{'barcode'}; > $getreserv{itemtype} = $itemtypeinfo->{'description'}; > >- # check if we have a waitin status for reservations >- if ( $num_res->{'found'} eq 'W' ) { >+ # check if we have a waitin status for reservations >+ if ( defined $num_res->{found} and $num_res->{'found'} eq 'W' ) { > $getreserv{color} = 'reserved'; > $getreserv{waiting} = 1; > } > >- # check transfers with the itemnumber foud in th reservation loop >+ # check transfers with the itemnumber foud in th reservation loop > if ($transfertwhen) { > $getreserv{color} = 'transfered'; > $getreserv{transfered} = 1; >@@ -436,6 +435,7 @@ $template->param( > SuspendHoldsIntranet => C4::Context->preference('SuspendHoldsIntranet'), > RoutingSerials => C4::Context->preference('RoutingSerials'), > debarments => GetDebarments({ borrowernumber => $borrowernumber }), >+ PatronsPerPage => C4::Context->preference("PatronsPerPage") || 20, > ); > $template->param( $error => 1 ) if $error; > >diff --git a/svc/members/add_to_list b/svc/members/add_to_list >new file mode 100755 >index 0000000..970ae18 >--- /dev/null >+++ b/svc/members/add_to_list >@@ -0,0 +1,63 @@ >+#!/usr/bin/perl >+ >+# Copyright 2013 BibLibre >+# >+# This file is part of Koha >+# >+# 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 2 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, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+use CGI; >+ >+use C4::Auth qw( check_cookie_auth ); >+use JSON qw( to_json ); >+use Koha::List::Patron; >+ >+my $input = new CGI; >+ >+my ( $auth_status, $sessionID ) = check_cookie_auth( >+ $input->cookie('CGISESSID'), >+ { tools => 'manage_patron_lists' }, >+); >+ >+exit 0 if $auth_status ne "ok"; >+my $add_to_patron_list = $input->param('add_to_patron_list'); >+my $new_patron_list = $input->param('new_patron_list'); >+my @borrowernumbers = $input->param('borrowernumbers[]'); >+ >+my $response; >+if ($add_to_patron_list) { >+ my $patron_list = []; >+ >+ if ( $add_to_patron_list eq 'new' ) { >+ $patron_list = AddPatronList( { name => $new_patron_list } ); >+ } >+ else { >+ $patron_list = >+ [ GetPatronLists( { patron_list_id => $add_to_patron_list } ) ]->[0]; >+ } >+ >+ my @patrons_added_to_list = AddPatronsToList( { list => $patron_list, borrowernumbers => \@borrowernumbers } ); >+ >+ $response->{patron_list} = { patron_list_id => $patron_list->patron_list_id, name => $patron_list->name }; >+ $response->{patrons_added_to_list} = scalar( @patrons_added_to_list ); >+} >+ >+binmode STDOUT, ":encoding(UTF-8)"; >+print $input->header( >+ -type => 'application/json', >+ -charset => 'UTF-8' >+); >+ >+print to_json( $response ); >diff --git a/svc/members/search b/svc/members/search >new file mode 100755 >index 0000000..56b9e86 >--- /dev/null >+++ b/svc/members/search >@@ -0,0 +1,119 @@ >+#!/usr/bin/perl >+ >+# Copyright 2013 BibLibre >+# >+# This file is part of Koha >+# >+# 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 2 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, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+use CGI; >+ >+use C4::Auth qw( get_template_and_user ); >+use C4::Output qw( output_with_http_headers ); >+use C4::Utils::DataTables qw( dt_get_params ); >+use C4::Utils::DataTables::Members qw( search ); >+ >+my $input = new CGI; >+ >+exit unless $input->param('template_path'); >+ >+my ($template, $user, $cookie) = get_template_and_user({ >+ template_name => $input->param('template_path'), >+ query => $input, >+ type => "intranet", >+ authnotrequired => 0, >+ flagsrequired => { borrowers => 1 } >+}); >+ >+my $searchmember = $input->param('searchmember'); >+my $firstletter = $input->param('firstletter'); >+my $categorycode = $input->param('categorycode'); >+my $branchcode = $input->param('branchcode'); >+my $searchtype = $input->param('searchtype'); >+my $searchfieldstype = $input->param('searchfieldstype'); >+ >+# variable information for DataTables (id) >+my $sEcho = $input->param('sEcho'); >+ >+my %dt_params = dt_get_params($input); >+foreach (grep {$_ =~ /^mDataProp/} keys %dt_params) { >+ $dt_params{$_} =~ s/^dt_//; >+} >+ >+# Perform the patrons search >+my $results = C4::Utils::DataTables::Members::search( >+ { >+ searchmember => $searchmember, >+ firstletter => $firstletter, >+ categorycode => $categorycode, >+ branchcode => $branchcode, >+ searchtype => $searchtype, >+ searchfieldstype => $searchfieldstype, >+ dt_params => \%dt_params, >+ >+ } >+); >+ >+$template->param( >+ sEcho => $sEcho, >+ iTotalRecords => $results->{iTotalRecords}, >+ iTotalDisplayRecords => $results->{iTotalDisplayRecords}, >+ aaData => $results->{patrons} >+); >+ >+output_with_http_headers $input, $cookie, $template->output, 'json'; >+ >+__END__ >+ >+=head1 NAME >+ >+search - a search script for finding patrons >+ >+=head1 SYNOPSIS >+ >+This script provides a service for template for patron search using DataTables >+ >+=head2 Performing a search >+ >+Call this script from a DataTables table my $searchmember = $input->param('searchmember'); >+All following params are optional: >+ searchmember => the search terms >+ firstletter => search patrons with surname begins with this pattern (currently only used for 1 letter) >+ categorycode and branchcode => search patrons belong to a given categorycode or a branchcode >+ searchtype: can be 'contain' or 'start_with' >+ searchfieldstype: Can be 'standard', 'email', 'borrowernumber', 'phone' or 'address' >+ >+=cut >+ >+=back >+ >+=head1 LICENSE >+ >+Copyright 2013 BibLibre >+ >+This file is part of Koha. >+ >+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 2 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, write to the Free Software Foundation, Inc., >+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >diff --git a/t/DataTables/Members.t b/t/DataTables/Members.t >new file mode 100644 >index 0000000..942f93f >--- /dev/null >+++ b/t/DataTables/Members.t >@@ -0,0 +1,14 @@ >+use Modern::Perl; >+use Test::More tests => 4; >+ >+use_ok( "C4::Utils::DataTables::Members" ); >+ >+my $patrons = C4::Utils::DataTables::Members::search({ >+ searchmember => "Doe", >+ searchfieldstype => 'standard', >+ searchtype => 'contains' >+}); >+ >+isnt( $patrons->{iTotalDisplayRecords}, undef, "The iTotalDisplayRecords key is defined"); >+isnt( $patrons->{iTotalRecords}, undef, "The iTotalRecords key is defined"); >+is( ref $patrons->{patrons}, 'ARRAY', "The patrons key is an arrayref"); >-- >1.7.10.4
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 9811
:
16096
|
16111
|
16112
|
16320
|
16362
|
16403
|
16957
|
19056
|
19057
|
19307
|
19633
|
19642
|
20094
|
20095
|
20096
|
20174
|
20266
|
20290
|
20291
|
21169
|
21170
|
21171
|
21172
|
21173
|
21174
|
21175
|
22265
|
23261
|
24095
|
24111
|
26067
|
26068
|
26261
|
26336
|
26337
|
26342
|
26347
|
27642
|
27643
|
27644
|
27645
|
27646
|
27711
|
28738
|
28739
|
28740
|
28741
|
28742
|
28743
|
28744
|
28745
|
28746
|
28747
|
28781
|
28782
|
29019
|
29020
|
29021
|
29022
|
29023
|
29024
|
29025
|
29026
|
29027
|
29028
|
29029
|
29442