# along with Koha; if not, see <http://www.gnu.org/licenses>.
use Modern::Perl;
-require Exporter;
use C4::Context;
-use C4::Biblio; # GetMarcFromKohaField, GetBiblioData
-use C4::Koha; # getFacets
+use C4::Biblio qw( TransformMarcToKoha GetMarcFromKohaField GetFrameworkCode GetAuthorisedValueDesc GetBiblioData );
+use C4::Koha qw( getFacets GetVariationsOfISBN GetNormalizedUPC GetNormalizedEAN GetNormalizedOCLCNumber GetNormalizedISBN getitemtypeimagelocation );
use Koha::DateUtils;
use Koha::Libraries;
+use Koha::SearchEngine::QueryBuilder;
use Lingua::Stem;
-use C4::Search::PazPar2;
use XML::Simple;
-use C4::XSLT;
-use C4::Reserves; # GetReserveStatus
-use C4::Debug;
-use C4::Charset;
+use C4::XSLT qw( XSLTParse4Display );
+use C4::Reserves qw( GetReserveStatus );
+use C4::Charset qw( SetUTF8Flag );
use Koha::AuthorisedValues;
use Koha::ItemTypes;
use Koha::Libraries;
+use Koha::Logger;
use Koha::Patrons;
-use YAML;
+use Koha::Recalls;
+use Koha::RecordProcessor;
+use Koha::SearchFilters;
use URI::Escape;
use Business::ISBN;
use MARC::Record;
use MARC::Field;
-use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $DEBUG);
+our (@ISA, @EXPORT_OK);
BEGIN {
- $DEBUG = ($ENV{DEBUG}) ? 1 : 0;
+ require Exporter;
+ @ISA = qw(Exporter);
+ @EXPORT_OK = qw(
+ FindDuplicate
+ SimpleSearch
+ searchResults
+ getRecords
+ buildQuery
+ GetDistinctValues
+ enabled_staff_search_views
+ new_record_from_zebra
+ z3950_search_args
+ getIndexes
+ );
}
=head1 NAME
=cut
-@ISA = qw(Exporter);
-@EXPORT = qw(
- &FindDuplicate
- &SimpleSearch
- &searchResults
- &getRecords
- &buildQuery
- &GetDistinctValues
- &enabled_staff_search_views
-);
-
# make all your functions, whether exported or not;
=head2 FindDuplicate
sub FindDuplicate {
my ($record) = @_;
my $dbh = C4::Context->dbh;
- my $result = TransformMarcToKoha( $record, '' );
+ my $result = TransformMarcToKoha({ record => $record });
my $sth;
my $query;
- my $search;
- my $type;
- my ( $biblionumber, $title );
# search duplicate on ISBN, easy and fast..
# ... normalize first
$query = "isbn:$result->{isbn}";
}
else {
- my $QParser;
- $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser'));
- my $titleindex;
- my $authorindex;
- my $op;
-
- if ($QParser) {
- $titleindex = 'title|exact';
- $authorindex = 'author|exact';
- $op = '&&';
- $QParser->custom_data->{'QueryAutoTruncate'} = C4::Context->preference('QueryAutoTruncate');
- } else {
- $titleindex = 'ti,ext';
- $authorindex = 'au,ext';
- $op = 'and';
- }
+
+ my $titleindex = 'ti,ext';
+ my $authorindex = 'au,ext';
+ my $op = 'AND';
$result->{title} =~ s /\\//g;
$result->{title} =~ s /\"//g;
$result->{title} =~ s /\(//g;
$result->{title} =~ s /\)//g;
- # FIXME: instead of removing operators, could just do
- # quotes around the value
- $result->{title} =~ s/(and|or|not)//g;
$query = "$titleindex:\"$result->{title}\"";
if ( $result->{author} ) {
$result->{author} =~ s /\\//g;
$result->{author} =~ s /\(//g;
$result->{author} =~ s /\)//g;
- # remove valid operators
- $result->{author} =~ s/(and|or|not)//g;
$query .= " $op $authorindex:\"$result->{author}\"";
}
}
- my ( $error, $searchresults, undef ) = SimpleSearch($query); # FIXME :: hardcoded !
+ my $searcher = Koha::SearchEngine::Search->new({index => $Koha::SearchEngine::BIBLIOS_INDEX});
+ my ( $error, $searchresults, undef ) = $searcher->simple_search_compat($query,0,50);
my @results;
if (!defined $error) {
foreach my $possible_duplicate_record (@{$searchresults}) {
$possible_duplicate_record
);
- my $result = TransformMarcToKoha( $marcrecord, '' );
+ my $result = TransformMarcToKoha({ record => $marcrecord });
# FIXME :: why 2 $biblionumber ?
if ($result) {
for my $r ( @{$marcresults} ) {
my $marcrecord = MARC::File::USMARC::decode($r);
- my $biblio = TransformMarcToKoha($marcrecord,q{});
+ my $biblio = TransformMarcToKoha({ record => $marcrecord });
#build the iarray of hashs for the template.
push @results, {
my $results = [];
my $total_hits = 0;
- my $QParser;
- $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser') && ! ($query =~ m/\w,\w|\w=\w/));
- if ($QParser) {
- $QParser->custom_data->{'QueryAutoTruncate'} = C4::Context->preference('QueryAutoTruncate');
- }
-
# Initialize & Search Zebra
for ( my $i = 0 ; $i < @servers ; $i++ ) {
eval {
$zconns[$i] = C4::Context->Zconn( $servers[$i], 1 );
- if ($QParser) {
- $query =~ s/=/:/g unless $options{skip_normalize};
- $QParser->parse( $query );
- $query = $QParser->target_syntax($servers[$i]);
- $zoom_queries[$i] = new ZOOM::Query::PQF( $query, $zconns[$i]);
- } else {
- $query =~ s/:/=/g unless $options{skip_normalize};
- $zoom_queries[$i] = new ZOOM::Query::CCL2RPN( $query, $zconns[$i]);
- }
+ $query =~ s/:/=/g unless $options{skip_normalize};
+ $zoom_queries[$i] = ZOOM::Query::CCL2RPN->new( $query, $zconns[$i]);
$tmpresults[$i] = $zconns[$i]->search( $zoom_queries[$i] );
# error handling
$offset = 0 if $offset < 0;
# Initialize variables for the ZOOM connection and results object
- my $zconn;
my @zconns;
my @results;
my $results_hashref = ();
# TODO simplify this structure ( { branchcode => $branchname } is enought) and remove this parameter
- $branches ||= { map { $_->branchcode => { branchname => $_->branchname } } Koha::Libraries->search };
+ $branches ||= { map { $_->branchcode => { branchname => $_->branchname } } Koha::Libraries->search->as_list };
# Initialize variables for the faceted results objects
my $facets_counter = {};
# if this is a local search, use the $koha-query, if it's a federated one, use the federated-query
my $query_to_use = ($servers[$i] =~ /biblioserver/) ? $koha_query : $simple_query;
- #$query_to_use = $simple_query if $scan;
- warn $simple_query if ( $scan and $DEBUG );
+ Koha::Logger->get->debug($simple_query) if $scan;
# Check if we've got a query_type defined, if so, use it
eval {
if ($query_type) {
if ($query_type =~ /^ccl/) {
$query_to_use =~ s/\:/\=/g; # change : to = last minute (FIXME)
- $results[$i] = $zconns[$i]->search(new ZOOM::Query::CCL2RPN($query_to_use, $zconns[$i]));
+ $results[$i] = $zconns[$i]->search(ZOOM::Query::CCL2RPN->new($query_to_use, $zconns[$i]));
} elsif ($query_type =~ /^cql/) {
- $results[$i] = $zconns[$i]->search(new ZOOM::Query::CQL($query_to_use, $zconns[$i]));
+ $results[$i] = $zconns[$i]->search(ZOOM::Query::CQL->new($query_to_use, $zconns[$i]));
} elsif ($query_type =~ /^pqf/) {
- $results[$i] = $zconns[$i]->search(new ZOOM::Query::PQF($query_to_use, $zconns[$i]));
+ $results[$i] = $zconns[$i]->search(ZOOM::Query::PQF->new($query_to_use, $zconns[$i]));
} else {
warn "Unknown query_type '$query_type'. Results undetermined.";
}
} elsif ($scan) {
- $results[$i] = $zconns[$i]->scan( new ZOOM::Query::CCL2RPN($query_to_use, $zconns[$i]));
+ $results[$i] = $zconns[$i]->scan( ZOOM::Query::CCL2RPN->new($query_to_use, $zconns[$i]));
} else {
- $results[$i] = $zconns[$i]->search(new ZOOM::Query::CCL2RPN($query_to_use, $zconns[$i]));
+ $results[$i] = $zconns[$i]->search(ZOOM::Query::CCL2RPN->new($query_to_use, $zconns[$i]));
}
};
if ($@) {
elsif ( $sort eq "title_za" || $sort eq "title_dsc" ) {
$sort_by .= "1=4 >i ";
}
+ elsif ( $sort eq "biblionumber_az" || $sort eq "biblionumber_asc" ) {
+ $sort_by .= "1=12 <i ";
+ }
+ elsif ( $sort eq "biblionumber_za" || $sort eq "biblionumber_dsc" ) {
+ $sort_by .= "1=12 >i ";
+ }
else {
warn "Ignoring unrecognized sort '$sort' requested" if $sort_by;
}
}
for ( my $j = $offset ; $j < $times ; $j++ ) {
- my $records_hash;
my $record;
## Check if it's an index scan
# BUILD FACETS
if ( $servers[ $i - 1 ] =~ /biblioserver/ ) {
for my $link_value (
- sort { $facets_counter->{$b} <=> $facets_counter->{$a} }
- keys %$facets_counter
+ sort { $a cmp $b } keys %$facets_counter
)
{
my @this_facets_array;
# This sorts the facets into alphabetical order
if (@facets_loop) {
foreach my $f (@facets_loop) {
- $f->{facets} = [ sort { uc($a->{facet_label_value}) cmp uc($b->{facet_label_value}) } @{ $f->{facets} } ];
+ if( C4::Context->preference('FacetOrder') eq 'Alphabetical' ){
+ $f->{facets} =
+ [ sort { uc($a->{facet_label_value}) cmp uc($b->{facet_label_value}) } @{ $f->{facets} } ];
+ }
}
- @facets_loop = sort {defined $a->{expand} && defined $b->{expand} && $a->{expand} cmp $b->{expand}} @facets_loop;
}
return ( undef, $results_hashref, \@facets_loop );
next if $field->indicator(1) eq 'z';
my $data = $field->as_string( $subfield_letters, $facet->{ sep } );
+ $data =~ s/\s*(?<!\p{Uppercase})[.\-,;]*\s*$//;
unless ( grep { $_ eq $data } @used_datas ) {
push @used_datas, $data;
my $facets = {};
foreach my $term ( @terms ) {
my $facet_value = $term->textContent;
+ $facet_value =~ s/\s*(?<!\p{Uppercase})[.\-,;]*\s*$//;
$facet_value =~ s/\Q$internal_sep\E/$sep/ if defined $sep;
- $facets->{ $facet_value } = $term->getAttribute( 'occur' );
+ $facets->{ $facet_value } += $term->getAttribute( 'occur' );
}
return $facets;
return $facets_info;
}
-sub pazGetRecords {
- my (
- $koha_query, $simple_query, $sort_by_ref, $servers_ref,
- $results_per_page, $offset, $branches, $query_type,
- $scan
- ) = @_;
- #NOTE: Parameter $branches is not used here !
-
- my $paz = C4::Search::PazPar2->new(C4::Context->config('pazpar2url'));
- $paz->init();
- $paz->search($simple_query);
- sleep 1; # FIXME: WHY?
-
- # do results
- my $results_hashref = {};
- my $stats = XMLin($paz->stat);
- my $results = XMLin($paz->show($offset, $results_per_page, 'work-title:1'), forcearray => 1);
-
- # for a grouped search result, the number of hits
- # is the number of groups returned; 'bib_hits' will have
- # the total number of bibs.
- $results_hashref->{'biblioserver'}->{'hits'} = $results->{'merged'}->[0];
- $results_hashref->{'biblioserver'}->{'bib_hits'} = $stats->{'hits'};
-
- HIT: foreach my $hit (@{ $results->{'hit'} }) {
- my $recid = $hit->{recid}->[0];
-
- my $work_title = $hit->{'md-work-title'}->[0];
- my $work_author;
- if (exists $hit->{'md-work-author'}) {
- $work_author = $hit->{'md-work-author'}->[0];
- }
- my $group_label = (defined $work_author) ? "$work_title / $work_author" : $work_title;
-
- my $result_group = {};
- $result_group->{'group_label'} = $group_label;
- $result_group->{'group_merge_key'} = $recid;
-
- my $count = 1;
- if (exists $hit->{count}) {
- $count = $hit->{count}->[0];
- }
- $result_group->{'group_count'} = $count;
-
- for (my $i = 0; $i < $count; $i++) {
- # FIXME -- may need to worry about diacritics here
- my $rec = $paz->record($recid, $i);
- push @{ $result_group->{'RECORDS'} }, $rec;
- }
-
- push @{ $results_hashref->{'biblioserver'}->{'GROUPS'} }, $result_group;
- }
-
- # pass through facets
- my $termlist_xml = $paz->termlist('author,subject');
- my $terms = XMLin($termlist_xml, forcearray => 1);
- my @facets_loop = ();
- #die Dumper($results);
-# foreach my $list (sort keys %{ $terms->{'list'} }) {
-# my @facets = ();
-# foreach my $facet (sort @{ $terms->{'list'}->{$list}->{'term'} } ) {
-# push @facets, {
-# facet_label_value => $facet->{'name'}->[0],
-# };
-# }
-# push @facets_loop, ( {
-# type_label => $list,
-# facets => \@facets,
-# } );
-# }
-
- return ( undef, $results_hashref, \@facets_loop );
-}
-
# TRUNCATION
sub _detect_truncation {
my ( $operand, $index ) = @_;
unless ( $stem =~ /(and$|or$|not$)/ ) || ( length($stem) < 3 );
$stemmed_operand .= " ";
}
- warn "STEMMED OPERAND: $stemmed_operand" if $DEBUG;
+
+ Koha::Logger->get->debug("STEMMED OPERAND: $stemmed_operand");
return $stemmed_operand;
}
my $fuzzy_enabled = C4::Context->preference("QueryFuzzy") || 0;
$operand =~ s/"/ /g; # Bug 7518: searches with quotation marks don't work
- my $weighted_query .= "(rk=("; # Specifies that we're applying rank
+ my $weighted_query = "(rk=("; # Specifies that we're applying rank
# Keyword, or, no index specified
if ( ( $index eq 'kw' ) || ( !$index ) ) {
'Conference-name-seealso',
'Content-type',
'Control-number',
+ 'Control-number-identifier',
+ 'cni',
'copydate',
'Corporate-name',
'Corporate-name-heading',
'mc-itemtype',
'mc-rtype',
'mus',
+ 'Multipart-resource-level',
+ 'mrl',
'name',
'Music-number',
'Name-geographic',
return \@indexes;
}
-=head2 _handle_exploding_index
-
- my $query = _handle_exploding_index($index, $term)
-
-Callback routine to generate the search for "exploding" indexes (i.e.
-those indexes which are turned into multiple or-connected searches based
-on authority data).
-
-=cut
-
-sub _handle_exploding_index {
- my ($QParser, $filter, $params, $negate, $server) = @_;
- my $index = $filter;
- my $term = join(' ', @$params);
-
- return unless ($index =~ m/(su-br|su-na|su-rl)/ && $term);
-
- my $marcflavour = C4::Context->preference('marcflavour');
-
- my $codesubfield = $marcflavour eq 'UNIMARC' ? '5' : 'w';
- my $wantedcodes = '';
- my @subqueries = ( "\@attr 1=Subject \@attr 4=1 \"$term\"");
- my ($error, $results, $total_hits) = SimpleSearch( "he:$term", undef, undef, [ "authorityserver" ] );
- foreach my $auth (@$results) {
- my $record = MARC::Record->new_from_usmarc($auth);
- my @references = $record->field('5..');
- if (@references) {
- if ($index eq 'su-br') {
- $wantedcodes = 'g';
- } elsif ($index eq 'su-na') {
- $wantedcodes = 'h';
- } elsif ($index eq 'su-rl') {
- $wantedcodes = '';
- }
- foreach my $reference (@references) {
- my $codes = $reference->subfield($codesubfield);
- push @subqueries, '@attr 1=Subject @attr 4=1 "' . $reference->as_string('abcdefghijlmnopqrstuvxyz') . '"' if (($codes && $codes eq $wantedcodes) || !$wantedcodes);
- }
- }
- }
- my $query = ' @or ' x (scalar(@subqueries) - 1) . join(' ', @subqueries);
- return $query;
-}
-
-=head2 parseQuery
-
- ( $operators, $operands, $indexes, $limits,
- $sort_by, $scan, $lang ) =
- parseQuery ( $operators, $operands, $indexes, $limits, $sort_by, $scan, $lang);
-
-Shim function to ease the transition from buildQuery to a new QueryParser.
-This function is called at the beginning of buildQuery, and modifies
-buildQuery's input. If it can handle the input, it returns a query that
-buildQuery will not try to parse.
-
-=cut
-
-sub parseQuery {
- my ( $operators, $operands, $indexes, $limits, $sort_by, $scan, $lang) = @_;
-
- my @operators = $operators ? @$operators : ();
- my @indexes = $indexes ? @$indexes : ();
- my @operands = $operands ? @$operands : ();
- my @limits = $limits ? @$limits : ();
- my @sort_by = $sort_by ? @$sort_by : ();
-
- my $query = $operands[0];
- my $index;
- my $term;
- my $query_desc;
-
- my $QParser;
- $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser') || $query =~ s/^qp=//);
- undef $QParser if ($query =~ m/^(ccl=|pqf=|cql=)/ || grep (/\w,\w|\w=\w/, @operands, @indexes) );
- undef $QParser if (scalar @limits > 0);
-
- if ($QParser)
- {
- $QParser->custom_data->{'QueryAutoTruncate'} = C4::Context->preference('QueryAutoTruncate');
- $query = '';
- for ( my $ii = 0 ; $ii <= @operands ; $ii++ ) {
- next unless $operands[$ii];
- $query .= $operators[$ii-1] && $operators[ $ii - 1 ] eq 'or' ? ' || ' : ' && '
- if ($query);
- if ( $operands[$ii] =~ /^[^"]\W*[-|_\w]*:\w.*[^"]$/ ) {
- $query .= $operands[$ii];
- }
- elsif ( $indexes[$ii] && $indexes[$ii] =~ m/su-/ ) {
- $query .= $indexes[$ii] . '(' . $operands[$ii] . ')';
- }
- else {
- $query .=
- ( $indexes[$ii] ? "$indexes[$ii]:" : '' ) . $operands[$ii];
- }
- }
- foreach my $limit (@limits) {
- }
- if ( scalar(@sort_by) > 0 ) {
- my $modifier_re =
- '#(' . join( '|', @{ $QParser->modifiers } ) . ')';
- $query =~ s/$modifier_re//g;
- foreach my $modifier (@sort_by) {
- $query .= " #$modifier";
- }
- }
-
- $query_desc = $query;
- $query_desc =~ s/\s+/ /g;
- if ( C4::Context->preference("QueryWeightFields") ) {
- }
- $QParser->add_bib1_filter_map( 'su-br' => 'biblioserver' =>
- { 'target_syntax_callback' => \&_handle_exploding_index } );
- $QParser->add_bib1_filter_map( 'su-na' => 'biblioserver' =>
- { 'target_syntax_callback' => \&_handle_exploding_index } );
- $QParser->add_bib1_filter_map( 'su-rl' => 'biblioserver' =>
- { 'target_syntax_callback' => \&_handle_exploding_index } );
- $QParser->parse($query);
- $operands[0] = "pqf=" . $QParser->target_syntax('biblioserver');
- }
- else {
- require Koha::QueryParser::Driver::PQF;
- my $modifier_re = '#(' . join( '|', @{Koha::QueryParser::Driver::PQF->modifiers}) . ')';
- s/$modifier_re//g for @operands;
- }
-
- return ( $operators, \@operands, $indexes, $limits, $sort_by, $scan, $lang, $query_desc);
-}
-
=head2 buildQuery
( $error, $query,
sub buildQuery {
my ( $operators, $operands, $indexes, $limits, $sort_by, $scan, $lang) = @_;
- warn "---------\nEnter buildQuery\n---------" if $DEBUG;
-
my $query_desc;
- ( $operators, $operands, $indexes, $limits, $sort_by, $scan, $lang, $query_desc) = parseQuery($operators, $operands, $indexes, $limits, $sort_by, $scan, $lang);
# dereference
my @operators = $operators ? @$operators : ();
my $weight_fields = C4::Context->preference("QueryWeightFields") || 0;
my $fuzzy_enabled = C4::Context->preference("QueryFuzzy") || 0;
- my $query = $operands[0];
+ my $query = $operands[0] // "";
my $simple_query = $operands[0];
# initialize the variables we're passing back
my $query_cgi;
my $query_type;
- my $limit;
+ my $limit = q{};
my $limit_cgi;
my $limit_desc;
$query = "ccl=$query" if $cclq;
}
+ # add limits
+ my %group_OR_limits;
+ my $availability_limit;
+ foreach my $this_limit (@limits) {
+ next unless $this_limit;
+ if ( $this_limit =~ /available/ ) {
+#
+## 'available' is defined as (items.onloan is NULL) and (items.itemlost = 0)
+## In English:
+## all records not indexed in the onloan register (zebra) and all records with a value of lost equal to 0
+ $availability_limit .=
+"( (allrecords,AlwaysMatches='') and (not-onloan-count,st-numeric >= 1) and (lost,st-numeric=0) )";
+ $limit_cgi .= "&limit=available";
+ $limit_desc .= "";
+ }
+
+ # group_OR_limits, prefixed by mc-
+ # OR every member of the group
+ elsif ( $this_limit =~ /mc/ ) {
+ my ($k,$v) = split(/:/, $this_limit,2);
+ if ( $k !~ /mc-i(tem)?type/ ) {
+ # in case the mc-ccode value has complicating chars like ()'s inside it we wrap in quotes
+ $this_limit =~ tr/"//d;
+ $this_limit = $k.':"'.$v.'"';
+ }
+
+ $group_OR_limits{$k} .= " or " if $group_OR_limits{$k};
+ $limit_desc .= " or " if $group_OR_limits{$k};
+ $group_OR_limits{$k} .= "$this_limit";
+ $limit_cgi .= "&limit=" . uri_escape_utf8($this_limit);
+ $limit_desc .= " $this_limit";
+ }
+ elsif ( $this_limit =~ '^multibranchlimit:|^branch:' ) {
+ $limit_cgi .= "&limit=" . uri_escape_utf8($this_limit);
+ $limit .= " and " if $limit || $query;
+ my $branchfield = C4::Context->preference('SearchLimitLibrary');
+ my @branchcodes;
+ if( $this_limit =~ '^multibranchlimit:' ){
+ my ($group_id) = ( $this_limit =~ /^multibranchlimit:(.*)$/ );
+ my $search_group = Koha::Library::Groups->find( $group_id );
+ @branchcodes = map { $_->branchcode } $search_group->all_libraries;
+ @branchcodes = sort { $a cmp $b } @branchcodes;
+ } else {
+ @branchcodes = ( $this_limit =~ /^branch:(.*)$/ );
+ }
+
+ if (@branchcodes) {
+ if ( $branchfield eq "homebranch" ) {
+ $this_limit = sprintf "(%s)", join " or ", map { 'homebranch: ' . $_ } @branchcodes;
+ }
+ elsif ( $branchfield eq "holdingbranch" ) {
+ $this_limit = sprintf "(%s)", join " or ", map { 'holdingbranch: ' . $_ } @branchcodes;
+ }
+ else {
+ $this_limit = sprintf "(%s or %s)",
+ join( " or ", map { 'homebranch: ' . $_ } @branchcodes ),
+ join( " or ", map { 'holdingbranch: ' . $_ } @branchcodes );
+ }
+ }
+ $limit .= "$this_limit";
+ $limit_desc .= " $this_limit";
+ } elsif ( $this_limit =~ '^search_filter:' ) {
+ # Here we will get the query as a string, append to the limits, and pass through buildQuery
+ # again to clean the terms and handle nested filters
+ $limit_cgi .= "&limit=" . uri_escape_utf8($this_limit);
+ my ($filter_id) = ( $this_limit =~ /^search_filter:(.*)$/ );
+ my $search_filter = Koha::SearchFilters->find( $filter_id );
+ next unless $search_filter;
+ my ($expanded_lim, $query_lim) = $search_filter->expand_filter;
+ push @$expanded_lim, $query_lim;
+ my ( $error, undef, undef, undef, undef, $fixed_limit, undef, undef, undef ) = buildQuery ( undef, undef, undef, $expanded_lim, undef, undef, $lang);
+ $limit .= " and " if $limit || $query;
+ $limit .= "$fixed_limit";
+ $limit_desc .= " $limit";
+ }
+
+ # Regular old limits
+ else {
+ $limit .= " and " if $limit || $query;
+ $limit .= "$this_limit";
+ $limit_cgi .= "&limit=" . uri_escape_utf8($this_limit);
+ $limit_desc .= " $this_limit";
+ }
+ }
+ foreach my $k (keys (%group_OR_limits)) {
+ $limit .= " and " if ( $query || $limit );
+ $limit .= "($group_OR_limits{$k})";
+ }
+ if ($availability_limit) {
+ $limit .= " and " if ( $query || $limit );
+ $limit .= "($availability_limit)";
+ }
+
# for handling ccl, cql, pqf queries in diagnostic mode, skip the rest of the steps
# DIAGNOSTIC ONLY!!
if ( $query =~ /^ccl=/ ) {
my $q=$';
# This is needed otherwise ccl= and &limit won't work together, and
# this happens when selecting a subject on the opac-detail page
- @limits = grep {!/^$/} @limits;
my $original_q = $q; # without available part
- unless ( grep { $_ eq 'available' } @limits ) {
- $q =~ s| and \( \(allrecords,AlwaysMatches=''\) and \(not-onloan-count,st-numeric >= 1\) and \(lost,st-numeric=0\) \)||;
- $original_q = $q;
- }
- if ( @limits ) {
- if ( grep { $_ eq 'available' } @limits ) {
- $q .= q| and ( (allrecords,AlwaysMatches='') and (not-onloan-count,st-numeric >= 1) and (lost,st-numeric=0) )|;
- @limits = grep {!/^available$/} @limits;
- }
- $q .= ' and '.join(' and ', @limits) if @limits;
- }
+ $q .= $limit if $limit;
return ( undef, $q, $q, "q=ccl=".uri_escape_utf8($q), $original_q, '', '', '', 'ccl' );
}
if ( $query =~ /^cql=/ ) {
return ( undef, $', $', "q=cql=".uri_escape_utf8($'), $', '', '', '', 'cql' );
}
if ( $query =~ /^pqf=/ ) {
- if ($query_desc) {
- $query_cgi = "q=".uri_escape_utf8($query_desc);
- } else {
- $query_desc = $';
- $query_cgi = "q=pqf=".uri_escape_utf8($');
- }
+ $query_desc = $';
+ $query_cgi = "q=pqf=".uri_escape_utf8($');
return ( undef, $', $', $query_cgi, $query_desc, '', '', '', 'pqf' );
}
# Add index-specific attributes
- #Afaik, this 'yr' condition will only ever be met in the staff client advanced search
+ #Afaik, this 'yr' condition will only ever be met in the staff interface advanced search
#for "Publication date", since typing 'yr:YYYY' into the search box produces a CCL query,
#which is processed higher up in this sub. Other than that, year searches are typically
#handled as limits which are not processed her either.
if ( $index eq 'nb' ) {
if ( C4::Context->preference("SearchWithISBNVariations") ) {
my @isbns = C4::Koha::GetVariationsOfISBN( $operand );
- $operands[$i] = $operand = '(nb=' . join(' OR nb=', @isbns) . ')';
+ $operands[$i] = $operand = '(' . join( ' OR ', map { 'nb=' . $_ } @isbns ) . ')';
+ $indexes[$i] = $index = 'kw';
+ }
+ }
+ if ( $index eq 'ns' ) {
+ if ( C4::Context->preference("SearchWithISSNVariations") ) {
+ my @issns = C4::Koha::GetVariationsOfISSN( $operand );
+ $operands[$i] = $operand = '(' . join( ' OR ', map { 'ns=' . $_ } @issns ) . ')';
$indexes[$i] = $index = 'kw';
}
}
$operand=join(" ",map{
(index($_,"*")>0?"$_":"$_*")
}split (/\s+/,$operand));
- warn $operand if $DEBUG;
}
}
# Detect Truncation
- my $truncated_operand;
+ my $truncated_operand = q{};
my( $nontruncated, $righttruncated, $lefttruncated,
$rightlefttruncated, $regexpr
) = _detect_truncation( $operand, $index );
- warn
-"TRUNCATION: NON:>@$nontruncated< RIGHT:>@$righttruncated< LEFT:>@$lefttruncated< RIGHTLEFT:>@$rightlefttruncated< REGEX:>@$regexpr<"
- if $DEBUG;
+
+ Koha::Logger->get->debug(
+ "TRUNCATION: NON:>@$nontruncated< RIGHT:>@$righttruncated< LEFT:>@$lefttruncated< RIGHTLEFT:>@$rightlefttruncated< REGEX:>@$regexpr<");
# Apply Truncation
if (
}
}
$operand = $truncated_operand if $truncated_operand;
- warn "TRUNCATED OPERAND: >$truncated_operand<" if $DEBUG;
+ Koha::Logger->get->debug("TRUNCATED OPERAND: >$truncated_operand<");
# Handle Stemming
- my $stemmed_operand;
+ my $stemmed_operand = q{};
$stemmed_operand = _build_stemmed_operand($operand, $lang)
if $stemming;
- warn "STEMMED OPERAND: >$stemmed_operand<" if $DEBUG;
+ Koha::Logger->get->debug("STEMMED OPERAND: >$stemmed_operand<");
# Handle Field Weighting
- my $weighted_operand;
+ my $weighted_operand = q{};
if ($weight_fields) {
$weighted_operand = _build_weighted_query( $operand, $stemmed_operand, $index );
$operand = $weighted_operand;
$indexes_set = 1;
}
- warn "FIELD WEIGHTED OPERAND: >$weighted_operand<" if $DEBUG;
+ Koha::Logger->get->debug("FIELD WEIGHTED OPERAND: >$weighted_operand<");
+
+ #Use relevance ranking when not using a weighted query (which adds relevance ranking of its own)
+
+ #N.B. Truncation is mutually exclusive with Weighted Queries,
+ #so even if QueryWeightFields is turned on, QueryAutoTruncate will turn it off, thus
+ #the need for this relevance wrapper.
+ $operand = "(rk=($operand))" unless $weight_fields;
($query,$query_cgi,$query_desc,$previous_operand) = _build_initial_query({
query => $query,
} #/if $operands
} # /for
}
- warn "QUERY BEFORE LIMITS: >$query<" if $DEBUG;
-
- # add limits
- my %group_OR_limits;
- my $availability_limit;
- foreach my $this_limit (@limits) {
- next unless $this_limit;
- if ( $this_limit =~ /available/ ) {
-#
-## 'available' is defined as (items.onloan is NULL) and (items.itemlost = 0)
-## In English:
-## all records not indexed in the onloan register (zebra) and all records with a value of lost equal to 0
- $availability_limit .=
-"( (allrecords,AlwaysMatches='') and (not-onloan-count,st-numeric >= 1) and (lost,st-numeric=0) )";
- $limit_cgi .= "&limit=available";
- $limit_desc .= "";
- }
-
- # group_OR_limits, prefixed by mc-
- # OR every member of the group
- elsif ( $this_limit =~ /mc/ ) {
- my ($k,$v) = split(/:/, $this_limit,2);
- if ( $k !~ /mc-i(tem)?type/ ) {
- # in case the mc-ccode value has complicating chars like ()'s inside it we wrap in quotes
- $this_limit =~ tr/"//d;
- $this_limit = $k.":'".$v."'";
- }
-
- $group_OR_limits{$k} .= " or " if $group_OR_limits{$k};
- $limit_desc .= " or " if $group_OR_limits{$k};
- $group_OR_limits{$k} .= "$this_limit";
- $limit_cgi .= "&limit=" . uri_escape_utf8($this_limit);
- $limit_desc .= " $this_limit";
- }
-
- # Regular old limits
- else {
- $limit .= " and " if $limit || $query;
- $limit .= "$this_limit";
- $limit_cgi .= "&limit=" . uri_escape_utf8($this_limit);
- if ($this_limit =~ /^branch:(.+)/) {
- my $branchcode = $1;
- my $library = Koha::Libraries->find( $branchcode );
- if (defined $library) {
- $limit_desc .= " branch:" . $library->branchname;
- } else {
- $limit_desc .= " $this_limit";
- }
- } else {
- $limit_desc .= " $this_limit";
- }
- }
- }
- foreach my $k (keys (%group_OR_limits)) {
- $limit .= " and " if ( $query || $limit );
- $limit .= "($group_OR_limits{$k})";
- }
- if ($availability_limit) {
- $limit .= " and " if ( $query || $limit );
- $limit .= "($availability_limit)";
- }
+ Koha::Logger->get->debug("QUERY BEFORE LIMITS: >$query<");
# Normalize the query and limit strings
# This is flawed , means we can't search anything with : in it
$query =~ s/(?<=(st-date-normalized)):/=/g;
# Removing warnings for later substitutions
- $query //= q{};
- $query_desc //= q{};
- $query_cgi //= q{};
- $limit //= q{};
- $limit_desc //= q{};
+ $query //= q{};
+ $query_desc //= q{};
+ $query_cgi //= q{};
+ $limit //= q{};
+ $limit_desc //= q{};
+ $limit_cgi //= q{};
+ $simple_query //= q{};
$limit =~ s/:/=/g;
for ( $query, $query_desc, $limit, $limit_desc ) {
s/ +/ /g; # remove extra spaces
# append the limit to the query
$query .= " " . $limit;
- # Warnings if DEBUG
- if ($DEBUG) {
- warn "QUERY:" . $query;
- warn "QUERY CGI:" . $query_cgi;
- warn "QUERY DESC:" . $query_desc;
- warn "LIMIT:" . $limit;
- warn "LIMIT CGI:" . $limit_cgi;
- warn "LIMIT DESC:" . $limit_desc;
- warn "---------\nLeave buildQuery\n---------";
- }
+ Koha::Logger->get->debug(
+ sprintf "buildQuery returns\nQUERY:%s\nQUERY CGI:%s\nQUERY DESC:%s\nLIMIT:%s\nLIMIT CGI:%s\nLIMIT DESC:%s",
+ $query, $query_cgi, $query_desc, $limit, $limit_cgi, $limit_desc );
return (
undef, $query, $simple_query, $query_cgi,
my $operator = "";
if ($params->{previous_operand}){
#If there is a previous operand, add a supplied operator or the default 'and'
- $operator = ($params->{operator}) ? " ".($params->{operator})." " : ' and ';
+ $operator = ($params->{operator}) ? ($params->{operator}) : 'AND';
}
#NOTE: indexes_set is typically set when doing truncation or field weighting
#e.g. "kw,wrdl:test"
#e.g. " and kw,wrdl:test"
- $params->{query} .= $operator . $operand;
+ $params->{query} .= " " . $operator . " " . $operand;
$params->{query_cgi} .= "&op=".uri_escape_utf8($operator) if $operator;
$params->{query_cgi} .= "&idx=".uri_escape_utf8($params->{index}) if $params->{index};
- $params->{query_cgi} .= "&q=".uri_escape_utf8($params->{original_operand}) if $params->{original_operand};
+ $params->{query_cgi} .= "&q=".uri_escape_utf8($params->{original_operand}) if ( $params->{original_operand} ne '' );
#e.g. " and kw,wrdl: test"
- $params->{query_desc} .= $operator . $params->{index_plus} . " " . $params->{original_operand};
+ $params->{query_desc} .= " " . $operator . " " . ( $params->{index_plus} // q{} ) . " " . ( $params->{original_operand} // q{} );
$params->{previous_operand} = 1 unless $params->{previous_operand}; #If there is no previous operand, mark this as one
# IMO this subroutine is pretty messy still -- it's responsible for
# building the HTML output for the template
sub searchResults {
- my ( $search_context, $searchdesc, $hits, $results_per_page, $offset, $scan, $marcresults ) = @_;
+ my ( $search_context, $searchdesc, $hits, $results_per_page, $offset, $scan, $marcresults, $xslt_variables ) = @_;
my $dbh = C4::Context->dbh;
my @newresults;
$is_opac = 1;
}
+ my $record_processor = Koha::RecordProcessor->new({
+ filters => 'ViewPolicy'
+ });
+
#Build branchnames hash
- my %branches = map { $_->branchcode => $_->branchname } Koha::Libraries->search({}, { order_by => 'branchname' });
+ my %branches = map { $_->branchcode => $_->branchname } Koha::Libraries->search({}, { order_by => 'branchname' })->as_list;
# FIXME - We build an authorised values hash here, using the default framework
# though it is possible to have different authvals for different fws.
}
# handle which records to actually retrieve
- my $times;
+ my $times; # Times is which record to process up to
if ( $hits && $offset + $results_per_page <= $hits ) {
$times = $offset + $results_per_page;
}
else {
- $times = $hits; # FIXME: if $hits is undefined, why do we want to equal it?
+ $times = $hits; # If less hits than results_per_page+offset we go to the end
}
my $marcflavour = C4::Context->preference("marcflavour");
my ($bibliotag,$bibliosubf)=GetMarcFromKohaField( 'biblio.biblionumber' );
# set stuff for XSLT processing here once, not later again for every record we retrieved
- my $xslfile;
- my $xslsyspref;
- if( $is_opac ){
- $xslsyspref = "OPACXSLTResultsDisplay";
- $xslfile = C4::Context->preference( $xslsyspref );
- } else {
- $xslsyspref = "XSLTResultsDisplay";
- $xslfile = C4::Context->preference( $xslsyspref ) || "default";
- }
- my $lang = $xslfile ? C4::Languages::getlanguage() : undef;
- my $sysxml = $xslfile ? C4::XSLT::get_xslt_sysprefs() : undef;
my $userenv = C4::Context->userenv;
my $logged_in_user
: GetFrameworkCode($marcrecord->subfield($bibliotag,$bibliosubf));
SetUTF8Flag($marcrecord);
- my $oldbiblio = TransformMarcToKoha( $marcrecord, $fw );
+ my $oldbiblio = TransformMarcToKoha({ record => $marcrecord, limit_table => 'no_items' });
$oldbiblio->{result_number} = $i + 1;
$oldbiblio->{normalized_upc} = GetNormalizedUPC( $marcrecord,$marcflavour);
$oldbiblio->{normalized_ean} = GetNormalizedEAN( $marcrecord,$marcflavour);
$oldbiblio->{normalized_oclc} = GetNormalizedOCLCNumber($marcrecord,$marcflavour);
- $oldbiblio->{normalized_isbn} = GetNormalizedISBN(undef,$marcrecord,$marcflavour);
+ $oldbiblio->{normalized_isbn} = GetNormalizedISBN($oldbiblio->{isbn},$marcrecord,$marcflavour); # Use existing ISBN from record if we got one
$oldbiblio->{content_identifier_exists} = 1 if ($oldbiblio->{normalized_isbn} or $oldbiblio->{normalized_oclc} or $oldbiblio->{normalized_ean} or $oldbiblio->{normalized_upc});
# edition information, if any
# Build summary if there is one (the summary is defined in the itemtypes table)
$oldbiblio->{description} = $itemtype ? $itemtype->{translated_description} : q{};
- # FIXME: this is only used in the deprecated non-XLST opac results
- if ( !$xslfile && $is_opac && $itemtype && $itemtype->{summary} ) {
- my $summary = $itemtypes{ $oldbiblio->{itemtype} }->{summary};
- my @fields = $marcrecord->fields();
-
- my $newsummary;
- foreach my $line ( "$summary\n" =~ /(.*)\n/g ){
- my $tags = {};
- foreach my $tag ( $line =~ /\[(\d{3}[\w|\d])\]/ ) {
- $tag =~ /(.{3})(.)/;
- if($marcrecord->field($1)){
- my @abc = $marcrecord->field($1)->subfield($2);
- $tags->{$tag} = $#abc + 1 ;
- }
- }
-
- # We catch how many times to repeat this line
- my $max = 0;
- foreach my $tag (keys(%$tags)){
- $max = $tags->{$tag} if($tags->{$tag} > $max);
- }
-
- # we replace, and repeat each line
- for (my $i = 0 ; $i < $max ; $i++){
- my $newline = $line;
-
- foreach my $tag ( $newline =~ /\[(\d{3}[\w|\d])\]/g ) {
- $tag =~ /(.{3})(.)/;
-
- if($marcrecord->field($1)){
- my @repl = $marcrecord->field($1)->subfield($2);
- my $subfieldvalue = $repl[$i];
- $newline =~ s/\[$tag\]/$subfieldvalue/g;
- }
- }
- $newsummary .= "$newline\n";
- }
- }
-
- $newsummary =~ s/\[(.*?)]//g;
- $newsummary =~ s/\n/<br\/>/g;
- $oldbiblio->{summary} = $newsummary;
- }
-
# Pull out the items fields
my @fields = $marcrecord->field($itemtag);
+ $marcrecord->delete_fields( @fields ) unless C4::Context->preference('PassItemMarcToXSLT');
my $marcflavor = C4::Context->preference("marcflavour");
# adding linked items that belong to host records
if ( C4::Context->preference('EasyAnalyticalRecords') ) {
my $analyticsfield = '773';
- if ($marcflavor eq 'MARC21' || $marcflavor eq 'NORMARC') {
+ if ($marcflavor eq 'MARC21') {
$analyticsfield = '773';
} elsif ($marcflavor eq 'UNIMARC') {
$analyticsfield = '461';
my $hostbiblionumber = $hostfield->subfield("0");
my $linkeditemnumber = $hostfield->subfield("9");
if( $hostbiblionumber ) {
- my $hostbiblio = GetMarcBiblio({
- biblionumber => $hostbiblionumber,
- embed_items => 1 });
- my ($itemfield, undef) = GetMarcFromKohaField( 'items.itemnumber' );
- if( $hostbiblio ) {
- my @hostitems = $hostbiblio->field($itemfield);
- foreach my $hostitem (@hostitems){
- if ($hostitem->subfield("9") eq $linkeditemnumber){
- my $linkeditem =$hostitem;
- # append linked items if they exist
- push @fields, $linkeditem if $linkeditem;
- }
+ my $linkeditemmarc = C4::Items::GetMarcItem( $hostbiblionumber, $linkeditemnumber );
+ if ($linkeditemmarc) {
+ my $linkeditemfield = $linkeditemmarc->field($itemtag);
+ if ($linkeditemfield) {
+ push( @fields, $linkeditemfield );
}
}
}
my $itembinding_count = 0;
my $itemdamaged_count = 0;
my $item_in_transit_count = 0;
- my $can_place_holds = 0;
my $item_onhold_count = 0;
my $notforloan_count = 0;
+ my $item_recalled_count = 0;
my $items_count = scalar(@fields);
my $maxitems_pref = C4::Context->preference('maxItemsinSearchResults');
my $maxitems = $maxitems_pref ? $maxitems_pref - 1 : 1;
foreach my $code ( keys %subfieldstosearch ) {
$item->{$code} = $field->subfield( $subfieldstosearch{$code} );
}
+
+ unless ( $item->{itemnumber} ) {
+ warn "MARC item without itemnumber retrieved for biblio ($oldbiblio->{biblionumber})";
+ next;
+ }
+
$item->{description} = $itemtypes{ $item->{itype} }{translated_description} if $item->{itype};
- # OPAC hidden items
+ # OPAC hidden items
if ($is_opac) {
- # hidden because lost
- if ($hidelostitems && $item->{itemlost}) {
- $hideatopac_count++;
- next;
- }
- # hidden based on OpacHiddenItems syspref
- my @hi = C4::Items::GetHiddenItemnumbers({ items=> [ $item ], borcat => $search_context->{category} });
- if (scalar @hi) {
- push @hiddenitems, @hi;
+ # hidden based on OpacHiddenItems syspref or because lost
+ my $hi = Koha::Items->search( { itemnumber => $item->{itemnumber} } )
+ ->filter_by_visible_in_opac({ patron => $search_context->{patron} });
+ unless ( $hi->count ) {
+ push @hiddenitems, $item->{itemnumber};
$hideatopac_count++;
next;
}
$onloan_items->{$key}->{due_date} = $item->{onloan};
$onloan_items->{$key}->{count}++ if $item->{$hbranch};
$onloan_items->{$key}->{branchname} = $item->{branchname};
- $onloan_items->{$key}->{location} = $shelflocations->{ $item->{location} };
+ $onloan_items->{$key}->{location} = $shelflocations->{ $item->{location} } if $item->{location};
$onloan_items->{$key}->{itemcallnumber} = $item->{itemcallnumber};
$onloan_items->{$key}->{description} = $item->{description};
$onloan_items->{$key}->{imageurl} =
$onloan_items->{$key}->{longoverdue}++;
$longoverdue_count++;
}
- else { # can place holds as long as item isn't lost
- $can_place_holds = 1;
- }
}
# items not on loan, but still unavailable ( lost, withdrawn, damaged )
# is item on the reserve shelf?
my $reservestatus = '';
+ # is item a waiting recall?
+ my $recallstatus = '';
+
unless ($item->{withdrawn}
|| $item->{itemlost}
|| $item->{damaged}
# FIXME: to avoid having the query the database like this, and to make
# the in transit status count as unavailable for search limiting,
# should map transit status to record indexed in Zebra.
- #
- ($transfertwhen, $transfertfrom, $transfertto) = C4::Circulation::GetTransfers($item->{itemnumber});
+
+ my $item_object = Koha::Items->find($item->{itemnumber});
+ my $transfer = defined($item_object) ? $item_object->get_transfer : undef;
+ ( $transfertwhen, $transfertfrom, $transfertto ) =
+ defined($transfer)
+ ? (
+ $transfer->datesent, $transfer->frombranch,
+ $transfer->tobranch
+ )
+ : ( '', '', '' );
$reservestatus = C4::Reserves::GetReserveStatus( $item->{itemnumber} );
+ if ( C4::Context->preference('UseRecalls') ) {
+ if ( Koha::Recalls->search({ item_id => $item->{itemnumber}, status => 'waiting' })->count ) {
+ $recallstatus = 'Waiting';
+ }
+ }
}
# item is withdrawn, lost, damaged, not for loan, reserved or in transit
|| $item->{damaged}
|| $item->{notforloan}
|| $reservestatus eq 'Waiting'
+ || $recallstatus eq 'Waiting'
|| ($transfertwhen && $transfertwhen ne ''))
{
$withdrawn_count++ if $item->{withdrawn};
$itemdamaged_count++ if $item->{damaged};
$item_in_transit_count++ if $transfertwhen && $transfertwhen ne '';
$item_onhold_count++ if $reservestatus eq 'Waiting';
+ $item_recalled_count++ if $recallstatus eq 'Waiting';
$item->{status} = ($item->{withdrawn}//q{}) . "-" . ($item->{itemlost}//q{}) . "-" . ($item->{damaged}//q{}) . "-" . ($item->{notforloan}//q{});
- # can place a hold on a item if
- # not lost nor withdrawn
- # not damaged unless AllowHoldsOnDamagedItems is true
- # item is either for loan or on order (notforloan < 0)
- $can_place_holds = 1
- if (
- !$item->{itemlost}
- && !$item->{withdrawn}
- && ( !$item->{damaged} || C4::Context->preference('AllowHoldsOnDamagedItems') )
- && ( !$item->{notforloan} || $item->{notforloan} < 0 )
- );
-
$other_count++;
my $key = $prefix . $item->{status};
$other_items->{$key}->{$_} = $item->{$_};
}
$other_items->{$key}->{intransit} = ( $transfertwhen ne '' ) ? 1 : 0;
+ $other_items->{$key}->{recalled} = ($recallstatus) ? 1 : 0;
$other_items->{$key}->{onhold} = ($reservestatus) ? 1 : 0;
$other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value and $item->{notforloan};
$other_items->{$key}->{count}++ if $item->{$hbranch};
}
# item is available
else {
- $can_place_holds = 1;
$available_count++;
$available_items->{$prefix}->{count}++ if $item->{$hbranch};
foreach (qw(branchname itemcallnumber description)) {
} # notforloan, item level and biblioitem level
# if all items are hidden, do not show the record
- if ($items_count > 0 && $hideatopac_count == $items_count) {
+ if ( C4::Context->preference('OpacHiddenItemsHidesRecord') && $items_count > 0 && $hideatopac_count == $items_count) {
next;
}
# XSLT processing of some stuff
# we fetched the sysprefs already before the loop through all retrieved record!
- if (!$scan && $xslfile) {
- $oldbiblio->{XSLTResultsRecord} = XSLTParse4Display($oldbiblio->{biblionumber}, $marcrecord, $xslsyspref, 1, \@hiddenitems, $sysxml, $xslfile, $lang);
+ if (!$scan) {
+ $record_processor->options({
+ frameworkcode => $fw,
+ interface => $search_context->{'interface'}
+ });
+
+ $record_processor->process($marcrecord);
+
+ $oldbiblio->{XSLTResultsRecord} = XSLTParse4Display(
+ {
+ biblionumber => $oldbiblio->{biblionumber},
+ record => $marcrecord,
+ xsl_syspref => (
+ $is_opac
+ ? 'OPACXSLTResultsDisplay'
+ : 'XSLTResultsDisplay'
+ ),
+ fix_amps => 1,
+ hidden_items => \@hiddenitems,
+ xslt_variables => $xslt_variables,
+ }
+ );
}
+ my $biblio_object = Koha::Biblios->find( $oldbiblio->{biblionumber} );
+ $oldbiblio->{biblio_object} = $biblio_object;
+ $oldbiblio->{coins} = eval { $biblio_object->get_coins }
+ if $biblio_object
+ && C4::Context->preference('COinSinOPACResults')
+ && $is_opac;
+
+ my $can_place_holds = 1;
# if biblio level itypes are used and itemtype is notforloan, it can't be reserved either
if (!C4::Context->preference("item-level_itypes")) {
if ($itemtype && $itemtype->{notforloan}) {
$can_place_holds = 0;
}
+ } else {
+ $can_place_holds = $biblio_object->items->filter_by_for_hold()->count if $biblio_object;
}
$oldbiblio->{norequests} = 1 unless $can_place_holds;
$oldbiblio->{items_count} = $items_count;
$oldbiblio->{damagedcount} = $itemdamaged_count;
$oldbiblio->{intransitcount} = $item_in_transit_count;
$oldbiblio->{onholdcount} = $item_onhold_count;
+ $oldbiblio->{recalledcount} = $item_recalled_count;
$oldbiblio->{orderedcount} = $ordered_count;
$oldbiblio->{notforloancount} = $notforloan_count;
$oldbiblio->{'alternateholdings_count'} = $alternateholdingscount;
}
- $oldbiblio->{biblio_object} = Koha::Biblios->find( $oldbiblio->{biblionumber} );
-
push( @newresults, $oldbiblio );
}
if ($fieldname=~/\./){
my ($table,$column)=split /\./, $fieldname;
my $dbh = C4::Context->dbh;
- warn "select DISTINCT($column) as value, count(*) as cnt from $table group by lib order by $column " if $DEBUG;
my $sth = $dbh->prepare("select DISTINCT($column) as value, count(*) as cnt from $table ".($string?" where $column like \"$string%\"":"")."group by value order by $column ");
$sth->execute;
my $elements=$sth->fetchall_arrayref({});