Koha/C4/Utils/DataTables/Members.pm
Jonathan Druart ca5b112376
Bug 25142: Fix wrong grep logical test
To test and understand what's going on, you can try that bit of code:
my @a = qw( a b c a);
my @b = qw( b c d );
my @c;
@c = grep { 'a' eq $_ } @a ? 'ok' : ();
say @c;
@c = ( grep { 'a' eq $_ } @a ) ? 'ok' : ();
say @c;
@c = grep { 'a' eq $_ } @a ? ('ok') : (undef);
say @c;

The problem here:
Have patrons in 3 branches CPL, MPL, SPL
Have a non superlibrarian with edit_borrowers permission but
without view_borrower_infos_from_any_libraries, from CPL
Create a library group with CPL, MPL
Use that non superlibrarian to search for patrons
You can search for patrons fro CPL and MPL
BUT, edit the value for CPL, use SPL (edit the DOM)
Search and... oops

Apply this patch, try again

Also use a superlibrarian patron (and/or with view_borrower_infos_from_any_libraries)
and confirm that they can see all patrons

Signed-off-by: Bernardo Gonzalez Kriegel <bgkriegel@gmail.com>
Signed-off-by: Katrin Fischer <katrin.fischer.83@web.de>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>
2020-04-27 10:44:49 +01:00

303 lines
11 KiB
Perl

