X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;ds=sidebyside;f=C4%2FSearch.pm;h=11b4909a10d3c210fd31feb4d87932ef44d727d5;hb=2df3334705489a45712c746a1ffe053bd19f3f22;hp=b8cda0bb181c91d226acc6dafa47d639803060c5;hpb=2c658d882a5ca43c107c26396b09f11ad6e69f9b;p=koha.git diff --git a/C4/Search.pm b/C4/Search.pm index b8cda0bb18..11b4909a10 100644 --- a/C4/Search.pm +++ b/C4/Search.pm @@ -2,18 +2,18 @@ package C4::Search; # 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 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. +# 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., 59 Temple Place, -# Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . use strict; #use warnings; FIXME - Bug 2505 @@ -36,7 +36,6 @@ use URI::Escape; use Business::ISBN; use MARC::Record; use MARC::Field; -use utf8; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $DEBUG); # set the version for version checking @@ -68,10 +67,8 @@ This module provides searching functions for Koha's bibliographic databases &searchResults &getRecords &buildQuery - &AddSearchHistory &GetDistinctValues &enabled_staff_search_views - &PurgeSearchHistory ); # make all your functions, whether exported or not; @@ -173,7 +170,7 @@ This function provides a simple search API on the bibliographic catalog * $query can be a simple keyword or a complete CCL query * @servers is optional. Defaults to biblioserver as found in koha-conf.xml - * $offset - If present, represents the number of records at the beggining to omit. Defaults to 0 + * $offset - If present, represents the number of records at the beginning to omit. Defaults to 0 * $max_results - if present, determines the maximum number of records to fetch. undef is All. defaults to undef. @@ -341,10 +338,9 @@ sub getRecords { my $results_hashref = (); # Initialize variables for the faceted results objects - my $facets_counter = (); - my $facets_info = (); + my $facets_counter = {}; + my $facets_info = {}; my $facets = getFacets(); - my $facets_maxrecs = C4::Context->preference('maxRecordsForFacets')||20; my @facets_loop; # stores the ref to array of hashes for template facets loop @@ -457,7 +453,7 @@ sub getRecords { ## Check if it's an index scan if ($scan) { - my ( $term, $occ ) = $results[ $i - 1 ]->term($j); + my ( $term, $occ ) = $results[ $i - 1 ]->display_term($j); # here we create a minimal MARC record and hand it off to the # template just like a normal result ... perhaps not ideal, but @@ -500,56 +496,13 @@ sub getRecords { } $results_hashref->{ $servers[ $i - 1 ] } = $results_hash; -# Fill the facets while we're looping, but only for the biblioserver and not for a scan + # Fill the facets while we're looping, but only for the + # biblioserver and not for a scan if ( !$scan && $servers[ $i - 1 ] =~ /biblioserver/ ) { - - my $jmax = - $size > $facets_maxrecs ? $facets_maxrecs : $size; - for my $facet (@$facets) { - for ( my $j = 0 ; $j < $jmax ; $j++ ) { - - my $marc_record = new_record_from_zebra ( - 'biblioserver', - $results[ $i - 1 ]->record($j)->raw() - ); - - if ( ! defined $marc_record ) { - warn "ERROR DECODING RECORD - $@: " . - $results[ $i - 1 ]->record($j)->raw(); - next; - } - - my @used_datas = (); - - foreach my $tag ( @{ $facet->{tags} } ) { - - # avoid first line - my $tag_num = substr( $tag, 0, 3 ); - my $subfield_letters = substr( $tag, 3 ); - # Removed when as_string fixed - my @subfields = $subfield_letters =~ /./sg; - - my @fields = $marc_record->field($tag_num); - foreach my $field (@fields) { - my $data = $field->as_string( $subfield_letters, $facet->{sep} ); - - unless ( $data ~~ @used_datas ) { - push @used_datas, $data; - $facets_counter->{ $facet->{idx} }->{$data}++; - } - } # fields - } # field codes - } # records - $facets_info->{ $facet->{idx} }->{label_value} = - $facet->{label}; - $facets_info->{ $facet->{idx} }->{expanded} = - $facet->{expanded}; - } # facets + $facets_counter = GetFacets( $results[ $i - 1 ] ); + $facets_info = _get_facets_info( $facets ); } - # warn "connection ", $i-1, ": $size hits"; - # warn $results[$i-1]->record(0)->render() if $size > 0; - # BUILD FACETS if ( $servers[ $i - 1 ] =~ /biblioserver/ ) { for my $link_value ( @@ -617,7 +570,7 @@ sub getRecords { { $facet_label_value = $itemtypes->{$one_facet} - ->{'description'}; + ->{translated_description}; } } @@ -674,6 +627,217 @@ sub getRecords { return ( undef, $results_hashref, \@facets_loop ); } +sub GetFacets { + + my $rs = shift; + my $facets; + + my $indexing_mode = C4::Context->config('zebra_bib_index_mode') // 'dom'; + my $use_zebra_facets = C4::Context->config('use_zebra_facets') // 0; + + if ( $indexing_mode eq 'dom' && + $use_zebra_facets ) { + $facets = _get_facets_from_zebra( $rs ); + } else { + $facets = _get_facets_from_records( $rs ); + } + + return $facets; +} + +sub _get_facets_from_records { + + my $rs = shift; + + my $facets_maxrecs = C4::Context->preference('maxRecordsForFacets') // 20; + my $facets_config = getFacets(); + my $facets = {}; + my $size = $rs->size(); + my $jmax = $size > $facets_maxrecs + ? $facets_maxrecs + : $size; + + for ( my $j = 0 ; $j < $jmax ; $j++ ) { + + my $marc_record = new_record_from_zebra ( + 'biblioserver', + $rs->record( $j )->raw() + ); + + if ( ! defined $marc_record ) { + warn "ERROR DECODING RECORD - $@: " . + $rs->record( $j )->raw(); + next; + } + + _get_facets_data_from_record( $marc_record, $facets_config, $facets ); + } + + return $facets; +} + +=head2 _get_facets_data_from_record + + C4::Search::_get_facets_data_from_record( $marc_record, $facets, $facets_counter ); + +Internal function that extracts facets information from a MARC::Record object +and populates $facets_counter for using in getRecords. + +$facets is expected to be filled with C4::Koha::getFacets output (i.e. the configured +facets for Zebra). + +=cut + +sub _get_facets_data_from_record { + + my ( $marc_record, $facets, $facets_counter ) = @_; + + for my $facet (@$facets) { + + my @used_datas = (); + + foreach my $tag ( @{ $facet->{ tags } } ) { + + # tag number is the first three digits + my $tag_num = substr( $tag, 0, 3 ); + # subfields are the remainder + my $subfield_letters = substr( $tag, 3 ); + + my @fields = $marc_record->field( $tag_num ); + foreach my $field (@fields) { + # If $field->indicator(1) eq 'z', it means it is a 'see from' + # field introduced because of IncludeSeeFromInSearches, so skip it + next if $field->indicator(1) eq 'z'; + + my $data = $field->as_string( $subfield_letters, $facet->{ sep } ); + + unless ( grep { /^\Q$data\E$/ } @used_datas ) { + push @used_datas, $data; + $facets_counter->{ $facet->{ idx } }->{ $data }++; + } + } + } + } +} + +=head2 _get_facets_from_zebra + + my $facets = _get_facets_from_zebra( $result_set ) + +Retrieves facets for a specified result set. It loops through the facets defined +in C4::Koha::getFacets and returns a hash with the following structure: + + { facet_idx => { + facet_value => count + }, + ... + } + +=cut + +sub _get_facets_from_zebra { + + my $rs = shift; + + # save current elementSetName + my $elementSetName = $rs->option( 'elementSetName' ); + + my $facets_loop = getFacets(); + my $facets_data = {}; + # loop through defined facets and fill the facets hashref + foreach my $facet ( @$facets_loop ) { + + my $idx = $facet->{ idx }; + my $sep = $facet->{ sep }; + my $facet_values = _get_facet_from_result_set( $idx, $rs, $sep ); + if ( $facet_values ) { + # we've actually got a result + $facets_data->{ $idx } = $facet_values; + } + } + # set elementSetName to its previous value to avoid side effects + $rs->option( elementSetName => $elementSetName ); + + return $facets_data; +} + +=head2 _get_facet_from_result_set + + my $facet_values = + C4::Search::_get_facet_from_result_set( $facet_idx, $result_set, $sep ) + +Internal function that extracts facet information for a specific index ($facet_idx) and +returns a hash containing facet values and count: + + { + $facet_value => $count , + ... + } + +Warning: this function has the side effect of changing the elementSetName for the result +set. It is a helper function for the main loop, which takes care of backing it up for +restoring. + +=cut + +sub _get_facet_from_result_set { + + my $facet_idx = shift; + my $rs = shift; + my $sep = shift; + + my $internal_sep = '<*>'; + my $facetMaxCount = C4::Context->preference('FacetMaxCount') // 20; + + return if ( ! defined $facet_idx || ! defined $rs ); + # zebra's facet element, untokenized index + my $facet_element = 'zebra::facet::' . $facet_idx . ':0:' . $facetMaxCount; + # configure zebra results for retrieving the desired facet + $rs->option( elementSetName => $facet_element ); + # get the facet record from result set + my $facet = $rs->record( 0 )->raw; + # if the facet has no restuls... + return if !defined $facet; + # TODO: benchmark DOM vs. SAX performance + my $facet_dom = XML::LibXML->load_xml( + string => ($facet) + ); + my @terms = $facet_dom->getElementsByTagName('term'); + return if ! @terms; + + my $facets = {}; + foreach my $term ( @terms ) { + my $facet_value = $term->textContent; + $facet_value =~ s/\Q$internal_sep\E/$sep/ if defined $sep; + $facets->{ $facet_value } = $term->getAttribute( 'occur' ); + } + + return $facets; +} + +=head2 _get_facets_info + + my $facets_info = C4::Search::_get_facets_info( $facets ) + +Internal function that extracts facets information and properly builds +the data structure needed to render facet labels. + +=cut + +sub _get_facets_info { + + my $facets = shift; + + my $facets_info = {}; + + for my $facet ( @$facets ) { + $facets_info->{ $facet->{ idx } }->{ label_value } = $facet->{ label }; + $facets_info->{ $facet->{ idx } }->{ expanded } = $facet->{ expanded }; + } + + return $facets_info; +} + sub pazGetRecords { my ( $koha_query, $simple_query, $sort_by_ref, $servers_ref, @@ -757,7 +921,7 @@ sub _remove_stopwords { # remove stopwords from operand : parse all stopwords & remove them (case insensitive) # we use IsAlpha unicode definition, to deal correctly with diacritics. -# otherwise, a French word like "leçon" woudl be split into "le" "çon", "le" +# otherwise, a French word like "leçon" would be split into "le" "çon", "le" # is a stopword, we'd get "çon" and wouldn't find anything... # foreach ( keys %{ C4::Context->stopwords } ) { @@ -962,11 +1126,13 @@ sub getIndexes{ 'Corporate-name-seealso', 'Country-publication', 'ctype', + 'curriculum', 'date-entered-on-file', 'Date-of-acquisition', 'Date-of-publication', 'Dewey-classification', 'Dissertation-information', + 'diss', 'EAN', 'extent', 'fic', @@ -982,6 +1148,8 @@ sub getIndexes{ 'Host-item', 'id-other', 'Illustration-code', + 'Index-term-genre', + 'Index-term-uncontrolled', 'ISBN', 'isbn', 'ISSN', @@ -995,8 +1163,11 @@ sub getIndexes{ 'lc-card', 'LC-card-number', 'lcn', + 'lex', 'llength', 'ln', + 'ln-audio', + 'ln-subtitle', 'Local-classification', 'Local-number', 'Match-heading', @@ -1027,6 +1198,8 @@ sub getIndexes{ 'popularity', 'pubdate', 'Publisher', + 'Provider', + 'pv', 'Record-control-number', 'rcn', 'Record-type', @@ -1307,17 +1480,17 @@ sub buildQuery { if ( @limits ) { $q .= ' and '.join(' and ', @limits); } - return ( undef, $q, $q, "q=ccl=".uri_escape($q), $q, '', '', '', '', 'ccl' ); + return ( undef, $q, $q, "q=ccl=".uri_escape_utf8($q), $q, '', '', '', '', 'ccl' ); } if ( $query =~ /^cql=/ ) { - return ( undef, $', $', "q=cql=".uri_escape($'), $', '', '', '', '', 'cql' ); + return ( undef, $', $', "q=cql=".uri_escape_utf8($'), $', '', '', '', '', 'cql' ); } if ( $query =~ /^pqf=/ ) { if ($query_desc) { - $query_cgi = "q=".uri_escape($query_desc); + $query_cgi = "q=".uri_escape_utf8($query_desc); } else { $query_desc = $'; - $query_cgi = "q=pqf=".uri_escape($'); + $query_cgi = "q=pqf=".uri_escape_utf8($'); } return ( undef, $', $', $query_cgi, $query_desc, '', '', '', '', 'pqf' ); } @@ -1366,17 +1539,28 @@ sub buildQuery { my $index = $indexes[$i]; # Add index-specific attributes + + #Afaik, this 'yr' condition will only ever be met in the staff client 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. + # Date of Publication - if ( $index eq 'yr' ) { - $index .= ",st-numeric"; - $indexes_set++; + if ( $index =~ /yr/ ) { + #weight_fields/relevance search causes errors with date ranges + #In the case of YYYY-, it will only return records with a 'yr' of YYYY (not the range) + #In the case of YYYY-YYYY, it will return no results $stemming = $auto_truncation = $weight_fields = $fuzzy_enabled = $remove_stopwords = 0; } # Date of Acquisition - elsif ( $index eq 'acqdate' ) { - $index .= ",st-date-normalized"; - $indexes_set++; + elsif ( $index =~ /acqdate/ ) { + #stemming and auto_truncation would have zero impact since it already is YYYY-MM-DD format + #Weight_fields probably SHOULD be turned OFF, otherwise you'll get records floating to the + #top of the results just because they have lots of item records matching that date. + #Fuzzy actually only applies during _build_weighted_query, and is reset there anyway, so + #irrelevant here + #remove_stopwords doesn't function anymore so is irrelevant $stemming = $auto_truncation = $weight_fields = $fuzzy_enabled = $remove_stopwords = 0; } # ISBN,ISSN,Standard Number, don't need special treatment @@ -1387,6 +1571,13 @@ sub buildQuery { $remove_stopwords ) = ( 0, 0, 0, 0, 0 ); + if ( $index eq 'nb' ) { + if ( C4::Context->preference("SearchWithISBNVariations") ) { + my @isbns = C4::Koha::GetVariationsOfISBN( $operand ); + $operands[$i] = $operand = '(nb=' . join(' OR nb=', @isbns) . ')'; + $indexes[$i] = $index = ''; + } + } } if(not $index){ @@ -1481,43 +1672,19 @@ sub buildQuery { warn "FIELD WEIGHTED OPERAND: >$weighted_operand<" if $DEBUG; - # If there's a previous operand, we need to add an operator - if ($previous_operand) { - - # User-specified operator - if ( $operators[ $i - 1 ] ) { - $query .= " $operators[$i-1] "; - $query .= " $index_plus " unless $indexes_set; - $query .= " $operand"; - $query_cgi .= "&op=".uri_escape($operators[$i-1]); - $query_cgi .= "&idx=".uri_escape($index) if $index; - $query_cgi .= "&q=".uri_escape($operands[$i]) if $operands[$i]; - $query_desc .= - " $operators[$i-1] $index_plus $operands[$i]"; - } - - # Default operator is and - else { - $query .= " and "; - $query .= "$index_plus " unless $indexes_set; - $query .= "$operand"; - $query_cgi .= "&op=and&idx=".uri_escape($index) if $index; - $query_cgi .= "&q=".uri_escape($operands[$i]) if $operands[$i]; - $query_desc .= " and $index_plus $operands[$i]"; - } - } + ($query,$query_cgi,$query_desc,$previous_operand) = _build_initial_query({ + query => $query, + query_cgi => $query_cgi, + query_desc => $query_desc, + operator => ($operators[ $i - 1 ]) ? $operators[ $i - 1 ] : '', + parsed_operand => $operand, + original_operand => ($operands[$i]) ? $operands[$i] : '', + index => $index, + index_plus => $index_plus, + indexes_set => $indexes_set, + previous_operand => $previous_operand, + }); - # There isn't a pervious operand, don't need an operator - else { - - # Field-weighted queries already have indexes set - $query .= " $index_plus " unless $indexes_set; - $query .= $operand; - $query_desc .= " $index_plus $operands[$i]"; - $query_cgi .= "&idx=".uri_escape($index) if $index; - $query_cgi .= "&q=".uri_escape($operands[$i]) if $operands[$i]; - $previous_operand = 1; - } } #/if $operands } # /for } @@ -1546,13 +1713,13 @@ sub buildQuery { 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."\""; + $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=$this_limit"; + $limit_cgi .= "&limit=" . uri_escape_utf8($this_limit); $limit_desc .= " $this_limit"; } @@ -1560,7 +1727,7 @@ sub buildQuery { else { $limit .= " and " if $limit || $query; $limit .= "$this_limit"; - $limit_cgi .= "&limit=$this_limit"; + $limit_cgi .= "&limit=" . uri_escape_utf8($this_limit); if ($this_limit =~ /^branch:(.+)/) { my $branchcode = $1; my $branchname = GetBranchName($branchcode); @@ -1587,9 +1754,13 @@ sub buildQuery { # This is flawed , means we can't search anything with : in it # if user wants to do ccl or cql, start the query with that # $query =~ s/:/=/g; + #NOTE: We use several several different regexps here as you can't have variable length lookback assertions $query =~ s/(?<=(ti|au|pb|su|an|kw|mc|nb|ns)):/=/g; $query =~ s/(?<=(wrdl)):/=/g; $query =~ s/(?<=(trn|phr)):/=/g; + $query =~ s/(?<=(st-numeric)):/=/g; + $query =~ s/(?<=(st-year)):/=/g; + $query =~ s/(?<=(st-date-normalized)):/=/g; $limit =~ s/:/=/g; for ( $query, $query_desc, $limit, $limit_desc ) { s/ +/ /g; # remove extra spaces @@ -1622,6 +1793,42 @@ sub buildQuery { ); } +=head2 _build_initial_query + + ($query, $query_cgi, $query_desc, $previous_operand) = _build_initial_query($initial_query_params); + + Build a section of the initial query containing indexes, operators, and operands. + +=cut + +sub _build_initial_query { + my ($params) = @_; + + 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 '; + } + + #NOTE: indexes_set is typically set when doing truncation or field weighting + my $operand = ($params->{indexes_set}) ? $params->{parsed_operand} : $params->{index_plus}.$params->{parsed_operand}; + + #e.g. "kw,wrdl:test" + #e.g. " and kw,wrdl:test" + $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}; + + #e.g. " and kw,wrdl: test" + $params->{query_desc} .= $operator . $params->{index_plus} . " " . $params->{original_operand}; + + $params->{previous_operand} = 1 unless $params->{previous_operand}; #If there is no previous operand, mark this as one + + return ($params->{query}, $params->{query_cgi}, $params->{query_desc}, $params->{previous_operand}); +} + =head2 searchResults my @search_results = searchResults($search_context, $searchdesc, $hits, @@ -1665,28 +1872,16 @@ sub searchResults { # get notforloan authorised value list (see $shelflocations FIXME) my $notforloan_authorised_value = GetAuthValCode('items.notforloan',''); - #Build itemtype hash - #find itemtype & itemtype image - my %itemtypes; - $bsth = - $dbh->prepare( - "SELECT itemtype,description,imageurl,summary,notforloan FROM itemtypes" - ); - $bsth->execute(); - while ( my $bdata = $bsth->fetchrow_hashref ) { - foreach (qw(description imageurl summary notforloan)) { - $itemtypes{ $bdata->{'itemtype'} }->{$_} = $bdata->{$_}; - } - } + #Get itemtype hash + my %itemtypes = %{ GetItemTypes() }; #search item field code my ($itemtag, undef) = &GetMarcFromKohaField( "items.itemnumber", "" ); ## find column names of items related to MARC - my $sth2 = $dbh->prepare("SHOW COLUMNS FROM items"); - $sth2->execute; my %subfieldstosearch; - while ( ( my $column ) = $sth2->fetchrow ) { + my @columns = Koha::Database->new()->schema()->resultset('Item')->result_source->columns; + for my $column ( @columns ) { my ( $tagfield, $tagsubfield ) = &GetMarcFromKohaField( "items." . $column, "" ); if ( defined $tagsubfield ) { @@ -1732,6 +1927,8 @@ sub searchResults { : $bibliotag < 10 ? GetFrameworkCode($marcrecord->field($bibliotag)->data) : GetFrameworkCode($marcrecord->subfield($bibliotag,$bibliosubf)); + + SetUTF8Flag($marcrecord); my $oldbiblio = TransformMarcToKoha( $dbh, $marcrecord, $fw ); $oldbiblio->{subtitle} = GetRecordValue('subtitle', $marcrecord, $fw); $oldbiblio->{result_number} = $i + 1; @@ -1748,7 +1945,7 @@ sub searchResults { # edition information, if any $oldbiblio->{edition} = $oldbiblio->{editionstatement}; - $oldbiblio->{description} = $itemtypes{ $oldbiblio->{itemtype} }->{description}; + $oldbiblio->{description} = $itemtypes{ $oldbiblio->{itemtype} }->{translated_description}; # Build summary if there is one (the summary is defined in the itemtypes table) # FIXME: is this used anywhere, I think it can be commented out? -- JF if ( $itemtypes{ $oldbiblio->{itemtype} }->{summary} ) { @@ -1782,12 +1979,7 @@ sub searchResults { if($marcrecord->field($1)){ my @repl = $marcrecord->field($1)->subfield($2); my $subfieldvalue = $repl[$i]; - - if (! utf8::is_utf8($subfieldvalue)) { - utf8::decode($subfieldvalue); - } - - $newline =~ s/\[$tag\]/$subfieldvalue/g; + $newline =~ s/\[$tag\]/$subfieldvalue/g; } } $newsummary .= "$newline\n"; @@ -1851,6 +2043,7 @@ sub searchResults { my $item_in_transit_count = 0; my $can_place_holds = 0; my $item_onhold_count = 0; + my $notforloan_count = 0; my $items_count = scalar(@fields); my $maxitems_pref = C4::Context->preference('maxItemsinSearchResults'); my $maxitems = $maxitems_pref ? $maxitems_pref - 1 : 1; @@ -1864,7 +2057,7 @@ sub searchResults { foreach my $code ( keys %subfieldstosearch ) { $item->{$code} = $field->subfield( $subfieldstosearch{$code} ); } - $item->{description} = $itemtypes{ $item->{itype} }{description}; + $item->{description} = $itemtypes{ $item->{itype} }{translated_description}; # OPAC hidden items if ($is_opac) { @@ -1882,8 +2075,8 @@ sub searchResults { } } - my $hbranch = C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch' ? 'homebranch' : 'holdingbranch'; - my $otherbranch = C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch' ? 'holdingbranch' : 'homebranch'; + my $hbranch = C4::Context->preference('StaffSearchResultsDisplayBranch'); + my $otherbranch = $hbranch eq 'homebranch' ? 'holdingbranch' : 'homebranch'; # set item's branch name, use HomeOrHoldingBranch syspref first, fall back to the other one if ($item->{$hbranch}) { @@ -1896,21 +2089,26 @@ sub searchResults { my $prefix = $item->{$hbranch} . '--' . $item->{location} . $item->{itype} . $item->{itemcallnumber}; # For each grouping of items (onloan, available, unavailable), we build a key to store relevant info about that item my $userenv = C4::Context->userenv; - if ( $item->{onloan} && !(C4::Members::GetHideLostItemsPreference($userenv->{'number'}) && $item->{itemlost}) ) { + if ( $item->{onloan} + && !( C4::Members::GetHideLostItemsPreference( $userenv->{'number'} ) && $item->{itemlost} ) ) + { $onloan_count++; - my $key = $prefix . $item->{onloan} . $item->{barcode}; - $onloan_items->{$key}->{due_date} = format_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}->{itemcallnumber} = $item->{itemcallnumber}; - $onloan_items->{$key}->{description} = $item->{description}; - $onloan_items->{$key}->{imageurl} = getitemtypeimagelocation( $search_context, $itemtypes{ $item->{itype} }->{imageurl} ); + my $key = $prefix . $item->{onloan} . $item->{barcode}; + $onloan_items->{$key}->{due_date} = format_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}->{itemcallnumber} = $item->{itemcallnumber}; + $onloan_items->{$key}->{description} = $item->{description}; + $onloan_items->{$key}->{imageurl} = + getitemtypeimagelocation( $search_context, $itemtypes{ $item->{itype} }->{imageurl} ); + # if something's checked out and lost, mark it as 'long overdue' if ( $item->{itemlost} ) { - $onloan_items->{$prefix}->{longoverdue}++; + $onloan_items->{$key}->{longoverdue}++; $longoverdue_count++; - } else { # can place holds as long as item isn't lost + } + else { # can place holds as long as item isn't lost $can_place_holds = 1; } } @@ -1918,9 +2116,13 @@ sub searchResults { # items not on loan, but still unavailable ( lost, withdrawn, damaged ) else { + $item->{notforloan}=1 if !$item->{notforloan} && $itemtypes{ C4::Context->preference("item-level_itypes")? $item->{itype}: $oldbiblio->{itemtype} }->{notforloan}; + # item is on order if ( $item->{notforloan} < 0 ) { $ordered_count++; + } elsif ( $item->{notforloan} > 0 ) { + $notforloan_count++; } # is item in transit? @@ -1949,7 +2151,7 @@ sub searchResults { # should map transit status to record indexed in Zebra. # ($transfertwhen, $transfertfrom, $transfertto) = C4::Circulation::GetTransfers($item->{itemnumber}); - $reservestatus = C4::Reserves::GetReserveStatus( $item->{itemnumber}, $oldbiblio->{biblionumber} ); + $reservestatus = C4::Reserves::GetReserveStatus( $item->{itemnumber} ); } # item is withdrawn, lost, damaged, not for loan, reserved or in transit @@ -2027,13 +2229,10 @@ sub searchResults { } # XSLT processing of some stuff - use C4::Charset; - SetUTF8Flag($marcrecord); - warn $marcrecord->as_formatted if $DEBUG; - my $interface = $search_context eq 'opac' ? 'OPAC' : ''; - if (!$scan && C4::Context->preference($interface . "XSLTResultsDisplay")) { + my $interface = $search_context eq 'opac' ? 'OPAC' : ''; + if (!$scan && C4::Context->preference($interface . "XSLTResultsDisplay")) { $oldbiblio->{XSLTResultsRecord} = XSLTParse4Display($oldbiblio->{biblionumber}, $marcrecord, $interface."XSLTResultsDisplay", 1, \@hiddenitems); - # the last parameter tells Koha to clean up the problematic ampersand entities that Zebra outputs + # the last parameter tells Koha to clean up the problematic ampersand entities that Zebra outputs } # if biblio level itypes are used and itemtype is notforloan, it can't be reserved either @@ -2060,6 +2259,7 @@ sub searchResults { $oldbiblio->{intransitcount} = $item_in_transit_count; $oldbiblio->{onholdcount} = $item_onhold_count; $oldbiblio->{orderedcount} = $ordered_count; + $oldbiblio->{notforloancount} = $notforloan_count; if (C4::Context->preference("AlternateHoldingsField") && $items_count == 0) { my $fieldspec = C4::Context->preference("AlternateHoldingsField"); @@ -2217,35 +2417,6 @@ sub enabled_staff_search_views ); } -sub AddSearchHistory{ - my ($borrowernumber,$session,$query_desc,$query_cgi, $total)=@_; - my $dbh = C4::Context->dbh; - - # Add the request the user just made - my $sql = "INSERT INTO search_history(userid, sessionid, query_desc, query_cgi, total, time) VALUES(?, ?, ?, ?, ?, NOW())"; - my $sth = $dbh->prepare($sql); - $sth->execute($borrowernumber, $session, $query_desc, $query_cgi, $total); - return $dbh->last_insert_id(undef, 'search_history', undef,undef,undef); -} - -sub GetSearchHistory{ - my ($borrowernumber,$session)=@_; - my $dbh = C4::Context->dbh; - - # Add the request the user just made - my $query = "SELECT FROM search_history WHERE (userid=? OR sessionid=?)"; - my $sth = $dbh->prepare($query); - $sth->execute($borrowernumber, $session); - return $sth->fetchall_hashref({}); -} - -sub PurgeSearchHistory{ - my ($pSearchhistory)=@_; - my $dbh = C4::Context->dbh; - my $sth = $dbh->prepare("DELETE FROM search_history WHERE time < DATE_SUB( NOW(), INTERVAL ? DAY )"); - $sth->execute($pSearchhistory) or die $dbh->errstr; -} - =head2 z3950_search_args $arrayref = z3950_search_args($matchpoints) @@ -2413,7 +2584,7 @@ sub new_record_from_zebra { my $raw_data = shift; # Set the default indexing modes my $index_mode = ( $server eq 'biblioserver' ) - ? C4::Context->config('zebra_bib_index_mode') // 'grs1' + ? C4::Context->config('zebra_bib_index_mode') // 'dom' : C4::Context->config('zebra_auth_index_mode') // 'dom'; my $marc_record = eval {