X-Git-Url: http://koha-dev.rot13.org:8081/gitweb/?a=blobdiff_plain;f=C4%2FSearch.pm;h=5d6816557498bb66df5285d271c0ae1dd80c7d41;hb=4fcf7af117153690cc4aca55eb47290dfc5814f6;hp=a23fe135b5cec74aa61e5c81fc7a34cc3a64a739;hpb=d3184fa85b8906d0921522ce0c26dab98a4a2778;p=koha-ffzg.git diff --git a/C4/Search.pm b/C4/Search.pm index a23fe135b5..5d68165574 100644 --- a/C4/Search.pm +++ b/C4/Search.pm @@ -15,32 +15,32 @@ package C4::Search; # 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 +use Modern::Perl; require Exporter; use C4::Context; use C4::Biblio; # GetMarcFromKohaField, GetBiblioData use C4::Koha; # getFacets use Koha::DateUtils; +use Koha::Libraries; use Lingua::Stem; use C4::Search::PazPar2; use XML::Simple; -use C4::Members qw(GetHideLostItemsPreference); use C4::XSLT; -use C4::Branch; use C4::Reserves; # GetReserveStatus use C4::Debug; use C4::Charset; +use Koha::AuthorisedValues; +use Koha::ItemTypes; +use Koha::Libraries; +use Koha::Patrons; use YAML; use URI::Escape; use Business::ISBN; use MARC::Record; use MARC::Field; -use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $DEBUG); +use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $DEBUG); -# set the version for version checking BEGIN { - $VERSION = 3.07.00.049; $DEBUG = ($ENV{DEBUG}) ? 1 : 0; } @@ -84,7 +84,7 @@ This function attempts to find duplicate records using a hard-coded, fairly simp sub FindDuplicate { my ($record) = @_; my $dbh = C4::Context->dbh; - my $result = TransformMarcToKoha( $dbh, $record, '' ); + my $result = TransformMarcToKoha( $record, '' ); my $sth; my $query; my $search; @@ -146,7 +146,7 @@ sub FindDuplicate { $possible_duplicate_record ); - my $result = TransformMarcToKoha( $dbh, $marcrecord, '' ); + my $result = TransformMarcToKoha( $marcrecord, '' ); # FIXME :: why 2 $biblionumber ? if ($result) { @@ -160,7 +160,7 @@ sub FindDuplicate { =head2 SimpleSearch -( $error, $results, $total_hits ) = SimpleSearch( $query, $offset, $max_results, [@servers] ); +( $error, $results, $total_hits ) = SimpleSearch( $query, $offset, $max_results, [@servers], [%options] ); This function provides a simple search API on the bibliographic catalog @@ -172,6 +172,7 @@ This function provides a simple search API on the bibliographic catalog * @servers is optional. Defaults to biblioserver as found in koha-conf.xml * $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. + * %options is optional. (e.g. "skip_normalize" allows you to skip changing : to = ) =item C @@ -202,7 +203,7 @@ my @results; for my $r ( @{$marcresults} ) { my $marcrecord = MARC::File::USMARC::decode($r); - my $biblio = TransformMarcToKoha(C4::Context->dbh,$marcrecord,q{}); + my $biblio = TransformMarcToKoha($marcrecord,q{}); #build the iarray of hashs for the template. push @results, { @@ -221,7 +222,7 @@ $template->param(result=>\@results); =cut sub SimpleSearch { - my ( $query, $offset, $max_results, $servers ) = @_; + my ( $query, $offset, $max_results, $servers, %options ) = @_; return ( 'No query entered', undef, undef ) unless $query; # FIXME hardcoded value. See catalog/search.pl & opac-search.pl too. @@ -243,12 +244,12 @@ sub SimpleSearch { eval { $zconns[$i] = C4::Context->Zconn( $servers[$i], 1 ); if ($QParser) { - $query =~ s/=/:/g; + $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; + $query =~ s/:/=/g unless $options{skip_normalize}; $zoom_queries[$i] = new ZOOM::Query::CCL2RPN( $query, $zconns[$i]); } $tmpresults[$i] = $zconns[$i]->search( $zoom_queries[$i] ); @@ -310,26 +311,27 @@ sub SimpleSearch { ( undef, $results_hashref, \@facets_loop ) = getRecords ( $koha_query, $simple_query, $sort_by_ref, $servers_ref, - $results_per_page, $offset, $expanded_facet, $branches,$itemtypes, - $query_type, $scan + $results_per_page, $offset, $branches, $itemtypes, + $query_type, $scan, $opac ); The all singing, all dancing, multi-server, asynchronous, scanning, searching, record nabbing, facet-building -See verbse embedded documentation. +See verbose embedded documentation. =cut sub getRecords { my ( $koha_query, $simple_query, $sort_by_ref, $servers_ref, - $results_per_page, $offset, $expanded_facet, $branches, - $itemtypes, $query_type, $scan, $opac + $results_per_page, $offset, $branches, $itemtypes, + $query_type, $scan, $opac ) = @_; my @servers = @$servers_ref; my @sort_by = @$sort_by_ref; + $offset = 0 if $offset < 0; # Initialize variables for the ZOOM connection and results object my $zconn; @@ -337,6 +339,9 @@ sub getRecords { 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 }; + # Initialize variables for the faceted results objects my $facets_counter = {}; my $facets_info = {}; @@ -510,8 +515,6 @@ sub getRecords { keys %$facets_counter ) { - my $expandable; - my $number_of_facets; my @this_facets_array; for my $one_facet ( sort { @@ -521,87 +524,80 @@ sub getRecords { } keys %{ $facets_counter->{$link_value} } ) { - $number_of_facets++; - if ( ( $number_of_facets <= 5 ) - || ( $expanded_facet eq $link_value ) - || ( $facets_info->{$link_value}->{'expanded'} ) - ) - { - # Sanitize the link value : parenthesis, question and exclamation mark will cause errors with CCL - my $facet_link_value = $one_facet; - $facet_link_value =~ s/[()!?¡¿؟]/ /g; - - # fix the length that will display in the label, - my $facet_label_value = $one_facet; - my $facet_max_length = C4::Context->preference( - 'FacetLabelTruncationLength') - || 20; - $facet_label_value = - substr( $one_facet, 0, $facet_max_length ) - . "..." - if length($facet_label_value) > - $facet_max_length; - - # if it's a branch, label by the name, not the code, - if ( $link_value =~ /branch/ ) { - if ( defined $branches - && ref($branches) eq "HASH" - && defined $branches->{$one_facet} - && ref( $branches->{$one_facet} ) eq - "HASH" ) - { - $facet_label_value = - $branches->{$one_facet} - ->{'branchname'}; - } - else { - $facet_label_value = "*"; - } + my $facet_link_value = $one_facet; + $facet_link_value =~ s/[()!?¡¿؟]/ /g; + + # fix the length that will display in the label, + my $facet_label_value = $one_facet; + my $facet_max_length = C4::Context->preference( + 'FacetLabelTruncationLength') + || 20; + $facet_label_value = + substr( $one_facet, 0, $facet_max_length ) + . "..." + if length($facet_label_value) > + $facet_max_length; + + # if it's a branch, label by the name, not the code, + if ( $link_value =~ /branch/ ) { + if ( defined $branches + && ref($branches) eq "HASH" + && defined $branches->{$one_facet} + && ref( $branches->{$one_facet} ) eq + "HASH" ) + { + $facet_label_value = + $branches->{$one_facet} + ->{'branchname'}; } - - # if it's a itemtype, label by the name, not the code, - if ( $link_value =~ /itype/ ) { - if ( defined $itemtypes - && ref($itemtypes) eq "HASH" - && defined $itemtypes->{$one_facet} - && ref( $itemtypes->{$one_facet} ) eq - "HASH" ) - { - $facet_label_value = - $itemtypes->{$one_facet} - ->{translated_description}; - } + else { + $facet_label_value = "*"; } + } - # also, if it's a location code, use the name instead of the code - if ( $link_value =~ /location/ ) { + # if it's a itemtype, label by the name, not the code, + if ( $link_value =~ /itype/ ) { + if ( defined $itemtypes + && ref($itemtypes) eq "HASH" + && defined $itemtypes->{$one_facet} + && ref( $itemtypes->{$one_facet} ) eq + "HASH" ) + { $facet_label_value = - GetKohaAuthorisedValueLib( 'LOC', - $one_facet, $opac ); + $itemtypes->{$one_facet} + ->{translated_description}; } + } - # but we're down with the whole label being in the link's title. - push @this_facets_array, - { - facet_count => - $facets_counter->{$link_value} - ->{$one_facet}, - facet_label_value => $facet_label_value, - facet_title_value => $one_facet, - facet_link_value => $facet_link_value, - type_link_value => $link_value, - } - if ($facet_label_value); + # also, if it's a location code, use the name instead of the code + if ( $link_value =~ /location/ ) { + # TODO Retrieve all authorised values at once, instead of 1 query per entry + my $av = Koha::AuthorisedValues->search({ category => 'LOC', authorised_value => $one_facet }); + $facet_label_value = $av->count ? $av->next->opac_description : ''; } - } - # handle expanded option - unless ( $facets_info->{$link_value}->{'expanded'} ) { - $expandable = 1 - if ( ( $number_of_facets > 5 ) - && ( $expanded_facet ne $link_value ) ); + # also, if it's a collection code, use the name instead of the code + if ( $link_value =~ /ccode/ ) { + # TODO Retrieve all authorised values at once, instead of 1 query per entry + my $av = Koha::AuthorisedValues->search({ category => 'CCODE', authorised_value => $one_facet }); + $facet_label_value = $av->count ? $av->next->opac_description : ''; + } + + # but we're down with the whole label being in the link's title. + push @this_facets_array, + { + facet_count => + $facets_counter->{$link_value} + ->{$one_facet}, + facet_label_value => $facet_label_value, + facet_title_value => $one_facet, + facet_link_value => $facet_link_value, + type_link_value => $link_value, + } + if ($facet_label_value); } + push @facets_loop, { type_link_value => $link_value, @@ -610,20 +606,27 @@ sub getRecords { . $facets_info->{$link_value}->{'label_value'} => 1, facets => \@this_facets_array, - expandable => $expandable, - expand => $link_value, } unless ( ( $facets_info->{$link_value}->{'label_value'} =~ /Libraries/ ) - and ( C4::Context->preference('singleBranchMode') ) + and ( Koha::Libraries->search->count == 1 ) ); } } } ); + + # 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} } ]; + } + @facets_loop = sort {$a->{expand} cmp $b->{expand}} @facets_loop; + } + return ( undef, $results_hashref, \@facets_loop ); } @@ -632,11 +635,9 @@ 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 ) { + if ( $use_zebra_facets ) { $facets = _get_facets_from_zebra( $rs ); } else { $facets = _get_facets_from_records( $rs ); @@ -832,7 +833,6 @@ sub _get_facets_info { for my $facet ( @$facets ) { $facets_info->{ $facet->{ idx } }->{ label_value } = $facet->{ label }; - $facets_info->{ $facet->{ idx } }->{ expanded } = $facet->{ expanded }; } return $facets_info; @@ -841,9 +841,10 @@ sub _get_facets_info { sub pazGetRecords { my ( $koha_query, $simple_query, $sort_by_ref, $servers_ref, - $results_per_page, $offset, $expanded_facet, $branches, - $query_type, $scan + $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(); @@ -947,6 +948,9 @@ sub _build_stemmed_operand { require Lingua::Stem::Snowball ; my $stemmed_operand=q{}; + # Stemmer needs language + return $operand unless $lang; + # If operand contains a digit, it is almost certainly an identifier, and should # not be stemmed. This is particularly relevant for ISBNs and ISSNs, which # can contain the letter "X" - for example, _build_stemmend_operand would reduce @@ -1021,6 +1025,11 @@ sub _build_weighted_query { $weighted_query .= "an=\"$operand\""; } + # If the index is numeric, don't autoquote it. + elsif ( $index =~ /,st-numeric$/ ) { + $weighted_query .= " $index=$operand"; + } + # If the index already has more than one qualifier, wrap the operand # in quotes and pass it back (assumption is that the user knows what they # are doing and won't appreciate us mucking up their query @@ -1060,6 +1069,8 @@ sub getIndexes{ 'an', 'Any', 'at', + 'arl', + 'arp', 'au', 'aub', 'aud', @@ -1104,9 +1115,11 @@ sub getIndexes{ 'date-entered-on-file', 'Date-of-acquisition', 'Date-of-publication', + 'Date-time-last-modified', 'Dewey-classification', 'Dissertation-information', 'diss', + 'dtlm', 'EAN', 'extent', 'fic', @@ -1121,9 +1134,13 @@ sub getIndexes{ 'Heading-use-subject-added-entry', 'Host-item', 'id-other', + 'ident', + 'Identifier-standard', 'Illustration-code', 'Index-term-genre', 'Index-term-uncontrolled', + 'Interest-age-level', + 'Interest-grade-level', 'ISBN', 'isbn', 'ISSN', @@ -1138,6 +1155,7 @@ sub getIndexes{ 'LC-card-number', 'lcn', 'lex', + 'lexile-number', 'llength', 'ln', 'ln-audio', @@ -1161,6 +1179,7 @@ sub getIndexes{ 'notes', 'ns', 'nt', + 'Other-control-number', 'pb', 'Personal-name', 'Personal-name-heading', @@ -1174,6 +1193,7 @@ sub getIndexes{ 'Publisher', 'Provider', 'pv', + 'Reading-grade-level', 'Record-control-number', 'rcn', 'Record-type', @@ -1305,12 +1325,13 @@ sub _handle_exploding_index { ( $operators, $operands, $indexes, $limits, $sort_by, $scan, $lang ) = - buildQuery ( $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 { @@ -1448,10 +1469,19 @@ sub buildQuery { # 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 { /^available$/ } @limits ) { + $q =~ s| and \( \(allrecords,AlwaysMatches=''\) and \(not-onloan-count,st-numeric >= 1\) and \(lost,st-numeric=0\) \)||; + $original_q = $q; + } if ( @limits ) { - $q .= ' and '.join(' and ', @limits); + if ( grep { /^available$/ } @limits ) { + $q .= q| and ( (allrecords,AlwaysMatches='') and (not-onloan-count,st-numeric >= 1) and (lost,st-numeric=0) )|; + delete $limits['available']; + } + $q .= ' and '.join(' and ', @limits) if @limits; } - return ( undef, $q, $q, "q=ccl=".uri_escape_utf8($q), $q, '', '', '', 'ccl' ); + return ( undef, $q, $q, "q=ccl=".uri_escape_utf8($q), $original_q, '', '', '', 'ccl' ); } if ( $query =~ /^cql=/ ) { return ( undef, $', $', "q=cql=".uri_escape_utf8($'), $', '', '', '', 'cql' ); @@ -1492,7 +1522,7 @@ sub buildQuery { for ( my $i = 0 ; $i <= @operands ; $i++ ) { # COMBINE OPERANDS, INDEXES AND OPERATORS - if ( $operands[$i] ) { + if ( ($operands[$i] // '') ne '' ) { $operands[$i]=~s/^\s+//; # A flag to determine whether or not to add the index to the query @@ -1515,8 +1545,8 @@ sub buildQuery { #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 =~ /yr/ ) { + # Search ranges: Date of Publication, st-numeric + if ( $index =~ /(yr|st-numeric)/ ) { #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 @@ -1533,7 +1563,7 @@ sub buildQuery { $stemming = $auto_truncation = $weight_fields = $fuzzy_enabled = 0; } # ISBN,ISSN,Standard Number, don't need special treatment - elsif ( $index eq 'nb' || $index eq 'ns' ) { + elsif ( $index eq 'nb' || $index eq 'ns' || $index eq 'hi' ) { ( $stemming, $auto_truncation, $weight_fields, $fuzzy_enabled @@ -1637,7 +1667,7 @@ sub buildQuery { query_desc => $query_desc, operator => ($operators[ $i - 1 ]) ? $operators[ $i - 1 ] : '', parsed_operand => $operand, - original_operand => ($operands[$i]) ? $operands[$i] : '', + original_operand => $operands[$i] // '', index => $index, index_plus => $index_plus, indexes_set => $indexes_set, @@ -1660,7 +1690,7 @@ sub buildQuery { ## 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='' not onloan,AlwaysMatches='') and (lost,st-numeric=0) )"; #or ( allrecords,AlwaysMatches='' not lost,AlwaysMatches='')) )"; +"( (allrecords,AlwaysMatches='') and (not-onloan-count,st-numeric >= 1) and (lost,st-numeric=0) )"; $limit_cgi .= "&limit=available"; $limit_desc .= ""; } @@ -1689,9 +1719,9 @@ sub buildQuery { $limit_cgi .= "&limit=" . uri_escape_utf8($this_limit); if ($this_limit =~ /^branch:(.+)/) { my $branchcode = $1; - my $branchname = GetBranchName($branchcode); - if (defined $branchname) { - $limit_desc .= " branch:$branchname"; + my $library = Koha::Libraries->find( $branchcode ); + if (defined $library) { + $limit_desc .= " branch:" . $library->branchname; } else { $limit_desc .= " $this_limit"; } @@ -1745,6 +1775,7 @@ sub buildQuery { warn "LIMIT DESC:" . $limit_desc; warn "---------\nLeave buildQuery\n---------"; } + return ( undef, $query, $simple_query, $query_cgi, $query_desc, $limit, $limit_cgi, $limit_desc, @@ -1807,42 +1838,39 @@ sub searchResults { require C4::Items; - $search_context = 'opac' if !$search_context || $search_context ne 'intranet'; + $search_context->{'interface'} = 'opac' if !$search_context->{'interface'} || $search_context->{'interface'} ne 'intranet'; my ($is_opac, $hidelostitems); - if ($search_context eq 'opac') { + if ($search_context->{'interface'} eq 'opac') { $hidelostitems = C4::Context->preference('hidelostitems'); $is_opac = 1; } #Build branchnames hash - #find branchname - #get branch information..... - my %branches; - my $bsth =$dbh->prepare("SELECT branchcode,branchname FROM branches"); # FIXME : use C4::Branch::GetBranches - $bsth->execute(); - while ( my $bdata = $bsth->fetchrow_hashref ) { - $branches{ $bdata->{'branchcode'} } = $bdata->{'branchname'}; - } + my %branches = map { $_->branchcode => $_->branchname } Koha::Libraries->search({}, { order_by => 'branchname' }); + # FIXME - We build an authorised values hash here, using the default framework # though it is possible to have different authvals for different fws. - my $shelflocations =GetKohaAuthorisedValues('items.location',''); + my $shelflocations = + { map { $_->{authorised_value} => $_->{lib} } Koha::AuthorisedValues->get_descriptions_by_koha_field( { frameworkcode => '', kohafield => 'items.location' } ) }; # get notforloan authorised value list (see $shelflocations FIXME) - my $notforloan_authorised_value = GetAuthValCode('items.notforloan',''); + my $av = Koha::MarcSubfieldStructures->search({ frameworkcode => '', kohafield => 'items.notforloan', authorised_value => [ -and => {'!=' => undef }, {'!=' => ''}] }); + my $notforloan_authorised_value = $av->count ? $av->next->authorised_value : undef; #Get itemtype hash - my %itemtypes = %{ GetItemTypes() }; + my $itemtypes = Koha::ItemTypes->search_with_localization; + my %itemtypes = map { $_->{itemtype} => $_ } @{ $itemtypes->unblessed }; #search item field code - my ($itemtag, undef) = &GetMarcFromKohaField( "items.itemnumber", "" ); + my ($itemtag, undef) = &GetMarcFromKohaField( "items.itemnumber" ); ## find column names of items related to MARC my %subfieldstosearch; my @columns = Koha::Database->new()->schema()->resultset('Item')->result_source->columns; for my $column ( @columns ) { my ( $tagfield, $tagsubfield ) = - &GetMarcFromKohaField( "items." . $column, "" ); + &GetMarcFromKohaField( "items." . $column ); if ( defined $tagsubfield ) { $subfieldstosearch{$column} = $tagsubfield; } @@ -1859,7 +1887,27 @@ sub searchResults { my $marcflavour = C4::Context->preference("marcflavour"); # We get the biblionumber position in MARC - my ($bibliotag,$bibliosubf)=GetMarcFromKohaField('biblio.biblionumber',''); + 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 + = ( defined $userenv and $userenv->{number} ) + ? Koha::Patrons->find( $userenv->{number} ) + : undef; + my $patron_category_hide_lost_items = ($logged_in_user) ? $logged_in_user->category->hidelostitems : 0; # loop through all of the records we've retrieved for ( my $i = $offset ; $i <= $times - 1 ; $i++ ) { @@ -1888,14 +1936,12 @@ sub searchResults { : GetFrameworkCode($marcrecord->subfield($bibliotag,$bibliosubf)); SetUTF8Flag($marcrecord); - my $oldbiblio = TransformMarcToKoha( $dbh, $marcrecord, $fw ); - $oldbiblio->{subtitle} = GetRecordValue('subtitle', $marcrecord, $fw); + my $oldbiblio = TransformMarcToKoha( $marcrecord, $fw ); $oldbiblio->{result_number} = $i + 1; # add imageurl to itemtype if there is one - $oldbiblio->{imageurl} = getitemtypeimagelocation( $search_context, $itemtypes{ $oldbiblio->{itemtype} }->{imageurl} ); + $oldbiblio->{imageurl} = getitemtypeimagelocation( $search_context->{'interface'}, $itemtypes{ $oldbiblio->{itemtype} }->{imageurl} ); - $oldbiblio->{'authorised_value_images'} = ($search_context eq 'opac' && C4::Context->preference('AuthorisedValueImages')) || ($search_context eq 'intranet' && C4::Context->preference('StaffAuthorisedValueImages')) ? C4::Items::get_authorised_value_images( C4::Biblio::get_biblio_authorised_values( $oldbiblio->{'biblionumber'}, $marcrecord ) ) : []; $oldbiblio->{normalized_upc} = GetNormalizedUPC( $marcrecord,$marcflavour); $oldbiblio->{normalized_ean} = GetNormalizedEAN( $marcrecord,$marcflavour); $oldbiblio->{normalized_oclc} = GetNormalizedOCLCNumber($marcrecord,$marcflavour); @@ -1906,8 +1952,9 @@ sub searchResults { $oldbiblio->{edition} = $oldbiblio->{editionstatement}; $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} ) { + + # FIXME: this is only used in the deprecated non-XLST opac results + if ( !$xslfile && $is_opac && $itemtypes{ $oldbiblio->{itemtype} }->{summary} ) { my $summary = $itemtypes{ $oldbiblio->{itemtype} }->{summary}; my @fields = $marcrecord->fields(); @@ -1953,27 +2000,31 @@ sub searchResults { # Pull out the items fields my @fields = $marcrecord->field($itemtag); my $marcflavor = C4::Context->preference("marcflavour"); + # adding linked items that belong to host records - my $analyticsfield = '773'; - if ($marcflavor eq 'MARC21' || $marcflavor eq 'NORMARC') { - $analyticsfield = '773'; - } elsif ($marcflavor eq 'UNIMARC') { - $analyticsfield = '461'; - } - foreach my $hostfield ( $marcrecord->field($analyticsfield)) { - my $hostbiblionumber = $hostfield->subfield("0"); - my $linkeditemnumber = $hostfield->subfield("9"); - if(!$hostbiblionumber eq undef){ - my $hostbiblio = GetMarcBiblio($hostbiblionumber, 1); - my ($itemfield, undef) = GetMarcFromKohaField( 'items.itemnumber', GetFrameworkCode($hostbiblionumber) ); - if(!$hostbiblio eq undef){ - my @hostitems = $hostbiblio->field($itemfield); - foreach my $hostitem (@hostitems){ - if ($hostitem->subfield("9") eq $linkeditemnumber){ - my $linkeditem =$hostitem; - # append linked items if they exist - if (!$linkeditem eq undef){ - push (@fields, $linkeditem);} + if ( C4::Context->preference('EasyAnalyticalRecords') ) { + my $analyticsfield = '773'; + if ($marcflavor eq 'MARC21' || $marcflavor eq 'NORMARC') { + $analyticsfield = '773'; + } elsif ($marcflavor eq 'UNIMARC') { + $analyticsfield = '461'; + } + foreach my $hostfield ( $marcrecord->field($analyticsfield)) { + 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; + } } } } @@ -2026,7 +2077,7 @@ sub searchResults { next; } # hidden based on OpacHiddenItems syspref - my @hi = C4::Items::GetHiddenItemnumbers($item); + my @hi = C4::Items::GetHiddenItemnumbers({ items=> [ $item ], borcat => $search_context->{category} }); if (scalar @hi) { push @hiddenitems, @hi; $hideatopac_count++; @@ -2047,20 +2098,20 @@ 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} ) ) + and $logged_in_user + and !( $patron_category_hide_lost_items and $item->{itemlost} ) ) { $onloan_count++; my $key = $prefix . $item->{onloan} . $item->{barcode}; - $onloan_items->{$key}->{due_date} = output_pref( { dt => dt_from_string( $item->{onloan} ), dateonly => 1 } ); + $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}->{itemcallnumber} = $item->{itemcallnumber}; $onloan_items->{$key}->{description} = $item->{description}; $onloan_items->{$key}->{imageurl} = - getitemtypeimagelocation( $search_context, $itemtypes{ $item->{itype} }->{imageurl} ); + getitemtypeimagelocation( $search_context->{'interface'}, $itemtypes{ $item->{itype} }->{imageurl} ); # if something's checked out and lost, mark it as 'long overdue' if ( $item->{itemlost} ) { @@ -2095,7 +2146,8 @@ sub searchResults { || $item->{itemlost} || $item->{damaged} || $item->{notforloan} - || $items_count > C4::Context->preference('MaxSearchResultsItemsPerRecordStatusCheck') ) { + || ( C4::Context->preference('MaxSearchResultsItemsPerRecordStatusCheck') + && $items_count > C4::Context->preference('MaxSearchResultsItemsPerRecordStatusCheck') ) ) { # A couple heuristics to limit how many times # we query the database for item transfer information, sacrificing @@ -2149,21 +2201,21 @@ sub searchResults { $other_items->{$key}->{intransit} = ( $transfertwhen ne '' ) ? 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}; - $other_items->{$key}->{location} = $shelflocations->{ $item->{location} }; - $other_items->{$key}->{description} = $item->{description}; - $other_items->{$key}->{imageurl} = getitemtypeimagelocation( $search_context, $itemtypes{ $item->{itype} }->{imageurl} ); + $other_items->{$key}->{count}++ if $item->{$hbranch}; + $other_items->{$key}->{location} = $shelflocations->{ $item->{location} }; + $other_items->{$key}->{description} = $item->{description}; + $other_items->{$key}->{imageurl} = getitemtypeimagelocation( $search_context->{'interface'}, $itemtypes{ $item->{itype} }->{imageurl} ); } # item is available else { $can_place_holds = 1; $available_count++; - $available_items->{$prefix}->{count}++ if $item->{$hbranch}; - foreach (qw(branchname itemcallnumber description)) { - $available_items->{$prefix}->{$_} = $item->{$_}; - } - $available_items->{$prefix}->{location} = $shelflocations->{ $item->{location} }; - $available_items->{$prefix}->{imageurl} = getitemtypeimagelocation( $search_context, $itemtypes{ $item->{itype} }->{imageurl} ); + $available_items->{$prefix}->{count}++ if $item->{$hbranch}; + foreach (qw(branchname itemcallnumber description)) { + $available_items->{$prefix}->{$_} = $item->{$_}; + } + $available_items->{$prefix}->{location} = $shelflocations->{ $item->{location} }; + $available_items->{$prefix}->{imageurl} = getitemtypeimagelocation( $search_context->{'interface'}, $itemtypes{ $item->{itype} }->{imageurl} ); } } } # notforloan, item level and biblioitem level @@ -2188,10 +2240,9 @@ sub searchResults { } # XSLT processing of some stuff - 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 + # 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 biblio level itypes are used and itemtype is notforloan, it can't be reserved either @@ -2201,7 +2252,6 @@ sub searchResults { } } $oldbiblio->{norequests} = 1 unless $can_place_holds; - $oldbiblio->{itemsplural} = 1 if $items_count > 1; $oldbiblio->{items_count} = $items_count; $oldbiblio->{available_items_loop} = \@available_items_loop; $oldbiblio->{onloan_items_loop} = \@onloan_items_loop; @@ -2248,101 +2298,14 @@ sub searchResults { $oldbiblio->{'alternateholdings_count'} = $alternateholdingscount; } + $oldbiblio->{biblio_object} = Koha::Biblios->find( $oldbiblio->{biblionumber} ); + push( @newresults, $oldbiblio ); } return @newresults; } -=head2 SearchAcquisitions - Search for acquisitions -=cut - -sub SearchAcquisitions{ - my ($datebegin, $dateend, $itemtypes,$criteria, $orderby) = @_; - - my $dbh=C4::Context->dbh; - # Variable initialization - my $str=qq| - SELECT marcxml - FROM biblio - LEFT JOIN biblioitems ON biblioitems.biblionumber=biblio.biblionumber - LEFT JOIN items ON items.biblionumber=biblio.biblionumber - WHERE dateaccessioned BETWEEN ? AND ? - |; - - my (@params,@loopcriteria); - - push @params, $datebegin->output("iso"); - push @params, $dateend->output("iso"); - - if (scalar(@$itemtypes)>0 and $criteria ne "itemtype" ){ - if(C4::Context->preference("item-level_itypes")){ - $str .= "AND items.itype IN (?".( ',?' x scalar @$itemtypes - 1 ).") "; - }else{ - $str .= "AND biblioitems.itemtype IN (?".( ',?' x scalar @$itemtypes - 1 ).") "; - } - push @params, @$itemtypes; - } - - if ($criteria =~/itemtype/){ - if(C4::Context->preference("item-level_itypes")){ - $str .= "AND items.itype=? "; - }else{ - $str .= "AND biblioitems.itemtype=? "; - } - - if(scalar(@$itemtypes) == 0){ - my $itypes = GetItemTypes(); - for my $key (keys %$itypes){ - push @$itemtypes, $key; - } - } - - @loopcriteria= @$itemtypes; - }elsif ($criteria=~/itemcallnumber/){ - $str .= "AND (items.itemcallnumber LIKE CONCAT(?,'%') - OR items.itemcallnumber is NULL - OR items.itemcallnumber = '')"; - - @loopcriteria = ("AA".."ZZ", "") unless (scalar(@loopcriteria)>0); - }else { - $str .= "AND biblio.title LIKE CONCAT(?,'%') "; - @loopcriteria = ("A".."z") unless (scalar(@loopcriteria)>0); - } - - if ($orderby =~ /date_desc/){ - $str.=" ORDER BY dateaccessioned DESC"; - } else { - $str.=" ORDER BY title"; - } - - my $qdataacquisitions=$dbh->prepare($str); - - my @loopacquisitions; - foreach my $value(@loopcriteria){ - push @params,$value; - my %cell; - $cell{"title"}=$value; - $cell{"titlecode"}=$value; - - eval{$qdataacquisitions->execute(@params);}; - - if ($@){ warn "recentacquisitions Error :$@";} - else { - my @loopdata; - while (my $data=$qdataacquisitions->fetchrow_hashref){ - push @loopdata, {"summary"=>GetBiblioSummary( $data->{'marcxml'} ) }; - } - $cell{"loopdata"}=\@loopdata; - } - push @loopacquisitions,\%cell if (scalar(@{$cell{loopdata}})>0); - pop @params; - } - $qdataacquisitions->finish; - return \@loopacquisitions; -} - =head2 enabled_staff_search_views %hash = enabled_staff_search_views() @@ -2526,7 +2489,7 @@ sub _ZOOM_event_loop { =head2 new_record_from_zebra -Given raw data from a Zebra result set, return a MARC::Record object +Given raw data from a searchengine result set, return a MARC::Record object This helper function is needed to take into account all the involved system preferences and configuration variables to properly create the @@ -2535,6 +2498,9 @@ MARC::Record object. If we are using GRS-1, then the raw data we get from Zebra should be USMARC data. If we are using DOM, then it has to be MARCXML. +If we are using elasticsearch, it'll already be a MARC::Record and this +function needs a new name. + =cut sub new_record_from_zebra { @@ -2542,6 +2508,10 @@ sub new_record_from_zebra { my $server = shift; my $raw_data = shift; # Set the default indexing modes + my $search_engine = C4::Context->preference("SearchEngine"); + if ($search_engine eq 'Elasticsearch') { + return ref $raw_data eq 'MARC::Record' ? $raw_data : MARC::Record->new_from_xml( $raw_data, 'UTF-8' ); + } my $index_mode = ( $server eq 'biblioserver' ) ? C4::Context->config('zebra_bib_index_mode') // 'dom' : C4::Context->config('zebra_auth_index_mode') // 'dom';