package C4::Utils::DataTables::Members;
use Modern::Perl;
use C4::Context;
use C4::Utils::DataTables;
use Koha::DateUtils;
sub search {
my ( $params ) = @_;
my $searchmember = $params->{searchmember};
my $firstletter = $params->{firstletter};
my $categorycode = $params->{categorycode};
my $branchcode = $params->{branchcode};
my $searchtype = $params->{searchtype} || 'contain';
my $searchfieldstype = $params->{searchfieldstype} || 'standard';
my $has_permission = $params->{has_permission};
my $dt_params = $params->{dt_params};
unless ( $searchmember ) {
$searchmember = $dt_params->{sSearch} // '';
}
# If branches are independent and user is not superlibrarian
# The search has to be only on the user branch
my $userenv = C4::Context->userenv;
my $logged_in_user = Koha::Patrons->find( $userenv->{number} );
my @restricted_branchcodes = $logged_in_user->libraries_where_can_see_patrons;
my ($sth, $query, $iTotalQuery, $iTotalRecords, $iTotalDisplayRecords);
my $dbh = C4::Context->dbh;
# Get the module_bit from a given permission code
if ( $has_permission ) {
($has_permission->{module_bit}) = $dbh->selectrow_array(q|
SELECT bit FROM userflags WHERE flag=?
|, undef, $has_permission->{permission});
}
# Get the iTotalRecords DataTable variable
$iTotalQuery = "SELECT COUNT(borrowers.borrowernumber) FROM borrowers";
if ( $has_permission ) {
$iTotalQuery .= ' LEFT JOIN user_permissions on borrowers.borrowernumber=user_permissions.borrowernumber';
}
my (@where, @conditions);
if ( @restricted_branchcodes ) {
push @where, "borrowers.branchcode IN (" . join( ',', ('?') x @restricted_branchcodes ) . ")";
push @conditions, @restricted_branchcodes;
}
if ( $has_permission ) {
push @where, '( borrowers.flags = 1 OR borrowers.flags & (1 << ?) OR module_bit=? AND code=? )';
push @conditions, ($has_permission->{module_bit}) x 2, $has_permission->{subpermission};
}
$iTotalQuery .= ' WHERE ' . join ' AND ', @where if @where;
($iTotalRecords) = $dbh->selectrow_array( $iTotalQuery, undef, @conditions );
# Do that after iTotalQuery!
if ( defined $branchcode and $branchcode ) {
@restricted_branchcodes = @restricted_branchcodes
? grep ({ $_ eq $branchcode } @restricted_branchcodes)
? ($branchcode)
: (undef) # Do not return any results
: ($branchcode);
}
if ( $searchfieldstype eq 'dateofbirth' ) {
# Return an empty list if the date of birth is not correctly formatted
$searchmember = eval { output_pref( { str => $searchmember, dateformat => 'iso', dateonly => 1 } ); };
if ( $@ or not $searchmember ) {
return {
iTotalRecords => $iTotalRecords,
iTotalDisplayRecords => 0,
patrons => [],
};
}
}
my $select = "SELECT
borrowers.borrowernumber, borrowers.surname, borrowers.firstname,
borrowers.othernames,
borrowers.flags,
borrowers.streetnumber, borrowers.streettype, borrowers.address,
borrowers.address2, borrowers.city, borrowers.state, borrowers.zipcode,
borrowers.country, cardnumber, borrowers.dateexpiry,
borrowers.borrowernotes, borrowers.branchcode, borrowers.email,
borrowers.userid, borrowers.dateofbirth, borrowers.categorycode,
categories.description AS category_description, categories.category_type,
branches.branchname, borrowers.phone";
my $from = "FROM borrowers
LEFT JOIN branches ON borrowers.branchcode = branches.branchcode
LEFT JOIN categories ON borrowers.categorycode = categories.categorycode";
if ( $has_permission ) {
$from .= '
LEFT JOIN user_permissions on borrowers.borrowernumber=user_permissions.borrowernumber';
}
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(@restricted_branchcodes ) {
push @where_strs, "borrowers.branchcode IN (" . join( ',', ('?') x @restricted_branchcodes ) . ")";
push @where_args, @restricted_branchcodes;
}
my $searchfields = {
standard => C4::Context->preference('DefaultPatronSearchFields') || 'surname,firstname,othernames,cardnumber,userid',
email => 'email,emailpro,B_email',
borrowernumber => 'borrowernumber',
phone => 'phone,phonepro,B_phone,altcontactphone,mobile',
address => 'streetnumber,streettype,address,address2,city,state,zipcode,country',
};
# * is replaced with % for sql
$searchmember =~ s/\*/%/g;
# split into search terms
my @terms;
# consider coma as space
$searchmember =~ s/,/ /g;
if ( $searchtype eq 'contain' ) {
@terms = split / /, $searchmember;
} else {
@terms = ($searchmember);
}
foreach my $term (@terms) {
next unless $term;
my $term_dt = eval { local $SIG{__WARN__} = {}; output_pref( { str => $term, dateonly => 1, dateformat => 'sql' } ); };
if ($term_dt) {
$term = $term_dt;
} else {
$term .= '%' # end with anything
if $term !~ /%$/;
$term = "%$term" # begin with anythin unless start_with
if $searchtype eq 'contain' && $term !~ /^%/;
}
my @where_strs_or;
if ( defined $searchfields->{$searchfieldstype} ) {
for my $searchfield ( split /,/, $searchfields->{$searchfieldstype} ) {
push @where_strs_or, "borrowers." . $dbh->quote_identifier($searchfield) . " LIKE ?";
push @where_args, $term;
}
} else {
push @where_strs_or, "borrowers." . $dbh->quote_identifier($searchfieldstype) . " LIKE ?";
push @where_args, $term;
}
if ( $searchfieldstype eq 'standard' and C4::Context->preference('ExtendedPatronAttributes') and $searchmember ) {
my @matching_borrowernumbers = Koha::Patrons->filter_by_attribute_value($searchmember)->get_column('borrowernumber');
for my $borrowernumber ( @matching_borrowernumbers ) {
push @where_strs_or, "borrowers.borrowernumber = ?";
push @where_args, $borrowernumber;
}
}
push @where_strs, '('. join (' OR ', @where_strs_or) . ')'
if @where_strs_or;
}
if ( $has_permission ) {
push @where_strs, '( borrowers.flags = 1 OR borrowers.flags & (1 << ?) OR module_bit=? AND code=? )';
push @where_args, ($has_permission->{module_bit}) x 2, $has_permission->{subpermission};
}
my $where = @where_strs ? " WHERE " . join (" AND ", @where_strs) : undef;
my $orderby = dt_build_orderby($dt_params);
my $limit;
# If iDisplayLength == -1, we want to display all patrons
if ( !$dt_params->{iDisplayLength} || $dt_params->{iDisplayLength} > -1 ) {
# In order to avoid sql injection
$dt_params->{iDisplayStart} =~ s/\D//g if defined($dt_params->{iDisplayStart});
$dt_params->{iDisplayLength} =~ s/\D//g if defined($dt_params->{iDisplayLength});
$dt_params->{iDisplayStart} //= 0;
$dt_params->{iDisplayLength} //= 20;
$limit = "LIMIT $dt_params->{iDisplayStart},$dt_params->{iDisplayLength}";
}
$query = join(
" ",
($select ? $select : ""),
($from ? $from : ""),
($where ? $where : ""),
($orderby ? $orderby : ""),
($limit ? $limit : "")
);
$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 some information on patrons
foreach my $patron (@$patrons) {
my $patron_object = Koha::Patrons->find( $patron->{borrowernumber} );
$patron->{overdues} = $patron_object->get_overdues->count;
$patron->{issues} = $patron_object->checkouts->count;
my $balance = $patron_object->account->balance;
# FIXME Should be formatted from the template
$patron->{fines} = sprintf("%.2f", $balance);
if( $patron->{dateexpiry} ) {
# FIXME We should not format the date here, do it in template-side instead
$patron->{dateexpiry} = output_pref( { dt => scalar dt_from_string( $patron->{dateexpiry}, 'iso'), dateonly => 1} );
} else {
$patron->{dateexpiry} = '';
}
}
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 'start_with' or 'contain' (default value). Used for the searchmember parameter.
=item searchfieldstype
Can be 'standard' (default value), '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
This file is part of Koha.
Copyright 2013 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
(at your option) any later version.
Koha is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Koha; if not, see <http://www.gnu.org/licenses>.